1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00
This commit is contained in:
SmallJoker 2025-06-27 17:28:16 +08:00 committed by GitHub
commit 23aba644cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 286 additions and 48 deletions

View file

@ -3653,6 +3653,8 @@ Some types may inherit styles from parent types.
* `+<number>`/`-<number>`: Offsets default font size by `number` points. * `+<number>`/`-<number>`: Offsets default font size by `number` points.
* `*<number>`: Multiplies default font size by `number`, similar to CSS `em`. * `*<number>`: Multiplies default font size by `number`, similar to CSS `em`.
* border - boolean, draw border. Set to false to hide the bevelled button pane. Default true. * border - boolean, draw border. Set to false to hide the bevelled button pane. Default true.
* border_img - string, a texture that overrides the border style.
* border_img_middle - rect, to render `border_img` in 9-sliced mode. Refer to `background9[...]`.
* content_offset - 2d vector, shifts the position of the button's content without resizing it. * content_offset - 2d vector, shifts the position of the button's content without resizing it.
* noclip - boolean, set to true to allow the element to exceed formspec bounds. * noclip - boolean, set to true to allow the element to exceed formspec bounds.
* padding - rect, adds space between the edges of the button and the content. This value is * padding - rect, adds space between the edges of the button and the content. This value is

View file

@ -38,6 +38,10 @@ A key-value config file with the following keys:
* `textdomain`: Textdomain used to translate title and description. * `textdomain`: Textdomain used to translate title and description.
Defaults to the texture pack name. Defaults to the texture pack name.
See [Translating content meta](lua_api.md#translating-content-meta). See [Translating content meta](lua_api.md#translating-content-meta).
* `formspec_theme`: optional. Formspec elements that define the defaut style.
* Allowed elements: `bgcolor`, `style_type`
* `formspec_version_theme`: optional. Indicates the minimal [formspec version](lua_api.md)
needed to properly display `formspec_theme`. On older clients, this suppresses warnings.
### `description.txt` ### `description.txt`
**Deprecated**, you should use texture_pack.conf instead. **Deprecated**, you should use texture_pack.conf instead.

View file

@ -28,6 +28,8 @@
#endif #endif
#include "os.h" #include "os.h"
irr::gui::IGUISkin *impl_create_irr_guiskin(irr::video::IVideoDriver *driver);
namespace irr namespace irr
{ {
namespace gui namespace gui
@ -590,7 +592,7 @@ If you no longer need the skin, you should call IGUISkin::drop().
See IReferenceCounted::drop() for more information. */ See IReferenceCounted::drop() for more information. */
IGUISkin *CGUIEnvironment::createSkin() IGUISkin *CGUIEnvironment::createSkin()
{ {
IGUISkin *skin = new CGUISkin(Driver); IGUISkin *skin = impl_create_irr_guiskin(Driver);
IGUIFont *builtinfont = getBuiltInFont(); IGUIFont *builtinfont = getBuiltInFont();
IGUIFontBitmap *bitfont = 0; IGUIFontBitmap *bitfont = 0;

View file

@ -291,7 +291,7 @@ namespace gui
//! gets the colors //! gets the colors
virtual void getColors(video::SColor* colors); // ::PATCH: virtual void getColors(video::SColor* colors); // ::PATCH:
private: protected:
float Scale = 1.0f; float Scale = 1.0f;
video::SColor Colors[EGDC_COUNT]; video::SColor Colors[EGDC_COUNT];

View file

@ -14,6 +14,7 @@
#include "inputhandler.h" #include "inputhandler.h"
#include "profiler.h" #include "profiler.h"
#include "gui/guiEngine.h" #include "gui/guiEngine.h"
#include "gui/guiSkin.h"
#include "fontengine.h" #include "fontengine.h"
#include "clientlauncher.h" #include "clientlauncher.h"
#include "version.h" #include "version.h"
@ -338,6 +339,11 @@ static video::ITexture *loadTexture(video::IVideoDriver *driver, const char *pat
return texture; return texture;
} }
gui::IGUISkin *impl_create_irr_guiskin(video::IVideoDriver *driver)
{
return new GUISkin(driver);
}
void ClientLauncher::config_guienv() void ClientLauncher::config_guienv()
{ {
gui::IGUISkin *skin = guienv->getSkin(); gui::IGUISkin *skin = guienv->getSkin();

View file

@ -19,6 +19,7 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp

View file

@ -13,6 +13,9 @@
#include <array> #include <array>
#include <vector> #include <vector>
class StyleSpec;
using StyleSpecMap = std::unordered_map<std::string, std::vector<StyleSpec>>;
class StyleSpec class StyleSpec
{ {
@ -59,6 +62,8 @@ public:
STATE_INVALID = 1 << 4, STATE_INVALID = 1 << 4,
}; };
using StateMap = std::array<StyleSpec, NUM_STATES>;
private: private:
std::array<bool, NUM_PROPERTIES> property_set{}; std::array<bool, NUM_PROPERTIES> property_set{};
std::array<std::string, NUM_PROPERTIES> properties; std::array<std::string, NUM_PROPERTIES> properties;
@ -380,12 +385,24 @@ public:
StyleSpec &operator|=(const StyleSpec &other) StyleSpec &operator|=(const StyleSpec &other)
{ {
u32 props_set = 0;
static_assert(sizeof(props_set) * 8 > NUM_PROPERTIES);
for (size_t i = 0; i < NUM_PROPERTIES; i++) { 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)) {
props_set |= (1 << i);
set(prop, other.get(prop, "")); set(prop, other.get(prop, ""));
} }
} }
if ((props_set & (1 << FGIMG | 1 << FGIMG_MIDDLE)) == (1 << FGIMG)) {
// Image was specified without 9-slice. Reset to non-9-slice.
set(FGIMG_MIDDLE, "");
}
if ((props_set & (1 << BGIMG | 1 << BGIMG_MIDDLE)) == (1 << BGIMG)) {
// Image was specified without 9-slice. Reset to non-9-slice.
set(BGIMG_MIDDLE, "");
}
return *this; return *this;
} }

View file

@ -303,13 +303,17 @@ void GUIButton::draw()
// PATCH // PATCH
video::ITexture* texture = ButtonImages[(u32)imageState].Texture; video::ITexture* texture = ButtonImages[(u32)imageState].Texture;
// FIXME: Vertices can only be darkened because [0, 255] is normalized to [0, 1]
// For reference: irr/src/OpenGL/Driver.cpp -> `vt2DImage`
video::SColor image_colors[] = { BgColor, BgColor, BgColor, BgColor }; video::SColor image_colors[] = { BgColor, BgColor, BgColor, BgColor };
if (BgMiddle.getArea() == 0) { if (BgMiddle.getArea() == 0) {
// Regular image button
driver->draw2DImage(texture, driver->draw2DImage(texture,
ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()), ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
sourceRect, &AbsoluteClippingRect, sourceRect, &AbsoluteClippingRect,
image_colors, UseAlphaChannel); image_colors, UseAlphaChannel);
} else { } else {
// This is generally used to replace the default border style
draw2DImage9Slice(driver, texture, draw2DImage9Slice(driver, texture,
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()), ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
sourceRect, BgMiddle, &AbsoluteClippingRect, image_colors); sourceRect, BgMiddle, &AbsoluteClippingRect, image_colors);

View file

@ -8,6 +8,7 @@
#include "client/guiscalingfilter.h" #include "client/guiscalingfilter.h"
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "client/shader.h" #include "client/shader.h"
#include "client/texturepaths.h"
#include "client/tile.h" #include "client/tile.h"
#include "clientdynamicinfo.h" #include "clientdynamicinfo.h"
#include "config.h" #include "config.h"
@ -43,14 +44,18 @@ void TextDestGuiEngine::gotText(const StringMap &fields)
} }
/******************************************************************************/ /******************************************************************************/
MenuTextureSource::MenuTextureSource(video::IVideoDriver* driver) :
m_driver(driver)
{
g_settings->registerChangedCallback("texture_path", onTxpSettingChanged, this);
}
MenuTextureSource::~MenuTextureSource() MenuTextureSource::~MenuTextureSource()
{ {
u32 before = m_driver->getTextureCount(); g_settings->deregisterAllChangedCallbacks(this);
for (const auto &it: m_to_delete) { u32 before = m_driver->getTextureCount();
m_driver->removeTexture(it); cleanupTextures();
}
m_to_delete.clear();
infostream << "~MenuTextureSource() before cleanup: "<< before infostream << "~MenuTextureSource() before cleanup: "<< before
<< " after: " << m_driver->getTextureCount() << std::endl; << " after: " << m_driver->getTextureCount() << std::endl;
@ -70,8 +75,14 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
if (retval) if (retval)
return retval; return retval;
verbosestream << "MenuTextureSource: loading " << name << std::endl; // Try to find the texture in the active texture pack
video::IImage *image = m_driver->createImageFromFile(name.c_str()); std::string path;
if (!fs::IsPathAbsolute(name))
path = getTexturePath(name, nullptr);
const char *filepath = path.empty() ? name.c_str() : path.c_str();
verbosestream << "MenuTextureSource: loading " << filepath << std::endl;
video::IImage *image = m_driver->createImageFromFile(filepath);
if (!image) if (!image)
return NULL; return NULL;
@ -83,6 +94,20 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
return retval; return retval;
} }
void MenuTextureSource::cleanupTextures()
{
for (const auto &it: m_to_delete) {
m_driver->removeTexture(it);
}
m_to_delete.clear();
}
void MenuTextureSource::onTxpSettingChanged(const std::string &name, void *data)
{
((MenuTextureSource *)data)->cleanupTextures();
clearTextureNameCache();
}
/******************************************************************************/ /******************************************************************************/
/** MenuMusicFetcher */ /** MenuMusicFetcher */
/******************************************************************************/ /******************************************************************************/

View file

@ -76,7 +76,7 @@ public:
* default constructor * default constructor
* @param driver the video driver to load textures from * @param driver the video driver to load textures from
*/ */
MenuTextureSource(video::IVideoDriver *driver) : m_driver(driver) {}; MenuTextureSource(video::IVideoDriver *driver);
/** /**
* destructor, removes all loaded textures * destructor, removes all loaded textures
@ -91,6 +91,12 @@ public:
video::ITexture *getTexture(const std::string &name, u32 *id = NULL); video::ITexture *getTexture(const std::string &name, u32 *id = NULL);
private: private:
/** Unloads all textures in `m_to_delete` */
void cleanupTextures();
/** Update the texture cache */
static void onTxpSettingChanged(const std::string &name, void *data);
/** driver to get textures from */ /** driver to get textures from */
video::IVideoDriver *m_driver = nullptr; video::IVideoDriver *m_driver = nullptr;
/** set of textures to delete */ /** set of textures to delete */

View file

@ -56,6 +56,7 @@
#include "guiScrollContainer.h" #include "guiScrollContainer.h"
#include "guiHyperText.h" #include "guiHyperText.h"
#include "guiScene.h" #include "guiScene.h"
#include "guiSkin.h"
#define MY_CHECKPOS(a,b) \ #define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \ if (v_pos.size() != 2) { \
@ -114,10 +115,15 @@ GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay"); m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname"); m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname");
g_settings->registerChangedCallback("texture_path", onTxpSettingChanged, this);
setThemeFromSettings();
} }
GUIFormSpecMenu::~GUIFormSpecMenu() GUIFormSpecMenu::~GUIFormSpecMenu()
{ {
g_settings->deregisterAllChangedCallbacks(this);
removeAll(); removeAll();
delete m_selected_item; delete m_selected_item;
@ -2618,15 +2624,9 @@ void GUIFormSpecMenu::parsePadding(parserData *data, const std::string &element)
<< "'" << std::endl; << "'" << std::endl;
} }
void GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element) void GUIFormSpecMenu::parse_style_to_map(StyleSpecMap &out, const std::string &element,
std::unordered_set<std::string> *prop_warned)
{ {
if (data->type != "style" && data->type != "style_type") {
errorstream << "Invalid style element type: '" << data->type << "'" << std::endl;
return;
}
bool style_type = (data->type == "style_type");
std::vector<std::string> parts = split(element, ';'); std::vector<std::string> parts = split(element, ';');
if (parts.size() < 2) { if (parts.size() < 2) {
@ -2653,11 +2653,11 @@ void GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element)
StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname); StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname);
if (prop == StyleSpec::NONE) { if (prop == StyleSpec::NONE) {
if (property_warned.find(propname) != property_warned.end()) { if (prop_warned && prop_warned->find(propname) != prop_warned->end()) {
warningstream << "Invalid style element (Unknown property " << propname << "): '" warningstream << "Invalid style element (Unknown property " << propname << "): '"
<< element << element
<< "'" << std::endl; << "'" << std::endl;
property_warned.insert(propname); prop_warned->insert(propname);
} }
continue; continue;
} }
@ -2705,11 +2705,7 @@ void GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element)
continue; continue;
} }
if (style_type) { out[selector].push_back(selector_spec);
theme_by_type[selector].push_back(selector_spec);
} else {
theme_by_name[selector].push_back(selector_spec);
}
// Backwards-compatibility for existing _hovered/_pressed properties // Backwards-compatibility for existing _hovered/_pressed properties
if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED) if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED)
@ -2728,11 +2724,7 @@ void GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element)
hover_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_HOVERED, "")); hover_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_HOVERED, ""));
} }
if (style_type) { out[selector].push_back(hover_spec);
theme_by_type[selector].push_back(hover_spec);
} else {
theme_by_name[selector].push_back(hover_spec);
}
} }
if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED) if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED)
|| selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED) || selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED)
@ -2750,15 +2742,31 @@ void GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element)
press_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_PRESSED, "")); press_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_PRESSED, ""));
} }
if (style_type) { out[selector].push_back(press_spec);
theme_by_type[selector].push_back(press_spec);
} else {
theme_by_name[selector].push_back(press_spec);
}
} }
} }
}
return; void GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element)
{
if (data->type != "style" && data->type != "style_type") {
errorstream << "Invalid style element type: '" << data->type << "'" << std::endl;
return;
}
bool style_type = (data->type == "style_type");
bool do_warn = m_formspec_version <= FORMSPEC_API_VERSION;
StyleSpecMap *map = style_type ? &theme_by_type : &theme_by_name;
if (data->reading_theme) {
GUISkin *skin = (GUISkin *)Environment->getSkin();
map = &skin->getThemeRef();
}
parse_style_to_map(
*map,
element,
do_warn ? &property_warned : nullptr
);
} }
void GUIFormSpecMenu::parseSetFocus(parserData*, const std::string &element) void GUIFormSpecMenu::parseSetFocus(parserData*, const std::string &element)
@ -2898,8 +2906,8 @@ void GUIFormSpecMenu::removeAll()
scroll_container_it.second->drop(); scroll_container_it.second->drop();
} }
const std::unordered_map<std::string, std::function<void(GUIFormSpecMenu*, GUIFormSpecMenu::parserData *data, const std::unordered_map<std::string, GUIFormSpecMenu::parser_function_t>
const std::string &description)>> GUIFormSpecMenu::element_parsers = { GUIFormSpecMenu::element_parsers = {
{"container", &GUIFormSpecMenu::parseContainer}, {"container", &GUIFormSpecMenu::parseContainer},
{"container_end", &GUIFormSpecMenu::parseContainerEnd}, {"container_end", &GUIFormSpecMenu::parseContainerEnd},
{"list", &GUIFormSpecMenu::parseList}, {"list", &GUIFormSpecMenu::parseList},
@ -2948,14 +2956,20 @@ const std::unordered_map<std::string, std::function<void(GUIFormSpecMenu*, GUIFo
{"allow_close", &GUIFormSpecMenu::parseAllowClose}, {"allow_close", &GUIFormSpecMenu::parseAllowClose},
}; };
// Formspec elements allowed for themes
const std::unordered_map<std::string, GUIFormSpecMenu::parser_function_t>
GUIFormSpecMenu::element_parsers_theme = {
{"bgcolor", &GUIFormSpecMenu::parseBackgroundColor},
{"style_type", &GUIFormSpecMenu::parseStyle},
};
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) void GUIFormSpecMenu::parseElement(parserData *data, const std::string &element, bool is_theme)
{ {
//some prechecks //some prechecks
if (element.empty()) if (element.empty())
return; return;
if (parseVersionDirect(element)) if (!is_theme && parseVersionDirect(element))
return; return;
size_t pos = element.find('['); size_t pos = element.find('[');
@ -2968,8 +2982,9 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
// They remain here due to bool flags, for now // They remain here due to bool flags, for now
data->type = type; data->type = type;
auto it = element_parsers.find(type); auto &parser_lut = is_theme ? element_parsers_theme : element_parsers;
if (it != element_parsers.end()) { auto it = parser_lut.find(type);
if (it != parser_lut.end()) {
it->second(this, data, description); it->second(this, data, description);
return; return;
} }
@ -5054,6 +5069,40 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
return L""; return L"";
} }
void GUIFormSpecMenu::setThemeFromSettings()
{
GUISkin *skin = (GUISkin *)Environment->getSkin();
skin->getThemeRef().clear();
skin->setTextureSource(m_tsrc);
const std::string settingspath = g_settings->get("texture_path") + DIR_DELIM + "texture_pack.conf";
Settings settings;
if (!settings.readConfigFile(settingspath.c_str()))
return;
if (!settings.exists("formspec_theme"))
return;
settings.getU16NoEx("formspec_version_theme", m_theme_formspec_version);
auto theme_elements = split(settings.get("formspec_theme"), ']');
parserData mydata;
mydata.reading_theme = true;
const u16 version_backup = m_formspec_version;
m_formspec_version = m_theme_formspec_version;
for (const std::string &element : theme_elements)
parseElement(&mydata, element, true);
m_formspec_version = version_backup;
}
void GUIFormSpecMenu::onTxpSettingChanged(const std::string &name, void *data)
{
GUIFormSpecMenu *me = (GUIFormSpecMenu *)data;
me->setThemeFromSettings();
me->regenerateGui(me->m_screensize_old);
}
StyleSpec GUIFormSpecMenu::getDefaultStyleForElement(const std::string &type, StyleSpec GUIFormSpecMenu::getDefaultStyleForElement(const std::string &type,
const std::string &name, const std::string &parent_type) { const std::string &name, const std::string &parent_type) {
return getStyleForElement(type, name, parent_type)[StyleSpec::STATE_DEFAULT]; return getStyleForElement(type, name, parent_type)[StyleSpec::STATE_DEFAULT];

View file

@ -302,10 +302,14 @@ protected:
bool precheckElement(const std::string &name, const std::string &element, bool precheckElement(const std::string &name, const std::string &element,
size_t args_min, size_t args_max, std::vector<std::string> &parts); size_t args_min, size_t args_max, std::vector<std::string> &parts);
std::unordered_map<std::string, std::vector<StyleSpec>> theme_by_type; StyleSpecMap theme_by_type, theme_by_name;
std::unordered_map<std::string, std::vector<StyleSpec>> theme_by_name;
std::unordered_set<std::string> property_warned; std::unordered_set<std::string> property_warned;
// Texturepack-definied formspec theming support
u16 m_theme_formspec_version;
void setThemeFromSettings();
static void onTxpSettingChanged(const std::string &name, void *data);
StyleSpec getDefaultStyleForElement(const std::string &type, StyleSpec getDefaultStyleForElement(const std::string &type,
const std::string &name="", const std::string &parent_type=""); const std::string &name="", const std::string &parent_type="");
std::array<StyleSpec, StyleSpec::NUM_STATES> getStyleForElement(const std::string &type, std::array<StyleSpec, StyleSpec::NUM_STATES> getStyleForElement(const std::string &type,
@ -387,8 +391,9 @@ private:
bool m_show_debug = false; bool m_show_debug = false;
struct parserData { struct parserData {
bool explicit_size; bool explicit_size = false;
bool real_coordinates; bool real_coordinates = false;
bool reading_theme = false;
u8 simple_field_count; u8 simple_field_count;
v2f invsize; v2f invsize;
v2s32 size; v2s32 size;
@ -419,7 +424,9 @@ private:
std::string type; std::string type;
}; };
static const std::unordered_map<std::string, std::function<void(GUIFormSpecMenu*, GUIFormSpecMenu::parserData *data, const std::string &description)>> element_parsers; using parser_function_t = std::function<void(GUIFormSpecMenu*, parserData *, const std::string &)>;
static const std::unordered_map<std::string, parser_function_t>
element_parsers, element_parsers_theme;
struct fs_key_pending { struct fs_key_pending {
bool key_up; bool key_up;
@ -433,7 +440,7 @@ private:
void removeAll(); void removeAll();
void parseElement(parserData* data, const std::string &element); void parseElement(parserData* data, const std::string &element, bool is_theme = false);
void parseSize(parserData* data, const std::string &element); void parseSize(parserData* data, const std::string &element);
void parseContainer(parserData* data, const std::string &element); void parseContainer(parserData* data, const std::string &element);
@ -483,6 +490,8 @@ private:
void parseAnchor(parserData *data, const std::string &element); void parseAnchor(parserData *data, const std::string &element);
bool parsePaddingDirect(parserData *data, const std::string &element); bool parsePaddingDirect(parserData *data, const std::string &element);
void parsePadding(parserData *data, const std::string &element); void parsePadding(parserData *data, const std::string &element);
static void parse_style_to_map(StyleSpecMap &out, const std::string &element,
std::unordered_set<std::string> *prop_warned);
void parseStyle(parserData *data, const std::string &element); void parseStyle(parserData *data, const std::string &element);
void parseSetFocus(parserData *, const std::string &element); void parseSetFocus(parserData *, const std::string &element);
void parseModel(parserData *data, const std::string &element); void parseModel(parserData *data, const std::string &element);

76
src/gui/guiSkin.cpp Normal file
View file

@ -0,0 +1,76 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 Krock/SmallJoker <mk939@ymail.com>
#include "guiSkin.h"
#include "client/guiscalingfilter.h"
GUISkin::GUISkin(video::IVideoDriver *driver) : gui::CGUISkin(driver)
{
}
GUISkin::~GUISkin()
{
}
void GUISkin::drawColored3DButtonPanePressed(gui::IGUIElement *element,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
const video::SColor *colors)
{
if (!Driver)
return;
if (tryDrawPane("_skin_button", StyleSpec::STATE_PRESSED, rect, clip))
return;
gui::CGUISkin::drawColored3DButtonPanePressed(element, rect, clip, colors);
}
void GUISkin::drawColored3DButtonPaneStandard(gui::IGUIElement *element,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
const video::SColor *colors)
{
if (!Driver)
return;
if (tryDrawPane("_skin_button", StyleSpec::STATE_DEFAULT, rect, clip))
return;
gui::CGUISkin::drawColored3DButtonPaneStandard(element, rect, clip, colors);
}
bool GUISkin::tryDrawPane(const char *type, StyleSpec::State state,
const core::rect<s32> &rect,
const core::rect<s32> *clip)
{
auto it = m_theme.find(type);
if (it == m_theme.end())
return false;
video::SColor c = 0xFFFFFFFF;
video::SColor image_colors[] = { c, c, c, c };
// Similar to GUIFormSpecMenu::getStyleForElement
StyleSpec::StateMap states;
for (const StyleSpec &spec : it->second)
states[(u32)spec.getState()] |= spec;
StyleSpec style = StyleSpec::getStyleFromStatePropagation(states, state);
video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, m_texture_source);
core::recti source_rect = core::rect<s32>(core::position2di(0,0), texture->getOriginalSize());
core::recti bg_middle = style.getRect(StyleSpec::BGIMG_MIDDLE, core::recti());
if (bg_middle.getArea() == 0) {
Driver->draw2DImage(texture, rect, source_rect, clip, image_colors, true);
} else {
draw2DImage9Slice(Driver, texture, rect, source_rect, bg_middle, clip, image_colors);
}
return true;
}

37
src/gui/guiSkin.h Normal file
View file

@ -0,0 +1,37 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 Krock/SmallJoker <mk939@ymail.com>
#pragma once
#include "StyleSpec.h" // StyleSpecMap
#include "../../irr/src/CGUISkin.h"
class GUISkin : public gui::CGUISkin {
public:
GUISkin(video::IVideoDriver *driver);
virtual ~GUISkin();
void setTextureSource(ISimpleTextureSource *src) { m_texture_source = src; }
virtual void drawColored3DButtonPaneStandard(gui::IGUIElement *element,
const core::rect<s32> &rect,
const core::rect<s32> *clip = 0,
const video::SColor *colors = 0) override;
virtual void drawColored3DButtonPanePressed(gui::IGUIElement *element,
const core::rect<s32> &rect,
const core::rect<s32> *clip = 0,
const video::SColor *colors = 0) override;
StyleSpecMap &getThemeRef() { return m_theme; }
private:
bool tryDrawPane(const char *type, StyleSpec::State state,
const core::rect<s32> &rect,
const core::rect<s32> *clip = 0);
ISimpleTextureSource *m_texture_source = nullptr;
StyleSpecMap m_theme;
};