From 8c2ecc2a884dafe8ed59b59c89bbafb9fb012ef8 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 15 May 2025 00:46:02 -0700 Subject: [PATCH] Allow LT and RT to toggle between open gui windows --- apps/openmw/mwbase/windowmanager.hpp | 5 +- apps/openmw/mwgui/container.cpp | 6 ++ apps/openmw/mwgui/container.hpp | 2 + apps/openmw/mwgui/itemview.cpp | 8 +++ apps/openmw/mwgui/itemview.hpp | 1 + apps/openmw/mwgui/windowbase.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 70 ++++++++++++++++++++--- apps/openmw/mwgui/windowmanagerimp.hpp | 5 +- apps/openmw/mwinput/controllermanager.cpp | 17 +++++- 9 files changed, 103 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d2f1fc6514..e00837265f 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -382,7 +382,10 @@ namespace MWBase /// Same as viewer->getCamera()->getCullMask(), provided for consistency. virtual uint32_t getCullMask() = 0; - virtual MWGui::WindowBase* getTopWindow() = 0; + /// Return the window that should receive controller events + virtual MWGui::WindowBase* getActiveControllerWindow() = 0; + /// Cycle to the next window to receive controller events + virtual void cycleActiveControllerWindow(bool next) = 0; // Used in Lua bindings virtual const std::vector& getGuiModeStack() const = 0; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 658eedfcd2..c47000d753 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -398,4 +398,10 @@ namespace MWGui } return false; } + + void ContainerWindow::setActiveControllerWindow(bool active) + { + mItemView->setActiveControllerWindow(active); + WindowBase::setActiveControllerWindow(active); + } } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 5d49b4c3ff..f7f4ef3c31 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -40,6 +40,8 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Container"; } + void setActiveControllerWindow(bool active) override; + private: DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index e568da5900..526f231956 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -180,6 +180,14 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } + void ItemView::setActiveControllerWindow(bool active) + { + if (active) + updateControllerFocus(-1, mControllerFocus); + else + updateControllerFocus(mControllerFocus, -1); + } + void ItemView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (!mItemCount) diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 8a53d3678e..b1fa941ef4 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -32,6 +32,7 @@ namespace MWGui void resetScrollBars(); + void setActiveControllerWindow(bool active); int getControllerFocus() { return mControllerFocus; } int getItemCount() { return mItemCount; } void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 3db4399d85..fd1c743c62 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -62,11 +62,13 @@ namespace MWGui // REMOVEME // virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) = 0; // virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) = 0; + virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: virtual void onTitleDoubleClicked(); MyGUI::Widget* mMouseFocus = nullptr; + bool mActiveControllerWindow = false; void trackFocusEvents(MyGUI::Widget* widget); private: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index b328176014..d52a71a417 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -859,7 +859,7 @@ namespace MWGui mHud->setPlayerPos(x, y, u, v); } - WindowBase* WindowManager::getTopWindow() + WindowBase* WindowManager::getActiveControllerWindow() { if (!mCurrentModals.empty()) return mCurrentModals.back(); @@ -869,15 +869,18 @@ namespace MWGui if (!mGuiModes.empty()) { - GuiModeState& state = mGuiModeStates[mGuiModes.back()]; + GuiMode mode = mGuiModes.back(); + GuiModeState& state = mGuiModeStates[mode]; + int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); + // REMOVEME - Log(Debug::Error) << "getTopWindow: " << state.mWindows.size() << " windows in state " << mGuiModes.back(); - // find the topmost window - for (WindowBase* window : state.mWindows) - if (window->isVisible()) - return window; - else - Log(Debug::Error) << "-- Skipping hidden window " << window; + Log(Debug::Error) << "getActiveControllerWindow: " << state.mWindows.size() << " windows in state, mActiveControllerWindows[mode] = " << mActiveControllerWindows[mode]; + + // If the active window is no longer visible, find the next visible window. + if (!state.mWindows[activeIndex]->isVisible()) + cycleActiveControllerWindow(true); + + return state.mWindows[activeIndex]; } else { @@ -893,6 +896,48 @@ namespace MWGui return nullptr; } + void WindowManager::cycleActiveControllerWindow(bool next) + { + if (mGuiModes.empty()) + return; + + GuiMode mode = mGuiModes.back(); + int winCount = mGuiModeStates[mode].mWindows.size(); + + int activeIndex = 0; + if (winCount > 1) + { + // Find next/previous visible window + activeIndex = mActiveControllerWindows[mode]; + int delta = next ? 1 : -1; + + for (int i = 0; i < winCount; i++) + { + activeIndex += delta; + if (activeIndex < 0) + activeIndex = winCount - 1; + else if (activeIndex >= winCount) + activeIndex = 0; + + if (mGuiModeStates[mode].mWindows[activeIndex]->isVisible()) + break; + } + } + + // REMOVEME + Log(Debug::Error) << "focusNextWindow: mode=" << mode << ", activeIndex=" << activeIndex; + + if (mActiveControllerWindows[mode] != activeIndex) + { + mActiveControllerWindows[mode] = activeIndex; + for (int i = 0; i < winCount; i++) + { + mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); + } + playSound(ESM::RefId::stringRefId("Menu Click")); + } + } + void WindowManager::update(float frameDuration) { handleScheduledMessageBoxes(); @@ -1316,6 +1361,13 @@ namespace MWGui { for (WindowBase* window : mGuiModeStates[mode].mWindows) window->setPtr(arg); + + // Activate first visible window + mActiveControllerWindows[mode] = -1; + cycleActiveControllerWindow(true); + + // REMOVEME + Log(Debug::Error) << "pushGuiMode: mode=" << mode << ", activeIndex=" << mActiveControllerWindows[mode]; } catch (...) { diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index fd035ed12e..a1c01f6fa6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -387,7 +387,8 @@ namespace MWGui void asyncPrepareSaveMap() override; - WindowBase* getTopWindow() override; + WindowBase* getActiveControllerWindow() override; + void cycleActiveControllerWindow(bool next) override; // Used in Lua bindings const std::vector& getGuiModeStack() const override { return mGuiModes; } @@ -495,6 +496,8 @@ namespace MWGui std::map mGuiModeStates; // The currently active stack of GUI modes (top mode is the one we are in). std::vector mGuiModes; + // The active window for controller mode for each GUI mode. + std::map mActiveControllerWindows; std::unique_ptr mCursorManager; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 7aff8172ce..f12c9a31ce 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -243,7 +243,7 @@ namespace MWInput { if (Settings::gui().mControllerMenus) { - MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); + MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getActiveControllerWindow(); if (topWin && topWin->onControllerButtonEvent(arg)) return true; } @@ -311,7 +311,20 @@ namespace MWInput { if (Settings::gui().mControllerMenus) { - MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); + // Left and right triggers toggle through open GUI windows. + if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + { + if (arg.value == 32767) // Treat like a button. + MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(true); + return true; + } + else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) + { + if (arg.value == 32767) // Treat like a button. + MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(false); + return true; + } + MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getActiveControllerWindow(); if (topWin && topWin->onControllerThumbstickEvent(arg)) return true; }