mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp.git
synced 2025-10-30 08:03:03 +00:00
Improve radio navigation and setup mod config submenu navigation setup
This commit is contained in:
parent
f92edb590c
commit
d93ac4b49a
9 changed files with 144 additions and 7 deletions
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace recompui {
|
||||
|
||||
Container::Container(Element *parent, FlexDirection direction, JustifyContent justify_content) : Element(parent) {
|
||||
Container::Container(Element *parent, FlexDirection direction, JustifyContent justify_content, uint32_t events_enabled) : Element(parent, events_enabled) {
|
||||
set_display(Display::Flex);
|
||||
set_flex_direction(direction);
|
||||
set_justify_content(justify_content);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace recompui {
|
|||
protected:
|
||||
std::string_view get_type_name() override { return "Container"; }
|
||||
public:
|
||||
Container(Element* parent, FlexDirection direction, JustifyContent justify_content);
|
||||
Container(Element* parent, FlexDirection direction, JustifyContent justify_content, uint32_t events_enabled = 0);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
|
|
|
|||
|
|
@ -456,6 +456,10 @@ void Element::focus() {
|
|||
base->Focus();
|
||||
}
|
||||
|
||||
void Element::blur() {
|
||||
base->Blur();
|
||||
}
|
||||
|
||||
void Element::queue_update() {
|
||||
ContextId cur_context = get_current_context();
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public:
|
|||
float get_client_height();
|
||||
void enable_focus();
|
||||
void focus();
|
||||
void blur();
|
||||
void queue_update();
|
||||
void register_callback(ContextId context, PTR(void) callback, PTR(void) userdata);
|
||||
uint32_t get_input_value_u32();
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ namespace recompui {
|
|||
apply_styles();
|
||||
queue_update();
|
||||
}
|
||||
if (focus_queued) {
|
||||
focus_queued = false;
|
||||
focus();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -102,9 +106,23 @@ namespace recompui {
|
|||
}, val);
|
||||
}
|
||||
|
||||
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart) {
|
||||
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart, Events(EventType::Focus)) {
|
||||
set_gap(24.0f);
|
||||
set_align_items(AlignItems::FlexStart);
|
||||
enable_focus();
|
||||
}
|
||||
|
||||
void Radio::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Focus:
|
||||
if (!options.empty()) {
|
||||
if (std::get<EventFocus>(e.variant).active) {
|
||||
blur();
|
||||
options[index]->queue_focus();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Radio::~Radio() {
|
||||
|
|
@ -120,6 +138,11 @@ namespace recompui {
|
|||
if (options.size() == 1) {
|
||||
set_index_internal(0, true, false);
|
||||
}
|
||||
// At least one other option already existed, so set up navigation.
|
||||
else {
|
||||
options[options.size() - 2]->set_nav(NavDirection::Right, options[options.size() - 1]);
|
||||
options[options.size() - 1]->set_nav(NavDirection::Left, options[options.size() - 2]);
|
||||
}
|
||||
}
|
||||
|
||||
void Radio::set_index(uint32_t index) {
|
||||
|
|
@ -133,5 +156,85 @@ namespace recompui {
|
|||
void Radio::add_index_changed_callback(std::function<void(uint32_t)> callback) {
|
||||
index_changed_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void Radio::set_nav_auto(NavDirection dir) {
|
||||
Element::set_nav_auto(dir);
|
||||
if (!options.empty()) {
|
||||
switch (dir) {
|
||||
case NavDirection::Up:
|
||||
case NavDirection::Down:
|
||||
for (Element* e : options) {
|
||||
e->set_nav_auto(dir);
|
||||
}
|
||||
break;
|
||||
case NavDirection::Left:
|
||||
options.front()->set_nav_auto(dir);
|
||||
break;
|
||||
case NavDirection::Right:
|
||||
options.back()->set_nav_auto(dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Radio::set_nav_none(NavDirection dir) {
|
||||
Element::set_nav_none(dir);
|
||||
if (!options.empty()) {
|
||||
switch (dir) {
|
||||
case NavDirection::Up:
|
||||
case NavDirection::Down:
|
||||
for (Element* e : options) {
|
||||
e->set_nav_none(dir);
|
||||
}
|
||||
break;
|
||||
case NavDirection::Left:
|
||||
options.front()->set_nav_none(dir);
|
||||
break;
|
||||
case NavDirection::Right:
|
||||
options.back()->set_nav_none(dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Radio::set_nav(NavDirection dir, Element* element) {
|
||||
Element::set_nav(dir, element);
|
||||
if (!options.empty()) {
|
||||
switch (dir) {
|
||||
case NavDirection::Up:
|
||||
case NavDirection::Down:
|
||||
for (Element* e : options) {
|
||||
e->set_nav(dir, element);
|
||||
}
|
||||
break;
|
||||
case NavDirection::Left:
|
||||
options.front()->set_nav(dir, element);
|
||||
break;
|
||||
case NavDirection::Right:
|
||||
options.back()->set_nav(dir, element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Radio::set_nav_manual(NavDirection dir, const std::string& target) {
|
||||
Element::set_nav_manual(dir, target);
|
||||
if (!options.empty()) {
|
||||
switch (dir) {
|
||||
case NavDirection::Up:
|
||||
case NavDirection::Down:
|
||||
for (Element* e : options) {
|
||||
e->set_nav_manual(dir, target);
|
||||
}
|
||||
break;
|
||||
case NavDirection::Left:
|
||||
options.front()->set_nav_manual(dir, target);
|
||||
break;
|
||||
case NavDirection::Right:
|
||||
options.back()->set_nav_manual(dir, target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -11,6 +11,7 @@ namespace recompui {
|
|||
Style pulsing_style;
|
||||
std::function<void(uint32_t)> pressed_callback = nullptr;
|
||||
uint32_t index = 0;
|
||||
bool focus_queued = false;
|
||||
protected:
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "LabelRadioOption"; }
|
||||
|
|
@ -18,6 +19,7 @@ namespace recompui {
|
|||
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);
|
||||
void queue_focus() { focus_queued = true; queue_update(); }
|
||||
};
|
||||
|
||||
class Radio : public Container {
|
||||
|
|
@ -31,6 +33,7 @@ namespace recompui {
|
|||
void set_input_value(const ElementValue& val) override;
|
||||
ElementValue get_element_value() override { return get_index(); }
|
||||
protected:
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "LabelRadio"; }
|
||||
public:
|
||||
Radio(Element *parent);
|
||||
|
|
@ -39,6 +42,13 @@ namespace recompui {
|
|||
void set_index(uint32_t index);
|
||||
uint32_t get_index() const;
|
||||
void add_index_changed_callback(std::function<void(uint32_t)> callback);
|
||||
size_t num_options() const { return options.size(); }
|
||||
RadioOption* get_option_element(size_t option_index) { return options[option_index]; }
|
||||
RadioOption* get_current_option_element() { return options.empty() ? nullptr : options[index]; }
|
||||
void set_nav_auto(NavDirection dir) override;
|
||||
void set_nav_none(NavDirection dir) override;
|
||||
void set_nav(NavDirection dir, Element* element) override;
|
||||
void set_nav_manual(NavDirection dir, const std::string& target) override;
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
|
|
@ -94,10 +94,10 @@ namespace recompui {
|
|||
void set_drag(Drag drag);
|
||||
void set_tab_index(TabIndex focus);
|
||||
void set_font_family(std::string_view family);
|
||||
void set_nav_auto(NavDirection dir);
|
||||
void set_nav_none(NavDirection dir);
|
||||
void set_nav(NavDirection dir, Element* element);
|
||||
void set_nav_manual(NavDirection dir, const std::string& target);
|
||||
virtual void set_nav_auto(NavDirection dir);
|
||||
virtual void set_nav_none(NavDirection dir);
|
||||
virtual void set_nav(NavDirection dir, Element* element);
|
||||
virtual void set_nav_manual(NavDirection dir, const std::string& target);
|
||||
void set_tab_index_auto();
|
||||
void set_tab_index_none();
|
||||
void set_focusable(bool focusable);
|
||||
|
|
|
|||
|
|
@ -172,6 +172,8 @@ ConfigSubMenu::ConfigSubMenu(Element *parent) : Element(parent) {
|
|||
description_label = context.create_element<Label>(body_container, "Description", LabelStyle::Small);
|
||||
description_label->set_min_width(800.0f);
|
||||
}
|
||||
|
||||
recompui::get_current_context().set_autofocus_element(back_button);
|
||||
}
|
||||
|
||||
ConfigSubMenu::~ConfigSubMenu() {
|
||||
|
|
@ -193,6 +195,15 @@ void ConfigSubMenu::add_option(ConfigOptionElement *option, std::string_view id,
|
|||
option->set_name(name);
|
||||
option->set_description(description);
|
||||
option->set_hover_callback([this](ConfigOptionElement *option, bool active){ option_hovered(option, active); });
|
||||
if (config_option_elements.empty()) {
|
||||
back_button->set_nav(NavDirection::Down, option->get_focus_element());
|
||||
option->set_nav(NavDirection::Up, back_button);
|
||||
}
|
||||
else {
|
||||
config_option_elements.back()->set_nav(NavDirection::Down, option->get_focus_element());
|
||||
option->set_nav(NavDirection::Up, config_option_elements.back()->get_focus_element());
|
||||
}
|
||||
|
||||
config_option_elements.emplace_back(option);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ public:
|
|||
void set_description(std::string_view description);
|
||||
void set_hover_callback(std::function<void(ConfigOptionElement *, bool)> callback);
|
||||
const std::string &get_description() const;
|
||||
void set_nav_auto(NavDirection dir) override { get_focus_element()->set_nav_auto(dir); }
|
||||
void set_nav_none(NavDirection dir) override { get_focus_element()->set_nav_none(dir); }
|
||||
void set_nav(NavDirection dir, Element* element) override { get_focus_element()->set_nav(dir, element); }
|
||||
void set_nav_manual(NavDirection dir, const std::string& target) override { get_focus_element()->set_nav_manual(dir, target); }
|
||||
virtual Element* get_focus_element() { return this; }
|
||||
};
|
||||
|
||||
class ConfigOptionSlider : public ConfigOptionElement {
|
||||
|
|
@ -42,6 +47,7 @@ protected:
|
|||
std::string_view get_type_name() override { return "ConfigOptionSlider"; }
|
||||
public:
|
||||
ConfigOptionSlider(Element *parent, double value, double min_value, double max_value, double step_value, bool percent, std::function<void(const std::string &, double)> callback);
|
||||
Element* get_focus_element() override { return slider; }
|
||||
};
|
||||
|
||||
class ConfigOptionTextInput : public ConfigOptionElement {
|
||||
|
|
@ -53,6 +59,7 @@ protected:
|
|||
std::string_view get_type_name() override { return "ConfigOptionTextInput"; }
|
||||
public:
|
||||
ConfigOptionTextInput(Element *parent, std::string_view value, std::function<void(const std::string &, const std::string &)> callback);
|
||||
Element* get_focus_element() override { return text_input; }
|
||||
};
|
||||
|
||||
class ConfigOptionRadio : public ConfigOptionElement {
|
||||
|
|
@ -64,6 +71,7 @@ protected:
|
|||
std::string_view get_type_name() override { return "ConfigOptionRadio"; }
|
||||
public:
|
||||
ConfigOptionRadio(Element *parent, uint32_t value, const std::vector<std::string> &options, std::function<void(const std::string &, uint32_t)> callback);
|
||||
Element* get_focus_element() override { return radio; }
|
||||
};
|
||||
|
||||
class ConfigSubMenu : public Element {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue