diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp index 9d695cf39..20395a2ed 100644 --- a/SDK/src/NDK/Widgets/TextAreaWidget.cpp +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -10,6 +10,12 @@ namespace Ndk { + namespace + { + constexpr float paddingWidth = 5.f; + constexpr float paddingHeight = 3.f; + } + TextAreaWidget::TextAreaWidget(BaseWidget* parent) : BaseWidget(parent), m_characterFilter(), @@ -22,12 +28,6 @@ namespace Ndk m_readOnly(false), m_tabEnabled(false) { - m_cursorEntity = CreateEntity(); - m_cursorEntity->AddComponent(); - m_cursorEntity->AddComponent().SetParent(this); - m_cursorEntity->GetComponent().SetPosition(5.f, 3.f); - m_cursorEntity->Enable(false); - m_textSprite = Nz::TextSprite::New(); m_textEntity = CreateEntity(); @@ -35,7 +35,13 @@ namespace Ndk auto& textNode = m_textEntity->AddComponent(); textNode.SetParent(this); - textNode.SetPosition(5.f, 3.f); + textNode.SetPosition(paddingWidth, paddingHeight); + + m_cursorEntity = CreateEntity(); + m_cursorEntity->AddComponent(); + m_cursorEntity->AddComponent().SetParent(m_textEntity); + m_cursorEntity->GetComponent(); + m_cursorEntity->Enable(false); SetCursor(Nz::SystemCursor_Text); SetCharacterSize(GetCharacterSize()); //< Actualize minimum / preferred size @@ -134,6 +140,11 @@ namespace Ndk Nz::Vector2ui TextAreaWidget::GetHoveredGlyph(float x, float y) const { + auto& textNode = m_textEntity->GetComponent(); + Nz::Vector2f textPosition = Nz::Vector2f(textNode.GetPosition(Nz::CoordSys_Local)); + x -= textPosition.x; + y -= textPosition.y; + std::size_t glyphCount = m_drawer.GetGlyphCount(); if (glyphCount > 0) { @@ -401,14 +412,14 @@ namespace Ndk { 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 {})); + 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 {})); + cursorPositionEnd + (cursorPositionEnd.y == line ? Nz::Vector2ui { 1U, 0U } : Nz::Vector2ui {})); } } } @@ -445,7 +456,7 @@ namespace Ndk { SetFocus(); - Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x) - 5.f, float(y) - 5.f); + Nz::Vector2ui hoveredGlyph = GetHoveredGlyph(float(x), float(y)); // Shift extends selection if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RShift)) @@ -475,7 +486,7 @@ namespace Ndk void TextAreaWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY) { if (m_isMouseButtonDown) - SetSelection(m_selectionCursor, GetHoveredGlyph(float(x) - 5.f, float(y) - 3.f)); + SetSelection(m_selectionCursor, GetHoveredGlyph(float(x), float(y))); } void TextAreaWidget::OnTextEntered(char32_t character, bool /*repeated*/) @@ -552,6 +563,46 @@ namespace Ndk if (m_readOnly) return; + auto GetGlyph = [&](const Nz::Vector2ui& glyphPosition, std::size_t* glyphIndex) -> const Nz::AbstractTextDrawer::Glyph* + { + const auto& lineInfo = m_drawer.GetLine(glyphPosition.y); + + std::size_t cursorGlyph = GetGlyphIndex({ glyphPosition.x, glyphPosition.y }); + if (glyphIndex) + *glyphIndex = cursorGlyph; + + std::size_t glyphCount = m_drawer.GetGlyphCount(); + float position; + if (glyphCount > 0 && lineInfo.glyphIndex < cursorGlyph) + { + const auto& glyph = m_drawer.GetGlyph(std::min(cursorGlyph, glyphCount - 1)); + return &glyph; + } + else + return nullptr; + }; + + // Move text so that cursor is always visible + const auto* glyph = GetGlyph(m_cursorPositionEnd, nullptr); + float glyphPos = (glyph) ? glyph->bounds.x : 0.f; + float glyphWidth = (glyph) ? glyph->bounds.width : 0.f; + + auto& node = m_textEntity->GetComponent(); + float textPosition = node.GetPosition(Nz::CoordSys_Local).x - paddingWidth; + float cursorPosition = glyphPos + textPosition; + float width = GetWidth(); + + if (width <= m_drawer.GetBounds().width) + { + if (cursorPosition + glyphWidth > width) + node.Move(width - cursorPosition - glyphWidth, 0.f); + else if (cursorPosition - glyphWidth < 0.f) + node.Move(-cursorPosition + glyphWidth, 0.f); + } + else + node.Move(-textPosition, 0.f); // Reset text position if we have enough room to show everything + + // Show cursor/selection std::size_t selectionLineCount = m_cursorPositionEnd.y - m_cursorPositionBegin.y + 1; std::size_t oldSpriteCount = m_cursorSprites.size(); if (m_cursorSprites.size() != selectionLineCount) @@ -576,27 +627,24 @@ namespace Ndk Nz::SpriteRef& cursorSprite = m_cursorSprites[i - m_cursorPositionBegin.y]; if (i == m_cursorPositionBegin.y || i == m_cursorPositionEnd.y) { - auto GetGlyphPos = [&](unsigned int localGlyphPos) + auto GetGlyphPos = [&](const Nz::Vector2ui& glyphPosition) { - std::size_t cursorGlyph = GetGlyphIndex({ localGlyphPos, i }); - - std::size_t glyphCount = m_drawer.GetGlyphCount(); - float position; - if (glyphCount > 0 && lineInfo.glyphIndex < cursorGlyph) + std::size_t glyphIndex; + const auto* glyph = GetGlyph(glyphPosition, &glyphIndex); + if (glyph) { - const auto& glyph = m_drawer.GetGlyph(std::min(cursorGlyph, glyphCount - 1)); - position = glyph.bounds.x; - if (cursorGlyph >= glyphCount) - position += glyph.bounds.width; + float position = glyph->bounds.x; + if (glyphIndex >= m_drawer.GetGlyphCount()) + position += glyph->bounds.width; + + return position; } else - position = 0.f; - - return position; + return 0.f; }; - float beginX = (i == m_cursorPositionBegin.y) ? GetGlyphPos(m_cursorPositionBegin.x) : 0.f; - float endX = (i == m_cursorPositionEnd.y) ? GetGlyphPos(m_cursorPositionEnd.x) : lineInfo.bounds.width; + float beginX = (i == m_cursorPositionBegin.y) ? GetGlyphPos({ m_cursorPositionBegin.x, i }) : 0.f; + float endX = (i == m_cursorPositionEnd.y) ? GetGlyphPos({ m_cursorPositionEnd.x, i }) : lineInfo.bounds.width; float spriteSize = std::max(endX - beginX, 1.f); cursorSprite->SetColor((m_cursorPositionBegin == m_cursorPositionEnd) ? Nz::Color::Black : Nz::Color(0, 0, 0, 50));