From 9be8e26434752026cf8dda56953458ededb1804a Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:10:18 +0100 Subject: [PATCH 1/7] Hypertext: accept em as a length unit --- games/devtest/mods/testformspec/formspec.lua | 5 +- src/gui/guiHyperText.cpp | 48 ++++++++++++++++++-- src/gui/guiHyperText.h | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 48c8763de..4bdf933d8 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -67,7 +67,8 @@ This is a normal text. style test - . + + Tag test @@ -108,6 +109,8 @@ Normal: float=right: +width=1em height=2em: + item test Normal: diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 8aba3083c..554982b49 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -48,6 +48,46 @@ static bool check_integer(const std::string &str) return *endptr == '\0'; } +struct size_with_unit { + double size; + std::string unit; +}; + +static size_with_unit parse_length(const std::string &str) +{ + char *unitptr = nullptr; + double size = strtod(str.c_str(), &unitptr); + size_with_unit length; + + if (size <= 0) + size = 0; + length.size = size; + length.unit = unitptr; + + return length; +} + +static bool check_length(const std::string &str) +{ + return parse_length(str).size > 0; +} + +static u32 get_length_value(const std::string &str, ParsedText::Element &parent) +{ + size_with_unit length = parse_length(str); + if (!length.unit.empty()) + { + printf("%g %s\n", length.size, length.unit.c_str()); + printf("%d\n", parent.font_size); + } + if (length.unit.empty()) + return length.size; + else if (length.unit == "em") + return length.size * parent.font_size; + else + return 0; +} + // ----------------------------------------------------------------------------- // ParsedText - A text parser @@ -62,7 +102,7 @@ void ParsedText::Element::setStyle(StyleList &style) if (parseColorString(style["hovercolor"], color, false)) this->hovercolor = color; - unsigned int font_size = std::atoi(style["fontsize"].c_str()); + font_size = get_length_value(style["fontsize"], *this); FontMode font_mode = FM_Standard; if (style["fontstyle"] == "mono") @@ -340,7 +380,7 @@ void ParsedText::parseGenericStyleAttr( style[name] = is_yes(value); } else if (name == "size") { - if (check_integer(value)) + if (check_length(value)) style["fontsize"] = value; } else if (name == "font") { @@ -511,13 +551,13 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) } if (attrs.count("width")) { - int width = stoi(attrs["width"]); + int width = get_length_value(attrs["width"], *m_element); if (width > 0) m_element->dim.Width = width; } if (attrs.count("height")) { - int height = stoi(attrs["height"]); + int height = get_length_value(attrs["height"], *m_element); if (height > 0) m_element->dim.Height = height; } diff --git a/src/gui/guiHyperText.h b/src/gui/guiHyperText.h index 0616a37ce..4687d08a1 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -98,6 +98,7 @@ public: ValignType valign; gui::IGUIFont *font; + unsigned int font_size; irr::video::SColor color; irr::video::SColor hovercolor; From 01b2a33156c010a5b0c6fba3f5d51f95996d1bd5 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Mon, 12 Feb 2024 23:28:36 +0100 Subject: [PATCH 2/7] Scale text correctly --- games/devtest/mods/testformspec/formspec.lua | 5 +- src/gui/guiHyperText.cpp | 73 ++++++++++++-------- src/gui/guiHyperText.h | 10 +++ 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 4bdf933d8..3e90b52ca 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -68,7 +68,8 @@ This is a normal text. style test - + + Tag test @@ -111,6 +112,8 @@ Normal: width=1em height=2em: +width=2em inside width=2em + item test Normal: diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 554982b49..1671a5e5b 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -48,16 +48,11 @@ static bool check_integer(const std::string &str) return *endptr == '\0'; } -struct size_with_unit { - double size; - std::string unit; -}; - -static size_with_unit parse_length(const std::string &str) +static ParsedText::LengthValue parse_length(const std::string &str) { char *unitptr = nullptr; double size = strtod(str.c_str(), &unitptr); - size_with_unit length; + ParsedText::LengthValue length; if (size <= 0) size = 0; @@ -69,25 +64,37 @@ static size_with_unit parse_length(const std::string &str) static bool check_length(const std::string &str) { - return parse_length(str).size > 0; + return parse_length(str).isValid(); } -static u32 get_length_value(const std::string &str, ParsedText::Element &parent) +// ----------------------------------------------------------------------------- +// LengthValue - A data structure representing a (possibly relative) length + +bool ParsedText::LengthValue::isValid() { - size_with_unit length = parse_length(str); - if (!length.unit.empty()) - { - printf("%g %s\n", length.size, length.unit.c_str()); - printf("%d\n", parent.font_size); - } - if (length.unit.empty()) - return length.size; - else if (length.unit == "em") - return length.size * parent.font_size; + return this->getAbsoluteValue(1) > 0; +} + +double ParsedText::LengthValue::getAbsoluteValue(const double &em_size) +{ + if (this->unit.empty()) + return this->size; + else if (this->unit == "em") + return this->size * em_size; else return 0; } +double ParsedText::LengthValue::getAbsoluteValue() +{ + return getAbsoluteValue(0); +} + +bool ParsedText::LengthValue::isAbsolute() +{ + return getAbsoluteValue() > 0; +} + // ----------------------------------------------------------------------------- // ParsedText - A text parser @@ -102,7 +109,7 @@ void ParsedText::Element::setStyle(StyleList &style) if (parseColorString(style["hovercolor"], color, false)) this->hovercolor = color; - font_size = get_length_value(style["fontsize"], *this); + this->font_size = atoi(style["fontsize"].c_str()); FontMode font_mode = FM_Standard; if (style["fontstyle"] == "mono") @@ -111,9 +118,9 @@ void ParsedText::Element::setStyle(StyleList &style) // hypertext[] only accepts absolute font size values and has a hardcoded // default font size of 16. This is the only way to make hypertext[] // respect font size settings that I can think of. - font_size = myround(font_size / 16.0f * g_fontengine->getFontSize(font_mode)); + this->font_size = myround(this->font_size / 16.0f * g_fontengine->getFontSize(font_mode)); - FontSpec spec(font_size, font_mode, + FontSpec spec(this->font_size, font_mode, is_yes(style["bold"]), is_yes(style["italic"])); // TODO: find a way to check font validity @@ -122,7 +129,7 @@ void ParsedText::Element::setStyle(StyleList &style) if (!this->font) printf("No font found ! Size=%d, mode=%d, bold=%s, italic=%s\n", - font_size, font_mode, style["bold"].c_str(), + this->font_size, font_mode, style["bold"].c_str(), style["italic"].c_str()); } @@ -382,7 +389,6 @@ void ParsedText::parseGenericStyleAttr( } else if (name == "size") { if (check_length(value)) style["fontsize"] = value; - } else if (name == "font") { if (value == "mono" || value == "normal") style["fontstyle"] = value; @@ -551,13 +557,13 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) } if (attrs.count("width")) { - int width = get_length_value(attrs["width"], *m_element); + int width = parse_length(attrs["width"]).getAbsoluteValue(m_element->font_size); if (width > 0) m_element->dim.Width = width; } if (attrs.count("height")) { - int height = get_length_value(attrs["height"], *m_element); + int height = parse_length(attrs["height"]).getAbsoluteValue(m_element->font_size); if (height > 0) m_element->dim.Height = height; } @@ -634,9 +640,18 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) // Update styles accordingly m_style.clear(); - for (auto tag = m_active_tags.crbegin(); tag != m_active_tags.crend(); ++tag) - for (const auto &prop : (*tag)->style) - m_style[prop.first] = prop.second; + unsigned int font_size = 0; + for (auto tag = m_active_tags.crbegin(); tag != m_active_tags.crend(); ++tag) { + for (const auto &prop : (*tag)->style) { + if (prop.first == "fontsize") { + // resolve font size + font_size = parse_length(prop.second).getAbsoluteValue(font_size); + } else { + m_style[prop.first] = prop.second; + } + } + } + m_style["fontsize"] = std::to_string(font_size); return cursor; } diff --git a/src/gui/guiHyperText.h b/src/gui/guiHyperText.h index 4687d08a1..f11a3611b 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -76,6 +76,16 @@ public: typedef std::unordered_map StyleList; typedef std::unordered_map AttrsList; + struct LengthValue + { + double size; + std::string unit; + bool isValid(); + bool isAbsolute(); + double getAbsoluteValue(); + double getAbsoluteValue(const double &em_size); + }; + struct Tag { std::string name; From 8ce75da24a8ca2b4ff7dbdbfa0de17ff1dd4c516 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Mon, 12 Feb 2024 23:59:19 +0100 Subject: [PATCH 3/7] Fix erroneous testcase --- games/devtest/mods/testformspec/formspec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 3e90b52ca..b0b29efd1 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -112,8 +112,8 @@ Normal: width=1em height=2em: -width=2em inside width=2em - +width=2em inside width=2em (should render at width=4em): + item test Normal: From 14cad3dfb5277a3410431a67d84e7ced3d376745 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Tue, 13 Feb 2024 00:07:25 +0100 Subject: [PATCH 4/7] Use C++ constructor for ParsedText::LengthValue --- src/gui/guiHyperText.cpp | 32 ++++++++++++++------------------ src/gui/guiHyperText.h | 1 + 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 1671a5e5b..329b0a2c0 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -48,28 +48,24 @@ static bool check_integer(const std::string &str) return *endptr == '\0'; } -static ParsedText::LengthValue parse_length(const std::string &str) -{ - char *unitptr = nullptr; - double size = strtod(str.c_str(), &unitptr); - ParsedText::LengthValue length; - - if (size <= 0) - size = 0; - length.size = size; - length.unit = unitptr; - - return length; -} - static bool check_length(const std::string &str) { - return parse_length(str).isValid(); + return ParsedText::LengthValue(str).isValid(); } // ----------------------------------------------------------------------------- // LengthValue - A data structure representing a (possibly relative) length +ParsedText::LengthValue::LengthValue(const std::string &str) +{ + char *unitptr = nullptr; + this->size = strtod(str.c_str(), &unitptr); + + if (this->size <= 0) + this->size = 0; + this->unit = unitptr; +} + bool ParsedText::LengthValue::isValid() { return this->getAbsoluteValue(1) > 0; @@ -557,13 +553,13 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) } if (attrs.count("width")) { - int width = parse_length(attrs["width"]).getAbsoluteValue(m_element->font_size); + int width = LengthValue(attrs["width"]).getAbsoluteValue(m_element->font_size); if (width > 0) m_element->dim.Width = width; } if (attrs.count("height")) { - int height = parse_length(attrs["height"]).getAbsoluteValue(m_element->font_size); + int height = LengthValue(attrs["height"]).getAbsoluteValue(m_element->font_size); if (height > 0) m_element->dim.Height = height; } @@ -645,7 +641,7 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) for (const auto &prop : (*tag)->style) { if (prop.first == "fontsize") { // resolve font size - font_size = parse_length(prop.second).getAbsoluteValue(font_size); + font_size = LengthValue(prop.second).getAbsoluteValue(font_size); } else { m_style[prop.first] = prop.second; } diff --git a/src/gui/guiHyperText.h b/src/gui/guiHyperText.h index f11a3611b..bc0344b54 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -80,6 +80,7 @@ public: { double size; std::string unit; + LengthValue(const std::string &str); bool isValid(); bool isAbsolute(); double getAbsoluteValue(); From 6e57555d9ee3d241c86e17ac1b9eebd2d4d40f85 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:25:56 +0100 Subject: [PATCH 5/7] Document use of the `em` unit --- doc/lua_api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 6f5bb8683..53ef2df59 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -3496,6 +3496,10 @@ Some tags can enclose text, they open with `` and close with ` Date: Tue, 13 Feb 2024 16:00:40 +0100 Subject: [PATCH 6/7] Bump formspec version --- doc/lua_api.md | 2 ++ src/network/networkprotocol.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 53ef2df59..ca7ef0e7d 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2657,6 +2657,8 @@ Version History * Formspec version 7 (5.8.0): * style[]: Add focused state for buttons * Add field_enter_after_edit[] (experimental) +* Formspec version 8 (5.9.0): + * hypertext[]: Allow `em` as a length unit Elements -------- diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 6de00803e..aa5740d96 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -246,7 +246,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // base64-encoded SHA-1 (27+\0). // See also formspec [Version History] in doc/lua_api.md -#define FORMSPEC_API_VERSION 7 +#define FORMSPEC_API_VERSION 8 #define TEXTURENAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-" From 177b52acb0316757da411c7315710275984f495a Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Wed, 14 Feb 2024 21:35:32 +0100 Subject: [PATCH 7/7] add std:: prepend where appropriate --- src/gui/guiHyperText.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 329b0a2c0..2250747db 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -59,7 +59,7 @@ static bool check_length(const std::string &str) ParsedText::LengthValue::LengthValue(const std::string &str) { char *unitptr = nullptr; - this->size = strtod(str.c_str(), &unitptr); + this->size = std::strtod(str.c_str(), &unitptr); if (this->size <= 0) this->size = 0; @@ -105,7 +105,7 @@ void ParsedText::Element::setStyle(StyleList &style) if (parseColorString(style["hovercolor"], color, false)) this->hovercolor = color; - this->font_size = atoi(style["fontsize"].c_str()); + this->font_size = std::atoi(style["fontsize"].c_str()); FontMode font_mode = FM_Standard; if (style["fontstyle"] == "mono")