From 3b94af2c07ea07e0c8b2b315ba63f6c8c2619daf Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 9 Jan 2021 00:11:17 +0100 Subject: [PATCH] LibGUI: Have widgets signal willingness to accept drops If a widget accept()'s a "drag enter" event, that widget now becomes the application-wide "pending drop" widget. That state is cleared if the drag moves over another widget (or leaves the window entirely.) --- Libraries/LibGUI/Application.cpp | 26 ++++++++++++++++++++++---- Libraries/LibGUI/Application.h | 7 +++++++ Libraries/LibGUI/Widget.cpp | 32 +++++++++++++++++++------------- Libraries/LibGUI/Widget.h | 2 ++ Libraries/LibGUI/Window.cpp | 12 ++++++++++-- 5 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Libraries/LibGUI/Application.cpp b/Libraries/LibGUI/Application.cpp index 96504acbbd..8aaae7026e 100644 --- a/Libraries/LibGUI/Application.cpp +++ b/Libraries/LibGUI/Application.cpp @@ -93,6 +93,9 @@ Application::Application(int argc, char** argv) if (getenv("GUI_FOCUS_DEBUG")) m_focus_debugging_enabled = true; + if (getenv("GUI_DND_DEBUG")) + m_dnd_debugging_enabled = true; + for (int i = 1; i < argc; i++) { String arg(argv[i]); m_args.append(move(arg)); @@ -247,21 +250,36 @@ void Application::window_did_become_inactive(Badge, Window& window) m_active_window = nullptr; } +void Application::set_pending_drop_widget(Widget* widget) +{ + if (m_pending_drop_widget == widget) + return; + if (m_pending_drop_widget) + m_pending_drop_widget->update(); + m_pending_drop_widget = widget; + if (m_pending_drop_widget) + m_pending_drop_widget->update(); +} + void Application::set_drag_hovered_widget_impl(Widget* widget, const Gfx::IntPoint& position, const String& mime_type) { if (widget == m_drag_hovered_widget) return; if (m_drag_hovered_widget) { - m_drag_hovered_widget->update(); - Core::EventLoop::current().post_event(*m_drag_hovered_widget, make(Event::DragLeave)); + Event leave_event(Event::DragLeave); + m_drag_hovered_widget->dispatch_event(leave_event, m_drag_hovered_widget->window()); } + set_pending_drop_widget(nullptr); m_drag_hovered_widget = widget; if (m_drag_hovered_widget) { - m_drag_hovered_widget->update(); - Core::EventLoop::current().post_event(*m_drag_hovered_widget, make(Event::DragEnter, position, mime_type)); + DragEvent enter_event(Event::DragEnter, position, mime_type); + enter_event.ignore(); + m_drag_hovered_widget->dispatch_event(enter_event, m_drag_hovered_widget->window()); + if (enter_event.is_accepted()) + set_pending_drop_widget(m_drag_hovered_widget); } } diff --git a/Libraries/LibGUI/Application.h b/Libraries/LibGUI/Application.h index 10b668a5cb..ea073c17e8 100644 --- a/Libraries/LibGUI/Application.h +++ b/Libraries/LibGUI/Application.h @@ -74,6 +74,7 @@ public: void set_system_palette(SharedBuffer&); bool focus_debugging_enabled() const { return m_focus_debugging_enabled; } + bool dnd_debugging_enabled() const { return m_dnd_debugging_enabled; } Core::EventLoop& event_loop() { return *m_event_loop; } @@ -86,6 +87,9 @@ public: Widget* drag_hovered_widget() { return m_drag_hovered_widget.ptr(); } const Widget* drag_hovered_widget() const { return m_drag_hovered_widget.ptr(); } + Widget* pending_drop_widget() { return m_pending_drop_widget.ptr(); } + const Widget* pending_drop_widget() const { return m_pending_drop_widget.ptr(); } + void set_drag_hovered_widget(Badge, Widget* widget, const Gfx::IntPoint& position = {}, const String& mime_type = {}) { set_drag_hovered_widget_impl(widget, position, mime_type); @@ -99,6 +103,7 @@ private: void tooltip_hide_timer_did_fire(); void set_drag_hovered_widget_impl(Widget*, const Gfx::IntPoint& = {}, const String& = {}); + void set_pending_drop_widget(Widget*); OwnPtr m_event_loop; RefPtr m_menubar; @@ -113,9 +118,11 @@ private: WeakPtr m_active_window; bool m_quit_when_last_window_deleted { true }; bool m_focus_debugging_enabled { false }; + bool m_dnd_debugging_enabled { false }; String m_invoked_as; Vector m_args; WeakPtr m_drag_hovered_widget; + WeakPtr m_pending_drop_widget; }; } diff --git a/Libraries/LibGUI/Widget.cpp b/Libraries/LibGUI/Widget.cpp index 924ef1fe4b..273d048f4a 100644 --- a/Libraries/LibGUI/Widget.cpp +++ b/Libraries/LibGUI/Widget.cpp @@ -314,17 +314,22 @@ void Widget::handle_paint_event(PaintEvent& event) }); second_paint_event(event); + auto* app = Application::the(); + + if (app && app->dnd_debugging_enabled() && has_pending_drop()) { + Painter painter(*this); + painter.draw_rect(rect(), Color::Blue); + } + + if (app && app->focus_debugging_enabled() && is_focused()) { + Painter painter(*this); + painter.draw_rect(rect(), Color::Cyan); + } + if (is_being_inspected()) { Painter painter(*this); painter.draw_rect(rect(), Color::Magenta); } - - if (Application::the()->focus_debugging_enabled()) { - if (is_focused()) { - Painter painter(*this); - painter.draw_rect(rect(), Color::Cyan); - } - } } void Widget::set_layout(NonnullRefPtr layout) @@ -499,27 +504,23 @@ void Widget::change_event(Event&) { } -void Widget::drag_move_event(DragEvent& event) +void Widget::drag_move_event(DragEvent&) { - event.ignore(); } void Widget::drag_enter_event(DragEvent& event) { dbgln("{} {:p} DRAG ENTER @ {}, {}", class_name(), this, event.position(), event.data_type()); - event.ignore(); } -void Widget::drag_leave_event(Event& event) +void Widget::drag_leave_event(Event&) { dbgln("{} {:p} DRAG LEAVE", class_name(), this); - event.ignore(); } void Widget::drop_event(DropEvent& event) { dbgln("{} {:p} DROP @ {}, '{}'", class_name(), this, event.position(), event.text()); - event.ignore(); } void Widget::theme_change_event(ThemeChangeEvent&) @@ -1035,4 +1036,9 @@ void Widget::set_shrink_to_fit(bool b) invalidate_layout(); } +bool Widget::has_pending_drop() const +{ + return Application::the()->pending_drop_widget() == this; +} + } diff --git a/Libraries/LibGUI/Widget.h b/Libraries/LibGUI/Widget.h index 22b3a487c8..692d92378f 100644 --- a/Libraries/LibGUI/Widget.h +++ b/Libraries/LibGUI/Widget.h @@ -302,6 +302,8 @@ public: void set_shrink_to_fit(bool); bool is_shrink_to_fit() const { return m_shrink_to_fit; } + bool has_pending_drop() const; + protected: Widget(); diff --git a/Libraries/LibGUI/Window.cpp b/Libraries/LibGUI/Window.cpp index b3471026a8..0094175c84 100644 --- a/Libraries/LibGUI/Window.cpp +++ b/Libraries/LibGUI/Window.cpp @@ -444,10 +444,18 @@ void Window::handle_drag_move_event(DragEvent& event) if (!m_main_widget) return; auto result = m_main_widget->hit_test(event.position()); - auto local_event = make(static_cast(event.type()), result.local_position, event.data_type()); ASSERT(result.widget); + Application::the()->set_drag_hovered_widget({}, result.widget, result.local_position, event.data_type()); - return result.widget->dispatch_event(*local_event, this); + + // NOTE: Setting the drag hovered widget may have executed arbitrary code, so re-check that the widget is still there. + if (!result.widget) + return; + + if (result.widget->has_pending_drop()) { + DragEvent drag_move_event(static_cast(event.type()), result.local_position, event.data_type()); + result.widget->dispatch_event(drag_move_event, this); + } } void Window::handle_left_event()