123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- /*
- Minetest
- Copyright (C) 2019 rubenwardy
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "client/tile.h" // ITextureSource
- #include "client/fontengine.h"
- #include "debug.h"
- #include "irrlichttypes_extrabloated.h"
- #include "util/string.h"
- #include <algorithm>
- #include <array>
- #include <vector>
- #pragma once
- class StyleSpec
- {
- public:
- enum Property
- {
- TEXTCOLOR,
- BGCOLOR,
- BGCOLOR_HOVERED, // Note: Deprecated property
- BGCOLOR_PRESSED, // Note: Deprecated property
- NOCLIP,
- BORDER,
- BGIMG,
- BGIMG_HOVERED, // Note: Deprecated property
- BGIMG_MIDDLE,
- BGIMG_PRESSED, // Note: Deprecated property
- FGIMG,
- FGIMG_HOVERED, // Note: Deprecated property
- FGIMG_MIDDLE,
- FGIMG_PRESSED, // Note: Deprecated property
- ALPHA,
- CONTENT_OFFSET,
- PADDING,
- FONT,
- FONT_SIZE,
- COLORS,
- BORDERCOLORS,
- BORDERWIDTHS,
- SOUND,
- SPACING,
- SIZE,
- 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)
- {
- if (name == "textcolor") {
- return TEXTCOLOR;
- } else if (name == "bgcolor") {
- return BGCOLOR;
- } else if (name == "bgcolor_hovered") {
- return BGCOLOR_HOVERED;
- } else if (name == "bgcolor_pressed") {
- return BGCOLOR_PRESSED;
- } else if (name == "noclip") {
- return NOCLIP;
- } else if (name == "border") {
- return BORDER;
- } else if (name == "bgimg") {
- return BGIMG;
- } else if (name == "bgimg_hovered") {
- return BGIMG_HOVERED;
- } else if (name == "bgimg_middle") {
- return BGIMG_MIDDLE;
- } else if (name == "bgimg_pressed") {
- return BGIMG_PRESSED;
- } else if (name == "fgimg") {
- return FGIMG;
- } else if (name == "fgimg_hovered") {
- return FGIMG_HOVERED;
- } else if (name == "fgimg_middle") {
- return FGIMG_MIDDLE;
- } else if (name == "fgimg_pressed") {
- return FGIMG_PRESSED;
- } else if (name == "alpha") {
- return ALPHA;
- } else if (name == "content_offset") {
- return CONTENT_OFFSET;
- } else if (name == "padding") {
- return PADDING;
- } else if (name == "font") {
- return FONT;
- } else if (name == "font_size") {
- return FONT_SIZE;
- } else if (name == "colors") {
- return COLORS;
- } else if (name == "bordercolors") {
- return BORDERCOLORS;
- } else if (name == "borderwidths") {
- return BORDERWIDTHS;
- } else if (name == "sound") {
- return SOUND;
- } else if (name == "spacing") {
- return SPACING;
- } else if (name == "size") {
- return SIZE;
- } else {
- return NONE;
- }
- }
- std::string get(Property prop, std::string def) const
- {
- 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;
- }
- //! 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];
- if (val.empty()) {
- return def;
- }
- parseColorString(val, def, false, 0xFF);
- return def;
- }
- video::SColor getColor(Property prop) const
- {
- const auto &val = properties[prop];
- FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
- video::SColor color;
- parseColorString(val, color, false, 0xFF);
- return color;
- }
- std::array<video::SColor, 4> getColorArray(Property prop,
- std::array<video::SColor, 4> def) const
- {
- const auto &val = properties[prop];
- if (val.empty())
- return def;
- std::vector<std::string> strs;
- if (!parseArray(val, strs))
- return def;
- for (size_t i = 0; i <= 3; i++) {
- video::SColor color;
- if (parseColorString(strs[i], color, false, 0xff))
- def[i] = color;
- }
- return def;
- }
- std::array<s32, 4> getIntArray(Property prop, std::array<s32, 4> def) const
- {
- const auto &val = properties[prop];
- if (val.empty())
- return def;
- std::vector<std::string> strs;
- if (!parseArray(val, strs))
- return def;
- for (size_t i = 0; i <= 3; i++)
- def[i] = stoi(strs[i]);
- return def;
- }
- irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
- {
- const auto &val = properties[prop];
- if (val.empty())
- return def;
- irr::core::rect<s32> rect;
- if (!parseRect(val, &rect))
- return def;
- return rect;
- }
- irr::core::rect<s32> getRect(Property prop) const
- {
- const auto &val = properties[prop];
- FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
- irr::core::rect<s32> rect;
- parseRect(val, &rect);
- return rect;
- }
- v2f32 getVector2f(Property prop, v2f32 def) const
- {
- const auto &val = properties[prop];
- if (val.empty())
- return def;
- v2f32 vec;
- if (!parseVector2f(val, &vec))
- return def;
- return vec;
- }
- v2s32 getVector2i(Property prop, v2s32 def) const
- {
- const auto &val = properties[prop];
- if (val.empty())
- return def;
- v2f32 vec;
- if (!parseVector2f(val, &vec))
- return def;
- return v2s32(vec.X, vec.Y);
- }
- v2s32 getVector2i(Property prop) const
- {
- const auto &val = properties[prop];
- FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
- v2f32 vec;
- parseVector2f(val, &vec);
- return v2s32(vec.X, vec.Y);
- }
- gui::IGUIFont *getFont() const
- {
- FontSpec spec(FONT_SIZE_UNSPECIFIED, FM_Standard, false, false);
- const std::string &font = properties[FONT];
- const std::string &size = properties[FONT_SIZE];
- if (font.empty() && size.empty())
- return nullptr;
- std::vector<std::string> modes = split(font, ',');
- for (size_t i = 0; i < modes.size(); i++) {
- if (modes[i] == "normal")
- spec.mode = FM_Standard;
- else if (modes[i] == "mono")
- spec.mode = FM_Mono;
- else if (modes[i] == "bold")
- spec.bold = true;
- else if (modes[i] == "italic")
- spec.italic = true;
- }
- if (!size.empty()) {
- int calc_size = 1;
- if (size[0] == '*') {
- std::string new_size = size.substr(1); // Remove '*' (invalid for stof)
- calc_size = stof(new_size) * g_fontengine->getFontSize(spec.mode);
- } else if (size[0] == '+' || size[0] == '-') {
- calc_size = stoi(size) + g_fontengine->getFontSize(spec.mode);
- } else {
- calc_size = stoi(size);
- }
- spec.size = (unsigned)std::min(std::max(calc_size, 1), 999);
- }
- return g_fontengine->getFont(spec);
- }
- video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc,
- video::ITexture *def) const
- {
- const auto &val = properties[prop];
- if (val.empty()) {
- return def;
- }
- video::ITexture *texture = tsrc->getTexture(val);
- return texture;
- }
- video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc) const
- {
- const auto &val = properties[prop];
- FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
- video::ITexture *texture = tsrc->getTexture(val);
- return texture;
- }
- 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();
- }
- 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)) {
- set(prop, other.get(prop, ""));
- }
- }
- return *this;
- }
- StyleSpec operator|(const StyleSpec &other) const
- {
- StyleSpec newspec = *this;
- newspec |= other;
- return newspec;
- }
- private:
- bool parseArray(const std::string &value, std::vector<std::string> &arr) const
- {
- std::vector<std::string> strs = split(value, ',');
- if (strs.size() == 1) {
- arr = {strs[0], strs[0], strs[0], strs[0]};
- } else if (strs.size() == 2) {
- arr = {strs[0], strs[1], strs[0], strs[1]};
- } else if (strs.size() == 4) {
- arr = strs;
- } else {
- warningstream << "Invalid array size (" << strs.size()
- << " arguments): \"" << value << "\"" << std::endl;
- return false;
- }
- return true;
- }
- bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
- {
- irr::core::rect<s32> rect;
- std::vector<std::string> v_rect = split(value, ',');
- if (v_rect.size() == 1) {
- s32 x = stoi(v_rect[0]);
- rect.UpperLeftCorner = irr::core::vector2di(x, x);
- rect.LowerRightCorner = irr::core::vector2di(-x, -x);
- } else if (v_rect.size() == 2) {
- s32 x = stoi(v_rect[0]);
- s32 y = stoi(v_rect[1]);
- rect.UpperLeftCorner = irr::core::vector2di(x, y);
- rect.LowerRightCorner = irr::core::vector2di(-x, -y);
- // `-x` is interpreted as `w - x`
- } else if (v_rect.size() == 4) {
- rect.UpperLeftCorner = irr::core::vector2di(
- stoi(v_rect[0]), stoi(v_rect[1]));
- rect.LowerRightCorner = irr::core::vector2di(
- stoi(v_rect[2]), stoi(v_rect[3]));
- } else {
- warningstream << "Invalid rectangle string format: \"" << value
- << "\"" << std::endl;
- return false;
- }
- *parsed_rect = rect;
- return true;
- }
- bool parseVector2f(const std::string &value, v2f32 *parsed_vec) const
- {
- v2f32 vec;
- std::vector<std::string> v_vector = split(value, ',');
- if (v_vector.size() == 1) {
- f32 x = stof(v_vector[0]);
- vec.X = x;
- vec.Y = x;
- } else if (v_vector.size() == 2) {
- vec.X = stof(v_vector[0]);
- vec.Y = stof(v_vector[1]);
- } else {
- warningstream << "Invalid 2d vector string format: \"" << value
- << "\"" << std::endl;
- return false;
- }
- *parsed_vec = vec;
- return true;
- }
- };
|