Add styles to most elements
authorrubenwardy <rw@rubenwardy.com>
Sat, 16 Mar 2019 21:38:36 +0000 (21:38 +0000)
committerrubenwardy <rw@rubenwardy.com>
Sat, 3 Aug 2019 18:36:30 +0000 (19:36 +0100)
build/android/jni/Android.mk
builtin/mainmenu/dlg_delete_content.lua
builtin/mainmenu/dlg_delete_world.lua
builtin/mainmenu/tab_local.lua
doc/lua_api.txt
src/client/renderingengine.cpp
src/gui/StyleSpec.h
src/gui/guiFormSpecMenu.cpp
src/gui/guiFormSpecMenu.h

index 75d20ab95a532e117ba9b3053ce0819a301c8c2b..ade2bb9c507cde3f299bc9181e5ef8f8f0531d26 100644 (file)
@@ -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   \
index a8da2efc75fe157eb43298ea59101cda18209e1b..a24171541e3659e8b134a99a661ec1c670e7be73 100644 (file)
@@ -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") .. "]"
 
index 0039353503d550d4c054f7cf65d67f1ceaa3edce..33e7bc94548918a4ee6cb879150de5f2eda068ac 100644 (file)
@@ -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
index 512b4f844facf524b88cc439846fe11b0ad2db49..15ef96dc855ff18aa5ac3da2957cc1e8719154f5 100644 (file)
@@ -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") .. "]" ..
index bcc304584af7f8847d1fbaf177752dbe7498a567..7efdc836ee7a2ffccb3f526272f55b983c92ba68 100644 (file)
@@ -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=<value>`: 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[<name>;<propery>;<value]`
+### `style[<name>;<prop1>;<prop2>;...]`
 
-Set the style for the named element `name`.
-Note: this **must** be before the element's tag.
+* Set the style for the named element `name`.
+* Note: this **must** be before the element is defined.
+* See [Styling Formspecs].
 
-See [Styling Formspecs].
 
+### `style_type[<type>;<prop1>;<prop2>;...]`
 
-### `style_type[<type>;<propery>;<value>]`
-
-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[<name>;<prop1>;<prop2>;...]
+    style_type[<type>;<prop1>;<prop2>;...]
+
+Where a prop is:
 
-       style[ELEMENT_NAME;PROPERTY;VALUE]
-       style_type[ELEMENT_TYPE;PROPERTY;VALUE]
+    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]
 
-### Valid Properties
+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
 
-* button and button_exit
-       * bgcolor - sets button tint
-       * textcolor
+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
-       * bgcolor - tab background
-       * textcolor
 
 
+### Valid Properties
+
+* 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
+    * noclip - boolean, set to true to allow the element to exceed formspec bounds.
+    * textcolor - color. Default white.
 
 Inventory
 =========
index e56367afee8c7150a8237b4758d548f6be7e7b83..631616c06fc09b6e4d34053d3cb50724fe2b5cb3 100644 (file)
@@ -55,17 +55,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 RenderingEngine *RenderingEngine::s_singleton = nullptr;
 
 
-static gui::GUISkincreateSkin(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::GUISkinskin = new gui::GUISkin(type, driver);
+       gui::GUISkin *skin = new gui::GUISkin(type, driver);
 
-       gui::IGUIFontbuiltinfont = 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::IGUISpriteBankbank = 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();
 }
index f81727e93ecfc9c3a227c1846922c580fc957644..29aae083647ee3dc12fe93f4535efb130cee8917 100644 (file)
@@ -18,85 +18,118 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "irrlichttypes_extrabloated.h"
+#include <array>
 
 #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<Property, std::string> properties;
+       std::array<bool, NUM_PROPERTIES> property_set;
+       std::array<std::string, NUM_PROPERTIES> 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()) {
-                       return def;
-               }
-
-               return it->second;
+       std::string get(Property prop, std::string def) const
+       {
+               const auto &val = properties[prop];
+               return val.empty() ? def : val;
        }
 
-       void set(Property prop, std::string value) {
-               properties[prop] = std::move(value);
+       void set(Property prop, const std::string &value)
+       {
+               properties[prop] = value;
+               property_set[prop] = true;
        }
 
-       video::SColor getColor(Property prop, video::SColor def) const {
-               auto it = properties.find(prop);
-               if (it == properties.end()) {
+       video::SColor getColor(Property prop, video::SColor def) const
+       {
+               const auto &val = properties[prop];
+               if (val.empty()) {
                        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);
+       }
+
+       inline bool isNotDefault(Property prop) const
+       {
+               return !properties[prop].empty();
        }
 
-       StyleSpec &operator|=(const StyleSpec &other) {
-               for (size_t i = 1; i < NUM_PROPERTIES; i++) {
+       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;
        }
 };
-
index 3bb654972bef4b50daa6e650e4d86fff6f6e5465..c9ae7675051e1c08ed6b302e0014e9b6405da8d8 100644 (file)
@@ -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->setSprite();
+               e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+               e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+
+               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<std::string>());
@@ -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<std::string> 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;
index b310f8a773cc8a175971f84a454bd21c18614972..e4dc6615199d3ce5fa36e1fa0c1f0d76bef51788 100644 (file)
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <utility>
 #include <stack>
+#include <unordered_set>
 
 #include "irrlichttypes_extrabloated.h"
 #include "inventorymanager.h"
@@ -404,8 +405,10 @@ protected:
 
        std::unordered_map<std::string, StyleSpec> theme_by_type;
        std::unordered_map<std::string, StyleSpec> theme_by_name;
+       std::unordered_set<std::string> 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