mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp.git
synced 2026-04-27 12:41:39 +00:00
Add MouseButton UI event and use it to fix focus issue on radio, also fix sliders not moving until mouse is released
This commit is contained in:
parent
8cf381005e
commit
80aee602c9
8 changed files with 99 additions and 17 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
Clickable::Clickable(Element *parent, bool draggable) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, draggable ? EventType::Drag : EventType::None)) {
|
Clickable::Clickable(Element *parent, bool draggable) : Element(parent, Events(EventType::Click, EventType::MouseButton, EventType::Hover, EventType::Enable, draggable ? EventType::Drag : EventType::None)) {
|
||||||
set_cursor(Cursor::Pointer);
|
set_cursor(Cursor::Pointer);
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
set_drag(Drag::Drag);
|
set_drag(Drag::Drag);
|
||||||
|
|
@ -14,12 +14,23 @@ namespace recompui {
|
||||||
case EventType::Click: {
|
case EventType::Click: {
|
||||||
if (is_enabled()) {
|
if (is_enabled()) {
|
||||||
const EventClick &click = std::get<EventClick>(e.variant);
|
const EventClick &click = std::get<EventClick>(e.variant);
|
||||||
for (const auto &function : pressed_callbacks) {
|
for (const auto &function : clicked_callbacks) {
|
||||||
function(click.x, click.y);
|
function(click.x, click.y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case EventType::MouseButton: {
|
||||||
|
if (is_enabled()) {
|
||||||
|
const EventMouseButton &mousebutton = std::get<EventMouseButton>(e.variant);
|
||||||
|
if (mousebutton.button == MouseButton::Left && mousebutton.pressed) {
|
||||||
|
for (const auto &function : pressed_callbacks) {
|
||||||
|
function(mousebutton.x, mousebutton.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
case EventType::Hover:
|
case EventType::Hover:
|
||||||
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
|
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
|
||||||
break;
|
break;
|
||||||
|
|
@ -51,6 +62,10 @@ namespace recompui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Clickable::add_clicked_callback(std::function<void(float, float)> callback) {
|
||||||
|
clicked_callbacks.emplace_back(callback);
|
||||||
|
}
|
||||||
|
|
||||||
void Clickable::add_pressed_callback(std::function<void(float, float)> callback) {
|
void Clickable::add_pressed_callback(std::function<void(float, float)> callback) {
|
||||||
pressed_callbacks.emplace_back(callback);
|
pressed_callbacks.emplace_back(callback);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ namespace recompui {
|
||||||
|
|
||||||
class Clickable : public Element {
|
class Clickable : public Element {
|
||||||
protected:
|
protected:
|
||||||
|
std::vector<std::function<void(float, float)>> clicked_callbacks;
|
||||||
std::vector<std::function<void(float, float)>> pressed_callbacks;
|
std::vector<std::function<void(float, float)>> pressed_callbacks;
|
||||||
std::vector<std::function<void(float, float, DragPhase)>> dragged_callbacks;
|
std::vector<std::function<void(float, float, DragPhase)>> dragged_callbacks;
|
||||||
|
|
||||||
|
|
@ -14,6 +15,7 @@ namespace recompui {
|
||||||
std::string_view get_type_name() override { return "Clickable"; }
|
std::string_view get_type_name() override { return "Clickable"; }
|
||||||
public:
|
public:
|
||||||
Clickable(Element *parent, bool draggable = false);
|
Clickable(Element *parent, bool draggable = false);
|
||||||
|
void add_clicked_callback(std::function<void(float, float)> callback);
|
||||||
void add_pressed_callback(std::function<void(float, float)> callback);
|
void add_pressed_callback(std::function<void(float, float)> callback);
|
||||||
void add_dragged_callback(std::function<void(float, float, DragPhase)> callback);
|
void add_dragged_callback(std::function<void(float, float, DragPhase)> callback);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,11 @@ void Element::register_event_listeners(uint32_t events_enabled) {
|
||||||
base->AddEventListener(Rml::EventId::Click, this);
|
base->AddEventListener(Rml::EventId::Click, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (events_enabled & Events(EventType::MouseButton)) {
|
||||||
|
base->AddEventListener(Rml::EventId::Mousedown, this);
|
||||||
|
base->AddEventListener(Rml::EventId::Mouseup, this);
|
||||||
|
}
|
||||||
|
|
||||||
if (events_enabled & Events(EventType::Focus)) {
|
if (events_enabled & Events(EventType::Focus)) {
|
||||||
base->AddEventListener(Rml::EventId::Focus, this);
|
base->AddEventListener(Rml::EventId::Focus, this);
|
||||||
base->AddEventListener(Rml::EventId::Blur, this);
|
base->AddEventListener(Rml::EventId::Blur, this);
|
||||||
|
|
@ -152,6 +157,19 @@ void Element::set_id(const std::string& new_id) {
|
||||||
base->SetId(new_id);
|
base->SetId(new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recompui::MouseButton convert_rml_mouse_button(int button) {
|
||||||
|
switch (button) {
|
||||||
|
case 0:
|
||||||
|
return recompui::MouseButton::Left;
|
||||||
|
case 1:
|
||||||
|
return recompui::MouseButton::Right;
|
||||||
|
case 2:
|
||||||
|
return recompui::MouseButton::Middle;
|
||||||
|
default:
|
||||||
|
return recompui::MouseButton::Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Element::ProcessEvent(Rml::Event &event) {
|
void Element::ProcessEvent(Rml::Event &event) {
|
||||||
ContextId prev_context = recompui::try_close_current_context();
|
ContextId prev_context = recompui::try_close_current_context();
|
||||||
ContextId context = ContextId::null();
|
ContextId context = ContextId::null();
|
||||||
|
|
@ -172,6 +190,22 @@ void Element::ProcessEvent(Rml::Event &event) {
|
||||||
case Rml::EventId::Click:
|
case Rml::EventId::Click:
|
||||||
handle_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f)));
|
handle_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f)));
|
||||||
break;
|
break;
|
||||||
|
case Rml::EventId::Mousedown:
|
||||||
|
{
|
||||||
|
MouseButton mouse_button = convert_rml_mouse_button(event.GetParameter("button", 3));
|
||||||
|
if (mouse_button != MouseButton::Count) {
|
||||||
|
handle_event(Event::mousebutton_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), mouse_button, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Rml::EventId::Mouseup:
|
||||||
|
{
|
||||||
|
MouseButton mouse_button = convert_rml_mouse_button(event.GetParameter("button", 3));
|
||||||
|
if (mouse_button != MouseButton::Count) {
|
||||||
|
handle_event(Event::mousebutton_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f), mouse_button, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Rml::EventId::Keydown:
|
case Rml::EventId::Keydown:
|
||||||
switch ((Rml::Input::KeyIdentifier)event.GetParameter<int>("key_identifier", 0)) {
|
switch ((Rml::Input::KeyIdentifier)event.GetParameter<int>("key_identifier", 0)) {
|
||||||
case Rml::Input::KeyIdentifier::KI_LEFT:
|
case Rml::Input::KeyIdentifier::KI_LEFT:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ namespace recompui {
|
||||||
|
|
||||||
// RadioOption
|
// RadioOption
|
||||||
|
|
||||||
RadioOption::RadioOption(Element *parent, std::string_view name, uint32_t index) : Element(parent, Events(EventType::Click, EventType::Focus, EventType::Hover, EventType::Enable, EventType::Update), "label", true) {
|
RadioOption::RadioOption(Element *parent, std::string_view name, uint32_t index) : Element(parent, Events(EventType::MouseButton, EventType::Click, EventType::Focus, EventType::Hover, EventType::Enable, EventType::Update), "label", true) {
|
||||||
this->index = index;
|
this->index = index;
|
||||||
|
|
||||||
enable_focus();
|
enable_focus();
|
||||||
|
|
@ -43,6 +43,14 @@ namespace recompui {
|
||||||
|
|
||||||
void RadioOption::process_event(const Event &e) {
|
void RadioOption::process_event(const Event &e) {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
|
case EventType::MouseButton:
|
||||||
|
{
|
||||||
|
const EventMouseButton &mousebutton = std::get<EventMouseButton>(e.variant);
|
||||||
|
if (mousebutton.button == MouseButton::Left && mousebutton.pressed) {
|
||||||
|
pressed_callback(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case EventType::Click:
|
case EventType::Click:
|
||||||
pressed_callback(index);
|
pressed_callback(index);
|
||||||
break;
|
break;
|
||||||
|
|
@ -67,10 +75,6 @@ namespace recompui {
|
||||||
apply_styles();
|
apply_styles();
|
||||||
queue_update();
|
queue_update();
|
||||||
}
|
}
|
||||||
if (focus_queued) {
|
|
||||||
focus_queued = false;
|
|
||||||
focus();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -106,7 +110,7 @@ namespace recompui {
|
||||||
}, val);
|
}, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart, Events(EventType::Focus)) {
|
Radio::Radio(Element *parent) : Container(parent, FlexDirection::Row, JustifyContent::FlexStart, Events(EventType::Focus, EventType::Update)) {
|
||||||
set_gap(24.0f);
|
set_gap(24.0f);
|
||||||
set_align_items(AlignItems::FlexStart);
|
set_align_items(AlignItems::FlexStart);
|
||||||
enable_focus();
|
enable_focus();
|
||||||
|
|
@ -118,10 +122,15 @@ namespace recompui {
|
||||||
if (!options.empty()) {
|
if (!options.empty()) {
|
||||||
if (std::get<EventFocus>(e.variant).active) {
|
if (std::get<EventFocus>(e.variant).active) {
|
||||||
blur();
|
blur();
|
||||||
options[index]->queue_focus();
|
queue_child_focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case EventType::Update:
|
||||||
|
if (child_focus_queued) {
|
||||||
|
child_focus_queued = false;
|
||||||
|
options[index]->focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +140,7 @@ namespace recompui {
|
||||||
|
|
||||||
void Radio::add_option(std::string_view name) {
|
void Radio::add_option(std::string_view name) {
|
||||||
RadioOption *option = get_current_context().create_element<RadioOption>(this, name, uint32_t(options.size()));
|
RadioOption *option = get_current_context().create_element<RadioOption>(this, name, uint32_t(options.size()));
|
||||||
option->set_pressed_callback([this](uint32_t index){ option_selected(index); });
|
option->set_pressed_callback([this](uint32_t index){ options[index]->focus(); option_selected(index); });
|
||||||
options.emplace_back(option);
|
options.emplace_back(option);
|
||||||
|
|
||||||
// The first option was added, select it.
|
// The first option was added, select it.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ namespace recompui {
|
||||||
Style pulsing_style;
|
Style pulsing_style;
|
||||||
std::function<void(uint32_t)> pressed_callback = nullptr;
|
std::function<void(uint32_t)> pressed_callback = nullptr;
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
bool focus_queued = false;
|
|
||||||
protected:
|
protected:
|
||||||
virtual void process_event(const Event &e) override;
|
virtual void process_event(const Event &e) override;
|
||||||
std::string_view get_type_name() override { return "LabelRadioOption"; }
|
std::string_view get_type_name() override { return "LabelRadioOption"; }
|
||||||
|
|
@ -19,7 +18,6 @@ namespace recompui {
|
||||||
RadioOption(Element *parent, std::string_view name, uint32_t index);
|
RadioOption(Element *parent, std::string_view name, uint32_t index);
|
||||||
void set_pressed_callback(std::function<void(uint32_t)> callback);
|
void set_pressed_callback(std::function<void(uint32_t)> callback);
|
||||||
void set_selected_state(bool enable);
|
void set_selected_state(bool enable);
|
||||||
void queue_focus() { focus_queued = true; queue_update(); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Radio : public Container {
|
class Radio : public Container {
|
||||||
|
|
@ -27,6 +25,7 @@ namespace recompui {
|
||||||
std::vector<RadioOption *> options;
|
std::vector<RadioOption *> options;
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
std::vector<std::function<void(uint32_t)>> index_changed_callbacks;
|
std::vector<std::function<void(uint32_t)>> index_changed_callbacks;
|
||||||
|
bool child_focus_queued = false;
|
||||||
|
|
||||||
void set_index_internal(uint32_t index, bool setup, bool trigger_callbacks);
|
void set_index_internal(uint32_t index, bool setup, bool trigger_callbacks);
|
||||||
void option_selected(uint32_t index);
|
void option_selected(uint32_t index);
|
||||||
|
|
@ -35,6 +34,7 @@ namespace recompui {
|
||||||
protected:
|
protected:
|
||||||
virtual void process_event(const Event &e) override;
|
virtual void process_event(const Event &e) override;
|
||||||
std::string_view get_type_name() override { return "LabelRadio"; }
|
std::string_view get_type_name() override { return "LabelRadio"; }
|
||||||
|
void queue_child_focus() { child_focus_queued = true; queue_update(); }
|
||||||
public:
|
public:
|
||||||
Radio(Element *parent);
|
Radio(Element *parent);
|
||||||
virtual ~Radio();
|
virtual ~Radio();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace recompui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Slider::bar_clicked(float x, float) {
|
void Slider::bar_pressed(float x, float) {
|
||||||
update_value_from_mouse(x);
|
update_value_from_mouse(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,7 +151,7 @@ namespace recompui {
|
||||||
|
|
||||||
slider_element = context.create_element<Clickable>(this, true);
|
slider_element = context.create_element<Clickable>(this, true);
|
||||||
slider_element->set_flex(1.0f, 0.0f);
|
slider_element->set_flex(1.0f, 0.0f);
|
||||||
slider_element->add_pressed_callback([this](float x, float y){ bar_clicked(x, y); focus(); });
|
slider_element->add_pressed_callback([this](float x, float y){ bar_pressed(x, y); focus(); });
|
||||||
slider_element->add_dragged_callback([this](float x, float y, recompui::DragPhase phase){ bar_dragged(x, y, phase); focus(); });
|
slider_element->add_dragged_callback([this](float x, float y, recompui::DragPhase phase){ bar_dragged(x, y, phase); focus(); });
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -160,7 +160,7 @@ namespace recompui {
|
||||||
bar_element->set_height(2.0f);
|
bar_element->set_height(2.0f);
|
||||||
bar_element->set_margin_top(8.0f);
|
bar_element->set_margin_top(8.0f);
|
||||||
bar_element->set_background_color(Color{ 255, 255, 255, 50 });
|
bar_element->set_background_color(Color{ 255, 255, 255, 50 });
|
||||||
bar_element->add_pressed_callback([this](float x, float y){ bar_clicked(x, y); focus(); });
|
bar_element->add_pressed_callback([this](float x, float y){ bar_pressed(x, y); focus(); });
|
||||||
bar_element->add_dragged_callback([this](float x, float y, recompui::DragPhase phase){ bar_dragged(x, y, phase); focus(); });
|
bar_element->add_dragged_callback([this](float x, float y, recompui::DragPhase phase){ bar_dragged(x, y, phase); focus(); });
|
||||||
|
|
||||||
circle_element = context.create_element<Clickable>(bar_element, true);
|
circle_element = context.create_element<Clickable>(bar_element, true);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace recompui {
|
||||||
std::vector<std::function<void(double)>> value_changed_callbacks;
|
std::vector<std::function<void(double)>> value_changed_callbacks;
|
||||||
|
|
||||||
void set_value_internal(double v, bool setup, bool trigger_callbacks);
|
void set_value_internal(double v, bool setup, bool trigger_callbacks);
|
||||||
void bar_clicked(float x, float y);
|
void bar_pressed(float x, float y);
|
||||||
void bar_dragged(float x, float y, DragPhase phase);
|
void bar_dragged(float x, float y, DragPhase phase);
|
||||||
void circle_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_value_from_mouse(float x);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ namespace recompui {
|
||||||
Text,
|
Text,
|
||||||
Update,
|
Update,
|
||||||
Navigate,
|
Navigate,
|
||||||
|
MouseButton,
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -50,6 +51,13 @@ namespace recompui {
|
||||||
Left
|
Left
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MouseButton {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Middle,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -91,7 +99,14 @@ namespace recompui {
|
||||||
NavDirection direction;
|
NavDirection direction;
|
||||||
};
|
};
|
||||||
|
|
||||||
using EventVariant = std::variant<EventClick, EventFocus, EventHover, EventEnable, EventDrag, EventText, EventNavigate, std::monostate>;
|
struct EventMouseButton {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
MouseButton button;
|
||||||
|
bool pressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
using EventVariant = std::variant<EventClick, EventFocus, EventHover, EventEnable, EventDrag, EventText, EventNavigate, EventMouseButton, std::monostate>;
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
EventType type;
|
EventType type;
|
||||||
|
|
@ -153,6 +168,13 @@ namespace recompui {
|
||||||
e.variant = EventNavigate{ direction };
|
e.variant = EventNavigate{ direction };
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Event mousebutton_event(float x, float y, MouseButton button, bool pressed) {
|
||||||
|
Event e;
|
||||||
|
e.type = EventType::MouseButton;
|
||||||
|
e.variant = EventMouseButton{ x, y, button, pressed };
|
||||||
|
return e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Display {
|
enum class Display {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue