mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp.git
synced 2025-10-30 08:03:03 +00:00
Config submenu.
This commit is contained in:
parent
f703c918bc
commit
921eb5aee3
26 changed files with 799 additions and 32 deletions
|
|
@ -166,6 +166,7 @@ set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/ui_config_sub_menu.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
|
||||||
|
|
@ -186,11 +187,14 @@ set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeTextField.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeTextField.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/presets.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/presets.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_clickable.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_radio.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,14 @@
|
||||||
}
|
}
|
||||||
.col {
|
.col {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link type="text/template" href="config_menu/general.rml" />
|
<link type="text/template" href="config_menu/general.rml" />
|
||||||
<link type="text/template" href="config_menu/controls.rml" />
|
<link type="text/template" href="config_menu/controls.rml" />
|
||||||
<link type="text/template" href="config_menu/graphics.rml" />
|
<link type="text/template" href="config_menu/graphics.rml" />
|
||||||
<link type="text/template" href="config_menu/sound.rml" />
|
<link type="text/template" href="config_menu/sound.rml" />
|
||||||
<link type="text/template" href="config_menu/mods.rml" />
|
<link type="text/template" href="config_menu/mods.rml" />
|
||||||
<link type="text/template" href="config_menu/debug.rml" />
|
<link type="text/template" href="config_menu/debug.rml" />
|
||||||
<link type="text/template" href="config_menu/cheats.rml" />
|
<link type="text/template" href="config_menu/cheats.rml" />
|
||||||
<link type="text/template" href="components/prompt.rml" />
|
<link type="text/template" href="components/prompt.rml" />
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
<svg src="icons/X.svg" />
|
<svg src="icons/X.svg" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<recomp-config-sub-menu id="config_sub_menu" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="centered-page__controls"
|
class="centered-page__controls"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form class="config__form">
|
<form class="config__form">
|
||||||
<recomp-mod-menu />
|
<recomp-mod-menu id="menu_mods" />
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit b5bf23ddde92dc5bdbf3ab949cbab512fe866b31
|
Subproject commit a893ea6386e0c842f90a726a53c9b9e888797519
|
||||||
45
src/ui/elements/ui_clickable.cpp
Normal file
45
src/ui/elements/ui_clickable.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#include "ui_clickable.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
static const std::string_view hover_state = "hover";
|
||||||
|
static const std::string_view disabled_state = "disabled";
|
||||||
|
|
||||||
|
Clickable::Clickable(Element *parent, bool draggable) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, draggable ? EventType::Drag : EventType::None)) {
|
||||||
|
if (draggable) {
|
||||||
|
set_drag(Drag::Drag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clickable::process_event(const Event &e) {
|
||||||
|
switch (e.type) {
|
||||||
|
case EventType::Click:
|
||||||
|
for (const auto &function : pressed_callbacks) {
|
||||||
|
function(e.click.mouse.x, e.click.mouse.y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EventType::Hover:
|
||||||
|
set_style_enabled(hover_state, e.hover.active);
|
||||||
|
break;
|
||||||
|
case EventType::Enable:
|
||||||
|
set_style_enabled(disabled_state, !e.enable.enable);
|
||||||
|
break;
|
||||||
|
case EventType::Drag:
|
||||||
|
for (const auto &function : dragged_callbacks) {
|
||||||
|
function(e.drag.mouse.x, e.drag.mouse.y, e.drag.phase);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clickable::add_pressed_callback(std::function<void(float, float)> callback) {
|
||||||
|
pressed_callbacks.emplace_back(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clickable::add_dragged_callback(std::function<void(float, float, DragPhase)> callback) {
|
||||||
|
dragged_callbacks.emplace_back(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
20
src/ui/elements/ui_clickable.h
Normal file
20
src/ui/elements/ui_clickable.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui_element.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
class Clickable : public Element {
|
||||||
|
protected:
|
||||||
|
std::vector<std::function<void(float, float)>> pressed_callbacks;
|
||||||
|
std::vector<std::function<void(float, float, DragPhase)>> dragged_callbacks;
|
||||||
|
|
||||||
|
// Element overrides.
|
||||||
|
virtual void process_event(const Event &e) override;
|
||||||
|
public:
|
||||||
|
Clickable(Element *parent, bool draggable = false);
|
||||||
|
void add_pressed_callback(std::function<void(float, float)> callback);
|
||||||
|
void add_dragged_callback(std::function<void(float, float, DragPhase)> callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace recompui
|
||||||
|
|
@ -16,6 +16,7 @@ Element::Element(Rml::Element *base) {
|
||||||
Element::Element(Element* parent, uint32_t events_enabled, Rml::String base_class) {
|
Element::Element(Element* parent, uint32_t events_enabled, Rml::String base_class) {
|
||||||
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);
|
||||||
|
|
||||||
if (parent != nullptr) {
|
if (parent != nullptr) {
|
||||||
base = parent->base->AppendChild(std::move(base_owning));
|
base = parent->base->AppendChild(std::move(base_owning));
|
||||||
parent->add_child(this);
|
parent->add_child(this);
|
||||||
|
|
@ -82,6 +83,12 @@ void Element::register_event_listeners(uint32_t events_enabled) {
|
||||||
base->AddEventListener(Rml::EventId::Mouseover, this);
|
base->AddEventListener(Rml::EventId::Mouseover, this);
|
||||||
base->AddEventListener(Rml::EventId::Mouseout, this);
|
base->AddEventListener(Rml::EventId::Mouseout, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (events_enabled & Events(EventType::Drag)) {
|
||||||
|
base->AddEventListener(Rml::EventId::Drag, this);
|
||||||
|
base->AddEventListener(Rml::EventId::Dragstart, this);
|
||||||
|
base->AddEventListener(Rml::EventId::Dragend, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::apply_style(Style *style) {
|
void Element::apply_style(Style *style) {
|
||||||
|
|
@ -135,6 +142,9 @@ void Element::ProcessEvent(Rml::Event &event) {
|
||||||
case Rml::EventId::Mousedown:
|
case Rml::EventId::Mousedown:
|
||||||
process_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f)));
|
process_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f)));
|
||||||
break;
|
break;
|
||||||
|
case Rml::EventId::Drag:
|
||||||
|
process_event(Event::drag_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), DragPhase::Move));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +164,12 @@ void Element::ProcessEvent(Rml::Event &event) {
|
||||||
case Rml::EventId::Blur:
|
case Rml::EventId::Blur:
|
||||||
process_event(Event::focus_event(false));
|
process_event(Event::focus_event(false));
|
||||||
break;
|
break;
|
||||||
|
case Rml::EventId::Dragstart:
|
||||||
|
process_event(Event::drag_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), DragPhase::Start));
|
||||||
|
break;
|
||||||
|
case Rml::EventId::Dragend:
|
||||||
|
process_event(Event::drag_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), DragPhase::End));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -212,10 +228,10 @@ bool Element::is_enabled() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::set_text(const std::string &text) {
|
void Element::set_text(const std::string &text) {
|
||||||
base->SetInnerRML(text);
|
base->SetInnerRML(std::string(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::set_style_enabled(const std::string_view &style_name, bool enable) {
|
void Element::set_style_enabled(std::string_view style_name, bool enable) {
|
||||||
if (enable && style_active_set.find(style_name) == style_active_set.end()) {
|
if (enable && style_active_set.find(style_name) == style_active_set.end()) {
|
||||||
// Style was disabled and will be enabled.
|
// Style was disabled and will be enabled.
|
||||||
style_active_set.emplace(style_name);
|
style_active_set.emplace(style_name);
|
||||||
|
|
@ -243,4 +259,28 @@ void Element::set_style_enabled(const std::string_view &style_name, bool enable)
|
||||||
apply_styles();
|
apply_styles();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
float Element::get_absolute_left() {
|
||||||
|
return base->GetAbsoluteLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Element::get_absolute_top() {
|
||||||
|
return base->GetAbsoluteTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Element::get_client_left() {
|
||||||
|
return base->GetClientLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Element::get_client_top() {
|
||||||
|
return base->GetClientTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Element::get_client_width() {
|
||||||
|
return base->GetClientWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Element::get_client_height() {
|
||||||
|
return base->GetClientHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -43,14 +43,19 @@ public:
|
||||||
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");
|
||||||
virtual ~Element();
|
virtual ~Element();
|
||||||
void clear_children();
|
void clear_children();
|
||||||
void add_style(Style *style, const 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);
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
void set_text(const std::string &text);
|
void set_text(const std::string &text);
|
||||||
void set_style_enabled(const std::string_view &style_name, bool enabled);
|
void set_style_enabled(std::string_view style_name, bool enabled);
|
||||||
|
|
||||||
bool is_element() override { return true; }
|
bool is_element() override { return true; }
|
||||||
|
float get_absolute_left();
|
||||||
|
float get_absolute_top();
|
||||||
|
float get_client_left();
|
||||||
|
float get_client_top();
|
||||||
|
float get_client_width();
|
||||||
|
float get_client_height();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace recompui
|
} // namespace recompui
|
||||||
5
src/ui/elements/ui_radio.cpp
Normal file
5
src/ui/elements/ui_radio.cpp
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "ui_radio.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
};
|
||||||
7
src/ui/elements/ui_radio.h
Normal file
7
src/ui/elements/ui_radio.h
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui_element.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
} // namespace recompui
|
||||||
142
src/ui/elements/ui_slider.cpp
Normal file
142
src/ui/elements/ui_slider.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#include "ui_slider.h"
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
void Slider::set_value_internal(double v, bool setup, bool trigger_callbacks) {
|
||||||
|
if (step_value != 0.0) {
|
||||||
|
v = std::lround(v / step_value) * step_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != v || setup) {
|
||||||
|
value = v;
|
||||||
|
update_circle_position();
|
||||||
|
update_label_text();
|
||||||
|
|
||||||
|
if (trigger_callbacks) {
|
||||||
|
for (auto callback : value_changed_callbacks) {
|
||||||
|
callback(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::bar_clicked(float x, float) {
|
||||||
|
update_value_from_mouse(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::bar_dragged(float x, float, DragPhase) {
|
||||||
|
update_value_from_mouse(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::circle_dragged(float x, float, DragPhase) {
|
||||||
|
update_value_from_mouse(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::update_value_from_mouse(float x) {
|
||||||
|
double left = slider_element->get_absolute_left();
|
||||||
|
double width = slider_element->get_client_width();
|
||||||
|
double ratio = std::clamp((x - left) / width, 0.0, 1.0);
|
||||||
|
set_value_internal(min_value + ratio * (max_value - min_value), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::update_circle_position() {
|
||||||
|
double ratio = std::clamp((value - min_value) / (max_value - min_value), 0.0, 1.0);
|
||||||
|
circle_element->set_left(slider_width_dp * ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::update_label_text() {
|
||||||
|
char text_buffer[32];
|
||||||
|
int precision = type == SliderType::Double ? 1 : 0;
|
||||||
|
auto result = std::to_chars(text_buffer, text_buffer + sizeof(text_buffer) - 1, value, std::chars_format::fixed, precision);
|
||||||
|
if (result.ec == std::errc()) {
|
||||||
|
if (type == SliderType::Percent) {
|
||||||
|
*result.ptr = '%';
|
||||||
|
result.ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_label->set_text(std::string(text_buffer, result.ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider::Slider(SliderType type, Element *parent) : Element(parent) {
|
||||||
|
this->type = type;
|
||||||
|
|
||||||
|
set_display(Display::Flex);
|
||||||
|
set_flex(1.0f, 1.0f, 100.0f, Unit::Percent);
|
||||||
|
set_flex_direction(FlexDirection::Row);
|
||||||
|
|
||||||
|
value_label = new Label("0", LabelStyle::Small, this);
|
||||||
|
value_label->set_margin_right(20.0f);
|
||||||
|
value_label->set_min_width(60.0f);
|
||||||
|
value_label->set_max_width(60.0f);
|
||||||
|
|
||||||
|
slider_element = new Element(this);
|
||||||
|
slider_element->set_width(slider_width_dp);
|
||||||
|
|
||||||
|
{
|
||||||
|
bar_element = new Clickable(slider_element, true);
|
||||||
|
bar_element->set_width(100.0f, Unit::Percent);
|
||||||
|
bar_element->set_height(2.0f);
|
||||||
|
bar_element->set_margin_top(8.0f);
|
||||||
|
bar_element->set_background_color(Color{ 255, 255, 255, 50 });
|
||||||
|
bar_element->add_pressed_callback(std::bind(&Slider::bar_clicked, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
bar_element->add_dragged_callback(std::bind(&Slider::bar_dragged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||||
|
|
||||||
|
circle_element = new Clickable(slider_element, true);
|
||||||
|
circle_element->set_position(Position::Relative);
|
||||||
|
circle_element->set_width(16.0f);
|
||||||
|
circle_element->set_height(16.0f);
|
||||||
|
circle_element->set_margin_top(-8.0f);
|
||||||
|
circle_element->set_margin_right(-8.0f);
|
||||||
|
circle_element->set_margin_left(-8.0f);
|
||||||
|
circle_element->set_background_color(Color{ 204, 204, 204, 255 });
|
||||||
|
circle_element->set_border_radius(8.0f);
|
||||||
|
circle_element->add_dragged_callback(std::bind(&Slider::circle_dragged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||||
|
circle_element->set_cursor(Cursor::Pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_value_internal(value, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider::~Slider() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::set_value(double v) {
|
||||||
|
set_value_internal(v, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
double Slider::get_value() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
void Slider::set_min_value(double v) {
|
||||||
|
min_value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Slider::get_min_value() const {
|
||||||
|
return min_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::set_max_value(double v) {
|
||||||
|
max_value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Slider::get_max_value() const {
|
||||||
|
return max_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::set_step_value(double v) {
|
||||||
|
step_value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Slider::get_step_value() const {
|
||||||
|
return step_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slider::add_value_changed_callback(std::function<void(double)> callback) {
|
||||||
|
value_changed_callbacks.emplace_back(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace recompui
|
||||||
50
src/ui/elements/ui_slider.h
Normal file
50
src/ui/elements/ui_slider.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui_clickable.h"
|
||||||
|
#include "ui_label.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
enum SliderType {
|
||||||
|
Double,
|
||||||
|
Percent,
|
||||||
|
Integer
|
||||||
|
};
|
||||||
|
|
||||||
|
class Slider : public Element {
|
||||||
|
private:
|
||||||
|
SliderType type = SliderType::Percent;
|
||||||
|
Label *value_label = nullptr;
|
||||||
|
Element *slider_element = nullptr;
|
||||||
|
Clickable *bar_element = nullptr;
|
||||||
|
Clickable *circle_element = nullptr;
|
||||||
|
double value = 50.0;
|
||||||
|
double min_value = 0.0;
|
||||||
|
double max_value = 100.0;
|
||||||
|
double step_value = 1.0;
|
||||||
|
float slider_width_dp = 300.0;
|
||||||
|
std::vector<std::function<void(double)>> value_changed_callbacks;
|
||||||
|
|
||||||
|
void set_value_internal(double v, bool setup, bool trigger_callbacks);
|
||||||
|
void bar_clicked(float x, float y);
|
||||||
|
void bar_dragged(float x, float y, DragPhase phase);
|
||||||
|
void circle_dragged(float x, float y, DragPhase phase);
|
||||||
|
void update_value_from_mouse(float x);
|
||||||
|
void update_circle_position();
|
||||||
|
void update_label_text();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Slider(SliderType type, Element *parent);
|
||||||
|
virtual ~Slider();
|
||||||
|
void set_value(double v);
|
||||||
|
double get_value() const;
|
||||||
|
void set_min_value(double v);
|
||||||
|
double get_min_value() const;
|
||||||
|
void set_max_value(double v);
|
||||||
|
double get_max_value() const;
|
||||||
|
void set_step_value(double v);
|
||||||
|
double get_step_value() const;
|
||||||
|
void add_value_changed_callback(std::function<void(double)> callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace recompui
|
||||||
|
|
@ -67,6 +67,24 @@ namespace recompui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Rml::Style::Drag to_rml(Drag drag) {
|
||||||
|
switch (drag) {
|
||||||
|
case Drag::None:
|
||||||
|
return Rml::Style::Drag::None;
|
||||||
|
case Drag::Drag:
|
||||||
|
return Rml::Style::Drag::Drag;
|
||||||
|
case Drag::DragDrop:
|
||||||
|
return Rml::Style::Drag::DragDrop;
|
||||||
|
case Drag::Block:
|
||||||
|
return Rml::Style::Drag::Block;
|
||||||
|
case Drag::Clone:
|
||||||
|
return Rml::Style::Drag::Clone;
|
||||||
|
default:
|
||||||
|
assert(false && "Unknown drag.");
|
||||||
|
return Rml::Style::Drag::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Style::set_property(Rml::PropertyId property_id, const Rml::Property &property, Animation) {
|
void Style::set_property(Rml::PropertyId property_id, const Rml::Property &property, Animation) {
|
||||||
property_map[property_id] = property;
|
property_map[property_id] = property;
|
||||||
}
|
}
|
||||||
|
|
@ -437,4 +455,8 @@ namespace recompui {
|
||||||
set_property(Rml::PropertyId::ColumnGap, Rml::Property(size, to_rml(unit)), animation);
|
set_property(Rml::PropertyId::ColumnGap, Rml::Property(size, to_rml(unit)), animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Style::set_drag(Drag drag) {
|
||||||
|
set_property(Rml::PropertyId::Drag, to_rml(drag), Animation());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace recompui
|
} // namespace recompui
|
||||||
|
|
@ -82,7 +82,7 @@ namespace recompui {
|
||||||
void set_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
void set_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||||
void set_row_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
void set_row_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||||
void set_column_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
void set_column_gap(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||||
|
void set_drag(Drag drag);
|
||||||
virtual bool is_element() { return false; }
|
virtual bool is_element() { return false; }
|
||||||
ResourceId get_resource_id() { return resource_id; }
|
ResourceId get_resource_id() { return resource_id; }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -46,16 +46,16 @@ namespace recompui {
|
||||||
floater->add_style(&floater_disabled_style, disabled_state);
|
floater->add_style(&floater_disabled_style, disabled_state);
|
||||||
floater->add_style(&floater_disabled_checked_style, { checked_state, disabled_state });
|
floater->add_style(&floater_disabled_checked_style, { checked_state, disabled_state });
|
||||||
|
|
||||||
set_checked_internal(false, false, true);
|
set_checked_internal(false, false, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toggle::set_checked_internal(bool checked, bool animate, bool setup) {
|
void Toggle::set_checked_internal(bool checked, bool animate, bool setup, bool trigger_callbacks) {
|
||||||
if (this->checked != checked || setup) {
|
if (this->checked != checked || setup) {
|
||||||
this->checked = checked;
|
this->checked = checked;
|
||||||
|
|
||||||
floater->set_left(floater_left_target(), Unit::Dp, animate ? Animation::tween(0.1f) : Animation::set());
|
floater->set_left(floater_left_target(), Unit::Dp, animate ? Animation::tween(0.1f) : Animation::set());
|
||||||
|
|
||||||
if (!setup) {
|
if (trigger_callbacks) {
|
||||||
for (const auto &function : checked_callbacks) {
|
for (const auto &function : checked_callbacks) {
|
||||||
function(checked);
|
function(checked);
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +74,7 @@ namespace recompui {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case EventType::Click:
|
case EventType::Click:
|
||||||
if (is_enabled()) {
|
if (is_enabled()) {
|
||||||
set_checked_internal(!checked, true, false);
|
set_checked_internal(!checked, true, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -93,7 +93,7 @@ namespace recompui {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toggle::set_checked(bool checked) {
|
void Toggle::set_checked(bool checked) {
|
||||||
set_checked_internal(checked, false, false);
|
set_checked_internal(checked, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toggle::is_checked() const {
|
bool Toggle::is_checked() const {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace recompui {
|
||||||
Style floater_disabled_checked_style;
|
Style floater_disabled_checked_style;
|
||||||
bool checked = false;
|
bool checked = false;
|
||||||
|
|
||||||
void set_checked_internal(bool checked, bool animate, bool setup);
|
void set_checked_internal(bool checked, bool animate, bool setup, bool trigger_callbacks);
|
||||||
float floater_left_target() const;
|
float floater_left_target() const;
|
||||||
|
|
||||||
// Element overrides.
|
// Element overrides.
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,17 @@ namespace recompui {
|
||||||
Focus,
|
Focus,
|
||||||
Hover,
|
Hover,
|
||||||
Enable,
|
Enable,
|
||||||
|
Drag,
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DragPhase {
|
||||||
|
None,
|
||||||
|
Start,
|
||||||
|
Move,
|
||||||
|
End
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
|
template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
|
||||||
constexpr uint32_t Events(Enum first) {
|
constexpr uint32_t Events(Enum first) {
|
||||||
return 1u << static_cast<uint32_t>(first);
|
return 1u << static_cast<uint32_t>(first);
|
||||||
|
|
@ -44,6 +52,8 @@ namespace recompui {
|
||||||
EventType type;
|
EventType type;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
uint64_t raw;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
Mouse mouse;
|
Mouse mouse;
|
||||||
} click;
|
} click;
|
||||||
|
|
@ -59,6 +69,11 @@ namespace recompui {
|
||||||
struct {
|
struct {
|
||||||
bool enable;
|
bool enable;
|
||||||
} enable;
|
} enable;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
Mouse mouse;
|
||||||
|
DragPhase phase;
|
||||||
|
} drag;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Event click_event(float x, float y) {
|
static Event click_event(float x, float y) {
|
||||||
|
|
@ -89,6 +104,15 @@ namespace recompui {
|
||||||
e.enable.enable = enable;
|
e.enable.enable = enable;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Event drag_event(float x, float y, DragPhase phase) {
|
||||||
|
Event e = {};
|
||||||
|
e.type = EventType::Drag;
|
||||||
|
e.drag.mouse.x = x;
|
||||||
|
e.drag.mouse.y = y;
|
||||||
|
e.drag.phase = phase;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Display {
|
enum class Display {
|
||||||
|
|
@ -154,6 +178,14 @@ namespace recompui {
|
||||||
Justify
|
Justify
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Drag {
|
||||||
|
None,
|
||||||
|
Drag,
|
||||||
|
DragDrop,
|
||||||
|
Block,
|
||||||
|
Clone
|
||||||
|
};
|
||||||
|
|
||||||
struct Animation {
|
struct Animation {
|
||||||
AnimationType type = AnimationType::None;
|
AnimationType type = AnimationType::None;
|
||||||
float duration = 0.0f;
|
float duration = 0.0f;
|
||||||
|
|
|
||||||
200
src/ui/ui_config_sub_menu.cpp
Normal file
200
src/ui/ui_config_sub_menu.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
#include "ui_config_sub_menu.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
// ConfigOptionElement
|
||||||
|
|
||||||
|
|
||||||
|
void ConfigOptionElement::process_event(const Event &e) {
|
||||||
|
switch (e.type) {
|
||||||
|
case EventType::Hover:
|
||||||
|
hover_callback(this, e.hover.active);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false && "Unknown event type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigOptionElement::ConfigOptionElement(Element *parent) : Element(parent, Events(EventType::Hover)) {
|
||||||
|
set_min_height(100.0f);
|
||||||
|
|
||||||
|
name_label = new Label(LabelStyle::Normal, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigOptionElement::~ConfigOptionElement() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigOptionElement::set_name(std::string_view name) {
|
||||||
|
this->name = name;
|
||||||
|
name_label->set_text(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigOptionElement::set_description(std::string_view description) {
|
||||||
|
this->description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigOptionElement::set_hover_callback(std::function<void(ConfigOptionElement *, bool)> callback) {
|
||||||
|
hover_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &ConfigOptionElement::get_description() const {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigOptionSlider
|
||||||
|
|
||||||
|
void ConfigOptionSlider::slider_value_changed(double v) {
|
||||||
|
// TODO: Hook up to whatever API Recomp exposes to set the value of the persisent configuration in mods.
|
||||||
|
printf("%s changed to %f.\n", name.c_str(), v);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigOptionSlider::ConfigOptionSlider(Element *parent) : ConfigOptionElement(parent) {
|
||||||
|
slider = new Slider(SliderType::Percent, this);
|
||||||
|
slider->add_value_changed_callback(std::bind(&ConfigOptionSlider::slider_value_changed, this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigOptionSlider::~ConfigOptionSlider() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigOptionSlider::set_value(double v) {
|
||||||
|
slider->set_value(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigOptionSlider::set_min_value(double v) {
|
||||||
|
slider->set_min_value(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigOptionSlider::set_max_value(double v) {
|
||||||
|
slider->set_max_value(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigSubMenu
|
||||||
|
|
||||||
|
void ConfigSubMenu::back_button_pressed() {
|
||||||
|
if (quit_sub_menu_callback != nullptr) {
|
||||||
|
quit_sub_menu_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::option_hovered(ConfigOptionElement *option, bool active) {
|
||||||
|
if (active) {
|
||||||
|
hover_option_elements.emplace(option);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hover_option_elements.erase(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hover_option_elements.empty()) {
|
||||||
|
description_label->set_text("");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
description_label->set_text((*hover_option_elements.begin())->get_description());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSubMenu::ConfigSubMenu(Element *parent) : Element(parent) {
|
||||||
|
set_display(Display::Flex);
|
||||||
|
set_flex(1, 1, 100.0f, Unit::Percent);
|
||||||
|
set_flex_direction(FlexDirection::Column);
|
||||||
|
set_height(100.0f, Unit::Percent);
|
||||||
|
|
||||||
|
header_container = new Container(FlexDirection::Row, JustifyContent::FlexStart, this);
|
||||||
|
|
||||||
|
{
|
||||||
|
back_button = new Button("Back", ButtonStyle::Secondary, header_container);
|
||||||
|
back_button->add_pressed_callback(std::bind(&ConfigSubMenu::back_button_pressed, this));
|
||||||
|
title_label = new Label("Title", LabelStyle::Large, header_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_container = new Container(FlexDirection::Row, JustifyContent::SpaceEvenly, this);
|
||||||
|
{
|
||||||
|
config_container = new Container(FlexDirection::Column, JustifyContent::Center, body_container);
|
||||||
|
config_container->set_display(Display::Block);
|
||||||
|
config_container->set_flex_basis(100.0f);
|
||||||
|
config_container->set_align_items(AlignItems::Center);
|
||||||
|
{
|
||||||
|
config_scroll_container = new ScrollContainer(ScrollDirection::Vertical, config_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
description_label = new Label("Description", LabelStyle::Small, body_container);
|
||||||
|
description_label->set_min_width(800.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSubMenu::~ConfigSubMenu() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::enter(std::string_view title) {
|
||||||
|
title_label->set_text(title);
|
||||||
|
|
||||||
|
if (enter_sub_menu_callback != nullptr) {
|
||||||
|
enter_sub_menu_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::clear_options() {
|
||||||
|
config_scroll_container->clear_children();
|
||||||
|
config_option_elements.clear();
|
||||||
|
hover_option_elements.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::add_option(ConfigOptionElement *option, std::string_view name, std::string_view description) {
|
||||||
|
option->set_name(name);
|
||||||
|
option->set_description(description);
|
||||||
|
option->set_hover_callback(std::bind(&ConfigSubMenu::option_hovered, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
config_option_elements.emplace_back(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::add_slider_option(std::string_view name, std::string_view description, double min, double max) {
|
||||||
|
ConfigOptionSlider *option_slider = new ConfigOptionSlider(config_scroll_container);
|
||||||
|
option_slider->set_min_value(min);
|
||||||
|
option_slider->set_max_value(max);
|
||||||
|
add_option(option_slider, name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::set_enter_sub_menu_callback(std::function<void()> callback) {
|
||||||
|
enter_sub_menu_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigSubMenu::set_quit_sub_menu_callback(std::function<void()> callback) {
|
||||||
|
quit_sub_menu_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElementConfigSubMenu
|
||||||
|
|
||||||
|
ElementConfigSubMenu::ElementConfigSubMenu(const Rml::String &tag) : Rml::Element(tag) {
|
||||||
|
SetProperty(Rml::PropertyId::Display, Rml::Style::Display::None);
|
||||||
|
SetProperty("width", "100%");
|
||||||
|
SetProperty("height", "100%");
|
||||||
|
|
||||||
|
recompui::Element this_compat(this);
|
||||||
|
config_sub_menu = std::make_unique<ConfigSubMenu>(&this_compat);
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementConfigSubMenu::~ElementConfigSubMenu() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementConfigSubMenu::set_display(bool display) {
|
||||||
|
SetProperty(Rml::PropertyId::Display, display ? Rml::Style::Display::Block : Rml::Style::Display::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementConfigSubMenu::set_enter_sub_menu_callback(std::function<void()> callback) {
|
||||||
|
config_sub_menu->set_enter_sub_menu_callback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementConfigSubMenu::set_quit_sub_menu_callback(std::function<void()> callback) {
|
||||||
|
config_sub_menu->set_quit_sub_menu_callback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSubMenu *ElementConfigSubMenu::get_config_sub_menu_element() const {
|
||||||
|
return config_sub_menu.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
src/ui/ui_config_sub_menu.h
Normal file
85
src/ui/ui_config_sub_menu.h
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
#ifndef RECOMPUI_CONFIG_SUB_MENU_H
|
||||||
|
#define RECOMPUI_CONFIG_SUB_MENU_H
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "elements/ui_button.h"
|
||||||
|
#include "elements/ui_container.h"
|
||||||
|
#include "elements/ui_label.h"
|
||||||
|
#include "elements/ui_scroll_container.h"
|
||||||
|
#include "elements/ui_slider.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
class ConfigOptionElement : public Element {
|
||||||
|
protected:
|
||||||
|
Label *name_label = nullptr;
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::function<void(ConfigOptionElement *, bool)> hover_callback = nullptr;
|
||||||
|
|
||||||
|
virtual void process_event(const Event &e) override;
|
||||||
|
public:
|
||||||
|
ConfigOptionElement(Element *parent);
|
||||||
|
virtual ~ConfigOptionElement();
|
||||||
|
void set_name(std::string_view name);
|
||||||
|
void set_description(std::string_view description);
|
||||||
|
void set_hover_callback(std::function<void(ConfigOptionElement *, bool)> callback);
|
||||||
|
const std::string &get_description() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigOptionSlider : public ConfigOptionElement {
|
||||||
|
protected:
|
||||||
|
Slider *slider = nullptr;
|
||||||
|
|
||||||
|
void slider_value_changed(double v);
|
||||||
|
public:
|
||||||
|
ConfigOptionSlider(Element *parent);
|
||||||
|
virtual ~ConfigOptionSlider();
|
||||||
|
void set_value(double v);
|
||||||
|
void set_min_value(double v);
|
||||||
|
void set_max_value(double v);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigSubMenu : public Element {
|
||||||
|
private:
|
||||||
|
Container *header_container = nullptr;
|
||||||
|
Button *back_button = nullptr;
|
||||||
|
Label *title_label = nullptr;
|
||||||
|
Container *body_container = nullptr;
|
||||||
|
Label *description_label = nullptr;
|
||||||
|
Container *config_container = nullptr;
|
||||||
|
ScrollContainer *config_scroll_container = nullptr;
|
||||||
|
std::function<void()> enter_sub_menu_callback = nullptr;
|
||||||
|
std::function<void()> quit_sub_menu_callback = nullptr;
|
||||||
|
std::vector<ConfigOptionElement *> config_option_elements;
|
||||||
|
std::unordered_set<ConfigOptionElement *> hover_option_elements;
|
||||||
|
|
||||||
|
void back_button_pressed();
|
||||||
|
void option_hovered(ConfigOptionElement *option, bool active);
|
||||||
|
void add_option(ConfigOptionElement *option, std::string_view name, std::string_view description);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConfigSubMenu(Element *parent);
|
||||||
|
virtual ~ConfigSubMenu();
|
||||||
|
void enter(std::string_view title);
|
||||||
|
void clear_options();
|
||||||
|
void add_slider_option(std::string_view name, std::string_view description, double min, double max);
|
||||||
|
void set_enter_sub_menu_callback(std::function<void()> callback);
|
||||||
|
void set_quit_sub_menu_callback(std::function<void()> callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ElementConfigSubMenu : public Rml::Element {
|
||||||
|
public:
|
||||||
|
ElementConfigSubMenu(const Rml::String &tag);
|
||||||
|
virtual ~ElementConfigSubMenu();
|
||||||
|
void set_display(bool display);
|
||||||
|
void set_enter_sub_menu_callback(std::function<void()> callback);
|
||||||
|
void set_quit_sub_menu_callback(std::function<void()> callback);
|
||||||
|
ConfigSubMenu *get_config_sub_menu_element() const;
|
||||||
|
private:
|
||||||
|
std::unique_ptr<ConfigSubMenu> config_sub_menu;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -20,6 +20,7 @@ static RecompElementConfig custom_elements[] = {
|
||||||
CUSTOM_ELEMENT("recomp-option-type-radio-tabs", recompui::ElementOptionTypeRadioTabs),
|
CUSTOM_ELEMENT("recomp-option-type-radio-tabs", recompui::ElementOptionTypeRadioTabs),
|
||||||
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
||||||
CUSTOM_ELEMENT("recomp-mod-menu", recompui::ElementModMenu),
|
CUSTOM_ELEMENT("recomp-mod-menu", recompui::ElementModMenu),
|
||||||
|
CUSTOM_ELEMENT("recomp-config-sub-menu", recompui::ElementConfigSubMenu),
|
||||||
};
|
};
|
||||||
|
|
||||||
void recompui::register_custom_elements() {
|
void recompui::register_custom_elements() {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "elements/ElementOptionTypeTextField.h"
|
#include "elements/ElementOptionTypeTextField.h"
|
||||||
#include "elements/ElementDescription.h"
|
#include "elements/ElementDescription.h"
|
||||||
#include "ui_mod_menu.h"
|
#include "ui_mod_menu.h"
|
||||||
|
#include "ui_config_sub_menu.h"
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
void register_custom_elements();
|
void register_custom_elements();
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ ModDetailsPanel::ModDetailsPanel(Element *parent) : Element(parent) {
|
||||||
enable_toggle = context.create_element<Toggle>(buttons_container);
|
enable_toggle = context.create_element<Toggle>(buttons_container);
|
||||||
enable_toggle->add_checked_callback(std::bind(&ModDetailsPanel::enable_toggle_checked, this, std::placeholders::_1));
|
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>("Configure", recompui::ButtonStyle::Secondary, buttons_container);
|
||||||
|
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>("Erase", recompui::ButtonStyle::Secondary, buttons_container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,10 +90,20 @@ void ModDetailsPanel::set_mod_toggled_callback(std::function<void(bool)> callbac
|
||||||
mod_toggled_callback = callback;
|
mod_toggled_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModDetailsPanel::set_mod_configure_pressed_callback(std::function<void()> callback) {
|
||||||
|
mod_configure_pressed_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
void ModDetailsPanel::enable_toggle_checked(bool checked) {
|
void ModDetailsPanel::enable_toggle_checked(bool checked) {
|
||||||
if (mod_toggled_callback != nullptr) {
|
if (mod_toggled_callback != nullptr) {
|
||||||
mod_toggled_callback(checked);
|
mod_toggled_callback(checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModDetailsPanel::configure_button_pressed() {
|
||||||
|
if (mod_configure_pressed_callback != nullptr) {
|
||||||
|
mod_configure_pressed_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace recompui
|
} // namespace recompui
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ public:
|
||||||
virtual ~ModDetailsPanel();
|
virtual ~ModDetailsPanel();
|
||||||
void set_mod_details(const recomp::mods::ModDetails& details, bool mod_enabled, bool toggle_enabled);
|
void set_mod_details(const recomp::mods::ModDetails& details, bool mod_enabled, bool toggle_enabled);
|
||||||
void set_mod_toggled_callback(std::function<void(bool)> callback);
|
void set_mod_toggled_callback(std::function<void(bool)> callback);
|
||||||
|
void set_mod_configure_pressed_callback(std::function<void()> callback);
|
||||||
private:
|
private:
|
||||||
recomp::mods::ModDetails cur_details;
|
recomp::mods::ModDetails cur_details;
|
||||||
Container *thumbnail_container = nullptr;
|
Container *thumbnail_container = nullptr;
|
||||||
|
|
@ -32,9 +33,11 @@ private:
|
||||||
Toggle *enable_toggle = nullptr;
|
Toggle *enable_toggle = nullptr;
|
||||||
Button *configure_button = nullptr;
|
Button *configure_button = nullptr;
|
||||||
Button *erase_button = nullptr;
|
Button *erase_button = nullptr;
|
||||||
std::function<void(bool)> mod_toggled_callback = {};
|
std::function<void(bool)> mod_toggled_callback = nullptr;
|
||||||
|
std::function<void()> mod_configure_pressed_callback = nullptr;
|
||||||
|
|
||||||
void enable_toggle_checked(bool checked);
|
void enable_toggle_checked(bool checked);
|
||||||
|
void configure_button_pressed();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace recompui
|
} // namespace recompui
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,10 @@ void ModMenu::set_active_mod(int32_t mod_index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModMenu::set_config_sub_menu(ConfigSubMenu *config_sub_menu) {
|
||||||
|
ext_config_sub_menu = config_sub_menu;
|
||||||
|
}
|
||||||
|
|
||||||
void ModMenu::refresh_mods() {
|
void ModMenu::refresh_mods() {
|
||||||
recomp::mods::scan_mods();
|
recomp::mods::scan_mods();
|
||||||
mod_details = recomp::mods::get_mod_details(game_mod_id);
|
mod_details = recomp::mods::get_mod_details(game_mod_id);
|
||||||
|
|
@ -93,6 +97,24 @@ void ModMenu::mod_toggled(bool enabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModMenu::mod_configure_requested() {
|
||||||
|
if (active_mod_index >= 0) {
|
||||||
|
ext_config_sub_menu->clear_options();
|
||||||
|
ext_config_sub_menu->add_slider_option("Simple Option", "Description for simple option.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Slider Option", "Description for slider option.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option B", "Description for option B.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option C", "Description for option C.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option D", "Description for option D.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option E", "Description for option E.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option F", "Description for option F.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option G", "Description for option G.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option H", "Description for option H.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option J", "Description for option J.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->add_slider_option("Option K", "Description for option K.", 0.0, 100.0);
|
||||||
|
ext_config_sub_menu->enter(mod_details[active_mod_index].mod_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ModMenu::create_mod_list() {
|
void ModMenu::create_mod_list() {
|
||||||
ContextId context = get_current_context();
|
ContextId context = get_current_context();
|
||||||
|
|
||||||
|
|
@ -140,6 +162,7 @@ ModMenu::ModMenu(Element *parent) : Element(parent) {
|
||||||
|
|
||||||
mod_details_panel = context.create_element<ModDetailsPanel>(body_container);
|
mod_details_panel = context.create_element<ModDetailsPanel>(body_container);
|
||||||
mod_details_panel->set_mod_toggled_callback(std::bind(&ModMenu::mod_toggled, this, std::placeholders::_1));
|
mod_details_panel->set_mod_toggled_callback(std::bind(&ModMenu::mod_toggled, this, std::placeholders::_1));
|
||||||
|
mod_details_panel->set_mod_configure_pressed_callback(std::bind(&ModMenu::mod_configure_requested, this));
|
||||||
} // body_container
|
} // body_container
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -179,4 +202,8 @@ ElementModMenu::~ElementModMenu() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ElementModMenu::set_config_sub_menu(ConfigSubMenu *config_sub_menu) {
|
||||||
|
mod_menu->set_config_sub_menu(config_sub_menu);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace recompui
|
} // namespace recompui
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "librecomp/mods.hpp"
|
#include "librecomp/mods.hpp"
|
||||||
#include "elements/ui_scroll_container.h"
|
#include "elements/ui_scroll_container.h"
|
||||||
|
#include "ui_config_sub_menu.h"
|
||||||
#include "ui_mod_details_panel.h"
|
#include "ui_mod_details_panel.h"
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
@ -29,9 +30,11 @@ public:
|
||||||
ModMenu(Element *parent);
|
ModMenu(Element *parent);
|
||||||
virtual ~ModMenu();
|
virtual ~ModMenu();
|
||||||
void set_active_mod(int32_t mod_index);
|
void set_active_mod(int32_t mod_index);
|
||||||
|
void set_config_sub_menu(ConfigSubMenu *config_sub_menu);
|
||||||
private:
|
private:
|
||||||
void refresh_mods();
|
void refresh_mods();
|
||||||
void mod_toggled(bool enabled);
|
void mod_toggled(bool enabled);
|
||||||
|
void mod_configure_requested();
|
||||||
void create_mod_list();
|
void create_mod_list();
|
||||||
|
|
||||||
Container *body_container = nullptr;
|
Container *body_container = nullptr;
|
||||||
|
|
@ -40,6 +43,7 @@ private:
|
||||||
ModDetailsPanel *mod_details_panel = nullptr;
|
ModDetailsPanel *mod_details_panel = nullptr;
|
||||||
Container *footer_container = nullptr;
|
Container *footer_container = nullptr;
|
||||||
Button *refresh_button = nullptr;
|
Button *refresh_button = nullptr;
|
||||||
|
ConfigSubMenu *ext_config_sub_menu = nullptr;
|
||||||
int32_t active_mod_index = -1;
|
int32_t active_mod_index = -1;
|
||||||
std::vector<ModEntry *> mod_entries;
|
std::vector<ModEntry *> mod_entries;
|
||||||
std::vector<recomp::mods::ModDetails> mod_details{};
|
std::vector<recomp::mods::ModDetails> mod_details{};
|
||||||
|
|
@ -50,6 +54,7 @@ class ElementModMenu : public Rml::Element {
|
||||||
public:
|
public:
|
||||||
ElementModMenu(const Rml::String& tag);
|
ElementModMenu(const Rml::String& tag);
|
||||||
virtual ~ElementModMenu();
|
virtual ~ElementModMenu();
|
||||||
|
void set_config_sub_menu(ConfigSubMenu *config_sub_menu);
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ModMenu> mod_menu;
|
std::unique_ptr<ModMenu> mod_menu;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include "RmlUi_Platform_SDL.h"
|
#include "RmlUi_Platform_SDL.h"
|
||||||
|
|
||||||
#include "ui_elements.h"
|
#include "ui_elements.h"
|
||||||
|
#include "ui_mod_menu.h"
|
||||||
#include "librecomp/config.hpp"
|
#include "librecomp/config.hpp"
|
||||||
|
|
||||||
#include "InterfaceVS.hlsl.spirv.h"
|
#include "InterfaceVS.hlsl.spirv.h"
|
||||||
|
|
@ -764,7 +765,7 @@ Rml::Element* find_autofocus_element(Rml::Element* start) {
|
||||||
|
|
||||||
struct UIContext {
|
struct UIContext {
|
||||||
struct UIRenderContext render;
|
struct UIRenderContext render;
|
||||||
class {
|
class Context {
|
||||||
std::unordered_map<recompui::Menu, std::unique_ptr<recompui::MenuController>> menus;
|
std::unordered_map<recompui::Menu, std::unique_ptr<recompui::MenuController>> menus;
|
||||||
std::unordered_map<recompui::Menu, Rml::ElementDocument*> documents;
|
std::unordered_map<recompui::Menu, Rml::ElementDocument*> documents;
|
||||||
Rml::ElementDocument* current_document;
|
Rml::ElementDocument* current_document;
|
||||||
|
|
@ -787,6 +788,10 @@ struct UIContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap_document(recompui::Menu menu) {
|
void swap_document(recompui::Menu menu) {
|
||||||
|
if (menu != recompui::Menu::Config) {
|
||||||
|
quit_sub_menu();
|
||||||
|
}
|
||||||
|
|
||||||
if (current_document != nullptr) {
|
if (current_document != nullptr) {
|
||||||
Rml::Element* window_el = current_document->GetElementById("window");
|
Rml::Element* window_el = current_document->GetElementById("window");
|
||||||
if (window_el != nullptr) {
|
if (window_el != nullptr) {
|
||||||
|
|
@ -815,21 +820,77 @@ struct UIContext {
|
||||||
mouse_is_active = false;
|
mouse_is_active = false;
|
||||||
mouse_is_active_changed = false;
|
mouse_is_active_changed = false;
|
||||||
mouse_is_active_initialized = false;
|
mouse_is_active_initialized = false;
|
||||||
|
|
||||||
|
if (menu == recompui::Menu::Config) {
|
||||||
|
recompui::ElementModMenu *mods_menu = get_mods_menu();
|
||||||
|
recompui::ElementConfigSubMenu *config_sub_menu = get_config_sub_menu();
|
||||||
|
if (mods_menu != nullptr && config_sub_menu != nullptr) {
|
||||||
|
mods_menu->set_config_sub_menu(config_sub_menu->get_config_sub_menu_element());
|
||||||
|
config_sub_menu->set_enter_sub_menu_callback(std::bind(&Context::enter_sub_menu, this));
|
||||||
|
config_sub_menu->set_quit_sub_menu_callback(std::bind(&Context::quit_sub_menu, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::ElementTabSet *get_config_tabset() {
|
||||||
|
if (current_document != nullptr) {
|
||||||
|
Rml::Element *config_tabset_base = current_document->GetElementById("config_tabset");
|
||||||
|
if (config_tabset_base != nullptr) {
|
||||||
|
return rmlui_dynamic_cast<Rml::ElementTabSet *>(config_tabset_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
recompui::ElementModMenu *get_mods_menu() {
|
||||||
|
if (current_document != nullptr) {
|
||||||
|
Rml::Element *menu_mods_base = current_document->GetElementById("menu_mods");
|
||||||
|
if (menu_mods_base != nullptr) {
|
||||||
|
return rmlui_dynamic_cast<recompui::ElementModMenu *>(menu_mods_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
recompui::ElementConfigSubMenu *get_config_sub_menu() {
|
||||||
|
if (current_document != nullptr) {
|
||||||
|
Rml::Element *config_sub_menu_base = current_document->GetElementById("config_sub_menu");
|
||||||
|
if (config_sub_menu_base != nullptr) {
|
||||||
|
return rmlui_dynamic_cast<recompui::ElementConfigSubMenu *>(config_sub_menu_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap_config_menu(recompui::ConfigSubmenu submenu) {
|
void swap_config_menu(recompui::ConfigSubmenu submenu) {
|
||||||
if (current_document != nullptr) {
|
Rml::ElementTabSet* config_tabset = get_config_tabset();
|
||||||
Rml::Element* config_tabset_base = current_document->GetElementById("config_tabset");
|
if (config_tabset != nullptr) {
|
||||||
if (config_tabset_base != nullptr) {
|
config_tabset->SetActiveTab(static_cast<int>(submenu));
|
||||||
Rml::ElementTabSet* config_tabset = rmlui_dynamic_cast<Rml::ElementTabSet*>(config_tabset_base);
|
prev_focused = nullptr;
|
||||||
if (config_tabset != nullptr) {
|
mouse_is_active = false;
|
||||||
config_tabset->SetActiveTab(static_cast<int>(submenu));
|
mouse_is_active_changed = false;
|
||||||
prev_focused = nullptr;
|
mouse_is_active_initialized = false;
|
||||||
mouse_is_active = false;
|
}
|
||||||
mouse_is_active_changed = false;
|
}
|
||||||
mouse_is_active_initialized = false;
|
|
||||||
}
|
void enter_sub_menu() {
|
||||||
}
|
Rml::ElementTabSet *config_tabset = get_config_tabset();
|
||||||
|
recompui::ElementConfigSubMenu *config_sub_menu = get_config_sub_menu();
|
||||||
|
if (config_tabset != nullptr && config_sub_menu != nullptr) {
|
||||||
|
config_tabset->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::None);
|
||||||
|
config_sub_menu->set_display(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void quit_sub_menu() {
|
||||||
|
Rml::ElementTabSet *config_tabset = get_config_tabset();
|
||||||
|
recompui::ElementConfigSubMenu *config_sub_menu = get_config_sub_menu();
|
||||||
|
if (config_tabset != nullptr && config_sub_menu != nullptr) {
|
||||||
|
config_tabset->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::Flex);
|
||||||
|
config_sub_menu->set_display(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue