1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-07 17:34:33 +00:00

Merge branch openmw:master into master

This commit is contained in:
Igilq 2025-08-06 08:56:12 +00:00
commit cf5eb9cd63
178 changed files with 5459 additions and 169 deletions

View file

@ -75,6 +75,7 @@
Feature #8580: Sort characters in the save loading menu
Feature #8597: Lua: Add more built-in event handlers
Feature #8629: Expose path grid data to Lua
Feature #8654: Allow lua world.createRecord to create NPC records
0.49.0
------

View file

@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 50)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 87)
set(OPENMW_LUA_API_REVISION 90)
set(OPENMW_POSTPROCESSING_API_REVISION 3)
set(OPENMW_VERSION_COMMITHASH "")

View file

@ -303,6 +303,9 @@ bool Launcher::SettingsPage::loadSettings()
loadSettingBool(Settings::gui().mColorTopicEnable, *changeDialogTopicsCheckBox);
showOwnedComboBox->setCurrentIndex(Settings::game().mShowOwned);
loadSettingBool(Settings::gui().mStretchMenuBackground, *stretchBackgroundCheckBox);
connect(controllerMenusCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotControllerMenusToggled);
loadSettingBool(Settings::gui().mControllerMenus, *controllerMenusCheckBox);
loadSettingBool(Settings::gui().mControllerTooltips, *controllerMenuTooltipsCheckBox);
loadSettingBool(Settings::map().mAllowZooming, *useZoomOnMapCheckBox);
loadSettingBool(Settings::game().mGraphicHerbalism, *graphicHerbalismCheckBox);
scalingSpinBox->setValue(Settings::gui().mScalingFactor);
@ -496,6 +499,8 @@ void Launcher::SettingsPage::saveSettings()
saveSettingBool(*changeDialogTopicsCheckBox, Settings::gui().mColorTopicEnable);
saveSettingInt(*showOwnedComboBox, Settings::game().mShowOwned);
saveSettingBool(*stretchBackgroundCheckBox, Settings::gui().mStretchMenuBackground);
saveSettingBool(*controllerMenusCheckBox, Settings::gui().mControllerMenus);
saveSettingBool(*controllerMenuTooltipsCheckBox, Settings::gui().mControllerTooltips);
saveSettingBool(*useZoomOnMapCheckBox, Settings::map().mAllowZooming);
saveSettingBool(*graphicHerbalismCheckBox, Settings::game().mGraphicHerbalism);
Settings::gui().mScalingFactor.set(scalingSpinBox->value());
@ -554,6 +559,11 @@ void Launcher::SettingsPage::slotAnimSourcesToggled(bool checked)
}
}
void Launcher::SettingsPage::slotControllerMenusToggled(bool checked)
{
controllerMenuTooltipsCheckBox->setEnabled(checked);
}
void Launcher::SettingsPage::slotPostProcessToggled(bool checked)
{
postprocessTransparentPostpassCheckBox->setEnabled(checked);

View file

@ -34,6 +34,7 @@ namespace Launcher
void slotSkyBlendingToggled(bool checked);
void slotShadowDistLimitToggled(bool checked);
void slotDistantLandToggled(bool checked);
void slotControllerMenusToggled(bool checked);
private:
Config::GameSettings& mGameSettings;

View file

@ -1433,6 +1433,29 @@
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="controllerMenusCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make it easier to use game menus with a controller.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Controller Menus</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="controllerMenuTooltipsCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When using controller menus, make tooltips visible by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show Controller Tooltips By Default</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="showOwnedLabel">
<property name="text">

View file

@ -291,8 +291,7 @@ int CSMDoc::WriteCellCollectionStage::setup()
return mDocument.getData().getCells().getSize();
}
void CSMDoc::WriteCellCollectionStage::writeReferences(
const std::deque<int>& references, bool interior, unsigned int& newRefNum)
void CSMDoc::WriteCellCollectionStage::writeReferences(const std::deque<int>& references, bool interior)
{
ESM::ESMWriter& writer = mState.getWriter();
@ -304,6 +303,8 @@ void CSMDoc::WriteCellCollectionStage::writeReferences(
{
CSMWorld::CellRef refRecord = ref.get();
const bool isLocal = refRecord.mRefNum.mContentFile == -1;
// -1 is the current file, saved indices are 1-based
refRecord.mRefNum.mContentFile++;
@ -316,12 +317,7 @@ void CSMDoc::WriteCellCollectionStage::writeReferences(
}
ESM::RefId streamId = ESM::RefId::stringRefId(stream.str());
if (refRecord.mNew || refRecord.mRefNum.mIndex == 0
|| (!interior && ref.mState == CSMWorld::RecordBase::State_ModifiedOnly && refRecord.mCell != streamId))
{
refRecord.mRefNum.mIndex = newRefNum++;
}
else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != streamId
if (!isLocal && (refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != streamId
&& !interior)
{
// An empty mOriginalCell is meant to indicate that it is the same as
@ -362,9 +358,6 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages)
CSMWorld::Cell cellRecord = cell.get();
const bool interior = !cellRecord.mId.startsWith("#");
// count new references and adjust RefNumCount accordingsly
unsigned int newRefNum = cellRecord.mRefNumCounter;
if (references != nullptr)
{
for (std::deque<int>::const_iterator iter(references->begin()); iter != references->end(); ++iter)
@ -390,9 +383,6 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages)
ESM::RefId::stringRefId(CSMWorld::CellCoordinates(refRecord.getCellIndex()).getId(""))
!= refRecord.mCell))
++cellRecord.mRefNumCounter;
if (refRecord.mRefNum.mIndex >= newRefNum)
newRefNum = refRecord.mRefNum.mIndex + 1;
}
}
@ -415,9 +405,9 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages)
// write references
if (references != nullptr)
{
writeReferences(persistentRefs, interior, newRefNum);
writeReferences(persistentRefs, interior);
cellRecord.saveTempMarker(writer, static_cast<int>(references->size()) - persistentRefs.size());
writeReferences(tempRefs, interior, newRefNum);
writeReferences(tempRefs, interior);
}
writer.endRecord(cellRecord.sRecordId);

View file

@ -167,7 +167,7 @@ namespace CSMDoc
Document& mDocument;
SavingState& mState;
void writeReferences(const std::deque<int>& references, bool interior, unsigned int& newRefNum);
void writeReferences(const std::deque<int>& references, bool interior);
public:
WriteCellCollectionStage(Document& document, SavingState& state);

View file

@ -273,15 +273,6 @@ namespace CSMWorld
copy->mState = RecordBase::State_ModifiedOnly;
setRecordId(destination, copy->get());
if constexpr (std::is_same_v<ESXRecordT, CSMWorld::CellRef>)
{
if (type == UniversalId::Type_Reference)
{
CSMWorld::CellRef* ptr = (CSMWorld::CellRef*)&copy->mModified;
ptr->mRefNum.mIndex = 0;
}
}
if constexpr (std::is_same_v<ESXRecordT, ESM::Dialogue>)
{
copy->mModified.mStringId = copy->mModified.mId.getRefIdString();

View file

@ -175,6 +175,9 @@ void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool b
ref.mIdNum = mNextId; // FIXME: fragile
ref.mId = ESM::RefId::stringRefId(getNewId());
if (!base && ref.mRefNum.mIndex > mHighestUsedRefNum)
mHighestUsedRefNum = ref.mRefNum.mIndex;
cache.emplace(ref.mRefNum, ref.mIdNum);
auto record = std::make_unique<Record<CellRef>>();
@ -222,6 +225,11 @@ std::string CSMWorld::RefCollection::getNewId()
return "ref#" + std::to_string(mNextId++);
}
uint32_t CSMWorld::RefCollection::getNextRefNum()
{
return ++mHighestUsedRefNum;
}
unsigned int CSMWorld::RefCollection::extractIdNum(std::string_view id) const
{
std::string::size_type separator = id.find_last_of('#');
@ -283,6 +291,7 @@ void CSMWorld::RefCollection::appendBlankRecord(const ESM::RefId& id, UniversalI
record->get().mId = id;
record->get().mIdNum = extractIdNum(id.getRefIdString());
record->get().mRefNum.mIndex = getNextRefNum();
Collection<CellRef>::appendRecord(std::move(record));
}
@ -298,15 +307,13 @@ void CSMWorld::RefCollection::cloneRecord(
copy->get().mId = destination;
copy->get().mIdNum = extractIdNum(destination.getRefIdString());
copy->get().mRefNum.mIndex = getNextRefNum();
if (copy->get().mRefNum.hasContentFile())
{
mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(copy.get())->get().mIdNum, index));
copy->get().mRefNum.mContentFile = -1;
copy->get().mRefNum.mIndex = index;
}
else
copy->get().mRefNum.mIndex = copy->get().mIdNum;
insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord()
}

View file

@ -1,6 +1,7 @@
#ifndef CSM_WOLRD_REFCOLLECTION_H
#define CSM_WOLRD_REFCOLLECTION_H
#include <cstdint>
#include <map>
#include <memory>
#include <string>
@ -40,9 +41,12 @@ namespace CSMWorld
std::map<unsigned int, int> mRefIndex; // CellRef index keyed by CSMWorld::CellRef::mIdNum
int mNextId;
uint32_t mHighestUsedRefNum = 0;
unsigned int extractIdNum(std::string_view id) const;
uint32_t getNextRefNum();
int getIntIndex(unsigned int id) const;
int searchId(unsigned int id) const;

View file

@ -1277,9 +1277,8 @@ void CSVRender::InstanceMode::cloneSelectedInstances()
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(tag.get()))
{
macro.push(new CSMWorld::CloneCommand(referencesTable, objectTag->mObject->getReferenceId(),
"ref#" + std::to_string(referencesTable.rowCount()), CSMWorld::UniversalId::Type_Reference));
document.getData().getReferences().getNewId(), CSMWorld::UniversalId::Type_Reference));
}
// getWorldspaceWidget().clearSelection(Mask_Reference);
}
void CSVRender::InstanceMode::dropInstance(CSVRender::Object* object, float dropHeight)

View file

@ -44,7 +44,7 @@ add_openmw_dir (mwgui
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
postprocessorhud settings worlditemmodel itemtransfer
postprocessorhud settings worlditemmodel itemtransfer controllerbuttonsoverlay inventorytabsoverlay
)
add_openmw_dir (mwdialogue

View file

@ -19,6 +19,11 @@ namespace ESM
class ESMWriter;
}
namespace MyGUI
{
class Widget;
}
namespace MWBase
{
/// \brief Interface for input manager (implemented in MWInput)
@ -61,6 +66,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;
@ -77,6 +83,8 @@ namespace MWBase
/// @return true if joystick, false otherwise
virtual bool joystickLastUsed() = 0;
virtual void setJoystickLastUsed(bool enabled) = 0;
virtual std::string getControllerButtonIcon(int button) = 0;
virtual std::string getControllerAxisIcon(int axis) = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0;

View file

@ -78,6 +78,8 @@ namespace MWGui
class MessageBox;
class PostProcessorHud;
class SettingsWindow;
class HUD;
class WindowBase;
enum ShowInDialogueMode
{
@ -157,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;
@ -381,6 +385,17 @@ namespace MWBase
/// Same as viewer->getCamera()->getCullMask(), provided for consistency.
virtual uint32_t getCullMask() = 0;
/// Return the window that should receive controller events
virtual MWGui::WindowBase* getActiveControllerWindow() = 0;
/// Return the available height for menus accounting for visible controller overlays
virtual int getControllerMenuHeight() = 0;
/// Cycle to the next window to receive controller events
virtual void cycleActiveControllerWindow(bool next) = 0;
virtual void setActiveControllerWindow(MWGui::GuiMode mode, int activeIndex) = 0;
virtual bool getControllerTooltip() const = 0;
virtual void setControllerTooltip(bool enabled) = 0;
virtual void updateControllerButtonsOverlay() = 0;
// Used in Lua bindings
virtual const std::vector<MWGui::GuiMode>& getGuiModeStack() const = 0;
virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0;

View file

@ -6,6 +6,7 @@
#include <MyGUI_ControllerRepeatClick.h>
#include <MyGUI_EditBox.h>
#include <MyGUI_Gui.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_UString.h>
#include <components/esm3/loadappa.hpp>
@ -91,6 +92,15 @@ namespace MWGui
mFilterValue->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);
mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mX = "#{sCreate}";
mControllerButtons.mY = "#{sMagicEffects}";
mControllerButtons.mR3 = "#{sInfo}";
}
center();
}
@ -165,7 +175,12 @@ namespace MWGui
std::string_view ingredient = wm->getGameSettingString("sIngredients", "Ingredients");
if (mFilterType->getCaption() == ingredient)
mCurrentFilter = FilterType::ByName;
{
if (Settings::gui().mControllerMenus)
switchFilterType(mFilterType);
else
mCurrentFilter = FilterType::ByName;
}
else
mCurrentFilter = FilterType::ByEffect;
updateFilters();
@ -291,6 +306,9 @@ namespace MWGui
initFilter();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
if (Settings::gui().mControllerMenus)
mItemView->setActiveControllerWindow(true);
}
void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)
@ -528,4 +546,86 @@ namespace MWGui
if (currentCount > 1)
mBrewCountEdit->setValue(currentCount - 1);
}
void AlchemyWindow::filterListButtonHandler(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_Y)
{
// Select the highlighted entry in the combo box and close it. List is closed by focusing on another
// widget.
size_t index = mFilterValue->getIndexSelected();
mFilterValue->setIndexSelected(index);
onFilterChanged(mFilterValue, index);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
// Close the list without selecting anything. List is closed by focusing on another widget.
mFilterValue->clearIndexSelected();
onFilterEdited(mFilterValue);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
}
bool AlchemyWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
bool isFilterListOpen
= focus != nullptr && focus->getParent() != nullptr && focus->getParent()->getParent() == mFilterValue;
if (isFilterListOpen)
{
// When the filter list combo box is open, send all inputs to it.
filterListButtonHandler(arg);
return true;
}
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
// Remove active ingredients or close the window, starting with right-most slot.
for (int i = mIngredients.size() - 1; i >= 0; --i)
{
if (mIngredients[i]->isUserString("ToolTipType"))
{
onIngredientSelected(mIngredients[i]);
return true;
}
}
// If the ingredients list is empty, B closes the menu.
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
onCreateButtonClicked(mCreateButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_Y && mFilterValue->getItemCount() > 0)
{
// Magical effects/ingredients filter
if (mFilterValue->getIndexSelected() != MyGUI::ITEM_NONE)
{
// Clear the active filter
mFilterValue->clearIndexSelected();
onFilterEdited(mFilterValue);
}
else
{
// Open the combo box to choose the a filter
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mFilterValue);
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
}
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
onDecreaseButtonTriggered();
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
onIncreaseButtonTriggered();
else
mItemView->onControllerButton(arg.button);
return true;
}
}

View file

@ -99,6 +99,9 @@ namespace MWGui
std::vector<ItemWidget*> mApparatus;
std::vector<ItemWidget*> mIngredients;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void filterListButtonHandler(const SDL_ControllerButtonEvent& arg);
};
}

View file

@ -50,15 +50,20 @@ namespace MWGui
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept);
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
getWidget(mBackButton, "BackButton");
mBackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->setCaption(
getWidget(mOkButton, "OKButton");
mOkButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {})));
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mLStick = "#{sMouse}";
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
}
updateBirths();
updateSpells();
@ -70,8 +75,17 @@ namespace MWGui
getWidget(okButton, "OKButton");
if (shown)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {})));
mControllerButtons.mX = "#{sNext}";
}
else if (Settings::gui().mControllerMenus)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {})));
mControllerButtons.mX = "#{sDone}";
}
else
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {})));
@ -271,4 +285,30 @@ namespace MWGui
mSpellArea->setVisibleVScroll(true);
mSpellArea->setViewOffset(MyGUI::IntPoint(0, 0));
}
bool BirthDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onBackClicked(mBackButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkClicked(mOkButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mBirthList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mBirthList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
}
return true;
}
}

View file

@ -53,8 +53,12 @@ namespace MWGui
MyGUI::ScrollView* mSpellArea;
MyGUI::ImageBox* mBirthImage;
std::vector<MyGUI::Widget*> mSpellItems;
MyGUI::Button* mBackButton;
MyGUI::Button* mOkButton;
ESM::RefId mCurrentBirthId;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}
#endif

View file

@ -105,6 +105,18 @@ namespace MWGui
Styles mStyles;
MyGUI::IntRect mRect;
void setColour(size_t section, size_t line, size_t run, const MyGUI::Colour& colour) const override
{
if (section >= mSections.size())
return;
if (line >= mSections[section].mLines.size())
return;
if (run >= mSections[section].mLines[line].mRuns.size())
return;
mSections[section].mLines[line].mRuns[run].mStyle->mNormalColour = colour;
}
virtual ~TypesetBookImpl() {}
Range addContent(const BookTypesetter::Utf8Span& text)
@ -1288,6 +1300,12 @@ namespace MWGui
void unadviseLinkClicked() override { mPageDisplay->mLinkClicked = std::function<void(InteractiveId)>(); }
void setFocusItem(BookTypesetter::Style* itemStyle) override
{
mPageDisplay->mFocusItem = static_cast<TypesetBookImpl::StyleImpl*>(itemStyle);
mPageDisplay->dirtyFocusItem();
}
protected:
void initialiseOverride() override
{

View file

@ -31,6 +31,9 @@ namespace MWGui
/// text combined prior to pagination.
virtual std::pair<unsigned int, unsigned int> getSize() const = 0;
/// Used to highlight journal indices
virtual void setColour(size_t section, size_t line, size_t run, const MyGUI::Colour& colour) const = 0;
virtual ~TypesetBook() = default;
};
@ -164,6 +167,8 @@ namespace MWGui
/// Register the widget and associated sub-widget with MyGUI. Should be
/// called once near the beginning of the program.
static void registerMyGUIComponents();
virtual void setFocusItem(BookTypesetter::Style* itemStyle) = 0;
};
}

View file

@ -66,6 +66,10 @@ namespace MWGui
MyGUI::IntCoord(0, 0, (64 - 7) * scale, mNextPageButton->getSize().height * scale));
}
mControllerButtons.mL1 = "#{sPrev}";
mControllerButtons.mR1 = "#{sNext}";
mControllerButtons.mB = "#{Interface:Close}";
center();
}
@ -218,4 +222,26 @@ namespace MWGui
}
}
ControllerButtons* BookWindow::getControllerButtons()
{
mControllerButtons.mA = mTakeButton->getVisible() ? "#{sTake}" : "";
return &mControllerButtons;
}
bool BookWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mTakeButton->getVisible())
onTakeButtonClicked(mTakeButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCloseButtonClicked(mCloseButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
prevPage();
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
nextPage();
return true;
}
}

View file

@ -18,8 +18,10 @@ namespace MWGui
void setInventoryAllowed(bool allowed);
void onResChange(int, int) override { center(); }
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
std::string_view getWindowIdForLua() const override { return "Book"; }
ControllerButtons* getControllerButtons() override;
protected:
void onNextPageButtonClicked(MyGUI::Widget* sender);

View file

@ -18,6 +18,7 @@
#include <components/esm3/loadnpc.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/settings/values.hpp>
#include <components/vfs/manager.hpp>
#include "tooltips.hpp"
@ -46,15 +47,21 @@ namespace MWGui
getWidget(mClassImage, "ClassImage");
getWidget(mClassName, "ClassName");
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
getWidget(mBackButton, "BackButton");
mBackButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}");
mBackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
getWidget(mOkButton, "OKButton");
mOkButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
if (Settings::gui().mControllerMenus)
{
mOkButton->setStateSelected(true);
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
}
center();
}
@ -71,6 +78,30 @@ namespace MWGui
center();
}
bool GenerateClassResultDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mOkButtonFocus)
onOkClicked(mOkButton);
else
onBackClicked(mBackButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onBackClicked(mBackButton);
}
else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus)
|| (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus))
{
mOkButtonFocus = !mOkButtonFocus;
mOkButton->setStateSelected(mOkButtonFocus);
mBackButton->setStateSelected(!mOkButtonFocus);
}
return true;
}
// widget controls
void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender)
@ -110,13 +141,18 @@ namespace MWGui
getWidget(mClassImage, "ClassImage");
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked);
getWidget(mBackButton, "BackButton");
mBackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked);
getWidget(mOkButton, "OKButton");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mLStick = "#{sMouse}";
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
}
updateClasses();
updateStats();
@ -128,8 +164,17 @@ namespace MWGui
getWidget(okButton, "OKButton");
if (shown)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {})));
mControllerButtons.mX = "#{sNext}";
}
else if (Settings::gui().mControllerMenus)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {})));
mControllerButtons.mX = "#{sDone}";
}
else
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {})));
@ -278,6 +323,32 @@ namespace MWGui
setClassImage(mClassImage, mCurrentClassId);
}
bool PickClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onBackClicked(mBackButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkClicked(mOkButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mClassList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mClassList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
}
return true;
}
/* InfoBoxDialog */
void InfoBoxDialog::fitToText(MyGUI::TextBox* widget)
@ -312,6 +383,7 @@ namespace MWGui
InfoBoxDialog::InfoBoxDialog()
: WindowModal("openmw_infobox.layout")
, mControllerFocus(0)
{
getWidget(mTextBox, "TextBox");
getWidget(mText, "Text");
@ -319,6 +391,9 @@ namespace MWGui
getWidget(mButtonBar, "ButtonBar");
center();
mDisableGamepadCursor = Settings::gui().mControllerMenus;
mControllerButtons.mA = "#{sSelect}";
}
void InfoBoxDialog::setText(const std::string& str)
@ -353,6 +428,13 @@ namespace MWGui
fitToText(button);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked);
coord.top += button->getHeight();
if (Settings::gui().mControllerMenus && buttons.size() > 1 && this->mButtons.empty())
{
// First button is selected by default
button->setStateSelected(true);
}
this->mButtons.push_back(button);
}
}
@ -382,6 +464,44 @@ namespace MWGui
}
}
bool InfoBoxDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mButtons.size())
onButtonClicked(mButtons[mControllerFocus]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
if (mButtons.size() == 1)
onButtonClicked(mButtons[0]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mButtons.size() <= 1)
return true;
if (mButtons.size() == 2 && mControllerFocus == 0)
return true;
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mButtons.size() <= 1)
return true;
if (mButtons.size() == 2 && mControllerFocus == 1)
return true;
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
return true;
}
/* ClassChoiceDialog */
ClassChoiceDialog::ClassChoiceDialog()
@ -405,6 +525,7 @@ namespace MWGui
: WindowModal("openmw_chargen_create_class.layout")
, mAffectedAttribute(nullptr)
, mAffectedSkill(nullptr)
, mControllerFocus(2)
{
// Centre dialog
center();
@ -450,14 +571,25 @@ namespace MWGui
MyGUI::Button* descriptionButton;
getWidget(descriptionButton, "DescriptionButton");
descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked);
mButtons.push_back(descriptionButton);
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked);
mButtons.push_back(backButton);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked);
mButtons.push_back(okButton);
if (Settings::gui().mControllerMenus)
{
okButton->setStateSelected(true);
mControllerButtons.mLStick = "#{sMouse}";
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
}
// Set default skills, attributes
@ -545,13 +677,56 @@ namespace MWGui
getWidget(okButton, "OKButton");
if (shown)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {})));
mControllerButtons.mX = "#{sNext}";
}
else if (Settings::gui().mControllerMenus)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {})));
mControllerButtons.mX = "#{sDone}";
}
else
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {})));
}
bool CreateClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus == 0)
onDescriptionClicked(mButtons[0]);
else if (mControllerFocus == 1)
onBackClicked(mButtons[1]);
else
onOkClicked(mButtons[2]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onBackClicked(mButtons[1]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkClicked(mButtons[2]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
{
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
return true;
}
// widget controls
void CreateClassDialog::onDialogCancel()
@ -708,6 +883,9 @@ namespace MWGui
MyGUI::Button* cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
SelectSpecializationDialog::~SelectSpecializationDialog() {}
@ -739,6 +917,16 @@ namespace MWGui
return true;
}
bool SelectSpecializationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelClicked(nullptr);
return true;
}
return false;
}
/* SelectAttributeDialog */
SelectAttributeDialog::SelectAttributeDialog()
@ -760,6 +948,7 @@ namespace MWGui
widget->setAttributeId(attribute.mId);
widget->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked);
ToolTips::createAttributeToolTip(widget, attribute.mId);
mAttributeButtons.emplace_back(widget);
}
attributes->setVisibleVScroll(false);
@ -770,6 +959,16 @@ namespace MWGui
MyGUI::Button* cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked);
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
if (mAttributeButtons.size() > 0)
mAttributeButtons[0]->setStateSelected(true);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
// widget controls
@ -791,6 +990,33 @@ namespace MWGui
return true;
}
bool SelectAttributeDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mAttributeButtons.size())
onAttributeClicked(mAttributeButtons[mControllerFocus]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelClicked(nullptr);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
mAttributeButtons[mControllerFocus]->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus - 1, mAttributeButtons.size());
mAttributeButtons[mControllerFocus]->setStateSelected(true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
mAttributeButtons[mControllerFocus]->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus + 1, mAttributeButtons.size());
mAttributeButtons[mControllerFocus]->setStateSelected(true);
}
return true;
}
/* SelectSkillDialog */
SelectSkillDialog::SelectSkillDialog()
@ -820,6 +1046,8 @@ namespace MWGui
skillWidget->setSkillId(skill.mId);
skillWidget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked);
ToolTips::createSkillToolTip(skillWidget, skill.mId);
mSkillButtons.emplace_back(skillWidget);
mNumSkillsPerSpecialization[skill.mData.mSpecialization]++;
}
for (const auto& [widget, coord] : specializations)
{
@ -832,6 +1060,16 @@ namespace MWGui
MyGUI::Button* cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked);
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
if (mSkillButtons.size() > 0)
mSkillButtons[0]->setStateSelected(true);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
SelectSkillDialog::~SelectSkillDialog() {}
@ -855,6 +1093,75 @@ namespace MWGui
return true;
}
bool SelectSkillDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mSkillButtons.size())
onSkillClicked(mSkillButtons[mControllerFocus]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelClicked(nullptr);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
mSkillButtons[mControllerFocus]->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus - 1, mSkillButtons.size());
mSkillButtons[mControllerFocus]->setStateSelected(true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
mSkillButtons[mControllerFocus]->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus + 1, mSkillButtons.size());
mSkillButtons[mControllerFocus]->setStateSelected(true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
mSkillButtons[mControllerFocus]->setStateSelected(false);
selectNextColumn(arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT ? -1 : 1);
mSkillButtons[mControllerFocus]->setStateSelected(true);
}
return true;
}
void SelectSkillDialog::selectNextColumn(int direction)
{
// Find which column (specialization) the current index is in.
size_t specialization = 0;
size_t nextSpecializationIndex = 0;
for (; specialization < mNumSkillsPerSpecialization.size(); ++specialization)
{
nextSpecializationIndex += mNumSkillsPerSpecialization[specialization];
if (mControllerFocus < nextSpecializationIndex)
break;
}
if (direction < 0)
{
if (mControllerFocus < mNumSkillsPerSpecialization[0])
{
// Wrap around to the right column
for (size_t i = 0; i < mNumSkillsPerSpecialization.size() - 1; ++i)
mControllerFocus += mNumSkillsPerSpecialization[i];
}
else
mControllerFocus -= mNumSkillsPerSpecialization[specialization];
}
else
{
if (mControllerFocus + mNumSkillsPerSpecialization.back() >= mSkillButtons.size())
{
// Wrap around to the left column
for (size_t i = 0; i < mNumSkillsPerSpecialization.size() - 1; ++i)
mControllerFocus -= mNumSkillsPerSpecialization[i];
}
else
mControllerFocus += mNumSkillsPerSpecialization[specialization];
}
}
/* DescriptionDialog */
DescriptionDialog::DescriptionDialog()
@ -873,6 +1180,8 @@ namespace MWGui
// Make sure the edit box has focus
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);
mControllerButtons.mA = "#{Interface:OK}";
}
DescriptionDialog::~DescriptionDialog() {}
@ -904,4 +1213,13 @@ namespace MWGui
imageBox->setImageTexture(classImage);
}
bool DescriptionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_B)
{
onOkClicked(nullptr);
return true;
}
return false;
}
}

View file

@ -42,6 +42,7 @@ namespace MWGui
protected:
void onButtonClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
void fitToText(MyGUI::TextBox* widget);
@ -50,6 +51,7 @@ namespace MWGui
MyGUI::TextBox* mText;
MyGUI::Widget* mButtonBar;
std::vector<MyGUI::Button*> mButtons;
size_t mControllerFocus;
};
// Lets the player choose between 3 ways of creating a class
@ -92,10 +94,14 @@ namespace MWGui
protected:
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
bool mOkButtonFocus = true;
private:
MyGUI::ImageBox* mClassImage;
MyGUI::TextBox* mClassName;
MyGUI::Button* mBackButton;
MyGUI::Button* mOkButton;
ESM::RefId mCurrentClassId;
};
@ -140,11 +146,15 @@ namespace MWGui
MyGUI::ImageBox* mClassImage;
MyGUI::ListBox* mClassList;
MyGUI::TextBox* mSpecializationName;
MyGUI::Button* mBackButton;
MyGUI::Button* mOkButton;
Widgets::MWAttributePtr mFavoriteAttribute[2];
Widgets::MWSkillPtr mMajorSkill[5];
Widgets::MWSkillPtr mMinorSkill[5];
ESM::RefId mCurrentClassId;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
class SelectSpecializationDialog : public WindowModal
@ -173,6 +183,7 @@ namespace MWGui
protected:
void onSpecializationClicked(MyGUI::Widget* _sender);
void onCancelClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
MyGUI::TextBox *mSpecialization0, *mSpecialization1, *mSpecialization2;
@ -206,6 +217,9 @@ namespace MWGui
protected:
void onAttributeClicked(Widgets::MWAttributePtr _sender);
void onCancelClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
size_t mControllerFocus;
std::vector<Widgets::MWAttribute*> mAttributeButtons;
private:
ESM::RefId mAttributeId;
@ -237,9 +251,15 @@ namespace MWGui
protected:
void onSkillClicked(Widgets::MWSkillPtr _sender);
void onCancelClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
size_t mControllerFocus;
std::vector<Widgets::MWSkill*> mSkillButtons;
private:
ESM::RefId mSkillId;
std::array<size_t, 3> mNumSkillsPerSpecialization;
void selectNextColumn(int direction);
};
class DescriptionDialog : public WindowModal
@ -258,6 +278,7 @@ namespace MWGui
protected:
void onOkClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
MyGUI::EditBox* mTextEdit;
@ -314,6 +335,7 @@ namespace MWGui
private:
MyGUI::EditBox* mEditName;
MyGUI::TextBox* mSpecializationName;
std::vector<MyGUI::Button*> mButtons;
Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1;
std::array<Widgets::MWSkillPtr, 5> mMajorSkill;
std::array<Widgets::MWSkillPtr, 5> mMinorSkill;
@ -329,6 +351,9 @@ namespace MWGui
Widgets::MWAttributePtr mAffectedAttribute;
Widgets::MWSkillPtr mAffectedSkill;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
size_t mControllerFocus;
};
}
#endif

View file

@ -6,6 +6,8 @@
#include <MyGUI_EditBox.h>
#include <MyGUI_InputManager.h>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@ -61,6 +63,11 @@ namespace MWGui
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked);
setCoord(200, 0, 600, 300);
mControllerButtons.mA = "#{sTake}";
mControllerButtons.mB = "#{Interface:Close}";
mControllerButtons.mR3 = "#{sInfo}";
mControllerButtons.mL2 = "#{sInventory}";
}
void CompanionWindow::onItemSelected(int index)
@ -96,13 +103,12 @@ namespace MWGui
name += MWGui::ToolTips::getSoulString(object.getCellRef());
dialog->openCountDialog(name, "#{sTake}", count);
dialog->eventOkClicked.clear();
if (MyGUI::InputManager::getInstance().isAltPressed())
if (Settings::gui().mControllerMenus || MyGUI::InputManager::getInstance().isAltPressed())
dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::transferItem);
else
dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem);
}
else if (MyGUI::InputManager::getInstance().isAltPressed())
else if (Settings::gui().mControllerMenus || MyGUI::InputManager::getInstance().isAltPressed())
transferItem(nullptr, count);
else
dragItem(nullptr, count);
@ -243,4 +249,32 @@ namespace MWGui
{
mItemTransfer->removeTarget(*mItemView);
}
bool CompanionWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
int index = mItemView->getControllerFocus();
if (index >= 0 && index < mItemView->getItemCount())
onItemSelected(index);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCloseButtonClicked(mCloseButton);
}
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->onControllerButton(arg.button);
}
return true;
}
void CompanionWindow::setActiveControllerWindow(bool active)
{
mItemView->setActiveControllerWindow(active);
WindowBase::setActiveControllerWindow(active);
}
}

View file

@ -40,6 +40,12 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Companion"; }
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
MWGui::ItemView* getItemView() { return mItemView; }
CompanionItemModel* getModel() { return mModel; }
private:
ItemView* mItemView;
SortFilterItemModel* mSortModel;

View file

@ -3,6 +3,8 @@
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@ -17,6 +19,13 @@ namespace MWGui
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onCancelButtonClicked);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked);
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{Interface:OK}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
void ConfirmationDialog::askForConfirmation(const std::string& message)
@ -35,6 +44,13 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
if (Settings::gui().mControllerMenus)
{
mOkButtonFocus = true;
mOkButton->setStateSelected(true);
mCancelButton->setStateSelected(false);
}
center();
}
@ -56,4 +72,28 @@ namespace MWGui
eventOkClicked();
}
bool ConfirmationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mOkButtonFocus)
onOkButtonClicked(mOkButton);
else
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mOkButtonFocus)
|| (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mOkButtonFocus))
{
mOkButtonFocus = !mOkButtonFocus;
mOkButton->setStateSelected(mOkButtonFocus);
mCancelButton->setStateSelected(!mOkButtonFocus);
}
return true;
}
}

View file

@ -27,6 +27,9 @@ namespace MWGui
void onCancelButtonClicked(MyGUI::Widget* _sender);
void onOkButtonClicked(MyGUI::Widget* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
bool mOkButtonFocus = true;
};
}

View file

@ -3,6 +3,8 @@
#include <MyGUI_Button.h>
#include <MyGUI_InputManager.h>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
@ -56,6 +58,12 @@ namespace MWGui
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);
setCoord(200, 0, 600, 300);
mControllerButtons.mA = "#{sTake}";
mControllerButtons.mB = "#{Interface:Close}";
mControllerButtons.mX = "#{sTakeAll}";
mControllerButtons.mR3 = "#{sInfo}";
mControllerButtons.mL2 = "#{sInventory}";
}
void ContainerWindow::onItemSelected(int index)
@ -90,13 +98,12 @@ namespace MWGui
name += MWGui::ToolTips::getSoulString(object.getCellRef());
dialog->openCountDialog(name, "#{sTake}", count);
dialog->eventOkClicked.clear();
if (MyGUI::InputManager::getInstance().isAltPressed())
if (Settings::gui().mControllerMenus || MyGUI::InputManager::getInstance().isAltPressed())
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::transferItem);
else
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem);
}
else if (MyGUI::InputManager::getInstance().isAltPressed())
else if (Settings::gui().mControllerMenus || MyGUI::InputManager::getInstance().isAltPressed())
transferItem(nullptr, count);
else
dragItem(nullptr, count);
@ -346,6 +353,49 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
ControllerButtons* ContainerWindow::getControllerButtons()
{
mControllerButtons.mR1 = mDisposeCorpseButton->getVisible() ? "#{sDisposeofCorpse}" : "";
return &mControllerButtons;
}
bool ContainerWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
int index = mItemView->getControllerFocus();
if (index >= 0 && index < mItemView->getItemCount())
onItemSelected(index);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCloseButtonClicked(mCloseButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onTakeAllButtonClicked(mTakeButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
if (mDisposeCorpseButton->getVisible())
onDisposeCorpseButtonClicked(mDisposeCorpseButton);
}
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->onControllerButton(arg.button);
}
return true;
}
void ContainerWindow::setActiveControllerWindow(bool active)
{
mItemView->setActiveControllerWindow(active);
WindowBase::setActiveControllerWindow(active);
}
void ContainerWindow::onFrame(float dt)
{
checkReferenceAvailable();

View file

@ -48,6 +48,13 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Container"; }
ControllerButtons* getControllerButtons() override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
MWGui::ItemView* getItemView() { return mItemView; }
ItemModel* getModel() { return mModel; }
private:
Misc::NotNullPtr<DragAndDrop> mDragAndDrop;
Misc::NotNullPtr<ItemTransfer> mItemTransfer;

View file

@ -0,0 +1,106 @@
#include "controllerbuttonsoverlay.hpp"
#include <MyGUI_Window.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
namespace MWGui
{
static constexpr ControllerButtonsOverlay::ButtonDefinition sButtonDefs[] = {
{ ControllerButtonsOverlay::Button::Button_A, "A", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_A }, &ControllerButtons::mA },
{ ControllerButtonsOverlay::Button::Button_B, "B", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_B }, &ControllerButtons::mB },
{ ControllerButtonsOverlay::Button::Button_Dpad, "Dpad", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_DPAD_UP }, &ControllerButtons::mDpad },
{ ControllerButtonsOverlay::Button::Button_L1, "L1", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_LEFTSHOULDER }, &ControllerButtons::mL1 },
{ ControllerButtonsOverlay::Button::Button_L2, "L2", ControllerButtonsOverlay::InputType_Axis,
{ .mAxis = SDL_CONTROLLER_AXIS_TRIGGERLEFT }, &ControllerButtons::mL2 },
{ ControllerButtonsOverlay::Button::Button_L3, "L3", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_LEFTSTICK }, &ControllerButtons::mL3 },
{ ControllerButtonsOverlay::Button::Button_LStick, "LStick", ControllerButtonsOverlay::InputType_Axis,
{ .mAxis = SDL_CONTROLLER_AXIS_LEFTY }, &ControllerButtons::mLStick },
{ ControllerButtonsOverlay::Button::Button_Menu, "Menu", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_BACK }, &ControllerButtons::mMenu },
{ ControllerButtonsOverlay::Button::Button_R1, "R1", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER }, &ControllerButtons::mR1 },
{ ControllerButtonsOverlay::Button::Button_R2, "R2", ControllerButtonsOverlay::InputType_Axis,
{ .mAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT }, &ControllerButtons::mR2 },
{ ControllerButtonsOverlay::Button::Button_R3, "R3", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_RIGHTSTICK }, &ControllerButtons::mR3 },
{ ControllerButtonsOverlay::Button::Button_RStick, "RStick", ControllerButtonsOverlay::InputType_Axis,
{ .mAxis = SDL_CONTROLLER_AXIS_RIGHTY }, &ControllerButtons::mRStick },
{ ControllerButtonsOverlay::Button::Button_View, "View", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_START }, &ControllerButtons::mView },
{ ControllerButtonsOverlay::Button::Button_X, "X", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_X }, &ControllerButtons::mX },
{ ControllerButtonsOverlay::Button::Button_Y, "Y", ControllerButtonsOverlay::InputType_Button,
{ .mButton = SDL_CONTROLLER_BUTTON_Y }, &ControllerButtons::mY },
};
ControllerButtonsOverlay::ControllerButtonsOverlay()
: WindowBase("openmw_controllerbuttons.layout")
{
MWBase::InputManager* inputMgr = MWBase::Environment::get().getInputManager();
for (size_t i = 0; i < mButtons.size(); i++)
{
getWidget(mButtons[i].mImage, "Btn" + sButtonDefs[i].mName + "Image");
getWidget(mButtons[i].mText, "Btn" + sButtonDefs[i].mName + "Text");
getWidget(mButtons[i].mHBox, "Btn" + sButtonDefs[i].mName);
if (sButtonDefs[i].mInputType == InputType_Axis)
setIcon(mButtons[i].mImage, inputMgr->getControllerAxisIcon(sButtonDefs[i].mId.mAxis));
else
setIcon(mButtons[i].mImage, inputMgr->getControllerButtonIcon(sButtonDefs[i].mId.mButton));
}
getWidget(mHBox, "ButtonBox");
}
int ControllerButtonsOverlay::getHeight()
{
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
return window->getHeight();
}
void ControllerButtonsOverlay::setButtons(ControllerButtons* buttons)
{
int buttonCount = 0;
if (buttons != nullptr)
{
for (const auto& row : sButtonDefs)
buttonCount += updateButton(row.mButton, buttons->*(row.mField));
mHBox->notifyChildrenSizeChanged();
}
setVisible(buttonCount > 0);
}
void ControllerButtonsOverlay::setIcon(MyGUI::ImageBox* image, const std::string& imagePath)
{
if (!imagePath.empty())
image->setImageTexture(imagePath);
}
int ControllerButtonsOverlay::updateButton(ControllerButtonsOverlay::Button button, const std::string& buttonStr)
{
if (buttonStr.empty())
{
mButtons[button].mHBox->setVisible(false);
mButtons[button].mHBox->setUserString("Hidden", "true");
return 0;
}
else
{
mButtons[button].mHBox->setVisible(true);
mButtons[button].mHBox->setUserString("Hidden", "false");
mButtons[button].mText->setCaptionWithReplacing(buttonStr);
return 1;
}
}
}

View file

@ -0,0 +1,83 @@
#ifndef MWGUI_CONTROLLERBUTTONSOVERLAY_H
#define MWGUI_CONTROLLERBUTTONSOVERLAY_H
#include <MyGUI_ImageBox.h>
#include <MyGUI_TextBox.h>
#include <components/widgets/box.hpp>
#include "windowbase.hpp"
namespace MWGui
{
class ControllerButtonsOverlay : public WindowBase
{
public:
ControllerButtonsOverlay();
int getHeight();
void setButtons(ControllerButtons* buttons);
enum Button
{
Button_A = 0,
Button_B,
Button_Dpad,
Button_L1,
Button_L2,
Button_L3,
Button_LStick,
Button_Menu,
Button_R1,
Button_R2,
Button_R3,
Button_RStick,
Button_View,
Button_X,
Button_Y,
Button_Max,
};
enum InputType
{
InputType_Button,
InputType_Axis
};
struct ButtonDefinition
{
Button mButton;
std::string mName;
InputType mInputType;
union
{
SDL_GameControllerButton mButton;
SDL_GameControllerAxis mAxis;
} mId;
std::string MWGui::ControllerButtons::*mField;
};
private:
struct ButtonWidgets
{
MyGUI::ImageBox* mImage;
MyGUI::TextBox* mText;
Gui::HBox* mHBox;
ButtonWidgets()
: mImage(nullptr)
, mText(nullptr)
, mHBox(nullptr)
{
}
};
std::array<ButtonWidgets, Button::Button_Max> mButtons;
Gui::HBox* mHBox;
void setIcon(MyGUI::ImageBox* image, const std::string& imagePath);
int updateButton(Button button, const std::string& buttonStr);
};
}
#endif

View file

@ -27,6 +27,9 @@ namespace MWGui
mSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &CountDialog::onSliderMoved);
// make sure we read the enter key being pressed to accept multiple items
mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed);
mControllerButtons.mA = "#{Interface:OK}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
void CountDialog::openCountDialog(const std::string& item, const std::string& message, const int maxCount)
@ -38,7 +41,7 @@ namespace MWGui
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
mSlider->setScrollRange(maxCount);
mItemText->setCaption(item);
mItemText->setCaptionWithReplacing(item);
int width = std::max(mItemText->getTextSize().width + 160, 320);
setCoord(viewSize.width / 2 - width / 2, viewSize.height / 2 - mMainWidget->getHeight() / 2, width,
@ -54,6 +57,13 @@ namespace MWGui
mItemEdit->setValue(maxCount);
}
void CountDialog::setCount(int count)
{
count = std::clamp(count, 1, static_cast<int>(mSlider->getScrollRange()));
mSlider->setScrollPosition(count - 1);
mItemEdit->setValue(count);
}
void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
{
setVisible(false);
@ -61,17 +71,16 @@ namespace MWGui
void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender)
{
eventOkClicked(nullptr, mSlider->getScrollPosition() + 1);
// The order here matters. Hide the dialog first so the OK event tooltips reappear.
setVisible(false);
eventOkClicked(nullptr, mSlider->getScrollPosition() + 1);
}
// essentially duplicating what the OK button does if user presses
// Enter key
void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender)
{
eventOkClicked(nullptr, mSlider->getScrollPosition() + 1);
setVisible(false);
onOkButtonClicked(_sender);
// To do not spam onEnterKeyPressed() again and again
MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);
@ -86,4 +95,22 @@ namespace MWGui
{
mItemEdit->setValue(_position + 1);
}
bool CountDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
onOkButtonClicked(mOkButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancelButtonClicked(mCancelButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
setCount(1);
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
setCount(static_cast<int>(mSlider->getScrollRange()));
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false);
return true;
}
}

View file

@ -15,6 +15,7 @@ namespace MWGui
public:
CountDialog();
void openCountDialog(const std::string& item, const std::string& message, const int maxCount);
void setCount(int count);
/** Event : Ok button was clicked.\n
signature : void method(MyGUI::Widget* sender, std::size_t count)\n
@ -34,6 +35,7 @@ namespace MWGui
void onEditValueChanged(int value);
void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position);
void onEnterKeyPressed(MyGUI::EditBox* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}

View file

@ -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.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
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 (size_t i = 0; i < mButtons.size(); i++)
mButtons[i]->setStateSelected(i == 0);
}
WindowModal::onOpen();
}
@ -152,6 +174,31 @@ namespace MWGui
return mAdmireButton;
}
bool PersuasionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
onPersuade(mButtons[mControllerFocus]);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancel(mCancelButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
return true;
}
// --------------------------------------------------------------------------------------------------
Response::Response(std::string_view text, std::string_view title, bool needMargin)
@ -300,6 +347,9 @@ namespace MWGui
// --------------------------------------------------------------------------------------------------
// Morrowind uses 3 px invisible borders for padding topics
static constexpr int sVerticalPadding = 3;
DialogueWindow::DialogueWindow()
: WindowBase("openmw_dialogue_window.layout")
, mIsCompanion(false)
@ -335,6 +385,11 @@ namespace MWGui
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord
+= MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
mControllerScrollWidget = mHistory->getParent();
mControllerButtons.mA = "#{sAsk}";
mControllerButtons.mB = "#{sGoodbye}";
mControllerButtons.mRStick = "#{sScrollup}";
}
void DialogueWindow::onTradeComplete()
@ -488,6 +543,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();
}
@ -543,6 +606,10 @@ namespace MWGui
void DialogueWindow::updateTopicsPane()
{
std::string focusedTopic;
if (Settings::gui().mControllerMenus && mControllerFocus < static_cast<int>(mTopicsList->getItemCount()))
focusedTopic = mTopicsList->getItemNameAt(mControllerFocus);
mTopicsList->clear();
for (auto& linkPair : mTopicLinks)
mDeleteLater.push_back(std::move(linkPair.second));
@ -588,22 +655,25 @@ namespace MWGui
if (mTopicsList->getItemCount() > 0)
mTopicsList->addSeparator();
// Morrowind uses 3 px invisible borders for padding topics
constexpr int verticalPadding = 3;
for (const auto& keyword : mKeywords)
{
std::string topicId = Misc::StringUtils::lowerCase(keyword);
mTopicsList->addItem(keyword, verticalPadding);
mTopicsList->addItem(keyword, sVerticalPadding);
auto t = std::make_unique<Topic>(keyword);
mKeywordSearch.seed(topicId, intptr_t(t.get()));
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
mTopicLinks[topicId] = std::move(t);
if (keyword == focusedTopic)
mControllerFocus = mTopicsList->getItemCount() - 1;
}
redrawTopicsList();
updateHistory();
if (Settings::gui().mControllerMenus)
setControllerFocus(mControllerFocus, true);
}
void DialogueWindow::updateHistory(bool scrollbar)
@ -630,6 +700,8 @@ namespace MWGui
// choices
const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
mChoices = MWBase::Environment::get().getDialogueManager()->getChoices();
mChoiceStyles.clear();
mControllerChoice = -1; // -1 so you must make a choice (and can't accidentally pick the first answer)
for (std::pair<std::string, int>& choice : mChoices)
{
auto link = std::make_unique<Choice>(choice.second);
@ -641,6 +713,7 @@ namespace MWGui
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(
body, textColours.answer, textColours.answerOver, textColours.answerPressed, interactiveId);
typesetter->write(questionStyle, to_utf8_span(choice.first));
mChoiceStyles.push_back(questionStyle);
}
mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye();
@ -850,4 +923,120 @@ namespace MWGui
&& actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion");
}
void DialogueWindow::setControllerFocus(size_t index, bool focused)
{
// List is mTopicsList + "Goodbye" button below the list.
if (index > mTopicsList->getItemCount())
return;
if (index == mTopicsList->getItemCount())
{
mGoodbyeButton->setStateSelected(focused);
}
else
{
const std::string& keyword = mTopicsList->getItemNameAt(mControllerFocus);
if (keyword.empty())
return;
MyGUI::Button* button = mTopicsList->getItemWidget(keyword);
button->setStateSelected(focused);
}
if (focused)
{
// Scroll the side bar to keep the active item in view
int offset = 0;
for (int i = 6; i < static_cast<int>(index); i++)
{
const std::string& keyword = mTopicsList->getItemNameAt(i);
if (keyword.empty())
offset += 18 + sVerticalPadding * 2;
else
offset += mTopicsList->getItemWidget(keyword)->getHeight() + sVerticalPadding * 2;
}
mTopicsList->setViewOffset(-offset);
}
}
bool DialogueWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mChoices.size() > 0)
{
if (mChoices.size() == 1)
onChoiceActivated(mChoices[0].second);
else if (mControllerChoice >= 0 && mControllerChoice < static_cast<int>(mChoices.size()))
onChoiceActivated(mChoices[mControllerChoice].second);
}
else if (mControllerFocus == static_cast<int>(mTopicsList->getItemCount()))
onGoodbyeActivated();
else
onSelectListItem(mTopicsList->getItemNameAt(mControllerFocus), mControllerFocus);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B && mChoices.empty())
{
onGoodbyeActivated();
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mChoices.size() > 0)
{
// In-dialogue choice (red text)
mControllerChoice = std::clamp(mControllerChoice - 1, 0, static_cast<int>(mChoices.size()) - 1);
mHistory->setFocusItem(mChoiceStyles.at(mControllerChoice));
}
else
{
// Number of items is mTopicsList.length+1 because of "Goodbye" button.
setControllerFocus(mControllerFocus, false);
if (mControllerFocus <= 0)
mControllerFocus = mTopicsList->getItemCount(); // "Goodbye" button
else if (mTopicsList->getItemNameAt(mControllerFocus - 1).empty())
mControllerFocus -= 2; // Skip separator
else
mControllerFocus--;
setControllerFocus(mControllerFocus, true);
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mChoices.size() > 0)
{
// In-dialogue choice (red text)
mControllerChoice = std::clamp(mControllerChoice + 1, 0, static_cast<int>(mChoices.size()) - 1);
mHistory->setFocusItem(mChoiceStyles.at(mControllerChoice));
}
else
{
// Number of items is mTopicsList.length+1 because of "Goodbye" button.
setControllerFocus(mControllerFocus, false);
if (mControllerFocus >= static_cast<int>(mTopicsList->getItemCount()))
mControllerFocus = 0;
else if (mControllerFocus == static_cast<int>(mTopicsList->getItemCount()) - 1)
mControllerFocus = mTopicsList->getItemCount(); // "Goodbye" button
else if (mTopicsList->getItemNameAt(mControllerFocus + 1).empty())
mControllerFocus += 2; // Skip separator
else
mControllerFocus++;
setControllerFocus(mControllerFocus, true);
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER && mChoices.size() == 0)
{
setControllerFocus(mControllerFocus, false);
mControllerFocus = std::max(mControllerFocus - 5, 0);
setControllerFocus(mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER && mChoices.size() == 0)
{
setControllerFocus(mControllerFocus, false);
mControllerFocus = std::min(mControllerFocus + 5, static_cast<int>(mTopicsList->getItemCount()));
setControllerFocus(mControllerFocus, true);
}
return true;
}
}

View file

@ -49,6 +49,9 @@ namespace MWGui
MyGUI::Widget* getDefaultKeyFocus() override;
protected:
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
std::unique_ptr<ResponseCallback> mCallback;
@ -65,6 +68,9 @@ namespace MWGui
MyGUI::Widget* mActionsBox;
Gui::AutoSizedTextBox* mGoldLabel;
std::vector<MyGUI::Button*> 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();
@ -197,6 +205,7 @@ namespace MWGui
std::vector<std::unique_ptr<DialogueText>> mHistoryContents;
std::vector<std::pair<std::string, int>> mChoices;
std::vector<BookTypesetter::Style*> mChoiceStyles;
bool mGoodbye;
std::vector<std::unique_ptr<Link>> mLinks;
@ -220,6 +229,10 @@ namespace MWGui
std::unique_ptr<ResponseCallback> mCallback;
std::unique_ptr<ResponseCallback> mGreetingCallback;
void setControllerFocus(size_t index, bool focused);
int mControllerFocus = 0;
int mControllerChoice = -1;
void updateTopicFormat();
};
}

View file

@ -59,6 +59,12 @@ namespace MWGui
mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked);
mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked);
mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mY = "#{OMWEngine:EnchantType}";
mControllerButtons.mL1 = "#{sItem}";
mControllerButtons.mR1 = "#{sSoulGem}";
}
void EnchantingDialog::onOpen()
@ -152,6 +158,7 @@ namespace MWGui
mEnchanting.setSelfEnchanting(false);
mEnchanting.setEnchanter(ptr);
mBuyButton->setCaptionWithReplacing("#{sBuy}");
mControllerButtons.mX = "#{sBuy}";
mChanceLayout->setVisible(false);
mPtr = ptr;
setSoulGem(MWWorld::Ptr());
@ -163,6 +170,7 @@ namespace MWGui
mEnchanting.setSelfEnchanting(true);
mEnchanting.setEnchanter(MWMechanics::getPlayer());
mBuyButton->setCaptionWithReplacing("#{sCreate}");
mControllerButtons.mX = "#{sCreate}";
mChanceLayout->setVisible(Settings::game().mShowEnchantChance);
mPtr = MWMechanics::getPlayer();
setSoulGem(ptr);
@ -382,4 +390,22 @@ namespace MWGui
}
}
}
bool EnchantingDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancelButtonClicked(mCancelButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
onBuyButtonClicked(mBuyButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_Y)
onTypeButtonClicked(mTypeButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
onSelectItem(mItemBox);
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
onSelectSoul(mSoulBox);
else
return EffectEditorBase::onControllerButtonEvent(arg);
return true;
}
}

View file

@ -73,6 +73,8 @@ namespace MWGui
MWMechanics::Enchanting mEnchanting;
ESM::EffectList mEffectList;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}

View file

@ -188,6 +188,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())

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

@ -0,0 +1,63 @@
#include "inventorytabsoverlay.hpp"
#include <MyGUI_Button.h>
#include <MyGUI_ImageBox.h>
#include <MyGUI_Window.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
namespace MWGui
{
InventoryTabsOverlay::InventoryTabsOverlay()
: WindowBase("openmw_inventory_tabs.layout")
{
MyGUI::Button* tab;
static const char* kTabIds[] = { "TabMap", "TabInventory", "TabSpells", "TabStats" };
for (const char* id : kTabIds)
{
getWidget(tab, id);
tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked);
mTabs.push_back(tab);
}
MyGUI::ImageBox* image;
getWidget(image, "BtnL2Image");
image->setImageTexture(
MWBase::Environment::get().getInputManager()->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERLEFT));
getWidget(image, "BtnR2Image");
image->setImageTexture(
MWBase::Environment::get().getInputManager()->getControllerAxisIcon(SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
}
int InventoryTabsOverlay::getHeight()
{
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
return window->getHeight();
}
void InventoryTabsOverlay::onTabClicked(MyGUI::Widget* sender)
{
if (!MWBase::Environment::get().getWindowManager()->getJournalAllowed())
return;
for (int i = 0; i < static_cast<int>(mTabs.size()); i++)
{
if (mTabs[i] == sender)
{
MWBase::Environment::get().getWindowManager()->setActiveControllerWindow(GM_Inventory, i);
setTab(i);
break;
}
}
}
void InventoryTabsOverlay::setTab(int index)
{
for (int i = 0; i < static_cast<int>(mTabs.size()); i++)
mTabs[i]->setStateSelected(i == index);
}
}

View file

@ -0,0 +1,28 @@
#ifndef MWGUI_INVENTORYTABSSOVERLAY_H
#define MWGUI_INVENTORYTABSSOVERLAY_H
#include "windowbase.hpp"
namespace MyGUI
{
class Button;
}
namespace MWGui
{
class InventoryTabsOverlay : public WindowBase
{
public:
InventoryTabsOverlay();
int getHeight();
void setTab(int index);
private:
std::vector<MyGUI::Button*> mTabs;
void onTabClicked(MyGUI::Widget* sender);
};
}
#endif

View file

@ -19,6 +19,7 @@
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/luamanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
@ -31,13 +32,17 @@
#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 "itemtransfer.hpp"
#include "itemview.hpp"
#include "settings.hpp"
#include "sortfilteritemmodel.hpp"
#include "statswindow.hpp"
#include "tooltips.hpp"
#include "tradeitemmodel.hpp"
#include "tradewindow.hpp"
@ -89,6 +94,7 @@ namespace MWGui
, mPreview(std::make_unique<MWRender::InventoryPreview>(parent, resourceSystem, MWMechanics::getPlayer()))
, mTrading(false)
, mUpdateNextFrame(false)
, mPendingControllerAction(ControllerAction::None)
{
mPreviewTexture
= std::make_unique<MyGUIPlatform::OSGTexture>(mPreview->getTexture(), mPreview->getTextureStateSet());
@ -129,6 +135,25 @@ namespace MWGui
setGuiMode(mGuiMode);
if (Settings::gui().mControllerMenus)
{
// Show L1 and R1 buttons next to tabs
MyGUI::ImageBox* image;
getWidget(image, "BtnL1Image");
image->setVisible(true);
image->setUserString("Hidden", "false");
image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon(
SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
getWidget(image, "BtnR1Image");
image->setVisible(true);
image->setUserString("Hidden", "false");
image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon(
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER));
mControllerButtons.mR3 = "#{sInfo}";
}
adjustPanes();
}
@ -208,9 +233,13 @@ namespace MWGui
void InventoryWindow::setGuiMode(GuiMode mode)
{
if (Settings::gui().mControllerMenus && mGuiMode == mode && isVisible())
return;
mGuiMode = mode;
const WindowSettingValues settings = getModeSettings(mGuiMode);
setPinButtonVisible(mode != GM_Container && mode != GM_Companion && mode != GM_Barter);
setPinButtonVisible(
mode != GM_Container && mode != GM_Companion && mode != GM_Barter && !Settings::gui().mControllerMenus);
const WindowRectSettingValues& rect = settings.mIsMaximized ? settings.mMaximized : settings.mRegular;
@ -306,18 +335,26 @@ 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 && mPendingControllerAction != ControllerAction::Use)
{
CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();
std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}";
std::string message = "#{sTake}";
if (mTrading || mPendingControllerAction == ControllerAction::Sell)
message = "#{sQuanityMenuMessage01}";
else if (mPendingControllerAction == ControllerAction::Drop)
message = "#{sDrop}";
std::string name{ object.getClass().getName(object) };
name += MWGui::ToolTips::getSoulString(object.getCellRef());
dialog->openCountDialog(name, message, count);
dialog->eventOkClicked.clear();
if (mTrading)
if (mTrading || mPendingControllerAction == ControllerAction::Sell)
dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem);
else if (MyGUI::InputManager::getInstance().isAltPressed())
else if (mPendingControllerAction == ControllerAction::Drop)
dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dropItem);
else if (MyGUI::InputManager::getInstance().isAltPressed()
|| mPendingControllerAction == ControllerAction::Transfer)
dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::transferItem);
else
dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem);
@ -328,13 +365,35 @@ namespace MWGui
{
mSelectedItem = index;
if (mTrading)
if (mTrading || mPendingControllerAction == ControllerAction::Sell)
sellItem(nullptr, count);
else if (MyGUI::InputManager::getInstance().isAltPressed())
else if (mPendingControllerAction == ControllerAction::Use)
{
dragItem(nullptr, count);
if (item.mType == ItemStack::Type_Equipped)
{
// Drop the item on the inventory background to unequip it.
onBackgroundSelected();
}
else
{
// Drop the item on the avatar to activate or equip it.
onAvatarClicked(nullptr);
// 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 (mPendingControllerAction == ControllerAction::Drop)
dropItem(nullptr, count);
else if (MyGUI::InputManager::getInstance().isAltPressed()
|| mPendingControllerAction == ControllerAction::Transfer)
transferItem(nullptr, count);
else
dragItem(nullptr, count);
}
mPendingControllerAction = ControllerAction::None;
}
void InventoryWindow::ensureSelectedItemUnequipped(int count)
@ -408,6 +467,19 @@ namespace MWGui
notifyContentChanged();
}
void InventoryWindow::dropItem(MyGUI::Widget* sender, size_t count)
{
if (mGuiMode != MWGui::GM_Inventory)
return;
if (!mDragAndDrop->mIsOnDragAndDrop)
dragItem(sender, count);
// Drop the item into the gameworld
if (mDragAndDrop->mIsOnDragAndDrop)
MWBase::Environment::get().getWindowManager()->getHud()->dropDraggedItem(0.5f, 0.5f);
}
void InventoryWindow::updateItemView()
{
MWBase::Environment::get().getWindowManager()->updateSpellWindow();
@ -523,7 +595,9 @@ namespace MWGui
void InventoryWindow::onTitleDoubleClicked()
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
if (Settings::gui().mControllerMenus && mGuiMode == GM_Inventory)
return;
else if (MyGUI::InputManager::getInstance().isShiftPressed())
toggleMaximized();
else if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
@ -890,4 +964,151 @@ namespace MWGui
const MyGUI::IntSize viewport = getPreviewViewportSize();
return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1));
}
ControllerButtons* InventoryWindow::getControllerButtons()
{
switch (mGuiMode)
{
case MWGui::GM_Companion:
mControllerButtons.mA = "#{OMWEngine:InventorySelect}";
mControllerButtons.mB = "#{Interface:Close}";
mControllerButtons.mX.clear();
mControllerButtons.mR2 = "#{sCompanionShare}";
break;
case MWGui::GM_Container:
mControllerButtons.mA = "#{OMWEngine:InventorySelect}";
mControllerButtons.mB = "#{Interface:Close}";
mControllerButtons.mX = "#{sTakeAll}";
mControllerButtons.mR2 = "#{sContainer}";
break;
case MWGui::GM_Barter:
mControllerButtons.mA = "#{sSell}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mX = "#{sOffer}";
mControllerButtons.mR2 = "#{sBarter}";
break;
case MWGui::GM_Inventory:
default:
mControllerButtons.mA = "#{sEquip}";
mControllerButtons.mB = "#{sBack}";
mControllerButtons.mX = "#{sDrop}";
mControllerButtons.mR2.clear();
break;
}
return &mControllerButtons;
}
bool InventoryWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
mPendingControllerAction = ControllerAction::None; // Clear any pending controller actions
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
else if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mGuiMode == MWGui::GM_Inventory)
mPendingControllerAction = ControllerAction::Use;
else if (mGuiMode == MWGui::GM_Companion || mGuiMode == MWGui::GM_Container)
mPendingControllerAction = ControllerAction::Transfer;
else if (mGuiMode == MWGui::GM_Barter)
mPendingControllerAction = ControllerAction::Sell;
mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
if (mGuiMode == MWGui::GM_Inventory)
{
mPendingControllerAction = ControllerAction::Drop;
mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A);
}
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 = static_cast<MWGui::ContainerWindow*>(
MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0));
containerWindow->onControllerButtonEvent(arg);
}
else if (mGuiMode == MWGui::GM_Barter)
{
// Offer. Pass the button press to the barter window and let it do the logic
// of making an offer.
MWGui::TradeWindow* tradeWindow = static_cast<MWGui::TradeWindow*>(
MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(1));
tradeWindow->onControllerButtonEvent(arg);
}
}
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);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
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);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else
{
mItemView->onControllerButton(arg.button);
}
return true;
}
void InventoryWindow::setActiveControllerWindow(bool active)
{
if (!Settings::gui().mControllerMenus)
return;
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
if (winMgr->getMode() == MWGui::GM_Inventory)
{
// Fill the screen, or limit to a certain size on large screens. Size chosen to
// match the size of the stats window.
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
int width = std::min(viewSize.width, 1600);
int height = std::min(winMgr->getControllerMenuHeight(), StatsWindow::getIdealHeight());
int x = (viewSize.width - width) / 2;
int y = (viewSize.height - height) / 2;
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(x, active ? y : viewSize.height + 1, width, height);
adjustPanes();
updatePreviewSize();
}
// Show L1 and R1 buttons next to tabs
MyGUI::Widget* image;
getWidget(image, "BtnL1Image");
image->setVisible(active);
getWidget(image, "BtnR1Image");
image->setVisible(active);
mItemView->setActiveControllerWindow(active);
WindowBase::setActiveControllerWindow(active);
}
}

View file

@ -77,8 +77,12 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Inventory"; }
ControllerButtons* getControllerButtons() override;
protected:
void onTitleDoubleClicked() override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
private:
Misc::NotNullPtr<DragAndDrop> mDragAndDrop;
@ -126,9 +130,20 @@ namespace MWGui
void onBackgroundSelected();
enum class ControllerAction
{
None,
Use,
Transfer,
Sell,
Drop,
};
ControllerAction mPendingControllerAction;
void sellItem(MyGUI::Widget* sender, std::size_t count);
void dragItem(MyGUI::Widget* sender, std::size_t count);
void transferItem(MyGUI::Widget* sender, std::size_t count);
void dropItem(MyGUI::Widget* sender, std::size_t count);
void onWindowResize(MyGUI::Window* _sender);
void onFilterChanged(MyGUI::Widget* _sender);

View file

@ -1,16 +1,17 @@
#include "itemchargeview.hpp"
#include <SDL_gamecontroller.h>
#include <set>
#include <MyGUI_FactoryManager.h>
#include <MyGUI_Gui.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_TextBox.h>
#include <MyGUI_UString.h>
#include <components/esm3/loadench.hpp>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/spellutil.hpp"
@ -19,6 +20,8 @@
#include "itemmodel.hpp"
#include "itemwidget.hpp"
#include "textcolours.hpp"
#include "windowbase.hpp"
namespace MWGui
{
@ -156,11 +159,20 @@ namespace MWGui
mScrollView->setCanvasSize(
MyGUI::IntSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), currentY)));
mScrollView->setVisibleVScroll(true);
if (Settings::gui().mControllerMenus)
updateControllerFocus(-1, mControllerFocus);
}
void ItemChargeView::resetScrollbars()
{
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
if (Settings::gui().mControllerMenus)
{
updateControllerFocus(mControllerFocus, 0);
mControllerFocus = 0;
}
}
void ItemChargeView::setSize(const MyGUI::IntSize& value)
@ -224,4 +236,52 @@ namespace MWGui
mScrollView->setViewOffset(
MyGUI::IntPoint(0, static_cast<int>(mScrollView->getViewOffset().top + rel * 0.3f)));
}
void ItemChargeView::onControllerButton(const unsigned char button)
{
if (mLines.empty())
return;
int prevFocus = mControllerFocus;
if (button == SDL_CONTROLLER_BUTTON_A)
{
// Select the focused item, if any.
if (mControllerFocus >= 0 && mControllerFocus < static_cast<int>(mLines.size()))
onIconClicked(mLines[mControllerFocus].mIcon);
}
else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP)
mControllerFocus = wrap(mControllerFocus - 1, mLines.size());
else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
mControllerFocus = wrap(mControllerFocus + 1, mLines.size());
if (prevFocus != mControllerFocus)
updateControllerFocus(prevFocus, mControllerFocus);
}
void ItemChargeView::updateControllerFocus(int prevFocus, int newFocus)
{
if (mLines.empty())
return;
const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() };
if (prevFocus >= 0 && prevFocus < static_cast<int>(mLines.size()))
{
mLines[prevFocus].mText->setTextColour(textColours.normal);
mLines[prevFocus].mIcon->setControllerFocus(false);
}
if (newFocus >= 0 && newFocus < static_cast<int>(mLines.size()))
{
mLines[newFocus].mText->setTextColour(textColours.link);
mLines[newFocus].mIcon->setControllerFocus(true);
// Scroll the list to keep the active item in view
if (newFocus <= 3)
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
else
mScrollView->setViewOffset(MyGUI::IntPoint(0, -55 * (newFocus - 3)));
}
}
}

View file

@ -52,6 +52,8 @@ namespace MWGui
MyGUI::delegates::MultiDelegate<MyGUI::Widget*, const MWWorld::Ptr&> eventItemClicked;
void onControllerButton(const unsigned char button);
private:
struct Line
{
@ -72,6 +74,9 @@ namespace MWGui
std::unique_ptr<ItemModel> mModel;
MyGUI::ScrollView* mScrollView;
DisplayMode mDisplayMode;
int mControllerFocus;
void updateControllerFocus(int prevFocus, int newFocus);
};
}

View file

@ -3,6 +3,8 @@
#include <MyGUI_Button.h>
#include <MyGUI_TextBox.h>
#include <components/settings/values.hpp>
#include "inventoryitemmodel.hpp"
#include "itemview.hpp"
#include "sortfilteritemmodel.hpp"
@ -26,6 +28,10 @@ namespace MWGui
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked);
center();
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mR3 = "#{sInfo}";
}
bool ItemSelectionDialog::exit()
@ -40,6 +46,8 @@ namespace MWGui
mSortModel = sortModel.get();
mItemView->setModel(std::move(sortModel));
mItemView->resetScrollBars();
if (Settings::gui().mControllerMenus)
mItemView->setActiveControllerWindow(true);
}
void ItemSelectionDialog::setCategory(int category)
@ -65,4 +73,13 @@ namespace MWGui
exit();
}
bool ItemSelectionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancelButtonClicked(nullptr);
else
mItemView->onControllerButton(arg.button);
return true;
}
}

View file

@ -41,6 +41,7 @@ namespace MWGui
void onSelectedItem(int index);
void onCancelButtonClicked(MyGUI::Widget* sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}

View file

@ -7,6 +7,12 @@
#include <MyGUI_ImageBox.h>
#include <MyGUI_ScrollView.h>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "itemmodel.hpp"
#include "itemwidget.hpp"
@ -15,6 +21,7 @@ namespace MWGui
ItemView::ItemView()
: mScrollView(nullptr)
, mControllerActiveWindow(false)
{
}
@ -46,13 +53,16 @@ namespace MWGui
MyGUI::Widget* dragArea = mScrollView->getChildAt(0);
int maxHeight = mScrollView->getHeight();
int rows = maxHeight / 42;
rows = std::max(rows, 1);
bool showScrollbar = int(std::ceil(dragArea->getChildCount() / float(rows))) > mScrollView->getWidth() / 42;
mRows = std::max(maxHeight / 42, 1);
mItemCount = dragArea->getChildCount();
bool showScrollbar = static_cast<int>(std::ceil(mItemCount / float(mRows))) > mScrollView->getWidth() / 42;
if (showScrollbar)
{
maxHeight -= 18;
mRows = std::max(maxHeight / 42, 1);
}
for (unsigned int i = 0; i < dragArea->getChildCount(); ++i)
for (int i = 0; i < mItemCount; ++i)
{
MyGUI::Widget* w = dragArea->getChildAt(i);
@ -60,7 +70,7 @@ namespace MWGui
y += 42;
if (y > maxHeight - 42 && i < dragArea->getChildCount() - 1)
if (y > maxHeight - 42 && i < mItemCount - 1)
{
x += 42;
y = 0;
@ -70,6 +80,12 @@ namespace MWGui
MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height);
if (Settings::gui().mControllerMenus)
{
mControllerFocus = std::clamp(mControllerFocus, 0, mItemCount - 1);
updateControllerFocus(-1, mControllerFocus);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
// scrollbar is hidden
mScrollView->setVisibleVScroll(false);
@ -122,6 +138,11 @@ namespace MWGui
void ItemView::resetScrollBars()
{
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
if (Settings::gui().mControllerMenus)
{
updateControllerFocus(mControllerFocus, 0);
mControllerFocus = 0;
}
}
void ItemView::onSelectedItem(MyGUI::Widget* sender)
@ -165,4 +186,108 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ItemView>("Widget");
}
void ItemView::setActiveControllerWindow(bool active)
{
mControllerActiveWindow = active;
MWBase::Environment::get().getWindowManager()->setControllerTooltip(
active && Settings::gui().mControllerTooltips);
if (active)
updateControllerFocus(-1, mControllerFocus);
else
updateControllerFocus(mControllerFocus, -1);
}
void ItemView::onControllerButton(const unsigned char button)
{
if (!mItemCount)
return;
int prevFocus = mControllerFocus;
switch (button)
{
case 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));
}
break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
// Toggle info tooltip
MWBase::Environment::get().getWindowManager()->setControllerTooltip(
!MWBase::Environment::get().getWindowManager()->getControllerTooltip());
updateControllerFocus(-1, mControllerFocus);
break;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
if (mControllerFocus % mRows == 0)
mControllerFocus = std::min(mControllerFocus + mRows - 1, mItemCount - 1);
else
mControllerFocus--;
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
if (mControllerFocus % mRows == mRows - 1 || mControllerFocus == mItemCount - 1)
mControllerFocus -= mControllerFocus % mRows;
else
mControllerFocus++;
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
if (mControllerFocus >= mRows)
mControllerFocus -= mRows;
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
if (mControllerFocus + mRows < mItemCount)
mControllerFocus += mRows;
else if (mControllerFocus / mRows != (mItemCount - 1) / mRows)
mControllerFocus = mItemCount - 1;
break;
default:
return;
}
if (prevFocus != mControllerFocus)
updateControllerFocus(prevFocus, mControllerFocus);
else
updateControllerFocus(-1, mControllerFocus);
}
void ItemView::updateControllerFocus(int prevFocus, int newFocus)
{
MWBase::Environment::get().getWindowManager()->setCursorVisible(
!MWBase::Environment::get().getWindowManager()->getControllerTooltip());
if (!mItemCount)
return;
MyGUI::Widget* dragArea = mScrollView->getChildAt(0);
if (prevFocus >= 0 && prevFocus < mItemCount)
{
ItemWidget* prev = static_cast<ItemWidget*>(dragArea->getChildAt(prevFocus));
if (prev)
prev->setControllerFocus(false);
}
if (mControllerActiveWindow && newFocus >= 0 && newFocus < mItemCount)
{
ItemWidget* focused = static_cast<ItemWidget*>(dragArea->getChildAt(newFocus));
if (focused)
{
focused->setControllerFocus(true);
// Scroll the list to keep the active item in view
int column = newFocus / mRows;
if (column <= 3)
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
else
mScrollView->setViewOffset(MyGUI::IntPoint(-42 * (column - 3), 0));
if (MWBase::Environment::get().getWindowManager()->getControllerTooltip())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused);
}
}
}
}

View file

@ -33,6 +33,11 @@ namespace MWGui
void resetScrollBars();
void setActiveControllerWindow(bool active);
int getControllerFocus() { return mControllerFocus; }
int getItemCount() { return mItemCount; }
void onControllerButton(const unsigned char button);
private:
void initialiseOverride() override;
@ -47,6 +52,12 @@ namespace MWGui
std::unique_ptr<ItemModel> mModel;
MyGUI::ScrollView* mScrollView;
int mItemCount = 0;
int mRows;
int mControllerFocus = 0;
bool mControllerActiveWindow;
void updateControllerFocus(int prevFocus, int newFocus);
};
}

View file

@ -58,6 +58,7 @@ namespace MWGui
: mItem(nullptr)
, mItemShadow(nullptr)
, mFrame(nullptr)
, mControllerBorder(nullptr)
, mText(nullptr)
{
}
@ -82,10 +83,22 @@ namespace MWGui
assignWidget(mText, "Text");
if (mText)
mText->setNeedMouseFocus(false);
if (Settings::gui().mControllerMenus)
{
assignWidget(mControllerBorder, "ControllerBorder");
if (mControllerBorder)
mControllerBorder->setNeedMouseFocus(false);
}
Base::initialiseOverride();
}
void ItemWidget::setControllerFocus(bool focus)
{
if (mControllerBorder)
mControllerBorder->setVisible(focus);
}
void ItemWidget::setCount(int count)
{
if (!mText)

View file

@ -40,12 +40,15 @@ namespace MWGui
void setIcon(const MWWorld::Ptr& ptr);
void setFrame(const std::string& frame, const MyGUI::IntCoord& coord);
void setControllerFocus(bool focus);
protected:
void initialiseOverride() override;
MyGUI::ImageBox* mItem;
MyGUI::ImageBox* mItemShadow;
MyGUI::ImageBox* mFrame;
MyGUI::ImageBox* mControllerBorder;
MyGUI::TextBox* mText;
std::string mCurrentIcon;

View file

@ -156,6 +156,13 @@ namespace MWGui
return MWGui::BookTypesetter::Utf8Span(begin, begin + text.length());
}
int getCyrillicIndexPageCount()
{
// For small font size split alphabet to two columns (2x15 characers), for big font size split it to three
// colums (3x10 characters).
return Settings::gui().mFontSize < 18 ? 2 : 3;
}
typedef TypesetBook::Ptr book;
JournalBooks::JournalBooks(JournalViewModel::Ptr model, ToUTF8::FromType encoding)
@ -169,7 +176,7 @@ namespace MWGui
{
BookTypesetter::Ptr typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, MyGUI::Colour(0.60f, 0.00f, 0.00f));
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
typesetter->write(header, to_utf8_span("You have no journal entries!"));
@ -184,7 +191,7 @@ namespace MWGui
{
BookTypesetter::Ptr typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, MyGUI::Colour(0.60f, 0.00f, 0.00f));
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
mModel->visitJournalEntries({}, AddJournalEntry(typesetter, body, header, true));
@ -196,7 +203,7 @@ namespace MWGui
{
BookTypesetter::Ptr typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, MyGUI::Colour(0.60f, 0.00f, 0.00f));
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
mModel->visitTopicName(topicId, AddTopicName(typesetter, header));
@ -212,7 +219,7 @@ namespace MWGui
{
BookTypesetter::Ptr typesetter = createTypesetter();
BookTypesetter::Style* header = typesetter->createStyle({}, MyGUI::Colour(0.60f, 0.00f, 0.00f));
BookTypesetter::Style* header = typesetter->createStyle({}, journalHeaderColour);
BookTypesetter::Style* body = typesetter->createStyle({}, MyGUI::Colour::Black);
AddQuestName addName(typesetter, header);
@ -277,13 +284,8 @@ namespace MWGui
// for small font size split alphabet to two columns (2x15 characers), for big font size split it to three
// colums (3x10 characters).
int sectionBreak = 10;
mIndexPagesCount = 3;
if (Settings::gui().mFontSize < 18)
{
sectionBreak = 15;
mIndexPagesCount = 2;
}
mIndexPagesCount = getCyrillicIndexPageCount();
int sectionBreak = 30 / mIndexPagesCount;
unsigned char ch[3] = { 0xd0, 0x90, 0x00 }; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8

View file

@ -9,6 +9,9 @@
namespace MWGui
{
MWGui::BookTypesetter::Utf8Span to_utf8_span(std::string_view text);
int getCyrillicIndexPageCount();
const MyGUI::Colour journalHeaderColour = MyGUI::Colour(0.60f, 0.00f, 0.00f);
struct JournalBooks
{

View file

@ -218,6 +218,16 @@ namespace
}
}
// Latin = 26 (13 + 13)
mIndexRowCount = 13;
bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251);
if (isRussian) // Cyrillic is either (10 + 10 + 10) or (15 + 15)
mIndexRowCount = MWGui::getCyrillicIndexPageCount();
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mX = "#{OMWEngine:JournalQuests}";
mControllerButtons.mY = "#{sTopics}";
mQuestMode = false;
mAllQuests = false;
mOptionsMode = false;
@ -248,6 +258,9 @@ namespace
}
updateShowingPages();
if (Settings::gui().mControllerMenus)
setControllerFocusedQuest(0);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget<MyGUI::Widget>(CloseBTN));
}
@ -275,6 +288,8 @@ namespace
updateShowingPages();
updateCloseJournalButton();
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void setOptionsMode()
@ -307,6 +322,8 @@ namespace
notifyQuests(getWidget<MyGUI::Widget>(QuestsList));
else
notifyTopics(getWidget<MyGUI::Widget>(TopicsList));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void pushBook(Book& book, unsigned int page)
@ -338,6 +355,7 @@ namespace
{
setVisible(CloseBTN, mStates.size() < 2);
setVisible(JournalBTN, mStates.size() >= 2);
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void updateShowingPages()
@ -380,6 +398,8 @@ namespace
setText(PageOneNum, page + 1);
setText(PageTwoNum, page + 2);
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void notifyKeyPress(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character)
@ -407,6 +427,7 @@ namespace
mTopicsMode = false;
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page"));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void notifyTopicSelected(const std::string& topicIdString, int id)
@ -439,6 +460,7 @@ namespace
mOptionsMode = false;
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page"));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void notifyOptions(MyGUI::Widget* _sender)
@ -459,6 +481,9 @@ namespace
getPage(LeftTopicIndex)->showPage(mTopicIndexBook, 0);
getPage(RightTopicIndex)->showPage(mTopicIndexBook, 1);
}
if (Settings::gui().mControllerMenus)
setIndexControllerFocus(true);
}
void notifyJournal(MyGUI::Widget* _sender)
@ -467,6 +492,22 @@ namespace
popBook();
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page"));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void addControllerButtons(Gui::MWList* _list, size_t _selectedIndex)
{
mButtons.clear();
for (size_t i = 0; i < _list->getItemCount(); i++)
{
MyGUI::Button* listItem = _list->getItemWidget(_list->getItemNameAt(i));
if (listItem)
{
listItem->setTextColour(
mButtons.size() == _selectedIndex ? MWGui::journalHeaderColour : MyGUI::Colour::Black);
mButtons.push_back(listItem);
}
}
}
void notifyIndexLinkClicked(MWGui::TypesetBook::InteractiveId index)
@ -487,7 +528,14 @@ namespace
list->adjustSize();
if (Settings::gui().mControllerMenus)
{
setControllerFocusedQuest(0);
addControllerButtons(list, mSelectedQuest);
}
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page"));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void notifyTopics(MyGUI::Widget* _sender)
@ -503,6 +551,7 @@ namespace
setVisible(ShowActiveBTN, false);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page"));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
struct AddNamesToList
@ -554,6 +603,12 @@ namespace
list->sort();
list->adjustSize();
if (Settings::gui().mControllerMenus)
{
addControllerButtons(list, mSelectedQuest);
setControllerFocusedQuest(MWGui::wrap(mSelectedQuest, mButtons.size()));
}
if (mAllQuests)
{
SetNamesInactive setInactive(list);
@ -561,6 +616,7 @@ namespace
}
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page"));
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void notifyShowAll(MyGUI::Widget* _sender)
@ -639,6 +695,240 @@ namespace
}
}
}
MWGui::ControllerButtons* getControllerButtons() override
{
mControllerButtons.mB = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{Interface:Close}";
mControllerButtons.mL1 = mOptionsMode ? "" : "#{sPrev}";
mControllerButtons.mR1 = mOptionsMode ? "" : "#{sNext}";
mControllerButtons.mR3 = mOptionsMode && mQuestMode ? "#{OMWEngine:JournalShowAll}" : "";
return &mControllerButtons;
}
void setIndexControllerFocus(bool focused)
{
int col = mSelectedIndex / mIndexRowCount;
int row = mSelectedIndex % mIndexRowCount;
mTopicIndexBook->setColour(col, row, 0, focused ? MWGui::journalHeaderColour : MyGUI::Colour::Black);
}
void moveSelectedIndex(int offset)
{
setIndexControllerFocus(false);
int numChars = mEncoding == ToUTF8::WINDOWS_1251 ? 30 : 26;
int col = mSelectedIndex / mIndexRowCount;
if (offset == -1) // Up
{
if (mSelectedIndex % mIndexRowCount == 0)
mSelectedIndex = (col * mIndexRowCount) + mIndexRowCount - 1;
else
mSelectedIndex--;
}
else if (offset == 1) // Down
{
if (mSelectedIndex % mIndexRowCount == mIndexRowCount - 1)
mSelectedIndex = col * mIndexRowCount;
else
mSelectedIndex++;
}
else
{
// mSelectedIndex is unsigned, so we have to be careful with our math.
if (offset < 0)
offset += numChars;
mSelectedIndex = (mSelectedIndex + offset) % numChars;
}
setIndexControllerFocus(true);
setText(PageOneNum, 1); // Redraw the list
}
bool optionsModeButtonHandler(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A) // A: Mouse click or Select
{
if (mQuestMode)
{
// Choose a quest
Gui::MWList* list = getWidget<Gui::MWList>(QuestsList);
if (mSelectedQuest < list->getItemCount())
notifyQuestClicked(list->getItemNameAt(mSelectedQuest), 0);
}
else if (mTopicsMode)
{
// Choose a topic
Gui::MWList* list = getWidget<Gui::MWList>(TopicsList);
if (mSelectedQuest < list->getItemCount())
notifyTopicSelected(list->getItemNameAt(mSelectedQuest), 0);
}
else
{
// Choose an index. Cyrillic capital A is a 0xd090 in UTF-8.
// Words can not be started with characters 26 or 28.
int russianOffset = 0xd090;
if (mSelectedIndex >= 26)
russianOffset++;
if (mSelectedIndex >= 27)
russianOffset++; // 27, not 28, because of skipping char 26
bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251);
notifyIndexLinkClicked(isRussian ? mSelectedIndex + russianOffset : mSelectedIndex + 'A');
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back
{
// Hide the options overlay
notifyCancel(getWidget<MyGUI::Widget>(CancelBTN));
mQuestMode = false;
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Quests
{
if (mQuestMode)
{
// Hide the quest overlay if visible
notifyCancel(getWidget<MyGUI::Widget>(CancelBTN));
mQuestMode = false;
}
else
{
// Show the quest overlay if viewing the topics list
notifyQuests(getWidget<MyGUI::Widget>(QuestsBTN));
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Topics
{
if (!mQuestMode)
{
// Hide the topics overlay if visible
notifyCancel(getWidget<MyGUI::Widget>(CancelBTN));
}
else
{
// Show the topics overlay if viewing the quest list
notifyTopics(getWidget<MyGUI::Widget>(TopicsBTN));
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK && mQuestMode) // R3: Show All/Some
{
if (mAllQuests)
notifyShowActive(getWidget<MyGUI::Widget>(ShowActiveBTN));
else
notifyShowAll(getWidget<MyGUI::Widget>(ShowAllBTN));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mQuestMode || mTopicsMode)
{
if (mButtons.size() <= 1)
return true;
// Scroll through the list of quests or topics
setControllerFocusedQuest(MWGui::wrap(mSelectedQuest - 1, mButtons.size()));
}
else
moveSelectedIndex(-1);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mQuestMode || mTopicsMode)
{
if (mButtons.size() <= 1)
return true;
// Scroll through the list of quests or topics
setControllerFocusedQuest(MWGui::wrap(mSelectedQuest + 1, mButtons.size()));
}
else
moveSelectedIndex(1);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mQuestMode && !mTopicsMode)
moveSelectedIndex(-mIndexRowCount);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mQuestMode && !mTopicsMode)
moveSelectedIndex(mIndexRowCount);
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER && (mQuestMode || mTopicsMode))
{
// Scroll up 5 items in the list of quests or topics
setControllerFocusedQuest(std::max(static_cast<int>(mSelectedQuest) - 5, 0));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER && (mQuestMode || mTopicsMode))
{
// Scroll down 5 items in the list of quests or topics
setControllerFocusedQuest(std::min(mSelectedQuest + 5, mButtons.size() - 1));
}
return true;
}
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override
{
// If the topics or quest list is open, it should handle the buttons.
if (mOptionsMode)
return optionsModeButtonHandler(arg);
if (arg.button == SDL_CONTROLLER_BUTTON_A)
return false;
else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back
{
if (mStates.size() > 1)
{
// Pop the current book. If in quest mode, reopen the quest list.
notifyJournal(getWidget<MyGUI::Widget>(JournalBTN));
if (mQuestMode)
{
notifyOptions(getWidget<MyGUI::Widget>(OptionsBTN));
notifyQuests(getWidget<MyGUI::Widget>(QuestsBTN));
}
}
else
{
// Close the journal window
notifyClose(getWidget<MyGUI::Widget>(CloseBTN));
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Quests
{
// Show the quest overlay
notifyOptions(getWidget<MyGUI::Widget>(OptionsBTN));
if (!mQuestMode)
notifyQuests(getWidget<MyGUI::Widget>(QuestsBTN));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Topics
{
// Show the topics overlay
notifyOptions(getWidget<MyGUI::Widget>(OptionsBTN));
if (mQuestMode)
notifyTopics(getWidget<MyGUI::Widget>(TopicsBTN));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
notifyPrevPage(getWidget<MyGUI::Widget>(PrevPageBTN));
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT
|| arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
notifyNextPage(getWidget<MyGUI::Widget>(NextPageBTN));
return true;
}
void setControllerFocusedQuest(size_t index)
{
size_t listSize = mButtons.size();
if (mSelectedQuest < listSize)
mButtons[mSelectedQuest]->setTextColour(MyGUI::Colour::Black);
mSelectedQuest = index;
if (mSelectedQuest < listSize)
{
mButtons[mSelectedQuest]->setTextColour(MWGui::journalHeaderColour);
// Scroll the list to keep the active item in view
Gui::MWList* list = getWidget<Gui::MWList>(mQuestMode ? QuestsList : TopicsList);
int offset = 0;
for (int i = 4; i < static_cast<int>(mSelectedQuest); i++)
offset += mButtons[i]->getHeight();
list->setViewOffset(-offset);
}
}
};
}

View file

@ -31,6 +31,16 @@ namespace MWGui
void setVisible(bool newValue) override = 0;
std::string_view getWindowIdForLua() const override { return "Journal"; }
size_t mIndexRowCount;
std::vector<MyGUI::Button*> mButtons;
size_t mSelectedQuest = 0;
size_t mSelectedIndex = 0;
void moveSelectedIndex(int offset);
void setIndexControllerFocus(bool focused);
void setControllerFocusedQuest(size_t index);
bool optionsModeButtonHandler(const SDL_ControllerButtonEvent& arg);
};
}

View file

@ -8,6 +8,7 @@
#include <MyGUI_UString.h>
#include <components/fallback/fallback.hpp>
#include <components/settings/values.hpp>
#include <components/widgets/box.hpp>
#include "../mwbase/environment.hpp"
@ -48,13 +49,12 @@ namespace MWGui
{
const auto& store = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>();
const size_t perCol
= static_cast<size_t>(std::ceil(store.getSize() / static_cast<float>(std::size(sColumnOffsets))));
mPerCol = static_cast<size_t>(std::ceil(store.getSize() / static_cast<float>(std::size(sColumnOffsets))));
size_t i = 0;
for (const ESM::Attribute& attribute : store)
{
const int offset = sColumnOffsets[i / perCol];
const int row = static_cast<int>(i % perCol);
const int offset = sColumnOffsets[i / mPerCol];
const int row = static_cast<int>(i % mPerCol);
Widgets widgets;
widgets.mMultiplier = mAssignWidget->createWidget<MyGUI::TextBox>(
"SandTextVCenter", { offset, 20 * row, 100, 20 }, MyGUI::Align::Default);
@ -72,12 +72,13 @@ namespace MWGui
widgets.mButton->setCaption(attribute.mName);
widgets.mValue = hbox->createWidget<Gui::AutoSizedTextBox>("SandText", {}, MyGUI::Align::Default);
mAttributeWidgets.emplace(attribute.mId, widgets);
mAttributeButtons.emplace_back(widgets.mButton);
++i;
}
mAssignWidget->setVisibleVScroll(false);
mAssignWidget->setCanvasSize(MyGUI::IntSize(
mAssignWidget->getWidth(), std::max(mAssignWidget->getHeight(), static_cast<int>(20 * perCol))));
mAssignWidget->getWidth(), std::max(mAssignWidget->getHeight(), static_cast<int>(20 * mPerCol))));
mAssignWidget->setVisibleVScroll(true);
mAssignWidget->setViewOffset(MyGUI::IntPoint());
}
@ -90,6 +91,15 @@ namespace MWGui
mCoins.push_back(image);
}
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mX = "#{sDone}";
mOkButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {})));
}
center();
}
@ -217,6 +227,13 @@ namespace MWGui
center();
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
for (size_t i = 0; i < mAttributeButtons.size(); i++)
setControllerFocus(mAttributeButtons, i, i == 0);
}
// Play LevelUp Music
MWBase::Environment::get().getSoundManager()->streamMusic(MWSound::triumphMusic, MWSound::MusicType::Normal);
}
@ -363,4 +380,44 @@ namespace MWGui
return ret;
}
bool LevelupDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mAttributeButtons.size())
onAttributeClicked(mAttributeButtons[mControllerFocus]);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Item Gold Up"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkButtonClicked(mOkButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
setControllerFocus(mAttributeButtons, mControllerFocus, false);
if (mControllerFocus % mPerCol == 0)
mControllerFocus += mPerCol - 1;
else
mControllerFocus--;
setControllerFocus(mAttributeButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
setControllerFocus(mAttributeButtons, mControllerFocus, false);
if (mControllerFocus % mPerCol == mPerCol - 1)
mControllerFocus -= mPerCol - 1;
else
mControllerFocus++;
setControllerFocus(mAttributeButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
setControllerFocus(mAttributeButtons, mControllerFocus, false);
mControllerFocus = (mControllerFocus + mPerCol) % mAttributeButtons.size();
setControllerFocus(mAttributeButtons, mControllerFocus, true);
}
return true;
}
}

View file

@ -37,6 +37,7 @@ namespace MWGui
std::vector<ESM::Attribute::AttributeID> mSpentAttributes;
size_t mPerCol;
unsigned int mCoinCount;
void onOkButtonClicked(MyGUI::Widget* sender);
@ -49,6 +50,10 @@ namespace MWGui
std::string_view getLevelupClassImage(
const int combatIncreases, const int magicIncreases, const int stealthIncreases);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
std::vector<MyGUI::Button*> mAttributeButtons;
size_t mControllerFocus;
};
}

View file

@ -1,6 +1,7 @@
#include "mainmenu.hpp"
#include <MyGUI_Gui.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_TextBox.h>
@ -105,6 +106,7 @@ namespace MWGui
constexpr VFS::Path::NormalizedView menuBackgroundVideo("video/menu_background.bik");
mHasAnimatedMenu = mVFS->exists(menuBackgroundVideo);
mDisableGamepadCursor = Settings::gui().mControllerMenus;
updateMenu();
}
@ -163,9 +165,7 @@ namespace MWGui
const std::string& name = *sender->getUserData<std::string>();
winMgr->playSound(ESM::RefId::stringRefId("Menu Click"));
if (name == "return")
{
winMgr->removeGuiMode(GM_MainMenu);
}
else if (name == "credits")
winMgr->playVideo("mw_credits.bik", true);
else if (name == "exitgame")
@ -208,6 +208,32 @@ namespace MWGui
}
}
bool MainMenu::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Space, 0, false);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B || arg.button == SDL_CONTROLLER_BUTTON_START)
{
if (mButtons["return"]->getVisible())
onButtonClicked(mButtons["return"]);
else
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Escape, 0, false);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::LeftShift);
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Tab, 0, false);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::LeftShift);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Tab, 0, false);
}
return true;
}
void MainMenu::showBackground(bool show)
{
if (mVideo && !show)

View file

@ -49,6 +49,7 @@ namespace MWGui
MainMenu(int w, int h, const VFS::Manager* vfs, const std::string& versionDescription);
void onResChange(int w, int h) override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setVisible(bool visible) override;

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>
@ -830,6 +831,14 @@ namespace MWGui
mGlobalMap->setVisible(global);
mLocalMap->setVisible(!global);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mB = "#{sBack}";
mControllerButtons.mX = global ? "#{sLocal}" : "#{sWorld}";
mControllerButtons.mY = "#{sCenter}";
mControllerButtons.mDpad = Settings::map().mAllowZooming ? "" : "#{sMove}";
}
}
void MapWindow::onNoteEditOk()
@ -1018,7 +1027,20 @@ namespace MWGui
void MapWindow::setVisible(bool visible)
{
WindowBase::setVisible(visible);
mButton->setVisible(visible && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_None);
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
mButton->setVisible(visible && mode != MWGui::GM_None);
if (Settings::gui().mControllerMenus && mode == MWGui::GM_None && pinned() && visible)
{
// Restore the window to pinned size.
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
const float x = Settings::windows().mMapX * viewSize.width;
const float y = Settings::windows().mMapY * viewSize.height;
const float w = Settings::windows().mMapW * viewSize.width;
const float h = Settings::windows().mMapH * viewSize.height;
window->setCoord(x, y, w, h);
}
}
void MapWindow::renderGlobalMap()
@ -1206,6 +1228,8 @@ namespace MWGui
mLocalMap->setVisible(!global);
mButton->setCaptionWithReplacing(global ? "#{sLocal}" : "#{sWorld}");
mControllerButtons.mX = global ? "#{sLocal}" : "#{sWorld}";
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
}
void MapWindow::onPinToggled()
@ -1217,7 +1241,9 @@ namespace MWGui
void MapWindow::onTitleDoubleClicked()
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
if (Settings::gui().mControllerMenus)
return;
else if (MyGUI::InputManager::getInstance().isShiftPressed())
MWBase::Environment::get().getWindowManager()->toggleMaximized(this);
else if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map);
@ -1367,6 +1393,73 @@ 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);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_Y)
{
centerView();
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
shiftMap(0, 100);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
shiftMap(0, -100);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
shiftMap(100, 0);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
shiftMap(-100, 0);
return true;
}
void MapWindow::shiftMap(int dx, int dy)
{
if (dx == 0 && dy == 0)
return;
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));
}
}
void MapWindow::setActiveControllerWindow(bool active)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
if (winMgr->getMode() == MWGui::GM_Inventory)
{
// Fill the screen, or limit to a certain size on large screens. Size chosen to
// show the entire local map without scrolling.
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::IntSize canvasSize = mLocalMap->getCanvasSize();
MyGUI::IntSize borderSize = mMainWidget->getSize() - mMainWidget->getClientWidget()->getSize();
int width = std::min(viewSize.width, canvasSize.width + borderSize.width);
int height = std::min(winMgr->getControllerMenuHeight(), canvasSize.height + borderSize.height);
int x = (viewSize.width - width) / 2;
int y = (viewSize.height - height) / 2;
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(x, active ? y : viewSize.height + 1, width, height);
}
WindowBase::setActiveControllerWindow(active);
}
// -------------------------------------------------------------------
EditNoteDialog::EditNoteDialog()
@ -1380,6 +1473,12 @@ namespace MWGui
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onCancelButtonClicked);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onOkButtonClicked);
mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onDeleteButtonClicked);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mA = "#{Interface:OK}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
void EditNoteDialog::showDeleteButton(bool show)
@ -1407,6 +1506,13 @@ namespace MWGui
WindowModal::onOpen();
center();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);
if (Settings::gui().mControllerMenus)
{
mControllerFocus = getDeleteButtonShown() ? 1 : 0;
mOkButton->setStateSelected(true);
mCancelButton->setStateSelected(false);
}
}
void EditNoteDialog::onCancelButtonClicked(MyGUI::Widget* sender)
@ -1424,6 +1530,78 @@ namespace MWGui
eventDeleteClicked();
}
ControllerButtons* EditNoteDialog::getControllerButtons()
{
mControllerButtons.mX = getDeleteButtonShown() ? "#{sDelete}" : "";
return &mControllerButtons;
}
bool EditNoteDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (getDeleteButtonShown())
{
if (mControllerFocus == 0)
onDeleteButtonClicked(mDeleteButton);
else if (mControllerFocus == 1)
onOkButtonClicked(mOkButton);
else
onCancelButtonClicked(mCancelButton);
}
else
{
if (mControllerFocus == 0)
onOkButtonClicked(mOkButton);
else
onCancelButtonClicked(mCancelButton);
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
if (getDeleteButtonShown())
onDeleteButtonClicked(mDeleteButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
{
if (getDeleteButtonShown())
{
mControllerFocus = wrap(mControllerFocus - 1, 3);
mDeleteButton->setStateSelected(mControllerFocus == 0);
mOkButton->setStateSelected(mControllerFocus == 1);
mCancelButton->setStateSelected(mControllerFocus == 2);
}
else
{
mControllerFocus = 0;
mOkButton->setStateSelected(mControllerFocus == 0);
mCancelButton->setStateSelected(mControllerFocus == 1);
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
if (getDeleteButtonShown())
{
mControllerFocus = wrap(mControllerFocus + 1, 3);
mDeleteButton->setStateSelected(mControllerFocus == 0);
mOkButton->setStateSelected(mControllerFocus == 1);
mCancelButton->setStateSelected(mControllerFocus == 2);
}
else
{
mControllerFocus = 1;
mOkButton->setStateSelected(mControllerFocus == 0);
mCancelButton->setStateSelected(mControllerFocus == 1);
}
}
return true;
}
bool LocalMapBase::MarkerUserData::isPositionExplored() const
{
if (!mLocalMapRender)

View file

@ -212,6 +212,8 @@ namespace MWGui
EventHandle_Void eventDeleteClicked;
EventHandle_Void eventOkClicked;
ControllerButtons* getControllerButtons() override;
private:
void onCancelButtonClicked(MyGUI::Widget* sender);
void onOkButtonClicked(MyGUI::Widget* sender);
@ -221,6 +223,9 @@ namespace MWGui
MyGUI::Button* mOkButton;
MyGUI::Button* mCancelButton;
MyGUI::Button* mDeleteButton;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
int mControllerFocus;
};
class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop
@ -265,6 +270,10 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Map"; }
protected:
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& 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);
@ -283,6 +292,7 @@ namespace MWGui
void setGlobalMapMarkerTooltip(MyGUI::Widget* widget, int x, int y);
float getMarkerSize(size_t agregatedWeight) const;
void resizeGlobalMap();
void shiftMap(int dx, int dy);
void worldPosToGlobalMapImageSpace(float x, float z, float& imageX, float& imageY) const;
MyGUI::IntCoord createMarkerCoords(float x, float y, float agregatedWeight) const;
MyGUI::Widget* createMarker(const std::string& name, float x, float y, float agregatedWeight);

View file

@ -28,6 +28,13 @@ namespace MWGui
getWidget(mGoldLabel, "PlayerGold");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick);
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{sRepair}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
void MerchantRepair::setPtr(const MWWorld::Ptr& actor)
@ -38,6 +45,7 @@ namespace MWGui
while (mList->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0));
mButtons.clear();
const int lineHeight = Settings::gui().mFontSize + 2;
int currentY = 0;
@ -101,6 +109,15 @@ namespace MWGui
button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel);
button->setUserString("ToolTipType", "ItemPtr");
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick);
if (price <= playerGold)
mButtons.emplace_back(std::make_pair(button, mButtons.size()));
}
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
if (mButtons.size() > 0)
mButtons[0].first->setStateSelected(true);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
@ -157,4 +174,49 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair);
}
bool MerchantRepair::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mButtons.size())
onRepairButtonClick(mButtons[mControllerFocus].first);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onOkButtonClick(mOkButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mButtons.size() <= 1)
return true;
mButtons[mControllerFocus].first->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus - 1, mButtons.size());
mButtons[mControllerFocus].first->setStateSelected(true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mButtons.size() <= 1)
return true;
mButtons[mControllerFocus].first->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus + 1, mButtons.size());
mButtons[mControllerFocus].first->setStateSelected(true);
}
// Scroll the list to keep the active item in view
if (mControllerFocus < mButtons.size())
{
size_t line = mButtons[mControllerFocus].second;
if (line <= 5)
mList->setViewOffset(MyGUI::IntPoint(0, 0));
else
{
const int lineHeight = Settings::gui().mFontSize + 2;
mList->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5)));
}
}
return true;
}
}

View file

@ -22,13 +22,18 @@ namespace MWGui
MyGUI::ScrollView* mList;
MyGUI::Button* mOkButton;
MyGUI::TextBox* mGoldLabel;
/// List of enabled/repairable items and their index in the full list.
std::vector<std::pair<MyGUI::Button*, size_t>> mButtons;
MWWorld::Ptr mActor;
size_t mControllerFocus;
protected:
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void onRepairButtonClick(MyGUI::Widget* sender);
void onOkButtonClick(MyGUI::Widget* sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}

View file

@ -8,6 +8,7 @@
#include <components/debug/debuglog.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
@ -217,7 +218,7 @@ namespace MWGui
}
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message,
const std::vector<std::string>& buttons, bool immediate, int defaultFocus)
const std::vector<std::string>& buttons, bool immediate, size_t defaultFocus)
: WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode()
? "openmw_interactive_messagebox_notransp.layout"
: "openmw_interactive_messagebox.layout")
@ -225,6 +226,7 @@ namespace MWGui
, mButtonPressed(-1)
, mDefaultFocus(defaultFocus)
, mImmediate(immediate)
, mControllerFocus(0)
{
int textPadding = 10; // padding between text-widget and main-widget
int textButtonPadding = 10; // padding between the text-widget und the button-widget
@ -280,6 +282,22 @@ namespace MWGui
}
}
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{Interface:OK}";
// If we have more than one button, we need to set the focus to the first one.
if (mButtons.size() > 1)
{
mControllerFocus = 0;
if (mDefaultFocus < mButtons.size())
mControllerFocus = mDefaultFocus;
for (size_t i = 0; i < mButtons.size(); ++i)
mButtons[i]->setStateSelected(i == mControllerFocus);
}
}
MyGUI::IntSize mainWidgetSize;
if (buttonsWidth < textSize.width)
{
@ -380,7 +398,7 @@ namespace MWGui
MyGUI::Widget* InteractiveMessageBox::getDefaultKeyFocus()
{
if (mDefaultFocus >= 0 && mDefaultFocus < static_cast<int>(mButtons.size()))
if (mDefaultFocus < mButtons.size())
return mButtons[mDefaultFocus];
auto& languageManager = MyGUI::LanguageManager::getInstance();
std::vector<MyGUI::UString> keywords{ languageManager.replaceTags("#{sOk}"),
@ -431,4 +449,45 @@ namespace MWGui
return mButtonPressed;
}
bool InteractiveMessageBox::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (!mButtons.empty())
{
if (mControllerFocus >= mButtons.size())
mControllerFocus = mButtons.size() - 1;
buttonActivated(mButtons[mControllerFocus]);
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
if (mButtons.size() == 1)
buttonActivated(mButtons[0]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
{
if (mButtons.size() <= 1)
return true;
if (mButtons.size() == 2 && mControllerFocus == 0)
return true;
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
if (mButtons.size() <= 1)
return true;
if (mButtons.size() == 2 && mControllerFocus == 1)
return true;
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
return true;
}
}

View file

@ -93,7 +93,7 @@ namespace MWGui
{
public:
InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message,
const std::vector<std::string>& buttons, bool immediate, int defaultFocus);
const std::vector<std::string>& buttons, bool immediate, size_t defaultFocus);
void mousePressed(MyGUI::Widget* _widget);
int readPressedButton();
@ -103,6 +103,8 @@ namespace MWGui
bool mMarkedToDelete;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
void buttonActivated(MyGUI::Widget* _widget);
@ -112,8 +114,9 @@ namespace MWGui
std::vector<MyGUI::Button*> mButtons;
int mButtonPressed;
int mDefaultFocus;
size_t mDefaultFocus;
bool mImmediate;
size_t mControllerFocus;
};
}

View file

@ -11,6 +11,7 @@
#include <components/esm3/quickkeys.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/settings/values.hpp>
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
@ -39,7 +40,7 @@ namespace MWGui
, mKey(std::vector<keyData>(10))
, mSelected(nullptr)
, mActivated(nullptr)
, mControllerFocus(0)
{
getWidget(mOkButton, "OKButton");
getWidget(mInstructionLabel, "InstructionLabel");
@ -58,6 +59,12 @@ namespace MWGui
unassign(&mKey[i]);
}
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:OK}";
}
}
void QuickKeysMenu::clear()
@ -108,6 +115,13 @@ namespace MWGui
{
validate(index);
}
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
for (size_t i = 0; i < mKey.size(); i++)
mKey[i].button->setControllerFocus(i == mControllerFocus);
}
}
void QuickKeysMenu::onClose()
@ -454,11 +468,45 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->updateSpellWindow();
}
bool QuickKeysMenu::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
onQuickKeyButtonClicked(mKey[mControllerFocus].button);
if (arg.button == SDL_CONTROLLER_BUTTON_B)
onOkButtonClicked(mOkButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
mControllerFocus = (mControllerFocus + 5) % 10;
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
{
if (mControllerFocus == 0)
mControllerFocus = 4;
else if (mControllerFocus == 5)
mControllerFocus = 9;
else
mControllerFocus--;
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
if (mControllerFocus == 4)
mControllerFocus = 0;
else if (mControllerFocus == 9)
mControllerFocus = 5;
else
mControllerFocus++;
}
for (size_t i = 0; i < mKey.size(); i++)
mKey[i].button->setControllerFocus(i == mControllerFocus);
return true;
}
// ---------------------------------------------------------------------------------------------------------
QuickKeysMenuAssign::QuickKeysMenuAssign(QuickKeysMenu* parent)
: WindowModal("openmw_quickkeys_menu_assign.layout")
, mParent(parent)
, mControllerFocus(0)
{
getWidget(mLabel, "Label");
getWidget(mItemButton, "ItemButton");
@ -489,9 +537,45 @@ namespace MWGui
mCancelButton->setCoord((maxWidth - mCancelButton->getTextSize().width - 24) / 2 + 8, mCancelButton->getTop(),
mCancelButton->getTextSize().width + 24, mCancelButton->getHeight());
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mItemButton->setStateSelected(true);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
center();
}
bool QuickKeysMenuAssign::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus == 0)
mParent->onItemButtonClicked(mItemButton);
else if (mControllerFocus == 1)
mParent->onMagicButtonClicked(mMagicButton);
else if (mControllerFocus == 2)
mParent->onUnassignButtonClicked(mUnassignButton);
else if (mControllerFocus == 3)
mParent->onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
mParent->onCancelButtonClicked(mCancelButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
mControllerFocus = wrap(mControllerFocus - 1, 4);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
mControllerFocus = wrap(mControllerFocus + 1, 4);
mItemButton->setStateSelected(mControllerFocus == 0);
mMagicButton->setStateSelected(mControllerFocus == 1);
mUnassignButton->setStateSelected(mControllerFocus == 2);
mCancelButton->setStateSelected(mControllerFocus == 3);
return true;
}
void QuickKeysMenu::write(ESM::ESMWriter& writer)
{
writer.startRecord(ESM::REC_KEYS);
@ -601,6 +685,12 @@ namespace MWGui
mMagicList->setHighlightSelected(false);
mMagicList->eventSpellClicked += MyGUI::newDelegate(this, &MagicSelectionDialog::onModelIndexSelected);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
center();
}
@ -632,4 +722,13 @@ namespace MWGui
mParent->onAssignMagic(spell.mId);
}
bool MagicSelectionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancelButtonClicked(mCancelButton);
else
mMagicList->onControllerButton(arg.button);
return true;
}
}

View file

@ -73,6 +73,9 @@ namespace MWGui
inline void validate(int index);
void unassign(keyData* key);
void assignItem(MWWorld::Ptr item);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
size_t mControllerFocus;
};
class QuickKeysMenuAssign : public WindowModal
@ -88,6 +91,9 @@ namespace MWGui
MyGUI::Button* mCancelButton;
QuickKeysMenu* mParent;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
int mControllerFocus;
};
class MagicSelectionDialog : public WindowModal
@ -106,6 +112,9 @@ namespace MWGui
void onCancelButtonClicked(MyGUI::Widget* sender);
void onModelIndexSelected(SpellModel::ModelIndex index);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
int mControllerFocus;
};
}

View file

@ -23,16 +23,6 @@
namespace
{
int wrap(int index, int max)
{
if (index < 0)
return max - 1;
else if (index >= max)
return 0;
else
return index;
}
bool sortRaces(const std::pair<ESM::RefId, std::string>& left, const std::pair<ESM::RefId, std::string>& right)
{
return left.second.compare(right.second) < 0;
@ -108,15 +98,23 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials"));
getWidget(mSpellPowerList, "SpellPowerList");
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked);
getWidget(mBackButton, "BackButton");
mBackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->setCaption(
getWidget(mOkButton, "OKButton");
mOkButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {})));
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mLStick = "#{sMouse}";
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
mControllerButtons.mY = "#{sSex}";
mControllerButtons.mL1 = "#{sHair}";
mControllerButtons.mR1 = "#{sFace}";
}
updateRaces();
updateSkills();
@ -129,8 +127,17 @@ namespace MWGui
getWidget(okButton, "OKButton");
if (shown)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {})));
mControllerButtons.mX = "#{sNext}";
}
else if (Settings::gui().mControllerMenus)
{
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {})));
mControllerButtons.mX = "#{sDone}";
}
else
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {})));
@ -462,6 +469,55 @@ namespace MWGui
}
}
bool RaceDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onBackClicked(mBackButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkClicked(mOkButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_Y)
{
onSelectNextGender(nullptr);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{
onSelectNextHair(nullptr);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
onSelectNextFace(nullptr);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mRaceList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mRaceList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
}
return true;
}
bool RaceDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg)
{
if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX)
{
onPreviewScroll(nullptr, arg.value < 0 ? 1 : -1);
return true;
}
return false;
}
const ESM::NPC& RaceDialog::getResult() const
{
return mPreview->getPrototype();

View file

@ -101,6 +101,8 @@ namespace MWGui
MyGUI::ImageBox* mPreviewImage;
MyGUI::ListBox* mRaceList;
MyGUI::ScrollBar* mHeadRotate;
MyGUI::Button* mBackButton;
MyGUI::Button* mOkButton;
MyGUI::Widget* mSkillList;
std::vector<MyGUI::Widget*> mSkillItems;
@ -118,6 +120,9 @@ namespace MWGui
std::unique_ptr<MyGUI::ITexture> mPreviewTexture;
bool mPreviewDirty;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override;
};
}
#endif

View file

@ -39,6 +39,10 @@ namespace MWGui
mBox->setDisplayMode(ItemChargeView::DisplayMode_EnchantmentCharge);
mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem);
mControllerButtons.mA = "#{OMWEngine:RechargeSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mY = "#{sSoulGem}";
}
void Recharge::onOpen()
@ -136,4 +140,18 @@ namespace MWGui
updateView();
}
bool Recharge::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if ((arg.button == SDL_CONTROLLER_BUTTON_A && !mGemBox->getVisible()) || arg.button == SDL_CONTROLLER_BUTTON_Y)
{
onSelectItem(mGemIcon);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancel(mCancelButton);
else
mBox->onControllerButton(arg.button);
return true;
}
}

View file

@ -51,6 +51,8 @@ namespace MWGui
void onItemClicked(MyGUI::Widget* sender, const MWWorld::Ptr& item);
void onCancel(MyGUI::Widget* sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}

View file

@ -39,6 +39,10 @@ namespace MWGui
mRepairBox->setDisplayMode(ItemChargeView::DisplayMode_Health);
mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem);
mControllerButtons.mA = "#{sRepair}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mY = "#{OMWEngine:RepairTool}";
}
void Repair::onOpen()
@ -150,4 +154,18 @@ namespace MWGui
updateRepairView();
}
bool Repair::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if ((arg.button == SDL_CONTROLLER_BUTTON_A && !mToolBox->getVisible()) || arg.button == SDL_CONTROLLER_BUTTON_Y)
{
onSelectItem(mToolIcon);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancel(mCancelButton);
else
mRepairBox->onControllerButton(arg.button);
return true;
}
}

View file

@ -50,6 +50,8 @@ namespace MWGui
void onRepairItem(MyGUI::Widget* sender, const MWWorld::Ptr& ptr);
void onCancel(MyGUI::Widget* sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
};
}

View file

@ -36,6 +36,7 @@ namespace MWGui
ReviewDialog::ReviewDialog()
: WindowModal("openmw_chargen_review.layout")
, mUpdateSkillArea(false)
, mControllerFocus(5)
{
// Centre dialog
center();
@ -46,21 +47,25 @@ namespace MWGui
getWidget(button, "NameButton");
adjustButtonSize(button);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);
mButtons.push_back(button);
getWidget(mRaceWidget, "RaceText");
getWidget(button, "RaceButton");
adjustButtonSize(button);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);
mButtons.push_back(button);
getWidget(mClassWidget, "ClassText");
getWidget(button, "ClassButton");
adjustButtonSize(button);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);
mButtons.push_back(button);
getWidget(mBirthSignWidget, "SignText");
getWidget(button, "SignButton");
adjustButtonSize(button);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);
mButtons.push_back(button);
// Setup dynamic stats
getWidget(mHealth, "Health");
@ -108,10 +113,22 @@ namespace MWGui
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked);
mButtons.push_back(backButton);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked);
mButtons.push_back(okButton);
if (Settings::gui().mControllerMenus)
{
setControllerFocus(mButtons, mControllerFocus, true);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
mControllerButtons.mX = "#{sDone}";
okButton->setCaption(
MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {})));
}
}
void ReviewDialog::onOpen()
@ -522,4 +539,54 @@ namespace MWGui
MyGUI::IntPoint(0, static_cast<int>(mSkillView->getViewOffset().top + _rel * 0.3)));
}
bool ReviewDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
switch (mControllerFocus)
{
case 0:
onNameClicked(mButtons[0]);
break;
case 1:
onRaceClicked(mButtons[1]);
break;
case 2:
onClassClicked(mButtons[2]);
break;
case 3:
onBirthSignClicked(mButtons[3]);
break;
case 4:
onBackClicked(mButtons[4]);
break;
case 5:
onOkClicked(mButtons[5]);
break;
}
return true;
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onBackClicked(mButtons[4]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkClicked(mButtons[5]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
{
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
setControllerFocus(mButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mButtons.size());
setControllerFocus(mButtons, mControllerFocus, true);
}
return true;
}
}

View file

@ -72,6 +72,7 @@ namespace MWGui
void onBirthSignClicked(MyGUI::Widget* _sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
void addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
@ -100,6 +101,10 @@ namespace MWGui
std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information
bool mUpdateSkillArea;
// 0 = Name, 1 = Race, 2 = Class, 3 = BirthSign, 4 = Back, 5 = OK
std::vector<MyGUI::Button*> mButtons;
int mControllerFocus;
};
}
#endif

View file

@ -17,6 +17,7 @@
#include <components/files/conversion.hpp>
#include <components/files/memorystream.hpp>
#include <components/l10n/manager.hpp>
#include <components/misc/strings/format.hpp>
#include <components/misc/strings/lower.hpp>
#include <components/misc/timeconvert.hpp>
#include <components/myguiplatform/myguitexture.hpp>
@ -28,6 +29,7 @@
#include "../mwbase/world.hpp"
#include "../mwworld/datetimemanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/timestamp.hpp"
#include "../mwstate/character.hpp"
@ -64,6 +66,9 @@ namespace MWGui
// To avoid accidental deletions
mDeleteButton->setNeedKeyFocus(false);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
void SaveGameDialog::onSlotActivated(MyGUI::ListBox* sender, size_t pos)
@ -145,6 +150,28 @@ namespace MWGui
WindowModal::onOpen();
mSaveNameEdit->setCaption({});
if (Settings::gui().mControllerMenus && mSaving)
{
// For controller mode, set a default save file name. The format is
// "Day 24 - Last Steed 7 p.m."
const MWWorld::DateTimeManager& timeManager = *MWBase::Environment::get().getWorld()->getTimeManager();
std::string_view month = timeManager.getMonthName();
int hour = static_cast<int>(timeManager.getTimeStamp().getHour());
bool pm = hour >= 12;
if (hour >= 13)
hour -= 12;
if (hour == 0)
hour = 12;
ESM::EpochTimeStamp currentDate = timeManager.getEpochTimeStamp();
std::string daysPassed
= Misc::StringUtils::format("#{Calendar:day} %i", timeManager.getTimeStamp().getDay());
std::string_view formattedHour(pm ? "#{Calendar:pm}" : "#{Calendar:am}");
std::string autoFilename = Misc::StringUtils::format(
"%s - %i %s %i %s", daysPassed, currentDate.mDay, month, hour, formattedHour);
mSaveNameEdit->setCaptionWithReplacing(autoFilename);
}
if (mSaving)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);
else
@ -159,6 +186,13 @@ namespace MWGui
mSaveList->removeAllItems();
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
if (Settings::gui().mControllerMenus)
{
mOkButtonFocus = true;
mOkButton->setStateSelected(true);
mCancelButton->setStateSelected(false);
}
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
if (mgr->characterBegin() == mgr->characterEnd())
return;
@ -491,4 +525,55 @@ namespace MWGui
mScreenshotTexture = std::make_unique<MyGUIPlatform::OSGTexture>(texture);
mScreenshot->setRenderItemTexture(mScreenshotTexture.get());
}
ControllerButtons* SaveGameDialog::getControllerButtons()
{
mControllerButtons.mY = mSaving ? "" : "#{OMWEngine:LoadingSelectCharacter}";
return &mControllerButtons;
}
bool SaveGameDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mOkButtonFocus)
onOkButtonClicked(mOkButton);
else
onCancelButtonClicked(mCancelButton);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_Y)
{
size_t index = mCharacterSelection->getIndexSelected();
index = wrap(index + 1, mCharacterSelection->getItemCount());
mCharacterSelection->setIndexSelected(index);
onCharacterSelected(mCharacterSelection, index);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mSaveList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setKeyFocusWidget(mSaveList);
winMgr->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false);
}
else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mOkButtonFocus)
|| (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mOkButtonFocus))
{
mOkButtonFocus = !mOkButtonFocus;
mOkButton->setStateSelected(mOkButtonFocus);
mCancelButton->setStateSelected(!mOkButtonFocus);
}
return true;
}
}

View file

@ -24,6 +24,8 @@ namespace MWGui
void setLoadOrSave(bool load);
ControllerButtons* getControllerButtons() override;
private:
void confirmDeleteSave();
@ -67,6 +69,9 @@ namespace MWGui
const MWState::Character* mCurrentCharacter;
const MWState::Slot* mCurrentSlot;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
bool mOkButtonFocus = true;
};
}

View file

@ -7,6 +7,7 @@
#include <components/widgets/imagebutton.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
@ -38,6 +39,10 @@ namespace MWGui
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);
mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);
mControllerScrollWidget = mTextView;
mControllerButtons.mB = "#{Interface:Close}";
mControllerButtons.mDpad = "#{sScrolldown}";
center();
}
@ -115,4 +120,32 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
}
void ScrollWindow::onClose()
{
if (Settings::gui().mControllerMenus)
MWBase::Environment::get().getInputManager()->setGamepadGuiCursorEnabled(true);
BookWindowBase::onClose();
}
ControllerButtons* ScrollWindow::getControllerButtons()
{
mControllerButtons.mA = mTakeButton->getVisible() ? "#{sTake}" : "";
return &mControllerButtons;
}
bool ScrollWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mTakeButton->getVisible())
onTakeButtonClicked(mTakeButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCloseButtonClicked(mCloseButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
return false; // Fall through to keyboard
return true;
}
}

View file

@ -20,15 +20,19 @@ namespace MWGui
void setPtr(const MWWorld::Ptr& scroll) override;
void setInventoryAllowed(bool allowed);
void onClose() override;
void onResChange(int, int) override { center(); }
std::string_view getWindowIdForLua() const override { return "Scroll"; }
ControllerButtons* getControllerButtons() override;
protected:
void onCloseButtonClicked(MyGUI::Widget* _sender);
void onTakeButtonClicked(MyGUI::Widget* _sender);
void setTakeButtonShow(bool show);
void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
Gui::ImageButton* mCloseButton;

View file

@ -462,6 +462,10 @@ namespace MWGui
i++;
}
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:OK}";
mControllerButtons.mLStick = "#{sMouse}";
}
void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/)
@ -471,7 +475,7 @@ namespace MWGui
void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender)
{
setVisible(false);
MWBase::Environment::get().getWindowManager()->toggleSettingsWindow();
}
void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index)
@ -1139,4 +1143,32 @@ namespace MWGui
mResolutionList->setScrollPosition(0);
mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0));
}
bool SettingsWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onOkButtonClicked(mOkButton);
return true;
}
else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{
size_t index = mSettingsTab->getIndexSelected();
index = wrap(index - 1, mSettingsTab->getItemCount());
mSettingsTab->setIndexSelected(index);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
return true;
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
size_t index = mSettingsTab->getIndexSelected();
index = wrap(index + 1, mSettingsTab->getItemCount());
mSettingsTab->setIndexSelected(index);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
return true;
}
return false;
}
}

View file

@ -29,6 +29,8 @@ namespace MWGui
void onResChange(int, int) override;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
protected:
MyGUI::TabControl* mSettingsTab;
MyGUI::Button* mOkButton;

View file

@ -9,6 +9,7 @@
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
@ -26,12 +27,21 @@ namespace MWGui
SpellBuyingWindow::SpellBuyingWindow()
: WindowBase("openmw_spell_buying_window.layout")
, mCurrentY(0)
, mControllerFocus(0)
{
getWidget(mCancelButton, "CancelButton");
getWidget(mPlayerGold, "PlayerGold");
getWidget(mSpellsView, "SpellsView");
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked);
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{sBuy}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mR3 = "#{sInfo}";
}
}
bool SpellBuyingWindow::sortSpells(const ESM::Spell* left, const ESM::Spell* right)
@ -71,6 +81,8 @@ namespace MWGui
toAdd->setUserString("SpellCost", std::to_string(spell.mData.mCost));
toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick);
mSpellsWidgetMap.insert(std::make_pair(toAdd, spell.mId));
if (price <= playerGold)
mSpellButtons.emplace_back(std::make_pair(toAdd, mSpellsWidgetMap.size()));
}
void SpellBuyingWindow::clearSpells()
@ -80,6 +92,7 @@ namespace MWGui
while (mSpellsView->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mSpellsView->getChildAt(0));
mSpellsWidgetMap.clear();
mSpellButtons.clear();
}
void SpellBuyingWindow::setPtr(const MWWorld::Ptr& actor)
@ -130,6 +143,20 @@ namespace MWGui
updateLabels();
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
if (mSpellButtons.size() > 0)
{
mSpellButtons[0].first->setStateSelected(true);
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
winMgr->setControllerTooltip(Settings::gui().mControllerTooltips);
if (winMgr->getControllerTooltip())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(mSpellButtons[0].first);
}
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
// scrollbar is hidden
mSpellsView->setVisibleVScroll(false);
@ -200,4 +227,62 @@ namespace MWGui
mSpellsView->setViewOffset(
MyGUI::IntPoint(0, static_cast<int>(mSpellsView->getViewOffset().top + _rel * 0.3f)));
}
bool SpellBuyingWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mSpellButtons.size())
onSpellButtonClick(mSpellButtons[mControllerFocus].first);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK)
{
// Toggle info tooltip
MWBase::Environment::get().getWindowManager()->setControllerTooltip(
!MWBase::Environment::get().getWindowManager()->getControllerTooltip());
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mSpellButtons.size() <= 1)
return true;
mSpellButtons[mControllerFocus].first->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus - 1, mSpellButtons.size());
mSpellButtons[mControllerFocus].first->setStateSelected(true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mSpellButtons.size() <= 1)
return true;
mSpellButtons[mControllerFocus].first->setStateSelected(false);
mControllerFocus = wrap(mControllerFocus + 1, mSpellButtons.size());
mSpellButtons[mControllerFocus].first->setStateSelected(true);
}
else
return true;
if (mControllerFocus < mSpellButtons.size())
{
// Scroll the list to keep the active item in view
size_t line = mSpellButtons[mControllerFocus].second;
if (line <= 5)
mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0));
else
{
const int lineHeight = Settings::gui().mFontSize + 2;
mSpellsView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5)));
}
// Warp the mouse to the selected spell to show the tooltip
if (MWBase::Environment::get().getWindowManager()->getControllerTooltip())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(mSpellButtons[mControllerFocus].first);
}
return true;
}
}

View file

@ -39,6 +39,8 @@ namespace MWGui
MyGUI::ScrollView* mSpellsView;
std::map<MyGUI::Widget*, ESM::RefId> mSpellsWidgetMap;
/// List of enabled/purchasable spells and their index in the full list.
std::vector<std::pair<MyGUI::Button*, size_t>> mSpellButtons;
void onCancelButtonClicked(MyGUI::Widget* _sender);
void onSpellButtonClick(MyGUI::Widget* _sender);
@ -55,6 +57,8 @@ namespace MWGui
private:
static bool sortSpells(const ESM::Spell* left, const ESM::Spell* right);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
size_t mControllerFocus;
};
}

View file

@ -7,11 +7,13 @@
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/settings/values.hpp>
#include <components/widgets/list.hpp>
#include <components/esm3/loadgmst.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
@ -25,8 +27,8 @@
#include "../mwmechanics/spellutil.hpp"
#include "class.hpp"
#include "textcolours.hpp"
#include "tooltips.hpp"
#include "widgets.hpp"
namespace
{
@ -95,6 +97,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.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mX = "#{Interface:OK}";
}
}
void EditEffectDialog::setConstantEffect(bool constant)
@ -154,6 +163,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 +205,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 +258,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 +386,195 @@ namespace MWGui
eventEffectModified(mEffect);
}
bool EditEffectDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
int prevFocus = mControllerFocus;
mControllerFocus = std::clamp(mControllerFocus, 0, static_cast<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);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
onCancelButtonClicked(mCancelButton);
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onOkButtonClicked(mOkButton);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mControllerFocus == 0)
mControllerFocus = static_cast<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 < static_cast<int>(mButtons.size()))
{
MyGUI::TextBox* button = mButtons[prevFocus];
if (button == mMagnitudeMinValue || button == mMagnitudeMaxValue || button == mDurationValue
|| button == mAreaValue)
{
button->setTextColour(textColours.normal);
}
else
{
static_cast<MyGUI::Button*>(button)->setStateSelected(false);
}
}
if (newFocus >= 0 && newFocus < static_cast<int>(mButtons.size()))
{
MyGUI::TextBox* button = mButtons[newFocus];
if (button == mMagnitudeMinValue || button == mMagnitudeMaxValue || button == mDurationValue
|| button == mAreaValue)
{
button->setTextColour(textColours.link);
}
else
{
static_cast<MyGUI::Button*>(button)->setStateSelected(true);
}
}
}
// ------------------------------------------------------------------------------------------------
SpellCreationDialog::SpellCreationDialog()
@ -361,6 +596,14 @@ namespace MWGui
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept);
setWidgets(mAvailableEffectsList, mUsedEffectsView);
if (Settings::gui().mControllerMenus)
{
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mX = "#{sBuy}";
mControllerButtons.mR3 = "#{sInfo}";
}
}
void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor)
@ -495,6 +738,22 @@ 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)
@ -566,6 +825,7 @@ namespace MWGui
mAvailableEffectsList->adjustSize();
mAvailableEffectsList->scrollToTop();
mAvailableButtons.clear();
for (const short effectId : knownEffects)
{
const std::string& name = MWBase::Environment::get()
@ -573,13 +833,27 @@ namespace MWGui
->get<ESM::GameSetting>()
.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);
if (MWBase::Environment::get().getWindowManager()->getControllerTooltip())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(mAvailableButtons[0]);
}
}
}
void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView)
@ -691,6 +965,7 @@ namespace MWGui
MyGUI::IntSize size(0, 0);
mEffectButtons.clear();
int i = 0;
for (const ESM::ENAMstruct& effectInfo : mEffects)
{
@ -723,6 +998,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
@ -760,4 +1037,102 @@ namespace MWGui
effect.mRange = ESM::RT_Self;
mConstantEffect = constant;
}
bool EffectEditorBase::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < static_cast<int>(mAvailableButtons.size()))
{
onAvailableEffectClicked(mAvailableButtons[mAvailableFocus]);
winMgr->playSound(ESM::RefId::stringRefId("Menu Click"));
}
else if (mRightColumn && mEffectFocus >= 0 && mEffectFocus < static_cast<int>(mEffectButtons.size()))
{
onEditEffect(mEffectButtons[mEffectFocus].second);
winMgr->playSound(ESM::RefId::stringRefId("Menu Click"));
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK)
{
// Toggle info tooltip
winMgr->setControllerTooltip(!mRightColumn && !winMgr->getControllerTooltip());
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mRightColumn && mEffectButtons.size() > 0)
{
if (mEffectFocus >= 0 && mEffectFocus < static_cast<int>(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 < static_cast<int>(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 < static_cast<int>(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 < static_cast<int>(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 < static_cast<int>(mEffectButtons.size()))
mEffectButtons[mEffectFocus].first->setStateSelected(false);
if (mAvailableFocus >= 0 && mAvailableFocus < static_cast<int>(mAvailableButtons.size()))
mAvailableButtons[mAvailableFocus]->setStateSelected(true);
winMgr->setControllerTooltip(Settings::gui().mControllerTooltips);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mRightColumn && mEffectButtons.size() > 0)
{
mRightColumn = true;
if (mAvailableFocus >= 0 && mAvailableFocus < static_cast<int>(mAvailableButtons.size()))
mAvailableButtons[mAvailableFocus]->setStateSelected(false);
if (mEffectFocus >= 0 && mEffectFocus < static_cast<int>(mEffectButtons.size()))
mEffectButtons[mEffectFocus].first->setStateSelected(true);
winMgr->setControllerTooltip(false);
}
else
return 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));
}
if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < static_cast<int>(mAvailableButtons.size()))
{
// Warp the mouse to the selected spell to show the tooltip
if (winMgr->getControllerTooltip())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(mAvailableButtons[mAvailableFocus]);
}
return true;
}
}

View file

@ -7,6 +7,7 @@
#include <components/esm3/loadspel.hpp>
#include "referenceinterface.hpp"
#include "widgets.hpp"
#include "windowbase.hpp"
namespace Gui
@ -83,13 +84,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<MyGUI::TextBox*> mButtons;
};
class EffectEditorBase
@ -142,8 +148,16 @@ namespace MWGui
virtual void notifyEffectsChanged() {}
virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg);
private:
Type mType;
int mAvailableFocus;
int mEffectFocus;
bool mRightColumn;
std::vector<MyGUI::Button*> mAvailableButtons;
std::vector<std::pair<Widgets::MWSpellEffectPtr, MyGUI::Button*>> mEffectButtons;
};
class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase
@ -166,6 +180,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;

View file

@ -9,7 +9,12 @@
#include <components/widgets/box.hpp>
#include <components/widgets/sharedstatebutton.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "tooltips.hpp"
#include "windowbase.hpp"
namespace MWGui
{
@ -28,6 +33,8 @@ namespace MWGui
: mScrollView(nullptr)
, mShowCostColumn(true)
, mHighlightSelected(true)
, mControllerActiveWindow(false)
, mControllerFocus(0)
{
}
@ -88,6 +95,8 @@ namespace MWGui
const int spellHeight = Settings::gui().mFontSize + 2;
mLines.clear();
mButtons.clear();
mGroupIndices.clear();
while (mScrollView->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));
@ -106,7 +115,9 @@ namespace MWGui
curType = spell.mType;
}
const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped";
std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped";
if (Settings::gui().mControllerMenus)
skin = spell.mActive ? "SpellTextEquippedController" : "SpellTextUnequippedController";
const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount);
Gui::SharedStateButton* t = mScrollView->createWidget<Gui::SharedStateButton>(
@ -115,6 +126,7 @@ namespace MWGui
t->setCaption(spell.mName + captionSuffix);
t->setTextAlign(MyGUI::Align::Left);
adjustSpellWidget(spell, i, t);
mButtons.emplace_back(std::make_pair(t, i));
if (!spell.mCostColumn.empty() && mShowCostColumn)
{
@ -132,7 +144,7 @@ namespace MWGui
mLines.emplace_back(t, costChance, i);
}
else
mLines.emplace_back(t, (MyGUI::Widget*)nullptr, i);
mLines.emplace_back(t, static_cast<MyGUI::Widget*>(nullptr), i);
t->setStateSelected(spell.mSelected);
}
@ -221,6 +233,12 @@ namespace MWGui
height += lineHeight;
}
if (Settings::gui().mControllerMenus)
{
mControllerFocus = wrap(mControllerFocus, mButtons.size());
updateControllerFocus(-1, mControllerFocus);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
// scrollbar is hidden
mScrollView->setVisibleVScroll(false);
@ -235,7 +253,7 @@ namespace MWGui
MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>(
"MW_HLine", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top);
separator->setNeedMouseFocus(false);
mLines.emplace_back(separator, (MyGUI::Widget*)nullptr, NoSpellIndex);
mLines.emplace_back(separator, static_cast<MyGUI::Widget*>(nullptr), NoSpellIndex);
}
MyGUI::TextBox* groupWidget = mScrollView->createWidget<Gui::TextBox>("SandBrightText",
@ -255,7 +273,9 @@ namespace MWGui
mLines.emplace_back(groupWidget, groupWidget2, NoSpellIndex);
}
else
mLines.emplace_back(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex);
mLines.emplace_back(groupWidget, static_cast<MyGUI::Widget*>(nullptr), NoSpellIndex);
mGroupIndices.push_back(mButtons.size());
}
void SpellView::setSize(const MyGUI::IntSize& _value)
@ -316,4 +336,124 @@ namespace MWGui
{
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
}
void SpellView::setActiveControllerWindow(bool active)
{
mControllerActiveWindow = active;
if (active)
update();
}
void SpellView::onControllerButton(const unsigned char button)
{
if (mButtons.empty())
return;
int prevFocus = mControllerFocus;
switch (button)
{
case SDL_CONTROLLER_BUTTON_A:
// Select the focused item, if any.
if (mControllerFocus >= 0 && mControllerFocus < static_cast<int>(mButtons.size()))
{
onSpellSelected(mButtons[mControllerFocus].first);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
// Toggle info tooltip
MWBase::Environment::get().getWindowManager()->setControllerTooltip(
!MWBase::Environment::get().getWindowManager()->getControllerTooltip());
break;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
mControllerFocus--;
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
mControllerFocus++;
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
mControllerFocus = std::max(0, mControllerFocus - 10);
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
mControllerFocus = std::min(mControllerFocus + 10, static_cast<int>(mButtons.size()) - 1);
break;
case 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;
}
break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
{
// Jump to first item in next group
int newFocus = mControllerFocus;
for (int groupIndex : mGroupIndices)
{
if (groupIndex > mControllerFocus)
{
newFocus = groupIndex;
break;
}
}
// If on last group, jump to bottom of whole list
if (newFocus == mControllerFocus)
newFocus = mButtons.size() - 1;
mControllerFocus = newFocus;
}
break;
default:
return;
}
mControllerFocus = wrap(mControllerFocus, mButtons.size());
if (prevFocus != mControllerFocus)
updateControllerFocus(prevFocus, mControllerFocus);
else
updateControllerFocus(-1, mControllerFocus);
}
void SpellView::updateControllerFocus(int prevFocus, int newFocus)
{
if (mButtons.empty())
return;
if (prevFocus >= 0 && prevFocus < static_cast<int>(mButtons.size()))
{
Gui::SharedStateButton* prev = mButtons[prevFocus].first;
if (prev)
prev->onMouseLostFocus(nullptr);
}
if (mControllerActiveWindow && newFocus >= 0 && newFocus < static_cast<int>(mButtons.size()))
{
Gui::SharedStateButton* focused = mButtons[newFocus].first;
if (focused)
{
focused->onMouseSetFocus(nullptr);
// Scroll the list to keep the active item in view
int line = mButtons[newFocus].second;
if (line <= 5)
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
else
{
const int lineHeight = focused->getHeight();
mScrollView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5)));
}
if (MWBase::Environment::get().getWindowManager()->getControllerTooltip())
MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused);
}
}
}
}

View file

@ -13,6 +13,11 @@ namespace MyGUI
class ScrollView;
}
namespace Gui
{
class SharedStateButton;
}
namespace MWGui
{
@ -54,6 +59,9 @@ namespace MWGui
void resetScrollbars();
void setActiveControllerWindow(bool active);
void onControllerButton(const unsigned char button);
private:
MyGUI::ScrollView* mScrollView;
@ -89,6 +97,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 and their index in the full list.
std::vector<std::pair<Gui::SharedStateButton*, int>> mButtons;
/// Keep a list of group offsets for controller navigation
std::vector<int> mGroupIndices;
bool mControllerActiveWindow;
int mControllerFocus;
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>
@ -27,6 +29,7 @@
#include "confirmationdialog.hpp"
#include "spellicons.hpp"
#include "spellview.hpp"
#include "statswindow.hpp"
namespace MWGui
{
@ -55,6 +58,14 @@ 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)
{
setPinButtonVisible(false);
mControllerButtons.mA = "#{sSelect}";
mControllerButtons.mB = "#{sBack}";
mControllerButtons.mR3 = "#{sInfo}";
}
}
void SpellWindow::onPinToggled()
@ -66,7 +77,9 @@ namespace MWGui
void SpellWindow::onTitleDoubleClicked()
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
if (Settings::gui().mControllerMenus)
return;
else if (MyGUI::InputManager::getInstance().isShiftPressed())
MWBase::Environment::get().getWindowManager()->toggleMaximized(this);
else if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);
@ -288,4 +301,39 @@ namespace MWGui
onSpellSelected(selectedSpell.mId);
}
}
bool SpellWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_B)
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
else
mSpellView->onControllerButton(arg.button);
return true;
}
void SpellWindow::setActiveControllerWindow(bool active)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
if (winMgr->getMode() == MWGui::GM_Inventory)
{
// Fill the screen, or limit to a certain size on large screens. Size chosen to
// match the size of the stats window.
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
int width = std::min(viewSize.width, StatsWindow::getIdealWidth());
int height = std::min(winMgr->getControllerMenuHeight(), StatsWindow::getIdealHeight());
int x = (viewSize.width - width) / 2;
int y = (viewSize.height - height) / 2;
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(x, active ? y : viewSize.height + 1, width, height);
MWBase::Environment::get().getWindowManager()->setControllerTooltip(
active && Settings::gui().mControllerTooltips);
}
mSpellView->setActiveControllerWindow(active);
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,14 @@ namespace MWGui
MyGUI::Window* t = mMainWidget->castType<MyGUI::Window>();
t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize);
if (Settings::gui().mControllerMenus)
{
setPinButtonVisible(false);
mControllerButtons.mLStick = "#{sMouse}";
mControllerButtons.mRStick = "#{sScrolldown}";
mControllerButtons.mB = "#{sBack}";
}
onWindowResize(t);
}
@ -714,7 +723,9 @@ namespace MWGui
void StatsWindow::onTitleDoubleClicked()
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
if (Settings::gui().mControllerMenus)
return;
else if (MyGUI::InputManager::getInstance().isShiftPressed())
{
MWBase::Environment::get().getWindowManager()->toggleMaximized(this);
MyGUI::Window* t = mMainWidget->castType<MyGUI::Window>();
@ -723,4 +734,35 @@ 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)
{
MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager();
if (winMgr->getMode() == MWGui::GM_Inventory)
{
// Fill the screen, or limit to a certain size on large screens. Size chosen to
// show all stats.
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
int width = std::min(viewSize.width, getIdealWidth());
int height = std::min(winMgr->getControllerMenuHeight(), getIdealHeight());
int x = (viewSize.width - width) / 2;
int y = (viewSize.height - height) / 2;
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(x, active ? y : viewSize.height + 1, width, height);
if (active)
onWindowResize(window);
}
WindowBase::setActiveControllerWindow(active);
}
}

View file

@ -13,6 +13,10 @@ namespace MWGui
public:
typedef std::map<ESM::RefId, int> FactionList;
/// It would be nice to measure these, but for now they're hardcoded.
static int getIdealHeight() { return 750; }
static int getIdealWidth() { return 600; }
StatsWindow(DragAndDrop* drag);
/// automatically updates all the data in the stats window, but only if it has changed.
@ -47,6 +51,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

@ -7,6 +7,8 @@
#include <MyGUI_EditBox.h>
#include <MyGUI_UString.h>
#include <components/esm/refid.hpp>
namespace MWGui
{
@ -25,6 +27,8 @@ namespace MWGui
// Make sure the edit box has focus
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit);
mControllerButtons.mA = "#{Interface:OK}";
}
void TextInputDialog::setNextButtonShow(bool shown)
@ -83,4 +87,15 @@ namespace MWGui
mTextEdit->setCaption(text);
}
bool TextInputDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
onOkClicked(nullptr);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
return true;
}
return false;
}
}

View file

@ -27,6 +27,7 @@ namespace MWGui
protected:
void onOkClicked(MyGUI::Widget* _sender);
void onTextAccepted(MyGUI::EditBox* _sender);
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
private:
MyGUI::EditBox* mTextEdit;

View file

@ -94,7 +94,7 @@ namespace MWGui
if (guiMode)
{
if (!winMgr->getCursorVisible())
if (!winMgr->getCursorVisible() && !winMgr->getControllerTooltip())
return;
const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition();

View file

@ -3,6 +3,7 @@
#include <MyGUI_Button.h>
#include <MyGUI_ControllerManager.h>
#include <MyGUI_ControllerRepeatClick.h>
#include <MyGUI_ImageBox.h>
#include <MyGUI_InputManager.h>
#include <components/misc/rng.hpp>
@ -11,6 +12,7 @@
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
@ -169,6 +171,29 @@ namespace MWGui
std::numeric_limits<int>::min() + 1); // disallow INT_MIN since abs(INT_MIN) is undefined
setCoord(400, 0, 400, 300);
if (Settings::gui().mControllerMenus)
{
// Show L1 and R1 buttons next to tabs
MyGUI::ImageBox* image;
getWidget(image, "BtnL1Image");
image->setVisible(true);
image->setUserString("Hidden", "false");
image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon(
SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
getWidget(image, "BtnR1Image");
image->setVisible(true);
image->setUserString("Hidden", "false");
image->setImageTexture(MWBase::Environment::get().getInputManager()->getControllerButtonIcon(
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER));
mControllerButtons.mA = "#{sBuy}";
mControllerButtons.mB = "#{Interface:Cancel}";
mControllerButtons.mX = "#{sOffer}";
mControllerButtons.mR3 = "#{sInfo}";
mControllerButtons.mL2 = "#{sInventory}";
}
}
void TradeWindow::setPtr(const MWWorld::Ptr& actor)
@ -203,6 +228,10 @@ namespace MWGui
onFilterChanged(mFilterAll);
mFilterEdit->setCaption({});
// Cycle to the buy window if it's not active.
if (Settings::gui().mControllerMenus && !mActiveControllerWindow)
MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(true);
for (const auto& source : itemSources)
source.getClass().getContainerStore(source).setContListener(this);
}
@ -353,6 +382,13 @@ namespace MWGui
}
}
void TradeWindow::onOfferSubmitted(MyGUI::Widget* _sender, size_t offerAmount)
{
mCurrentBalance = offerAmount * (mCurrentBalance < 0 ? -1 : 1);
updateLabels();
onOfferButtonClicked(mOfferButton);
}
void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender)
{
TradeItemModel* playerItemModel
@ -658,6 +694,91 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
}
bool TradeWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
int index = mItemView->getControllerFocus();
if (index >= 0 && index < mItemView->getItemCount())
onItemSelected(index);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
if (mCurrentBalance == 0)
return true;
// Show a count dialog to allow for bartering.
CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();
if (mCurrentBalance < 0)
{
// Buying from the merchant
dialog->openCountDialog("#{sTotalcost}:", "#{sOffer}", -mCurrentMerchantOffer);
dialog->setCount(-mCurrentBalance);
}
else
{
// Selling to the merchant
dialog->openCountDialog("#{sTotalsold}:", "#{sOffer}", getMerchantGold());
dialog->setCount(mCurrentBalance);
}
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &TradeWindow::onOfferSubmitted);
}
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);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
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);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
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->onControllerButton(arg.button);
}
return true;
}
void TradeWindow::setActiveControllerWindow(bool active)
{
// Show L1 and R1 buttons next to tabs
MyGUI::Widget* image;
getWidget(image, "BtnL1Image");
image->setVisible(active);
getWidget(image, "BtnR1Image");
image->setVisible(active);
mItemView->setActiveControllerWindow(active);
WindowBase::setActiveControllerWindow(active);
}
void TradeWindow::updateItemView()
{
mItemView->update();

View file

@ -49,6 +49,9 @@ namespace MWGui
std::string_view getWindowIdForLua() const override { return "Trade"; }
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
void setActiveControllerWindow(bool active) override;
private:
friend class InventoryWindow;
@ -113,6 +116,7 @@ namespace MWGui
void onBalanceButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onBalanceValueChanged(int value);
void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller);
void onOfferSubmitted(MyGUI::Widget* _sender, size_t offerAmount);
void addRepeatController(MyGUI::Widget* widget);

View file

@ -36,6 +36,13 @@ namespace MWGui
mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &TrainingWindow::onTrainingProgressChanged);
mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &TrainingWindow::onTrainingFinished);
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{sBuy}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
void TrainingWindow::onOpen()
@ -104,6 +111,7 @@ namespace MWGui
const int lineHeight = Settings::gui().mFontSize + 2;
mTrainingButtons.clear();
for (size_t i = 0; i < skills.size(); ++i)
{
const ESM::Skill* skill = skills[i].first;
@ -127,6 +135,16 @@ namespace MWGui
button->setSize(button->getTextSize().width + 12, button->getSize().height);
ToolTips::createSkillToolTip(button, skill->mId);
if (price <= playerGold)
mTrainingButtons.emplace_back(button);
}
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
if (mTrainingButtons.size() > 0)
mTrainingButtons[0]->setStateSelected(true);
}
center();
@ -228,4 +246,36 @@ namespace MWGui
return !mTimeAdvancer.isRunning();
}
bool TrainingWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mTrainingButtons.size())
onTrainingSelected(mTrainingButtons[mControllerFocus]);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mTrainingButtons.size() <= 1)
return true;
setControllerFocus(mTrainingButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mTrainingButtons.size());
setControllerFocus(mTrainingButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mTrainingButtons.size() <= 1)
return true;
setControllerFocus(mTrainingButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mTrainingButtons.size());
setControllerFocus(mTrainingButtons, mControllerFocus, true);
}
return true;
}
}

View file

@ -49,9 +49,13 @@ namespace MWGui
MyGUI::Widget* mTrainingOptions;
MyGUI::Button* mCancelButton;
MyGUI::TextBox* mPlayerGold;
std::vector<MyGUI::Button*> mTrainingButtons;
WaitDialogProgressBar mProgressBar;
TimeAdvancer mTimeAdvancer;
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
size_t mControllerFocus;
};
}

View file

@ -37,6 +37,13 @@ namespace MWGui
getWidget(mDestinationsView, "DestinationsView");
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onCancelButtonClicked);
if (Settings::gui().mControllerMenus)
{
mDisableGamepadCursor = true;
mControllerButtons.mA = "#{sTravel}";
mControllerButtons.mB = "#{Interface:Cancel}";
}
}
void TravelWindow::addDestination(const ESM::RefId& name, const ESM::Position& pos, bool interior)
@ -94,6 +101,8 @@ namespace MWGui
toAdd->setUserString("Destination", nameString);
toAdd->setUserData(pos);
toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onTravelButtonClick);
if (price <= playerGold)
mDestinationButtons.emplace_back(toAdd);
}
void TravelWindow::clearDestinations()
@ -102,6 +111,7 @@ namespace MWGui
mCurrentY = 0;
while (mDestinationsView->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0));
mDestinationButtons.clear();
}
void TravelWindow::setPtr(const MWWorld::Ptr& actor)
@ -146,6 +156,14 @@ namespace MWGui
}
updateLabels();
if (Settings::gui().mControllerMenus)
{
mControllerFocus = 0;
if (mDestinationButtons.size() > 0)
mDestinationButtons[0]->setStateSelected(true);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
// scrollbar is hidden
mDestinationsView->setVisibleVScroll(false);
@ -240,4 +258,49 @@ namespace MWGui
mDestinationsView->setViewOffset(
MyGUI::IntPoint(0, static_cast<int>(mDestinationsView->getViewOffset().top + _rel * 0.3f)));
}
bool TravelWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
if (mControllerFocus < mDestinationButtons.size())
{
onTravelButtonClick(mDestinationButtons[mControllerFocus]);
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click"));
}
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCancelButtonClicked(mCancelButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
if (mDestinationButtons.size() <= 1)
return true;
setControllerFocus(mDestinationButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus - 1, mDestinationButtons.size());
setControllerFocus(mDestinationButtons, mControllerFocus, true);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
if (mDestinationButtons.size() <= 1)
return true;
setControllerFocus(mDestinationButtons, mControllerFocus, false);
mControllerFocus = wrap(mControllerFocus + 1, mDestinationButtons.size());
setControllerFocus(mDestinationButtons, mControllerFocus, true);
}
// Scroll the list to keep the active item in view
if (mControllerFocus <= 5)
mDestinationsView->setViewOffset(MyGUI::IntPoint(0, 0));
else
{
const int lineHeight = Settings::gui().mFontSize + 2;
mDestinationsView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (mControllerFocus - 5)));
}
return true;
}
}

Some files were not shown because too many files have changed in this diff Show more