Begin implementing mod UI API

This commit is contained in:
Mr-Wiseguy 2025-01-20 18:26:50 -05:00
parent 563e645c85
commit 2f37efa8a5
30 changed files with 366 additions and 57 deletions

View File

@ -163,6 +163,7 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_details_panel.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_menu.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api.cpp
${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp
${CMAKE_SOURCE_DIR}/src/ui/core/ui_context.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp

View File

@ -108,6 +108,7 @@ namespace recompui {
Rml::ElementPtr create_custom_element(Rml::Element* parent, std::string tag);
Rml::ElementDocument* load_document(const std::filesystem::path& path);
Rml::ElementDocument* create_empty_document();
}
#endif

View File

@ -599,6 +599,8 @@ int main(int argc, char** argv) {
recomp::register_game(game);
}
recompui::register_ui_exports();
zelda64::register_overlays();
zelda64::register_patches();
zelda64::load_config();

View File

@ -178,6 +178,34 @@ recompui::ContextId recompui::create_context(Rml::ElementDocument* document) {
return create_context_impl(document);
}
recompui::ContextId recompui::create_context() {
Rml::ElementDocument* doc = create_empty_document();
ContextId ret = create_context_impl(doc);
Element* root = ret.get_root_element();
// Mark the root element as not being a shim, as that's only needed for elements that were parented to Rml ones manually.
root->shim = false;
// TODO move these defaults elsewhere. Copied from the existing rcss.
ret.open();
root->set_width(100.0f, Unit::Percent);
root->set_height(100.0f, Unit::Percent);
root->set_display(Display::Flex);
root->set_opacity(1.0f);
root->set_font_family("chiaro");
root->set_font_style(FontStyle::Normal);
root->set_font_weight(400);
float sz = 16.0f;
float spacing = 0.0f;
float sz_add = sz + 4;
root->set_font_size(sz_add, Unit::Dp);
root->set_letter_spacing(sz_add * spacing, Unit::Dp);
root->set_line_height(sz_add, Unit::Dp);
ret.close();
return ret;
}
void recompui::destroy_context(ContextId id) {
bool existed = false;
@ -209,6 +237,8 @@ void recompui::destroy_context(ContextId id) {
}
void recompui::destroy_all_contexts() {
recompui::hide_all_contexts();
std::lock_guard lock{ context_state.all_contexts_lock };
// TODO prevent deletion of a context while its mutex is in use. Second lock on the context's mutex before popping

View File

@ -51,8 +51,11 @@ namespace recompui {
ContextId create_context(const std::filesystem::path& path);
ContextId create_context(Rml::ElementDocument* document);
ContextId create_context();
void destroy_context(ContextId id);
ContextId get_current_context();
ContextId get_context_from_document(Rml::ElementDocument* document);
void destroy_all_contexts();
void register_ui_exports();
} // namespace recompui

View File

@ -8,6 +8,8 @@ namespace recompui {
struct ResourceId {
uint32_t slot_id;
bool operator==(const ResourceId& rhs) const = default;
const Style* operator*() const;
Style* operator*();

View File

@ -4,7 +4,7 @@
namespace recompui {
Button::Button(const std::string &text, ButtonStyle style, Element *parent) : 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") {
this->style = style;
set_text(text);

View File

@ -20,7 +20,7 @@ namespace recompui {
// Element overrides.
virtual void process_event(const Event &e) override;
public:
Button(const std::string &text, ButtonStyle style, Element *parent);
Button(Element *parent, const std::string &text, ButtonStyle style);
void add_pressed_callback(std::function<void()> callback);
};

View File

@ -4,7 +4,7 @@
namespace recompui {
Container::Container(FlexDirection direction, JustifyContent justify_content, Element *parent) : Element(parent) {
Container::Container(Element *parent, FlexDirection direction, JustifyContent justify_content) : Element(parent) {
set_display(Display::Flex);
set_flex(1.0f, 1.0f);
set_flex_direction(direction);

View File

@ -6,7 +6,7 @@ namespace recompui {
class Container : public Element {
public:
Container(FlexDirection direction, JustifyContent justify_content, Element *parent);
Container(Element* parent, FlexDirection direction, JustifyContent justify_content);
};
} // namespace recompui

View File

@ -203,6 +203,9 @@ void Element::process_event(const Event &) {
}
void Element::clear_children() {
if (children.empty()) {
return;
}
ContextId context = get_current_context();
// Remove the children from the context.

View File

@ -8,6 +8,7 @@
namespace recompui {
class Element : public Style, public Rml::EventListener {
friend ContextId create_context(const std::filesystem::path& path);
friend ContextId create_context();
private:
Rml::Element *base = nullptr;
Rml::ElementPtr base_owning = {};

View File

@ -4,7 +4,7 @@
namespace recompui {
Label::Label(LabelStyle label_style, Element *parent) : Element(parent) {
Label::Label(Element *parent, LabelStyle label_style) : Element(parent) {
switch (label_style) {
case LabelStyle::Small:
set_font_size(20.0f);
@ -29,7 +29,7 @@ namespace recompui {
set_font_style(FontStyle::Normal);
}
Label::Label(const std::string &text, LabelStyle label_style, Element *parent) : Label(label_style, parent) {
Label::Label(Element *parent, const std::string &text, LabelStyle label_style) : Label(parent, label_style) {
set_text(text);
}

View File

@ -12,8 +12,8 @@ namespace recompui {
class Label : public Element {
public:
Label(LabelStyle label_style, Element *parent);
Label(const std::string &text, LabelStyle label_style, Element *parent);
Label(Element *parent, LabelStyle label_style);
Label(Element *parent, const std::string &text, LabelStyle label_style);
};
} // namespace recompui

View File

@ -4,7 +4,7 @@ namespace recompui {
// RadioOption
RadioOption::RadioOption(std::string_view name, uint32_t index, Element *parent) : 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") {
this->index = index;
set_text(name);
@ -68,7 +68,7 @@ namespace recompui {
set_index_internal(index, false, true);
}
Radio::Radio(Element *parent) : Container(FlexDirection::Row, JustifyContent::FlexStart, parent) {
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart) {
set_gap(12.0f);
}
@ -77,7 +77,7 @@ namespace recompui {
}
void Radio::add_option(std::string_view name) {
RadioOption *option = get_current_context().create_element<RadioOption>(name, uint32_t(options.size()), this);
RadioOption *option = get_current_context().create_element<RadioOption>(this, name, uint32_t(options.size()));
option->set_pressed_callback(std::bind(&Radio::option_selected, this, std::placeholders::_1));
options.emplace_back(option);

View File

@ -13,7 +13,7 @@ namespace recompui {
protected:
virtual void process_event(const Event &e) override;
public:
RadioOption(std::string_view name, uint32_t index, Element *parent);
RadioOption(Element *parent, std::string_view name, uint32_t index);
void set_pressed_callback(std::function<void(uint32_t)> callback);
void set_selected_state(bool enable);
};

View File

@ -4,7 +4,7 @@
namespace recompui {
ScrollContainer::ScrollContainer(ScrollDirection direction, Element *parent) : Element(parent) {
ScrollContainer::ScrollContainer(Element *parent, ScrollDirection direction) : Element(parent) {
set_flex(1.0f, 1.0f, 100.0f);
set_width(100.0f, Unit::Percent);
set_height(100.0f, Unit::Percent);

View File

@ -11,7 +11,7 @@ namespace recompui {
class ScrollContainer : public Element {
public:
ScrollContainer(ScrollDirection direction, Element *parent);
ScrollContainer(Element *parent, ScrollDirection direction);
};
} // namespace recompui

View File

@ -60,7 +60,7 @@ namespace recompui {
}
}
Slider::Slider(SliderType type, Element *parent) : Element(parent) {
Slider::Slider(Element *parent, SliderType type) : Element(parent) {
this->type = type;
set_display(Display::Flex);
@ -69,7 +69,7 @@ namespace recompui {
ContextId context = get_current_context();
value_label = context.create_element<Label>("0", LabelStyle::Small, this);
value_label = context.create_element<Label>(this, "0", LabelStyle::Small);
value_label->set_margin_right(20.0f);
value_label->set_min_width(60.0f);
value_label->set_max_width(60.0f);

View File

@ -34,7 +34,7 @@ namespace recompui {
void update_label_text();
public:
Slider(SliderType type, Element *parent);
Slider(Element *parent, SliderType type);
virtual ~Slider();
void set_value(double v);
double get_value() const;

View File

@ -475,4 +475,8 @@ namespace recompui {
set_property(Rml::PropertyId::TabIndex, to_rml(tab_index), Animation());
}
void Style::set_font_family(std::string_view family) {
set_property(Rml::PropertyId::FontFamily, Rml::Property(Rml::String{ family }, Rml::Unit::UNKNOWN), Animation());
}
} // namespace recompui

View File

@ -1,5 +1,7 @@
#pragma once
#include <string_view>
#include "RmlUi/Core.h"
#include "../core/ui_resource.h"
@ -84,6 +86,7 @@ namespace recompui {
void set_column_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
void set_drag(Drag drag);
void set_tab_index(TabIndex focus);
void set_font_family(std::string_view family);
virtual bool is_element() { return false; }
ResourceId get_resource_id() { return resource_id; }
};

View File

@ -171,7 +171,7 @@ namespace recompui {
Percent
};
enum class AnimationType {
enum class AnimationType : uint32_t {
None,
Set,
Tween

254
src/ui/ui_api.cpp Normal file
View File

@ -0,0 +1,254 @@
#include "recomp_ui.h"
#include "core/ui_context.h"
#include "core/ui_resource.h"
#include "elements/ui_element.h"
#include "elements/ui_button.h"
#include "elements/ui_clickable.h"
#include "elements/ui_container.h"
#include "elements/ui_image.h"
#include "elements/ui_label.h"
#include "elements/ui_radio.h"
#include "elements/ui_scroll_container.h"
#include "elements/ui_slider.h"
#include "elements/ui_style.h"
#include "elements/ui_text_input.h"
#include "elements/ui_toggle.h"
#include "elements/ui_types.h"
#include "librecomp/overlays.hpp"
#include "librecomp/helpers.hpp"
using namespace recompui;
constexpr ResourceId root_element_id{ 0xFFFFFFFE };
// Helpers
ContextId get_context(uint8_t* rdram, recomp_context* ctx) {
uint32_t context_id = _arg<0, uint32_t>(rdram, ctx);
return ContextId{ .slot_id = context_id };
}
template <int arg_index>
std::string arg_string(uint8_t* rdram, recomp_context* ctx) {
PTR(char) str = _arg<arg_index, PTR(char)>(rdram, ctx);
// Get the length of the byteswapped string.
size_t len = 0;
while (MEM_B(str, len) != 0x00) {
len++;
}
std::string ret{};
ret.reserve(len + 1);
for (size_t i = 0; i < len; i++) {
ret += (char)MEM_B(str, i);
}
return ret;
}
template <int arg_index>
ResourceId arg_resource_id(uint8_t* rdram, recomp_context* ctx) {
uint32_t slot_id = _arg<arg_index, uint32_t>(rdram, ctx);
return ResourceId{ .slot_id = slot_id };
}
template <int arg_index>
Element* arg_element(uint8_t* rdram, recomp_context* ctx, ContextId ui_context) {
ResourceId resource = arg_resource_id<arg_index>(rdram, ctx);
if (resource == ResourceId::null()) {
return nullptr;
}
else if (resource == root_element_id) {
return ui_context.get_root_element();
}
return resource.as_element();
}
template <int arg_index>
Style* arg_style(uint8_t* rdram, recomp_context* ctx) {
ResourceId resource = arg_resource_id<arg_index>(rdram, ctx);
if (resource == ResourceId::null()) {
return nullptr;
}
return *resource;
}
template <int arg_index>
Animation arg_animation(uint8_t* rdram, recomp_context* ctx) {
PTR(Animation) anim_ptr = _arg<arg_index, PTR(Animation)>(rdram, ctx);
if (anim_ptr == NULLPTR) {
return Animation{};
}
else {
return *TO_PTR(Animation, anim_ptr);
}
}
void return_resource(recomp_context* ctx, ResourceId resource) {
_return<uint32_t>(ctx, resource.slot_id);
}
// Context functions
extern "C" void recompui_create_context(uint8_t* rdram, recomp_context* ctx) {
(void)rdram;
ContextId ui_context = create_context();
_return<uint32_t>(ctx, ui_context.slot_id);
}
extern "C" void recompui_open_context(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
ui_context.open();
}
extern "C" void recompui_close_context(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
ui_context.close();
}
extern "C" void recompui_context_root(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
(void)ui_context;
return_resource(ctx, root_element_id);
}
extern "C" void recompui_show_context(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
recompui::show_context(ui_context, "");
}
extern "C" void recompui_hide_context(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
recompui::hide_context(ui_context);
}
// Resource creation functions
extern "C" void recompui_create_style(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
Style* ret = ui_context.create_style();
return_resource(ctx, ret->get_resource_id());
}
extern "C" void recompui_create_element(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
Element* parent = arg_element<1>(rdram, ctx, ui_context);
Element* ret = ui_context.create_element<Element>(parent);
return_resource(ctx, ret->get_resource_id());
}
extern "C" void recompui_create_button(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
Element* parent = arg_element<1>(rdram, ctx, ui_context);
std::string text = arg_string<2>(rdram, ctx);
uint32_t style = _arg<3, uint32_t>(rdram, ctx);
Button* ret = ui_context.create_element<Button>(parent, text, static_cast<ButtonStyle>(style));
return_resource(ctx, ret->get_resource_id());
}
// Style functions
extern "C" void recompui_set_width(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
float width = _arg_float_a1(rdram, ctx);
uint32_t unit = _arg<2, uint32_t>(rdram, ctx);
Animation animation = arg_animation<3>(rdram, ctx);
resource->set_width(width, static_cast<Unit>(unit), animation);
}
extern "C" void recompui_set_height(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
float height = _arg_float_a1(rdram, ctx);
uint32_t unit = _arg<2, uint32_t>(rdram, ctx);
Animation animation = arg_animation<3>(rdram, ctx);
resource->set_height(height, static_cast<Unit>(unit), animation);
}
extern "C" void recompui_set_display(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
uint32_t display = _arg<1, uint32_t>(rdram, ctx);
resource->set_display(static_cast<Display>(display));
}
extern "C" void recompui_set_flex_direction(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
uint32_t direction = _arg<1, uint32_t>(rdram, ctx);
resource->set_flex_direction(static_cast<FlexDirection>(direction));
}
extern "C" void recompui_set_flex_grow(uint8_t* rdram, recomp_context* ctx) { // float grow, Animation animation = Animation()
Style* resource = arg_style<0>(rdram, ctx);
float grow = _arg_float_a1(rdram, ctx);
Animation animation = arg_animation<2>(rdram, ctx);
resource->set_flex_grow(grow, animation);
}
extern "C" void recompui_set_flex_shrink(uint8_t* rdram, recomp_context* ctx) { // float shrink, Animation animation = Animation()
Style* resource = arg_style<0>(rdram, ctx);
float shrink = _arg_float_a1(rdram, ctx);
Animation animation = arg_animation<2>(rdram, ctx);
resource->set_flex_shrink(shrink, animation);
}
extern "C" void recompui_set_flex_basis_auto(uint8_t* rdram, recomp_context* ctx) {
Style* resource = arg_style<0>(rdram, ctx);
resource->set_flex_basis_auto();
}
extern "C" void recompui_set_flex_basis(uint8_t* rdram, recomp_context* ctx) { // float basis, Unit unit = Unit::Percent, Animation animation = Animation()
Style* resource = arg_style<0>(rdram, ctx);
float basis = _arg_float_a1(rdram, ctx);
uint32_t unit = _arg<2, uint32_t>(rdram, ctx);
Animation animation = arg_animation<3>(rdram, ctx);
resource->set_flex_basis(basis, static_cast<Unit>(unit), animation);
}
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
void recompui::register_ui_exports() {
REGISTER_FUNC(recompui_create_context);
REGISTER_FUNC(recompui_open_context);
REGISTER_FUNC(recompui_close_context);
REGISTER_FUNC(recompui_context_root);
REGISTER_FUNC(recompui_show_context);
REGISTER_FUNC(recompui_hide_context);
REGISTER_FUNC(recompui_create_style);
REGISTER_FUNC(recompui_create_element);
REGISTER_FUNC(recompui_create_button);
REGISTER_FUNC(recompui_set_width);
REGISTER_FUNC(recompui_set_height);
REGISTER_FUNC(recompui_set_display);
REGISTER_FUNC(recompui_set_flex_direction);
REGISTER_FUNC(recompui_set_flex_grow);
REGISTER_FUNC(recompui_set_flex_shrink);
REGISTER_FUNC(recompui_set_flex_basis_auto);
REGISTER_FUNC(recompui_set_flex_basis);
}

View File

@ -25,7 +25,7 @@ ConfigOptionElement::ConfigOptionElement(Element *parent) : Element(parent, Even
set_gap(8.0f);
set_min_height(100.0f);
name_label = get_current_context().create_element<Label>(LabelStyle::Normal, this);
name_label = get_current_context().create_element<Label>(this, LabelStyle::Normal);
}
ConfigOptionElement::~ConfigOptionElement() {
@ -56,8 +56,8 @@ void ConfigOptionSlider::slider_value_changed(double v) {
printf("%s changed to %f.\n", name.c_str(), v);
}
ConfigOptionSlider::ConfigOptionSlider(double value, double min_value, double max_value, double step_value, bool percent, Element *parent) : ConfigOptionElement(parent) {
slider = get_current_context().create_element<Slider>(percent ? SliderType::Percent : SliderType::Double, this);
ConfigOptionSlider::ConfigOptionSlider(Element *parent, double value, double min_value, double max_value, double step_value, bool percent) : ConfigOptionElement(parent) {
slider = get_current_context().create_element<Slider>(this, percent ? SliderType::Percent : SliderType::Double);
slider->set_value(value);
slider->set_min_value(min_value);
slider->set_max_value(max_value);
@ -83,7 +83,7 @@ void ConfigOptionRadio::index_changed(uint32_t index) {
printf("%s changed to %d.\n", name.c_str(), index);
}
ConfigOptionRadio::ConfigOptionRadio(const std::vector<std::string> &options, Element *parent) : ConfigOptionElement(parent) {
ConfigOptionRadio::ConfigOptionRadio(Element *parent, const std::vector<std::string> &options) : ConfigOptionElement(parent) {
radio = get_current_context().create_element<Radio>(this);
radio->add_index_changed_callback(std::bind(&ConfigOptionRadio::index_changed, this, std::placeholders::_1));
for (std::string_view option : options) {
@ -127,25 +127,25 @@ ConfigSubMenu::ConfigSubMenu(Element *parent) : Element(parent) {
set_height(100.0f, Unit::Percent);
recompui::ContextId context = get_current_context();
header_container = context.create_element<Container>(FlexDirection::Row, JustifyContent::FlexStart, this);
header_container = context.create_element<Container>(this, FlexDirection::Row, JustifyContent::FlexStart);
{
back_button = context.create_element<Button>("Back", ButtonStyle::Secondary, header_container);
back_button = context.create_element<Button>(header_container, "Back", ButtonStyle::Secondary);
back_button->add_pressed_callback(std::bind(&ConfigSubMenu::back_button_pressed, this));
title_label = context.create_element<Label>("Title", LabelStyle::Large, header_container);
title_label = context.create_element<Label>(header_container, "Title", LabelStyle::Large);
}
body_container = context.create_element<Container>(FlexDirection::Row, JustifyContent::SpaceEvenly, this);
body_container = context.create_element<Container>(this, FlexDirection::Row, JustifyContent::SpaceEvenly);
{
config_container = context.create_element<Container>(FlexDirection::Column, JustifyContent::Center, body_container);
config_container = context.create_element<Container>(body_container, FlexDirection::Column, JustifyContent::Center);
config_container->set_display(Display::Block);
config_container->set_flex_basis(100.0f);
config_container->set_align_items(AlignItems::Center);
{
config_scroll_container = context.create_element<ScrollContainer>(ScrollDirection::Vertical, config_container);
config_scroll_container = context.create_element<ScrollContainer>(config_container, ScrollDirection::Vertical);
}
description_label = context.create_element<Label>("Description", LabelStyle::Small, body_container);
description_label = context.create_element<Label>(body_container, "Description", LabelStyle::Small);
description_label->set_min_width(800.0f);
}
}
@ -172,7 +172,7 @@ void ConfigSubMenu::add_option(ConfigOptionElement *option, std::string_view nam
}
void ConfigSubMenu::add_slider_option(std::string_view name, std::string_view description, double min, double max, double step, bool percent) {
ConfigOptionSlider *option_slider = get_current_context().create_element<ConfigOptionSlider>((min + max) / 2.0, min, max, step, percent, config_scroll_container);
ConfigOptionSlider *option_slider = get_current_context().create_element<ConfigOptionSlider>(config_scroll_container, (min + max) / 2.0, min, max, step, percent);
add_option(option_slider, name, description);
}
@ -182,7 +182,7 @@ void ConfigSubMenu::add_text_option(std::string_view name, std::string_view desc
}
void ConfigSubMenu::add_radio_option(std::string_view name, std::string_view description, const std::vector<std::string> &options) {
ConfigOptionRadio *option_radio = get_current_context().create_element<ConfigOptionRadio>(options, config_scroll_container);
ConfigOptionRadio *option_radio = get_current_context().create_element<ConfigOptionRadio>(config_scroll_container, options);
add_option(option_radio, name, description);
}

View File

@ -36,7 +36,7 @@ protected:
void slider_value_changed(double v);
public:
ConfigOptionSlider(double value, double min_value, double max_value, double step_value, bool percent, Element *parent);
ConfigOptionSlider(Element *parent, double value, double min_value, double max_value, double step_value, bool percent);
};
class ConfigOptionTextInput : public ConfigOptionElement {
@ -54,7 +54,7 @@ protected:
void index_changed(uint32_t index);
public:
ConfigOptionRadio(const std::vector<std::string> &options, Element *parent);
ConfigOptionRadio(Element *parent, const std::vector<std::string> &options);
};
class ConfigSubMenu : public Element {

View File

@ -14,13 +14,13 @@ ModDetailsPanel::ModDetailsPanel(Element *parent) : Element(parent) {
ContextId context = get_current_context();
header_container = context.create_element<Container>(FlexDirection::Row, JustifyContent::FlexStart, this);
header_container = context.create_element<Container>(this, FlexDirection::Row, JustifyContent::FlexStart);
header_container->set_flex(0.0f, 0.0f);
header_container->set_padding(16.0f);
header_container->set_gap(16.0f);
header_container->set_background_color(Color{ 0, 0, 0, 89 });
{
thumbnail_container = context.create_element<Container>(FlexDirection::Column, JustifyContent::SpaceEvenly, header_container);
thumbnail_container = context.create_element<Container>(header_container, FlexDirection::Column, JustifyContent::SpaceEvenly);
thumbnail_container->set_flex(0.0f, 0.0f);
{
thumbnail_image = context.create_element<Image>(thumbnail_container);
@ -29,38 +29,38 @@ ModDetailsPanel::ModDetailsPanel(Element *parent) : Element(parent) {
thumbnail_image->set_background_color(Color{ 190, 184, 219, 25 });
}
header_details_container = context.create_element<Container>(FlexDirection::Column, JustifyContent::SpaceEvenly, header_container);
header_details_container = context.create_element<Container>(header_container, FlexDirection::Column, JustifyContent::SpaceEvenly);
header_details_container->set_flex(1.0f, 1.0f);
header_details_container->set_flex_basis(100.0f, Unit::Percent);
header_details_container->set_text_align(TextAlign::Left);
{
title_label = context.create_element<Label>(LabelStyle::Large, header_details_container);
version_label = context.create_element<Label>(LabelStyle::Normal, header_details_container);
title_label = context.create_element<Label>(header_details_container, LabelStyle::Large);
version_label = context.create_element<Label>(header_details_container, LabelStyle::Normal);
}
}
body_container = context.create_element<Container>(FlexDirection::Column, JustifyContent::FlexStart, this);
body_container = context.create_element<Container>(this, FlexDirection::Column, JustifyContent::FlexStart);
body_container->set_flex(0.0f, 0.0f);
body_container->set_text_align(TextAlign::Left);
body_container->set_padding(16.0f);
body_container->set_gap(16.0f);
{
description_label = context.create_element<Label>(LabelStyle::Normal, body_container);
authors_label = context.create_element<Label>(LabelStyle::Normal, body_container);
description_label = context.create_element<Label>(body_container, LabelStyle::Normal);
authors_label = context.create_element<Label>(body_container, LabelStyle::Normal);
}
spacer_element = context.create_element<Element>(this);
spacer_element->set_flex(1.0f, 0.0f);
buttons_container = context.create_element<Container>(FlexDirection::Row, JustifyContent::SpaceAround, this);
buttons_container = context.create_element<Container>(this, FlexDirection::Row, JustifyContent::SpaceAround);
buttons_container->set_flex(0.0f, 0.0f);
buttons_container->set_padding(16.0f);
{
enable_toggle = context.create_element<Toggle>(buttons_container);
enable_toggle->add_checked_callback(std::bind(&ModDetailsPanel::enable_toggle_checked, this, std::placeholders::_1));
configure_button = context.create_element<Button>("Configure", recompui::ButtonStyle::Secondary, buttons_container);
configure_button = context.create_element<Button>(buttons_container, "Configure", recompui::ButtonStyle::Secondary);
configure_button->add_pressed_callback(std::bind(&ModDetailsPanel::configure_button_pressed, this));
erase_button = context.create_element<Button>("Erase", recompui::ButtonStyle::Secondary, buttons_container);
erase_button = context.create_element<Button>(buttons_container, "Erase", recompui::ButtonStyle::Secondary);
}
}

View File

@ -40,15 +40,15 @@ ModEntry::ModEntry(Element *parent, const recomp::mods::ModDetails &details, uin
thumbnail_image->set_min_height(100.0f);
thumbnail_image->set_background_color(Color{ 190, 184, 219, 25 });
body_container = context.create_element<Container>(FlexDirection::Column, JustifyContent::FlexStart, this);
body_container = context.create_element<Container>(this, FlexDirection::Column, JustifyContent::FlexStart);
body_container->set_width_auto();
body_container->set_height(100.0f);
body_container->set_margin_left(16.0f);
body_container->set_overflow(Overflow::Hidden);
{
name_label = context.create_element<Label>(details.mod_id, LabelStyle::Normal, body_container);
description_label = context.create_element<Label>("Short description of mod here.", LabelStyle::Small, body_container);
name_label = context.create_element<Label>(body_container, details.mod_id, LabelStyle::Normal);
description_label = context.create_element<Label>(body_container, "Short description of mod here.", LabelStyle::Small);
} // body_container
} // this
}
@ -174,12 +174,12 @@ ModMenu::ModMenu(Element *parent) : Element(parent) {
set_height(100.0f, Unit::Percent);
{
body_container = context.create_element<Container>(FlexDirection::Row, JustifyContent::FlexStart, this);
body_container = context.create_element<Container>(this, FlexDirection::Row, JustifyContent::FlexStart);
body_container->set_flex(1.0f, 1.0f, 100.0f);
body_container->set_width(100.0f, Unit::Percent);
body_container->set_height(100.0f, Unit::Percent);
{
list_container = context.create_element<Container>(FlexDirection::Column, JustifyContent::Center, body_container);
list_container = context.create_element<Container>(body_container, FlexDirection::Column, JustifyContent::Center);
list_container->set_display(Display::Block);
list_container->set_flex_basis(100.0f);
list_container->set_align_items(AlignItems::Center);
@ -187,7 +187,7 @@ ModMenu::ModMenu(Element *parent) : Element(parent) {
list_container->set_background_color(Color{ 0, 0, 0, 89 });
list_container->set_border_bottom_left_radius(16.0f);
{
list_scroll_container = context.create_element<ScrollContainer>(ScrollDirection::Vertical, list_container);
list_scroll_container = context.create_element<ScrollContainer>(list_container, ScrollDirection::Vertical);
} // list_container
mod_details_panel = context.create_element<ModDetailsPanel>(body_container);
@ -196,7 +196,7 @@ ModMenu::ModMenu(Element *parent) : Element(parent) {
} // body_container
footer_container = context.create_element<Container>(FlexDirection::Row, JustifyContent::SpaceBetween, this);
footer_container = context.create_element<Container>(this, FlexDirection::Row, JustifyContent::SpaceBetween);
footer_container->set_width(100.0f, recompui::Unit::Percent);
footer_container->set_align_items(recompui::AlignItems::Center);
footer_container->set_background_color(Color{ 0, 0, 0, 89 });
@ -206,7 +206,7 @@ ModMenu::ModMenu(Element *parent) : Element(parent) {
footer_container->set_border_bottom_left_radius(16.0f);
footer_container->set_border_bottom_right_radius(16.0f);
{
refresh_button = context.create_element<Button>("Refresh", recompui::ButtonStyle::Primary, footer_container);
refresh_button = context.create_element<Button>(footer_container, "Refresh", recompui::ButtonStyle::Primary);
refresh_button->add_pressed_callback(std::bind(&ModMenu::refresh_mods, this));
} // footer_container
} // this

View File

@ -4,10 +4,10 @@
#include <memory>
namespace RT64 {
class RenderInterface;
class RenderDevice;
class RenderCommandList;
class RenderFramebuffer;
struct RenderInterface;
struct RenderDevice;
struct RenderCommandList;
struct RenderFramebuffer;
};
namespace Rml {

View File

@ -650,7 +650,6 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
break;
}
recompui::hide_all_contexts();
if (open_config) {
recompui::show_context(recompui::get_config_context_id(), "");
}
@ -770,3 +769,9 @@ Rml::ElementDocument* recompui::load_document(const std::filesystem::path& path)
return ui_state->context->LoadDocument(path.string());
}
Rml::ElementDocument* recompui::create_empty_document() {
std::lock_guard lock{ui_state_mutex};
return ui_state->context->CreateDocument();
}