Support overlapping scroll regions, always allow mouse scrolling

Support scrolling all scroll regions with the mouse wheel also while popup menus are open. Support overlapping scroll regions by always scrolling the top-most hovered scroll region on mouse wheel events.

The hot scroll region is now tracked separated by `CUi`, as tracking the IDs of all UI elements which are contained in scroll regions is not feasible. The separate active state for scroll regions is therefore unnecessary.

It's still necessary to disable `CListBox`es when popup menus are open, to ensure that only one list box consumes the key events.

Closes #8087. Supersedes #8090.
This commit is contained in:
Robert Müller 2024-03-12 18:14:55 +01:00
parent f291362d88
commit ae9a8fe3d4
5 changed files with 35 additions and 17 deletions

View file

@ -113,11 +113,6 @@ CUi::CUi()
{
m_Enabled = true;
m_pHotItem = nullptr;
m_pActiveItem = nullptr;
m_pLastActiveItem = nullptr;
m_pBecomingHotItem = nullptr;
m_MouseX = 0;
m_MouseY = 0;
m_MouseWorldX = 0;
@ -220,6 +215,8 @@ void CUi::Update(float MouseX, float MouseY, float MouseDeltaX, float MouseDelta
if(m_pActiveItem)
m_pHotItem = m_pActiveItem;
m_pBecomingHotItem = nullptr;
m_pHotScrollRegion = m_pBecomingHotScrollRegion;
m_pBecomingHotScrollRegion = nullptr;
if(Enabled())
{
@ -231,6 +228,7 @@ void CUi::Update(float MouseX, float MouseY, float MouseDeltaX, float MouseDelta
{
m_pHotItem = nullptr;
m_pActiveItem = nullptr;
m_pHotScrollRegion = nullptr;
}
}
@ -1416,7 +1414,10 @@ void CUi::RenderPopupMenus()
const bool Active = i == m_vPopupMenus.size() - 1;
if(Active)
{
// Prevent UI elements below the popup menu from being activated.
SetHotItem(pId);
}
if(CheckActiveItem(pId))
{
@ -1437,6 +1438,12 @@ void CUi::RenderPopupMenus()
SetActiveItem(pId);
}
if(Inside)
{
// Prevent scroll regions directly behind popup menus from using the mouse scroll events.
SetHotScrollRegion(nullptr);
}
CUIRect PopupRect = PopupMenu.m_Rect;
PopupRect.Draw(PopupMenu.m_Props.m_BorderColor, PopupMenu.m_Props.m_Corners, 3.0f);
PopupRect.Margin(SPopupMenu::POPUP_BORDER, &PopupRect);

View file

@ -14,6 +14,7 @@
#include <string>
#include <vector>
class CScrollRegion;
class IClient;
class IGraphics;
class IKernel;
@ -322,10 +323,12 @@ public:
private:
bool m_Enabled;
const void *m_pHotItem;
const void *m_pActiveItem;
const void *m_pLastActiveItem; // only used internally to track active CLineInput
const void *m_pBecomingHotItem;
const void *m_pHotItem = nullptr;
const void *m_pActiveItem = nullptr;
const void *m_pLastActiveItem = nullptr; // only used internally to track active CLineInput
const void *m_pBecomingHotItem = nullptr;
const CScrollRegion *m_pHotScrollRegion = nullptr;
const CScrollRegion *m_pBecomingHotScrollRegion = nullptr;
bool m_ActiveItemValid = false;
vec2 m_UpdatedMousePos = vec2(0.0f, 0.0f);
@ -464,9 +467,11 @@ public:
}
return false;
}
void SetHotScrollRegion(const CScrollRegion *pId) { m_pBecomingHotScrollRegion = pId; }
const void *HotItem() const { return m_pHotItem; }
const void *NextHotItem() const { return m_pBecomingHotItem; }
const void *ActiveItem() const { return m_pActiveItem; }
const CScrollRegion *HotScrollRegion() const { return m_pHotScrollRegion; }
void StartCheck() { m_ActiveItemValid = false; }
void FinishCheck()
@ -629,7 +634,7 @@ public:
struct SSelectionPopupContext : public SPopupMenuId
{
CUi *m_pUI; // set by CUi when popup is shown
class CScrollRegion *m_pScrollRegion;
CScrollRegion *m_pScrollRegion;
SPopupMenuProperties m_Props;
char m_aMessage[256];
std::vector<std::string> m_vEntries;

View file

@ -117,7 +117,6 @@ void CListBox::DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsP
// setup the scrollbar
m_ScrollOffset = vec2(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_Active = m_Active;
ScrollParams.m_ScrollbarWidth = ScrollbarWidthMax();
ScrollParams.m_ScrollbarMargin = ScrollbarMargin();
ScrollParams.m_ScrollUnit = (m_ListBoxRowHeight + m_AutoSpacing) * RowsPerScroll;
@ -208,7 +207,7 @@ CListboxItem CListBox::DoSubheader()
int CListBox::DoEnd()
{
m_ScrollRegion.End();
m_Active |= m_ScrollRegion.Params().m_Active;
m_Active |= m_ScrollRegion.Active();
m_ScrollbarShown = m_ScrollRegion.ScrollbarShown();
if(m_ListBoxNewSelOffset != 0 && m_ListBoxNumItems > 0 && m_ListBoxSelectedIndex == m_ListBoxNewSelected)

View file

@ -79,7 +79,7 @@ void CScrollRegion::End()
CUIRect RegionRect = m_ClipRect;
RegionRect.w += m_Params.m_ScrollbarWidth;
if(m_ScrollDirection != SCROLLRELATIVE_NONE || (Ui()->Enabled() && m_Params.m_Active && Ui()->MouseHovered(&RegionRect)))
if(m_ScrollDirection != SCROLLRELATIVE_NONE || Ui()->HotScrollRegion() == this)
{
bool ProgrammaticScroll = false;
if(Ui()->ConsumeHotkey(CUi::HOTKEY_SCROLL_UP))
@ -106,6 +106,11 @@ void CScrollRegion::End()
}
}
if(Ui()->Enabled() && Ui()->MouseHovered(&RegionRect))
{
Ui()->SetHotScrollRegion(this);
}
const float SliderHeight = maximum(m_Params.m_SliderMinHeight, m_ClipRect.h / m_ContentH * m_RailRect.h);
CUIRect Slider = m_RailRect;
@ -163,7 +168,6 @@ void CScrollRegion::End()
m_SliderGrabPos = Ui()->MouseY() - Slider.y;
m_AnimTargetScrollY = m_ScrollY;
m_AnimTime = 0.0f;
m_Params.m_Active = true;
}
}
else if(InsideRail && Ui()->MouseButtonClicked(0))
@ -174,7 +178,6 @@ void CScrollRegion::End()
m_SliderGrabPos = Slider.h / 2.0f;
m_AnimTargetScrollY = m_ScrollY;
m_AnimTime = 0.0f;
m_Params.m_Active = true;
}
else if(Ui()->CheckActiveItem(pId) && !Ui()->MouseButton(0))
{
@ -259,3 +262,8 @@ bool CScrollRegion::Animating() const
{
return m_AnimTime > 0.0f;
}
bool CScrollRegion::Active() const
{
return Ui()->ActiveItem() == &m_ScrollY;
}

View file

@ -7,7 +7,6 @@
struct CScrollRegionParams
{
bool m_Active;
float m_ScrollbarWidth;
float m_ScrollbarMargin;
bool m_ScrollbarNoMarginRight;
@ -28,7 +27,6 @@ struct CScrollRegionParams
CScrollRegionParams()
{
m_Active = true;
m_ScrollbarWidth = 20.0f;
m_ScrollbarMargin = 5.0f;
m_ScrollbarNoMarginRight = false;
@ -140,6 +138,7 @@ public:
bool RectClipped(const CUIRect &Rect) const;
bool ScrollbarShown() const;
bool Animating() const;
bool Active() const;
const CScrollRegionParams &Params() const { return m_Params; }
};