diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index d8302df87c..f096beddcb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -25,8 +26,8 @@ #include "../mwmechanics/spellutil.hpp" #include "class.hpp" +#include "textcolours.hpp" #include "tooltips.hpp" -#include "widgets.hpp" namespace { @@ -95,6 +96,13 @@ namespace MWGui += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sOk}"; + } } void EditEffectDialog::setConstantEffect(bool constant) @@ -154,6 +162,15 @@ namespace MWGui mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); + if (Settings::gui().mControllerMenus) + { + mRangeButton->setStateSelected(true); + mDeleteButton->setStateSelected(false); + mOkButton->setStateSelected(false); + mCancelButton->setStateSelected(false); + mControllerFocus = 0; + } + setVisible(true); } @@ -187,6 +204,15 @@ namespace MWGui onDurationChanged(mDurationSlider, effect.mDuration - 1); eventEffectModified(mEffect); + if (Settings::gui().mControllerMenus) + { + mRangeButton->setStateSelected(true); + mDeleteButton->setStateSelected(false); + mOkButton->setStateSelected(false); + mCancelButton->setStateSelected(false); + mControllerFocus = 0; + } + updateBoxes(); } @@ -231,6 +257,25 @@ namespace MWGui mAreaBox->setVisible(true); // curY += mAreaBox->getSize().height; } + + if (Settings::gui().mControllerMenus) + { + mButtons.clear(); + mButtons.emplace_back(mRangeButton); + if (mMagnitudeBox->getVisible()) + { + mButtons.emplace_back(mMagnitudeMinValue); + mButtons.emplace_back(mMagnitudeMaxValue); + } + if (mDurationBox->getVisible()) + mButtons.emplace_back(mDurationValue); + if (mAreaBox->getVisible()) + mButtons.emplace_back(mAreaValue); + if (mDeleteButton->getVisible()) + mButtons.emplace_back(mDeleteButton); + mButtons.emplace_back(mOkButton); + mButtons.emplace_back(mCancelButton); + } } void EditEffectDialog::onRangeButtonClicked(MyGUI::Widget* sender) @@ -340,6 +385,195 @@ namespace MWGui eventEffectModified(mEffect); } + bool EditEffectDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + int prevFocus = mControllerFocus; + mControllerFocus = std::clamp(mControllerFocus, 0, (int)mButtons.size() - 1); + MyGUI::TextBox* button = mButtons[mControllerFocus]; + + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (button == mRangeButton) + onRangeButtonClicked(mRangeButton); + else if (button == mCancelButton) + onCancelButtonClicked(mCancelButton); + else if (button == mOkButton) + onOkButtonClicked(mOkButton); + else if (button == mDeleteButton) + onDeleteButtonClicked(mDeleteButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancelButtonClicked(mCancelButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + onOkButtonClicked(mOkButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mControllerFocus == 0) + mControllerFocus = (int)mButtons.size() - 2; + else if (button == mCancelButton && mDeleteButton->getVisible()) + mControllerFocus -= 3; + else if (button == mCancelButton || (button == mOkButton && mDeleteButton->getVisible())) + mControllerFocus -= 2; + else + mControllerFocus = std::max(mControllerFocus - 1, 0); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (button == mDeleteButton || button == mOkButton || button == mCancelButton) + mControllerFocus = 0; + else + mControllerFocus++; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + { + if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition(0); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition(mMagnitudeMinSlider->getScrollPosition()); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(0); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(0); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition(mMagnitudeMaxSlider->getScrollPosition()); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition(mMagnitudeMaxSlider->getScrollRange() - 1); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(mDurationSlider->getScrollRange() - 1); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(mAreaSlider->getScrollRange() - 1); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + if (button == mRangeButton) + onRangeButtonClicked(mRangeButton); + else if (button == mCancelButton) + mControllerFocus--; + else if (button == mOkButton && mDeleteButton->getVisible()) + mControllerFocus--; + else if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition(mMagnitudeMinSlider->getScrollPosition() - 1); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition( + std::max(mMagnitudeMaxSlider->getScrollPosition() - 1, mMagnitudeMinSlider->getScrollPosition())); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(mDurationSlider->getScrollPosition() - 1); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(mAreaSlider->getScrollPosition() - 1); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + if (button == mRangeButton) + onRangeButtonClicked(mRangeButton); + else if (button == mDeleteButton) + mControllerFocus++; + else if (button == mOkButton) + mControllerFocus++; + else if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition( + std::min(mMagnitudeMinSlider->getScrollPosition() + 1, mMagnitudeMaxSlider->getScrollPosition())); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition(mMagnitudeMaxSlider->getScrollPosition() + 1); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(mDurationSlider->getScrollPosition() + 1); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(mAreaSlider->getScrollPosition() + 1); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + + if (prevFocus != mControllerFocus) + updateControllerFocus(prevFocus, mControllerFocus); + + return true; + } + + void EditEffectDialog::updateControllerFocus(int prevFocus, int newFocus) + { + const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; + + if (prevFocus >= 0 && prevFocus < mButtons.size()) + { + MyGUI::TextBox* button = mButtons[prevFocus]; + if (button == mMagnitudeMinValue || + button == mMagnitudeMaxValue || + button == mDurationValue || + button == mAreaValue) + { + button->setTextColour(textColours.normal); + } + else + { + ((MyGUI::Button*)button)->setStateSelected(false); + } + } + + if (newFocus >= 0 && newFocus < mButtons.size()) + { + MyGUI::TextBox* button = mButtons[newFocus]; + if (button == mMagnitudeMinValue || + button == mMagnitudeMaxValue || + button == mDurationValue || + button == mAreaValue) + { + button->setTextColour(textColours.link); + } + else + { + ((MyGUI::Button*)button)->setStateSelected(true); + } + } + } + // ------------------------------------------------------------------------------------------------ SpellCreationDialog::SpellCreationDialog() @@ -360,6 +594,13 @@ namespace MWGui mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); setWidgets(mAvailableEffectsList, mUsedEffectsView); + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sBuy}"; + } } void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor) @@ -490,6 +731,24 @@ namespace MWGui mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); } + + bool SpellCreationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCancelButtonClicked(mCancelButton); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + onBuyButtonClicked(mBuyButton); + return true; + } + else + return EffectEditorBase::onControllerButtonEvent(arg); + } + + // ------------------------------------------------------------------------------------------------ EffectEditorBase::EffectEditorBase(Type type) @@ -561,6 +820,7 @@ namespace MWGui mAvailableEffectsList->adjustSize(); mAvailableEffectsList->scrollToTop(); + mAvailableButtons.clear(); for (const short effectId : knownEffects) { const std::string& name = MWBase::Environment::get() @@ -568,13 +828,23 @@ namespace MWGui ->get() .find(ESM::MagicEffect::indexToGmstString(effectId)) ->mValue.getString(); - MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); + MyGUI::Button* w = mAvailableEffectsList->getItemWidget(name); + mAvailableButtons.emplace_back(w); ToolTips::createMagicEffectToolTip(w, effectId); } mEffects.clear(); updateEffectsView(); + + if (Settings::gui().mControllerMenus) + { + mAvailableFocus = 0; + mEffectFocus = 0; + mRightColumn = false; + if (mAvailableButtons.size() > 0) + mAvailableButtons[0]->setStateSelected(true); + } } void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView) @@ -686,6 +956,7 @@ namespace MWGui MyGUI::IntSize size(0, 0); + mEffectButtons.clear(); int i = 0; for (const ESM::ENAMstruct& effectInfo : mEffects) { @@ -718,6 +989,8 @@ namespace MWGui size.width = std::max(size.width, effect->getRequestedWidth()); size.height += 24; ++i; + + mEffectButtons.emplace_back(std::pair(effect, button)); } // Canvas size must be expressed with HScroll disabled, otherwise MyGUI would expand the scroll area when the @@ -755,4 +1028,80 @@ namespace MWGui effect.mRange = ESM::RT_Self; mConstantEffect = constant; } + + bool EffectEditorBase::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + { + onAvailableEffectClicked(mAvailableButtons[mAvailableFocus]); + } + else if (mRightColumn && mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + { + onEditEffect(mEffectButtons[mEffectFocus].second); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mRightColumn && mEffectButtons.size() > 0) + { + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(false); + mEffectFocus = wrap(mEffectFocus - 1, mEffectButtons.size()); + mEffectButtons[mEffectFocus].first->setStateSelected(true); + } + else if (!mRightColumn && mAvailableButtons.size() > 0) + { + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(false); + mAvailableFocus = wrap(mAvailableFocus - 1, mAvailableButtons.size()); + mAvailableButtons[mAvailableFocus]->setStateSelected(true); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mRightColumn && mEffectButtons.size() > 0) + { + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(false); + mEffectFocus = wrap(mEffectFocus + 1, mEffectButtons.size()); + mEffectButtons[mEffectFocus].first->setStateSelected(true); + } + else if (!mRightColumn && mAvailableButtons.size() > 0) + { + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(false); + mAvailableFocus = wrap(mAvailableFocus + 1, mAvailableButtons.size()); + mAvailableButtons[mAvailableFocus]->setStateSelected(true); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mRightColumn) + { + mRightColumn = false; + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(false); + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mRightColumn && mEffectButtons.size() > 0) + { + mRightColumn = true; + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(false); + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(true); + } + + // Scroll the list to keep the active item in view + if (mAvailableFocus <= 5) + mAvailableEffectsList->setViewOffset(0); + else + { + const int lineHeight = Settings::gui().mFontSize + 3; + mAvailableEffectsList->setViewOffset(-lineHeight * (mAvailableFocus - 5)); + } + + return true; + } } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index b2bd71d9c4..cbd57014ce 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -2,12 +2,14 @@ #define MWGUI_SPELLCREATION_H #include +#include #include #include #include #include "referenceinterface.hpp" +#include "widgets.hpp" #include "windowbase.hpp" namespace Gui @@ -84,13 +86,18 @@ namespace MWGui void updateBoxes(); - protected: + private: ESM::ENAMstruct mEffect; ESM::ENAMstruct mOldEffect; const ESM::MagicEffect* mMagicEffect; bool mConstantEffect; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + void updateControllerFocus(int prevFocus, int newFocus); + int mControllerFocus; + std::vector mButtons; }; class EffectEditorBase @@ -143,8 +150,16 @@ namespace MWGui virtual void notifyEffectsChanged() {} + virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); + private: Type mType; + + int mAvailableFocus; + int mEffectFocus; + bool mRightColumn; + std::vector mAvailableButtons; + std::vector> mEffectButtons; }; class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase @@ -167,6 +182,7 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* sender); void onBuyButtonClicked(MyGUI::Widget* sender); void onAccept(MyGUI::EditBox* sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; void notifyEffectsChanged() override;