From 0aad0d379aafe41352b55b1f10657fb51134d23d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 18 May 2025 22:45:22 -0700 Subject: [PATCH] Minimal controller support for dialog window --- apps/openmw/mwgui/dialogue.cpp | 135 +++++++++++++++++++++++++++++++++ apps/openmw/mwgui/dialogue.hpp | 11 +++ components/widgets/list.cpp | 5 ++ components/widgets/list.hpp | 1 + 4 files changed, 152 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e14c400978..3633c90e6a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -88,6 +88,10 @@ namespace MWGui mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + + mDisableGamepadCursor = Settings::gui().mControllerMenus; + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; } void PersuasionDialog::adjustAction(MyGUI::Widget* action, int& totalHeight) @@ -144,6 +148,24 @@ namespace MWGui else mMainWidget->setSize(mInitialMainWidgetWidth, mMainWidget->getSize().height); + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + mButtons.clear(); + mButtons.push_back(mAdmireButton); + mButtons.push_back(mIntimidateButton); + mButtons.push_back(mTauntButton); + if (mBribe10Button->getEnabled()) + mButtons.push_back(mBribe10Button); + if (mBribe100Button->getEnabled()) + mButtons.push_back(mBribe100Button); + if (mBribe1000Button->getEnabled()) + mButtons.push_back(mBribe1000Button); + + for (int i = 0; i < mButtons.size(); i++) + mButtons[i]->setStateSelected(i == 0); + } + WindowModal::onOpen(); } @@ -152,6 +174,34 @@ namespace MWGui return mAdmireButton; } + bool PersuasionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + onPersuade(mButtons[mControllerFocus]); + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancel(mCancelButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); + } + + return true; + } + // -------------------------------------------------------------------------------------------------- Response::Response(std::string_view text, std::string_view title, bool needMargin) @@ -335,6 +385,9 @@ namespace MWGui mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + + mControllerButtons.a = "#{sAsk}"; + mControllerButtons.b = "#{sGoodbye}"; } void DialogueWindow::onTradeComplete() @@ -488,6 +541,14 @@ namespace MWGui updateTopics(); updateTopicsPane(); // force update for new services + if (Settings::gui().mControllerMenus && !sameActor) + { + setControllerFocus(mControllerFocus, false); + // Reset focus to very top. Maybe change this to mTopicsList->getItemCount() - mKeywords.size()? + mControllerFocus = 0; + setControllerFocus(mControllerFocus, true); + } + updateDisposition(); restock(); } @@ -601,6 +662,9 @@ namespace MWGui redrawTopicsList(); updateHistory(); + + if (Settings::gui().mControllerMenus) + setControllerFocus(mControllerFocus, true); } void DialogueWindow::updateHistory(bool scrollbar) @@ -847,4 +911,75 @@ namespace MWGui && actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"); } + void DialogueWindow::setControllerFocus(int index, bool focused) + { + // List is mTopicsList + "Goodbye" button below the list. + if (index < 0 || index > mTopicsList->getItemCount()) + return; + + if (index == mTopicsList->getItemCount()) + { + mGoodbyeButton->setStateSelected(focused); + } + else { + std::string keyword = mTopicsList->getItemNameAt(mControllerFocus); + if (keyword.length() == 0) + return; + + MyGUI::Button* button = mTopicsList->getItemWidget(keyword); + button->setStateSelected(focused); + } + + if (focused) + { + // Scroll the side bar to keep the active item in view + if (index <= 5) + mTopicsList->setViewOffset(0); + else + mTopicsList->setViewOffset(-20 * (index - 5)); + } + } + + bool DialogueWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mControllerFocus == mTopicsList->getItemCount()) + onGoodbyeActivated(); + else + onSelectListItem(mTopicsList->getItemNameAt(mControllerFocus), mControllerFocus); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onGoodbyeActivated(); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + // Number of items is mTopicsList.length+1 because of "Goodbye" button. + setControllerFocus(mControllerFocus, false); + if (mControllerFocus <= 0) + mControllerFocus = mTopicsList->getItemCount(); + else if (mTopicsList->getItemNameAt(mControllerFocus - 1).length() == 0) + mControllerFocus -= 2; // Skip separator + else + mControllerFocus--; + setControllerFocus(mControllerFocus, true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + // Number of items is mTopicsList.length+1 because of "Goodbye" button. + setControllerFocus(mControllerFocus, false); + if (mControllerFocus >= mTopicsList->getItemCount()) + mControllerFocus = 0; + else if (mControllerFocus == mTopicsList->getItemCount() - 1) + mControllerFocus = mTopicsList->getItemCount(); + else if (mTopicsList->getItemNameAt(mControllerFocus + 1).length() == 0) + mControllerFocus += 2; // Skip separator + else + mControllerFocus++; + setControllerFocus(mControllerFocus, true); + } + + return true; + } } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 8a8b309401..68de4b73d7 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -49,6 +49,9 @@ namespace MWGui MyGUI::Widget* getDefaultKeyFocus() override; + protected: + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + private: std::unique_ptr mCallback; @@ -65,6 +68,9 @@ namespace MWGui MyGUI::Widget* mActionsBox; Gui::AutoSizedTextBox* mGoldLabel; + std::vector mButtons; + int mControllerFocus = 0; + void adjustAction(MyGUI::Widget* action, int& totalHeight); void onCancel(MyGUI::Widget* sender); @@ -186,6 +192,8 @@ namespace MWGui void onReferenceUnavailable() override; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + private: void updateDisposition(); void restock(); @@ -220,6 +228,9 @@ namespace MWGui std::unique_ptr mCallback; std::unique_ptr mGreetingCallback; + void setControllerFocus(int index, bool focused); + int mControllerFocus = 0; + void updateTopicFormat(); }; } diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 896057443c..28fa48e081 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -173,4 +173,9 @@ namespace Gui { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } + + void MWList::setViewOffset(int offset) + { + mScrollView->setViewOffset(MyGUI::IntPoint(0, offset)); + } } diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 3d5e320cf7..022214dd1c 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -48,6 +48,7 @@ namespace Gui ///< get widget for an item name, useful to set up tooltip void scrollToTop(); + void setViewOffset(int offset); void setPropertyOverride(std::string_view _key, std::string_view _value) override;