wasm: move DataTransfer to dom::
Change-Id: I069292154bafd1c08a0d0f2e8a62052f596a80f3 Done-with: Mikolaj.Boc@qt.io Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
d048249c33
commit
6b9270fd72
@ -376,112 +376,6 @@ bool jsHaveAsyncify() { return false; }
|
||||
bool jsHaveJspi() { return false; }
|
||||
|
||||
#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/"_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<emscripten::val>(
|
||||
"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<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
|
||||
|
||||
ArrayBuffer::ArrayBuffer(uint32_t size)
|
||||
@ -948,13 +842,6 @@ bool canBlockCallingThread()
|
||||
return haveAsyncify() || !emscripten_is_main_runtime_thread();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
BlobIODevice::BlobIODevice(Blob blob)
|
||||
: m_blob(blob)
|
||||
{
|
||||
|
@ -290,11 +290,6 @@ namespace qstdweb {
|
||||
{
|
||||
};
|
||||
|
||||
Q_CORE_EXPORT std::shared_ptr<CancellationFlag>
|
||||
readDataTransfer(emscripten::val webObject, std::function<QVariant(QByteArray)> imageReader,
|
||||
std::function<void(std::unique_ptr<QMimeData>)> onDone);
|
||||
|
||||
|
||||
#if QT_CONFIG(thread)
|
||||
template<class T>
|
||||
T proxyCall(std::function<T()> task, emscripten::ProxyingQueue *queue)
|
||||
|
@ -71,24 +71,15 @@ static void qClipboardPasteTo(val event)
|
||||
event.call<void>("preventDefault"); // prevent browser from handling drop event
|
||||
|
||||
static std::shared_ptr<qstdweb::CancellationFlag> readDataCancellation = nullptr;
|
||||
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;
|
||||
|
||||
// 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<val>("execCommand", val("copy"));
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include <qpa/qplatformclipboard.h>
|
||||
#include <private/qstdweb_p.h>
|
||||
#include <QMimeData>
|
||||
|
||||
#include <emscripten/bind.h>
|
||||
|
@ -8,12 +8,152 @@
|
||||
#include <QtCore/qrect.h>
|
||||
#include <QtGui/qimage.h>
|
||||
#include <private/qstdweb_p.h>
|
||||
#include <QtCore/qurl.h>
|
||||
|
||||
#include <utility>
|
||||
#include <emscripten/wire.h>
|
||||
|
||||
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<void>("setDragImage", element, emscripten::val(hotspot.x()),
|
||||
emscripten::val(hotspot.y()));
|
||||
}
|
||||
|
||||
void DataTransfer::setData(std::string format, std::string data)
|
||||
{
|
||||
webDataTransfer.call<void>("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<QUrl> uriList;
|
||||
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: {
|
||||
m_webFile = item.call<emscripten::val>("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<emscripten::val>(
|
||||
"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<QUrl> uriList;
|
||||
for (int i = 0; i < webDataTransfer["items"]["length"].as<int>(); ++i) {
|
||||
const auto item = webDataTransfer["items"][i];
|
||||
if (item["kind"].as<std::string>() == "file") {
|
||||
uriList.append(QUrl("blob://placeholder"));
|
||||
} else {
|
||||
const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
|
||||
data->setData(itemMimeType, QByteArray());
|
||||
}
|
||||
}
|
||||
data->setUrls(uriList);
|
||||
return data;
|
||||
}
|
||||
|
||||
void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
|
||||
{
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <QtCore/qtconfigmacros.h>
|
||||
#include <QtCore/QPointF>
|
||||
#include <private/qstdweb_p.h>
|
||||
#include <QtCore/qnamespace.h>
|
||||
|
||||
#include <emscripten/val.h>
|
||||
|
||||
@ -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");
|
||||
|
@ -5,6 +5,7 @@
|
||||
#define QWASMEVENT_H
|
||||
|
||||
#include "qwasmplatform.h"
|
||||
#include "qwasmdom.h"
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qnamespace.h>
|
||||
@ -241,7 +242,7 @@ struct DragEvent : public MouseEvent
|
||||
void acceptDrop();
|
||||
|
||||
Qt::DropAction dropAction;
|
||||
emscripten::val dataTransfer;
|
||||
dom::DataTransfer dataTransfer;
|
||||
QWindow *targetWindow;
|
||||
};
|
||||
|
||||
|
@ -114,7 +114,6 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
|
||||
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", pointerCallback);
|
||||
m_pointerLeaveCallback =
|
||||
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", pointerCallback);
|
||||
|
||||
m_dropCallback = std::make_unique<qstdweb::EventCallback>(
|
||||
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<QMimeData> 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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user