Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Formspecs: Add state-selection to style elements (#9378)
  • Loading branch information
Df458 committed Apr 11, 2020
1 parent ba3587e commit f780bae
Show file tree
Hide file tree
Showing 22 changed files with 454 additions and 317 deletions.
61 changes: 45 additions & 16 deletions doc/lua_api.txt
Expand Up @@ -2188,12 +2188,12 @@ Elements

* 9-sliced background. See https://en.wikipedia.org/wiki/9-slice_scaling
* Middle is a rect which defines the middle of the 9-slice.
* `x` - The middle will be x pixels from all sides.
* `x,y` - The middle will be x pixels from the horizontal and y from the vertical.
* `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values
will be added to the width and height of the texture, allowing it to be used as the
distance from the far end.
* All numbers in middle are integers.
* `x` - The middle will be x pixels from all sides.
* `x,y` - The middle will be x pixels from the horizontal and y from the vertical.
* `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values
will be added to the width and height of the texture, allowing it to be used as the
distance from the far end.
* All numbers in middle are integers.
* Example for formspec 8x4 in 16x resolution:
image shall be sized 8 times 16px times 4 times 16px
* If `auto_clip` is `true`, the background is clipped to the formspec size
Expand Down Expand Up @@ -2508,16 +2508,28 @@ Elements
* `span=<value>`: number of following columns to affect
(default: infinite).

### `style[<name 1>,<name 2>,...;<prop1>;<prop2>;...]`
### `style[<selector 1>,<selector 2>;<prop1>;<prop2>;...]`

* Set the style for the named element(s) `name`.
* Set the style for the element(s) matching `selector` by name.
* `selector` can be one of:
* `<name>` - An element name.
* `<name>:<state>` - An element name, a colon, and one or more states.
* `state` is a list of states separated by the `+` character.
* If a state is provided, the style will only take effect when the element is in that state.
* All provided states must be active for the style to apply.
* Note: this **must** be before the element is defined.
* See [Styling Formspecs].


### `style_type[<type 1>,<type 2>,...;<prop1>;<prop2>;...]`
### `style_type[<selector 1>,<selector 2>;<prop1>;<prop2>;...]`

* Sets the style for all elements of type(s) `type` which appear after this element.
* Set the style for the element(s) matching `selector` by type.
* `selector` can be one of:
* `<type>` - An element type.
* `<type>:<state>` - An element type, a colon, and one or more states.
* `state` is a list of states separated by the `+` character.
* If a state is provided, the style will only take effect when the element is in that state.
* All provided states must be active for the style to apply.
* See [Styling Formspecs].

Migrating to Real Coordinates
Expand Down Expand Up @@ -2560,23 +2572,32 @@ Styling Formspecs

Formspec elements can be themed using the style elements:

style[<name 1>,<name 2>,...;<prop1>;<prop2>;...]
style_type[<type 1>,<type 2>,...;<prop1>;<prop2>;...]
style[<name 1>,<name 2>;<prop1>;<prop2>;...]
style[<name 1>:<state>,<name 2>:<state>;<prop1>;<prop2>;...]
style_type[<type 1>,<type 2>;<prop1>;<prop2>;...]
style_type[<type 1>:<state>,<type 2>:<state>;<prop1>;<prop2>;...]

Where a prop is:

property_name=property_value

For example:

style_type[button;bgcolor=#006699]
style[world_delete;bgcolor=red;textcolor=yellow]
button[4,3.95;2.6,1;world_delete;Delete]

A name/type can optionally be a comma separated list of names/types, like so:

world_delete,world_create,world_configure
button,image_button

For example:
Any name/type in the list can also be accompanied by a `+`-separated list of states, like so:

style_type[button;bgcolor=#006699]
style[world_delete;bgcolor=red;textcolor=yellow]
button[4,3.95;2.6,1;world_delete;Delete]
world_delete:hovered+pressed
button:pressed

States allow you to apply styles in response to changes in the element, instead of applying at all times.

Setting a property to nothing will reset it to the default value. For example:

Expand Down Expand Up @@ -2654,6 +2675,14 @@ Some types may inherit styles from parent types.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* textcolor - color. Default white.

### Valid States

* *all elements*
* default - Equivalent to providing no states
* button, button_exit, image_button, item_image_button
* hovered - Active when the mouse is hovering over the element
* pressed - Active when the button is pressed

Markup Language
---------------

Expand Down
5 changes: 4 additions & 1 deletion games/minimal/mods/test/formspec.lua
Expand Up @@ -65,7 +65,10 @@ local style_fs = [[
style[one_btn13;border=false]
item_image_button[1.25,8.35;1,1;default:sword_steel;one_btn13;NoBor]
style[one_btn14;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png;fgimg=bubble.png;fgimg_hovered=default_apple.png;fgimg_pressed=heart.png]
style[one_btn14;border=false;bgimg=test_bg.png;fgimg=bubble.png]
style[one_btn14:hovered;bgimg=test_bg_hovered.png;fgimg=default_apple.png;textcolor=red]
style[one_btn14:pressed;bgimg=test_bg_pressed.png;fgimg=heart.png;textcolor=green]
style[one_btn14:hovered+pressed;textcolor=blue]
image_button[0,9.6;1,1;bubble.png;one_btn14;Bg]
style[one_btn15;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png]
Expand Down
9 changes: 5 additions & 4 deletions src/client/game.cpp
Expand Up @@ -1556,7 +1556,8 @@ bool Game::connectToServer(const std::string &playername,
} else {
registration_confirmation_shown = true;
(new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
&g_menumgr, client, playername, password, connection_aborted))->drop();
&g_menumgr, client, playername, password,
connection_aborted, texture_src))->drop();
}
} else {
wait_time += dtime;
Expand Down Expand Up @@ -1711,19 +1712,19 @@ inline bool Game::handleCallbacks()

if (g_gamecallback->changepassword_requested) {
(new GUIPasswordChange(guienv, guiroot, -1,
&g_menumgr, client))->drop();
&g_menumgr, client, texture_src))->drop();
g_gamecallback->changepassword_requested = false;
}

if (g_gamecallback->changevolume_requested) {
(new GUIVolumeChange(guienv, guiroot, -1,
&g_menumgr))->drop();
&g_menumgr, texture_src))->drop();
g_gamecallback->changevolume_requested = false;
}

if (g_gamecallback->keyconfig_requested) {
(new GUIKeyChangeMenu(guienv, guiroot, -1,
&g_menumgr))->drop();
&g_menumgr, texture_src))->drop();
g_gamecallback->keyconfig_requested = false;
}

Expand Down
65 changes: 59 additions & 6 deletions src/gui/StyleSpec.h
Expand Up @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/

#include "client/tile.h" // ITextureSource
#include "debug.h"
#include "irrlichttypes_extrabloated.h"
#include "util/string.h"
#include <array>
Expand All @@ -31,25 +32,34 @@ class StyleSpec
{
TEXTCOLOR,
BGCOLOR,
BGCOLOR_HOVERED,
BGCOLOR_PRESSED,
BGCOLOR_HOVERED, // Note: Deprecated property
BGCOLOR_PRESSED, // Note: Deprecated property
NOCLIP,
BORDER,
BGIMG,
BGIMG_HOVERED,
BGIMG_HOVERED, // Note: Deprecated property
BGIMG_MIDDLE,
BGIMG_PRESSED,
BGIMG_PRESSED, // Note: Deprecated property
FGIMG,
FGIMG_HOVERED,
FGIMG_PRESSED,
FGIMG_HOVERED, // Note: Deprecated property
FGIMG_PRESSED, // Note: Deprecated property
ALPHA,
NUM_PROPERTIES,
NONE
};
enum State
{
STATE_DEFAULT = 0,
STATE_HOVERED = 1 << 0,
STATE_PRESSED = 1 << 1,
NUM_STATES = 1 << 2,
STATE_INVALID = 1 << 3,
};

private:
std::array<bool, NUM_PROPERTIES> property_set{};
std::array<std::string, NUM_PROPERTIES> properties;
State state_map = STATE_DEFAULT;

public:
static Property GetPropertyByName(const std::string &name)
Expand Down Expand Up @@ -99,6 +109,49 @@ class StyleSpec
property_set[prop] = true;
}

//! Parses a name and returns the corresponding state enum
static State getStateByName(const std::string &name)
{
if (name == "default") {
return STATE_DEFAULT;
} else if (name == "hovered") {
return STATE_HOVERED;
} else if (name == "pressed") {
return STATE_PRESSED;
} else {
return STATE_INVALID;
}
}

//! Gets the state that this style is intended for
State getState() const
{
return state_map;
}

//! Set the given state on this style
void addState(State state)
{
FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received");

state_map = static_cast<State>(state_map | state);
}

//! Using a list of styles mapped to state values, calculate the final
// combined style for a state by propagating values in its component states
static StyleSpec getStyleFromStatePropagation(const std::array<StyleSpec, NUM_STATES> &styles, State state)
{
StyleSpec temp = styles[StyleSpec::STATE_DEFAULT];
temp.state_map = state;
for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) {
if ((state & i) != 0) {
temp = temp | styles[i];
}
}

return temp;
}

video::SColor getColor(Property prop, video::SColor def) const
{
const auto &val = properties[prop];
Expand Down

0 comments on commit f780bae

Please sign in to comment.