From a747a10eabd12427bf3c4626e6929041aa81fa7c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 18 Apr 2019 04:12:27 +0200 Subject: [PATCH] LibGUI: Refactor context menus to be event-driven instead of declarative. The declarative approach had way too many limitations. This patch adds a context menu event that can be hooked to prepare a custom context menu on demand just-in-time. :^) --- Applications/VisualBuilder/VBForm.cpp | 12 ++++++++---- Applications/VisualBuilder/VBForm.h | 2 ++ LibGUI/GEvent.h | 18 ++++++++++++++++++ LibGUI/GWidget.cpp | 24 ++++++++---------------- LibGUI/GWidget.h | 8 +------- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/Applications/VisualBuilder/VBForm.cpp b/Applications/VisualBuilder/VBForm.cpp index 2666901775f..3b9c638d28a 100644 --- a/Applications/VisualBuilder/VBForm.cpp +++ b/Applications/VisualBuilder/VBForm.cpp @@ -35,16 +35,20 @@ VBForm::VBForm(const String& name, GWidget* parent) groupbox1->set_rect({ 300, 150, 161, 51 }); m_widgets.append(move(groupbox1)); - auto context_menu = make("Context menu"); - context_menu->add_action(GAction::create("Move to front", [this] (auto&) { + m_context_menu = make("Context menu"); + m_context_menu->add_action(GAction::create("Move to front", [this] (auto&) { if (m_selected_widget) m_selected_widget->gwidget()->move_to_front(); })); - context_menu->add_action(GAction::create("Move to back", [this] (auto&) { + m_context_menu->add_action(GAction::create("Move to back", [this] (auto&) { if (m_selected_widget) m_selected_widget->gwidget()->move_to_back(); })); - set_context_menu(move(context_menu), GWidget::ContextMenuMode::PassthroughMouseEvent); +} + +void VBForm::context_menu_event(GContextMenuEvent& event) +{ + m_context_menu->popup(event.screen_position()); } void VBForm::insert_widget(VBWidgetType type) diff --git a/Applications/VisualBuilder/VBForm.h b/Applications/VisualBuilder/VBForm.h index ed7624889b3..e28f133a71d 100644 --- a/Applications/VisualBuilder/VBForm.h +++ b/Applications/VisualBuilder/VBForm.h @@ -31,6 +31,7 @@ protected: virtual void mousedown_event(GMouseEvent&) override; virtual void mousemove_event(GMouseEvent&) override; virtual void mouseup_event(GMouseEvent&) override; + virtual void context_menu_event(GContextMenuEvent&) override; virtual void keydown_event(GKeyEvent&) override; private: @@ -47,4 +48,5 @@ private: Rect m_transform_widget_origin_rect; Point m_next_insertion_position; Direction m_resize_direction { Direction::None }; + OwnPtr m_context_menu; }; diff --git a/LibGUI/GEvent.h b/LibGUI/GEvent.h index cc0cd8aee2b..ecbe51ead3e 100644 --- a/LibGUI/GEvent.h +++ b/LibGUI/GEvent.h @@ -29,6 +29,7 @@ public: FocusIn, FocusOut, WindowCloseRequest, + ContextMenu, WM_WindowRemoved, WM_WindowStateChanged, WM_WindowIconChanged, @@ -141,6 +142,23 @@ private: Size m_size; }; +class GContextMenuEvent final : public GEvent { +public: + explicit GContextMenuEvent(const Point& position, const Point& screen_position) + : GEvent(GEvent::ContextMenu) + , m_position(position) + , m_screen_position(screen_position) + { + } + + const Point& position() const { return m_position; } + const Point& screen_position() const { return m_screen_position; } + +private: + Point m_position; + Point m_screen_position; +}; + class GShowEvent final : public GEvent { public: GShowEvent() diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 8dca7e125ba..59c0cb045aa 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -178,18 +178,14 @@ void GWidget::handle_mousedown_event(GMouseEvent& event) { if (accepts_focus()) set_focus(true); - if (event.button() == GMouseButton::Right) { - if (m_context_menu) { - if (m_context_menu_mode == ContextMenuMode::PassthroughMouseEvent) - mousedown_event(event); - m_context_menu->popup(screen_relative_rect().location().translated(event.position())); - return; - } - } // FIXME: Maybe the click clock should be per-button. if (!m_click_clock.is_valid()) m_click_clock.start(); mousedown_event(event); + if (event.button() == GMouseButton::Right) { + GContextMenuEvent c_event(event.position(), screen_relative_rect().location().translated(event.position())); + context_menu_event(c_event); + } } void GWidget::handle_enter_event(CEvent& event) @@ -253,6 +249,10 @@ void GWidget::mousemove_event(GMouseEvent&) { } +void GWidget::context_menu_event(GContextMenuEvent&) +{ +} + void GWidget::focusin_event(CEvent&) { } @@ -437,14 +437,6 @@ void GWidget::set_enabled(bool enabled) update(); } -void GWidget::set_context_menu(OwnPtr&& context_menu, ContextMenuMode mode) -{ - // FIXME: Support switching context menus. - ASSERT(!m_context_menu); - m_context_menu = move(context_menu); - m_context_menu_mode = mode; -} - void GWidget::move_to_front() { auto* parent = parent_widget(); diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 5fc98de9f59..1b53543cd5d 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -42,11 +42,6 @@ public: bool is_enabled() const { return m_enabled; } void set_enabled(bool); - enum class ContextMenuMode { SwallowMouseEvent, PassthroughMouseEvent }; - - const GMenu* context_menu() const { return m_context_menu.ptr(); } - void set_context_menu(OwnPtr&&, ContextMenuMode = ContextMenuMode::SwallowMouseEvent); - virtual void event(CEvent&) override; virtual void paint_event(GPaintEvent&); virtual void resize_event(GResizeEvent&); @@ -59,6 +54,7 @@ public: virtual void mouseup_event(GMouseEvent&); virtual void click_event(GMouseEvent&); virtual void doubleclick_event(GMouseEvent&); + virtual void context_menu_event(GContextMenuEvent&); virtual void focusin_event(CEvent&); virtual void focusout_event(CEvent&); virtual void enter_event(CEvent&); @@ -206,6 +202,4 @@ private: bool m_enabled { true }; CElapsedTimer m_click_clock; - OwnPtr m_context_menu; - ContextMenuMode m_context_menu_mode { ContextMenuMode::SwallowMouseEvent }; };