3 Copyright (C) 2019 rubenwardy
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "client/tile.h" // ITextureSource
22 #include "irrlichttypes_extrabloated.h"
23 #include "util/string.h"
35 BGCOLOR_HOVERED, // Note: Deprecated property
36 BGCOLOR_PRESSED, // Note: Deprecated property
40 BGIMG_HOVERED, // Note: Deprecated property
42 BGIMG_PRESSED, // Note: Deprecated property
44 FGIMG_HOVERED, // Note: Deprecated property
45 FGIMG_PRESSED, // Note: Deprecated property
55 STATE_HOVERED = 1 << 0,
56 STATE_PRESSED = 1 << 1,
58 STATE_INVALID = 1 << 3,
62 std::array<bool, NUM_PROPERTIES> property_set{};
63 std::array<std::string, NUM_PROPERTIES> properties;
64 State state_map = STATE_DEFAULT;
67 static Property GetPropertyByName(const std::string &name)
69 if (name == "textcolor") {
71 } else if (name == "bgcolor") {
73 } else if (name == "bgcolor_hovered") {
74 return BGCOLOR_HOVERED;
75 } else if (name == "bgcolor_pressed") {
76 return BGCOLOR_PRESSED;
77 } else if (name == "noclip") {
79 } else if (name == "border") {
81 } else if (name == "bgimg") {
83 } else if (name == "bgimg_hovered") {
85 } else if (name == "bgimg_middle") {
87 } else if (name == "bgimg_pressed") {
89 } else if (name == "fgimg") {
91 } else if (name == "fgimg_hovered") {
93 } else if (name == "fgimg_pressed") {
95 } else if (name == "alpha") {
97 } else if (name == "content_offset") {
98 return CONTENT_OFFSET;
99 } else if (name == "padding") {
106 std::string get(Property prop, std::string def) const
108 const auto &val = properties[prop];
109 return val.empty() ? def : val;
112 void set(Property prop, const std::string &value)
114 properties[prop] = value;
115 property_set[prop] = true;
118 //! Parses a name and returns the corresponding state enum
119 static State getStateByName(const std::string &name)
121 if (name == "default") {
122 return STATE_DEFAULT;
123 } else if (name == "hovered") {
124 return STATE_HOVERED;
125 } else if (name == "pressed") {
126 return STATE_PRESSED;
128 return STATE_INVALID;
132 //! Gets the state that this style is intended for
133 State getState() const
138 //! Set the given state on this style
139 void addState(State state)
141 FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received");
143 state_map = static_cast<State>(state_map | state);
146 //! Using a list of styles mapped to state values, calculate the final
147 // combined style for a state by propagating values in its component states
148 static StyleSpec getStyleFromStatePropagation(const std::array<StyleSpec, NUM_STATES> &styles, State state)
150 StyleSpec temp = styles[StyleSpec::STATE_DEFAULT];
151 temp.state_map = state;
152 for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) {
153 if ((state & i) != 0) {
154 temp = temp | styles[i];
161 video::SColor getColor(Property prop, video::SColor def) const
163 const auto &val = properties[prop];
168 parseColorString(val, def, false, 0xFF);
172 video::SColor getColor(Property prop) const
174 const auto &val = properties[prop];
175 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
178 parseColorString(val, color, false, 0xFF);
182 irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
184 const auto &val = properties[prop];
188 irr::core::rect<s32> rect;
189 if (!parseRect(val, &rect))
195 irr::core::rect<s32> getRect(Property prop) const
197 const auto &val = properties[prop];
198 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
200 irr::core::rect<s32> rect;
201 parseRect(val, &rect);
205 irr::core::vector2d<s32> getVector2i(Property prop, irr::core::vector2d<s32> def) const
207 const auto &val = properties[prop];
211 irr::core::vector2d<s32> vec;
212 if (!parseVector2i(val, &vec))
218 irr::core::vector2d<s32> getVector2i(Property prop) const
220 const auto &val = properties[prop];
221 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
223 irr::core::vector2d<s32> vec;
224 parseVector2i(val, &vec);
228 video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc,
229 video::ITexture *def) const
231 const auto &val = properties[prop];
236 video::ITexture *texture = tsrc->getTexture(val);
241 video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc) const
243 const auto &val = properties[prop];
244 FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
246 video::ITexture *texture = tsrc->getTexture(val);
251 bool getBool(Property prop, bool def) const
253 const auto &val = properties[prop];
261 inline bool isNotDefault(Property prop) const
263 return !properties[prop].empty();
266 inline bool hasProperty(Property prop) const { return property_set[prop]; }
268 StyleSpec &operator|=(const StyleSpec &other)
270 for (size_t i = 0; i < NUM_PROPERTIES; i++) {
271 auto prop = (Property)i;
272 if (other.hasProperty(prop)) {
273 set(prop, other.get(prop, ""));
280 StyleSpec operator|(const StyleSpec &other) const
282 StyleSpec newspec = *this;
288 bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
290 irr::core::rect<s32> rect;
291 std::vector<std::string> v_rect = split(value, ',');
293 if (v_rect.size() == 1) {
294 s32 x = stoi(v_rect[0]);
295 rect.UpperLeftCorner = irr::core::vector2di(x, x);
296 rect.LowerRightCorner = irr::core::vector2di(-x, -x);
297 } else if (v_rect.size() == 2) {
298 s32 x = stoi(v_rect[0]);
299 s32 y = stoi(v_rect[1]);
300 rect.UpperLeftCorner = irr::core::vector2di(x, y);
301 rect.LowerRightCorner = irr::core::vector2di(-x, -y);
302 // `-x` is interpreted as `w - x`
303 } else if (v_rect.size() == 4) {
304 rect.UpperLeftCorner = irr::core::vector2di(
305 stoi(v_rect[0]), stoi(v_rect[1]));
306 rect.LowerRightCorner = irr::core::vector2di(
307 stoi(v_rect[2]), stoi(v_rect[3]));
309 warningstream << "Invalid rectangle string format: \"" << value
310 << "\"" << std::endl;
319 bool parseVector2i(const std::string &value, irr::core::vector2d<s32> *parsed_vec) const
321 irr::core::vector2d<s32> vec;
322 std::vector<std::string> v_vector = split(value, ',');
324 if (v_vector.size() == 1) {
325 s32 x = stoi(v_vector[0]);
328 } else if (v_vector.size() == 2) {
329 s32 x = stoi(v_vector[0]);
330 s32 y = stoi(v_vector[1]);
334 warningstream << "Invalid vector2d string format: \"" << value
335 << "\"" << std::endl;