From 40cba7962cb8466dc876ac4330d6e368f14df618 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 6 Jan 2025 16:43:54 +0100 Subject: [PATCH 001/455] Bump us up to 0.50.0 --- CHANGELOG.md | 4 ++++ CMakeLists.txt | 2 +- README.md | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 6 ++++-- components/esm3/formatversion.hpp | 3 ++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fab893258f..8115cc875c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +0.50.0 +------ + + 0.49.0 ------ diff --git a/CMakeLists.txt b/CMakeLists.txt index bcb27f7649..26b1df7075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,7 @@ endif() message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 49) +set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_LUA_API_REVISION 69) set(OPENMW_POSTPROCESSING_API_REVISION 2) diff --git a/README.md b/README.md index bca5851c7b..f65f74e9d8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ OpenMW is an open-source open-world RPG game engine that supports playing Morrow OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set. -* Version: 0.49.0 +* Version: 0.50.0 * License: GPLv3 (see [LICENSE](https://gitlab.com/OpenMW/openmw/-/raw/master/LICENSE) for more information) * Website: https://www.openmw.org * IRC: #openmw on irc.libera.chat diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9e292a3eee..dac9776514 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -660,11 +660,13 @@ void MWState::StateManager::loadGame(const Character* character, const std::file // Report the last version still capable of reading this save if (e.getFormatVersion() <= ESM::OpenMW0_48SaveGameFormatVersion) release = "OpenMW 0.48.0"; + else if (e.getFormatVersion() <= ESM::OpenMW0_49SaveGameFormatVersion) + release = "OpenMW 0.49.0"; else { // Insert additional else if statements above to cover future releases - static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_49SaveGameFormatVersion); - release = "OpenMW 0.49.0"; + static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_50SaveGameFormatVersion); + release = "OpenMW 0.50.0"; } auto l10n = MWBase::Environment::get().getL10nManager()->getContext("OMWEngine"); std::string error = l10n->formatMessage("LoadingRequiresOldVersionError", { "version" }, { release }); diff --git a/components/esm3/formatversion.hpp b/components/esm3/formatversion.hpp index 32e245d7d1..c205f2fbb7 100644 --- a/components/esm3/formatversion.hpp +++ b/components/esm3/formatversion.hpp @@ -32,7 +32,8 @@ namespace ESM inline constexpr FormatVersion MinSupportedSaveGameFormatVersion = 5; inline constexpr FormatVersion OpenMW0_48SaveGameFormatVersion = 21; - inline constexpr FormatVersion OpenMW0_49SaveGameFormatVersion = CurrentSaveGameFormatVersion; + inline constexpr FormatVersion OpenMW0_49SaveGameFormatVersion = 34; + inline constexpr FormatVersion OpenMW0_50SaveGameFormatVersion = CurrentSaveGameFormatVersion; } #endif From 88fe079f95e16cd8e0e1f3184d968690ce4c6b91 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 17 Jan 2025 00:53:19 +0000 Subject: [PATCH 002/455] Don't mangle settings with the comment character in their value '#' is a valid character in setting values - it's only a comment if it's the first non-" \t\r\n" character on a line. Making the comment ignoring match the parser we use elsewhere should avoid mangling data. --- apps/mwiniimporter/importer.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 8c7c238b4a..cc38eed211 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -352,12 +352,10 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::pat std::string line; while (std::getline(file, line)) { - - // we cant say comment by only looking at first char anymore - int comment_pos = static_cast(line.find('#')); - if (comment_pos > 0) + // ignore comments - keep in sync with configfileparser.cpp + if (line.find('#') == line.find_first_not_of(" \t\r\n")) { - line = line.substr(0, comment_pos); + continue; } if (line.empty()) From e345fca99a4fe987bb9a13aa9ee7a79e6191a2a2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 17 Jan 2025 01:21:24 +0000 Subject: [PATCH 003/455] trim_ws, too --- apps/mwiniimporter/importer.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index cc38eed211..c3b23eff5c 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -11,6 +11,23 @@ namespace sfs = std::filesystem; +namespace +{ + // from configfileparser.cpp + std::string trim_ws(const std::string& s) + { + std::string::size_type n, n2; + n = s.find_first_not_of(" \t\r\n"); + if (n == std::string::npos) + return std::string(); + else + { + n2 = s.find_last_not_of(" \t\r\n"); + return s.substr(n, n2 - n + 1); + } + } +} + MwIniImporter::MwIniImporter() : mVerbose(false) , mEncoding(ToUTF8::WINDOWS_1250) @@ -371,6 +388,8 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::pat std::string key(line.substr(0, pos)); std::string value(line.substr(pos + 1)); + key = trim_ws(key); + value = trim_ws(key); if (map.find(key) == map.end()) { From 33553c0cf7aeebd334a1a5c7b6e857251409451f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 17 Jan 2025 01:34:08 +0000 Subject: [PATCH 004/455] Handle encoding a bit more cleverly * use the value from the existing openmw.cfg if it exists and we weren't told to use something else on the command line * write the value to openmw.cfg if it wasn't there or we've overridden it --- apps/mwiniimporter/main.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index 0beb2b38cc..b934d0967e 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -126,12 +126,20 @@ int wmain(int argc, wchar_t* wargv[]) MwIniImporter importer; importer.setVerbose(vm.count("verbose") != 0); + MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); + // Font encoding settings - std::string encoding(vm["encoding"].as()); + std::string encoding; + if (vm["encoding"].defaulted() && cfg.contains("encoding") && !cfg["encoding"].empty()) + encoding = cfg["encoding"].back(); + else + { + encoding = vm["encoding"].as(); + cfg["encoding"] = {encoding}; + } importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); - MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); if (!vm.count("fonts")) { From 84c497b1fb4ca4d254e3b2c5daa6e5e9b5eb214d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 17 Jan 2025 01:45:09 +0000 Subject: [PATCH 005/455] capitulate --- apps/mwiniimporter/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index b934d0967e..6e4242cb4e 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -135,7 +135,7 @@ int wmain(int argc, wchar_t* wargv[]) else { encoding = vm["encoding"].as(); - cfg["encoding"] = {encoding}; + cfg["encoding"] = { encoding }; } importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); From 3b50bcfb3a668db7529596370b49bf8d85771e71 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 19 Jan 2025 20:07:53 +0100 Subject: [PATCH 006/455] Allow GetSpellEffects to detect enchantments by id --- apps/openmw/mwscript/miscextensions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ed2ef756e6..95238d4299 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -884,8 +884,8 @@ namespace MWScript return; } - const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); - runtime.push(stats.getActiveSpells().isSpellActive(id)); + const auto& activeSpells = ptr.getClass().getCreatureStats(ptr).getActiveSpells(); + runtime.push(activeSpells.isSpellActive(id) || activeSpells.isEnchantmentActive(id)); } }; From a645ec0910579c15fbba3c8872dabcdf7ea043b1 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 21 Jan 2025 20:25:13 +0100 Subject: [PATCH 007/455] Allow filters to apply to creatures --- apps/openmw/mwdialogue/filter.cpp | 19 +++++++++---------- apps/openmw/mwdialogue/selectwrapper.cpp | 23 ----------------------- apps/openmw/mwdialogue/selectwrapper.hpp | 3 --- 3 files changed, 9 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 295d690ce5..e21a57aad9 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -265,10 +265,6 @@ bool MWDialogue::Filter::testFunctionLocal(const MWDialogue::SelectWrapper& sele bool MWDialogue::Filter::testSelectStruct(const SelectWrapper& select) const { - if (select.isNpcOnly() && (mActor.getType() != ESM::NPC::sRecordId)) - // If the actor is a creature, we pass all conditions only applicable to NPCs. - return true; - if (select.getFunction() == ESM::DialogueCondition::Function_Choice && mChoice == -1) // If not currently in a choice, we reject all conditions that test against choices. return false; @@ -504,7 +500,8 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons return MWBase::Environment::get().getWorld()->getCurrentWeather(); case ESM::DialogueCondition::Function_Reputation: - + if (!mActor.getClass().isNpc()) + return 0; return mActor.getClass().getNpcStats(mActor).getReputation(); case ESM::DialogueCondition::Function_FactionRankDifference: @@ -586,11 +583,11 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con case ESM::DialogueCondition::Function_NotClass: - return mActor.get()->mBase->mClass != select.getId(); + return !mActor.getClass().isNpc() || mActor.get()->mBase->mClass != select.getId(); case ESM::DialogueCondition::Function_NotRace: - return mActor.get()->mBase->mRace != select.getId(); + return !mActor.getClass().isNpc() || mActor.get()->mBase->mRace != select.getId(); case ESM::DialogueCondition::Function_NotCell: { @@ -598,12 +595,14 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con return !Misc::StringUtils::ciStartsWith(actorCell, select.getCellName()); } case ESM::DialogueCondition::Function_SameSex: - + if (!mActor.getClass().isNpc()) + return false; return (player.get()->mBase->mFlags & ESM::NPC::Female) == (mActor.get()->mBase->mFlags & ESM::NPC::Female); case ESM::DialogueCondition::Function_SameRace: - + if (!mActor.getClass().isNpc()) + return false; return mActor.get()->mBase->mRace == player.get()->mBase->mRace; case ESM::DialogueCondition::Function_SameFaction: @@ -668,7 +667,7 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con case ESM::DialogueCondition::Function_Werewolf: - return mActor.getClass().getNpcStats(mActor).isWerewolf(); + return mActor.getClass().isNpc() && mActor.getClass().getNpcStats(mActor).isWerewolf(); default: diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index 02c9d29b59..f9469bf9a9 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -247,29 +247,6 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const }; } -bool MWDialogue::SelectWrapper::isNpcOnly() const -{ - switch (mSelect.mFunction) - { - case ESM::DialogueCondition::Function_NotFaction: - case ESM::DialogueCondition::Function_NotClass: - case ESM::DialogueCondition::Function_NotRace: - case ESM::DialogueCondition::Function_SameSex: - case ESM::DialogueCondition::Function_SameRace: - case ESM::DialogueCondition::Function_SameFaction: - case ESM::DialogueCondition::Function_RankRequirement: - case ESM::DialogueCondition::Function_Reputation: - case ESM::DialogueCondition::Function_FactionRankDifference: - case ESM::DialogueCondition::Function_Werewolf: - case ESM::DialogueCondition::Function_PcWerewolfKills: - case ESM::DialogueCondition::Function_FacReactionLowest: - case ESM::DialogueCondition::Function_FacReactionHighest: - return true; - default: - return false; - } -} - bool MWDialogue::SelectWrapper::selectCompare(int value) const { return selectCompareImp(mSelect, value); diff --git a/apps/openmw/mwdialogue/selectwrapper.hpp b/apps/openmw/mwdialogue/selectwrapper.hpp index d831b6cea0..d15334cbe1 100644 --- a/apps/openmw/mwdialogue/selectwrapper.hpp +++ b/apps/openmw/mwdialogue/selectwrapper.hpp @@ -28,9 +28,6 @@ namespace MWDialogue Type getType() const; - bool isNpcOnly() const; - ///< \attention Do not call any of the select functions for this select struct! - bool selectCompare(int value) const; bool selectCompare(float value) const; From 7d2dd3422dc552564321c3267267f705cb18798a Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 21 Jan 2025 20:31:12 +0100 Subject: [PATCH 008/455] Ignore missing global variables when filtering dialogue --- apps/openmw/mwdialogue/filter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index e21a57aad9..b7d4a1361c 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -301,9 +301,13 @@ bool MWDialogue::Filter::testSelectStructNumeric(const SelectWrapper& select) co switch (select.getFunction()) { case ESM::DialogueCondition::Function_Global: - + { + const auto& world = MWBase::Environment::get().getWorld(); + if (world->getGlobalVariableType(select.getName()) == ' ') + return true; // ignore this filter if the global doesn't exist // internally all globals are float :( - return select.selectCompare(MWBase::Environment::get().getWorld()->getGlobalFloat(select.getName())); + return select.selectCompare(world->getGlobalFloat(select.getName())); + } case ESM::DialogueCondition::Function_Local: { From 0f9be64904734c217792002c1b2636ff42aec0ea Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 4 Feb 2025 09:17:31 +0300 Subject: [PATCH 009/455] Use the final effect cost to calculate enchantment price (#8340) --- apps/openmw/mwmechanics/enchanting.cpp | 27 ++++++++++++++++++++------ apps/openmw/mwmechanics/enchanting.hpp | 2 ++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 7d0007f9e3..66bef89e2c 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -185,18 +185,18 @@ namespace MWMechanics * * Formula on UESPWiki is not entirely correct. */ - float Enchanting::getEnchantPoints(bool precise) const + std::vector Enchanting::getEffectCosts() const { + std::vector costs; if (mEffectList.mList.empty()) - // No effects added, cost = 0 - return 0; + return costs; + costs.reserve(mEffectList.mList.size()); const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); const float fEffectCostMult = store.get().find("fEffectCostMult")->mValue.getFloat(); const float fEnchantmentConstantDurationMult = store.get().find("fEnchantmentConstantDurationMult")->mValue.getFloat(); - float enchantmentCost = 0.f; float cost = 0.f; for (const ESM::IndexedENAMstruct& effect : mEffectList.mList) { @@ -215,9 +215,18 @@ namespace MWMechanics if (effect.mData.mRange == ESM::RT_Target) cost *= 1.5f; - enchantmentCost += precise ? cost : std::floor(cost); + costs.push_back(cost); } + return costs; + } + + float Enchanting::getEnchantPoints(bool precise) const + { + float enchantmentCost = 0.f; + for (float cost : getEffectCosts()) + enchantmentCost += precise ? cost : std::floor(cost); + return enchantmentCost; } @@ -278,13 +287,19 @@ namespace MWMechanics if (mEnchanter.isEmpty()) return 0; + // Use the final effect's accumulated cost + float finalEffectCost = 0.f; + std::vector effectCosts = getEffectCosts(); + if (!effectCosts.empty()) + finalEffectCost = effectCosts.back(); + float priceMultipler = MWBase::Environment::get() .getESMStore() ->get() .find("fEnchantmentValueMult") ->mValue.getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer( - mEnchanter, static_cast(getEnchantPoints() * priceMultipler), true); + mEnchanter, static_cast(finalEffectCost * priceMultipler), true); price *= count * getTypeMultiplier(); return std::max(1, price); } diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 5db02b8cba..98e0982f7c 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -2,6 +2,7 @@ #define GAME_MWMECHANICS_ENCHANTING_H #include +#include #include #include @@ -32,6 +33,7 @@ namespace MWMechanics float getTypeMultiplier() const; void payForEnchantment(int count) const; int getEnchantPrice(int count) const; + std::vector getEffectCosts() const; public: Enchanting(); From 1a9e29844b663dfde8b93f19e6adf25cd0da1c4f Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 12 Feb 2025 23:12:07 +0300 Subject: [PATCH 010/455] Implement TCB interpolation for vectors and scalars (#2379) --- components/nif/nifkey.hpp | 94 ++++++++++++++++++++++++-------- components/nifosg/controller.hpp | 2 +- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 604cf92c33..bd362101c6 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -3,7 +3,9 @@ #ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP #define OPENMW_COMPONENTS_NIF_NIFKEY_HPP +#include #include +#include #include "exception.hpp" #include "niffile.hpp" @@ -17,7 +19,7 @@ namespace Nif InterpolationType_Unknown = 0, InterpolationType_Linear = 1, InterpolationType_Quadratic = 2, - InterpolationType_TBC = 3, + InterpolationType_TCB = 3, InterpolationType_XYZ = 4, InterpolationType_Constant = 5 }; @@ -28,18 +30,19 @@ namespace Nif T mValue; T mInTan; // Only for Quadratic interpolation, and never for QuaternionKeyList T mOutTan; // Only for Quadratic interpolation, and never for QuaternionKeyList - - // FIXME: Implement TBC interpolation - /* - float mTension; // Only for TBC interpolation - float mBias; // Only for TBC interpolation - float mContinuity; // Only for TBC interpolation - */ }; - using FloatKey = KeyT; - using Vector3Key = KeyT; - using Vector4Key = KeyT; - using QuaternionKey = KeyT; + + template + struct TCBKey + { + float mTime; + T mValue{}; + T mInTan{}; + T mOutTan{}; + float mTension; + float mContinuity; + float mBias; + }; template struct KeyMapT @@ -101,15 +104,20 @@ namespace Nif mKeys[time] = key; } } - else if (mInterpolationType == InterpolationType_TBC) + else if (mInterpolationType == InterpolationType_TCB) { - for (size_t i = 0; i < count; i++) + std::vector> tcbKeys(count); + for (TCBKey& key : tcbKeys) { - float time; - nif->read(time); - readTBC(*nif, key); - mKeys[time] = key; + nif->read(key.mTime); + key.mValue = ((*nif).*getValue)(); + nif->read(key.mTension); + nif->read(key.mContinuity); + nif->read(key.mBias); } + generateTCBTangents(tcbKeys); + for (TCBKey& key : tcbKeys) + mKeys[key.mTime] = KeyType{ std::move(key.mValue), std::move(key.mInTan), std::move(key.mOutTan) }; } else if (mInterpolationType == InterpolationType_XYZ) { @@ -140,12 +148,52 @@ namespace Nif static void readQuadratic(NIFStream& nif, KeyT& key) { readValue(nif, key); } - static void readTBC(NIFStream& nif, KeyT& key) + template + static void generateTCBTangents(std::vector>& keys) { - readValue(nif, key); - /*key.mTension = */ nif.get(); - /*key.mBias = */ nif.get(); - /*key.mContinuity = */ nif.get(); + if (keys.size() <= 1) + return; + + std::sort(keys.begin(), keys.end(), [](const auto& a, const auto& b) { return a.mTime < b.mTime; }); + for (size_t i = 0; i < keys.size(); ++i) + { + TCBKey& curr = keys[i]; + const TCBKey& prev = (i == 0) ? curr : keys[i - 1]; + const TCBKey& next = (i == keys.size() - 1) ? curr : keys[i + 1]; + const float prevLen = curr.mTime - prev.mTime; + const float nextLen = next.mTime - curr.mTime; + if (prevLen + nextLen <= 0.f) + continue; + + const U prevDelta = curr.mValue - prev.mValue; + const U nextDelta = next.mValue - curr.mValue; + const float t = curr.mTension; + const float c = curr.mContinuity; + const float b = curr.mBias; + + U x{}, y{}, z{}, w{}; + if (prevLen > 0.f) + x = prevDelta / prevLen * (1 - t) * (1 - c) * (1 + b); + if (nextLen > 0.f) + y = nextDelta / nextLen * (1 - t) * (1 + c) * (1 - b); + if (prevLen > 0.f) + z = prevDelta / prevLen * (1 - t) * (1 + c) * (1 + b); + if (nextLen > 0.f) + w = nextDelta / nextLen * (1 - t) * (1 - c) * (1 - b); + + curr.mInTan = (x + y) * prevLen / (prevLen + nextLen); + curr.mOutTan = (z + w) * nextLen / (prevLen + nextLen); + } + } + + static void generateTCBTangents(std::vector>& keys) + { + // TODO: is this even legal? + } + + static void generateTCBTangents(std::vector>& keys) + { + // TODO: implement TCB interpolation for quaternions } }; using FloatKeyMap = KeyMapT>; diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 99d3df9545..468668ce76 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -131,6 +131,7 @@ namespace NifOsg case Nif::InterpolationType_Constant: return fraction > 0.5f ? b.mValue : a.mValue; case Nif::InterpolationType_Quadratic: + case Nif::InterpolationType_TCB: { // Using a cubic Hermite spline. // b1(t) = 2t^3 - 3t^2 + 1 @@ -147,7 +148,6 @@ namespace NifOsg const float b4 = t3 - t2; return a.mValue * b1 + b.mValue * b2 + a.mOutTan * b3 + b.mInTan * b4; } - // TODO: Implement TBC interpolation default: return a.mValue + ((b.mValue - a.mValue) * fraction); } From 0254feefe381227709f5a0ace05fb1f704d3e3fe Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 20:32:49 +0300 Subject: [PATCH 011/455] Rename bsa_file.cpp/hpp to follow naming conventions --- CI/file_name_exceptions.txt | 2 -- components/CMakeLists.txt | 2 +- components/bsa/ba2dx10file.hpp | 2 +- components/bsa/ba2gnrlfile.hpp | 2 +- components/bsa/{bsa_file.cpp => bsafile.cpp} | 4 ++-- components/bsa/{bsa_file.hpp => bsafile.hpp} | 2 +- components/bsa/compressedbsafile.hpp | 4 ++-- components/vfs/bsaarchive.hpp | 2 +- 8 files changed, 9 insertions(+), 11 deletions(-) rename components/bsa/{bsa_file.cpp => bsafile.cpp} (99%) rename components/bsa/{bsa_file.hpp => bsafile.hpp} (98%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 14d106169b..f1dcf0f38f 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -28,8 +28,6 @@ apps/openmw_tests/mwdialogue/test_keywordsearch.cpp apps/openmw_tests/mwscript/test_scripts.cpp apps/openmw_tests/mwscript/test_utils.hpp apps/openmw_tests/mwworld/test_store.cpp -components/bsa/bsa_file.cpp -components/bsa/bsa_file.hpp components/crashcatcher/windows_crashcatcher.cpp components/crashcatcher/windows_crashcatcher.hpp components/crashcatcher/windows_crashmonitor.cpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index b6734e6fc6..83270f3c6f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -111,7 +111,7 @@ add_component_dir (bgsm ) add_component_dir (bsa - bsa_file compressedbsafile ba2gnrlfile ba2dx10file ba2file memorystream + bsafile compressedbsafile ba2gnrlfile ba2dx10file ba2file memorystream ) add_component_dir (bullethelpers diff --git a/components/bsa/ba2dx10file.hpp b/components/bsa/ba2dx10file.hpp index 59db10745b..5d4249365b 100644 --- a/components/bsa/ba2dx10file.hpp +++ b/components/bsa/ba2dx10file.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include "bsafile.hpp" namespace Bsa { diff --git a/components/bsa/ba2gnrlfile.hpp b/components/bsa/ba2gnrlfile.hpp index 6a212c5e81..5f1f21dec9 100644 --- a/components/bsa/ba2gnrlfile.hpp +++ b/components/bsa/ba2gnrlfile.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include "bsafile.hpp" namespace Bsa { diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsafile.cpp similarity index 99% rename from components/bsa/bsa_file.cpp rename to components/bsa/bsafile.cpp index 46639a729e..03ee185e9e 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsafile.cpp @@ -4,7 +4,7 @@ Email: < korslund@gmail.com > WWW: https://openmw.org/ - This file (bsa_file.cpp) is part of the OpenMW package. + This file (bsafile.cpp) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -21,7 +21,7 @@ */ -#include "bsa_file.hpp" +#include "bsafile.hpp" #include #include diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsafile.hpp similarity index 98% rename from components/bsa/bsa_file.hpp rename to components/bsa/bsafile.hpp index 03a0703885..9e79df85a1 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsafile.hpp @@ -4,7 +4,7 @@ Email: < korslund@gmail.com > WWW: https://openmw.org/ - This file (bsa_file.h) is part of the OpenMW package. + This file (bsafile.hpp) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index 8fa5c9a62a..1179dcc00b 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -26,11 +26,11 @@ #ifndef BSA_COMPRESSED_BSA_FILE_H #define BSA_COMPRESSED_BSA_FILE_H +#include #include #include -#include -#include +#include "bsafile.hpp" namespace Bsa { diff --git a/components/vfs/bsaarchive.hpp b/components/vfs/bsaarchive.hpp index 664466fa40..2e6fac6558 100644 --- a/components/vfs/bsaarchive.hpp +++ b/components/vfs/bsaarchive.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include From 8b911ce3eb29002f7b4c3eaf921066cccad32a26 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 21:00:40 +0300 Subject: [PATCH 012/455] Rename components/fx files to follow naming conventions --- CI/file_name_exceptions.txt | 2 -- components/CMakeLists.txt | 2 +- components/fx/lexer.cpp | 1 - components/fx/lexer.hpp | 2 +- components/fx/{lexer_types.hpp => lexertypes.hpp} | 6 +++--- components/fx/{parse_constants.hpp => parseconstants.hpp} | 4 ++-- components/fx/technique.cpp | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) rename components/fx/{lexer_types.hpp => lexertypes.hpp} (98%) rename components/fx/{parse_constants.hpp => parseconstants.hpp} (98%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index f1dcf0f38f..0923a70567 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -33,8 +33,6 @@ components/crashcatcher/windows_crashcatcher.hpp components/crashcatcher/windows_crashmonitor.cpp components/crashcatcher/windows_crashmonitor.hpp components/crashcatcher/windows_crashshm.hpp -components/fx/lexer_types.hpp -components/fx/parse_constants.hpp components/platform/file.posix.cpp components/platform/file.stdio.cpp components/platform/file.win32.cpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 83270f3c6f..ccf3a329b3 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -173,7 +173,7 @@ add_component_dir(esm attr common defs esmcommon records util luascripts format exteriorcelllocation ) -add_component_dir(fx pass technique lexer lexer_types parse_constants widgets stateupdater) +add_component_dir(fx pass technique lexer lexertypes parseconstants widgets stateupdater) add_component_dir(std140 ubo) diff --git a/components/fx/lexer.cpp b/components/fx/lexer.cpp index 6140c7375c..2fc25e44f1 100644 --- a/components/fx/lexer.cpp +++ b/components/fx/lexer.cpp @@ -6,7 +6,6 @@ #include #include -#include #include namespace fx diff --git a/components/fx/lexer.hpp b/components/fx/lexer.hpp index fc7d4ec9d7..adc8974904 100644 --- a/components/fx/lexer.hpp +++ b/components/fx/lexer.hpp @@ -7,7 +7,7 @@ #include #include -#include "lexer_types.hpp" +#include "lexertypes.hpp" namespace fx { diff --git a/components/fx/lexer_types.hpp b/components/fx/lexertypes.hpp similarity index 98% rename from components/fx/lexer_types.hpp rename to components/fx/lexertypes.hpp index 9fe13ac827..c0df1a9da8 100644 --- a/components/fx/lexer_types.hpp +++ b/components/fx/lexertypes.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_LEXER_TYPES_H -#define OPENMW_COMPONENTS_FX_LEXER_TYPES_H +#ifndef OPENMW_COMPONENTS_FX_LEXERTYPES_H +#define OPENMW_COMPONENTS_FX_LEXERTYPES_H #include #include @@ -165,4 +165,4 @@ namespace fx } } -#endif \ No newline at end of file +#endif diff --git a/components/fx/parse_constants.hpp b/components/fx/parseconstants.hpp similarity index 98% rename from components/fx/parse_constants.hpp rename to components/fx/parseconstants.hpp index 2057476f3e..412a19980b 100644 --- a/components/fx/parse_constants.hpp +++ b/components/fx/parseconstants.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_PARSE_CONSTANTS_H -#define OPENMW_COMPONENTS_FX_PARSE_CONSTANTS_H +#ifndef OPENMW_COMPONENTS_FX_PARSECONSTANTS_H +#define OPENMW_COMPONENTS_FX_PARSECONSTANTS_H #include #include diff --git a/components/fx/technique.cpp b/components/fx/technique.cpp index 5963e274ec..b0b7f282aa 100644 --- a/components/fx/technique.cpp +++ b/components/fx/technique.cpp @@ -17,7 +17,7 @@ #include #include -#include "parse_constants.hpp" +#include "parseconstants.hpp" namespace { From 5d5595cc5ba130b9c8326b6e7ec776ce01d667f0 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 21:12:28 +0300 Subject: [PATCH 013/455] Standardize components/bsa file include guards and order --- components/bsa/ba2dx10file.cpp | 6 ++++-- components/bsa/ba2dx10file.hpp | 4 ++-- components/bsa/ba2file.hpp | 4 ++-- components/bsa/ba2gnrlfile.cpp | 6 ++++-- components/bsa/ba2gnrlfile.hpp | 4 ++-- components/bsa/bsafile.cpp | 6 +++--- components/bsa/bsafile.hpp | 4 ++-- components/bsa/compressedbsafile.cpp | 4 +++- components/bsa/compressedbsafile.hpp | 4 ++-- components/bsa/memorystream.hpp | 7 ++++--- 10 files changed, 28 insertions(+), 21 deletions(-) diff --git a/components/bsa/ba2dx10file.cpp b/components/bsa/ba2dx10file.cpp index 502ca043ab..a438121d5b 100644 --- a/components/bsa/ba2dx10file.cpp +++ b/components/bsa/ba2dx10file.cpp @@ -24,13 +24,15 @@ #endif #include -#include -#include + #include #include #include #include +#include "ba2file.hpp" +#include "memorystream.hpp" + namespace Bsa { BA2DX10File::BA2DX10File() {} diff --git a/components/bsa/ba2dx10file.hpp b/components/bsa/ba2dx10file.hpp index 5d4249365b..cd1f822179 100644 --- a/components/bsa/ba2dx10file.hpp +++ b/components/bsa/ba2dx10file.hpp @@ -1,5 +1,5 @@ -#ifndef BSA_BA2_DX10_FILE_H -#define BSA_BA2_DX10_FILE_H +#ifndef OPENMW_COMPONENTS_BSA_BA2DX10FILE_HPP +#define OPENMW_COMPONENTS_BSA_BA2DX10FILE_HPP #include #include diff --git a/components/bsa/ba2file.hpp b/components/bsa/ba2file.hpp index 9a68d3afd0..0d51be4c0e 100644 --- a/components/bsa/ba2file.hpp +++ b/components/bsa/ba2file.hpp @@ -1,5 +1,5 @@ -#ifndef BSA_BA2_FILE_H -#define BSA_BA2_FILE_H +#ifndef OPENMW_COMPONENTS_BSA_BA2FILE_HPP +#define OPENMW_COMPONENTS_BSA_BA2FILE_HPP #include #include diff --git a/components/bsa/ba2gnrlfile.cpp b/components/bsa/ba2gnrlfile.cpp index 63dd3d1d50..75e7305245 100644 --- a/components/bsa/ba2gnrlfile.cpp +++ b/components/bsa/ba2gnrlfile.cpp @@ -22,13 +22,15 @@ #endif #include -#include -#include + #include #include #include #include +#include "ba2file.hpp" +#include "memorystream.hpp" + namespace Bsa { // special marker for invalid records, diff --git a/components/bsa/ba2gnrlfile.hpp b/components/bsa/ba2gnrlfile.hpp index 5f1f21dec9..0bc94eae0e 100644 --- a/components/bsa/ba2gnrlfile.hpp +++ b/components/bsa/ba2gnrlfile.hpp @@ -1,5 +1,5 @@ -#ifndef BSA_BA2_GNRL_FILE_H -#define BSA_BA2_GNRL_FILE_H +#ifndef OPENMW_COMPONENTS_BSA_BA2GNRLFILE_HPP +#define OPENMW_COMPONENTS_BSA_BA2GNRLFILE_HPP #include #include diff --git a/components/bsa/bsafile.cpp b/components/bsa/bsafile.cpp index 03ee185e9e..948b9dac8d 100644 --- a/components/bsa/bsafile.cpp +++ b/components/bsa/bsafile.cpp @@ -23,15 +23,15 @@ #include "bsafile.hpp" -#include -#include - #include #include #include #include #include +#include +#include + using namespace Bsa; /// Error handling diff --git a/components/bsa/bsafile.hpp b/components/bsa/bsafile.hpp index 9e79df85a1..ad7acdad17 100644 --- a/components/bsa/bsafile.hpp +++ b/components/bsa/bsafile.hpp @@ -21,8 +21,8 @@ */ -#ifndef BSA_BSA_FILE_H -#define BSA_BSA_FILE_H +#ifndef OPENMW_COMPONENTS_BSA_BSAFILE_HPP +#define OPENMW_COMPONENTS_BSA_BSAFILE_HPP #include #include diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index 14d90f5d91..8426c5965c 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -45,11 +45,13 @@ #endif #include -#include + #include #include #include +#include "memorystream.hpp" + namespace Bsa { /// Read header information from the input source diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index 1179dcc00b..83620f11bc 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -23,8 +23,8 @@ */ -#ifndef BSA_COMPRESSED_BSA_FILE_H -#define BSA_COMPRESSED_BSA_FILE_H +#ifndef OPENMW_COMPONENTS_BSA_COMPRESSEDBSAFILE_HPP +#define OPENMW_COMPONENTS_BSA_COMPRESSEDBSAFILE_HPP #include #include diff --git a/components/bsa/memorystream.hpp b/components/bsa/memorystream.hpp index 5662dde8ff..945d0b33d9 100644 --- a/components/bsa/memorystream.hpp +++ b/components/bsa/memorystream.hpp @@ -23,13 +23,14 @@ */ -#ifndef BSA_MEMORY_STREAM_H -#define BSA_MEMORY_STREAM_H +#ifndef OPENMW_COMPONENTS_BSA_MEMORYSTREAM_HPP +#define OPENMW_COMPONENTS_BSA_MEMORYSTREAM_HPP -#include #include #include +#include + namespace Bsa { /** From 3793ff8be882da974e654789c4f6808faafab62d Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 21:14:58 +0300 Subject: [PATCH 014/455] Standardize components/fx file include guards --- components/fx/lexer.hpp | 4 ++-- components/fx/lexertypes.hpp | 4 ++-- components/fx/parseconstants.hpp | 4 ++-- components/fx/pass.hpp | 4 ++-- components/fx/technique.hpp | 4 ++-- components/fx/types.hpp | 4 ++-- components/fx/widgets.hpp | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/fx/lexer.hpp b/components/fx/lexer.hpp index adc8974904..dda6b3a0f6 100644 --- a/components/fx/lexer.hpp +++ b/components/fx/lexer.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_LEXER_H -#define OPENMW_COMPONENTS_FX_LEXER_H +#ifndef OPENMW_COMPONENTS_FX_LEXER_HPP +#define OPENMW_COMPONENTS_FX_LEXER_HPP #include #include diff --git a/components/fx/lexertypes.hpp b/components/fx/lexertypes.hpp index c0df1a9da8..2a56a84a1a 100644 --- a/components/fx/lexertypes.hpp +++ b/components/fx/lexertypes.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_LEXERTYPES_H -#define OPENMW_COMPONENTS_FX_LEXERTYPES_H +#ifndef OPENMW_COMPONENTS_FX_LEXERTYPES_HPP +#define OPENMW_COMPONENTS_FX_LEXERTYPES_HPP #include #include diff --git a/components/fx/parseconstants.hpp b/components/fx/parseconstants.hpp index 412a19980b..3ad9abd959 100644 --- a/components/fx/parseconstants.hpp +++ b/components/fx/parseconstants.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_PARSECONSTANTS_H -#define OPENMW_COMPONENTS_FX_PARSECONSTANTS_H +#ifndef OPENMW_COMPONENTS_FX_PARSECONSTANTS_HPP +#define OPENMW_COMPONENTS_FX_PARSECONSTANTS_HPP #include #include diff --git a/components/fx/pass.hpp b/components/fx/pass.hpp index e176afc699..1c417ac8cf 100644 --- a/components/fx/pass.hpp +++ b/components/fx/pass.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_PASS_H -#define OPENMW_COMPONENTS_FX_PASS_H +#ifndef OPENMW_COMPONENTS_FX_PASS_HPP +#define OPENMW_COMPONENTS_FX_PASS_HPP #include #include diff --git a/components/fx/technique.hpp b/components/fx/technique.hpp index 2778763a9a..ad5e876faa 100644 --- a/components/fx/technique.hpp +++ b/components/fx/technique.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_TECHNIQUE_H -#define OPENMW_COMPONENTS_FX_TECHNIQUE_H +#ifndef OPENMW_COMPONENTS_FX_TECHNIQUE_HPP +#define OPENMW_COMPONENTS_FX_TECHNIQUE_HPP #include #include diff --git a/components/fx/types.hpp b/components/fx/types.hpp index 596b54c217..1536cda115 100644 --- a/components/fx/types.hpp +++ b/components/fx/types.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_TYPES_H -#define OPENMW_COMPONENTS_FX_TYPES_H +#ifndef OPENMW_COMPONENTS_FX_TYPES_HPP +#define OPENMW_COMPONENTS_FX_TYPES_HPP #include #include diff --git a/components/fx/widgets.hpp b/components/fx/widgets.hpp index 6217af7fee..59787ed9aa 100644 --- a/components/fx/widgets.hpp +++ b/components/fx/widgets.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_FX_WIDGETS_H -#define OPENMW_COMPONENTS_FX_WIDGETS_H +#ifndef OPENMW_COMPONENTS_FX_WIDGETS_HPP +#define OPENMW_COMPONENTS_FX_WIDGETS_HPP #include #include From 7df74664e43cca949e1c57230e34f1bbc2747030 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 21:44:58 +0300 Subject: [PATCH 015/455] Rename gl4es-init.cpp/h to follow naming conventions --- CI/file_name_exceptions.txt | 2 -- components/CMakeLists.txt | 2 +- components/sdlutil/{gl4es_init.cpp => gl4esinit.cpp} | 2 +- components/sdlutil/{gl4es_init.h => gl4esinit.h} | 6 +++--- components/sdlutil/sdlgraphicswindow.cpp | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) rename components/sdlutil/{gl4es_init.cpp => gl4esinit.cpp} (97%) rename components/sdlutil/{gl4es_init.h => gl4esinit.h} (67%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 0923a70567..6ae56912dd 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -36,8 +36,6 @@ components/crashcatcher/windows_crashshm.hpp components/platform/file.posix.cpp components/platform/file.stdio.cpp components/platform/file.win32.cpp -components/sdlutil/gl4es_init.cpp -components/sdlutil/gl4es_init.h components/to_utf8/gen_iconv.cpp components/to_utf8/tables_gen.hpp components/to_utf8/to_utf8.cpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index ccf3a329b3..da91cec7ef 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -362,7 +362,7 @@ add_component_dir (fontloader add_component_dir (sdlutil events - gl4es_init + gl4esinit imagetosurface sdlcursormanager sdlgraphicswindow diff --git a/components/sdlutil/gl4es_init.cpp b/components/sdlutil/gl4esinit.cpp similarity index 97% rename from components/sdlutil/gl4es_init.cpp rename to components/sdlutil/gl4esinit.cpp index 044b54ce02..13015dee82 100644 --- a/components/sdlutil/gl4es_init.cpp +++ b/components/sdlutil/gl4esinit.cpp @@ -1,7 +1,7 @@ // EGL does not work reliably for feature detection. // Instead, we initialize gl4es manually. #ifdef OPENMW_GL4ES_MANUAL_INIT -#include "gl4es_init.h" +#include "gl4esinit.h" // For glHint #include diff --git a/components/sdlutil/gl4es_init.h b/components/sdlutil/gl4esinit.h similarity index 67% rename from components/sdlutil/gl4es_init.h rename to components/sdlutil/gl4esinit.h index 6a19d3dfe5..8916d50a7e 100644 --- a/components/sdlutil/gl4es_init.h +++ b/components/sdlutil/gl4esinit.h @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H -#define OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H +#ifndef OPENMW_COMPONENTS_SDLUTIL_GL4ESINIT_H +#define OPENMW_COMPONENTS_SDLUTIL_GL4ESINIT_H #ifdef OPENMW_GL4ES_MANUAL_INIT #include @@ -10,4 +10,4 @@ extern "C" void openmw_gl4es_init(SDL_Window* window); #endif // OPENMW_GL4ES_MANUAL_INIT -#endif // OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H +#endif // OPENMW_COMPONENTS_SDLUTIL_GL4ESINIT_H diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index 36947460df..9ff0545930 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -3,7 +3,7 @@ #include #ifdef OPENMW_GL4ES_MANUAL_INIT -#include "gl4es_init.h" +#include "gl4esinit.h" #endif namespace SDLUtil From b997386cd3865c181ba14e41da15565f7af97cd5 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 21:53:24 +0300 Subject: [PATCH 016/455] Rename android-main.cpp to follow naming conventions --- CI/file_name_exceptions.txt | 1 - apps/openmw/CMakeLists.txt | 4 ++-- apps/openmw/{android_main.cpp => androidmain.cpp} | 0 3 files changed, 2 insertions(+), 3 deletions(-) rename apps/openmw/{android_main.cpp => androidmain.cpp} (100%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 6ae56912dd..f4c12f19a3 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -1,4 +1,3 @@ -apps/openmw/android_main.cpp apps/openmw/mwsound/efx-presets.h apps/openmw/mwsound/ffmpeg_decoder.cpp apps/openmw/mwsound/ffmpeg_decoder.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 37de0abeab..37e9b39a01 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,7 +15,7 @@ set(OPENMW_HEADERS profile.hpp ) -source_group(apps/openmw FILES main.cpp android_main.cpp ${OPENMW_SOURCES} ${OPENMW_HEADERS} ${OPENMW_RESOURCES}) +source_group(apps/openmw FILES main.cpp androidmain.cpp ${OPENMW_SOURCES} ${OPENMW_HEADERS} ${OPENMW_RESOURCES}) add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky skyutil npcanimation esm4npcanimation vismask @@ -127,7 +127,7 @@ if(BUILD_OPENMW) if (ANDROID) add_library(openmw SHARED main.cpp - android_main.cpp + androidmain.cpp ) else() openmw_add_executable(openmw diff --git a/apps/openmw/android_main.cpp b/apps/openmw/androidmain.cpp similarity index 100% rename from apps/openmw/android_main.cpp rename to apps/openmw/androidmain.cpp From dd16c870808997867d757538d91ca47c654c6bd2 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 21:59:55 +0300 Subject: [PATCH 017/455] Rename components/platform files to follow naming conventions --- CI/file_name_exceptions.txt | 3 --- components/CMakeLists.txt | 6 +++--- components/platform/{file.posix.cpp => fileposix.cpp} | 0 components/platform/{file.stdio.cpp => filestdio.cpp} | 0 components/platform/{file.win32.cpp => filewin32.cpp} | 0 5 files changed, 3 insertions(+), 6 deletions(-) rename components/platform/{file.posix.cpp => fileposix.cpp} (100%) rename components/platform/{file.stdio.cpp => filestdio.cpp} (100%) rename components/platform/{file.win32.cpp => filewin32.cpp} (100%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index f4c12f19a3..185b4ba995 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -32,9 +32,6 @@ components/crashcatcher/windows_crashcatcher.hpp components/crashcatcher/windows_crashmonitor.cpp components/crashcatcher/windows_crashmonitor.hpp components/crashcatcher/windows_crashshm.hpp -components/platform/file.posix.cpp -components/platform/file.stdio.cpp -components/platform/file.win32.cpp components/to_utf8/gen_iconv.cpp components/to_utf8/tables_gen.hpp components/to_utf8/to_utf8.cpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index da91cec7ef..d57b0ed823 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -500,15 +500,15 @@ add_component_dir(platform if (WIN32) add_component_dir(platform - file.win32 + filewin32 ) elseif (UNIX) add_component_dir(platform - file.posix + fileposix ) else () add_component_dir(platform - file.stdio + filestdio ) endif() diff --git a/components/platform/file.posix.cpp b/components/platform/fileposix.cpp similarity index 100% rename from components/platform/file.posix.cpp rename to components/platform/fileposix.cpp diff --git a/components/platform/file.stdio.cpp b/components/platform/filestdio.cpp similarity index 100% rename from components/platform/file.stdio.cpp rename to components/platform/filestdio.cpp diff --git a/components/platform/file.win32.cpp b/components/platform/filewin32.cpp similarity index 100% rename from components/platform/file.win32.cpp rename to components/platform/filewin32.cpp From a3e19f9bb7a8192704fe5297c2f98d99f068746b Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 22:04:27 +0300 Subject: [PATCH 018/455] Rename apps/openmw_tests files to follow naming conventions --- CI/file_name_exceptions.txt | 4 ---- apps/openmw_tests/CMakeLists.txt | 6 +++--- .../{test_keywordsearch.cpp => testkeywordsearch.cpp} | 0 .../mwscript/{test_scripts.cpp => testscripts.cpp} | 4 ++-- .../openmw_tests/mwscript/{test_utils.hpp => testutils.hpp} | 0 apps/openmw_tests/mwworld/{test_store.cpp => teststore.cpp} | 0 6 files changed, 5 insertions(+), 9 deletions(-) rename apps/openmw_tests/mwdialogue/{test_keywordsearch.cpp => testkeywordsearch.cpp} (100%) rename apps/openmw_tests/mwscript/{test_scripts.cpp => testscripts.cpp} (99%) rename apps/openmw_tests/mwscript/{test_utils.hpp => testutils.hpp} (100%) rename apps/openmw_tests/mwworld/{test_store.cpp => teststore.cpp} (100%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 185b4ba995..424dc3f691 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -23,10 +23,6 @@ apps/components_tests/lua/test_yaml.cpp apps/components_tests/misc/test_endianness.cpp apps/components_tests/misc/test_resourcehelpers.cpp apps/components_tests/misc/test_stringops.cpp -apps/openmw_tests/mwdialogue/test_keywordsearch.cpp -apps/openmw_tests/mwscript/test_scripts.cpp -apps/openmw_tests/mwscript/test_utils.hpp -apps/openmw_tests/mwworld/test_store.cpp components/crashcatcher/windows_crashcatcher.cpp components/crashcatcher/windows_crashcatcher.hpp components/crashcatcher/windows_crashmonitor.cpp diff --git a/apps/openmw_tests/CMakeLists.txt b/apps/openmw_tests/CMakeLists.txt index 9b57113110..4e57d610a3 100644 --- a/apps/openmw_tests/CMakeLists.txt +++ b/apps/openmw_tests/CMakeLists.txt @@ -6,14 +6,14 @@ file(GLOB UNITTEST_SRC_FILES options.cpp - mwworld/test_store.cpp + mwworld/teststore.cpp mwworld/testduration.cpp mwworld/testtimestamp.cpp mwworld/testptr.cpp - mwdialogue/test_keywordsearch.cpp + mwdialogue/testkeywordsearch.cpp - mwscript/test_scripts.cpp + mwscript/testscripts.cpp ) source_group(apps\\openmw-tests FILES ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_tests/mwdialogue/test_keywordsearch.cpp b/apps/openmw_tests/mwdialogue/testkeywordsearch.cpp similarity index 100% rename from apps/openmw_tests/mwdialogue/test_keywordsearch.cpp rename to apps/openmw_tests/mwdialogue/testkeywordsearch.cpp diff --git a/apps/openmw_tests/mwscript/test_scripts.cpp b/apps/openmw_tests/mwscript/testscripts.cpp similarity index 99% rename from apps/openmw_tests/mwscript/test_scripts.cpp rename to apps/openmw_tests/mwscript/testscripts.cpp index 0de542bdc6..b9e422daed 100644 --- a/apps/openmw_tests/mwscript/test_scripts.cpp +++ b/apps/openmw_tests/mwscript/testscripts.cpp @@ -1,7 +1,7 @@ #include #include -#include "test_utils.hpp" +#include "testutils.hpp" namespace { @@ -935,4 +935,4 @@ End)mwscript"; registerExtensions(); EXPECT_FALSE(!compile(sIssue6807)); } -} \ No newline at end of file +} diff --git a/apps/openmw_tests/mwscript/test_utils.hpp b/apps/openmw_tests/mwscript/testutils.hpp similarity index 100% rename from apps/openmw_tests/mwscript/test_utils.hpp rename to apps/openmw_tests/mwscript/testutils.hpp diff --git a/apps/openmw_tests/mwworld/test_store.cpp b/apps/openmw_tests/mwworld/teststore.cpp similarity index 100% rename from apps/openmw_tests/mwworld/test_store.cpp rename to apps/openmw_tests/mwworld/teststore.cpp From 89426af94a6467225bf15197dd3901e631fd228a Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 22:16:47 +0300 Subject: [PATCH 019/455] Rename apps/components_tests files to follow naming conventions --- CI/file_name_exceptions.txt | 16 --------- apps/components_tests/CMakeLists.txt | 35 +++++++++---------- ...t_fixed_string.cpp => testfixedstring.cpp} | 0 ...nversion_tests.cpp => conversiontests.cpp} | 0 .../lua/{test_async.cpp => testasync.cpp} | 0 ...onfiguration.cpp => testconfiguration.cpp} | 0 ..._inputactions.cpp => testinputactions.cpp} | 0 .../lua/{test_l10n.cpp => testl10n.cpp} | 0 .../lua/{test_lua.cpp => testlua.cpp} | 0 ...container.cpp => testscriptscontainer.cpp} | 0 ...erialization.cpp => testserialization.cpp} | 0 .../lua/{test_storage.cpp => teststorage.cpp} | 0 ...{test_ui_content.cpp => testuicontent.cpp} | 0 ...st_utilpackage.cpp => testutilpackage.cpp} | 0 .../lua/{test_yaml.cpp => testyaml.cpp} | 0 ...test_endianness.cpp => testendianness.cpp} | 0 ...rcehelpers.cpp => testresourcehelpers.cpp} | 0 .../{test_stringops.cpp => teststringops.cpp} | 0 18 files changed, 17 insertions(+), 34 deletions(-) rename apps/components_tests/esm/{test_fixed_string.cpp => testfixedstring.cpp} (100%) rename apps/components_tests/files/{conversion_tests.cpp => conversiontests.cpp} (100%) rename apps/components_tests/lua/{test_async.cpp => testasync.cpp} (100%) rename apps/components_tests/lua/{test_configuration.cpp => testconfiguration.cpp} (100%) rename apps/components_tests/lua/{test_inputactions.cpp => testinputactions.cpp} (100%) rename apps/components_tests/lua/{test_l10n.cpp => testl10n.cpp} (100%) rename apps/components_tests/lua/{test_lua.cpp => testlua.cpp} (100%) rename apps/components_tests/lua/{test_scriptscontainer.cpp => testscriptscontainer.cpp} (100%) rename apps/components_tests/lua/{test_serialization.cpp => testserialization.cpp} (100%) rename apps/components_tests/lua/{test_storage.cpp => teststorage.cpp} (100%) rename apps/components_tests/lua/{test_ui_content.cpp => testuicontent.cpp} (100%) rename apps/components_tests/lua/{test_utilpackage.cpp => testutilpackage.cpp} (100%) rename apps/components_tests/lua/{test_yaml.cpp => testyaml.cpp} (100%) rename apps/components_tests/misc/{test_endianness.cpp => testendianness.cpp} (100%) rename apps/components_tests/misc/{test_resourcehelpers.cpp => testresourcehelpers.cpp} (100%) rename apps/components_tests/misc/{test_stringops.cpp => teststringops.cpp} (100%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 424dc3f691..92fca5c11f 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -7,22 +7,6 @@ apps/openmw/mwsound/sound_buffer.cpp apps/openmw/mwsound/sound_buffer.hpp apps/openmw/mwsound/sound_decoder.hpp apps/openmw/mwsound/sound_output.hpp -apps/components_tests/esm/test_fixed_string.cpp -apps/components_tests/files/conversion_tests.cpp -apps/components_tests/lua/test_async.cpp -apps/components_tests/lua/test_configuration.cpp -apps/components_tests/lua/test_l10n.cpp -apps/components_tests/lua/test_lua.cpp -apps/components_tests/lua/test_scriptscontainer.cpp -apps/components_tests/lua/test_serialization.cpp -apps/components_tests/lua/test_storage.cpp -apps/components_tests/lua/test_ui_content.cpp -apps/components_tests/lua/test_utilpackage.cpp -apps/components_tests/lua/test_inputactions.cpp -apps/components_tests/lua/test_yaml.cpp -apps/components_tests/misc/test_endianness.cpp -apps/components_tests/misc/test_resourcehelpers.cpp -apps/components_tests/misc/test_stringops.cpp components/crashcatcher/windows_crashcatcher.cpp components/crashcatcher/windows_crashcatcher.hpp components/crashcatcher/windows_crashmonitor.cpp diff --git a/apps/components_tests/CMakeLists.txt b/apps/components_tests/CMakeLists.txt index 22bb542538..7595681313 100644 --- a/apps/components_tests/CMakeLists.txt +++ b/apps/components_tests/CMakeLists.txt @@ -4,29 +4,28 @@ include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES main.cpp - esm/test_fixed_string.cpp - esm/variant.cpp + esm/testfixedstring.cpp esm/testrefid.cpp + esm/variant.cpp - lua/test_lua.cpp - lua/test_scriptscontainer.cpp - lua/test_utilpackage.cpp - lua/test_serialization.cpp - lua/test_configuration.cpp - lua/test_l10n.cpp - lua/test_storage.cpp - lua/test_async.cpp - lua/test_inputactions.cpp - lua/test_yaml.cpp - - lua/test_ui_content.cpp + lua/testasync.cpp + lua/testconfiguration.cpp + lua/testinputactions.cpp + lua/testl10n.cpp + lua/testlua.cpp + lua/testscriptscontainer.cpp + lua/testserialization.cpp + lua/teststorage.cpp + lua/testuicontent.cpp + lua/testutilpackage.cpp + lua/testyaml.cpp misc/compression.cpp misc/progressreporter.cpp - misc/test_endianness.cpp - misc/test_resourcehelpers.cpp - misc/test_stringops.cpp + misc/testendianness.cpp misc/testmathutil.cpp + misc/testresourcehelpers.cpp + misc/teststringops.cpp nifloader/testbulletnifloader.cpp @@ -64,8 +63,8 @@ file(GLOB UNITTEST_SRC_FILES esmloader/esmdata.cpp esmloader/record.cpp + files/conversiontests.cpp files/hash.cpp - files/conversion_tests.cpp toutf8/toutf8.cpp diff --git a/apps/components_tests/esm/test_fixed_string.cpp b/apps/components_tests/esm/testfixedstring.cpp similarity index 100% rename from apps/components_tests/esm/test_fixed_string.cpp rename to apps/components_tests/esm/testfixedstring.cpp diff --git a/apps/components_tests/files/conversion_tests.cpp b/apps/components_tests/files/conversiontests.cpp similarity index 100% rename from apps/components_tests/files/conversion_tests.cpp rename to apps/components_tests/files/conversiontests.cpp diff --git a/apps/components_tests/lua/test_async.cpp b/apps/components_tests/lua/testasync.cpp similarity index 100% rename from apps/components_tests/lua/test_async.cpp rename to apps/components_tests/lua/testasync.cpp diff --git a/apps/components_tests/lua/test_configuration.cpp b/apps/components_tests/lua/testconfiguration.cpp similarity index 100% rename from apps/components_tests/lua/test_configuration.cpp rename to apps/components_tests/lua/testconfiguration.cpp diff --git a/apps/components_tests/lua/test_inputactions.cpp b/apps/components_tests/lua/testinputactions.cpp similarity index 100% rename from apps/components_tests/lua/test_inputactions.cpp rename to apps/components_tests/lua/testinputactions.cpp diff --git a/apps/components_tests/lua/test_l10n.cpp b/apps/components_tests/lua/testl10n.cpp similarity index 100% rename from apps/components_tests/lua/test_l10n.cpp rename to apps/components_tests/lua/testl10n.cpp diff --git a/apps/components_tests/lua/test_lua.cpp b/apps/components_tests/lua/testlua.cpp similarity index 100% rename from apps/components_tests/lua/test_lua.cpp rename to apps/components_tests/lua/testlua.cpp diff --git a/apps/components_tests/lua/test_scriptscontainer.cpp b/apps/components_tests/lua/testscriptscontainer.cpp similarity index 100% rename from apps/components_tests/lua/test_scriptscontainer.cpp rename to apps/components_tests/lua/testscriptscontainer.cpp diff --git a/apps/components_tests/lua/test_serialization.cpp b/apps/components_tests/lua/testserialization.cpp similarity index 100% rename from apps/components_tests/lua/test_serialization.cpp rename to apps/components_tests/lua/testserialization.cpp diff --git a/apps/components_tests/lua/test_storage.cpp b/apps/components_tests/lua/teststorage.cpp similarity index 100% rename from apps/components_tests/lua/test_storage.cpp rename to apps/components_tests/lua/teststorage.cpp diff --git a/apps/components_tests/lua/test_ui_content.cpp b/apps/components_tests/lua/testuicontent.cpp similarity index 100% rename from apps/components_tests/lua/test_ui_content.cpp rename to apps/components_tests/lua/testuicontent.cpp diff --git a/apps/components_tests/lua/test_utilpackage.cpp b/apps/components_tests/lua/testutilpackage.cpp similarity index 100% rename from apps/components_tests/lua/test_utilpackage.cpp rename to apps/components_tests/lua/testutilpackage.cpp diff --git a/apps/components_tests/lua/test_yaml.cpp b/apps/components_tests/lua/testyaml.cpp similarity index 100% rename from apps/components_tests/lua/test_yaml.cpp rename to apps/components_tests/lua/testyaml.cpp diff --git a/apps/components_tests/misc/test_endianness.cpp b/apps/components_tests/misc/testendianness.cpp similarity index 100% rename from apps/components_tests/misc/test_endianness.cpp rename to apps/components_tests/misc/testendianness.cpp diff --git a/apps/components_tests/misc/test_resourcehelpers.cpp b/apps/components_tests/misc/testresourcehelpers.cpp similarity index 100% rename from apps/components_tests/misc/test_resourcehelpers.cpp rename to apps/components_tests/misc/testresourcehelpers.cpp diff --git a/apps/components_tests/misc/test_stringops.cpp b/apps/components_tests/misc/teststringops.cpp similarity index 100% rename from apps/components_tests/misc/test_stringops.cpp rename to apps/components_tests/misc/teststringops.cpp From ac9505b53672f4097b471a955353be351821c49a Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 22:35:33 +0300 Subject: [PATCH 020/455] Rename components/to_utf8 directory and files to follow naming conventions --- CI/file_name_exceptions.txt | 4 ---- apps/bulletobjecttool/main.cpp | 2 +- apps/components_tests/esmloader/load.cpp | 2 +- apps/components_tests/toutf8/toutf8.cpp | 2 +- apps/esmtool/tes4.cpp | 2 +- apps/essimporter/importer.cpp | 2 +- apps/mwiniimporter/importer.hpp | 2 +- apps/navmeshtool/main.cpp | 2 +- apps/opencs/editor.cpp | 2 +- apps/opencs/model/doc/document.hpp | 2 +- apps/opencs/model/doc/documentmanager.hpp | 2 +- apps/opencs/model/doc/saving.hpp | 2 +- apps/opencs/model/doc/savingstate.hpp | 2 +- apps/opencs/model/tools/mergeoperation.hpp | 2 +- apps/opencs/model/tools/mergestages.hpp | 2 +- apps/opencs/model/tools/tools.hpp | 2 +- apps/opencs/model/world/data.hpp | 2 +- apps/openmw/mwgui/journalbooks.hpp | 2 +- apps/openmw/mwgui/journalwindow.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- apps/wizard/inisettings.hpp | 2 +- components/CMakeLists.txt | 6 +++--- components/esm3/esmreader.hpp | 2 +- components/esm3/esmwriter.cpp | 2 +- components/esm4/reader.cpp | 2 +- components/fontloader/fontloader.hpp | 2 +- components/nif/nifstream.cpp | 4 ++-- components/to_utf8/.gitignore | 1 - components/to_utf8/Makefile | 8 -------- components/toutf8/.gitignore | 1 + components/toutf8/Makefile | 8 ++++++++ components/{to_utf8/gen_iconv.cpp => toutf8/geniconv.cpp} | 4 ++-- .../{to_utf8/tables_gen.hpp => toutf8/tablesgen.hpp} | 4 ++-- components/{to_utf8/to_utf8.cpp => toutf8/toutf8.cpp} | 4 ++-- components/{to_utf8/to_utf8.hpp => toutf8/toutf8.hpp} | 4 ++-- components/translation/translation.hpp | 2 +- 36 files changed, 47 insertions(+), 51 deletions(-) delete mode 100644 components/to_utf8/.gitignore delete mode 100644 components/to_utf8/Makefile create mode 100644 components/toutf8/.gitignore create mode 100644 components/toutf8/Makefile rename components/{to_utf8/gen_iconv.cpp => toutf8/geniconv.cpp} (95%) rename components/{to_utf8/tables_gen.hpp => toutf8/tablesgen.hpp} (99%) rename components/{to_utf8/to_utf8.cpp => toutf8/toutf8.cpp} (99%) rename components/{to_utf8/to_utf8.hpp => toutf8/toutf8.hpp} (97%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 92fca5c11f..95ecafa856 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -12,7 +12,3 @@ components/crashcatcher/windows_crashcatcher.hpp components/crashcatcher/windows_crashmonitor.cpp components/crashcatcher/windows_crashmonitor.hpp components/crashcatcher/windows_crashshm.hpp -components/to_utf8/gen_iconv.cpp -components/to_utf8/tables_gen.hpp -components/to_utf8/to_utf8.cpp -components/to_utf8/to_utf8.hpp diff --git a/apps/bulletobjecttool/main.cpp b/apps/bulletobjecttool/main.cpp index 190eb3364d..857eb357aa 100644 --- a/apps/bulletobjecttool/main.cpp +++ b/apps/bulletobjecttool/main.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/apps/components_tests/esmloader/load.cpp b/apps/components_tests/esmloader/load.cpp index 20e06507d1..e5e1b15876 100644 --- a/apps/components_tests/esmloader/load.cpp +++ b/apps/components_tests/esmloader/load.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include diff --git a/apps/components_tests/toutf8/toutf8.cpp b/apps/components_tests/toutf8/toutf8.cpp index 704ee6742d..3c84da65ee 100644 --- a/apps/components_tests/toutf8/toutf8.cpp +++ b/apps/components_tests/toutf8/toutf8.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include diff --git a/apps/esmtool/tes4.cpp b/apps/esmtool/tes4.cpp index 5b657da573..4ff632a217 100644 --- a/apps/esmtool/tes4.cpp +++ b/apps/esmtool/tes4.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include namespace EsmTool { diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 5cc9a8259b..cf04fee163 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include "importercontext.hpp" diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index 4c42caf5a3..342670713c 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include class MwIniImporter { diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index 27f84104ac..5c7fe53126 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 4cab88e5f2..0072a18d0d 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "view/doc/viewmanager.hpp" diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 60516cdc8c..ac8f06712c 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include "../world/data.hpp" #include "../world/idcompletionmanager.hpp" diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 2c9ee1e98e..53caf04a0a 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include "loader.hpp" diff --git a/apps/opencs/model/doc/saving.hpp b/apps/opencs/model/doc/saving.hpp index 5dcdbb6803..6197798fdf 100644 --- a/apps/opencs/model/doc/saving.hpp +++ b/apps/opencs/model/doc/saving.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include "operation.hpp" #include "savingstate.hpp" diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index c42dff0366..af8caa2c4d 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include namespace CSMDoc { diff --git a/apps/opencs/model/tools/mergeoperation.hpp b/apps/opencs/model/tools/mergeoperation.hpp index 2cce2bec0d..c50a7eefb0 100644 --- a/apps/opencs/model/tools/mergeoperation.hpp +++ b/apps/opencs/model/tools/mergeoperation.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include "../doc/operation.hpp" diff --git a/apps/opencs/model/tools/mergestages.hpp b/apps/opencs/model/tools/mergestages.hpp index 42f06858b1..40cc799c81 100644 --- a/apps/opencs/model/tools/mergestages.hpp +++ b/apps/opencs/model/tools/mergestages.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include "../doc/stage.hpp" diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index c9e8937c90..939dea9bb8 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -6,7 +6,7 @@ #include -#include +#include #include diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 237b746746..c119541381 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "cell.hpp" #include "idcollection.hpp" diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index 1970830eab..792edcc070 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -4,7 +4,7 @@ #include "bookpage.hpp" #include "journalviewmodel.hpp" -#include +#include namespace MWGui { diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 22e7048acf..f0f394156c 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -3,7 +3,7 @@ #include "windowbase.hpp" -#include +#include #include diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 052a269188..3b85b74a8d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "charactercreation.hpp" #include "draganddrop.hpp" diff --git a/apps/wizard/inisettings.hpp b/apps/wizard/inisettings.hpp index c8cd30c3c1..6ea8fc4adb 100644 --- a/apps/wizard/inisettings.hpp +++ b/apps/wizard/inisettings.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include namespace Wizard { diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d57b0ed823..217839ce61 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -155,9 +155,9 @@ add_component_dir (nifbullet bulletnifloader ) -add_component_dir (to_utf8 - tables_gen - to_utf8 +add_component_dir (toutf8 + tablesgen + toutf8 ) add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge esmterrain diff --git a/components/esm3/esmreader.hpp b/components/esm3/esmreader.hpp index 5af5e75573..9bae5f217e 100644 --- a/components/esm3/esmreader.hpp +++ b/components/esm3/esmreader.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include "components/esm/decompose.hpp" #include "components/esm/esmcommon.hpp" diff --git a/components/esm3/esmwriter.cpp b/components/esm3/esmwriter.cpp index 47c861e3ca..8bae844585 100644 --- a/components/esm3/esmwriter.cpp +++ b/components/esm3/esmwriter.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "formatversion.hpp" diff --git a/components/esm4/reader.cpp b/components/esm4/reader.cpp index 505922601d..43a9e26418 100644 --- a/components/esm4/reader.cpp +++ b/components/esm4/reader.cpp @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include "grouptype.hpp" diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index 7e9220d58d..e5705f6af1 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace VFS { diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index ef63bae937..6c361401e8 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -2,9 +2,9 @@ #include -#include "niffile.hpp" +#include -#include "../to_utf8/to_utf8.hpp" +#include "niffile.hpp" namespace { diff --git a/components/to_utf8/.gitignore b/components/to_utf8/.gitignore deleted file mode 100644 index 4e0357749e..0000000000 --- a/components/to_utf8/.gitignore +++ /dev/null @@ -1 +0,0 @@ -gen_iconv diff --git a/components/to_utf8/Makefile b/components/to_utf8/Makefile deleted file mode 100644 index 5234d455ae..0000000000 --- a/components/to_utf8/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -tables_gen.hpp: gen_iconv - ./gen_iconv > tables_gen.hpp - -gen_iconv: gen_iconv.cpp - g++ -Wall $^ -o $@ - -clean: - rm -f ./gen_iconv \ No newline at end of file diff --git a/components/toutf8/.gitignore b/components/toutf8/.gitignore new file mode 100644 index 0000000000..f1f54f5aea --- /dev/null +++ b/components/toutf8/.gitignore @@ -0,0 +1 @@ +geniconv diff --git a/components/toutf8/Makefile b/components/toutf8/Makefile new file mode 100644 index 0000000000..e46176d3ed --- /dev/null +++ b/components/toutf8/Makefile @@ -0,0 +1,8 @@ +tablesgen.hpp: geniconv + ./geniconv > tablesgen.hpp + +geniconv: geniconv.cpp + g++ -Wall $^ -o $@ + +clean: + rm -f ./geniconv diff --git a/components/to_utf8/gen_iconv.cpp b/components/toutf8/geniconv.cpp similarity index 95% rename from components/to_utf8/gen_iconv.cpp rename to components/toutf8/geniconv.cpp index 062b1173fc..48a7fb4627 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/toutf8/geniconv.cpp @@ -1,4 +1,4 @@ -// This program generates the file tables_gen.hpp +// This program generates the file tablesgen.hpp #include @@ -88,7 +88,7 @@ int write_table(const std::string& charset, const std::string& tableName) int main() { // Write header guard - std::cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; + std::cout << "#ifndef OPENMW_COMPONENTS_TOUTF8_TABLESGEN_HPP\n#define OPENMW_COMPONENTS_TOUTF8_TABLESGEN_HPP\n\n"; // Write namespace std::cout << "namespace ToUTF8\n{\n\n"; diff --git a/components/to_utf8/tables_gen.hpp b/components/toutf8/tablesgen.hpp similarity index 99% rename from components/to_utf8/tables_gen.hpp rename to components/toutf8/tablesgen.hpp index b25bb54bab..9ef5097ece 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/toutf8/tablesgen.hpp @@ -1,5 +1,5 @@ -#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H -#define COMPONENTS_TOUTF8_TABLE_GEN_H +#ifndef OPENMW_COMPONENTS_TOUTF8_TABLESGEN_HPP +#define OPENMW_COMPONENTS_TOUTF8_TABLESGEN_HPP namespace ToUTF8 { diff --git a/components/to_utf8/to_utf8.cpp b/components/toutf8/toutf8.cpp similarity index 99% rename from components/to_utf8/to_utf8.cpp rename to components/toutf8/toutf8.cpp index 15fb8b26c0..e9b2a7fe2f 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/toutf8/toutf8.cpp @@ -1,4 +1,4 @@ -#include "to_utf8.hpp" +#include "toutf8.hpp" #include #include @@ -41,7 +41,7 @@ */ // Generated tables -#include "tables_gen.hpp" +#include "tablesgen.hpp" using namespace ToUTF8; diff --git a/components/to_utf8/to_utf8.hpp b/components/toutf8/toutf8.hpp similarity index 97% rename from components/to_utf8/to_utf8.hpp rename to components/toutf8/toutf8.hpp index 80af6586c9..1da4ebffbb 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/toutf8/toutf8.hpp @@ -1,5 +1,5 @@ -#ifndef COMPONENTS_TOUTF8_H -#define COMPONENTS_TOUTF8_H +#ifndef OPENMW_COMPONENTS_TOUTF8_TOUTF8_HPP +#define OPENMW_COMPONENTS_TOUTF8_TOUTF8_HPP #include #include diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index a7b6087ac7..18e40713e5 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -2,7 +2,7 @@ #define COMPONENTS_TRANSLATION_DATA_H #include -#include +#include namespace Translation { From fc850cfe69c75482d0434255a9f38ecf91fbf3ee Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 22:56:21 +0300 Subject: [PATCH 021/455] Rename components/crashcatcher files to follow naming conventions --- CI/file_name_exceptions.txt | 5 ----- components/CMakeLists.txt | 6 +++--- components/crashcatcher/crashcatcher.hpp | 4 ++-- ...{windows_crashcatcher.cpp => windowscrashcatcher.cpp} | 9 +++++---- ...{windows_crashcatcher.hpp => windowscrashcatcher.hpp} | 9 +++++---- components/crashcatcher/windowscrashdumppathhelpers.hpp | 6 ++++-- ...{windows_crashmonitor.cpp => windowscrashmonitor.cpp} | 9 +++++---- ...{windows_crashmonitor.hpp => windowscrashmonitor.hpp} | 6 +++--- .../{windows_crashshm.hpp => windowscrashshm.hpp} | 6 +++--- components/debug/debugging.cpp | 2 +- 10 files changed, 31 insertions(+), 31 deletions(-) rename components/crashcatcher/{windows_crashcatcher.cpp => windowscrashcatcher.cpp} (98%) rename components/crashcatcher/{windows_crashcatcher.hpp => windowscrashcatcher.hpp} (94%) rename components/crashcatcher/{windows_crashmonitor.cpp => windowscrashmonitor.cpp} (99%) rename components/crashcatcher/{windows_crashmonitor.hpp => windowscrashmonitor.hpp} (90%) rename components/crashcatcher/{windows_crashshm.hpp => windowscrashshm.hpp} (89%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index 95ecafa856..a388ebf630 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -7,8 +7,3 @@ apps/openmw/mwsound/sound_buffer.cpp apps/openmw/mwsound/sound_buffer.hpp apps/openmw/mwsound/sound_decoder.hpp apps/openmw/mwsound/sound_output.hpp -components/crashcatcher/windows_crashcatcher.cpp -components/crashcatcher/windows_crashcatcher.hpp -components/crashcatcher/windows_crashmonitor.cpp -components/crashcatcher/windows_crashmonitor.hpp -components/crashcatcher/windows_crashshm.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 217839ce61..2385b4ca83 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -390,10 +390,10 @@ copy_resource_file("lua_ui/content.lua" "${OPENMW_RESOURCES_ROOT}" "resources/lu if(WIN32) add_component_dir (crashcatcher - windows_crashcatcher - windows_crashmonitor - windows_crashshm + windowscrashcatcher windowscrashdumppathhelpers + windowscrashmonitor + windowscrashshm ) elseif(NOT ANDROID) add_component_dir (crashcatcher diff --git a/components/crashcatcher/crashcatcher.hpp b/components/crashcatcher/crashcatcher.hpp index 16b416cf98..60f749e57a 100644 --- a/components/crashcatcher/crashcatcher.hpp +++ b/components/crashcatcher/crashcatcher.hpp @@ -1,5 +1,5 @@ -#ifndef CRASHCATCHER_H -#define CRASHCATCHER_H +#ifndef OPENMW_COMPONENTS_CRASHCATCHER_CRASHCATCHER_HPP +#define OPENMW_COMPONENTS_CRASHCATCHER_CRASHCATCHER_HPP #include diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windowscrashcatcher.cpp similarity index 98% rename from components/crashcatcher/windows_crashcatcher.cpp rename to components/crashcatcher/windowscrashcatcher.cpp index 23dfac549c..40c54834ac 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windowscrashcatcher.cpp @@ -1,17 +1,18 @@ -#include "windows_crashcatcher.hpp" +#include "windowscrashcatcher.hpp" #include #include #include #include -#include "windows_crashmonitor.hpp" -#include "windows_crashshm.hpp" -#include "windowscrashdumppathhelpers.hpp" #include #include +#include "windowscrashdumppathhelpers.hpp" +#include "windowscrashmonitor.hpp" +#include "windowscrashshm.hpp" + namespace Crash { namespace diff --git a/components/crashcatcher/windows_crashcatcher.hpp b/components/crashcatcher/windowscrashcatcher.hpp similarity index 94% rename from components/crashcatcher/windows_crashcatcher.hpp rename to components/crashcatcher/windowscrashcatcher.hpp index 89678c9ada..ea53fcb520 100644 --- a/components/crashcatcher/windows_crashcatcher.hpp +++ b/components/crashcatcher/windowscrashcatcher.hpp @@ -1,11 +1,12 @@ -#ifndef WINDOWS_CRASHCATCHER_HPP -#define WINDOWS_CRASHCATCHER_HPP +#ifndef OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHCATCHER_HPP +#define OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHCATCHER_HPP #include -#include #include +#include "crashcatcher.hpp" + namespace Crash { @@ -78,4 +79,4 @@ namespace Crash } // namespace Crash -#endif // WINDOWS_CRASHCATCHER_HPP +#endif diff --git a/components/crashcatcher/windowscrashdumppathhelpers.hpp b/components/crashcatcher/windowscrashdumppathhelpers.hpp index fa64969301..9e11c57834 100644 --- a/components/crashcatcher/windowscrashdumppathhelpers.hpp +++ b/components/crashcatcher/windowscrashdumppathhelpers.hpp @@ -1,8 +1,10 @@ -#ifndef COMPONENTS_CRASH_WINDOWSCRASHDUMPPATHHELPERS_H -#include "windows_crashshm.hpp" +#ifndef OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHDUMPPATHHELPERS_HPP +#define OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHDUMPPATHHELPERS_HPP #include +#include "windowscrashshm.hpp" + namespace Crash { std::filesystem::path getCrashDumpPath(const CrashSHM& crashShm); diff --git a/components/crashcatcher/windows_crashmonitor.cpp b/components/crashcatcher/windowscrashmonitor.cpp similarity index 99% rename from components/crashcatcher/windows_crashmonitor.cpp rename to components/crashcatcher/windowscrashmonitor.cpp index 3708283efa..a92c310f63 100644 --- a/components/crashcatcher/windows_crashmonitor.cpp +++ b/components/crashcatcher/windowscrashmonitor.cpp @@ -1,4 +1,4 @@ -#include "windows_crashmonitor.hpp" +#include "windowscrashmonitor.hpp" #include #include @@ -10,12 +10,13 @@ #include -#include "windows_crashcatcher.hpp" -#include "windows_crashshm.hpp" -#include "windowscrashdumppathhelpers.hpp" #include #include +#include "windowscrashcatcher.hpp" +#include "windowscrashdumppathhelpers.hpp" +#include "windowscrashshm.hpp" + namespace Crash { std::unordered_map CrashMonitor::smEventHookOwners{}; diff --git a/components/crashcatcher/windows_crashmonitor.hpp b/components/crashcatcher/windowscrashmonitor.hpp similarity index 90% rename from components/crashcatcher/windows_crashmonitor.hpp rename to components/crashcatcher/windowscrashmonitor.hpp index 16e173169e..cb650df6dd 100644 --- a/components/crashcatcher/windows_crashmonitor.hpp +++ b/components/crashcatcher/windowscrashmonitor.hpp @@ -1,5 +1,5 @@ -#ifndef WINDOWS_CRASHMONITOR_HPP -#define WINDOWS_CRASHMONITOR_HPP +#ifndef OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHMONITOR_HPP +#define OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHMONITOR_HPP #include @@ -60,4 +60,4 @@ namespace Crash } // namespace Crash -#endif // WINDOWS_CRASHMONITOR_HPP +#endif diff --git a/components/crashcatcher/windows_crashshm.hpp b/components/crashcatcher/windowscrashshm.hpp similarity index 89% rename from components/crashcatcher/windows_crashshm.hpp rename to components/crashcatcher/windowscrashshm.hpp index b919757890..a9357710ca 100644 --- a/components/crashcatcher/windows_crashshm.hpp +++ b/components/crashcatcher/windowscrashshm.hpp @@ -1,5 +1,5 @@ -#ifndef WINDOWS_CRASHSHM_HPP -#define WINDOWS_CRASHSHM_HPP +#ifndef OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHSHM_HPP +#define OPENMW_COMPONENTS_CRASHCATCHER_WINDOWSCRASHSHM_HPP #include @@ -44,4 +44,4 @@ namespace Crash } // namespace Crash -#endif // WINDOWS_CRASHSHM_HPP +#endif diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 32aec8f0fc..11d74be606 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -22,7 +22,7 @@ #include #ifdef _WIN32 -#include +#include #include #include From 72cbf61b4351e6be9ec24581b96b5471c3b6f34c Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 23:45:51 +0300 Subject: [PATCH 022/455] Rename apps/openmw/mwsound files and classes to follow naming conventions --- CI/file_name_exceptions.txt | 9 -- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/mwbase/soundmanager.hpp | 4 +- .../mwsound/{efx-presets.h => efxpresets.h} | 6 +- .../{ffmpeg_decoder.cpp => ffmpegdecoder.cpp} | 40 ++++---- .../{ffmpeg_decoder.hpp => ffmpegdecoder.hpp} | 16 ++-- apps/openmw/mwsound/loudness.hpp | 2 +- apps/openmw/mwsound/movieaudiofactory.cpp | 6 +- .../{openal_output.cpp => openaloutput.cpp} | 93 +++++++++---------- .../{openal_output.hpp => openaloutput.hpp} | 16 ++-- apps/openmw/mwsound/sound.hpp | 4 +- .../{sound_buffer.cpp => soundbuffer.cpp} | 32 +++---- .../{sound_buffer.hpp => soundbuffer.hpp} | 43 ++++----- .../{sound_decoder.hpp => sounddecoder.hpp} | 14 +-- apps/openmw/mwsound/soundmanagerimp.cpp | 50 +++++----- apps/openmw/mwsound/soundmanagerimp.hpp | 22 ++--- .../{sound_output.hpp => soundoutput.hpp} | 18 ++-- 17 files changed, 185 insertions(+), 194 deletions(-) rename apps/openmw/mwsound/{efx-presets.h => efxpresets.h} (99%) rename apps/openmw/mwsound/{ffmpeg_decoder.cpp => ffmpegdecoder.cpp} (93%) rename apps/openmw/mwsound/{ffmpeg_decoder.hpp => ffmpegdecoder.hpp} (89%) rename apps/openmw/mwsound/{openal_output.cpp => openaloutput.cpp} (95%) rename apps/openmw/mwsound/{openal_output.hpp => openaloutput.hpp} (91%) rename apps/openmw/mwsound/{sound_buffer.cpp => soundbuffer.cpp} (85%) rename apps/openmw/mwsound/{sound_buffer.hpp => soundbuffer.hpp} (68%) rename apps/openmw/mwsound/{sound_decoder.hpp => sounddecoder.hpp} (80%) rename apps/openmw/mwsound/{sound_output.hpp => soundoutput.hpp} (88%) diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt index a388ebf630..e69de29bb2 100644 --- a/CI/file_name_exceptions.txt +++ b/CI/file_name_exceptions.txt @@ -1,9 +0,0 @@ -apps/openmw/mwsound/efx-presets.h -apps/openmw/mwsound/ffmpeg_decoder.cpp -apps/openmw/mwsound/ffmpeg_decoder.hpp -apps/openmw/mwsound/openal_output.cpp -apps/openmw/mwsound/openal_output.hpp -apps/openmw/mwsound/sound_buffer.cpp -apps/openmw/mwsound/sound_buffer.hpp -apps/openmw/mwsound/sound_decoder.hpp -apps/openmw/mwsound/sound_output.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 37e9b39a01..7319791d87 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -71,8 +71,8 @@ add_openmw_dir (mwlua ) add_openmw_dir (mwsound - soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output - loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater + soundmanagerimp openaloutput ffmpegdecoder sound soundbuffer sounddecoder soundoutput + loudness movieaudiofactory alext efx efxpresets regionsoundselector watersoundupdater ) add_openmw_dir (mwworld diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 5f96a4e095..532bc771ba 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -39,8 +39,8 @@ namespace MWSound class Sound; class Stream; - struct Sound_Decoder; - typedef std::shared_ptr DecoderPtr; + struct SoundDecoder; + typedef std::shared_ptr DecoderPtr; /* These must all fit together */ enum class PlayMode diff --git a/apps/openmw/mwsound/efx-presets.h b/apps/openmw/mwsound/efxpresets.h similarity index 99% rename from apps/openmw/mwsound/efx-presets.h rename to apps/openmw/mwsound/efxpresets.h index a9662936d5..a36c41db03 100644 --- a/apps/openmw/mwsound/efx-presets.h +++ b/apps/openmw/mwsound/efxpresets.h @@ -1,7 +1,7 @@ /* Reverb presets for EFX */ -#ifndef EFX_PRESETS_H -#define EFX_PRESETS_H +#ifndef GAME_SOUND_EFXPRESETS_H +#define GAME_SOUND_EFXPRESETS_H #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED @@ -852,4 +852,4 @@ typedef struct 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 \ } -#endif /* EFX_PRESETS_H */ +#endif diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpegdecoder.cpp similarity index 93% rename from apps/openmw/mwsound/ffmpeg_decoder.cpp rename to apps/openmw/mwsound/ffmpegdecoder.cpp index 54fd126c41..5a0f336a93 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpegdecoder.cpp @@ -1,4 +1,4 @@ -#include "ffmpeg_decoder.hpp" +#include "ffmpegdecoder.hpp" #include #include @@ -44,11 +44,11 @@ namespace MWSound av_frame_free(&ptr); } - int FFmpeg_Decoder::readPacket(void* user_data, uint8_t* buf, int buf_size) + int FFmpegDecoder::readPacket(void* user_data, uint8_t* buf, int buf_size) { try { - std::istream& stream = *static_cast(user_data)->mDataStream; + std::istream& stream = *static_cast(user_data)->mDataStream; stream.clear(); stream.read((char*)buf, buf_size); std::streamsize count = stream.gcount(); @@ -65,18 +65,18 @@ namespace MWSound } #if OPENMW_FFMPEG_CONST_WRITEPACKET - int FFmpeg_Decoder::writePacket(void*, const uint8_t*, int) + int FFmpegDecoder::writePacket(void*, const uint8_t*, int) #else - int FFmpeg_Decoder::writePacket(void*, uint8_t*, int) + int FFmpegDecoder::writePacket(void*, uint8_t*, int) #endif { Log(Debug::Error) << "can't write to read-only stream"; return -1; } - int64_t FFmpeg_Decoder::seek(void* user_data, int64_t offset, int whence) + int64_t FFmpegDecoder::seek(void* user_data, int64_t offset, int whence) { - std::istream& stream = *static_cast(user_data)->mDataStream; + std::istream& stream = *static_cast(user_data)->mDataStream; whence &= ~AVSEEK_FORCE; @@ -106,7 +106,7 @@ namespace MWSound /* Used by getAV*Data to search for more compressed data, and buffer it in the * correct stream. It won't buffer data for streams that the app doesn't have a * handle for. */ - bool FFmpeg_Decoder::getNextPacket() + bool FFmpegDecoder::getNextPacket() { if (!mStream) return false; @@ -129,7 +129,7 @@ namespace MWSound return false; } - bool FFmpeg_Decoder::getAVAudioData() + bool FFmpegDecoder::getAVAudioData() { bool got_frame = false; @@ -192,7 +192,7 @@ namespace MWSound return true; } - size_t FFmpeg_Decoder::readAVAudioData(void* data, size_t length) + size_t FFmpegDecoder::readAVAudioData(void* data, size_t length) { size_t dec = 0; @@ -227,7 +227,7 @@ namespace MWSound return dec; } - void FFmpeg_Decoder::open(VFS::Path::NormalizedView fname) + void FFmpegDecoder::open(VFS::Path::NormalizedView fname) { close(); mDataStream = mResourceMgr->get(fname); @@ -317,7 +317,7 @@ namespace MWSound mStream = stream; } - void FFmpeg_Decoder::close() + void FFmpegDecoder::close() { mStream = nullptr; mCodecCtx.reset(); @@ -332,7 +332,7 @@ namespace MWSound mDataStream.reset(); } - std::string FFmpeg_Decoder::getName() + std::string FFmpegDecoder::getName() { // In the FFMpeg 4.0 a "filename" field was replaced by "url" #if LIBAVCODEC_VERSION_INT < 3805796 @@ -342,7 +342,7 @@ namespace MWSound #endif } - void FFmpeg_Decoder::getInfo(int* samplerate, ChannelConfig* chans, SampleType* type) + void FFmpegDecoder::getInfo(int* samplerate, ChannelConfig* chans, SampleType* type) { if (!mStream) throw std::runtime_error("No audio stream info"); @@ -459,7 +459,7 @@ namespace MWSound } } - size_t FFmpeg_Decoder::read(char* buffer, size_t bytes) + size_t FFmpegDecoder::read(char* buffer, size_t bytes) { if (!mStream) { @@ -469,7 +469,7 @@ namespace MWSound return readAVAudioData(buffer, bytes); } - void FFmpeg_Decoder::readAll(std::vector& output) + void FFmpegDecoder::readAll(std::vector& output) { if (!mStream) { @@ -490,7 +490,7 @@ namespace MWSound } } - size_t FFmpeg_Decoder::getSampleOffset() + size_t FFmpegDecoder::getSampleOffset() { #if OPENMW_FFMPEG_5_OR_GREATER std::size_t delay = (mFrameSize - mFramePos) / mOutputChannelLayout.nb_channels @@ -501,8 +501,8 @@ namespace MWSound return static_cast(mNextPts * mCodecCtx->sample_rate) - delay; } - FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) - : Sound_Decoder(vfs) + FFmpegDecoder::FFmpegDecoder(const VFS::Manager* vfs) + : SoundDecoder(vfs) , mStream(nullptr) , mFrameSize(0) , mFramePos(0) @@ -534,7 +534,7 @@ namespace MWSound } } - FFmpeg_Decoder::~FFmpeg_Decoder() + FFmpegDecoder::~FFmpegDecoder() { close(); } diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpegdecoder.hpp similarity index 89% rename from apps/openmw/mwsound/ffmpeg_decoder.hpp rename to apps/openmw/mwsound/ffmpegdecoder.hpp index e67b8efbf3..c71b935bc4 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpegdecoder.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_SOUND_FFMPEG_DECODER_H -#define GAME_SOUND_FFMPEG_DECODER_H +#ifndef GAME_SOUND_FFMPEGDECODER_H +#define GAME_SOUND_FFMPEGDECODER_H #include @@ -31,7 +31,7 @@ extern "C" #include -#include "sound_decoder.hpp" +#include "sounddecoder.hpp" namespace MWSound { @@ -63,7 +63,7 @@ namespace MWSound using AVFramePtr = std::unique_ptr; - class FFmpeg_Decoder final : public Sound_Decoder + class FFmpegDecoder final : public SoundDecoder { AVIOContextPtr mIoCtx; AVFormatContextPtr mFormatCtx; @@ -114,13 +114,13 @@ namespace MWSound void readAll(std::vector& output) override; size_t getSampleOffset() override; - FFmpeg_Decoder& operator=(const FFmpeg_Decoder& rhs); - FFmpeg_Decoder(const FFmpeg_Decoder& rhs); + FFmpegDecoder& operator=(const FFmpegDecoder& rhs); + FFmpegDecoder(const FFmpegDecoder& rhs); public: - explicit FFmpeg_Decoder(const VFS::Manager* vfs); + explicit FFmpegDecoder(const VFS::Manager* vfs); - virtual ~FFmpeg_Decoder(); + virtual ~FFmpegDecoder(); friend class SoundManager; }; diff --git a/apps/openmw/mwsound/loudness.hpp b/apps/openmw/mwsound/loudness.hpp index 1800ec246f..edc201f38a 100644 --- a/apps/openmw/mwsound/loudness.hpp +++ b/apps/openmw/mwsound/loudness.hpp @@ -4,7 +4,7 @@ #include #include -#include "sound_decoder.hpp" +#include "sounddecoder.hpp" namespace MWSound { diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 962086701a..61992bc0d5 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -7,17 +7,17 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" -#include "sound_decoder.hpp" +#include "sounddecoder.hpp" namespace MWSound { class MovieAudioDecoder; - class MWSoundDecoderBridge final : public Sound_Decoder + class MWSoundDecoderBridge final : public SoundDecoder { public: MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) - : Sound_Decoder(nullptr) + : SoundDecoder(nullptr) , mDecoder(decoder) { } diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openaloutput.cpp similarity index 95% rename from apps/openmw/mwsound/openal_output.cpp rename to apps/openmw/mwsound/openaloutput.cpp index f829ea458a..0f27524912 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openaloutput.cpp @@ -17,14 +17,13 @@ #include #include +#include "efxpresets.h" #include "loudness.hpp" -#include "openal_output.hpp" +#include "openaloutput.hpp" #include "sound.hpp" -#include "sound_decoder.hpp" +#include "sounddecoder.hpp" #include "soundmanagerimp.hpp" -#include "efx-presets.h" - #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 #endif @@ -301,7 +300,7 @@ namespace MWSound OpenAL_SoundStream(const OpenAL_SoundStream& rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream& rhs); - friend class OpenAL_Output; + friend class OpenALOutput; public: OpenAL_SoundStream(ALuint src, DecoderPtr decoder); @@ -323,7 +322,7 @@ namespace MWSound // // A background streaming thread (keeps active streams processed) // - struct OpenAL_Output::StreamThread + struct OpenALOutput::StreamThread { std::vector mStreams; @@ -393,13 +392,13 @@ namespace MWSound StreamThread& operator=(const StreamThread& rhs) = delete; }; - class OpenAL_Output::DefaultDeviceThread + class OpenALOutput::DefaultDeviceThread { public: std::basic_string mCurrentName; private: - OpenAL_Output& mOutput; + OpenALOutput& mOutput; std::atomic mQuitNow; std::mutex mMutex; @@ -433,7 +432,7 @@ namespace MWSound } public: - DefaultDeviceThread(OpenAL_Output& output, std::basic_string_view name = {}) + DefaultDeviceThread(OpenALOutput& output, std::basic_string_view name = {}) : mCurrentName(name) , mOutput(output) , mQuitNow(false) @@ -655,7 +654,7 @@ namespace MWSound // // An OpenAL output device // - std::vector OpenAL_Output::enumerate() + std::vector OpenALOutput::enumerate() { std::vector devlist; const ALCchar* devnames; @@ -672,14 +671,14 @@ namespace MWSound return devlist; } - void OpenAL_Output::eventCallback( + void OpenALOutput::eventCallback( ALenum eventType, ALuint object, ALuint param, ALsizei length, const ALchar* message, void* userParam) { if (eventType == AL_EVENT_TYPE_DISCONNECTED_SOFT) - static_cast(userParam)->onDisconnect(); + static_cast(userParam)->onDisconnect(); } - void OpenAL_Output::onDisconnect() + void OpenALOutput::onDisconnect() { if (!mInitialized || !alcReopenDeviceSOFT) return; @@ -702,7 +701,7 @@ namespace MWSound } } - bool OpenAL_Output::init(const std::string& devname, const std::string& hrtfname, HrtfMode hrtfmode) + bool OpenALOutput::init(const std::string& devname, const std::string& hrtfname, HrtfMode hrtfmode) { deinit(); std::lock_guard lock(mReopenMutex); @@ -802,7 +801,7 @@ namespace MWSound { static const std::array events{ { AL_EVENT_TYPE_DISCONNECTED_SOFT } }; alEventControlSOFT(events.size(), events.data(), AL_TRUE); - alEventCallbackSOFT(&OpenAL_Output::eventCallback, this); + alEventCallbackSOFT(&OpenALOutput::eventCallback, this); } else Log(Debug::Warning) << "Cannot detect audio device changes"; @@ -970,7 +969,7 @@ namespace MWSound return true; } - void OpenAL_Output::deinit() + void OpenALOutput::deinit() { mStreamThread->removeAll(); mDefaultDeviceThread.reset(); @@ -1006,7 +1005,7 @@ namespace MWSound mInitialized = false; } - std::vector OpenAL_Output::enumerateHrtf() + std::vector OpenALOutput::enumerateHrtf() { std::vector ret; @@ -1028,7 +1027,7 @@ namespace MWSound return ret; } - std::pair OpenAL_Output::loadSound(VFS::Path::NormalizedView fname) + std::pair OpenALOutput::loadSound(VFS::Path::NormalizedView fname) { getALError(); @@ -1076,7 +1075,7 @@ namespace MWSound return std::make_pair(MAKE_PTRID(buf), size); } - size_t OpenAL_Output::unloadSound(Sound_Handle data) + size_t OpenALOutput::unloadSound(Sound_Handle data) { ALuint buffer = GET_PTRID(data); if (!buffer) @@ -1105,7 +1104,7 @@ namespace MWSound return size; } - void OpenAL_Output::initCommon2D( + void OpenALOutput::initCommon2D( ALuint source, const osg::Vec3f& pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f); @@ -1143,7 +1142,7 @@ namespace MWSound alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } - void OpenAL_Output::initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, + void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { alSourcef(source, AL_REFERENCE_DISTANCE, mindist); @@ -1183,7 +1182,7 @@ namespace MWSound alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } - void OpenAL_Output::updateCommon( + void OpenALOutput::updateCommon( ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv) { if (useenv && mListenerEnv == Env_Underwater && !mWaterFilter) @@ -1199,7 +1198,7 @@ namespace MWSound alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } - bool OpenAL_Output::playSound(Sound* sound, Sound_Handle data, float offset) + bool OpenALOutput::playSound(Sound* sound, Sound_Handle data, float offset) { ALuint source; @@ -1238,7 +1237,7 @@ namespace MWSound return true; } - bool OpenAL_Output::playSound3D(Sound* sound, Sound_Handle data, float offset) + bool OpenALOutput::playSound3D(Sound* sound, Sound_Handle data, float offset) { ALuint source; @@ -1277,7 +1276,7 @@ namespace MWSound return true; } - void OpenAL_Output::finishSound(Sound* sound) + void OpenALOutput::finishSound(Sound* sound) { if (!sound->mHandle) return; @@ -1294,7 +1293,7 @@ namespace MWSound mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound)); } - bool OpenAL_Output::isSoundPlaying(Sound* sound) + bool OpenALOutput::isSoundPlaying(Sound* sound) { if (!sound->mHandle) return false; @@ -1307,7 +1306,7 @@ namespace MWSound return state == AL_PLAYING || state == AL_PAUSED; } - void OpenAL_Output::updateSound(Sound* sound) + void OpenALOutput::updateSound(Sound* sound) { if (!sound->mHandle) return; @@ -1318,7 +1317,7 @@ namespace MWSound getALError(); } - bool OpenAL_Output::streamSound(DecoderPtr decoder, Stream* sound, bool getLoudnessData) + bool OpenALOutput::streamSound(DecoderPtr decoder, Stream* sound, bool getLoudnessData) { if (mFreeSources.empty()) { @@ -1349,7 +1348,7 @@ namespace MWSound return true; } - bool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream* sound, bool getLoudnessData) + bool OpenALOutput::streamSound3D(DecoderPtr decoder, Stream* sound, bool getLoudnessData) { if (mFreeSources.empty()) { @@ -1380,7 +1379,7 @@ namespace MWSound return true; } - void OpenAL_Output::finishStream(Stream* sound) + void OpenALOutput::finishStream(Stream* sound) { if (!sound->mHandle) return; @@ -1402,7 +1401,7 @@ namespace MWSound delete stream; } - double OpenAL_Output::getStreamDelay(Stream* sound) + double OpenALOutput::getStreamDelay(Stream* sound) { if (!sound->mHandle) return 0.0; @@ -1410,7 +1409,7 @@ namespace MWSound return stream->getStreamDelay(); } - double OpenAL_Output::getStreamOffset(Stream* sound) + double OpenALOutput::getStreamOffset(Stream* sound) { if (!sound->mHandle) return 0.0; @@ -1419,7 +1418,7 @@ namespace MWSound return stream->getStreamOffset(); } - float OpenAL_Output::getStreamLoudness(Stream* sound) + float OpenALOutput::getStreamLoudness(Stream* sound) { if (!sound->mHandle) return 0.0; @@ -1428,7 +1427,7 @@ namespace MWSound return stream->getCurrentLoudness(); } - bool OpenAL_Output::isStreamPlaying(Stream* sound) + bool OpenALOutput::isStreamPlaying(Stream* sound) { if (!sound->mHandle) return false; @@ -1437,7 +1436,7 @@ namespace MWSound return stream->isPlaying(); } - void OpenAL_Output::updateStream(Stream* sound) + void OpenALOutput::updateStream(Stream* sound) { if (!sound->mHandle) return; @@ -1449,17 +1448,17 @@ namespace MWSound getALError(); } - void OpenAL_Output::startUpdate() + void OpenALOutput::startUpdate() { alcSuspendContext(alcGetCurrentContext()); } - void OpenAL_Output::finishUpdate() + void OpenALOutput::finishUpdate() { alcProcessContext(alcGetCurrentContext()); } - void OpenAL_Output::updateListener( + void OpenALOutput::updateListener( const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) { if (mContext) @@ -1501,7 +1500,7 @@ namespace MWSound mListenerEnv = env; } - void OpenAL_Output::pauseSounds(int types) + void OpenALOutput::pauseSounds(int types) { std::vector sources; for (Sound* sound : mActiveSounds) @@ -1524,7 +1523,7 @@ namespace MWSound } } - void OpenAL_Output::pauseActiveDevice() + void OpenALOutput::pauseActiveDevice() { if (mDevice == nullptr) return; @@ -1540,7 +1539,7 @@ namespace MWSound alListenerf(AL_GAIN, 0.0f); } - void OpenAL_Output::resumeActiveDevice() + void OpenALOutput::resumeActiveDevice() { if (mDevice == nullptr) return; @@ -1556,7 +1555,7 @@ namespace MWSound alListenerf(AL_GAIN, 1.0f); } - void OpenAL_Output::resumeSounds(int types) + void OpenALOutput::resumeSounds(int types) { std::vector sources; for (Sound* sound : mActiveSounds) @@ -1579,8 +1578,8 @@ namespace MWSound } } - OpenAL_Output::OpenAL_Output(SoundManager& mgr) - : Sound_Output(mgr) + OpenALOutput::OpenALOutput(SoundManager& mgr) + : SoundOutput(mgr) , mDevice(nullptr) , mContext(nullptr) , mListenerPos(0.0f, 0.0f, 0.0f) @@ -1593,12 +1592,12 @@ namespace MWSound { } - OpenAL_Output::~OpenAL_Output() + OpenALOutput::~OpenALOutput() { - OpenAL_Output::deinit(); + OpenALOutput::deinit(); } - float OpenAL_Output::getTimeScaledPitch(SoundBase* sound) + float OpenALOutput::getTimeScaledPitch(SoundBase* sound) { const bool shouldScale = !(sound->mParams.mFlags & PlayMode::NoScaling); return shouldScale ? sound->getPitch() * mManager.getSimulationTimeScale() : sound->getPitch(); diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openaloutput.hpp similarity index 91% rename from apps/openmw/mwsound/openal_output.hpp rename to apps/openmw/mwsound/openaloutput.hpp index b419038eab..4e96dd1627 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openaloutput.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_SOUND_OPENAL_OUTPUT_H -#define GAME_SOUND_OPENAL_OUTPUT_H +#ifndef GAME_SOUND_OPENALOUTPUT_H +#define GAME_SOUND_OPENALOUTPUT_H #include #include @@ -13,7 +13,7 @@ #include "alc.h" #include "alext.h" -#include "sound_output.hpp" +#include "soundoutput.hpp" namespace MWSound { @@ -22,7 +22,7 @@ namespace MWSound class Sound; class Stream; - class OpenAL_Output : public Sound_Output + class OpenALOutput : public SoundOutput { ALCdevice* mDevice; ALCcontext* mContext; @@ -72,8 +72,8 @@ namespace MWSound float getTimeScaledPitch(SoundBase* sound); - OpenAL_Output& operator=(const OpenAL_Output& rhs); - OpenAL_Output(const OpenAL_Output& rhs); + OpenALOutput& operator=(const OpenALOutput& rhs); + OpenALOutput(const OpenALOutput& rhs); static void eventCallback( ALenum eventType, ALuint object, ALuint param, ALsizei length, const ALchar* message, void* userParam); @@ -117,8 +117,8 @@ namespace MWSound void pauseActiveDevice() override; void resumeActiveDevice() override; - OpenAL_Output(SoundManager& mgr); - virtual ~OpenAL_Output(); + OpenALOutput(SoundManager& mgr); + virtual ~OpenALOutput(); }; } diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 48e8975340..5160b2934f 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -3,7 +3,7 @@ #include -#include "sound_output.hpp" +#include "soundoutput.hpp" namespace MWSound { @@ -53,7 +53,7 @@ namespace MWSound protected: Sound_Instance mHandle = nullptr; - friend class OpenAL_Output; + friend class OpenALOutput; public: void setPosition(const osg::Vec3f& pos) { mParams.mPos = pos; } diff --git a/apps/openmw/mwsound/sound_buffer.cpp b/apps/openmw/mwsound/soundbuffer.cpp similarity index 85% rename from apps/openmw/mwsound/sound_buffer.cpp rename to apps/openmw/mwsound/soundbuffer.cpp index f28b268df2..722d89f0eb 100644 --- a/apps/openmw/mwsound/sound_buffer.cpp +++ b/apps/openmw/mwsound/soundbuffer.cpp @@ -1,4 +1,4 @@ -#include "sound_buffer.hpp" +#include "soundbuffer.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" @@ -35,7 +35,7 @@ namespace MWSound } } - SoundBufferPool::SoundBufferPool(Sound_Output& output) + SoundBufferPool::SoundBufferPool(SoundOutput& output) : mOutput(&output) , mBufferCacheMax(Settings::sound().mBufferCacheMax * 1024 * 1024) , mBufferCacheMin( @@ -48,31 +48,31 @@ namespace MWSound clear(); } - Sound_Buffer* SoundBufferPool::lookup(const ESM::RefId& soundId) const + SoundBuffer* SoundBufferPool::lookup(const ESM::RefId& soundId) const { const auto it = mBufferNameMap.find(soundId); if (it != mBufferNameMap.end()) { - Sound_Buffer* sfx = it->second; + SoundBuffer* sfx = it->second; if (sfx->getHandle() != nullptr) return sfx; } return nullptr; } - Sound_Buffer* SoundBufferPool::lookup(std::string_view fileName) const + SoundBuffer* SoundBufferPool::lookup(std::string_view fileName) const { const auto it = mBufferFileNameMap.find(std::string(fileName)); if (it != mBufferFileNameMap.end()) { - Sound_Buffer* sfx = it->second; + SoundBuffer* sfx = it->second; if (sfx->getHandle() != nullptr) return sfx; } return nullptr; } - Sound_Buffer* SoundBufferPool::loadSfx(Sound_Buffer* sfx) + SoundBuffer* SoundBufferPool::loadSfx(SoundBuffer* sfx) { if (sfx->getHandle() != nullptr) return sfx; @@ -95,7 +95,7 @@ namespace MWSound return sfx; } - Sound_Buffer* SoundBufferPool::load(const ESM::RefId& soundId) + SoundBuffer* SoundBufferPool::load(const ESM::RefId& soundId) { if (mBufferNameMap.empty()) { @@ -103,7 +103,7 @@ namespace MWSound insertSound(sound.mId, sound); } - Sound_Buffer* sfx; + SoundBuffer* sfx; const auto it = mBufferNameMap.find(soundId); if (it != mBufferNameMap.end()) sfx = it->second; @@ -118,9 +118,9 @@ namespace MWSound return loadSfx(sfx); } - Sound_Buffer* SoundBufferPool::load(std::string_view fileName) + SoundBuffer* SoundBufferPool::load(std::string_view fileName) { - Sound_Buffer* sfx; + SoundBuffer* sfx; const auto it = mBufferFileNameMap.find(std::string(fileName)); if (it != mBufferFileNameMap.end()) sfx = it->second; @@ -146,7 +146,7 @@ namespace MWSound mUnusedBuffers.clear(); } - Sound_Buffer* SoundBufferPool::insertSound(std::string_view fileName) + SoundBuffer* SoundBufferPool::insertSound(std::string_view fileName) { static const AudioParams audioParams = makeAudioParams(MWBase::Environment::get().getESMStore()->get()); @@ -158,13 +158,13 @@ namespace MWSound min = std::max(min, 1.0f); max = std::max(min, max); - Sound_Buffer& sfx = mSoundBuffers.emplace_back(fileName, volume, min, max); + SoundBuffer& sfx = mSoundBuffers.emplace_back(fileName, volume, min, max); mBufferFileNameMap.emplace(fileName, &sfx); return &sfx; } - Sound_Buffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM::Sound& sound) + SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM::Sound& sound) { static const AudioParams audioParams = makeAudioParams(MWBase::Environment::get().getESMStore()->get()); @@ -183,7 +183,7 @@ namespace MWSound min = std::max(min, 1.0f); max = std::max(min, max); - Sound_Buffer& sfx = mSoundBuffers.emplace_back( + SoundBuffer& sfx = mSoundBuffers.emplace_back( Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(sound.mSound)), volume, min, max); mBufferNameMap.emplace(soundId, &sfx); @@ -194,7 +194,7 @@ namespace MWSound { while (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMin) { - Sound_Buffer* const unused = mUnusedBuffers.back(); + SoundBuffer* const unused = mUnusedBuffers.back(); mBufferCacheSize -= mOutput->unloadSound(unused->getHandle()); unused->mHandle = nullptr; diff --git a/apps/openmw/mwsound/sound_buffer.hpp b/apps/openmw/mwsound/soundbuffer.hpp similarity index 68% rename from apps/openmw/mwsound/sound_buffer.hpp rename to apps/openmw/mwsound/soundbuffer.hpp index 7de6dab9ae..f7e7639b2d 100644 --- a/apps/openmw/mwsound/sound_buffer.hpp +++ b/apps/openmw/mwsound/soundbuffer.hpp @@ -1,14 +1,15 @@ -#ifndef GAME_SOUND_SOUND_BUFFER_H -#define GAME_SOUND_SOUND_BUFFER_H +#ifndef GAME_SOUND_SOUNDBUFFER_H +#define GAME_SOUND_SOUNDBUFFER_H #include #include #include #include -#include "sound_output.hpp" #include +#include "soundoutput.hpp" + namespace ESM { struct Sound; @@ -23,11 +24,11 @@ namespace MWSound { class SoundBufferPool; - class Sound_Buffer + class SoundBuffer { public: template - Sound_Buffer(T&& resname, float volume, float mindist, float maxdist) + SoundBuffer(T&& resname, float volume, float mindist, float maxdist) : mResourceName(std::forward(resname)) , mVolume(volume) , mMinDist(mindist) @@ -59,7 +60,7 @@ namespace MWSound class SoundBufferPool { public: - SoundBufferPool(Sound_Output& output); + SoundBufferPool(SoundOutput& output); SoundBufferPool(const SoundBufferPool&) = delete; @@ -67,20 +68,20 @@ namespace MWSound /// Lookup a soundId for its sound data (resource name, local volume, /// minRange, and maxRange) - Sound_Buffer* lookup(const ESM::RefId& soundId) const; + SoundBuffer* lookup(const ESM::RefId& soundId) const; /// Lookup a sound by file name for its sound data (resource name, local volume, /// minRange, and maxRange) - Sound_Buffer* lookup(std::string_view fileName) const; + SoundBuffer* lookup(std::string_view fileName) const; /// Lookup a soundId for its sound data (resource name, local volume, /// minRange, and maxRange), and ensure it's ready for use. - Sound_Buffer* load(const ESM::RefId& soundId); + SoundBuffer* load(const ESM::RefId& soundId); // Lookup for a sound by file name, and ensure it's ready for use. - Sound_Buffer* load(std::string_view fileName); + SoundBuffer* load(std::string_view fileName); - void use(Sound_Buffer& sfx) + void use(SoundBuffer& sfx) { if (sfx.mUses++ == 0) { @@ -90,7 +91,7 @@ namespace MWSound } } - void release(Sound_Buffer& sfx) + void release(SoundBuffer& sfx) { if (--sfx.mUses == 0) mUnusedBuffers.push_front(&sfx); @@ -99,23 +100,23 @@ namespace MWSound void clear(); private: - Sound_Buffer* loadSfx(Sound_Buffer* sfx); + SoundBuffer* loadSfx(SoundBuffer* sfx); - Sound_Output* mOutput; - std::deque mSoundBuffers; - std::unordered_map mBufferNameMap; - std::unordered_map mBufferFileNameMap; + SoundOutput* mOutput; + std::deque mSoundBuffers; + std::unordered_map mBufferNameMap; + std::unordered_map mBufferFileNameMap; std::size_t mBufferCacheMax; std::size_t mBufferCacheMin; std::size_t mBufferCacheSize = 0; // NOTE: unused buffers are stored in front-newest order. - std::deque mUnusedBuffers; + std::deque mUnusedBuffers; - inline Sound_Buffer* insertSound(const ESM::RefId& soundId, const ESM::Sound& sound); - inline Sound_Buffer* insertSound(std::string_view fileName); + inline SoundBuffer* insertSound(const ESM::RefId& soundId, const ESM::Sound& sound); + inline SoundBuffer* insertSound(std::string_view fileName); inline void unloadUnused(); }; } -#endif /* GAME_SOUND_SOUND_BUFFER_H */ +#endif /* GAME_SOUND_SOUNDBUFFER_H */ diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sounddecoder.hpp similarity index 80% rename from apps/openmw/mwsound/sound_decoder.hpp rename to apps/openmw/mwsound/sounddecoder.hpp index 17f9d28909..675fad4197 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sounddecoder.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_SOUND_SOUND_DECODER_H -#define GAME_SOUND_SOUND_DECODER_H +#ifndef GAME_SOUND_SOUNDDECODER_H +#define GAME_SOUND_SOUNDDECODER_H #include @@ -34,7 +34,7 @@ namespace MWSound size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type); size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type); - struct Sound_Decoder + struct SoundDecoder { const VFS::Manager* mResourceMgr; @@ -48,15 +48,15 @@ namespace MWSound virtual void readAll(std::vector& output); virtual size_t getSampleOffset() = 0; - Sound_Decoder(const VFS::Manager* resourceMgr) + SoundDecoder(const VFS::Manager* resourceMgr) : mResourceMgr(resourceMgr) { } - virtual ~Sound_Decoder() {} + virtual ~SoundDecoder() {} private: - Sound_Decoder(const Sound_Decoder& rhs); - Sound_Decoder& operator=(const Sound_Decoder& rhs); + SoundDecoder(const SoundDecoder& rhs); + SoundDecoder& operator=(const SoundDecoder& rhs); }; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 1d9a6d457c..66bdfbdbfa 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -26,12 +26,12 @@ #include "../mwmechanics/actorutil.hpp" #include "constants.hpp" -#include "ffmpeg_decoder.hpp" -#include "openal_output.hpp" +#include "ffmpegdecoder.hpp" +#include "openaloutput.hpp" #include "sound.hpp" -#include "sound_buffer.hpp" -#include "sound_decoder.hpp" -#include "sound_output.hpp" +#include "soundbuffer.hpp" +#include "sounddecoder.hpp" +#include "soundoutput.hpp" namespace MWSound { @@ -57,7 +57,7 @@ namespace MWSound return settings; } - float initialFadeVolume(float squaredDist, Sound_Buffer* sfx, Type type, PlayMode mode) + float initialFadeVolume(float squaredDist, SoundBuffer* sfx, Type type, PlayMode mode) { // If a sound is farther away than its maximum distance, start playing it with a zero fade volume. // It can still become audible once the player moves closer. @@ -111,7 +111,7 @@ namespace MWSound SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) : mVFS(vfs) - , mOutput(std::make_unique(*this)) + , mOutput(std::make_unique(*this)) , mWaterSoundUpdater(makeWaterSoundUpdaterSettings()) , mSoundBuffers(*mOutput) , mMusicType(MWSound::MusicType::Normal) @@ -169,7 +169,7 @@ namespace MWSound // Return a new decoder instance, used as needed by the output implementations DecoderPtr SoundManager::getDecoder() { - return std::make_shared(mVFS); + return std::make_shared(mVFS); } DecoderPtr SoundManager::loadVoice(VFS::Path::NormalizedView voicefile) @@ -459,7 +459,7 @@ namespace MWSound return false; } - Sound* SoundManager::playSound(Sound_Buffer* sfx, float volume, float pitch, Type type, PlayMode mode, float offset) + Sound* SoundManager::playSound(SoundBuffer* sfx, float volume, float pitch, Type type, PlayMode mode, float offset) { if (!mOutput->isInitialized()) return nullptr; @@ -495,7 +495,7 @@ namespace MWSound if (!mVFS->exists(normalizedName)) return nullptr; - Sound_Buffer* sfx = mSoundBuffers.load(normalizedName); + SoundBuffer* sfx = mSoundBuffers.load(normalizedName); if (!sfx) return nullptr; @@ -508,14 +508,14 @@ namespace MWSound if (!mOutput->isInitialized()) return nullptr; - Sound_Buffer* sfx = mSoundBuffers.load(soundId); + SoundBuffer* sfx = mSoundBuffers.load(soundId); if (!sfx) return nullptr; return playSound(sfx, volume, pitch, type, mode, offset); } - Sound* SoundManager::playSound3D(const MWWorld::ConstPtr& ptr, Sound_Buffer* sfx, float volume, float pitch, + Sound* SoundManager::playSound3D(const MWWorld::ConstPtr& ptr, SoundBuffer* sfx, float volume, float pitch, Type type, PlayMode mode, float offset) { if (!mOutput->isInitialized()) @@ -576,7 +576,7 @@ namespace MWSound return nullptr; // Look up the sound in the ESM data - Sound_Buffer* sfx = mSoundBuffers.load(soundId); + SoundBuffer* sfx = mSoundBuffers.load(soundId); if (!sfx) return nullptr; @@ -594,7 +594,7 @@ namespace MWSound if (!mVFS->exists(normalizedName)) return nullptr; - Sound_Buffer* sfx = mSoundBuffers.load(normalizedName); + SoundBuffer* sfx = mSoundBuffers.load(normalizedName); if (!sfx) return nullptr; @@ -608,7 +608,7 @@ namespace MWSound return nullptr; // Look up the sound in the ESM data - Sound_Buffer* sfx = mSoundBuffers.load(soundId); + SoundBuffer* sfx = mSoundBuffers.load(soundId); if (!sfx) return nullptr; @@ -642,7 +642,7 @@ namespace MWSound mOutput->finishSound(sound); } - void SoundManager::stopSound(Sound_Buffer* sfx, const MWWorld::ConstPtr& ptr) + void SoundManager::stopSound(SoundBuffer* sfx, const MWWorld::ConstPtr& ptr) { SoundMap::iterator snditer = mActiveSounds.find(ptr.mRef); if (snditer != mActiveSounds.end()) @@ -660,7 +660,7 @@ namespace MWSound if (!mOutput->isInitialized()) return; - Sound_Buffer* sfx = mSoundBuffers.lookup(soundId); + SoundBuffer* sfx = mSoundBuffers.lookup(soundId); if (!sfx) return; @@ -673,7 +673,7 @@ namespace MWSound return; std::string normalizedName = VFS::Path::normalizeFilename(fileName); - Sound_Buffer* sfx = mSoundBuffers.lookup(normalizedName); + SoundBuffer* sfx = mSoundBuffers.lookup(normalizedName); if (!sfx) return; @@ -725,7 +725,7 @@ namespace MWSound SoundMap::iterator snditer = mActiveSounds.find(ptr.mRef); if (snditer != mActiveSounds.end()) { - Sound_Buffer* sfx = mSoundBuffers.lookup(soundId); + SoundBuffer* sfx = mSoundBuffers.lookup(soundId); if (sfx == nullptr) return; for (SoundBufferRefPair& sndbuf : snditer->second.mList) @@ -743,7 +743,7 @@ namespace MWSound SoundMap::const_iterator snditer = mActiveSounds.find(ptr.mRef); if (snditer != mActiveSounds.end()) { - Sound_Buffer* sfx = mSoundBuffers.lookup(normalizedName); + SoundBuffer* sfx = mSoundBuffers.lookup(normalizedName); if (!sfx) return false; @@ -761,7 +761,7 @@ namespace MWSound SoundMap::const_iterator snditer = mActiveSounds.find(ptr.mRef); if (snditer != mActiveSounds.end()) { - Sound_Buffer* sfx = mSoundBuffers.lookup(soundId); + SoundBuffer* sfx = mSoundBuffers.lookup(soundId); if (!sfx) return false; @@ -846,7 +846,7 @@ namespace MWSound const auto update = mWaterSoundUpdater.update(player, *world); WaterSoundAction action; - Sound_Buffer* sfx; + SoundBuffer* sfx; std::tie(action, sfx) = getWaterSoundAction(update, curcell); switch (action) @@ -870,7 +870,7 @@ namespace MWSound mLastCell = curcell; } - std::pair SoundManager::getWaterSoundAction( + std::pair SoundManager::getWaterSoundAction( const WaterSoundUpdate& update, const MWWorld::Cell* cell) const { if (mNearWaterSound) @@ -880,7 +880,7 @@ namespace MWSound bool soundIdChanged = false; - Sound_Buffer* sfx = mSoundBuffers.lookup(update.mId); + SoundBuffer* sfx = mSoundBuffers.lookup(update.mId); if (mLastCell != cell) { const auto snditer = mActiveSounds.find(nullptr); @@ -1168,7 +1168,7 @@ namespace MWSound // Default readAll implementation, for decoders that can't do anything // better - void Sound_Decoder::readAll(std::vector& output) + void SoundDecoder::readAll(std::vector& output) { size_t total = output.size(); size_t got; diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index a5e5b2c45f..8fc7e6701f 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -15,7 +15,7 @@ #include "../mwbase/soundmanager.hpp" #include "regionsoundselector.hpp" -#include "sound_buffer.hpp" +#include "soundbuffer.hpp" #include "type.hpp" #include "watersoundupdater.hpp" @@ -37,8 +37,8 @@ namespace MWWorld namespace MWSound { - class Sound_Output; - struct Sound_Decoder; + class SoundOutput; + struct SoundDecoder; class SoundBase; class Sound; class Stream; @@ -50,7 +50,7 @@ namespace MWSound { const VFS::Manager* mVFS; - std::unique_ptr mOutput; + std::unique_ptr mOutput; WaterSoundUpdater mWaterSoundUpdater; @@ -60,7 +60,7 @@ namespace MWSound Misc::ObjectPool mStreams; - typedef std::pair SoundBufferRefPair; + typedef std::pair SoundBufferRefPair; typedef std::vector SoundBufferRefPairList; struct ActiveSound @@ -109,7 +109,7 @@ namespace MWSound Sound* mCurrentRegionSound; - Sound_Buffer* insertSound(const std::string& soundId, const ESM::Sound* sound); + SoundBuffer* insertSound(const std::string& soundId, const ESM::Sound* sound); // returns a decoder to start streaming, or nullptr if the sound was not found DecoderPtr loadVoice(VFS::Path::NormalizedView voicefile); @@ -126,9 +126,9 @@ namespace MWSound bool remove3DSoundAtDistance(PlayMode mode, const MWWorld::ConstPtr& ptr) const; - Sound* playSound(Sound_Buffer* sfx, float volume, float pitch, Type type = Type::Sfx, + Sound* playSound(SoundBuffer* sfx, float volume, float pitch, Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0); - Sound* playSound3D(const MWWorld::ConstPtr& ptr, Sound_Buffer* sfx, float volume, float pitch, Type type, + Sound* playSound3D(const MWWorld::ConstPtr& ptr, SoundBuffer* sfx, float volume, float pitch, Type type, PlayMode mode, float offset); void updateSounds(float duration); @@ -144,7 +144,7 @@ namespace MWSound PlaySound, }; - std::pair getWaterSoundAction( + std::pair getWaterSoundAction( const WaterSoundUpdate& update, const MWWorld::Cell* cell) const; SoundManager(const SoundManager& rhs); @@ -152,9 +152,9 @@ namespace MWSound protected: DecoderPtr getDecoder(); - friend class OpenAL_Output; + friend class OpenALOutput; - void stopSound(Sound_Buffer* sfx, const MWWorld::ConstPtr& ptr); + void stopSound(SoundBuffer* sfx, const MWWorld::ConstPtr& ptr); ///< Stop the given object from playing given sound buffer. public: diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/soundoutput.hpp similarity index 88% rename from apps/openmw/mwsound/sound_output.hpp rename to apps/openmw/mwsound/soundoutput.hpp index 5a77124985..e5c266b204 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/soundoutput.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_SOUND_SOUND_OUTPUT_H -#define GAME_SOUND_SOUND_OUTPUT_H +#ifndef GAME_SOUND_SOUNDOUTPUT_H +#define GAME_SOUND_SOUNDOUTPUT_H #include #include @@ -13,7 +13,7 @@ namespace MWSound { class SoundManager; - struct Sound_Decoder; + struct SoundDecoder; class Sound; class Stream; @@ -30,7 +30,7 @@ namespace MWSound using HrtfMode = Settings::HrtfMode; - class Sound_Output + class SoundOutput { SoundManager& mManager; @@ -71,24 +71,24 @@ namespace MWSound virtual void pauseActiveDevice() = 0; virtual void resumeActiveDevice() = 0; - Sound_Output& operator=(const Sound_Output& rhs); - Sound_Output(const Sound_Output& rhs); + SoundOutput& operator=(const SoundOutput& rhs); + SoundOutput(const SoundOutput& rhs); protected: bool mInitialized; - Sound_Output(SoundManager& mgr) + SoundOutput(SoundManager& mgr) : mManager(mgr) , mInitialized(false) { } public: - virtual ~Sound_Output() {} + virtual ~SoundOutput() {} bool isInitialized() const { return mInitialized; } - friend class OpenAL_Output; + friend class OpenALOutput; friend class SoundManager; friend class SoundBufferPool; }; From cf7e276a6ae066464720ca2f00a1d573ec28c2b2 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Feb 2025 23:52:17 +0300 Subject: [PATCH 023/455] Remove file name exceptions (#7249) --- CI/check_file_names.sh | 3 +-- CI/file_name_exceptions.txt | 0 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 CI/file_name_exceptions.txt diff --git a/CI/check_file_names.sh b/CI/check_file_names.sh index b04416659e..2a0f421cc9 100755 --- a/CI/check_file_names.sh +++ b/CI/check_file_names.sh @@ -1,7 +1,6 @@ #!/bin/bash -ex git ls-files -- ':(exclude)extern/' '*.cpp' '*.hpp' '*.h' | - grep -vP '/[a-z0-9]+\.(cpp|hpp|h)$' | - grep -vFf CI/file_name_exceptions.txt && + grep -vP '/[a-z0-9]+\.(cpp|hpp|h)$' && ( echo 'File names do not follow the naming convention, see https://wiki.openmw.org/index.php?title=Naming_Conventions#Files'; exit -1 ) exit 0 diff --git a/CI/file_name_exceptions.txt b/CI/file_name_exceptions.txt deleted file mode 100644 index e69de29bb2..0000000000 From ec2c031792c32accd069d86c31ae7b846326f314 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 27 Feb 2025 18:25:37 +0300 Subject: [PATCH 024/455] Make powers immune to silence (#8371) --- apps/openmw/mwmechanics/spellutil.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index 022aaec262..fc7637afc1 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -274,15 +274,15 @@ namespace MWMechanics CreatureStats& stats = actor.getClass().getCreatureStats(actor); - if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Silence).getMagnitude() && !godmode) - return 0; - if (spell->mData.mType == ESM::Spell::ST_Power) return stats.getSpells().canUsePower(spell) ? 100 : 0; if (godmode) return 100; + if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Silence).getMagnitude()) + return 0; + if (spell->mData.mType != ESM::Spell::ST_Spell) return 100; From 61e2117e9d15e08a4e18be1ce34df2e991d8cca6 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 4 Mar 2025 03:41:27 +0300 Subject: [PATCH 025/455] Remove excessive spacing between travel destination and price --- apps/openmw/mwgui/travelwindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 6ba3a55286..8c4999a13d 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -93,8 +93,7 @@ namespace MWGui const std::string& nameString = name.getRefIdString(); toAdd->setUserString("price", std::to_string(price)); - toAdd->setCaptionWithReplacing( - "#{sCell=" + nameString + "} - " + MyGUI::utility::toString(price) + "#{sgp}"); + toAdd->setCaptionWithReplacing("#{sCell=" + nameString + "} - " + MyGUI::utility::toString(price) + "#{sgp}"); toAdd->setSize(mDestinationsView->getWidth(), lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", nameString); From 5b18edf938626c7e342500f52ffea61edfce2542 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 5 Mar 2025 19:55:21 +0300 Subject: [PATCH 026/455] Interrupt bound item effect if equipment failed (#8383) --- apps/openmw/mwmechanics/spelleffects.cpp | 42 ++++++++++++++---------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index 7035c7f61c..c5af5f2bae 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -174,7 +174,7 @@ namespace return -1; } - void addBoundItem(const ESM::RefId& itemId, const MWWorld::Ptr& actor) + bool addBoundItem(const ESM::RefId& itemId, const MWWorld::Ptr& actor) { MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); MWWorld::Ptr boundPtr = *store.MWWorld::ContainerStore::add(itemId, 1); @@ -185,9 +185,6 @@ namespace MWWorld::ActionEquip action(boundPtr); action.execute(actor); - if (actor != MWMechanics::getPlayer()) - return; - MWWorld::Ptr newItem; auto it = slot >= 0 ? store.getSlot(slot) : store.end(); // Equip can fail because beast races cannot equip boots/helmets @@ -195,16 +192,24 @@ namespace newItem = *it; if (newItem.isEmpty() || boundPtr != newItem) - return; + { + store.remove(boundPtr, 1); + return false; + } - MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + if (actor == MWMechanics::getPlayer()) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - // change draw state only if the item is in player's right hand - if (slot == MWWorld::InventoryStore::Slot_CarriedRight) - player.setDrawState(MWMechanics::DrawState::Weapon); + // change draw state only if the item is in player's right hand + if (slot == MWWorld::InventoryStore::Slot_CarriedRight) + player.setDrawState(MWMechanics::DrawState::Weapon); - if (prevItem != store.end()) - player.setPreviousItem(itemId, prevItem->getCellRef().getRefId()); + if (prevItem != store.end()) + player.setPreviousItem(itemId, prevItem->getCellRef().getRefId()); + } + + return true; } void removeBoundItem(const ESM::RefId& itemId, const MWWorld::Ptr& actor) @@ -626,16 +631,19 @@ namespace MWMechanics case ESM::MagicEffect::BoundHelm: case ESM::MagicEffect::BoundBoots: case ESM::MagicEffect::BoundShield: + { if (!target.getClass().hasInventoryStore(target)) - invalid = true; - else { - const std::string& item = sBoundItemsMap.at(effect.mEffectId); - addBoundItem(ESM::RefId::stringRefId( - world->getStore().get().find(item)->mValue.getString()), - target); + invalid = true; + break; } + const std::string& item = sBoundItemsMap.at(effect.mEffectId); + const MWWorld::Store& gmst = world->getStore().get(); + const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); + if (!addBoundItem(itemId, target)) + invalid = true; break; + } case ESM::MagicEffect::FireDamage: case ESM::MagicEffect::ShockDamage: case ESM::MagicEffect::FrostDamage: From 918a6352e4ce3098a59ea06d69431676aecfbbf4 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 5 Mar 2025 23:43:35 +0300 Subject: [PATCH 027/455] Let cancelled bound item effect remain active for the frame --- apps/openmw/mwmechanics/spelleffects.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index c5af5f2bae..91e24f946f 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -192,10 +192,7 @@ namespace newItem = *it; if (newItem.isEmpty() || boundPtr != newItem) - { - store.remove(boundPtr, 1); return false; - } if (actor == MWMechanics::getPlayer()) { @@ -641,7 +638,7 @@ namespace MWMechanics const MWWorld::Store& gmst = world->getStore().get(); const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); if (!addBoundItem(itemId, target)) - invalid = true; + effect.mTimeLeft = 0.f; break; } case ESM::MagicEffect::FireDamage: From 59edf4750bd6e00fa311c4cce97c67320ced9951 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 4 Mar 2025 05:23:40 +0300 Subject: [PATCH 028/455] Fix Travel window header alignment --- apps/openmw/mwgui/travelwindow.cpp | 6 ------ apps/openmw/mwgui/travelwindow.hpp | 2 -- files/data/mygui/openmw_travel_window.layout | 13 ++++++------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 6ba3a55286..d1b578ff9a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -33,15 +33,9 @@ namespace MWGui { getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); - getWidget(mSelect, "Select"); - getWidget(mDestinations, "Travel"); getWidget(mDestinationsView, "DestinationsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onCancelButtonClicked); - - mDestinations->setCoord(450 / 2 - mDestinations->getTextSize().width / 2, mDestinations->getTop(), - mDestinations->getTextSize().width, mDestinations->getHeight()); - mSelect->setCoord(8, mSelect->getTop(), mSelect->getTextSize().width, mSelect->getHeight()); } void TravelWindow::addDestination(const ESM::RefId& name, const ESM::Position& pos, bool interior) diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 6d7c1c7376..630e27518a 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -24,8 +24,6 @@ namespace MWGui protected: MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; - MyGUI::TextBox* mDestinations; - MyGUI::TextBox* mSelect; MyGUI::ScrollView* mDestinationsView; diff --git a/files/data/mygui/openmw_travel_window.layout b/files/data/mygui/openmw_travel_window.layout index 96de2fc08d..03c2e713e5 100644 --- a/files/data/mygui/openmw_travel_window.layout +++ b/files/data/mygui/openmw_travel_window.layout @@ -4,18 +4,17 @@ - - - - - + - + + + + - + From 3f63700e993367c45548c995f6bf0fb30d02d737 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 9 Mar 2025 19:20:24 +0300 Subject: [PATCH 029/455] Improve topic and magic effect list padding accuracy This also touches the quest list, but there are bigger problems with the journal than just padding --- apps/openmw/mwgui/dialogue.cpp | 5 ++++- components/widgets/list.cpp | 29 ++++++++++++++++------------- components/widgets/list.hpp | 15 +++++++++++++-- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e14c400978..6b1e007770 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -588,10 +588,13 @@ 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); + mTopicsList->addItem(keyword, verticalPadding); auto t = std::make_unique(keyword); mKeywordSearch.seed(topicId, intptr_t(t.get())); diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 896057443c..416590ed48 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -29,14 +29,14 @@ namespace Gui MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); } - void MWList::addItem(std::string_view name) + void MWList::addItem(std::string_view name, int verticalPadding) { - mItems.emplace_back(name); + mItems.emplace_back(name, verticalPadding); } void MWList::addSeparator() { - mItems.emplace_back(std::string{}); + addItem({}); } void MWList::adjustSize() @@ -48,7 +48,6 @@ namespace Gui { constexpr int _scrollBarWidth = 20; // fetch this from skin? const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; - constexpr int spacing = 3; int viewPosition = -mScrollView->getViewOffset().top; while (mScrollView->getChildCount()) @@ -60,25 +59,27 @@ namespace Gui int i = 0; for (const auto& item : mItems) { - if (!item.empty()) + mItemHeight += item.mVPadding; + if (!item.mName.empty()) { if (mListItemSkin.empty()) return; MyGUI::Button* button = mScrollView->createWidget(mListItemSkin, MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), - MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + item); - button->setCaption(item); + MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + item.mName); + button->setCaption(item.mName); button->getSubWidgetText()->setWordWrap(true); button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheelMoved); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); button->setNeedKeyFocus(true); - int height = button->getTextSize().height; + // Morrowind list item text widgets are typically 18 pixels tall + int height = button->getTextSize().height + 2; button->setSize(MyGUI::IntSize(button->getSize().width, height)); button->setUserData(i); - mItemHeight += height + spacing; + mItemHeight += height; } else { @@ -87,8 +88,9 @@ namespace Gui MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); separator->setNeedMouseFocus(false); - mItemHeight += 18 + spacing; + mItemHeight += 18; } + mItemHeight += item.mVPadding; ++i; } @@ -123,18 +125,19 @@ namespace Gui const std::string& MWList::getItemNameAt(size_t at) { assert(at < mItems.size() && "List item out of bounds"); - return mItems[at]; + return mItems[at].mName; } void MWList::sort() { // A special case for separators is not needed for now - std::sort(mItems.begin(), mItems.end(), Misc::StringUtils::ciLess); + std::sort(mItems.begin(), mItems.end(), + [](const auto& left, const auto& right) { return Misc::StringUtils::ciLess(left.mName, right.mName); }); } void MWList::removeItem(const std::string& name) { - auto it = std::find(mItems.begin(), mItems.end(), name); + auto it = std::find_if(mItems.begin(), mItems.end(), [&name](const auto& item) { return item.mName == name; }); assert(it != mItems.end()); mItems.erase(it); } diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 3d5e320cf7..f67a7da97b 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -36,7 +36,7 @@ namespace Gui void adjustSize(); void sort(); - void addItem(std::string_view name); + void addItem(std::string_view name, int verticalPadding = 0); void addSeparator(); ///< add a seperator between the current and the next item. void removeItem(const std::string& name); size_t getItemCount(); @@ -64,7 +64,18 @@ namespace Gui MyGUI::Widget* mClient; std::string mListItemSkin; - std::vector mItems; + struct ListItemData + { + std::string mName; + int mVPadding; + + ListItemData(std::string_view name, int verticalPadding) + : mName(name) + , mVPadding(verticalPadding) + { + } + }; + std::vector mItems; int mItemHeight; // height of all items }; From 71ef86078cc7ca755aee3f58a88527e40110a45d Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 11 Mar 2025 22:21:42 +0300 Subject: [PATCH 030/455] Clip HUD item widgets to not overlap the borders (#7740) --- files/data/mygui/openmw_hud.layout | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/files/data/mygui/openmw_hud.layout b/files/data/mygui/openmw_hud.layout index a189075f44..0650df1f43 100644 --- a/files/data/mygui/openmw_hud.layout +++ b/files/data/mygui/openmw_hud.layout @@ -44,8 +44,8 @@ - - + + @@ -56,7 +56,9 @@ - + + + @@ -131,4 +133,4 @@ - \ No newline at end of file + From 34a5eb7512f2dae1efe3e818875fc51ef1f5a32a Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 13 Mar 2025 18:42:55 +0300 Subject: [PATCH 031/455] Editor: Account for pixel ratio in instance mode mouse coordinates conversion (#6573) --- apps/opencs/view/render/instancemode.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index fb085f075a..03872a3d6c 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -171,16 +171,18 @@ osg::Vec3f CSVRender::InstanceMode::getProjectionSpaceCoords(const osg::Vec3f& p osg::Vec3f CSVRender::InstanceMode::getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart) { - osg::Matrix viewMatrix; - viewMatrix.invert(getWorldspaceWidget().getCamera()->getViewMatrix()); - osg::Matrix projMatrix; - projMatrix.invert(getWorldspaceWidget().getCamera()->getProjectionMatrix()); - osg::Matrix combined = projMatrix * viewMatrix; + const osg::Matrix viewMatrix = getWorldspaceWidget().getCamera()->getViewMatrix(); + const osg::Matrix projMatrix = getWorldspaceWidget().getCamera()->getProjectionMatrix(); + const osg::Matrix combined = osg::Matrix::inverse(viewMatrix * projMatrix); /* calculate viewport normalized coordinates note: is there a reason to use getCamera()->getViewport()->computeWindowMatrix() instead? */ - float x = (point.x() * 2) / getWorldspaceWidget().getCamera()->getViewport()->width() - 1.0f; - float y = 1.0f - (point.y() * 2) / getWorldspaceWidget().getCamera()->getViewport()->height(); + const float scale = getWorldspaceWidget().devicePixelRatioF(); + const osg::Viewport* viewport = getWorldspaceWidget().getCamera()->getViewport(); + float x = point.x() * scale / viewport->width(); + float y = point.y() * scale / viewport->height(); + x = x * 2.0f - 1.0f; + y = 1.0f - y * 2.0f; osg::Vec3f mousePlanePoint = osg::Vec3f(x, y, dragStart.z()) * combined; From 4db41c21116f26924177215ac8493e1818ee96b6 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 15 Mar 2025 14:29:13 +0300 Subject: [PATCH 032/455] Merchant repair menu layout tweaks --- apps/openmw/mwgui/merchantrepair.cpp | 2 +- files/data/mygui/openmw_merchantrepair.layout | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index a59f225e9e..54f9ae4187 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -75,7 +75,7 @@ namespace MWGui int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); std::string name{ iter->getClass().getName(*iter) }; - name += " - " + MyGUI::utility::toString(price) + name += " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getESMStore()->get().find("sgp")->mValue.getString(); items.emplace_back(name, price, *iter); diff --git a/files/data/mygui/openmw_merchantrepair.layout b/files/data/mygui/openmw_merchantrepair.layout index 60f5e05096..940bf10f15 100644 --- a/files/data/mygui/openmw_merchantrepair.layout +++ b/files/data/mygui/openmw_merchantrepair.layout @@ -1,28 +1,28 @@ - + - - - - - + + + + + - - + + - + - + From 887e1c04ef49f3604c7f8dd07a970b930767d634 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 5 Mar 2025 05:52:26 +0300 Subject: [PATCH 033/455] Spell buying window layout tweaks --- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- .../mygui/openmw_spell_buying_window.layout | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 9fca86caba..a66b21ba2b 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -62,7 +62,7 @@ namespace MWGui mCurrentY += lineHeight; toAdd->setUserData(price); - toAdd->setCaptionWithReplacing(spell.mName + " - " + MyGUI::utility::toString(price) + "#{sgp}"); + toAdd->setCaptionWithReplacing(spell.mName + " - " + MyGUI::utility::toString(price) + "#{sgp}"); toAdd->setSize(mSpellsView->getWidth(), lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); diff --git a/files/data/mygui/openmw_spell_buying_window.layout b/files/data/mygui/openmw_spell_buying_window.layout index 25a02945ee..d3ec75ef45 100644 --- a/files/data/mygui/openmw_spell_buying_window.layout +++ b/files/data/mygui/openmw_spell_buying_window.layout @@ -1,28 +1,28 @@ - + - - - - - + + + + + - - + + - + - + From 8634b6c3acb29af8bf542f89211a30a2014e2c1f Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 20 Feb 2025 22:25:12 +0300 Subject: [PATCH 034/455] Revise saved game dialog save info layout (#8313) --- apps/openmw/mwgui/savegamedialog.cpp | 25 +++++++++++-------- apps/openmw/mwgui/savegamedialog.hpp | 1 + .../data/mygui/openmw_savegame_dialog.layout | 10 ++++++-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 94f25e118b..4ce6abfcd7 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -43,6 +43,7 @@ namespace MWGui { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); + getWidget(mCellName, "CellName"); getWidget(mInfoText, "InfoText"); getWidget(mOkButton, "OkButton"); getWidget(mCancelButton, "CancelButton"); @@ -390,6 +391,7 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter) { mCurrentSlot = nullptr; + mCellName->setCaption({}); mInfoText->setCaption({}); mScreenshot->setImageTexture({}); return; @@ -411,15 +413,19 @@ namespace MWGui std::stringstream text; - text << Misc::fileTimeToString(mCurrentSlot->mTimeStamp, "%Y.%m.%d %T") << "\n"; + const std::string& playerName = mCurrentSlot->mProfile.mPlayerName; + if (!playerName.empty()) + text << playerName << "\n"; + + text << "#{OMWEngine:Level} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; + + if (mCurrentSlot->mProfile.mCurrentDay > 0) + text << "#{Calendar:day} " << mCurrentSlot->mProfile.mCurrentDay << "\n"; if (mCurrentSlot->mProfile.mMaximumHealth > 0) text << "#{OMWEngine:Health} " << static_cast(mCurrentSlot->mProfile.mCurrentHealth) << "/" << static_cast(mCurrentSlot->mProfile.mMaximumHealth) << "\n"; - text << "#{OMWEngine:Level} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; - text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCellName << "}\n"; - int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour); bool pm = hour >= 12; if (hour >= 13) @@ -427,20 +433,19 @@ namespace MWGui if (hour == 0) hour = 12; - if (mCurrentSlot->mProfile.mCurrentDay > 0) - text << "#{Calendar:day} " << mCurrentSlot->mProfile.mCurrentDay << "\n"; - text << mCurrentSlot->mProfile.mInGameTime.mDay << " " << MWBase::Environment::get().getWorld()->getTimeManager()->getMonthName( mCurrentSlot->mProfile.mInGameTime.mMonth) - << " " << hour << " " << (pm ? "#{Calendar:pm}" : "#{Calendar:am}"); + << " " << hour << " " << (pm ? "#{Calendar:pm}" : "#{Calendar:am}") << "\n"; if (mCurrentSlot->mProfile.mTimePlayed > 0) { - text << "\n" - << "#{OMWEngine:TimePlayed}: " << formatTimeplayed(mCurrentSlot->mProfile.mTimePlayed); + text << "#{OMWEngine:TimePlayed}: " << formatTimeplayed(mCurrentSlot->mProfile.mTimePlayed) << "\n"; } + text << Misc::fileTimeToString(mCurrentSlot->mTimeStamp, "%Y.%m.%d %T") << "\n"; + + mCellName->setCaptionWithReplacing("#{sCell=" + mCurrentSlot->mProfile.mPlayerCellName + "}"); mInfoText->setCaptionWithReplacing(text.str()); // Reset the image for the case we're unable to recover a screenshot diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 35e65fbed0..af831f066e 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -57,6 +57,7 @@ namespace MWGui bool mSaving; MyGUI::ComboBox* mCharacterSelection; + MyGUI::EditBox* mCellName; MyGUI::EditBox* mInfoText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; diff --git a/files/data/mygui/openmw_savegame_dialog.layout b/files/data/mygui/openmw_savegame_dialog.layout index 1e40b5dd34..8bb6a64401 100644 --- a/files/data/mygui/openmw_savegame_dialog.layout +++ b/files/data/mygui/openmw_savegame_dialog.layout @@ -37,14 +37,20 @@ + + + + + + + + - - From d6916c35bcde30710fb9391ad6f1d03c5832bf95 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Fri, 21 Mar 2025 03:18:22 +0300 Subject: [PATCH 035/455] Only print the player name if it doesn't match the profile --- apps/openmw/mwgui/savegamedialog.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 4ce6abfcd7..967bb23919 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -197,7 +197,8 @@ namespace MWGui title << " (#{OMWEngine:Level} " << signature.mPlayerLevel << " " << MyGUI::TextIterator::toTagsString(MyGUI::UString(className)) << ")"; - mCharacterSelection->addItem(MyGUI::LanguageManager::getInstance().replaceTags(title.str())); + const MyGUI::UString playerDesc = MyGUI::LanguageManager::getInstance().replaceTags(title.str()); + mCharacterSelection->addItem(playerDesc, signature.mPlayerName); if (mCurrentCharacter == &*it || (!mCurrentCharacter && !mSaving @@ -413,9 +414,11 @@ namespace MWGui std::stringstream text; - const std::string& playerName = mCurrentSlot->mProfile.mPlayerName; - if (!playerName.empty()) - text << playerName << "\n"; + const size_t profileIndex = mCharacterSelection->getIndexSelected(); + const std::string& slotPlayerName = mCurrentSlot->mProfile.mPlayerName; + const std::string& profilePlayerName = *mCharacterSelection->getItemDataAt(profileIndex); + if (slotPlayerName != profilePlayerName) + text << slotPlayerName << "\n"; text << "#{OMWEngine:Level} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; From 7fb5d4f47ac38da75449119086674517e7fab141 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 22 Mar 2025 23:50:34 +0300 Subject: [PATCH 036/455] Inform the player about both resting hindrances (#8408) --- apps/openmw/mwbase/world.hpp | 11 +++++---- apps/openmw/mwgui/waitdialog.cpp | 38 ++++++++++++++++---------------- apps/openmw/mwworld/worldimp.cpp | 23 ++++++++++--------- apps/openmw/mwworld/worldimp.hpp | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 20a27e5bc3..f268ed0e52 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -406,17 +406,16 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - enum RestPermitted + enum RestFlags { - Rest_Allowed = 0, - Rest_OnlyWaiting = 1, + Rest_PlayerIsUnderwater = 1, Rest_PlayerIsInAir = 2, - Rest_PlayerIsUnderwater = 3, - Rest_EnemiesAreNearby = 4 + Rest_EnemiesAreNearby = 4, + Rest_CanSleep = 8, }; /// check if the player is allowed to rest - virtual RestPermitted canRest() const = 0; + virtual int canRest() const = 0; /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 568f05abc3..222a34e53b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -84,15 +84,29 @@ namespace MWGui void WaitDialog::setPtr(const MWWorld::Ptr& ptr) { - setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld()->canRest() == MWBase::World::Rest_Allowed); + const int restFlags = MWBase::Environment::get().getWorld()->canRest(); - if (ptr.isEmpty() && MWBase::Environment::get().getWorld()->canRest() == MWBase::World::Rest_PlayerIsInAir) + const bool underwater = (restFlags & MWBase::World::Rest_PlayerIsUnderwater) != 0; + // Resting in air is allowed if you're using a bed + const bool inAir = ptr.isEmpty() && (restFlags & MWBase::World::Rest_PlayerIsInAir) != 0; + const bool enemiesNearby = (restFlags & MWBase::World::Rest_EnemiesAreNearby) != 0; + const bool solidGround = !underwater && !inAir; + + if (!solidGround || enemiesNearby) { - // Resting in air is not allowed unless you're using a bed - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage1}"); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Rest); + if (!solidGround) + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage1}"); + + if (enemiesNearby) + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + return; } + const bool canSleep = !ptr.isEmpty() || (restFlags & MWBase::World::Rest_CanSleep) != 0; + setCanRest(canSleep); + if (mUntilHealedButton->getVisible()) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); else @@ -138,20 +152,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode(); } - MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld()->canRest(); - - if (canRest == MWBase::World::Rest_EnemiesAreNearby) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } - else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) - { - // resting underwater not allowed - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage1}"); - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } - onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition(0); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7b2f178343..9744a893ef 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2333,34 +2333,35 @@ namespace MWWorld Log(Debug::Warning) << "Player agent bounds are not supported by navigator: " << agentBounds; } - World::RestPermitted World::canRest() const + int World::canRest() const { + int result = 0; + CellStore* currentCell = mWorldScene->getCurrentCell(); Ptr player = mPlayer->getPlayer(); - RefData& refdata = player.getRefData(); - osg::Vec3f playerPos(refdata.getPosition().asVec3()); const MWPhysics::Actor* actor = mPhysics->getActor(player); if (!actor) throw std::runtime_error("can't find player"); - if (mPlayer->enemiesNearby()) - return Rest_EnemiesAreNearby; - + const osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); if (isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) - return Rest_PlayerIsUnderwater; + result |= Rest_PlayerIsUnderwater; float fallHeight = player.getClass().getCreatureStats(player).getFallHeight(); float epsilon = 1e-4; if ((actor->getCollisionMode() && (!mPhysics->isOnSolidGround(player) || fallHeight >= epsilon)) || isFlying(player)) - return Rest_PlayerIsInAir; + result |= Rest_PlayerIsInAir; - if (currentCell->getCell()->noSleep() || player.getClass().getNpcStats(player).isWerewolf()) - return Rest_OnlyWaiting; + if (mPlayer->enemiesNearby()) + result |= Rest_EnemiesAreNearby; - return Rest_Allowed; + if (!currentCell->getCell()->noSleep() && !player.getClass().getNpcStats(player).isWerewolf()) + result |= Rest_CanSleep; + + return result; } MWRender::Animation* World::getAnimation(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6f06812e20..55034ec1e4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -504,7 +504,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - RestPermitted canRest() const override; + int canRest() const override; ///< check if the player is allowed to rest void rest(double hours) override; From 88cac9b0fa2e634034bc8bde3ffbecfc5cf54fa5 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 23 Mar 2025 18:25:20 +0100 Subject: [PATCH 037/455] Reset initial wander position when commanding actors and don't create return packages when stacking actual ai packages --- apps/openmw/mwmechanics/aipackage.hpp | 1 + apps/openmw/mwmechanics/aisequence.cpp | 25 +++++++++++++++++-------- apps/openmw/mwmechanics/aistate.hpp | 7 +------ apps/openmw/mwmechanics/aiwander.cpp | 9 ++++++++- apps/openmw/mwmechanics/aiwander.hpp | 2 ++ 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index ca33f5dc90..edb62c97c4 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -119,6 +119,7 @@ namespace MWMechanics /// Reset pathfinding state void reset(); + virtual void resetInitialPosition() {} /// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise /// actor should rotate while standing. diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 019aaf7c0a..4d1e5a0502 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -400,7 +400,7 @@ namespace MWMechanics const auto newTypeId = package.getTypeId(); if (currentTypeId <= MWMechanics::AiPackageTypeId::Wander && !hasPackage(MWMechanics::AiPackageTypeId::InternalTravel) - && (newTypeId <= MWMechanics::AiPackageTypeId::Combat || newTypeId == MWMechanics::AiPackageTypeId::Pursue + && (newTypeId == MWMechanics::AiPackageTypeId::Combat || newTypeId == MWMechanics::AiPackageTypeId::Pursue || newTypeId == MWMechanics::AiPackageTypeId::Cast)) { osg::Vec3f dest; @@ -432,8 +432,14 @@ namespace MWMechanics } // insert new package in correct place depending on priority + bool resetInitialPositions = false; for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { + if (resetInitialPositions) + { + (*it)->resetInitialPosition(); + continue; + } // We should override current AiCast package, if we try to add a new one. if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast && package.getTypeId() == MWMechanics::AiPackageTypeId::Cast) @@ -444,22 +450,25 @@ namespace MWMechanics if ((*it)->getPriority() <= package.getPriority()) { + if (cancelOther && isActualAiPackage((*it)->getTypeId())) + mAiState.reset(); onPackageAdded(package); - mPackages.insert(it, package.clone()); - return; + it = mPackages.insert(it, package.clone()); + if (newTypeId == MWMechanics::AiPackageTypeId::Follow) + resetInitialPositions = true; + else + return; } } + if (resetInitialPositions) + return; onPackageAdded(package); mPackages.push_back(package.clone()); // Make sure that temporary storage is empty if (cancelOther) - { - mAiState.moveIn(std::make_unique()); - mAiState.moveIn(std::make_unique()); - mAiState.moveIn(std::make_unique()); - } + mAiState.reset(); } bool MWMechanics::AiSequence::isEmpty() const diff --git a/apps/openmw/mwmechanics/aistate.hpp b/apps/openmw/mwmechanics/aistate.hpp index f2ce17fd9c..bb88557b6d 100644 --- a/apps/openmw/mwmechanics/aistate.hpp +++ b/apps/openmw/mwmechanics/aistate.hpp @@ -59,12 +59,7 @@ namespace MWMechanics mStorage = std::make_unique(payload); } - /// \brief takes ownership of the passed object - template - void moveIn(std::unique_ptr&& storage) - { - mStorage = std::move(storage); - } + void reset() { mStorage.reset(); } }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 3c299c1490..dd0c65a2f2 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -676,6 +676,13 @@ namespace MWMechanics stopMovement(actor); } + void AiWander::resetInitialPosition() + { + mStoredInitialActorPosition = false; + mPathFinder.clearPath(); + mHasDestination = false; + } + bool AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) { if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle)) @@ -804,7 +811,7 @@ namespace MWMechanics converter.toWorld(dest); - state.moveIn(std::make_unique()); + state.reset(); osg::Vec3f pos(static_cast(dest.mX), static_cast(dest.mY), static_cast(dest.mZ)); MWBase::Environment::get().getWorld()->moveObject(actor, pos); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index aed7214f4d..39525e6e65 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -121,6 +121,8 @@ namespace MWMechanics static std::string_view getIdleGroupName(size_t index) { return sIdleSelectToGroupName[index]; } + void resetInitialPosition() override; + private: void stopWalking(const MWWorld::Ptr& actor); From 73811b45b193b02ddd2ee0e0cf935ebba26f6034 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 23 Mar 2025 21:44:04 +0100 Subject: [PATCH 038/455] Clarify the resetInitialPosition loop --- apps/openmw/mwmechanics/aisequence.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 4d1e5a0502..70b94b1eba 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -432,14 +432,8 @@ namespace MWMechanics } // insert new package in correct place depending on priority - bool resetInitialPositions = false; for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { - if (resetInitialPositions) - { - (*it)->resetInitialPosition(); - continue; - } // We should override current AiCast package, if we try to add a new one. if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast && package.getTypeId() == MWMechanics::AiPackageTypeId::Cast) @@ -455,13 +449,13 @@ namespace MWMechanics onPackageAdded(package); it = mPackages.insert(it, package.clone()); if (newTypeId == MWMechanics::AiPackageTypeId::Follow) - resetInitialPositions = true; - else - return; + { + for (++it; it != mPackages.end(); ++it) + (*it)->resetInitialPosition(); + } + return; } } - if (resetInitialPositions) - return; onPackageAdded(package); mPackages.push_back(package.clone()); From 652113c46da069bc9ec74eb53c15e683cd6942d4 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 24 Mar 2025 21:26:37 +0300 Subject: [PATCH 039/455] Don't enable water collision when collision is disabled (#8414) --- apps/openmw/mwphysics/physicssystem.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index e236db4633..5e7c70788d 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -656,20 +656,18 @@ namespace MWPhysics auto ptr = physicActor->getPtr(); if (!ptr.getClass().isMobile(ptr)) continue; - float waterlevel = -std::numeric_limits::max(); - const MWWorld::CellStore* cell = ptr.getCell(); - if (cell->getCell()->hasWater()) - waterlevel = cell->getWaterLevel(); + const MWWorld::CellStore& cell = *ptr.getCell(); const auto& stats = ptr.getClass().getCreatureStats(ptr); const MWMechanics::MagicEffects& effects = stats.getMagicEffects(); + float waterlevel = -std::numeric_limits::max(); bool waterCollision = false; - if (cell->getCell()->hasWater() && effects.getOrDefault(ESM::MagicEffect::WaterWalking).getMagnitude()) + if (cell.getCell()->hasWater()) { - if (physicActor->getCollisionMode() - || !world->isUnderwater(ptr.getCell(), ptr.getRefData().getPosition().asVec3())) - waterCollision = true; + waterlevel = cell.getWaterLevel(); + if (physicActor->getCollisionMode()) + waterCollision = effects.getOrDefault(ESM::MagicEffect::WaterWalking).getMagnitude(); } physicActor->setCanWaterWalk(waterCollision); From 17938cdb7e475d83514cf91b9e20d99f20d3dd39 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 1 Apr 2025 01:25:08 +0300 Subject: [PATCH 040/455] Make the crosshair smaller --- files/data/mygui/openmw_hud.layout | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/files/data/mygui/openmw_hud.layout b/files/data/mygui/openmw_hud.layout index a189075f44..78b8ff1aed 100644 --- a/files/data/mygui/openmw_hud.layout +++ b/files/data/mygui/openmw_hud.layout @@ -108,8 +108,7 @@ - - + @@ -131,4 +130,4 @@ - \ No newline at end of file + From 468724075e99afb7cb7b790b50df445b319864aa Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 3 Apr 2025 11:00:00 +0300 Subject: [PATCH 041/455] Restore 0.45.0 pin button visibility conditions (#8437) --- apps/openmw/mwgui/inventorywindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a773b4635b..b4a2024052 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -206,7 +206,7 @@ namespace MWGui { mGuiMode = mode; const WindowSettingValues settings = getModeSettings(mGuiMode); - setPinButtonVisible(mode == GM_Inventory); + setPinButtonVisible(mode != GM_Container && mode != GM_Companion && mode != GM_Barter); const WindowRectSettingValues& rect = settings.mIsMaximized ? settings.mMaximized : settings.mRegular; From 0eb2ced0723bdc271aa7c814eda22254a3e3a148 Mon Sep 17 00:00:00 2001 From: Kindi Date: Fri, 4 Apr 2025 12:58:07 +0800 Subject: [PATCH 042/455] update spell windows after selecting spell using quickkey --- apps/openmw/mwgui/quickkeysmenu.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 93b0ef071f..c8932c97b6 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -421,6 +421,9 @@ namespace MWGui } store.setSelectedEnchantItem(it); + // to reset WindowManager::mSelectedSpell immediately + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(*it); + MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState::Spell); } } @@ -448,6 +451,9 @@ namespace MWGui store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState::Weapon); } + + // Updates the state of equipped/not equipped (skin) in spellwindow + MWBase::Environment::get().getWindowManager()->updateSpellWindow(); } // --------------------------------------------------------------------------------------------------------- From 0a49c5f71ef91a646ab730df1784e6c0659eeaf0 Mon Sep 17 00:00:00 2001 From: Kindi Date: Fri, 4 Apr 2025 15:36:01 +0800 Subject: [PATCH 043/455] show global script variables in showVars --- apps/openmw/mwscript/globalscripts.hpp | 2 ++ apps/openmw/mwscript/miscextensions.cpp | 45 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 083695b51b..47c3f1ef6c 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -64,6 +64,8 @@ namespace MWScript void removeScript(const ESM::RefId& name); + const std::unordered_map>& getScripts() const { return mScripts; } + bool isRunning(const ESM::RefId& name) const; void run(); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ed2ef756e6..42e556c982 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1223,6 +1223,50 @@ namespace MWScript runtime.getContext().report(str.str()); } + void printGlobalScriptsVars(Interpreter::Runtime& runtime) + { + std::stringstream str; + str << std::endl << "Global Scripts:"; + + const auto& scripts = MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScripts(); + + // sort for user convenience + std::map> globalScripts(scripts.begin(), scripts.end()); + + auto printVariables = [&str](std::string_view scptName, const auto& names, const auto& values, + std::string_view type) { + size_t size = std::min(names.size(), values.size()); + for (size_t i = 0; i < size; ++i) + { + str << std::endl << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")"; + } + }; + + for (const auto& [refId, script] : globalScripts) + { + // Skip dormant global scripts + if (!script->mRunning) + continue; + + const std::string scptName = refId.serializeText(); + const Compiler::Locals& complocals + = MWBase::Environment::get().getScriptManager()->getLocals(refId); + const Locals& locals + = MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocals(refId); + + if (locals.isEmpty()) + str << std::endl << "No variables in script " << scptName; + else + { + printVariables(scptName, complocals.get('s'), locals.mShorts, "short"); + printVariables(scptName, complocals.get('l'), locals.mLongs, "long"); + printVariables(scptName, complocals.get('f'), locals.mFloats, "float"); + } + } + + runtime.getContext().report(str.str()); + } + public: void execute(Interpreter::Runtime& runtime) override { @@ -1233,6 +1277,7 @@ namespace MWScript { // No reference, no problem. printGlobalVars(runtime); + printGlobalScriptsVars(runtime) } } }; From 71a6b26b4fbc69a4568049c62b65e4e5e946619d Mon Sep 17 00:00:00 2001 From: Kindi Date: Fri, 4 Apr 2025 19:36:06 +0800 Subject: [PATCH 044/455] add missing semicolon, add leading whitespace, --- apps/openmw/mwscript/miscextensions.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 42e556c982..04d7fb8f2a 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1192,6 +1192,10 @@ namespace MWScript MWBase::World* world = MWBase::Environment::get().getWorld(); std::vector names = runtime.getContext().getGlobals(); + + // sort for user convenience + std::sort(names.begin(), names.end()); + for (size_t i = 0; i < names.size(); ++i) { char type = world->getGlobalVariableType(names[i]); @@ -1238,7 +1242,7 @@ namespace MWScript size_t size = std::min(names.size(), values.size()); for (size_t i = 0; i < size; ++i) { - str << std::endl << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")"; + str << std::endl << " " << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")"; } }; @@ -1255,7 +1259,7 @@ namespace MWScript = MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocals(refId); if (locals.isEmpty()) - str << std::endl << "No variables in script " << scptName; + str << std::endl << " No variables in script " << scptName; else { printVariables(scptName, complocals.get('s'), locals.mShorts, "short"); @@ -1277,7 +1281,7 @@ namespace MWScript { // No reference, no problem. printGlobalVars(runtime); - printGlobalScriptsVars(runtime) + printGlobalScriptsVars(runtime); } } }; From a5580718f7078cc9e082188052274c27dfc87b27 Mon Sep 17 00:00:00 2001 From: Kindi Date: Sat, 5 Apr 2025 11:26:34 +0800 Subject: [PATCH 045/455] clangformat --- apps/openmw/mwscript/miscextensions.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 04d7fb8f2a..bdab10b084 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1237,14 +1237,15 @@ namespace MWScript // sort for user convenience std::map> globalScripts(scripts.begin(), scripts.end()); - auto printVariables = [&str](std::string_view scptName, const auto& names, const auto& values, - std::string_view type) { - size_t size = std::min(names.size(), values.size()); - for (size_t i = 0; i < size; ++i) - { - str << std::endl << " " << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")"; - } - }; + auto printVariables + = [&str](std::string_view scptName, const auto& names, const auto& values, std::string_view type) { + size_t size = std::min(names.size(), values.size()); + for (size_t i = 0; i < size; ++i) + { + str << std::endl + << " " << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")"; + } + }; for (const auto& [refId, script] : globalScripts) { From ec77ec299df5eda6b50b941dc2fce91d31462adb Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 10 Apr 2025 16:24:21 +0300 Subject: [PATCH 046/455] Rescale the base travel cost by the number of followers (#8446) Rather than the offered price. --- apps/openmw/mwgui/travelwindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 6ba3a55286..3baae10add 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -70,9 +70,6 @@ namespace MWGui price = static_cast(d); } - price = std::max(1, price); - price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); - // Add price for the travelling followers std::set followers; MWWorld::ActionTeleport::getFollowers(player, followers, !interior); @@ -80,6 +77,9 @@ namespace MWGui // Apply followers cost, unlike vanilla the first follower doesn't travel for free price *= 1 + static_cast(followers.size()); + price = std::max(1, price); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); + const int lineHeight = Settings::gui().mFontSize + 2; MyGUI::Button* toAdd = mDestinationsView->createWidget( From 115fc089045513c30ee8913b46df3bde9505c2cd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 20 Apr 2025 00:12:53 +0100 Subject: [PATCH 047/455] Don't forget parallax when reapplying shader visitor Fixes https://gitlab.com/OpenMW/openmw/-/issues/8341 I don't think this should go into 0.49 because there may be implications beyond what I've thought of and I'd rather we had a full dev cycle to notice any regressions. The fix is a little janky, but makes use of some dead code we've had since the introduction of normal-height maps nearly a decade ago, so it's a safe bet that it was never intended to be dead code. The main effect of the jankiness is that we'll add some pointless @defines for normalHeightMap that none of our shaders use and which will always be zero. --- components/shader/shadervisitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index d97276576f..a60716f0a6 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -287,7 +287,7 @@ namespace Shader addedState->setName("addedState"); } - const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", + const char* defaultTextures[] = { "diffuseMap", "normalMap", "normalHeightMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap", "bumpMap", "glossMap" }; bool isTextureNameRecognized(std::string_view name) { @@ -440,7 +440,7 @@ namespace Shader writableStateSet = getWritableStateSet(node); writableStateSet->setTextureAttributeAndModes(unit, normalMapTex, osg::StateAttribute::ON); writableStateSet->setTextureAttributeAndModes( - unit, new SceneUtil::TextureType("normalMap"), osg::StateAttribute::ON); + unit, new SceneUtil::TextureType(normalHeight ? "normalHeightMap" : "normalMap"), osg::StateAttribute::ON); mRequirements.back().mTextures[unit] = "normalMap"; mRequirements.back().mTexStageRequiringTangents = unit; mRequirements.back().mShaderRequired = true; From 4707c7e2fc2d3f2fbe4364ae378d79c7248564a7 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 20 Apr 2025 00:17:03 +0100 Subject: [PATCH 048/455] Format --- components/shader/shadervisitor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index a60716f0a6..092ff47dca 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -287,8 +287,8 @@ namespace Shader addedState->setName("addedState"); } - const char* defaultTextures[] = { "diffuseMap", "normalMap", "normalHeightMap", "emissiveMap", "darkMap", "detailMap", "envMap", - "specularMap", "decalMap", "bumpMap", "glossMap" }; + const char* defaultTextures[] = { "diffuseMap", "normalMap", "normalHeightMap", "emissiveMap", "darkMap", + "detailMap", "envMap", "specularMap", "decalMap", "bumpMap", "glossMap" }; bool isTextureNameRecognized(std::string_view name) { return std::find(std::begin(defaultTextures), std::end(defaultTextures), name) != std::end(defaultTextures); @@ -439,8 +439,9 @@ namespace Shader if (!writableStateSet) writableStateSet = getWritableStateSet(node); writableStateSet->setTextureAttributeAndModes(unit, normalMapTex, osg::StateAttribute::ON); - writableStateSet->setTextureAttributeAndModes( - unit, new SceneUtil::TextureType(normalHeight ? "normalHeightMap" : "normalMap"), osg::StateAttribute::ON); + writableStateSet->setTextureAttributeAndModes(unit, + new SceneUtil::TextureType(normalHeight ? "normalHeightMap" : "normalMap"), + osg::StateAttribute::ON); mRequirements.back().mTextures[unit] = "normalMap"; mRequirements.back().mTexStageRequiringTangents = unit; mRequirements.back().mShaderRequired = true; From 085e5d198872e4f770716cd02bc15a5e6b863d8c Mon Sep 17 00:00:00 2001 From: Aussiemon Date: Sat, 19 Apr 2025 22:31:58 -0600 Subject: [PATCH 049/455] Calculate spell cost when building buying window --- apps/openmw/mwgui/spellbuyingwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 9fca86caba..778949d906 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -19,6 +19,7 @@ #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spells.hpp" +#include "../mwmechanics/spellutil.hpp" namespace MWGui { @@ -44,7 +45,7 @@ namespace MWGui int price = std::max(1, static_cast( - spell.mData.mCost * store.get().find("fSpellValueMult")->mValue.getFloat())); + MWMechanics::calcSpellCost(spell) * store.get().find("fSpellValueMult")->mValue.getFloat())); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); MWWorld::Ptr player = MWMechanics::getPlayer(); From 22c01b22c1482b8a7fd10950d1f58e9d59c8b636 Mon Sep 17 00:00:00 2001 From: Aussiemon Date: Sat, 19 Apr 2025 23:06:30 -0600 Subject: [PATCH 050/455] Clang format --- apps/openmw/mwgui/spellbuyingwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 778949d906..d77fff88d2 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -44,8 +44,8 @@ namespace MWGui const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); int price = std::max(1, - static_cast( - MWMechanics::calcSpellCost(spell) * store.get().find("fSpellValueMult")->mValue.getFloat())); + static_cast(MWMechanics::calcSpellCost(spell) + * store.get().find("fSpellValueMult")->mValue.getFloat())); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); MWWorld::Ptr player = MWMechanics::getPlayer(); From e2e7b58b3a8fb4fabe148e3696ba48e25ad4856e Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 20 Apr 2025 23:55:38 +0100 Subject: [PATCH 051/455] Handle normalHeightMap as special case --- components/shader/shadervisitor.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 092ff47dca..0e6b5e79c6 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -287,11 +287,17 @@ namespace Shader addedState->setName("addedState"); } - const char* defaultTextures[] = { "diffuseMap", "normalMap", "normalHeightMap", "emissiveMap", "darkMap", - "detailMap", "envMap", "specularMap", "decalMap", "bumpMap", "glossMap" }; + // This list is used both for detecting known texture types (including added normal maps etc.) and setting the + // shader defines. Normal maps and normal height maps both get sent to the shader as a normal map, so the latter + // must be detected separately. + const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", + "specularMap", "decalMap", "bumpMap", "glossMap" }; bool isTextureNameRecognized(std::string_view name) { - return std::find(std::begin(defaultTextures), std::end(defaultTextures), name) != std::end(defaultTextures); + if (std::find(std::begin(defaultTextures), std::end(defaultTextures), name) != std::end(defaultTextures)) + return true; + else + return name == "normalHeightMap"; } void ShaderVisitor::applyStateSet(osg::ref_ptr stateset, osg::Node& node) From 0547f09bf09e6e4258b39bd56d189a8ab6458200 Mon Sep 17 00:00:00 2001 From: Kindi Date: Tue, 1 Apr 2025 13:03:11 +0800 Subject: [PATCH 052/455] update container window when item is added or removed --- apps/openmw/mwgui/container.cpp | 6 ++++++ apps/openmw/mwgui/container.hpp | 9 ++++++++- apps/openmw/mwgui/draganddrop.cpp | 2 ++ apps/openmw/mwgui/inventorywindow.cpp | 2 ++ apps/openmw/mwgui/inventorywindow.hpp | 6 +++++- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 6ab2c862d4..b0c3197519 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,6 +160,12 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); + mPtr.getClass().getContainerStore(mPtr).setContListener(this); + } + + void ContainerWindow::updateItemView() + { + mItemView->update(); } void ContainerWindow::resetReference() diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 555fa8e1ae..4898a0eb1c 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -6,6 +6,8 @@ #include "itemmodel.hpp" +#include "../mwworld/containerstore.hpp" + namespace MyGUI { class Gui; @@ -21,7 +23,7 @@ namespace MWGui namespace MWGui { - class ContainerWindow : public WindowBase, public ReferenceInterface + class ContainerWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener { public: ContainerWindow(DragAndDrop* dragAndDrop); @@ -38,6 +40,11 @@ namespace MWGui void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; } + void updateItemView(); + + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + std::string_view getWindowIdForLua() const override { return "Container"; } private: diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index 0fa2cc4e21..36cfe649d6 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -91,6 +91,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setDragDrop(true); mIsOnDragAndDrop = true; + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } void DragAndDrop::drop(ItemModel* targetModel, ItemView* targetView) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a773b4635b..27dab07d34 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -145,6 +145,8 @@ namespace MWGui auto tradeModel = std::make_unique(std::make_unique(mPtr), MWWorld::Ptr()); mTradeModel = tradeModel.get(); + mPtr.getClass().getInventoryStore(mPtr).setContListener(this); + if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings mSortModel->setSourceModel(std::move(tradeModel)); else diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 9fc77ceec5..1dd12aa550 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -5,6 +5,7 @@ #include "windowpinnablebase.hpp" #include "../mwrender/characterpreview.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwworld/ptr.hpp" namespace osg @@ -30,7 +31,7 @@ namespace MWGui class DragAndDrop; class ItemModel; - class InventoryWindow : public WindowPinnableBase + class InventoryWindow : public WindowPinnableBase, public MWWorld::ContainerStoreListener { public: InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem); @@ -62,6 +63,9 @@ namespace MWGui void setGuiMode(GuiMode mode); + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + /// Cycle to previous/next weapon void cycle(bool next); From 15ceee4b1a35842c80bb683fe7e714604dac3996 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 3 Apr 2025 11:26:56 +0800 Subject: [PATCH 053/455] also update companion window and tradewindow --- apps/openmw/mwgui/companionwindow.cpp | 7 +++++++ apps/openmw/mwgui/companionwindow.hpp | 9 ++++++++- apps/openmw/mwgui/container.cpp | 11 ++++++----- apps/openmw/mwgui/tradewindow.cpp | 8 ++++++++ apps/openmw/mwgui/tradewindow.hpp | 9 ++++++++- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 240198eddc..731403f3a3 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -134,6 +134,8 @@ namespace MWGui mItemView->resetScrollBars(); setTitle(actor.getClass().getName(actor)); + + mPtr.getClass().getContainerStore(mPtr).setContListener(this); } void CompanionWindow::onFrame(float dt) @@ -202,4 +204,9 @@ namespace MWGui mSortModel = nullptr; } + void CompanionWindow::updateItemView() + { + mItemView->update(); + } + } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 97f3a0072e..6f8452997f 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -4,6 +4,8 @@ #include "referenceinterface.hpp" #include "windowbase.hpp" +#include "../mwworld/containerstore.hpp" + namespace MWGui { namespace Widgets @@ -17,7 +19,7 @@ namespace MWGui class SortFilterItemModel; class CompanionItemModel; - class CompanionWindow : public WindowBase, public ReferenceInterface + class CompanionWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener { public: CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); @@ -30,6 +32,11 @@ namespace MWGui void onFrame(float dt) override; void clear() override { resetReference(); } + void updateItemView(); + + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + std::string_view getWindowIdForLua() const override { return "Companion"; } private: diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b0c3197519..bb0f90ddc9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,12 +160,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); - mPtr.getClass().getContainerStore(mPtr).setContListener(this); - } - void ContainerWindow::updateItemView() - { - mItemView->update(); + mPtr.getClass().getContainerStore(mPtr).setContListener(this); } void ContainerWindow::resetReference() @@ -326,4 +322,9 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + + void ContainerWindow::updateItemView() + { + mItemView->update(); + } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ba752303d2..ad5165c6ae 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -201,6 +201,9 @@ namespace MWGui onFilterChanged(mFilterAll); mFilterEdit->setCaption({}); + + for (const auto& source : itemSources) + source.getClass().getContainerStore(source).setContListener(this); } void TradeWindow::onFrame(float dt) @@ -643,4 +646,9 @@ namespace MWGui if (mTradeModel && mTradeModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } + + void TradeWindow::updateItemView() + { + mItemView->update(); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 33c39cb269..8614655c47 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -4,6 +4,8 @@ #include "referenceinterface.hpp" #include "windowbase.hpp" +#include "../mwworld/containerstore.hpp" + namespace Gui { class NumericEditBox; @@ -20,7 +22,7 @@ namespace MWGui class SortFilterItemModel; class TradeItemModel; - class TradeWindow : public WindowBase, public ReferenceInterface + class TradeWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener { public: TradeWindow(); @@ -42,6 +44,11 @@ namespace MWGui void onDeleteCustomData(const MWWorld::Ptr& ptr) override; + void updateItemView(); + + void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone; EventHandle_TradeDone eventTradeDone; From a9870bbcde803890dfd2e42062787c2ca2b0bfef Mon Sep 17 00:00:00 2001 From: Kindi Date: Tue, 1 Apr 2025 13:03:11 +0800 Subject: [PATCH 054/455] update container window when item is added or removed --- apps/openmw/mwgui/container.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bb0f90ddc9..b0c3197519 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,10 +160,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); - mPtr.getClass().getContainerStore(mPtr).setContListener(this); } + void ContainerWindow::updateItemView() + { + mItemView->update(); + } + void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); @@ -322,9 +326,4 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } - - void ContainerWindow::updateItemView() - { - mItemView->update(); - } } From bdf025a5325f85cdfa97720b01a5798505c0f221 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 3 Apr 2025 11:26:56 +0800 Subject: [PATCH 055/455] also update companion window and tradewindow --- apps/openmw/mwgui/container.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b0c3197519..bb0f90ddc9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -160,12 +160,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); - mPtr.getClass().getContainerStore(mPtr).setContListener(this); - } - void ContainerWindow::updateItemView() - { - mItemView->update(); + mPtr.getClass().getContainerStore(mPtr).setContListener(this); } void ContainerWindow::resetReference() @@ -326,4 +322,9 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + + void ContainerWindow::updateItemView() + { + mItemView->update(); + } } From 4b94b6f678cd8c812ceaf78d59b02a708ba6d6dd Mon Sep 17 00:00:00 2001 From: Kindi Date: Wed, 23 Apr 2025 17:22:01 +0800 Subject: [PATCH 056/455] also update draganddrop and trade items --- apps/openmw/mwgui/draganddrop.cpp | 28 ++++++++++++++++++++------- apps/openmw/mwgui/draganddrop.hpp | 4 +++- apps/openmw/mwgui/inventorywindow.cpp | 15 ++++++++++++++ apps/openmw/mwgui/inventorywindow.hpp | 2 +- apps/openmw/mwgui/tradeitemmodel.cpp | 19 ++++++++++++++++++ apps/openmw/mwgui/tradeitemmodel.hpp | 3 +++ apps/openmw/mwgui/tradewindow.cpp | 9 +++++++++ apps/openmw/mwgui/tradewindow.hpp | 4 +++- 8 files changed, 74 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index 36cfe649d6..17ce5360c0 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -11,7 +11,6 @@ #include "controllers.hpp" #include "inventorywindow.hpp" #include "itemview.hpp" -#include "itemwidget.hpp" #include "sortfilteritemmodel.hpp" namespace MWGui @@ -72,19 +71,18 @@ namespace MWGui mSourceSortModel->addDragItem(mItem.mBase, count); } - ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget( + mDraggedWidget = MyGUI::Gui::getInstance().createWidget( "MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop"); Controllers::ControllerFollowMouse* controller = MyGUI::ControllerManager::getInstance() .createItem(Controllers::ControllerFollowMouse::getClassTypeName()) ->castType(); - MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller); + MyGUI::ControllerManager::getInstance().addItem(mDraggedWidget, controller); - mDraggedWidget = baseWidget; - baseWidget->setItem(mItem.mBase); - baseWidget->setNeedMouseFocus(false); - baseWidget->setCount(count); + mDraggedWidget->setItem(mItem.mBase); + mDraggedWidget->setNeedMouseFocus(false); + mDraggedWidget->setCount(count); sourceView->update(); @@ -126,6 +124,22 @@ namespace MWGui mSourceView->update(); } + void DragAndDrop::update() + { + if (mIsOnDragAndDrop) + { + int count = mItem.mBase.getCellRef().getCount(); + if (count < mDraggedCount) + { + mItem.mCount = count; + mDraggedCount = count; + mDraggedWidget->setCount(mDraggedCount); + mSourceSortModel->clearDragItems(); + mSourceSortModel->addDragItem(mItem.mBase, mDraggedCount); + } + } + } + void DragAndDrop::onFrame() { if (mIsOnDragAndDrop && mItem.mBase.getCellRef().getCount() == 0) diff --git a/apps/openmw/mwgui/draganddrop.hpp b/apps/openmw/mwgui/draganddrop.hpp index fab7f30d75..94da6cce5f 100644 --- a/apps/openmw/mwgui/draganddrop.hpp +++ b/apps/openmw/mwgui/draganddrop.hpp @@ -2,6 +2,7 @@ #define OPENMW_MWGUI_DRAGANDDROP_H #include "itemmodel.hpp" +#include "itemwidget.hpp" namespace MyGUI { @@ -18,7 +19,7 @@ namespace MWGui { public: bool mIsOnDragAndDrop; - MyGUI::Widget* mDraggedWidget; + ItemWidget* mDraggedWidget; ItemModel* mSourceModel; ItemView* mSourceView; SortFilterItemModel* mSourceSortModel; @@ -30,6 +31,7 @@ namespace MWGui void startDrag( int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); void drop(ItemModel* targetModel, ItemView* targetView); + void update(); void onFrame(); void finish(); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 27dab07d34..656f1a3db4 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -850,6 +850,21 @@ namespace MWGui mPreview->rebuild(); } + void InventoryWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) + { + if (mDragAndDrop->mIsOnDragAndDrop && mDragAndDrop->mItem.mBase == item) + mDragAndDrop->update(); + + if (mTrading) + { + mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->getTradeModel()->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateItemView(); + } + + updateItemView(); + } + MyGUI::IntSize InventoryWindow::getPreviewViewportSize() const { const MyGUI::IntSize previewWindowSize = mAvatarImage->getSize(); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 1dd12aa550..079e629673 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -64,7 +64,7 @@ namespace MWGui void setGuiMode(GuiMode mode); void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } - void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override; /// Cycle to previous/next weapon void cycle(bool next); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 50a55f5061..5df048d6db 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -113,6 +113,25 @@ namespace MWGui encumbrance = std::max(0.f, encumbrance); } + void TradeItemModel::updateBorrowed() + { + auto update = [](std::vector& list) { + for (auto it = list.begin(); it != list.end();) + { + int actualCount = it->mBase.getCellRef().getCount(); + if (actualCount < it->mCount) + it->mCount = actualCount; + if (it->mCount == 0) + it = list.erase(it); + else + ++it; + } + }; + + update(mBorrowedFromUs); + update(mBorrowedToUs); + } + void TradeItemModel::abort() { mBorrowedFromUs.clear(); diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp index d395744d2a..856f33563d 100644 --- a/apps/openmw/mwgui/tradeitemmodel.hpp +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -31,6 +31,9 @@ namespace MWGui void returnItemBorrowedFromUs(ModelIndex itemIndex, ItemModel* source, size_t count); + /// Update borrowed items in this model + void updateBorrowed(); + /// Permanently transfers items that were borrowed to us from another model to this model void transferItems(); /// Aborts trade diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ad5165c6ae..60ec6d589a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -651,4 +651,13 @@ namespace MWGui { mItemView->update(); } + + void TradeWindow::itemRemoved(const MWWorld::ConstPtr& item, int count) + { + mTradeModel->updateBorrowed(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->updateBorrowed(); + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + updateItemView(); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 8614655c47..c4bde5fc11 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -44,10 +44,12 @@ namespace MWGui void onDeleteCustomData(const MWWorld::Ptr& ptr) override; + TradeItemModel* getTradeModel() { return mTradeModel; }; + void updateItemView(); void itemAdded(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } - void itemRemoved(const MWWorld::ConstPtr& item, int count) override { updateItemView(); } + void itemRemoved(const MWWorld::ConstPtr& item, int count) override; typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone; EventHandle_TradeDone eventTradeDone; From 0247082e198ded78e06b13d6c039d102d7de43e0 Mon Sep 17 00:00:00 2001 From: Kindi Date: Thu, 24 Apr 2025 20:24:55 +0800 Subject: [PATCH 057/455] fix signedness, remove extra semicolon, update offer --- apps/openmw/mwgui/tradeitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 2 ++ apps/openmw/mwgui/tradewindow.hpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 5df048d6db..660e940367 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -118,7 +118,7 @@ namespace MWGui auto update = [](std::vector& list) { for (auto it = list.begin(); it != list.end();) { - int actualCount = it->mBase.getCellRef().getCount(); + size_t actualCount = it->mBase.getCellRef().getCount(); if (actualCount < it->mCount) it->mCount = actualCount; if (it->mCount == 0) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 60ec6d589a..cf882d96cf 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -659,5 +659,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); updateItemView(); + + updateOffer(); } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index c4bde5fc11..748d7ce7d5 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -44,7 +44,7 @@ namespace MWGui void onDeleteCustomData(const MWWorld::Ptr& ptr) override; - TradeItemModel* getTradeModel() { return mTradeModel; }; + TradeItemModel* getTradeModel() { return mTradeModel; } void updateItemView(); From 58fcc8f66d36abe6a4bd9031bad78d5da5e075ab Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Fri, 25 Apr 2025 21:24:27 +0300 Subject: [PATCH 058/455] Require a non-empty argument in ShowMap (#8466) --- apps/openmw/mwscript/guiextensions.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 07855f18ef..3f84362c34 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -115,6 +115,11 @@ namespace MWScript std::string_view cell = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); + // In Morrowind, using an empty string either errors out (e.g. console) or kills the game + // so it should be reasonable to interrupt the script + if (cell.empty()) + throw std::runtime_error("ShowMap substring must not be empty"); + // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's // House as well." http://www.uesp.net/wiki/Tes3Mod:ShowMap From 87014016a259cf56ad00b991ed7fbe6e077c41e8 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 3 May 2025 17:25:27 +0200 Subject: [PATCH 059/455] Stop postponing physics for objects that _don't_ have physics --- apps/openmw/mwworld/scene.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index fb3aee958c..478bdb5bb8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -375,7 +375,6 @@ namespace MWWorld if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) mNavigator.removeObject(DetourNavigator::ObjectId(object), navigatorUpdateGuard); mPhysics->remove(ptr); - ptr.mRef->mData.mPhysicsPostponed = false; } else if (mPhysics->getActor(ptr)) { @@ -383,6 +382,8 @@ namespace MWWorld mRendering.removeActorPath(ptr); mPhysics->remove(ptr); } + else + ptr.mRef->mData.mPhysicsPostponed = false; MWBase::Environment::get().getLuaManager()->objectRemovedFromScene(ptr); } From 71aa11404ccd2abca26e008b3b29ef6ed6e246a3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 5 May 2025 22:47:55 -0700 Subject: [PATCH 060/455] Add checkbox to enable controller menus to openmw launcher. --- apps/launcher/settingspage.cpp | 2 ++ apps/launcher/ui/settingspage.ui | 10 ++++++++++ components/settings/categories/gui.hpp | 1 + files/settings-default.cfg | 3 +++ 4 files changed, 16 insertions(+) diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index dfddc45bc5..94a1a0b8a0 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -304,6 +304,7 @@ bool Launcher::SettingsPage::loadSettings() loadSettingBool(Settings::gui().mColorTopicEnable, *changeDialogTopicsCheckBox); showOwnedComboBox->setCurrentIndex(Settings::game().mShowOwned); loadSettingBool(Settings::gui().mStretchMenuBackground, *stretchBackgroundCheckBox); + loadSettingBool(Settings::gui().mControllerMenus, *controllerMenusCheckBox); loadSettingBool(Settings::map().mAllowZooming, *useZoomOnMapCheckBox); loadSettingBool(Settings::game().mGraphicHerbalism, *graphicHerbalismCheckBox); scalingSpinBox->setValue(Settings::gui().mScalingFactor); @@ -497,6 +498,7 @@ 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(*useZoomOnMapCheckBox, Settings::map().mAllowZooming); saveSettingBool(*graphicHerbalismCheckBox, Settings::game().mGraphicHerbalism); Settings::gui().mScalingFactor.set(scalingSpinBox->value()); diff --git a/apps/launcher/ui/settingspage.ui b/apps/launcher/ui/settingspage.ui index e792ac2843..e501ed156f 100644 --- a/apps/launcher/ui/settingspage.ui +++ b/apps/launcher/ui/settingspage.ui @@ -1398,6 +1398,16 @@ + + + + <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> + + + Enable Controller Menus + + + diff --git a/components/settings/categories/gui.hpp b/components/settings/categories/gui.hpp index a26364c5dd..139d55d9c8 100644 --- a/components/settings/categories/gui.hpp +++ b/components/settings/categories/gui.hpp @@ -25,6 +25,7 @@ namespace Settings SettingValue mMenuTransparency{ mIndex, "GUI", "menu transparency", makeClampSanitizerFloat(0, 1) }; SettingValue mTooltipDelay{ mIndex, "GUI", "tooltip delay", makeMaxSanitizerFloat(0) }; SettingValue mStretchMenuBackground{ mIndex, "GUI", "stretch menu background" }; + SettingValue mControllerMenus{ mIndex, "GUI", "controller menus" }; SettingValue mSubtitles{ mIndex, "GUI", "subtitles" }; SettingValue mHitFader{ mIndex, "GUI", "hit fader" }; SettingValue mWerewolfOverlay{ mIndex, "GUI", "werewolf overlay" }; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index e5654794cc..6a0701803b 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -201,6 +201,9 @@ tooltip delay = 0.0 # Stretch menus, load screens, etc. to the window aspect ratio. stretch menu background = false +# Make menus easier to navigate with a controller. +controller menus = false + # Subtitles for NPC spoken dialog and some sound effects. subtitles = false From d1196ea66756e8975e49668abc3c6823ad116a14 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 6 May 2025 20:09:48 +0200 Subject: [PATCH 061/455] Ignore resistances for base diseases --- apps/openmw/mwmechanics/activespells.cpp | 6 +++--- apps/openmw/mwmechanics/activespells.hpp | 4 ++-- apps/openmw/mwmechanics/disease.hpp | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 9e64996e46..0f361f7ffc 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -267,7 +267,7 @@ namespace MWMechanics if (spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power && !isSpellActive(spell->mId)) { - mSpells.emplace_back(ActiveSpellParams{ spell, ptr }); + mSpells.emplace_back(ActiveSpellParams{ spell, ptr, true }); mSpells.back().setActiveSpellId(MWBase::Environment::get().getESMStore()->generateId()); } } @@ -506,9 +506,9 @@ namespace MWMechanics mQueue.emplace_back(params); } - void ActiveSpells::addSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor) + void ActiveSpells::addSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, bool ignoreResistances) { - mQueue.emplace_back(ActiveSpellParams{ spell, actor, true }); + mQueue.emplace_back(ActiveSpellParams{ spell, actor, ignoreResistances }); } void ActiveSpells::purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index e4fa60ddb6..3e4dafdb26 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -137,8 +137,8 @@ namespace MWMechanics /// void addSpell(const ActiveSpellParams& params); - /// Bypasses resistances - void addSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor); + /// Force resistances + void addSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, bool ignoreResistances = true); /// Removes the active effects from this spell/potion/.. with \a id void removeEffectsBySourceSpellId(const MWWorld::Ptr& ptr, const ESM::RefId& id); diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index c793d5d540..262f813916 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -66,7 +66,9 @@ namespace MWMechanics if (Misc::Rng::rollDice(10000, prng) < x) { // Contracted disease! - actor.getClass().getCreatureStats(actor).getSpells().add(spell); + MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); + creatureStats.getSpells().add(spell); + creatureStats.getActiveSpells().addSpell(spell, actor, false); MWBase::Environment::get().getWorld()->applyLoopingParticles(actor); std::string msg = MWBase::Environment::get() From a8824b46a8f2f871c280fc32897a767361dd24bf Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 9 May 2025 22:56:04 -0700 Subject: [PATCH 062/455] Add first batch of controller-enabled windows --- apps/openmw/mwbase/windowmanager.hpp | 3 ++ apps/openmw/mwgui/confirmationdialog.cpp | 17 ++++++++++++ apps/openmw/mwgui/confirmationdialog.hpp | 2 ++ apps/openmw/mwgui/mainmenu.cpp | 31 +++++++++++++++++++-- apps/openmw/mwgui/mainmenu.hpp | 1 + apps/openmw/mwgui/waitdialog.cpp | 25 +++++++++++++++++ apps/openmw/mwgui/waitdialog.hpp | 1 + apps/openmw/mwgui/windowbase.cpp | 25 +++++++++++++++++ apps/openmw/mwgui/windowbase.hpp | 15 ++++++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 34 +++++++++++++++++++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ apps/openmw/mwinput/controllermanager.cpp | 12 ++++++++ 12 files changed, 166 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 8164501b4b..d2f1fc6514 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -12,6 +12,7 @@ #include #include "../mwgui/mode.hpp" +#include "../mwgui/windowbase.hpp" #include @@ -381,6 +382,8 @@ namespace MWBase /// Same as viewer->getCamera()->getCullMask(), provided for consistency. virtual uint32_t getCullMask() = 0; + virtual MWGui::WindowBase* getTopWindow() = 0; + // Used in Lua bindings virtual const std::vector& getGuiModeStack() const = 0; virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0; diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 48b209f17e..903fb4f898 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -17,6 +17,8 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onCancelButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked); + + trackFocusEvents(mCancelButton); } void ConfirmationDialog::askForConfirmation(const std::string& message) @@ -56,4 +58,19 @@ namespace MWGui eventOkClicked(); } + + bool ConfirmationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + onOkButtonClicked(mOkButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancelButtonClicked(mCancelButton); + + return true; + } } diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 1344f2a501..2a1886398b 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -27,6 +27,8 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 1b3619bd9f..ae5b5f9be2 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -1,6 +1,7 @@ #include "mainmenu.hpp" #include +#include #include #include @@ -163,9 +164,7 @@ namespace MWGui const std::string& name = *sender->getUserData(); 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 +207,34 @@ namespace MWGui } } + bool MainMenu::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + // REMOVEME + Log(Debug::Verbose) << "MainMenu::onControllerButtonEvent " << arg.button; + + MyGUI::KeyCode key = MyGUI::KeyCode::None; + switch (arg.button) + { + case 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); + return true; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + key = MyGUI::KeyCode::Tab; + break; + case SDL_CONTROLLER_BUTTON_A: + key = MyGUI::KeyCode::Space; + break; + case SDL_CONTROLLER_BUTTON_B: + case SDL_CONTROLLER_BUTTON_START: + onButtonClicked(mButtons["return"]); + return true; + } + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); + return true; + } + void MainMenu::showBackground(bool show) { if (mVideo && !show) diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 06a8c945c1..453a16b5e4 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -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; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 568f05abc3..3c39590dee 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -80,6 +80,10 @@ namespace MWGui mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &WaitDialog::onWaitingProgressChanged); mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); + + trackFocusEvents(mUntilHealedButton); + trackFocusEvents(mWaitButton); + trackFocusEvents(mCancelButton); } void WaitDialog::setPtr(const MWWorld::Ptr& ptr) @@ -326,6 +330,27 @@ namespace MWGui } } + bool WaitDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + onWaitButtonClicked(mWaitButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancelButtonClicked(mCancelButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_X && mUntilHealedButton->getVisible()) + onUntilHealedButtonClicked(mUntilHealedButton); + 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; + } + void WaitDialog::stopWaiting() { MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2f); diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 3d66584f54..8a38dd0976 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -36,6 +36,7 @@ namespace MWGui void clear() override; void onFrame(float dt) override; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; bool getSleeping() { return mTimeAdvancer.isRunning() && mSleeping; } void wakeUp(); diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index f5d90590f8..d01822f704 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include #include #include "draganddrop.hpp" @@ -105,6 +106,30 @@ void WindowBase::clampWindowCoordinates(MyGUI::Window* window) window->setPosition(left, top); } +void WindowBase::focusGain(MyGUI::Widget* _new, MyGUI::Widget* _old) +{ + // REMOVEME + Log(Debug::Verbose) << "WindowBase::focusGain new=" << _new << ", old=" << _old; + mMouseFocus = _new; +} + +void WindowBase::focusLoss(MyGUI::Widget* _old, MyGUI::Widget* _new) +{ + // REMOVEME + Log(Debug::Verbose) << "WindowBase::focusLoss old=" << _old << ", new=" << _new; + if (mMouseFocus == _old) + mMouseFocus = nullptr; +} + +void WindowBase::trackFocusEvents(MyGUI::Widget* widget) +{ + if (!Settings::gui().mControllerMenus) + return; + + widget->eventMouseSetFocus += MyGUI::newDelegate(this, &WindowBase::focusGain); + widget->eventMouseLostFocus += MyGUI::newDelegate(this, &WindowBase::focusLoss); +} + WindowModal::WindowModal(const std::string& parLayout) : WindowBase(parLayout) { diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 466060c6ad..3db4399d85 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -1,6 +1,8 @@ #ifndef MWGUI_WINDOW_BASE_H #define MWGUI_WINDOW_BASE_H +#include + #include "layout.hpp" namespace MWWorld @@ -54,13 +56,26 @@ namespace MWGui static void clampWindowCoordinates(MyGUI::Window* window); + /// Called by controllermanager to handle controller events + virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; }; + virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return true; }; + // REMOVEME + // virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) = 0; + // virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) = 0; + protected: virtual void onTitleDoubleClicked(); + MyGUI::Widget* mMouseFocus = nullptr; + void trackFocusEvents(MyGUI::Widget* widget); + private: void onDoubleClick(MyGUI::Widget* _sender); bool mDisabledByLua = false; + + void focusGain(MyGUI::Widget* _new, MyGUI::Widget* _old); + void focusLoss(MyGUI::Widget* _old, MyGUI::Widget* _new); }; /* diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 565fb43127..b328176014 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -859,6 +859,40 @@ namespace MWGui mHud->setPlayerPos(x, y, u, v); } + WindowBase* WindowManager::getTopWindow() + { + if (!mCurrentModals.empty()) + return mCurrentModals.back(); + + if (isSettingsWindowVisible()) + return mSettingsWindow; + + if (!mGuiModes.empty()) + { + GuiModeState& state = mGuiModeStates[mGuiModes.back()]; + // REMOVEME + Log(Debug::Error) << "getTopWindow: " << state.mWindows.size() << " windows in state " << mGuiModes.back(); + // find the topmost window + for (WindowBase* window : state.mWindows) + if (window->isVisible()) + return window; + else + Log(Debug::Error) << "-- Skipping hidden window " << window; + } + else + { + // return pinned windows if visible + // REMOVEME + Log(Debug::Error) << "getTopWindow: " << mGuiModeStates[GM_Inventory].mWindows.size() << " pinned windows"; + for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows) + if (window->isVisible()) + return window; + else + Log(Debug::Error) << "-- Skipping hidden window " << window; + } + return nullptr; + } + void WindowManager::update(float frameDuration) { handleScheduledMessageBoxes(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 03902e21c4..fd035ed12e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -387,6 +387,8 @@ namespace MWGui void asyncPrepareSaveMap() override; + WindowBase* getTopWindow() override; + // Used in Lua bindings const std::vector& getGuiModeStack() const override { return mGuiModes; } void setDisabledByLua(std::string_view windowId, bool disabled) override; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 1a8490d8b7..404b156c24 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -241,6 +241,12 @@ namespace MWInput bool ControllerManager::gamepadToGuiControl(const SDL_ControllerButtonEvent& arg) { + if (Settings::gui().mControllerMenus) + { + MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); + return topWin && topWin->onControllerButtonEvent(arg); + } + // Presumption of GUI mode will be removed in the future. // MyGUI KeyCodes *may* change. MyGUI::KeyCode key = MyGUI::KeyCode::None; @@ -302,6 +308,12 @@ namespace MWInput bool ControllerManager::gamepadToGuiControl(const SDL_ControllerAxisEvent& arg) { + if (Settings::gui().mControllerMenus) + { + MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); + return topWin && topWin->onControllerThumbstickEvent(arg); + } + switch (arg.axis) { case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: From fc86878922f72d673e2d160be8084d7e023a9644 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 9 May 2025 23:43:29 -0700 Subject: [PATCH 063/455] Allow exiting credits by pressing B --- apps/openmw/mwgui/mainmenu.cpp | 10 ++++++++-- apps/openmw/mwinput/controllermanager.cpp | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ae5b5f9be2..5d0e2dc585 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -228,8 +228,14 @@ namespace MWGui break; case SDL_CONTROLLER_BUTTON_B: case SDL_CONTROLLER_BUTTON_START: - onButtonClicked(mButtons["return"]); - return true; + if (mButtons["return"]->getVisible()) + { + onButtonClicked(mButtons["return"]); + return true; + } + else + key = MyGUI::KeyCode::Escape; + break; } MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); return true; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 404b156c24..7aff8172ce 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -244,7 +244,8 @@ namespace MWInput if (Settings::gui().mControllerMenus) { MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); - return topWin && topWin->onControllerButtonEvent(arg); + if (topWin && topWin->onControllerButtonEvent(arg)) + return true; } // Presumption of GUI mode will be removed in the future. @@ -311,7 +312,8 @@ namespace MWInput if (Settings::gui().mControllerMenus) { MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); - return topWin && topWin->onControllerThumbstickEvent(arg); + if (topWin && topWin->onControllerThumbstickEvent(arg)) + return true; } switch (arg.axis) From 2970913d553aed9881489acdfd6b71ae474e23e8 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 9 May 2025 23:44:06 -0700 Subject: [PATCH 064/455] Minimal controller bindings for settings window --- apps/openmw/mwgui/settingswindow.cpp | 34 ++++++++++++++++++++++++++++ apps/openmw/mwgui/settingswindow.hpp | 2 ++ 2 files changed, 36 insertions(+) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 02353c5d41..7f8d98ee05 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1128,4 +1128,38 @@ 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) + { + uint32_t index = mSettingsTab->getIndexSelected(); + if (index <= 0) + index = mSettingsTab->getItemCount() - 1; + else + index--; + mSettingsTab->setIndexSelected(index); + + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + uint32_t index = mSettingsTab->getIndexSelected(); + if (index >= mSettingsTab->getItemCount() - 1) + index = 0; + else + index++; + mSettingsTab->setIndexSelected(index); + + return true; + } + + return false; + } + } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index dc4e09f8ac..555468d806 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -26,6 +26,8 @@ namespace MWGui void onResChange(int, int) override; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + protected: MyGUI::TabControl* mSettingsTab; MyGUI::Button* mOkButton; From 58c4e0ddf735747105c5fc4c2db7a0aa912888a3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 10 May 2025 01:41:53 -0700 Subject: [PATCH 065/455] Minimal controller bindings for save/load window --- apps/openmw/mwgui/savegamedialog.cpp | 58 ++++++++++++++++++++++++++++ apps/openmw/mwgui/savegamedialog.hpp | 2 + 2 files changed, 60 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 94f25e118b..fc6171b8dc 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -63,6 +63,9 @@ namespace MWGui // To avoid accidental deletions mDeleteButton->setNeedKeyFocus(false); + + trackFocusEvents(mCancelButton); + trackFocusEvents(mDeleteButton); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox* sender, size_t pos) @@ -487,4 +490,59 @@ namespace MWGui mScreenshotTexture = std::make_unique(texture); mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); } + + bool SaveGameDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + onOkButtonClicked(mOkButton); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCancelButtonClicked(mCancelButton); + return true; + } + 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); + return true; + } + 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); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + { + uint32_t index = mCharacterSelection->getIndexSelected(); + if (index <= 0) + index = mCharacterSelection->getItemCount() - 1; + else + index--; + mCharacterSelection->setIndexSelected(index); + + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + uint32_t index = mCharacterSelection->getIndexSelected(); + if (index >= mCharacterSelection->getItemCount() - 1) + index = 0; + else + index++; + mCharacterSelection->setIndexSelected(index); + + return true; + } + + return false; + } } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 35e65fbed0..caabff2431 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,6 +24,8 @@ namespace MWGui void setLoadOrSave(bool load); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + private: void confirmDeleteSave(); From bb88becc2bb1a0fe22a9b98aa3161e11ab7095a5 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 10 May 2025 22:50:16 -0700 Subject: [PATCH 066/455] Basic controller support for the journal --- apps/openmw/mwgui/journalwindow.cpp | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 574c425d3e..7fd6e3d995 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -639,6 +639,67 @@ namespace } } } + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Fall through to mouse click + return false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + if (mOptionsMode) + notifyCancel(getWidget(CancelBTN)); + else if (mStates.size() > 1) + notifyJournal(getWidget(JournalBTN)); + else + notifyClose(getWidget(CloseBTN)); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + if (mQuestMode) + { + if (!mOptionsMode) + notifyOptions(getWidget(OptionsBTN)); + notifyTopics(getWidget(TopicsBTN)); + } + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) + { + if (!mQuestMode) + { + if (!mOptionsMode) + notifyOptions(getWidget(OptionsBTN)); + notifyQuests(getWidget(QuestsBTN)); + } + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + notifyPrevPage(getWidget(PrevPageBTN)); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + notifyNextPage(getWidget(NextPageBTN)); + return true; + } + + return false; + } }; } From be298f78cb3d8172e0fa18608475ac3803e9e79e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 10 May 2025 23:17:23 -0700 Subject: [PATCH 067/455] Basic controller support for the books --- apps/openmw/mwgui/bookwindow.cpp | 42 ++++++++++++++++++++++++++++++++ apps/openmw/mwgui/bookwindow.hpp | 1 + 2 files changed, 43 insertions(+) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index ef875a18b9..91b1af615e 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -27,15 +27,19 @@ namespace MWGui { getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); + trackFocusEvents(mCloseButton); getWidget(mTakeButton, "TakeButton"); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); + trackFocusEvents(mTakeButton); getWidget(mNextPageButton, "NextPageBTN"); mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); + trackFocusEvents(mNextPageButton); getWidget(mPrevPageButton, "PrevPageBTN"); mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); + trackFocusEvents(mPrevPageButton); getWidget(mLeftPageNumber, "LeftPageNumber"); getWidget(mRightPageNumber, "RightPageNumber"); @@ -218,4 +222,42 @@ namespace MWGui } } + bool BookWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + if (mTakeButton->getVisible()) + onTakeButtonClicked(mTakeButton); + else + onCloseButtonClicked(mCloseButton); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCloseButtonClicked(mCloseButton); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + prevPage(); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + nextPage(); + return true; + } + + return false; + } } diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 5a3dfdf584..062c6e9dbd 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -18,6 +18,7 @@ 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"; } From 80166cddd06e7d49639762a73578ed1901089cd3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 00:19:01 -0700 Subject: [PATCH 068/455] Make main menu honor controller mouse clicks --- apps/openmw/mwgui/mainmenu.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5d0e2dc585..cfcb63e4d5 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -224,6 +224,8 @@ namespace MWGui key = MyGUI::KeyCode::Tab; break; case SDL_CONTROLLER_BUTTON_A: + if (mMouseFocus != nullptr) + return false; key = MyGUI::KeyCode::Space; break; case SDL_CONTROLLER_BUTTON_B: @@ -341,6 +343,7 @@ namespace MWGui button->setProperty("ImagePushed", "textures\\menu_" + buttonId + "_pressed.dds"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); button->setUserData(buttonId); + trackFocusEvents(button); } } From 3b42d02cfc602230c2d92f2369235269e2cecb8a Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 00:20:31 -0700 Subject: [PATCH 069/455] Add controller support to message boxes --- apps/openmw/mwgui/messagebox.cpp | 50 ++++++++++++++++++++++++++++++++ apps/openmw/mwgui/messagebox.hpp | 3 ++ 2 files changed, 53 insertions(+) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 1d6e1511c4..7bc6d70931 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" @@ -259,6 +260,7 @@ namespace MWGui button->setCaptionWithReplacing(buttonId); button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); + trackFocusEvents(button); mButtons.push_back(button); @@ -280,6 +282,12 @@ namespace MWGui } } + if (Settings::gui().mControllerMenus && mButtons.size() > 1) + { + // If we have more than one button, we need to set the focus to the first one. + mButtons[0]->setStateSelected(true); + } + MyGUI::IntSize mainWidgetSize; if (buttonsWidth < textSize.width) { @@ -431,4 +439,46 @@ namespace MWGui return mButtonPressed; } + bool InteractiveMessageBox::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + buttonActivated(mButtons[mControllerFocus]); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + if (mButtons.size() == 1) + buttonActivated(mButtons[0]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + if (mButtons.size() > 1) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + if (mButtons.size() > 1) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); + } + } + + return true; + } } diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index feb717e0ad..e6128ee0d1 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -103,6 +103,8 @@ namespace MWGui bool mMarkedToDelete; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + private: void buttonActivated(MyGUI::Widget* _widget); @@ -114,6 +116,7 @@ namespace MWGui int mButtonPressed; int mDefaultFocus; bool mImmediate; + int mControllerFocus = 0; }; } From 4c5db612f05f12cce0cf1fb4f44c5d06a54bc494 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 00:32:29 -0700 Subject: [PATCH 070/455] Improve controller support for confirmation dialogs by highlighting selected choice --- apps/openmw/mwgui/confirmationdialog.cpp | 23 +++++++++++++++++++++-- apps/openmw/mwgui/confirmationdialog.hpp | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 903fb4f898..67da1f94a7 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -18,7 +20,12 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onCancelButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked); - trackFocusEvents(mCancelButton); + if (Settings::gui().mControllerMenus) + { + mOkButton->setStateSelected(true); + trackFocusEvents(mOkButton); + trackFocusEvents(mCancelButton); + } } void ConfirmationDialog::askForConfirmation(const std::string& message) @@ -66,10 +73,22 @@ namespace MWGui if (mMouseFocus != nullptr) return false; - onOkButtonClicked(mOkButton); + 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; } diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 2a1886398b..93d7f360c5 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -29,6 +29,7 @@ namespace MWGui void onOkButtonClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mOkButtonFocus = true; }; } From c9ce93a22f68a23812795167c5c79729e0c33a79 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 13:48:50 -0700 Subject: [PATCH 071/455] Controller menus: don't wrap focus when only two buttons --- apps/openmw/mwgui/messagebox.cpp | 40 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 7bc6d70931..be0f284b04 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -456,27 +456,31 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { - if (mButtons.size() > 1) - { - mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == 0) - mControllerFocus = mButtons.size() - 1; - else - mControllerFocus--; - mButtons[mControllerFocus]->setStateSelected(true); - } + if (mButtons.size() <= 1) + return true; + if (mButtons.size() == 2 && mControllerFocus == 0) + return true; + + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { - if (mButtons.size() > 1) - { - mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == mButtons.size() - 1) - mControllerFocus = 0; - else - mControllerFocus++; - mButtons[mControllerFocus]->setStateSelected(true); - } + if (mButtons.size() <= 1) + return true; + if (mButtons.size() == 2 && mControllerFocus == mButtons.size() - 1) + return true; + + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); } return true; From 1e200ed0485362982dbff799c0c114da78f08b6b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 14:48:42 -0700 Subject: [PATCH 072/455] Add controller support to race menu --- apps/openmw/mwgui/race.cpp | 55 ++++++++++++++++++++++++++++++++++++++ apps/openmw/mwgui/race.hpp | 3 +++ 2 files changed, 58 insertions(+) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 7b445d419f..6d7ec19f88 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -462,6 +462,61 @@ namespace MWGui } } + bool RaceDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Have A button do nothing so mouse controller still works. + return false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_START) + { + onOkClicked(nullptr); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onBackClicked(nullptr); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + 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) + { + if (arg.value < -1000 || arg.value > 1000) + onPreviewScroll(nullptr, arg.value < 0 ? 1 : -1); + return true; + } + + return false; + } + const ESM::NPC& RaceDialog::getResult() const { return mPreview->getPrototype(); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index a6ac0e2813..5452753747 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -118,6 +118,9 @@ namespace MWGui std::unique_ptr mPreviewTexture; bool mPreviewDirty; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; }; } #endif From 5e7761bef1c1a47aff52285535eba31dd667d4db Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 14:56:58 -0700 Subject: [PATCH 073/455] Add controller support to text input menu --- apps/openmw/mwgui/textinput.cpp | 10 ++++++++++ apps/openmw/mwgui/textinput.hpp | 1 + 2 files changed, 11 insertions(+) diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 5f47b96f03..44e0defef6 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -83,4 +83,14 @@ namespace MWGui mTextEdit->setCaption(text); } + bool TextInputDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + onOkClicked(nullptr); + return true; + } + + return false; + } } diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index c11d40f1a9..ad7896ff27 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -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; From b2620c861a43ef5655c11d68a7515a856b36f3cc Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 22:24:05 -0700 Subject: [PATCH 074/455] Add scrollbar override to capture mouse hover events --- apps/openmw/mwgui/waitdialog.cpp | 2 ++ apps/openmw/mwgui/waitdialog.hpp | 3 ++- components/CMakeLists.txt | 2 +- components/widgets/scrollbar.cpp | 17 +++++++++++++++++ components/widgets/scrollbar.hpp | 18 ++++++++++++++++++ components/widgets/widgets.cpp | 2 ++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 components/widgets/scrollbar.cpp create mode 100644 components/widgets/scrollbar.hpp diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 3c39590dee..5b92699982 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -84,6 +84,8 @@ namespace MWGui trackFocusEvents(mUntilHealedButton); trackFocusEvents(mWaitButton); trackFocusEvents(mCancelButton); + for (MyGUI::Widget* widget : mHourSlider->getAllWidgets()) + trackFocusEvents(widget); } void WaitDialog::setPtr(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 8a38dd0976..0d6d2a82d2 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -4,6 +4,7 @@ #include "timeadvancer.hpp" #include "windowbase.hpp" #include +#include namespace MWGui { @@ -53,7 +54,7 @@ namespace MWGui MyGUI::Button* mUntilHealedButton; MyGUI::Button* mWaitButton; MyGUI::Button* mCancelButton; - MyGUI::ScrollBar* mHourSlider; + Gui::ScrollBar* mHourSlider; TimeAdvancer mTimeAdvancer; bool mSleeping; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7659de0ffd..6e6d3ee984 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -353,7 +353,7 @@ add_component_dir (myguiplatform ) add_component_dir (widgets - box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets + box fontwrapper imagebutton tags list numericeditbox scrollbar sharedstatebutton windowcaption widgets ) add_component_dir (fontloader diff --git a/components/widgets/scrollbar.cpp b/components/widgets/scrollbar.cpp new file mode 100644 index 0000000000..0384003e65 --- /dev/null +++ b/components/widgets/scrollbar.cpp @@ -0,0 +1,17 @@ +#include "scrollbar.hpp" + +#include + +namespace Gui +{ + std::vector ScrollBar::getAllWidgets() + { + std::vector widgets; + widgets.push_back(mWidgetStart); + widgets.push_back(mWidgetEnd); + widgets.push_back(mWidgetTrack); + widgets.push_back(mWidgetFirstPart); + widgets.push_back(mWidgetSecondPart); + return widgets; + } +} diff --git a/components/widgets/scrollbar.hpp b/components/widgets/scrollbar.hpp new file mode 100644 index 0000000000..fa4cee01f0 --- /dev/null +++ b/components/widgets/scrollbar.hpp @@ -0,0 +1,18 @@ +#ifndef OPENMW_COMPONENTS_WIDGETS_SCROLLBAR_H +#define OPENMW_COMPONENTS_WIDGETS_SCROLLBAR_H + +#include + +namespace Gui +{ + /// @brief A scrollbar that can return all its widgets for binding hover listeners. + class ScrollBar : public MyGUI::ScrollBar + { + MYGUI_RTTI_DERIVED(ScrollBar) + + public: + std::vector getAllWidgets(); + }; +} + +#endif diff --git a/components/widgets/widgets.cpp b/components/widgets/widgets.cpp index d27d7e5fc9..58b5736b30 100644 --- a/components/widgets/widgets.cpp +++ b/components/widgets/widgets.cpp @@ -6,6 +6,7 @@ #include "imagebutton.hpp" #include "list.hpp" #include "numericeditbox.hpp" +#include "scrollbar.hpp" #include "sharedstatebutton.hpp" #include "windowcaption.hpp" @@ -26,6 +27,7 @@ namespace Gui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } From 8f415ceab640ea62d97a81d23662d9dca2d75140 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 22:30:58 -0700 Subject: [PATCH 075/455] Convert UI widgets to local scrollbar implementation --- apps/openmw/mwgui/countdialog.hpp | 4 +++- apps/openmw/mwgui/spellcreationdialog.hpp | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 9cdf231549..95d4a9a8d4 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -3,6 +3,8 @@ #include "windowbase.hpp" +#include + namespace Gui { class NumericEditBox; @@ -24,7 +26,7 @@ namespace MWGui EventHandle_WidgetInt eventOkClicked; private: - MyGUI::ScrollBar* mSlider; + Gui::ScrollBar* mSlider; Gui::NumericEditBox* mItemEdit; MyGUI::TextBox* mItemText; MyGUI::TextBox* mLabelText; diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 6dfe61fc57..b2bd71d9c4 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "referenceinterface.hpp" #include "windowbase.hpp" @@ -57,10 +58,10 @@ namespace MWGui MyGUI::TextBox* mDurationValue; MyGUI::TextBox* mAreaValue; - MyGUI::ScrollBar* mMagnitudeMinSlider; - MyGUI::ScrollBar* mMagnitudeMaxSlider; - MyGUI::ScrollBar* mDurationSlider; - MyGUI::ScrollBar* mAreaSlider; + Gui::ScrollBar* mMagnitudeMinSlider; + Gui::ScrollBar* mMagnitudeMaxSlider; + Gui::ScrollBar* mDurationSlider; + Gui::ScrollBar* mAreaSlider; MyGUI::TextBox* mAreaText; From c4a87cfe4d15558db843927dcd8f469377540390 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 11 May 2025 22:59:11 -0700 Subject: [PATCH 076/455] Fix selecting saves with mouse controller --- apps/openmw/mwgui/savegamedialog.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index fc6171b8dc..51fa4c6717 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -362,6 +362,12 @@ namespace MWGui } else onSlotSelected(mSaveList, MyGUI::ITEM_NONE); + + if (Settings::gui().mControllerMenus) + { + for (int i = 0; i < mSaveList->getItemCount(); i++) + trackFocusEvents(mSaveList->getWidgetByIndex(i)); + } } std::string formatTimeplayed(const double timeInSeconds) From 83162477ec50199a06c11336fa378736c0efe043 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 01:34:28 -0700 Subject: [PATCH 077/455] Basic controller support for all character creation windows --- apps/openmw/mwgui/birth.cpp | 31 ++++ apps/openmw/mwgui/birth.hpp | 2 + apps/openmw/mwgui/class.cpp | 192 ++++++++++++++++++++++- apps/openmw/mwgui/class.hpp | 13 ++ apps/openmw/mwgui/confirmationdialog.hpp | 2 +- apps/openmw/mwgui/race.cpp | 8 + apps/openmw/mwgui/race.hpp | 3 +- apps/openmw/mwgui/review.cpp | 76 +++++++++ apps/openmw/mwgui/review.hpp | 5 + 9 files changed, 322 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 3dfdd17627..bf495273be 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -271,4 +271,35 @@ namespace MWGui mSpellArea->setVisibleVScroll(true); mSpellArea->setViewOffset(MyGUI::IntPoint(0, 0)); } + + bool BirthDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Have A button do nothing so mouse controller still works. + return false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_START) + { + onOkClicked(nullptr); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onBackClicked(nullptr); + } + 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; + } } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index db9e997b6c..20db2b70de 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -55,6 +55,8 @@ namespace MWGui std::vector mSpellItems; ESM::RefId mCurrentBirthId; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } #endif diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 839f0f5072..25218c9e25 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "tooltips.hpp" @@ -46,15 +47,20 @@ 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); + trackFocusEvents(mBackButton); + trackFocusEvents(mOkButton); + } center(); } @@ -71,6 +77,33 @@ namespace MWGui center(); } + bool GenerateClassResultDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + 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) @@ -278,6 +311,37 @@ namespace MWGui setClassImage(mClassImage, mCurrentClassId); } + bool PickClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Have A button do nothing so mouse controller still works. + return false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_START) + { + onOkClicked(nullptr); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onBackClicked(nullptr); + } + 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) @@ -353,6 +417,14 @@ 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); + } + trackFocusEvents(button); + this->mButtons.push_back(button); } } @@ -382,6 +454,53 @@ namespace MWGui } } + bool InfoBoxDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + onButtonClicked(mButtons[mControllerFocus]); + return true; + } + 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; + + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mButtons.size() <= 1) + return true; + if (mButtons.size() == 2 && mControllerFocus == mButtons.size() - 1) + return true; + + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); + } + + return true; + } + /* ClassChoiceDialog */ ClassChoiceDialog::ClassChoiceDialog() @@ -552,6 +671,24 @@ namespace MWGui MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); } + bool CreateClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Have A button do nothing so mouse controller still works. + return false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_START) + { + onOkClicked(nullptr); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onBackClicked(nullptr); + } + return true; + } + // widget controls void CreateClassDialog::onDialogCancel() @@ -739,6 +876,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() @@ -791,6 +938,16 @@ namespace MWGui return true; } + bool SelectAttributeDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCancelClicked(nullptr); + return true; + } + return false; + } + /* SelectSkillDialog */ SelectSkillDialog::SelectSkillDialog() @@ -855,6 +1012,16 @@ namespace MWGui return true; } + bool SelectSkillDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCancelClicked(nullptr); + return true; + } + return false; + } + /* DescriptionDialog */ DescriptionDialog::DescriptionDialog() @@ -904,4 +1071,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; + } } diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index f89a0c7d88..4567776beb 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -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 mButtons; + int mControllerFocus = 0; }; // 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; }; @@ -132,6 +138,7 @@ namespace MWGui void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: void updateClasses(); @@ -173,6 +180,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 +214,7 @@ namespace MWGui protected: void onAttributeClicked(Widgets::MWAttributePtr _sender); void onCancelClicked(MyGUI::Widget* _sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: ESM::RefId mAttributeId; @@ -237,6 +246,7 @@ namespace MWGui protected: void onSkillClicked(Widgets::MWSkillPtr _sender); void onCancelClicked(MyGUI::Widget* _sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: ESM::RefId mSkillId; @@ -258,6 +268,7 @@ namespace MWGui protected: void onOkClicked(MyGUI::Widget* _sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: MyGUI::EditBox* mTextEdit; @@ -329,6 +340,8 @@ namespace MWGui Widgets::MWAttributePtr mAffectedAttribute; Widgets::MWSkillPtr mAffectedSkill; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } #endif diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 93d7f360c5..9b26e3a3c9 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -29,7 +29,7 @@ namespace MWGui void onOkButtonClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - int mOkButtonFocus = true; + bool mOkButtonFocus = true; }; } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 6d7ec19f88..6a62fb3b47 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -501,6 +501,14 @@ namespace MWGui winMgr->setKeyFocusWidget(mRaceList); winMgr->injectKeyPress(MyGUI::KeyCode::ArrowDown, 0, false); } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + onPreviewScroll(nullptr, 5); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + onPreviewScroll(nullptr, -5); + } return true; } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 5452753747..8589743b9e 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -3,6 +3,7 @@ #include "windowbase.hpp" #include +#include #include namespace MWRender @@ -100,7 +101,7 @@ namespace MWGui MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; - MyGUI::ScrollBar* mHeadRotate; + Gui::ScrollBar* mHeadRotate; MyGUI::Widget* mSkillList; std::vector mSkillItems; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index ddce2c5f50..b113ace2be 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -46,21 +46,29 @@ namespace MWGui getWidget(button, "NameButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked); + trackFocusEvents(button); + mButtons.push_back(button); getWidget(mRaceWidget, "RaceText"); getWidget(button, "RaceButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked); + trackFocusEvents(button); + mButtons.push_back(button); getWidget(mClassWidget, "ClassText"); getWidget(button, "ClassButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked); + trackFocusEvents(button); + mButtons.push_back(button); getWidget(mBirthSignWidget, "SignText"); getWidget(button, "SignButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked); + trackFocusEvents(button); + mButtons.push_back(button); // Setup dynamic stats getWidget(mHealth, "Health"); @@ -108,10 +116,17 @@ namespace MWGui MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + trackFocusEvents(backButton); + mButtons.push_back(backButton); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); + trackFocusEvents(okButton); + mButtons.push_back(okButton); + + if (Settings::gui().mControllerMenus) + mButtons[mControllerFocus]->setStateSelected(true); } void ReviewDialog::onOpen() @@ -522,4 +537,65 @@ namespace MWGui MyGUI::IntPoint(0, static_cast(mSkillView->getViewOffset().top + _rel * 0.3))); } + bool ReviewDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + 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_START) + { + onOkClicked(mButtons[5]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onBackClicked(mButtons[4]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); + } + + return true; + } } diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 7226ad628d..fe53787fe3 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -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& skills, const std::string& titleId, @@ -100,6 +101,10 @@ namespace MWGui std::vector mSkillWidgets; //< Skills and other information bool mUpdateSkillArea; + + // 0 = Name, 1 = Race, 2 = Class, 3 = BirthSign, 4 = Back, 5 = OK + std::vector mButtons; + int mControllerFocus = 5; }; } #endif From 4bad7a30741efed63a19da9fa93098736b67924c Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 02:20:28 -0700 Subject: [PATCH 078/455] Update character creation controller support to work better with gamepad mouse mode --- apps/openmw/mwgui/birth.cpp | 48 ++++++++++++----- apps/openmw/mwgui/birth.hpp | 5 ++ apps/openmw/mwgui/class.cpp | 102 +++++++++++++++++++++++++++++------- apps/openmw/mwgui/class.hpp | 12 ++++- apps/openmw/mwgui/race.cpp | 49 ++++++++++------- apps/openmw/mwgui/race.hpp | 4 ++ 6 files changed, 168 insertions(+), 52 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index bf495273be..b121c1978b 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -50,15 +50,16 @@ 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) + mOkButton->setStateSelected(true); updateBirths(); updateSpells(); @@ -277,29 +278,50 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { // Have A button do nothing so mouse controller still works. - return false; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_START) - { - onOkClicked(nullptr); + if (mUsingGamepadGuiCursor) + return false; + + if (mOkButtonFocus) + onOkClicked(mOkButton); + else + onBackClicked(mBackButton); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { - onBackClicked(nullptr); + onBackClicked(mBackButton); } 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); + mUsingGamepadGuiCursor = 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); + mUsingGamepadGuiCursor = false; + } + 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); + mUsingGamepadGuiCursor = false; } return true; } + + bool BirthDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + { + mUsingGamepadGuiCursor = true; + } + return false; + } } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 20db2b70de..8a7190b934 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -53,10 +53,15 @@ namespace MWGui MyGUI::ScrollView* mSpellArea; MyGUI::ImageBox* mBirthImage; std::vector mSpellItems; + MyGUI::Button* mBackButton; + MyGUI::Button* mOkButton; ESM::RefId mCurrentBirthId; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + bool mOkButtonFocus = true; + bool mUsingGamepadGuiCursor = false; }; } #endif diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 25218c9e25..a7b5b36aa5 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -143,13 +143,14 @@ 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) + mOkButton->setStateSelected(true); updateClasses(); updateStats(); @@ -316,32 +317,53 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { // Have A button do nothing so mouse controller still works. - return false; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_START) - { - onOkClicked(nullptr); + if (mUsingGamepadGuiCursor) + return false; + + if (mOkButtonFocus) + onOkClicked(mOkButton); + else + onBackClicked(mBackButton); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { - onBackClicked(nullptr); + onBackClicked(mBackButton); } 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); + mUsingGamepadGuiCursor = 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); + mUsingGamepadGuiCursor = false; + } + 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); + mUsingGamepadGuiCursor = false; } return true; } + bool PickClassDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + { + mUsingGamepadGuiCursor = true; + } + return false; + } + /* InfoBoxDialog */ void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) @@ -569,14 +591,20 @@ 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); // Set default skills, attributes @@ -676,19 +704,57 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { // Have A button do nothing so mouse controller still works. - return false; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_START) - { - onOkClicked(nullptr); + if (mUsingGamepadGuiCursor) + return false; + + 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(nullptr); + onBackClicked(mButtons[1]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + mUsingGamepadGuiCursor = false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); + mUsingGamepadGuiCursor = false; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); + mUsingGamepadGuiCursor = false; } return true; } + bool CreateClassDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + { + mUsingGamepadGuiCursor = true; + } + return false; + } + // widget controls void CreateClassDialog::onDialogCancel() diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 4567776beb..e2566ce6c0 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -138,7 +138,6 @@ namespace MWGui void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); - bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: void updateClasses(); @@ -147,11 +146,18 @@ 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; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + bool mOkButtonFocus = true; + bool mUsingGamepadGuiCursor = false; }; class SelectSpecializationDialog : public WindowModal @@ -325,6 +331,7 @@ namespace MWGui private: MyGUI::EditBox* mEditName; MyGUI::TextBox* mSpecializationName; + std::vector mButtons; Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1; std::array mMajorSkill; std::array mMinorSkill; @@ -342,6 +349,9 @@ namespace MWGui Widgets::MWSkillPtr mAffectedSkill; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + int mControllerFocus = 2; + bool mUsingGamepadGuiCursor = false; }; } #endif diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 6a62fb3b47..1ff5cc4784 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -108,15 +108,16 @@ 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) + mOkButton->setStateSelected(true); updateRaces(); updateSkills(); @@ -467,15 +468,17 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { // Have A button do nothing so mouse controller still works. - return false; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_START) - { - onOkClicked(nullptr); + if (mUsingGamepadGuiCursor) + return false; + + if (mOkButtonFocus) + onOkClicked(mOkButton); + else + onBackClicked(mBackButton); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { - onBackClicked(nullptr); + onBackClicked(mBackButton); } else if (arg.button == SDL_CONTROLLER_BUTTON_X) { @@ -494,20 +497,22 @@ namespace MWGui MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); winMgr->setKeyFocusWidget(mRaceList); winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); + mUsingGamepadGuiCursor = 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); + mUsingGamepadGuiCursor = false; } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus) || + (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus)) { - onPreviewScroll(nullptr, 5); - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - { - onPreviewScroll(nullptr, -5); + mOkButtonFocus = !mOkButtonFocus; + mOkButton->setStateSelected(mOkButtonFocus); + mBackButton->setStateSelected(!mOkButtonFocus); + mUsingGamepadGuiCursor = false; } return true; @@ -515,7 +520,11 @@ namespace MWGui bool RaceDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { - if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX) + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + { + mUsingGamepadGuiCursor = true; + } + else if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX) { if (arg.value < -1000 || arg.value > 1000) onPreviewScroll(nullptr, arg.value < 0 ? 1 : -1); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 8589743b9e..c3b322ba8b 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -102,6 +102,8 @@ namespace MWGui MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; Gui::ScrollBar* mHeadRotate; + MyGUI::Button* mBackButton; + MyGUI::Button* mOkButton; MyGUI::Widget* mSkillList; std::vector mSkillItems; @@ -122,6 +124,8 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + bool mOkButtonFocus = true; + bool mUsingGamepadGuiCursor = false; }; } #endif From a78bdee941b0b1f585cf4702b356c90444a63bf8 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 20:08:47 -0700 Subject: [PATCH 079/455] Reset selected button when confirmation dialog is reopened --- apps/openmw/mwgui/confirmationdialog.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 67da1f94a7..b284db16b2 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -22,7 +22,6 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mOkButton->setStateSelected(true); trackFocusEvents(mOkButton); trackFocusEvents(mCancelButton); } @@ -44,6 +43,13 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton); + if (Settings::gui().mControllerMenus) + { + mOkButtonFocus = true; + mOkButton->setStateSelected(true); + mCancelButton->setStateSelected(false); + } + center(); } From fa52fea59b2a5bca2f4574a430f76fb7ee91495b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 20:09:24 -0700 Subject: [PATCH 080/455] Better joystick/mouse coordination in save game diaglog --- apps/openmw/mwgui/savegamedialog.cpp | 44 +++++++++++++++++++++------- apps/openmw/mwgui/savegamedialog.hpp | 7 +++-- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 51fa4c6717..37ed137b23 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -161,6 +161,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; @@ -501,30 +508,40 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) + // Have A button do nothing so mouse controller still works. + if (mUsingGamepadGuiCursor) return false; - onOkButtonClicked(mOkButton); - return true; + if (mOkButtonFocus) + onOkButtonClicked(mOkButton); + else + onCancelButtonClicked(mCancelButton); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { onCancelButtonClicked(mCancelButton); - return true; } 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); - return true; + mUsingGamepadGuiCursor = 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); - return true; + mUsingGamepadGuiCursor = 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); + mUsingGamepadGuiCursor = false; } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { @@ -534,8 +551,7 @@ namespace MWGui else index--; mCharacterSelection->setIndexSelected(index); - - return true; + mUsingGamepadGuiCursor = false; } else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { @@ -545,10 +561,18 @@ namespace MWGui else index++; mCharacterSelection->setIndexSelected(index); - - return true; + mUsingGamepadGuiCursor = false; } + return true; + } + + bool SaveGameDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + { + mUsingGamepadGuiCursor = true; + } return false; } } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index caabff2431..2561fc60f6 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,8 +24,6 @@ namespace MWGui void setLoadOrSave(bool load); - bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - private: void confirmDeleteSave(); @@ -68,6 +66,11 @@ namespace MWGui const MWState::Character* mCurrentCharacter; const MWState::Slot* mCurrentSlot; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + bool mOkButtonFocus = true; + bool mUsingGamepadGuiCursor = false; }; } From 07be682b885940ad80b244de8f5b7a323b984223 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 20:10:13 -0700 Subject: [PATCH 081/455] Better controller support for journal; mouse for topic index A-Z --- apps/openmw/mwgui/journalwindow.cpp | 163 +++++++++++++++++++++++----- apps/openmw/mwgui/journalwindow.hpp | 4 + 2 files changed, 139 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 7fd6e3d995..3dc46459b4 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -248,6 +248,8 @@ namespace } updateShowingPages(); + mSelectedQuest = 0; + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget(CloseBTN)); } @@ -469,6 +471,20 @@ namespace MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); } + void addControllerButtons(Gui::MWList* _list, int _selectedIndex) + { + mButtons.clear(); + for (int i = 0; i < _list->getItemCount(); i++) + { + MyGUI::Button* listItem = _list->getItemWidget(_list->getItemNameAt(i)); + if (listItem) + { + listItem->setStateSelected(mButtons.size() == _selectedIndex); + mButtons.push_back(listItem); + } + } + } + void notifyIndexLinkClicked(MWGui::TypesetBook::InteractiveId index) { setVisible(LeftTopicIndex, false); @@ -487,6 +503,9 @@ namespace list->adjustSize(); + if (Settings::gui().mControllerMenus) + addControllerButtons(list, mSelectedQuest); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); } @@ -554,6 +573,9 @@ namespace list->sort(); list->adjustSize(); + if (Settings::gui().mControllerMenus) + addControllerButtons(list, mSelectedQuest); + if (mAllQuests) { SetNamesInactive setInactive(list); @@ -642,64 +664,149 @@ namespace bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override { - if (arg.button == SDL_CONTROLLER_BUTTON_A) + if (arg.button == SDL_CONTROLLER_BUTTON_A) // A: Mouse click or Select { // Fall through to mouse click - return false; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_B) - { - if (mOptionsMode) - notifyCancel(getWidget(CancelBTN)); - else if (mStates.size() > 1) - notifyJournal(getWidget(JournalBTN)); - else - notifyClose(getWidget(CloseBTN)); - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_X) - { - if (mQuestMode) + if (mUsingGamepadGuiCursor) + return false; + + if (mOptionsMode && mQuestMode) { - if (!mOptionsMode) - notifyOptions(getWidget(OptionsBTN)); - notifyTopics(getWidget(TopicsBTN)); + // Choose a quest + Gui::MWList* list = getWidget(QuestsList); + notifyQuestClicked(list->getItemNameAt(mSelectedQuest), 0); + } + else if (mOptionsMode && mTopicsMode) + { + // Choose a topic + Gui::MWList* list = getWidget(TopicsList); + notifyTopicSelected(list->getItemNameAt(mSelectedQuest), 0); } return true; } - else if (arg.button == SDL_CONTROLLER_BUTTON_Y) + else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back { - if (!mQuestMode) + if (mOptionsMode) { + // Hide the options overlay + notifyCancel(getWidget(CancelBTN)); + mQuestMode = false; + } + else if (mStates.size() > 1) + { + // Pop the current book. If in quest mode, reopen the quest list. + notifyJournal(getWidget(JournalBTN)); + if (mQuestMode) + { + notifyOptions(getWidget(OptionsBTN)); + notifyQuests(getWidget(QuestsBTN)); + } + } + else + { + // Close the journal window + notifyClose(getWidget(CloseBTN)); + } + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Quests + { + if (mOptionsMode && mQuestMode) + { + // Hide the quest overlay if visible + notifyCancel(getWidget(CancelBTN)); + mQuestMode = false; + } + else { + // Show the quest overlay if viewing a journal entry or the topics if (!mOptionsMode) notifyOptions(getWidget(OptionsBTN)); - notifyQuests(getWidget(QuestsBTN)); + if (!mQuestMode) + notifyQuests(getWidget(QuestsBTN)); + } + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Topics + { + if (mOptionsMode && !mQuestMode) + { + // Hide the topics overlay if visible + notifyCancel(getWidget(CancelBTN)); + mQuestMode = false; + } + else { + // Show the topics overlay if viewing a journal entry or the quest list + if (!mOptionsMode) + notifyOptions(getWidget(OptionsBTN)); + if (mQuestMode) + notifyTopics(getWidget(TopicsBTN)); } return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { + if (mOptionsMode && (mQuestMode || mTopicsMode)) + { + // Scroll through the list of quests or topics + mButtons[mSelectedQuest]->setStateSelected(false); + mSelectedQuest--; + if (mSelectedQuest < 0) + mSelectedQuest = mButtons.size() - 1; + mButtons[mSelectedQuest]->setStateSelected(true); + } + mUsingGamepadGuiCursor = false; return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { + if (mOptionsMode && (mQuestMode || mTopicsMode)) + { + // Scroll through the list of quests or topics + mButtons[mSelectedQuest]->setStateSelected(false); + mSelectedQuest++; + if (mSelectedQuest > mButtons.size() - 1) + mSelectedQuest = 0; + mButtons[mSelectedQuest]->setStateSelected(true); + } + mUsingGamepadGuiCursor = false; return true; } - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { - notifyPrevPage(getWidget(PrevPageBTN)); + if (!mOptionsMode) + notifyPrevPage(getWidget(PrevPageBTN)); + mUsingGamepadGuiCursor = false; return true; } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { - notifyNextPage(getWidget(NextPageBTN)); + if (!mOptionsMode) + notifyNextPage(getWidget(NextPageBTN)); + mUsingGamepadGuiCursor = false; + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) // LB: Previous Page + { + if (!mOptionsMode) + notifyPrevPage(getWidget(PrevPageBTN)); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) // RB: Next Page + { + if (!mOptionsMode) + notifyNextPage(getWidget(NextPageBTN)); return true; } return false; } + + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override + { + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + mUsingGamepadGuiCursor = true; + return false; + } }; } diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 22e7048acf..5da2038575 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -31,6 +31,10 @@ namespace MWGui void setVisible(bool newValue) override = 0; std::string_view getWindowIdForLua() const override { return "Journal"; } + + std::vector mButtons; + int mSelectedQuest = 0; + bool mUsingGamepadGuiCursor = false; }; } From 9aff9cb48e728dd0f71223ea20f070b9b3ba58c3 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 20:31:15 -0700 Subject: [PATCH 082/455] Swap Topics and Quests buttons --- apps/openmw/mwgui/journalwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 3dc46459b4..907de6f515 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -709,7 +709,7 @@ namespace } return true; } - else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Quests + else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Quests { if (mOptionsMode && mQuestMode) { @@ -726,7 +726,7 @@ namespace } return true; } - else if (arg.button == SDL_CONTROLLER_BUTTON_X) // X: Topics + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) // Y: Topics { if (mOptionsMode && !mQuestMode) { From 9a72ecb61c2f045d9945cdf6e646c37c0a7e8754 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 20:32:59 -0700 Subject: [PATCH 083/455] Don't close a book with A --- apps/openmw/mwgui/bookwindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 91b1af615e..90d3c4143a 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -231,8 +231,6 @@ namespace MWGui if (mTakeButton->getVisible()) onTakeButtonClicked(mTakeButton); - else - onCloseButtonClicked(mCloseButton); return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_B) From 65bab3858a2570f66bd78719ac4469c19c444e88 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 22:36:28 -0700 Subject: [PATCH 084/455] Add basic controller support to scrolls --- apps/openmw/mwgui/scrollwindow.cpp | 43 ++++++++++++++++++++++++++++++ apps/openmw/mwgui/scrollwindow.hpp | 3 +++ 2 files changed, 46 insertions(+) diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 0b1658fd84..7c7ad338ce 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -7,6 +7,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" @@ -28,9 +29,11 @@ namespace MWGui getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); + trackFocusEvents(mCloseButton); getWidget(mTakeButton, "TakeButton"); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); + trackFocusEvents(mTakeButton); adjustButton("CloseButton"); adjustButton("TakeButton"); @@ -115,4 +118,44 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } + + void ScrollWindow::onClose() + { + if (Settings::gui().mControllerMenus) + MWBase::Environment::get().getInputManager()->setGamepadGuiCursorEnabled(true); + BookWindowBase::onClose(); + } + + bool ScrollWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mMouseFocus != nullptr) + return false; + + if (mTakeButton->getVisible()) + onTakeButtonClicked(mTakeButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCloseButtonClicked(mCloseButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + onKeyButtonPressed(nullptr, MyGUI::KeyCode::ArrowUp, 0); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + onKeyButtonPressed(nullptr, MyGUI::KeyCode::ArrowDown, 0); + + return true; + } + + bool ScrollWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTY) + { + MWBase::Environment::get().getInputManager()->setGamepadGuiCursorEnabled(false); + + int scroll = -30.0f * arg.value / 32767; + mTextView->setViewOffset(mTextView->getViewOffset() + MyGUI::IntPoint(0, scroll)); + return true; + } + return false; + } } diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 7daea98894..fa3ca5b25d 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -20,6 +20,7 @@ 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"; } @@ -29,6 +30,8 @@ namespace MWGui 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; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; private: Gui::ImageButton* mCloseButton; From 77e17743f7d216d3d5d6a8392e735b7deb3a655b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 12 May 2025 22:37:11 -0700 Subject: [PATCH 085/455] Only use bumpers to turn pages in books --- apps/openmw/mwgui/bookwindow.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 90d3c4143a..79cfb9fa88 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -231,31 +231,14 @@ namespace MWGui if (mTakeButton->getVisible()) onTakeButtonClicked(mTakeButton); - return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_B) - { onCloseButtonClicked(mCloseButton); - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - { - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) - { + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) prevPage(); - return true; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - { + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) nextPage(); - return true; - } - return false; + return true; } } From f055ccf5bac2525177492e749cead48e5f253aa1 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Wed, 14 May 2025 02:31:32 -0700 Subject: [PATCH 086/455] Update item view to allow controller support; test it in Container menu --- apps/openmw/mwgui/container.cpp | 80 ++++++++++++++++++- apps/openmw/mwgui/container.hpp | 5 ++ apps/openmw/mwgui/itemview.cpp | 71 ++++++++++++++-- apps/openmw/mwgui/itemview.hpp | 10 +++ apps/openmw/mwgui/itemwidget.cpp | 13 +++ apps/openmw/mwgui/itemwidget.hpp | 3 + files/data/CMakeLists.txt | 1 + files/data/mygui/openmw_resources.xml | 4 + files/data/textures/omw_menu_icon_active.dds | Bin 0 -> 5616 bytes 9 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 files/data/textures/omw_menu_icon_active.dds diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 6ab2c862d4..658eedfcd2 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" @@ -88,8 +90,13 @@ namespace MWGui name += MWGui::ToolTips::getSoulString(object.getCellRef()); dialog->openCountDialog(name, "#{sTake}", count); dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); + if (Settings::gui().mControllerMenus && !mUsingGamepadGuiCursor) + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::takeItem); + else + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } + else if (Settings::gui().mControllerMenus && !mUsingGamepadGuiCursor) + takeItem(nullptr, count); else dragItem(nullptr, count); } @@ -105,6 +112,30 @@ namespace MWGui mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } + void ContainerWindow::takeItem(MyGUI::Widget* sender, int count) + { + if (!mModel) + return; + + const ItemStack& item = mModel->getItem(mSelectedItem); + if (!onTakeItem(item, count)) + return; + + MWGui::InventoryWindow* inventoryWindow + = MWBase::Environment::get().getWindowManager()->getInventoryWindow(); + ItemModel* playerModel = inventoryWindow->getModel(); + + mModel->moveItem(item, count, playerModel); + + inventoryWindow->updateItemView(); + mItemView->update(); + + // play the item's sound + MWWorld::Ptr itemBase = item.mBase; + const ESM::RefId& sound = itemBase.getClass().getUpSoundId(itemBase); + MWBase::Environment::get().getWindowManager()->playSound(sound); + } + void ContainerWindow::dropItem() { if (!mModel) @@ -320,4 +351,51 @@ namespace MWGui if (mModel && mModel->usesContainer(ptr)) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + + bool ContainerWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mUsingGamepadGuiCursor) + return false; + + 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_LEFTSHOULDER) + { + if (mDisposeCorpseButton->getVisible()) + onDisposeCorpseButtonClicked(mDisposeCorpseButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + mItemView->onControllerButtonEvent(arg); + mUsingGamepadGuiCursor = false; + } + + return true; + } + + bool ContainerWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) + { + mUsingGamepadGuiCursor = true; + } + return false; + } } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 555fa8e1ae..5d49b4c3ff 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -55,6 +55,7 @@ namespace MWGui void onItemSelected(int index); void onBackgroundSelected(); void dragItem(MyGUI::Widget* sender, int count); + void takeItem(MyGUI::Widget* sender, int count); void dropItem(); void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); @@ -64,6 +65,10 @@ namespace MWGui bool onTakeItem(const ItemStack& item, int count); void onReferenceUnavailable() override; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + bool mUsingGamepadGuiCursor = false; }; } #endif // CONTAINER_H diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index ff05a8b2d6..e568da5900 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "itemmodel.hpp" #include "itemwidget.hpp" @@ -46,13 +48,14 @@ 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 = maxHeight / 42; + mRows = std::max(mRows, 1); + mItemCount = dragArea->getChildCount(); + bool showScrollbar = int(std::ceil(mItemCount / float(mRows))) > mScrollView->getWidth() / 42; if (showScrollbar) maxHeight -= 18; - for (unsigned int i = 0; i < dragArea->getChildCount(); ++i) + for (unsigned int i = 0; i < mItemCount; ++i) { MyGUI::Widget* w = dragArea->getChildAt(i); @@ -60,7 +63,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 +73,13 @@ namespace MWGui MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height); + if (Settings::gui().mControllerMenus) + { + if (mControllerFocus >= mItemCount) + mControllerFocus = 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 +132,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 +180,50 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } + void ItemView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (!mItemCount) + return; + + int prevFocus = mControllerFocus; + + if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP && mControllerFocus % mRows != 0) + mControllerFocus--; + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN && mControllerFocus % mRows != mRows - 1) + mControllerFocus++; + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows) + mControllerFocus -= mRows; + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mControllerFocus + mRows < mItemCount) + mControllerFocus += mRows; + + if (mControllerFocus < 0) + mControllerFocus = 0; + else if (mControllerFocus >= mItemCount - 1) + mControllerFocus = mItemCount - 1; + + if (prevFocus != mControllerFocus) + updateControllerFocus(prevFocus, mControllerFocus); + } + + void ItemView::updateControllerFocus(int _prevFocus, int _newFocus) + { + if (!mItemCount) + return; + + MyGUI::Widget* dragArea = mScrollView->getChildAt(0); + + if (_prevFocus >= 0 && _prevFocus < mItemCount) + { + ItemWidget* prev = (ItemWidget *)dragArea->getChildAt(_prevFocus); + if (prev) + prev->setControllerFocus(false); + } + + if (_newFocus >= 0 && _newFocus < mItemCount) + { + ItemWidget* focused = (ItemWidget *)dragArea->getChildAt(_newFocus); + if (focused) + focused->setControllerFocus(true); + } + } } diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index cfbc8a37ac..8a53d3678e 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -2,6 +2,7 @@ #define MWGUI_ITEMVIEW_H #include +#include #include "itemmodel.hpp" @@ -31,6 +32,10 @@ namespace MWGui void resetScrollBars(); + int getControllerFocus() { return mControllerFocus; } + int getItemCount() { return mItemCount; } + void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); + private: void initialiseOverride() override; @@ -45,6 +50,11 @@ namespace MWGui std::unique_ptr mModel; MyGUI::ScrollView* mScrollView; + + int mItemCount = 0; + int mRows; + int mControllerFocus = 0; + void updateControllerFocus(int prevFocus, int newFocus); }; } diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index 05fff2d40f..5e47577b27 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -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) diff --git a/apps/openmw/mwgui/itemwidget.hpp b/apps/openmw/mwgui/itemwidget.hpp index 63837ae92f..1191a23342 100644 --- a/apps/openmw/mwgui/itemwidget.hpp +++ b/apps/openmw/mwgui/itemwidget.hpp @@ -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; diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index d9218b45b2..0290519806 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -9,6 +9,7 @@ set(BUILTIN_DATA_FILES textures/omw_menu_scroll_right.dds textures/omw_menu_scroll_center_h.dds textures/omw_menu_scroll_center_v.dds + textures/omw_menu_icon_active.dds textures/omw/water_nm.png fonts/DejaVuFontLicense.txt diff --git a/files/data/mygui/openmw_resources.xml b/files/data/mygui/openmw_resources.xml index 4c32512004..08586ea75c 100644 --- a/files/data/mygui/openmw_resources.xml +++ b/files/data/mygui/openmw_resources.xml @@ -144,6 +144,10 @@ + + + + diff --git a/files/data/textures/omw_menu_icon_active.dds b/files/data/textures/omw_menu_icon_active.dds new file mode 100644 index 0000000000000000000000000000000000000000..be743ace7f84a7e9fa9a9dfa43097909bfec2ff1 GIT binary patch literal 5616 zcmeHLy-piJ5MG~;VCP3VMZy#yamA&K1Vy4qm%@NA$zqQak_g2V3BuM2l<)==79b?L z(-lY*yaS|gO-V_K^MJ^a`{1BN4gji01% z#K+`>J>b51)%pr%wwYm5fALlFWy(K>4#V5;4lXhA`J+DzB!7qagUDG7FU>!SZ+iXH z`?<#++W++?^a|JTodFn`AUc>f3GOGB6MX7crL|C@H-{15AaAekRy zyc$v_?QbsOC2@Y#s_s4_{G}zW9oz47GmU=%B)Y>` literal 0 HcmV?d00001 From 8c2ecc2a884dafe8ed59b59c89bbafb9fb012ef8 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 15 May 2025 00:46:02 -0700 Subject: [PATCH 087/455] Allow LT and RT to toggle between open gui windows --- apps/openmw/mwbase/windowmanager.hpp | 5 +- apps/openmw/mwgui/container.cpp | 6 ++ apps/openmw/mwgui/container.hpp | 2 + apps/openmw/mwgui/itemview.cpp | 8 +++ apps/openmw/mwgui/itemview.hpp | 1 + apps/openmw/mwgui/windowbase.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 70 ++++++++++++++++++++--- apps/openmw/mwgui/windowmanagerimp.hpp | 5 +- apps/openmw/mwinput/controllermanager.cpp | 17 +++++- 9 files changed, 103 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d2f1fc6514..e00837265f 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -382,7 +382,10 @@ namespace MWBase /// Same as viewer->getCamera()->getCullMask(), provided for consistency. virtual uint32_t getCullMask() = 0; - virtual MWGui::WindowBase* getTopWindow() = 0; + /// Return the window that should receive controller events + virtual MWGui::WindowBase* getActiveControllerWindow() = 0; + /// Cycle to the next window to receive controller events + virtual void cycleActiveControllerWindow(bool next) = 0; // Used in Lua bindings virtual const std::vector& getGuiModeStack() const = 0; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 658eedfcd2..c47000d753 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -398,4 +398,10 @@ namespace MWGui } return false; } + + void ContainerWindow::setActiveControllerWindow(bool active) + { + mItemView->setActiveControllerWindow(active); + WindowBase::setActiveControllerWindow(active); + } } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 5d49b4c3ff..f7f4ef3c31 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -40,6 +40,8 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Container"; } + void setActiveControllerWindow(bool active) override; + private: DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index e568da5900..526f231956 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -180,6 +180,14 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } + void ItemView::setActiveControllerWindow(bool active) + { + if (active) + updateControllerFocus(-1, mControllerFocus); + else + updateControllerFocus(mControllerFocus, -1); + } + void ItemView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (!mItemCount) diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 8a53d3678e..b1fa941ef4 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -32,6 +32,7 @@ namespace MWGui void resetScrollBars(); + void setActiveControllerWindow(bool active); int getControllerFocus() { return mControllerFocus; } int getItemCount() { return mItemCount; } void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 3db4399d85..fd1c743c62 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -62,11 +62,13 @@ namespace MWGui // REMOVEME // virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) = 0; // virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) = 0; + virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: virtual void onTitleDoubleClicked(); MyGUI::Widget* mMouseFocus = nullptr; + bool mActiveControllerWindow = false; void trackFocusEvents(MyGUI::Widget* widget); private: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index b328176014..d52a71a417 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -859,7 +859,7 @@ namespace MWGui mHud->setPlayerPos(x, y, u, v); } - WindowBase* WindowManager::getTopWindow() + WindowBase* WindowManager::getActiveControllerWindow() { if (!mCurrentModals.empty()) return mCurrentModals.back(); @@ -869,15 +869,18 @@ namespace MWGui if (!mGuiModes.empty()) { - GuiModeState& state = mGuiModeStates[mGuiModes.back()]; + GuiMode mode = mGuiModes.back(); + GuiModeState& state = mGuiModeStates[mode]; + int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); + // REMOVEME - Log(Debug::Error) << "getTopWindow: " << state.mWindows.size() << " windows in state " << mGuiModes.back(); - // find the topmost window - for (WindowBase* window : state.mWindows) - if (window->isVisible()) - return window; - else - Log(Debug::Error) << "-- Skipping hidden window " << window; + Log(Debug::Error) << "getActiveControllerWindow: " << state.mWindows.size() << " windows in state, mActiveControllerWindows[mode] = " << mActiveControllerWindows[mode]; + + // If the active window is no longer visible, find the next visible window. + if (!state.mWindows[activeIndex]->isVisible()) + cycleActiveControllerWindow(true); + + return state.mWindows[activeIndex]; } else { @@ -893,6 +896,48 @@ namespace MWGui return nullptr; } + void WindowManager::cycleActiveControllerWindow(bool next) + { + if (mGuiModes.empty()) + return; + + GuiMode mode = mGuiModes.back(); + int winCount = mGuiModeStates[mode].mWindows.size(); + + int activeIndex = 0; + if (winCount > 1) + { + // Find next/previous visible window + activeIndex = mActiveControllerWindows[mode]; + int delta = next ? 1 : -1; + + for (int i = 0; i < winCount; i++) + { + activeIndex += delta; + if (activeIndex < 0) + activeIndex = winCount - 1; + else if (activeIndex >= winCount) + activeIndex = 0; + + if (mGuiModeStates[mode].mWindows[activeIndex]->isVisible()) + break; + } + } + + // REMOVEME + Log(Debug::Error) << "focusNextWindow: mode=" << mode << ", activeIndex=" << activeIndex; + + if (mActiveControllerWindows[mode] != activeIndex) + { + mActiveControllerWindows[mode] = activeIndex; + for (int i = 0; i < winCount; i++) + { + mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); + } + playSound(ESM::RefId::stringRefId("Menu Click")); + } + } + void WindowManager::update(float frameDuration) { handleScheduledMessageBoxes(); @@ -1316,6 +1361,13 @@ namespace MWGui { for (WindowBase* window : mGuiModeStates[mode].mWindows) window->setPtr(arg); + + // Activate first visible window + mActiveControllerWindows[mode] = -1; + cycleActiveControllerWindow(true); + + // REMOVEME + Log(Debug::Error) << "pushGuiMode: mode=" << mode << ", activeIndex=" << mActiveControllerWindows[mode]; } catch (...) { diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index fd035ed12e..a1c01f6fa6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -387,7 +387,8 @@ namespace MWGui void asyncPrepareSaveMap() override; - WindowBase* getTopWindow() override; + WindowBase* getActiveControllerWindow() override; + void cycleActiveControllerWindow(bool next) override; // Used in Lua bindings const std::vector& getGuiModeStack() const override { return mGuiModes; } @@ -495,6 +496,8 @@ namespace MWGui std::map mGuiModeStates; // The currently active stack of GUI modes (top mode is the one we are in). std::vector mGuiModes; + // The active window for controller mode for each GUI mode. + std::map mActiveControllerWindows; std::unique_ptr mCursorManager; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 7aff8172ce..f12c9a31ce 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -243,7 +243,7 @@ namespace MWInput { if (Settings::gui().mControllerMenus) { - MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); + MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getActiveControllerWindow(); if (topWin && topWin->onControllerButtonEvent(arg)) return true; } @@ -311,7 +311,20 @@ namespace MWInput { if (Settings::gui().mControllerMenus) { - MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getTopWindow(); + // Left and right triggers toggle through open GUI windows. + if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + { + if (arg.value == 32767) // Treat like a button. + MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(true); + return true; + } + else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) + { + if (arg.value == 32767) // Treat like a button. + MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(false); + return true; + } + MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getActiveControllerWindow(); if (topWin && topWin->onControllerThumbstickEvent(arg)) return true; } From c0694d3c0edfb5e2e90406c291646f1d4e05c1ab Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 16 May 2025 21:41:28 -0700 Subject: [PATCH 088/455] Add overlay showing what controller buttons do --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/birth.cpp | 9 ++++ apps/openmw/mwgui/birth.hpp | 2 + apps/openmw/mwgui/bookwindow.cpp | 8 +++ apps/openmw/mwgui/bookwindow.hpp | 1 + apps/openmw/mwgui/class.cpp | 43 +++++++++++++++ apps/openmw/mwgui/class.hpp | 14 +++++ apps/openmw/mwgui/confirmationdialog.cpp | 5 ++ apps/openmw/mwgui/confirmationdialog.hpp | 2 + apps/openmw/mwgui/container.cpp | 8 +++ apps/openmw/mwgui/container.hpp | 1 + .../openmw/mwgui/controllerbuttonsoverlay.cpp | 26 ++++++++++ .../openmw/mwgui/controllerbuttonsoverlay.hpp | 20 +++++++ apps/openmw/mwgui/journalwindow.cpp | 17 ++++++ apps/openmw/mwgui/mainmenu.cpp | 5 ++ apps/openmw/mwgui/mainmenu.hpp | 2 + apps/openmw/mwgui/messagebox.cpp | 5 ++ apps/openmw/mwgui/messagebox.hpp | 1 + apps/openmw/mwgui/race.cpp | 9 ++++ apps/openmw/mwgui/race.hpp | 2 + apps/openmw/mwgui/review.cpp | 13 +++-- apps/openmw/mwgui/review.hpp | 2 + apps/openmw/mwgui/savegamedialog.cpp | 38 +++++++------- apps/openmw/mwgui/savegamedialog.hpp | 2 + apps/openmw/mwgui/scrollwindow.cpp | 8 +++ apps/openmw/mwgui/scrollwindow.hpp | 2 + apps/openmw/mwgui/settingswindow.cpp | 7 ++- apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwgui/textinput.cpp | 5 ++ apps/openmw/mwgui/textinput.hpp | 2 + apps/openmw/mwgui/waitdialog.cpp | 13 +++++ apps/openmw/mwgui/waitdialog.hpp | 2 + apps/openmw/mwgui/windowbase.cpp | 1 + apps/openmw/mwgui/windowbase.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 52 ++++++++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++ files/data/CMakeLists.txt | 1 + .../mygui/openmw_controllerbuttons.layout | 19 +++++++ files/data/mygui/openmw_layers.xml | 1 + 40 files changed, 330 insertions(+), 27 deletions(-) create mode 100644 apps/openmw/mwgui/controllerbuttonsoverlay.cpp create mode 100644 apps/openmw/mwgui/controllerbuttonsoverlay.hpp create mode 100644 files/data/mygui/openmw_controllerbuttons.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 37de0abeab..eeb46edce0 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 + postprocessorhud settings controllerbuttonsoverlay ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index e00837265f..5302270ee0 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -386,6 +386,7 @@ namespace MWBase virtual MWGui::WindowBase* getActiveControllerWindow() = 0; /// Cycle to the next window to receive controller events virtual void cycleActiveControllerWindow(bool next) = 0; + virtual void updateControllerButtonsOverlay() = 0; // Used in Lua bindings virtual const std::vector& getGuiModeStack() const = 0; diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index b121c1978b..bc6b37df50 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -273,6 +273,11 @@ namespace MWGui mSpellArea->setViewOffset(MyGUI::IntPoint(0, 0)); } + std::string BirthDialog::getButtonStr() + { + return "(A) #{sSelect} (X) #{sDone} (B) #{sBack}"; + } + bool BirthDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -290,6 +295,10 @@ namespace MWGui { 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(); diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 8a7190b934..841ff872b0 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -38,6 +38,8 @@ namespace MWGui */ EventHandle_WindowBase eventDone; + std::string getButtonStr() override; + protected: void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 79cfb9fa88..6f6a6187b9 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -222,6 +222,14 @@ namespace MWGui } } + std::string BookWindow::getButtonStr() + { + if (mTakeButton->getVisible()) + return "(A) #{sTake} (LB) #{sPrev} (RB) #{sNext} (B) #{sClose}"; + else + return "(LB) #{sPrev} (RB) #{sNext} (B) #{sClose}"; + } + bool BookWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 062c6e9dbd..f6b1fa7bc9 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -21,6 +21,7 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; std::string_view getWindowIdForLua() const override { return "Book"; } + std::string getButtonStr() override; protected: void onNextPageButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index a7b5b36aa5..7c9d7c724e 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -77,6 +77,11 @@ namespace MWGui center(); } + std::string GenerateClassResultDialog::getButtonStr() + { + return "(A) #{sSelect} (B) #{sBack}"; + } + bool GenerateClassResultDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -312,6 +317,11 @@ namespace MWGui setClassImage(mClassImage, mCurrentClassId); } + std::string PickClassDialog::getButtonStr() + { + return "(A) #{sSelect} (X) #{sDone} (B) #{sBack}"; + } + bool PickClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -329,6 +339,10 @@ namespace MWGui { 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(); @@ -476,6 +490,11 @@ namespace MWGui } } + std::string InfoBoxDialog::getButtonStr() + { + return "(A) #{sSelect}"; + } + bool InfoBoxDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -699,6 +718,11 @@ namespace MWGui MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); } + std::string CreateClassDialog::getButtonStr() + { + return "(A) #{sSelect} (X) #{sDone} (B) #{sCancel}"; + } + bool CreateClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -718,6 +742,10 @@ namespace MWGui { onBackClicked(mButtons[1]); } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + onOkClicked(mButtons[2]); + } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { @@ -942,6 +970,11 @@ namespace MWGui return true; } + std::string SelectSpecializationDialog::getButtonStr() + { + return "(A) #{sSelect} (B) #{sCancel}"; + } + bool SelectSpecializationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -1004,6 +1037,11 @@ namespace MWGui return true; } + std::string SelectAttributeDialog::getButtonStr() + { + return "(A) #{sSelect} (B) #{sCancel}"; + } + bool SelectAttributeDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -1078,6 +1116,11 @@ namespace MWGui return true; } + std::string SelectSkillDialog::getButtonStr() + { + return "(A) #{sSelect} (B) #{sCancel}"; + } + bool SelectSkillDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index e2566ce6c0..af18e5d660 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -40,6 +40,8 @@ namespace MWGui */ EventHandle_Int eventButtonSelected; + std::string getButtonStr() override; + protected: void onButtonClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; @@ -91,6 +93,8 @@ namespace MWGui */ EventHandle_WindowBase eventDone; + std::string getButtonStr() override; + protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); @@ -132,6 +136,8 @@ namespace MWGui */ EventHandle_WindowBase eventDone; + std::string getButtonStr() override; + protected: void onSelectClass(MyGUI::ListBox* _sender, size_t _index); void onAccept(MyGUI::ListBox* _sender, size_t _index); @@ -183,6 +189,8 @@ namespace MWGui */ EventHandle_Void eventItemSelected; + std::string getButtonStr() override; + protected: void onSpecializationClicked(MyGUI::Widget* _sender); void onCancelClicked(MyGUI::Widget* _sender); @@ -217,6 +225,8 @@ namespace MWGui */ EventHandle_Void eventItemSelected; + std::string getButtonStr() override; + protected: void onAttributeClicked(Widgets::MWAttributePtr _sender); void onCancelClicked(MyGUI::Widget* _sender); @@ -249,6 +259,8 @@ namespace MWGui */ EventHandle_Void eventItemSelected; + std::string getButtonStr() override; + protected: void onSkillClicked(Widgets::MWSkillPtr _sender); void onCancelClicked(MyGUI::Widget* _sender); @@ -310,6 +322,8 @@ namespace MWGui */ EventHandle_WindowBase eventDone; + std::string getButtonStr() override; + protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index b284db16b2..8fa4afab16 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -72,6 +72,11 @@ namespace MWGui eventOkClicked(); } + std::string ConfirmationDialog::getButtonStr() + { + return "(A) #{sOk} (B) #{sCancel}"; + } + bool ConfirmationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 9b26e3a3c9..2e2f7df0af 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -20,6 +20,8 @@ namespace MWGui EventHandle_Void eventOkClicked; EventHandle_Void eventCancelClicked; + std::string getButtonStr() override; + private: MyGUI::EditBox* mMessage; MyGUI::Button* mOkButton; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index c47000d753..05e56d10ee 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -352,6 +352,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + std::string ContainerWindow::getButtonStr() + { + if (mDisposeCorpseButton->getVisible()) + return "(A) #{sTake} (X) #{sTakeAll} (LB) #{sDisposeofCorpse} (Y) #{sInfo} [LT] #{sInventory} (B) #{sClose}"; + else + return "(A) #{sTake} (X) #{sTakeAll} (Y) #{sInfo} [LT] #{sInventory} (B) #{sClose}"; + } + bool ContainerWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index f7f4ef3c31..22f5f2425a 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -40,6 +40,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Container"; } + std::string getButtonStr() override; void setActiveControllerWindow(bool active) override; private: diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp new file mode 100644 index 0000000000..2e7ec3ffcd --- /dev/null +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -0,0 +1,26 @@ +#include "controllerbuttonsoverlay.hpp" + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +namespace MWGui +{ + ControllerButtonsOverlay::ControllerButtonsOverlay() + : WindowBase("openmw_controllerbuttons.layout") + { + getWidget(mButtonStr, "ButtonStr"); + } + + void ControllerButtonsOverlay::setButtonStr(const std::string& buttonStr) + { + mButtonStr->setCaptionWithReplacing(buttonStr); + + // int height = mMessage->getTextSize().height + 60; + // int width = mMessage->getTextSize().width + 24; + // mMainWidget->setSize(width, height); + // mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24); + } +} diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp new file mode 100644 index 0000000000..c2a9b110bf --- /dev/null +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -0,0 +1,20 @@ +#ifndef MWGUI_CONTROLLERBUTTONSOVERLAY_H +#define MWGUI_CONTROLLERBUTTONSOVERLAY_H + +#include "windowbase.hpp" + +namespace MWGui +{ + class ControllerButtonsOverlay : public WindowBase + { + public: + ControllerButtonsOverlay(); + + void setButtonStr(const std::string& buttonStr); + + private: + MyGUI::TextBox* mButtonStr; + }; +} + +#endif diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 907de6f515..029104d27c 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -309,6 +309,8 @@ namespace notifyQuests(getWidget(QuestsList)); else notifyTopics(getWidget(TopicsList)); + + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void pushBook(Book& book, unsigned int page) @@ -340,6 +342,7 @@ namespace { setVisible(CloseBTN, mStates.size() < 2); setVisible(JournalBTN, mStates.size() >= 2); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void updateShowingPages() @@ -382,6 +385,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) @@ -409,6 +414,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) @@ -441,6 +447,7 @@ namespace mOptionsMode = false; MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void notifyOptions(MyGUI::Widget* _sender) @@ -662,6 +669,16 @@ namespace } } + std::string getButtonStr() override + { + if (mOptionsMode) + return "(A) #{sSelect} (X) Quests (Y) #{sTopics} (B) #{sBack}"; + else if (mStates.size() > 1) + return "(A) #{sSelect} (LB) #{sPrev} (RB) #{sNext} (X) Quests (Y) #{sTopics} (B) #{sBack}"; + else + return "(A) #{sSelect} (LB) #{sPrev} (RB) #{sNext} (X) Quests (Y) #{sTopics} (B) #{sClose}"; + } + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override { if (arg.button == SDL_CONTROLLER_BUTTON_A) // A: Mouse click or Select diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index cfcb63e4d5..62740c030d 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -207,6 +207,11 @@ namespace MWGui } } + std::string MainMenu::getButtonStr() + { + return ""; + } + bool MainMenu::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { // REMOVEME diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 453a16b5e4..aa4cba4257 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -55,6 +55,8 @@ namespace MWGui bool exit() override; + std::string getButtonStr() override; + private: const VFS::Manager* mVFS; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index be0f284b04..00c49e44d0 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -439,6 +439,11 @@ namespace MWGui return mButtonPressed; } + std::string InteractiveMessageBox::getButtonStr() + { + return "InteractiveMessageBox (A) #{sOk}"; + } + bool InteractiveMessageBox::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index e6128ee0d1..3bc3127828 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -103,6 +103,7 @@ namespace MWGui bool mMarkedToDelete; + std::string getButtonStr() override; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 1ff5cc4784..c26d42f0f7 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -463,6 +463,11 @@ namespace MWGui } } + std::string RaceDialog::getButtonStr() + { + return "(A) #{sSelect} (Y) #{sSex} (LB) #{sHair} (RB) #{sFace} (X) #{sDone} (B) #{sBack}"; + } + bool RaceDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -481,6 +486,10 @@ namespace MWGui onBackClicked(mBackButton); } else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + onOkClicked(mOkButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) { onSelectNextGender(nullptr); } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index c3b322ba8b..b5eafb51f1 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -65,6 +65,8 @@ namespace MWGui */ EventHandle_WindowBase eventDone; + std::string getButtonStr() override; + protected: void onPreviewScroll(MyGUI::Widget* _sender, int _delta); void onHeadRotate(MyGUI::ScrollBar* _sender, size_t _position); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b113ace2be..40be524daf 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -537,6 +537,11 @@ namespace MWGui MyGUI::IntPoint(0, static_cast(mSkillView->getViewOffset().top + _rel * 0.3))); } + std::string ReviewDialog::getButtonStr() + { + return "(A) #{sSelect} (X) #{sDone} (B) #{sBack}"; + } + bool ReviewDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -567,14 +572,14 @@ namespace MWGui } return true; } - else if (arg.button == SDL_CONTROLLER_BUTTON_START) - { - onOkClicked(mButtons[5]); - } 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) { diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index fe53787fe3..e14bf834cd 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -62,6 +62,8 @@ namespace MWGui EventHandle_Int eventActivateDialog; + std::string getButtonStr() override; + protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 37ed137b23..9db28afc10 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -504,6 +504,14 @@ namespace MWGui mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); } + std::string SaveGameDialog::getButtonStr() + { + if (mSaving) + return "(A) #{sSelect} (B) #{sClose}"; + else + return "(A) #{sSelect} (Y) Select Character (B) #{sClose}"; + } + bool SaveGameDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -521,6 +529,16 @@ namespace MWGui { onCancelButtonClicked(mCancelButton); } + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) + { + uint32_t index = mCharacterSelection->getIndexSelected(); + if (index >= mCharacterSelection->getItemCount() - 1) + index = 0; + else + index++; + mCharacterSelection->setIndexSelected(index); + mUsingGamepadGuiCursor = false; + } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); @@ -543,26 +561,6 @@ namespace MWGui mCancelButton->setStateSelected(!mOkButtonFocus); mUsingGamepadGuiCursor = false; } - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) - { - uint32_t index = mCharacterSelection->getIndexSelected(); - if (index <= 0) - index = mCharacterSelection->getItemCount() - 1; - else - index--; - mCharacterSelection->setIndexSelected(index); - mUsingGamepadGuiCursor = false; - } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) - { - uint32_t index = mCharacterSelection->getIndexSelected(); - if (index >= mCharacterSelection->getItemCount() - 1) - index = 0; - else - index++; - mCharacterSelection->setIndexSelected(index); - mUsingGamepadGuiCursor = false; - } return true; } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 2561fc60f6..e9872a0d49 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,6 +24,8 @@ namespace MWGui void setLoadOrSave(bool load); + std::string getButtonStr() override; + private: void confirmDeleteSave(); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 7c7ad338ce..5b7ef4238a 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -126,6 +126,14 @@ namespace MWGui BookWindowBase::onClose(); } + std::string ScrollWindow::getButtonStr() + { + if (mTakeButton->getVisible()) + return "(A) #{sTake} (RS) #{sScrolldown} (B) #{sClose}"; + else + return "(A) #{sTake} (RS) #{sScrolldown} (B) #{sClose}"; + } + bool ScrollWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index fa3ca5b25d..4e447707d0 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -25,6 +25,8 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Scroll"; } + std::string getButtonStr() override; + protected: void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeButtonClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 7f8d98ee05..201204bc74 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -468,7 +468,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) @@ -1129,6 +1129,11 @@ namespace MWGui mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); } + std::string SettingsWindow::getButtonStr() + { + return "(A) #{sSelect} (LB) #{sPrev} Tab (RB) #{sNext} Tab (B) #{sOk}"; + } + bool SettingsWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 555468d806..e8d7f248cb 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -26,6 +26,7 @@ namespace MWGui void onResChange(int, int) override; + std::string getButtonStr() override; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; protected: diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 44e0defef6..e6352c928e 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -83,6 +83,11 @@ namespace MWGui mTextEdit->setCaption(text); } + std::string TextInputDialog::getButtonStr() + { + return "(A) #{sOk}"; + } + bool TextInputDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index ad7896ff27..67ca8ba757 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -24,6 +24,8 @@ namespace MWGui */ EventHandle_WindowBase eventDone; + std::string getButtonStr() override; + protected: void onOkClicked(MyGUI::Widget* _sender); void onTextAccepted(MyGUI::EditBox* _sender); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 5b92699982..9886f036e4 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -332,6 +332,19 @@ namespace MWGui } } + std::string WaitDialog::getButtonStr() + { + if (mSleeping) + { + if (mUntilHealedButton->getVisible()) + return "(X) #{sUntilHealed} (A) #{sRest} (B) #{sCancel}"; + else + return "(A) #{sRest} (B) #{sCancel}"; + } + else + return "(A) #{sWait} (B) #{sCancel}"; + } + bool WaitDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 0d6d2a82d2..77593c3049 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -47,6 +47,8 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "WaitDialog"; } + std::string getButtonStr() override; + protected: MyGUI::TextBox* mDateTimeText; MyGUI::TextBox* mRestText; diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index d01822f704..a3d1b27fb4 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -147,6 +147,7 @@ void WindowModal::onOpen() void WindowModal::onClose() { MWBase::Environment::get().getWindowManager()->removeCurrentModal(this); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index fd1c743c62..cd4d2874ca 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -62,6 +62,7 @@ namespace MWGui // REMOVEME // virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) = 0; // virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) = 0; + virtual std::string getButtonStr() { return ""; } virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d52a71a417..b9df5b0b4e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -181,6 +181,7 @@ namespace MWGui , mPostProcessorHud(nullptr) , mJailScreen(nullptr) , mContainerWindow(nullptr) + , mControllerButtonsOverlay(nullptr) , mTranslationDataStorage(translationDataStorage) , mInputBlocker(nullptr) , mHudEnabled(true) @@ -505,6 +506,10 @@ namespace MWGui mWindows.push_back(std::move(postProcessorHud)); trackWindow(mPostProcessorHud, makePostprocessorWindowSettingValues()); + auto controllerButtonsOverlay = std::make_unique(); + mControllerButtonsOverlay = controllerButtonsOverlay.get(); + mWindows.push_back(std::move(controllerButtonsOverlay)); + mInputBlocker = MyGUI::Gui::getInstance().createWidget( {}, 0, 0, w, h, MyGUI::Align::Stretch, "InputBlocker"); @@ -662,6 +667,9 @@ namespace MWGui && !(mForceHidden & GW_Inventory) && (mAllowed & GW_Inventory)); mSpellWindow->setVisible( mSpellWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Magic) && (mAllowed & GW_Magic)); + + if (Settings::gui().mControllerMenus && mControllerButtonsOverlay) + mControllerButtonsOverlay->setVisible(false); return; } else if (getMode() != GM_Inventory) @@ -691,6 +699,8 @@ namespace MWGui mStatsWindow->setVisible(eff & GW_Stats); } + updateControllerButtonsOverlay(); + switch (mode) { // FIXME: refactor chargen windows to use modes properly (or not use them at all) @@ -720,6 +730,7 @@ namespace MWGui return; dialog->setVisible(false); mGarbageDialogs.push_back(std::move(dialog)); + updateControllerButtonsOverlay(); } void WindowManager::exitCurrentGuiMode() @@ -871,10 +882,13 @@ namespace MWGui { GuiMode mode = mGuiModes.back(); GuiModeState& state = mGuiModeStates[mode]; + if (state.mWindows.size() == 0) + return nullptr; + int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); // REMOVEME - Log(Debug::Error) << "getActiveControllerWindow: " << state.mWindows.size() << " windows in state, mActiveControllerWindows[mode] = " << mActiveControllerWindows[mode]; + Log(Debug::Error) << "getActiveControllerWindow: " << state.mWindows.size() << " windows in state, mActiveControllerWindows[mode] = " << mActiveControllerWindows[mode] << ", activeIndex=" << activeIndex; // If the active window is no longer visible, find the next visible window. if (!state.mWindows[activeIndex]->isVisible()) @@ -1001,6 +1015,9 @@ namespace MWGui if (isSettingsWindowVisible()) mSettingsWindow->onFrame(frameDuration); + if (mControllerButtonsOverlay && mControllerButtonsOverlay->isVisible()) + mControllerButtonsOverlay->onFrame(frameDuration); + if (!gameRunning) return; @@ -2046,6 +2063,7 @@ namespace MWGui if (!window->exit()) return; window->setVisible(false); + updateControllerButtonsOverlay(); } } @@ -2059,6 +2077,8 @@ namespace MWGui mKeyboardNavigation->setModalWindow(input->mMainWidget); mKeyboardNavigation->setDefaultFocus(input->mMainWidget, input->getDefaultKeyFocus()); + + updateControllerButtonsOverlay(); } void WindowManager::removeCurrentModal(WindowModal* input) @@ -2526,4 +2546,34 @@ namespace MWGui } return res; } + + void WindowManager::updateControllerButtonsOverlay() + { + if (!Settings::gui().mControllerMenus ||!mControllerButtonsOverlay) + return; + + WindowBase* topWin = this->getActiveControllerWindow(); + if (!topWin || !topWin->isVisible()) + { + // REMOVEME + Log(Debug::Error) << "WindowManager::updateControllerButtonsOverlay: hiding overlay"; + mControllerButtonsOverlay->setVisible(false); + return; + } + + std::string buttonStr = topWin->getButtonStr(); + // REMOVEME + Log(Debug::Error) << "WindowManager::updateControllerButtonsOverlay: showing overlay: " << buttonStr; + if (buttonStr.length() > 0) + { + mControllerButtonsOverlay->setButtonStr(buttonStr); + mControllerButtonsOverlay->setVisible(true); + } + else + { + // REMOVEME + Log(Debug::Error) << "WindowManager::updateControllerButtonsOverlay: ...psych, hiding it"; + mControllerButtonsOverlay->setVisible(false); + } + } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a1c01f6fa6..5dbf3c4689 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -25,6 +25,7 @@ #include #include "charactercreation.hpp" +#include "controllerbuttonsoverlay.hpp" #include "draganddrop.hpp" #include "mapwindow.hpp" #include "messagebox.hpp" @@ -118,6 +119,7 @@ namespace MWGui class PostProcessorHud; class JailScreen; class KeyboardNavigation; + class ControllerButtonsOverlay; class WindowManager : public MWBase::WindowManager { @@ -389,6 +391,7 @@ namespace MWGui WindowBase* getActiveControllerWindow() override; void cycleActiveControllerWindow(bool next) override; + void updateControllerButtonsOverlay() override; // Used in Lua bindings const std::vector& getGuiModeStack() const override { return mGuiModes; } @@ -457,6 +460,7 @@ namespace MWGui PostProcessorHud* mPostProcessorHud; JailScreen* mJailScreen; ContainerWindow* mContainerWindow; + ControllerButtonsOverlay* mControllerButtonsOverlay; std::vector> mWindows; diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index 0290519806..97b49d1002 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -147,6 +147,7 @@ set(BUILTIN_DATA_FILES mygui/openmw_console.layout mygui/openmw_console.skin.xml mygui/openmw_container_window.layout + mygui/openmw_controllerbuttons.layout mygui/openmw_count_window.layout mygui/openmw_dialogue_window.layout mygui/openmw_dialogue_window.skin.xml diff --git a/files/data/mygui/openmw_controllerbuttons.layout b/files/data/mygui/openmw_controllerbuttons.layout new file mode 100644 index 0000000000..d12785f0fc --- /dev/null +++ b/files/data/mygui/openmw_controllerbuttons.layout @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/files/data/mygui/openmw_layers.xml b/files/data/mygui/openmw_layers.xml index 459db3fcb9..597ba0de4c 100644 --- a/files/data/mygui/openmw_layers.xml +++ b/files/data/mygui/openmw_layers.xml @@ -20,6 +20,7 @@ + From e01291ec4a7c8ef9fa77c5e439083776dec55251 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 17 May 2025 22:32:03 -0700 Subject: [PATCH 089/455] Add whole set of button icons --- apps/openmw/mwgui/birth.cpp | 10 +- apps/openmw/mwgui/birth.hpp | 2 - apps/openmw/mwgui/bookwindow.cpp | 12 +- apps/openmw/mwgui/bookwindow.hpp | 2 +- apps/openmw/mwgui/class.cpp | 58 +++--- apps/openmw/mwgui/class.hpp | 14 -- apps/openmw/mwgui/confirmationdialog.cpp | 7 +- apps/openmw/mwgui/confirmationdialog.hpp | 2 - apps/openmw/mwgui/container.cpp | 14 +- apps/openmw/mwgui/container.hpp | 2 +- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 95 ++++++++- .../openmw/mwgui/controllerbuttonsoverlay.hpp | 53 ++++- apps/openmw/mwgui/journalwindow.cpp | 16 +- apps/openmw/mwgui/mainmenu.cpp | 5 - apps/openmw/mwgui/mainmenu.hpp | 2 - apps/openmw/mwgui/messagebox.cpp | 12 +- apps/openmw/mwgui/messagebox.hpp | 1 - apps/openmw/mwgui/race.cpp | 13 +- apps/openmw/mwgui/race.hpp | 2 - apps/openmw/mwgui/review.cpp | 10 +- apps/openmw/mwgui/review.hpp | 2 - apps/openmw/mwgui/savegamedialog.cpp | 10 +- apps/openmw/mwgui/savegamedialog.hpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 11 +- apps/openmw/mwgui/scrollwindow.hpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 10 +- apps/openmw/mwgui/settingswindow.hpp | 1 - apps/openmw/mwgui/textinput.cpp | 7 +- apps/openmw/mwgui/textinput.hpp | 2 - apps/openmw/mwgui/waitdialog.cpp | 16 +- apps/openmw/mwgui/waitdialog.hpp | 2 +- apps/openmw/mwgui/windowbase.hpp | 21 +- apps/openmw/mwgui/windowmanagerimp.cpp | 16 +- files/data/CMakeLists.txt | 14 ++ .../mygui/openmw_controllerbuttons.layout | 187 +++++++++++++++++- files/data/textures/omw_steam_button_a.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_b.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_l1.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_l2.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_l3.dds | Bin 0 -> 22000 bytes .../data/textures/omw_steam_button_lstick.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_menu.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_r1.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_r2.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_r3.dds | Bin 0 -> 22000 bytes .../data/textures/omw_steam_button_rstick.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_view.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_x.dds | Bin 0 -> 22000 bytes files/data/textures/omw_steam_button_y.dds | Bin 0 -> 22000 bytes 49 files changed, 457 insertions(+), 178 deletions(-) create mode 100644 files/data/textures/omw_steam_button_a.dds create mode 100644 files/data/textures/omw_steam_button_b.dds create mode 100644 files/data/textures/omw_steam_button_l1.dds create mode 100644 files/data/textures/omw_steam_button_l2.dds create mode 100644 files/data/textures/omw_steam_button_l3.dds create mode 100644 files/data/textures/omw_steam_button_lstick.dds create mode 100644 files/data/textures/omw_steam_button_menu.dds create mode 100644 files/data/textures/omw_steam_button_r1.dds create mode 100644 files/data/textures/omw_steam_button_r2.dds create mode 100644 files/data/textures/omw_steam_button_r3.dds create mode 100644 files/data/textures/omw_steam_button_rstick.dds create mode 100644 files/data/textures/omw_steam_button_view.dds create mode 100644 files/data/textures/omw_steam_button_x.dds create mode 100644 files/data/textures/omw_steam_button_y.dds diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index bc6b37df50..53f0f9793b 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -59,7 +59,12 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); if (Settings::gui().mControllerMenus) + { mOkButton->setStateSelected(true); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sDone}"; + } updateBirths(); updateSpells(); @@ -273,11 +278,6 @@ namespace MWGui mSpellArea->setViewOffset(MyGUI::IntPoint(0, 0)); } - std::string BirthDialog::getButtonStr() - { - return "(A) #{sSelect} (X) #{sDone} (B) #{sBack}"; - } - bool BirthDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 841ff872b0..8a7190b934 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -38,8 +38,6 @@ namespace MWGui */ EventHandle_WindowBase eventDone; - std::string getButtonStr() override; - protected: void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 6f6a6187b9..c48493c1b4 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -70,6 +70,10 @@ namespace MWGui MyGUI::IntCoord(0, 0, (64 - 7) * scale, mNextPageButton->getSize().height * scale)); } + mControllerButtons.l1 = "#{sPrev}"; + mControllerButtons.r1 = "#{sNext}"; + mControllerButtons.b = "#{sClose}"; + center(); } @@ -222,12 +226,10 @@ namespace MWGui } } - std::string BookWindow::getButtonStr() + ControllerButtonStr* BookWindow::getControllerButtons() { - if (mTakeButton->getVisible()) - return "(A) #{sTake} (LB) #{sPrev} (RB) #{sNext} (B) #{sClose}"; - else - return "(LB) #{sPrev} (RB) #{sNext} (B) #{sClose}"; + mControllerButtons.a = mTakeButton->getVisible() ? "#{sTake}" : ""; + return &mControllerButtons; } bool BookWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index f6b1fa7bc9..0a1beb7342 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -21,7 +21,7 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; std::string_view getWindowIdForLua() const override { return "Book"; } - std::string getButtonStr() override; + ControllerButtonStr* getControllerButtons() override; protected: void onNextPageButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 7c9d7c724e..0ac39e4bac 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -60,6 +60,8 @@ namespace MWGui mOkButton->setStateSelected(true); trackFocusEvents(mBackButton); trackFocusEvents(mOkButton); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; } center(); @@ -77,11 +79,6 @@ namespace MWGui center(); } - std::string GenerateClassResultDialog::getButtonStr() - { - return "(A) #{sSelect} (B) #{sBack}"; - } - bool GenerateClassResultDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -155,7 +152,12 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); if (Settings::gui().mControllerMenus) + { mOkButton->setStateSelected(true); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sDone}"; + } updateClasses(); updateStats(); @@ -317,11 +319,6 @@ namespace MWGui setClassImage(mClassImage, mCurrentClassId); } - std::string PickClassDialog::getButtonStr() - { - return "(A) #{sSelect} (X) #{sDone} (B) #{sBack}"; - } - bool PickClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -419,6 +416,8 @@ namespace MWGui getWidget(mButtonBar, "ButtonBar"); center(); + + mControllerButtons.a = "#{sSelect}"; } void InfoBoxDialog::setText(const std::string& str) @@ -490,11 +489,6 @@ namespace MWGui } } - std::string InfoBoxDialog::getButtonStr() - { - return "(A) #{sSelect}"; - } - bool InfoBoxDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -623,7 +617,12 @@ namespace MWGui mButtons.push_back(okButton); if (Settings::gui().mControllerMenus) + { okButton->setStateSelected(true); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sDone}"; + } // Set default skills, attributes @@ -718,11 +717,6 @@ namespace MWGui MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); } - std::string CreateClassDialog::getButtonStr() - { - return "(A) #{sSelect} (X) #{sDone} (B) #{sCancel}"; - } - bool CreateClassDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -939,6 +933,9 @@ namespace MWGui MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; } SelectSpecializationDialog::~SelectSpecializationDialog() {} @@ -970,11 +967,6 @@ namespace MWGui return true; } - std::string SelectSpecializationDialog::getButtonStr() - { - return "(A) #{sSelect} (B) #{sCancel}"; - } - bool SelectSpecializationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -1016,6 +1008,9 @@ namespace MWGui MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; } // widget controls @@ -1037,11 +1032,6 @@ namespace MWGui return true; } - std::string SelectAttributeDialog::getButtonStr() - { - return "(A) #{sSelect} (B) #{sCancel}"; - } - bool SelectAttributeDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -1093,6 +1083,9 @@ namespace MWGui MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; } SelectSkillDialog::~SelectSkillDialog() {} @@ -1116,11 +1109,6 @@ namespace MWGui return true; } - std::string SelectSkillDialog::getButtonStr() - { - return "(A) #{sSelect} (B) #{sCancel}"; - } - bool SelectSkillDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index af18e5d660..e2566ce6c0 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -40,8 +40,6 @@ namespace MWGui */ EventHandle_Int eventButtonSelected; - std::string getButtonStr() override; - protected: void onButtonClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; @@ -93,8 +91,6 @@ namespace MWGui */ EventHandle_WindowBase eventDone; - std::string getButtonStr() override; - protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); @@ -136,8 +132,6 @@ namespace MWGui */ EventHandle_WindowBase eventDone; - std::string getButtonStr() override; - protected: void onSelectClass(MyGUI::ListBox* _sender, size_t _index); void onAccept(MyGUI::ListBox* _sender, size_t _index); @@ -189,8 +183,6 @@ namespace MWGui */ EventHandle_Void eventItemSelected; - std::string getButtonStr() override; - protected: void onSpecializationClicked(MyGUI::Widget* _sender); void onCancelClicked(MyGUI::Widget* _sender); @@ -225,8 +217,6 @@ namespace MWGui */ EventHandle_Void eventItemSelected; - std::string getButtonStr() override; - protected: void onAttributeClicked(Widgets::MWAttributePtr _sender); void onCancelClicked(MyGUI::Widget* _sender); @@ -259,8 +249,6 @@ namespace MWGui */ EventHandle_Void eventItemSelected; - std::string getButtonStr() override; - protected: void onSkillClicked(Widgets::MWSkillPtr _sender); void onCancelClicked(MyGUI::Widget* _sender); @@ -322,8 +310,6 @@ namespace MWGui */ EventHandle_WindowBase eventDone; - std::string getButtonStr() override; - protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 8fa4afab16..1d8f14176a 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -24,6 +24,8 @@ namespace MWGui { trackFocusEvents(mOkButton); trackFocusEvents(mCancelButton); + mControllerButtons.a = "#{sOk}"; + mControllerButtons.b = "#{sCancel}"; } } @@ -72,11 +74,6 @@ namespace MWGui eventOkClicked(); } - std::string ConfirmationDialog::getButtonStr() - { - return "(A) #{sOk} (B) #{sCancel}"; - } - bool ConfirmationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 2e2f7df0af..9b26e3a3c9 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -20,8 +20,6 @@ namespace MWGui EventHandle_Void eventOkClicked; EventHandle_Void eventCancelClicked; - std::string getButtonStr() override; - private: MyGUI::EditBox* mMessage; MyGUI::Button* mOkButton; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 05e56d10ee..615c4801d3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -56,6 +56,12 @@ namespace MWGui mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); setCoord(200, 0, 600, 300); + + mControllerButtons.a = "#{sTake}"; + mControllerButtons.b = "#{sClose}"; + mControllerButtons.x = "#{sTakeAll}"; + mControllerButtons.y = "#{sInfo}"; + mControllerButtons.l2 = "#{sInventory}"; } void ContainerWindow::onItemSelected(int index) @@ -352,12 +358,10 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } - std::string ContainerWindow::getButtonStr() + ControllerButtonStr* ContainerWindow::getControllerButtons() { - if (mDisposeCorpseButton->getVisible()) - return "(A) #{sTake} (X) #{sTakeAll} (LB) #{sDisposeofCorpse} (Y) #{sInfo} [LT] #{sInventory} (B) #{sClose}"; - else - return "(A) #{sTake} (X) #{sTakeAll} (Y) #{sInfo} [LT] #{sInventory} (B) #{sClose}"; + mControllerButtons.l1 = mDisposeCorpseButton->getVisible() ? "#{sDisposeofCorpse}" : ""; + return &mControllerButtons; } bool ContainerWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 22f5f2425a..d2d8c9f4aa 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -40,7 +40,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Container"; } - std::string getButtonStr() override; + ControllerButtonStr* getControllerButtons() override; void setActiveControllerWindow(bool active) override; private: diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index 2e7ec3ffcd..f4b35ac4de 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -1,8 +1,5 @@ #include "controllerbuttonsoverlay.hpp" -#include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -11,16 +8,94 @@ namespace MWGui ControllerButtonsOverlay::ControllerButtonsOverlay() : WindowBase("openmw_controllerbuttons.layout") { - getWidget(mButtonStr, "ButtonStr"); + getWidget(mImageA, "BtnAImage"); + getWidget(mTextA, "BtnAText"); + + getWidget(mImageB, "BtnBImage"); + getWidget(mTextB, "BtnBText"); + + getWidget(mImageL1, "BtnL1Image"); + getWidget(mTextL1, "BtnL1Text"); + + getWidget(mImageL2, "BtnL2Image"); + getWidget(mTextL2, "BtnL2Text"); + + getWidget(mImageL3, "BtnL3Image"); + getWidget(mTextL3, "BtnL3Text"); + + getWidget(mImageLStick, "BtnLStickImage"); + getWidget(mTextLStick, "BtnLStickText"); + + getWidget(mImageMenu, "BtnMenuImage"); + getWidget(mTextMenu, "BtnMenuText"); + + getWidget(mImageR1, "BtnR1Image"); + getWidget(mTextR1, "BtnR1Text"); + + getWidget(mImageR2, "BtnR2Image"); + getWidget(mTextR2, "BtnR2Text"); + + getWidget(mImageR3, "BtnR3Image"); + getWidget(mTextR3, "BtnR3Text"); + + getWidget(mImageRStick, "BtnRStickImage"); + getWidget(mTextRStick, "BtnRStickText"); + + getWidget(mImageView, "BtnViewImage"); + getWidget(mTextView, "BtnViewText"); + + getWidget(mImageX, "BtnXImage"); + getWidget(mTextX, "BtnXText"); + + getWidget(mImageY, "BtnYImage"); + getWidget(mTextY, "BtnYText"); + + getWidget(mHBox, "ButtonBox"); } - void ControllerButtonsOverlay::setButtonStr(const std::string& buttonStr) + void ControllerButtonsOverlay::setButtons(ControllerButtonStr* buttons) { - mButtonStr->setCaptionWithReplacing(buttonStr); + int buttonCount = 0; + buttonCount += updateButton(mTextA, mImageA, buttons->a); + buttonCount += updateButton(mTextB, mImageB, buttons->b); + buttonCount += updateButton(mTextL1, mImageL1, buttons->l1); + buttonCount += updateButton(mTextL2, mImageL2, buttons->l2); + buttonCount += updateButton(mTextL3, mImageL3, buttons->l3); + buttonCount += updateButton(mTextLStick, mImageLStick, buttons->lStick); + buttonCount += updateButton(mTextMenu, mImageMenu, buttons->menu); + buttonCount += updateButton(mTextR1, mImageR1, buttons->r1); + buttonCount += updateButton(mTextR2, mImageR2, buttons->r2); + buttonCount += updateButton(mTextR3, mImageR3, buttons->r3); + buttonCount += updateButton(mTextRStick, mImageRStick, buttons->rStick); + buttonCount += updateButton(mTextView, mImageView, buttons->view); + buttonCount += updateButton(mTextX, mImageX, buttons->x); + buttonCount += updateButton(mTextY, mImageY, buttons->y); + mHBox->notifyChildrenSizeChanged(); - // int height = mMessage->getTextSize().height + 60; - // int width = mMessage->getTextSize().width + 24; - // mMainWidget->setSize(width, height); - // mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24); + setVisible(buttonCount > 0); + } + + int ControllerButtonsOverlay::updateButton(MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr) + { + if (buttonStr.length() > 0) + { + image->setVisible(true); + image->setUserString("Hidden", "false"); + + text->setCaptionWithReplacing(buttonStr); + text->setVisible(true); + text->setUserString("Hidden", "false"); + text->setSize(text->getTextSize().width + 16, 48); + return 1; + } + else + { + image->setVisible(false); + image->setUserString("Hidden", "true"); + + text->setVisible(false); + text->setUserString("Hidden", "true"); + return 0; + } } } diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp index c2a9b110bf..7663bbed44 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.hpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.hpp @@ -1,6 +1,11 @@ #ifndef MWGUI_CONTROLLERBUTTONSOVERLAY_H #define MWGUI_CONTROLLERBUTTONSOVERLAY_H +#include +#include + +#include + #include "windowbase.hpp" namespace MWGui @@ -10,10 +15,54 @@ namespace MWGui public: ControllerButtonsOverlay(); - void setButtonStr(const std::string& buttonStr); + void setButtons(ControllerButtonStr* buttons); private: - MyGUI::TextBox* mButtonStr; + MyGUI::ImageBox* mImageA; + MyGUI::TextBox* mTextA; + + MyGUI::ImageBox* mImageB; + MyGUI::TextBox* mTextB; + + MyGUI::ImageBox* mImageL1; + MyGUI::TextBox* mTextL1; + + MyGUI::ImageBox* mImageL2; + MyGUI::TextBox* mTextL2; + + MyGUI::ImageBox* mImageL3; + MyGUI::TextBox* mTextL3; + + MyGUI::ImageBox* mImageLStick; + MyGUI::TextBox* mTextLStick; + + MyGUI::ImageBox* mImageMenu; + MyGUI::TextBox* mTextMenu; + + MyGUI::ImageBox* mImageR1; + MyGUI::TextBox* mTextR1; + + MyGUI::ImageBox* mImageR2; + MyGUI::TextBox* mTextR2; + + MyGUI::ImageBox* mImageR3; + MyGUI::TextBox* mTextR3; + + MyGUI::ImageBox* mImageRStick; + MyGUI::TextBox* mTextRStick; + + MyGUI::ImageBox* mImageView; + MyGUI::TextBox* mTextView; + + MyGUI::ImageBox* mImageX; + MyGUI::TextBox* mTextX; + + MyGUI::ImageBox* mImageY; + MyGUI::TextBox* mTextY; + + Gui::HBox* mHBox; + + int updateButton(MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr); }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 029104d27c..abb1843178 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -218,6 +218,10 @@ namespace } } + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.x = "Quests"; + mControllerButtons.y = "#{sTopics}"; + mQuestMode = false; mAllQuests = false; mOptionsMode = false; @@ -669,14 +673,12 @@ namespace } } - std::string getButtonStr() override + MWGui::ControllerButtonStr* getControllerButtons() override { - if (mOptionsMode) - return "(A) #{sSelect} (X) Quests (Y) #{sTopics} (B) #{sBack}"; - else if (mStates.size() > 1) - return "(A) #{sSelect} (LB) #{sPrev} (RB) #{sNext} (X) Quests (Y) #{sTopics} (B) #{sBack}"; - else - return "(A) #{sSelect} (LB) #{sPrev} (RB) #{sNext} (X) Quests (Y) #{sTopics} (B) #{sClose}"; + mControllerButtons.b = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{sClose}"; + mControllerButtons.l1 = mOptionsMode ? "" : "#{sPrev}"; + mControllerButtons.r1 = mOptionsMode ? "" : "#{sNext}"; + return &mControllerButtons; } bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 62740c030d..cfcb63e4d5 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -207,11 +207,6 @@ namespace MWGui } } - std::string MainMenu::getButtonStr() - { - return ""; - } - bool MainMenu::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { // REMOVEME diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index aa4cba4257..453a16b5e4 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -55,8 +55,6 @@ namespace MWGui bool exit() override; - std::string getButtonStr() override; - private: const VFS::Manager* mVFS; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 00c49e44d0..8c5a968c99 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -282,10 +282,13 @@ namespace MWGui } } - if (Settings::gui().mControllerMenus && mButtons.size() > 1) + if (Settings::gui().mControllerMenus) { + mControllerButtons.a = "#{sOk}"; + // If we have more than one button, we need to set the focus to the first one. - mButtons[0]->setStateSelected(true); + if (mButtons.size() > 1) + mButtons[0]->setStateSelected(true); } MyGUI::IntSize mainWidgetSize; @@ -439,11 +442,6 @@ namespace MWGui return mButtonPressed; } - std::string InteractiveMessageBox::getButtonStr() - { - return "InteractiveMessageBox (A) #{sOk}"; - } - bool InteractiveMessageBox::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 3bc3127828..e6128ee0d1 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -103,7 +103,6 @@ namespace MWGui bool mMarkedToDelete; - std::string getButtonStr() override; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; private: diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index c26d42f0f7..9b97dfbb53 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -117,7 +117,15 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); if (Settings::gui().mControllerMenus) + { mOkButton->setStateSelected(true); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sDone}"; + mControllerButtons.y = "#{sSex}"; + mControllerButtons.l1 = "#{sHair}"; + mControllerButtons.r1 = "#{sFace}"; + } updateRaces(); updateSkills(); @@ -463,11 +471,6 @@ namespace MWGui } } - std::string RaceDialog::getButtonStr() - { - return "(A) #{sSelect} (Y) #{sSex} (LB) #{sHair} (RB) #{sFace} (X) #{sDone} (B) #{sBack}"; - } - bool RaceDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index b5eafb51f1..c3b322ba8b 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -65,8 +65,6 @@ namespace MWGui */ EventHandle_WindowBase eventDone; - std::string getButtonStr() override; - protected: void onPreviewScroll(MyGUI::Widget* _sender, int _delta); void onHeadRotate(MyGUI::ScrollBar* _sender, size_t _position); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 40be524daf..4d0d9d4d2c 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -126,7 +126,12 @@ namespace MWGui mButtons.push_back(okButton); if (Settings::gui().mControllerMenus) + { mButtons[mControllerFocus]->setStateSelected(true); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sDone}"; + } } void ReviewDialog::onOpen() @@ -537,11 +542,6 @@ namespace MWGui MyGUI::IntPoint(0, static_cast(mSkillView->getViewOffset().top + _rel * 0.3))); } - std::string ReviewDialog::getButtonStr() - { - return "(A) #{sSelect} (X) #{sDone} (B) #{sBack}"; - } - bool ReviewDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index e14bf834cd..fe53787fe3 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -62,8 +62,6 @@ namespace MWGui EventHandle_Int eventActivateDialog; - std::string getButtonStr() override; - protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 9db28afc10..568c12d00d 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -66,6 +66,8 @@ namespace MWGui trackFocusEvents(mCancelButton); trackFocusEvents(mDeleteButton); + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sClose}"; } void SaveGameDialog::onSlotActivated(MyGUI::ListBox* sender, size_t pos) @@ -504,12 +506,10 @@ namespace MWGui mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); } - std::string SaveGameDialog::getButtonStr() + ControllerButtonStr* SaveGameDialog::getControllerButtons() { - if (mSaving) - return "(A) #{sSelect} (B) #{sClose}"; - else - return "(A) #{sSelect} (Y) Select Character (B) #{sClose}"; + mControllerButtons.y = mSaving ? "" : "Select Character"; + return &mControllerButtons; } bool SaveGameDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index e9872a0d49..fbe319aed1 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,7 +24,7 @@ namespace MWGui void setLoadOrSave(bool load); - std::string getButtonStr() override; + ControllerButtonStr* getControllerButtons() override; private: void confirmDeleteSave(); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 5b7ef4238a..5146d77e5d 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -41,6 +41,9 @@ namespace MWGui mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); + mControllerButtons.b = "#{sClose}"; + mControllerButtons.rStick = "#{sScrolldown}"; + center(); } @@ -126,12 +129,10 @@ namespace MWGui BookWindowBase::onClose(); } - std::string ScrollWindow::getButtonStr() + ControllerButtonStr* ScrollWindow::getControllerButtons() { - if (mTakeButton->getVisible()) - return "(A) #{sTake} (RS) #{sScrolldown} (B) #{sClose}"; - else - return "(A) #{sTake} (RS) #{sScrolldown} (B) #{sClose}"; + mControllerButtons.a = mTakeButton->getVisible() ? "#{sTake}" : ""; + return &mControllerButtons; } bool ScrollWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 4e447707d0..f1047fdeba 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -25,7 +25,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Scroll"; } - std::string getButtonStr() override; + ControllerButtonStr* getControllerButtons() override; protected: void onCloseButtonClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 201204bc74..2fa6a72fd8 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -459,6 +459,11 @@ namespace MWGui i++; } + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sOk}"; + mControllerButtons.l1 = "#{sPrev} Tab"; + mControllerButtons.r1 = "#{sNext} Tab"; } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) @@ -1129,11 +1134,6 @@ namespace MWGui mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); } - std::string SettingsWindow::getButtonStr() - { - return "(A) #{sSelect} (LB) #{sPrev} Tab (RB) #{sNext} Tab (B) #{sOk}"; - } - bool SettingsWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index e8d7f248cb..555468d806 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -26,7 +26,6 @@ namespace MWGui void onResChange(int, int) override; - std::string getButtonStr() override; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; protected: diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index e6352c928e..6ef8f1ef8e 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -25,6 +25,8 @@ namespace MWGui // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); + + mControllerButtons.a = "#{sOk}"; } void TextInputDialog::setNextButtonShow(bool shown) @@ -83,11 +85,6 @@ namespace MWGui mTextEdit->setCaption(text); } - std::string TextInputDialog::getButtonStr() - { - return "(A) #{sOk}"; - } - bool TextInputDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index 67ca8ba757..ad7896ff27 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -24,8 +24,6 @@ namespace MWGui */ EventHandle_WindowBase eventDone; - std::string getButtonStr() override; - protected: void onOkClicked(MyGUI::Widget* _sender); void onTextAccepted(MyGUI::EditBox* _sender); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 9886f036e4..fa5cefb238 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -86,6 +86,8 @@ namespace MWGui trackFocusEvents(mCancelButton); for (MyGUI::Widget* widget : mHourSlider->getAllWidgets()) trackFocusEvents(widget); + + mControllerButtons.b = "#{sCancel}"; } void WaitDialog::setPtr(const MWWorld::Ptr& ptr) @@ -332,17 +334,11 @@ namespace MWGui } } - std::string WaitDialog::getButtonStr() + ControllerButtonStr* WaitDialog::getControllerButtons() { - if (mSleeping) - { - if (mUntilHealedButton->getVisible()) - return "(X) #{sUntilHealed} (A) #{sRest} (B) #{sCancel}"; - else - return "(A) #{sRest} (B) #{sCancel}"; - } - else - return "(A) #{sWait} (B) #{sCancel}"; + mControllerButtons.a = mSleeping ? "#{sRest}" : "#{sWait}"; + mControllerButtons.x = mSleeping && mUntilHealedButton->getVisible() ? "#{sUntilHealed}" : ""; + return &mControllerButtons; } bool WaitDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 77593c3049..4a7ccfcd00 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -47,7 +47,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "WaitDialog"; } - std::string getButtonStr() override; + ControllerButtonStr* getControllerButtons() override; protected: MyGUI::TextBox* mDateTimeText; diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index cd4d2874ca..2a9d4592d3 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -14,6 +14,24 @@ namespace MWGui { class DragAndDrop; + struct ControllerButtonStr + { + std::string a; + std::string b; + std::string l1; + std::string l2; + std::string l3; + std::string lStick; + std::string menu; + std::string r1; + std::string r2; + std::string r3; + std::string rStick; + std::string view; + std::string x; + std::string y; + }; + class WindowBase : public Layout { public: @@ -62,12 +80,13 @@ namespace MWGui // REMOVEME // virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) = 0; // virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) = 0; - virtual std::string getButtonStr() { return ""; } + virtual ControllerButtonStr* getControllerButtons() { return &mControllerButtons; } virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: virtual void onTitleDoubleClicked(); + ControllerButtonStr mControllerButtons; MyGUI::Widget* mMouseFocus = nullptr; bool mActiveControllerWindow = false; void trackFocusEvents(MyGUI::Widget* widget); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index b9df5b0b4e..ff2e9754af 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2561,19 +2561,7 @@ namespace MWGui return; } - std::string buttonStr = topWin->getButtonStr(); - // REMOVEME - Log(Debug::Error) << "WindowManager::updateControllerButtonsOverlay: showing overlay: " << buttonStr; - if (buttonStr.length() > 0) - { - mControllerButtonsOverlay->setButtonStr(buttonStr); - mControllerButtonsOverlay->setVisible(true); - } - else - { - // REMOVEME - Log(Debug::Error) << "WindowManager::updateControllerButtonsOverlay: ...psych, hiding it"; - mControllerButtonsOverlay->setVisible(false); - } + // setButtons will handle setting visibility based on if any buttons are defined. + mControllerButtonsOverlay->setButtons(topWin->getControllerButtons()); } } diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index 97b49d1002..6339344500 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -10,6 +10,20 @@ set(BUILTIN_DATA_FILES textures/omw_menu_scroll_center_h.dds textures/omw_menu_scroll_center_v.dds textures/omw_menu_icon_active.dds + textures/omw_steam_button_a.dds + textures/omw_steam_button_b.dds + textures/omw_steam_button_l1.dds + textures/omw_steam_button_l2.dds + textures/omw_steam_button_l3.dds + textures/omw_steam_button_lstick.dds + textures/omw_steam_button_menu.dds + textures/omw_steam_button_r1.dds + textures/omw_steam_button_r2.dds + textures/omw_steam_button_r3.dds + textures/omw_steam_button_rstick.dds + textures/omw_steam_button_view.dds + textures/omw_steam_button_x.dds + textures/omw_steam_button_y.dds textures/omw/water_nm.png fonts/DejaVuFontLicense.txt diff --git a/files/data/mygui/openmw_controllerbuttons.layout b/files/data/mygui/openmw_controllerbuttons.layout index d12785f0fc..42c89e10b4 100644 --- a/files/data/mygui/openmw_controllerbuttons.layout +++ b/files/data/mygui/openmw_controllerbuttons.layout @@ -4,15 +4,194 @@ - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/data/textures/omw_steam_button_a.dds b/files/data/textures/omw_steam_button_a.dds new file mode 100644 index 0000000000000000000000000000000000000000..447aa6081540c570208bd37a9338954feaf6990d GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_b.dds b/files/data/textures/omw_steam_button_b.dds new file mode 100644 index 0000000000000000000000000000000000000000..0be30636439019f9695ca56f5abfa614474cef26 GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_l1.dds b/files/data/textures/omw_steam_button_l1.dds new file mode 100644 index 0000000000000000000000000000000000000000..2b79a67b985b3e73eef56c54a3bb66e895456044 GIT binary patch literal 22000 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 I6!LiXe`A%mtpET3 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_l2.dds b/files/data/textures/omw_steam_button_l2.dds new file mode 100644 index 0000000000000000000000000000000000000000..156b1ca96c57d9a3120871fd7994214482d2f7a5 GIT binary patch literal 22000 zcmeHP4OCQB9=~sf8D$V=6vmIzFvU{S1XI*XXH>TBoeqdAv)Z;5A}N_5eHgmhLg~>w zrop)NWN%7NeH2N}r+LS5m9kDvJ4-@p$)`6=OVSNrG> zi9zraHzR5E<(})RGhX^z&09+c#E^* zbQmq~X%*j8n^6@^@thF82@eWJ6sL6q92LN~N*}W|p#RhmBdvd`lK%<+J@j3Qxqdmr z(JEa6;WrUXK(Np4!@m>wHYFuSb5+HkmiG#$&+BbD>UZyXESBOk&zAqsvThXElW0-R zf2pdqtO{cMHy7}SY?c6K`nk#aXj+jy$!1YAKI-{jCLcbcNu%vq8}jE#hLYTa6m$I) zkAD_nhW-p;rV*i;>hgl=i}Z|t$LyF338l#z`aJ$=$hX1a{aN{~F8kpqk_Z>^C>OEi z8yjyC{uIDBUmv*T{J?ZjJmuq{>$v$9zI7AD_59Y7^SwhE|5KIc9xLG2XKJ>Xng4HfeOc!GGS@}Zk&1j1FT!^KQ*DU-;a2Mdx7ZbDa=O8IF1Dv> z#SG)ICRq}#%CiEx%+g?AKYsB0`(JS z#OreX?;_^&li(&#ko4v?iOSJ%Q=HZf$oJqsnp>1Ny}a;Kntm{*z9B3|&-izJ9Z|I`TGDLM04VGIX-T-elFp^;OhKyJ$9#jusn^$3md~7nbTg*&ms~LdD=>f z>w-6%A$}C`MI65;>ZOejP<*rHyV>+*9LK3QTLcmW`}DUy`SW-L;V)<~tFIb2@A*ioKXU$?WIcqnN18uV`AL2$ z{=%rtTK6$z}!sgbmyT01i zT<78W&9wfG>QA2EU;d@`BBVE&Ouf;fyl2X#cyPzyMqPMJch>*g4_Grb8@%FyLs9Y9 z9}nDY{}&XJs*=g+Q}e^CnvDY`FD84Nd-XH!&6ci=yqV&|7Yz8C*M{vE`>p(6K< zBi1)y{Y&Ie&W{`$Dq=I5U(fhidY*g}FQPL;8E!OqV0nIDIJ=^?L;kF@`ak8r2P)5h zraZ3~5sCb5v&g@eVv+w$HXnp|@Xq4DdPVyV556iYZJ|Oi=ayHFt+lZU^&Zd5^*yB) zV*_rDB##I>Wb5tMhO;YRFmqZVH+^3CdfoVX`#-7OWYX*Vpz?xe7UeVkZ9`-9(El0# z?RMz=n;TgCC-CnP@5uGz3*T)2@2frjrW+jD&g$H2_a~87Tl!+#tfQU9x4gX_;UB_3 z{ZOvFV6g}P)nj2X075|8Ue^yB8iLo`*?xe$et+?u#=o{2`g?Z6ZmN&m)!VN+KY;kp zy`L)f?-7pI9qs$9B)i*R2wyu5`^!J<2hF_K_QEXE8kI`LB`M1b7cW}C#`_a9V$LTx zr2T+1uH=8zzjWYOXSM%!=O5;8gl`rvI?%$VC@+5}j+|5pb z z2R76N7aN(shw&cPf2I9CU+wXg?l1p_>?cz^`N&>|lCOph*3^unu^Cq!Z;3z0xS6f* zY0Gl^)qqQv`19`H@_1gG9qU(nE^Tj%1G~VDVN5|5ZBP5h z5%%Cx&;4K6{GX?|VdFtZ<-gnWU&6nS^B}NI-enRlZ#WsVGszxNozQj-qatBaodB~zBknGgMTPu z^TGXtRwZU+71q-FPd=yE9sjU|8@J(1u!U(=-*#`G_64W zUrcR#km|pl&nexOnr@(YZAkC&7tbAK>0G};BNsjl)~`uza01-DUqz5`$*|l%aNTsu zCu5TZ`v42k59f|&?y9mf{-y@4s#}94JTth5-Qt1ggrG!EoYoD&%R|!h0qmbXW@}KK z%|m}vmG*r{T#`Pyem{;1Rv_5#7T8VX)Ni@hGvR%ZB$J8qPtpOwCV*%L*NVOD1M+if z^K9xrNLie^rmN?9E=~9!EYe*g?vR$zb5+-WFa4Jd%s&S*AL!*RR;&g`1Xl;vR4L$uHkcD_+wo}&dfN8-jv zT3*+kFFuAo9`BwHB_(yOO$|Hu#C$l_ORS6IZi4v}LB_=K z=!0T$e;pv4$6kT5@)(8#+88P7fU+gx^KfLt$9}Od`4v54Q%YjzmXPw`{Ew=2EC|0v zl3#wl6V5Ax@RIyflIZz;@Re}s%AUwL1~(}W)Okt!a0t?z*sX}B@~cDW`6kct_+wRm zU$^|+#c?3~7SH`pa=ukM@6b{C)18J!VKVOKKkWiC1Np)Q;v)Q7AM%UIgOPpdptO%5 z3z@AO6SEKq&#w(47{ph7*wRvlomQ4#ZV$fXHzI!Rx~hWSq&z~G>vtP6HP*T-_WzE` zFK?e5LoiH&L@~a$A%#U6^TUu+R!ej4mkTV4z%BFJl+aC^HZ8_1SP?Ivugn~UNC8KJ>e4UtWz#e{(KG1zXdJ)DT*He z>%YYNT|<6Z`8oT^`3Wm2PH2z6{GSZH;5Gg?y4q{;SHGU|-p%xJYn&8sbwdA64`KfLcEflM3Q9Y4gsuPEx%bK@c&rD@=X+e&y(x74 zj#HUT6HuwS7ED0wU7SfdEq(y+;m@GUC)coaWV+q(RAiq^OxW!mOyQ{ED2u0zNo@U7 zux-J<**EdauY5i{mdp?N9x(qS{+dI|N4@=bXJc+Ol%LZN;%ic$5#jKd3tS|L_e6$1 zcY4P|osDrwiL!Bb*jKbZj5g~+TFq?yKP^mkrT9F;1^xT$p+bBN1`%}fEXj!@$psUGwwalwojFeA?BT7uhPs*Ae?i4j2eG zDJ=-Dx$WT0vRlaG?)s~WB8o`72)ov6^{qOu8mU2`M$?H^@)^zZFJ& z$}Tt7!|$E#Efv_~eNB1&?eM;K_{su7K6sp40DR;4xALfbpA%2dE0}-pD8?bXyPeS) zm_Jr&JoevV{xc4(H#7ePJw*8^RPK%kNss9$=0UfP7%{{GR1B8neEF?KEl=;^4hY^UEZ*> z@fi2*lR8nA#Po}Y#F(cakp7=sTc6~Au5)QwdY*HUy~`4_lppofO%e7Vp)t^;!QXnv z+j0!e9{eA?iuPAiV;_DbUzup4?QQMF4_MhZ?kS3!)vE{1%U?|(WJSo)Yt)X3mR}4M cAAV`4p?gaAHj0bh@0O#0=O`}TNODQ||4q~$wg3PC literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_l3.dds b/files/data/textures/omw_steam_button_l3.dds new file mode 100644 index 0000000000000000000000000000000000000000..3b6b174e482c790cf00896e35d34f2683af53313 GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_menu.dds b/files/data/textures/omw_steam_button_menu.dds new file mode 100644 index 0000000000000000000000000000000000000000..ef0a0a501c8c3cc085b8de4b369fb96c36640bfb GIT binary patch literal 22000 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 LO#U@*1mph)YI8aW literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_r1.dds b/files/data/textures/omw_steam_button_r1.dds new file mode 100644 index 0000000000000000000000000000000000000000..bdd93de2b2975c69b7d431ad106dcbad015f1b02 GIT binary patch literal 22000 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 C_0qNg literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_r2.dds b/files/data/textures/omw_steam_button_r2.dds new file mode 100644 index 0000000000000000000000000000000000000000..052e97ebe1aca5f4511a54f5170c846435b01567 GIT binary patch literal 22000 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 X>*CL)FkD_ag@hq*KV=oP9*+MXXwoi9 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_r3.dds b/files/data/textures/omw_steam_button_r3.dds new file mode 100644 index 0000000000000000000000000000000000000000..73b4bea1f7bb3ecb45eb2df16d2e84757b7d2075 GIT binary patch literal 22000 zcmeHP4_s7LzCUM%8D>CWhRxynYr(G?r_eXxuqkiZSAM~x#E21 zoO{mi|M{Iicb<4+M#wURkh{tQ8Q_2N4{6CCgcR^kH|_CXKMDVIQ=aC(yWO7*VzB{NQhueWM)|rx&(ReB z-)LR@kvcV|_}F8*_^K*7lizU>J|V&%i*V^g=jTtkz~cJi_Qd^(=@HZWp;|Y+Dp;97 z`B|lGKi2td8sitc_f}!I5GgJH_t(E6v2xXdrzyV%q{~>T+Y(A~Rj;?gm0MI%6n9j< z|K+q?6^jpF+mJR>m&xjLb-KxmGToScT(+We6NC`@{5K+8?*fbKi`#?#9?f*;b!?3>|n@#aw(UOcD2mRiZ_=e*EqlA`s*cuL8iSxcfF_Mu__RF5XP(;VC`-?j{ zU6$|Q3}4a<>HphcgIj_@5l%7Ae@Bm{y-Fw^1LK|LjVe zKeu>*-O2baA4d66W8IdE#aEfWvph?!M;`et^XE$w3SY{)JW5IH|E4{E+WG4_b1A+F z@nMI9JtWz5TP)u$w9xW@wf?!GBwZKu{Vl3I7Un>26Vh$j*ff`oXO||lAG;9b`68u1 z%X9g3ZjKkr2h=#KHmudb9^4^6NXuo<|J3XOE5W%}$%j*ZBvHsMp?o)se@BBlmS#P2 zj_J2OdCAqJoKcLQu7@*25R$X_*w3QdK7iS^h8QZbC0CnWbyCx zfWoCIH~zus>q?K97nu_-=o@0>?v2O7=<~Z#LUX7ta~Z`P#K(<;LfCjd@p^0?DI*t& zuNfQiU$4|y`cr(r?ZG}Nma};Nka@t=DhGW}y@Totucr@|)^+pXL4lY73@H06K zd1)!$4e_i_cZl)dg^(UM8FOfPSNittXUhe9I6a`Ppz~S59wH^nr)wH~+G&04o)7V% z-%*P1wLOT(?@_okx2Q0M*?&n8=zGNT%$~nT--__KkC^?-_v9xAo917p^m3?4kBOY% zBJy$-&x^~rmi}B*e_9_8I_Om?U%>L``BrtjV|#O)UQNDxz<)+y%J}~`YdBM8#Cl<`1?ThW< zMUg!OG(k;=c&<9^9M7@BG-t+@Pnm!Dn5_r6D|c>BV*15+%zLSg8bT~wL>{uerU6wM z){iI#duYW6jm#d~lD2PeQ+ckVR?+AK5Ul^I0JtXz=TkH4RSjz^f{MIP(v&yu?_6 zN;l`MS^og-|21V{c1pzmuz26PxT)29XF2nCY}I9yugPKd3gg{|KxKv}eg1CcA%5PK z{>A(N_O5}O(lh65ffc6;>jU0r)%nwOt9{qNz255u(F7nNo&E)?{?v+t4zW<)fx zcyB4Vn(VPf(7z4Etr(QNg5@*%GbhZ}8)lXd-D`RDRR5YMObfeA3xg?tnlkc9%JOp# zl}IK|6V_u~$wyPBPBOombl>put=uAjLigZY2a@Q47vCL`0o?X!21J@RUp|I7T+U$1=A z%H(;q1%ZoqsR7{3iR)NnwA$0-zFWuQCMFN!p#FU^>?oS2Qet$LH{LVn^Zn<)AnuE z@2EWBrg%v9o3EeWCFoy&=8W0))lWzN3;#g$m&p9zmA*A*Q{qT<(oD)V<;-t5wnnxG8-lV>hOagPY<9F(|(W`VS)dhY&!YFB*PT z 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 C+cc5@ literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_view.dds b/files/data/textures/omw_steam_button_view.dds new file mode 100644 index 0000000000000000000000000000000000000000..8fb56f847fef49cf612c21ff664e4e25d661c561 GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/files/data/textures/omw_steam_button_x.dds b/files/data/textures/omw_steam_button_x.dds new file mode 100644 index 0000000000000000000000000000000000000000..9619ed243cfa4a27a4d5a4cfccd8a0f0551f12bd GIT binary patch literal 22000 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 Date: Sun, 18 May 2025 01:27:38 -0700 Subject: [PATCH 090/455] Consolidate all gamepad cursor listening in controllermanager --- apps/openmw/mwgui/birth.cpp | 16 ------ apps/openmw/mwgui/birth.hpp | 2 - apps/openmw/mwgui/class.cpp | 38 +------------ apps/openmw/mwgui/class.hpp | 4 -- apps/openmw/mwgui/confirmationdialog.cpp | 1 + apps/openmw/mwgui/container.cpp | 19 +------ apps/openmw/mwgui/container.hpp | 2 - apps/openmw/mwgui/journalwindow.cpp | 15 ------ apps/openmw/mwgui/journalwindow.hpp | 1 - apps/openmw/mwgui/mainmenu.cpp | 1 + apps/openmw/mwgui/messagebox.cpp | 1 + apps/openmw/mwgui/race.cpp | 13 +---- apps/openmw/mwgui/race.hpp | 1 - apps/openmw/mwgui/savegamedialog.cpp | 17 ------ apps/openmw/mwgui/savegamedialog.hpp | 2 - apps/openmw/mwgui/waitdialog.cpp | 1 + apps/openmw/mwgui/windowbase.cpp | 18 +++---- apps/openmw/mwgui/windowbase.hpp | 14 ++--- apps/openmw/mwinput/controllermanager.cpp | 65 +++++++++++++++++------ 19 files changed, 76 insertions(+), 155 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 53f0f9793b..b047f6dc5f 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -282,10 +282,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - // Have A button do nothing so mouse controller still works. - if (mUsingGamepadGuiCursor) - return false; - if (mOkButtonFocus) onOkClicked(mOkButton); else @@ -304,14 +300,12 @@ namespace MWGui MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); winMgr->setKeyFocusWidget(mBirthList); winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); - mUsingGamepadGuiCursor = 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); - mUsingGamepadGuiCursor = false; } else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus) || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus)) @@ -319,18 +313,8 @@ namespace MWGui mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); mBackButton->setStateSelected(!mOkButtonFocus); - mUsingGamepadGuiCursor = false; } return true; } - - bool BirthDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) - { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - { - mUsingGamepadGuiCursor = true; - } - return false; - } } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 8a7190b934..09a0b7b1b5 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -59,9 +59,7 @@ namespace MWGui ESM::RefId mCurrentBirthId; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; bool mOkButtonFocus = true; - bool mUsingGamepadGuiCursor = false; }; } #endif diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 0ac39e4bac..cc44de7b0c 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -58,6 +58,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mOkButton->setStateSelected(true); + mDisableGamepadCursor = true; trackFocusEvents(mBackButton); trackFocusEvents(mOkButton); mControllerButtons.a = "#{sSelect}"; @@ -323,10 +324,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - // Have A button do nothing so mouse controller still works. - if (mUsingGamepadGuiCursor) - return false; - if (mOkButtonFocus) onOkClicked(mOkButton); else @@ -345,14 +342,12 @@ namespace MWGui MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); winMgr->setKeyFocusWidget(mClassList); winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); - mUsingGamepadGuiCursor = 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); - mUsingGamepadGuiCursor = false; } else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus) || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus)) @@ -360,21 +355,11 @@ namespace MWGui mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); mBackButton->setStateSelected(!mOkButtonFocus); - mUsingGamepadGuiCursor = false; } return true; } - bool PickClassDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) - { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - { - mUsingGamepadGuiCursor = true; - } - return false; - } - /* InfoBoxDialog */ void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) @@ -417,6 +402,7 @@ namespace MWGui center(); + mDisableGamepadCursor = Settings::gui().mControllerMenus; mControllerButtons.a = "#{sSelect}"; } @@ -721,10 +707,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - // Have A button do nothing so mouse controller still works. - if (mUsingGamepadGuiCursor) - return false; - if (mControllerFocus == 0) onDescriptionClicked(mButtons[0]); else if (mControllerFocus == 1) @@ -740,11 +722,6 @@ namespace MWGui { onOkClicked(mButtons[2]); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - { - mUsingGamepadGuiCursor = false; - } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { mButtons[mControllerFocus]->setStateSelected(false); @@ -753,7 +730,6 @@ namespace MWGui else mControllerFocus--; mButtons[mControllerFocus]->setStateSelected(true); - mUsingGamepadGuiCursor = false; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { @@ -763,20 +739,10 @@ namespace MWGui else mControllerFocus++; mButtons[mControllerFocus]->setStateSelected(true); - mUsingGamepadGuiCursor = false; } return true; } - bool CreateClassDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) - { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - { - mUsingGamepadGuiCursor = true; - } - return false; - } - // widget controls void CreateClassDialog::onDialogCancel() diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index e2566ce6c0..515416973e 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -155,9 +155,7 @@ namespace MWGui ESM::RefId mCurrentClassId; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; bool mOkButtonFocus = true; - bool mUsingGamepadGuiCursor = false; }; class SelectSpecializationDialog : public WindowModal @@ -349,9 +347,7 @@ namespace MWGui Widgets::MWSkillPtr mAffectedSkill; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; int mControllerFocus = 2; - bool mUsingGamepadGuiCursor = false; }; } #endif diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 1d8f14176a..6a65b0b3ec 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -22,6 +22,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { + mDisableGamepadCursor = true; trackFocusEvents(mOkButton); trackFocusEvents(mCancelButton); mControllerButtons.a = "#{sOk}"; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 615c4801d3..54eb6122c5 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -96,12 +96,12 @@ namespace MWGui name += MWGui::ToolTips::getSoulString(object.getCellRef()); dialog->openCountDialog(name, "#{sTake}", count); dialog->eventOkClicked.clear(); - if (Settings::gui().mControllerMenus && !mUsingGamepadGuiCursor) + if (Settings::gui().mControllerMenus) dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::takeItem); else dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } - else if (Settings::gui().mControllerMenus && !mUsingGamepadGuiCursor) + else if (Settings::gui().mControllerMenus) takeItem(nullptr, count); else dragItem(nullptr, count); @@ -368,14 +368,9 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mUsingGamepadGuiCursor) - return false; - int index = mItemView->getControllerFocus(); if (index >= 0 && index < mItemView->getItemCount()) - { onItemSelected(index); - } } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { @@ -396,21 +391,11 @@ namespace MWGui arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { mItemView->onControllerButtonEvent(arg); - mUsingGamepadGuiCursor = false; } return true; } - bool ContainerWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) - { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - { - mUsingGamepadGuiCursor = true; - } - return false; - } - void ContainerWindow::setActiveControllerWindow(bool active) { mItemView->setActiveControllerWindow(active); diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index d2d8c9f4aa..08497e0369 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -70,8 +70,6 @@ namespace MWGui void onReferenceUnavailable() override; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; - bool mUsingGamepadGuiCursor = false; }; } #endif // CONTAINER_H diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index abb1843178..20b7acbd9b 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -685,10 +685,6 @@ namespace { if (arg.button == SDL_CONTROLLER_BUTTON_A) // A: Mouse click or Select { - // Fall through to mouse click - if (mUsingGamepadGuiCursor) - return false; - if (mOptionsMode && mQuestMode) { // Choose a quest @@ -773,7 +769,6 @@ namespace mSelectedQuest = mButtons.size() - 1; mButtons[mSelectedQuest]->setStateSelected(true); } - mUsingGamepadGuiCursor = false; return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) @@ -787,21 +782,18 @@ namespace mSelectedQuest = 0; mButtons[mSelectedQuest]->setStateSelected(true); } - mUsingGamepadGuiCursor = false; return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { if (!mOptionsMode) notifyPrevPage(getWidget(PrevPageBTN)); - mUsingGamepadGuiCursor = false; return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { if (!mOptionsMode) notifyNextPage(getWidget(NextPageBTN)); - mUsingGamepadGuiCursor = false; return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) // LB: Previous Page @@ -819,13 +811,6 @@ namespace return false; } - - bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override - { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - mUsingGamepadGuiCursor = true; - return false; - } }; } diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 5da2038575..45c4100563 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -34,7 +34,6 @@ namespace MWGui std::vector mButtons; int mSelectedQuest = 0; - bool mUsingGamepadGuiCursor = false; }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index cfcb63e4d5..286167a61e 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -106,6 +106,7 @@ namespace MWGui constexpr VFS::Path::NormalizedView menuBackgroundVideo("video/menu_background.bik"); mHasAnimatedMenu = mVFS->exists(menuBackgroundVideo); + mDisableGamepadCursor = Settings::gui().mControllerMenus; updateMenu(); } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8c5a968c99..7e372fa215 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -284,6 +284,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { + mDisableGamepadCursor = true; mControllerButtons.a = "#{sOk}"; // If we have more than one button, we need to set the focus to the first one. diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 9b97dfbb53..6aecaa0b88 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -475,10 +475,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - // Have A button do nothing so mouse controller still works. - if (mUsingGamepadGuiCursor) - return false; - if (mOkButtonFocus) onOkClicked(mOkButton); else @@ -509,14 +505,12 @@ namespace MWGui MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); winMgr->setKeyFocusWidget(mRaceList); winMgr->injectKeyPress(MyGUI::KeyCode::ArrowUp, 0, false); - mUsingGamepadGuiCursor = 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); - mUsingGamepadGuiCursor = false; } else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus) || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus)) @@ -524,7 +518,6 @@ namespace MWGui mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); mBackButton->setStateSelected(!mOkButtonFocus); - mUsingGamepadGuiCursor = false; } return true; @@ -532,11 +525,7 @@ namespace MWGui bool RaceDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - { - mUsingGamepadGuiCursor = true; - } - else if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX) + if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTX) { if (arg.value < -1000 || arg.value > 1000) onPreviewScroll(nullptr, arg.value < 0 ? 1 : -1); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index c3b322ba8b..3652343308 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -125,7 +125,6 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; bool mOkButtonFocus = true; - bool mUsingGamepadGuiCursor = false; }; } #endif diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 568c12d00d..b07c307d60 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -516,10 +516,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - // Have A button do nothing so mouse controller still works. - if (mUsingGamepadGuiCursor) - return false; - if (mOkButtonFocus) onOkButtonClicked(mOkButton); else @@ -537,21 +533,18 @@ namespace MWGui else index++; mCharacterSelection->setIndexSelected(index); - mUsingGamepadGuiCursor = false; } 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); - mUsingGamepadGuiCursor = 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); - mUsingGamepadGuiCursor = false; } else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mOkButtonFocus) || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mOkButtonFocus)) @@ -559,18 +552,8 @@ namespace MWGui mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); mCancelButton->setStateSelected(!mOkButtonFocus); - mUsingGamepadGuiCursor = false; } return true; } - - bool SaveGameDialog::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) - { - if (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY) - { - mUsingGamepadGuiCursor = true; - } - return false; - } } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index fbe319aed1..4717279dfb 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -70,9 +70,7 @@ namespace MWGui const MWState::Slot* mCurrentSlot; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; bool mOkButtonFocus = true; - bool mUsingGamepadGuiCursor = false; }; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index fa5cefb238..c57e813767 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -88,6 +88,7 @@ namespace MWGui trackFocusEvents(widget); mControllerButtons.b = "#{sCancel}"; + mDisableGamepadCursor = Settings::gui().mControllerMenus; } void WaitDialog::setPtr(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index a3d1b27fb4..d1ce72f067 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -109,25 +109,25 @@ void WindowBase::clampWindowCoordinates(MyGUI::Window* window) void WindowBase::focusGain(MyGUI::Widget* _new, MyGUI::Widget* _old) { // REMOVEME - Log(Debug::Verbose) << "WindowBase::focusGain new=" << _new << ", old=" << _old; - mMouseFocus = _new; + //Log(Debug::Verbose) << "WindowBase::focusGain new=" << _new << ", old=" << _old; + //mMouseFocus = _new; } void WindowBase::focusLoss(MyGUI::Widget* _old, MyGUI::Widget* _new) { // REMOVEME - Log(Debug::Verbose) << "WindowBase::focusLoss old=" << _old << ", new=" << _new; - if (mMouseFocus == _old) - mMouseFocus = nullptr; + //Log(Debug::Verbose) << "WindowBase::focusLoss old=" << _old << ", new=" << _new; + //if (mMouseFocus == _old) + // mMouseFocus = nullptr; } void WindowBase::trackFocusEvents(MyGUI::Widget* widget) { - if (!Settings::gui().mControllerMenus) - return; + // if (!Settings::gui().mControllerMenus) + // return; - widget->eventMouseSetFocus += MyGUI::newDelegate(this, &WindowBase::focusGain); - widget->eventMouseLostFocus += MyGUI::newDelegate(this, &WindowBase::focusLoss); + // widget->eventMouseSetFocus += MyGUI::newDelegate(this, &WindowBase::focusGain); + // widget->eventMouseLostFocus += MyGUI::newDelegate(this, &WindowBase::focusLoss); } WindowModal::WindowModal(const std::string& parLayout) diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 2a9d4592d3..25d97d9308 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -74,28 +74,30 @@ namespace MWGui static void clampWindowCoordinates(MyGUI::Window* window); - /// Called by controllermanager to handle controller events + virtual ControllerButtonStr* getControllerButtons() { return &mControllerButtons; } + bool isGamepadCursorAllowed() { return !mDisableGamepadCursor; } virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; }; virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return true; }; - // REMOVEME - // virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) = 0; - // virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) = 0; - virtual ControllerButtonStr* getControllerButtons() { return &mControllerButtons; } virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: virtual void onTitleDoubleClicked(); ControllerButtonStr mControllerButtons; - MyGUI::Widget* mMouseFocus = nullptr; bool mActiveControllerWindow = false; + bool mDisableGamepadCursor = false; + + // REMOVEME void trackFocusEvents(MyGUI::Widget* widget); + // REMOVEME + MyGUI::Widget* mMouseFocus = nullptr; private: void onDoubleClick(MyGUI::Widget* _sender); bool mDisabledByLua = false; + // REMOVEME void focusGain(MyGUI::Widget* _new, MyGUI::Widget* _old); void focusLoss(MyGUI::Widget* _old, MyGUI::Widget* _new); }; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index f12c9a31ce..d1deef5011 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -241,11 +241,26 @@ namespace MWInput bool ControllerManager::gamepadToGuiControl(const SDL_ControllerButtonEvent& arg) { + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (Settings::gui().mControllerMenus) { - MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getActiveControllerWindow(); - if (topWin && topWin->onControllerButtonEvent(arg)) - return true; + // Update cursor state. + bool treatAsMouse = winMgr->getCursorVisible(); + winMgr->setCursorActive(false); + + MWGui::WindowBase* topWin = winMgr->getActiveControllerWindow(); + if (topWin) + { + mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); + + // Fall through to mouse click + if (mGamepadGuiCursorEnabled && treatAsMouse && arg.button == SDL_CONTROLLER_BUTTON_A) + return false; + + if (topWin->onControllerButtonEvent(arg)) + return true; + } } // Presumption of GUI mode will be removed in the future. @@ -273,9 +288,9 @@ namespace MWInput break; case SDL_CONTROLLER_BUTTON_B: if (MyGUI::InputManager::getInstance().isModalAny()) - MWBase::Environment::get().getWindowManager()->exitCurrentModal(); + winMgr->exitCurrentModal(); else - MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + winMgr->exitCurrentGuiMode(); return true; case SDL_CONTROLLER_BUTTON_X: key = MyGUI::KeyCode::Semicolon; @@ -285,7 +300,7 @@ namespace MWInput break; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::LeftShift); - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Tab, 0, false); + winMgr->injectKeyPress(MyGUI::KeyCode::Tab, 0, false); MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::LeftShift); return true; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: @@ -293,7 +308,7 @@ namespace MWInput return true; case SDL_CONTROLLER_BUTTON_LEFTSTICK: mGamepadGuiCursorEnabled = !mGamepadGuiCursorEnabled; - MWBase::Environment::get().getWindowManager()->setCursorActive(mGamepadGuiCursorEnabled); + winMgr->setCursorActive(mGamepadGuiCursorEnabled); return true; default: return false; @@ -303,41 +318,61 @@ namespace MWInput if (SDL_IsTextInputActive()) return false; - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); + winMgr->injectKeyPress(key, 0, false); return true; } bool ControllerManager::gamepadToGuiControl(const SDL_ControllerAxisEvent& arg) { + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (Settings::gui().mControllerMenus) { // Left and right triggers toggle through open GUI windows. if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) { if (arg.value == 32767) // Treat like a button. - MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(true); + winMgr->cycleActiveControllerWindow(true); return true; } else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) { if (arg.value == 32767) // Treat like a button. - MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(false); + winMgr->cycleActiveControllerWindow(false); return true; } - MWGui::WindowBase* topWin = MWBase::Environment::get().getWindowManager()->getActiveControllerWindow(); - if (topWin && topWin->onControllerThumbstickEvent(arg)) - return true; + + MWGui::WindowBase* topWin = winMgr->getActiveControllerWindow(); + if (topWin) + { + // Update cursor state + mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); + if (!mGamepadGuiCursorEnabled) + winMgr->setCursorActive(false); + + if (mGamepadGuiCursorEnabled + && (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY)) + { + // Treat the left stick like a cursor. Fall through. + return false; + } + else if (topWin->onControllerThumbstickEvent(arg)) + { + // Window handled the event. + return true; + } + } } switch (arg.axis) { case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: if (arg.value == 32767) // Treat like a button. - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Minus, 0, false); + winMgr->injectKeyPress(MyGUI::KeyCode::Minus, 0, false); break; case SDL_CONTROLLER_AXIS_TRIGGERLEFT: if (arg.value == 32767) // Treat like a button. - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Equals, 0, false); + winMgr->injectKeyPress(MyGUI::KeyCode::Equals, 0, false); break; case SDL_CONTROLLER_AXIS_LEFTX: case SDL_CONTROLLER_AXIS_LEFTY: From f15cc663aeb224685328796347b08583a7cab4b2 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 18 May 2025 01:42:11 -0700 Subject: [PATCH 091/455] Remove old way of listening for gamepad mouse events --- apps/openmw/mwgui/bookwindow.cpp | 7 ---- apps/openmw/mwgui/class.cpp | 10 ----- apps/openmw/mwgui/confirmationdialog.cpp | 5 --- apps/openmw/mwgui/mainmenu.cpp | 49 +++++++++--------------- apps/openmw/mwgui/messagebox.cpp | 5 --- apps/openmw/mwgui/review.cpp | 9 ----- apps/openmw/mwgui/savegamedialog.cpp | 8 ---- apps/openmw/mwgui/scrollwindow.cpp | 5 --- apps/openmw/mwgui/waitdialog.cpp | 11 ------ apps/openmw/mwgui/windowbase.cpp | 24 ------------ apps/openmw/mwgui/windowbase.hpp | 9 ----- 11 files changed, 19 insertions(+), 123 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index c48493c1b4..beba6e5968 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -27,19 +27,15 @@ namespace MWGui { getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); - trackFocusEvents(mCloseButton); getWidget(mTakeButton, "TakeButton"); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); - trackFocusEvents(mTakeButton); getWidget(mNextPageButton, "NextPageBTN"); mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); - trackFocusEvents(mNextPageButton); getWidget(mPrevPageButton, "PrevPageBTN"); mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); - trackFocusEvents(mPrevPageButton); getWidget(mLeftPageNumber, "LeftPageNumber"); getWidget(mRightPageNumber, "RightPageNumber"); @@ -236,9 +232,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - if (mTakeButton->getVisible()) onTakeButtonClicked(mTakeButton); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index cc44de7b0c..d4de67dbec 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -59,8 +59,6 @@ namespace MWGui { mOkButton->setStateSelected(true); mDisableGamepadCursor = true; - trackFocusEvents(mBackButton); - trackFocusEvents(mOkButton); mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; } @@ -84,9 +82,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - if (mOkButtonFocus) onOkClicked(mOkButton); else @@ -444,7 +439,6 @@ namespace MWGui // First button is selected by default button->setStateSelected(true); } - trackFocusEvents(button); this->mButtons.push_back(button); } @@ -479,11 +473,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - onButtonClicked(mButtons[mControllerFocus]); - return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 6a65b0b3ec..36b01112e5 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -23,8 +23,6 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mDisableGamepadCursor = true; - trackFocusEvents(mOkButton); - trackFocusEvents(mCancelButton); mControllerButtons.a = "#{sOk}"; mControllerButtons.b = "#{sCancel}"; } @@ -79,9 +77,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - if (mOkButtonFocus) onOkButtonClicked(mOkButton); else diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 286167a61e..d9d5b5c33b 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -210,37 +210,27 @@ namespace MWGui bool MainMenu::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { - // REMOVEME - Log(Debug::Verbose) << "MainMenu::onControllerButtonEvent " << arg.button; - - MyGUI::KeyCode key = MyGUI::KeyCode::None; - switch (arg.button) + if (arg.button == SDL_CONTROLLER_BUTTON_A) { - case 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); - return true; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: - key = MyGUI::KeyCode::Tab; - break; - case SDL_CONTROLLER_BUTTON_A: - if (mMouseFocus != nullptr) - return false; - key = MyGUI::KeyCode::Space; - break; - case SDL_CONTROLLER_BUTTON_B: - case SDL_CONTROLLER_BUTTON_START: - if (mButtons["return"]->getVisible()) - { - onButtonClicked(mButtons["return"]); - return true; - } - else - key = MyGUI::KeyCode::Escape; - break; + 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); } - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); return true; } @@ -344,7 +334,6 @@ namespace MWGui button->setProperty("ImagePushed", "textures\\menu_" + buttonId + "_pressed.dds"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); button->setUserData(buttonId); - trackFocusEvents(button); } } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 7e372fa215..311743fd36 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -260,7 +260,6 @@ namespace MWGui button->setCaptionWithReplacing(buttonId); button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); - trackFocusEvents(button); mButtons.push_back(button); @@ -447,11 +446,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - buttonActivated(mButtons[mControllerFocus]); - return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 4d0d9d4d2c..19dcda90e4 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -46,28 +46,24 @@ namespace MWGui getWidget(button, "NameButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked); - trackFocusEvents(button); mButtons.push_back(button); getWidget(mRaceWidget, "RaceText"); getWidget(button, "RaceButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked); - trackFocusEvents(button); mButtons.push_back(button); getWidget(mClassWidget, "ClassText"); getWidget(button, "ClassButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked); - trackFocusEvents(button); mButtons.push_back(button); getWidget(mBirthSignWidget, "SignText"); getWidget(button, "SignButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked); - trackFocusEvents(button); mButtons.push_back(button); // Setup dynamic stats @@ -116,13 +112,11 @@ namespace MWGui MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); - trackFocusEvents(backButton); mButtons.push_back(backButton); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); - trackFocusEvents(okButton); mButtons.push_back(okButton); if (Settings::gui().mControllerMenus) @@ -546,9 +540,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - switch(mControllerFocus) { case 0: diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index b07c307d60..2484b0b1d2 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -64,8 +64,6 @@ namespace MWGui // To avoid accidental deletions mDeleteButton->setNeedKeyFocus(false); - trackFocusEvents(mCancelButton); - trackFocusEvents(mDeleteButton); mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sClose}"; } @@ -371,12 +369,6 @@ namespace MWGui } else onSlotSelected(mSaveList, MyGUI::ITEM_NONE); - - if (Settings::gui().mControllerMenus) - { - for (int i = 0; i < mSaveList->getItemCount(); i++) - trackFocusEvents(mSaveList->getWidgetByIndex(i)); - } } std::string formatTimeplayed(const double timeInSeconds) diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 5146d77e5d..ae47a361a2 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -29,11 +29,9 @@ namespace MWGui getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); - trackFocusEvents(mCloseButton); getWidget(mTakeButton, "TakeButton"); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); - trackFocusEvents(mTakeButton); adjustButton("CloseButton"); adjustButton("TakeButton"); @@ -139,9 +137,6 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mMouseFocus != nullptr) - return false; - if (mTakeButton->getVisible()) onTakeButtonClicked(mTakeButton); } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index c57e813767..8df47e7d58 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -81,12 +81,6 @@ namespace MWGui mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); - trackFocusEvents(mUntilHealedButton); - trackFocusEvents(mWaitButton); - trackFocusEvents(mCancelButton); - for (MyGUI::Widget* widget : mHourSlider->getAllWidgets()) - trackFocusEvents(widget); - mControllerButtons.b = "#{sCancel}"; mDisableGamepadCursor = Settings::gui().mControllerMenus; } @@ -345,12 +339,7 @@ namespace MWGui bool WaitDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) - { - if (mMouseFocus != nullptr) - return false; - onWaitButtonClicked(mWaitButton); - } else if (arg.button == SDL_CONTROLLER_BUTTON_B) onCancelButtonClicked(mCancelButton); else if (arg.button == SDL_CONTROLLER_BUTTON_X && mUntilHealedButton->getVisible()) diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index d1ce72f067..ae12076d2d 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -106,30 +106,6 @@ void WindowBase::clampWindowCoordinates(MyGUI::Window* window) window->setPosition(left, top); } -void WindowBase::focusGain(MyGUI::Widget* _new, MyGUI::Widget* _old) -{ - // REMOVEME - //Log(Debug::Verbose) << "WindowBase::focusGain new=" << _new << ", old=" << _old; - //mMouseFocus = _new; -} - -void WindowBase::focusLoss(MyGUI::Widget* _old, MyGUI::Widget* _new) -{ - // REMOVEME - //Log(Debug::Verbose) << "WindowBase::focusLoss old=" << _old << ", new=" << _new; - //if (mMouseFocus == _old) - // mMouseFocus = nullptr; -} - -void WindowBase::trackFocusEvents(MyGUI::Widget* widget) -{ - // if (!Settings::gui().mControllerMenus) - // return; - - // widget->eventMouseSetFocus += MyGUI::newDelegate(this, &WindowBase::focusGain); - // widget->eventMouseLostFocus += MyGUI::newDelegate(this, &WindowBase::focusLoss); -} - WindowModal::WindowModal(const std::string& parLayout) : WindowBase(parLayout) { diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 25d97d9308..f14c41dca1 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -87,19 +87,10 @@ namespace MWGui bool mActiveControllerWindow = false; bool mDisableGamepadCursor = false; - // REMOVEME - void trackFocusEvents(MyGUI::Widget* widget); - // REMOVEME - MyGUI::Widget* mMouseFocus = nullptr; - private: void onDoubleClick(MyGUI::Widget* _sender); bool mDisabledByLua = false; - - // REMOVEME - void focusGain(MyGUI::Widget* _new, MyGUI::Widget* _old); - void focusLoss(MyGUI::Widget* _old, MyGUI::Widget* _new); }; /* From f80d46dc94af710079d66bbc2351e76f4229c26e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 18 May 2025 15:34:22 -0700 Subject: [PATCH 092/455] Add controller support to count dialog --- apps/openmw/mwgui/countdialog.cpp | 17 +++++++++++++++++ apps/openmw/mwgui/countdialog.hpp | 1 + 2 files changed, 18 insertions(+) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 2ca6657a17..75a2fc6034 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -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.a = "#{sOk}"; + mControllerButtons.b = "#{sCancel}"; } void CountDialog::openCountDialog(const std::string& item, const std::string& message, const int maxCount) @@ -86,4 +89,18 @@ 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_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; + } } diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 95d4a9a8d4..695de09215 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -38,6 +38,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; }; } From a144793753a489a067da28ffbd6019bc3617a5e9 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 18 May 2025 22:45:03 -0700 Subject: [PATCH 093/455] Fix Y button changing character --- apps/openmw/mwgui/savegamedialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 2484b0b1d2..fe45fa8729 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -525,6 +525,7 @@ namespace MWGui else index++; mCharacterSelection->setIndexSelected(index); + onCharacterSelected(mCharacterSelection, index); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { From 0aad0d379aafe41352b55b1f10657fb51134d23d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 18 May 2025 22:45:22 -0700 Subject: [PATCH 094/455] Minimal controller support for dialog window --- apps/openmw/mwgui/dialogue.cpp | 135 +++++++++++++++++++++++++++++++++ apps/openmw/mwgui/dialogue.hpp | 11 +++ components/widgets/list.cpp | 5 ++ components/widgets/list.hpp | 1 + 4 files changed, 152 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e14c400978..3633c90e6a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -88,6 +88,10 @@ namespace MWGui mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + + mDisableGamepadCursor = Settings::gui().mControllerMenus; + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; } void PersuasionDialog::adjustAction(MyGUI::Widget* action, int& totalHeight) @@ -144,6 +148,24 @@ namespace MWGui else mMainWidget->setSize(mInitialMainWidgetWidth, mMainWidget->getSize().height); + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + mButtons.clear(); + mButtons.push_back(mAdmireButton); + mButtons.push_back(mIntimidateButton); + mButtons.push_back(mTauntButton); + if (mBribe10Button->getEnabled()) + mButtons.push_back(mBribe10Button); + if (mBribe100Button->getEnabled()) + mButtons.push_back(mBribe100Button); + if (mBribe1000Button->getEnabled()) + mButtons.push_back(mBribe1000Button); + + for (int i = 0; i < mButtons.size(); i++) + mButtons[i]->setStateSelected(i == 0); + } + WindowModal::onOpen(); } @@ -152,6 +174,34 @@ namespace MWGui return mAdmireButton; } + bool PersuasionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + onPersuade(mButtons[mControllerFocus]); + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancel(mCancelButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == 0) + mControllerFocus = mButtons.size() - 1; + else + mControllerFocus--; + mButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + mButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus == mButtons.size() - 1) + mControllerFocus = 0; + else + mControllerFocus++; + mButtons[mControllerFocus]->setStateSelected(true); + } + + return true; + } + // -------------------------------------------------------------------------------------------------- Response::Response(std::string_view text, std::string_view title, bool needMargin) @@ -335,6 +385,9 @@ namespace MWGui mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + + mControllerButtons.a = "#{sAsk}"; + mControllerButtons.b = "#{sGoodbye}"; } void DialogueWindow::onTradeComplete() @@ -488,6 +541,14 @@ namespace MWGui updateTopics(); updateTopicsPane(); // force update for new services + if (Settings::gui().mControllerMenus && !sameActor) + { + setControllerFocus(mControllerFocus, false); + // Reset focus to very top. Maybe change this to mTopicsList->getItemCount() - mKeywords.size()? + mControllerFocus = 0; + setControllerFocus(mControllerFocus, true); + } + updateDisposition(); restock(); } @@ -601,6 +662,9 @@ namespace MWGui redrawTopicsList(); updateHistory(); + + if (Settings::gui().mControllerMenus) + setControllerFocus(mControllerFocus, true); } void DialogueWindow::updateHistory(bool scrollbar) @@ -847,4 +911,75 @@ namespace MWGui && actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"); } + void DialogueWindow::setControllerFocus(int index, bool focused) + { + // List is mTopicsList + "Goodbye" button below the list. + if (index < 0 || index > mTopicsList->getItemCount()) + return; + + if (index == mTopicsList->getItemCount()) + { + mGoodbyeButton->setStateSelected(focused); + } + else { + std::string keyword = mTopicsList->getItemNameAt(mControllerFocus); + if (keyword.length() == 0) + return; + + MyGUI::Button* button = mTopicsList->getItemWidget(keyword); + button->setStateSelected(focused); + } + + if (focused) + { + // Scroll the side bar to keep the active item in view + if (index <= 5) + mTopicsList->setViewOffset(0); + else + mTopicsList->setViewOffset(-20 * (index - 5)); + } + } + + bool DialogueWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mControllerFocus == mTopicsList->getItemCount()) + onGoodbyeActivated(); + else + onSelectListItem(mTopicsList->getItemNameAt(mControllerFocus), mControllerFocus); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onGoodbyeActivated(); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + // Number of items is mTopicsList.length+1 because of "Goodbye" button. + setControllerFocus(mControllerFocus, false); + if (mControllerFocus <= 0) + mControllerFocus = mTopicsList->getItemCount(); + else if (mTopicsList->getItemNameAt(mControllerFocus - 1).length() == 0) + mControllerFocus -= 2; // Skip separator + else + mControllerFocus--; + setControllerFocus(mControllerFocus, true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + // Number of items is mTopicsList.length+1 because of "Goodbye" button. + setControllerFocus(mControllerFocus, false); + if (mControllerFocus >= mTopicsList->getItemCount()) + mControllerFocus = 0; + else if (mControllerFocus == mTopicsList->getItemCount() - 1) + mControllerFocus = mTopicsList->getItemCount(); + else if (mTopicsList->getItemNameAt(mControllerFocus + 1).length() == 0) + mControllerFocus += 2; // Skip separator + else + mControllerFocus++; + setControllerFocus(mControllerFocus, true); + } + + return true; + } } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 8a8b309401..68de4b73d7 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -49,6 +49,9 @@ namespace MWGui MyGUI::Widget* getDefaultKeyFocus() override; + protected: + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + private: std::unique_ptr mCallback; @@ -65,6 +68,9 @@ namespace MWGui MyGUI::Widget* mActionsBox; Gui::AutoSizedTextBox* mGoldLabel; + std::vector mButtons; + int mControllerFocus = 0; + void adjustAction(MyGUI::Widget* action, int& totalHeight); void onCancel(MyGUI::Widget* sender); @@ -186,6 +192,8 @@ namespace MWGui void onReferenceUnavailable() override; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + private: void updateDisposition(); void restock(); @@ -220,6 +228,9 @@ namespace MWGui std::unique_ptr mCallback; std::unique_ptr mGreetingCallback; + void setControllerFocus(int index, bool focused); + int mControllerFocus = 0; + void updateTopicFormat(); }; } diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 896057443c..28fa48e081 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -173,4 +173,9 @@ namespace Gui { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } + + void MWList::setViewOffset(int offset) + { + mScrollView->setViewOffset(MyGUI::IntPoint(0, offset)); + } } diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 3d5e320cf7..022214dd1c 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -48,6 +48,7 @@ namespace Gui ///< get widget for an item name, useful to set up tooltip void scrollToTop(); + void setViewOffset(int offset); void setPropertyOverride(std::string_view _key, std::string_view _value) override; From f36401438fa915b10d82b9fcef6e0b3c2408536b Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 19 May 2025 23:43:28 -0700 Subject: [PATCH 095/455] Move wrap function to windowbase so all windows can use it --- apps/openmw/mwgui/race.cpp | 10 ---------- apps/openmw/mwgui/windowbase.cpp | 10 ++++++++++ apps/openmw/mwgui/windowbase.hpp | 2 ++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 6aecaa0b88..172812bf47 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -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& left, const std::pair& right) { return left.second.compare(right.second) < 0; diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index ae12076d2d..995731a775 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -15,6 +15,16 @@ using namespace MWGui; +int MWGui::wrap(int index, int max) +{ + if (index < 0) + return max - 1; + else if (index >= max) + return 0; + else + return index; +} + WindowBase::WindowBase(std::string_view parLayout) : Layout(parLayout) { diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index f14c41dca1..2b1afbda85 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -14,6 +14,8 @@ namespace MWGui { class DragAndDrop; + int wrap(int index, int max); + struct ControllerButtonStr { std::string a; From f03f242e4a9016ac1dcfc0d72aae7480825a8ef2 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 20 May 2025 00:12:34 -0700 Subject: [PATCH 096/455] Allow controller to select choice text in dialogue --- apps/openmw/mwgui/bookpage.cpp | 6 ++++ apps/openmw/mwgui/bookpage.hpp | 2 ++ apps/openmw/mwgui/dialogue.cpp | 64 ++++++++++++++++++++++++---------- apps/openmw/mwgui/dialogue.hpp | 2 ++ 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 47e85b1f4b..cb0717edf3 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -1288,6 +1288,12 @@ namespace MWGui void unadviseLinkClicked() override { mPageDisplay->mLinkClicked = std::function(); } + void setFocusItem(BookTypesetter::Style* itemStyle) override + { + mPageDisplay->mFocusItem = (TypesetBookImpl::StyleImpl*)itemStyle; + mPageDisplay->dirtyFocusItem(); + } + protected: void initialiseOverride() override { diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index d42fb4783f..43de2c09ac 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -164,6 +164,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; }; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 3633c90e6a..5472f02f04 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -691,6 +691,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& choice : mChoices) { auto link = std::make_unique(choice.second); @@ -702,6 +704,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(); @@ -944,7 +947,12 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus == mTopicsList->getItemCount()) + if (mChoices.size() > 0) + { + if (mControllerChoice >= 0 && mControllerChoice < mChoices.size()) + onChoiceActivated(mControllerChoice + 1); // +1 because choices are indexed starting at 1 + } + else if (mControllerFocus == mTopicsList->getItemCount()) onGoodbyeActivated(); else onSelectListItem(mTopicsList->getItemNameAt(mControllerFocus), mControllerFocus); @@ -955,29 +963,47 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { - // Number of items is mTopicsList.length+1 because of "Goodbye" button. - setControllerFocus(mControllerFocus, false); - if (mControllerFocus <= 0) - mControllerFocus = mTopicsList->getItemCount(); - else if (mTopicsList->getItemNameAt(mControllerFocus - 1).length() == 0) - mControllerFocus -= 2; // Skip separator + if (mChoices.size() > 0) + { + // In-dialogue choice (red text) + mControllerChoice = std::clamp(mControllerChoice - 1, 0, (int)mChoices.size() - 1); + mHistory->setFocusItem(mChoiceStyles.at(mControllerChoice)); + } else - mControllerFocus--; - setControllerFocus(mControllerFocus, true); + { + // 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).length() == 0) + mControllerFocus -= 2; // Skip separator + else + mControllerFocus--; + setControllerFocus(mControllerFocus, true); + } } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { - // Number of items is mTopicsList.length+1 because of "Goodbye" button. - setControllerFocus(mControllerFocus, false); - if (mControllerFocus >= mTopicsList->getItemCount()) - mControllerFocus = 0; - else if (mControllerFocus == mTopicsList->getItemCount() - 1) - mControllerFocus = mTopicsList->getItemCount(); - else if (mTopicsList->getItemNameAt(mControllerFocus + 1).length() == 0) - mControllerFocus += 2; // Skip separator + if (mChoices.size() > 0) + { + // In-dialogue choice (red text) + mControllerChoice = std::clamp(mControllerChoice + 1, 0, (int)mChoices.size() - 1); + mHistory->setFocusItem(mChoiceStyles.at(mControllerChoice)); + } else - mControllerFocus++; - setControllerFocus(mControllerFocus, true); + { + // Number of items is mTopicsList.length+1 because of "Goodbye" button. + setControllerFocus(mControllerFocus, false); + if (mControllerFocus >= mTopicsList->getItemCount()) + mControllerFocus = 0; + else if (mControllerFocus == mTopicsList->getItemCount() - 1) + mControllerFocus = mTopicsList->getItemCount(); // "Goodbye" button + else if (mTopicsList->getItemNameAt(mControllerFocus + 1).length() == 0) + mControllerFocus += 2; // Skip separator + else + mControllerFocus++; + setControllerFocus(mControllerFocus, true); + } } return true; diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 68de4b73d7..9a19617425 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -205,6 +205,7 @@ namespace MWGui std::vector> mHistoryContents; std::vector> mChoices; + std::vector mChoiceStyles; bool mGoodbye; std::vector> mLinks; @@ -230,6 +231,7 @@ namespace MWGui void setControllerFocus(int index, bool focused); int mControllerFocus = 0; + int mControllerChoice = -1; void updateTopicFormat(); }; From 23c733ef947c067d32118878b7e2149d687279fc Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 20 May 2025 00:27:14 -0700 Subject: [PATCH 097/455] Use wrap helper function to simplify some controller handler logic --- apps/openmw/mwgui/class.cpp | 20 ++++---------------- apps/openmw/mwgui/dialogue.cpp | 10 ++-------- apps/openmw/mwgui/journalwindow.cpp | 8 ++------ apps/openmw/mwgui/messagebox.cpp | 10 ++-------- apps/openmw/mwgui/savegamedialog.cpp | 5 +---- apps/openmw/mwgui/settingswindow.cpp | 10 ++-------- 6 files changed, 13 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index d4de67dbec..e658001e5a 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -488,10 +488,7 @@ namespace MWGui return true; mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == 0) - mControllerFocus = mButtons.size() - 1; - else - mControllerFocus--; + mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) @@ -502,10 +499,7 @@ namespace MWGui return true; mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == mButtons.size() - 1) - mControllerFocus = 0; - else - mControllerFocus++; + mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } @@ -715,19 +709,13 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == 0) - mControllerFocus = mButtons.size() - 1; - else - mControllerFocus--; + mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == mButtons.size() - 1) - mControllerFocus = 0; - else - mControllerFocus++; + mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } return true; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 5472f02f04..a76e793d81 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -183,19 +183,13 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == 0) - mControllerFocus = mButtons.size() - 1; - else - mControllerFocus--; + mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == mButtons.size() - 1) - mControllerFocus = 0; - else - mControllerFocus++; + mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 20b7acbd9b..69004171db 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -764,9 +764,7 @@ namespace { // Scroll through the list of quests or topics mButtons[mSelectedQuest]->setStateSelected(false); - mSelectedQuest--; - if (mSelectedQuest < 0) - mSelectedQuest = mButtons.size() - 1; + mSelectedQuest = MWGui::wrap(mSelectedQuest - 1, mButtons.size()); mButtons[mSelectedQuest]->setStateSelected(true); } return true; @@ -777,9 +775,7 @@ namespace { // Scroll through the list of quests or topics mButtons[mSelectedQuest]->setStateSelected(false); - mSelectedQuest++; - if (mSelectedQuest > mButtons.size() - 1) - mSelectedQuest = 0; + mSelectedQuest = MWGui::wrap(mSelectedQuest + 1, mButtons.size()); mButtons[mSelectedQuest]->setStateSelected(true); } return true; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 311743fd36..94807dd54c 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -461,10 +461,7 @@ namespace MWGui return true; mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == 0) - mControllerFocus = mButtons.size() - 1; - else - mControllerFocus--; + mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) @@ -475,10 +472,7 @@ namespace MWGui return true; mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == mButtons.size() - 1) - mControllerFocus = 0; - else - mControllerFocus++; + mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index fe45fa8729..9e215637a0 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -520,10 +520,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_Y) { uint32_t index = mCharacterSelection->getIndexSelected(); - if (index >= mCharacterSelection->getItemCount() - 1) - index = 0; - else - index++; + index = wrap(index + 1, mCharacterSelection->getItemCount()); mCharacterSelection->setIndexSelected(index); onCharacterSelected(mCharacterSelection, index); } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 2fa6a72fd8..70f2ab8ff3 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1144,10 +1144,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { uint32_t index = mSettingsTab->getIndexSelected(); - if (index <= 0) - index = mSettingsTab->getItemCount() - 1; - else - index--; + index = wrap(index - 1, mSettingsTab->getItemCount()); mSettingsTab->setIndexSelected(index); return true; @@ -1155,10 +1152,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { uint32_t index = mSettingsTab->getIndexSelected(); - if (index >= mSettingsTab->getItemCount() - 1) - index = 0; - else - index++; + index = wrap(index + 1, mSettingsTab->getItemCount()); mSettingsTab->setIndexSelected(index); return true; From ba0a57937128528c14b3b654ca9a28f6f62a27b2 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 20 May 2025 19:32:13 -0700 Subject: [PATCH 098/455] Play correct sound and update button overlay when swapping between menus --- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ff2e9754af..2fefe8f249 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -948,7 +948,9 @@ namespace MWGui { mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); } - playSound(ESM::RefId::stringRefId("Menu Click")); + updateControllerButtonsOverlay(); + if (winCount > 1) + playSound(ESM::RefId::stringRefId("Menu Size")); } } From 5e3a49ade5ebe93e1e2ef91643a310401bc88bd8 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 20 May 2025 22:41:17 -0700 Subject: [PATCH 099/455] Make right stick scroll dialog history --- apps/openmw/mwgui/dialogue.cpp | 2 ++ apps/openmw/mwgui/scrollwindow.cpp | 1 + apps/openmw/mwgui/windowbase.hpp | 2 ++ apps/openmw/mwinput/controllermanager.cpp | 9 +++++++-- apps/openmw/mwinput/mousemanager.cpp | 13 +++++++++++++ apps/openmw/mwinput/mousemanager.hpp | 1 + 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index a76e793d81..1dd3f051ef 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -380,8 +380,10 @@ namespace MWGui mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + mControllerScrollWidget = mHistory->getParent(); mControllerButtons.a = "#{sAsk}"; mControllerButtons.b = "#{sGoodbye}"; + mControllerButtons.rStick = "#{sScrollup}"; } void DialogueWindow::onTradeComplete() diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index ae47a361a2..d032963417 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -39,6 +39,7 @@ namespace MWGui mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); + mControllerScrollWidget = mTextView; mControllerButtons.b = "#{sClose}"; mControllerButtons.rStick = "#{sScrolldown}"; diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 2b1afbda85..8fb499ec7e 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -77,6 +77,7 @@ namespace MWGui static void clampWindowCoordinates(MyGUI::Window* window); virtual ControllerButtonStr* getControllerButtons() { return &mControllerButtons; } + MyGUI::Widget* getControllerScrollWidget() { return mControllerScrollWidget; } bool isGamepadCursorAllowed() { return !mDisableGamepadCursor; } virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; }; virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return true; }; @@ -88,6 +89,7 @@ namespace MWGui ControllerButtonStr mControllerButtons; bool mActiveControllerWindow = false; bool mDisableGamepadCursor = false; + MyGUI::Widget* mControllerScrollWidget = nullptr; private: void onDoubleClick(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index d1deef5011..48034cae01 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -353,10 +353,15 @@ namespace MWInput if (mGamepadGuiCursorEnabled && (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY)) { - // Treat the left stick like a cursor. Fall through. + // Treat the left stick like a cursor, which is the default behavior. return false; } - else if (topWin->onControllerThumbstickEvent(arg)) + + // On some windows, treat right stick like a scroll wheel. + if (arg.axis == SDL_CONTROLLER_AXIS_RIGHTY && topWin->getControllerScrollWidget() != nullptr) + mMouseManager->warpMouseToWidget(topWin->getControllerScrollWidget()); + + if (topWin->onControllerThumbstickEvent(arg)) { // Window handled the event. return true; diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index eed95cf1c9..2c6798406b 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -263,4 +263,17 @@ namespace MWInput mInputWrapper->warpMouse( static_cast(mGuiCursorX * guiUiScale), static_cast(mGuiCursorY * guiUiScale)); } + + void MouseManager::warpMouseToWidget(MyGUI::Widget* widget) + { + float widgetX = widget->getAbsoluteCoord().left + 4; + float widgetY = widget->getAbsoluteCoord().top + 4; + if (std::abs(mGuiCursorX - widgetX) > 1 || std::abs(mGuiCursorY - widgetY) > 1) + { + mGuiCursorX = widgetX; + mGuiCursorY = widgetY; + warpMouse(); + } + } + } diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index 5de8a8f3bc..0a9c4eccd7 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -32,6 +32,7 @@ namespace MWInput bool injectMouseButtonRelease(Uint8 button); void injectMouseMove(float xMove, float yMove, float mouseWheelMove); void warpMouse(); + void warpMouseToWidget(MyGUI::Widget* widget); void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; } void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; } From e3daff7b171def4f62288452e5a0339e90bb7827 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 10 May 2025 06:02:33 +0300 Subject: [PATCH 100/455] Support extended selection in the directory picker (#8113) --- apps/launcher/datafilespage.cpp | 72 ++++++++++++++++------------- apps/launcher/datafilespage.hpp | 11 +++-- apps/launcher/ui/directorypicker.ui | 9 +++- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 9f92e8ed0f..aa68cf11e3 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -38,8 +38,6 @@ #include "utils/profilescombobox.hpp" #include "utils/textinputdialog.hpp" -#include "ui_directorypicker.h" - const char* Launcher::DataFilesPage::mDefaultContentListName = "Default"; namespace @@ -154,6 +152,7 @@ namespace Launcher Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, Config::LauncherSettings& launcherSettings, MainDialog* parent) : QWidget(parent) + , mDirectoryPickerDialog(new QDialog(this)) , mMainDialog(parent) , mCfgMgr(cfg) , mGameSettings(gameSettings) @@ -162,6 +161,7 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C , mReloadCellsThread(&DataFilesPage::reloadCells, this) { ui.setupUi(this); + mDirectoryPicker.setupUi(mDirectoryPickerDialog); setObjectName("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector(ui.contentSelectorWidget, /*showOMWScripts=*/true); const QString encoding = mGameSettings.value("encoding", { "win1252" }).value; @@ -259,6 +259,7 @@ void Launcher::DataFilesPage::buildView() buildArchiveContextMenu(); buildDataFilesContextMenu(); + buildDirectoryPickerContextMenu(); } void Launcher::DataFilesPage::slotCopySelectedItemsPaths() @@ -291,8 +292,10 @@ void Launcher::DataFilesPage::buildArchiveContextMenu() &DataFilesPage::slotShowArchiveContextMenu); mArchiveContextMenu = new QMenu(ui.archiveListWidget); - mArchiveContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); - mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); + mArchiveContextMenu->addAction(tr("&Check Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Checked); }); + mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Unchecked); }); } void Launcher::DataFilesPage::buildDataFilesContextMenu() @@ -307,6 +310,18 @@ void Launcher::DataFilesPage::buildDataFilesContextMenu() tr("&Open Path in File Explorer"), this, &Launcher::DataFilesPage::slotOpenSelectedItemsPaths); } +void Launcher::DataFilesPage::buildDirectoryPickerContextMenu() +{ + connect(mDirectoryPicker.dirListWidget, &QListWidget::customContextMenuRequested, this, + &DataFilesPage::slotShowDirectoryPickerContextMenu); + + mDirectoryPickerMenu = new QMenu(mDirectoryPicker.dirListWidget); + mDirectoryPickerMenu->addAction(tr("&Check Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Checked); }); + mDirectoryPickerMenu->addAction(tr("&Uncheck Selected"), this, + [this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Unchecked); }); +} + bool Launcher::DataFilesPage::loadSettings() { ui.navMeshMaxSizeSpinBox->setValue(getMaxNavMeshDbFileSizeMiB()); @@ -813,28 +828,22 @@ void Launcher::DataFilesPage::addSubdirectories(bool append) return; } - QDialog dialog; - Ui::SelectSubdirs select; - - select.setupUi(&dialog); + mDirectoryPicker.dirListWidget->clear(); for (const auto& dir : subdirs) { if (!ui.directoryListWidget->findItems(dir, Qt::MatchFixedString).isEmpty()) continue; - const auto lastRow = select.dirListWidget->count(); - select.dirListWidget->addItem(dir); - select.dirListWidget->item(lastRow)->setCheckState(Qt::Unchecked); + QListWidgetItem* newDir = new QListWidgetItem(dir, mDirectoryPicker.dirListWidget); + newDir->setCheckState(Qt::Unchecked); } - dialog.show(); - - if (dialog.exec() == QDialog::Rejected) + if (mDirectoryPickerDialog->exec() == QDialog::Rejected) return; - for (int i = 0; i < select.dirListWidget->count(); ++i) + for (int i = 0; i < mDirectoryPicker.dirListWidget->count(); ++i) { - const auto* dir = select.dirListWidget->item(i); + const auto* dir = mDirectoryPicker.dirListWidget->item(i); if (dir->checkState() == Qt::Checked) { ui.directoryListWidget->insertItem(selectedRow, dir->text()); @@ -885,38 +894,35 @@ void Launcher::DataFilesPage::removeDirectory() refreshDataFilesView(); } +void Launcher::DataFilesPage::showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos) +{ + QPoint globalPos = list->viewport()->mapToGlobal(pos); + menu->exec(globalPos); +} + void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos) { - QPoint globalPos = ui.archiveListWidget->viewport()->mapToGlobal(pos); - mArchiveContextMenu->exec(globalPos); + showContextMenu(mArchiveContextMenu, ui.archiveListWidget, pos); } void Launcher::DataFilesPage::slotShowDataFilesContextMenu(const QPoint& pos) { - QPoint globalPos = ui.directoryListWidget->viewport()->mapToGlobal(pos); - mDataFilesContextMenu->exec(globalPos); + showContextMenu(mDataFilesContextMenu, ui.directoryListWidget, pos); } -void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(bool checked) +void Launcher::DataFilesPage::slotShowDirectoryPickerContextMenu(const QPoint& pos) { - Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked; + showContextMenu(mDirectoryPickerMenu, mDirectoryPicker.dirListWidget, pos); +} - for (QListWidgetItem* selectedItem : ui.archiveListWidget->selectedItems()) +void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState) +{ + for (QListWidgetItem* selectedItem : list->selectedItems()) { selectedItem->setCheckState(checkState); } } -void Launcher::DataFilesPage::slotUncheckMultiSelectedItems() -{ - setCheckStateForMultiSelectedItems(false); -} - -void Launcher::DataFilesPage::slotCheckMultiSelectedItems() -{ - setCheckStateForMultiSelectedItems(true); -} - void Launcher::DataFilesPage::moveSources(QListWidget* sourceList, int step) { const QList> sortedItems = sortedSelectedItems(sourceList, step > 0); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index ac98743dab..bf2b8044ef 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -2,6 +2,7 @@ #define DATAFILESPAGE_H #include "ui_datafilespage.h" +#include "ui_directorypicker.h" #include @@ -45,8 +46,11 @@ namespace Launcher ContentSelectorView::ContentSelector* mSelector; Ui::DataFilesPage ui; + QDialog* mDirectoryPickerDialog; + Ui::SelectSubdirs mDirectoryPicker; QMenu* mArchiveContextMenu; QMenu* mDataFilesContextMenu; + QMenu* mDirectoryPickerMenu; public: explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, @@ -86,8 +90,7 @@ namespace Launcher void slotShowArchiveContextMenu(const QPoint& pos); void slotShowDataFilesContextMenu(const QPoint& pos); - void slotCheckMultiSelectedItems(); - void slotUncheckMultiSelectedItems(); + void slotShowDirectoryPickerContextMenu(const QPoint& pos); void on_newProfileAction_triggered(); void on_cloneProfileAction_triggered(); @@ -143,7 +146,9 @@ namespace Launcher void buildView(); void buildArchiveContextMenu(); void buildDataFilesContextMenu(); - void setCheckStateForMultiSelectedItems(bool checked); + void buildDirectoryPickerContextMenu(); + void showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos); + void setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState); void setProfile(int index, bool savePrevious); void setProfile(const QString& previous, const QString& current, bool savePrevious); void removeProfile(const QString& profile); diff --git a/apps/launcher/ui/directorypicker.ui b/apps/launcher/ui/directorypicker.ui index 6350bcd2d3..c32a78b53e 100644 --- a/apps/launcher/ui/directorypicker.ui +++ b/apps/launcher/ui/directorypicker.ui @@ -25,7 +25,14 @@ - + + + QAbstractItemView::ExtendedSelection + + + Qt::CustomContextMenu + + From b5aaf4ca30357e5a08c968dc3fd019a6313b365f Mon Sep 17 00:00:00 2001 From: Aussiemon Date: Thu, 22 May 2025 16:03:52 -0600 Subject: [PATCH 101/455] Fix mPickpocketDetected flag being set after use --- apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index fa7bce449b..437a21fb23 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -129,8 +129,8 @@ namespace MWGui { MWBase::Environment::get().getMechanicsManager()->commitCrime( player, mActor, MWBase::MechanicsManager::OT_Pickpocket, ESM::RefId(), 0, true); - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); return false; } else From 192cf1535b9743f7ff2a1f97cae9df1d10d24f4b Mon Sep 17 00:00:00 2001 From: Aussiemon Date: Thu, 22 May 2025 16:10:05 -0600 Subject: [PATCH 102/455] Fix second instance of mPickpocketDetected set after use --- apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 437a21fb23..8756427589 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -101,8 +101,8 @@ namespace MWGui { MWBase::Environment::get().getMechanicsManager()->commitCrime( player, mActor, MWBase::MechanicsManager::OT_Pickpocket, ESM::RefId(), 0, true); - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); } } From 2df7ded814e3fd3f8e997ad29ab609cf7c8df23f Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Fri, 23 May 2025 18:15:24 +0800 Subject: [PATCH 103/455] respawning npc respawns in origin cell --- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwworld/cellstore.cpp | 9 +++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0b61436d11..c03d132503 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1427,7 +1427,8 @@ namespace MWClass ptr.getRefData().setCustomData(nullptr); // Reset to original position - MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3()); + MWBase::Environment::get().getWorld()->moveObject( + ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3()); MWBase::Environment::get().getWorld()->rotateObject( ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none); } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index fca0135e13..61618c001e 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1354,6 +1354,15 @@ namespace MWWorld return {}; } + CellStore* MWWorld::CellStore::getOriginCell(const Ptr& object) const + { + MovedRefTracker::const_iterator found = mMovedHere.find(object.getBase()); + if (found != mMovedHere.end()) + return found->second; + + return object.getCell(); + } + Ptr CellStore::getPtr(ESM::RefId id) { if (mState == CellStore::State_Unloaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 126935ace5..59127d6186 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -338,6 +338,8 @@ namespace MWWorld Ptr getMovedActor(int actorId) const; + CellStore* getOriginCell(const Ptr& object) const; + Ptr getPtr(ESM::RefId id); private: From 86783e87580fe6e73a0852ed71d76ff34ade824e Mon Sep 17 00:00:00 2001 From: Kuyondo Date: Fri, 23 May 2025 23:46:42 +0800 Subject: [PATCH 104/455] also for creatures --- apps/openmw/mwclass/creature.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9224f6f0d8..324ba5f98b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -871,7 +871,8 @@ namespace MWClass ptr.getRefData().setCustomData(nullptr); // Reset to original position - MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3()); + MWBase::Environment::get().getWorld()->moveObject( + ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3()); MWBase::Environment::get().getWorld()->rotateObject( ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none); } From d759418f016c79885c9607a488e9ccc9668cff13 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 24 May 2025 14:17:41 -0700 Subject: [PATCH 105/455] Add controller support for all four inventory menus --- apps/openmw/mwbase/inputmanager.hpp | 2 + apps/openmw/mwbase/windowmanager.hpp | 3 + apps/openmw/mwgui/companionwindow.hpp | 5 + apps/openmw/mwgui/container.cpp | 9 +- apps/openmw/mwgui/container.hpp | 6 +- apps/openmw/mwgui/hud.cpp | 16 ++- apps/openmw/mwgui/hud.hpp | 2 + apps/openmw/mwgui/inventorywindow.cpp | 157 ++++++++++++++++++++++- apps/openmw/mwgui/inventorywindow.hpp | 4 + apps/openmw/mwgui/itemview.cpp | 55 ++++++-- apps/openmw/mwgui/itemview.hpp | 4 +- apps/openmw/mwgui/mapwindow.cpp | 54 ++++++++ apps/openmw/mwgui/mapwindow.hpp | 5 + apps/openmw/mwgui/spellview.cpp | 92 +++++++++++++ apps/openmw/mwgui/spellview.hpp | 14 ++ apps/openmw/mwgui/spellwindow.cpp | 28 ++++ apps/openmw/mwgui/spellwindow.hpp | 2 + apps/openmw/mwgui/statswindow.cpp | 25 ++++ apps/openmw/mwgui/statswindow.hpp | 4 + apps/openmw/mwgui/windowmanagerimp.cpp | 69 +++++----- apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 7 + apps/openmw/mwinput/inputmanagerimp.hpp | 1 + components/widgets/sharedstatebutton.hpp | 5 +- 24 files changed, 515 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 5ee20476b3..f5adc42340 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -61,6 +62,7 @@ namespace MWBase virtual float getControllerAxisValue(SDL_GameControllerAxis axis) const = 0; // returns value in range [-1, 1] virtual int getMouseMoveX() const = 0; virtual int getMouseMoveY() const = 0; + virtual void warpMouseToWidget(MyGUI::Widget* widget) = 0; /// Actions available for binding to keyboard buttons virtual const std::initializer_list& getActionKeySorting() = 0; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 5302270ee0..27331699cf 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -11,6 +11,7 @@ #include +#include "../mwgui/hud.hpp" #include "../mwgui/mode.hpp" #include "../mwgui/windowbase.hpp" @@ -158,7 +159,9 @@ namespace MWBase virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; + virtual MWGui::HUD* getHud() = 0; virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0; + virtual std::vector 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; diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 97f3a0072e..21869c58f5 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_MWGUI_COMPANIONWINDOW_H #define OPENMW_MWGUI_COMPANIONWINDOW_H +#include "companionitemmodel.hpp" +#include "itemmodel.hpp" #include "referenceinterface.hpp" #include "windowbase.hpp" @@ -32,6 +34,9 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Companion"; } + MWGui::ItemView* getItemView() { return mItemView; } + ItemModel* getModel() { return mModel; } + private: ItemView* mItemView; SortFilterItemModel* mSortModel; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 54eb6122c5..c0896d5b5e 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -58,9 +58,9 @@ namespace MWGui setCoord(200, 0, 600, 300); mControllerButtons.a = "#{sTake}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{sBack}"; mControllerButtons.x = "#{sTakeAll}"; - mControllerButtons.y = "#{sInfo}"; + mControllerButtons.r3 = "#{sInfo}"; mControllerButtons.l2 = "#{sInventory}"; } @@ -385,12 +385,13 @@ namespace MWGui if (mDisposeCorpseButton->getVisible()) onDisposeCorpseButtonClicked(mDisposeCorpseButton); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { - mItemView->onControllerButtonEvent(arg); + mItemView->onControllerButtonEvent(arg.button); } return true; diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 08497e0369..96ee6a6380 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -41,8 +41,12 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Container"; } ControllerButtonStr* getControllerButtons() override; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; void setActiveControllerWindow(bool active) override; + MWGui::ItemView* getItemView() { return mItemView; } + ItemModel* getModel() { return mModel; } + private: DragAndDrop* mDragAndDrop; @@ -68,8 +72,6 @@ namespace MWGui bool onTakeItem(const ItemStack& item, int count); void onReferenceUnavailable() override; - - bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } #endif // CONTAINER_H diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 0a37c93b4f..a62120fc10 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -243,6 +243,17 @@ namespace MWGui mDrowningBar->setVisible(visible); } + void HUD::dropDraggedItem(float mouseX, float mouseY) + { + if (!mDragAndDrop->mIsOnDragAndDrop) + return; + + MWBase::Environment::get().getWorld()->breakInvisibility(MWMechanics::getPlayer()); + + WorldItemModel drop(mouseX, mouseY); + mDragAndDrop->drop(&drop, nullptr); + } + void HUD::onWorldClicked(MyGUI::Widget* _sender) { if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) @@ -252,15 +263,12 @@ namespace MWGui if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld - MWBase::Environment::get().getWorld()->breakInvisibility(MWMechanics::getPlayer()); - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); - WorldItemModel drop(mouseX, mouseY); - mDragAndDrop->drop(&drop, nullptr); + dropDraggedItem(mouseX, mouseY); winMgr->changePointer("arrow"); } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 8dd98628c4..1a1076ff68 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -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; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a773b4635b..27c4bbf57d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -31,8 +31,11 @@ #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/npcstats.hpp" +#include "companionwindow.hpp" +#include "container.hpp" #include "countdialog.hpp" #include "draganddrop.hpp" +#include "hud.hpp" #include "inventoryitemmodel.hpp" #include "itemview.hpp" #include "settings.hpp" @@ -127,6 +130,13 @@ namespace MWGui setGuiMode(mGuiMode); + if (Settings::gui().mControllerMenus) + { + mControllerButtons.b = "#{sBack}"; + mControllerButtons.r1 = "Filter"; + mControllerButtons.r3 = "#{sInfo}"; + } + adjustPanes(); } @@ -302,7 +312,9 @@ namespace MWGui } } - if (count > 1 && !shift) + // Show a dialog to select a count of items, but not when using an item from the inventory + // in controller mode. In that case, we skip the dialog and just use one item immediately. + if (count > 1 && !shift && !(Settings::gui().mControllerMenus && mGuiMode == MWGui::GM_Inventory)) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}"; @@ -866,4 +878,147 @@ namespace MWGui const MyGUI::IntSize viewport = getPreviewViewportSize(); return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1)); } + + ControllerButtonStr* InventoryWindow::getControllerButtons() + { + switch (mGuiMode) + { + case MWGui::GM_Companion: + case MWGui::GM_Container: + mControllerButtons.a = "Put"; + mControllerButtons.x = "#{sTakeAll}"; + mControllerButtons.y = ""; + mControllerButtons.r2 = "#{sContainer}"; + break; + case MWGui::GM_Barter: + mControllerButtons.a = "#{sSell}"; + mControllerButtons.x = ""; + mControllerButtons.y = ""; + mControllerButtons.r2 = "#{sBarter}"; + break; + case MWGui::GM_Inventory: + default: + mControllerButtons.a = "#{sEquip}"; + mControllerButtons.x = "#{sDrop}"; + mControllerButtons.y = "#{sUnequip}"; + mControllerButtons.r2 = ""; + break; + } + return &mControllerButtons; + } + + bool InventoryWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A); + // The following actions are done here, not in onItemSelectedFromSourceModel, because we + // want the mouse to work even in controller mode. + if (mGuiMode == MWGui::GM_Inventory && mDragAndDrop->mIsOnDragAndDrop) + { + // Drag and drop the item on the avatar to activate it. + onAvatarClicked(nullptr); // Equip or use + // Drop any remaining items back in inventory. This is needed when clicking on a + // stack of items; we only want to use the first item. + onBackgroundSelected(); + } + else if (mGuiMode == MWGui::GM_Companion && mDragAndDrop->mIsOnDragAndDrop) + { + // Drag and drop the item on the companion's window. + MWGui::CompanionWindow* companionWindow = + (MWGui::CompanionWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0); + mDragAndDrop->drop(companionWindow->getModel(), companionWindow->getItemView()); + } + else if (mGuiMode == MWGui::GM_Container && mDragAndDrop->mIsOnDragAndDrop) + { + // Drag and drop the item on the container window. + MWGui::ContainerWindow* containerWindow = + (MWGui::ContainerWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0); + mDragAndDrop->drop(containerWindow->getModel(), containerWindow->getItemView()); + } + // GM_Barter is handled by onControllerButtonEvent. No other steps are necessary. + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + if (mGuiMode == MWGui::GM_Inventory) + { + // Drop the item into the gameworld + mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A); + if (mDragAndDrop->mIsOnDragAndDrop) + MWBase::Environment::get().getWindowManager()->getHud()->dropDraggedItem(0.5f, 0.5f); + } + else if (mGuiMode == MWGui::GM_Container) + { + // Take all. Pass the button press to the container window and let it do the + // logic of taking all. + MWGui::ContainerWindow* containerWindow = + (MWGui::ContainerWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0); + containerWindow->onControllerButtonEvent(arg); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) + { + if (mGuiMode == MWGui::GM_Inventory) + { + // Unequip an item. + mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A); + onBackgroundSelected(); // Drop on inventory background to unequip + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + { + if (mFilterAll->getStateSelected()) + onFilterChanged(mFilterMisc); + else if (mFilterWeapon->getStateSelected()) + onFilterChanged(mFilterAll); + else if (mFilterApparel->getStateSelected()) + onFilterChanged(mFilterWeapon); + else if (mFilterMagic->getStateSelected()) + onFilterChanged(mFilterApparel); + else if (mFilterMisc->getStateSelected()) + onFilterChanged(mFilterMagic); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + if (mFilterAll->getStateSelected()) + onFilterChanged(mFilterWeapon); + else if (mFilterWeapon->getStateSelected()) + onFilterChanged(mFilterApparel); + else if (mFilterApparel->getStateSelected()) + onFilterChanged(mFilterMagic); + else if (mFilterMagic->getStateSelected()) + onFilterChanged(mFilterMisc); + else if (mFilterMisc->getStateSelected()) + onFilterChanged(mFilterAll); + } + else + { + mItemView->onControllerButtonEvent(arg.button); + } + + return true; + } + + void InventoryWindow::setActiveControllerWindow(bool active) + { + if (!Settings::gui().mControllerMenus) + return; + + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) + { + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + + adjustPanes(); + updatePreviewSize(); + } + + mItemView->setActiveControllerWindow(active); + WindowBase::setActiveControllerWindow(active); + } } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 9fc77ceec5..5b4d61f272 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -67,8 +67,12 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Inventory"; } + ControllerButtonStr* getControllerButtons() override; + protected: void onTitleDoubleClicked() override; + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + void setActiveControllerWindow(bool active) override; private: DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 526f231956..a223bde5e0 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -9,6 +9,10 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" + #include "itemmodel.hpp" #include "itemwidget.hpp" @@ -75,8 +79,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - if (mControllerFocus >= mItemCount) - mControllerFocus = mItemCount - 1; + mControllerTooltip = false; + mControllerFocus = std::clamp(mControllerFocus, 0, mItemCount - 1); updateControllerFocus(-1, mControllerFocus); } @@ -182,26 +186,49 @@ namespace MWGui void ItemView::setActiveControllerWindow(bool active) { + mControllerActiveWindow = active; + + if (mControllerTooltip) + { + MWBase::Environment::get().getWindowManager()->setCursorActive(false); + mControllerTooltip = false; + } + if (active) updateControllerFocus(-1, mControllerFocus); else updateControllerFocus(mControllerFocus, -1); } - void ItemView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + void ItemView::onControllerButtonEvent(const unsigned char button) { if (!mItemCount) return; int prevFocus = mControllerFocus; - if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP && mControllerFocus % mRows != 0) + if (button == SDL_CONTROLLER_BUTTON_A) + { + // Select the focused item, if any. + if (mControllerFocus >= 0 && mControllerFocus < mItemCount) + { + MyGUI::Widget* dragArea = mScrollView->getChildAt(0); + onSelectedItem(dragArea->getChildAt(mControllerFocus)); + } + } + else if (button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) + { + // Toggle info tooltip + mControllerTooltip = !mControllerTooltip; + updateControllerFocus(-1, mControllerFocus); + } + else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP && mControllerFocus % mRows != 0) mControllerFocus--; - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN && mControllerFocus % mRows != mRows - 1) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN && mControllerFocus % mRows != mRows - 1) mControllerFocus++; - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows) mControllerFocus -= mRows; - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mControllerFocus + mRows < mItemCount) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mControllerFocus + mRows < mItemCount) mControllerFocus += mRows; if (mControllerFocus < 0) @@ -213,25 +240,29 @@ namespace MWGui updateControllerFocus(prevFocus, mControllerFocus); } - void ItemView::updateControllerFocus(int _prevFocus, int _newFocus) + void ItemView::updateControllerFocus(int prevFocus, int newFocus) { if (!mItemCount) return; MyGUI::Widget* dragArea = mScrollView->getChildAt(0); - if (_prevFocus >= 0 && _prevFocus < mItemCount) + if (prevFocus >= 0 && prevFocus < mItemCount) { - ItemWidget* prev = (ItemWidget *)dragArea->getChildAt(_prevFocus); + ItemWidget* prev = (ItemWidget *)dragArea->getChildAt(prevFocus); if (prev) prev->setControllerFocus(false); } - if (_newFocus >= 0 && _newFocus < mItemCount) + if (mControllerActiveWindow && newFocus >= 0 && newFocus < mItemCount) { - ItemWidget* focused = (ItemWidget *)dragArea->getChildAt(_newFocus); + ItemWidget* focused = (ItemWidget *)dragArea->getChildAt(newFocus); if (focused) + { focused->setControllerFocus(true); + if (mControllerTooltip) + MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); + } } } } diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index b1fa941ef4..091436ab05 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -35,7 +35,7 @@ namespace MWGui void setActiveControllerWindow(bool active); int getControllerFocus() { return mControllerFocus; } int getItemCount() { return mItemCount; } - void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); + void onControllerButtonEvent(const unsigned char button); private: void initialiseOverride() override; @@ -55,6 +55,8 @@ namespace MWGui int mItemCount = 0; int mRows; int mControllerFocus = 0; + bool mControllerTooltip; + bool mControllerActiveWindow; void updateControllerFocus(int prevFocus, int newFocus); }; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index bf4bd7644c..ec9c509797 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -832,6 +833,14 @@ namespace MWGui mGlobalMap->setVisible(global); mLocalMap->setVisible(!global); + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}"; + mControllerButtons.y = "#{sCenter}"; + mControllerButtons.rStick = "#{sMove}"; + } } void MapWindow::onNoteEditOk() @@ -1208,6 +1217,8 @@ namespace MWGui mLocalMap->setVisible(!global); mButton->setCaptionWithReplacing(global ? "#{sLocal}" : "#{sWorld}"); + mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}"; + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void MapWindow::onPinToggled() @@ -1368,6 +1379,49 @@ namespace MWGui mGlobalMapRender->asyncWritePng(); } + bool MapWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + onWorldButtonClicked(mButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_Y) + centerView(); + + return true; + } + + bool MapWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) + { + int dx = arg.axis == SDL_CONTROLLER_AXIS_RIGHTX ? -10.0f * arg.value / 32767 : 0; + int dy = arg.axis == SDL_CONTROLLER_AXIS_RIGHTY ? -10.0f * arg.value / 32767 : 0; + if (dx == 0 && dy == 0) + return true; + else if (!Settings::map().mGlobal) + { + mNeedDoorMarkersUpdate = true; + mLocalMap->setViewOffset( + MyGUI::IntPoint( + mLocalMap->getViewOffset().left + dx, mLocalMap->getViewOffset().top + dy)); + } + else + { + mGlobalMap->setViewOffset( + MyGUI::IntPoint( + mGlobalMap->getViewOffset().left + dx, mGlobalMap->getViewOffset().top + dy)); + } + return true; + } + + void MapWindow::setActiveControllerWindow(bool active) + { + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + + WindowBase::setActiveControllerWindow(active); + } + // ------------------------------------------------------------------- EditNoteDialog::EditNoteDialog() diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index ed070c5407..31fe971c60 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -265,6 +265,11 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Map"; } + protected: + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; + void setActiveControllerWindow(bool active) override; + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 678f6ffe1f..739699f35c 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -9,6 +9,9 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" + #include "tooltips.hpp" namespace MWGui @@ -88,6 +91,9 @@ namespace MWGui const int spellHeight = Settings::gui().mFontSize + 2; mLines.clear(); + mButtons.clear(); + mGroupIndices.clear(); + mControllerTooltip = false; while (mScrollView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); @@ -115,6 +121,7 @@ namespace MWGui t->setCaption(spell.mName + captionSuffix); t->setTextAlign(MyGUI::Align::Left); adjustSpellWidget(spell, i, t); + mButtons.emplace_back(t); if (!spell.mCostColumn.empty() && mShowCostColumn) { @@ -256,6 +263,8 @@ namespace MWGui } else mLines.emplace_back(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex); + + mGroupIndices.push_back(mButtons.size()); } void SpellView::setSize(const MyGUI::IntSize& _value) @@ -316,4 +325,87 @@ namespace MWGui { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } + + void SpellView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (mButtons.empty()) + return; + + int prevFocus = mControllerFocus; + + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Select the focused item, if any. + if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + onSpellSelected(mButtons.at(mControllerFocus)); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) + { + // Toggle info tooltip + mControllerTooltip = !mControllerTooltip; + if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons.at(mControllerFocus)); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + mControllerFocus--; + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + mControllerFocus++; + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + { + // Jump to first item in previous group + int prevGroupIndex = 0; + for (int groupIndex : mGroupIndices) + { + if (groupIndex >= mControllerFocus) + break; + else + prevGroupIndex = groupIndex; + } + mControllerFocus = prevGroupIndex; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + // Jump to first item in next group + for (int groupIndex : mGroupIndices) + { + if (groupIndex > mControllerFocus) + { + mControllerFocus = groupIndex; + break; + } + } + } + + if (mControllerFocus < 0) + mControllerFocus = mButtons.size() - 1; + else if (mControllerFocus >= mButtons.size()) + mControllerFocus = 0; + + if (prevFocus != mControllerFocus) + updateControllerFocus(prevFocus, mControllerFocus); + } + + void SpellView::updateControllerFocus(int prevFocus, int newFocus) + { + if (mButtons.empty()) + return; + + if (prevFocus >= 0 && prevFocus < mButtons.size()) + { + Gui::SharedStateButton* prev = mButtons.at(prevFocus); + if (prev) + prev->onMouseLostFocus(nullptr); + } + + if (newFocus >= 0 && newFocus < mButtons.size()) + { + Gui::SharedStateButton* focused = mButtons.at(newFocus); + if (focused) + { + focused->onMouseSetFocus(nullptr); + if (mControllerTooltip) + MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); + } + } + } } diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index caff43a33e..cf3a43354f 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -5,6 +5,9 @@ #include #include +#include + +#include #include "spellmodel.hpp" @@ -54,6 +57,8 @@ namespace MWGui void resetScrollbars(); + void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); + private: MyGUI::ScrollView* mScrollView; @@ -89,6 +94,15 @@ namespace MWGui void addGroup(const std::string& label1, const std::string& label2); void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget); + /// Keep a list of buttons for controller navigation + std::vector mButtons; + /// Keep a list of group offsets for controller navigation + std::vector mGroupIndices; + + int mControllerFocus; + bool mControllerTooltip; + void updateControllerFocus(int prevFocus, int newFocus); + void onSpellSelected(MyGUI::Widget* _sender); void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index d183a00273..63ba83a895 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -55,6 +57,13 @@ namespace MWGui // Adjust the spell filtering widget size because of MyGUI limitations. int filterWidth = mSpellView->getSize().width - deleteButton->getSize().width - 3; mFilterEdit->setSize(filterWidth, mFilterEdit->getSize().height); + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.r3 = "#{sInfo}"; + } } void SpellWindow::onPinToggled() @@ -259,4 +268,23 @@ namespace MWGui else onSpellSelected(spell.mId); } + + bool SpellWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + else + mSpellView->onControllerButtonEvent(arg); + + return true; + } + + void SpellWindow::setActiveControllerWindow(bool active) + { + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + + WindowBase::setActiveControllerWindow(active); + } } diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index e35c5cdc4c..c27ec276a3 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -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 mSpellIcons; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 69f0b4b449..cc5c934662 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,11 @@ namespace MWGui MyGUI::Window* t = mMainWidget->castType(); t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); + if (Settings::gui().mControllerMenus) + { + mControllerButtons.b = "#{sBack}"; + } + onWindowResize(t); } @@ -723,4 +729,23 @@ namespace MWGui else if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } + + bool StatsWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + + return true; + } + + void StatsWindow::setActiveControllerWindow(bool active) + { + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + + onWindowResize(window); + + WindowBase::setActiveControllerWindow(active); + } } diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index a3fc3157c5..3021873aa8 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -47,6 +47,10 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Stats"; } + protected: + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + void setActiveControllerWindow(bool active) override; + private: void addSkills(const std::vector& skills, const std::string& titleId, const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2fefe8f249..ac58f1e589 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -896,23 +896,13 @@ namespace MWGui return state.mWindows[activeIndex]; } - else - { - // return pinned windows if visible - // REMOVEME - Log(Debug::Error) << "getTopWindow: " << mGuiModeStates[GM_Inventory].mWindows.size() << " pinned windows"; - for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows) - if (window->isVisible()) - return window; - else - Log(Debug::Error) << "-- Skipping hidden window " << window; - } + return nullptr; } void WindowManager::cycleActiveControllerWindow(bool next) { - if (mGuiModes.empty()) + if (!Settings::gui().mControllerMenus || mGuiModes.empty()) return; GuiMode mode = mGuiModes.back(); @@ -1380,13 +1370,6 @@ namespace MWGui { for (WindowBase* window : mGuiModeStates[mode].mWindows) window->setPtr(arg); - - // Activate first visible window - mActiveControllerWindows[mode] = -1; - cycleActiveControllerWindow(true); - - // REMOVEME - Log(Debug::Error) << "pushGuiMode: mode=" << mode << ", activeIndex=" << mActiveControllerWindows[mode]; } catch (...) { @@ -1398,6 +1381,13 @@ namespace MWGui updateVisible(); MWBase::Environment::get().getLuaManager()->uiModeChanged(arg); + + if (Settings::gui().mControllerMenus) + { + // Activate first visible window. This needs to be called after updateVisible. + mActiveControllerWindows[mode] = std::max(mActiveControllerWindows[mode] - 1, -1); + cycleActiveControllerWindow(true); + } } void WindowManager::setCullMask(uint32_t mask) @@ -1452,6 +1442,15 @@ namespace MWGui // To make sure that console window get focus again if (mConsole && mConsole->isVisible()) mConsole->onOpen(); + + if (Settings::gui().mControllerMenus && !mGuiModes.empty()) + { + // Re-apply any controller-specific window changes. + const GuiMode mode = mGuiModes.back(); + int winCount = mGuiModeStates[mode].mWindows.size(); + for (int i = 0; i < winCount; i++) + mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == mActiveControllerWindows[mode]); + } } void WindowManager::removeGuiMode(GuiMode mode) @@ -1577,6 +1576,10 @@ namespace MWGui mConsole->executeFile(path); } + std::vector WindowManager::getGuiModeWindows(GuiMode mode) + { + return mGuiModeStates[mode].mWindows; + } MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; @@ -1589,6 +1592,10 @@ namespace MWGui { return mConfirmationDialog; } + MWGui::HUD* WindowManager::getHud() + { + return mHud; + } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; @@ -2119,20 +2126,22 @@ namespace MWGui void WindowManager::updatePinnedWindows() { mInventoryWindow->setPinned(Settings::windows().mInventoryPin); - if (Settings::windows().mInventoryHidden) - mShown = (GuiWindow)(mShown ^ GW_Inventory); - mMap->setPinned(Settings::windows().mMapPin); - if (Settings::windows().mMapHidden) - mShown = (GuiWindow)(mShown ^ GW_Map); - mSpellWindow->setPinned(Settings::windows().mSpellsPin); - if (Settings::windows().mSpellsHidden) - mShown = (GuiWindow)(mShown ^ GW_Magic); - mStatsWindow->setPinned(Settings::windows().mStatsPin); - if (Settings::windows().mStatsHidden) - mShown = (GuiWindow)(mShown ^ GW_Stats); + + // Hide hidden inventory windows, but not in controller mode. + if (!Settings::gui().mControllerMenus) + { + if (Settings::windows().mInventoryHidden) + mShown = (GuiWindow)(mShown ^ GW_Inventory); + if (Settings::windows().mMapHidden) + mShown = (GuiWindow)(mShown ^ GW_Map); + if (Settings::windows().mSpellsHidden) + mShown = (GuiWindow)(mShown ^ GW_Magic); + if (Settings::windows().mStatsHidden) + mShown = (GuiWindow)(mShown ^ GW_Stats); + } } void WindowManager::pinWindow(GuiWindow window) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 5dbf3c4689..54695e8234 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -185,7 +185,9 @@ namespace MWGui MWGui::CountDialog* getCountDialog() override; MWGui::ConfirmationDialog* getConfirmationDialog() override; MWGui::TradeWindow* getTradeWindow() override; + MWGui::HUD* getHud() override; MWGui::PostProcessorHud* getPostProcessorHud() override; + std::vector getGuiModeWindows(GuiMode mode) override; /// Make the player use an item, while updating GUI state accordingly void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions = false) override; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 328757a954..12d56a3321 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -189,6 +189,13 @@ namespace MWInput return mMouseManager->getMouseMoveY(); } + void InputManager::warpMouseToWidget(MyGUI::Widget* widget) + { + mMouseManager->warpMouseToWidget(widget); + mMouseManager->injectMouseMove(1, 0, 0); + MWBase::Environment::get().getWindowManager()->setCursorActive(true); + } + const std::initializer_list& InputManager::getActionKeySorting() { return mBindingsManager->getActionKeySorting(); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 39a1133db5..b2899e6831 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -81,6 +81,7 @@ namespace MWInput float getControllerAxisValue(SDL_GameControllerAxis axis) const override; int getMouseMoveX() const override; int getMouseMoveY() const override; + void warpMouseToWidget(MyGUI::Widget* widget) override; int getNumActions() override { return A_Last; } const std::initializer_list& getActionKeySorting() override; diff --git a/components/widgets/sharedstatebutton.hpp b/components/widgets/sharedstatebutton.hpp index 33dd70c763..1cf7364bf0 100644 --- a/components/widgets/sharedstatebutton.hpp +++ b/components/widgets/sharedstatebutton.hpp @@ -21,13 +21,14 @@ namespace Gui public: SharedStateButton(); + void onMouseSetFocus(MyGUI::Widget* _old) override; + void onMouseLostFocus(MyGUI::Widget* _new) override; + protected: void updateButtonState(); void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override; void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) override; - void onMouseSetFocus(MyGUI::Widget* _old) override; - void onMouseLostFocus(MyGUI::Widget* _new) override; void baseUpdateEnable() override; void shutdownOverride() override; From f1c34ea7b1bef0ad9be2bb8ee156d14536c6a3d6 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 24 May 2025 16:00:13 -0700 Subject: [PATCH 106/455] Add a tab bar above inventory to show active tab and change tabs with mouse --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/inventorytabsoverlay.cpp | 49 ++++++++++++++++ apps/openmw/mwgui/inventorytabsoverlay.hpp | 24 ++++++++ apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 2 +- apps/openmw/mwgui/spellwindow.cpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 58 ++++++++++++++----- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++ files/data/CMakeLists.txt | 1 + files/data/mygui/openmw_inventory_tabs.layout | 51 ++++++++++++++++ 12 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 apps/openmw/mwgui/inventorytabsoverlay.cpp create mode 100644 apps/openmw/mwgui/inventorytabsoverlay.hpp create mode 100644 files/data/mygui/openmw_inventory_tabs.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index eeb46edce0..399514cab4 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 controllerbuttonsoverlay + postprocessorhud settings controllerbuttonsoverlay inventorytabsoverlay ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 27331699cf..180c9bfee3 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -389,6 +389,7 @@ namespace MWBase virtual MWGui::WindowBase* getActiveControllerWindow() = 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 void updateControllerButtonsOverlay() = 0; // Used in Lua bindings diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp new file mode 100644 index 0000000000..21254c7840 --- /dev/null +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -0,0 +1,49 @@ +#include "inventorytabsoverlay.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +namespace MWGui +{ + InventoryTabsOverlay::InventoryTabsOverlay() + : WindowBase("openmw_inventory_tabs.layout") + { + MyGUI::Button* tab; + + getWidget(tab, "TabMap"); + tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); + mTabs.push_back(tab); + + getWidget(tab, "TabInventory"); + tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); + mTabs.push_back(tab); + + getWidget(tab, "TabSpells"); + tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); + mTabs.push_back(tab); + + getWidget(tab, "TabStats"); + tab->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryTabsOverlay::onTabClicked); + mTabs.push_back(tab); + } + + void InventoryTabsOverlay::onTabClicked(MyGUI::Widget* sender) + { + for (int i = 0; i < mTabs.size(); i++) + { + if (mTabs[i] == sender) + { + Log(Debug::Verbose) << "InventoryTabsOverlay::onTabClicked " << i; + MWBase::Environment::get().getWindowManager()->setActiveControllerWindow(GM_Inventory, i); + //setTab(i); + break; + } + } + } + + void InventoryTabsOverlay::setTab(int index) + { + for (int i = 0; i < mTabs.size(); i++) + mTabs[i]->setStateSelected(i == index); + } +} diff --git a/apps/openmw/mwgui/inventorytabsoverlay.hpp b/apps/openmw/mwgui/inventorytabsoverlay.hpp new file mode 100644 index 0000000000..5368d9710f --- /dev/null +++ b/apps/openmw/mwgui/inventorytabsoverlay.hpp @@ -0,0 +1,24 @@ +#ifndef MWGUI_INVENTORYTABSSOVERLAY_H +#define MWGUI_INVENTORYTABSSOVERLAY_H + +#include + +#include "windowbase.hpp" + +namespace MWGui +{ + class InventoryTabsOverlay : public WindowBase + { + public: + InventoryTabsOverlay(); + + void setTab(int index); + + private: + std::vector mTabs; + + void onTabClicked(MyGUI::Widget* sender); + }; +} + +#endif diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 27c4bbf57d..6b5bbde324 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1012,7 +1012,7 @@ namespace MWGui { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); adjustPanes(); updatePreviewSize(); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ec9c509797..ce3524d052 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1417,7 +1417,7 @@ namespace MWGui { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 63ba83a895..fb61b59ed9 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -283,7 +283,7 @@ namespace MWGui { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index cc5c934662..e67417f60e 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -742,7 +742,7 @@ namespace MWGui { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 0 : viewSize.height + 1, viewSize.width, viewSize.height - 48); + window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); onWindowResize(window); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ac58f1e589..732ff405d3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -182,6 +182,7 @@ namespace MWGui , mJailScreen(nullptr) , mContainerWindow(nullptr) , mControllerButtonsOverlay(nullptr) + , mInventoryTabsOverlay(nullptr) , mTranslationDataStorage(translationDataStorage) , mInputBlocker(nullptr) , mHudEnabled(true) @@ -510,6 +511,10 @@ namespace MWGui mControllerButtonsOverlay = controllerButtonsOverlay.get(); mWindows.push_back(std::move(controllerButtonsOverlay)); + auto inventoryTabsOverlay = std::make_unique(); + mInventoryTabsOverlay = inventoryTabsOverlay.get(); + mWindows.push_back(std::move(inventoryTabsOverlay)); + mInputBlocker = MyGUI::Gui::getInstance().createWidget( {}, 0, 0, w, h, MyGUI::Align::Stretch, "InputBlocker"); @@ -668,8 +673,13 @@ namespace MWGui mSpellWindow->setVisible( mSpellWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Magic) && (mAllowed & GW_Magic)); - if (Settings::gui().mControllerMenus && mControllerButtonsOverlay) - mControllerButtonsOverlay->setVisible(false); + if (Settings::gui().mControllerMenus) + { + if (mControllerButtonsOverlay) + mControllerButtonsOverlay->setVisible(false); + if (mInventoryTabsOverlay) + mInventoryTabsOverlay->setVisible(false); + } return; } else if (getMode() != GM_Inventory) @@ -929,19 +939,27 @@ namespace MWGui } // REMOVEME - Log(Debug::Error) << "focusNextWindow: mode=" << mode << ", activeIndex=" << activeIndex; + Log(Debug::Error) << "cycleActiveControllerWindow: mode=" << mode << ", activeIndex=" << activeIndex; if (mActiveControllerWindows[mode] != activeIndex) - { - mActiveControllerWindows[mode] = activeIndex; - for (int i = 0; i < winCount; i++) - { - mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); - } - updateControllerButtonsOverlay(); - if (winCount > 1) - playSound(ESM::RefId::stringRefId("Menu Size")); - } + setActiveControllerWindow(mode, activeIndex); + } + + void WindowManager::setActiveControllerWindow(GuiMode mode, int activeIndex) + { + int winCount = mGuiModeStates[mode].mWindows.size(); + if (winCount == 0) + return; + + mActiveControllerWindows[mode] = std::clamp(activeIndex, 0, winCount - 1); + + for (int i = 0; i < winCount; i++) + mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); + + updateControllerButtonsOverlay(); + + if (winCount > 1) + playSound(ESM::RefId::stringRefId("Menu Size")); } void WindowManager::update(float frameDuration) @@ -1010,6 +1028,9 @@ namespace MWGui if (mControllerButtonsOverlay && mControllerButtonsOverlay->isVisible()) mControllerButtonsOverlay->onFrame(frameDuration); + if (mInventoryTabsOverlay && mInventoryTabsOverlay->isVisible()) + mInventoryTabsOverlay->onFrame(frameDuration); + if (!gameRunning) return; @@ -2560,19 +2581,24 @@ namespace MWGui void WindowManager::updateControllerButtonsOverlay() { - if (!Settings::gui().mControllerMenus ||!mControllerButtonsOverlay) + if (!Settings::gui().mControllerMenus || !mControllerButtonsOverlay) return; WindowBase* topWin = this->getActiveControllerWindow(); if (!topWin || !topWin->isVisible()) { - // REMOVEME - Log(Debug::Error) << "WindowManager::updateControllerButtonsOverlay: hiding overlay"; mControllerButtonsOverlay->setVisible(false); + mInventoryTabsOverlay->setVisible(false); return; } // setButtons will handle setting visibility based on if any buttons are defined. mControllerButtonsOverlay->setButtons(topWin->getControllerButtons()); + if (getMode() == GM_Inventory) { + mInventoryTabsOverlay->setVisible(true); + mInventoryTabsOverlay->setTab(mActiveControllerWindows[GM_Inventory]); + } + else + mInventoryTabsOverlay->setVisible(false); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 54695e8234..d5e36a0971 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -27,6 +27,7 @@ #include "charactercreation.hpp" #include "controllerbuttonsoverlay.hpp" #include "draganddrop.hpp" +#include "inventorytabsoverlay.hpp" #include "mapwindow.hpp" #include "messagebox.hpp" #include "settings.hpp" @@ -120,6 +121,7 @@ namespace MWGui class JailScreen; class KeyboardNavigation; class ControllerButtonsOverlay; + class InventoryTabsOverlay; class WindowManager : public MWBase::WindowManager { @@ -393,6 +395,7 @@ namespace MWGui WindowBase* getActiveControllerWindow() override; void cycleActiveControllerWindow(bool next) override; + void setActiveControllerWindow(GuiMode mode, int activeIndex) override; void updateControllerButtonsOverlay() override; // Used in Lua bindings @@ -463,6 +466,7 @@ namespace MWGui JailScreen* mJailScreen; ContainerWindow* mContainerWindow; ControllerButtonsOverlay* mControllerButtonsOverlay; + InventoryTabsOverlay* mInventoryTabsOverlay; std::vector> mWindows; diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index 6339344500..2b8ea7a568 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -172,6 +172,7 @@ set(BUILTIN_DATA_FILES mygui/openmw_infobox.layout mygui/openmw_interactive_messagebox.layout mygui/openmw_interactive_messagebox_notransp.layout + mygui/openmw_inventory_tabs.layout mygui/openmw_inventory_window.layout mygui/openmw_journal.layout mygui/openmw_journal.skin.xml diff --git a/files/data/mygui/openmw_inventory_tabs.layout b/files/data/mygui/openmw_inventory_tabs.layout new file mode 100644 index 0000000000..3787452586 --- /dev/null +++ b/files/data/mygui/openmw_inventory_tabs.layout @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3eec24b2a63fb07ac40edcc033f2f0b74d15a0cc Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 24 May 2025 20:08:45 -0700 Subject: [PATCH 107/455] Limit the size of controller-focused inventory windows on large screens --- apps/openmw/mwgui/inventorywindow.cpp | 8 +++++++- apps/openmw/mwgui/mapwindow.cpp | 9 ++++++++- apps/openmw/mwgui/spellwindow.cpp | 9 ++++++++- apps/openmw/mwgui/statswindow.cpp | 9 ++++++++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 6b5bbde324..5258c12db5 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1010,9 +1010,15 @@ namespace MWGui if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) { + // Fill the screen, or limit to a certain size on large screens. MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + int width = std::min(viewSize.width, 2200); + int height = std::min(viewSize.height - 48 - 48, 1200); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; + MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); adjustPanes(); updatePreviewSize(); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ce3524d052..9c43b44572 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1415,9 +1415,16 @@ namespace MWGui void MapWindow::setActiveControllerWindow(bool active) { + // 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(); + int width = std::min(viewSize.width, 1552); + int height = std::min(viewSize.height - 48 - 48, 1572); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; + MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index fb61b59ed9..fbeb6f35c3 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -281,9 +281,16 @@ namespace MWGui void SpellWindow::setActiveControllerWindow(bool active) { + // 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, 600); + int height = std::min(viewSize.height - 48 - 48, 750); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; + MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index e67417f60e..9971b15799 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -740,9 +740,16 @@ namespace MWGui void StatsWindow::setActiveControllerWindow(bool active) { + // 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, 600); + int height = std::min(viewSize.height - 48 - 48, 750); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; + MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(0, active ? 48 : viewSize.height + 49, viewSize.width, viewSize.height - 48 - 48); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); onWindowResize(window); From cf26020ed6b0ad40162d9a3471be206e790c3135 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 24 May 2025 20:56:43 -0700 Subject: [PATCH 108/455] Fix several minor issues with controller inventory menus --- apps/openmw/mwgui/mapwindow.cpp | 31 +++++++++++++++++++++----- apps/openmw/mwgui/mapwindow.hpp | 1 + apps/openmw/mwgui/spellview.cpp | 4 ++++ apps/openmw/mwgui/spellwindow.cpp | 3 +++ apps/openmw/mwgui/statswindow.cpp | 5 ++++- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 9c43b44572..ab55d261df 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1384,20 +1384,42 @@ namespace MWGui 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; } bool MapWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { - int dx = arg.axis == SDL_CONTROLLER_AXIS_RIGHTX ? -10.0f * arg.value / 32767 : 0; - int dy = arg.axis == SDL_CONTROLLER_AXIS_RIGHTY ? -10.0f * arg.value / 32767 : 0; + int dx = arg.axis == SDL_CONTROLLER_AXIS_RIGHTX ? -30.0f * arg.value / 32767 : 0; + int dy = arg.axis == SDL_CONTROLLER_AXIS_RIGHTY ? -30.0f * arg.value / 32767 : 0; + shiftMap(dx, dy); + + return true; + } + + void MapWindow::shiftMap(int dx, int dy) + { if (dx == 0 && dy == 0) - return true; - else if (!Settings::map().mGlobal) + return; + + if (!Settings::map().mGlobal) { mNeedDoorMarkersUpdate = true; mLocalMap->setViewOffset( @@ -1410,7 +1432,6 @@ namespace MWGui MyGUI::IntPoint( mGlobalMap->getViewOffset().left + dx, mGlobalMap->getViewOffset().top + dy)); } - return true; } void MapWindow::setActiveControllerWindow(bool active) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 31fe971c60..f7e1fcc52f 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -288,6 +288,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); diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 739699f35c..2ebd1751da 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "tooltips.hpp" @@ -337,7 +338,10 @@ namespace MWGui { // Select the focused item, if any. if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + { onSpellSelected(mButtons.at(mControllerFocus)); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); + } } else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) { diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index fbeb6f35c3..e218b9beed 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -292,6 +292,9 @@ namespace MWGui MyGUI::Window* window = mMainWidget->castType(); window->setCoord(x, active ? y : viewSize.height + 1, width, height); + if (active) + mSpellView->update(); + WindowBase::setActiveControllerWindow(active); } } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 9971b15799..1d0af185d4 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -83,6 +83,8 @@ namespace MWGui if (Settings::gui().mControllerMenus) { + mControllerButtons.lStick = "#{sInfo}"; + mControllerButtons.rStick = "#{sScrolldown}"; mControllerButtons.b = "#{sBack}"; } @@ -751,7 +753,8 @@ namespace MWGui MyGUI::Window* window = mMainWidget->castType(); window->setCoord(x, active ? y : viewSize.height + 1, width, height); - onWindowResize(window); + if (active) + onWindowResize(window); WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 732ff405d3..96b4303bbb 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -957,6 +957,7 @@ namespace MWGui mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); updateControllerButtonsOverlay(); + setCursorActive(false); if (winCount > 1) playSound(ESM::RefId::stringRefId("Menu Size")); From d6c23f7664a60bde3ad363249ab9f44c75be9b5a Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 24 May 2025 21:28:43 -0700 Subject: [PATCH 109/455] Add controller support to map's node dialog --- apps/openmw/mwgui/mapwindow.cpp | 85 +++++++++++++++++++++++++++++++++ apps/openmw/mwgui/mapwindow.hpp | 5 ++ 2 files changed, 90 insertions(+) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ab55d261df..f8063657b8 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1463,6 +1463,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.a = "#{sOk}"; + mControllerButtons.b = "#{sCancel}"; + } } void EditNoteDialog::showDeleteButton(bool show) @@ -1490,6 +1496,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) @@ -1507,6 +1520,78 @@ namespace MWGui eventDeleteClicked(); } + ControllerButtonStr* EditNoteDialog::getControllerButtons() + { + mControllerButtons.x = 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) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index f7e1fcc52f..240c30adc0 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -212,6 +212,8 @@ namespace MWGui EventHandle_Void eventDeleteClicked; EventHandle_Void eventOkClicked; + ControllerButtonStr* 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 From 45de167b6b8fbd85db7738296d906c2f86176ff1 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 24 May 2025 21:29:06 -0700 Subject: [PATCH 110/455] Adjust layer of controller button overlay so it doesn't hide tooltips --- files/data/mygui/openmw_layers.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/data/mygui/openmw_layers.xml b/files/data/mygui/openmw_layers.xml index 597ba0de4c..f2eed488e1 100644 --- a/files/data/mygui/openmw_layers.xml +++ b/files/data/mygui/openmw_layers.xml @@ -14,13 +14,13 @@ + - From d6ed4164028e1caec48625001d52a8045d1be930 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 25 May 2025 16:12:19 -0700 Subject: [PATCH 111/455] Allow pinning map window in controller mode --- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 36 +++++++++++++++------ apps/openmw/mwgui/spellwindow.cpp | 22 +++++++------ apps/openmw/mwgui/statswindow.cpp | 26 +++++++++------- apps/openmw/mwgui/windowmanagerimp.cpp | 43 +++++++++++++++++--------- 5 files changed, 83 insertions(+), 46 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 5258c12db5..7f314fed14 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -216,7 +216,7 @@ namespace MWGui { mGuiMode = mode; const WindowSettingValues settings = getModeSettings(mGuiMode); - setPinButtonVisible(mode == GM_Inventory); + setPinButtonVisible(mode == GM_Inventory && !Settings::gui().mControllerMenus); const WindowRectSettingValues& rect = settings.mIsMaximized ? settings.mMaximized : settings.mRegular; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index f8063657b8..f90180fb44 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1029,7 +1029,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::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() @@ -1436,16 +1449,19 @@ namespace MWGui void MapWindow::setActiveControllerWindow(bool active) { - // 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(); - int width = std::min(viewSize.width, 1552); - int height = std::min(viewSize.height - 48 - 48, 1572); - int x = (viewSize.width - width) / 2; - int y = (viewSize.height - height) / 2; + if (MWBase::Environment::get().getWindowManager()->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(); + int width = std::min(viewSize.width, 1552); + int height = std::min(viewSize.height - 48 - 48, 1572); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; - MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(x, active ? y : viewSize.height + 1, width, height); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); + } WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index e218b9beed..3eb041f6ab 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -60,6 +60,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { + setPinButtonVisible(false); mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; mControllerButtons.r3 = "#{sInfo}"; @@ -281,16 +282,19 @@ namespace MWGui void SpellWindow::setActiveControllerWindow(bool active) { - // 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, 600); - int height = std::min(viewSize.height - 48 - 48, 750); - int x = (viewSize.width - width) / 2; - int y = (viewSize.height - height) / 2; + if (MWBase::Environment::get().getWindowManager()->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, 600); + int height = std::min(viewSize.height - 48 - 48, 750); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; - MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(x, active ? y : viewSize.height + 1, width, height); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); + } if (active) mSpellView->update(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 1d0af185d4..e71b35e364 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -83,6 +83,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { + setPinButtonVisible(false); mControllerButtons.lStick = "#{sInfo}"; mControllerButtons.rStick = "#{sScrolldown}"; mControllerButtons.b = "#{sBack}"; @@ -742,19 +743,22 @@ namespace MWGui void StatsWindow::setActiveControllerWindow(bool active) { - // 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, 600); - int height = std::min(viewSize.height - 48 - 48, 750); - int x = (viewSize.width - width) / 2; - int y = (viewSize.height - height) / 2; + if (MWBase::Environment::get().getWindowManager()->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, 600); + int height = std::min(viewSize.height - 48 - 48, 750); + int x = (viewSize.width - width) / 2; + int y = (viewSize.height - height) / 2; - MyGUI::Window* window = mMainWidget->castType(); - window->setCoord(x, active ? y : viewSize.height + 1, width, height); + MyGUI::Window* window = mMainWidget->castType(); + window->setCoord(x, active ? y : viewSize.height + 1, width, height); - if (active) - onWindowResize(window); + if (active) + onWindowResize(window); + } WindowBase::setActiveControllerWindow(active); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 96b4303bbb..77224790dd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1901,6 +1901,11 @@ namespace MWGui void WindowManager::onWindowChangeCoord(MyGUI::Window* window) { + // If using controller menus, don't persist changes to size of the stats or magic + // windows. + if (Settings::gui().mControllerMenus && (window == (MyGUI::Window*)mStatsWindow || window == (MyGUI::Window*)mSpellWindow)) + return; + const auto it = mTrackedWindows.find(window); if (it == mTrackedWindows.end()) return; @@ -2147,23 +2152,31 @@ namespace MWGui void WindowManager::updatePinnedWindows() { - mInventoryWindow->setPinned(Settings::windows().mInventoryPin); - mMap->setPinned(Settings::windows().mMapPin); - mSpellWindow->setPinned(Settings::windows().mSpellsPin); - mStatsWindow->setPinned(Settings::windows().mStatsPin); - - // Hide hidden inventory windows, but not in controller mode. - if (!Settings::gui().mControllerMenus) + if (Settings::gui().mControllerMenus) { - if (Settings::windows().mInventoryHidden) - mShown = (GuiWindow)(mShown ^ GW_Inventory); - if (Settings::windows().mMapHidden) - mShown = (GuiWindow)(mShown ^ GW_Map); - if (Settings::windows().mSpellsHidden) - mShown = (GuiWindow)(mShown ^ GW_Magic); - if (Settings::windows().mStatsHidden) - mShown = (GuiWindow)(mShown ^ GW_Stats); + // In controller mode, don't hide any menus and only allow pinning the map. + mInventoryWindow->setPinned(false); + mMap->setPinned(Settings::windows().mMapPin); + mSpellWindow->setPinned(false); + mStatsWindow->setPinned(false); + return; } + + mInventoryWindow->setPinned(Settings::windows().mInventoryPin); + if (Settings::windows().mInventoryHidden) + mShown = (GuiWindow)(mShown ^ GW_Inventory); + + mMap->setPinned(Settings::windows().mMapPin); + if (Settings::windows().mMapHidden) + mShown = (GuiWindow)(mShown ^ GW_Map); + + mSpellWindow->setPinned(Settings::windows().mSpellsPin); + if (Settings::windows().mSpellsHidden) + mShown = (GuiWindow)(mShown ^ GW_Magic); + + mStatsWindow->setPinned(Settings::windows().mStatsPin); + if (Settings::windows().mStatsHidden) + mShown = (GuiWindow)(mShown ^ GW_Stats); } void WindowManager::pinWindow(GuiWindow window) From 25fa9484d3da1604c44488879a60297ffdb4a997 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 25 May 2025 22:08:43 -0700 Subject: [PATCH 112/455] Add a default save game name in controller mode --- apps/openmw/mwgui/savegamedialog.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 9e215637a0..af75152e4b 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/timestamp.hpp" #include "../mwstate/character.hpp" @@ -147,6 +149,27 @@ 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(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 From cd745c7df3b96029753c375c0ca2f58dfc4b340e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 25 May 2025 22:51:43 -0700 Subject: [PATCH 113/455] Swap some controller button assignments --- apps/openmw/mwgui/container.cpp | 4 ++-- apps/openmw/mwgui/journalwindow.cpp | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index c0896d5b5e..1657225343 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -360,7 +360,7 @@ namespace MWGui ControllerButtonStr* ContainerWindow::getControllerButtons() { - mControllerButtons.l1 = mDisposeCorpseButton->getVisible() ? "#{sDisposeofCorpse}" : ""; + mControllerButtons.r1 = mDisposeCorpseButton->getVisible() ? "#{sDisposeofCorpse}" : ""; return &mControllerButtons; } @@ -380,7 +380,7 @@ namespace MWGui { onTakeAllButtonClicked(mTakeButton); } - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { if (mDisposeCorpseButton->getVisible()) onDisposeCorpseButtonClicked(mDisposeCorpseButton); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 69004171db..8fbeb161ec 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -281,6 +281,8 @@ namespace updateShowingPages(); updateCloseJournalButton(); + + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void setOptionsMode() @@ -480,6 +482,7 @@ namespace popBook(); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void addControllerButtons(Gui::MWList* _list, int _selectedIndex) @@ -518,6 +521,7 @@ namespace addControllerButtons(list, mSelectedQuest); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void notifyTopics(MyGUI::Widget* _sender) @@ -533,6 +537,7 @@ namespace setVisible(ShowActiveBTN, false); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } struct AddNamesToList @@ -594,6 +599,7 @@ namespace } MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); + MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); } void notifyShowAll(MyGUI::Widget* _sender) @@ -678,6 +684,7 @@ namespace mControllerButtons.b = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{sClose}"; mControllerButtons.l1 = mOptionsMode ? "" : "#{sPrev}"; mControllerButtons.r1 = mOptionsMode ? "" : "#{sNext}"; + mControllerButtons.r3 = mOptionsMode && mQuestMode ? "Show All" : ""; return &mControllerButtons; } @@ -758,6 +765,13 @@ namespace } return true; } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) // R3: Show All/Some + { + if (mAllQuests) + notifyShowActive(getWidget(ShowActiveBTN)); + else + notifyShowAll(getWidget(ShowAllBTN)); + } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { if (mOptionsMode && (mQuestMode || mTopicsMode)) From 71fd8b8840e25d6cd3857beb9f24555fc854af0e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 25 May 2025 23:14:13 -0700 Subject: [PATCH 114/455] Add controller support to spell buying and training windows --- apps/openmw/mwgui/spellbuyingwindow.cpp | 55 +++++++++++++++++++++++++ apps/openmw/mwgui/spellbuyingwindow.hpp | 3 ++ apps/openmw/mwgui/trainingwindow.cpp | 49 ++++++++++++++++++++++ apps/openmw/mwgui/trainingwindow.hpp | 4 ++ 4 files changed, 111 insertions(+) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 9fca86caba..e1f12ae6e0 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -31,6 +31,13 @@ namespace MWGui getWidget(mSpellsView, "SpellsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); + + if (Settings::gui().mControllerMenus) + { + mDisableGamepadCursor = true; + mControllerButtons.a = "#{sBuy}"; + mControllerButtons.b = "#{sBack}"; + } } bool SpellBuyingWindow::sortSpells(const ESM::Spell* left, const ESM::Spell* right) @@ -70,6 +77,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(toAdd); } void SpellBuyingWindow::clearSpells() @@ -79,6 +88,7 @@ namespace MWGui while (mSpellsView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mSpellsView->getChildAt(0)); mSpellsWidgetMap.clear(); + mSpellButtons.clear(); } void SpellBuyingWindow::setPtr(const MWWorld::Ptr& actor) @@ -129,6 +139,13 @@ namespace MWGui updateLabels(); + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + if (mSpellButtons.size() > 0) + mSpellButtons[0]->setStateSelected(true); + } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the // scrollbar is hidden mSpellsView->setVisibleVScroll(false); @@ -199,4 +216,42 @@ namespace MWGui mSpellsView->setViewOffset( MyGUI::IntPoint(0, static_cast(mSpellsView->getViewOffset().top + _rel * 0.3f))); } + + bool SpellBuyingWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + onSpellButtonClick(mSpellButtons[mControllerFocus]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCancelButtonClicked(mCancelButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mSpellButtons.size() <= 1) + return true; + + mSpellButtons[mControllerFocus]->setStateSelected(false); + mControllerFocus = wrap(mControllerFocus - 1, mSpellButtons.size()); + mSpellButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mSpellButtons.size() <= 1) + return true; + + mSpellButtons[mControllerFocus]->setStateSelected(false); + mControllerFocus = wrap(mControllerFocus + 1, mSpellButtons.size()); + mSpellButtons[mControllerFocus]->setStateSelected(true); + } + + // Scroll the list to keep the active item in view + if (mControllerFocus <= 5) + mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSpellsView->setViewOffset(MyGUI::IntPoint(0, -10 * (mControllerFocus - 5))); + + return true; + } } diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 257b8a0df9..234dcb5b9d 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -39,6 +39,7 @@ namespace MWGui MyGUI::ScrollView* mSpellsView; std::map mSpellsWidgetMap; + std::vector mSpellButtons; void onCancelButtonClicked(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender); @@ -55,6 +56,8 @@ namespace MWGui private: static bool sortSpells(const ESM::Spell* left, const ESM::Spell* right); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; }; } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 890aa0ba68..371f0e95aa 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -37,6 +37,13 @@ namespace MWGui mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &TrainingWindow::onTrainingProgressChanged); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &TrainingWindow::onTrainingFinished); + + if (Settings::gui().mControllerMenus) + { + mDisableGamepadCursor = true; + mControllerButtons.a = "#{sBuy}"; + mControllerButtons.b = "#{sBack}"; + } } void TrainingWindow::onOpen() @@ -105,6 +112,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; @@ -128,6 +136,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(); @@ -229,4 +247,35 @@ namespace MWGui return !mTimeAdvancer.isRunning(); } + bool TrainingWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + 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; + + mTrainingButtons[mControllerFocus]->setStateSelected(false); + mControllerFocus = wrap(mControllerFocus - 1, mTrainingButtons.size()); + mTrainingButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mTrainingButtons.size() <= 1) + return true; + + mTrainingButtons[mControllerFocus]->setStateSelected(false); + mControllerFocus = wrap(mControllerFocus + 1, mTrainingButtons.size()); + mTrainingButtons[mControllerFocus]->setStateSelected(true); + } + + return true; + } } diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index ee13f24b23..4f866f820e 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -49,9 +49,13 @@ namespace MWGui MyGUI::Widget* mTrainingOptions; MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; + std::vector mTrainingButtons; WaitDialogProgressBar mProgressBar; TimeAdvancer mTimeAdvancer; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; }; } From 14b0c9afbee0740f04e3c097e532c67d6233b8a5 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 26 May 2025 21:08:05 -0700 Subject: [PATCH 115/455] Add controller support to barter window --- apps/openmw/mwgui/countdialog.cpp | 9 ++- apps/openmw/mwgui/countdialog.hpp | 1 + apps/openmw/mwgui/inventorywindow.cpp | 11 +++- apps/openmw/mwgui/itemview.cpp | 13 +++- apps/openmw/mwgui/tradewindow.cpp | 94 +++++++++++++++++++++++++++ apps/openmw/mwgui/tradewindow.hpp | 4 ++ 6 files changed, 127 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 75a2fc6034..d922b990cd 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -41,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, @@ -57,6 +57,13 @@ namespace MWGui mItemEdit->setValue(maxCount); } + void CountDialog::setCount(int count) + { + count = std::clamp(count, 1, (int)mSlider->getScrollRange()); + mSlider->setScrollPosition(count - 1); + mItemEdit->setValue(count); + } + void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { setVisible(false); diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 695de09215..b3a1aab3b3 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -17,6 +17,7 @@ namespace MWGui public: CountDialog(); void openCountDialog(const std::string& item, const std::string& message, const int maxCount); + void setCount(int count); typedef MyGUI::delegates::MultiDelegate EventHandle_WidgetInt; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7f314fed14..2e128f15f8 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -133,7 +133,6 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.b = "#{sBack}"; - mControllerButtons.r1 = "Filter"; mControllerButtons.r3 = "#{sInfo}"; } @@ -892,7 +891,7 @@ namespace MWGui break; case MWGui::GM_Barter: mControllerButtons.a = "#{sSell}"; - mControllerButtons.x = ""; + mControllerButtons.x = "#{sOffer}"; mControllerButtons.y = ""; mControllerButtons.r2 = "#{sBarter}"; break; @@ -959,6 +958,14 @@ namespace MWGui (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 = + (MWGui::TradeWindow*)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(1); + tradeWindow->onControllerButtonEvent(arg); + } } else if (arg.button == SDL_CONTROLLER_BUTTON_Y) { diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index a223bde5e0..9cde52f0c2 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -52,12 +52,14 @@ namespace MWGui MyGUI::Widget* dragArea = mScrollView->getChildAt(0); int maxHeight = mScrollView->getHeight(); - mRows = maxHeight / 42; - mRows = std::max(mRows, 1); + mRows = std::max(maxHeight / 42, 1); mItemCount = dragArea->getChildCount(); bool showScrollbar = 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 < mItemCount; ++i) { @@ -262,6 +264,13 @@ namespace MWGui focused->setControllerFocus(true); if (mControllerTooltip) MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); + + // 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)); } } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ba752303d2..3e4b8732bb 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -168,6 +168,12 @@ namespace MWGui std::numeric_limits::min() + 1); // disallow INT_MIN since abs(INT_MIN) is undefined setCoord(400, 0, 400, 300); + + mControllerButtons.a = "#{sBuy}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sOffer}"; + mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.l2 = "#{sInventory}"; } void TradeWindow::setPtr(const MWWorld::Ptr& actor) @@ -201,6 +207,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); } void TradeWindow::onFrame(float dt) @@ -339,6 +349,13 @@ namespace MWGui } } + void TradeWindow::onOfferSubmitted(MyGUI::Widget* _sender, int offerAmount) + { + mCurrentBalance = mCurrentBalance < 0 ? -offerAmount : offerAmount; + updateLabels(); + onOfferButtonClicked(mOfferButton); + } + void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { TradeItemModel* playerItemModel @@ -643,4 +660,81 @@ namespace MWGui if (mTradeModel && mTradeModel->usesContainer(ptr)) 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); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + if (mFilterAll->getStateSelected()) + onFilterChanged(mFilterWeapon); + else if (mFilterWeapon->getStateSelected()) + onFilterChanged(mFilterApparel); + else if (mFilterApparel->getStateSelected()) + onFilterChanged(mFilterMagic); + else if (mFilterMagic->getStateSelected()) + onFilterChanged(mFilterMisc); + else if (mFilterMisc->getStateSelected()) + onFilterChanged(mFilterAll); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || + arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + mItemView->onControllerButtonEvent(arg.button); + } + + return true; + } + + void TradeWindow::setActiveControllerWindow(bool active) + { + mItemView->setActiveControllerWindow(active); + WindowBase::setActiveControllerWindow(active); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 33c39cb269..afd271ed2f 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -47,6 +47,9 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Trade"; } + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + void setActiveControllerWindow(bool active) override; + private: ItemView* mItemView; SortFilterItemModel* mSortModel; @@ -102,6 +105,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, int offerAmount); void addRepeatController(MyGUI::Widget* widget); From 274434e0d61dbe9b2fff9a71a138857be9e13415 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 26 May 2025 22:52:10 -0700 Subject: [PATCH 116/455] Add controller support to travel window --- apps/openmw/mwgui/travelwindow.cpp | 59 ++++++++++++++++++++++++++++++ apps/openmw/mwgui/travelwindow.hpp | 5 +++ 2 files changed, 64 insertions(+) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 6ba3a55286..f954daafff 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -42,6 +42,13 @@ namespace MWGui mDestinations->setCoord(450 / 2 - mDestinations->getTextSize().width / 2, mDestinations->getTop(), mDestinations->getTextSize().width, mDestinations->getHeight()); mSelect->setCoord(8, mSelect->getTop(), mSelect->getTextSize().width, mSelect->getHeight()); + + if (Settings::gui().mControllerMenus) + { + mDisableGamepadCursor = true; + mControllerButtons.a = "#{sTravel}"; + mControllerButtons.b = "#{sBack}"; + } } void TravelWindow::addDestination(const ESM::RefId& name, const ESM::Position& pos, bool interior) @@ -100,6 +107,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() @@ -108,6 +117,7 @@ namespace MWGui mCurrentY = 0; while (mDestinationsView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0)); + mDestinationButtons.clear(); } void TravelWindow::setPtr(const MWWorld::Ptr& actor) @@ -141,6 +151,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); @@ -235,4 +253,45 @@ namespace MWGui mDestinationsView->setViewOffset( MyGUI::IntPoint(0, static_cast(mDestinationsView->getViewOffset().top + _rel * 0.3f))); } + + bool TravelWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + onTravelButtonClick(mDestinationButtons[mControllerFocus]); + } + 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; + + mDestinationButtons[mControllerFocus]->setStateSelected(false); + mControllerFocus = wrap(mControllerFocus - 1, mDestinationButtons.size()); + mDestinationButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mDestinationButtons.size() <= 1) + return true; + + mDestinationButtons[mControllerFocus]->setStateSelected(false); + mControllerFocus = wrap(mControllerFocus + 1, mDestinationButtons.size()); + mDestinationButtons[mControllerFocus]->setStateSelected(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; + } } diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 6d7c1c7376..1136718e72 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -26,6 +26,7 @@ namespace MWGui MyGUI::TextBox* mPlayerGold; MyGUI::TextBox* mDestinations; MyGUI::TextBox* mSelect; + std::vector mDestinationButtons; MyGUI::ScrollView* mDestinationsView; @@ -39,6 +40,10 @@ namespace MWGui void updateLabels(); void onReferenceUnavailable() override; + + private: + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; }; } From f67aae086a70a8af2bd231da9e573af489ebe1a7 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 26 May 2025 23:07:20 -0700 Subject: [PATCH 117/455] Improve scrolling in list windows and fix a crash in a few menus when no items are selectable --- apps/openmw/mwgui/class.cpp | 3 ++- apps/openmw/mwgui/messagebox.cpp | 1 + apps/openmw/mwgui/spellbuyingwindow.cpp | 23 ++++++++++++-------- apps/openmw/mwgui/spellbuyingwindow.hpp | 3 ++- apps/openmw/mwgui/spellview.cpp | 29 +++++++++++++++++-------- apps/openmw/mwgui/spellview.hpp | 4 ++-- apps/openmw/mwgui/trainingwindow.cpp | 3 ++- apps/openmw/mwgui/travelwindow.cpp | 3 ++- 8 files changed, 45 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index e658001e5a..b1e6b77ca0 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -473,7 +473,8 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - onButtonClicked(mButtons[mControllerFocus]); + if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + onButtonClicked(mButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 94807dd54c..ec42993f4d 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -446,6 +446,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { + mControllerFocus = std::clamp(mControllerFocus, 0, (int)mButtons.size() - 1); buttonActivated(mButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index e1f12ae6e0..1c986180a8 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -78,7 +78,7 @@ namespace MWGui toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick); mSpellsWidgetMap.insert(std::make_pair(toAdd, spell.mId)); if (price <= playerGold) - mSpellButtons.emplace_back(toAdd); + mSpellButtons.emplace_back(std::make_pair(toAdd, mSpellsWidgetMap.size())); } void SpellBuyingWindow::clearSpells() @@ -143,7 +143,7 @@ namespace MWGui { mControllerFocus = 0; if (mSpellButtons.size() > 0) - mSpellButtons[0]->setStateSelected(true); + mSpellButtons[0].first->setStateSelected(true); } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the @@ -221,7 +221,8 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - onSpellButtonClick(mSpellButtons[mControllerFocus]); + if (mControllerFocus >= 0 && mControllerFocus < mSpellButtons.size()) + onSpellButtonClick(mSpellButtons[mControllerFocus].first); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { @@ -232,25 +233,29 @@ namespace MWGui if (mSpellButtons.size() <= 1) return true; - mSpellButtons[mControllerFocus]->setStateSelected(false); + mSpellButtons[mControllerFocus].first->setStateSelected(false); mControllerFocus = wrap(mControllerFocus - 1, mSpellButtons.size()); - mSpellButtons[mControllerFocus]->setStateSelected(true); + mSpellButtons[mControllerFocus].first->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { if (mSpellButtons.size() <= 1) return true; - mSpellButtons[mControllerFocus]->setStateSelected(false); + mSpellButtons[mControllerFocus].first->setStateSelected(false); mControllerFocus = wrap(mControllerFocus + 1, mSpellButtons.size()); - mSpellButtons[mControllerFocus]->setStateSelected(true); + mSpellButtons[mControllerFocus].first->setStateSelected(true); } // Scroll the list to keep the active item in view - if (mControllerFocus <= 5) + int line = mSpellButtons[mControllerFocus].second; + if (line <= 5) mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); else - mSpellsView->setViewOffset(MyGUI::IntPoint(0, -10 * (mControllerFocus - 5))); + { + const int lineHeight = Settings::gui().mFontSize + 2; + mSpellsView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5))); + } return true; } diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 234dcb5b9d..e67dfde76c 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -39,7 +39,8 @@ namespace MWGui MyGUI::ScrollView* mSpellsView; std::map mSpellsWidgetMap; - std::vector mSpellButtons; + /// List of enabled/purchasable spells and their index in the full list. + std::vector> mSpellButtons; void onCancelButtonClicked(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 2ebd1751da..6ef73c7794 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -122,7 +122,7 @@ namespace MWGui t->setCaption(spell.mName + captionSuffix); t->setTextAlign(MyGUI::Align::Left); adjustSpellWidget(spell, i, t); - mButtons.emplace_back(t); + mButtons.emplace_back(std::make_pair(t, i)); if (!spell.mCostColumn.empty() && mShowCostColumn) { @@ -339,7 +339,7 @@ namespace MWGui // Select the focused item, if any. if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) { - onSpellSelected(mButtons.at(mControllerFocus)); + onSpellSelected(mButtons[mControllerFocus].first); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } } @@ -348,12 +348,16 @@ namespace MWGui // Toggle info tooltip mControllerTooltip = !mControllerTooltip; if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size()) - MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons.at(mControllerFocus)); + MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons[mControllerFocus].first); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) mControllerFocus--; else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) mControllerFocus++; + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + mControllerFocus = std::max(0, mControllerFocus - 10); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + mControllerFocus = std::min(mControllerFocus + 10, (int)mButtons.size() - 1); else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { // Jump to first item in previous group @@ -380,10 +384,7 @@ namespace MWGui } } - if (mControllerFocus < 0) - mControllerFocus = mButtons.size() - 1; - else if (mControllerFocus >= mButtons.size()) - mControllerFocus = 0; + mControllerFocus = wrap(mControllerFocus, mButtons.size()); if (prevFocus != mControllerFocus) updateControllerFocus(prevFocus, mControllerFocus); @@ -396,19 +397,29 @@ namespace MWGui if (prevFocus >= 0 && prevFocus < mButtons.size()) { - Gui::SharedStateButton* prev = mButtons.at(prevFocus); + Gui::SharedStateButton* prev = mButtons[prevFocus].first; if (prev) prev->onMouseLostFocus(nullptr); } if (newFocus >= 0 && newFocus < mButtons.size()) { - Gui::SharedStateButton* focused = mButtons.at(newFocus); + Gui::SharedStateButton* focused = mButtons[newFocus].first; if (focused) { focused->onMouseSetFocus(nullptr); if (mControllerTooltip) MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); + + // 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))); + } } } } diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index cf3a43354f..222d5f1ba3 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -94,8 +94,8 @@ namespace MWGui void addGroup(const std::string& label1, const std::string& label2); void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget); - /// Keep a list of buttons for controller navigation - std::vector mButtons; + /// Keep a list of buttons for controller navigation and their index in the full list. + std::vector> mButtons; /// Keep a list of group offsets for controller navigation std::vector mGroupIndices; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 371f0e95aa..fcead9d543 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -251,7 +251,8 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - onTrainingSelected(mTrainingButtons[mControllerFocus]); + if (mControllerFocus >= 0 && mControllerFocus < mTrainingButtons.size()) + onTrainingSelected(mTrainingButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index f954daafff..083bba391b 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -258,7 +258,8 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - onTravelButtonClick(mDestinationButtons[mControllerFocus]); + if (mControllerFocus >= 0 && mControllerFocus < mDestinationButtons.size()) + onTravelButtonClick(mDestinationButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { From 451d68461ceb983694f7e359ce4701d5928b81f4 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 27 May 2025 22:40:00 -0700 Subject: [PATCH 118/455] Add controller support to skill and attribute menus --- apps/openmw/mwgui/class.cpp | 88 +++++++++++++++++++++++++++++++---- apps/openmw/mwgui/class.hpp | 4 ++ apps/openmw/mwgui/widgets.cpp | 26 +++++++++++ apps/openmw/mwgui/widgets.hpp | 8 ++++ 4 files changed, 116 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index b1e6b77ca0..c0660b5f02 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -943,6 +943,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); @@ -954,8 +955,15 @@ namespace MWGui getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + if (mAttributeButtons.size() > 0) + mAttributeButtons[0]->setStateSelected(true); + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; + } } // widget controls @@ -979,12 +987,29 @@ namespace MWGui bool SelectAttributeDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { - if (arg.button == SDL_CONTROLLER_BUTTON_B) + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mControllerFocus >= 0 && mControllerFocus < mAttributeButtons.size()) + onAttributeClicked(mAttributeButtons[mControllerFocus]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) { onCancelClicked(nullptr); - return true; } - return false; + 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 */ @@ -1016,6 +1041,7 @@ namespace MWGui skillWidget->setSkillId(skill.mId); skillWidget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); ToolTips::createSkillToolTip(skillWidget, skill.mId); + mSkillButtons.emplace_back(skillWidget); } for (const auto& [widget, coord] : specializations) { @@ -1029,8 +1055,15 @@ namespace MWGui getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); - mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sCancel}"; + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + if (mSkillButtons.size() > 0) + mSkillButtons[0]->setStateSelected(true); + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; + } } SelectSkillDialog::~SelectSkillDialog() {} @@ -1056,12 +1089,47 @@ namespace MWGui bool SelectSkillDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { - if (arg.button == SDL_CONTROLLER_BUTTON_B) + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mControllerFocus >= 0 && mControllerFocus < mSkillButtons.size()) + onSkillClicked(mSkillButtons[mControllerFocus]); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) { onCancelClicked(nullptr); - return true; } - return false; + 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) + { + mSkillButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus < 9) + mControllerFocus += 18; + else + mControllerFocus -= 9; + mSkillButtons[mControllerFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + mSkillButtons[mControllerFocus]->setStateSelected(false); + if (mControllerFocus >= 18) + mControllerFocus -= 18; + else + mControllerFocus += 9; + mSkillButtons[mControllerFocus]->setStateSelected(true); + } + + return true; } /* DescriptionDialog */ diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 515416973e..9f04fa9e13 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -219,6 +219,8 @@ namespace MWGui void onAttributeClicked(Widgets::MWAttributePtr _sender); void onCancelClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; + std::vector mAttributeButtons; private: ESM::RefId mAttributeId; @@ -251,6 +253,8 @@ namespace MWGui void onSkillClicked(Widgets::MWSkillPtr _sender); void onCancelClicked(MyGUI::Widget* _sender); bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; + std::vector mSkillButtons; private: ESM::RefId mSkillId; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 6cc5bdfdf5..8995f04b92 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -14,6 +14,8 @@ #include #include +#include "textcolours.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -66,6 +68,12 @@ namespace MWGui::Widgets } } + void MWSkill::setStateSelected(bool selected) + { + const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; + mSkillNameWidget->setTextColour(selected ? textColours.link : textColours.normal); + } + void MWSkill::onClicked(MyGUI::Widget* _sender) { eventClicked(this); @@ -150,6 +158,12 @@ namespace MWGui::Widgets } } + void MWAttribute::setStateSelected(bool selected) + { + const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; + mAttributeNameWidget->setTextColour(selected ? textColours.link : textColours.normal); + } + void MWAttribute::initialiseOverride() { Base::initialiseOverride(); @@ -231,6 +245,12 @@ namespace MWGui::Widgets } } + void MWSpell::setStateSelected(bool selected) + { + const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; + mSpellNameWidget->setTextColour(selected ? textColours.link : textColours.normal); + } + void MWSpell::initialiseOverride() { Base::initialiseOverride(); @@ -461,6 +481,12 @@ namespace MWGui::Widgets MWSpellEffect::~MWSpellEffect() {} + void MWSpellEffect::setStateSelected(bool selected) + { + const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; + mTextWidget->setTextColour(selected ? textColours.link : textColours.normal); + } + void MWSpellEffect::initialiseOverride() { Base::initialiseOverride(); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d562e4e07f..c51846d9df 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -110,6 +110,8 @@ namespace MWGui */ EventHandle_SkillVoid eventClicked; + void setStateSelected(bool selected); + protected: virtual ~MWSkill(); @@ -149,6 +151,8 @@ namespace MWGui */ EventHandle_AttributeVoid eventClicked; + void setStateSelected(bool selected); + protected: ~MWAttribute() override = default; @@ -191,6 +195,8 @@ namespace MWGui const ESM::RefId& getSpellId() const { return mId; } + void setStateSelected(bool selected); + protected: virtual ~MWSpell(); @@ -256,6 +262,8 @@ namespace MWGui int getRequestedWidth() const { return mRequestedWidth; } + void setStateSelected(bool selected); + protected: virtual ~MWSpellEffect(); From af27e9e5d61e64beb7f85d13319d310fb8d38b54 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 27 May 2025 22:51:03 -0700 Subject: [PATCH 119/455] Allow shoulder buttons to jump count slider to either extreme --- apps/openmw/mwgui/countdialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index d922b990cd..816d67921c 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -103,6 +103,10 @@ namespace MWGui 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((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) From d18413f63a12bcc6a2e898fd22302041705c4b42 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 30 May 2025 01:10:57 -0700 Subject: [PATCH 120/455] Add controller support to spell making window --- apps/openmw/mwgui/spellcreationdialog.cpp | 353 +++++++++++++++++++++- apps/openmw/mwgui/spellcreationdialog.hpp | 18 +- 2 files changed, 368 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index d8302df87c..f096beddcb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -25,8 +26,8 @@ #include "../mwmechanics/spellutil.hpp" #include "class.hpp" +#include "textcolours.hpp" #include "tooltips.hpp" -#include "widgets.hpp" namespace { @@ -95,6 +96,13 @@ namespace MWGui += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sOk}"; + } } void EditEffectDialog::setConstantEffect(bool constant) @@ -154,6 +162,15 @@ namespace MWGui mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); + if (Settings::gui().mControllerMenus) + { + mRangeButton->setStateSelected(true); + mDeleteButton->setStateSelected(false); + mOkButton->setStateSelected(false); + mCancelButton->setStateSelected(false); + mControllerFocus = 0; + } + setVisible(true); } @@ -187,6 +204,15 @@ namespace MWGui onDurationChanged(mDurationSlider, effect.mDuration - 1); eventEffectModified(mEffect); + if (Settings::gui().mControllerMenus) + { + mRangeButton->setStateSelected(true); + mDeleteButton->setStateSelected(false); + mOkButton->setStateSelected(false); + mCancelButton->setStateSelected(false); + mControllerFocus = 0; + } + updateBoxes(); } @@ -231,6 +257,25 @@ namespace MWGui mAreaBox->setVisible(true); // curY += mAreaBox->getSize().height; } + + if (Settings::gui().mControllerMenus) + { + mButtons.clear(); + mButtons.emplace_back(mRangeButton); + if (mMagnitudeBox->getVisible()) + { + mButtons.emplace_back(mMagnitudeMinValue); + mButtons.emplace_back(mMagnitudeMaxValue); + } + if (mDurationBox->getVisible()) + mButtons.emplace_back(mDurationValue); + if (mAreaBox->getVisible()) + mButtons.emplace_back(mAreaValue); + if (mDeleteButton->getVisible()) + mButtons.emplace_back(mDeleteButton); + mButtons.emplace_back(mOkButton); + mButtons.emplace_back(mCancelButton); + } } void EditEffectDialog::onRangeButtonClicked(MyGUI::Widget* sender) @@ -340,6 +385,195 @@ namespace MWGui eventEffectModified(mEffect); } + bool EditEffectDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + int prevFocus = mControllerFocus; + mControllerFocus = std::clamp(mControllerFocus, 0, (int)mButtons.size() - 1); + MyGUI::TextBox* button = mButtons[mControllerFocus]; + + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (button == mRangeButton) + onRangeButtonClicked(mRangeButton); + else if (button == mCancelButton) + onCancelButtonClicked(mCancelButton); + else if (button == mOkButton) + onOkButtonClicked(mOkButton); + else if (button == mDeleteButton) + onDeleteButtonClicked(mDeleteButton); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancelButtonClicked(mCancelButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + onOkButtonClicked(mOkButton); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mControllerFocus == 0) + mControllerFocus = (int)mButtons.size() - 2; + else if (button == mCancelButton && mDeleteButton->getVisible()) + mControllerFocus -= 3; + else if (button == mCancelButton || (button == mOkButton && mDeleteButton->getVisible())) + mControllerFocus -= 2; + else + mControllerFocus = std::max(mControllerFocus - 1, 0); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (button == mDeleteButton || button == mOkButton || button == mCancelButton) + mControllerFocus = 0; + else + mControllerFocus++; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + { + if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition(0); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition(mMagnitudeMinSlider->getScrollPosition()); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(0); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(0); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + { + if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition(mMagnitudeMaxSlider->getScrollPosition()); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition(mMagnitudeMaxSlider->getScrollRange() - 1); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(mDurationSlider->getScrollRange() - 1); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(mAreaSlider->getScrollRange() - 1); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + { + if (button == mRangeButton) + onRangeButtonClicked(mRangeButton); + else if (button == mCancelButton) + mControllerFocus--; + else if (button == mOkButton && mDeleteButton->getVisible()) + mControllerFocus--; + else if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition(mMagnitudeMinSlider->getScrollPosition() - 1); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition( + std::max(mMagnitudeMaxSlider->getScrollPosition() - 1, mMagnitudeMinSlider->getScrollPosition())); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(mDurationSlider->getScrollPosition() - 1); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(mAreaSlider->getScrollPosition() - 1); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + if (button == mRangeButton) + onRangeButtonClicked(mRangeButton); + else if (button == mDeleteButton) + mControllerFocus++; + else if (button == mOkButton) + mControllerFocus++; + else if (button == mMagnitudeMinValue) + { + mMagnitudeMinSlider->setScrollPosition( + std::min(mMagnitudeMinSlider->getScrollPosition() + 1, mMagnitudeMaxSlider->getScrollPosition())); + onMagnitudeMinChanged(nullptr, mMagnitudeMinSlider->getScrollPosition()); + } + else if (button == mMagnitudeMaxValue) + { + mMagnitudeMaxSlider->setScrollPosition(mMagnitudeMaxSlider->getScrollPosition() + 1); + onMagnitudeMaxChanged(nullptr, mMagnitudeMaxSlider->getScrollPosition()); + } + else if (button == mDurationValue) + { + mDurationSlider->setScrollPosition(mDurationSlider->getScrollPosition() + 1); + onDurationChanged(nullptr, mDurationSlider->getScrollPosition()); + } + else if (button == mAreaValue) + { + mAreaSlider->setScrollPosition(mAreaSlider->getScrollPosition() + 1); + onAreaChanged(nullptr, mAreaSlider->getScrollPosition()); + } + } + + if (prevFocus != mControllerFocus) + updateControllerFocus(prevFocus, mControllerFocus); + + return true; + } + + void EditEffectDialog::updateControllerFocus(int prevFocus, int newFocus) + { + const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; + + if (prevFocus >= 0 && prevFocus < mButtons.size()) + { + MyGUI::TextBox* button = mButtons[prevFocus]; + if (button == mMagnitudeMinValue || + button == mMagnitudeMaxValue || + button == mDurationValue || + button == mAreaValue) + { + button->setTextColour(textColours.normal); + } + else + { + ((MyGUI::Button*)button)->setStateSelected(false); + } + } + + if (newFocus >= 0 && newFocus < mButtons.size()) + { + MyGUI::TextBox* button = mButtons[newFocus]; + if (button == mMagnitudeMinValue || + button == mMagnitudeMaxValue || + button == mDurationValue || + button == mAreaValue) + { + button->setTextColour(textColours.link); + } + else + { + ((MyGUI::Button*)button)->setStateSelected(true); + } + } + } + // ------------------------------------------------------------------------------------------------ SpellCreationDialog::SpellCreationDialog() @@ -360,6 +594,13 @@ namespace MWGui mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); setWidgets(mAvailableEffectsList, mUsedEffectsView); + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sBuy}"; + } } void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor) @@ -490,6 +731,24 @@ namespace MWGui mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); } + + bool SpellCreationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + onCancelButtonClicked(mCancelButton); + return true; + } + else if (arg.button == SDL_CONTROLLER_BUTTON_X) + { + onBuyButtonClicked(mBuyButton); + return true; + } + else + return EffectEditorBase::onControllerButtonEvent(arg); + } + + // ------------------------------------------------------------------------------------------------ EffectEditorBase::EffectEditorBase(Type type) @@ -561,6 +820,7 @@ namespace MWGui mAvailableEffectsList->adjustSize(); mAvailableEffectsList->scrollToTop(); + mAvailableButtons.clear(); for (const short effectId : knownEffects) { const std::string& name = MWBase::Environment::get() @@ -568,13 +828,23 @@ namespace MWGui ->get() .find(ESM::MagicEffect::indexToGmstString(effectId)) ->mValue.getString(); - MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); + MyGUI::Button* w = mAvailableEffectsList->getItemWidget(name); + mAvailableButtons.emplace_back(w); ToolTips::createMagicEffectToolTip(w, effectId); } mEffects.clear(); updateEffectsView(); + + if (Settings::gui().mControllerMenus) + { + mAvailableFocus = 0; + mEffectFocus = 0; + mRightColumn = false; + if (mAvailableButtons.size() > 0) + mAvailableButtons[0]->setStateSelected(true); + } } void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView) @@ -686,6 +956,7 @@ namespace MWGui MyGUI::IntSize size(0, 0); + mEffectButtons.clear(); int i = 0; for (const ESM::ENAMstruct& effectInfo : mEffects) { @@ -718,6 +989,8 @@ namespace MWGui size.width = std::max(size.width, effect->getRequestedWidth()); size.height += 24; ++i; + + mEffectButtons.emplace_back(std::pair(effect, button)); } // Canvas size must be expressed with HScroll disabled, otherwise MyGUI would expand the scroll area when the @@ -755,4 +1028,80 @@ namespace MWGui effect.mRange = ESM::RT_Self; mConstantEffect = constant; } + + bool EffectEditorBase::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + { + onAvailableEffectClicked(mAvailableButtons[mAvailableFocus]); + } + else if (mRightColumn && mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + { + onEditEffect(mEffectButtons[mEffectFocus].second); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mRightColumn && mEffectButtons.size() > 0) + { + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(false); + mEffectFocus = wrap(mEffectFocus - 1, mEffectButtons.size()); + mEffectButtons[mEffectFocus].first->setStateSelected(true); + } + else if (!mRightColumn && mAvailableButtons.size() > 0) + { + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(false); + mAvailableFocus = wrap(mAvailableFocus - 1, mAvailableButtons.size()); + mAvailableButtons[mAvailableFocus]->setStateSelected(true); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mRightColumn && mEffectButtons.size() > 0) + { + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(false); + mEffectFocus = wrap(mEffectFocus + 1, mEffectButtons.size()); + mEffectButtons[mEffectFocus].first->setStateSelected(true); + } + else if (!mRightColumn && mAvailableButtons.size() > 0) + { + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(false); + mAvailableFocus = wrap(mAvailableFocus + 1, mAvailableButtons.size()); + mAvailableButtons[mAvailableFocus]->setStateSelected(true); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mRightColumn) + { + mRightColumn = false; + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(false); + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mRightColumn && mEffectButtons.size() > 0) + { + mRightColumn = true; + if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + mAvailableButtons[mAvailableFocus]->setStateSelected(false); + if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + mEffectButtons[mEffectFocus].first->setStateSelected(true); + } + + // Scroll the list to keep the active item in view + if (mAvailableFocus <= 5) + mAvailableEffectsList->setViewOffset(0); + else + { + const int lineHeight = Settings::gui().mFontSize + 3; + mAvailableEffectsList->setViewOffset(-lineHeight * (mAvailableFocus - 5)); + } + + return true; + } } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index b2bd71d9c4..cbd57014ce 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -2,12 +2,14 @@ #define MWGUI_SPELLCREATION_H #include +#include #include #include #include #include "referenceinterface.hpp" +#include "widgets.hpp" #include "windowbase.hpp" namespace Gui @@ -84,13 +86,18 @@ namespace MWGui void updateBoxes(); - protected: + private: ESM::ENAMstruct mEffect; ESM::ENAMstruct mOldEffect; const ESM::MagicEffect* mMagicEffect; bool mConstantEffect; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + void updateControllerFocus(int prevFocus, int newFocus); + int mControllerFocus; + std::vector mButtons; }; class EffectEditorBase @@ -143,8 +150,16 @@ namespace MWGui virtual void notifyEffectsChanged() {} + virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); + private: Type mType; + + int mAvailableFocus; + int mEffectFocus; + bool mRightColumn; + std::vector mAvailableButtons; + std::vector> mEffectButtons; }; class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase @@ -167,6 +182,7 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* sender); void onBuyButtonClicked(MyGUI::Widget* sender); void onAccept(MyGUI::EditBox* sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; void notifyEffectsChanged() override; From 40441a065a54daf5e4d0f2bdc48c5343d84c8712 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 11:58:35 -0700 Subject: [PATCH 121/455] Add controller support to alcmhemy menu --- apps/openmw/mwgui/alchemywindow.cpp | 95 ++++++++++++++++++++++++++++- apps/openmw/mwgui/alchemywindow.hpp | 2 + 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 5a6245fca0..fe82ead93f 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -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.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.x = "#{sCreate}"; + mControllerButtons.y = "#{sMagicEffects}"; + mControllerButtons.r3 = "#{sInfo}"; + } + center(); } @@ -109,6 +119,8 @@ namespace MWGui void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { + if (Settings::gui().mControllerMenus && mNameEdit->getCaption().length() == 0) + mNameEdit->setCaption("Unknown potion"); mAlchemy->setPotionName(mNameEdit->getCaption()); int count = mAlchemy->countPotionsToBrew(); count = std::min(count, mBrewCountEdit->getValue()); @@ -165,7 +177,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 +308,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 +548,77 @@ namespace MWGui if (currentCount > 1) mBrewCountEdit->setValue(currentCount - 1); } + + 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. + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + // Select the highlighted entry in the combo box and close it. + int index = mFilterValue->getIndexSelected(); + mFilterValue->setIndexSelected(index); + onFilterChanged(mFilterValue, index); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); // Close list + } + else if (arg.button == SDL_CONTROLLER_BUTTON_B || arg.button == SDL_CONTROLLER_BUTTON_Y) + { + // Close the list without selecting anything + mFilterValue->clearIndexSelected(); + onFilterEdited(mFilterValue); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); // Close list + } + 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); + } + else + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + { + // Remove active ingredients or close the window + if (mIngredients[3]->isUserString("ToolTipType")) + onIngredientSelected(mIngredients[3]); + else if (mIngredients[2]->isUserString("ToolTipType")) + onIngredientSelected(mIngredients[2]); + else if (mIngredients[1]->isUserString("ToolTipType")) + onIngredientSelected(mIngredients[1]); + else if (mIngredients[0]->isUserString("ToolTipType")) + onIngredientSelected(mIngredients[0]); + else + 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); + } + } + else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + onDecreaseButtonTriggered(); + else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + onIncreaseButtonTriggered(); + else + mItemView->onControllerButtonEvent(arg.button); + } + + return true; + } } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 82e5c3f583..e79c41b659 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -99,6 +99,8 @@ namespace MWGui std::vector mApparatus; std::vector mIngredients; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } From 2d532100ebbe32418be1ff4f23c0c840f0b1e2a9 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 15:28:13 -0700 Subject: [PATCH 122/455] Add controller supoprt to enchanting menu --- apps/openmw/mwgui/enchantingdialog.cpp | 26 ++++++++++++++++++++++++++ apps/openmw/mwgui/enchantingdialog.hpp | 2 ++ apps/openmw/mwgui/itemselection.cpp | 16 ++++++++++++++++ apps/openmw/mwgui/itemselection.hpp | 1 + 4 files changed, 45 insertions(+) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index af4a3e8ce3..d0d9fb7401 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -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.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.y = "Cast Type"; + mControllerButtons.l1 = "#{sItem}"; + mControllerButtons.r1 = "#{sSoulGem}"; } void EnchantingDialog::onOpen() @@ -152,6 +158,7 @@ namespace MWGui mEnchanting.setSelfEnchanting(false); mEnchanting.setEnchanter(ptr); mBuyButton->setCaptionWithReplacing("#{sBuy}"); + mControllerButtons.x = "#{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.x = "#{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; + } } diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index 4c720a11fc..3cda350152 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -73,6 +73,8 @@ namespace MWGui MWMechanics::Enchanting mEnchanting; ESM::EffectList mEffectList; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 4fe40ce693..86d584f24d 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "inventoryitemmodel.hpp" #include "itemview.hpp" #include "sortfilteritemmodel.hpp" @@ -26,6 +28,9 @@ namespace MWGui cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); center(); + + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sBack}"; } bool ItemSelectionDialog::exit() @@ -40,6 +45,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 +72,13 @@ namespace MWGui exit(); } + bool ItemSelectionDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancelButtonClicked(nullptr); + else + mItemView->onControllerButtonEvent(arg.button); + + return true; + } } diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index fe87d7e38a..83af6d4840 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -41,6 +41,7 @@ namespace MWGui void onSelectedItem(int index); void onCancelButtonClicked(MyGUI::Widget* sender); + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; }; } From 5276d7bab2aae7ffeeed2932d77bd6a531fb65c1 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 16:02:13 -0700 Subject: [PATCH 123/455] Several tweaks to the inventory menu --- apps/openmw/mwgui/inventorytabsoverlay.cpp | 6 ++++-- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 8 ++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index 21254c7840..14fd110830 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -29,13 +29,15 @@ namespace MWGui void InventoryTabsOverlay::onTabClicked(MyGUI::Widget* sender) { + if (!MWBase::Environment::get().getWindowManager()->getJournalAllowed()) + return; + for (int i = 0; i < mTabs.size(); i++) { if (mTabs[i] == sender) { - Log(Debug::Verbose) << "InventoryTabsOverlay::onTabClicked " << i; MWBase::Environment::get().getWindowManager()->setActiveControllerWindow(GM_Inventory, i); - //setTab(i); + setTab(i); break; } } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index e71b35e364..430101f069 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -84,7 +84,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { setPinButtonVisible(false); - mControllerButtons.lStick = "#{sInfo}"; + mControllerButtons.lStick = "#{sMouse}"; mControllerButtons.rStick = "#{sScrolldown}"; mControllerButtons.b = "#{sBack}"; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 77224790dd..00a35df774 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -514,6 +514,7 @@ namespace MWGui auto inventoryTabsOverlay = std::make_unique(); mInventoryTabsOverlay = inventoryTabsOverlay.get(); mWindows.push_back(std::move(inventoryTabsOverlay)); + mActiveControllerWindows[GM_Inventory] = 1; // Start on Inventory page mInputBlocker = MyGUI::Gui::getInstance().createWidget( {}, 0, 0, w, h, MyGUI::Align::Stretch, "InputBlocker"); @@ -927,12 +928,7 @@ namespace MWGui for (int i = 0; i < winCount; i++) { - activeIndex += delta; - if (activeIndex < 0) - activeIndex = winCount - 1; - else if (activeIndex >= winCount) - activeIndex = 0; - + activeIndex = wrap(activeIndex + delta, winCount); if (mGuiModeStates[mode].mWindows[activeIndex]->isVisible()) break; } From d3c7904e64d35f07c71de10cab2ae4c080231ba6 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 16:34:07 -0700 Subject: [PATCH 124/455] Rename controller help function in item view --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 8 ++++---- apps/openmw/mwgui/itemselection.cpp | 2 +- apps/openmw/mwgui/itemview.cpp | 2 +- apps/openmw/mwgui/itemview.hpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index fe82ead93f..32334962fb 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -616,7 +616,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) onIncreaseButtonTriggered(); else - mItemView->onControllerButtonEvent(arg.button); + mItemView->onControllerButton(arg.button); } return true; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 1657225343..c6c810c80e 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -391,7 +391,7 @@ namespace MWGui arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { - mItemView->onControllerButtonEvent(arg.button); + mItemView->onControllerButton(arg.button); } return true; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 2e128f15f8..da071cb753 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -914,7 +914,7 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_A) { - mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A); + mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); // The following actions are done here, not in onItemSelectedFromSourceModel, because we // want the mouse to work even in controller mode. if (mGuiMode == MWGui::GM_Inventory && mDragAndDrop->mIsOnDragAndDrop) @@ -946,7 +946,7 @@ namespace MWGui if (mGuiMode == MWGui::GM_Inventory) { // Drop the item into the gameworld - mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A); + mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); if (mDragAndDrop->mIsOnDragAndDrop) MWBase::Environment::get().getWindowManager()->getHud()->dropDraggedItem(0.5f, 0.5f); } @@ -972,7 +972,7 @@ namespace MWGui if (mGuiMode == MWGui::GM_Inventory) { // Unequip an item. - mItemView->onControllerButtonEvent(SDL_CONTROLLER_BUTTON_A); + mItemView->onControllerButton(SDL_CONTROLLER_BUTTON_A); onBackgroundSelected(); // Drop on inventory background to unequip } } @@ -1004,7 +1004,7 @@ namespace MWGui } else { - mItemView->onControllerButtonEvent(arg.button); + mItemView->onControllerButton(arg.button); } return true; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 86d584f24d..a00c754c3d 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -77,7 +77,7 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_B) onCancelButtonClicked(nullptr); else - mItemView->onControllerButtonEvent(arg.button); + mItemView->onControllerButton(arg.button); return true; } diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 9cde52f0c2..08e5236fc6 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -202,7 +202,7 @@ namespace MWGui updateControllerFocus(mControllerFocus, -1); } - void ItemView::onControllerButtonEvent(const unsigned char button) + void ItemView::onControllerButton(const unsigned char button) { if (!mItemCount) return; diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 091436ab05..29d44c6b09 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -35,7 +35,7 @@ namespace MWGui void setActiveControllerWindow(bool active); int getControllerFocus() { return mControllerFocus; } int getItemCount() { return mItemCount; } - void onControllerButtonEvent(const unsigned char button); + void onControllerButton(const unsigned char button); private: void initialiseOverride() override; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 3e4b8732bb..69b16dfc93 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -726,7 +726,7 @@ namespace MWGui arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { - mItemView->onControllerButtonEvent(arg.button); + mItemView->onControllerButton(arg.button); } return true; From cbe74cdab46661a39ee3c9ca0abc55eb30082348 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 18:58:30 -0700 Subject: [PATCH 125/455] Add controller support to repair and recharge menus --- apps/openmw/mwgui/itemchargeview.cpp | 62 ++++++++++++++++++++++++++ apps/openmw/mwgui/itemchargeview.hpp | 5 +++ apps/openmw/mwgui/itemselection.cpp | 1 + apps/openmw/mwgui/messagebox.cpp | 4 +- apps/openmw/mwgui/recharge.cpp | 16 +++++++ apps/openmw/mwgui/recharge.hpp | 2 + apps/openmw/mwgui/repair.cpp | 16 +++++++ apps/openmw/mwgui/repair.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++ files/data/mygui/openmw_resources.xml | 4 ++ 10 files changed, 113 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 02c3cc182c..abbf328a8c 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -1,5 +1,6 @@ #include "itemchargeview.hpp" +#include #include #include @@ -9,8 +10,11 @@ #include #include +#include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/spellutil.hpp" @@ -19,6 +23,7 @@ #include "itemmodel.hpp" #include "itemwidget.hpp" +#include "textcolours.hpp" namespace MWGui { @@ -156,11 +161,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 +238,52 @@ namespace MWGui mScrollView->setViewOffset( MyGUI::IntPoint(0, static_cast(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 < 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 < mLines.size()) + { + mLines[prevFocus].mText->setTextColour(textColours.normal); + mLines[prevFocus].mIcon->setControllerFocus(false); + } + + if (newFocus >= 0 && newFocus < mLines.size()) + { + mLines[newFocus].mText->setTextColour(textColours.link); + mLines[newFocus].mIcon->setControllerFocus(true); + + // Scroll the list to keep the active item in view + if (mControllerFocus <= 3) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(-55 * (mControllerFocus - 3), 0)); + } + } } diff --git a/apps/openmw/mwgui/itemchargeview.hpp b/apps/openmw/mwgui/itemchargeview.hpp index f7617d37eb..73bf9c3de2 100644 --- a/apps/openmw/mwgui/itemchargeview.hpp +++ b/apps/openmw/mwgui/itemchargeview.hpp @@ -52,6 +52,8 @@ namespace MWGui MyGUI::delegates::MultiDelegate eventItemClicked; + void onControllerButton(const unsigned char button); + private: struct Line { @@ -72,6 +74,9 @@ namespace MWGui std::unique_ptr mModel; MyGUI::ScrollView* mScrollView; DisplayMode mDisplayMode; + + int mControllerFocus; + void updateControllerFocus(int prevFocus, int newFocus); }; } diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index a00c754c3d..ec4ecb9d0f 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -31,6 +31,7 @@ namespace MWGui mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; + mControllerButtons.r3 = "#{sInfo}"; } bool ItemSelectionDialog::exit() diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index ec42993f4d..5f074a67c6 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -454,7 +454,7 @@ namespace MWGui if (mButtons.size() == 1) buttonActivated(mButtons[0]); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { if (mButtons.size() <= 1) return true; @@ -465,7 +465,7 @@ namespace MWGui mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); mButtons[mControllerFocus]->setStateSelected(true); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { if (mButtons.size() <= 1) return true; diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 7d57988d97..605a9a1a94 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -39,6 +39,10 @@ namespace MWGui mBox->setDisplayMode(ItemChargeView::DisplayMode_EnchantmentCharge); mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem); + + mControllerButtons.a = "Recharge"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.y = "#{sSoulGem}"; } void Recharge::onOpen() @@ -136,4 +140,16 @@ 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); + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancel(mCancelButton); + else + mBox->onControllerButton(arg.button); + + return true; + } } diff --git a/apps/openmw/mwgui/recharge.hpp b/apps/openmw/mwgui/recharge.hpp index f8a037d2db..c10f96e71e 100644 --- a/apps/openmw/mwgui/recharge.hpp +++ b/apps/openmw/mwgui/recharge.hpp @@ -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; }; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index c1602b8407..6f5d256d86 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -39,6 +39,10 @@ namespace MWGui mRepairBox->setDisplayMode(ItemChargeView::DisplayMode_Health); mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem); + + mControllerButtons.a = "#{sRepair}"; + mControllerButtons.b = "#{sBack}"; + mControllerButtons.y = "Tool"; } void Repair::onOpen() @@ -150,4 +154,16 @@ 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); + else if (arg.button == SDL_CONTROLLER_BUTTON_B) + onCancel(mCancelButton); + else + mRepairBox->onControllerButton(arg.button); + + return true; + } } diff --git a/apps/openmw/mwgui/repair.hpp b/apps/openmw/mwgui/repair.hpp index 093a10e3fa..986b28b613 100644 --- a/apps/openmw/mwgui/repair.hpp +++ b/apps/openmw/mwgui/repair.hpp @@ -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; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 00a35df774..9350ef4d1a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -952,6 +952,9 @@ namespace MWGui for (int i = 0; i < winCount; i++) mGuiModeStates[mode].mWindows[i]->setActiveControllerWindow(i == activeIndex); + MWBase::Environment::get().getInputManager()->setGamepadGuiCursorEnabled( + mGuiModeStates[mode].mWindows[activeIndex]->isGamepadCursorAllowed()); + updateControllerButtonsOverlay(); setCursorActive(false); diff --git a/files/data/mygui/openmw_resources.xml b/files/data/mygui/openmw_resources.xml index 08586ea75c..d107c094e3 100644 --- a/files/data/mygui/openmw_resources.xml +++ b/files/data/mygui/openmw_resources.xml @@ -172,6 +172,10 @@ + + + + From 2dc1d8fed7f59fccfb19f3ddca068789b5341c78 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 19:24:27 -0700 Subject: [PATCH 126/455] Add controller support to merchant repair window --- apps/openmw/mwgui/merchantrepair.cpp | 59 ++++++++++++++++++++++++++++ apps/openmw/mwgui/merchantrepair.hpp | 5 +++ 2 files changed, 64 insertions(+) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index a59f225e9e..e8155ea790 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -28,6 +28,13 @@ namespace MWGui getWidget(mGoldLabel, "PlayerGold"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick); + + if (Settings::gui().mControllerMenus) + { + mDisableGamepadCursor = true; + mControllerButtons.a = "#{sRepair}"; + mControllerButtons.b = "#{sBack}"; + } } 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,46 @@ 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 >= 0 && 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 + int 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; + } } diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index ffe5b86bdb..e878d34b7d 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -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> mButtons; MWWorld::Ptr mActor; + int 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; }; } From e3a9b71bb9b5b2872c14018c43d7136e414a8026 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 19:43:28 -0700 Subject: [PATCH 127/455] Add helper function for toggling controller focus on lists --- apps/openmw/mwgui/class.cpp | 16 ++++++++-------- apps/openmw/mwgui/dialogue.cpp | 8 ++++---- apps/openmw/mwgui/messagebox.cpp | 8 ++++---- apps/openmw/mwgui/review.cpp | 20 +++++++------------- apps/openmw/mwgui/trainingwindow.cpp | 8 ++++---- apps/openmw/mwgui/travelwindow.cpp | 8 ++++---- apps/openmw/mwgui/windowbase.cpp | 6 ++++++ apps/openmw/mwgui/windowbase.hpp | 1 + 8 files changed, 38 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index c0660b5f02..700c431246 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -488,9 +488,9 @@ namespace MWGui if (mButtons.size() == 2 && mControllerFocus == 0) return true; - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { @@ -499,9 +499,9 @@ namespace MWGui if (mButtons.size() == 2 && mControllerFocus == mButtons.size() - 1) return true; - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } return true; @@ -709,15 +709,15 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } return true; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 1dd3f051ef..1a8808c8dc 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -182,15 +182,15 @@ namespace MWGui onCancel(mCancelButton); else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } return true; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 5f074a67c6..492ac30052 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -461,9 +461,9 @@ namespace MWGui if (mButtons.size() == 2 && mControllerFocus == 0) return true; - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus - 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { @@ -472,9 +472,9 @@ namespace MWGui if (mButtons.size() == 2 && mControllerFocus == mButtons.size() - 1) return true; - mButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); } return true; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 19dcda90e4..dda00bd33a 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -121,7 +121,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, true); mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; mControllerButtons.x = "#{sDone}"; @@ -574,22 +574,16 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { - mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == 0) - mControllerFocus = mButtons.size() - 1; - else - mControllerFocus--; - mButtons[mControllerFocus]->setStateSelected(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) { - mButtons[mControllerFocus]->setStateSelected(false); - if (mControllerFocus == mButtons.size() - 1) - mControllerFocus = 0; - else - mControllerFocus++; - mButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mButtons, mControllerFocus, false); + mControllerFocus = wrap(mControllerFocus + 1, mButtons.size()); + setControllerFocus(mButtons, mControllerFocus, true); } return true; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index fcead9d543..65e468ebd4 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -263,18 +263,18 @@ namespace MWGui if (mTrainingButtons.size() <= 1) return true; - mTrainingButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mTrainingButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus - 1, mTrainingButtons.size()); - mTrainingButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mTrainingButtons, mControllerFocus, true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { if (mTrainingButtons.size() <= 1) return true; - mTrainingButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mTrainingButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus + 1, mTrainingButtons.size()); - mTrainingButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mTrainingButtons, mControllerFocus, true); } return true; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 083bba391b..eeb2df868f 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -270,18 +270,18 @@ namespace MWGui if (mDestinationButtons.size() <= 1) return true; - mDestinationButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mDestinationButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus - 1, mDestinationButtons.size()); - mDestinationButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mDestinationButtons, mControllerFocus, true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { if (mDestinationButtons.size() <= 1) return true; - mDestinationButtons[mControllerFocus]->setStateSelected(false); + setControllerFocus(mDestinationButtons, mControllerFocus, false); mControllerFocus = wrap(mControllerFocus + 1, mDestinationButtons.size()); - mDestinationButtons[mControllerFocus]->setStateSelected(true); + setControllerFocus(mDestinationButtons, mControllerFocus, true); } // Scroll the list to keep the active item in view diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 995731a775..4ecc26449f 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -25,6 +25,12 @@ int MWGui::wrap(int index, int max) return index; } +void MWGui::setControllerFocus(std::vector buttons, int index, bool focused) +{ + if (index >= 0 && index < buttons.size()) + buttons[index]->setStateSelected(focused); +} + WindowBase::WindowBase(std::string_view parLayout) : Layout(parLayout) { diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 8fb499ec7e..87bea55e38 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -15,6 +15,7 @@ namespace MWGui class DragAndDrop; int wrap(int index, int max); + void setControllerFocus(std::vector buttons, int index, bool selected); struct ControllerButtonStr { From 63a533cd51c4dfc188049864f022536713bcbf40 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 31 May 2025 20:23:29 -0700 Subject: [PATCH 128/455] Add controller support to the level up dialog --- apps/openmw/mwgui/levelupdialog.cpp | 59 +++++++++++++++++++++++++++++ apps/openmw/mwgui/levelupdialog.hpp | 4 ++ 2 files changed, 63 insertions(+) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 87f2db55a5..3b14415206 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -72,6 +73,7 @@ namespace MWGui widgets.mButton->setCaption(attribute.mName); widgets.mValue = hbox->createWidget("SandText", {}, MyGUI::Align::Default); mAttributeWidgets.emplace(attribute.mId, widgets); + mAttributeButtons.emplace_back(widgets.mButton); ++i; } @@ -90,6 +92,13 @@ namespace MWGui mCoins.push_back(image); } + if (Settings::gui().mControllerMenus) + { + mDisableGamepadCursor = true; + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.x = "#{sDone}"; + } + center(); } @@ -217,6 +226,13 @@ namespace MWGui center(); + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + for (int 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 +379,47 @@ namespace MWGui return ret; } + + bool LevelupDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + { + if (arg.button == SDL_CONTROLLER_BUTTON_A) + { + if (mControllerFocus >= 0 && mControllerFocus < mAttributeButtons.size()) + onAttributeClicked(mAttributeButtons[mControllerFocus]); + } + 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 == 0) + mControllerFocus = 3; + else if (mControllerFocus == 4) + mControllerFocus = 7; + else + mControllerFocus--; + setControllerFocus(mAttributeButtons, mControllerFocus, true); + } + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + setControllerFocus(mAttributeButtons, mControllerFocus, false); + if (mControllerFocus == 3) + mControllerFocus = 0; + else if (mControllerFocus == 7) + mControllerFocus = 4; + 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 + 4) % mAttributeButtons.size(); + setControllerFocus(mAttributeButtons, mControllerFocus, true); + } + + return true; + } } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 486390679b..8a34a94c8b 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -49,6 +49,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 mAttributeButtons; + int mControllerFocus; }; } From 95ed6b51da6699e4832631a794485e4e017e567d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 01:49:57 -0700 Subject: [PATCH 129/455] Allow navigating journal indices with a controller --- apps/openmw/mwgui/bookpage.cpp | 12 +++ apps/openmw/mwgui/bookpage.hpp | 3 + apps/openmw/mwgui/journalbooks.cpp | 8 +- apps/openmw/mwgui/journalbooks.hpp | 2 + apps/openmw/mwgui/journalwindow.cpp | 141 +++++++++++++++++++++++++++- apps/openmw/mwgui/journalwindow.hpp | 2 + 6 files changed, 159 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index cb0717edf3..113ac7d104 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -105,6 +105,18 @@ namespace MWGui Styles mStyles; MyGUI::IntRect mRect; + void setColour(int section, int line, int run, MyGUI::Colour colour) const override + { + if (section < 0 || section >= mSections.size()) + return; + if (line < 0 || line >= mSections[section].mLines.size()) + return; + if (run < 0 || 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) diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 43de2c09ac..34dae0cc0e 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -31,6 +31,9 @@ namespace MWGui /// text combined prior to pagination. virtual std::pair getSize() const = 0; + /// Used to highlight journal indices + virtual void setColour(int section, int line, int run, MyGUI::Colour colour) const = 0; + virtual ~TypesetBook() = default; }; diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 86b45b4863..c127508062 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -169,7 +169,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 +184,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 +196,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 +212,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); diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index 1970830eab..dd6cb402c5 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -10,6 +10,8 @@ namespace MWGui { MWGui::BookTypesetter::Utf8Span to_utf8_span(std::string_view text); + const MyGUI::Colour journalHeaderColour = MyGUI::Colour(0.60f, 0.00f, 0.00f); + struct JournalBooks { typedef TypesetBook::Ptr Book; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 8fbeb161ec..bff4b66c72 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -474,6 +474,9 @@ namespace getPage(LeftTopicIndex)->showPage(mTopicIndexBook, 0); getPage(RightTopicIndex)->showPage(mTopicIndexBook, 1); } + + if (Settings::gui().mControllerMenus) + setIndexControllerFocus(mSelectedIndex, true); } void notifyJournal(MyGUI::Widget* _sender) @@ -493,7 +496,7 @@ namespace MyGUI::Button* listItem = _list->getItemWidget(_list->getItemNameAt(i)); if (listItem) { - listItem->setStateSelected(mButtons.size() == _selectedIndex); + listItem->setTextColour(mButtons.size() == _selectedIndex ? MWGui::journalHeaderColour : MyGUI::Colour::Black); mButtons.push_back(listItem); } } @@ -518,7 +521,10 @@ namespace list->adjustSize(); if (Settings::gui().mControllerMenus) + { + mSelectedQuest = 0; addControllerButtons(list, mSelectedQuest); + } MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("book page")); MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay(); @@ -688,8 +694,30 @@ namespace return &mControllerButtons; } + void setIndexControllerFocus(int index, bool focused) + { + int col, row; + bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); + if (isRussian) + { + // Cyrillic = 30 (10 + 10 + 10) + col = index / 10; + row = index % 10; + } + else + { + // Latin = 26 (13 + 13) + col = index / 13; + row = index % 13; + } + + mTopicIndexBook->setColour(col, row, 0, focused ? MWGui::journalHeaderColour : MyGUI::Colour::Black); + } + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override { + bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); + if (arg.button == SDL_CONTROLLER_BUTTON_A) // A: Mouse click or Select { if (mOptionsMode && mQuestMode) @@ -704,6 +732,15 @@ namespace Gui::MWList* list = getWidget(TopicsList); notifyTopicSelected(list->getItemNameAt(mSelectedQuest), 0); } + else if (mOptionsMode) + { + // 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 + notifyIndexLinkClicked(isRussian ? mSelectedIndex + russianOffset : mSelectedIndex + 'A'); + } return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_B) // B: Back @@ -776,10 +813,41 @@ namespace { if (mOptionsMode && (mQuestMode || mTopicsMode)) { + if (mButtons.size() <= 1) + return true; + // Scroll through the list of quests or topics - mButtons[mSelectedQuest]->setStateSelected(false); + mButtons[mSelectedQuest]->setTextColour(MyGUI::Colour::Black); mSelectedQuest = MWGui::wrap(mSelectedQuest - 1, mButtons.size()); - mButtons[mSelectedQuest]->setStateSelected(true); + mButtons[mSelectedQuest]->setTextColour(MWGui::journalHeaderColour); + } + else if (mOptionsMode) + { + setIndexControllerFocus(mSelectedIndex, false); + if (isRussian) + { + // Cyrillic = 30 (10 + 10 + 10) + if (mSelectedIndex == 0) + mSelectedIndex = 9; + else if (mSelectedIndex == 10) + mSelectedIndex = 19; + else if (mSelectedIndex == 20) + mSelectedIndex = 29; + else + mSelectedIndex--; + } + else + { + // Latin = 26 (13 + 13) + if (mSelectedIndex == 0) + mSelectedIndex = 12; + else if (mSelectedIndex == 13) + mSelectedIndex = 25; + else + mSelectedIndex--; + } + setIndexControllerFocus(mSelectedIndex, true); + setText(PageOneNum, 1); // Redraw the list } return true; } @@ -787,10 +855,41 @@ namespace { if (mOptionsMode && (mQuestMode || mTopicsMode)) { + if (mButtons.size() <= 1) + return true; + // Scroll through the list of quests or topics - mButtons[mSelectedQuest]->setStateSelected(false); + mButtons[mSelectedQuest]->setTextColour(MyGUI::Colour::Black); mSelectedQuest = MWGui::wrap(mSelectedQuest + 1, mButtons.size()); - mButtons[mSelectedQuest]->setStateSelected(true); + mButtons[mSelectedQuest]->setTextColour(MWGui::journalHeaderColour); + } + else if (mOptionsMode) + { + setIndexControllerFocus(mSelectedIndex, false); + if (isRussian) + { + // Cyrillic = 30 (10 + 10 + 10) + if (mSelectedIndex == 9) + mSelectedIndex = 0; + else if (mSelectedIndex == 19) + mSelectedIndex = 10; + else if (mSelectedIndex == 29) + mSelectedIndex = 20; + else + mSelectedIndex++; + } + else + { + // Latin = 26 (13 + 13) + if (mSelectedIndex == 12) + mSelectedIndex = 0; + else if (mSelectedIndex == 25) + mSelectedIndex = 13; + else + mSelectedIndex++; + } + setIndexControllerFocus(mSelectedIndex, true); + setText(PageOneNum, 1); // Redraw the list } return true; } @@ -798,12 +897,44 @@ namespace { if (!mOptionsMode) notifyPrevPage(getWidget(PrevPageBTN)); + else if (mOptionsMode && !mQuestMode && !mTopicsMode) + { + setIndexControllerFocus(mSelectedIndex, false); + if (isRussian) + { + // Cyrillic = 30 (10 + 10 + 10) + mSelectedIndex = (mSelectedIndex + 20) % 30; + } + else + { + // Latin = 26 (13 + 13) + mSelectedIndex = (mSelectedIndex + 13) % 26; + } + setIndexControllerFocus(mSelectedIndex, true); + setText(PageOneNum, 1); // Redraw the list + } return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { if (!mOptionsMode) notifyNextPage(getWidget(NextPageBTN)); + else if (mOptionsMode && !mQuestMode && !mTopicsMode) + { + setIndexControllerFocus(mSelectedIndex, false); + if (isRussian) + { + // Cyrillic = 30 (10 + 10 + 10) + mSelectedIndex = (mSelectedIndex + 10) % 30; + } + else + { + // Latin = 26 (13 + 13) + mSelectedIndex = (mSelectedIndex + 13) % 26; + } + setIndexControllerFocus(mSelectedIndex, true); + setText(PageOneNum, 1); // Redraw the list + } return true; } else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) // LB: Previous Page diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 45c4100563..3c66421906 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -34,6 +34,8 @@ namespace MWGui std::vector mButtons; int mSelectedQuest = 0; + int mSelectedIndex = 0; + void setIndexControllerFocus(int index, bool focused); }; } From 263863f3f292635947ed151c37b3073ec6528252 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 16:22:22 -0700 Subject: [PATCH 130/455] Fix modals opening above inventory causing it to change size --- apps/openmw/mwgui/inventorywindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index da071cb753..008e96356c 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -213,6 +213,9 @@ 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_Inventory && !Settings::gui().mControllerMenus); From 2aa9847b248c868326438f6ca1c262503f0085f8 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 16:27:49 -0700 Subject: [PATCH 131/455] Update controller buttons for character creation and level up --- apps/openmw/mwgui/birth.cpp | 28 ++++++++------------ apps/openmw/mwgui/birth.hpp | 1 - apps/openmw/mwgui/class.cpp | 41 ++++++++++++++++------------- apps/openmw/mwgui/class.hpp | 1 - apps/openmw/mwgui/levelupdialog.cpp | 2 ++ apps/openmw/mwgui/race.cpp | 28 ++++++++------------ apps/openmw/mwgui/race.hpp | 1 - apps/openmw/mwgui/review.cpp | 2 ++ 8 files changed, 49 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index b047f6dc5f..5a5605aa6e 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -60,10 +60,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mOkButton->setStateSelected(true); + mControllerButtons.lStick = "#{sMouse}"; mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = "#{sDone}"; } updateBirths(); @@ -76,8 +75,17 @@ namespace MWGui getWidget(okButton, "OKButton"); if (shown) + { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); + mControllerButtons.x = "#{sNext}"; + } + else if (Settings::gui().mControllerMenus) + { + okButton->setCaption( + MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); + mControllerButtons.x = "#{sDone}"; + } else okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); @@ -280,14 +288,7 @@ namespace MWGui bool BirthDialog::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) + if (arg.button == SDL_CONTROLLER_BUTTON_B) { onBackClicked(mBackButton); } @@ -307,13 +308,6 @@ namespace MWGui winMgr->setKeyFocusWidget(mBirthList); 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); - mBackButton->setStateSelected(!mOkButtonFocus); - } return true; } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 09a0b7b1b5..b41b1fbb9a 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -59,7 +59,6 @@ namespace MWGui ESM::RefId mCurrentBirthId; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool mOkButtonFocus = true; }; } #endif diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 700c431246..f0fe5537da 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -149,10 +149,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mOkButton->setStateSelected(true); + mControllerButtons.lStick = "#{sMouse}"; mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = "#{sDone}"; } updateClasses(); @@ -165,8 +164,17 @@ namespace MWGui getWidget(okButton, "OKButton"); if (shown) + { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); + mControllerButtons.x = "#{sNext}"; + } + else if (Settings::gui().mControllerMenus) + { + okButton->setCaption( + MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); + mControllerButtons.x = "#{sDone}"; + } else okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); @@ -317,14 +325,7 @@ namespace MWGui bool PickClassDialog::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) + if (arg.button == SDL_CONTROLLER_BUTTON_B) { onBackClicked(mBackButton); } @@ -344,13 +345,6 @@ namespace MWGui winMgr->setKeyFocusWidget(mClassList); 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); - mBackButton->setStateSelected(!mOkButtonFocus); - } return true; } @@ -590,9 +584,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { okButton->setStateSelected(true); + mControllerButtons.lStick = "#{sMouse}"; mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = "#{sDone}"; } // Set default skills, attributes @@ -681,8 +675,17 @@ namespace MWGui getWidget(okButton, "OKButton"); if (shown) + { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); + mControllerButtons.x = "#{sNext}"; + } + else if (Settings::gui().mControllerMenus) + { + okButton->setCaption( + MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); + mControllerButtons.x = "#{sDone}"; + } else okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); @@ -1150,6 +1153,8 @@ namespace MWGui // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); + + mControllerButtons.a = "#{sOk}"; } DescriptionDialog::~DescriptionDialog() {} diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 9f04fa9e13..a4db03d4ab 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -155,7 +155,6 @@ namespace MWGui ESM::RefId mCurrentClassId; bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; - bool mOkButtonFocus = true; }; class SelectSpecializationDialog : public WindowModal diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 3b14415206..3ed3e3b6fc 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -97,6 +97,8 @@ namespace MWGui mDisableGamepadCursor = true; mControllerButtons.a = "#{sSelect}"; mControllerButtons.x = "#{sDone}"; + mOkButton->setCaption( + MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); } center(); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 172812bf47..fdc8725fa0 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -108,10 +108,9 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mOkButton->setStateSelected(true); + mControllerButtons.lStick = "#{sMouse}"; mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; - mControllerButtons.x = "#{sDone}"; mControllerButtons.y = "#{sSex}"; mControllerButtons.l1 = "#{sHair}"; mControllerButtons.r1 = "#{sFace}"; @@ -128,8 +127,17 @@ namespace MWGui getWidget(okButton, "OKButton"); if (shown) + { okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", {}))); + mControllerButtons.x = "#{sNext}"; + } + else if (Settings::gui().mControllerMenus) + { + okButton->setCaption( + MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); + mControllerButtons.x = "#{sDone}"; + } else okButton->setCaption( MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", {}))); @@ -463,14 +471,7 @@ namespace MWGui bool RaceDialog::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) + if (arg.button == SDL_CONTROLLER_BUTTON_B) { onBackClicked(mBackButton); } @@ -502,13 +503,6 @@ namespace MWGui winMgr->setKeyFocusWidget(mRaceList); 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); - mBackButton->setStateSelected(!mOkButtonFocus); - } return true; } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 3652343308..ead058214c 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -124,7 +124,6 @@ namespace MWGui bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) override; - bool mOkButtonFocus = true; }; } #endif diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index dda00bd33a..003cdb49cb 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -125,6 +125,8 @@ namespace MWGui mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sBack}"; mControllerButtons.x = "#{sDone}"; + okButton->setCaption( + MyGUI::UString(MWBase::Environment::get().getWindowManager()->getGameSettingString("sDone", {}))); } } From df237b5f9b45db444df0084bded4b9f7e1f33714 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 21:34:20 -0700 Subject: [PATCH 132/455] Add controller support to quick key menu --- apps/openmw/mwgui/quickkeysmenu.cpp | 98 +++++++++++++++++++++++++++ apps/openmw/mwgui/quickkeysmenu.hpp | 9 +++ apps/openmw/mwgui/spellview.cpp | 18 ++--- apps/openmw/mwgui/spellview.hpp | 2 +- apps/openmw/mwgui/spellwindow.cpp | 2 +- files/data/mygui/openmw_resources.xml | 5 +- 6 files changed, 122 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 93b0ef071f..daf49e1101 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -58,6 +58,12 @@ namespace MWGui unassign(&mKey[i]); } + + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sOK}"; + } } void QuickKeysMenu::clear() @@ -109,6 +115,13 @@ namespace MWGui { validate(index); } + + if (Settings::gui().mControllerMenus) + { + mControllerFocus = 0; + for (int i = 0; i < mKey.size(); i++) + mKey[i].button->setControllerFocus(i == mControllerFocus); + } } void QuickKeysMenu::onClose() @@ -450,6 +463,40 @@ namespace MWGui } } + 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 (int i = 0; i < mKey.size(); i++) + mKey[i].button->setControllerFocus(i == mControllerFocus); + + return true; + } + // --------------------------------------------------------------------------------------------------------- QuickKeysMenuAssign::QuickKeysMenuAssign(QuickKeysMenu* parent) @@ -485,9 +532,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.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; + } + 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); @@ -597,6 +680,12 @@ namespace MWGui mMagicList->setHighlightSelected(false); mMagicList->eventSpellClicked += MyGUI::newDelegate(this, &MagicSelectionDialog::onModelIndexSelected); + if (Settings::gui().mControllerMenus) + { + mControllerButtons.a = "#{sSelect}"; + mControllerButtons.b = "#{sCancel}"; + } + center(); } @@ -628,4 +717,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; + } } diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 904029b9a0..228eb926b4 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -72,6 +72,9 @@ namespace MWGui // Check if quick key is still valid inline void validate(int index); void unassign(keyData* key); + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; }; class QuickKeysMenuAssign : public WindowModal @@ -87,6 +90,9 @@ namespace MWGui MyGUI::Button* mCancelButton; QuickKeysMenu* mParent; + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; }; class MagicSelectionDialog : public WindowModal @@ -105,6 +111,9 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* sender); void onModelIndexSelected(SpellModel::ModelIndex index); + + bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override; + int mControllerFocus; }; } diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 6ef73c7794..e36a862730 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -327,14 +327,14 @@ namespace MWGui mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } - void SpellView::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) + void SpellView::onControllerButton(const unsigned char button) { if (mButtons.empty()) return; int prevFocus = mControllerFocus; - if (arg.button == SDL_CONTROLLER_BUTTON_A) + if (button == SDL_CONTROLLER_BUTTON_A) { // Select the focused item, if any. if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) @@ -343,22 +343,22 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) + else if (button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) { // Toggle info tooltip mControllerTooltip = !mControllerTooltip; if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size()) MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons[mControllerFocus].first); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) mControllerFocus--; - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) mControllerFocus++; - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) mControllerFocus = std::max(0, mControllerFocus - 10); - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) mControllerFocus = std::min(mControllerFocus + 10, (int)mButtons.size() - 1); - else if (arg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + else if (button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { // Jump to first item in previous group int prevGroupIndex = 0; @@ -371,7 +371,7 @@ namespace MWGui } mControllerFocus = prevGroupIndex; } - else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + else if (button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { // Jump to first item in next group for (int groupIndex : mGroupIndices) diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index 222d5f1ba3..5a22dcb4d9 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -57,7 +57,7 @@ namespace MWGui void resetScrollbars(); - void onControllerButtonEvent(const SDL_ControllerButtonEvent& arg); + void onControllerButton(const unsigned char button); private: MyGUI::ScrollView* mScrollView; diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 3eb041f6ab..b4c970c9bd 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -275,7 +275,7 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_B) MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); else - mSpellView->onControllerButtonEvent(arg); + mSpellView->onControllerButton(arg.button); return true; } diff --git a/files/data/mygui/openmw_resources.xml b/files/data/mygui/openmw_resources.xml index d107c094e3..47fa71e984 100644 --- a/files/data/mygui/openmw_resources.xml +++ b/files/data/mygui/openmw_resources.xml @@ -192,7 +192,10 @@ - + + + + From ea71a0bb4f0519bc3bcaa0de2538de8cc4da158a Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 22:13:58 -0700 Subject: [PATCH 133/455] Make controller selection wrap in inventory or containers --- apps/openmw/mwgui/itemview.cpp | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 08e5236fc6..3d01073694 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -224,19 +224,29 @@ namespace MWGui mControllerTooltip = !mControllerTooltip; updateControllerFocus(-1, mControllerFocus); } - else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP && mControllerFocus % mRows != 0) - mControllerFocus--; - else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN && mControllerFocus % mRows != mRows - 1) - mControllerFocus++; + else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) + { + if (mControllerFocus % mRows == 0) + mControllerFocus = std::min(mControllerFocus + mRows - 1, mItemCount - 1); + else + mControllerFocus--; + } + else if (button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + { + if (mControllerFocus % mRows == mRows - 1 || mControllerFocus == mItemCount - 1) + mControllerFocus -= mControllerFocus % mRows; + else + mControllerFocus++; + } else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mControllerFocus >= mRows) mControllerFocus -= mRows; - else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mControllerFocus + mRows < mItemCount) - mControllerFocus += mRows; - - if (mControllerFocus < 0) - mControllerFocus = 0; - else if (mControllerFocus >= mItemCount - 1) - mControllerFocus = mItemCount - 1; + else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + { + if (mControllerFocus + mRows < mItemCount) + mControllerFocus += mRows; + else if (mControllerFocus / mRows != (mItemCount - 1) / mRows) + mControllerFocus = mItemCount - 1; + } if (prevFocus != mControllerFocus) updateControllerFocus(prevFocus, mControllerFocus); From 6bb92c0589bbfa29dad55dcbf5c0240091e22737 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 22:55:21 -0700 Subject: [PATCH 134/455] Clean up logs --- apps/openmw/mwgui/windowmanagerimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9350ef4d1a..9066cbfdd9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -898,8 +898,7 @@ namespace MWGui int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); - // REMOVEME - Log(Debug::Error) << "getActiveControllerWindow: " << state.mWindows.size() << " windows in state, mActiveControllerWindows[mode] = " << mActiveControllerWindows[mode] << ", activeIndex=" << activeIndex; + Log(Debug::Debug) << "Getting active controller window: mode=" << mode << ", " << state.mWindows.size() << " window(s), activeIndex=" << activeIndex; // If the active window is no longer visible, find the next visible window. if (!state.mWindows[activeIndex]->isVisible()) @@ -934,8 +933,7 @@ namespace MWGui } } - // REMOVEME - Log(Debug::Error) << "cycleActiveControllerWindow: mode=" << mode << ", activeIndex=" << activeIndex; + Log(Debug::Debug) << "Cycling active controller window: mode=" << mode << ", activeIndex=" << activeIndex; if (mActiveControllerWindows[mode] != activeIndex) setActiveControllerWindow(mode, activeIndex); From 21286aa3762b9dabd9459866fc2c77d1ea35ec44 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 1 Jun 2025 23:27:49 -0700 Subject: [PATCH 135/455] Change 'Back' to 'Cancel' in button prompts to match windows --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 2 +- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 4 ++- apps/openmw/mwgui/itemselection.cpp | 2 +- apps/openmw/mwgui/merchantrepair.cpp | 2 +- apps/openmw/mwgui/recharge.cpp | 2 +- apps/openmw/mwgui/repair.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- apps/openmw/mwgui/spellcreationdialog.cpp | 4 +-- apps/openmw/mwgui/tradewindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 2 +- apps/openmw/mwgui/travelwindow.cpp | 2 +- .../mygui/openmw_controllerbuttons.layout | 26 +++++++++---------- 16 files changed, 31 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 32334962fb..270b0f1cfe 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -95,7 +95,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sCreate}"; mControllerButtons.y = "#{sMagicEffects}"; mControllerButtons.r3 = "#{sInfo}"; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index c6c810c80e..9fe098f6d5 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -58,7 +58,7 @@ namespace MWGui setCoord(200, 0, 600, 300); mControllerButtons.a = "#{sTake}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sClose}"; mControllerButtons.x = "#{sTakeAll}"; mControllerButtons.r3 = "#{sInfo}"; mControllerButtons.l2 = "#{sInventory}"; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 1a8808c8dc..ff04d5237b 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -91,7 +91,7 @@ namespace MWGui mDisableGamepadCursor = Settings::gui().mControllerMenus; mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; } void PersuasionDialog::adjustAction(MyGUI::Widget* action, int& totalHeight) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d0d9fb7401..b5e0444f25 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -61,7 +61,7 @@ namespace MWGui mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.y = "Cast Type"; mControllerButtons.l1 = "#{sItem}"; mControllerButtons.r1 = "#{sSoulGem}"; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 008e96356c..d9061ebc9e 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -132,7 +132,6 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerButtons.b = "#{sBack}"; mControllerButtons.r3 = "#{sInfo}"; } @@ -888,12 +887,14 @@ namespace MWGui case MWGui::GM_Companion: case MWGui::GM_Container: mControllerButtons.a = "Put"; + mControllerButtons.b = "#{sClose}"; mControllerButtons.x = "#{sTakeAll}"; mControllerButtons.y = ""; mControllerButtons.r2 = "#{sContainer}"; break; case MWGui::GM_Barter: mControllerButtons.a = "#{sSell}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sOffer}"; mControllerButtons.y = ""; mControllerButtons.r2 = "#{sBarter}"; @@ -901,6 +902,7 @@ namespace MWGui case MWGui::GM_Inventory: default: mControllerButtons.a = "#{sEquip}"; + mControllerButtons.b = "#{sBack}"; mControllerButtons.x = "#{sDrop}"; mControllerButtons.y = "#{sUnequip}"; mControllerButtons.r2 = ""; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index ec4ecb9d0f..ad2b141d8d 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -30,7 +30,7 @@ namespace MWGui center(); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.r3 = "#{sInfo}"; } diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index e8155ea790..c720971c5b 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -33,7 +33,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sRepair}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; } } diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 605a9a1a94..7fe1be9266 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -41,7 +41,7 @@ namespace MWGui mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem); mControllerButtons.a = "Recharge"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.y = "#{sSoulGem}"; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 6f5d256d86..32ddf58702 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -41,7 +41,7 @@ namespace MWGui mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem); mControllerButtons.a = "#{sRepair}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.y = "Tool"; } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index af75152e4b..0f6bfefb54 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -67,7 +67,7 @@ namespace MWGui mDeleteButton->setNeedKeyFocus(false); mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sClose}"; + mControllerButtons.b = "#{sCancel}"; } void SaveGameDialog::onSlotActivated(MyGUI::ListBox* sender, size_t pos) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 1c986180a8..48d7134b8a 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -36,7 +36,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; } } diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index f096beddcb..cc54fb0ceb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -100,7 +100,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sOk}"; } } @@ -598,7 +598,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerButtons.a = "#{sSelect}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sBuy}"; } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 69b16dfc93..379c18f59d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -170,7 +170,7 @@ namespace MWGui setCoord(400, 0, 400, 300); mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sOffer}"; mControllerButtons.r3 = "#{sInfo}"; mControllerButtons.l2 = "#{sInventory}"; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 65e468ebd4..d5c4f7efbc 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -42,7 +42,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; } } diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index eeb2df868f..d205fcba31 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -47,7 +47,7 @@ namespace MWGui { mDisableGamepadCursor = true; mControllerButtons.a = "#{sTravel}"; - mControllerButtons.b = "#{sBack}"; + mControllerButtons.b = "#{sCancel}"; } } diff --git a/files/data/mygui/openmw_controllerbuttons.layout b/files/data/mygui/openmw_controllerbuttons.layout index 42c89e10b4..ffcfeaee16 100644 --- a/files/data/mygui/openmw_controllerbuttons.layout +++ b/files/data/mygui/openmw_controllerbuttons.layout @@ -101,19 +101,6 @@ - - - - - - - - - - - - - @@ -179,6 +166,19 @@ + + + + + + + + + + + + + From 8ae193abe8fa3ceaaf5f5a0956ac82740cd4991e Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 2 Jun 2025 00:11:01 -0700 Subject: [PATCH 136/455] Fix issue when selecting an inventory item with the tooltip visible --- apps/openmw/mwgui/inventorywindow.cpp | 5 +++++ apps/openmw/mwgui/inventorywindow.hpp | 1 + apps/openmw/mwgui/itemview.hpp | 1 + apps/openmw/mwinput/controllermanager.cpp | 9 +++++++++ 4 files changed, 16 insertions(+) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index d9061ebc9e..99af4d63a3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -880,6 +880,11 @@ namespace MWGui return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1)); } + bool InventoryWindow::isControllerTooltipVisible() + { + return mItemView->isControllerTooltipVisible(); + } + ControllerButtonStr* InventoryWindow::getControllerButtons() { switch (mGuiMode) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 5b4d61f272..86f31191f9 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -68,6 +68,7 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Inventory"; } ControllerButtonStr* getControllerButtons() override; + bool isControllerTooltipVisible(); protected: void onTitleDoubleClicked() override; diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 29d44c6b09..2f5cb99846 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -35,6 +35,7 @@ namespace MWGui void setActiveControllerWindow(bool active); int getControllerFocus() { return mControllerFocus; } int getItemCount() { return mItemCount; } + bool isControllerTooltipVisible() { return mControllerTooltip; } void onControllerButton(const unsigned char button); private: diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 48034cae01..1647a5bab2 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -16,6 +16,7 @@ #include "../mwbase/luamanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwgui/inventorywindow.hpp" #include "actions.hpp" #include "bindingsmanager.hpp" @@ -252,6 +253,14 @@ namespace MWInput MWGui::WindowBase* topWin = winMgr->getActiveControllerWindow(); if (topWin) { + // When the inventory tooltip is visible, we don't actually want the A button to + // act like a mouse button; it should act normally. + if (treatAsMouse + && arg.button == SDL_CONTROLLER_BUTTON_A + && (MWGui::InventoryWindow *)topWin == winMgr->getInventoryWindow() + && ((MWGui::InventoryWindow *)topWin)->isControllerTooltipVisible()) + treatAsMouse = false; + mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); // Fall through to mouse click From 6da6b9b98f9ea64035cf0f3ac6456d2bd721c6ea Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 2 Jun 2025 00:17:10 -0700 Subject: [PATCH 137/455] Update controller support for scroll window to use the keybaord for smoother scrolling --- apps/openmw/mwgui/scrollwindow.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index d032963417..4be1461d0c 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -143,10 +143,8 @@ namespace MWGui } else if (arg.button == SDL_CONTROLLER_BUTTON_B) onCloseButtonClicked(mCloseButton); - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) - onKeyButtonPressed(nullptr, MyGUI::KeyCode::ArrowUp, 0); - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) - onKeyButtonPressed(nullptr, MyGUI::KeyCode::ArrowDown, 0); + else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + return false; // Fall through to keyboard return true; } From e50822d1d279c6f26cf1a83745c928934b3226e4 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 2 Jun 2025 00:41:09 -0700 Subject: [PATCH 138/455] Remember the selected dialog topic when new topics are added to the list --- apps/openmw/mwgui/dialogue.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index ff04d5237b..c928e6a7cf 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -600,6 +600,11 @@ namespace MWGui void DialogueWindow::updateTopicsPane() { + const std::string focusedTopic = + Settings::gui().mControllerMenus && mControllerFocus < mTopicsList->getItemCount() + ? mTopicsList->getItemNameAt(mControllerFocus) + : ""; + mTopicsList->clear(); for (auto& linkPair : mTopicLinks) mDeleteLater.push_back(std::move(linkPair.second)); @@ -654,6 +659,9 @@ namespace MWGui 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(); From a6d03717cb47a63889e91141ea2dfb408dd0e9e6 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 3 Jun 2025 00:44:45 -0700 Subject: [PATCH 139/455] Hide cursor icon when showing controller tooltips --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/inventorywindow.cpp | 5 ----- apps/openmw/mwgui/inventorywindow.hpp | 1 - apps/openmw/mwgui/itemview.cpp | 22 +++++++++++++++------- apps/openmw/mwgui/itemview.hpp | 2 -- apps/openmw/mwgui/tooltips.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 11 +++++++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 +++ apps/openmw/mwinput/controllermanager.cpp | 8 ++++++-- apps/openmw/mwinput/mousemanager.cpp | 4 ++-- 10 files changed, 40 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 180c9bfee3..ee1990878a 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -390,6 +390,8 @@ namespace MWBase /// 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 const bool getControllerTooltip() const = 0; + virtual void setControllerTooltip(bool enabled) = 0; virtual void updateControllerButtonsOverlay() = 0; // Used in Lua bindings diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 99af4d63a3..d9061ebc9e 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -880,11 +880,6 @@ namespace MWGui return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1)); } - bool InventoryWindow::isControllerTooltipVisible() - { - return mItemView->isControllerTooltipVisible(); - } - ControllerButtonStr* InventoryWindow::getControllerButtons() { switch (mGuiMode) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 86f31191f9..5b4d61f272 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -68,7 +68,6 @@ namespace MWGui std::string_view getWindowIdForLua() const override { return "Inventory"; } ControllerButtonStr* getControllerButtons() override; - bool isControllerTooltipVisible(); protected: void onTitleDoubleClicked() override; diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 3d01073694..3d8cf91f8a 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -81,7 +81,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { - mControllerTooltip = false; + MWBase::Environment::get().getWindowManager()->setControllerTooltip(false); mControllerFocus = std::clamp(mControllerFocus, 0, mItemCount - 1); updateControllerFocus(-1, mControllerFocus); } @@ -190,10 +190,11 @@ namespace MWGui { mControllerActiveWindow = active; - if (mControllerTooltip) + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getControllerTooltip()) { - MWBase::Environment::get().getWindowManager()->setCursorActive(false); - mControllerTooltip = false; + winMgr->setCursorActive(false); + winMgr->setControllerTooltip(false); } if (active) @@ -221,7 +222,8 @@ namespace MWGui else if (button == SDL_CONTROLLER_BUTTON_RIGHTSTICK) { // Toggle info tooltip - mControllerTooltip = !mControllerTooltip; + MWBase::Environment::get().getWindowManager()->setControllerTooltip( + !MWBase::Environment::get().getWindowManager()->getControllerTooltip()); updateControllerFocus(-1, mControllerFocus); } else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) @@ -250,10 +252,15 @@ namespace MWGui 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; @@ -272,8 +279,6 @@ namespace MWGui if (focused) { focused->setControllerFocus(true); - if (mControllerTooltip) - MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); // Scroll the list to keep the active item in view int column = newFocus / mRows; @@ -281,6 +286,9 @@ namespace MWGui 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); } } } diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 2f5cb99846..7915a4dcf1 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -35,7 +35,6 @@ namespace MWGui void setActiveControllerWindow(bool active); int getControllerFocus() { return mControllerFocus; } int getItemCount() { return mItemCount; } - bool isControllerTooltipVisible() { return mControllerTooltip; } void onControllerButton(const unsigned char button); private: @@ -56,7 +55,6 @@ namespace MWGui int mItemCount = 0; int mRows; int mControllerFocus = 0; - bool mControllerTooltip; bool mControllerActiveWindow; void updateControllerFocus(int prevFocus, int newFocus); }; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 28f0b80010..67c10b980f 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -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(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9066cbfdd9..5b32cb8709 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -941,6 +941,9 @@ namespace MWGui void WindowManager::setActiveControllerWindow(GuiMode mode, int activeIndex) { + if (!Settings::gui().mControllerMenus) + return; + int winCount = mGuiModeStates[mode].mWindows.size(); if (winCount == 0) return; @@ -2590,6 +2593,14 @@ namespace MWGui return res; } + void WindowManager::setControllerTooltip(bool enabled) + { + if (!Settings::gui().mControllerMenus) + return; + + mControllerTooltip = enabled; + } + void WindowManager::updateControllerButtonsOverlay() { if (!Settings::gui().mControllerMenus || !mControllerButtonsOverlay) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index d5e36a0971..97847551b1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -396,6 +396,8 @@ namespace MWGui WindowBase* getActiveControllerWindow() override; void cycleActiveControllerWindow(bool next) override; void setActiveControllerWindow(GuiMode mode, int activeIndex) override; + const bool getControllerTooltip() const override { return mControllerTooltip; } + void setControllerTooltip(bool enabled) override; void updateControllerButtonsOverlay() override; // Used in Lua bindings @@ -508,6 +510,7 @@ namespace MWGui std::vector mGuiModes; // The active window for controller mode for each GUI mode. std::map mActiveControllerWindows; + bool mControllerTooltip; std::unique_ptr mCursorManager; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 1647a5bab2..ac2e07e0cc 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -257,8 +257,7 @@ namespace MWInput // act like a mouse button; it should act normally. if (treatAsMouse && arg.button == SDL_CONTROLLER_BUTTON_A - && (MWGui::InventoryWindow *)topWin == winMgr->getInventoryWindow() - && ((MWGui::InventoryWindow *)topWin)->isControllerTooltipVisible()) + && winMgr->getControllerTooltip()) treatAsMouse = false; mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); @@ -363,6 +362,11 @@ namespace MWInput && (arg.axis == SDL_CONTROLLER_AXIS_LEFTX || arg.axis == SDL_CONTROLLER_AXIS_LEFTY)) { // Treat the left stick like a cursor, which is the default behavior. + if (winMgr->getControllerTooltip()) + { + winMgr->setControllerTooltip(false); + winMgr->setCursorVisible(true); + } return false; } diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 2c6798406b..1367084872 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -266,8 +266,8 @@ namespace MWInput void MouseManager::warpMouseToWidget(MyGUI::Widget* widget) { - float widgetX = widget->getAbsoluteCoord().left + 4; - float widgetY = widget->getAbsoluteCoord().top + 4; + float widgetX = widget->getAbsoluteCoord().left + widget->getWidth() / 2; + float widgetY = widget->getAbsoluteCoord().top + widget->getHeight() - 30; if (std::abs(mGuiCursorX - widgetX) > 1 || std::abs(mGuiCursorY - widgetY) > 1) { mGuiCursorX = widgetX; From a824993a605e86a0487cc70c300cab47f43bb247 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 3 Jun 2025 08:56:39 -0700 Subject: [PATCH 140/455] Fix controller tooltips in magic menu appearing for wrong spell --- apps/openmw/mwinput/mousemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 1367084872..540c334b1e 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -267,7 +267,7 @@ namespace MWInput void MouseManager::warpMouseToWidget(MyGUI::Widget* widget) { float widgetX = widget->getAbsoluteCoord().left + widget->getWidth() / 2; - float widgetY = widget->getAbsoluteCoord().top + widget->getHeight() - 30; + float widgetY = widget->getAbsoluteCoord().top + widget->getHeight() / 4; if (std::abs(mGuiCursorX - widgetX) > 1 || std::abs(mGuiCursorY - widgetY) > 1) { mGuiCursorX = widgetX; From ae676e1d702352faf1c8d08dfde4d51367b3da03 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 3 Jun 2025 20:17:53 -0700 Subject: [PATCH 141/455] Fix not checking bounds in some controller menus --- apps/openmw/mwgui/itemchargeview.cpp | 4 ++-- apps/openmw/mwgui/merchantrepair.cpp | 15 +++++++++------ apps/openmw/mwgui/spellbuyingwindow.cpp | 17 ++++++++++------- apps/openmw/mwgui/spellview.cpp | 5 +++-- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index abbf328a8c..4ac59642ea 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -280,10 +280,10 @@ namespace MWGui mLines[newFocus].mIcon->setControllerFocus(true); // Scroll the list to keep the active item in view - if (mControllerFocus <= 3) + if (newFocus <= 3) mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); else - mScrollView->setViewOffset(MyGUI::IntPoint(-55 * (mControllerFocus - 3), 0)); + mScrollView->setViewOffset(MyGUI::IntPoint(-55 * (newFocus - 3), 0)); } } } diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index c720971c5b..c659cac139 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -205,13 +205,16 @@ namespace MWGui } // Scroll the list to keep the active item in view - int line = mButtons[mControllerFocus].second; - if (line <= 5) - mList->setViewOffset(MyGUI::IntPoint(0, 0)); - else + if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) { - const int lineHeight = Settings::gui().mFontSize + 2; - mList->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5))); + int 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; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 48d7134b8a..27065bc2c3 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -247,14 +247,17 @@ namespace MWGui mSpellButtons[mControllerFocus].first->setStateSelected(true); } - // Scroll the list to keep the active item in view - int line = mSpellButtons[mControllerFocus].second; - if (line <= 5) - mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); - else + if (mControllerFocus >= 0 && mControllerFocus < mSpellButtons.size()) { - const int lineHeight = Settings::gui().mFontSize + 2; - mSpellsView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5))); + // Scroll the list to keep the active item in view + int 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))); + } } return true; diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index e36a862730..8a18eb440f 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -408,8 +408,6 @@ namespace MWGui if (focused) { focused->onMouseSetFocus(nullptr); - if (mControllerTooltip) - MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); // Scroll the list to keep the active item in view int line = mButtons[newFocus].second; @@ -420,6 +418,9 @@ namespace MWGui const int lineHeight = focused->getHeight(); mScrollView->setViewOffset(MyGUI::IntPoint(0, -lineHeight * (line - 5))); } + + if (mControllerTooltip) + MWBase::Environment::get().getInputManager()->warpMouseToWidget(focused); } } } From 2dfe1ef7b1ad520173cfa98061777f31fc5a3c59 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 3 Jun 2025 21:23:21 -0700 Subject: [PATCH 142/455] Show L1 and R1 buttons next to tabs --- apps/openmw/mwgui/inventorywindow.cpp | 16 ++++++++++ apps/openmw/mwgui/tradewindow.cpp | 29 +++++++++++++++---- .../data/mygui/openmw_inventory_window.layout | 12 ++++++++ files/data/mygui/openmw_trade_window.layout | 12 ++++++++ 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index d9061ebc9e..c68fd0fbc7 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -132,6 +132,14 @@ namespace MWGui if (Settings::gui().mControllerMenus) { + // Show L1 and R1 buttons next to tabs + MyGUI::Widget* image; + getWidget(image, "BtnL1Image"); + image->setUserString("Hidden", "false"); + + getWidget(image, "BtnR1Image"); + image->setUserString("Hidden", "false"); + mControllerButtons.r3 = "#{sInfo}"; } @@ -1036,6 +1044,14 @@ namespace MWGui 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); } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 379c18f59d..ca0924396e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -169,11 +169,22 @@ namespace MWGui setCoord(400, 0, 400, 300); - mControllerButtons.a = "#{sBuy}"; - mControllerButtons.b = "#{sCancel}"; - mControllerButtons.x = "#{sOffer}"; - mControllerButtons.r3 = "#{sInfo}"; - mControllerButtons.l2 = "#{sInventory}"; + if (Settings::gui().mControllerMenus) + { + // Show L1 and R1 buttons next to tabs + MyGUI::Widget* image; + getWidget(image, "BtnL1Image"); + image->setUserString("Hidden", "false"); + + getWidget(image, "BtnR1Image"); + image->setUserString("Hidden", "false"); + + mControllerButtons.a = "#{sBuy}"; + mControllerButtons.b = "#{sCancel}"; + mControllerButtons.x = "#{sOffer}"; + mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.l2 = "#{sInventory}"; + } } void TradeWindow::setPtr(const MWWorld::Ptr& actor) @@ -734,6 +745,14 @@ namespace MWGui 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); } diff --git a/files/data/mygui/openmw_inventory_window.layout b/files/data/mygui/openmw_inventory_window.layout index a555c94031..c8b3a6857b 100644 --- a/files/data/mygui/openmw_inventory_window.layout +++ b/files/data/mygui/openmw_inventory_window.layout @@ -30,6 +30,12 @@ + + + + + + @@ -50,6 +56,12 @@ + + + + + + diff --git a/files/data/mygui/openmw_trade_window.layout b/files/data/mygui/openmw_trade_window.layout index 49ccf25ba2..c514155981 100644 --- a/files/data/mygui/openmw_trade_window.layout +++ b/files/data/mygui/openmw_trade_window.layout @@ -7,6 +7,12 @@ + + + + + + @@ -27,6 +33,12 @@ + + + + + + From c35e0d733629f90c4018562ba06d257fb15eae66 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 3 Jun 2025 21:57:43 -0700 Subject: [PATCH 143/455] Add menu click sounds to some menus when activated by the controller --- apps/openmw/mwgui/alchemywindow.cpp | 6 ++++-- apps/openmw/mwgui/dialogue.cpp | 4 ++++ apps/openmw/mwgui/inventorywindow.cpp | 2 ++ apps/openmw/mwgui/levelupdialog.cpp | 1 + apps/openmw/mwgui/recharge.cpp | 3 +++ apps/openmw/mwgui/repair.cpp | 3 +++ apps/openmw/mwgui/savegamedialog.cpp | 2 ++ apps/openmw/mwgui/settingswindow.cpp | 4 ++-- apps/openmw/mwgui/spellcreationdialog.cpp | 6 ++++++ apps/openmw/mwgui/textinput.cpp | 1 + apps/openmw/mwgui/tradewindow.cpp | 2 ++ apps/openmw/mwgui/travelwindow.cpp | 3 +++ apps/openmw/mwgui/waitdialog.cpp | 6 ++++++ 13 files changed, 39 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 270b0f1cfe..f79abf11c5 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -557,15 +557,16 @@ namespace MWGui if (isFilterListOpen) { // When the filter list combo box is open, send all inputs to it. - if (arg.button == SDL_CONTROLLER_BUTTON_A) + if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_Y) { // Select the highlighted entry in the combo box and close it. int index = mFilterValue->getIndexSelected(); mFilterValue->setIndexSelected(index); onFilterChanged(mFilterValue, index); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); // Close list + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } - else if (arg.button == SDL_CONTROLLER_BUTTON_B || arg.button == SDL_CONTROLLER_BUTTON_Y) + else if (arg.button == SDL_CONTROLLER_BUTTON_B) { // Close the list without selecting anything mFilterValue->clearIndexSelected(); @@ -610,6 +611,7 @@ namespace MWGui 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(); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index c928e6a7cf..d409cd8856 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -177,7 +177,10 @@ namespace MWGui 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) @@ -960,6 +963,7 @@ namespace MWGui 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) { diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index c68fd0fbc7..c943611820 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1001,6 +1001,7 @@ namespace MWGui 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) { @@ -1014,6 +1015,7 @@ namespace MWGui onFilterChanged(mFilterMisc); else if (mFilterMisc->getStateSelected()) onFilterChanged(mFilterAll); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } else { diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 3ed3e3b6fc..de79e2a8b3 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -388,6 +388,7 @@ namespace MWGui { if (mControllerFocus >= 0 && 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) { diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 7fe1be9266..bf71972d0d 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -144,7 +144,10 @@ namespace MWGui { 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 diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 32ddf58702..c1241f53f5 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -158,7 +158,10 @@ namespace MWGui { 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 diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 0f6bfefb54..7e9067b81a 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -535,6 +535,7 @@ namespace MWGui onOkButtonClicked(mOkButton); else onCancelButtonClicked(mCancelButton); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { @@ -546,6 +547,7 @@ namespace MWGui 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) { diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 70f2ab8ff3..89cdb6694a 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1146,7 +1146,7 @@ namespace MWGui uint32_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) @@ -1154,7 +1154,7 @@ namespace MWGui uint32_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; } diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index cc54fb0ceb..103fa3df1d 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -401,11 +401,15 @@ namespace MWGui 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) @@ -1036,10 +1040,12 @@ namespace MWGui if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) { onAvailableEffectClicked(mAvailableButtons[mAvailableFocus]); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } else if (mRightColumn && mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) { onEditEffect(mEffectButtons[mEffectFocus].second); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 6ef8f1ef8e..7612cf4c67 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -90,6 +90,7 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { onOkClicked(nullptr); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); return true; } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ca0924396e..fbbd98cc31 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -717,6 +717,7 @@ namespace MWGui 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) { @@ -730,6 +731,7 @@ namespace MWGui 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 || diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index d205fcba31..48717b0825 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -259,7 +259,10 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { if (mControllerFocus >= 0 && mControllerFocus < mDestinationButtons.size()) + { onTravelButtonClick(mDestinationButtons[mControllerFocus]); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); + } } else if (arg.button == SDL_CONTROLLER_BUTTON_B) { diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 8df47e7d58..e2932caf46 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -339,11 +339,17 @@ namespace MWGui bool WaitDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_A) + { onWaitButtonClicked(mWaitButton); + 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 && mUntilHealedButton->getVisible()) + { onUntilHealedButtonClicked(mUntilHealedButton); + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); + } 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) From dc68f28a40580078a892e9a5e35951d2a4fc0481 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 6 Jun 2025 00:34:01 -0700 Subject: [PATCH 144/455] Disable right stick from panning map if zooming maps is enabled --- apps/openmw/mwgui/mapwindow.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index f90180fb44..1c1920f0b2 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -839,7 +839,7 @@ namespace MWGui mControllerButtons.b = "#{sBack}"; mControllerButtons.x = global ? "#{sLocal}" : "#{sWorld}"; mControllerButtons.y = "#{sCenter}"; - mControllerButtons.rStick = "#{sMove}"; + mControllerButtons.rStick = Settings::map().mAllowZooming ? "" : "#{sMove}"; } } @@ -1420,11 +1420,15 @@ namespace MWGui bool MapWindow::onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { - int dx = arg.axis == SDL_CONTROLLER_AXIS_RIGHTX ? -30.0f * arg.value / 32767 : 0; - int dy = arg.axis == SDL_CONTROLLER_AXIS_RIGHTY ? -30.0f * arg.value / 32767 : 0; - shiftMap(dx, dy); + if (!Settings::map().mAllowZooming) + { + int dx = arg.axis == SDL_CONTROLLER_AXIS_RIGHTX ? -30.0f * arg.value / 32767 : 0; + int dy = arg.axis == SDL_CONTROLLER_AXIS_RIGHTY ? -30.0f * arg.value / 32767 : 0; + shiftMap(dx, dy); - return true; + return true; + } + return false; } void MapWindow::shiftMap(int dx, int dy) From b2a5ded9294b3bf26438710526ca168221f111a2 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 6 Jun 2025 00:34:23 -0700 Subject: [PATCH 145/455] Fix scrolling list of items to repair or recharge --- apps/openmw/mwgui/itemchargeview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 4ac59642ea..0b9ea842d9 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -283,7 +283,7 @@ namespace MWGui if (newFocus <= 3) mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); else - mScrollView->setViewOffset(MyGUI::IntPoint(-55 * (newFocus - 3), 0)); + mScrollView->setViewOffset(MyGUI::IntPoint(0, -55 * (newFocus - 3))); } } } From b1989ffdf7e1d63b6d28f042352ccf8b56ea1e84 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Fri, 6 Jun 2025 00:34:42 -0700 Subject: [PATCH 146/455] Limit size of inventory menu in controller mode --- apps/openmw/mwgui/inventorywindow.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index c943611820..b735d89cf8 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1032,10 +1032,11 @@ namespace MWGui if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory) { - // Fill the screen, or limit to a certain size on large screens. + // 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, 2200); - int height = std::min(viewSize.height - 48 - 48, 1200); + int width = std::min(viewSize.width, 1600); + int height = std::min(viewSize.height - 48 - 48, 750); int x = (viewSize.width - width) / 2; int y = (viewSize.height - height) / 2; From ea70f2b5c22033858be178d0c9f09dedaea4218d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 7 Jun 2025 00:35:46 -0700 Subject: [PATCH 147/455] Show tooltips in controller mode on spell menus --- apps/openmw/mwgui/spellbuyingwindow.cpp | 19 +++++++++++++++++++ apps/openmw/mwgui/spellcreationdialog.cpp | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 27065bc2c3..3bc38b2e44 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -9,6 +9,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" @@ -37,6 +38,7 @@ namespace MWGui mDisableGamepadCursor = true; mControllerButtons.a = "#{sBuy}"; mControllerButtons.b = "#{sCancel}"; + mControllerButtons.r3 = "#{sInfo}"; } } @@ -144,6 +146,13 @@ namespace MWGui mControllerFocus = 0; if (mSpellButtons.size() > 0) mSpellButtons[0].first->setStateSelected(true); + + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (winMgr->getControllerTooltip()) + { + winMgr->setCursorActive(false); + winMgr->setControllerTooltip(false); + } } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the @@ -228,6 +237,12 @@ namespace MWGui { 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) @@ -258,6 +273,10 @@ namespace MWGui 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; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 103fa3df1d..119a32ffea 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -14,6 +14,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" @@ -102,6 +103,7 @@ namespace MWGui mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sCancel}"; mControllerButtons.x = "#{sOk}"; + mControllerButtons.r3 = "#{sInfo}"; } } @@ -615,6 +617,13 @@ namespace MWGui mPtr = actor; mNameEdit->setCaption({}); + MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); + if (Settings::gui().mControllerMenus && winMgr->getControllerTooltip()) + { + winMgr->setCursorActive(false); + winMgr->setControllerTooltip(false); + } + startEditing(); } @@ -1048,6 +1057,12 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } } + 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 (mRightColumn && mEffectButtons.size() > 0) @@ -1108,6 +1123,13 @@ namespace MWGui mAvailableEffectsList->setViewOffset(-lineHeight * (mAvailableFocus - 5)); } + if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + { + // Warp the mouse to the selected spell to show the tooltip + if (MWBase::Environment::get().getWindowManager()->getControllerTooltip()) + MWBase::Environment::get().getInputManager()->warpMouseToWidget(mAvailableButtons[mAvailableFocus]); + } + return true; } } From be32cbb4139ed7e10bf174c95ffec18c6db62103 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 7 Jun 2025 22:51:29 -0700 Subject: [PATCH 148/455] Fix clang format issues --- apps/openmw/mwgui/alchemywindow.cpp | 3 ++- apps/openmw/mwgui/class.cpp | 4 ++-- apps/openmw/mwgui/confirmationdialog.cpp | 4 ++-- apps/openmw/mwgui/container.cpp | 11 ++++----- .../openmw/mwgui/controllerbuttonsoverlay.cpp | 3 ++- apps/openmw/mwgui/dialogue.cpp | 11 +++++---- apps/openmw/mwgui/inventorywindow.cpp | 24 ++++++++++++------- apps/openmw/mwgui/itemview.cpp | 4 ++-- apps/openmw/mwgui/journalwindow.cpp | 15 ++++++++---- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 6 ++--- apps/openmw/mwgui/quickkeysmenu.cpp | 3 +-- apps/openmw/mwgui/recharge.cpp | 3 +-- apps/openmw/mwgui/repair.cpp | 3 +-- apps/openmw/mwgui/review.cpp | 8 +++---- apps/openmw/mwgui/savegamedialog.cpp | 11 +++++---- apps/openmw/mwgui/spellcreationdialog.cpp | 18 +++++--------- apps/openmw/mwgui/spellcreationdialog.hpp | 2 +- apps/openmw/mwgui/spellview.cpp | 2 +- apps/openmw/mwgui/spellview.hpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 8 +++---- apps/openmw/mwgui/windowmanagerimp.cpp | 9 ++++--- apps/openmw/mwinput/controllermanager.cpp | 4 +--- 23 files changed, 80 insertions(+), 80 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index f79abf11c5..074250985c 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -552,7 +552,8 @@ namespace MWGui 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; + bool isFilterListOpen + = focus != nullptr && focus->getParent() != nullptr && focus->getParent()->getParent() == mFilterValue; if (isFilterListOpen) { diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index f0fe5537da..730ade1615 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -91,8 +91,8 @@ namespace MWGui { onBackClicked(mBackButton); } - else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus) || - (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus)) + else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mOkButtonFocus) + || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mOkButtonFocus)) { mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 36b01112e5..f858b9628a 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -86,8 +86,8 @@ namespace MWGui { onCancelButtonClicked(mCancelButton); } - else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mOkButtonFocus) || - (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mOkButtonFocus)) + else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mOkButtonFocus) + || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mOkButtonFocus)) { mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 9fe098f6d5..0be99d38a0 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -127,8 +127,7 @@ namespace MWGui if (!onTakeItem(item, count)) return; - MWGui::InventoryWindow* inventoryWindow - = MWBase::Environment::get().getWindowManager()->getInventoryWindow(); + MWGui::InventoryWindow* inventoryWindow = MWBase::Environment::get().getWindowManager()->getInventoryWindow(); ItemModel* playerModel = inventoryWindow->getModel(); mModel->moveItem(item, count, playerModel); @@ -385,11 +384,9 @@ namespace MWGui 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) + 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); } diff --git a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp index f4b35ac4de..96438fa583 100644 --- a/apps/openmw/mwgui/controllerbuttonsoverlay.cpp +++ b/apps/openmw/mwgui/controllerbuttonsoverlay.cpp @@ -75,7 +75,8 @@ namespace MWGui setVisible(buttonCount > 0); } - int ControllerButtonsOverlay::updateButton(MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr) + int ControllerButtonsOverlay::updateButton( + MyGUI::TextBox* text, MyGUI::ImageBox* image, const std::string& buttonStr) { if (buttonStr.length() > 0) { diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index d409cd8856..5c325a52d3 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -603,10 +603,10 @@ namespace MWGui void DialogueWindow::updateTopicsPane() { - const std::string focusedTopic = - Settings::gui().mControllerMenus && mControllerFocus < mTopicsList->getItemCount() - ? mTopicsList->getItemNameAt(mControllerFocus) - : ""; + const std::string focusedTopic + = Settings::gui().mControllerMenus && mControllerFocus < mTopicsList->getItemCount() + ? mTopicsList->getItemNameAt(mControllerFocus) + : ""; mTopicsList->clear(); for (auto& linkPair : mTopicLinks) @@ -931,7 +931,8 @@ namespace MWGui { mGoodbyeButton->setStateSelected(focused); } - else { + else + { std::string keyword = mTopicsList->getItemNameAt(mControllerFocus); if (keyword.length() == 0) return; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index b735d89cf8..fd06286a6c 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -941,15 +941,19 @@ namespace MWGui else if (mGuiMode == MWGui::GM_Companion && mDragAndDrop->mIsOnDragAndDrop) { // Drag and drop the item on the companion's window. - MWGui::CompanionWindow* companionWindow = - (MWGui::CompanionWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0); + MWGui::CompanionWindow* companionWindow = (MWGui::CompanionWindow*)MWBase::Environment::get() + .getWindowManager() + ->getGuiModeWindows(mGuiMode) + .at(0); mDragAndDrop->drop(companionWindow->getModel(), companionWindow->getItemView()); } else if (mGuiMode == MWGui::GM_Container && mDragAndDrop->mIsOnDragAndDrop) { // Drag and drop the item on the container window. - MWGui::ContainerWindow* containerWindow = - (MWGui::ContainerWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0); + MWGui::ContainerWindow* containerWindow = (MWGui::ContainerWindow*)MWBase::Environment::get() + .getWindowManager() + ->getGuiModeWindows(mGuiMode) + .at(0); mDragAndDrop->drop(containerWindow->getModel(), containerWindow->getItemView()); } // GM_Barter is handled by onControllerButtonEvent. No other steps are necessary. @@ -967,16 +971,20 @@ namespace MWGui { // Take all. Pass the button press to the container window and let it do the // logic of taking all. - MWGui::ContainerWindow* containerWindow = - (MWGui::ContainerWindow *)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(0); + MWGui::ContainerWindow* containerWindow = (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 = - (MWGui::TradeWindow*)MWBase::Environment::get().getWindowManager()->getGuiModeWindows(mGuiMode).at(1); + MWGui::TradeWindow* tradeWindow = (MWGui::TradeWindow*)MWBase::Environment::get() + .getWindowManager() + ->getGuiModeWindows(mGuiMode) + .at(1); tradeWindow->onControllerButtonEvent(arg); } } diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 3d8cf91f8a..464cec5800 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -268,14 +268,14 @@ namespace MWGui if (prevFocus >= 0 && prevFocus < mItemCount) { - ItemWidget* prev = (ItemWidget *)dragArea->getChildAt(prevFocus); + ItemWidget* prev = (ItemWidget*)dragArea->getChildAt(prevFocus); if (prev) prev->setControllerFocus(false); } if (mControllerActiveWindow && newFocus >= 0 && newFocus < mItemCount) { - ItemWidget* focused = (ItemWidget *)dragArea->getChildAt(newFocus); + ItemWidget* focused = (ItemWidget*)dragArea->getChildAt(newFocus); if (focused) { focused->setControllerFocus(true); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index bff4b66c72..77918f2a89 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -496,7 +496,8 @@ namespace MyGUI::Button* listItem = _list->getItemWidget(_list->getItemNameAt(i)); if (listItem) { - listItem->setTextColour(mButtons.size() == _selectedIndex ? MWGui::journalHeaderColour : MyGUI::Colour::Black); + listItem->setTextColour( + mButtons.size() == _selectedIndex ? MWGui::journalHeaderColour : MyGUI::Colour::Black); mButtons.push_back(listItem); } } @@ -737,8 +738,10 @@ namespace // 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 + if (mSelectedIndex >= 26) + russianOffset++; + if (mSelectedIndex >= 27) + russianOffset++; // 27, not 28, because of skipping char 26 notifyIndexLinkClicked(isRussian ? mSelectedIndex + russianOffset : mSelectedIndex + 'A'); } return true; @@ -776,7 +779,8 @@ namespace notifyCancel(getWidget(CancelBTN)); mQuestMode = false; } - else { + else + { // Show the quest overlay if viewing a journal entry or the topics if (!mOptionsMode) notifyOptions(getWidget(OptionsBTN)); @@ -793,7 +797,8 @@ namespace notifyCancel(getWidget(CancelBTN)); mQuestMode = false; } - else { + else + { // Show the topics overlay if viewing a journal entry or the quest list if (!mOptionsMode) notifyOptions(getWidget(OptionsBTN)); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index de79e2a8b3..8b022258cf 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -387,7 +387,7 @@ namespace MWGui if (arg.button == SDL_CONTROLLER_BUTTON_A) { if (mControllerFocus >= 0 && mControllerFocus < mAttributeButtons.size()) - onAttributeClicked(mAttributeButtons[mControllerFocus]); + onAttributeClicked(mAttributeButtons[mControllerFocus]); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Item Gold Up")); } else if (arg.button == SDL_CONTROLLER_BUTTON_X) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 1c1920f0b2..fb2074570f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -1440,14 +1440,12 @@ namespace MWGui { mNeedDoorMarkersUpdate = true; mLocalMap->setViewOffset( - MyGUI::IntPoint( - mLocalMap->getViewOffset().left + dx, mLocalMap->getViewOffset().top + dy)); + MyGUI::IntPoint(mLocalMap->getViewOffset().left + dx, mLocalMap->getViewOffset().top + dy)); } else { mGlobalMap->setViewOffset( - MyGUI::IntPoint( - mGlobalMap->getViewOffset().left + dx, mGlobalMap->getViewOffset().top + dy)); + MyGUI::IntPoint(mGlobalMap->getViewOffset().left + dx, mGlobalMap->getViewOffset().top + dy)); } } diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index daf49e1101..b834df83e2 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -469,8 +469,7 @@ namespace MWGui 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) + 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) { diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index bf71972d0d..cfe7fedc60 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -142,8 +142,7 @@ namespace MWGui bool Recharge::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { - if ((arg.button == SDL_CONTROLLER_BUTTON_A && !mGemBox->getVisible()) - || arg.button == SDL_CONTROLLER_BUTTON_Y) + 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")); diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index c1241f53f5..8dbac7ef48 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -156,8 +156,7 @@ namespace MWGui bool Repair::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { - if ((arg.button == SDL_CONTROLLER_BUTTON_A && !mToolBox->getVisible()) - || arg.button == SDL_CONTROLLER_BUTTON_Y) + 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")); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 003cdb49cb..dcaa49b1e2 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -542,7 +542,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - switch(mControllerFocus) + switch (mControllerFocus) { case 0: onNameClicked(mButtons[0]); @@ -573,15 +573,13 @@ namespace MWGui { onOkClicked(mButtons[5]); } - else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP || - arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + 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) + 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()); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 7e9067b81a..61cbae81f0 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -163,10 +163,11 @@ namespace MWGui hour = 12; ESM::EpochTimeStamp currentDate = timeManager.getEpochTimeStamp(); - std::string daysPassed = Misc::StringUtils::format("#{Calendar:day} %i", timeManager.getTimeStamp().getDay()); + 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); + std::string autoFilename = Misc::StringUtils::format( + "%s - %i %s %i %s", daysPassed, currentDate.mDay, month, hour, formattedHour); mSaveNameEdit->setCaptionWithReplacing(autoFilename); } @@ -561,8 +562,8 @@ namespace MWGui 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)) + else if ((arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && !mOkButtonFocus) + || (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && mOkButtonFocus)) { mOkButtonFocus = !mOkButtonFocus; mOkButton->setStateSelected(mOkButtonFocus); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 119a32ffea..5085464fdb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -13,8 +13,8 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" @@ -550,10 +550,8 @@ namespace MWGui if (prevFocus >= 0 && prevFocus < mButtons.size()) { MyGUI::TextBox* button = mButtons[prevFocus]; - if (button == mMagnitudeMinValue || - button == mMagnitudeMaxValue || - button == mDurationValue || - button == mAreaValue) + if (button == mMagnitudeMinValue || button == mMagnitudeMaxValue || button == mDurationValue + || button == mAreaValue) { button->setTextColour(textColours.normal); } @@ -566,10 +564,8 @@ namespace MWGui if (newFocus >= 0 && newFocus < mButtons.size()) { MyGUI::TextBox* button = mButtons[newFocus]; - if (button == mMagnitudeMinValue || - button == mMagnitudeMaxValue || - button == mDurationValue || - button == mAreaValue) + if (button == mMagnitudeMinValue || button == mMagnitudeMaxValue || button == mDurationValue + || button == mAreaValue) { button->setTextColour(textColours.link); } @@ -744,7 +740,6 @@ namespace MWGui mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); } - bool SpellCreationDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -761,7 +756,6 @@ namespace MWGui return EffectEditorBase::onControllerButtonEvent(arg); } - // ------------------------------------------------------------------------------------------------ EffectEditorBase::EffectEditorBase(Type type) @@ -856,7 +850,7 @@ namespace MWGui mEffectFocus = 0; mRightColumn = false; if (mAvailableButtons.size() > 0) - mAvailableButtons[0]->setStateSelected(true); + mAvailableButtons[0]->setStateSelected(true); } } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index cbd57014ce..2a4f5dcbb3 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,8 +1,8 @@ #ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H -#include #include +#include #include #include diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 8a18eb440f..b05d0c0435 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -393,7 +393,7 @@ namespace MWGui void SpellView::updateControllerFocus(int prevFocus, int newFocus) { if (mButtons.empty()) - return; + return; if (prevFocus >= 0 && prevFocus < mButtons.size()) { diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index 5a22dcb4d9..234ce08626 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -95,7 +95,7 @@ namespace MWGui 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> mButtons; + std::vector> mButtons; /// Keep a list of group offsets for controller navigation std::vector mGroupIndices; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index fbbd98cc31..f4f88dda04 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -733,11 +733,9 @@ namespace MWGui 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) + 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); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5b32cb8709..7ab909983e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -898,7 +898,8 @@ namespace MWGui int activeIndex = std::clamp(mActiveControllerWindows[mode], 0, (int)state.mWindows.size() - 1); - Log(Debug::Debug) << "Getting active controller window: mode=" << mode << ", " << state.mWindows.size() << " window(s), activeIndex=" << activeIndex; + Log(Debug::Debug) << "Getting active controller window: mode=" << mode << ", " << state.mWindows.size() + << " window(s), activeIndex=" << activeIndex; // If the active window is no longer visible, find the next visible window. if (!state.mWindows[activeIndex]->isVisible()) @@ -1903,7 +1904,8 @@ namespace MWGui { // If using controller menus, don't persist changes to size of the stats or magic // windows. - if (Settings::gui().mControllerMenus && (window == (MyGUI::Window*)mStatsWindow || window == (MyGUI::Window*)mSpellWindow)) + if (Settings::gui().mControllerMenus + && (window == (MyGUI::Window*)mStatsWindow || window == (MyGUI::Window*)mSpellWindow)) return; const auto it = mTrackedWindows.find(window); @@ -2616,7 +2618,8 @@ namespace MWGui // setButtons will handle setting visibility based on if any buttons are defined. mControllerButtonsOverlay->setButtons(topWin->getControllerButtons()); - if (getMode() == GM_Inventory) { + if (getMode() == GM_Inventory) + { mInventoryTabsOverlay->setVisible(true); mInventoryTabsOverlay->setTab(mActiveControllerWindows[GM_Inventory]); } diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index ac2e07e0cc..b6994cc3d6 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -255,9 +255,7 @@ namespace MWInput { // When the inventory tooltip is visible, we don't actually want the A button to // act like a mouse button; it should act normally. - if (treatAsMouse - && arg.button == SDL_CONTROLLER_BUTTON_A - && winMgr->getControllerTooltip()) + if (treatAsMouse && arg.button == SDL_CONTROLLER_BUTTON_A && winMgr->getControllerTooltip()) treatAsMouse = false; mGamepadGuiCursorEnabled = topWin->isGamepadCursorAllowed(); From ba9d85dc30de351985e06fe3bf529458858f5ba7 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 7 Jun 2025 23:18:48 -0700 Subject: [PATCH 149/455] Run lupdate and add translations --- files/lang/launcher_de.ts | 8 ++++++++ files/lang/launcher_en.ts | 8 ++++++++ files/lang/launcher_fr.ts | 8 ++++++++ files/lang/launcher_ru.ts | 8 ++++++++ files/lang/launcher_sv.ts | 8 ++++++++ 5 files changed, 40 insertions(+) diff --git a/files/lang/launcher_de.ts b/files/lang/launcher_de.ts index 86773e5a54..c4af468e8c 100644 --- a/files/lang/launcher_de.ts +++ b/files/lang/launcher_de.ts @@ -1447,5 +1447,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Run Script After Startup: + + <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> + + + + Enable Controller Menus + + diff --git a/files/lang/launcher_en.ts b/files/lang/launcher_en.ts index a0319318e8..5c24c3fa7d 100644 --- a/files/lang/launcher_en.ts +++ b/files/lang/launcher_en.ts @@ -1447,5 +1447,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov <html><head/><body><p>If enabled - makes transitions between different animations/poses much smoother. Also allows to load animation blending config YAML files that can be bundled with animations in order to customise blending styles.</p></body></html> + + <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> + + + + Enable Controller Menus + + diff --git a/files/lang/launcher_fr.ts b/files/lang/launcher_fr.ts index 569a460cd0..413a207abe 100644 --- a/files/lang/launcher_fr.ts +++ b/files/lang/launcher_fr.ts @@ -1450,5 +1450,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Run Script After Startup: Script à lancer après démarrage : + + Enable Controller Menus + Activer les menus du contrôleur + + + <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> + <html><head/><body><p>Faciliter l'utilisation des menus de jeu avec une manette.</p></body></html> + diff --git a/files/lang/launcher_ru.ts b/files/lang/launcher_ru.ts index 879f24dc76..189f8fdaee 100644 --- a/files/lang/launcher_ru.ts +++ b/files/lang/launcher_ru.ts @@ -1462,5 +1462,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Use the Camera as the Sound Listener Использовать камеру как слушателя + + <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> + + + + Enable Controller Menus + + diff --git a/files/lang/launcher_sv.ts b/files/lang/launcher_sv.ts index f2cca2346c..f86e15532f 100644 --- a/files/lang/launcher_sv.ts +++ b/files/lang/launcher_sv.ts @@ -1466,5 +1466,13 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin <html><head/><body><p>If enabled - makes transitions between different animations/poses much smoother. Also allows to load animation blending config YAML files that can be bundled with animations in order to customise blending styles.</p></body></html> <html><head/><body><p>Vid aktivering gör denna funktion att övergångarna mellan olika animationer och poser blir mycket mjukare. Funktionen gör det också möjligt att konfigurera animationsövergångarna i YAML-filer. Dessa filer kan buntas ihop tillsammans med nya animationsfiler.</p></body></html> + + <html><head/><body><p>Make it easier to use game menus with a controller.</p></body></html> + <html><head/><body><p>Gör det enklare att använda spelmenyer med en handkontroll.</p></body></html> + + + Enable Controller Menus + Aktivera kontrollmenyer + From 81720892f9a5ec0307df2c6243d9133b259f1a02 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 8 Jun 2025 00:10:47 -0700 Subject: [PATCH 150/455] Fix compiler warnings --- apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwgui/windowbase.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index ee1990878a..83a714cf05 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -390,7 +390,7 @@ namespace MWBase /// 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 const bool getControllerTooltip() const = 0; + virtual bool getControllerTooltip() const = 0; virtual void setControllerTooltip(bool enabled) = 0; virtual void updateControllerButtonsOverlay() = 0; diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 87bea55e38..0be84f12eb 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -80,8 +80,8 @@ namespace MWGui virtual ControllerButtonStr* getControllerButtons() { return &mControllerButtons; } MyGUI::Widget* getControllerScrollWidget() { return mControllerScrollWidget; } bool isGamepadCursorAllowed() { return !mDisableGamepadCursor; } - virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; }; - virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return true; }; + virtual bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { return true; } + virtual bool onControllerThumbstickEvent(const SDL_ControllerAxisEvent& arg) { return true; } virtual void setActiveControllerWindow(bool active) { mActiveControllerWindow = active; } protected: From 8094761c3e42a13b6b1d61a6e49614f4d594cfa5 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 8 Jun 2025 02:28:42 -0700 Subject: [PATCH 151/455] Fix sign-compare warnings --- apps/openmw/mwgui/bookpage.cpp | 6 ++--- apps/openmw/mwgui/class.cpp | 8 +++--- apps/openmw/mwgui/dialogue.cpp | 20 +++++++-------- apps/openmw/mwgui/inventorytabsoverlay.cpp | 4 +-- apps/openmw/mwgui/itemchargeview.cpp | 6 ++--- apps/openmw/mwgui/itemview.cpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 4 +-- apps/openmw/mwgui/levelupdialog.cpp | 4 +-- apps/openmw/mwgui/merchantrepair.cpp | 4 +-- apps/openmw/mwgui/messagebox.cpp | 4 +-- apps/openmw/mwgui/quickkeysmenu.cpp | 4 +-- apps/openmw/mwgui/spellbuyingwindow.cpp | 4 +-- apps/openmw/mwgui/spellcreationdialog.cpp | 30 +++++++++++----------- apps/openmw/mwgui/spellview.cpp | 10 ++++---- apps/openmw/mwgui/trainingwindow.cpp | 2 +- apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwgui/windowbase.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- 18 files changed, 59 insertions(+), 59 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 113ac7d104..d014910c74 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -107,11 +107,11 @@ namespace MWGui void setColour(int section, int line, int run, MyGUI::Colour colour) const override { - if (section < 0 || section >= mSections.size()) + if (section < 0 || section >= static_cast(mSections.size())) return; - if (line < 0 || line >= mSections[section].mLines.size()) + if (line < 0 || line >= static_cast(mSections[section].mLines.size())) return; - if (run < 0 || run >= mSections[section].mLines[line].mRuns.size()) + if (run < 0 || run >= static_cast(mSections[section].mLines[line].mRuns.size())) return; mSections[section].mLines[line].mRuns[run].mStyle->mNormalColour = colour; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 730ade1615..450a61b4f8 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -467,7 +467,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) onButtonClicked(mButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -490,7 +490,7 @@ namespace MWGui { if (mButtons.size() <= 1) return true; - if (mButtons.size() == 2 && mControllerFocus == mButtons.size() - 1) + if (mButtons.size() == 2 && mControllerFocus == static_cast(mButtons.size()) - 1) return true; setControllerFocus(mButtons, mControllerFocus, false); @@ -992,7 +992,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mAttributeButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mAttributeButtons.size())) onAttributeClicked(mAttributeButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -1094,7 +1094,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mSkillButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mSkillButtons.size())) onSkillClicked(mSkillButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 5c325a52d3..fe3675f6fb 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -162,7 +162,7 @@ namespace MWGui if (mBribe1000Button->getEnabled()) mButtons.push_back(mBribe1000Button); - for (int i = 0; i < mButtons.size(); i++) + for (size_t i = 0; i < mButtons.size(); i++) mButtons[i]->setStateSelected(i == 0); } @@ -604,7 +604,7 @@ namespace MWGui void DialogueWindow::updateTopicsPane() { const std::string focusedTopic - = Settings::gui().mControllerMenus && mControllerFocus < mTopicsList->getItemCount() + = Settings::gui().mControllerMenus && mControllerFocus < static_cast(mTopicsList->getItemCount()) ? mTopicsList->getItemNameAt(mControllerFocus) : ""; @@ -924,10 +924,10 @@ namespace MWGui void DialogueWindow::setControllerFocus(int index, bool focused) { // List is mTopicsList + "Goodbye" button below the list. - if (index < 0 || index > mTopicsList->getItemCount()) + if (index < 0 || index > static_cast(mTopicsList->getItemCount())) return; - if (index == mTopicsList->getItemCount()) + if (index == static_cast(mTopicsList->getItemCount())) { mGoodbyeButton->setStateSelected(focused); } @@ -957,10 +957,10 @@ namespace MWGui { if (mChoices.size() > 0) { - if (mControllerChoice >= 0 && mControllerChoice < mChoices.size()) + if (mControllerChoice >= 0 && mControllerChoice < static_cast(mChoices.size())) onChoiceActivated(mControllerChoice + 1); // +1 because choices are indexed starting at 1 } - else if (mControllerFocus == mTopicsList->getItemCount()) + else if (mControllerFocus == static_cast(mTopicsList->getItemCount())) onGoodbyeActivated(); else onSelectListItem(mTopicsList->getItemNameAt(mControllerFocus), mControllerFocus); @@ -975,7 +975,7 @@ namespace MWGui if (mChoices.size() > 0) { // In-dialogue choice (red text) - mControllerChoice = std::clamp(mControllerChoice - 1, 0, (int)mChoices.size() - 1); + mControllerChoice = std::clamp(mControllerChoice - 1, 0, static_cast(mChoices.size()) - 1); mHistory->setFocusItem(mChoiceStyles.at(mControllerChoice)); } else @@ -996,16 +996,16 @@ namespace MWGui if (mChoices.size() > 0) { // In-dialogue choice (red text) - mControllerChoice = std::clamp(mControllerChoice + 1, 0, (int)mChoices.size() - 1); + mControllerChoice = std::clamp(mControllerChoice + 1, 0, static_cast(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 >= mTopicsList->getItemCount()) + if (mControllerFocus >= static_cast(mTopicsList->getItemCount())) mControllerFocus = 0; - else if (mControllerFocus == mTopicsList->getItemCount() - 1) + else if (mControllerFocus == static_cast(mTopicsList->getItemCount()) - 1) mControllerFocus = mTopicsList->getItemCount(); // "Goodbye" button else if (mTopicsList->getItemNameAt(mControllerFocus + 1).length() == 0) mControllerFocus += 2; // Skip separator diff --git a/apps/openmw/mwgui/inventorytabsoverlay.cpp b/apps/openmw/mwgui/inventorytabsoverlay.cpp index 14fd110830..35b4d1d87f 100644 --- a/apps/openmw/mwgui/inventorytabsoverlay.cpp +++ b/apps/openmw/mwgui/inventorytabsoverlay.cpp @@ -32,7 +32,7 @@ namespace MWGui if (!MWBase::Environment::get().getWindowManager()->getJournalAllowed()) return; - for (int i = 0; i < mTabs.size(); i++) + for (int i = 0; i < static_cast(mTabs.size()); i++) { if (mTabs[i] == sender) { @@ -45,7 +45,7 @@ namespace MWGui void InventoryTabsOverlay::setTab(int index) { - for (int i = 0; i < mTabs.size(); i++) + for (int i = 0; i < static_cast(mTabs.size()); i++) mTabs[i]->setStateSelected(i == index); } } diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 0b9ea842d9..31b29122f3 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -249,7 +249,7 @@ namespace MWGui if (button == SDL_CONTROLLER_BUTTON_A) { // Select the focused item, if any. - if (mControllerFocus >= 0 && mControllerFocus < mLines.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mLines.size())) onIconClicked(mLines[mControllerFocus].mIcon); } else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) @@ -268,13 +268,13 @@ namespace MWGui const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; - if (prevFocus >= 0 && prevFocus < mLines.size()) + if (prevFocus >= 0 && prevFocus < static_cast(mLines.size())) { mLines[prevFocus].mText->setTextColour(textColours.normal); mLines[prevFocus].mIcon->setControllerFocus(false); } - if (newFocus >= 0 && newFocus < mLines.size()) + if (newFocus >= 0 && newFocus < static_cast(mLines.size())) { mLines[newFocus].mText->setTextColour(textColours.link); mLines[newFocus].mIcon->setControllerFocus(true); diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 464cec5800..c114db5e35 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -61,7 +61,7 @@ namespace MWGui mRows = std::max(maxHeight / 42, 1); } - for (unsigned int i = 0; i < mItemCount; ++i) + for (int i = 0; i < mItemCount; ++i) { MyGUI::Widget* w = dragArea->getChildAt(i); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 77918f2a89..23adfc5a58 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -491,13 +491,13 @@ namespace void addControllerButtons(Gui::MWList* _list, int _selectedIndex) { mButtons.clear(); - for (int i = 0; i < _list->getItemCount(); i++) + 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); + static_cast(mButtons.size()) == _selectedIndex ? MWGui::journalHeaderColour : MyGUI::Colour::Black); mButtons.push_back(listItem); } } diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 8b022258cf..b9c90e51e2 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -231,7 +231,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerFocus = 0; - for (int i = 0; i < mAttributeButtons.size(); i++) + for (size_t i = 0; i < mAttributeButtons.size(); i++) setControllerFocus(mAttributeButtons, i, i == 0); } @@ -386,7 +386,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mAttributeButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mAttributeButtons.size())) onAttributeClicked(mAttributeButtons[mControllerFocus]); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Item Gold Up")); } diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index c659cac139..7eb10def39 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -178,7 +178,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) onRepairButtonClick(mButtons[mControllerFocus].first); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -205,7 +205,7 @@ namespace MWGui } // Scroll the list to keep the active item in view - if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) { int line = mButtons[mControllerFocus].second; if (line <= 5) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 492ac30052..c7e997a565 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -446,7 +446,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - mControllerFocus = std::clamp(mControllerFocus, 0, (int)mButtons.size() - 1); + mControllerFocus = std::clamp(mControllerFocus, 0, static_cast(mButtons.size()) - 1); buttonActivated(mButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -469,7 +469,7 @@ namespace MWGui { if (mButtons.size() <= 1) return true; - if (mButtons.size() == 2 && mControllerFocus == mButtons.size() - 1) + if (mButtons.size() == 2 && mControllerFocus == static_cast(mButtons.size()) - 1) return true; setControllerFocus(mButtons, mControllerFocus, false); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index b834df83e2..ecbf3e6dfc 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -119,7 +119,7 @@ namespace MWGui if (Settings::gui().mControllerMenus) { mControllerFocus = 0; - for (int i = 0; i < mKey.size(); i++) + for (int i = 0; i < static_cast(mKey.size()); i++) mKey[i].button->setControllerFocus(i == mControllerFocus); } } @@ -490,7 +490,7 @@ namespace MWGui mControllerFocus++; } - for (int i = 0; i < mKey.size(); i++) + for (int i = 0; i < static_cast(mKey.size()); i++) mKey[i].button->setControllerFocus(i == mControllerFocus); return true; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 3bc38b2e44..754055aee5 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -230,7 +230,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mSpellButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mSpellButtons.size())) onSpellButtonClick(mSpellButtons[mControllerFocus].first); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) @@ -262,7 +262,7 @@ namespace MWGui mSpellButtons[mControllerFocus].first->setStateSelected(true); } - if (mControllerFocus >= 0 && mControllerFocus < mSpellButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mSpellButtons.size())) { // Scroll the list to keep the active item in view int line = mSpellButtons[mControllerFocus].second; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 5085464fdb..7ea5118883 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -390,7 +390,7 @@ namespace MWGui bool EditEffectDialog::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) { int prevFocus = mControllerFocus; - mControllerFocus = std::clamp(mControllerFocus, 0, (int)mButtons.size() - 1); + mControllerFocus = std::clamp(mControllerFocus, 0, static_cast(mButtons.size()) - 1); MyGUI::TextBox* button = mButtons[mControllerFocus]; if (arg.button == SDL_CONTROLLER_BUTTON_A) @@ -415,7 +415,7 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { if (mControllerFocus == 0) - mControllerFocus = (int)mButtons.size() - 2; + mControllerFocus = static_cast(mButtons.size()) - 2; else if (button == mCancelButton && mDeleteButton->getVisible()) mControllerFocus -= 3; else if (button == mCancelButton || (button == mOkButton && mDeleteButton->getVisible())) @@ -547,7 +547,7 @@ namespace MWGui { const TextColours& textColours{ MWBase::Environment::get().getWindowManager()->getTextColours() }; - if (prevFocus >= 0 && prevFocus < mButtons.size()) + if (prevFocus >= 0 && prevFocus < static_cast(mButtons.size())) { MyGUI::TextBox* button = mButtons[prevFocus]; if (button == mMagnitudeMinValue || button == mMagnitudeMaxValue || button == mDurationValue @@ -561,7 +561,7 @@ namespace MWGui } } - if (newFocus >= 0 && newFocus < mButtons.size()) + if (newFocus >= 0 && newFocus < static_cast(mButtons.size())) { MyGUI::TextBox* button = mButtons[newFocus]; if (button == mMagnitudeMinValue || button == mMagnitudeMaxValue || button == mDurationValue @@ -1040,12 +1040,12 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < static_cast(mAvailableButtons.size())) { onAvailableEffectClicked(mAvailableButtons[mAvailableFocus]); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } - else if (mRightColumn && mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + else if (mRightColumn && mEffectFocus >= 0 && mEffectFocus < static_cast(mEffectButtons.size())) { onEditEffect(mEffectButtons[mEffectFocus].second); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); @@ -1061,14 +1061,14 @@ namespace MWGui { if (mRightColumn && mEffectButtons.size() > 0) { - if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + if (mEffectFocus >= 0 && mEffectFocus < static_cast(mEffectButtons.size())) mEffectButtons[mEffectFocus].first->setStateSelected(false); mEffectFocus = wrap(mEffectFocus - 1, mEffectButtons.size()); mEffectButtons[mEffectFocus].first->setStateSelected(true); } else if (!mRightColumn && mAvailableButtons.size() > 0) { - if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + if (mAvailableFocus >= 0 && mAvailableFocus < static_cast(mAvailableButtons.size())) mAvailableButtons[mAvailableFocus]->setStateSelected(false); mAvailableFocus = wrap(mAvailableFocus - 1, mAvailableButtons.size()); mAvailableButtons[mAvailableFocus]->setStateSelected(true); @@ -1078,14 +1078,14 @@ namespace MWGui { if (mRightColumn && mEffectButtons.size() > 0) { - if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + if (mEffectFocus >= 0 && mEffectFocus < static_cast(mEffectButtons.size())) mEffectButtons[mEffectFocus].first->setStateSelected(false); mEffectFocus = wrap(mEffectFocus + 1, mEffectButtons.size()); mEffectButtons[mEffectFocus].first->setStateSelected(true); } else if (!mRightColumn && mAvailableButtons.size() > 0) { - if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + if (mAvailableFocus >= 0 && mAvailableFocus < static_cast(mAvailableButtons.size())) mAvailableButtons[mAvailableFocus]->setStateSelected(false); mAvailableFocus = wrap(mAvailableFocus + 1, mAvailableButtons.size()); mAvailableButtons[mAvailableFocus]->setStateSelected(true); @@ -1094,17 +1094,17 @@ namespace MWGui else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT && mRightColumn) { mRightColumn = false; - if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + if (mEffectFocus >= 0 && mEffectFocus < static_cast(mEffectButtons.size())) mEffectButtons[mEffectFocus].first->setStateSelected(false); - if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + if (mAvailableFocus >= 0 && mAvailableFocus < static_cast(mAvailableButtons.size())) mAvailableButtons[mAvailableFocus]->setStateSelected(true); } else if (arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && !mRightColumn && mEffectButtons.size() > 0) { mRightColumn = true; - if (mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + if (mAvailableFocus >= 0 && mAvailableFocus < static_cast(mAvailableButtons.size())) mAvailableButtons[mAvailableFocus]->setStateSelected(false); - if (mEffectFocus >= 0 && mEffectFocus < mEffectButtons.size()) + if (mEffectFocus >= 0 && mEffectFocus < static_cast(mEffectButtons.size())) mEffectButtons[mEffectFocus].first->setStateSelected(true); } @@ -1117,7 +1117,7 @@ namespace MWGui mAvailableEffectsList->setViewOffset(-lineHeight * (mAvailableFocus - 5)); } - if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < mAvailableButtons.size()) + if (!mRightColumn && mAvailableFocus >= 0 && mAvailableFocus < static_cast(mAvailableButtons.size())) { // Warp the mouse to the selected spell to show the tooltip if (MWBase::Environment::get().getWindowManager()->getControllerTooltip()) diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index b05d0c0435..fffd492fb7 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -337,7 +337,7 @@ namespace MWGui if (button == SDL_CONTROLLER_BUTTON_A) { // Select the focused item, if any. - if (mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) { onSpellSelected(mButtons[mControllerFocus].first); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); @@ -347,7 +347,7 @@ namespace MWGui { // Toggle info tooltip mControllerTooltip = !mControllerTooltip; - if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < mButtons.size()) + if (mControllerTooltip && mControllerFocus >= 0 && mControllerFocus < static_cast(mButtons.size())) MWBase::Environment::get().getInputManager()->warpMouseToWidget(mButtons[mControllerFocus].first); } else if (button == SDL_CONTROLLER_BUTTON_DPAD_UP) @@ -357,7 +357,7 @@ namespace MWGui else if (button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) mControllerFocus = std::max(0, mControllerFocus - 10); else if (button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - mControllerFocus = std::min(mControllerFocus + 10, (int)mButtons.size() - 1); + mControllerFocus = std::min(mControllerFocus + 10, static_cast(mButtons.size()) - 1); else if (button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { // Jump to first item in previous group @@ -395,14 +395,14 @@ namespace MWGui if (mButtons.empty()) return; - if (prevFocus >= 0 && prevFocus < mButtons.size()) + if (prevFocus >= 0 && prevFocus < static_cast(mButtons.size())) { Gui::SharedStateButton* prev = mButtons[prevFocus].first; if (prev) prev->onMouseLostFocus(nullptr); } - if (newFocus >= 0 && newFocus < mButtons.size()) + if (newFocus >= 0 && newFocus < static_cast(mButtons.size())) { Gui::SharedStateButton* focused = mButtons[newFocus].first; if (focused) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index d5c4f7efbc..5b4d355e61 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -251,7 +251,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mTrainingButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mTrainingButtons.size())) onTrainingSelected(mTrainingButtons[mControllerFocus]); } else if (arg.button == SDL_CONTROLLER_BUTTON_B) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 48717b0825..a667d3905f 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -258,7 +258,7 @@ namespace MWGui { if (arg.button == SDL_CONTROLLER_BUTTON_A) { - if (mControllerFocus >= 0 && mControllerFocus < mDestinationButtons.size()) + if (mControllerFocus >= 0 && mControllerFocus < static_cast(mDestinationButtons.size())) { onTravelButtonClick(mDestinationButtons[mControllerFocus]); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 4ecc26449f..b607ec50b2 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -27,7 +27,7 @@ int MWGui::wrap(int index, int max) void MWGui::setControllerFocus(std::vector buttons, int index, bool focused) { - if (index >= 0 && index < buttons.size()) + if (index >= 0 && index < static_cast(buttons.size())) buttons[index]->setStateSelected(focused); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 97847551b1..6cc61fb279 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -396,7 +396,7 @@ namespace MWGui WindowBase* getActiveControllerWindow() override; void cycleActiveControllerWindow(bool next) override; void setActiveControllerWindow(GuiMode mode, int activeIndex) override; - const bool getControllerTooltip() const override { return mControllerTooltip; } + bool getControllerTooltip() const override { return mControllerTooltip; } void setControllerTooltip(bool enabled) override; void updateControllerButtonsOverlay() override; From f6b06057fa34518d40e0db82af0a2c05a344934f Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sun, 8 Jun 2025 13:51:09 -0700 Subject: [PATCH 152/455] Fix clang warning --- apps/openmw/mwgui/journalwindow.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 23adfc5a58..2a1ee32739 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -496,8 +496,9 @@ namespace MyGUI::Button* listItem = _list->getItemWidget(_list->getItemNameAt(i)); if (listItem) { - listItem->setTextColour( - static_cast(mButtons.size()) == _selectedIndex ? MWGui::journalHeaderColour : MyGUI::Colour::Black); + listItem->setTextColour(static_cast(mButtons.size()) == _selectedIndex + ? MWGui::journalHeaderColour + : MyGUI::Colour::Black); mButtons.push_back(listItem); } } From 4e2e8d1a81c6cf1f087e07f9be59e24de220f26d Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 10 Jun 2025 01:08:04 +0000 Subject: [PATCH 153/455] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Joakim Berg --- files/lang/launcher_sv.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/lang/launcher_sv.ts b/files/lang/launcher_sv.ts index f86e15532f..11623ceb5a 100644 --- a/files/lang/launcher_sv.ts +++ b/files/lang/launcher_sv.ts @@ -1472,7 +1472,7 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin Enable Controller Menus - Aktivera kontrollmenyer + Aktivera handkontrollmenyer From 57ae0972574247db72f7c3aa2e485e7b404ebe64 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 9 Jun 2025 19:23:49 -0700 Subject: [PATCH 154/455] Add controller support to the companion window; also fix giving stacks --- apps/openmw/mwgui/companionwindow.cpp | 65 ++++++++++++++++++++++++++- apps/openmw/mwgui/companionwindow.hpp | 4 ++ apps/openmw/mwgui/inventorywindow.cpp | 33 +++++++++++++- apps/openmw/mwgui/inventorywindow.hpp | 1 + 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 240198eddc..4a7863b856 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -14,6 +16,7 @@ #include "companionitemmodel.hpp" #include "countdialog.hpp" #include "draganddrop.hpp" +#include "inventorywindow.hpp" #include "itemview.hpp" #include "messagebox.hpp" #include "sortfilteritemmodel.hpp" @@ -58,6 +61,11 @@ namespace MWGui mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked); setCoord(200, 0, 600, 300); + + mControllerButtons.a = "#{sTake}"; + mControllerButtons.b = "#{sClose}"; + mControllerButtons.r3 = "#{sInfo}"; + mControllerButtons.l2 = "#{sInventory}"; } void CompanionWindow::onItemSelected(int index) @@ -93,8 +101,13 @@ namespace MWGui name += MWGui::ToolTips::getSoulString(object.getCellRef()); dialog->openCountDialog(name, "#{sTake}", count); dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); + if (Settings::gui().mControllerMenus) + dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::takeItem); + else + dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); } + else if (Settings::gui().mControllerMenus) + takeItem(nullptr, count); else dragItem(nullptr, count); } @@ -110,6 +123,29 @@ namespace MWGui mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } + void CompanionWindow::takeItem(MyGUI::Widget* sender, int count) + { + if (!mModel) + return; + + const ItemStack& item = mModel->getItem(mSelectedItem); + if (!mModel->onTakeItem(item.mBase, count)) + return; + + MWGui::InventoryWindow* inventoryWindow = MWBase::Environment::get().getWindowManager()->getInventoryWindow(); + ItemModel* playerModel = inventoryWindow->getModel(); + + mModel->moveItem(item, count, playerModel); + + inventoryWindow->updateItemView(); + mItemView->update(); + + // play the item's sound + MWWorld::Ptr itemBase = item.mBase; + const ESM::RefId& sound = itemBase.getClass().getUpSoundId(itemBase); + MWBase::Environment::get().getWindowManager()->playSound(sound); + } + void CompanionWindow::onBackgroundSelected() { if (mDragAndDrop->mIsOnDragAndDrop) @@ -202,4 +238,31 @@ namespace MWGui mSortModel = nullptr; } + 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); + } } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 21869c58f5..c0cd87c9d5 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -34,6 +34,9 @@ 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; } ItemModel* getModel() { return mModel; } @@ -55,6 +58,7 @@ namespace MWGui void onNameFilterChanged(MyGUI::EditBox* _sender); void onBackgroundSelected(); void dragItem(MyGUI::Widget* sender, int count); + void takeItem(MyGUI::Widget* sender, int count); void onMessageBoxButtonClicked(int button); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index fd06286a6c..1f06ced300 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -331,7 +331,10 @@ namespace MWGui name += MWGui::ToolTips::getSoulString(object.getCellRef()); dialog->openCountDialog(name, message, count); dialog->eventOkClicked.clear(); - if (mTrading) + if (Settings::gui().mControllerMenus + && (mGuiMode == MWGui::GM_Companion || mGuiMode == MWGui::GM_Container)) + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::giveItem); + else if (mTrading) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); else dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem); @@ -411,6 +414,32 @@ namespace MWGui notifyContentChanged(); } + void InventoryWindow::giveItem(MyGUI::Widget* sender, int count) + { + ensureSelectedItemUnequipped(count); + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); + notifyContentChanged(); + + if (mGuiMode == MWGui::GM_Companion && mDragAndDrop->mIsOnDragAndDrop) + { + // Drag and drop the item on the companion's window. + MWGui::CompanionWindow* companionWindow = (MWGui::CompanionWindow*)MWBase::Environment::get() + .getWindowManager() + ->getGuiModeWindows(mGuiMode) + .at(1); + mDragAndDrop->drop(companionWindow->getModel(), companionWindow->getItemView()); + } + else if (mGuiMode == MWGui::GM_Container && mDragAndDrop->mIsOnDragAndDrop) + { + // Drag and drop the item on the container window. + MWGui::ContainerWindow* containerWindow = (MWGui::ContainerWindow*)MWBase::Environment::get() + .getWindowManager() + ->getGuiModeWindows(mGuiMode) + .at(0); + mDragAndDrop->drop(containerWindow->getModel(), containerWindow->getItemView()); + } + } + void InventoryWindow::updateItemView() { MWBase::Environment::get().getWindowManager()->updateSpellWindow(); @@ -944,7 +973,7 @@ namespace MWGui MWGui::CompanionWindow* companionWindow = (MWGui::CompanionWindow*)MWBase::Environment::get() .getWindowManager() ->getGuiModeWindows(mGuiMode) - .at(0); + .at(1); mDragAndDrop->drop(companionWindow->getModel(), companionWindow->getItemView()); } else if (mGuiMode == MWGui::GM_Container && mDragAndDrop->mIsOnDragAndDrop) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 5b4d61f272..a14e0f9477 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -122,6 +122,7 @@ namespace MWGui void sellItem(MyGUI::Widget* sender, int count); void dragItem(MyGUI::Widget* sender, int count); + void giveItem(MyGUI::Widget* sender, int count); void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); From 47b3674bac55d9efb02d3f172cad1407740e71c0 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Mon, 9 Jun 2025 22:03:12 -0700 Subject: [PATCH 155/455] Update controller focus to use a message box's default focus if available --- apps/openmw/mwgui/messagebox.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index c7e997a565..80b2a34a5b 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -288,7 +288,13 @@ namespace MWGui // If we have more than one button, we need to set the focus to the first one. if (mButtons.size() > 1) - mButtons[0]->setStateSelected(true); + { + mControllerFocus = 0; + if (mDefaultFocus >= 0 && mDefaultFocus < static_cast(mButtons.size())) + mControllerFocus = mDefaultFocus; + for (int i = 0; i < static_cast(mButtons.size()); ++i) + mButtons[i]->setStateSelected(i == mControllerFocus); + } } MyGUI::IntSize mainWidgetSize; From 0df8869f596a3a4a28129cbfbc499890df78c376 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 1 Jun 2025 22:16:21 +0300 Subject: [PATCH 156/455] Cache computed supported directions Invalidated when animation sources are cleared or added --- apps/openmw/mwrender/animation.cpp | 37 +++++++++++++++++++----------- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f07a325f7c..98a2246cb5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -722,6 +722,7 @@ namespace MWRender mAnimSources.push_back(animsrc); + mSupportedDirections.clear(); for (const std::string& group : mAnimSources.back()->getTextKeys().getGroups()) mSupportedAnimations.insert(group); @@ -795,6 +796,7 @@ namespace MWRender mAccumCtrl = nullptr; mSupportedAnimations.clear(); + mSupportedDirections.clear(); mAnimSources.clear(); mAnimVelocities.clear(); @@ -2012,20 +2014,29 @@ namespace MWRender std::span prefixes) const { MWWorld::MovementDirectionFlags result = 0; - for (const std::string_view animation : mSupportedAnimations) + for (const std::string_view prefix : prefixes) { - if (std::find_if( - prefixes.begin(), prefixes.end(), [&](std::string_view v) { return animation.starts_with(v); }) - == prefixes.end()) - continue; - if (animation.ends_with("forward")) - result |= MWWorld::MovementDirectionFlag_Forward; - else if (animation.ends_with("back")) - result |= MWWorld::MovementDirectionFlag_Back; - else if (animation.ends_with("left")) - result |= MWWorld::MovementDirectionFlag_Left; - else if (animation.ends_with("right")) - result |= MWWorld::MovementDirectionFlag_Right; + auto it = std::find_if(mSupportedDirections.begin(), mSupportedDirections.end(), + [prefix](const auto& direction) { return direction.first == prefix; }); + if (it == mSupportedDirections.end()) + { + mSupportedDirections.emplace_back(prefix, 0); + it = mSupportedDirections.end() - 1; + for (const std::string_view animation : mSupportedAnimations) + { + if (!animation.starts_with(prefix)) + continue; + if (animation.ends_with("forward")) + it->second |= MWWorld::MovementDirectionFlag_Forward; + else if (animation.ends_with("back")) + it->second |= MWWorld::MovementDirectionFlag_Back; + else if (animation.ends_with("left")) + it->second |= MWWorld::MovementDirectionFlag_Left; + else if (animation.ends_with("right")) + it->second |= MWWorld::MovementDirectionFlag_Right; + } + } + result |= it->second; } return result; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b6cb6f333c..8f7637804d 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -181,6 +181,7 @@ namespace MWRender AnimSourceList mAnimSources; std::unordered_set mSupportedAnimations; + mutable std::vector> mSupportedDirections; osg::ref_ptr mInsert; From 1e3ddee291b04ced03152034fbb3954a6d7a8bf9 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Tue, 10 Jun 2025 21:42:51 -0700 Subject: [PATCH 157/455] Remove default string when creating a potion that won't work --- apps/openmw/mwgui/alchemywindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 074250985c..6cf34702d7 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -119,8 +119,6 @@ namespace MWGui void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { - if (Settings::gui().mControllerMenus && mNameEdit->getCaption().length() == 0) - mNameEdit->setCaption("Unknown potion"); mAlchemy->setPotionName(mNameEdit->getCaption()); int count = mAlchemy->countPotionsToBrew(); count = std::min(count, mBrewCountEdit->getValue()); From e01b8d372c69440e734f86d48fe49fc4dad45cda Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Wed, 11 Jun 2025 23:16:08 -0700 Subject: [PATCH 158/455] In controller mode, make focused spell blue to match other menus --- apps/openmw/mwgui/spellview.cpp | 4 +++- files/data/mygui/openmw_text.skin.xml | 32 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index fffd492fb7..0265eea4cb 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -113,7 +113,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( diff --git a/files/data/mygui/openmw_text.skin.xml b/files/data/mygui/openmw_text.skin.xml index 5f96d0a57c..1724458522 100644 --- a/files/data/mygui/openmw_text.skin.xml +++ b/files/data/mygui/openmw_text.skin.xml @@ -120,6 +120,38 @@ color_misc=0,205,205 # ???? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b4ede02cce12c935883c26b12b24d3b686e70db0 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Thu, 12 Jun 2025 21:43:01 -0700 Subject: [PATCH 159/455] Move hardcoded strings to i10n --- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 4 ++-- apps/openmw/mwgui/recharge.cpp | 2 +- apps/openmw/mwgui/repair.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 4 ++-- apps/openmw/mwgui/settingswindow.cpp | 3 +-- files/data/l10n/OMWEngine/de.yaml | 12 +++++++++++- files/data/l10n/OMWEngine/en.yaml | 12 +++++++++++- files/data/l10n/OMWEngine/fr.yaml | 12 +++++++++++- files/data/l10n/OMWEngine/ru.yaml | 12 +++++++++++- files/data/l10n/OMWEngine/sv.yaml | 12 +++++++++++- files/data/mygui/openmw_savegame_dialog.layout | 2 +- 13 files changed, 65 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index b5e0444f25..7a1c1532f6 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -62,7 +62,7 @@ namespace MWGui mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sCancel}"; - mControllerButtons.y = "Cast Type"; + mControllerButtons.y = "#{OMWEngine:EnchantType}"; mControllerButtons.l1 = "#{sItem}"; mControllerButtons.r1 = "#{sSoulGem}"; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 1f06ced300..1f39ccc0ce 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -923,7 +923,7 @@ namespace MWGui { case MWGui::GM_Companion: case MWGui::GM_Container: - mControllerButtons.a = "Put"; + mControllerButtons.a = "#{OMWEngine:InventorySelect}"; mControllerButtons.b = "#{sClose}"; mControllerButtons.x = "#{sTakeAll}"; mControllerButtons.y = ""; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 2a1ee32739..ddfa16255c 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -219,7 +219,7 @@ namespace } mControllerButtons.a = "#{sSelect}"; - mControllerButtons.x = "Quests"; + mControllerButtons.x = "#{OMWEngine:JournalQuests}"; mControllerButtons.y = "#{sTopics}"; mQuestMode = false; @@ -692,7 +692,7 @@ namespace mControllerButtons.b = mOptionsMode || mStates.size() > 1 ? "#{sBack}" : "#{sClose}"; mControllerButtons.l1 = mOptionsMode ? "" : "#{sPrev}"; mControllerButtons.r1 = mOptionsMode ? "" : "#{sNext}"; - mControllerButtons.r3 = mOptionsMode && mQuestMode ? "Show All" : ""; + mControllerButtons.r3 = mOptionsMode && mQuestMode ? "#{OMWEngine:JournalShowAll}" : ""; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index cfe7fedc60..2dfb47fa23 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -40,7 +40,7 @@ namespace MWGui mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem); - mControllerButtons.a = "Recharge"; + mControllerButtons.a = "#{OMWEngine:RechargeSelect}"; mControllerButtons.b = "#{sCancel}"; mControllerButtons.y = "#{sSoulGem}"; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 8dbac7ef48..8962ae2abd 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -42,7 +42,7 @@ namespace MWGui mControllerButtons.a = "#{sRepair}"; mControllerButtons.b = "#{sCancel}"; - mControllerButtons.y = "Tool"; + mControllerButtons.y = "#{OMWEngine:RepairTool}"; } void Repair::onOpen() diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 61cbae81f0..2dfcd4cee7 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -245,7 +245,7 @@ namespace MWGui mCharacterSelection->setIndexSelected(selectedIndex); if (selectedIndex == MyGUI::ITEM_NONE) - mCharacterSelection->setCaptionWithReplacing("#{OMWEngine:SelectCharacter}"); + mCharacterSelection->setCaptionWithReplacing("#{OMWEngine:SelectCharacter}..."); fillSaveList(); } @@ -524,7 +524,7 @@ namespace MWGui ControllerButtonStr* SaveGameDialog::getControllerButtons() { - mControllerButtons.y = mSaving ? "" : "Select Character"; + mControllerButtons.y = mSaving ? "" : "#{OMWEngine:SelectCharacter}"; return &mControllerButtons; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 89cdb6694a..726809c583 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -462,8 +462,7 @@ namespace MWGui mControllerButtons.a = "#{sSelect}"; mControllerButtons.b = "#{sOk}"; - mControllerButtons.l1 = "#{sPrev} Tab"; - mControllerButtons.r1 = "#{sNext} Tab"; + mControllerButtons.lStick = "#{sMouse}"; } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) diff --git a/files/data/l10n/OMWEngine/de.yaml b/files/data/l10n/OMWEngine/de.yaml index a23dbebdae..25bc145159 100644 --- a/files/data/l10n/OMWEngine/de.yaml +++ b/files/data/l10n/OMWEngine/de.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nDrücken Sie „Kopieren“, um alle Namen in die Zwischenablage zu kopieren.} } OverwriteGameConfirmation: "Sind Sie sicher, dass Sie den Spielstand überschreiben wollen?" -SelectCharacter: "Charakter auswählen..." +SelectCharacter: "Charakter auswählen" TimePlayed: "Spielzeit" @@ -213,3 +213,13 @@ WindowModeWindowed: "Fenster" WindowModeWindowedFullscreen: "Fenster in Vollbildgröße" # More fitting translations of "wobbly" are welcome WobblyShores: "Wabbelige Uferlinien" + + +# Controller button names + +EnchantType: "Zaubertyp" +InventorySelect: "Geben" +JournalQuests: "Quests" +JournalShowAll: "Alle Anzeigen" +RechargeSelect: "Aufladen" +RepairTool: "Werkzeug" diff --git a/files/data/l10n/OMWEngine/en.yaml b/files/data/l10n/OMWEngine/en.yaml index 8a3217a03c..176e604f8e 100644 --- a/files/data/l10n/OMWEngine/en.yaml +++ b/files/data/l10n/OMWEngine/en.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nPress Copy to place their names to the clipboard.} } OverwriteGameConfirmation: "Are you sure you want to overwrite this saved game?" -SelectCharacter: "Select Character..." +SelectCharacter: "Select Character" TimePlayed: "Time Played" @@ -212,3 +212,13 @@ WindowModeHint: "Hint: Windowed Fullscreen mode\nalways uses the native display WindowModeWindowed: "Windowed" WindowModeWindowedFullscreen: "Windowed Fullscreen" WobblyShores: "Wobbly Shores" + + +# Controller button names + +EnchantType: "Cast Type" +InventorySelect: "Put" +JournalQuests: "Quests" +JournalShowAll: "Show All" +RechargeSelect: "Recharge" +RepairTool: "Tool" diff --git a/files/data/l10n/OMWEngine/fr.yaml b/files/data/l10n/OMWEngine/fr.yaml index fc584d8293..bd96ae0d80 100644 --- a/files/data/l10n/OMWEngine/fr.yaml +++ b/files/data/l10n/OMWEngine/fr.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nCliquez sur Copier pour placer ces noms dans le presse-papier.} } OverwriteGameConfirmation: "Écraser la sauvegarde précédente ?" -SelectCharacter: "Sélection du personnage..." +SelectCharacter: "Sélection du personnage" TimePlayed: "Temps de jeu" @@ -212,3 +212,13 @@ WindowModeHint: "Info : Le mode \"Fenêtré plein écran\" utilise toujours la r WindowModeWindowed: "Fenêtré" WindowModeWindowedFullscreen: "Fenêtré plein écran" WobblyShores: "Rivages vacillants" + + +# Controller button names + +EnchantType: "Type de lancement" +InventorySelect: "Placer" +JournalQuests: "Quêtes" +JournalShowAll: "Tout Afficher" +RechargeSelect: "Recharge" +RepairTool: "Outil" diff --git a/files/data/l10n/OMWEngine/ru.yaml b/files/data/l10n/OMWEngine/ru.yaml index c50fbac38e..6bee247c9d 100644 --- a/files/data/l10n/OMWEngine/ru.yaml +++ b/files/data/l10n/OMWEngine/ru.yaml @@ -58,7 +58,7 @@ MissingContentFilesListCopy: |- other{\n\nНажмите Скопировать, чтобы поместить их названия в буфер обмена.} } OverwriteGameConfirmation: "Вы уверены, что хотите перезаписать это сохранение?" -SelectCharacter: "Выберите персонажа..." +SelectCharacter: "Выберите персонажа" TimePlayed: "Время в игре" @@ -212,3 +212,13 @@ WindowModeHint: "Подсказка: режим Оконный без полей WindowModeWindowed: "Оконный" WindowModeWindowedFullscreen: "Оконный без полей" WobblyShores: "Колеблющиеся берега" + + +# Controller button names + +EnchantType: "Тип заклинания" +InventorySelect: "Поместить" +JournalQuests: "Квесты" +JournalShowAll: "Показать все" +RechargeSelect: "Перезарядить" +RepairTool: "Инструмент" diff --git a/files/data/l10n/OMWEngine/sv.yaml b/files/data/l10n/OMWEngine/sv.yaml index b3bb6788ed..9b2afed84f 100644 --- a/files/data/l10n/OMWEngine/sv.yaml +++ b/files/data/l10n/OMWEngine/sv.yaml @@ -59,7 +59,7 @@ MissingContentFilesListCopy: |- other{\n\nKlicka på kopiera för att placera deras namn i urklipp.} } OverwriteGameConfirmation: "Är du säker på att du vill skriva över det här sparade spelet?" -SelectCharacter: "Välj spelfigur..." +SelectCharacter: "Välj spelfigur" # Settings menu @@ -213,3 +213,13 @@ WindowModeHint: "Notera: Helskärm i fönsterläge\nanvänder alltid skärmens n WindowModeWindowed: "Fönster" WindowModeWindowedFullscreen: "Helskärm i fönsterläge" WobblyShores: "Vaggande stränder" + + +# Controller button names + +EnchantType: "Typ" +InventorySelect: "Sätta" +JournalQuests: "Uppdrag" +JournalShowAll: "Visa Alla" +RechargeSelect: "Ladda" +RepairTool: "Verktyg" diff --git a/files/data/mygui/openmw_savegame_dialog.layout b/files/data/mygui/openmw_savegame_dialog.layout index 1e40b5dd34..1683326065 100644 --- a/files/data/mygui/openmw_savegame_dialog.layout +++ b/files/data/mygui/openmw_savegame_dialog.layout @@ -6,7 +6,7 @@ - + From 780a4904bde94043b83b33e231d1b04fda22728a Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Fri, 13 Jun 2025 14:41:00 +0300 Subject: [PATCH 160/455] Handle implicitly checked files more consistently (#8563) --- .../contentselector/model/contentmodel.cpp | 26 ++++++++++++------- .../contentselector/model/contentmodel.hpp | 1 + .../contentselector/view/contentselector.cpp | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 2604a02885..53d5476fb6 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -129,7 +129,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index { if (depFile->isGameFile() && file->gameFiles().contains(depFile->fileName(), Qt::CaseInsensitive)) { - if (!depFile->builtIn() && !depFile->fromAnotherConfigFile() && !mCheckedFiles.contains(depFile)) + if (!isChecked(depFile)) break; return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; @@ -215,8 +215,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int if (file == mGameFile) return QVariant(); - return (file->builtIn() || file->fromAnotherConfigFile() || mCheckedFiles.contains(file)) ? Qt::Checked - : Qt::Unchecked; + return isChecked(file) ? Qt::Checked : Qt::Unchecked; } case Qt::UserRole: @@ -230,7 +229,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int } case Qt::UserRole + 1: - return mCheckedFiles.contains(file); + return isChecked(file); } return QVariant(); } @@ -264,9 +263,9 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const { int checkValue = value.toInt(); if (checkValue == Qt::Checked) - return mCheckedFiles.contains(file) || setCheckState(file, true); + return isChecked(file) || setCheckState(file, true); if (checkValue == Qt::Unchecked) - return !mCheckedFiles.contains(file) || setCheckState(file, false); + return !isChecked(file) || setCheckState(file, false); } } @@ -606,6 +605,14 @@ void ContentSelectorModel::ContentModel::sortFiles() emit layoutChanged(); } +bool ContentSelectorModel::ContentModel::isChecked(const EsmFile* file) const +{ + if (file == nullptr) + return false; + + return file->builtIn() || file->fromAnotherConfigFile() || mCheckedFiles.contains(file); +} + bool ContentSelectorModel::ContentModel::isEnabled(const QModelIndex& index) const { return (flags(index) & Qt::ItemIsEnabled); @@ -691,7 +698,7 @@ QList ContentSelectorModel::ContentModel:: } else { - if (!mCheckedFiles.contains(dependentFile)) + if (!isChecked(dependentFile)) { errors.append(LoadOrderError(LoadOrderError::ErrorCode_InactiveDependency, dependentfileName)); } @@ -706,7 +713,7 @@ QList ContentSelectorModel::ContentModel:: { // Warn the user if Bloodmoon is loaded before Tribunal (Tribunal is not a hard dependency) const EsmFile* tribunalFile = item("Tribunal.esm"); - if (tribunalFile != nullptr && mCheckedFiles.contains(tribunalFile) && row < indexFromItem(tribunalFile).row()) + if (isChecked(tribunalFile) && row < indexFromItem(tribunalFile).row()) errors.append(LoadOrderError(LoadOrderError::ErrorCode_LoadOrder, "Tribunal.esm")); } @@ -770,8 +777,9 @@ bool ContentSelectorModel::ContentModel::setCheckState(const EsmFile* file, bool for (const QString& upstreamName : file->gameFiles()) { const EsmFile* upstreamFile = item(upstreamName); - if (upstreamFile == nullptr || !mCheckedFiles.insert(upstreamFile).second) + if (upstreamFile == nullptr || isChecked(upstreamFile)) continue; + mCheckedFiles.insert(upstreamFile); QModelIndex upstreamIndex = indexFromItem(upstreamFile); emit dataChanged(upstreamIndex, upstreamIndex); } diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index cf182263f9..6260e5f1fe 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -58,6 +58,7 @@ namespace ContentSelectorModel QStringList gameFiles() const; void setCurrentGameFile(const EsmFile* file); + bool isChecked(const EsmFile* file) const; bool isEnabled(const QModelIndex& index) const; bool setCheckState(const EsmFile* file, bool isChecked); bool isNew(const QString& filepath) const; diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index f8374b5db5..5fd54ee789 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -157,7 +157,7 @@ void ContentSelectorView::ContentSelector::setGameFile(const QString& filename) index = ui->gameFileView->findText(file->fileName()); // verify that the current index is also checked in the model - if (!mContentModel->setCheckState(file, true)) + if (!mContentModel->isChecked(file) && !mContentModel->setCheckState(file, true)) { // throw error in case file not found? return; From ef651ee1875c74ff9acfd622ec82bd124face4bd Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 14 Jun 2025 13:13:36 -0700 Subject: [PATCH 161/455] Update swedish translation for putting an item in a container --- files/data/l10n/OMWEngine/sv.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/data/l10n/OMWEngine/sv.yaml b/files/data/l10n/OMWEngine/sv.yaml index 9b2afed84f..50a33582f3 100644 --- a/files/data/l10n/OMWEngine/sv.yaml +++ b/files/data/l10n/OMWEngine/sv.yaml @@ -218,7 +218,7 @@ WobblyShores: "Vaggande stränder" # Controller button names EnchantType: "Typ" -InventorySelect: "Sätta" +InventorySelect: "Placera" JournalQuests: "Uppdrag" JournalShowAll: "Visa Alla" RechargeSelect: "Ladda" From 8d545a43d51cb2f5a9686489e968de5221551478 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 14 Jun 2025 13:14:32 -0700 Subject: [PATCH 162/455] Scrolll the list of quests in controller mode --- apps/openmw/mwgui/journalwindow.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ddfa16255c..4c2e885e4e 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -826,6 +826,18 @@ namespace mButtons[mSelectedQuest]->setTextColour(MyGUI::Colour::Black); mSelectedQuest = MWGui::wrap(mSelectedQuest - 1, mButtons.size()); mButtons[mSelectedQuest]->setTextColour(MWGui::journalHeaderColour); + + // Scroll the list to keep the active item in view + Gui::MWList* list = getWidget(QuestsList); + if (mSelectedQuest <= 3) + list->setViewOffset(0); + else + { + int offset = 0; + for (int i = 0; i < mSelectedQuest - 3; i++) + offset += mButtons[i]->getHeight() + 3; + list->setViewOffset(-offset); + } } else if (mOptionsMode) { @@ -868,6 +880,18 @@ namespace mButtons[mSelectedQuest]->setTextColour(MyGUI::Colour::Black); mSelectedQuest = MWGui::wrap(mSelectedQuest + 1, mButtons.size()); mButtons[mSelectedQuest]->setTextColour(MWGui::journalHeaderColour); + + // Scroll the list to keep the active item in view + Gui::MWList* list = getWidget(QuestsList); + if (mSelectedQuest <= 3) + list->setViewOffset(0); + else + { + int offset = 0; + for (int i = 0; i < mSelectedQuest - 3; i++) + offset += mButtons[i]->getHeight() + 3; + list->setViewOffset(-offset); + } } else if (mOptionsMode) { From 0a0340327794b69f57a39930946098e5a03f5c82 Mon Sep 17 00:00:00 2001 From: Andrew Lanzone Date: Sat, 14 Jun 2025 13:42:51 -0700 Subject: [PATCH 163/455] Fix closing dialogue when a choice is active; improve scrolling --- apps/openmw/mwgui/dialogue.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index fe3675f6fb..d89c7753ce 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -944,10 +944,21 @@ namespace MWGui if (focused) { // Scroll the side bar to keep the active item in view - if (index <= 5) + if (index <= 8) mTopicsList->setViewOffset(0); else - mTopicsList->setViewOffset(-20 * (index - 5)); + { + int offset = 0; + for (int i = 0; i < index - 8; i++) + { + std::string keyword = mTopicsList->getItemNameAt(i); + if (keyword.length() == 0) + offset += 21; + else + offset += mTopicsList->getItemWidget(keyword)->getHeight() + 3; + } + mTopicsList->setViewOffset(-offset); + } } } @@ -966,7 +977,7 @@ namespace MWGui onSelectListItem(mTopicsList->getItemNameAt(mControllerFocus), mControllerFocus); MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Menu Click")); } - else if (arg.button == SDL_CONTROLLER_BUTTON_B) + else if (arg.button == SDL_CONTROLLER_BUTTON_B && mChoices.size() < 2) { onGoodbyeActivated(); } From 6d89ae1a751aa729a734d68370e48d45721b08ea Mon Sep 17 00:00:00 2001 From: Sarah Sunday <1644563-ssunday@users.noreply.gitlab.com> Date: Sat, 14 Jun 2025 16:09:07 -0500 Subject: [PATCH 164/455] [CI] Mac - use qt@6 --- CI/before_install.osx.sh | 4 ++-- CI/before_script.osx.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index d73399c102..a8fe3d094a 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -11,8 +11,8 @@ brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake -command -v qmake >/dev/null 2>&1 || brew install qt@5 -export PATH="/opt/homebrew/opt/qt@5/bin:$PATH" +command -v qmake >/dev/null 2>&1 || brew install qt@6 +export PATH="/opt/homebrew/opt/qt@6/bin:$PATH" # Install deps brew install openal-soft icu4c yaml-cpp sqlite diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 9f7a5bde8f..9be91f1632 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -9,7 +9,7 @@ cd build DEPENDENCIES_ROOT="/tmp/openmw-deps" -QT_PATH=$(brew --prefix qt@5) +QT_PATH=$(brew --prefix qt@6) ICU_PATH=$(brew --prefix icu4c) OPENAL_PATH=$(brew --prefix openal-soft) CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache From 302d92561d946a32f0a90783f824041452245464 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Mon, 16 Jun 2025 15:36:43 -0700 Subject: [PATCH 165/455] docs - begin restructing docs --- apps/openmw/mwlua/README.md | 2 +- docs/requirements.txt | 3 + docs/source/_static/luadoc.css | 60 ++++- docs/source/_static/theme.css | 11 + docs/source/conf.py | 29 ++- .../reference/lua-scripting/ai/combat.rst | 36 +++ .../reference/lua-scripting/ai/escort.rst | 49 ++++ .../reference/lua-scripting/ai/follow.rst | 37 +++ .../reference/lua-scripting/ai/pursue.rst | 25 ++ .../reference/lua-scripting/ai/travel.rst | 28 +++ .../reference/lua-scripting/ai/wander.rst | 53 +++++ .../reference/lua-scripting/aipackages.rst | 225 ------------------ docs/source/reference/lua-scripting/api.rst | 55 +---- .../lua-scripting/engine_handlers.rst | 14 ++ .../source/reference/lua-scripting/events.rst | 4 +- docs/source/reference/lua-scripting/index.rst | 6 +- .../lua-scripting/index_aipackages.rst | 24 ++ .../lua-scripting/index_auxpackages.rst | 21 ++ .../lua-scripting/index_interfaces.rst | 25 ++ .../lua-scripting/index_packages.rst | 37 +++ .../reference/lua-scripting/overview.rst | 8 +- .../lua-scripting/setting_renderers.rst | 4 +- .../lua-scripting/tables/aux_packages.rst | 31 ++- .../lua-scripting/tables/interfaces.rst | 79 +++--- .../lua-scripting/tables/packages.rst | 113 +++++---- .../lua-scripting/user_interface.rst | 96 ++++---- .../reference/lua-scripting/version.rst | 3 +- .../lua-scripting/widgets/container.rst | 45 +++- .../reference/lua-scripting/widgets/image.rst | 2 + .../reference/lua-scripting/widgets/text.rst | 2 + .../lua-scripting/widgets/textedit.rst | 2 + .../lua-scripting/widgets/widget.rst | 2 + docs/source/reference/modding/extended.rst | 2 +- docs/source/reference/modding/index.rst | 4 +- .../modding/texture-modding/index.rst | 6 +- .../source/reference/postprocessing/index.rst | 6 +- files/data/scripts/omw/ai.lua | 2 +- files/data/scripts/omw/settings/player.lua | 2 +- files/lua_api/openmw/ambient.lua | 4 +- files/lua_api/openmw/animation.lua | 3 +- files/lua_api/openmw/async.lua | 3 +- files/lua_api/openmw/camera.lua | 4 +- files/lua_api/openmw/core.lua | 4 +- files/lua_api/openmw/debug.lua | 4 +- files/lua_api/openmw/input.lua | 2 +- files/lua_api/openmw/interfaces.lua | 1 + files/lua_api/openmw/markup.lua | 3 +- files/lua_api/openmw/menu.lua | 3 +- files/lua_api/openmw/nearby.lua | 4 +- files/lua_api/openmw/postprocessing.lua | 4 +- files/lua_api/openmw/self.lua | 5 +- files/lua_api/openmw/storage.lua | 3 +- files/lua_api/openmw/types.lua | 3 +- files/lua_api/openmw/ui.lua | 4 +- files/lua_api/openmw/util.lua | 3 +- files/lua_api/openmw/vfs.lua | 3 +- files/lua_api/openmw/world.lua | 4 +- 57 files changed, 732 insertions(+), 485 deletions(-) create mode 100644 docs/source/_static/theme.css create mode 100644 docs/source/reference/lua-scripting/ai/combat.rst create mode 100644 docs/source/reference/lua-scripting/ai/escort.rst create mode 100644 docs/source/reference/lua-scripting/ai/follow.rst create mode 100644 docs/source/reference/lua-scripting/ai/pursue.rst create mode 100644 docs/source/reference/lua-scripting/ai/travel.rst create mode 100644 docs/source/reference/lua-scripting/ai/wander.rst delete mode 100644 docs/source/reference/lua-scripting/aipackages.rst create mode 100644 docs/source/reference/lua-scripting/index_aipackages.rst create mode 100644 docs/source/reference/lua-scripting/index_auxpackages.rst create mode 100644 docs/source/reference/lua-scripting/index_interfaces.rst create mode 100644 docs/source/reference/lua-scripting/index_packages.rst diff --git a/apps/openmw/mwlua/README.md b/apps/openmw/mwlua/README.md index ed911eb01d..a1cfeb1f1b 100644 --- a/apps/openmw/mwlua/README.md +++ b/apps/openmw/mwlua/README.md @@ -3,7 +3,7 @@ This folder contains the C++ implementation of the Lua scripting system. For user-facing documentation, see -[OpenMW Lua scripting](https://openmw.readthedocs.io/en/latest/reference/lua-scripting/index.html). +[Lua scripting](https://openmw.readthedocs.io/en/latest/reference/lua-scripting/index.html). The documentation is generated from [/docs/source/reference/lua-scripting](/docs/source/reference/lua-scripting). You can find instructions for generating the documentation at the diff --git a/docs/requirements.txt b/docs/requirements.txt index f46cc5c95d..0e02883062 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,6 @@ sphinx==7.1.2 docutils==0.18.1 jinja2==3.1.6 sphinx_rtd_theme==3.0.1 +sphinx-design==0.6.1 +furo==2024.8.6 +sphinx-copybutton==0.5.2 \ No newline at end of file diff --git a/docs/source/_static/luadoc.css b/docs/source/_static/luadoc.css index aa83013def..8a2e01c082 100644 --- a/docs/source/_static/luadoc.css +++ b/docs/source/_static/luadoc.css @@ -1,10 +1,9 @@ #luadoc tt { font-family: monospace; } -#luadoc p, +/* #luadoc p, #luadoc td, #luadoc th { font-size: .95em; line-height: 1.2em;} -#luadoc p, #luadoc ul { margin: 10px 0 0 10px;} @@ -109,5 +108,60 @@ #luadoc dl, #luadoc dd {margin: 0px; line-height: 1.2em;} -#luadoc li {list-style: bullet;} +#luadoc li {list-style: bullet;} */ +#luadoc pre.example { + background-color: #eeffcc; + border: 1px solid #e1e4e5; + padding: 10px; + margin: 10px 0 10px 0; + overflow-x: auto; +} + + +#luadoc pre.example code { + color: var(--color-content-foreground); + background-color: #eeffcc; + border: none; + white-space: pre; + padding: 0px; +} + +#luadoc code { + /* background-color: inherit; + color: inherit; + border: none; */ + font-family: monospace; +} + +body[data-theme="dark"] #luadoc pre.example { + background-color: #eeffcc; +} + +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) #luadoc pre.example { + background-color: #eeffcc; + } +} + +#luadoc a:not(:link) { + font-weight: bold; + color: var(--color-content-foreground); + text-decoration: none; + cursor: inherit; +} + +#luadoc p em { font-family: 'monospace';} + +#luadoc a:link { font-weight: bold; color: var(--color-link);; text-decoration: none; } +#luadoc a:visited { font-weight: bold; color: var(--color-link--hover); text-decoration: none; } +#luadoc a:link:hover { text-decoration: underline; } + +.context-wrapper { + display: flex; + gap: 4px; +} + +table.docutils { + width: 100%; +} \ No newline at end of file diff --git a/docs/source/_static/theme.css b/docs/source/_static/theme.css new file mode 100644 index 0000000000..2f0ef03b28 --- /dev/null +++ b/docs/source/_static/theme.css @@ -0,0 +1,11 @@ +.content { + width: 60em; +} + +a.next-page, a.prev-page { + color: var(--color-link); +} + +a.next-page:hover, a.prev-page:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index fbce7975da..38c1b01033 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -37,6 +37,8 @@ extensions = [ 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.autosectionlabel', + 'sphinx_design', + 'sphinx_copybutton', ] #autosectionlabel_prefix_document = True @@ -91,7 +93,14 @@ except Exception as ex: rst_prolog = f""" .. |luaApiRevision| replace:: {luaApiRevision} +.. |luaApiRevisionBadge| replace:: :bdg-link-info-line:`API v{luaApiRevision} ` + .. |ppApiRevision| replace:: {ppApiRevision} +.. |bdg-ctx-menu| replace:: :bdg-warning:`menu` +.. |bdg-ctx-global| replace:: :bdg-danger:`global` +.. |bdg-ctx-player| replace:: :bdg-secondary:`local:player` +.. |bdg-ctx-local| replace:: :bdg-info:`local` +.. |bdg-ctx-all| replace:: :bdg-danger:`global` :bdg-warning:`menu` :bdg-info:`local` """ # The language for content autogenerated by Sphinx. Refer to documentation @@ -138,7 +147,7 @@ primary_domain = 'c' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = 'furo' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -146,14 +155,20 @@ html_theme = 'sphinx_rtd_theme' html_theme_options = { 'navigation_with_keys': True, 'flyout_display': 'attached', + 'sidebar_hide_name': False, + 'top_of_page_buttons': [], } +html_css_files = [ + "theme.css", + "luadoc.css", + "figures.css" +] + # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] def setup(app): - app.add_css_file('figures.css') - app.add_css_file('luadoc.css') try: subprocess.call(['bash', project_root + '/docs/source/generate_luadoc.sh']) except Exception as e: @@ -161,19 +176,19 @@ def setup(app): # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +html_title = 'OpenMW' # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = 'OpenMW Documentation' +# html_short_title = 'OpenMW Documentation' # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = 'https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/_static/images/openmw.png' +html_logo = 'https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/_static/images/openmw.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = 'https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/_static/images/favicon.png' +html_favicon = 'https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/_static/images/favicon.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/source/reference/lua-scripting/ai/combat.rst b/docs/source/reference/lua-scripting/ai/combat.rst new file mode 100644 index 0000000000..06dd9c1452 --- /dev/null +++ b/docs/source/reference/lua-scripting/ai/combat.rst @@ -0,0 +1,36 @@ +Combat +====== + +.. include:: ../version.rst + +Attack another actor. + +**Arguments** + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type + - description + * - type + - string [required] + - the name of the package (see packages listed below) + * - cancelOther + - boolean [default=true] + - whether to cancel all other AI packages + * - target + - `GameObject <../openmw_core.html##(GameObject)>`_ [required] + - the actor to attack + +**Examples** + +.. code-block:: Lua + + -- from local script add package to self + local AI = require('openmw.interfaces').AI + AI.startPackage({type='Combat', target=anotherActor}) + + -- via event to any actor + actor:sendEvent('StartAIPackage', {type='Combat', target=anotherActor}) diff --git a/docs/source/reference/lua-scripting/ai/escort.rst b/docs/source/reference/lua-scripting/ai/escort.rst new file mode 100644 index 0000000000..fd9f2d05e5 --- /dev/null +++ b/docs/source/reference/lua-scripting/ai/escort.rst @@ -0,0 +1,49 @@ +Escort +====== + +.. include:: ../version.rst + +Escort another actor to the given location. + +**Arguments** + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type + - description + * - type + - string [required] + - the name of the package (see packages listed below) + * - cancelOther + - boolean [default=true] + - whether to cancel all other AI packages + * - target + - `GameObject <../openmw_core.html##(GameObject)>`_ [required] + - the actor to follow + * - destPosition + - `3d vector <../openmw_util.html##(Vector3)>`_ [required] + - the destination point + * - destCell + - Cell [optional] + - the destination cell + * - duration + - number [optional] + - duration in game time (will be rounded up to the next hour) + * - isRepeat + - boolean [optional] + - Will the package repeat (true or false) + +**Example** + +.. code-block:: Lua + + actor:sendEvent('StartAIPackage', { + type = 'Escort', + target = object.self, + destPosition = util.vector3(x, y, z), + duration = 3 * time.hour, + isRepeat = true + }) diff --git a/docs/source/reference/lua-scripting/ai/follow.rst b/docs/source/reference/lua-scripting/ai/follow.rst new file mode 100644 index 0000000000..ac6b70bcf7 --- /dev/null +++ b/docs/source/reference/lua-scripting/ai/follow.rst @@ -0,0 +1,37 @@ +Follow +====== + +.. include:: ../version.rst + +Follow another actor. + +**Arguments** + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type + - description + * - type + - string [required] + - the name of the package (see packages listed below) + * - cancelOther + - boolean [default=true] + - whether to cancel all other AI packages + * - target + - `GameObject <../openmw_core.html##(GameObject)>`_ [required] + - the actor to follow + * - destCell + - Cell [optional] + - the destination cell + * - duration + - number [optional] + - duration in game time (will be rounded up to the next hour) + * - destPosition + - `3d vector <../openmw_util.html##(Vector3)>`_ [optional] + - the destination point + * - isRepeat + - boolean [optional] + - Will the package repeat (true or false) diff --git a/docs/source/reference/lua-scripting/ai/pursue.rst b/docs/source/reference/lua-scripting/ai/pursue.rst new file mode 100644 index 0000000000..08385c57ae --- /dev/null +++ b/docs/source/reference/lua-scripting/ai/pursue.rst @@ -0,0 +1,25 @@ +Pursue +====== + +.. include:: ../version.rst + +Pursue another actor. + +**Arguments** + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type + - description + * - type + - string [required] + - the name of the package (see packages listed below) + * - cancelOther + - boolean [default=true] + - whether to cancel all other AI packages + * - target + - `GameObject <../openmw_core.html##(GameObject)>`_ [required] + - the actor to pursue diff --git a/docs/source/reference/lua-scripting/ai/travel.rst b/docs/source/reference/lua-scripting/ai/travel.rst new file mode 100644 index 0000000000..06a47c868d --- /dev/null +++ b/docs/source/reference/lua-scripting/ai/travel.rst @@ -0,0 +1,28 @@ +Travel +====== + +.. include:: ../version.rst + +Go to given location. + +**Arguments** + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type + - description + * - type + - string [required] + - the name of the package (see packages listed below) + * - cancelOther + - boolean [default=true] + - whether to cancel all other AI packages + * - destPosition + - `3d vector <../openmw_util.html##(Vector3)>`_ [required] + - the point to travel to + * - isRepeat + - boolean [optional] + - Will the package repeat (true or false) diff --git a/docs/source/reference/lua-scripting/ai/wander.rst b/docs/source/reference/lua-scripting/ai/wander.rst new file mode 100644 index 0000000000..fc702f12e2 --- /dev/null +++ b/docs/source/reference/lua-scripting/ai/wander.rst @@ -0,0 +1,53 @@ +Wander +====== + +.. include:: ../version.rst + +Wander nearby current position. + +**Arguments** + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type + - description + * - type + - string [required] + - the name of the package (see packages listed below) + * - distance + - float [default=0] + - the actor to follow + * - duration + - number [optional] + - duration in game time (will be rounded up to the next hour) + * - idle + - table [optional] + - Idle chance values, up to 8 + * - isRepeat + - boolean [optional] + - Will the package repeat (true or false) + +**Example** + +.. code-block:: Lua + + local idleTable = { + idle2 = 60, + idle3 = 50, + idle4 = 40, + idle5 = 30, + idle6 = 20, + idle7 = 10, + idle8 = 0, + idle9 = 25 + } + actor:sendEvent('StartAIPackage', { + type = 'Wander', + distance = 5000, + duration = 5 * time.hour, + idle = idleTable, + isRepeat = true + }) diff --git a/docs/source/reference/lua-scripting/aipackages.rst b/docs/source/reference/lua-scripting/aipackages.rst deleted file mode 100644 index 7a23d156f5..0000000000 --- a/docs/source/reference/lua-scripting/aipackages.rst +++ /dev/null @@ -1,225 +0,0 @@ -Built-in AI packages -==================== - -.. include:: version.rst - -Starting an AI package ----------------------- - -There are two ways to start AI package: - -.. code-block:: Lua - - -- from local script add package to self - local AI = require('openmw.interfaces').AI - AI.startPackage(options) - - -- via event to any actor - actor:sendEvent('StartAIPackage', options) - -``options`` is Lua table with arguments of the AI package. - -**Common arguments that can be used with any AI package** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - type - - string [required] - - the name of the package (see packages listed below) - * - cancelOther - - boolean [default=true] - - whether to cancel all other AI packages - -Combat ------- - -Attack another actor. - -**Arguments** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - target - - `GameObject `_ [required] - - the actor to attack - -**Examples** - -.. code-block:: Lua - - -- from local script add package to self - local AI = require('openmw.interfaces').AI - AI.startPackage({type='Combat', target=anotherActor}) - - -- via event to any actor - actor:sendEvent('StartAIPackage', {type='Combat', target=anotherActor}) - -Pursue ------- - -Pursue another actor. - -**Arguments** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - target - - `GameObject `_ [required] - - the actor to pursue - -Follow ------- - -Follow another actor. - -**Arguments** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - target - - `GameObject `_ [required] - - the actor to follow - * - destCell - - Cell [optional] - - the destination cell - * - duration - - number [optional] - - duration in game time (will be rounded up to the next hour) - * - destPosition - - `3d vector `_ [optional] - - the destination point - * - isRepeat - - boolean [optional] - - Will the package repeat (true or false) - -Escort ------- - -Escort another actor to the given location. - -**Arguments** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - target - - `GameObject `_ [required] - - the actor to follow - * - destPosition - - `3d vector `_ [required] - - the destination point - * - destCell - - Cell [optional] - - the destination cell - * - duration - - number [optional] - - duration in game time (will be rounded up to the next hour) - * - isRepeat - - boolean [optional] - - Will the package repeat (true or false) - -**Example** - -.. code-block:: Lua - - actor:sendEvent('StartAIPackage', { - type = 'Escort', - target = object.self, - destPosition = util.vector3(x, y, z), - duration = 3 * time.hour, - isRepeat = true - }) - -Wander ------- - -Wander nearby current position. - -**Arguments** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - distance - - float [default=0] - - the actor to follow - * - duration - - number [optional] - - duration in game time (will be rounded up to the next hour) - * - idle - - table [optional] - - Idle chance values, up to 8 - * - isRepeat - - boolean [optional] - - Will the package repeat (true or false) - -**Example** - -.. code-block:: Lua - - local idleTable = { - idle2 = 60, - idle3 = 50, - idle4 = 40, - idle5 = 30, - idle6 = 20, - idle7 = 10, - idle8 = 0, - idle9 = 25 - } - actor:sendEvent('StartAIPackage', { - type = 'Wander', - distance = 5000, - duration = 5 * time.hour, - idle = idleTable, - isRepeat = true - }) - -Travel ------- - -Go to given location. - -**Arguments** - -.. list-table:: - :header-rows: 1 - :widths: 20 20 60 - - * - name - - type - - description - * - destPosition - - `3d vector `_ [required] - - the point to travel to - * - isRepeat - - boolean [optional] - - Will the package repeat (true or false) diff --git a/docs/source/reference/lua-scripting/api.rst b/docs/source/reference/lua-scripting/api.rst index 82a860b355..adede0b0d6 100644 --- a/docs/source/reference/lua-scripting/api.rst +++ b/docs/source/reference/lua-scripting/api.rst @@ -7,54 +7,15 @@ Lua API reference .. toctree:: :hidden: - engine_handlers - user_interface - aipackages + index_packages + index_auxpackages + index_aipackages + index_interfaces + UI setting_renderers + Engine Handlers events - openmw_ambient - openmw_animation - openmw_async - openmw_camera - openmw_core - openmw_debug - openmw_input - openmw_markup - openmw_menu - openmw_nearby - openmw_postprocessing - openmw_self - openmw_storage - openmw_types - openmw_ui - openmw_util - openmw_vfs - openmw_world - openmw_aux_calendar - openmw_aux_time - openmw_aux_ui - openmw_aux_util - interface_activation - interface_ai - interface_animation - interface_camera - interface_controls - interface_gamepadcontrols - interface_item_usage - interface_mwui - interface_settings - interface_skill_progression - interface_ui - interface_crimes - iterables - - -- :ref:`Engine handlers reference` -- :ref:`User interface reference ` -- `Game object reference `_ -- `Cell reference `_ -- :ref:`Built-in AI packages` -- :ref:`Built-in events` + Iterables **API packages** @@ -66,7 +27,7 @@ Player scripts are local scripts that are attached to a player. .. include:: tables/packages.rst -**openmw_aux** +**Auxiliary packages** ``openmw_aux.*`` are built-in libraries that are itself implemented in Lua. They can not do anything that is not possible with the basic API, they only make it more convenient. Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can override them, but it is not recommended. diff --git a/docs/source/reference/lua-scripting/engine_handlers.rst b/docs/source/reference/lua-scripting/engine_handlers.rst index ac6979a236..29b14aee55 100644 --- a/docs/source/reference/lua-scripting/engine_handlers.rst +++ b/docs/source/reference/lua-scripting/engine_handlers.rst @@ -7,6 +7,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Can be defined by any script** +|bdg-ctx-all| + .. list-table:: :widths: 20 80 @@ -16,6 +18,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Can be defined by any non-menu script** +|bdg-ctx-global| |bdg-ctx-local| + .. list-table:: :widths: 20 80 @@ -39,6 +43,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Only for global scripts** +|bdg-ctx-global| + .. list-table:: :widths: 20 80 @@ -61,6 +67,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Only for local scripts** +|bdg-ctx-local| + .. list-table:: :widths: 20 80 @@ -86,6 +94,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Only menu scripts and local scripts attached to a player** +|bdg-ctx-menu| |bdg-ctx-player| + .. list-table:: :widths: 20 80 @@ -140,6 +150,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Only for local scripts attached to a player** +|bdg-ctx-player| + .. list-table:: :widths: 20 80 @@ -152,6 +164,8 @@ Engine handler is a function defined by a script, that can be called by the engi **Only for menu scripts** +|bdg-ctx-menu| + .. list-table:: :widths: 20 80 diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index 282e3d1173..007e0e43d1 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -1,5 +1,5 @@ -Built-in events -=============== +Events +====== .. include:: version.rst diff --git a/docs/source/reference/lua-scripting/index.rst b/docs/source/reference/lua-scripting/index.rst index f3764c4401..8db10bdae1 100644 --- a/docs/source/reference/lua-scripting/index.rst +++ b/docs/source/reference/lua-scripting/index.rst @@ -1,6 +1,6 @@ -#################### -OpenMW Lua scripting -#################### +############# +Lua scripting +############# .. note:: OpenMW Lua is not compatible with MWSE. diff --git a/docs/source/reference/lua-scripting/index_aipackages.rst b/docs/source/reference/lua-scripting/index_aipackages.rst new file mode 100644 index 0000000000..2b8b41bb08 --- /dev/null +++ b/docs/source/reference/lua-scripting/index_aipackages.rst @@ -0,0 +1,24 @@ +AI packages +============ + +.. include:: version.rst + +.. toctree:: + :hidden: + :glob: + + ai/* + +Starting an AI package +---------------------- + +There are two ways to start AI package: + +.. code-block:: Lua + + -- from local script add package to self + local AI = require('openmw.interfaces').AI + AI.startPackage(options) + + -- via event to any actor + actor:sendEvent('StartAIPackage', options) diff --git a/docs/source/reference/lua-scripting/index_auxpackages.rst b/docs/source/reference/lua-scripting/index_auxpackages.rst new file mode 100644 index 0000000000..e50fe45026 --- /dev/null +++ b/docs/source/reference/lua-scripting/index_auxpackages.rst @@ -0,0 +1,21 @@ +################## +Auxiliary Packages +################## + +.. include:: version.rst + +.. toctree:: + :hidden: + + aux_calendar + aux_time + aux_ui + aux_util + + +**Auxiliary packages** + +``openmw_aux.*`` are built-in libraries that are itself implemented in Lua. They can not do anything that is not possible with the basic API, they only make it more convenient. +Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can override them, but it is not recommended. + +.. include:: tables/aux_packages.rst diff --git a/docs/source/reference/lua-scripting/index_interfaces.rst b/docs/source/reference/lua-scripting/index_interfaces.rst new file mode 100644 index 0000000000..06f21ee7d5 --- /dev/null +++ b/docs/source/reference/lua-scripting/index_interfaces.rst @@ -0,0 +1,25 @@ +########## +Interfaces +########## + +.. include:: version.rst + +.. toctree:: + :hidden: + + activation + ai + animation + camera + controls + gamepadcontrols + item_usage + mwui + settings + skill_progression + ui + crimes + +**Interfaces of built-in scripts** + +.. include:: tables/interfaces.rst diff --git a/docs/source/reference/lua-scripting/index_packages.rst b/docs/source/reference/lua-scripting/index_packages.rst new file mode 100644 index 0000000000..53a836519f --- /dev/null +++ b/docs/source/reference/lua-scripting/index_packages.rst @@ -0,0 +1,37 @@ +######## +Packages +######## + +.. include:: version.rst + +.. toctree:: + :hidden: + + ambient + animation + async + camera + core + debug + input + markup + menu + nearby + postprocessing + self + storage + types + ui + util + vfs + world + +**API packages** + +API packages provide functions that can be called by scripts. I.e. it is a script-to-engine interaction. +A package can be loaded with ``require('')``. +It can not be overloaded even if there is a lua file with the same name. +The list of available packages is different for global and for local scripts. +Player scripts are local scripts that are attached to a player. + +.. include:: tables/packages.rst diff --git a/docs/source/reference/lua-scripting/overview.rst b/docs/source/reference/lua-scripting/overview.rst index b889b09a9f..2061e4efe9 100644 --- a/docs/source/reference/lua-scripting/overview.rst +++ b/docs/source/reference/lua-scripting/overview.rst @@ -384,8 +384,8 @@ Player scripts are local scripts that are attached to a player. .. include:: tables/packages.rst -openmw_aux ----------- +Auxiliary packages +------------------ ``openmw_aux.*`` are built-in libraries that are themselves implemented in Lua. They can not do anything that is not possible with the basic API, they only make it more convenient. Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can override them, but it is not recommended. @@ -544,7 +544,7 @@ The protection mod attaches an additional local script to every actor. The scrip In order to be able to intercept the event, the protection script should be placed in the load order below the original script. -See :ref:`the list of events ` that are used by built-in scripts. +See :ref:`the list of events ` that are used by built-in scripts. Timers @@ -618,7 +618,7 @@ An example: } } -Also in `openmw_aux`_ is the helper function ``runRepeatedly``, it is implemented on top of unsavable timers: +Also in `Auxiliary packages`_ is the helper function ``runRepeatedly``, it is implemented on top of unsavable timers: .. code-block:: Lua diff --git a/docs/source/reference/lua-scripting/setting_renderers.rst b/docs/source/reference/lua-scripting/setting_renderers.rst index b85c7fbaab..f315615cb4 100644 --- a/docs/source/reference/lua-scripting/setting_renderers.rst +++ b/docs/source/reference/lua-scripting/setting_renderers.rst @@ -1,5 +1,5 @@ -Built-in Setting Renderers -========================== +Setting Renderers +================= .. include:: version.rst diff --git a/docs/source/reference/lua-scripting/tables/aux_packages.rst b/docs/source/reference/lua-scripting/tables/aux_packages.rst index d0217ce202..d51949e1b2 100644 --- a/docs/source/reference/lua-scripting/tables/aux_packages.rst +++ b/docs/source/reference/lua-scripting/tables/aux_packages.rst @@ -1,12 +1,19 @@ -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ -| Built-in library | Can be used | Description | -+=========================================================+====================+===============================================================+ -|:ref:`openmw_aux.calendar ` | everywhere | | Game time calendar | -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw_aux.util ` | everywhere | | Miscellaneous utils | -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw_aux.time ` | everywhere | | Timers and game time utils | -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw_aux.ui ` | by player and menu | | User interface utils | -| | scripts | | -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ +.. list-table:: + :widths: 30 40 60 + :header-rows: 1 + + * - Module + - Context + - Description + * - :doc:`calendar ` + - |bdg-ctx-all| + - Game time calendar + * - :doc:`util ` + - |bdg-ctx-all| + - Miscellaneous utils + * - :doc:`time ` + - |bdg-ctx-all| + - Timers and game time utils + * - :doc:`ui ` + - |bdg-ctx-menu| |bdg-ctx-player| + - User interface utils diff --git a/docs/source/reference/lua-scripting/tables/interfaces.rst b/docs/source/reference/lua-scripting/tables/interfaces.rst index d8dfffe47d..bdf8104678 100644 --- a/docs/source/reference/lua-scripting/tables/interfaces.rst +++ b/docs/source/reference/lua-scripting/tables/interfaces.rst @@ -1,48 +1,43 @@ .. list-table:: - :widths: 20 20 60 + :widths: 30 40 60 + :header-rows: 1 * - Interface - - Can be used + - Context - Description - * - :ref:`Activation ` - - by global scripts + * - :doc:`Activation ` + - |bdg-ctx-global| - Allows to extend or override built-in activation mechanics. - * - :ref:`AI ` - - by local scripts - - Control basic AI of NPCs and creatures. - * - :ref:`AnimationController ` - - by local scripts - - Control animations of NPCs and creatures. - * - :ref:`Camera ` - - by player scripts - - | Allows to alter behavior of the built-in camera script - | without overriding the script completely. - * - :ref:`Controls ` - - by player scripts - - | Allows to alter behavior of the built-in script - | that handles player controls. - * - :ref:`GamepadControls ` - - by player scripts - - | Allows to alter behavior of the built-in script - | that handles player gamepad controls. - * - :ref:`ItemUsage ` - - by global scripts - - | Allows to extend or override built-in item usage - | mechanics. - * - :ref:`SkillProgression ` - - by local scripts - - | Control, extend, and override skill progression of the - | player. - * - :ref:`Settings ` - - by player, menu, and global scripts - - Save, display and track changes of setting values. - * - :ref:`MWUI ` - - by player and menu scripts - - Morrowind-style UI templates. - * - :ref:`UI ` - - by player scripts - - | High-level UI modes interface. Allows to override parts - | of the interface. - * - :ref:`Crimes ` - - by global scripts + * - :doc:`ItemUsage ` + - |bdg-ctx-global| + - Allows to extend or override built-in item usage mechanics. + * - :doc:`Crimes ` + - |bdg-ctx-global| - Commit crimes. + * - :doc:`AI ` + - |bdg-ctx-local| + - Control basic AI of NPCs and creatures. + * - :doc:`AnimationController ` + - |bdg-ctx-local| + - Control animations of NPCs and creatures. + * - :doc:`SkillProgression ` + - |bdg-ctx-local| + - Control, extend, and override skill progression of the player. + * - :doc:`Camera ` + - |bdg-ctx-player| + - Allows to alter behavior of the built-in camera script without overriding the script completely. + * - :doc:`Controls ` + - |bdg-ctx-player| + - Allows to alter behavior of the built-in script that handles player controls. + * - :doc:`GamepadControls ` + - |bdg-ctx-player| + - Allows to alter behavior of the built-in script that handles player gamepad controls. + * - :doc:`UI ` + - |bdg-ctx-player| + - High-level UI modes interface. Allows to override parts of the interface. + * - :doc:`Settings ` + - |bdg-ctx-global| |bdg-ctx-menu| |bdg-ctx-player| + - Save, display and track changes of setting values. + * - :doc:`MWUI ` + - |bdg-ctx-menu| |bdg-ctx-player| + - Morrowind-style UI templates. diff --git a/docs/source/reference/lua-scripting/tables/packages.rst b/docs/source/reference/lua-scripting/tables/packages.rst index e66926e5e4..290b7d3507 100644 --- a/docs/source/reference/lua-scripting/tables/packages.rst +++ b/docs/source/reference/lua-scripting/tables/packages.rst @@ -1,49 +1,64 @@ -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -| Package | Can be used | Description | -+============================================================+====================+===============================================================+ -|:ref:`openmw.ambient ` | by player and menu | | Controls background sounds for given player. | -| | scripts | | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.animation ` | by local and | | Animation controls | -| | player scripts | | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.async ` | everywhere | | Timers and callbacks. | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.camera ` | by player scripts | | Controls camera. | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.core ` | everywhere | | Functions that are common for both global and local scripts | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.debug ` | by player scripts | | Collection of debug utils. | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.input ` | by player and menu | | User input. | -| | scripts | | -+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.interfaces