mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 23:00:42 +00:00
Add edit box automatic scrolling
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
#include "PLTimeTaggedVOSEvent.h"
|
#include "PLTimeTaggedVOSEvent.h"
|
||||||
#include "ResolveCachingColor.h"
|
#include "ResolveCachingColor.h"
|
||||||
#include "TextPlacer.h"
|
#include "TextPlacer.h"
|
||||||
|
#include "Rect2i.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@@ -30,9 +31,11 @@ namespace PortabilityLayer
|
|||||||
, m_caratScrollLocked(false)
|
, m_caratScrollLocked(false)
|
||||||
, m_hasFocus(false)
|
, m_hasFocus(false)
|
||||||
, m_caratTimer(0)
|
, m_caratTimer(0)
|
||||||
|
, m_selectionScrollTimer(0)
|
||||||
, m_isMultiLine(false)
|
, m_isMultiLine(false)
|
||||||
, m_isDraggingSelection(false)
|
, m_isDraggingSelection(false)
|
||||||
, m_dragSelectionStartChar(false)
|
, m_dragSelectionStartChar(false)
|
||||||
|
, m_scrollOffset(0, 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +102,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
if (m_hasFocus && m_selEndChar == m_selStartChar && m_caratTimer < kCaratBlinkRate)
|
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 caratTop = caratPos.m_y;
|
||||||
int32_t caratBottom = caratTop + lineGap;
|
int32_t caratBottom = caratTop + lineGap;
|
||||||
@@ -260,6 +263,7 @@ namespace PortabilityLayer
|
|||||||
{
|
{
|
||||||
m_window->FocusWidget(this);
|
m_window->FocusWidget(this);
|
||||||
m_isDraggingSelection = true;
|
m_isDraggingSelection = true;
|
||||||
|
m_selectionScrollTimer = kMouseScrollRate;
|
||||||
return HandleDragSelection(evt);
|
return HandleDragSelection(evt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -321,6 +325,9 @@ namespace PortabilityLayer
|
|||||||
Redraw();
|
Redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_isDraggingSelection)
|
||||||
|
m_selectionScrollTimer++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditboxWidget::HandleCharacter(uint8_t ch, const uint32_t numRepeatsRequested)
|
void EditboxWidget::HandleCharacter(uint8_t ch, const uint32_t numRepeatsRequested)
|
||||||
@@ -355,6 +362,8 @@ namespace PortabilityLayer
|
|||||||
// Reset length;
|
// Reset length;
|
||||||
m_length = numPreSelChars + numInsertions + numPostSelChars;
|
m_length = numPreSelChars + numInsertions + numPostSelChars;
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
|
|
||||||
@@ -389,6 +398,9 @@ namespace PortabilityLayer
|
|||||||
m_length = prefixKeep + suffixKeep;
|
m_length = prefixKeep + suffixKeep;
|
||||||
m_selStartChar = m_selEndChar = prefixKeep;
|
m_selStartChar = m_selEndChar = prefixKeep;
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
AdjustScrollToTextBounds();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
|
|
||||||
@@ -423,6 +435,9 @@ namespace PortabilityLayer
|
|||||||
m_length = prefixKeep + suffixKeep;
|
m_length = prefixKeep + suffixKeep;
|
||||||
m_selStartChar = m_selEndChar = prefixKeep;
|
m_selStartChar = m_selEndChar = prefixKeep;
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
AdjustScrollToTextBounds();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
|
|
||||||
@@ -444,7 +459,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
if (!m_caratScrollLocked)
|
if (!m_caratScrollLocked)
|
||||||
{
|
{
|
||||||
m_caratScrollPosition = ResolveCaratPos(Vec2i(0, 0), rfont);
|
m_caratScrollPosition = ResolveCaratPos(rfont);
|
||||||
m_caratScrollLocked = true;
|
m_caratScrollLocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,6 +479,8 @@ namespace PortabilityLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
}
|
}
|
||||||
@@ -483,7 +500,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
if (!m_caratScrollLocked)
|
if (!m_caratScrollLocked)
|
||||||
{
|
{
|
||||||
m_caratScrollPosition = ResolveCaratPos(Vec2i(0, 0), rfont);
|
m_caratScrollPosition = ResolveCaratPos(rfont);
|
||||||
m_caratScrollLocked = true;
|
m_caratScrollLocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,6 +520,8 @@ namespace PortabilityLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
}
|
}
|
||||||
@@ -521,6 +540,8 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
m_caratScrollLocked = false;
|
m_caratScrollLocked = false;
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
}
|
}
|
||||||
@@ -539,6 +560,8 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
m_caratScrollLocked = false;
|
m_caratScrollLocked = false;
|
||||||
|
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
}
|
}
|
||||||
@@ -601,6 +624,7 @@ namespace PortabilityLayer
|
|||||||
return m_length;
|
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)
|
void EditboxWidget::HandleKeyMoveCarat(size_t newPos, bool shiftHeld)
|
||||||
{
|
{
|
||||||
if (shiftHeld)
|
if (shiftHeld)
|
||||||
@@ -673,6 +697,9 @@ namespace PortabilityLayer
|
|||||||
m_selStartChar = m_dragSelectionStartChar;
|
m_selStartChar = m_dragSelectionStartChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_selectionScrollTimer >= kMouseScrollRate)
|
||||||
|
AdjustScrollToCarat();
|
||||||
|
|
||||||
m_caratTimer = 0;
|
m_caratTimer = 0;
|
||||||
Redraw();
|
Redraw();
|
||||||
|
|
||||||
@@ -692,34 +719,6 @@ namespace PortabilityLayer
|
|||||||
{
|
{
|
||||||
PortabilityLayer::TextPlacer placer(basePoint, m_isMultiLine ? m_rect.Width() : -1, rfont, GetString());
|
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 globalSelStart;
|
||||||
PortabilityLayer::Vec2i globalSelEnd;
|
PortabilityLayer::Vec2i globalSelEnd;
|
||||||
bool endIsLineBreak = false;
|
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;
|
int32_t lineGap = rfont->GetMetrics().m_linegap;
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
|
||||||
PortabilityLayer::Vec2i caratPos = basePoint;
|
PortabilityLayer::Vec2i caratPos = Vec2i(0, 0);
|
||||||
|
|
||||||
const size_t caratChar = ResolveCaratChar();
|
const size_t caratChar = ResolveCaratChar();
|
||||||
|
|
||||||
if (caratChar > 0)
|
if (caratChar > 0)
|
||||||
{
|
{
|
||||||
PortabilityLayer::RenderedFont *rfont = GetRenderedFont();
|
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;
|
PortabilityLayer::GlyphPlacementCharacteristics characteristics;
|
||||||
for (size_t i = 0; i < caratChar; i++)
|
for (size_t i = 0; i < caratChar; i++)
|
||||||
@@ -815,7 +815,7 @@ namespace PortabilityLayer
|
|||||||
if (!failed)
|
if (!failed)
|
||||||
{
|
{
|
||||||
if (characteristics.m_character == '\r')
|
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
|
else
|
||||||
caratPos = characteristics.m_glyphEndPos;
|
caratPos = characteristics.m_glyphEndPos;
|
||||||
}
|
}
|
||||||
@@ -826,7 +826,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
Vec2i EditboxWidget::ResolveBasePoint() const
|
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
|
size_t EditboxWidget::ResolveCaratChar() const
|
||||||
@@ -837,6 +837,71 @@ namespace PortabilityLayer
|
|||||||
return m_selStartChar;
|
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
|
FontFamily *EditboxWidget::GetFontFamily() const
|
||||||
{
|
{
|
||||||
return PortabilityLayer::FontManager::GetInstance()->GetSystemFont(12, FontFamilyFlag_None);
|
return PortabilityLayer::FontManager::GetInstance()->GetSystemFont(12, FontFamilyFlag_None);
|
||||||
|
@@ -35,6 +35,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static const unsigned int kCaratBlinkRate = 20;
|
static const unsigned int kCaratBlinkRate = 20;
|
||||||
|
static const unsigned int kMouseScrollRate = 20;
|
||||||
|
|
||||||
enum CaratSelectionAnchor
|
enum CaratSelectionAnchor
|
||||||
{
|
{
|
||||||
@@ -61,9 +62,11 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
void DrawSelection(DrawSurface *surface, const Vec2i &basePoint, PortabilityLayer::RenderedFont *font) const;
|
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;
|
Vec2i ResolveBasePoint() const;
|
||||||
size_t ResolveCaratChar() const;
|
size_t ResolveCaratChar() const;
|
||||||
|
void AdjustScrollToCarat();
|
||||||
|
void AdjustScrollToTextBounds();
|
||||||
|
|
||||||
PortabilityLayer::FontFamily *GetFontFamily() const;
|
PortabilityLayer::FontFamily *GetFontFamily() const;
|
||||||
PortabilityLayer::RenderedFont *GetRenderedFont() const;
|
PortabilityLayer::RenderedFont *GetRenderedFont() const;
|
||||||
@@ -73,10 +76,12 @@ namespace PortabilityLayer
|
|||||||
size_t m_length;
|
size_t m_length;
|
||||||
size_t m_selStartChar;
|
size_t m_selStartChar;
|
||||||
size_t m_selEndChar;
|
size_t m_selEndChar;
|
||||||
CaratSelectionAnchor m_caratSelectionAnchor;
|
CaratSelectionAnchor m_caratSelectionAnchor; // Where the carat is attached to the selection range
|
||||||
|
|
||||||
Vec2i m_caratScrollPosition;
|
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;
|
bool m_caratScrollLocked; // If true, the vertical position
|
||||||
|
|
||||||
|
Vec2i m_scrollOffset;
|
||||||
|
|
||||||
bool m_hasFocus;
|
bool m_hasFocus;
|
||||||
bool m_isMultiLine;
|
bool m_isMultiLine;
|
||||||
@@ -84,5 +89,6 @@ namespace PortabilityLayer
|
|||||||
size_t m_dragSelectionStartChar;
|
size_t m_dragSelectionStartChar;
|
||||||
|
|
||||||
uint16_t m_caratTimer;
|
uint16_t m_caratTimer;
|
||||||
|
uint16_t m_selectionScrollTimer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user