mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-20 09:34:19 +00:00
Merge #5743
5743: Smooth console completion scrolling r=def- a=Robyt3 More smoothly scroll selected console completion option into view. See videos at https://github.com/teeworlds/teeworlds/pull/3054. The smooth scrolling logic is extracted in `CUI::DoSmoothScrollLogic` so it can eventually be reused for smooth input scrolling (see https://github.com/teeworlds/teeworlds/pull/3150). ## Checklist - [X] Tested the change ingame - [X] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [ ] Considered possible null pointers and out of bounds array indexing - [ ] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
commit
12273bd0d6
|
@ -147,6 +147,7 @@ void CGameConsole::CInstance::ClearHistory()
|
||||||
void CGameConsole::CInstance::Reset()
|
void CGameConsole::CInstance::Reset()
|
||||||
{
|
{
|
||||||
m_CompletionRenderOffset = 0.0f;
|
m_CompletionRenderOffset = 0.0f;
|
||||||
|
m_CompletionRenderOffsetChange = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameConsole::CInstance::ExecuteLine(const char *pLine)
|
void CGameConsole::CInstance::ExecuteLine(const char *pLine)
|
||||||
|
@ -533,25 +534,29 @@ struct CCompletionOptionRenderInfo
|
||||||
CTextCursor m_Cursor;
|
CTextCursor m_Cursor;
|
||||||
const char *m_pCurrentCmd;
|
const char *m_pCurrentCmd;
|
||||||
int m_WantedCompletion;
|
int m_WantedCompletion;
|
||||||
int m_EnumCount;
|
|
||||||
float m_Offset;
|
float m_Offset;
|
||||||
|
float *m_pOffsetChange;
|
||||||
float m_Width;
|
float m_Width;
|
||||||
|
float m_TotalWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
void CGameConsole::PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser)
|
void CGameConsole::PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser)
|
||||||
{
|
{
|
||||||
CCompletionOptionRenderInfo *pInfo = static_cast<CCompletionOptionRenderInfo *>(pUser);
|
CCompletionOptionRenderInfo *pInfo = static_cast<CCompletionOptionRenderInfo *>(pUser);
|
||||||
|
|
||||||
if(pInfo->m_EnumCount == pInfo->m_WantedCompletion)
|
if(Index == pInfo->m_WantedCompletion)
|
||||||
{
|
{
|
||||||
float tw = pInfo->m_pSelf->TextRender()->TextWidth(pInfo->m_Cursor.m_pFont, pInfo->m_Cursor.m_FontSize, pStr, -1, -1.0f);
|
float TextWidth = pInfo->m_pSelf->TextRender()->TextWidth(pInfo->m_Cursor.m_pFont, pInfo->m_Cursor.m_FontSize, pStr, -1, -1.0f);
|
||||||
pInfo->m_pSelf->Graphics()->DrawRect(pInfo->m_Cursor.m_X - 2.5f, pInfo->m_Cursor.m_Y - 4.f / 2.f, tw + 5.f, pInfo->m_Cursor.m_FontSize + 4.f, ColorRGBA(229.0f / 255.0f, 185.0f / 255.0f, 4.0f / 255.0f, 0.85f), IGraphics::CORNER_ALL, pInfo->m_Cursor.m_FontSize / 3.f);
|
const CUIRect Rect = {pInfo->m_Cursor.m_X - 2.5f, pInfo->m_Cursor.m_Y - 4.f / 2.f, TextWidth + 5.f, pInfo->m_Cursor.m_FontSize + 4.f};
|
||||||
|
Rect.Draw(ColorRGBA(229.0f / 255.0f, 185.0f / 255.0f, 4.0f / 255.0f, 0.85f), IGraphics::CORNER_ALL, pInfo->m_Cursor.m_FontSize / 3.f);
|
||||||
|
|
||||||
// scroll when out of sight
|
// scroll when out of sight
|
||||||
if(pInfo->m_Cursor.m_X < 3.0f)
|
const bool MoveLeft = Rect.x - *pInfo->m_pOffsetChange < 0.0f;
|
||||||
pInfo->m_Offset = 0.0f;
|
const bool MoveRight = Rect.x + Rect.w - *pInfo->m_pOffsetChange > pInfo->m_Width;
|
||||||
else if(pInfo->m_Cursor.m_X + tw > pInfo->m_Width)
|
if(MoveLeft && !MoveRight)
|
||||||
pInfo->m_Offset -= pInfo->m_Width / 2;
|
*pInfo->m_pOffsetChange -= -Rect.x + pInfo->m_Width / 4.0f;
|
||||||
|
else if(!MoveLeft && MoveRight)
|
||||||
|
*pInfo->m_pOffsetChange += Rect.x + Rect.w - pInfo->m_Width + pInfo->m_Width / 4.0f;
|
||||||
|
|
||||||
pInfo->m_pSelf->TextRender()->TextColor(0.05f, 0.05f, 0.05f, 1);
|
pInfo->m_pSelf->TextRender()->TextColor(0.05f, 0.05f, 0.05f, 1);
|
||||||
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, -1);
|
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, -1);
|
||||||
|
@ -576,8 +581,8 @@ void CGameConsole::PossibleCommandsRenderCallback(int Index, const char *pStr, v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pInfo->m_EnumCount++;
|
|
||||||
pInfo->m_Cursor.m_X += 7.0f;
|
pInfo->m_Cursor.m_X += 7.0f;
|
||||||
|
pInfo->m_TotalWidth = pInfo->m_Cursor.m_X + pInfo->m_Offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameConsole::OnRender()
|
void CGameConsole::OnRender()
|
||||||
|
@ -761,28 +766,30 @@ void CGameConsole::OnRender()
|
||||||
CCompletionOptionRenderInfo Info;
|
CCompletionOptionRenderInfo Info;
|
||||||
Info.m_pSelf = this;
|
Info.m_pSelf = this;
|
||||||
Info.m_WantedCompletion = pConsole->m_CompletionChosen;
|
Info.m_WantedCompletion = pConsole->m_CompletionChosen;
|
||||||
Info.m_EnumCount = 0;
|
|
||||||
Info.m_Offset = pConsole->m_CompletionRenderOffset;
|
Info.m_Offset = pConsole->m_CompletionRenderOffset;
|
||||||
|
Info.m_pOffsetChange = &pConsole->m_CompletionRenderOffsetChange;
|
||||||
Info.m_Width = Screen.w;
|
Info.m_Width = Screen.w;
|
||||||
|
Info.m_TotalWidth = 0.0f;
|
||||||
Info.m_pCurrentCmd = pConsole->m_aCompletionBuffer;
|
Info.m_pCurrentCmd = pConsole->m_aCompletionBuffer;
|
||||||
TextRender()->SetCursor(&Info.m_Cursor, InitialX + Info.m_Offset, InitialY + RowHeight + 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
|
TextRender()->SetCursor(&Info.m_Cursor, InitialX - Info.m_Offset, InitialY + RowHeight + 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
|
||||||
Info.m_Cursor.m_LineWidth = std::numeric_limits<float>::max();
|
Info.m_Cursor.m_LineWidth = std::numeric_limits<float>::max();
|
||||||
m_pConsole->PossibleCommands(Info.m_pCurrentCmd, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL && Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info);
|
const int NumCommands = m_pConsole->PossibleCommands(Info.m_pCurrentCmd, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL && Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info);
|
||||||
pConsole->m_CompletionRenderOffset = Info.m_Offset;
|
pConsole->m_CompletionRenderOffset = Info.m_Offset;
|
||||||
|
|
||||||
if(Info.m_EnumCount <= 0 && pConsole->m_IsCommand)
|
if(NumCommands <= 0 && pConsole->m_IsCommand)
|
||||||
{
|
{
|
||||||
const bool TuningCompletion = IsTuningCommandPrefix(Info.m_pCurrentCmd);
|
const bool TuningCompletion = IsTuningCommandPrefix(Info.m_pCurrentCmd);
|
||||||
|
int NumArguments = 0;
|
||||||
if(TuningCompletion)
|
if(TuningCompletion)
|
||||||
{
|
{
|
||||||
Info.m_WantedCompletion = pConsole->m_CompletionChosenArgument;
|
Info.m_WantedCompletion = pConsole->m_CompletionChosenArgument;
|
||||||
Info.m_EnumCount = 0;
|
Info.m_TotalWidth = 0.0f;
|
||||||
Info.m_pCurrentCmd = pConsole->m_aCompletionBufferArgument;
|
Info.m_pCurrentCmd = pConsole->m_aCompletionBufferArgument;
|
||||||
m_pClient->m_aTuning[g_Config.m_ClDummy].PossibleTunings(Info.m_pCurrentCmd, PossibleCommandsRenderCallback, &Info);
|
NumArguments = m_pClient->m_aTuning[g_Config.m_ClDummy].PossibleTunings(Info.m_pCurrentCmd, PossibleCommandsRenderCallback, &Info);
|
||||||
pConsole->m_CompletionRenderOffset = Info.m_Offset;
|
pConsole->m_CompletionRenderOffset = Info.m_Offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Info.m_EnumCount <= 0 && pConsole->m_IsCommand)
|
if(NumArguments <= 0 && pConsole->m_IsCommand)
|
||||||
{
|
{
|
||||||
char aBuf[512];
|
char aBuf[512];
|
||||||
str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp);
|
str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp);
|
||||||
|
@ -792,6 +799,8 @@ void CGameConsole::OnRender()
|
||||||
TextRender()->TextEx(&Info.m_Cursor, aBuf, -1);
|
TextRender()->TextEx(&Info.m_Cursor, aBuf, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UI()->DoSmoothScrollLogic(&pConsole->m_CompletionRenderOffset, &pConsole->m_CompletionRenderOffsetChange, Info.m_Width, Info.m_TotalWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
pConsole->m_BacklogLock.lock();
|
pConsole->m_BacklogLock.lock();
|
||||||
|
|
|
@ -50,6 +50,7 @@ class CGameConsole : public CComponent
|
||||||
int m_CompletionChosenArgument;
|
int m_CompletionChosenArgument;
|
||||||
int m_CompletionFlagmask;
|
int m_CompletionFlagmask;
|
||||||
float m_CompletionRenderOffset;
|
float m_CompletionRenderOffset;
|
||||||
|
float m_CompletionRenderOffsetChange;
|
||||||
|
|
||||||
char m_aUser[32];
|
char m_aUser[32];
|
||||||
bool m_UserGot;
|
bool m_UserGot;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <base/math.h>
|
#include <base/math.h>
|
||||||
#include <base/system.h>
|
#include <base/system.h>
|
||||||
|
|
||||||
|
#include <engine/client.h>
|
||||||
#include <engine/graphics.h>
|
#include <engine/graphics.h>
|
||||||
#include <engine/input.h>
|
#include <engine/input.h>
|
||||||
#include <engine/keys.h>
|
#include <engine/keys.h>
|
||||||
|
@ -83,6 +84,7 @@ float CUI::ms_FontmodHeight = 0.8f;
|
||||||
|
|
||||||
void CUI::Init(IKernel *pKernel)
|
void CUI::Init(IKernel *pKernel)
|
||||||
{
|
{
|
||||||
|
m_pClient = pKernel->RequestInterface<IClient>();
|
||||||
m_pGraphics = pKernel->RequestInterface<IGraphics>();
|
m_pGraphics = pKernel->RequestInterface<IGraphics>();
|
||||||
m_pInput = pKernel->RequestInterface<IInput>();
|
m_pInput = pKernel->RequestInterface<IInput>();
|
||||||
m_pTextRender = pKernel->RequestInterface<ITextRender>();
|
m_pTextRender = pKernel->RequestInterface<ITextRender>();
|
||||||
|
@ -372,6 +374,35 @@ int CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float CUI::DoTextLabel(float x, float y, float w, float h, const char *pText, float Size, int Align, const SLabelProperties &LabelProps)
|
float CUI::DoTextLabel(float x, float y, float w, float h, const char *pText, float Size, int Align, const SLabelProperties &LabelProps)
|
||||||
{
|
{
|
||||||
float AlignedSize = 0;
|
float AlignedSize = 0;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class IClient;
|
||||||
class IGraphics;
|
class IGraphics;
|
||||||
class IKernel;
|
class IKernel;
|
||||||
|
|
||||||
|
@ -203,6 +204,7 @@ class CUI
|
||||||
std::vector<CUIRect> m_vClips;
|
std::vector<CUIRect> m_vClips;
|
||||||
void UpdateClipping();
|
void UpdateClipping();
|
||||||
|
|
||||||
|
IClient *m_pClient;
|
||||||
IGraphics *m_pGraphics;
|
IGraphics *m_pGraphics;
|
||||||
IInput *m_pInput;
|
IInput *m_pInput;
|
||||||
ITextRender *m_pTextRender;
|
ITextRender *m_pTextRender;
|
||||||
|
@ -218,6 +220,7 @@ public:
|
||||||
|
|
||||||
void Init(IKernel *pKernel);
|
void Init(IKernel *pKernel);
|
||||||
void InitInputs(IInput::CEvent *pInputEventsArray, int *pInputEventCount);
|
void InitInputs(IInput::CEvent *pInputEventsArray, int *pInputEventCount);
|
||||||
|
IClient *Client() const { return m_pClient; }
|
||||||
IGraphics *Graphics() const { return m_pGraphics; }
|
IGraphics *Graphics() const { return m_pGraphics; }
|
||||||
IInput *Input() const { return m_pInput; }
|
IInput *Input() const { return m_pInput; }
|
||||||
ITextRender *TextRender() const { return m_pTextRender; }
|
ITextRender *TextRender() const { return m_pTextRender; }
|
||||||
|
@ -306,6 +309,7 @@ public:
|
||||||
|
|
||||||
int DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect);
|
int DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect);
|
||||||
int DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY);
|
int 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);
|
||||||
|
|
||||||
float DoTextLabel(float x, float y, float w, float h, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {});
|
float DoTextLabel(float x, float y, float w, float h, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {});
|
||||||
void DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {});
|
void DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {});
|
||||||
|
|
Loading…
Reference in a new issue