diff --git a/doc/lua_api.md b/doc/lua_api.md index 3cf586faa..b46973fba 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2681,6 +2681,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 -------- @@ -3535,6 +3537,10 @@ Some tags can enclose text, they open with `` and close with `style test - . + + + Tag test @@ -112,6 +114,10 @@ Normal: float=right: +width=1em height=2em: + +width=2em inside width=2em (should render at width=4em): + item test Normal: diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 76bc98a71..12d78dc5b 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -49,6 +49,49 @@ static bool check_integer(const std::string &str) return *endptr == '\0'; } +static bool check_length(const std::string &str) +{ + 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 = std::strtod(str.c_str(), &unitptr); + + if (this->size <= 0) + this->size = 0; + this->unit = unitptr; +} + +bool ParsedText::LengthValue::isValid() +{ + 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 @@ -63,7 +106,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()); + this->font_size = std::atoi(style["fontsize"].c_str()); FontMode font_mode = FM_Standard; if (style["fontstyle"] == "mono") @@ -72,9 +115,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 @@ -83,7 +126,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()); } @@ -341,9 +384,8 @@ 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") { if (value == "mono" || value == "normal") style["fontstyle"] = value; @@ -512,13 +554,13 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) } if (attrs.count("width")) { - int width = stoi(attrs["width"]); + int width = LengthValue(attrs["width"]).getAbsoluteValue(m_element->font_size); if (width > 0) m_element->dim.Width = width; } if (attrs.count("height")) { - int height = stoi(attrs["height"]); + int height = LengthValue(attrs["height"]).getAbsoluteValue(m_element->font_size); if (height > 0) m_element->dim.Height = height; } @@ -595,9 +637,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 = LengthValue(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 0616a37ce..bc0344b54 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -76,6 +76,17 @@ public: typedef std::unordered_map StyleList; typedef std::unordered_map AttrsList; + struct LengthValue + { + double size; + std::string unit; + LengthValue(const std::string &str); + bool isValid(); + bool isAbsolute(); + double getAbsoluteValue(); + double getAbsoluteValue(const double &em_size); + }; + struct Tag { std::string name; @@ -98,6 +109,7 @@ public: ValignType valign; gui::IGUIFont *font; + unsigned int font_size; irr::video::SColor color; irr::video::SColor hovercolor; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index add80b3b2..e1ea47b21 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -244,7 +244,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_.-"