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

View File

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

View File

@ -4,7 +4,7 @@
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;
set_text(text);

View File

@ -1,3 +1,5 @@
#include "RmlUi/Core/StringUtilities.h"
#include "ui_element.h"
#include "../core/ui_context.h"
@ -13,7 +15,7 @@ Element::Element(Rml::Element *base) {
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();
base_owning = context.get_document()->CreateElement(base_class);
@ -39,6 +41,10 @@ Element::~Element() {
void Element::add_child(Element *child) {
assert(child != nullptr);
if (can_set_text) {
assert(false && "Elements with settable text cannot have children");
return;
}
children.emplace_back(child);
@ -134,9 +140,11 @@ void Element::ProcessEvent(Rml::Event &event) {
context = get_context_from_document(doc);
}
bool did_open = false;
// TODO disallow null contexts once the entire UI system has been migrated.
if (context != ContextId::null()) {
context.open();
did_open = context.open_if_not_already();
}
// 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();
}
}
@ -215,6 +223,24 @@ void Element::clear_children() {
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) {
add_style(style, { style_name });
}
@ -247,8 +273,13 @@ bool Element::is_enabled() const {
}
void Element::set_text(std::string_view text) {
// TODO escape this
base->SetInnerRML(std::string(text));
if (can_set_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() {

View File

@ -34,6 +34,7 @@ private:
bool enabled = true;
bool disabled_attribute = false;
bool disabled_from_parent = false;
bool can_set_text = false;
void add_child(Element *child);
void register_event_listeners(uint32_t events_enabled);
@ -56,9 +57,11 @@ public:
Element(Rml::Element *base);
// 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();
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, const std::initializer_list<std::string_view> &style_names);
void set_enabled(bool enabled);

View File

@ -4,7 +4,7 @@
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) {
case LabelStyle::Annotation:
set_color(Color{ 185, 125, 242, 255 });

View File

@ -4,7 +4,7 @@ namespace recompui {
// 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;
set_text(name);

View File

@ -4,7 +4,7 @@
namespace recompui {
Span::Span(Element *parent) : Element(parent, 0, "span") {
Span::Span(Element *parent) : Element(parent, 0, "span", true) {
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) {
switch (position) {
case Position::Absolute:

View File

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

View File

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

View File

@ -168,6 +168,25 @@ void recompui_create_element(uint8_t* rdram, recomp_context* ctx) {
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) {
ContextId ui_context = get_context(rdram, ctx);
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
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) {
Style* resource = arg_style<0>(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
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) {
Style* resource = arg_style<0>(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));
}
// Getters
// Text
void recompui_get_input_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 get text of non-element");
recompui::message_box("Fatal error in mod - attempted to get input text of non-element");
assert(false);
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);
}
// Setters
void recompui_set_input_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");
recompui::message_box("Fatal error in mod - attempted to set input text of non-element");
assert(false);
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_create_style);
REGISTER_FUNC(recompui_create_element);
REGISTER_FUNC(recompui_destroy_element);
REGISTER_FUNC(recompui_create_label);
// REGISTER_FUNC(recompui_create_span);
REGISTER_FUNC(recompui_create_textinput);
REGISTER_FUNC(recompui_create_button);
REGISTER_FUNC(recompui_set_visibility);
REGISTER_FUNC(recompui_set_position);
REGISTER_FUNC(recompui_set_left);
REGISTER_FUNC(recompui_set_top);
@ -860,6 +900,7 @@ void recompui::register_ui_exports() {
REGISTER_FUNC(recompui_set_overflow);
REGISTER_FUNC(recompui_set_overflow_x);
REGISTER_FUNC(recompui_set_overflow_y);
REGISTER_FUNC(recompui_set_text);
REGISTER_FUNC(recompui_set_font_size);
REGISTER_FUNC(recompui_set_letter_spacing);
REGISTER_FUNC(recompui_set_line_height);