wasm: write file to storage on drop

Change-Id: Ibd1b5d623da07ad611cce577929a23ba991b6738
Pick-to: 6.7
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Lorn Potter 2023-10-19 16:33:25 +10:00
parent 8cc84a1386
commit 1f7d222cec
5 changed files with 99 additions and 34 deletions

View File

@ -70,16 +70,7 @@ static void qClipboardPasteTo(val event)
{ {
event.call<void>("preventDefault"); // prevent browser from handling drop event event.call<void>("preventDefault"); // prevent browser from handling drop event
static std::shared_ptr<qstdweb::CancellationFlag> readDataCancellation = nullptr; QWasmIntegration::get()->getWasmClipboard()->sendClipboardData(event);
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) { EMSCRIPTEN_BINDINGS(qtClipboardModule) {
@ -292,4 +283,22 @@ void QWasmClipboard::writeToClipboard()
document.call<val>("execCommand", val("copy")); document.call<val>("execCommand", val("copy"));
} }
void QWasmClipboard::sendClipboardData(emscripten::val event)
{
dom::DataTransfer transfer(event["clipboardData"]);
QMimeData *mData;
const auto pointerCallback = std::function([&](QMimeData &data) {
mData = &data;
// Persist clipboard data so that the app can read it when handling the CTRL+V
QWasmIntegration::get()->clipboard()->QPlatformClipboard::setMimeData(mData,
QClipboard::Clipboard);
QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_V,
Qt::ControlModifier, "V");
QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_V,
Qt::ControlModifier, "V");
});
transfer.toMimeDataWithFile(pointerCallback); // mimedata
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -38,6 +38,7 @@ public:
ProcessKeyboardResult processKeyboard(const KeyEvent &event); ProcessKeyboardResult processKeyboard(const KeyEvent &event);
static void installEventHandlers(const emscripten::val &target); static void installEventHandlers(const emscripten::val &target);
bool hasClipboardApi(); bool hasClipboardApi();
void sendClipboardData(emscripten::val event);
private: private:
void initClipboardPermissions(); void initClipboardPermissions();

View File

@ -3,7 +3,6 @@
#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 <QtGui/qimage.h>
@ -15,6 +14,9 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// this needs to live outside the life of DataTransfer
Q_GLOBAL_STATIC(QMimeData, resultMimeData);
namespace dom { namespace dom {
namespace { namespace {
std::string dropActionToDropEffect(Qt::DropAction action) std::string dropActionToDropEffect(Qt::DropAction action)
@ -36,7 +38,9 @@ std::string dropActionToDropEffect(Qt::DropAction action)
} }
} // namespace } // namespace
DataTransfer::DataTransfer(emscripten::val webDataTransfer) : webDataTransfer(webDataTransfer) { } DataTransfer::DataTransfer(emscripten::val webDataTransfer)
: webDataTransfer(webDataTransfer) {
}
DataTransfer::~DataTransfer() = default; DataTransfer::~DataTransfer() = default;
@ -78,10 +82,9 @@ void DataTransfer::setDataFromMimeData(const QMimeData &mimeData)
} }
} }
QMimeData *DataTransfer::toMimeDataWithFile() QMimeData *DataTransfer::toMimeDataWithFile(std::function<void(QMimeData &)> callback)
{ {
QMimeData *resultMimeData = new QMimeData(); // QScopedPointer? resultMimeData->clear();
enum class ItemKind { enum class ItemKind {
File, File,
String, String,
@ -99,8 +102,43 @@ QMimeData *DataTransfer::toMimeDataWithFile()
case ItemKind::File: { case ItemKind::File: {
m_webFile = item.call<emscripten::val>("getAsFile"); m_webFile = item.call<emscripten::val>("getAsFile");
qstdweb::File webfile(m_webFile); qstdweb::File webfile(m_webFile);
QUrl fileUrl(QStringLiteral("file:///") + QString::fromStdString(webfile.name())); if (webfile.size() == 0
uriList.append(fileUrl); || webfile.size() > 1e+9) { // limit file size to 1 GB
qWarning() << "Something happened, File size is" << webfile.size();
continue;
}
QByteArray fileContent(webfile.size(), Qt::Uninitialized);
QString mimeFormat = QString::fromStdString(webfile.type());
QString fileName = QString::fromStdString(webfile.name());
// there's a file, now read it
webfile.stream(fileContent.data(), [=]() {
QList<QUrl> fileUriList;
if (!fileContent.isEmpty()) {
if (mimeFormat.contains("image/")) {
QImage image;
image.loadFromData(fileContent, nullptr);
resultMimeData->setImageData(image);
} else {
QUrl fileUrl(QStringLiteral("file:///") +
QString::fromStdString(webfile.name()));
fileUriList.append(fileUrl);
QFile file(fileName);
if (!file.open(QFile::WriteOnly)) {
qWarning() << "File was not opened";
return;
}
if (file.write(fileContent) < 0)
qWarning() << "Write failed";
file.close();
resultMimeData->setUrls(fileUriList);
}
callback(*resultMimeData);
}
});
break; break;
} }
case ItemKind::String: case ItemKind::String:
@ -131,9 +169,15 @@ QMimeData *DataTransfer::toMimeDataWithFile()
} }
break; break;
} }
} } // end items
if (!uriList.isEmpty())
if (!uriList.isEmpty()) {
resultMimeData->setUrls(uriList); resultMimeData->setUrls(uriList);
}
if (resultMimeData->formats().length() > 0)
callback(*resultMimeData);
return resultMimeData; return resultMimeData;
} }

View File

@ -15,13 +15,14 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <QMimeData>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace qstdweb { namespace qstdweb {
struct CancellationFlag; struct CancellationFlag;
} }
class QMimeData;
class QPoint; class QPoint;
class QRect; class QRect;
@ -35,7 +36,7 @@ struct DataTransfer
DataTransfer &operator=(const DataTransfer &other); DataTransfer &operator=(const DataTransfer &other);
DataTransfer &operator=(DataTransfer &&other); DataTransfer &operator=(DataTransfer &&other);
QMimeData *toMimeDataWithFile() ; QMimeData *toMimeDataWithFile(std::function<void(QMimeData &)> callback);
QMimeData *toMimeDataPreview(); QMimeData *toMimeDataPreview();
void setDragImage(emscripten::val element, const QPoint &hotspot); void setDragImage(emscripten::val element, const QPoint &hotspot);
void setData(std::string format, std::string data); void setData(std::string format, std::string data);

View File

@ -14,6 +14,7 @@
#include <QtCore/qeventloop.h> #include <QtCore/qeventloop.h>
#include <QtCore/qmimedata.h> #include <QtCore/qmimedata.h>
#include <QtCore/qtimer.h> #include <QtCore/qtimer.h>
#include <QFile>
#include <functional> #include <functional>
#include <string> #include <string>
@ -155,19 +156,28 @@ void QWasmDrag::onNativeDrop(DragEvent *event)
? m_dragState->drag->supportedActions() ? m_dragState->drag->supportedActions()
: (Qt::DropAction::CopyAction | Qt::DropAction::MoveAction : (Qt::DropAction::CopyAction | Qt::DropAction::MoveAction
| Qt::DropAction::LinkAction); | Qt::DropAction::LinkAction);
Qt::MouseButton mouseButton = event->mouseButton;
QFlags<Qt::KeyboardModifier> modifiers = event->modifiers;
const auto pointerCallback = std::function([&, wasmWindow, pointInTargetWindowCoords,
actions, mouseButton, modifiers](QMimeData &data) {
if (data.formats().count() == 0)
return;
auto dropResponse = std::make_shared<QPlatformDropQtResponse>(true, Qt::DropAction::CopyAction); auto dropResponse = std::make_shared<QPlatformDropQtResponse>(true, Qt::DropAction::CopyAction);
QMimeData *data = event->dataTransfer.toMimeDataWithFile();
*dropResponse = QWindowSystemInterface::handleDrop(event->targetWindow, data, *dropResponse = QWindowSystemInterface::handleDrop(wasmWindow->window(), &data,
pointInTargetWindowCoords.toPoint(), actions, pointInTargetWindowCoords.toPoint(), actions,
event->mouseButton, event->modifiers); mouseButton, modifiers);
if (dropResponse->isAccepted()) { if (dropResponse->isAccepted()) {
event->acceptDrop(); // event->acceptDrop(); // boom
event->dataTransfer.setDropAction(dropResponse->acceptedAction()); // event->dataTransfer.setDropAction(dropResponse->acceptedAction());
m_dragState->dropAction = dropResponse->acceptedAction(); m_dragState->dropAction = dropResponse->acceptedAction();
} }
});
event->dataTransfer.toMimeDataWithFile(pointerCallback);
} }
void QWasmDrag::onNativeDragFinished(DragEvent *event) void QWasmDrag::onNativeDragFinished(DragEvent *event)