From c931e9a5095753c0cbe1b2d67c725f10887f6fea Mon Sep 17 00:00:00 2001 From: SirLynix Date: Fri, 26 Jan 2024 16:15:53 +0100 Subject: [PATCH] Widgets/TextAreaWidget: Add a way to limit text length --- .../Nazara/Widgets/AbstractTextAreaWidget.hpp | 3 ++ .../Nazara/Widgets/AbstractTextAreaWidget.inl | 6 +++ include/Nazara/Widgets/RichTextAreaWidget.hpp | 1 + include/Nazara/Widgets/TextAreaWidget.hpp | 1 + src/Nazara/Widgets/AbstractTextAreaWidget.cpp | 7 ++- src/Nazara/Widgets/RichTextAreaWidget.cpp | 52 +++++++++++++++++-- src/Nazara/Widgets/TextAreaWidget.cpp | 31 +++++++++-- 7 files changed, 92 insertions(+), 9 deletions(-) diff --git a/include/Nazara/Widgets/AbstractTextAreaWidget.hpp b/include/Nazara/Widgets/AbstractTextAreaWidget.hpp index 44becaa57..3dbecfc54 100644 --- a/include/Nazara/Widgets/AbstractTextAreaWidget.hpp +++ b/include/Nazara/Widgets/AbstractTextAreaWidget.hpp @@ -42,6 +42,7 @@ namespace Nz inline EchoMode GetEchoMode() const; inline std::size_t GetGlyphIndex() const; inline std::size_t GetGlyphIndex(const Vector2ui& cursorPosition) const; + inline std::size_t GetMaximumTextLength() const; Vector2ui GetHoveredGlyph(float x, float y) const; @@ -61,6 +62,7 @@ namespace Nz inline void SetCursorPosition(std::size_t glyphIndex); inline void SetCursorPosition(Vector2ui cursorPosition); inline void SetEchoMode(EchoMode echoMode); + virtual void SetMaximumTextLength(std::size_t maximumLength) = 0; inline void SetReadOnly(bool readOnly = true); inline void SetSelection(Vector2ui fromPosition, Vector2ui toPosition); @@ -131,6 +133,7 @@ namespace Nz }; std::shared_ptr m_textSprite; + std::size_t m_maximumTextLength; std::vector m_cursors; CharacterFilter m_characterFilter; EchoMode m_echoMode; diff --git a/include/Nazara/Widgets/AbstractTextAreaWidget.inl b/include/Nazara/Widgets/AbstractTextAreaWidget.inl index 6c2d6f1b9..1c2e09f4b 100644 --- a/include/Nazara/Widgets/AbstractTextAreaWidget.inl +++ b/include/Nazara/Widgets/AbstractTextAreaWidget.inl @@ -87,6 +87,11 @@ namespace Nz return glyphIndex; } + inline std::size_t AbstractTextAreaWidget::GetMaximumTextLength() const + { + return m_maximumTextLength; + } + inline bool AbstractTextAreaWidget::HasSelection() const { return m_cursorPositionBegin != m_cursorPositionEnd; @@ -257,3 +262,4 @@ namespace Nz } #include +#include "AbstractTextAreaWidget.hpp" diff --git a/include/Nazara/Widgets/RichTextAreaWidget.hpp b/include/Nazara/Widgets/RichTextAreaWidget.hpp index f3a6632d2..6231d5691 100644 --- a/include/Nazara/Widgets/RichTextAreaWidget.hpp +++ b/include/Nazara/Widgets/RichTextAreaWidget.hpp @@ -38,6 +38,7 @@ namespace Nz inline void SetCharacterSize(unsigned int characterSize); inline void SetCharacterSpacingOffset(float offset); inline void SetLineSpacingOffset(float offset); + void SetMaximumTextLength(std::size_t maximumLength) override; inline void SetTextColor(const Color& color); inline void SetTextFont(std::shared_ptr font); inline void SetTextOutlineColor(const Color& color); diff --git a/include/Nazara/Widgets/TextAreaWidget.hpp b/include/Nazara/Widgets/TextAreaWidget.hpp index af4aa6b2c..9b50a2e1b 100644 --- a/include/Nazara/Widgets/TextAreaWidget.hpp +++ b/include/Nazara/Widgets/TextAreaWidget.hpp @@ -41,6 +41,7 @@ namespace Nz inline void SetCharacterSize(unsigned int characterSize); inline void SetCharacterSpacingOffset(float offset); inline void SetLineSpacingOffset(float offset); + void SetMaximumTextLength(std::size_t maximumLength) override; inline void SetText(std::string text); inline void SetTextColor(const Color& text); inline void SetTextFont(std::shared_ptr font); diff --git a/src/Nazara/Widgets/AbstractTextAreaWidget.cpp b/src/Nazara/Widgets/AbstractTextAreaWidget.cpp index 797ee744b..92a07d79c 100644 --- a/src/Nazara/Widgets/AbstractTextAreaWidget.cpp +++ b/src/Nazara/Widgets/AbstractTextAreaWidget.cpp @@ -22,7 +22,7 @@ namespace Nz AbstractTextAreaWidget::AbstractTextAreaWidget(BaseWidget* parent) : BaseWidget(parent), - m_characterFilter(), + m_maximumTextLength(0), m_echoMode(EchoMode::Normal), m_cursorPositionBegin(0U, 0U), m_cursorPositionEnd(0U, 0U), @@ -120,6 +120,11 @@ namespace Nz return Vector2ui::Zero(); } + void AbstractTextAreaWidget::SetMaximumTextLength(std::size_t maximumLength) + { + m_maximumTextLength = maximumLength; + } + Color AbstractTextAreaWidget::GetCursorColor() const { if (m_cursorPositionBegin == m_cursorPositionEnd) diff --git a/src/Nazara/Widgets/RichTextAreaWidget.cpp b/src/Nazara/Widgets/RichTextAreaWidget.cpp index 085c5bfab..e34666169 100644 --- a/src/Nazara/Widgets/RichTextAreaWidget.cpp +++ b/src/Nazara/Widgets/RichTextAreaWidget.cpp @@ -132,22 +132,66 @@ namespace Nz UpdateDisplayText(); } + void RichTextAreaWidget::SetMaximumTextLength(std::size_t maximumLength) + { + AbstractTextAreaWidget::SetMaximumTextLength(maximumLength); + + if (m_maximumTextLength > 0 && m_drawer.HasBlocks()) + { + std::size_t blockIndex = m_drawer.FindBlock(m_maximumTextLength); + + auto blockRef = m_drawer.GetBlock(blockIndex); + const std::string& blockText = blockRef.GetText(); + + assert(m_maximumTextLength >= blockRef.GetFirstGlyphIndex()); + std::size_t maxBlockSize = m_maximumTextLength - blockRef.GetFirstGlyphIndex(); + std::size_t textLength = ComputeCharacterCount(blockText); + if (textLength > maxBlockSize) + { + blockRef.SetText(std::string(TrimRightCount(blockText, textLength - maxBlockSize, Nz::UnicodeAware{}))); + + // And then remove all blocks after the limit (in reverse order because of index shifting) + std::size_t lastBlockIndex = m_drawer.GetBlockCount(); + assert(lastBlockIndex > 0); + for (std::size_t i = lastBlockIndex - 1; i > blockIndex; --i) + m_drawer.RemoveBlock(i); + + UpdateDisplayText(); + } + } + } + void RichTextAreaWidget::Write(std::string_view text, std::size_t glyphPosition) { if (m_drawer.HasBlocks()) { - auto block = m_drawer.GetBlock(m_drawer.FindBlock((glyphPosition > 0) ? glyphPosition - 1 : glyphPosition)); - std::size_t firstGlyph = block.GetFirstGlyphIndex(); + if (m_maximumTextLength > 0) + { + auto lastBlockRef = m_drawer.GetBlock(m_drawer.GetBlockCount() - 1); + std::size_t currentLength = lastBlockRef.GetFirstGlyphIndex() + ComputeCharacterCount(lastBlockRef.GetText()); + if (m_maximumTextLength <= currentLength) + return; + + text = Substring(text, 0, m_maximumTextLength - currentLength, UnicodeAware{}); + } + + auto blockRef = m_drawer.GetBlock(m_drawer.FindBlock((glyphPosition > 0) ? glyphPosition - 1 : glyphPosition)); + std::size_t firstGlyph = blockRef.GetFirstGlyphIndex(); assert(glyphPosition >= firstGlyph); - std::string blockText = block.GetText(); + std::string blockText = blockRef.GetText(); std::size_t characterPosition = GetCharacterPosition(blockText, glyphPosition - firstGlyph); blockText.insert(characterPosition, text); - block.SetText(blockText); + blockRef.SetText(blockText); } else + { + if (m_maximumTextLength > 0) + text = Substring(text, 0, m_maximumTextLength, UnicodeAware{}); + m_drawer.AppendText(text); + } SetCursorPosition(glyphPosition + ComputeCharacterCount(text)); diff --git a/src/Nazara/Widgets/TextAreaWidget.cpp b/src/Nazara/Widgets/TextAreaWidget.cpp index 624389716..f2bf651cd 100644 --- a/src/Nazara/Widgets/TextAreaWidget.cpp +++ b/src/Nazara/Widgets/TextAreaWidget.cpp @@ -41,6 +41,15 @@ namespace Nz void TextAreaWidget::AppendText(std::string_view text) { + if (m_maximumTextLength > 0) + { + std::size_t currentLength = ComputeCharacterCount(m_text); + if (m_maximumTextLength <= currentLength) + return; + + text = Substring(text, 0, m_maximumTextLength - currentLength, UnicodeAware{}); + } + m_text += text; switch (m_echoMode) @@ -113,6 +122,14 @@ namespace Nz SetText(newText); } + void TextAreaWidget::SetMaximumTextLength(std::size_t maximumLength) + { + AbstractTextAreaWidget::SetMaximumTextLength(maximumLength); + + if (m_maximumTextLength > 0 && ComputeCharacterCount(m_text) > m_maximumTextLength) + SetText(std::string(Substring(m_text, 0, m_maximumTextLength, UnicodeAware{}))); + } + void TextAreaWidget::Write(std::string_view text, std::size_t glyphPosition) { if (glyphPosition >= m_drawer.GetGlyphCount()) @@ -123,6 +140,15 @@ namespace Nz } else { + if (m_maximumTextLength > 0) + { + std::size_t currentLength = ComputeCharacterCount(m_text); + if (m_maximumTextLength <= currentLength) + return; + + text = Substring(text, 0, m_maximumTextLength - currentLength, UnicodeAware{}); + } + m_text.insert(GetCharacterPosition(m_text, glyphPosition), text); SetText(m_text); @@ -284,10 +310,7 @@ namespace Nz if (clipboardString.empty()) return; - m_text.insert(targetIndex, clipboardString); - UpdateDisplayText(); - - SetCursorPosition(targetIndex + ComputeCharacterCount(clipboardString)); + Write(clipboardString, targetIndex); } void TextAreaWidget::UpdateDisplayText()