summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Müller <robytemueller@gmail.com>2022-04-21 23:39:06 +0200
committerRobert Müller <robytemueller@gmail.com>2023-06-16 20:39:29 +0200
commitd7a2c4ec06f67f492a85c27e93831b5229bffb9d (patch)
treebdfa49070d074147e06937fd406b4057f21823f2
parent73c84cadc9cca00092017caba9f7d6e59926bc8b (diff)
Implement smooth keyboard and mouse scrolling for UI editboxes
-rw-r--r--src/game/client/lineinput.cpp2
-rw-r--r--src/game/client/lineinput.h3
-rw-r--r--src/game/client/ui.cpp87
-rw-r--r--src/game/client/ui.h1
4 files changed, 58 insertions, 35 deletions
diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp
index 49614fd66..6b2ec0ab4 100644
--- a/src/game/client/lineinput.cpp
+++ b/src/game/client/lineinput.cpp
@@ -32,7 +32,7 @@ void CLineInput::SetBuffer(char *pStr, int MaxSize, int MaxChars)
m_WasChanged = m_pStr && pLastStr && m_WasChanged;
if(!pLastStr)
{
- m_ScrollOffset = 0;
+ m_ScrollOffset = m_ScrollOffsetChange = 0.0f;
m_CaretPosition = vec2(0, 0);
m_Hidden = false;
}
diff --git a/src/game/client/lineinput.h b/src/game/client/lineinput.h
index f7f75abbf..e4895081b 100644
--- a/src/game/client/lineinput.h
+++ b/src/game/client/lineinput.h
@@ -44,6 +44,7 @@ class CLineInput
int m_SelectionEnd;
float m_ScrollOffset;
+ float m_ScrollOffsetChange;
vec2 m_CaretPosition;
bool m_Hidden;
@@ -102,6 +103,8 @@ public:
// used either for vertical or horizontal scrolling
float GetScrollOffset() const { return m_ScrollOffset; }
void SetScrollOffset(float ScrollOffset) { m_ScrollOffset = ScrollOffset; }
+ float GetScrollOffsetChange() const { return m_ScrollOffsetChange; }
+ void SetScrollOffsetChange(float ScrollOffsetChange) { m_ScrollOffsetChange = ScrollOffsetChange; }
vec2 GetCaretPosition() const { return m_CaretPosition; } // only updated while the input is active
diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp
index b71bec531..d62a24c27 100644
--- a/src/game/client/ui.cpp
+++ b/src/game/client/ui.cpp
@@ -305,6 +305,35 @@ bool CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float
return true;
}
+void CUI::DoSmoothScrollLogic(float *pScrollOffset, float *pScrollOffsetChange, float ViewPortSize, float TotalSize, float ScrollSpeed)
+{
+ // instant scrolling if distance too long
+ if(absolute(*pScrollOffsetChange) > ViewPortSize)
+ {
+ *pScrollOffset += *pScrollOffsetChange;
+ *pScrollOffsetChange = 0.0f;
+ }
+ // smooth scrolling
+ if(*pScrollOffsetChange)
+ {
+ const float Delta = *pScrollOffsetChange * clamp(Client()->RenderFrameTime() * ScrollSpeed, 0.0f, 1.0f);
+ *pScrollOffset += Delta;
+ *pScrollOffsetChange -= Delta;
+ }
+ // clamp to first item
+ if(*pScrollOffset < 0.0f)
+ {
+ *pScrollOffset = 0.0f;
+ *pScrollOffsetChange = 0.0f;
+ }
+ // clamp to last item
+ if(TotalSize > ViewPortSize && *pScrollOffset > TotalSize - ViewPortSize)
+ {
+ *pScrollOffset = TotalSize - ViewPortSize;
+ *pScrollOffsetChange = 0.0f;
+ }
+}
+
void CUI::ApplyCursorAlign(class CTextCursor *pCursor, const CUIRect *pRect, int Align)
{
pCursor->m_Align = Align;
@@ -389,6 +418,7 @@ bool CUI::DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize
bool UpdateOffset = false;
float ScrollOffset = pLineInput->GetScrollOffset();
+ float ScrollOffsetChange = pLineInput->GetScrollOffsetChange();
static bool s_DoScroll = false;
static int s_SelectionStartOffset = -1;
@@ -397,16 +427,14 @@ bool CUI::DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize
CUIRect Textbox;
pRect->VMargin(VSpacing, &Textbox);
+ float ScrollSpeed = 10.0f;
if(Active)
{
- static float s_ScrollStartX = 0.0f;
-
int CursorOffset = pLineInput->GetCursorOffset();
if(Inside && MouseButton(0) && !Changed)
{
s_DoScroll = true;
- s_ScrollStartX = MouseX();
const float MxRel = MouseX() - Textbox.x;
float TotalTextWidth = 0.0f;
for(int i = 1, Offset = 0; i <= pLineInput->GetNumChars(); i++)
@@ -414,7 +442,7 @@ bool CUI::DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize
const int PrevOffset = Offset;
Offset = str_utf8_forward(pDisplayStr, Offset);
const float AddedTextWidth = TextRender()->TextWidth(FontSize, pDisplayStr + PrevOffset, Offset - PrevOffset);
- if(TotalTextWidth + AddedTextWidth/2.0f - ScrollOffset > MxRel)
+ if(TotalTextWidth + AddedTextWidth/2.0f - ScrollOffset - ScrollOffsetChange > MxRel)
{
CursorOffset = PrevOffset;
if(s_SelectionStartOffset < 0)
@@ -438,18 +466,20 @@ bool CUI::DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize
}
else if(s_DoScroll)
{
- // do scrolling
- if(MouseX() < Textbox.x && s_ScrollStartX-MouseX() > 10.0f)
- {
- CursorOffset = str_utf8_rewind(pDisplayStr, CursorOffset);
- s_ScrollStartX = MouseX();
- UpdateOffset = true;
- }
- else if(MouseX() > Textbox.x+Textbox.w && MouseX()-s_ScrollStartX > 10.0f)
+ if(absolute(ScrollOffsetChange) < 10.0f)
{
- CursorOffset = str_utf8_forward(pDisplayStr, CursorOffset);
- s_ScrollStartX = MouseX();
- UpdateOffset = true;
+ if(MouseX() < Textbox.x)
+ {
+ CursorOffset = str_utf8_rewind(pDisplayStr, CursorOffset);
+ ScrollSpeed *= clamp(Textbox.x - MouseX(), 1.0f, Textbox.w / 8.0f);
+ UpdateOffset = true;
+ }
+ else if(MouseX() > Textbox.x + Textbox.w)
+ {
+ CursorOffset = str_utf8_forward(pDisplayStr, CursorOffset);
+ ScrollSpeed *= clamp(MouseX() - Textbox.x - Textbox.w, 1.0f, Textbox.w / 8.0f);
+ UpdateOffset = true;
+ }
}
}
else if(!Inside && MouseButton(0))
@@ -519,28 +549,17 @@ bool CUI::DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize
// check if the text has to be moved
if(Active && !JustGotActive && (UpdateOffset || Changed))
{
- const float CaretX = pLineInput->GetCaretPosition().x - Textbox.x;
- if(CaretX - ScrollOffset > Textbox.w)
- {
- // move to the left
- do
- {
- ScrollOffset += clamp(pLineInput->GetCursor()->Width() - ScrollOffset - Textbox.w, 0.1f, Textbox.w / 3.0f);
- }
- while(CaretX - ScrollOffset > Textbox.w);
- }
- else if(CaretX - ScrollOffset < 0.0f)
- {
- // move to the right
- do
- {
- ScrollOffset = maximum(0.0f, ScrollOffset - Textbox.w / 3.0f);
- }
- while(CaretX - ScrollOffset < 0.0f);
- }
+ const float CaretX = pLineInput->GetCaretPosition().x - Textbox.x - ScrollOffset - ScrollOffsetChange;
+ if(CaretX > Textbox.w)
+ ScrollOffsetChange += CaretX - Textbox.w;
+ else if(CaretX < 0.0f)
+ ScrollOffsetChange += CaretX;
}
+ DoSmoothScrollLogic(&ScrollOffset, &ScrollOffsetChange, Textbox.w, pCursor->Width(), ScrollSpeed);
+
pLineInput->SetScrollOffset(ScrollOffset);
+ pLineInput->SetScrollOffsetChange(ScrollOffsetChange);
return Changed;
}
diff --git a/src/game/client/ui.h b/src/game/client/ui.h
index f541013b1..943dec673 100644
--- a/src/game/client/ui.h
+++ b/src/game/client/ui.h
@@ -259,6 +259,7 @@ public:
bool DoButtonLogic(const void *pID, const CUIRect *pRect, int Button = 0);
bool DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY);
+ void DoSmoothScrollLogic(float *pScrollOffset, float *pScrollOffsetChange, float ViewPortSize, float TotalSize, float ScrollSpeed = 10.0f);
// labels
void DoLabel(const CUIRect *pRect, const char *pText, float FontSize, int Align = TEXTALIGN_TL, float LineWidth = -1.0f, bool MultiLine = true);