From 4833ba06eaef20053f85343a13c0307394d67ded Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 17 Aug 2024 14:47:28 -0400 Subject: [PATCH] UI/Qt: Process drag-and-drop events through the web view This forwards all drag-and-drop events from the UI to the WebContent process. If the page accepts the events, the UI does not handle them. Otherwise, we will open the dropped files as file:// URLs. --- Ladybird/Qt/WebContentView.cpp | 141 +++++++++++++++++++++++++-------- Ladybird/Qt/WebContentView.h | 7 ++ 2 files changed, 113 insertions(+), 35 deletions(-) diff --git a/Ladybird/Qt/WebContentView.cpp b/Ladybird/Qt/WebContentView.cpp index b3a8084977f..86165c026e5 100644 --- a/Ladybird/Qt/WebContentView.cpp +++ b/Ladybird/Qt/WebContentView.cpp @@ -130,6 +130,10 @@ WebContentView::WebContentView(QWidget* window, RefPtr request_server_client {}; if (WebView::Application::web_content_options().use_lagom_networking == WebView::UseLagomNetworking::Yes) @@ -142,47 +146,47 @@ WebContentView::WebContentView(QWidget* window, RefPtrmimeData()->hasUrls()) - event->acceptProposedAction(); + if (!event->mimeData()->hasUrls()) + return; + + enqueue_native_event(Web::DragEvent::Type::DragStart, *event); + event->acceptProposedAction(); +} + +void WebContentView::dragMoveEvent(QDragMoveEvent* event) +{ + enqueue_native_event(Web::DragEvent::Type::DragMove, *event); + event->acceptProposedAction(); +} + +void WebContentView::dragLeaveEvent(QDragLeaveEvent*) +{ + // QDragLeaveEvent does not contain any mouse position or button information. + Web::DragEvent event {}; + event.type = Web::DragEvent::Type::DragEnd; + + enqueue_input_event(AK::move(event)); } void WebContentView::dropEvent(QDropEvent* event) { - VERIFY(event->mimeData()->hasUrls()); - emit urls_dropped(event->mimeData()->urls()); + enqueue_native_event(Web::DragEvent::Type::Drop, *event); event->acceptProposedAction(); } @@ -740,9 +761,9 @@ void WebContentView::enqueue_native_event(Web::MouseEvent::Type type, QSinglePoi Web::DevicePixelPoint position = { event.position().x() * m_device_pixel_ratio, event.position().y() * m_device_pixel_ratio }; auto screen_position = Gfx::IntPoint { event.globalPosition().x() * m_device_pixel_ratio, event.globalPosition().y() * m_device_pixel_ratio }; - auto button = get_button_from_qt_event(event); - auto buttons = get_buttons_from_qt_event(event); - auto modifiers = get_modifiers_from_qt_mouse_event(event); + auto button = get_button_from_qt_event(event.button()); + auto buttons = get_buttons_from_qt_event(event.buttons()); + auto modifiers = get_modifiers_from_qt_mouse_event(event.modifiers()); if (button == 0 && (type == Web::MouseEvent::Type::MouseDown || type == Web::MouseEvent::Type::MouseUp)) { // We could not convert Qt buttons to something that LibWeb can recognize - don't even bother propagating this @@ -776,6 +797,56 @@ void WebContentView::enqueue_native_event(Web::MouseEvent::Type type, QSinglePoi enqueue_input_event(Web::MouseEvent { type, position, screen_position.to_type(), button, buttons, modifiers, wheel_delta_x, wheel_delta_y, nullptr }); } +struct DragData : Web::ChromeInputData { + explicit DragData(QDropEvent const& event) + : urls(event.mimeData()->urls()) + { + } + + QList urls; +}; + +void WebContentView::enqueue_native_event(Web::DragEvent::Type type, QDropEvent const& event) +{ + Web::DevicePixelPoint position = { event.position().x() * m_device_pixel_ratio, event.position().y() * m_device_pixel_ratio }; + + auto global_position = mapToGlobal(event.position()); + auto screen_position = Gfx::IntPoint { global_position.x() * m_device_pixel_ratio, global_position.y() * m_device_pixel_ratio }; + + auto button = get_button_from_qt_event(Qt::LeftButton); + auto buttons = get_buttons_from_qt_event(event.buttons()); + auto modifiers = get_modifiers_from_qt_mouse_event(event.modifiers()); + + Vector files; + OwnPtr chrome_data; + + if (type == Web::DragEvent::Type::DragStart) { + VERIFY(event.mimeData()->hasUrls()); + + for (auto const& url : event.mimeData()->urls()) { + auto file_path = ak_byte_string_from_qstring(url.toLocalFile()); + + if (auto file = Web::HTML::SelectedFile::from_file_path(file_path); file.is_error()) + warnln("Unable to open file {}: {}", file_path, file.error()); + else + files.append(file.release_value()); + } + } else if (type == Web::DragEvent::Type::Drop) { + chrome_data = make(event); + } + + enqueue_input_event(Web::DragEvent { type, position, screen_position.to_type(), button, buttons, modifiers, AK::move(files), AK::move(chrome_data) }); +} + +void WebContentView::finish_handling_drag_event(Web::DragEvent const& event) +{ + if (event.type != Web::DragEvent::Type::Drop) + return; + + auto const& chrome_data = verify_cast(*event.chrome_data); + emit urls_dropped(chrome_data.urls); +} + struct KeyData : Web::ChromeInputData { explicit KeyData(QKeyEvent const& event) : event(adopt_own(*event.clone())) diff --git a/Ladybird/Qt/WebContentView.h b/Ladybird/Qt/WebContentView.h index 1b2760e726a..d2b77b8af70 100644 --- a/Ladybird/Qt/WebContentView.h +++ b/Ladybird/Qt/WebContentView.h @@ -58,6 +58,8 @@ public: virtual void wheelEvent(QWheelEvent*) override; virtual void mouseDoubleClickEvent(QMouseEvent*) override; virtual void dragEnterEvent(QDragEnterEvent*) override; + virtual void dragMoveEvent(QDragMoveEvent*) override; + virtual void dragLeaveEvent(QDragLeaveEvent*) override; virtual void dropEvent(QDropEvent*) override; virtual void keyPressEvent(QKeyEvent* event) override; virtual void keyReleaseEvent(QKeyEvent* event) override; @@ -101,8 +103,13 @@ private: void update_cursor(Gfx::StandardCursor cursor); void enqueue_native_event(Web::MouseEvent::Type, QSinglePointEvent const& event); + + void enqueue_native_event(Web::DragEvent::Type, QDropEvent const& event); + void finish_handling_drag_event(Web::DragEvent const&); + void enqueue_native_event(Web::KeyEvent::Type, QKeyEvent const& event); void finish_handling_key_event(Web::KeyEvent const&); + void update_screen_rects(); bool m_tooltip_override { false };