Streamline reading of js DataTransfer object
qwasmclipboard.cpp and qwasmdrag.cpp had the same logic that read the js DataTransfer object implemented twice with small differences. Use a single implementation in both. This also introduces a clearer memory ownership model in the reader code, and fixes a potential race condition by introducing a cancellation flag. Removed the useless QWasmDrag type which was in essence a SimpleDrag and made the m_drag in QWasmIntegration a smart pointer. Fixes: QTBUG-109626 Change-Id: I5b76dd3b70ab2e5a8364d9a136c970ee8d4fae9c Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io> (cherry picked from commit 16bf899557febb8c0762e64ce88acf21bc84e334) Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
This commit is contained in:
parent
4ab04b35ab
commit
00bb651919
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include <QtCore/qcoreapplication.h>
|
#include <QtCore/qcoreapplication.h>
|
||||||
#include <QtCore/qfile.h>
|
#include <QtCore/qfile.h>
|
||||||
|
#include <QtCore/qmimedata.h>
|
||||||
|
|
||||||
#include <emscripten/bind.h>
|
#include <emscripten/bind.h>
|
||||||
#include <emscripten/emscripten.h>
|
#include <emscripten/emscripten.h>
|
||||||
#include <emscripten/html5.h>
|
#include <emscripten/html5.h>
|
||||||
@ -368,6 +370,111 @@ bool jsHaveAsyncify() { return false; }
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct DataTransferReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using DoneCallback = std::function<void(std::unique_ptr<QMimeData>)>;
|
||||||
|
|
||||||
|
static std::shared_ptr<CancellationFlag> read(emscripten::val webDataTransfer,
|
||||||
|
std::function<QVariant(QByteArray)> imageReader,
|
||||||
|
DoneCallback onCompleted)
|
||||||
|
{
|
||||||
|
auto cancellationFlag = std::make_shared<CancellationFlag>();
|
||||||
|
(new DataTransferReader(std::move(onCompleted), std::move(imageReader), cancellationFlag))
|
||||||
|
->read(webDataTransfer);
|
||||||
|
return cancellationFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
~DataTransferReader() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataTransferReader(DoneCallback onCompleted, std::function<QVariant(QByteArray)> imageReader,
|
||||||
|
std::shared_ptr<CancellationFlag> cancellationFlag)
|
||||||
|
: mimeData(std::make_unique<QMimeData>()),
|
||||||
|
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<int>(); ++i) {
|
||||||
|
const auto item = items[i];
|
||||||
|
const auto itemKind =
|
||||||
|
item["kind"].as<std::string>() == "string" ? ItemKind::String : ItemKind::File;
|
||||||
|
const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
|
||||||
|
|
||||||
|
switch (itemKind) {
|
||||||
|
case ItemKind::File: {
|
||||||
|
++fileCount;
|
||||||
|
|
||||||
|
qstdweb::File file(item.call<emscripten::val>("getAsFile"));
|
||||||
|
|
||||||
|
QByteArray fileContent(file.size(), Qt::Uninitialized);
|
||||||
|
file.stream(fileContent.data(), [this, itemMimeType, fileContent]() {
|
||||||
|
if (!fileContent.isEmpty()) {
|
||||||
|
if (itemMimeType.startsWith("image/")) {
|
||||||
|
mimeData->setImageData(imageReader(fileContent));
|
||||||
|
} else {
|
||||||
|
mimeData->setData(itemMimeType, fileContent.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++doneCount;
|
||||||
|
onFileRead();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ItemKind::String:
|
||||||
|
if (itemMimeType.contains("STRING", Qt::CaseSensitive)
|
||||||
|
|| itemMimeType.contains("TEXT", Qt::CaseSensitive)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QString a;
|
||||||
|
const QString data = QString::fromStdString(webDataTransfer.call<std::string>(
|
||||||
|
"getData", emscripten::val(itemMimeType.toStdString())));
|
||||||
|
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
if (itemMimeType == "text/html")
|
||||||
|
mimeData->setHtml(data);
|
||||||
|
else if (itemMimeType.isEmpty() || itemMimeType == "text/plain")
|
||||||
|
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<DataTransferReader> deleteThisLater(this);
|
||||||
|
if (!cancellationFlag.expired())
|
||||||
|
onCompleted(std::move(mimeData));
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileCount = 0;
|
||||||
|
int doneCount = 0;
|
||||||
|
std::unique_ptr<QMimeData> mimeData;
|
||||||
|
std::function<QVariant(QByteArray)> imageReader;
|
||||||
|
DoneCallback onCompleted;
|
||||||
|
|
||||||
|
std::weak_ptr<CancellationFlag> cancellationFlag;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ArrayBuffer::ArrayBuffer(uint32_t size)
|
ArrayBuffer::ArrayBuffer(uint32_t size)
|
||||||
@ -738,6 +845,13 @@ bool haveAsyncify()
|
|||||||
return HaveAsyncify;
|
return HaveAsyncify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CancellationFlag>
|
||||||
|
readDataTransfer(emscripten::val webDataTransfer, std::function<QVariant(QByteArray)> imageReader,
|
||||||
|
std::function<void(std::unique_ptr<QMimeData>)> onDone)
|
||||||
|
{
|
||||||
|
return DataTransferReader::read(webDataTransfer, std::move(imageReader), std::move(onDone));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace qstdweb
|
} // namespace qstdweb
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -16,15 +16,22 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <private/qglobal_p.h>
|
#include <private/qglobal_p.h>
|
||||||
#include <emscripten/val.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <functional>
|
|
||||||
#include "initializer_list"
|
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
#include "QtCore/qhash.h"
|
#include "QtCore/qhash.h"
|
||||||
|
|
||||||
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QMimeData;
|
||||||
|
|
||||||
namespace qstdweb {
|
namespace qstdweb {
|
||||||
extern const char makeContextfulPromiseFunctionName[];
|
extern const char makeContextfulPromiseFunctionName[];
|
||||||
|
|
||||||
@ -195,6 +202,14 @@ namespace qstdweb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool haveAsyncify();
|
bool haveAsyncify();
|
||||||
|
|
||||||
|
struct CancellationFlag
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<CancellationFlag>
|
||||||
|
readDataTransfer(emscripten::val webObject, std::function<QVariant(QByteArray)> imageReader,
|
||||||
|
std::function<void(std::unique_ptr<QMimeData>)> onDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -2,20 +2,18 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
#include "qwasmclipboard.h"
|
#include "qwasmclipboard.h"
|
||||||
|
#include "qwasmdom.h"
|
||||||
#include "qwasmwindow.h"
|
#include "qwasmwindow.h"
|
||||||
#include "qwasmstring.h"
|
#include "qwasmstring.h"
|
||||||
#include <private/qstdweb_p.h>
|
#include <private/qstdweb_p.h>
|
||||||
|
|
||||||
#include <emscripten.h>
|
|
||||||
#include <emscripten/html5.h>
|
|
||||||
#include <emscripten/bind.h>
|
|
||||||
#include <emscripten/val.h>
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <qpa/qwindowsysteminterface.h>
|
#include <qpa/qwindowsysteminterface.h>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
using namespace emscripten;
|
using namespace emscripten;
|
||||||
|
|
||||||
@ -68,93 +66,29 @@ static void qClipboardCopyTo(val event)
|
|||||||
commonCopyEvent(event);
|
commonCopyEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qWasmClipboardPaste(QMimeData *mData)
|
static void qClipboardPasteTo(val event)
|
||||||
{
|
{
|
||||||
// Persist clipboard data so that the app can read it when handling the CTRL+V
|
event.call<void>("preventDefault"); // prevent browser from handling drop event
|
||||||
QWasmIntegration::get()->clipboard()->
|
|
||||||
QPlatformClipboard::setMimeData(mData, QClipboard::Clipboard);
|
|
||||||
|
|
||||||
QWindowSystemInterface::handleKeyEvent(
|
static std::shared_ptr<qstdweb::CancellationFlag> readDataCancellation = nullptr;
|
||||||
0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
|
readDataCancellation = qstdweb::readDataTransfer(
|
||||||
}
|
event["clipboardData"],
|
||||||
|
[](QByteArray fileContent) {
|
||||||
|
QImage image;
|
||||||
|
image.loadFromData(fileContent, nullptr);
|
||||||
|
return image;
|
||||||
|
},
|
||||||
|
[event](std::unique_ptr<QMimeData> data) {
|
||||||
|
if (data->formats().isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
static void qClipboardPasteTo(val dataTransfer)
|
// Persist clipboard data so that the app can read it when handling the CTRL+V
|
||||||
{
|
QWasmIntegration::get()->clipboard()->QPlatformClipboard::setMimeData(
|
||||||
enum class ItemKind {
|
data.release(), QClipboard::Clipboard);
|
||||||
File,
|
|
||||||
String,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Data
|
QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_V,
|
||||||
{
|
Qt::ControlModifier, "V");
|
||||||
std::unique_ptr<QMimeData> data;
|
});
|
||||||
int fileCount = 0;
|
|
||||||
int doneCount = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto sharedData = std::make_shared<Data>();
|
|
||||||
sharedData->data = std::make_unique<QMimeData>();
|
|
||||||
|
|
||||||
auto continuation = [sharedData]() {
|
|
||||||
Q_ASSERT(sharedData->doneCount <= sharedData->fileCount);
|
|
||||||
if (sharedData->doneCount < sharedData->fileCount)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!sharedData->data->formats().isEmpty())
|
|
||||||
qWasmClipboardPaste(sharedData->data.release());
|
|
||||||
};
|
|
||||||
|
|
||||||
const val clipboardData = dataTransfer["clipboardData"];
|
|
||||||
const val items = clipboardData["items"];
|
|
||||||
for (int i = 0; i < items["length"].as<int>(); ++i) {
|
|
||||||
const val item = items[i];
|
|
||||||
const auto itemKind =
|
|
||||||
item["kind"].as<std::string>() == "string" ? ItemKind::String : ItemKind::File;
|
|
||||||
const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
|
|
||||||
|
|
||||||
switch (itemKind) {
|
|
||||||
case ItemKind::File: {
|
|
||||||
++sharedData->fileCount;
|
|
||||||
|
|
||||||
qstdweb::File file(item.call<emscripten::val>("getAsFile"));
|
|
||||||
|
|
||||||
QByteArray fileContent(file.size(), Qt::Uninitialized);
|
|
||||||
file.stream(fileContent.data(),
|
|
||||||
[continuation, itemMimeType, fileContent, sharedData]() {
|
|
||||||
if (!fileContent.isEmpty()) {
|
|
||||||
if (itemMimeType.startsWith("image/")) {
|
|
||||||
QImage image;
|
|
||||||
image.loadFromData(fileContent, nullptr);
|
|
||||||
sharedData->data->setImageData(image);
|
|
||||||
} else {
|
|
||||||
sharedData->data->setData(itemMimeType, fileContent.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++sharedData->doneCount;
|
|
||||||
continuation();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ItemKind::String:
|
|
||||||
if (itemMimeType.contains("STRING", Qt::CaseSensitive)
|
|
||||||
|| itemMimeType.contains("TEXT", Qt::CaseSensitive)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const QString data = QWasmString::toQString(
|
|
||||||
clipboardData.call<val>("getData", val(itemMimeType.toStdString())));
|
|
||||||
|
|
||||||
if (!data.isEmpty()) {
|
|
||||||
if (itemMimeType == "text/html")
|
|
||||||
sharedData->data->setHtml(data);
|
|
||||||
else if (itemMimeType.isEmpty() || itemMimeType == "text/plain")
|
|
||||||
sharedData->data->setText(data); // the type can be empty
|
|
||||||
else
|
|
||||||
sharedData->data->setData(itemMimeType, data.toLocal8Bit());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continuation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
|
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
|
||||||
|
@ -3,14 +3,18 @@
|
|||||||
|
|
||||||
#include "qwasmdom.h"
|
#include "qwasmdom.h"
|
||||||
|
|
||||||
|
#include <QMimeData>
|
||||||
#include <QtCore/qpoint.h>
|
#include <QtCore/qpoint.h>
|
||||||
#include <QtCore/qrect.h>
|
#include <QtCore/qrect.h>
|
||||||
|
#include <QtGui/qimage.h>
|
||||||
|
#include <private/qstdweb_p.h>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
|
void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
|
||||||
{
|
{
|
||||||
if (flag) {
|
if (flag) {
|
||||||
@ -31,6 +35,7 @@ QPoint mapPoint(emscripten::val source, emscripten::val target, const QPoint &po
|
|||||||
auto offset = sourceBoundingRect.topLeft() - targetBoundingRect.topLeft();
|
auto offset = sourceBoundingRect.topLeft() - targetBoundingRect.topLeft();
|
||||||
return (point + offset).toPoint();
|
return (point + offset).toPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@ -3,207 +3,76 @@
|
|||||||
|
|
||||||
#include "qwasmdrag.h"
|
#include "qwasmdrag.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include "qwasmdom.h"
|
||||||
#include <QMimeDatabase>
|
#include "qwasmeventtranslator.h"
|
||||||
|
#include <qpa/qwindowsysteminterface.h>
|
||||||
|
#include <QMimeData>
|
||||||
|
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <emscripten/html5.h>
|
|
||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
#include <emscripten/bind.h>
|
#include <emscripten/bind.h>
|
||||||
#include <private/qstdweb_p.h>
|
|
||||||
#include <qpa/qwindowsysteminterface.h>
|
#include <memory>
|
||||||
#include <private/qsimpledrag_p.h>
|
#include <string>
|
||||||
#include "qwasmcompositor.h"
|
|
||||||
#include "qwasmeventtranslator.h"
|
|
||||||
#include <QtCore/QEventLoop>
|
|
||||||
#include <QMimeData>
|
|
||||||
#include <private/qshapedpixmapdndwindow_p.h>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
using namespace emscripten;
|
namespace {
|
||||||
|
Qt::DropAction parseDropActions(emscripten::val event)
|
||||||
static void getTextPlainCallback(val m_string)
|
|
||||||
{
|
{
|
||||||
QWasmDrag *thisDrag = static_cast<QWasmDrag*>(QWasmIntegration::get()->drag());
|
const std::string dEffect = event["dataTransfer"]["dropEffect"].as<std::string>();
|
||||||
thisDrag->m_mimeData->setText(QString::fromStdString(m_string.as<std::string>()));
|
|
||||||
thisDrag->qWasmDrop();
|
if (dEffect == "copy")
|
||||||
|
return Qt::CopyAction;
|
||||||
|
else if (dEffect == "move")
|
||||||
|
return Qt::MoveAction;
|
||||||
|
else if (dEffect == "link")
|
||||||
|
return Qt::LinkAction;
|
||||||
|
return Qt::IgnoreAction;
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
static void getTextUrlCallback(val m_string)
|
void dropEvent(emscripten::val event)
|
||||||
{
|
|
||||||
QWasmDrag *thisDrag = static_cast<QWasmDrag*>(QWasmIntegration::get()->drag());
|
|
||||||
thisDrag->m_mimeData->setData(QStringLiteral("text/uri-list"),
|
|
||||||
QByteArray::fromStdString(m_string.as<std::string>()));
|
|
||||||
|
|
||||||
thisDrag->qWasmDrop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void getTextHtmlCallback(val m_string)
|
|
||||||
{
|
|
||||||
QWasmDrag *thisDrag = static_cast<QWasmDrag*>(QWasmIntegration::get()->drag());
|
|
||||||
thisDrag->m_mimeData->setHtml(QString::fromStdString(m_string.as<std::string>()));
|
|
||||||
|
|
||||||
thisDrag->qWasmDrop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dropEvent(val event)
|
|
||||||
{
|
{
|
||||||
// someone dropped a file into the browser window
|
// someone dropped a file into the browser window
|
||||||
// event is dataTransfer object
|
// event is dataTransfer object
|
||||||
// if drop event from outside browser, we do not get any mouse release, maybe mouse move
|
// if drop event from outside browser, we do not get any mouse release, maybe mouse move
|
||||||
// after the drop event
|
// after the drop event
|
||||||
|
|
||||||
// data-context thing was not working here :(
|
|
||||||
QWasmDrag *wasmDrag = static_cast<QWasmDrag*>(QWasmIntegration::get()->drag());
|
|
||||||
|
|
||||||
wasmDrag->m_wasmScreen =
|
|
||||||
reinterpret_cast<QWasmScreen*>(event["target"]["data-qtdropcontext"].as<quintptr>());
|
|
||||||
|
|
||||||
wasmDrag->m_mouseDropPoint = QPoint(event["x"].as<int>(), event["y"].as<int>());
|
|
||||||
if (wasmDrag->m_mimeData)
|
|
||||||
delete wasmDrag->m_mimeData;
|
|
||||||
wasmDrag->m_mimeData = new QMimeData;
|
|
||||||
wasmDrag->m_qButton = MouseEvent::buttonFromWeb(event["button"].as<int>());
|
|
||||||
|
|
||||||
wasmDrag->m_keyModifiers = Qt::NoModifier;
|
|
||||||
if (event["altKey"].as<bool>())
|
|
||||||
wasmDrag->m_keyModifiers |= Qt::AltModifier;
|
|
||||||
if (event["ctrlKey"].as<bool>())
|
|
||||||
wasmDrag->m_keyModifiers |= Qt::ControlModifier;
|
|
||||||
if (event["metaKey"].as<bool>())
|
|
||||||
wasmDrag->m_keyModifiers |= Qt::MetaModifier;
|
|
||||||
|
|
||||||
event.call<void>("preventDefault"); // prevent browser from handling drop event
|
event.call<void>("preventDefault"); // prevent browser from handling drop event
|
||||||
|
|
||||||
std::string dEffect = event["dataTransfer"]["dropEffect"].as<std::string>();
|
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["currentTarget"]["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);
|
||||||
|
|
||||||
wasmDrag->m_dropActions = Qt::IgnoreAction;
|
QWindowSystemInterface::handleDrag(window, data.get(), mouseDropPoint, dropAction,
|
||||||
if (dEffect == "copy")
|
button, modifiers);
|
||||||
wasmDrag->m_dropActions = Qt::CopyAction;
|
|
||||||
if (dEffect == "move")
|
|
||||||
wasmDrag->m_dropActions = Qt::MoveAction;
|
|
||||||
if (dEffect == "link")
|
|
||||||
wasmDrag->m_dropActions = Qt::LinkAction;
|
|
||||||
|
|
||||||
val dt = event["dataTransfer"]["items"]["length"];
|
// drag drop
|
||||||
|
QWindowSystemInterface::handleDrop(window, data.get(), mouseDropPoint, dropAction,
|
||||||
|
button, modifiers);
|
||||||
|
|
||||||
val typesCount = event["dataTransfer"]["types"]["length"];
|
// drag leave
|
||||||
|
QWindowSystemInterface::handleDrag(window, nullptr, QPoint(), Qt::IgnoreAction, {},
|
||||||
// handle mimedata
|
{});
|
||||||
int count = dt.as<int>();
|
|
||||||
wasmDrag->m_mimeTypesCount = count;
|
|
||||||
// kind is file type: file or string
|
|
||||||
for (int i=0; i < count; i++) {
|
|
||||||
val item = event["dataTransfer"]["items"][i];
|
|
||||||
val kind = item["kind"];
|
|
||||||
val fileType = item["type"];
|
|
||||||
|
|
||||||
if (kind.as<std::string>() == "file") {
|
|
||||||
val m_file = item.call<val>("getAsFile");
|
|
||||||
if (m_file.isUndefined()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qstdweb::File file(m_file);
|
|
||||||
|
|
||||||
QString mimeFormat = QString::fromStdString(file.type());
|
|
||||||
QByteArray fileContent;
|
|
||||||
fileContent.resize(file.size());
|
|
||||||
|
|
||||||
file.stream(fileContent.data(), [mimeFormat, fileContent]() {
|
|
||||||
if (!fileContent.isEmpty()) {
|
|
||||||
QWasmDrag *wasmDrag = static_cast<QWasmDrag *>(QWasmIntegration::get()->drag());
|
|
||||||
if (mimeFormat.contains("image")) {
|
|
||||||
QImage image;
|
|
||||||
image.loadFromData(fileContent, nullptr);
|
|
||||||
wasmDrag->m_mimeData->setImageData(image);
|
|
||||||
} else {
|
|
||||||
wasmDrag->m_mimeData->setData(mimeFormat, fileContent.data());
|
|
||||||
}
|
|
||||||
wasmDrag->qWasmDrop();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} else { // string
|
|
||||||
|
|
||||||
if (fileType.as<std::string>() == "text/uri-list"
|
|
||||||
|| fileType.as<std::string>() == "text/x-moz-url") {
|
|
||||||
item.call<val>("getAsString", val::module_property("qtgetTextUrl"));
|
|
||||||
} else if (fileType.as<std::string>() == "text/html") {
|
|
||||||
item.call<val>("getAsString", val::module_property("qtgetTextHtml"));
|
|
||||||
} else { // handle everything else here as plain text
|
|
||||||
item.call<val>("getAsString", val::module_property("qtgetTextPlain"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(drop_module) {
|
EMSCRIPTEN_BINDINGS(drop_module)
|
||||||
|
{
|
||||||
function("qtDrop", &dropEvent);
|
function("qtDrop", &dropEvent);
|
||||||
function("qtgetTextPlain", &getTextPlainCallback);
|
|
||||||
function("qtgetTextUrl", &getTextUrlCallback);
|
|
||||||
function("qtgetTextHtml", &getTextHtmlCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QWasmDrag::QWasmDrag()
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
QWasmDrag::~QWasmDrag()
|
|
||||||
{
|
|
||||||
if (m_mimeData)
|
|
||||||
delete m_mimeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QWasmDrag::init()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void QWasmDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
|
|
||||||
{
|
|
||||||
QSimpleDrag::drop(globalPos, b, mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QWasmDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
|
|
||||||
{
|
|
||||||
QSimpleDrag::move(globalPos, b, mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QWasmDrag::qWasmDrop()
|
|
||||||
{
|
|
||||||
// collect mime
|
|
||||||
QWasmDrag *thisDrag = static_cast<QWasmDrag*>(QWasmIntegration::get()->drag());
|
|
||||||
|
|
||||||
if (thisDrag->m_mimeTypesCount != thisDrag->m_mimeData->formats().size())
|
|
||||||
return; // keep collecting mimetypes
|
|
||||||
|
|
||||||
// start drag enter
|
|
||||||
QWindowSystemInterface::handleDrag(thisDrag->m_wasmScreen->topLevelAt(thisDrag->m_mouseDropPoint),
|
|
||||||
thisDrag->m_mimeData,
|
|
||||||
thisDrag->m_mouseDropPoint,
|
|
||||||
thisDrag->m_dropActions,
|
|
||||||
thisDrag->m_qButton,
|
|
||||||
thisDrag->m_keyModifiers);
|
|
||||||
|
|
||||||
// drag drop
|
|
||||||
QWindowSystemInterface::handleDrop(thisDrag->m_wasmScreen->topLevelAt(thisDrag->m_mouseDropPoint),
|
|
||||||
thisDrag->m_mimeData,
|
|
||||||
thisDrag->m_mouseDropPoint,
|
|
||||||
thisDrag->m_dropActions,
|
|
||||||
thisDrag->m_qButton,
|
|
||||||
thisDrag->m_keyModifiers);
|
|
||||||
|
|
||||||
// drag leave
|
|
||||||
QWindowSystemInterface::handleDrag(thisDrag->m_wasmScreen->topLevelAt(thisDrag->m_mouseDropPoint),
|
|
||||||
nullptr,
|
|
||||||
QPoint(),
|
|
||||||
Qt::IgnoreAction, { }, { });
|
|
||||||
|
|
||||||
thisDrag->m_mimeData->clear();
|
|
||||||
thisDrag->m_mimeTypesCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -4,39 +4,13 @@
|
|||||||
#ifndef QWASMDRAG_H
|
#ifndef QWASMDRAG_H
|
||||||
#define QWASMDRAG_H
|
#define QWASMDRAG_H
|
||||||
|
|
||||||
#include <qpa/qplatformdrag.h>
|
#include <QtCore/qtconfigmacros.h>
|
||||||
#include <private/qsimpledrag_p.h>
|
|
||||||
#include <private/qstdweb_p.h>
|
|
||||||
#include <QDrag>
|
|
||||||
#include "qwasmscreen.h"
|
|
||||||
|
|
||||||
QT_REQUIRE_CONFIG(draganddrop);
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QWasmDrag : public QSimpleDrag
|
void dropEvent(emscripten::val event);
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
QWasmDrag();
|
|
||||||
~QWasmDrag();
|
|
||||||
|
|
||||||
void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
|
|
||||||
void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
|
|
||||||
|
|
||||||
Qt::MouseButton m_qButton;
|
|
||||||
QPoint m_mouseDropPoint;
|
|
||||||
QFlags<Qt::KeyboardModifier> m_keyModifiers;
|
|
||||||
Qt::DropActions m_dropActions;
|
|
||||||
QWasmScreen *m_wasmScreen = nullptr;
|
|
||||||
int m_mimeTypesCount = 0;
|
|
||||||
QMimeData *m_mimeData = nullptr;
|
|
||||||
void qWasmDrop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -81,6 +81,12 @@ QWasmIntegration::QWasmIntegration()
|
|||||||
m_clipboard(new QWasmClipboard),
|
m_clipboard(new QWasmClipboard),
|
||||||
m_accessibility(new QWasmAccessibility)
|
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;
|
s_instance = this;
|
||||||
|
|
||||||
touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>();
|
touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>();
|
||||||
@ -122,7 +128,7 @@ QWasmIntegration::QWasmIntegration()
|
|||||||
visualViewport.call<void>("addEventListener", val("resize"),
|
visualViewport.call<void>("addEventListener", val("resize"),
|
||||||
val::module_property("qtResizeAllScreens"));
|
val::module_property("qtResizeAllScreens"));
|
||||||
}
|
}
|
||||||
m_drag = new QWasmDrag();
|
m_drag = std::make_unique<QSimpleDrag>();
|
||||||
}
|
}
|
||||||
|
|
||||||
QWasmIntegration::~QWasmIntegration()
|
QWasmIntegration::~QWasmIntegration()
|
||||||
@ -139,7 +145,6 @@ QWasmIntegration::~QWasmIntegration()
|
|||||||
delete m_desktopServices;
|
delete m_desktopServices;
|
||||||
if (m_platformInputContext)
|
if (m_platformInputContext)
|
||||||
delete m_platformInputContext;
|
delete m_platformInputContext;
|
||||||
delete m_drag;
|
|
||||||
delete m_accessibility;
|
delete m_accessibility;
|
||||||
|
|
||||||
for (const auto &elementAndScreen : m_screens)
|
for (const auto &elementAndScreen : m_screens)
|
||||||
@ -328,7 +333,7 @@ quint64 QWasmIntegration::getTimestamp()
|
|||||||
#if QT_CONFIG(draganddrop)
|
#if QT_CONFIG(draganddrop)
|
||||||
QPlatformDrag *QWasmIntegration::drag() const
|
QPlatformDrag *QWasmIntegration::drag() const
|
||||||
{
|
{
|
||||||
return m_drag;
|
return m_drag.get();
|
||||||
}
|
}
|
||||||
#endif // QT_CONFIG(draganddrop)
|
#endif // QT_CONFIG(draganddrop)
|
||||||
|
|
||||||
|
@ -6,23 +6,25 @@
|
|||||||
|
|
||||||
#include "qwasmwindow.h"
|
#include "qwasmwindow.h"
|
||||||
|
|
||||||
|
#include "qwasminputcontext.h"
|
||||||
|
|
||||||
#include <qpa/qplatformintegration.h>
|
#include <qpa/qplatformintegration.h>
|
||||||
#include <qpa/qplatformscreen.h>
|
#include <qpa/qplatformscreen.h>
|
||||||
#include <qpa/qplatforminputcontext.h>
|
#include <qpa/qplatforminputcontext.h>
|
||||||
|
|
||||||
|
#if QT_CONFIG(draganddrop)
|
||||||
|
# include "qwasmdrag.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QtCore/qhash.h>
|
#include <QtCore/qhash.h>
|
||||||
|
|
||||||
|
#include <private/qsimpledrag_p.h>
|
||||||
|
#include <private/qstdweb_p.h>
|
||||||
|
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <emscripten/html5.h>
|
#include <emscripten/html5.h>
|
||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
#include "qwasminputcontext.h"
|
|
||||||
#include <private/qstdweb_p.h>
|
|
||||||
|
|
||||||
#if QT_CONFIG(draganddrop)
|
|
||||||
#include "qwasmdrag.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QWasmEventTranslator;
|
class QWasmEventTranslator;
|
||||||
@ -97,7 +99,7 @@ private:
|
|||||||
mutable QWasmInputContext *m_platformInputContext = nullptr;
|
mutable QWasmInputContext *m_platformInputContext = nullptr;
|
||||||
|
|
||||||
#if QT_CONFIG(draganddrop)
|
#if QT_CONFIG(draganddrop)
|
||||||
QWasmDrag *m_drag;
|
std::unique_ptr<QSimpleDrag> m_drag;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user