diff --git a/PortabilityLayer/PLEditboxWidget.cpp b/PortabilityLayer/PLEditboxWidget.cpp index 46a607d..61aeeda 100644 --- a/PortabilityLayer/PLEditboxWidget.cpp +++ b/PortabilityLayer/PLEditboxWidget.cpp @@ -13,6 +13,7 @@ #include "PLTimeTaggedVOSEvent.h" #include "ResolveCachingColor.h" #include "TextPlacer.h" +#include "Rect2i.h" #include @@ -30,9 +31,11 @@ namespace PortabilityLayer , m_caratScrollLocked(false) , m_hasFocus(false) , m_caratTimer(0) + , m_selectionScrollTimer(0) , m_isMultiLine(false) , m_isDraggingSelection(false) , m_dragSelectionStartChar(false) + , m_scrollOffset(0, 0) { } @@ -99,7 +102,7 @@ namespace PortabilityLayer if (m_hasFocus && m_selEndChar == m_selStartChar && m_caratTimer < kCaratBlinkRate) { - PortabilityLayer::Vec2i caratPos = ResolveCaratPos(basePoint, sysFont); + PortabilityLayer::Vec2i caratPos = ResolveCaratPos(sysFont) + basePoint; int32_t caratTop = caratPos.m_y; int32_t caratBottom = caratTop + lineGap; @@ -260,6 +263,7 @@ namespace PortabilityLayer { m_window->FocusWidget(this); m_isDraggingSelection = true; + m_selectionScrollTimer = kMouseScrollRate; return HandleDragSelection(evt); } else @@ -321,6 +325,9 @@ namespace PortabilityLayer Redraw(); } } + + if (m_isDraggingSelection) + m_selectionScrollTimer++; } void EditboxWidget::HandleCharacter(uint8_t ch, const uint32_t numRepeatsRequested) @@ -355,6 +362,8 @@ namespace PortabilityLayer // Reset length; m_length = numPreSelChars + numInsertions + numPostSelChars; + AdjustScrollToCarat(); + m_caratTimer = 0; Redraw(); @@ -389,6 +398,9 @@ namespace PortabilityLayer m_length = prefixKeep + suffixKeep; m_selStartChar = m_selEndChar = prefixKeep; + AdjustScrollToCarat(); + AdjustScrollToTextBounds(); + m_caratTimer = 0; Redraw(); @@ -423,6 +435,9 @@ namespace PortabilityLayer m_length = prefixKeep + suffixKeep; m_selStartChar = m_selEndChar = prefixKeep; + AdjustScrollToCarat(); + AdjustScrollToTextBounds(); + m_caratTimer = 0; Redraw(); @@ -444,7 +459,7 @@ namespace PortabilityLayer if (!m_caratScrollLocked) { - m_caratScrollPosition = ResolveCaratPos(Vec2i(0, 0), rfont); + m_caratScrollPosition = ResolveCaratPos(rfont); m_caratScrollLocked = true; } @@ -464,6 +479,8 @@ namespace PortabilityLayer } } + AdjustScrollToCarat(); + m_caratTimer = 0; Redraw(); } @@ -483,7 +500,7 @@ namespace PortabilityLayer if (!m_caratScrollLocked) { - m_caratScrollPosition = ResolveCaratPos(Vec2i(0, 0), rfont); + m_caratScrollPosition = ResolveCaratPos(rfont); m_caratScrollLocked = true; } @@ -503,6 +520,8 @@ namespace PortabilityLayer } } + AdjustScrollToCarat(); + m_caratTimer = 0; Redraw(); } @@ -521,6 +540,8 @@ namespace PortabilityLayer m_caratScrollLocked = false; + AdjustScrollToCarat(); + m_caratTimer = 0; Redraw(); } @@ -539,6 +560,8 @@ namespace PortabilityLayer m_caratScrollLocked = false; + AdjustScrollToCarat(); + m_caratTimer = 0; Redraw(); } @@ -601,6 +624,7 @@ namespace PortabilityLayer return m_length; } + // Handles adjustment of the selection range and anchor when the carat is moved with shift held void EditboxWidget::HandleKeyMoveCarat(size_t newPos, bool shiftHeld) { if (shiftHeld) @@ -673,6 +697,9 @@ namespace PortabilityLayer m_selStartChar = m_dragSelectionStartChar; } + if (m_selectionScrollTimer >= kMouseScrollRate) + AdjustScrollToCarat(); + m_caratTimer = 0; Redraw(); @@ -692,34 +719,6 @@ namespace PortabilityLayer { PortabilityLayer::TextPlacer placer(basePoint, m_isMultiLine ? m_rect.Width() : -1, rfont, GetString()); -#if 0 - if (m_selStartChar == m_selEndChar) - { - PortabilityLayer::GlyphPlacementCharacteristics characteristics; - - for (;;) - { - const bool placedGlyph = placer.PlaceGlyph(characteristics); - - if (!placedGlyph) - break; - - if (characteristics.m_characterIndex == m_selStartChar) - { - caratPos = characteristics.m_glyphStartPos; - break; - } - else if (characteristics.m_characterIndex < m_selStartChar) - caratPos = characteristics.m_glyphEndPos; - else - break; - } - - outCaratPos = Point::Create(caratPos.m_x, caratPos.m_y); - return; - } -#endif - PortabilityLayer::Vec2i globalSelStart; PortabilityLayer::Vec2i globalSelEnd; bool endIsLineBreak = false; @@ -788,19 +787,20 @@ namespace PortabilityLayer } } - Vec2i EditboxWidget::ResolveCaratPos(const Vec2i &basePoint, PortabilityLayer::RenderedFont *rfont) const + // This function returns the actual coordinate of the carat relative to the top-left corner of the text + Vec2i EditboxWidget::ResolveCaratPos(PortabilityLayer::RenderedFont *rfont) const { int32_t lineGap = rfont->GetMetrics().m_linegap; bool failed = false; - PortabilityLayer::Vec2i caratPos = basePoint; + PortabilityLayer::Vec2i caratPos = Vec2i(0, 0); const size_t caratChar = ResolveCaratChar(); if (caratChar > 0) { PortabilityLayer::RenderedFont *rfont = GetRenderedFont(); - PortabilityLayer::TextPlacer placer(basePoint, m_isMultiLine ? m_rect.Width() : -1, rfont, GetString()); + PortabilityLayer::TextPlacer placer(Vec2i(0, 0), m_isMultiLine ? m_rect.Width() : -1, rfont, GetString()); PortabilityLayer::GlyphPlacementCharacteristics characteristics; for (size_t i = 0; i < caratChar; i++) @@ -815,7 +815,7 @@ namespace PortabilityLayer if (!failed) { if (characteristics.m_character == '\r') - caratPos = PortabilityLayer::Vec2i(basePoint.m_x, characteristics.m_glyphStartPos.m_y + lineGap); + caratPos = PortabilityLayer::Vec2i(0, characteristics.m_glyphStartPos.m_y + lineGap); else caratPos = characteristics.m_glyphEndPos; } @@ -826,7 +826,7 @@ namespace PortabilityLayer Vec2i EditboxWidget::ResolveBasePoint() const { - return Vec2i(m_rect.left, m_rect.top); + return Vec2i(m_rect.left, m_rect.top) + m_scrollOffset; } size_t EditboxWidget::ResolveCaratChar() const @@ -837,6 +837,71 @@ namespace PortabilityLayer return m_selStartChar; } + void EditboxWidget::AdjustScrollToCarat() + { + PortabilityLayer::RenderedFont *rfont = GetRenderedFont(); + int32_t lineGap = rfont->GetMetrics().m_linegap; + + Vec2i caratRelativePos = m_scrollOffset + ResolveCaratPos(rfont); + + int32_t w = m_rect.Width(); + int32_t h = m_rect.Height(); + + Rect2i caratRect = Rect2i(caratRelativePos, caratRelativePos + Vec2i(1, lineGap)); + + Vec2i nudge = Vec2i(0, 0); + if (caratRect.Right() > w) + nudge.m_x = w - caratRect.Right(); + if (caratRect.Bottom() > h) + nudge.m_y = h - caratRect.Bottom(); + if (caratRect.Left() < 0) + nudge.m_x = -caratRect.Left(); + if (caratRect.Top() < 0) + nudge.m_y = -caratRect.Top(); + + m_scrollOffset += nudge; + } + + void EditboxWidget::AdjustScrollToTextBounds() + { + if (!m_isMultiLine) + return; + + PortabilityLayer::RenderedFont *rfont = GetRenderedFont(); + int32_t lineGap = rfont->GetMetrics().m_linegap; + + bool failed = false; + + PortabilityLayer::Vec2i caratPos = Vec2i(0, 0); + + PortabilityLayer::TextPlacer placer(Vec2i(0, 0), m_isMultiLine ? m_rect.Width() : -1, rfont, GetString()); + + PortabilityLayer::GlyphPlacementCharacteristics characteristics; + for (size_t i = 0; i < m_length; i++) + { + if (!placer.PlaceGlyph(characteristics)) + { + failed = true; + break; + } + } + + if (!failed) + { + int32_t lowerY = 0; + if (characteristics.m_character == '\r') + lowerY = characteristics.m_glyphStartPos.m_y + lineGap; + else + lowerY = characteristics.m_glyphEndPos.m_y; + + lowerY = lowerY + lineGap + m_scrollOffset.m_y; + + int32_t h = m_rect.Height(); + if (lowerY < h) + m_scrollOffset.m_y -= lowerY - h; + } + } + FontFamily *EditboxWidget::GetFontFamily() const { return PortabilityLayer::FontManager::GetInstance()->GetSystemFont(12, FontFamilyFlag_None); diff --git a/PortabilityLayer/PLEditboxWidget.h b/PortabilityLayer/PLEditboxWidget.h index 932753f..c150955 100644 --- a/PortabilityLayer/PLEditboxWidget.h +++ b/PortabilityLayer/PLEditboxWidget.h @@ -35,6 +35,7 @@ namespace PortabilityLayer private: static const unsigned int kCaratBlinkRate = 20; + static const unsigned int kMouseScrollRate = 20; enum CaratSelectionAnchor { @@ -61,9 +62,11 @@ namespace PortabilityLayer void DrawSelection(DrawSurface *surface, const Vec2i &basePoint, PortabilityLayer::RenderedFont *font) const; - Vec2i ResolveCaratPos(const Vec2i &basePoint, PortabilityLayer::RenderedFont *rfont) const; + Vec2i ResolveCaratPos(PortabilityLayer::RenderedFont *rfont) const; Vec2i ResolveBasePoint() const; size_t ResolveCaratChar() const; + void AdjustScrollToCarat(); + void AdjustScrollToTextBounds(); PortabilityLayer::FontFamily *GetFontFamily() const; PortabilityLayer::RenderedFont *GetRenderedFont() const; @@ -73,10 +76,12 @@ namespace PortabilityLayer size_t m_length; size_t m_selStartChar; size_t m_selEndChar; - CaratSelectionAnchor m_caratSelectionAnchor; + CaratSelectionAnchor m_caratSelectionAnchor; // Where the carat is attached to the selection range - Vec2i m_caratScrollPosition; - bool m_caratScrollLocked; + Vec2i m_caratScrollPosition; // Ideal position of the carat in the editbox, but not necessarily its actual location (i.e. may be in the middle of a glyph) + bool m_caratScrollLocked; // If true, the vertical position + + Vec2i m_scrollOffset; bool m_hasFocus; bool m_isMultiLine; @@ -84,5 +89,6 @@ namespace PortabilityLayer size_t m_dragSelectionStartChar; uint16_t m_caratTimer; + uint16_t m_selectionScrollTimer; }; }