From 95411657520cfaf6493076417df585b58e99a545 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 16 Mar 2019 21:38:36 +0000 Subject: [PATCH] Add styles to most elements --- build/android/jni/Android.mk | 1 + builtin/mainmenu/dlg_delete_content.lua | 2 +- builtin/mainmenu/dlg_delete_world.lua | 2 +- builtin/mainmenu/tab_local.lua | 3 - doc/lua_api.txt | 105 ++++++++++++---- src/client/renderingengine.cpp | 16 +-- src/gui/StyleSpec.h | 101 +++++++++------ src/gui/guiFormSpecMenu.cpp | 156 +++++++++++++++++++----- src/gui/guiFormSpecMenu.h | 6 +- 9 files changed, 288 insertions(+), 104 deletions(-) diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 75d20ab95..ade2bb9c5 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -177,6 +177,7 @@ LOCAL_SRC_FILES := \ jni/src/filesys.cpp \ jni/src/genericobject.cpp \ jni/src/gettext.cpp \ + jni/src/gui/guiButton.cpp \ jni/src/gui/guiChatConsole.cpp \ jni/src/gui/guiConfirmRegistration.cpp \ jni/src/gui/guiEditBoxWithScrollbar.cpp \ diff --git a/builtin/mainmenu/dlg_delete_content.lua b/builtin/mainmenu/dlg_delete_content.lua index a8da2efc7..a24171541 100644 --- a/builtin/mainmenu/dlg_delete_content.lua +++ b/builtin/mainmenu/dlg_delete_content.lua @@ -22,7 +22,7 @@ local function delete_content_formspec(dialogdata) "size[11.5,4.5,true]" .. "label[2,2;" .. fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name) .. "]".. - "style[dlg_delete_content_confirm;bgcolor;red]" .. + "style[dlg_delete_content_confirm;bgcolor=red]" .. "button[3.25,3.5;2.5,0.5;dlg_delete_content_confirm;" .. fgettext("Delete") .. "]" .. "button[5.75,3.5;2.5,0.5;dlg_delete_content_cancel;" .. fgettext("Cancel") .. "]" diff --git a/builtin/mainmenu/dlg_delete_world.lua b/builtin/mainmenu/dlg_delete_world.lua index 003935350..33e7bc945 100644 --- a/builtin/mainmenu/dlg_delete_world.lua +++ b/builtin/mainmenu/dlg_delete_world.lua @@ -21,7 +21,7 @@ local function delete_world_formspec(dialogdata) "size[10,2.5,true]" .. "label[0.5,0.5;" .. fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" .. - "style[world_delete_confirm;bgcolor;red]" .. + "style[world_delete_confirm;bgcolor=red]" .. "button[0.5,1.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" .. "button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]" return retval diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 512b4f844..15ef96dc8 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -102,9 +102,6 @@ local function get_formspec(tabview, name, tabdata) ) retval = retval .. - "style_type[button;bgcolor;#006699]" .. - "style[world_delete;bgcolor;red]" .. - "style[world_delete;textcolor;yellow]" .. "button[4,3.95;2.6,1;world_delete;".. fgettext("Delete") .. "]" .. "button[6.5,3.95;2.8,1;world_configure;".. fgettext("Configure") .. "]" .. "button[9.2,3.95;2.5,1;world_create;".. fgettext("New") .. "]" .. diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bcc304584..7efdc836e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1884,7 +1884,10 @@ When displaying text which can contain formspec code, e.g. text set by a player, use `minetest.formspec_escape`. For coloured text you can use `minetest.colorize`. -WARNING: Minetest allows you to add elements to every single formspec instance +**WARNING**: do _not_ use a element name starting with `key_`; those names are +reserved to pass key press events to formspec! + +**WARNING**: Minetest allows you to add elements to every single formspec instance using `player:set_formspec_prepend()`, which may be the reason backgrounds are appearing when you don't expect them to, or why things are styled differently to normal. See [`no_prepend[]`] and [Styling Formspecs]. @@ -2351,22 +2354,17 @@ Elements * `span=`: number of following columns to affect (default: infinite). -**Note**: do _not_ use a element name starting with `key_`; those names are -reserved to pass key press events to formspec! +### `style[;;;...]` -### `style[;;;;]` +### `style_type[;;;...]` -Sets the style for all elements of type `type` which appear after this tag. - -See [Styling Formspecs]. +* Sets the style for all elements of type `type` which appear after this element. +* See [Styling Formspecs]. Migrating to Real Coordinates ----------------------------- @@ -2406,27 +2404,82 @@ offsets when migrating: Styling Formspecs ----------------- -Formspec elements can be themed using the style tags: +Formspec elements can be themed using the style elements: - style[ELEMENT_NAME;PROPERTY;VALUE] - style_type[ELEMENT_TYPE;PROPERTY;VALUE] + style[;;;...] + style_type[;;;...] + +Where a prop is: + + property_name=property_value For example: - style_type[button;bgcolor;#006699] - style[world_delete;bgcolor;#ff0000] - button[4,3.95;2.6,1;world_delete;Delete] + style_type[button;bgcolor=#006699] + style[world_delete;bgcolor=red;textcolor=yellow] + button[4,3.95;2.6,1;world_delete;Delete] + +Setting a property to nothing will reset it to the default value. For example: + + style_type[button;bgimg=button.png;bgimg_pressed=button_pressed.png;border=false] + style[btn_exit;bgimg=;bgimg_pressed=;border=;bgcolor=red] + + +### Supported Element Types + +Some types may inherit styles from parent types. + +* button +* button_exit, inherits from button +* checkbox +* scrollbar +* table +* textlist +* dropdown +* field +* pwdfield, inherits from field +* textarea +* label +* vertlabel, inherits from field +* image_button +* item_image_button, inherits from image_button +* tabheader + ### Valid Properties -* button and button_exit - * bgcolor - sets button tint - * textcolor +* button, button_exit + * bgcolor - color, sets button tint + * textcolor - color, default white + * border - boolean, draw border. Set to false to hide the bevelled button pane. Default true. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * bgimg - standard image. Defaults to none. + * bgimg_pressed - image when pressed. Defaults to bgimg when not provided. + * alpha - boolean, whether to draw alpha in bgimg. Default true. +* checkbox + * noclip - boolean, set to true to allow the element to exceed formspec bounds. +* scrollbar + * noclip - boolean, set to true to allow the element to exceed formspec bounds. +* table, textlist + * noclip - boolean, set to true to allow the element to exceed formspec bounds. +* dropdown + * noclip - boolean, set to true to allow the element to exceed formspec bounds. +* field, pwdfield, textarea + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * border - set to false to hide the textbox background and border. Default true. + * textcolor - color. Default white. +* label, vertlabel + * bgcolor - color. Default unset. + * textcolor - color. Default white. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * border - boolean, set to true to get a border. Default true. +* image_button, item_image_button + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * border - boolean, draw border. Set to false to hide the bevelled button pane. Default false. + * alpha - boolean, whether to draw alpha in bgimg. Default true. * tabheader - * bgcolor - tab background - * textcolor - - + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * textcolor - color. Default white. Inventory ========= diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index e56367afe..631616c06 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -55,17 +55,17 @@ with this program; if not, write to the Free Software Foundation, Inc., RenderingEngine *RenderingEngine::s_singleton = nullptr; -static gui::GUISkin* createSkin(gui::IGUIEnvironment *environment, - gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver) +static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment, + gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver) { - gui::GUISkin* skin = new gui::GUISkin(type, driver); + gui::GUISkin *skin = new gui::GUISkin(type, driver); - gui::IGUIFont* builtinfont = environment->getBuiltInFont(); - gui::IGUIFontBitmap* bitfont = 0; + gui::IGUIFont *builtinfont = environment->getBuiltInFont(); + gui::IGUIFontBitmap *bitfont = nullptr; if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP) bitfont = (gui::IGUIFontBitmap*)builtinfont; - gui::IGUISpriteBank* bank = 0; + gui::IGUISpriteBank *bank = 0; skin->setFont(builtinfont); if (bitfont) @@ -102,7 +102,7 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) u32 i; for (i = 0; i != drivers.size(); i++) { if (!strcasecmp(driverstring.c_str(), - RenderingEngine::getVideoDriverName(drivers[i]))) { + RenderingEngine::getVideoDriverName(drivers[i]))) { driverType = drivers[i]; break; } @@ -139,7 +139,7 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) s_singleton = this; auto skin = createSkin(m_device->getGUIEnvironment(), - gui::EGST_WINDOWS_METALLIC, driver); + gui::EGST_WINDOWS_METALLIC, driver); m_device->getGUIEnvironment()->setSkin(skin); skin->drop(); } diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index f81727e93..29aae0836 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -18,85 +18,118 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "irrlichttypes_extrabloated.h" +#include #pragma once - class StyleSpec { public: - enum Property { - NONE = 0, + enum Property + { TEXTCOLOR, BGCOLOR, - NUM_PROPERTIES + NOCLIP, + BORDER, + BGIMG, + BGIMG_PRESSED, + ALPHA, + NUM_PROPERTIES, + NONE }; private: - std::unordered_map properties; + std::array property_set; + std::array properties; public: - static Property GetPropertyByName(const std::string &name) { + static Property GetPropertyByName(const std::string &name) + { if (name == "textcolor") { return TEXTCOLOR; } else if (name == "bgcolor") { return BGCOLOR; + } else if (name == "noclip") { + return NOCLIP; + } else if (name == "border") { + return BORDER; + } else if (name == "bgimg") { + return BGIMG; + } else if (name == "bgimg_pressed") { + return BGIMG_PRESSED; + } else if (name == "alpha") { + return ALPHA; } else { return NONE; } } - std::string get(Property prop, std::string def) const { - auto it = properties.find(prop); - if (it == properties.end()) { + std::string get(Property prop, std::string def) const + { + const auto &val = properties[prop]; + return val.empty() ? def : val; + } + + void set(Property prop, const std::string &value) + { + properties[prop] = value; + property_set[prop] = true; + } + + video::SColor getColor(Property prop, video::SColor def) const + { + const auto &val = properties[prop]; + if (val.empty()) { return def; } - return it->second; - } - - void set(Property prop, std::string value) { - properties[prop] = std::move(value); - } - - video::SColor getColor(Property prop, video::SColor def) const { - auto it = properties.find(prop); - if (it == properties.end()) { - return def; - } - - parseColorString(it->second, def, false, 0xFF); + parseColorString(val, def, false, 0xFF); return def; } - video::SColor getColor(Property prop) const { - auto it = properties.find(prop); - FATAL_ERROR_IF(it == properties.end(), "Unexpected missing property"); + video::SColor getColor(Property prop) const + { + const auto &val = properties[prop]; + FATAL_ERROR_IF(val.empty(), "Unexpected missing property"); video::SColor color; - parseColorString(it->second, color, false, 0xFF); + parseColorString(val, color, false, 0xFF); return color; } - bool hasProperty(Property prop) const { - return properties.find(prop) != properties.end(); + bool getBool(Property prop, bool def) const + { + const auto &val = properties[prop]; + if (val.empty()) { + return def; + } + + return is_yes(val); } - StyleSpec &operator|=(const StyleSpec &other) { - for (size_t i = 1; i < NUM_PROPERTIES; i++) { + inline bool isNotDefault(Property prop) const + { + return !properties[prop].empty(); + } + + inline bool hasProperty(Property prop) const { return property_set[prop]; } + + StyleSpec &operator|=(const StyleSpec &other) + { + for (size_t i = 0; i < NUM_PROPERTIES; i++) { auto prop = (Property)i; if (other.hasProperty(prop)) { - properties[prop] = other.get(prop, ""); + set(prop, other.get(prop, "")); } } return *this; } - StyleSpec operator|(const StyleSpec &other) const { + StyleSpec operator|(const StyleSpec &other) const + { StyleSpec newspec = *this; newspec |= other; return newspec; } }; - diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 3bb654972..c9ae76750 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -500,6 +500,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this, spec.fid, spec.flabel.c_str()); + auto style = getStyleForElement("checkbox", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } @@ -556,6 +559,9 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen gui::IGUIScrollBar* e = Environment->addScrollBar(is_horizontal,rect,this,spec.fid); + auto style = getStyleForElement("scrollbar", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setMax(1000); e->setMin(0); e->setPos(stoi(parts[4])); @@ -702,15 +708,35 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); - auto style = getThemeForElement(type, name); - if (style.hasProperty(StyleSpec::BGCOLOR)) { + auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); + if (style.isNotDefault(StyleSpec::BGCOLOR)) { e->setColor(style.getColor(StyleSpec::BGCOLOR)); } - if (style.hasProperty(StyleSpec::TEXTCOLOR)) { + if (style.isNotDefault(StyleSpec::TEXTCOLOR)) { e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR)); } + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); -// e->setSprite(); + if (style.isNotDefault(StyleSpec::BGIMG)) { + std::string image_name = style.get(StyleSpec::BGIMG, ""); + std::string pressed_image_name = style.get(StyleSpec::BGIMG_PRESSED, ""); + + video::ITexture *texture = 0; + video::ITexture *pressed_texture = 0; + texture = m_tsrc->getTexture(image_name); + if (!pressed_image_name.empty()) + pressed_texture = m_tsrc->getTexture(pressed_image_name); + else + pressed_texture = texture; + + e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); + e->setImage(guiScalingImageButton( + Environment->getVideoDriver(), texture, geom.X, geom.Y)); + e->setPressedImage(guiScalingImageButton( + Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); + e->setScaleImage(true); + } if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -889,6 +915,9 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); + auto style = getStyleForElement("table", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + m_tables.emplace_back(spec, e); m_fields.push_back(spec); return; @@ -963,6 +992,9 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); + auto style = getStyleForElement("textlist", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + m_tables.emplace_back(spec, e); m_fields.push_back(spec); return; @@ -1035,6 +1067,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element if (!str_initial_selection.empty()) e->setSelected(stoi(str_initial_selection)-1); + auto style = getStyleForElement("dropdown", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + m_fields.push_back(spec); m_dropdowns.emplace_back(spec, std::vector()); @@ -1118,6 +1153,11 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element e->setPasswordBox(true,L'*'); + auto style = getStyleForElement("pwdfield", name, "field"); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + irr::SEvent evt; evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.Key = KEY_END; @@ -1194,6 +1234,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, evt.KeyInput.PressedDown = true; e->OnEvent(evt); } + + auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + if (style.get(StyleSpec::BGCOLOR, "") == "transparent") { + e->setDrawBackground(false); + } } if (!spec.flabel.empty()) { @@ -1407,6 +1455,15 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); + + auto style = getStyleForElement("label", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, false)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + if (style.isNotDefault(StyleSpec::BGCOLOR)) { + e->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); + } + m_fields.push_back(spec); } @@ -1475,9 +1532,18 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen L"", 258+m_fields.size() ); - gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(), + gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); - t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + + auto style = getStyleForElement("vertlabel", spec.fname, "label"); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, false)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + if (style.isNotDefault(StyleSpec::BGCOLOR)) { + e->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); + } + m_fields.push_back(spec); return; } @@ -1563,14 +1629,21 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem Environment->setFocus(e); } - e->setUseAlphaChannel(true); + auto style = getStyleForElement("image_button", spec.fname); + + e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); e->setImage(guiScalingImageButton( Environment->getVideoDriver(), texture, geom.X, geom.Y)); e->setPressedImage(guiScalingImageButton( Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); e->setScaleImage(true); - e->setNotClipped(noclip); - e->setDrawBorder(drawborder); + if (parts.size() >= 7) { + e->setNotClipped(noclip); + e->setDrawBorder(drawborder); + } else { + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + } m_fields.push_back(spec); return; @@ -1656,7 +1729,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen pos.Y+geom.Y); gui::IGUITabControl *e = Environment->addTabControl(rect, this, - false, show_border, spec.fid); + show_background, show_border, spec.fid); e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT); e->setTabHeight(geom.Y); @@ -1665,16 +1738,13 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen Environment->setFocus(e); } - e->setNotClipped(true); - - auto style = getThemeForElement("tabheader", name); + auto style = getStyleForElement("tabheader", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { auto tab = e->addTab(unescape_translate(unescape_string( utf8_to_wide(button))).c_str(), -1); - tab->setDrawBackground(false); - tab->setBackgroundColor(video::SColor(0xFFFF0000)); - if (style.hasProperty(StyleSpec::BGCOLOR)) + if (style.isNotDefault(StyleSpec::BGCOLOR)) tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1753,6 +1823,10 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L""); + auto style = getStyleForElement("item_image_button", spec.fname, "image_button"); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } @@ -2043,30 +2117,46 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b { std::vector parts = split(element, ';'); - if (parts.size() != 3) { + if (parts.size() < 2) { errorstream << "Invalid style element (" << parts.size() << "): '" << element << "'" << std::endl; return false; } std::string selector = trim(parts[0]); - std::string propname = trim(parts[1]); - std::string value = trim(parts[2]); - - StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname); - if (prop == StyleSpec::NONE) { - errorstream << "Invalid style element (Unknown property " << prop << "): '" << element + if (selector.empty()) { + errorstream << "Invalid style element (Selector required): '" << element << "'" << std::endl; return false; } StyleSpec spec; - spec.set(prop, value); - if (selector.empty()) { - errorstream << "Invalid style element (Selector required): '" << element - << "'" << std::endl; - return false; + for (size_t i = 1; i < parts.size(); i++) { + size_t equal_pos = parts[i].find('='); + if (equal_pos == std::string::npos) { + errorstream << "Invalid style element (Property missing value): '" << element + << "'" << std::endl; + return false; + } + + std::string propname = trim(parts[i].substr(0, equal_pos)); + std::string value = trim(unescape_string(parts[i].substr(equal_pos + 1))); + + std::transform(propname.begin(), propname.end(), propname.begin(), ::tolower); + + StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname); + if (prop == StyleSpec::NONE) { + if (property_warned.find(propname) != property_warned.end()) { + warningstream << "Invalid style element (Unknown property " << propname << "): '" + << element + << "'" << std::endl; + property_warned.insert(propname); + } + return false; + } + + spec.set(prop, value); } if (style_type) { @@ -4115,9 +4205,17 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id) return L""; } -StyleSpec GUIFormSpecMenu::getThemeForElement(const std::string &type, const std::string &name) { +StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type, + const std::string &name, const std::string &parent_type) { StyleSpec ret; + if (!parent_type.empty()) { + auto it = theme_by_type.find(parent_type); + if (it != theme_by_type.end()) { + ret |= it->second; + } + } + auto it = theme_by_type.find(type); if (it != theme_by_type.end()) { ret |= it->second; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index b310f8a77..e4dc66151 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #include "irrlichttypes_extrabloated.h" #include "inventorymanager.h" @@ -404,8 +405,10 @@ protected: std::unordered_map theme_by_type; std::unordered_map theme_by_name; + std::unordered_set property_warned; - StyleSpec getThemeForElement(const std::string &type, const std::string &name); + StyleSpec getStyleForElement(const std::string &type, + const std::string &name="", const std::string &parent_type=""); v2s32 padding; v2f32 spacing; @@ -574,7 +577,6 @@ private: * and the default value for the setting is true. */ bool m_remap_dbl_click; - }; class FormspecFormSource: public IFormSource