Text input.

This commit is contained in:
Dario 2025-01-19 00:29:40 -03:00 committed by Mr-Wiseguy
parent 743f048826
commit 4967d31125
12 changed files with 189 additions and 69 deletions

View File

@ -186,6 +186,7 @@ set (SOURCES
${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_text_input.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp

View File

@ -68,10 +68,10 @@ namespace recompui {
}
break;
case EventType::Hover:
set_style_enabled(hover_state, e.hover.active);
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active);
break;
case EventType::Enable:
set_style_enabled(disabled_state, !e.enable.enable);
set_style_enabled(disabled_state, !std::get<EventEnable>(e.variant).active);
break;
default:
assert(false && "Unknown event type.");

View File

@ -13,22 +13,26 @@ namespace recompui {
void Clickable::process_event(const Event &e) {
switch (e.type) {
case EventType::Click:
case EventType::Click: {
const EventClick &click = std::get<EventClick>(e.variant);
for (const auto &function : pressed_callbacks) {
function(e.click.mouse.x, e.click.mouse.y);
function(click.x, click.y);
}
break;
}
case EventType::Hover:
set_style_enabled(hover_state, e.hover.active);
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active);
break;
case EventType::Enable:
set_style_enabled(disabled_state, !e.enable.enable);
set_style_enabled(disabled_state, !std::get<EventEnable>(e.variant).active);
break;
case EventType::Drag:
case EventType::Drag: {
const EventDrag &drag = std::get<EventDrag>(e.variant);
for (const auto &function : dragged_callbacks) {
function(e.drag.mouse.x, e.drag.mouse.y, e.drag.phase);
function(drag.x, drag.y, drag.phase);
}
break;
}
default:
break;
}

View File

@ -89,6 +89,10 @@ void Element::register_event_listeners(uint32_t events_enabled) {
base->AddEventListener(Rml::EventId::Dragstart, this);
base->AddEventListener(Rml::EventId::Dragend, this);
}
if (events_enabled & Events(EventType::Text)) {
base->AddEventListener(Rml::EventId::Change, this);
}
}
void Element::apply_style(Style *style) {
@ -170,6 +174,16 @@ void Element::ProcessEvent(Rml::Event &event) {
case Rml::EventId::Dragend:
process_event(Event::drag_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), DragPhase::End));
break;
case Rml::EventId::Change: {
if (events_enabled & Events(EventType::Text)) {
Rml::Variant *value_variant = base->GetAttribute("value");
if (value_variant != nullptr) {
process_event(Event::text_event(value_variant->Get<std::string>()));
}
}
break;
}
default:
break;
}
@ -180,6 +194,10 @@ void Element::ProcessEvent(Rml::Event &event) {
}
}
void Element::set_attribute(const Rml::String &attribute_key, const Rml::String &attribute_value) {
base->SetAttribute(attribute_key, attribute_value);
}
void Element::process_event(const Event &) {
// Does nothing by default.
}

View File

@ -34,6 +34,8 @@ private:
// Rml::EventListener overrides.
void ProcessEvent(Rml::Event &event) override final;
protected:
// Use of this method in inherited classes is discouraged unless it's necessary.
void set_attribute(const Rml::String &attribute_key, const Rml::String &attribute_value);
virtual void process_event(const Event &e);
public:
// Used for backwards compatibility with legacy UI elements.

View File

@ -0,0 +1,40 @@
#include "ui_text_input.h"
#include <cassert>
namespace recompui {
void TextInput::process_event(const Event &e) {
switch (e.type) {
case EventType::Text: {
const EventText &event = std::get<EventText>(e.variant);
text = event.text;
for (const auto &function : text_changed_callbacks) {
function(text);
}
break;
}
default:
break;
}
}
TextInput::TextInput(Element *parent) : Element(parent, Events(EventType::Text), "input") {
}
void TextInput::set_text(std::string_view text) {
this->text = std::string(text);
set_attribute("value", this->text);
}
const std::string &TextInput::get_text() {
return text;
}
void TextInput::add_text_changed_callback(std::function<void(const std::string &)> callback) {
text_changed_callbacks.emplace_back(callback);
}
};

View File

@ -0,0 +1,20 @@
#pragma once
#include "ui_element.h"
namespace recompui {
class TextInput : public Element {
private:
std::string text;
std::vector<std::function<void(const std::string &)>> text_changed_callbacks;
protected:
virtual void process_event(const Event &e) override;
public:
TextInput(Element *parent);
void set_text(std::string_view text);
const std::string &get_text();
void add_text_changed_callback(std::function<void(const std::string &)> callback);
};
} // namespace recompui

View File

@ -78,14 +78,18 @@ namespace recompui {
}
break;
case EventType::Hover:
set_style_enabled(hover_state, e.hover.active);
floater->set_style_enabled(hover_state, e.hover.active);
case EventType::Hover: {
bool hover_active = std::get<EventHover>(e.variant).active;
set_style_enabled(hover_state, hover_active);
floater->set_style_enabled(hover_state, hover_active);
break;
case EventType::Enable:
set_style_enabled(disabled_state, !e.enable.enable);
floater->set_style_enabled(disabled_state, !e.enable.enable);
}
case EventType::Enable: {
bool enable_active = std::get<EventEnable>(e.variant).active;
set_style_enabled(disabled_state, !enable_active);
floater->set_style_enabled(disabled_state, !enable_active);
break;
}
default:
assert(false && "Unknown event type.");
break;

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include <variant>
namespace recompui {
@ -23,6 +24,7 @@ namespace recompui {
Hover,
Enable,
Drag,
Text,
Count
};
@ -43,74 +45,79 @@ namespace recompui {
return Events(first) | Events(rest...);
}
struct EventClick {
float x;
float y;
};
struct EventFocus {
bool active;
};
struct EventHover {
bool active;
};
struct EventEnable {
bool active;
};
struct EventDrag {
float x;
float y;
DragPhase phase;
};
struct EventText {
std::string text;
};
using EventVariant = std::variant<EventClick, EventFocus, EventHover, EventEnable, EventDrag, EventText>;
struct Event {
struct Mouse {
float x;
float y;
};
EventType type;
EventVariant variant;
union {
uint64_t raw;
struct {
Mouse mouse;
} click;
struct {
bool active;
} focus;
struct {
bool active;
} hover;
struct {
bool enable;
} enable;
struct {
Mouse mouse;
DragPhase phase;
} drag;
};
// Factory methods for creating specific events
static Event click_event(float x, float y) {
Event e = {};
Event e;
e.type = EventType::Click;
e.click.mouse.x = x;
e.click.mouse.y = y;
e.variant = EventClick{ x, y };
return e;
}
static Event focus_event(bool active) {
Event e = {};
Event e;
e.type = EventType::Focus;
e.focus.active = active;
e.variant = EventFocus{ active };
return e;
}
static Event hover_event(bool active) {
Event e = {};
Event e;
e.type = EventType::Hover;
e.focus.active = active;
e.variant = EventHover{ active };
return e;
}
static Event enable_event(bool enable) {
Event e = {};
Event e;
e.type = EventType::Enable;
e.enable.enable = enable;
e.variant = EventEnable{ enable };
return e;
}
static Event drag_event(float x, float y, DragPhase phase) {
Event e = {};
Event e;
e.type = EventType::Drag;
e.drag.mouse.x = x;
e.drag.mouse.y = y;
e.drag.phase = phase;
e.variant = EventDrag{ x, y, phase };
return e;
}
static Event text_event(const std::string &text) {
Event e;
e.type = EventType::Text;
e.variant = EventText{ text };
return e;
}
};

View File

@ -10,7 +10,7 @@ namespace recompui {
void ConfigOptionElement::process_event(const Event &e) {
switch (e.type) {
case EventType::Hover:
hover_callback(this, e.hover.active);
hover_callback(this, std::get<EventHover>(e.variant).active);
break;
default:
assert(false && "Unknown event type.");
@ -73,6 +73,22 @@ void ConfigOptionSlider::set_max_value(double v) {
slider->set_max_value(v);
}
// ConfigOptionTextInput
void ConfigOptionTextInput::text_changed(const std::string &text) {
// TODO: Hook up to whatever API Recomp exposes to set the value of the persisent configuration in mods.
printf("%s changed to %s.\n", name.c_str(), text.c_str());
}
ConfigOptionTextInput::ConfigOptionTextInput(Element *parent) : ConfigOptionElement(parent) {
text_input = get_current_context().create_element<TextInput>(this);
text_input->add_text_changed_callback(std::bind(&ConfigOptionTextInput::text_changed, this, std::placeholders::_1));
}
ConfigOptionTextInput::~ConfigOptionTextInput() {
}
// ConfigSubMenu
void ConfigSubMenu::back_button_pressed() {
@ -159,6 +175,11 @@ void ConfigSubMenu::add_slider_option(std::string_view name, std::string_view de
add_option(option_slider, name, description);
}
void ConfigSubMenu::add_text_option(std::string_view name, std::string_view description) {
ConfigOptionTextInput *option_text_input = get_current_context().create_element<ConfigOptionTextInput>(config_scroll_container);
add_option(option_text_input, name, description);
}
void ConfigSubMenu::set_enter_sub_menu_callback(std::function<void()> callback) {
enter_sub_menu_callback = callback;
}

View File

@ -8,6 +8,7 @@
#include "elements/ui_label.h"
#include "elements/ui_scroll_container.h"
#include "elements/ui_slider.h"
#include "elements/ui_text_input.h"
namespace recompui {
@ -41,6 +42,16 @@ public:
void set_max_value(double v);
};
class ConfigOptionTextInput : public ConfigOptionElement {
protected:
TextInput *text_input = nullptr;
void text_changed(const std::string &text);
public:
ConfigOptionTextInput(Element *parent);
virtual ~ConfigOptionTextInput();
};
class ConfigSubMenu : public Element {
private:
Container *header_container = nullptr;
@ -65,6 +76,7 @@ public:
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 add_text_option(std::string_view name, std::string_view description);
void set_enter_sub_menu_callback(std::function<void()> callback);
void set_quit_sub_menu_callback(std::function<void()> callback);
};

View File

@ -100,17 +100,8 @@ 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->add_text_option("Text Option", "Description for simple option.");
ext_config_sub_menu->enter(mod_details[active_mod_index].mod_id);
}
}