diff options
author | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-01-20 15:12:38 +0100 |
---|---|---|
committer | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-01-20 18:00:26 +0100 |
commit | 9b64bf0874b9e9323d6eadad2a8023c888f25182 (patch) | |
tree | 89bfd88836f6ba61c653dbe083612c1fb3ae636f /src/plugins/platforms/wasm | |
parent | c290e742c6e780f0b507ea0f3ba287f2f6045572 (diff) |
Handle the drop event in the wasm window element
Drop events are now handled in the wasm window element, which allows
the browser to select the drop target automatically. This also fixes
the case where drop data transfer finishes reading when a window
has already been closed and destroyed - the cancellation flag is now
owned by window so it gets invalidated as soon as window is gone.
The code has also been structured with a new DragEvent passthrough.
Fixes: QTBUG-109581
Change-Id: Ie3eb7446e2181fd540517f39397e8b35f111d009
Reviewed-by: MikoĊaj Boc <Mikolaj.Boc@qt.io>
Diffstat (limited to 'src/plugins/platforms/wasm')
-rw-r--r-- | src/plugins/platforms/wasm/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmcompositor.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmdrag.cpp | 78 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmdrag.h | 17 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmevent.cpp | 113 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmevent.h | 43 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmintegration.cpp | 6 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmintegration.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 30 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.h | 13 |
10 files changed, 176 insertions, 137 deletions
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt index 8902597001..ade727423c 100644 --- a/src/plugins/platforms/wasm/CMakeLists.txt +++ b/src/plugins/platforms/wasm/CMakeLists.txt @@ -37,7 +37,6 @@ qt_internal_add_plugin(QWasmIntegrationPlugin qwasmwindowclientarea.cpp qwasmwindowclientarea.h qwasmwindownonclientarea.cpp qwasmwindownonclientarea.h qwasminputcontext.cpp qwasminputcontext.h - qwasmdrag.cpp qwasmdrag.h qwasmwindowstack.cpp qwasmwindowstack.h DEFINES QT_EGL_NO_X11 diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 70938b323b..0700d66a55 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -83,9 +83,6 @@ void QWasmCompositor::deregisterEventHandlers() emscripten_set_touchend_callback(screenElementSelector.constData(), 0, 0, NULL); emscripten_set_touchmove_callback(screenElementSelector.constData(), 0, 0, NULL); emscripten_set_touchcancel_callback(screenElementSelector.constData(), 0, 0, NULL); - - screen()->element().call<void>("removeEventListener", std::string("drop"), - val::module_property("qtDrop"), val(true)); } void QWasmCompositor::destroy() @@ -123,11 +120,6 @@ void QWasmCompositor::initEventHandlers() &touchCallback); emscripten_set_touchcancel_callback(screenElementSelector.constData(), (void *)this, UseCapture, &touchCallback); - - screen()->element().call<void>("addEventListener", std::string("drop"), - val::module_property("qtDrop"), val(true)); - screen()->element().set("data-qtdropcontext", // ? unique - emscripten::val(quintptr(reinterpret_cast<void *>(screen())))); } void QWasmCompositor::addWindow(QWasmWindow *window) diff --git a/src/plugins/platforms/wasm/qwasmdrag.cpp b/src/plugins/platforms/wasm/qwasmdrag.cpp deleted file mode 100644 index 144c30e0fb..0000000000 --- a/src/plugins/platforms/wasm/qwasmdrag.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include "qwasmdrag.h" - -#include "qwasmdom.h" -#include "qwasmeventtranslator.h" -#include <qpa/qwindowsysteminterface.h> -#include <QMimeData> - -#include <emscripten.h> -#include <emscripten/val.h> -#include <emscripten/bind.h> - -#include <memory> -#include <string> - -QT_BEGIN_NAMESPACE - -namespace { -Qt::DropAction parseDropActions(emscripten::val event) -{ - const std::string dEffect = event["dataTransfer"]["dropEffect"].as<std::string>(); - - if (dEffect == "copy") - return Qt::CopyAction; - else if (dEffect == "move") - return Qt::MoveAction; - else if (dEffect == "link") - return Qt::LinkAction; - return Qt::IgnoreAction; -} -} // namespace - -void dropEvent(emscripten::val event) -{ - // someone dropped a file into the browser window - // event is dataTransfer object - // if drop event from outside browser, we do not get any mouse release, maybe mouse move - // after the drop event - event.call<void>("preventDefault"); // prevent browser from handling drop event - - static std::shared_ptr<qstdweb::CancellationFlag> readDataCancellation = nullptr; - readDataCancellation = qstdweb::readDataTransfer( - event["dataTransfer"], - [](QByteArray fileContent) { - QImage image; - image.loadFromData(fileContent, nullptr); - return image; - }, - [wasmScreen = reinterpret_cast<QWasmScreen *>( - event["target"]["data-qtdropcontext"].as<quintptr>()), - event](std::unique_ptr<QMimeData> data) { - const auto mouseDropPoint = QPoint(event["x"].as<int>(), event["y"].as<int>()); - const auto button = MouseEvent::buttonFromWeb(event["button"].as<int>()); - const Qt::KeyboardModifiers modifiers = KeyboardModifier::getForEvent(event); - const auto dropAction = parseDropActions(event); - auto *window = wasmScreen->topLevelAt(mouseDropPoint); - - QWindowSystemInterface::handleDrag(window, data.get(), mouseDropPoint, dropAction, - button, modifiers); - - // drag drop - QWindowSystemInterface::handleDrop(window, data.get(), mouseDropPoint, dropAction, - button, modifiers); - - // drag leave - QWindowSystemInterface::handleDrag(window, nullptr, QPoint(), Qt::IgnoreAction, {}, - {}); - }); -} - -EMSCRIPTEN_BINDINGS(drop_module) -{ - function("qtDrop", &dropEvent); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmdrag.h b/src/plugins/platforms/wasm/qwasmdrag.h deleted file mode 100644 index 624e9e3db6..0000000000 --- a/src/plugins/platforms/wasm/qwasmdrag.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#ifndef QWASMDRAG_H -#define QWASMDRAG_H - -#include <QtCore/qtconfigmacros.h> - -#include <emscripten/val.h> - -QT_BEGIN_NAMESPACE - -void dropEvent(emscripten::val event); - -QT_END_NAMESPACE - -#endif // QWASMDRAG_H diff --git a/src/plugins/platforms/wasm/qwasmevent.cpp b/src/plugins/platforms/wasm/qwasmevent.cpp index 19730c2c68..b6568e13fa 100644 --- a/src/plugins/platforms/wasm/qwasmevent.cpp +++ b/src/plugins/platforms/wasm/qwasmevent.cpp @@ -16,10 +16,63 @@ QFlags<Qt::KeyboardModifier> getForEvent<EmscriptenKeyboardEvent>( } } // namespace KeyboardModifier -std::optional<PointerEvent> PointerEvent::fromWeb(emscripten::val event) +Event::Event(EventType type, emscripten::val target) : type(type), target(target) { } + +Event::~Event() = default; + +Event::Event(const Event &other) = default; + +Event::Event(Event &&other) = default; + +Event &Event::operator=(const Event &other) = default; + +Event &Event::operator=(Event &&other) = default; + +MouseEvent::MouseEvent(EventType type, emscripten::val event) : Event(type, event["target"]) +{ + mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>()); + mouseButtons = MouseEvent::buttonsFromWeb(event["buttons"].as<unsigned short>()); + // The current button state (event.buttons) may be out of sync for some PointerDown + // events where the "down" state is very brief, for example taps on Apple trackpads. + // Qt expects that the current button state is in sync with the event, so we sync + // it up here. + if (type == EventType::PointerDown) + mouseButtons |= mouseButton; + localPoint = QPoint(event["offsetX"].as<int>(), event["offsetY"].as<int>()); + pointInPage = QPoint(event["pageX"].as<int>(), event["pageY"].as<int>()); + pointInViewport = QPoint(event["clientX"].as<int>(), event["clientY"].as<int>()); + modifiers = KeyboardModifier::getForEvent(event); +} + +MouseEvent::~MouseEvent() = default; + +MouseEvent::MouseEvent(const MouseEvent &other) = default; + +MouseEvent::MouseEvent(MouseEvent &&other) = default; + +MouseEvent &MouseEvent::operator=(const MouseEvent &other) = default; + +MouseEvent &MouseEvent::operator=(MouseEvent &&other) = default; + +PointerEvent::PointerEvent(EventType type, emscripten::val event) : MouseEvent(type, event) { - PointerEvent ret; + pointerId = event["pointerId"].as<int>(); + pointerType = event["pointerType"].as<std::string>() == "mouse" ? PointerType::Mouse + : PointerType::Other; +} + +PointerEvent::~PointerEvent() = default; + +PointerEvent::PointerEvent(const PointerEvent &other) = default; + +PointerEvent::PointerEvent(PointerEvent &&other) = default; + +PointerEvent &PointerEvent::operator=(const PointerEvent &other) = default; +PointerEvent &PointerEvent::operator=(PointerEvent &&other) = default; + +std::optional<PointerEvent> PointerEvent::fromWeb(emscripten::val event) +{ const auto eventType = ([&event]() -> std::optional<EventType> { const auto eventTypeString = event["type"].as<std::string>(); @@ -38,27 +91,47 @@ std::optional<PointerEvent> PointerEvent::fromWeb(emscripten::val event) if (!eventType) return std::nullopt; - ret.type = *eventType; - ret.target = event["target"]; - ret.pointerType = event["pointerType"].as<std::string>() == "mouse" ? - PointerType::Mouse : PointerType::Other; - ret.mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>()); - ret.mouseButtons = MouseEvent::buttonsFromWeb(event["buttons"].as<unsigned short>()); + return PointerEvent(*eventType, event); +} - // The current button state (event.buttons) may be out of sync for some PointerDown - // events where the "down" state is very brief, for example taps on Apple trackpads. - // Qt expects that the current button state is in sync with the event, so we sync - // it up here. - if (*eventType == EventType::PointerDown) - ret.mouseButtons |= ret.mouseButton; +DragEvent::DragEvent(EventType type, emscripten::val event) + : MouseEvent(type, event), dataTransfer(event["dataTransfer"]) +{ + dropAction = ([event]() { + const std::string effect = event["dataTransfer"]["dropEffect"].as<std::string>(); + + if (effect == "copy") + return Qt::CopyAction; + else if (effect == "move") + return Qt::MoveAction; + else if (effect == "link") + return Qt::LinkAction; + return Qt::IgnoreAction; + })(); +} - ret.localPoint = QPoint(event["offsetX"].as<int>(), event["offsetY"].as<int>()); - ret.pointInPage = QPoint(event["pageX"].as<int>(), event["pageY"].as<int>()); - ret.pointInViewport = QPoint(event["clientX"].as<int>(), event["clientY"].as<int>()); - ret.pointerId = event["pointerId"].as<int>(); - ret.modifiers = KeyboardModifier::getForEvent(event); +DragEvent::~DragEvent() = default; - return ret; +DragEvent::DragEvent(const DragEvent &other) = default; + +DragEvent::DragEvent(DragEvent &&other) = default; + +DragEvent &DragEvent::operator=(const DragEvent &other) = default; + +DragEvent &DragEvent::operator=(DragEvent &&other) = default; + +std::optional<DragEvent> DragEvent::fromWeb(emscripten::val event) +{ + const auto eventType = ([&event]() -> std::optional<EventType> { + const auto eventTypeString = event["type"].as<std::string>(); + + if (eventTypeString == "drop") + return EventType::Drop; + return std::nullopt; + })(); + if (!eventType) + return std::nullopt; + return DragEvent(*eventType, event); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h index f9d6411d60..215d06bae5 100644 --- a/src/plugins/platforms/wasm/qwasmevent.h +++ b/src/plugins/platforms/wasm/qwasmevent.h @@ -18,6 +18,7 @@ QT_BEGIN_NAMESPACE enum class EventType { + Drop, PointerDown, PointerMove, PointerUp, @@ -107,13 +108,20 @@ QFlags<Qt::KeyboardModifier> getForEvent<EmscriptenKeyboardEvent>( } // namespace KeyboardModifier -struct Q_CORE_EXPORT Event +struct Event { EventType type; emscripten::val target = emscripten::val::undefined(); + + Event(EventType type, emscripten::val target); + ~Event(); + Event(const Event &other); + Event(Event &&other); + Event &operator=(const Event &other); + Event &operator=(Event &&other); }; -struct Q_CORE_EXPORT MouseEvent : public Event +struct MouseEvent : public Event { QPoint localPoint; QPoint pointInPage; @@ -122,6 +130,13 @@ struct Q_CORE_EXPORT MouseEvent : public Event Qt::MouseButtons mouseButtons; QFlags<Qt::KeyboardModifier> modifiers; + MouseEvent(EventType type, emscripten::val webEvent); + ~MouseEvent(); + MouseEvent(const MouseEvent &other); + MouseEvent(MouseEvent &&other); + MouseEvent &operator=(const MouseEvent &other); + MouseEvent &operator=(MouseEvent &&other); + static constexpr Qt::MouseButton buttonFromWeb(int webButton) { switch (webButton) { case 0: @@ -158,14 +173,36 @@ struct Q_CORE_EXPORT MouseEvent : public Event } }; -struct Q_CORE_EXPORT PointerEvent : public MouseEvent +struct PointerEvent : public MouseEvent { static std::optional<PointerEvent> fromWeb(emscripten::val webEvent); + PointerEvent(EventType type, emscripten::val webEvent); + ~PointerEvent(); + PointerEvent(const PointerEvent &other); + PointerEvent(PointerEvent &&other); + PointerEvent &operator=(const PointerEvent &other); + PointerEvent &operator=(PointerEvent &&other); + PointerType pointerType; int pointerId; }; +struct DragEvent : public MouseEvent +{ + static std::optional<DragEvent> fromWeb(emscripten::val webEvent); + + DragEvent(EventType type, emscripten::val webEvent); + ~DragEvent(); + DragEvent(const DragEvent &other); + DragEvent(DragEvent &&other); + DragEvent &operator=(const DragEvent &other); + DragEvent &operator=(DragEvent &&other); + + Qt::DropAction dropAction; + emscripten::val dataTransfer; +}; + QT_END_NAMESPACE #endif // QWASMEVENT_H diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 090218a118..b92e456668 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -80,12 +80,6 @@ QWasmIntegration::QWasmIntegration() m_clipboard(new QWasmClipboard), m_accessibility(new QWasmAccessibility) { - // Temporary measure to make dropEvent appear in the library. EMSCRIPTEN_KEEPALIVE does not - // work, nor does a Q_CONSTRUCTOR_FUNCTION in qwasmdrag.cpp. - volatile bool foolEmcc = false; - if (foolEmcc) - dropEvent(emscripten::val::undefined()); - s_instance = this; touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>(); diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 8468c4c9e1..decf25009e 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -12,10 +12,6 @@ #include <qpa/qplatformscreen.h> #include <qpa/qplatforminputcontext.h> -#if QT_CONFIG(draganddrop) -# include "qwasmdrag.h" -#endif - #include <QtCore/qhash.h> #include <private/qsimpledrag_p.h> diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index a8f577cfea..3735c7dff4 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -84,6 +84,12 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", callback); m_pointerLeaveCallback = std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", callback); + + m_dropCallback = std::make_unique<qstdweb::EventCallback>( + m_qtWindow, "drop", [this](emscripten::val event) { + if (processDrop(*DragEvent::fromWeb(event))) + event.call<void>("preventDefault"); + }); } QWasmWindow::~QWasmWindow() @@ -418,6 +424,30 @@ bool QWasmWindow::processPointer(const PointerEvent &event) return false; } +bool QWasmWindow::processDrop(const DragEvent &event) +{ + m_dropDataReadCancellationFlag = qstdweb::readDataTransfer( + event.dataTransfer, + [](QByteArray fileContent) { + QImage image; + image.loadFromData(fileContent, nullptr); + return image; + }, + [this, event](std::unique_ptr<QMimeData> data) { + QWindowSystemInterface::handleDrag(window(), data.get(), event.pointInPage, + event.dropAction, event.mouseButton, + event.modifiers); + + QWindowSystemInterface::handleDrop(window(), data.get(), event.pointInPage, + event.dropAction, event.mouseButton, + event.modifiers); + + QWindowSystemInterface::handleDrag(window(), nullptr, QPoint(), Qt::IgnoreAction, + {}, {}); + }); + return true; +} + QRect QWasmWindow::normalGeometry() const { return m_normalGeometry; diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h index 5651495c74..906a7a1daf 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.h +++ b/src/plugins/platforms/wasm/qwasmwindow.h @@ -18,13 +18,21 @@ #include <emscripten/val.h> +#include <memory> + QT_BEGIN_NAMESPACE namespace qstdweb { +struct CancellationFlag; +} + +namespace qstdweb { class EventCallback; } class ClientArea; +struct DragEvent; +struct PointerEvent; class QWasmWindow final : public QPlatformWindow { @@ -84,6 +92,7 @@ private: void applyWindowState(); bool processPointer(const PointerEvent &event); + bool processDrop(const DragEvent &event); QWindow *m_window = nullptr; QWasmCompositor *m_compositor = nullptr; @@ -105,6 +114,8 @@ private: std::unique_ptr<qstdweb::EventCallback> m_pointerEnterCallback; std::unique_ptr<qstdweb::EventCallback> m_pointerMoveCallback; + std::unique_ptr<qstdweb::EventCallback> m_dropCallback; + Qt::WindowStates m_state = Qt::WindowNoState; Qt::WindowStates m_previousWindowState = Qt::WindowNoState; @@ -120,6 +131,8 @@ private: friend class QWasmCompositor; friend class QWasmEventTranslator; bool windowIsPopupType(Qt::WindowFlags flags) const; + + std::shared_ptr<qstdweb::CancellationFlag> m_dropDataReadCancellationFlag; }; QT_END_NAMESPACE |