diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp index 38ade571bda..2de5fca0402 100644 --- a/src/corelib/platform/wasm/qstdweb.cpp +++ b/src/corelib/platform/wasm/qstdweb.cpp @@ -376,112 +376,6 @@ bool jsHaveAsyncify() { return false; } bool jsHaveJspi() { return false; } #endif - -struct DataTransferReader -{ -public: - using DoneCallback = std::function)>; - - static std::shared_ptr read(emscripten::val webDataTransfer, - std::function imageReader, - DoneCallback onCompleted) - { - auto cancellationFlag = std::make_shared(); - (new DataTransferReader(std::move(onCompleted), std::move(imageReader), cancellationFlag)) - ->read(webDataTransfer); - return cancellationFlag; - } - - ~DataTransferReader() = default; - -private: - DataTransferReader(DoneCallback onCompleted, std::function imageReader, - std::shared_ptr cancellationFlag) - : mimeData(std::make_unique()), - imageReader(std::move(imageReader)), - onCompleted(std::move(onCompleted)), - cancellationFlag(cancellationFlag) - { - } - - void read(emscripten::val webDataTransfer) - { - enum class ItemKind { - File, - String, - }; - - const auto items = webDataTransfer["items"]; - for (int i = 0; i < items["length"].as(); ++i) { - const auto item = items[i]; - const auto itemKind = - item["kind"].as() == "string" ? ItemKind::String : ItemKind::File; - const auto itemMimeType = QString::fromStdString(item["type"].as()); - - switch (itemKind) { - case ItemKind::File: { - ++fileCount; - - qstdweb::File file(item.call("getAsFile")); - - QByteArray fileContent(file.size(), Qt::Uninitialized); - file.stream(fileContent.data(), [this, itemMimeType, fileContent]() { - if (!fileContent.isEmpty()) { - if (itemMimeType.startsWith("image/"_L1)) { - mimeData->setImageData(imageReader(fileContent)); - } else { - mimeData->setData(itemMimeType, fileContent.data()); - } - } - ++doneCount; - onFileRead(); - }); - break; - } - case ItemKind::String: - if (itemMimeType.contains("STRING"_L1, Qt::CaseSensitive) - || itemMimeType.contains("TEXT"_L1, Qt::CaseSensitive)) { - break; - } - QString a; - const QString data = QString::fromEcmaString(webDataTransfer.call( - "getData", emscripten::val(itemMimeType.toStdString()))); - - if (!data.isEmpty()) { - if (itemMimeType == "text/html"_L1) - mimeData->setHtml(data); - else if (itemMimeType.isEmpty() || itemMimeType == "text/plain"_L1) - mimeData->setText(data); // the type can be empty - else - mimeData->setData(itemMimeType, data.toLocal8Bit()); - } - break; - } - } - - onFileRead(); - } - - void onFileRead() - { - Q_ASSERT(doneCount <= fileCount); - if (doneCount < fileCount) - return; - - std::unique_ptr deleteThisLater(this); - if (!cancellationFlag.expired()) - onCompleted(std::move(mimeData)); - } - - int fileCount = 0; - int doneCount = 0; - std::unique_ptr mimeData; - std::function imageReader; - DoneCallback onCompleted; - - std::weak_ptr cancellationFlag; -}; - } // namespace ArrayBuffer::ArrayBuffer(uint32_t size) @@ -948,13 +842,6 @@ bool canBlockCallingThread() return haveAsyncify() || !emscripten_is_main_runtime_thread(); } -std::shared_ptr -readDataTransfer(emscripten::val webDataTransfer, std::function imageReader, - std::function)> onDone) -{ - return DataTransferReader::read(webDataTransfer, std::move(imageReader), std::move(onDone)); -} - BlobIODevice::BlobIODevice(Blob blob) : m_blob(blob) { diff --git a/src/corelib/platform/wasm/qstdweb_p.h b/src/corelib/platform/wasm/qstdweb_p.h index 110e2c10ac4..707d96704b3 100644 --- a/src/corelib/platform/wasm/qstdweb_p.h +++ b/src/corelib/platform/wasm/qstdweb_p.h @@ -290,11 +290,6 @@ namespace qstdweb { { }; - Q_CORE_EXPORT std::shared_ptr - readDataTransfer(emscripten::val webObject, std::function imageReader, - std::function)> onDone); - - #if QT_CONFIG(thread) template T proxyCall(std::function task, emscripten::ProxyingQueue *queue) diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index 8916a946d4a..9c7088067eb 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -71,24 +71,15 @@ static void qClipboardPasteTo(val event) event.call("preventDefault"); // prevent browser from handling drop event static std::shared_ptr readDataCancellation = nullptr; - readDataCancellation = qstdweb::readDataTransfer( - event["clipboardData"], - [](QByteArray fileContent) { - QImage image; - image.loadFromData(fileContent, nullptr); - return image; - }, - [event](std::unique_ptr data) { - if (data->formats().isEmpty()) - return; - - // Persist clipboard data so that the app can read it when handling the CTRL+V - QWasmIntegration::get()->clipboard()->QPlatformClipboard::setMimeData( - data.release(), QClipboard::Clipboard); - - QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_V, - Qt::ControlModifier, "V"); - }); + dom::DataTransfer transfer(event["clipboardData"]); + auto data = transfer.toMimeDataWithFile(); + if (data->formats().isEmpty()) + return; + // Persist clipboard data so that the app can read it when handling the CTRL+V + QWasmIntegration::get()->clipboard()->QPlatformClipboard::setMimeData(data, + QClipboard::Clipboard); + QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_V, + Qt::ControlModifier, "V"); } EMSCRIPTEN_BINDINGS(qtClipboardModule) { @@ -300,4 +291,5 @@ void QWasmClipboard::writeToClipboard() val document = val::global("document"); document.call("execCommand", val("copy")); } + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h index 4a21252c8ed..11e7e9f714d 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.h +++ b/src/plugins/platforms/wasm/qwasmclipboard.h @@ -7,6 +7,7 @@ #include #include +#include #include #include diff --git a/src/plugins/platforms/wasm/qwasmdom.cpp b/src/plugins/platforms/wasm/qwasmdom.cpp index ebb2dd4807a..845aef3bc10 100644 --- a/src/plugins/platforms/wasm/qwasmdom.cpp +++ b/src/plugins/platforms/wasm/qwasmdom.cpp @@ -8,12 +8,152 @@ #include #include #include +#include #include +#include QT_BEGIN_NAMESPACE namespace dom { +namespace { +std::string dropActionToDropEffect(Qt::DropAction action) +{ + switch (action) { + case Qt::DropAction::CopyAction: + return "copy"; + case Qt::DropAction::IgnoreAction: + return "none"; + case Qt::DropAction::LinkAction: + return "link"; + case Qt::DropAction::MoveAction: + case Qt::DropAction::TargetMoveAction: + return "move"; + case Qt::DropAction::ActionMask: + Q_ASSERT(false); + return ""; + } +} +} // namespace + +DataTransfer::DataTransfer(emscripten::val webDataTransfer) : webDataTransfer(webDataTransfer) { } + +DataTransfer::~DataTransfer() = default; + +DataTransfer::DataTransfer(const DataTransfer &other) = default; + +DataTransfer::DataTransfer(DataTransfer &&other) = default; + +DataTransfer &DataTransfer::operator=(const DataTransfer &other) = default; + +DataTransfer &DataTransfer::operator=(DataTransfer &&other) = default; + +void DataTransfer::setDragImage(emscripten::val element, const QPoint &hotspot) +{ + webDataTransfer.call("setDragImage", element, emscripten::val(hotspot.x()), + emscripten::val(hotspot.y())); +} + +void DataTransfer::setData(std::string format, std::string data) +{ + webDataTransfer.call("setData", emscripten::val(std::move(format)), + emscripten::val(std::move(data))); +} + +void DataTransfer::setDropAction(Qt::DropAction action) +{ + webDataTransfer.set("dropEffect", emscripten::val(dropActionToDropEffect(action))); +} + +void DataTransfer::setDataFromMimeData(const QMimeData &mimeData) +{ + for (const auto &format : mimeData.formats()) { + auto data = mimeData.data(format); + + auto encoded = format.startsWith("text/") + ? QString::fromLocal8Bit(data).toStdString() + : "QB64" + QString::fromLocal8Bit(data.toBase64()).toStdString(); + + setData(format.toStdString(), std::move(encoded)); + } +} + +QMimeData *DataTransfer::toMimeDataWithFile() +{ + QMimeData *resultMimeData = new QMimeData(); // QScopedPointer? + + enum class ItemKind { + File, + String, + }; + + const auto items = webDataTransfer["items"]; + QList uriList; + for (int i = 0; i < items["length"].as(); ++i) { + const auto item = items[i]; + const auto itemKind = + item["kind"].as() == "string" ? ItemKind::String : ItemKind::File; + const auto itemMimeType = QString::fromStdString(item["type"].as()); + + switch (itemKind) { + case ItemKind::File: { + m_webFile = item.call("getAsFile"); + qstdweb::File webfile(m_webFile); + QUrl fileUrl(QStringLiteral("file:///") + QString::fromStdString(webfile.name())); + uriList.append(fileUrl); + break; + } + case ItemKind::String: + if (itemMimeType.contains("STRING", Qt::CaseSensitive) + || itemMimeType.contains("TEXT", Qt::CaseSensitive)) { + break; + } + QString a; + QString data = QString::fromEcmaString(webDataTransfer.call( + "getData", emscripten::val(itemMimeType.toStdString()))); + + if (!data.isEmpty()) { + if (itemMimeType == "text/html") + resultMimeData->setHtml(data); + else if (itemMimeType.isEmpty() || itemMimeType == "text/plain") + resultMimeData->setText(data); // the type can be empty + else { + // TODO improve encoding + if (data.startsWith("QB64")) { + data.remove(0, 4); + resultMimeData->setData(itemMimeType, + QByteArray::fromBase64(QByteArray::fromStdString( + data.toStdString()))); + } else { + resultMimeData->setData(itemMimeType, data.toLocal8Bit()); + } + } + } + break; + } + } + if (!uriList.isEmpty()) + resultMimeData->setUrls(uriList); + return resultMimeData; +} + +QMimeData *DataTransfer::toMimeDataPreview() +{ + auto data = new QMimeData(); + + QList uriList; + for (int i = 0; i < webDataTransfer["items"]["length"].as(); ++i) { + const auto item = webDataTransfer["items"][i]; + if (item["kind"].as() == "file") { + uriList.append(QUrl("blob://placeholder")); + } else { + const auto itemMimeType = QString::fromStdString(item["type"].as()); + data->setData(itemMimeType, QByteArray()); + } + } + data->setUrls(uriList); + return data; +} void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag) { diff --git a/src/plugins/platforms/wasm/qwasmdom.h b/src/plugins/platforms/wasm/qwasmdom.h index 853e54540ec..3ccafba534c 100644 --- a/src/plugins/platforms/wasm/qwasmdom.h +++ b/src/plugins/platforms/wasm/qwasmdom.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include @@ -15,10 +17,36 @@ QT_BEGIN_NAMESPACE +namespace qstdweb { + struct CancellationFlag; +} + +class QMimeData; class QPoint; class QRect; namespace dom { +struct DataTransfer +{ + explicit DataTransfer(emscripten::val webDataTransfer); + ~DataTransfer(); + DataTransfer(const DataTransfer &other); + DataTransfer(DataTransfer &&other); + DataTransfer &operator=(const DataTransfer &other); + DataTransfer &operator=(DataTransfer &&other); + + QMimeData *toMimeDataWithFile() ; + QMimeData *toMimeDataPreview(); + void setDragImage(emscripten::val element, const QPoint &hotspot); + void setData(std::string format, std::string data); + void setDropAction(Qt::DropAction dropAction); + void setDataFromMimeData(const QMimeData &mimeData); + + emscripten::val webDataTransfer; + emscripten::val m_webFile = emscripten::val::undefined(); + qstdweb::File m_file; +}; + inline emscripten::val document() { return emscripten::val::global("document"); diff --git a/src/plugins/platforms/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h index 8ccbbb71045..c5c20a0b5e9 100644 --- a/src/plugins/platforms/wasm/qwasmevent.h +++ b/src/plugins/platforms/wasm/qwasmevent.h @@ -5,6 +5,7 @@ #define QWASMEVENT_H #include "qwasmplatform.h" +#include "qwasmdom.h" #include #include @@ -241,7 +242,7 @@ struct DragEvent : public MouseEvent void acceptDrop(); Qt::DropAction dropAction; - emscripten::val dataTransfer; + dom::DataTransfer dataTransfer; QWindow *targetWindow; }; diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index bf2fdf47736..fa7eed14640 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -114,7 +114,6 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, std::make_unique(m_qtWindow, "pointerenter", pointerCallback); m_pointerLeaveCallback = std::make_unique(m_qtWindow, "pointerleave", pointerCallback); - m_dropCallback = std::make_unique( m_qtWindow, "drop", [this](emscripten::val event) { if (processDrop(*DragEvent::fromWeb(event, window()))) @@ -538,25 +537,19 @@ bool QWasmWindow::processPointer(const PointerEvent &event) 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 data) { - QWindowSystemInterface::handleDrag(window(), data.get(), - event.pointInPage.toPoint(), event.dropAction, - event.mouseButton, event.modifiers); + dom::DataTransfer transfer(event.dataTransfer.webDataTransfer["clipboardData"]); + QMimeData *data = transfer.toMimeDataWithFile(); + // TODO handle file + QWindowSystemInterface::handleDrag(window(), data, + event.pointInPage.toPoint(), event.dropAction, + event.mouseButton, event.modifiers); - QWindowSystemInterface::handleDrop(window(), data.get(), - event.pointInPage.toPoint(), event.dropAction, - event.mouseButton, event.modifiers); + QWindowSystemInterface::handleDrop(window(), data, + event.pointInPage.toPoint(), event.dropAction, + event.mouseButton, event.modifiers); - QWindowSystemInterface::handleDrag(window(), nullptr, QPoint(), Qt::IgnoreAction, - {}, {}); - }); + QWindowSystemInterface::handleDrag(window(), nullptr, QPoint(), Qt::IgnoreAction, + {}, {}); return true; }