mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:07:35 +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:
parent
dbd090fd95
commit
3b94af2c07
5 changed files with 60 additions and 19 deletions
|
@ -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& 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>(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<DragEvent>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Window>, 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<Core::EventLoop> m_event_loop;
|
||||
RefPtr<MenuBar> m_menubar;
|
||||
|
@ -113,9 +118,11 @@ private:
|
|||
WeakPtr<Window> 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<String> m_args;
|
||||
WeakPtr<Widget> m_drag_hovered_widget;
|
||||
WeakPtr<Widget> m_pending_drop_widget;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -314,16 +314,21 @@ void Widget::handle_paint_event(PaintEvent& event)
|
|||
});
|
||||
second_paint_event(event);
|
||||
|
||||
if (is_being_inspected()) {
|
||||
auto* app = Application::the();
|
||||
|
||||
if (app && app->dnd_debugging_enabled() && has_pending_drop()) {
|
||||
Painter painter(*this);
|
||||
painter.draw_rect(rect(), Color::Magenta);
|
||||
painter.draw_rect(rect(), Color::Blue);
|
||||
}
|
||||
|
||||
if (Application::the()->focus_debugging_enabled()) {
|
||||
if (is_focused()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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<DragEvent>(static_cast<Event::Type>(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>(event.type()), result.local_position, event.data_type());
|
||||
result.widget->dispatch_event(drag_move_event, this);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::handle_left_event()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue