From 6150fe81c735aa18f0af37bca667fb3b972e98fd Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 16 Aug 2025 09:02:03 +0200 Subject: [PATCH 1/4] Use std::format in MWGui::ToolTips --- apps/openmw/mwgui/tooltips.cpp | 35 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 76bfa18d5e..141bf80f07 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -1,5 +1,6 @@ #include "tooltips.hpp" +#include #include #include @@ -52,7 +53,7 @@ namespace MWGui mDynamicToolTipBox->setNeedMouseFocus(false); mMainWidget->setNeedMouseFocus(false); - for (unsigned int i = 0; i < mMainWidget->getChildCount(); ++i) + for (size_t i = 0; i < mMainWidget->getChildCount(); ++i) { mMainWidget->getChildAt(i)->setVisible(false); } @@ -77,7 +78,7 @@ namespace MWGui } // start by hiding everything - for (unsigned int i = 0; i < mMainWidget->getChildCount(); ++i) + for (size_t i = 0; i < mMainWidget->getChildCount(); ++i) { mMainWidget->getChildAt(i)->setVisible(false); } @@ -343,7 +344,7 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); } - for (unsigned int i = 0; i < mMainWidget->getChildCount(); ++i) + for (size_t i = 0; i < mMainWidget->getChildCount(); ++i) { mMainWidget->getChildAt(i)->setVisible(false); } @@ -660,40 +661,35 @@ namespace MWGui { if (weight == 0) return {}; - else - return "\n" + prefix + ": " + toString(weight); + return std::format("\n{}: {}", prefix, toString(weight)); } std::string ToolTips::getPercentString(const float value, const std::string& prefix) { if (value == 0) return {}; - else - return "\n" + prefix + ": " + toString(value * 100) + "%"; + return std::format("\n{}: {}%", prefix, toString(value * 100)); } std::string ToolTips::getValueString(const int value, const std::string& prefix) { if (value == 0) return {}; - else - return "\n" + prefix + ": " + toString(value); + return std::format("\n{}: {}", prefix, value); } std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) { if (text.empty()) return {}; - else - return "\n" + prefix + ": " + text; + return std::format("\n{}: {}", prefix, text); } std::string ToolTips::getCountString(const int value) { if (value == 1) return {}; - else - return " (" + MyGUI::utility::toString(value) + ")"; + return std::format(" ({})", value); } std::string ToolTips::getSoulString(const MWWorld::CellRef& cellref) @@ -706,8 +702,8 @@ namespace MWGui if (!creature) return {}; if (creature->mName.empty()) - return " (" + creature->mId.toDebugString() + ")"; - return " (" + creature->mName + ")"; + return std::format(" ({})", creature->mId.toDebugString()); + return std::format(" ({})", creature->mName); } std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref) @@ -740,10 +736,9 @@ namespace MWGui for (std::pair& owner : itemOwners) { if (owner.second == std::numeric_limits::max()) - ret += std::string("\nStolen from ") + owner.first.toDebugString(); // for legacy (ESS) savegames + ret += std::format("\nStolen from {}", owner.first.toDebugString()); // for legacy (ESS) savegames else - ret += std::string("\nStolen ") + MyGUI::utility::toString(owner.second) + " from " - + owner.first.toDebugString(); + ret += std::format("\nStolen {} from {}", owner.second, owner.first.toDebugString()); } ret += getMiscString(cellref.getGlobalVariable(), "Global"); @@ -754,8 +749,8 @@ namespace MWGui { auto l10n = MWBase::Environment::get().getL10nManager()->getContext("Interface"); - std::string ret; - ret = prefix + ": "; + std::string ret(prefix); + ret += ": "; if (duration < 1.f) { From d4a106135484b052ad7343010a71f32ae907fd41 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 16 Aug 2025 09:05:54 +0200 Subject: [PATCH 2/4] Use std::string_view in MWGui::ToolTips --- apps/openmw/mwgui/class.cpp | 6 +++--- apps/openmw/mwgui/tooltips.cpp | 12 ++++++------ apps/openmw/mwgui/tooltips.hpp | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 5a03e269cf..b60e0e3116 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -300,9 +300,9 @@ namespace MWGui ESM::Class::Specialization specialization = static_cast(currentClass->mData.mSpecialization); - std::string specName{ MWBase::Environment::get().getWindowManager()->getGameSettingString( - ESM::Class::sGmstSpecializationIds[specialization], ESM::Class::sGmstSpecializationIds[specialization]) }; - mSpecializationName->setCaption(specName); + std::string_view specName = MWBase::Environment::get().getWindowManager()->getGameSettingString( + ESM::Class::sGmstSpecializationIds[specialization], ESM::Class::sGmstSpecializationIds[specialization]); + mSpecializationName->setCaption(MyGUI::UString(specName)); ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); mFavoriteAttribute[0]->setAttributeId(ESM::Attribute::indexToRefId(currentClass->mData.mAttribute[0])); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 141bf80f07..2d5b2860a0 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -657,28 +657,28 @@ namespace MWGui return std::to_string(value); } - std::string ToolTips::getWeightString(const float weight, const std::string& prefix) + std::string ToolTips::getWeightString(const float weight, std::string_view prefix) { if (weight == 0) return {}; return std::format("\n{}: {}", prefix, toString(weight)); } - std::string ToolTips::getPercentString(const float value, const std::string& prefix) + std::string ToolTips::getPercentString(const float value, std::string_view prefix) { if (value == 0) return {}; return std::format("\n{}: {}%", prefix, toString(value * 100)); } - std::string ToolTips::getValueString(const int value, const std::string& prefix) + std::string ToolTips::getValueString(const int value, std::string_view prefix) { if (value == 0) return {}; return std::format("\n{}: {}", prefix, value); } - std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) + std::string ToolTips::getMiscString(std::string_view text, std::string_view prefix) { if (text.empty()) return {}; @@ -745,7 +745,7 @@ namespace MWGui return ret; } - std::string ToolTips::getDurationString(float duration, const std::string& prefix) + std::string ToolTips::getDurationString(float duration, std::string_view prefix) { auto l10n = MWBase::Environment::get().getL10nManager()->getContext("Interface"); @@ -853,7 +853,7 @@ namespace MWGui widget->setUserString("ImageTexture_AttributeImage", attribute->mIcon); } - void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) + void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, std::string_view name, int specId) { widget->setUserString("Caption_Caption", name); std::string specText; diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 2c817c3bde..80a555733c 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -67,12 +67,12 @@ namespace MWGui void setFocusObjectScreenCoords(float x, float y); ///< set the screen-space position of the tooltip for focused object - static std::string getWeightString(const float weight, const std::string& prefix); - static std::string getPercentString(const float value, const std::string& prefix); - static std::string getValueString(const int value, const std::string& prefix); + static std::string getWeightString(const float weight, std::string_view prefix); + static std::string getPercentString(const float value, std::string_view prefix); + static std::string getValueString(const int value, std::string_view prefix); ///< @return "prefix: value" or "" if value is 0 - static std::string getMiscString(const std::string& text, const std::string& prefix); + static std::string getMiscString(std::string_view text, std::string_view prefix); ///< @return "prefix: text" or "" if text is empty static std::string toString(const float value); @@ -87,14 +87,14 @@ namespace MWGui static std::string getCellRefString(const MWWorld::CellRef& cellref); ///< Returns a string containing debug tooltip information about the given cellref. - static std::string getDurationString(float duration, const std::string& prefix); + static std::string getDurationString(float duration, std::string_view prefix); ///< Returns duration as two largest time units, rounded down. Note: not localized; no line break. // these do not create an actual tooltip, but they fill in the data that is required so the tooltip // system knows what to show in case this widget is hovered static void createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId); static void createAttributeToolTip(MyGUI::Widget* widget, ESM::RefId attributeId); - static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId); + static void createSpecializationToolTip(MyGUI::Widget* widget, std::string_view name, int specId); static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId); static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace); static void createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass); From eb01a302f130e30d3bfaa92e171e312b3c9aa46f Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 16 Aug 2025 09:22:54 +0200 Subject: [PATCH 3/4] Use a custom formatter to limit float precision --- apps/openmw/mwgui/tooltips.cpp | 56 +++++++++++++++++++++++----- apps/openmw_tests/CMakeLists.txt | 2 + apps/openmw_tests/mwgui/tooltips.cpp | 24 ++++++++++++ 3 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 apps/openmw_tests/mwgui/tooltips.cpp diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 2d5b2860a0..788123c625 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -30,6 +30,50 @@ #include "itemmodel.hpp" +namespace +{ + template + struct LimitedFloat + { + float mValue; + + constexpr unsigned short getPrecision() { return Precision; } + }; +} + +template <> +struct std::formatter, char> +{ + template + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + FmtContext::iterator format(LimitedFloat<> v, FmtContext& ctx) const + { + constexpr unsigned short precision = v.getPrecision(); + // FLOAT_MAX needs 39 digits + 1 for the sign + 1 for the decimal separator + constexpr std::size_t bufferSize = 41 + precision; + char buffer[bufferSize]; + std::to_chars_result result + = std::to_chars(buffer, buffer + bufferSize, v.mValue, std::chars_format::fixed, precision); + if (result.ec != std::errc()) + { + // Should never happen, but just default to regular float formatting + return std::format_to(ctx.out(), "{}", v.mValue); + } + // Trim result so 1.00 turns into 1 + char* end = result.ptr; + while (end > buffer && *(end - 1) == '0') + --end; + if (end > buffer && *(end - 1) == '.') + --end; + return std::ranges::copy(buffer, end, ctx.out()).out; + } +}; + namespace MWGui { ToolTips::ToolTips() @@ -643,13 +687,7 @@ namespace MWGui std::string ToolTips::toString(const float value) { - std::ostringstream stream; - - if (value != int(value)) - stream << std::setprecision(3); - - stream << value; - return stream.str(); + return std::format("{}", LimitedFloat(value)); } std::string ToolTips::toString(const int value) @@ -661,14 +699,14 @@ namespace MWGui { if (weight == 0) return {}; - return std::format("\n{}: {}", prefix, toString(weight)); + return std::format("\n{}: {}", prefix, LimitedFloat(weight)); } std::string ToolTips::getPercentString(const float value, std::string_view prefix) { if (value == 0) return {}; - return std::format("\n{}: {}%", prefix, toString(value * 100)); + return std::format("\n{}: {}%", prefix, LimitedFloat(value * 100)); } std::string ToolTips::getValueString(const int value, std::string_view prefix) diff --git a/apps/openmw_tests/CMakeLists.txt b/apps/openmw_tests/CMakeLists.txt index adfce32537..9ad72e9199 100644 --- a/apps/openmw_tests/CMakeLists.txt +++ b/apps/openmw_tests/CMakeLists.txt @@ -14,6 +14,8 @@ file(GLOB UNITTEST_SRC_FILES mwdialogue/testkeywordsearch.cpp + mwgui/tooltips.cpp + mwscript/testscripts.cpp ) diff --git a/apps/openmw_tests/mwgui/tooltips.cpp b/apps/openmw_tests/mwgui/tooltips.cpp new file mode 100644 index 0000000000..61784dbfbd --- /dev/null +++ b/apps/openmw_tests/mwgui/tooltips.cpp @@ -0,0 +1,24 @@ +#include + +#include "apps/openmw/mwgui/tooltips.hpp" + +#include + +namespace MWGui +{ + namespace + { + TEST(MWGuiToolTipsTest, floatsShouldBeFormattedCorrectly) + { + EXPECT_EQ(ToolTips::toString(1.f), "1"); + EXPECT_EQ(ToolTips::toString(1.1f), "1.1"); + EXPECT_EQ(ToolTips::toString(1.12f), "1.12"); + EXPECT_EQ(ToolTips::toString(1234567.12f), "1234567.12"); + EXPECT_EQ(ToolTips::toString(0.001f), "0"); + EXPECT_EQ(ToolTips::toString(0.01f), "0.01"); + EXPECT_EQ(ToolTips::toString(0.01f), "0.01"); + EXPECT_EQ(ToolTips::toString(std::numeric_limits::infinity()), "inf"); + EXPECT_EQ(ToolTips::toString(std::numeric_limits::quiet_NaN()), "nan"); + } + } +} From b7997a5e885258c2ab5e5137f01da2e6984d9e3e Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 16 Aug 2025 11:33:58 +0200 Subject: [PATCH 4/4] Use a simpler way of formatting floats --- apps/openmw/mwgui/tooltips.cpp | 56 +++++----------------------- apps/openmw_tests/mwgui/tooltips.cpp | 2 +- 2 files changed, 10 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 788123c625..678847532c 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -30,50 +30,6 @@ #include "itemmodel.hpp" -namespace -{ - template - struct LimitedFloat - { - float mValue; - - constexpr unsigned short getPrecision() { return Precision; } - }; -} - -template <> -struct std::formatter, char> -{ - template - constexpr ParseContext::iterator parse(ParseContext& ctx) - { - return ctx.begin(); - } - - template - FmtContext::iterator format(LimitedFloat<> v, FmtContext& ctx) const - { - constexpr unsigned short precision = v.getPrecision(); - // FLOAT_MAX needs 39 digits + 1 for the sign + 1 for the decimal separator - constexpr std::size_t bufferSize = 41 + precision; - char buffer[bufferSize]; - std::to_chars_result result - = std::to_chars(buffer, buffer + bufferSize, v.mValue, std::chars_format::fixed, precision); - if (result.ec != std::errc()) - { - // Should never happen, but just default to regular float formatting - return std::format_to(ctx.out(), "{}", v.mValue); - } - // Trim result so 1.00 turns into 1 - char* end = result.ptr; - while (end > buffer && *(end - 1) == '0') - --end; - if (end > buffer && *(end - 1) == '.') - --end; - return std::ranges::copy(buffer, end, ctx.out()).out; - } -}; - namespace MWGui { ToolTips::ToolTips() @@ -687,7 +643,13 @@ namespace MWGui std::string ToolTips::toString(const float value) { - return std::format("{}", LimitedFloat(value)); + std::string s = std::format("{:.2f}", value); + // Trim result so 1.00 turns into 1 + while (!s.empty() && s.back() == '0') + s.pop_back(); + if (!s.empty() && s.back() == '.') + s.pop_back(); + return s; } std::string ToolTips::toString(const int value) @@ -699,14 +661,14 @@ namespace MWGui { if (weight == 0) return {}; - return std::format("\n{}: {}", prefix, LimitedFloat(weight)); + return std::format("\n{}: {}", prefix, toString(weight)); } std::string ToolTips::getPercentString(const float value, std::string_view prefix) { if (value == 0) return {}; - return std::format("\n{}: {}%", prefix, LimitedFloat(value * 100)); + return std::format("\n{}: {}%", prefix, toString(value * 100)); } std::string ToolTips::getValueString(const int value, std::string_view prefix) diff --git a/apps/openmw_tests/mwgui/tooltips.cpp b/apps/openmw_tests/mwgui/tooltips.cpp index 61784dbfbd..ee41b23721 100644 --- a/apps/openmw_tests/mwgui/tooltips.cpp +++ b/apps/openmw_tests/mwgui/tooltips.cpp @@ -16,7 +16,7 @@ namespace MWGui EXPECT_EQ(ToolTips::toString(1234567.12f), "1234567.12"); EXPECT_EQ(ToolTips::toString(0.001f), "0"); EXPECT_EQ(ToolTips::toString(0.01f), "0.01"); - EXPECT_EQ(ToolTips::toString(0.01f), "0.01"); + EXPECT_EQ(ToolTips::toString(0.012f), "0.01"); EXPECT_EQ(ToolTips::toString(std::numeric_limits::infinity()), "inf"); EXPECT_EQ(ToolTips::toString(std::numeric_limits::quiet_NaN()), "nan"); }