diff --git a/ChangeLog.md b/ChangeLog.md index 9fc275f46..da3410fb7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -188,6 +188,7 @@ Nazara Development Kit: - Fixed GraphicsComponent copy constructor not copying scissor rect - Force parent parameter to be present in widgets constructor - Added the possibility to write only specific characters with a predicate in TextAreaWidget +- Enable write of Tab character in TextAreaWidget - It is now possible to disable object culling in the RenderSystem - Make Nz::PhysWorld2D& Ndk::PhysicsSystem2D::GetWorld private and rename it into GetPhysWorld - Make Ndk::PhysicsSystem2D an interface of Nz::PhysWorld2D diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.hpp b/SDK/include/NDK/Widgets/TextAreaWidget.hpp index 4c3168c4e..f57e7a049 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.hpp +++ b/SDK/include/NDK/Widgets/TextAreaWidget.hpp @@ -32,8 +32,12 @@ namespace Ndk //virtual TextAreaWidget* Clone() const = 0; - inline void EnableMultiline(bool enable = true); + inline void EnableMultiline(bool enable = true); + inline void EnableTabWriting(bool enable = true); + + inline void Erase(std::size_t glyphPosition); + void Erase(std::size_t firstGlyph, std::size_t lastGlyph); void EraseSelection(); inline CharacterFilter GetCharacterFilter() const; @@ -52,6 +56,7 @@ namespace Ndk inline bool IsMultilineEnabled() const; inline bool IsReadOnly() const; + inline bool IsTabWritingEnabled() const; inline void MoveCursor(int offset); inline void MoveCursor(const Nz::Vector2i& offset); @@ -68,7 +73,9 @@ namespace Ndk inline void SetText(const Nz::String& text); inline void SetTextColor(const Nz::Color& text); - void Write(const Nz::String& text); + inline void Write(const Nz::String& text); + inline void Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition); + void Write(const Nz::String& text, std::size_t glyphPosition); TextAreaWidget& operator=(const TextAreaWidget&) = delete; TextAreaWidget& operator=(TextAreaWidget&&) = default; @@ -115,6 +122,7 @@ namespace Ndk bool m_isMouseButtonDown; bool m_multiLineEnabled; bool m_readOnly; + bool m_tabEnabled; // writes (Shift+)Tab character if set to true }; } diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.inl b/SDK/include/NDK/Widgets/TextAreaWidget.inl index 7b49ddb26..88be7520a 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.inl +++ b/SDK/include/NDK/Widgets/TextAreaWidget.inl @@ -23,6 +23,16 @@ namespace Ndk m_multiLineEnabled = enable; } + inline void TextAreaWidget::EnableTabWriting(bool enable) + { + m_tabEnabled = enable; + } + + inline void TextAreaWidget::Erase(std::size_t glyphPosition) + { + Erase(glyphPosition, glyphPosition + 1U); + } + inline TextAreaWidget::CharacterFilter TextAreaWidget::GetCharacterFilter() const { return m_characterFilter; @@ -102,6 +112,11 @@ namespace Ndk return m_multiLineEnabled; } + inline bool TextAreaWidget::IsTabWritingEnabled() const + { + return m_tabEnabled; + } + inline bool TextAreaWidget::IsReadOnly() const { return m_readOnly; @@ -233,4 +248,14 @@ namespace Ndk m_textSprite->Update(m_drawer); } + + inline void TextAreaWidget::Write(const Nz::String& text) + { + Write(text, GetGlyphIndex(m_cursorPositionBegin)); + } + + inline void TextAreaWidget::Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition) + { + Write(text, GetGlyphIndex(glyphPosition)); + } } diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp index 334a78079..34ebac579 100644 --- a/SDK/src/NDK/Widgets/TextAreaWidget.cpp +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -17,7 +17,8 @@ namespace Ndk m_cursorPositionEnd(0U, 0U), m_isMouseButtonDown(false), m_multiLineEnabled(false), - m_readOnly(false) + m_readOnly(false), + m_tabEnabled(false) { m_cursorEntity = CreateEntity(true); m_cursorEntity->AddComponent(); @@ -71,26 +72,41 @@ namespace Ndk OnTextChanged(this, m_text); } + void TextAreaWidget::Erase(std::size_t firstGlyph, std::size_t lastGlyph) + { + if (firstGlyph > lastGlyph) + std::swap(firstGlyph, lastGlyph); + + std::size_t textLength = m_text.GetLength(); + if (firstGlyph > textLength) + return; + + Nz::String newText; + if (firstGlyph > 0) + { + std::size_t characterPosition = m_text.GetCharacterPosition(firstGlyph - 1); + NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position"); + + newText.Append(m_text.SubString(0, characterPosition)); + } + + if (lastGlyph < textLength) + { + std::size_t characterPosition = m_text.GetCharacterPosition(lastGlyph); + NazaraAssert(characterPosition != Nz::String::npos, "Invalid character position"); + + newText.Append(m_text.SubString(characterPosition)); + } + + SetText(newText); + } + void TextAreaWidget::EraseSelection() { if (!HasSelection()) return; - std::size_t cursorGlyphBegin = GetGlyphIndex(m_cursorPositionBegin); - std::size_t cursorGlyphEnd = GetGlyphIndex(m_cursorPositionEnd); - - std::size_t textLength = m_text.GetLength(); - if (cursorGlyphBegin > textLength) - return; - - Nz::String newText; - if (cursorGlyphBegin > 0) - newText.Append(m_text.SubString(0, m_text.GetCharacterPosition(cursorGlyphBegin) - 1)); - - if (cursorGlyphEnd < textLength) - newText.Append(m_text.SubString(m_text.GetCharacterPosition(cursorGlyphEnd))); - - SetText(newText); + Erase(GetGlyphIndex(m_cursorPositionBegin), GetGlyphIndex(m_cursorPositionEnd)); } Nz::Vector2ui TextAreaWidget::GetHoveredGlyph(float x, float y) const @@ -129,21 +145,19 @@ namespace Ndk SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); } - void TextAreaWidget::Write(const Nz::String& text) + void TextAreaWidget::Write(const Nz::String& text, std::size_t glyphPosition) { - std::size_t cursorGlyph = GetGlyphIndex(m_cursorPositionBegin); - - if (cursorGlyph >= m_drawer.GetGlyphCount()) + if (glyphPosition >= m_drawer.GetGlyphCount()) { AppendText(text); SetCursorPosition(m_drawer.GetGlyphCount()); } else { - m_text.Insert(m_text.GetCharacterPosition(cursorGlyph), text); + m_text.Insert(m_text.GetCharacterPosition(glyphPosition), text); SetText(m_text); - SetCursorPosition(cursorGlyph + text.GetLength()); + SetCursorPosition(glyphPosition + text.GetLength()); } } @@ -181,23 +195,7 @@ namespace Ndk if (HasSelection()) EraseSelection(); else - { - std::size_t cursorGlyphBegin = GetGlyphIndex(m_cursorPositionBegin); - std::size_t cursorGlyphEnd = GetGlyphIndex(m_cursorPositionEnd); - - std::size_t textLength = m_text.GetLength(); - if (cursorGlyphBegin > textLength) - return true; - - Nz::String newText; - if (cursorGlyphBegin > 0) - newText.Append(m_text.SubString(0, m_text.GetCharacterPosition(cursorGlyphBegin) - 1)); - - if (cursorGlyphEnd < textLength) - newText.Append(m_text.SubString(m_text.GetCharacterPosition(cursorGlyphEnd + 1))); - - SetText(newText); - } + Erase(GetGlyphIndex(m_cursorPositionBegin)); return true; } @@ -338,6 +336,58 @@ namespace Ndk return true; } + case Nz::Keyboard::Tab: + { + if (!m_tabEnabled) + return false; + + if (HasSelection()) + { + for(unsigned line = m_cursorPositionBegin.y; line <= m_cursorPositionEnd.y; ++line) + { + const Nz::Vector2ui cursorPositionBegin = m_cursorPositionBegin; + const Nz::Vector2ui cursorPositionEnd = m_cursorPositionEnd; + + if (key.shift) + { + if (m_drawer.GetLineGlyphCount(line) == 0) + continue; + + std::size_t firstGlyph = GetGlyphIndex({ 0U, line }); + + if (m_text[m_text.GetCharacterPosition(firstGlyph)] == '\t') + { + Erase(firstGlyph); + SetSelection(cursorPositionBegin - (cursorPositionBegin.y == line && cursorPositionBegin.x != 0U ? Nz::Vector2ui { 1U, 0U } : Nz::Vector2ui {}), + cursorPositionEnd - (cursorPositionEnd.y == line && cursorPositionEnd.x != 0U ? Nz::Vector2ui { 1U, 0U } : Nz::Vector2ui {})); + } + } + else + { + Write(Nz::String('\t'), { 0U, line }); + SetSelection(cursorPositionBegin + (cursorPositionBegin.y == line && cursorPositionBegin.x != 0U ? Nz::Vector2ui { 1U, 0U } : Nz::Vector2ui {}), + cursorPositionEnd + (cursorPositionEnd.y == line ? Nz::Vector2ui { 1U, 0U } : Nz::Vector2ui {})); + } + } + } + else if (key.shift) + { + std::size_t currentGlyph = GetGlyphIndex(m_cursorPositionBegin); + + if (currentGlyph > 0 && m_text[m_text.GetCharacterPosition(currentGlyph - 1U)] == '\t') // Check if previous glyph is a tab + { + Erase(currentGlyph - 1U); + + if (m_cursorPositionBegin.x < static_cast(m_drawer.GetLineGlyphCount(m_cursorPositionBegin.y))) + MoveCursor(-1); + } + } + else + Write(Nz::String('\t')); + + return true; + } + default: return false; } @@ -438,6 +488,9 @@ namespace Ndk if (ignoreDefaultAction || !m_multiLineEnabled) break; + if (HasSelection()) + EraseSelection(); + Write(Nz::String('\n')); break; }