Skip to content

Commit

Permalink
NeoUI - Add scrollbar to scrollable sections
Browse files Browse the repository at this point in the history
* Current scrollable sections that's immediately noticable:
    * Settings -> Keys
    * Create server/game -> Map list selector
* There might be others like player lists, but they all have the same
  behavior/style anyway
* Handles mouse drag/click and paint just right-side of defined panel
* fixes NeotokyoRebuild#666
  • Loading branch information
nullsystem committed Oct 2, 2024
1 parent b0d47d9 commit 12a5f98
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 3 deletions.
74 changes: 71 additions & 3 deletions mp/src/game/client/neo/ui/neo_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ static const wchar_t *ENABLED_LABELS[] = {
#define IN_BETWEEN_AR(min, cmp, max) (((min) <= (cmp)) && ((cmp) < (max)))
#define IN_BETWEEN_EQ(min, cmp, max) (((min) <= (cmp)) && ((cmp) <= (max)))

[[nodiscard]] static bool InRect(const vgui::IntRect &rect, const int x, const int y)
{
return IN_BETWEEN_EQ(rect.x0, x, rect.x1) && IN_BETWEEN_EQ(rect.y0, y, rect.y1);
}

[[nodiscard]] static int LoopAroundMinMax(const int iValue, const int iMin, const int iMax)
{
if (iValue < iMin)
Expand Down Expand Up @@ -279,9 +284,20 @@ void EndSection()

// Scroll handling
const int iRowsInScreen = g_pCtx->dPanel.tall / g_pCtx->iRowTall;
if (g_pCtx->eMode == MODE_MOUSEWHEELED && g_pCtx->bMouseInPanel)
const bool bHasScroll = (g_pCtx->iPartitionY > iRowsInScreen);
vgui::IntRect rectScrollArea = (bHasScroll) ?
vgui::IntRect{
.x0 = g_pCtx->dPanel.x + g_pCtx->dPanel.wide,
.y0 = g_pCtx->dPanel.y,
.x1 = g_pCtx->dPanel.x + g_pCtx->dPanel.wide + g_pCtx->iRowTall,
.y1 = g_pCtx->dPanel.y + g_pCtx->dPanel.tall,
} : vgui::IntRect{};
const bool bMouseInScrollbar = bHasScroll && InRect(rectScrollArea, g_pCtx->iMouseAbsX, g_pCtx->iMouseAbsY);
const bool bMouseInWheelable = g_pCtx->bMouseInPanel || bMouseInScrollbar;

if (g_pCtx->eMode == MODE_MOUSEWHEELED && bMouseInWheelable)
{
if (g_pCtx->iPartitionY <= iRowsInScreen)
if (!bHasScroll)
{
g_pCtx->iYOffset[g_pCtx->iSection] = 0;
}
Expand All @@ -294,7 +310,7 @@ void EndSection()
else if (g_pCtx->eMode == MODE_KEYPRESSED && (g_pCtx->eCode == KEY_DOWN || g_pCtx->eCode == KEY_UP) &&
(g_pCtx->iActiveSection == g_pCtx->iSection || g_pCtx->iHotSection == g_pCtx->iSection))
{
if (g_pCtx->iPartitionY <= iRowsInScreen)
if (!bHasScroll)
{
// Disable scroll if it doesn't need to
g_pCtx->iYOffset[g_pCtx->iSection] = 0;
Expand All @@ -311,6 +327,58 @@ void EndSection()
}
}

// Scroll bar area painting and mouse interaction (no keyboard as that's handled by active widgets)
if (bHasScroll)
{
const int iYStart = g_pCtx->iYOffset[g_pCtx->iSection];
const int iYEnd = iYStart + iRowsInScreen;
const float flYPercStart = iYStart / (float)(g_pCtx->iWidget);
const float flYPercEnd = iYEnd / (float)(g_pCtx->iWidget);
vgui::IntRect rectHandle{
g_pCtx->dPanel.x + g_pCtx->dPanel.wide,
g_pCtx->dPanel.y + (int)(g_pCtx->dPanel.tall * flYPercStart),
g_pCtx->dPanel.x + g_pCtx->dPanel.wide + g_pCtx->iRowTall,
g_pCtx->dPanel.y + (int)(g_pCtx->dPanel.tall * flYPercEnd)
};

bool bAlterOffset = false;
switch (g_pCtx->eMode)
{
case MODE_PAINT:
surface()->DrawSetColor(g_pCtx->bgColor);
surface()->DrawFilledRectArray(&rectScrollArea, 1);
surface()->DrawSetColor(g_pCtx->abYMouseDragOffset[g_pCtx->iSection] ? g_pCtx->selectBgColor : g_pCtx->normalBgColor);
surface()->DrawFilledRectArray(&rectHandle, 1);
break;
case MODE_MOUSEPRESSED:
{
g_pCtx->abYMouseDragOffset[g_pCtx->iSection] = bMouseInScrollbar;
if (bMouseInScrollbar)
{
// If not pressed on handle, set the drag at the middle
const bool bInHandle = InRect(rectHandle, g_pCtx->iMouseAbsX, g_pCtx->iMouseAbsY);
g_pCtx->iStartMouseDragOffset[g_pCtx->iSection] = (bInHandle) ?
(g_pCtx->iMouseAbsY - rectHandle.y0) :((rectHandle.y1 - rectHandle.y0) / 2.0f);
bAlterOffset = true;
}
} break;
case MODE_MOUSERELEASED:
g_pCtx->abYMouseDragOffset[g_pCtx->iSection] = false;
break;
case MODE_MOUSEMOVED:
bAlterOffset = g_pCtx->abYMouseDragOffset[g_pCtx->iSection];
break;
default:
break;
}

if (bAlterOffset)
{
const float flXPercMouse = (float)(g_pCtx->iMouseRelY - g_pCtx->iStartMouseDragOffset[g_pCtx->iSection]) / (float)(g_pCtx->dPanel.tall);
g_pCtx->iYOffset[g_pCtx->iSection] = clamp(flXPercMouse * g_pCtx->iWidget, 0, (g_pCtx->iWidget - iRowsInScreen));
}
}

g_pCtx->iSectionCanActive[g_pCtx->iSection] = g_pCtx->iCanActives;
++g_pCtx->iSection;
}
Expand Down
2 changes: 2 additions & 0 deletions mp/src/game/client/neo/ui/neo_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ struct Context
int iLayoutY;
int iWgXPos;
int iYOffset[MAX_SECTIONS];
bool abYMouseDragOffset[MAX_SECTIONS];
int iStartMouseDragOffset[MAX_SECTIONS];

int iHorizontalWidth;
int iHorizontalMargin;
Expand Down

0 comments on commit 12a5f98

Please sign in to comment.