Add styles to most elements

This commit is contained in:
rubenwardy 2019-03-16 21:38:36 +00:00
parent ec3795a55c
commit 9541165752
9 changed files with 288 additions and 104 deletions

@ -177,6 +177,7 @@ LOCAL_SRC_FILES := \
jni/src/filesys.cpp \ jni/src/filesys.cpp \
jni/src/genericobject.cpp \ jni/src/genericobject.cpp \
jni/src/gettext.cpp \ jni/src/gettext.cpp \
jni/src/gui/guiButton.cpp \
jni/src/gui/guiChatConsole.cpp \ jni/src/gui/guiChatConsole.cpp \
jni/src/gui/guiConfirmRegistration.cpp \ jni/src/gui/guiConfirmRegistration.cpp \
jni/src/gui/guiEditBoxWithScrollbar.cpp \ jni/src/gui/guiEditBoxWithScrollbar.cpp \

@ -22,7 +22,7 @@ local function delete_content_formspec(dialogdata)
"size[11.5,4.5,true]" .. "size[11.5,4.5,true]" ..
"label[2,2;" .. "label[2,2;" ..
fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name) .. "]".. 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[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") .. "]" "button[5.75,3.5;2.5,0.5;dlg_delete_content_cancel;" .. fgettext("Cancel") .. "]"

@ -21,7 +21,7 @@ local function delete_world_formspec(dialogdata)
"size[10,2.5,true]" .. "size[10,2.5,true]" ..
"label[0.5,0.5;" .. "label[0.5,0.5;" ..
fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" .. 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[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") .. "]" "button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]"
return retval return retval

@ -102,9 +102,6 @@ local function get_formspec(tabview, name, tabdata)
) )
retval = retval .. 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[4,3.95;2.6,1;world_delete;".. fgettext("Delete") .. "]" ..
"button[6.5,3.95;2.8,1;world_configure;".. fgettext("Configure") .. "]" .. "button[6.5,3.95;2.8,1;world_configure;".. fgettext("Configure") .. "]" ..
"button[9.2,3.95;2.5,1;world_create;".. fgettext("New") .. "]" .. "button[9.2,3.95;2.5,1;world_create;".. fgettext("New") .. "]" ..

@ -1884,7 +1884,10 @@ When displaying text which can contain formspec code, e.g. text set by a player,
use `minetest.formspec_escape`. use `minetest.formspec_escape`.
For coloured text you can use `minetest.colorize`. 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 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 appearing when you don't expect them to, or why things are styled differently
to normal. See [`no_prepend[]`] and [Styling Formspecs]. to normal. See [`no_prepend[]`] and [Styling Formspecs].
@ -2351,22 +2354,17 @@ Elements
* `span=<value>`: number of following columns to affect * `span=<value>`: number of following columns to affect
(default: infinite). (default: infinite).
**Note**: do _not_ use a element name starting with `key_`; those names are ### `style[<name>;<prop1>;<prop2>;...]`
reserved to pass key press events to formspec!
### `style[<name>;<propery>;<value]` * Set the style for the named element `name`.
* Note: this **must** be before the element is defined.
Set the style for the named element `name`. * See [Styling Formspecs].
Note: this **must** be before the element's tag.
See [Styling Formspecs].
### `style_type[<type>;<propery>;<value>]` ### `style_type[<type>;<prop1>;<prop2>;...]`
Sets the style for all elements of type `type` which appear after this tag. * Sets the style for all elements of type `type` which appear after this element.
* See [Styling Formspecs].
See [Styling Formspecs].
Migrating to Real Coordinates Migrating to Real Coordinates
----------------------------- -----------------------------
@ -2406,27 +2404,82 @@ offsets when migrating:
Styling Formspecs 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[<name>;<prop1>;<prop2>;...]
style_type[ELEMENT_TYPE;PROPERTY;VALUE] style_type[<type>;<prop1>;<prop2>;...]
Where a prop is:
property_name=property_value
For example: For example:
style_type[button;bgcolor;#006699] style_type[button;bgcolor=#006699]
style[world_delete;bgcolor;#ff0000] style[world_delete;bgcolor=red;textcolor=yellow]
button[4,3.95;2.6,1;world_delete;Delete] 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 ### Valid Properties
* button and button_exit * button, button_exit
* bgcolor - sets button tint * bgcolor - color, sets button tint
* textcolor * 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 * tabheader
* bgcolor - tab background * noclip - boolean, set to true to allow the element to exceed formspec bounds.
* textcolor * textcolor - color. Default white.
Inventory Inventory
========= =========

@ -55,17 +55,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
RenderingEngine *RenderingEngine::s_singleton = nullptr; RenderingEngine *RenderingEngine::s_singleton = nullptr;
static gui::GUISkin* createSkin(gui::IGUIEnvironment *environment, static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment,
gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver) 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::IGUIFont *builtinfont = environment->getBuiltInFont();
gui::IGUIFontBitmap* bitfont = 0; gui::IGUIFontBitmap *bitfont = nullptr;
if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP) if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP)
bitfont = (gui::IGUIFontBitmap*)builtinfont; bitfont = (gui::IGUIFontBitmap*)builtinfont;
gui::IGUISpriteBank* bank = 0; gui::IGUISpriteBank *bank = 0;
skin->setFont(builtinfont); skin->setFont(builtinfont);
if (bitfont) if (bitfont)

@ -18,85 +18,118 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include <array>
#pragma once #pragma once
class StyleSpec class StyleSpec
{ {
public: public:
enum Property { enum Property
NONE = 0, {
TEXTCOLOR, TEXTCOLOR,
BGCOLOR, BGCOLOR,
NUM_PROPERTIES NOCLIP,
BORDER,
BGIMG,
BGIMG_PRESSED,
ALPHA,
NUM_PROPERTIES,
NONE
}; };
private: private:
std::unordered_map<Property, std::string> properties; std::array<bool, NUM_PROPERTIES> property_set;
std::array<std::string, NUM_PROPERTIES> properties;
public: public:
static Property GetPropertyByName(const std::string &name) { static Property GetPropertyByName(const std::string &name)
{
if (name == "textcolor") { if (name == "textcolor") {
return TEXTCOLOR; return TEXTCOLOR;
} else if (name == "bgcolor") { } else if (name == "bgcolor") {
return 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 { } else {
return NONE; return NONE;
} }
} }
std::string get(Property prop, std::string def) const { std::string get(Property prop, std::string def) const
auto it = properties.find(prop); {
if (it == properties.end()) { 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 def;
} }
return it->second; parseColorString(val, def, false, 0xFF);
}
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; return def;
} }
parseColorString(it->second, def, false, 0xFF); video::SColor getColor(Property prop) const
return def; {
} const auto &val = properties[prop];
FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
video::SColor getColor(Property prop) const {
auto it = properties.find(prop);
FATAL_ERROR_IF(it == properties.end(), "Unexpected missing property");
video::SColor color; video::SColor color;
parseColorString(it->second, color, false, 0xFF); parseColorString(val, color, false, 0xFF);
return color; return color;
} }
bool hasProperty(Property prop) const { bool getBool(Property prop, bool def) const
return properties.find(prop) != properties.end(); {
const auto &val = properties[prop];
if (val.empty()) {
return def;
} }
StyleSpec &operator|=(const StyleSpec &other) { return is_yes(val);
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; auto prop = (Property)i;
if (other.hasProperty(prop)) { if (other.hasProperty(prop)) {
properties[prop] = other.get(prop, ""); set(prop, other.get(prop, ""));
} }
} }
return *this; return *this;
} }
StyleSpec operator|(const StyleSpec &other) const { StyleSpec operator|(const StyleSpec &other) const
{
StyleSpec newspec = *this; StyleSpec newspec = *this;
newspec |= other; newspec |= other;
return newspec; return newspec;
} }
}; };

@ -500,6 +500,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this, gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
spec.fid, spec.flabel.c_str()); spec.fid, spec.flabel.c_str());
auto style = getStyleForElement("checkbox", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
if (spec.fname == data->focused_fieldname) { if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e); Environment->setFocus(e);
} }
@ -556,6 +559,9 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
gui::IGUIScrollBar* e = gui::IGUIScrollBar* e =
Environment->addScrollBar(is_horizontal,rect,this,spec.fid); Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
auto style = getStyleForElement("scrollbar", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
e->setMax(1000); e->setMax(1000);
e->setMin(0); e->setMin(0);
e->setPos(stoi(parts[4])); 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()); GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
auto style = getThemeForElement(type, name); auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
if (style.hasProperty(StyleSpec::BGCOLOR)) { if (style.isNotDefault(StyleSpec::BGCOLOR)) {
e->setColor(style.getColor(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->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) { if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e); 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") if (!str_initial_selection.empty() && str_initial_selection != "0")
e->setSelected(stoi(str_initial_selection)); 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_tables.emplace_back(spec, e);
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
@ -963,6 +992,9 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
if (!str_initial_selection.empty() && str_initial_selection != "0") if (!str_initial_selection.empty() && str_initial_selection != "0")
e->setSelected(stoi(str_initial_selection)); 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_tables.emplace_back(spec, e);
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
@ -1035,6 +1067,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
if (!str_initial_selection.empty()) if (!str_initial_selection.empty())
e->setSelected(stoi(str_initial_selection)-1); 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_fields.push_back(spec);
m_dropdowns.emplace_back(spec, std::vector<std::string>()); 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'*'); 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; irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT; evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END; evt.KeyInput.Key = KEY_END;
@ -1194,6 +1234,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
evt.KeyInput.PressedDown = true; evt.KeyInput.PressedDown = true;
e->OnEvent(evt); 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()) { if (!spec.flabel.empty()) {
@ -1407,6 +1455,15 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
gui::IGUIStaticText *e = gui::StaticText::add(Environment, gui::IGUIStaticText *e = gui::StaticText::add(Environment,
spec.flabel.c_str(), rect, false, false, this, spec.fid); spec.flabel.c_str(), rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); 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); m_fields.push_back(spec);
} }
@ -1475,9 +1532,18 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
L"", L"",
258+m_fields.size() 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); 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); m_fields.push_back(spec);
return; return;
} }
@ -1563,14 +1629,21 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
Environment->setFocus(e); Environment->setFocus(e);
} }
e->setUseAlphaChannel(true); auto style = getStyleForElement("image_button", spec.fname);
e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
e->setImage(guiScalingImageButton( e->setImage(guiScalingImageButton(
Environment->getVideoDriver(), texture, geom.X, geom.Y)); Environment->getVideoDriver(), texture, geom.X, geom.Y));
e->setPressedImage(guiScalingImageButton( e->setPressedImage(guiScalingImageButton(
Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
e->setScaleImage(true); e->setScaleImage(true);
if (parts.size() >= 7) {
e->setNotClipped(noclip); e->setNotClipped(noclip);
e->setDrawBorder(drawborder); e->setDrawBorder(drawborder);
} else {
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
}
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
@ -1656,7 +1729,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
pos.Y+geom.Y); pos.Y+geom.Y);
gui::IGUITabControl *e = Environment->addTabControl(rect, this, 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, e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT); irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
e->setTabHeight(geom.Y); e->setTabHeight(geom.Y);
@ -1665,16 +1738,13 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
Environment->setFocus(e); Environment->setFocus(e);
} }
e->setNotClipped(true); auto style = getStyleForElement("tabheader", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
auto style = getThemeForElement("tabheader", name);
for (const std::string &button : buttons) { for (const std::string &button : buttons) {
auto tab = e->addTab(unescape_translate(unescape_string( auto tab = e->addTab(unescape_translate(unescape_string(
utf8_to_wide(button))).c_str(), -1); utf8_to_wide(button))).c_str(), -1);
tab->setDrawBackground(false); if (style.isNotDefault(StyleSpec::BGCOLOR))
tab->setBackgroundColor(video::SColor(0xFFFF0000));
if (style.hasProperty(StyleSpec::BGCOLOR))
tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR));
tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); 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""); 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) { if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e); Environment->setFocus(e);
} }
@ -2043,32 +2117,48 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b
{ {
std::vector<std::string> parts = split(element, ';'); std::vector<std::string> parts = split(element, ';');
if (parts.size() != 3) { if (parts.size() < 2) {
errorstream << "Invalid style element (" << parts.size() << "): '" << element errorstream << "Invalid style element (" << parts.size() << "): '" << element
<< "'" << std::endl; << "'" << std::endl;
return false; return false;
} }
std::string selector = trim(parts[0]); std::string selector = trim(parts[0]);
std::string propname = trim(parts[1]); if (selector.empty()) {
std::string value = trim(parts[2]); errorstream << "Invalid style element (Selector required): '" << element
StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname);
if (prop == StyleSpec::NONE) {
errorstream << "Invalid style element (Unknown property " << prop << "): '" << element
<< "'" << std::endl; << "'" << std::endl;
return false; return false;
} }
StyleSpec spec; StyleSpec spec;
spec.set(prop, value);
if (selector.empty()) { for (size_t i = 1; i < parts.size(); i++) {
errorstream << "Invalid style element (Selector required): '" << element size_t equal_pos = parts[i].find('=');
if (equal_pos == std::string::npos) {
errorstream << "Invalid style element (Property missing value): '" << element
<< "'" << std::endl; << "'" << std::endl;
return false; 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) { if (style_type) {
theme_by_type[selector] |= spec; theme_by_type[selector] |= spec;
} else { } else {
@ -4115,9 +4205,17 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
return L""; 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; 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); auto it = theme_by_type.find(type);
if (it != theme_by_type.end()) { if (it != theme_by_type.end()) {
ret |= it->second; ret |= it->second;

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <utility> #include <utility>
#include <stack> #include <stack>
#include <unordered_set>
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "inventorymanager.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_type;
std::unordered_map<std::string, StyleSpec> theme_by_name; 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; v2s32 padding;
v2f32 spacing; v2f32 spacing;
@ -574,7 +577,6 @@ private:
* and the default value for the setting is true. * and the default value for the setting is true.
*/ */
bool m_remap_dbl_click; bool m_remap_dbl_click;
}; };
class FormspecFormSource: public IFormSource class FormspecFormSource: public IFormSource