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"); + } + } +}