Created mod UI API functions for setting visibility, setting text, and destroying elements

This commit is contained in:
Mr-Wiseguy 2025-03-11 19:05:25 -04:00
parent 2dffaf6148
commit bb10d5d090
12 changed files with 117 additions and 15 deletions

View file

@ -33,7 +33,7 @@ namespace recompui {
Rml::ElementDocument* document; Rml::ElementDocument* document;
Element root_element; Element root_element;
std::vector<Element*> loose_elements; std::vector<Element*> loose_elements;
std::unordered_set<ResourceId> to_update; std::unordered_set<ResourceId> to_update;
bool captures_input = true; bool captures_input = true;
bool captures_mouse = true; bool captures_mouse = true;
Context(Rml::ElementDocument* document) : document(document), root_element(document) {} Context(Rml::ElementDocument* document) : document(document), root_element(document) {}
@ -309,6 +309,15 @@ void recompui::ContextId::open() {
opened_context_id = *this; opened_context_id = *this;
} }
bool recompui::ContextId::open_if_not_already() {
if (opened_context_id == *this) {
return false;
}
open();
return true;
}
void recompui::ContextId::close() { void recompui::ContextId::close() {
// Ensure a context is currently opened by this thread. // Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) { if (opened_context_id == ContextId::null()) {

View file

@ -42,6 +42,7 @@ namespace recompui {
Element* get_root_element(); Element* get_root_element();
void open(); void open();
bool open_if_not_already();
void close(); void close();
void process_updates(); void process_updates();

View file

@ -4,7 +4,7 @@
namespace recompui { namespace recompui {
Button::Button(Element *parent, const std::string &text, ButtonStyle style) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable), "button") { Button::Button(Element *parent, const std::string &text, ButtonStyle style) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable), "button", true) {
this->style = style; this->style = style;
set_text(text); set_text(text);

View file

@ -1,3 +1,5 @@
#include "RmlUi/Core/StringUtilities.h"
#include "ui_element.h" #include "ui_element.h"
#include "../core/ui_context.h" #include "../core/ui_context.h"
@ -13,7 +15,7 @@ Element::Element(Rml::Element *base) {
this->shim = true; this->shim = true;
} }
Element::Element(Element* parent, uint32_t events_enabled, Rml::String base_class) { Element::Element(Element* parent, uint32_t events_enabled, Rml::String base_class, bool can_set_text) : can_set_text(can_set_text) {
ContextId context = get_current_context(); ContextId context = get_current_context();
base_owning = context.get_document()->CreateElement(base_class); base_owning = context.get_document()->CreateElement(base_class);
@ -39,6 +41,10 @@ Element::~Element() {
void Element::add_child(Element *child) { void Element::add_child(Element *child) {
assert(child != nullptr); assert(child != nullptr);
if (can_set_text) {
assert(false && "Elements with settable text cannot have children");
return;
}
children.emplace_back(child); children.emplace_back(child);
@ -134,9 +140,11 @@ void Element::ProcessEvent(Rml::Event &event) {
context = get_context_from_document(doc); context = get_context_from_document(doc);
} }
bool did_open = false;
// TODO disallow null contexts once the entire UI system has been migrated. // TODO disallow null contexts once the entire UI system has been migrated.
if (context != ContextId::null()) { if (context != ContextId::null()) {
context.open(); did_open = context.open_if_not_already();
} }
// Events that are processed during any phase. // Events that are processed during any phase.
@ -187,7 +195,7 @@ void Element::ProcessEvent(Rml::Event &event) {
} }
} }
if (context != ContextId::null()) { if (context != ContextId::null() && did_open) {
context.close(); context.close();
} }
} }
@ -215,6 +223,24 @@ void Element::clear_children() {
children.clear(); children.clear();
} }
bool Element::remove_child(ResourceId child) {
bool found = false;
ContextId context = get_current_context();
for (auto it = children.begin(); it != children.end(); ++it) {
Element* cur_child = *it;
if (cur_child->get_resource_id() == child) {
children.erase(it);
context.destroy_resource(cur_child);
found = true;
break;
}
}
return found;
}
void Element::add_style(Style *style, const std::string_view style_name) { void Element::add_style(Style *style, const std::string_view style_name) {
add_style(style, { style_name }); add_style(style, { style_name });
} }
@ -247,8 +273,13 @@ bool Element::is_enabled() const {
} }
void Element::set_text(std::string_view text) { void Element::set_text(std::string_view text) {
// TODO escape this if (can_set_text) {
base->SetInnerRML(std::string(text)); // Escape the string into Rml to prevent element injection.
base->SetInnerRML(Rml::StringUtilities::EncodeRml(std::string(text)));
}
else {
assert(false && "Attempted to set text of an element that cannot have its text set.");
}
} }
std::string Element::get_input_text() { std::string Element::get_input_text() {

View file

@ -34,6 +34,7 @@ private:
bool enabled = true; bool enabled = true;
bool disabled_attribute = false; bool disabled_attribute = false;
bool disabled_from_parent = false; bool disabled_from_parent = false;
bool can_set_text = false;
void add_child(Element *child); void add_child(Element *child);
void register_event_listeners(uint32_t events_enabled); void register_event_listeners(uint32_t events_enabled);
@ -56,9 +57,11 @@ public:
Element(Rml::Element *base); Element(Rml::Element *base);
// Used to actually construct elements. // Used to actually construct elements.
Element(Element* parent, uint32_t events_enabled = 0, Rml::String base_class = "div"); Element(Element* parent, uint32_t events_enabled = 0, Rml::String base_class = "div", bool can_set_text = false);
virtual ~Element(); virtual ~Element();
void clear_children(); void clear_children();
bool remove_child(ResourceId child);
bool remove_child(Element *child) { remove_child(child->get_resource_id()); }
void add_style(Style *style, std::string_view style_name); void add_style(Style *style, std::string_view style_name);
void add_style(Style *style, const std::initializer_list<std::string_view> &style_names); void add_style(Style *style, const std::initializer_list<std::string_view> &style_names);
void set_enabled(bool enabled); void set_enabled(bool enabled);

View file

@ -4,7 +4,7 @@
namespace recompui { namespace recompui {
Label::Label(Element *parent, LabelStyle label_style) : Element(parent) { Label::Label(Element *parent, LabelStyle label_style) : Element(parent, 0U, "div", true) {
switch (label_style) { switch (label_style) {
case LabelStyle::Annotation: case LabelStyle::Annotation:
set_color(Color{ 185, 125, 242, 255 }); set_color(Color{ 185, 125, 242, 255 });

View file

@ -4,7 +4,7 @@ namespace recompui {
// RadioOption // RadioOption
RadioOption::RadioOption(Element *parent, std::string_view name, uint32_t index) : Element(parent, Events(EventType::Click, EventType::Focus, EventType::Hover, EventType::Enable), "label") { RadioOption::RadioOption(Element *parent, std::string_view name, uint32_t index) : Element(parent, Events(EventType::Click, EventType::Focus, EventType::Hover, EventType::Enable), "label", true) {
this->index = index; this->index = index;
set_text(name); set_text(name);

View file

@ -4,7 +4,7 @@
namespace recompui { namespace recompui {
Span::Span(Element *parent) : Element(parent, 0, "span") { Span::Span(Element *parent) : Element(parent, 0, "span", true) {
set_font_style(FontStyle::Normal); set_font_style(FontStyle::Normal);
} }

View file

@ -181,6 +181,17 @@ namespace recompui {
} }
void Style::set_visibility(Visibility visibility) {
switch (visibility) {
case Visibility::Visible:
set_property(Rml::PropertyId::Visibility, Rml::Style::Visibility::Visible);
break;
case Visibility::Hidden:
set_property(Rml::PropertyId::Visibility, Rml::Style::Visibility::Hidden);
break;
}
}
void Style::set_position(Position position) { void Style::set_position(Position position) {
switch (position) { switch (position) {
case Position::Absolute: case Position::Absolute:

View file

@ -20,6 +20,7 @@ namespace recompui {
public: public:
Style(); Style();
virtual ~Style(); virtual ~Style();
void set_visibility(Visibility visibility);
void set_position(Position position); void set_position(Position position);
void set_left(float left, Unit unit = Unit::Dp); void set_left(float left, Unit unit = Unit::Dp);
void set_top(float top, Unit unit = Unit::Dp); void set_top(float top, Unit unit = Unit::Dp);

View file

@ -152,6 +152,11 @@ namespace recompui {
TableCell TableCell
}; };
enum class Visibility {
Visible,
Hidden
};
enum class Position { enum class Position {
Absolute, Absolute,
Relative Relative

View file

@ -168,6 +168,25 @@ void recompui_create_element(uint8_t* rdram, recomp_context* ctx) {
return_resource(ctx, ret->get_resource_id()); return_resource(ctx, ret->get_resource_id());
} }
void recompui_destroy_element(uint8_t* rdram, recomp_context* ctx) {
Style* parent_resource = arg_style<0>(rdram, ctx);
if (!parent_resource->is_element()) {
recompui::message_box("Fatal error in mod - attempted to remove child from non-element");
assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
}
Element* parent = static_cast<Element*>(parent_resource);
ResourceId to_remove = arg_resource_id<1>(rdram, ctx);
if (!parent->remove_child(to_remove)) {
recompui::message_box("Fatal error in mod - attempted to remove child from wrong parent");
assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
}
}
void recompui_create_label(uint8_t* rdram, recomp_context* ctx) { void recompui_create_label(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx); ContextId ui_context = get_context(rdram, ctx);
Element* parent = arg_element<1>(rdram, ctx, ui_context); Element* parent = arg_element<1>(rdram, ctx, ui_context);
@ -206,6 +225,13 @@ void recompui_create_button(uint8_t* rdram, recomp_context* ctx) {
} }
// Position and Layout // Position and Layout
void recompui_set_visibility(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
uint32_t visibility = _arg<1, uint32_t>(rdram, ctx);
resource->set_visibility(static_cast<Visibility>(visibility));
}
void recompui_set_position(uint8_t* rdram, recomp_context* ctx) { void recompui_set_position(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx); Style* resource = arg_style<0>(rdram, ctx);
uint32_t position = _arg<1, uint32_t>(rdram, ctx); uint32_t position = _arg<1, uint32_t>(rdram, ctx);
@ -645,6 +671,19 @@ void recompui_set_overflow_y(uint8_t* rdram, recomp_context* ctx) {
} }
// Text and Fonts // Text and Fonts
void recompui_set_text(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
if (!resource->is_element()) {
recompui::message_box("Fatal error in mod - attempted to set text of non-element");
assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
}
Element* element = static_cast<Element*>(resource);
element->set_text(_arg_string<1>(rdram, ctx));
}
void recompui_set_font_size(uint8_t* rdram, recomp_context* ctx) { void recompui_set_font_size(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx); Style* resource = arg_style<0>(rdram, ctx);
float size = _arg_float_a1(rdram, ctx); float size = _arg_float_a1(rdram, ctx);
@ -731,12 +770,12 @@ void recompui_set_tab_index(uint8_t* rdram, recomp_context* ctx) {
resource->set_tab_index(static_cast<TabIndex>(tab_index)); resource->set_tab_index(static_cast<TabIndex>(tab_index));
} }
// Getters // Text
void recompui_get_input_text(uint8_t* rdram, recomp_context* ctx) { void recompui_get_input_text(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx); Style* resource = arg_style<0>(rdram, ctx);
if (!resource->is_element()) { if (!resource->is_element()) {
recompui::message_box("Fatal error in mod - attempted to get text of non-element"); recompui::message_box("Fatal error in mod - attempted to get input text of non-element");
assert(false); assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__); ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
} }
@ -746,12 +785,11 @@ void recompui_get_input_text(uint8_t* rdram, recomp_context* ctx) {
return_string(rdram, ctx, ret); return_string(rdram, ctx, ret);
} }
// Setters
void recompui_set_input_text(uint8_t* rdram, recomp_context* ctx) { void recompui_set_input_text(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx); Style* resource = arg_style<0>(rdram, ctx);
if (!resource->is_element()) { if (!resource->is_element()) {
recompui::message_box("Fatal error in mod - attempted to set text of non-element"); recompui::message_box("Fatal error in mod - attempted to set input text of non-element");
assert(false); assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__); ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
} }
@ -798,10 +836,12 @@ void recompui::register_ui_exports() {
REGISTER_FUNC(recompui_set_context_captures_mouse); REGISTER_FUNC(recompui_set_context_captures_mouse);
REGISTER_FUNC(recompui_create_style); REGISTER_FUNC(recompui_create_style);
REGISTER_FUNC(recompui_create_element); REGISTER_FUNC(recompui_create_element);
REGISTER_FUNC(recompui_destroy_element);
REGISTER_FUNC(recompui_create_label); REGISTER_FUNC(recompui_create_label);
// REGISTER_FUNC(recompui_create_span); // REGISTER_FUNC(recompui_create_span);
REGISTER_FUNC(recompui_create_textinput); REGISTER_FUNC(recompui_create_textinput);
REGISTER_FUNC(recompui_create_button); REGISTER_FUNC(recompui_create_button);
REGISTER_FUNC(recompui_set_visibility);
REGISTER_FUNC(recompui_set_position); REGISTER_FUNC(recompui_set_position);
REGISTER_FUNC(recompui_set_left); REGISTER_FUNC(recompui_set_left);
REGISTER_FUNC(recompui_set_top); REGISTER_FUNC(recompui_set_top);
@ -860,6 +900,7 @@ void recompui::register_ui_exports() {
REGISTER_FUNC(recompui_set_overflow); REGISTER_FUNC(recompui_set_overflow);
REGISTER_FUNC(recompui_set_overflow_x); REGISTER_FUNC(recompui_set_overflow_x);
REGISTER_FUNC(recompui_set_overflow_y); REGISTER_FUNC(recompui_set_overflow_y);
REGISTER_FUNC(recompui_set_text);
REGISTER_FUNC(recompui_set_font_size); REGISTER_FUNC(recompui_set_font_size);
REGISTER_FUNC(recompui_set_letter_spacing); REGISTER_FUNC(recompui_set_letter_spacing);
REGISTER_FUNC(recompui_set_line_height); REGISTER_FUNC(recompui_set_line_height);