diff --git a/recompui/src/base/ui_game_option.cpp b/recompui/src/base/ui_game_option.cpp index 13f3f5c..804d57d 100644 --- a/recompui/src/base/ui_game_option.cpp +++ b/recompui/src/base/ui_game_option.cpp @@ -63,6 +63,13 @@ namespace recompui { } void GameOption::process_event(const Event &e) { + if (event_callback != nullptr) { + bool cancel = event_callback(e); + if (cancel) { + return; + } + } + switch (e.type) { case EventType::Click: if (is_enabled()) { @@ -116,4 +123,8 @@ namespace recompui { void GameOption::set_callback(std::function new_callback) { callback = new_callback; } + + void GameOption::set_event_callback(std::function new_callback) { + event_callback = new_callback; + } } diff --git a/recompui/src/base/ui_game_option.h b/recompui/src/base/ui_game_option.h index cfaab26..aa3dfd6 100644 --- a/recompui/src/base/ui_game_option.h +++ b/recompui/src/base/ui_game_option.h @@ -10,6 +10,7 @@ namespace recompui { private: std::string title; std::function callback; + std::function event_callback = nullptr; protected: Label *label; void set_styles(); @@ -19,6 +20,7 @@ namespace recompui { GameOption(ResourceId rid, Element* parent, const std::string& title, std::function callback, GameOptionsMenuLayout layout); void set_title(const std::string& new_title); void set_callback(std::function new_callback); + void set_event_callback(std::function new_callback); Label* get_label() { return label; } // These are public so you can modify them. Style hover_style; diff --git a/recompui/src/elements/ui_style.cpp b/recompui/src/elements/ui_style.cpp index 99c8e61..963956a 100644 --- a/recompui/src/elements/ui_style.cpp +++ b/recompui/src/elements/ui_style.cpp @@ -795,6 +795,135 @@ namespace recompui { set_property(Rml::PropertyId::Focus, focusable ? Rml::Style::Focus::Auto : Rml::Style::Focus::None); } + const std::unordered_map Style::decorator_type_names = { + {Style::DecoratorType::TiledHorizontal , "tiled-horizontal"}, + {Style::DecoratorType::TiledVertical , "tiled-vertical"}, + {Style::DecoratorType::TiledBox , "tiled-box"}, + {Style::DecoratorType::Image , "image"}, + {Style::DecoratorType::NinePatch , "ninepatch"}, + {Style::DecoratorType::Gradient , "gradient"}, + {Style::DecoratorType::HorizontalGradient , "horizontal-gradient"}, + {Style::DecoratorType::VerticalGradient , "vertical-gradient"}, + {Style::DecoratorType::Shader , "shader"}, + {Style::DecoratorType::LinearGradient , "linear-gradient"}, + {Style::DecoratorType::RepeatingLinearGradient , "repeating-linear-gradient"}, + {Style::DecoratorType::RadialGradient , "radial-gradient"}, + {Style::DecoratorType::RepeatingRadialGradient , "repeating-radial-gradient"}, + {Style::DecoratorType::ConicGradient , "conic-gradient"}, + {Style::DecoratorType::RepeatingConicGradient , "repeating-conic-gradient"}, + }; + + Rml::DecoratorsPtr Style::get_existing_decorators() { + if (property_map.find(Rml::PropertyId::Decorator) != property_map.end()) { + auto cur_decorators = property_map[Rml::PropertyId::Decorator].Get(); + if (cur_decorators != nullptr) { + return cur_decorators; + } + } + + return nullptr; + } + + Rml::DecoratorInstancer* Style::get_decorator_instancer(DecoratorType decorator_type) { + std::string decorator_type_name = decorator_type_names.at(decorator_type); + return Rml::Factory::GetDecoratorInstancer(decorator_type_name); + } + + void Style::set_decorator(DecoratorType decorator_type, Rml::PropertyDictionary properties) { + Rml::DecoratorDeclarationList decorators; + Rml::DecoratorsPtr existing_decorator = get_existing_decorators(); + bool found_existing = false; + std::string decorator_type_name = decorator_type_names.at(decorator_type); + + // BoxArea::Padding is rmlui default but we default containers to Border, so matching for now. + Rml::BoxArea paint_area = Rml::BoxArea::Border; + + Rml::DecoratorInstancer* instancer = Rml::Factory::GetDecoratorInstancer(decorator_type_name); + + if (existing_decorator != nullptr) { + auto& existing_decorators = existing_decorator->list; + for (int i = 0; i < existing_decorators.size(); i++) { + if (existing_decorators[i].type == decorator_type_name) { + decorators.list.emplace_back(Rml::DecoratorDeclaration{ + existing_decorators[i].type, + existing_decorators[i].instancer, + std::move(properties), + paint_area + }); + found_existing = true; + } else { + decorators.list.emplace_back(existing_decorators[i]); + } + } + } + + if (!found_existing) { + const Rml::PropertySpecification& specification = instancer->GetPropertySpecification(); + specification.SetPropertyDefaults(properties); + decorators.list.emplace_back(Rml::DecoratorDeclaration{ + decorator_type_name, + instancer, + std::move(properties), + paint_area + }); + } + + set_property( + Rml::PropertyId::Decorator, + Rml::Property( + Rml::Variant(Rml::MakeShared((std::move(decorators)))), + Rml::Unit::DECORATOR + ) + ); + } + + Rml::PropertyDictionary Style::create_decorator_properties( + Style::DecoratorType decorator_type, + std::initializer_list> props_to_set, + bool set_defaults + ) { + Rml::PropertyDictionary properties; + Rml::DecoratorInstancer* instancer = get_decorator_instancer(decorator_type); + const Rml::PropertySpecification &prop_spec = instancer->GetPropertySpecification(); + for (const auto& prop : props_to_set) { + properties.SetProperty(prop_spec.GetProperty(prop.first)->GetId(), prop.second); + } + if (set_defaults) { + prop_spec.SetPropertyDefaults(properties); + } + return properties; + } + + void Style::set_decorator_horizontal_gradient(const Color &color_left, const Color &color_right) { + Rml::PropertyDictionary properties = create_decorator_properties(DecoratorType::HorizontalGradient, { + { "start-color", color_left.to_rml_property() }, + { "stop-color", color_right.to_rml_property() } + }); + + set_decorator(DecoratorType::HorizontalGradient, properties); + } + void Style::set_decorator_horizontal_gradient(recompui::theme::color color_left, recompui::theme::color color_right, int opacity_left, int opacity_right) { + set_decorator_horizontal_gradient( + get_theme_color_with_opacity(color_left, opacity_left), + get_theme_color_with_opacity(color_right, opacity_right) + ); + } + + void Style::set_decorator_vertical_gradient(const Color &color_top, const Color &color_bottom) { + Rml::PropertyDictionary properties = create_decorator_properties(DecoratorType::VerticalGradient, { + { "start-color", color_top.to_rml_property() }, + { "stop-color", color_bottom.to_rml_property() } + }); + + set_decorator(DecoratorType::VerticalGradient, properties); + } + void Style::set_decorator_vertical_gradient(recompui::theme::color color_top, recompui::theme::color color_bottom, int opacity_left, int opacity_right) { + set_decorator_vertical_gradient( + get_theme_color_with_opacity(color_top, opacity_left), + get_theme_color_with_opacity(color_bottom, opacity_right) + ); + } + Rml::TransformPtr Style::get_existing_transform() { if (property_map.find(Rml::PropertyId::Transform) != property_map.end()) { auto curTransform = property_map[Rml::PropertyId::Transform].Get(); diff --git a/recompui/src/elements/ui_style.h b/recompui/src/elements/ui_style.h index 3fe8e04..9da228e 100644 --- a/recompui/src/elements/ui_style.h +++ b/recompui/src/elements/ui_style.h @@ -18,6 +18,50 @@ namespace recompui { private: std::map property_map; Rml::TransformPtr get_existing_transform(); + Rml::DecoratorsPtr get_existing_decorators(); + + // Various ways of applying effects to containers. These are built + // into RmlUI but some require custom implementations. + // It isn't worth exposing all at once, so instead we'll have methods. + // See: https://mikke89.github.io/RmlUiDoc/pages/rcss/decorators.html + enum class DecoratorType { + TiledHorizontal, + TiledVertical, + TiledBox, + Image, + NinePatch, + // (deprecated, use horizontal-gradient or vertical-gradient) + Gradient, + HorizontalGradient, + VerticalGradient, + // Custom shader, see DecoratorShader and https://mikke89.github.io/RmlUiDoc/pages/rcss/decorators/shader.html + // Requires implementation in recompui renderer. Currently not supported in recompui. + Shader, + // Not supported: needs implementation in recompui renderer + LinearGradient, + // Not supported: needs implementation in recompui renderer + RepeatingLinearGradient, + // Not supported: needs implementation in recompui renderer + RadialGradient, + // Not supported: needs implementation in recompui renderer + RepeatingRadialGradient, + // Not supported: needs implementation in recompui renderer + ConicGradient, + // Not supported: needs implementation in recompui renderer + RepeatingConicGradient, + }; + static const std::unordered_map decorator_type_names; + Rml::DecoratorInstancer* get_decorator_instancer(Style::DecoratorType decorator_type); + // Helper to create decorator properties since decorator properties are unique to each decorator type. + // Note: `props_to_set` is a list of property name to rml property pairs, with each pair like: + // `{ "start-color", Rml::Property(Rml::Colourb(r, g, b, a), Rml::Unit::COLOUR) }` + Rml::PropertyDictionary create_decorator_properties( + Style::DecoratorType decorator_type, + std::initializer_list> props_to_set, + bool set_defaults = true + ); + void set_decorator(DecoratorType decorator_type, Rml::PropertyDictionary properties); + void set_or_add_transformation(const Rml::TransformPrimitive& primitive); // Used with display_hide and display_show to restore the intended display value if wanting to @@ -141,6 +185,12 @@ namespace recompui { void set_pointer_events(PointerEvents pointer_events); virtual bool is_element() { return false; } ResourceId get_resource_id() { return resource_id; } + + void set_decorator_horizontal_gradient(const Color &color_left, const Color &color_right); + void set_decorator_horizontal_gradient(recompui::theme::color color_left, recompui::theme::color color_right, int opacity_left = ThemeDefaultOpacity, int opacity_right = ThemeDefaultOpacity); + + void set_decorator_vertical_gradient(const Color &color_top, const Color &color_bottom); + void set_decorator_vertical_gradient(recompui::theme::color color_top, recompui::theme::color color_bottom, int opacity_left = ThemeDefaultOpacity, int opacity_right = ThemeDefaultOpacity); }; } // namespace recompui diff --git a/recompui/src/elements/ui_types.h b/recompui/src/elements/ui_types.h index af97fc4..815e7e7 100644 --- a/recompui/src/elements/ui_types.h +++ b/recompui/src/elements/ui_types.h @@ -5,6 +5,8 @@ #include #include +#include "RmlUi/Core.h" + namespace recompui { constexpr std::string_view checked_state = "checked"; @@ -17,6 +19,10 @@ namespace recompui { uint8_t g = 255; uint8_t b = 255; uint8_t a = 255; + + Rml::Property to_rml_property() const { + return Rml::Property(Rml::Colourb(r, g, b, a), Rml::Unit::COLOUR); + } }; enum class Cursor {