1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 23:47:45 +00:00

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.)
This commit is contained in:
Andreas Kling 2021-01-09 00:11:17 +01:00
parent dbd090fd95
commit 3b94af2c07
5 changed files with 60 additions and 19 deletions

View file

@ -93,6 +93,9 @@ Application::Application(int argc, char** argv)
if (getenv("GUI_FOCUS_DEBUG")) if (getenv("GUI_FOCUS_DEBUG"))
m_focus_debugging_enabled = true; m_focus_debugging_enabled = true;
if (getenv("GUI_DND_DEBUG"))
m_dnd_debugging_enabled = true;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
String arg(argv[i]); String arg(argv[i]);
m_args.append(move(arg)); m_args.append(move(arg));
@ -247,21 +250,36 @@ void Application::window_did_become_inactive(Badge<Window>, Window& window)
m_active_window = nullptr; 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) void Application::set_drag_hovered_widget_impl(Widget* widget, const Gfx::IntPoint& position, const String& mime_type)
{ {
if (widget == m_drag_hovered_widget) if (widget == m_drag_hovered_widget)
return; return;
if (m_drag_hovered_widget) { if (m_drag_hovered_widget) {
m_drag_hovered_widget->update(); Event leave_event(Event::DragLeave);
Core::EventLoop::current().post_event(*m_drag_hovered_widget, make<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; m_drag_hovered_widget = widget;
if (m_drag_hovered_widget) { if (m_drag_hovered_widget) {
m_drag_hovered_widget->update(); DragEvent enter_event(Event::DragEnter, position, mime_type);
Core::EventLoop::current().post_event(*m_drag_hovered_widget, make<DragEvent>(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);
} }
} }

View file

@ -74,6 +74,7 @@ public:
void set_system_palette(SharedBuffer&); void set_system_palette(SharedBuffer&);
bool focus_debugging_enabled() const { return m_focus_debugging_enabled; } 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; } Core::EventLoop& event_loop() { return *m_event_loop; }
@ -86,6 +87,9 @@ public:
Widget* drag_hovered_widget() { return m_drag_hovered_widget.ptr(); } Widget* drag_hovered_widget() { return m_drag_hovered_widget.ptr(); }
const Widget* drag_hovered_widget() const { 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<Window>, Widget* widget, const Gfx::IntPoint& position = {}, const String& mime_type = {}) void set_drag_hovered_widget(Badge<Window>, Widget* widget, const Gfx::IntPoint& position = {}, const String& mime_type = {})
{ {
set_drag_hovered_widget_impl(widget, position, mime_type); set_drag_hovered_widget_impl(widget, position, mime_type);
@ -99,6 +103,7 @@ private:
void tooltip_hide_timer_did_fire(); void tooltip_hide_timer_did_fire();
void set_drag_hovered_widget_impl(Widget*, const Gfx::IntPoint& = {}, const String& = {}); void set_drag_hovered_widget_impl(Widget*, const Gfx::IntPoint& = {}, const String& = {});
void set_pending_drop_widget(Widget*);
OwnPtr<Core::EventLoop> m_event_loop; OwnPtr<Core::EventLoop> m_event_loop;
RefPtr<MenuBar> m_menubar; RefPtr<MenuBar> m_menubar;
@ -113,9 +118,11 @@ private:
WeakPtr<Window> m_active_window; WeakPtr<Window> m_active_window;
bool m_quit_when_last_window_deleted { true }; bool m_quit_when_last_window_deleted { true };
bool m_focus_debugging_enabled { false }; bool m_focus_debugging_enabled { false };
bool m_dnd_debugging_enabled { false };
String m_invoked_as; String m_invoked_as;
Vector<String> m_args; Vector<String> m_args;
WeakPtr<Widget> m_drag_hovered_widget; WeakPtr<Widget> m_drag_hovered_widget;
WeakPtr<Widget> m_pending_drop_widget;
}; };
} }

View file

@ -314,17 +314,22 @@ void Widget::handle_paint_event(PaintEvent& event)
}); });
second_paint_event(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()) { if (is_being_inspected()) {
Painter painter(*this); Painter painter(*this);
painter.draw_rect(rect(), Color::Magenta); 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> layout) void Widget::set_layout(NonnullRefPtr<Layout> 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) void Widget::drag_enter_event(DragEvent& event)
{ {
dbgln("{} {:p} DRAG ENTER @ {}, {}", class_name(), this, event.position(), event.data_type()); 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); dbgln("{} {:p} DRAG LEAVE", class_name(), this);
event.ignore();
} }
void Widget::drop_event(DropEvent& event) void Widget::drop_event(DropEvent& event)
{ {
dbgln("{} {:p} DROP @ {}, '{}'", class_name(), this, event.position(), event.text()); dbgln("{} {:p} DROP @ {}, '{}'", class_name(), this, event.position(), event.text());
event.ignore();
} }
void Widget::theme_change_event(ThemeChangeEvent&) void Widget::theme_change_event(ThemeChangeEvent&)
@ -1035,4 +1036,9 @@ void Widget::set_shrink_to_fit(bool b)
invalidate_layout(); invalidate_layout();
} }
bool Widget::has_pending_drop() const
{
return Application::the()->pending_drop_widget() == this;
}
} }

View file

@ -302,6 +302,8 @@ public:
void set_shrink_to_fit(bool); void set_shrink_to_fit(bool);
bool is_shrink_to_fit() const { return m_shrink_to_fit; } bool is_shrink_to_fit() const { return m_shrink_to_fit; }
bool has_pending_drop() const;
protected: protected:
Widget(); Widget();

View file

@ -444,10 +444,18 @@ void Window::handle_drag_move_event(DragEvent& event)
if (!m_main_widget) if (!m_main_widget)
return; return;
auto result = m_main_widget->hit_test(event.position()); auto result = m_main_widget->hit_test(event.position());
auto local_event = make<DragEvent>(static_cast<Event::Type>(event.type()), result.local_position, event.data_type());
ASSERT(result.widget); ASSERT(result.widget);
Application::the()->set_drag_hovered_widget({}, result.widget, result.local_position, event.data_type()); 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>(event.type()), result.local_position, event.data_type());
result.widget->dispatch_event(drag_move_event, this);
}
} }
void Window::handle_left_event() void Window::handle_left_event()