diff options
author | Robert Müller <robytemueller@gmail.com> | 2022-04-21 23:39:06 +0200 |
---|---|---|
committer | Robert Müller <robytemueller@gmail.com> | 2023-06-16 20:39:29 +0200 |
commit | d7a2c4ec06f67f492a85c27e93831b5229bffb9d (patch) | |
tree | bdfa49070d074147e06937fd406b4057f21823f2 | |
parent | 73c84cadc9cca00092017caba9f7d6e59926bc8b (diff) |
Implement smooth keyboard and mouse scrolling for UI editboxes
-rw-r--r-- | src/game/client/lineinput.cpp | 2 | ||||
-rw-r--r-- | src/game/client/lineinput.h | 3 | ||||
-rw-r--r-- | src/game/client/ui.cpp | 87 | ||||
-rw-r--r-- | src/game/client/ui.h | 1 |
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); |