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 \
"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") .. "]"
"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
)
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") .. "]" ..
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].
* `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
-----------------------------
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
=========
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)
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;
}
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();
}
*/
#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;
}
};
-
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);
}
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]));
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);
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;
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;
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>());
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;
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()) {
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);
}
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;
}
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;
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);
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)));
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);
}
{
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) {
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;
#include <utility>
#include <stack>
+#include <unordered_set>
#include "irrlichttypes_extrabloated.h"
#include "inventorymanager.h"
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;
* and the default value for the setting is true.
*/
bool m_remap_dbl_click;
-
};
class FormspecFormSource: public IFormSource