From 5c14bfeb1012ebd01151396972748d3a864653ca Mon Sep 17 00:00:00 2001 From: Xottab-DUTY Date: Fri, 10 Jan 2025 03:35:13 +0300 Subject: [PATCH] Ability to scroll in scroll views with gamepad (#943) --- src/xrUICore/ListWnd/UIListWnd.cpp | 26 +++++++++++++++++-- src/xrUICore/ListWnd/UIListWnd.h | 2 +- src/xrUICore/ListWnd/UIListWnd_script.cpp | 4 +++ src/xrUICore/ScrollView/UIScrollView.cpp | 31 +++++++++++++++++++++++ src/xrUICore/ScrollView/UIScrollView.h | 1 + src/xrUICore/Windows/UIWindow.h | 13 ++++++++++ 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/xrUICore/ListWnd/UIListWnd.cpp b/src/xrUICore/ListWnd/UIListWnd.cpp index 6bb66e764e6..292771dd0f3 100644 --- a/src/xrUICore/ListWnd/UIListWnd.cpp +++ b/src/xrUICore/ListWnd/UIListWnd.cpp @@ -1,6 +1,7 @@ #include"pch.hpp" #include "UIListWnd.h" //.#include "uiscrollbar.h" +#include "Cursor/UICursor.h" #include "Windows/UIFrameLineWnd.h" //#define ACTIVE_BACKGROUND "ui\\ui_pop_up_active_back" @@ -544,11 +545,11 @@ void CUIListWnd::ScrollToEnd() UpdateList(); } -void CUIListWnd::ScrollToPos(int position) +void CUIListWnd::ScrollToPos(int position, float center_y_ratio /*= 0.5f*/) { if (IsScrollBarEnabled()) { - int pos = position; + int pos = position - iFloor(float(m_iRowNum) * center_y_ratio); clamp(pos, m_ScrollBar->GetMinRange(), (m_ScrollBar->GetMaxRange() - m_ScrollBar->GetPageSize() / + 1)); m_ScrollBar->SetScrollPos(pos); m_iFirstShownIndex = m_ScrollBar->GetScrollPos(); @@ -564,6 +565,27 @@ void CUIListWnd::Update() m_bUpdateMouseMove = false; } + if (const auto focused = CursorOverWindow() ? UI().Focus().GetFocused() : nullptr) + { + const auto parentItem = focused->GetWindowBeforeParent(this); + + const auto listItem = dynamic_cast(parentItem); + const auto currentSelectedItem = GetItem(GetSelectedItem()); + + if (listItem && listItem != currentSelectedItem) + { + const auto prevPos = m_iFirstShownIndex; + + ScrollToPos(GetItemPos(listItem)); + + if (prevPos != m_iFirstShownIndex) + { + SendMessage(listItem, BUTTON_CLICKED, nullptr); + UI().GetUICursor().WarpToWindow(focused); + } + } + } + inherited::Update(); if (m_ActiveBackgroundFrame) m_ActiveBackgroundFrame->Update(); diff --git a/src/xrUICore/ListWnd/UIListWnd.h b/src/xrUICore/ListWnd/UIListWnd.h index 55e8302e835..e005a763a2a 100644 --- a/src/xrUICore/ListWnd/UIListWnd.h +++ b/src/xrUICore/ListWnd/UIListWnd.h @@ -76,7 +76,7 @@ class XRUICORE_API CUIListWnd final : public CUIWindow void ScrollToBegin(); void ScrollToEnd(); - void ScrollToPos(int position); + void ScrollToPos(int position, float center_y_ratio = 0.5f); IC bool IsActiveBackgroundEnabled() { return m_bActiveBackground; } void EnableActiveBackground(bool enable); diff --git a/src/xrUICore/ListWnd/UIListWnd_script.cpp b/src/xrUICore/ListWnd/UIListWnd_script.cpp index 3bef6f9cc78..58b638bfd1e 100644 --- a/src/xrUICore/ListWnd/UIListWnd_script.cpp +++ b/src/xrUICore/ListWnd/UIListWnd_script.cpp @@ -43,6 +43,10 @@ SCRIPT_EXPORT(CUIListWnd, (CUIWindow), .def("GetSize", &CUIListWnd::GetItemsCount) .def("ScrollToBegin", &CUIListWnd::ScrollToBegin) .def("ScrollToEnd", &CUIListWnd::ScrollToEnd) + .def("ScrollToPos", +[](CUIListWnd* self, int position) + { + self->ScrollToPos(position, 0.0f); + }) .def("ScrollToPos", &CUIListWnd::ScrollToPos) .def("SetWidth", &CUIListWnd::SetWidth) .def("SetTextColor", &CUIListWnd::SetTextColor) diff --git a/src/xrUICore/ScrollView/UIScrollView.cpp b/src/xrUICore/ScrollView/UIScrollView.cpp index 529bb2d7c22..034edb29622 100644 --- a/src/xrUICore/ScrollView/UIScrollView.cpp +++ b/src/xrUICore/ScrollView/UIScrollView.cpp @@ -115,6 +115,24 @@ void CUIScrollView::Update() if (m_flags.test(eNeedRecalc)) RecalcSize(); + if (const auto focused = CursorOverWindow() ? UI().Focus().GetFocused() : nullptr) + { + const auto scrollItem = focused->GetWindowBeforeParent(m_pad); + + if (scrollItem && GetSelected() != scrollItem) + { + const auto prevPos = GetCurrentScrollPos(); + + ScrollToWindow(scrollItem); + + if (m_flags.test(eItemsSelectabe)) + scrollItem->OnMouseDown(MOUSE_1); + + if (prevPos != GetCurrentScrollPos()) + UI().GetUICursor().WarpToWindow(focused); + } + } + inherited::Update(); } @@ -320,6 +338,19 @@ void CUIScrollView::ScrollToEnd() OnScrollV(nullptr, nullptr); } +void CUIScrollView::ScrollToWindow(CUIWindow* pWnd, float center_y_ratio /*= 0.5f*/) +{ + R_ASSERT2_CURE(pWnd->GetParent() == m_pad, "Requested window to scroll to doesn't belong to the scroll view", return); + + if (m_flags.test(eNeedRecalc)) + RecalcSize(); + + const float ratio = GetHeight() * center_y_ratio; + const int pos = iFloor(m_upIndent + pWnd->GetWndPos().y - ratio); + + SetScrollPos(pos); +} + void CUIScrollView::SetRightIndention(float val) { m_rightIndent = val; diff --git a/src/xrUICore/ScrollView/UIScrollView.h b/src/xrUICore/ScrollView/UIScrollView.h index 78ae5ebe843..91ff8aa1739 100644 --- a/src/xrUICore/ScrollView/UIScrollView.h +++ b/src/xrUICore/ScrollView/UIScrollView.h @@ -56,6 +56,7 @@ class XRUICORE_API CUIScrollView : public CUIWindow, public CUIWndCallback void Clear(); void ScrollToBegin(); void ScrollToEnd(); + void ScrollToWindow(CUIWindow* pWnd, float center_y_ratio = 0.5f); bool GetVertFlip() const { return !!m_flags.test(eVertFlip); } bool Empty() const { return m_pad->GetChildWndList().empty(); } diff --git a/src/xrUICore/Windows/UIWindow.h b/src/xrUICore/Windows/UIWindow.h index 09940ecb1e1..b8c040cf576 100644 --- a/src/xrUICore/Windows/UIWindow.h +++ b/src/xrUICore/Windows/UIWindow.h @@ -34,6 +34,19 @@ class XRUICORE_API CUIWindow : public CUISimpleWindow, public CUIDebuggable CUIWindow* GetParent() const { return m_pParentWnd; } void SetParent(CUIWindow* pNewParent); + [[nodiscard]] + CUIWindow* GetWindowBeforeParent(const CUIWindow* parent) + { + CUIWindow* result = this; + for (CUIWindow* it = GetParent(); it; it = it->GetParent()) + { + if (it == parent) + return result; + result = it; + } + return nullptr; + } + //получить окно самого верхнего уровня [[nodiscard]] CUIWindow* GetTop()