mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-05-23 22:45:12 -04:00
wip ui_select recompui component
This commit is contained in:
@@ -199,6 +199,7 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_pill_button.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_select.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_span.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
|
||||
|
||||
@@ -513,4 +513,32 @@ void Element::register_callback(ContextId context, PTR(void) callback, PTR(void)
|
||||
callbacks.emplace_back(UICallback{.context = context, .callback = callback, .userdata = userdata});
|
||||
}
|
||||
|
||||
}
|
||||
Element *Element::select_add_option(std::string_view text, std::string_view value) {
|
||||
if (base->GetTagName() != "select") {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Rml::ElementFormControlSelect* select = (Rml::ElementFormControlSelect *)(base);
|
||||
if (!select) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ContextId context = get_current_context();
|
||||
Element *option_element = context.create_element<Element>(this, 0, "option", true);
|
||||
option_element->set_text(text);
|
||||
option_element->set_input_text(value);
|
||||
|
||||
return option_element;
|
||||
}
|
||||
|
||||
Element Element::get_element_with_tag_name(std::string_view tag_name) {
|
||||
for (int i = 0; i < base->GetNumChildren(true); i++) {
|
||||
Rml::Element* child = base->GetChild(i);
|
||||
if (child->GetTagName() == tag_name) {
|
||||
return Element(child);
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Select element has no child with the specified tag name");
|
||||
}
|
||||
|
||||
} // namespace recompui
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
bool remove_child(Element *child) { return remove_child(child->get_resource_id()); }
|
||||
void add_style(Style *style, std::string_view style_name);
|
||||
void add_style(Style *style, const std::initializer_list<std::string_view> &style_names);
|
||||
Element get_element_with_tag_name(std::string_view tag_name);
|
||||
void set_enabled(bool enabled);
|
||||
bool is_enabled() const;
|
||||
void set_text(std::string_view text);
|
||||
@@ -99,8 +100,10 @@ public:
|
||||
void set_input_value_float(float val) { set_input_value(val); }
|
||||
void set_input_value_double(double val) { set_input_value(val); }
|
||||
const std::string& get_id() { return id; }
|
||||
|
||||
Element *select_add_option(std::string_view text, std::string_view value);
|
||||
};
|
||||
|
||||
void queue_ui_callback(recompui::ResourceId resource, const Event& e, const UICallback& callback);
|
||||
|
||||
} // namespace recompui
|
||||
} // namespace recompui
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
#include "ui_select.h"
|
||||
#include "ui_svg.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
Option::Option(Element *parent, const SelectOption &option) :
|
||||
Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, EventType::Focus), "option", true),
|
||||
option(option)
|
||||
{
|
||||
set_text(option.text);
|
||||
set_input_text(option.value);
|
||||
set_color(theme::color::Primary);
|
||||
|
||||
set_padding(12.0f);
|
||||
set_height_auto();
|
||||
set_width_auto();
|
||||
set_overflow(Overflow::Hidden);
|
||||
|
||||
set_min_width(128.0f);
|
||||
|
||||
set_font_size(18.0f);
|
||||
set_letter_spacing(2.52f);
|
||||
set_line_height(18.0f);
|
||||
set_font_weight(400);
|
||||
|
||||
set_background_color(theme::color::Transparent);
|
||||
set_cursor(Cursor::Pointer);
|
||||
|
||||
hover_style.set_color(theme::color::TextActive);
|
||||
hover_style.set_background_color(theme::color::White, 77);
|
||||
|
||||
focus_style.set_color(theme::color::TextActive);
|
||||
focus_style.set_background_color(theme::color::White, 77);
|
||||
|
||||
disabled_style.set_color(theme::color::TextDim, 128);
|
||||
disabled_style.set_background_color(theme::color::Transparent);
|
||||
disabled_style.set_cursor(Cursor::None);
|
||||
|
||||
add_style(&hover_style, hover_state);
|
||||
add_style(&focus_style, focus_state);
|
||||
add_style(&disabled_style, disabled_state);
|
||||
}
|
||||
|
||||
void Option::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Click:
|
||||
break;
|
||||
case EventType::Hover:
|
||||
printf("Option hovered: %s\n", option.text.c_str());
|
||||
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
|
||||
break;
|
||||
case EventType::Enable:
|
||||
{
|
||||
bool enable_active = std::get<EventEnable>(e.variant).active;
|
||||
set_style_enabled(disabled_state, !enable_active);
|
||||
if (enable_active) {
|
||||
set_cursor(Cursor::Pointer);
|
||||
set_focusable(true);
|
||||
}
|
||||
else {
|
||||
set_cursor(Cursor::None);
|
||||
set_focusable(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Focus:
|
||||
set_style_enabled(focus_state, std::get<EventFocus>(e.variant).active);
|
||||
break;
|
||||
case EventType::Update:
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown event type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
constexpr std::string_view select_element_selectbox = "selectbox";
|
||||
constexpr std::string_view select_element_selectarrow = "selectarrow";
|
||||
constexpr std::string_view select_element_selectvalue = "selectvalue";
|
||||
|
||||
Select::Select(
|
||||
Element *parent, std::vector<SelectOption> options, const std::string &label, const std::string &default_text
|
||||
) :
|
||||
Element(parent, Events(EventType::Text, EventType::Click, EventType::Hover, EventType::Enable, EventType::Focus), "select", false),
|
||||
label(label),
|
||||
default_text(default_text)
|
||||
{
|
||||
this->options = options;
|
||||
|
||||
set_display(Display::Flex);
|
||||
set_flex_direction(FlexDirection::Row);
|
||||
set_align_items(AlignItems::Center);
|
||||
set_justify_content(JustifyContent::SpaceBetween);
|
||||
set_text_align(TextAlign::Left);
|
||||
set_position(Position::Relative);
|
||||
set_padding(16.0f);
|
||||
set_height(76.0f);
|
||||
set_width_auto();
|
||||
set_overflow(Overflow::Hidden);
|
||||
|
||||
set_min_width(128.0f + 64.0f);
|
||||
|
||||
set_border_width(theme::border::width);
|
||||
set_border_radius(theme::border::radius_md);
|
||||
set_border_color(theme::color::Border, 204);
|
||||
|
||||
set_font_size(28.0f);
|
||||
set_letter_spacing(3.08f);
|
||||
set_line_height(28.0f);
|
||||
set_font_style(FontStyle::Normal);
|
||||
set_font_weight(700);
|
||||
set_focusable(true);
|
||||
set_color(theme::color::Text);
|
||||
set_background_color(theme::color::Transparent);
|
||||
set_cursor(Cursor::Pointer);
|
||||
|
||||
set_nav_auto(NavDirection::Up);
|
||||
set_nav_auto(NavDirection::Right);
|
||||
set_nav_auto(NavDirection::Down);
|
||||
set_nav_auto(NavDirection::Left);
|
||||
|
||||
hover_style.set_color(theme::color::TextActive);
|
||||
hover_style.set_background_color(theme::color::White, 77);
|
||||
hover_style.set_border_color(theme::color::Border, 255);
|
||||
|
||||
focus_style.set_color(theme::color::TextActive);
|
||||
focus_style.set_background_color(theme::color::White, 77);
|
||||
focus_style.set_border_color(theme::color::Border, 255);
|
||||
|
||||
disabled_style.set_color(theme::color::TextDim, 128);
|
||||
disabled_style.set_background_color(theme::color::Transparent);
|
||||
disabled_style.set_border_color(theme::color::Border, 77);
|
||||
disabled_style.set_cursor(Cursor::None);
|
||||
|
||||
hover_disabled_style.set_color(theme::color::TextDim, 128);
|
||||
hover_disabled_style.set_background_color(theme::color::Transparent);
|
||||
hover_disabled_style.set_border_color(theme::color::Border, 77);
|
||||
hover_disabled_style.set_cursor(Cursor::None);
|
||||
|
||||
Element selectbox_element = get_element_with_tag_name(select_element_selectbox);
|
||||
selectbox_element.set_margin_top(4.0f);
|
||||
selectbox_element.set_width_auto();
|
||||
selectbox_element.set_padding_top(8.0f);
|
||||
selectbox_element.set_padding_bottom(8.0f);
|
||||
selectbox_element.set_background_color(theme::color::Background3);
|
||||
selectbox_element.set_border_width(theme::border::width);
|
||||
selectbox_element.set_border_radius(theme::border::radius_sm);
|
||||
selectbox_element.set_border_color(theme::color::BorderSoft);
|
||||
|
||||
Element selectvalue_element = get_element_with_tag_name(select_element_selectvalue);
|
||||
selectvalue_element.set_display(Display::Block);
|
||||
selectvalue_element.set_height_auto();
|
||||
selectvalue_element.set_width_auto();
|
||||
selectvalue_element.set_flex(1, 1);
|
||||
selectvalue_element.set_flex_basis(100, Unit::Percent);
|
||||
|
||||
Element selectarrow_element = get_element_with_tag_name(select_element_selectarrow);
|
||||
selectarrow_element.set_display(Display::Block);
|
||||
selectarrow_element.set_height(24.0f);
|
||||
selectarrow_element.set_width(24.0f);
|
||||
ContextId context = get_current_context();
|
||||
Svg *arrow = context.create_element<Svg>(
|
||||
&selectarrow_element,
|
||||
"assets/icons/Arrow.svg"
|
||||
);
|
||||
|
||||
arrow->set_width(24.0f);
|
||||
arrow->set_height(24.0f);
|
||||
arrow->set_color(theme::color::TextDim);
|
||||
|
||||
|
||||
add_option_elements();
|
||||
|
||||
add_style(&hover_style, hover_state);
|
||||
add_style(&focus_style, focus_state);
|
||||
add_style(&disabled_style, disabled_state);
|
||||
add_style(&hover_disabled_style, { hover_state, disabled_state });
|
||||
}
|
||||
|
||||
void Select::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Click:
|
||||
break;
|
||||
case EventType::Hover:
|
||||
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
|
||||
break;
|
||||
case EventType::Enable:
|
||||
{
|
||||
bool enable_active = std::get<EventEnable>(e.variant).active;
|
||||
set_style_enabled(disabled_state, !enable_active);
|
||||
if (enable_active) {
|
||||
set_cursor(Cursor::Pointer);
|
||||
set_focusable(true);
|
||||
}
|
||||
else {
|
||||
set_cursor(Cursor::None);
|
||||
set_focusable(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Focus:
|
||||
set_style_enabled(focus_state, std::get<EventFocus>(e.variant).active);
|
||||
break;
|
||||
case EventType::Text:
|
||||
{
|
||||
const std::string& opt_value = std::get<EventText>(e.variant).text;
|
||||
|
||||
int index = -1;
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
if (options[i].value == opt_value) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
selected_option_index = index;
|
||||
for (const auto &callback : change_callbacks) {
|
||||
callback(options[index], selected_option_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Update:
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown event type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Select::add_change_callback(std::function<void(SelectOption& option, int option_index)> callback) {
|
||||
change_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void Select::add_option_elements() {
|
||||
clear_children();
|
||||
option_elements.clear();
|
||||
|
||||
ContextId context = get_current_context();
|
||||
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
auto &option = options[i];
|
||||
Option *option_element = context.create_element<Option>(this, option);
|
||||
option_elements.push_back(option_element);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
enum class SelectOptionStyle {
|
||||
Primary,
|
||||
Secondary,
|
||||
Tertiary,
|
||||
Success,
|
||||
Warning,
|
||||
Danger,
|
||||
Default = Tertiary,
|
||||
};
|
||||
|
||||
struct SelectOption {
|
||||
std::string text;
|
||||
std::string value;
|
||||
SelectOptionStyle style = SelectOptionStyle::Default;
|
||||
|
||||
SelectOption(const std::string &text, const std::string &value, SelectOptionStyle style = SelectOptionStyle::Default)
|
||||
: text(text), value(value), style(style) {}
|
||||
};
|
||||
|
||||
class Option : public Element {
|
||||
protected:
|
||||
SelectOption option;
|
||||
Style hover_style;
|
||||
Style focus_style;
|
||||
Style disabled_style;
|
||||
|
||||
// Element overrides.
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "Option"; }
|
||||
public:
|
||||
Option(Element *parent, const SelectOption &option);
|
||||
};
|
||||
|
||||
class Select : public Element {
|
||||
protected:
|
||||
int selected_option_index = -1;
|
||||
std::string label;
|
||||
std::string default_text;
|
||||
std::vector<SelectOption> options;
|
||||
std::vector<Option*> option_elements;
|
||||
|
||||
Style hover_style;
|
||||
Style focus_style;
|
||||
Style disabled_style;
|
||||
Style hover_disabled_style;
|
||||
std::list<std::function<void(SelectOption& option, int option_index)>> change_callbacks;
|
||||
|
||||
// Element overrides.
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "Select"; }
|
||||
public:
|
||||
Select(Element *parent, std::vector<SelectOption> options = {}, const std::string &label = "", const std::string &default_text = "");
|
||||
void add_change_callback(std::function<void(SelectOption& option, int option_index)> callback);
|
||||
Style* get_hover_style() { return &hover_style; }
|
||||
Style* get_focus_style() { return &focus_style; }
|
||||
Style* get_disabled_style() { return &disabled_style; }
|
||||
Style* get_hover_disabled_style() { return &hover_disabled_style; }
|
||||
private:
|
||||
void add_option_elements();
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
Reference in New Issue
Block a user