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

View file

@ -14,6 +14,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
class CScrollRegion;
class IClient; class IClient;
class IGraphics; class IGraphics;
class IKernel; class IKernel;
@ -322,10 +323,12 @@ public:
private: private:
bool m_Enabled; bool m_Enabled;
const void *m_pHotItem; const void *m_pHotItem = nullptr;
const void *m_pActiveItem; const void *m_pActiveItem = nullptr;
const void *m_pLastActiveItem; // only used internally to track active CLineInput const void *m_pLastActiveItem = nullptr; // only used internally to track active CLineInput
const void *m_pBecomingHotItem; const void *m_pBecomingHotItem = nullptr;
const CScrollRegion *m_pHotScrollRegion = nullptr;
const CScrollRegion *m_pBecomingHotScrollRegion = nullptr;
bool m_ActiveItemValid = false; bool m_ActiveItemValid = false;
vec2 m_UpdatedMousePos = vec2(0.0f, 0.0f); vec2 m_UpdatedMousePos = vec2(0.0f, 0.0f);
@ -464,9 +467,11 @@ public:
} }
return false; return false;
} }
void SetHotScrollRegion(const CScrollRegion *pId) { m_pBecomingHotScrollRegion = pId; }
const void *HotItem() const { return m_pHotItem; } const void *HotItem() const { return m_pHotItem; }
const void *NextHotItem() const { return m_pBecomingHotItem; } const void *NextHotItem() const { return m_pBecomingHotItem; }
const void *ActiveItem() const { return m_pActiveItem; } const void *ActiveItem() const { return m_pActiveItem; }
const CScrollRegion *HotScrollRegion() const { return m_pHotScrollRegion; }
void StartCheck() { m_ActiveItemValid = false; } void StartCheck() { m_ActiveItemValid = false; }
void FinishCheck() void FinishCheck()
@ -629,7 +634,7 @@ public:
struct SSelectionPopupContext : public SPopupMenuId struct SSelectionPopupContext : public SPopupMenuId
{ {
CUi *m_pUI; // set by CUi when popup is shown CUi *m_pUI; // set by CUi when popup is shown
class CScrollRegion *m_pScrollRegion; CScrollRegion *m_pScrollRegion;
SPopupMenuProperties m_Props; SPopupMenuProperties m_Props;
char m_aMessage[256]; char m_aMessage[256];
std::vector<std::string> m_vEntries; 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 // setup the scrollbar
m_ScrollOffset = vec2(0.0f, 0.0f); m_ScrollOffset = vec2(0.0f, 0.0f);
CScrollRegionParams ScrollParams; CScrollRegionParams ScrollParams;
ScrollParams.m_Active = m_Active;
ScrollParams.m_ScrollbarWidth = ScrollbarWidthMax(); ScrollParams.m_ScrollbarWidth = ScrollbarWidthMax();
ScrollParams.m_ScrollbarMargin = ScrollbarMargin(); ScrollParams.m_ScrollbarMargin = ScrollbarMargin();
ScrollParams.m_ScrollUnit = (m_ListBoxRowHeight + m_AutoSpacing) * RowsPerScroll; ScrollParams.m_ScrollUnit = (m_ListBoxRowHeight + m_AutoSpacing) * RowsPerScroll;
@ -208,7 +207,7 @@ CListboxItem CListBox::DoSubheader()
int CListBox::DoEnd() int CListBox::DoEnd()
{ {
m_ScrollRegion.End(); m_ScrollRegion.End();
m_Active |= m_ScrollRegion.Params().m_Active; m_Active |= m_ScrollRegion.Active();
m_ScrollbarShown = m_ScrollRegion.ScrollbarShown(); m_ScrollbarShown = m_ScrollRegion.ScrollbarShown();
if(m_ListBoxNewSelOffset != 0 && m_ListBoxNumItems > 0 && m_ListBoxSelectedIndex == m_ListBoxNewSelected) if(m_ListBoxNewSelOffset != 0 && m_ListBoxNumItems > 0 && m_ListBoxSelectedIndex == m_ListBoxNewSelected)

View file

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

View file

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