diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmclipboard.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmclipboard.cpp | 187 |
1 files changed, 64 insertions, 123 deletions
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index 4f4538eec6..1aa3ffa5b3 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -2,20 +2,19 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmclipboard.h" +#include "qwasmdom.h" +#include "qwasmevent.h" #include "qwasmwindow.h" -#include "qwasmstring.h" -#include <private/qstdweb_p.h> -#include <emscripten.h> -#include <emscripten/html5.h> -#include <emscripten/bind.h> -#include <emscripten/val.h> +#include <private/qstdweb_p.h> #include <QCoreApplication> #include <qpa/qwindowsysteminterface.h> #include <QBuffer> #include <QString> +#include <emscripten/val.h> + QT_BEGIN_NAMESPACE using namespace emscripten; @@ -27,12 +26,11 @@ static void commonCopyEvent(val event) // doing it this way seems to sanitize the text better that calling data() like down below if (_mimes->hasText()) { - event["clipboardData"].call<void>("setData", val("text/plain") - , QWasmString::fromQString(_mimes->text())); + event["clipboardData"].call<void>("setData", val("text/plain"), + _mimes->text().toEcmaString()); } if (_mimes->hasHtml()) { - event["clipboardData"].call<void>("setData", val("text/html") - , QWasmString::fromQString(_mimes->html())); + event["clipboardData"].call<void>("setData", val("text/html"), _mimes->html().toEcmaString()); } for (auto mimetype : _mimes->formats()) { @@ -40,21 +38,19 @@ static void commonCopyEvent(val event) continue; QByteArray ba = _mimes->data(mimetype); if (!ba.isEmpty()) - event["clipboardData"].call<void>("setData", QWasmString::fromQString(mimetype) - , val(ba.constData())); + event["clipboardData"].call<void>("setData", mimetype.toEcmaString(), + val(ba.constData())); } event.call<void>("preventDefault"); - QWasmIntegration::get()->getWasmClipboard()->m_isListener = false; } static void qClipboardCutTo(val event) { - QWasmIntegration::get()->getWasmClipboard()->m_isListener = true; - if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) { + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi()) { // Send synthetic Ctrl+X to make the app cut data to Qt's clipboard - QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( - 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "X"); + QWindowSystemInterface::handleKeyEvent( + 0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X"); } commonCopyEvent(event); @@ -62,80 +58,19 @@ static void qClipboardCutTo(val event) static void qClipboardCopyTo(val event) { - QWasmIntegration::get()->getWasmClipboard()->m_isListener = true; - - if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) { + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi()) { // Send synthetic Ctrl+C to make the app copy data to Qt's clipboard - QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( + QWindowSystemInterface::handleKeyEvent( 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C"); } commonCopyEvent(event); } -static void qClipboardPasteTo(val dataTransfer) +static void qClipboardPasteTo(val event) { - QWasmIntegration::get()->getWasmClipboard()->m_isListener = true; - val clipboardData = dataTransfer["clipboardData"]; - val types = clipboardData["types"]; - int typesCount = types["length"].as<int>(); - std::string stdMimeFormat; - QMimeData *mMimeData = new QMimeData; - for (int i = 0; i < typesCount; i++) { - stdMimeFormat = types[i].as<std::string>(); - QString mimeFormat = QString::fromStdString(stdMimeFormat); - if (mimeFormat.contains("STRING", Qt::CaseSensitive) || mimeFormat.contains("TEXT", Qt::CaseSensitive)) - continue; - - if (mimeFormat.contains("text")) { -// also "text/plain;charset=utf-8" -// "UTF8_STRING" "MULTIPLE" - val mimeData = clipboardData.call<val>("getData", val(stdMimeFormat)); // as DataTransfer - - const QString qstr = QWasmString::toQString(mimeData); + event.call<void>("preventDefault"); // prevent browser from handling drop event - if (qstr.length() > 0) { - if (mimeFormat.contains("text/html")) { - mMimeData->setHtml(qstr); - } else if (mimeFormat.isEmpty() || mimeFormat.contains("text/plain")) { - mMimeData->setText(qstr); // the type can be empty - } else { - mMimeData->setData(mimeFormat, qstr.toLocal8Bit());} - } - } else { - val items = clipboardData["items"]; - - int itemsCount = items["length"].as<int>(); - // handle data - for (int i = 0; i < itemsCount; i++) { - val item = items[i]; - val clipboardFile = item.call<emscripten::val>("getAsFile"); // string kind is handled above - if (clipboardFile.isUndefined() || item["kind"].as<std::string>() == "string" ) { - continue; - } - qstdweb::File file(clipboardFile); - - mimeFormat = QString::fromStdString(file.type()); - QByteArray fileContent; - fileContent.resize(file.size()); - - file.stream(fileContent.data(), [=]() { - if (!fileContent.isEmpty()) { - - if (mimeFormat.contains("image")) { - QImage image; - image.loadFromData(fileContent, nullptr); - mMimeData->setImageData(image); - } else { - mMimeData->setData(mimeFormat,fileContent.data()); - } - QWasmClipboard::qWasmClipboardPaste(mMimeData); - } - }); - } // next item - } - } - QWasmClipboard::qWasmClipboardPaste(mMimeData); - QWasmIntegration::get()->getWasmClipboard()->m_isListener = false; + QWasmIntegration::get()->getWasmClipboard()->sendClipboardData(event); } EMSCRIPTEN_BINDINGS(qtClipboardModule) { @@ -144,16 +79,14 @@ EMSCRIPTEN_BINDINGS(qtClipboardModule) { function("qtClipboardPasteTo", &qClipboardPasteTo); } -QWasmClipboard::QWasmClipboard() : - isPaste(false), - m_isListener(false) +QWasmClipboard::QWasmClipboard() { val clipboard = val::global("navigator")["clipboard"]; const bool hasPermissionsApi = !val::global("navigator")["permissions"].isUndefined(); - hasClipboardApi = !clipboard.isUndefined() && !clipboard["readText"].isUndefined(); + m_hasClipboardApi = !clipboard.isUndefined() && !clipboard["readText"].isUndefined(); - if (hasClipboardApi && hasPermissionsApi) + if (m_hasClipboardApi && hasPermissionsApi) initClipboardPermissions(); } @@ -171,31 +104,25 @@ QMimeData *QWasmClipboard::mimeData(QClipboard::Mode mode) void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) { - QPlatformClipboard::setMimeData(mimeData, mode); // handle setText/ setData programmatically - if (!isPaste) { - if (hasClipboardApi) { - writeToClipboardApi(); - } else if (!m_isListener) { - writeToClipboard(mimeData); - } - } - isPaste = false; + QPlatformClipboard::setMimeData(mimeData, mode); + if (m_hasClipboardApi) + writeToClipboardApi(); + else + writeToClipboard(); } -QWasmClipboard::ProcessKeyboardResult -QWasmClipboard::processKeyboard(const QWasmEventTranslator::TranslatedEvent &event, - const QFlags<Qt::KeyboardModifier> &modifiers) +QWasmClipboard::ProcessKeyboardResult QWasmClipboard::processKeyboard(const KeyEvent &event) { - if (event.type != QEvent::KeyPress || !modifiers.testFlag(Qt::ControlModifier)) + if (event.type != EventType::KeyDown || !event.modifiers.testFlag(Qt::ControlModifier)) return ProcessKeyboardResult::Ignored; if (event.key != Qt::Key_C && event.key != Qt::Key_V && event.key != Qt::Key_X) return ProcessKeyboardResult::Ignored; - isPaste = event.key == Qt::Key_V; + const bool isPaste = event.key == Qt::Key_V; - return hasClipboardApi && !isPaste + return m_hasClipboardApi && !isPaste ? ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded : ProcessKeyboardResult::NativeClipboardEventNeeded; } @@ -211,14 +138,6 @@ bool QWasmClipboard::ownsMode(QClipboard::Mode mode) const return false; } -void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData) -{ - QWasmIntegration::get()->clipboard()->setMimeData(mData, QClipboard::Clipboard); - - QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( - 0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V"); -} - void QWasmClipboard::initClipboardPermissions() { val permissions = val::global("navigator")["permissions"]; @@ -235,15 +154,15 @@ void QWasmClipboard::initClipboardPermissions() })()); } -void QWasmClipboard::installEventHandlers(const emscripten::val &canvas) +void QWasmClipboard::installEventHandlers(const emscripten::val &target) { emscripten::val cContext = val::undefined(); emscripten::val isChromium = val::global("window")["chrome"]; - if (!isChromium.isUndefined()) { + if (!isChromium.isUndefined()) { cContext = val::global("document"); - } else { - cContext = canvas; - } + } else { + cContext = target; + } // Fallback path for browsers which do not support direct clipboard access cContext.call<void>("addEventListener", val("cut"), val::module_property("qtClipboardCutTo"), true); @@ -253,16 +172,20 @@ void QWasmClipboard::installEventHandlers(const emscripten::val &canvas) val::module_property("qtClipboardPasteTo"), true); } +bool QWasmClipboard::hasClipboardApi() +{ + return m_hasClipboardApi; +} + void QWasmClipboard::writeToClipboardApi() { - if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) - return; + Q_ASSERT(m_hasClipboardApi); // copy event // browser event handler detected ctrl c if clipboard API // or Qt call from keyboard event handler - QMimeData *_mimes = QWasmIntegration::get()->getWasmClipboard()->mimeData(QClipboard::Clipboard); + QMimeData *_mimes = mimeData(QClipboard::Clipboard); if (!_mimes) return; @@ -320,12 +243,12 @@ void QWasmClipboard::writeToClipboardApi() // we have a blob, now create a ClipboardItem emscripten::val type = emscripten::val::array(); - type.set("type", val(QWasmString::fromQString(mimetype))); + type.set("type", mimetype.toEcmaString()); emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type); emscripten::val clipboardItemObject = emscripten::val::object(); - clipboardItemObject.set(val(QWasmString::fromQString(mimetype)), contentBlob); + clipboardItemObject.set(mimetype.toEcmaString(), contentBlob); val clipboardItemData = val::global("ClipboardItem").new_(clipboardItemObject); @@ -350,9 +273,8 @@ void QWasmClipboard::writeToClipboardApi() clipboardWriteArray); } -void QWasmClipboard::writeToClipboard(const QMimeData *data) +void QWasmClipboard::writeToClipboard() { - Q_UNUSED(data) // this works for firefox, chrome by generating // copy event, but not safari // execCommand has been deemed deprecated in the docs, but browsers do not seem @@ -360,4 +282,23 @@ void QWasmClipboard::writeToClipboard(const QMimeData *data) val document = val::global("document"); document.call<val>("execCommand", val("copy")); } + +void QWasmClipboard::sendClipboardData(emscripten::val event) +{ + qDebug() << "sendClipboardData"; + + dom::DataTransfer *transfer = new dom::DataTransfer(event["clipboardData"]); + const auto mimeCallback = std::function([transfer](QMimeData *data) { + + // 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"); + QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_V, + Qt::ControlModifier, "V"); + delete transfer; + }); + + transfer->toMimeDataWithFile(mimeCallback); +} QT_END_NAMESPACE |