diff --git a/src/ui/elements/ui_clickable.cpp b/src/ui/elements/ui_clickable.cpp index 790c8a2..5ce865c 100644 --- a/src/ui/elements/ui_clickable.cpp +++ b/src/ui/elements/ui_clickable.cpp @@ -2,7 +2,7 @@ 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); if (draggable) { set_drag(Drag::Drag); @@ -14,12 +14,23 @@ namespace recompui { case EventType::Click: { if (is_enabled()) { const EventClick &click = std::get(e.variant); - for (const auto &function : pressed_callbacks) { + for (const auto &function : clicked_callbacks) { function(click.x, click.y); } break; } } + case EventType::MouseButton: { + if (is_enabled()) { + const EventMouseButton &mousebutton = std::get(e.variant); + if (mousebutton.button == MouseButton::Left && mousebutton.pressed) { + for (const auto &function : pressed_callbacks) { + function(mousebutton.x, mousebutton.y); + } + } + break; + } + } case EventType::Hover: set_style_enabled(hover_state, std::get(e.variant).active && is_enabled()); break; @@ -51,6 +62,10 @@ namespace recompui { } } + void Clickable::add_clicked_callback(std::function callback) { + clicked_callbacks.emplace_back(callback); + } + void Clickable::add_pressed_callback(std::function callback) { pressed_callbacks.emplace_back(callback); } diff --git a/src/ui/elements/ui_clickable.h b/src/ui/elements/ui_clickable.h index 76ddf3b..97dceed 100644 --- a/src/ui/elements/ui_clickable.h +++ b/src/ui/elements/ui_clickable.h @@ -6,6 +6,7 @@ namespace recompui { class Clickable : public Element { protected: + std::vector> clicked_callbacks; std::vector> pressed_callbacks; std::vector> dragged_callbacks; @@ -14,6 +15,7 @@ namespace recompui { std::string_view get_type_name() override { return "Clickable"; } public: Clickable(Element *parent, bool draggable = false); + void add_clicked_callback(std::function callback); void add_pressed_callback(std::function callback); void add_dragged_callback(std::function callback); }; diff --git a/src/ui/elements/ui_element.cpp b/src/ui/elements/ui_element.cpp index 5e9ba8e..b1525f2 100644 --- a/src/ui/elements/ui_element.cpp +++ b/src/ui/elements/ui_element.cpp @@ -75,6 +75,11 @@ void Element::register_event_listeners(uint32_t events_enabled) { 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)) { base->AddEventListener(Rml::EventId::Focus, this); base->AddEventListener(Rml::EventId::Blur, this); @@ -152,6 +157,19 @@ void Element::set_id(const std::string& 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) { ContextId prev_context = recompui::try_close_current_context(); ContextId context = ContextId::null(); @@ -172,6 +190,22 @@ void Element::ProcessEvent(Rml::Event &event) { case Rml::EventId::Click: handle_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f))); 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: switch ((Rml::Input::KeyIdentifier)event.GetParameter("key_identifier", 0)) { case Rml::Input::KeyIdentifier::KI_LEFT: diff --git a/src/ui/elements/ui_radio.cpp b/src/ui/elements/ui_radio.cpp index 5d3e98a..f4ed112 100644 --- a/src/ui/elements/ui_radio.cpp +++ b/src/ui/elements/ui_radio.cpp @@ -6,7 +6,7 @@ namespace recompui { // 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; enable_focus(); @@ -43,6 +43,14 @@ namespace recompui { void RadioOption::process_event(const Event &e) { switch (e.type) { + case EventType::MouseButton: + { + const EventMouseButton &mousebutton = std::get(e.variant); + if (mousebutton.button == MouseButton::Left && mousebutton.pressed) { + pressed_callback(index); + } + } + break; case EventType::Click: pressed_callback(index); break; @@ -67,10 +75,6 @@ namespace recompui { apply_styles(); queue_update(); } - if (focus_queued) { - focus_queued = false; - focus(); - } break; default: break; @@ -106,7 +110,7 @@ namespace recompui { }, 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_align_items(AlignItems::FlexStart); enable_focus(); @@ -118,10 +122,15 @@ namespace recompui { if (!options.empty()) { if (std::get(e.variant).active) { blur(); - options[index]->queue_focus(); + queue_child_focus(); } } 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) { RadioOption *option = get_current_context().create_element(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); // The first option was added, select it. diff --git a/src/ui/elements/ui_radio.h b/src/ui/elements/ui_radio.h index f5b7dc9..d3cb54d 100644 --- a/src/ui/elements/ui_radio.h +++ b/src/ui/elements/ui_radio.h @@ -11,7 +11,6 @@ namespace recompui { Style pulsing_style; std::function 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"; } @@ -19,7 +18,6 @@ namespace recompui { RadioOption(Element *parent, std::string_view name, uint32_t index); void set_pressed_callback(std::function callback); void set_selected_state(bool enable); - void queue_focus() { focus_queued = true; queue_update(); } }; class Radio : public Container { @@ -27,6 +25,7 @@ namespace recompui { std::vector options; uint32_t index = 0; std::vector> index_changed_callbacks; + bool child_focus_queued = false; void set_index_internal(uint32_t index, bool setup, bool trigger_callbacks); void option_selected(uint32_t index); @@ -35,6 +34,7 @@ namespace recompui { protected: virtual void process_event(const Event &e) override; std::string_view get_type_name() override { return "LabelRadio"; } + void queue_child_focus() { child_focus_queued = true; queue_update(); } public: Radio(Element *parent); virtual ~Radio(); diff --git a/src/ui/elements/ui_slider.cpp b/src/ui/elements/ui_slider.cpp index 251a625..90a65a7 100644 --- a/src/ui/elements/ui_slider.cpp +++ b/src/ui/elements/ui_slider.cpp @@ -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); } @@ -151,7 +151,7 @@ namespace recompui { slider_element = context.create_element(this, true); 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(); }); { @@ -160,7 +160,7 @@ namespace recompui { 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([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(); }); circle_element = context.create_element(bar_element, true); diff --git a/src/ui/elements/ui_slider.h b/src/ui/elements/ui_slider.h index ce754c8..b5697ed 100644 --- a/src/ui/elements/ui_slider.h +++ b/src/ui/elements/ui_slider.h @@ -25,7 +25,7 @@ namespace recompui { std::vector> value_changed_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 circle_dragged(float x, float y, DragPhase phase); void update_value_from_mouse(float x); diff --git a/src/ui/elements/ui_types.h b/src/ui/elements/ui_types.h index e01ccac..08645ea 100644 --- a/src/ui/elements/ui_types.h +++ b/src/ui/elements/ui_types.h @@ -33,6 +33,7 @@ namespace recompui { Text, Update, Navigate, + MouseButton, Count }; @@ -50,6 +51,13 @@ namespace recompui { Left }; + enum class MouseButton { + Left, + Right, + Middle, + Count + }; + template >> constexpr uint32_t Events(Enum first) { return 1u << static_cast(first); @@ -91,7 +99,14 @@ namespace recompui { NavDirection direction; }; - using EventVariant = std::variant; + struct EventMouseButton { + float x; + float y; + MouseButton button; + bool pressed; + }; + + using EventVariant = std::variant; struct Event { EventType type; @@ -153,6 +168,13 @@ namespace recompui { e.variant = EventNavigate{ direction }; 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 {