Skip to content

Commit

Permalink
wasm: add support for drag into browser window
Browse files Browse the repository at this point in the history
Drag and drop into the browser will work.
Drag and drop out of the browser will not.

Fixes: QTBUG-102242
Change-Id: Id9981ab6f9514535e1409bec18068790833a67a6
Reviewed-by: Morten Johan Sørvig <[email protected]>
  • Loading branch information
lpotter authored and Morten Johan Sørvig committed May 10, 2022
1 parent 877c158 commit d490501
Show file tree
Hide file tree
Showing 10 changed files with 432 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/plugins/platforms/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
qwasmtheme.cpp qwasmtheme.h
qwasmwindow.cpp qwasmwindow.h
qwasminputcontext.cpp qwasminputcontext.h
qwasmdrag.cpp qwasmdrag.h
DEFINES
QT_EGL_NO_X11
QT_NO_FOREACH
Expand Down
13 changes: 12 additions & 1 deletion src/plugins/platforms/wasm/qwasmcompositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ void QWasmCompositor::deregisterEventHandlers()
emscripten_set_touchend_callback(canvasSelector.constData(), 0, 0, NULL);
emscripten_set_touchmove_callback(canvasSelector.constData(), 0, 0, NULL);
emscripten_set_touchcancel_callback(canvasSelector.constData(), 0, 0, NULL);

val canvas = screen()->canvas();
canvas.call<void>("removeEventListener",
std::string("drop"),
val::module_property("qtDrop"), val(true));
}

void QWasmCompositor::destroy()
Expand Down Expand Up @@ -193,6 +198,13 @@ void QWasmCompositor::initEventHandlers()
emscripten_set_touchend_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);

val canvas = screen()->canvas();
canvas.call<void>("addEventListener",
std::string("drop"),
val::module_property("qtDrop"), val(true));
canvas.set("data-qtdropcontext", // ? unique
emscripten::val(quintptr(reinterpret_cast<void *>(screen()))));
}

void QWasmCompositor::setEnabled(bool enabled)
Expand Down Expand Up @@ -1145,7 +1157,6 @@ bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mo

if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN && !accepted)
QGuiApplicationPrivate::instance()->closeAllPopups();

return accepted;
}

Expand Down
236 changes: 236 additions & 0 deletions src/plugins/platforms/wasm/qwasmdrag.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwasmdrag.h"

#include <iostream>
#include <QMimeDatabase>

#include <emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/val.h>
#include <emscripten/bind.h>
#include <private/qstdweb_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <private/qsimpledrag_p.h>
#include "qwasmcompositor.h"
#include "qwasmeventtranslator.h"
#include <QtCore/QEventLoop>
#include <QMimeData>
#include <private/qshapedpixmapdndwindow_p.h>

QT_BEGIN_NAMESPACE

using namespace emscripten;

static void getTextPlainCallback(val m_string)
{
QWasmDrag *thisDrag = static_cast<QWasmDrag*>(QWasmIntegration::get()->drag());
thisDrag->m_mimeData->setText(QString::fromStdString(m_string.as<std::string>()));
thisDrag->qWasmDrop();
}

static void getTextUrlCallback(val m_string)
{
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
// event is dataTransfer object
// if drop event from outside browser, we do not get any mouse release, maybe mouse move
// 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;
int button = event["button"].as<int>();
wasmDrag->m_qButton = QWasmEventTranslator::translateMouseButton(button);

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

std::string dEffect = event["dataTransfer"]["dropEffect"].as<std::string>();

wasmDrag->m_dropActions = Qt::IgnoreAction;
if (dEffect == "copy")
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"];

val typesCount = event["dataTransfer"]["types"]["length"];

// 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(), [=]() {
if (!fileContent.isEmpty()) {

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) {
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
69 changes: 69 additions & 0 deletions src/plugins/platforms/wasm/qwasmdrag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef QWASMDRAG_H
#define QWASMDRAG_H

#include <qpa/qplatformdrag.h>
#include <private/qsimpledrag_p.h>
#include <private/qstdweb_p.h>
#include <QDrag>
#include "qwasmscreen.h"

QT_REQUIRE_CONFIG(draganddrop);

QT_BEGIN_NAMESPACE

class QWasmDrag : public QSimpleDrag
{
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

#endif // QWASMDRAG_H
11 changes: 10 additions & 1 deletion src/plugins/platforms/wasm/qwasmintegration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

// this is where EGL headers are pulled in, make sure it is last
#include "qwasmscreen.h"

#include <private/qsimpledrag_p.h>
using namespace emscripten;
QT_BEGIN_NAMESPACE

Expand Down Expand Up @@ -176,6 +176,7 @@ QWasmIntegration::QWasmIntegration()
visualViewport.call<void>("addEventListener", val("resize"),
val::module_property("qtResizeAllScreens"));
}
m_drag = new QWasmDrag();
}

QWasmIntegration::~QWasmIntegration()
Expand All @@ -192,6 +193,7 @@ QWasmIntegration::~QWasmIntegration()
delete m_desktopServices;
if (m_platformInputContext)
delete m_platformInputContext;
delete m_drag;

for (const auto &elementAndScreen : m_screens)
QWindowSystemInterface::handleScreenRemoved(elementAndScreen.second);
Expand Down Expand Up @@ -374,4 +376,11 @@ quint64 QWasmIntegration::getTimestamp()
return emscripten_performance_now();
}

#if QT_CONFIG(draganddrop)
QPlatformDrag *QWasmIntegration::drag() const
{
return m_drag;
}
#endif // QT_CONFIG(draganddrop)

QT_END_NAMESPACE
Loading

0 comments on commit d490501

Please sign in to comment.