SDK/TextAreaWidget: Add text shifting by cursor position

This commit is contained in:
Lynix 2019-07-06 01:09:50 +02:00
parent e1621bc044
commit 7f324dda9a
1 changed files with 74 additions and 26 deletions

View File

@ -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<GraphicsComponent>();
m_cursorEntity->AddComponent<NodeComponent>().SetParent(this);
m_cursorEntity->GetComponent<NodeComponent>().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<NodeComponent>();
textNode.SetParent(this);
textNode.SetPosition(5.f, 3.f);
textNode.SetPosition(paddingWidth, paddingHeight);
m_cursorEntity = CreateEntity();
m_cursorEntity->AddComponent<GraphicsComponent>();
m_cursorEntity->AddComponent<NodeComponent>().SetParent(m_textEntity);
m_cursorEntity->GetComponent<NodeComponent>();
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<Ndk::NodeComponent>();
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<Ndk::NodeComponent>();
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));