1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2026-01-06 01:13:07 +00:00

Add controller support for all four inventory menus

This commit is contained in:
Andrew Lanzone 2025-05-24 14:17:41 -07:00
parent 5e3a49ade5
commit d759418f01
24 changed files with 515 additions and 56 deletions

View file

@ -5,6 +5,7 @@
#include <string>
#include <vector>
#include <MyGUI_Widget.h>
#include <SDL_gamecontroller.h>
#include <cstdint>
@ -61,6 +62,7 @@ namespace MWBase
virtual float getControllerAxisValue(SDL_GameControllerAxis axis) const = 0; // returns value in range [-1, 1]
virtual int getMouseMoveX() const = 0;
virtual int getMouseMoveY() const = 0;
virtual void warpMouseToWidget(MyGUI::Widget* widget) = 0;
/// Actions available for binding to keyboard buttons
virtual const std::initializer_list<int>& getActionKeySorting() = 0;

View file

@ -11,6 +11,7 @@
#include <MyGUI_KeyCode.h>
#include "../mwgui/hud.hpp"
#include "../mwgui/mode.hpp"
#include "../mwgui/windowbase.hpp"
@ -158,7 +159,9 @@ namespace MWBase
virtual MWGui::CountDialog* getCountDialog() = 0;
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
virtual MWGui::TradeWindow* getTradeWindow() = 0;
virtual MWGui::HUD* getHud() = 0;
virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0;
virtual std::vector<MWGui::WindowBase*> getGuiModeWindows(MWGui::GuiMode mode) = 0;
/// Make the player use an item, while updating GUI state accordingly
virtual void useItem(const MWWorld::Ptr& item, bool force = false) = 0;

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H
#define OPENMW_MWGUI_COMPANIONWINDOW_H
#include "companionitemmodel.hpp"
#include "itemmodel.hpp"
#include "referenceinterface.hpp"
#include "windowbase.hpp"
@ -32,6 +34,9 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Companion"; }
MWGui::ItemView* getItemView() { return mItemView; }
ItemModel* getModel() { return mModel; }
private:
ItemView* mItemView;
SortFilterItemModel* mSortModel;

View file

@ -58,9 +58,9 @@ namespace MWGui
setCoord(200, 0, 600, 300);
mControllerButtons.a = "#{sTake}";
mControllerButtons.b = "#{sClose}";
mControllerButtons.b = "#{sBack}";
mControllerButtons.x = "#{sTakeAll}";
mControllerButtons.y = "#{sInfo}";
mControllerButtons.r3 = "#{sInfo}";
mControllerButtons.l2 = "#{sInventory}";
}
@ -385,12 +385,13 @@ namespace MWGui
if (mDisposeCorpseButton->getVisible())
onDisposeCorpseButtonClicked(mDisposeCorpseButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP ||
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK ||
arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP ||
arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN ||
arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
mItemView->onControllerButtonEvent(arg);
mItemView->onControllerButtonEvent(arg.button);
}
return true;

View file

@ -41,8 +41,12 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Container"; }
ControllerButtonStr* getControllerButtons() override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
MWGui::ItemView* getItemView() { return mItemView; }
ItemModel* getModel() { return mModel; }
private:
DragAndDrop* mDragAndDrop;
@ -68,8 +72,6 @@ namespace MWGui
bool onTakeItem(const ItemStack& item, int count);
void onReferenceUnavailable() override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}
#endif // CONTAINER_H

View file

@ -243,6 +243,17 @@ namespace MWGui
mDrowningBar->setVisible(visible);
}
void HUD::dropDraggedItem(float mouseX, float mouseY)
{
if (!mDragAndDrop->mIsOnDragAndDrop)
return;
MWBase::Environment::get().getWorld()->breakInvisibility(MWMechanics::getPlayer());
WorldItemModel drop(mouseX, mouseY);
mDragAndDrop->drop(&drop, nullptr);
}
void HUD::onWorldClicked(MyGUI::Widget* _sender)
{
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
@ -252,15 +263,12 @@ namespace MWGui
if (mDragAndDrop->mIsOnDragAndDrop)
{
// drop item into the gameworld
MWBase::Environment::get().getWorld()->breakInvisibility(MWMechanics::getPlayer());
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition();
float mouseX = cursorPosition.left / float(viewSize.width);
float mouseY = cursorPosition.top / float(viewSize.height);
WorldItemModel drop(mouseX, mouseY);
mDragAndDrop->drop(&drop, nullptr);
dropDraggedItem(mouseX, mouseY);
winMgr->changePointer("arrow");
}

View file

@ -61,6 +61,8 @@ namespace MWGui
void clear() override;
void dropDraggedItem(float mouseX, float mouseY);
private:
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;
MyGUI::Widget* mHealthFrame;

View file

@ -31,8 +31,11 @@
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "companionwindow.hpp"
#include "container.hpp"
#include "countdialog.hpp"
#include "draganddrop.hpp"
#include "hud.hpp"
#include "inventoryitemmodel.hpp"
#include "itemview.hpp"
#include "settings.hpp"
@ -127,6 +130,13 @@ namespace MWGui
setGuiMode(mGuiMode);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.b = "#{sBack}";
mControllerButtons.r1 = "Filter";
mControllerButtons.r3 = "#{sInfo}";
}
adjustPanes();
}
@ -302,7 +312,9 @@ namespace MWGui
}
}
if (count > 1 && !shift)
// Show a dialog to select a count of items, but not when using an item from the inventory
// in controller mode. In that case, we skip the dialog and just use one item immediately.
if (count > 1 && !shift && !(Settings::gui().mControllerMenus && mGuiMode == MWGui::GM_Inventory))
{
CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();
std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}";
@ -866,4 +878,147 @@ namespace MWGui
const MyGUI::IntSize viewport = getPreviewViewportSize();
return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1));
}
ControllerButtonStr* InventoryWindow::getControllerButtons()
{
switch (mGuiMode)
{
case MWGui::GM_Companion:
case MWGui::GM_Container:
mControllerButtons.a = "Put";
mControllerButtons.x = "#{sTakeAll}";
mControllerButtons.y = "";
mControllerButtons.r2 = "#{sContainer}";
break;
case MWGui::GM_Barter:
mControllerButtons.a = "#{sSell}";
mControllerButtons.x = "";
mControllerButtons.y = "";
mControllerButtons.r2 = "#{sBarter}";
break;
case MWGui::GM_Inventory:
default:
mControllerButtons.a = "#{sEquip}";
mControllerButtons.x = "#{sDrop}";
mControllerButtons.y = "#{sUnequip}";
mControllerButtons.r2 = "";
break;
}
return &mControllerButtons;
}
bool InventoryWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
else if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A);
// The following actions are done here, not in onItemSelectedFromSourceModel, because we
// want the mouse to work even in controller mode.
if (mGuiMode == MWGui::GM_Inventory && mDragAndDrop->mIsOnDragAndDrop)
{
// Drag and drop the item on the avatar to activate it.
onAvatarClicked(nullptr); // Equip or use
// Drop any remaining items back in inventory. This is needed when clicking on a
// stack of items; we only want to use the first item.
onBackgroundSelected();
}
else if (mGuiMode == MWGui::GM_Companion && mDragAndDrop->mIsOnDragAndDrop)
{
// Drag and drop the item on the companion's window.
MWGui::CompanionWindow* companionWindow =
(MWGui::CompanionWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0);
mDragAndDrop->drop(companionWindow->getModel(), companionWindow->getItemView());
}
else if (mGuiMode == MWGui::GM_Container && mDragAndDrop->mIsOnDragAndDrop)
{
// Drag and drop the item on the container window.
MWGui::ContainerWindow* containerWindow =
(MWGui::ContainerWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0);
mDragAndDrop->drop(containerWindow->getModel(), containerWindow->getItemView());
}
// GM_Barter is handled by onControllerButtonEvent. No other steps are necessary.
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
if (mGuiMode == MWGui::GM_Inventory)
{
// Drop the item into the gameworld
mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A);
if (mDragAndDrop->mIsOnDragAndDrop)
MWBase::Environment::get().getWindowManager()->getHud()->dropDraggedItem(0.5f, 0.5f);
}
else if (mGuiMode == MWGui::GM_Container)
{
// Take all. Pass the button press to the container window and let it do the
// logic of taking all.
MWGui::ContainerWindow* containerWindow =
(MWGui::ContainerWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0);
containerWindow->onControllerButtonEvent(arg);
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_Y)
{
if (mGuiMode == MWGui::GM_Inventory)
{
// Unequip an item.
mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A);
onBackgroundSelected(); // Drop on inventory background to unequip
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{
if (mFilterAll->getStateSelected())
onFilterChanged(mFilterMisc);
else if (mFilterWeapon->getStateSelected())
onFilterChanged(mFilterAll);
else if (mFilterApparel->getStateSelected())
onFilterChanged(mFilterWeapon);
else if (mFilterMagic->getStateSelected())
onFilterChanged(mFilterApparel);
else if (mFilterMisc->getStateSelected())
onFilterChanged(mFilterMagic);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
if (mFilterAll->getStateSelected())
onFilterChanged(mFilterWeapon);
else if (mFilterWeapon->getStateSelected())
onFilterChanged(mFilterApparel);
else if (mFilterApparel->getStateSelected())
onFilterChanged(mFilterMagic);
else if (mFilterMagic->getStateSelected())
onFilterChanged(mFilterMisc);
else if (mFilterMisc->getStateSelected())
onFilterChanged(mFilterAll);
}
else
{
mItemView->onControllerButtonEvent(arg.button);
}
return true;
}
void InventoryWindow::setActiveControllerWindow(bool active)
{
if (!Settings::gui().mControllerMenus)
return;
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory)
{
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48);
adjustPanes();
updatePreviewSize();
}
mItemView->setActiveControllerWindow(active);
WindowBase::setActiveControllerWindow(active);
}
}

View file

@ -67,8 +67,12 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Inventory"; }
ControllerButtonStr* getControllerButtons() override;
protected:
void onTitleDoubleClicked() override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
private:
DragAndDrop* mDragAndDrop;

View file

@ -9,6 +9,10 @@
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "itemmodel.hpp"
#include "itemwidget.hpp"
@ -75,8 +79,8 @@ namespace MWGui
if (Settings::gui().mControllerMenus)
{
if (mControllerFocus >= mItemCount)
mControllerFocus = mItemCount - 1;
mControllerTooltip = false;
mControllerFocus = std::clamp(mControllerFocus, 0, mItemCount - 1);
updateControllerFocus(-1, mControllerFocus);
}
@ -182,26 +186,49 @@ namespace MWGui
void ItemView::setActiveControllerWindow(bool active)
{
mControllerActiveWindow = active;
if (mControllerTooltip)
{
MWBase::Environment::get().getWindowManager()->setCursorActive(false);
mControllerTooltip = false;
}
if (active)
updateControllerFocus(-1, mControllerFocus);
else
updateControllerFocus(mControllerFocus, -1);
}
void ItemView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
void ItemView::onControllerButtonEvent(const unsigned char button)
{
if (!mItemCount)
return;
int prevFocus = mControllerFocus;
if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP && mControllerFocus % mRows != 0)
if (button == SDL_CONTROLLER_BUTTON_A)
{
// Select the focused item, if any.
if (mControllerFocus >= 0 && mControllerFocus < mItemCount)
{
MyGUI::Widget* dragArea = mScrollView->getChildAt(0);
onSelectedItem(dragArea->getChildAt(mControllerFocus));
}
}
else if (button == SDL_CONTROLLER_BUTTON_RIGHTSTICK)
{
// Toggle info tooltip
mControllerTooltip = !mControllerTooltip;
updateControllerFocus(-1, mControllerFocus);
}
else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP && mControllerFocus % mRows != 0)
mControllerFocus--;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN && mControllerFocus % mRows != mRows - 1)
else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN && mControllerFocus % mRows != mRows - 1)
mControllerFocus++;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows)
else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows)
mControllerFocus -= mRows;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mControllerFocus + mRows < mItemCount)
else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mControllerFocus + mRows < mItemCount)
mControllerFocus += mRows;
if (mControllerFocus < 0)
@ -213,25 +240,29 @@ namespace MWGui
updateControllerFocus(prevFocus, mControllerFocus);
}
void ItemView::updateControllerFocus(int _prevFocus, int _newFocus)
void ItemView::updateControllerFocus(int prevFocus, int newFocus)
{
if (!mItemCount)
return;
MyGUI::Widget* dragArea = mScrollView->getChildAt(0);
if (_prevFocus >= 0 && _prevFocus < mItemCount)
if (prevFocus >= 0 && prevFocus < mItemCount)
{
ItemWidget* prev = (ItemWidget *)dragArea->getChildAt(_prevFocus);
ItemWidget* prev = (ItemWidget *)dragArea->getChildAt(prevFocus);
if (prev)
prev->setControllerFocus(false);
}
if (_newFocus >= 0 && _newFocus < mItemCount)
if (mControllerActiveWindow && newFocus >= 0 && newFocus < mItemCount)
{
ItemWidget* focused = (ItemWidget *)dragArea->getChildAt(_newFocus);
ItemWidget* focused = (ItemWidget *)dragArea->getChildAt(newFocus);
if (focused)
{
focused->setControllerFocus(true);
if (mControllerTooltip)
MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused);
}
}
}
}

View file

@ -35,7 +35,7 @@ namespace MWGui
void setActiveControllerWindow(bool active);
int getControllerFocus() { return mControllerFocus; }
int getItemCount() { return mItemCount; }
void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg);
void onControllerButtonEvent(const unsigned char button);
private:
void initialiseOverride() override;
@ -55,6 +55,8 @@ namespace MWGui
int mItemCount = 0;
int mRows;
int mControllerFocus = 0;
bool mControllerTooltip;
bool mControllerActiveWindow;
void updateControllerFocus(int prevFocus, int newFocus);
};

View file

@ -12,6 +12,7 @@
#include <MyGUI_RotatingSkin.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_TextIterator.h>
#include <MyGUI_Window.h>
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/globalmap.hpp>
@ -832,6 +833,14 @@ namespace MWGui
mGlobalMap->setVisible(global);
mLocalMap->setVisible(!global);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.b = "#{sBack}";
mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}";
mControllerButtons.y = "#{sCenter}";
mControllerButtons.rStick = "#{sMove}";
}
}
void MapWindow::onNoteEditOk()
@ -1208,6 +1217,8 @@ namespace MWGui
mLocalMap->setVisible(!global);
mButton->setCaptionWithReplacing(global ? "#{sLocal}" : "#{sWorld}");
mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}";
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void MapWindow::onPinToggled()
@ -1368,6 +1379,49 @@ namespace MWGui
mGlobalMapRender->asyncWritePng();
}
bool MapWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
onWorldButtonClicked(mButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_Y)
centerView();
return true;
}
bool MapWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg)
{
int dx = arg.axis == SDL_CONTROLLER_AXIS_RIGHTX ? -10.0f * arg.value / 32767 : 0;
int dy = arg.axis == SDL_CONTROLLER_AXIS_RIGHTY ? -10.0f * arg.value / 32767 : 0;
if (dx == 0 && dy == 0)
return true;
else if (!Settings::map().mGlobal)
{
mNeedDoorMarkersUpdate = true;
mLocalMap->setViewOffset(
MyGUI::IntPoint(
mLocalMap->getViewOffset().left + dx, mLocalMap->getViewOffset().top + dy));
}
else
{
mGlobalMap->setViewOffset(
MyGUI::IntPoint(
mGlobalMap->getViewOffset().left + dx, mGlobalMap->getViewOffset().top + dy));
}
return true;
}
void MapWindow::setActiveControllerWindow(bool active)
{
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48);
WindowBase::setActiveControllerWindow(active);
}
// -------------------------------------------------------------------
EditNoteDialog::EditNoteDialog()

View file

@ -265,6 +265,11 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Map"; }
protected:
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override;
void setActiveControllerWindow(bool active) override;
private:
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);

View file

@ -9,6 +9,9 @@
#include <components/widgets/box.hpp>
#include <components/widgets/sharedstatebutton.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "tooltips.hpp"
namespace MWGui
@ -88,6 +91,9 @@ namespace MWGui
const int spellHeight = Settings::gui().mFontSize + 2;
mLines.clear();
mButtons.clear();
mGroupIndices.clear();
mControllerTooltip = false;
while (mScrollView->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));
@ -115,6 +121,7 @@ namespace MWGui
t->setCaption(spell.mName + captionSuffix);
t->setTextAlign(MyGUI::Align::Left);
adjustSpellWidget(spell, i, t);
mButtons.emplace_back(t);
if (!spell.mCostColumn.empty() && mShowCostColumn)
{
@ -256,6 +263,8 @@ namespace MWGui
}
else
mLines.emplace_back(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex);
mGroupIndices.push_back(mButtons.size());
}
void SpellView::setSize(const MyGUI::IntSize& _value)
@ -316,4 +325,87 @@ namespace MWGui
{
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
}
void SpellView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (mButtons.empty())
return;
int prevFocus = mControllerFocus;
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
// Select the focused item, if any.
if (mControllerFocus >= 0 && mControllerFocus < mButtons.size())
onSpellSelected(mButtons.at(mControllerFocus));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK)
{
// Toggle info tooltip
mControllerTooltip = !mControllerTooltip;
if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons.at(mControllerFocus));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
mControllerFocus--;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
mControllerFocus++;
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{
// Jump to first item in previous group
int prevGroupIndex = 0;
for (int groupIndex : mGroupIndices)
{
if (groupIndex >= mControllerFocus)
break;
else
prevGroupIndex = groupIndex;
}
mControllerFocus = prevGroupIndex;
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
// Jump to first item in next group
for (int groupIndex : mGroupIndices)
{
if (groupIndex > mControllerFocus)
{
mControllerFocus = groupIndex;
break;
}
}
}
if (mControllerFocus < 0)
mControllerFocus = mButtons.size() - 1;
else if (mControllerFocus >= mButtons.size())
mControllerFocus = 0;
if (prevFocus != mControllerFocus)
updateControllerFocus(prevFocus, mControllerFocus);
}
void SpellView::updateControllerFocus(int prevFocus, int newFocus)
{
if (mButtons.empty())
return;
if (prevFocus >= 0 && prevFocus < mButtons.size())
{
Gui::SharedStateButton* prev = mButtons.at(prevFocus);
if (prev)
prev->onMouseLostFocus(nullptr);
}
if (newFocus >= 0 && newFocus < mButtons.size())
{
Gui::SharedStateButton* focused = mButtons.at(newFocus);
if (focused)
{
focused->onMouseSetFocus(nullptr);
if (mControllerTooltip)
MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused);
}
}
}
}

View file

@ -5,6 +5,9 @@
#include <tuple>
#include <MyGUI_Widget.h>
#include <SDL.h>
#include <components/widgets/sharedstatebutton.hpp>
#include "spellmodel.hpp"
@ -54,6 +57,8 @@ namespace MWGui
void resetScrollbars();
void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg);
private:
MyGUI::ScrollView* mScrollView;
@ -89,6 +94,15 @@ namespace MWGui
void addGroup(const std::string& label1, const std::string& label2);
void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget);
/// Keep a list of buttons for controller navigation
std::vector<Gui::SharedStateButton *> mButtons;
/// Keep a list of group offsets for controller navigation
std::vector<int> mGroupIndices;
int mControllerFocus;
bool mControllerTooltip;
void updateControllerFocus(int prevFocus, int newFocus);
void onSpellSelected(MyGUI::Widget* _sender);
void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel);

View file

@ -2,6 +2,8 @@
#include <MyGUI_EditBox.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_Window.h>
#include <components/esm3/loadbsgn.hpp>
#include <components/esm3/loadrace.hpp>
@ -55,6 +57,13 @@ namespace MWGui
// Adjust the spell filtering widget size because of MyGUI limitations.
int filterWidth = mSpellView->getSize().width - deleteButton->getSize().width - 3;
mFilterEdit->setSize(filterWidth, mFilterEdit->getSize().height);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.a = "#{sSelect}";
mControllerButtons.b = "#{sBack}";
mControllerButtons.r3 = "#{sInfo}";
}
}
void SpellWindow::onPinToggled()
@ -259,4 +268,23 @@ namespace MWGui
else
onSpellSelected(spell.mId);
}
bool SpellWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
else
mSpellView->onControllerButtonEvent(arg);
return true;
}
void SpellWindow::setActiveControllerWindow(bool active)
{
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48);
WindowBase::setActiveControllerWindow(active);
}
}

View file

@ -41,6 +41,8 @@ namespace MWGui
void onPinToggled() override;
void onTitleDoubleClicked() override;
void onOpen() override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
SpellView* mSpellView;
std::unique_ptr<SpellIcons> mSpellIcons;

View file

@ -6,6 +6,7 @@
#include <MyGUI_InputManager.h>
#include <MyGUI_LanguageManager.h>
#include <MyGUI_ProgressBar.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_TextIterator.h>
#include <MyGUI_Window.h>
@ -80,6 +81,11 @@ namespace MWGui
MyGUI::Window* t = mMainWidget->castType<MyGUI::Window>();
t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.b = "#{sBack}";
}
onWindowResize(t);
}
@ -723,4 +729,23 @@ namespace MWGui
else if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats);
}
bool StatsWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
return true;
}
void StatsWindow::setActiveControllerWindow(bool active)
{
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48);
onWindowResize(window);
WindowBase::setActiveControllerWindow(active);
}
}

View file

@ -47,6 +47,10 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Stats"; }
protected:
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
private:
void addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);

View file

@ -896,23 +896,13 @@ namespace MWGui
return state.mWindows[activeIndex];
}
else
{
// return pinned windows if visible
// REMOVEME
Log(Debug::Error) << "getTopWindow: " << mGuiModeStates[GM_Inventory].mWindows.size() << " pinned windows";
for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows)
if (window->isVisible())
return window;
else
Log(Debug::Error) << "-- Skipping hidden window " << window;
}
return nullptr;
}
void WindowManager::cycleActiveControllerWindow(bool next)
{
if (mGuiModes.empty())
if (!Settings::gui().mControllerMenus || mGuiModes.empty())
return;
GuiMode mode = mGuiModes.back();
@ -1380,13 +1370,6 @@ 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 (...)
{
@ -1398,6 +1381,13 @@ namespace MWGui
updateVisible();
MWBase::Environment::get().getLuaManager()->uiModeChanged(arg);
if (Settings::gui().mControllerMenus)
{
// Activate first visible window. This needs to be called after updateVisible.
mActiveControllerWindows[mode] = std::max(mActiveControllerWindows[mode] - 1, -1);
cycleActiveControllerWindow(true);
}
}
void WindowManager::setCullMask(uint32_t mask)
@ -1452,6 +1442,15 @@ namespace MWGui
// To make sure that console window get focus again
if (mConsole && mConsole->isVisible())
mConsole->onOpen();
if (Settings::gui().mControllerMenus && !mGuiModes.empty())
{
// Re-apply any controller-specific window changes.
const GuiMode mode = mGuiModes.back();
int winCount = mGuiModeStates[mode].mWindows.size();
for (int i = 0; i < winCount; i++)
mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == mActiveControllerWindows[mode]);
}
}
void WindowManager::removeGuiMode(GuiMode mode)
@ -1577,6 +1576,10 @@ namespace MWGui
mConsole->executeFile(path);
}
std::vector<MWGui::WindowBase*> WindowManager::getGuiModeWindows(GuiMode mode)
{
return mGuiModeStates[mode].mWindows;
}
MWGui::InventoryWindow* WindowManager::getInventoryWindow()
{
return mInventoryWindow;
@ -1589,6 +1592,10 @@ namespace MWGui
{
return mConfirmationDialog;
}
MWGui::HUD* WindowManager::getHud()
{
return mHud;
}
MWGui::TradeWindow* WindowManager::getTradeWindow()
{
return mTradeWindow;
@ -2119,20 +2126,22 @@ namespace MWGui
void WindowManager::updatePinnedWindows()
{
mInventoryWindow->setPinned(Settings::windows().mInventoryPin);
if (Settings::windows().mInventoryHidden)
mShown = (GuiWindow)(mShown ^ GW_Inventory);
mMap->setPinned(Settings::windows().mMapPin);
if (Settings::windows().mMapHidden)
mShown = (GuiWindow)(mShown ^ GW_Map);
mSpellWindow->setPinned(Settings::windows().mSpellsPin);
if (Settings::windows().mSpellsHidden)
mShown = (GuiWindow)(mShown ^ GW_Magic);
mStatsWindow->setPinned(Settings::windows().mStatsPin);
if (Settings::windows().mStatsHidden)
mShown = (GuiWindow)(mShown ^ GW_Stats);
// Hide hidden inventory windows, but not in controller mode.
if (!Settings::gui().mControllerMenus)
{
if (Settings::windows().mInventoryHidden)
mShown = (GuiWindow)(mShown ^ GW_Inventory);
if (Settings::windows().mMapHidden)
mShown = (GuiWindow)(mShown ^ GW_Map);
if (Settings::windows().mSpellsHidden)
mShown = (GuiWindow)(mShown ^ GW_Magic);
if (Settings::windows().mStatsHidden)
mShown = (GuiWindow)(mShown ^ GW_Stats);
}
}
void WindowManager::pinWindow(GuiWindow window)

View file

@ -185,7 +185,9 @@ namespace MWGui
MWGui::CountDialog* getCountDialog() override;
MWGui::ConfirmationDialog* getConfirmationDialog() override;
MWGui::TradeWindow* getTradeWindow() override;
MWGui::HUD* getHud() override;
MWGui::PostProcessorHud* getPostProcessorHud() override;
std::vector<MWGui::WindowBase*> getGuiModeWindows(GuiMode mode) override;
/// Make the player use an item, while updating GUI state accordingly
void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions = false) override;

View file

@ -189,6 +189,13 @@ namespace MWInput
return mMouseManager->getMouseMoveY();
}
void InputManager::warpMouseToWidget(MyGUI::Widget* widget)
{
mMouseManager->warpMouseToWidget(widget);
mMouseManager->injectMouseMove(1, 0, 0);
MWBase::Environment::get().getWindowManager()->setCursorActive(true);
}
const std::initializer_list<int>& InputManager::getActionKeySorting()
{
return mBindingsManager->getActionKeySorting();

View file

@ -81,6 +81,7 @@ namespace MWInput
float getControllerAxisValue(SDL_GameControllerAxis axis) const override;
int getMouseMoveX() const override;
int getMouseMoveY() const override;
void warpMouseToWidget(MyGUI::Widget* widget) override;
int getNumActions() override { return A_Last; }
const std::initializer_list<int>& getActionKeySorting() override;

View file

@ -21,13 +21,14 @@ namespace Gui
public:
SharedStateButton();
void onMouseSetFocus(MyGUI::Widget* _old) override;
void onMouseLostFocus(MyGUI::Widget* _new) override;
protected:
void updateButtonState();
void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override;
void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) override;
void onMouseSetFocus(MyGUI::Widget* _old) override;
void onMouseLostFocus(MyGUI::Widget* _new) override;
void baseUpdateEnable() override;
void shutdownOverride() override;