12#include <qpa/qwindowsysteminterface.h>
14#include <QtCore/private/qstdweb_p.h>
15#include <QtCore/qeventloop.h>
16#include <QtCore/qmimedata.h>
17#include <QtCore/qtimer.h>
18#include <QtGui/QPainter>
19#include <QtGui/QImage>
20#include <QtGui/QPixmap>
23#include <private/qshapedpixmapdndwindow_p.h>
24#include <private/qdnd_p.h>
36 QObject *source = drag->source();
37 QWindow *window =
nullptr;
38 while (source && !window) {
39 window = qobject_cast<QWindow *>(source);
40 if (!window && source->metaObject()->indexOfMethod(
"_q_closestWindowHandle()") != -1)
41 QMetaObject::invokeMethod(source,
"_q_closestWindowHandle",
42 Q_RETURN_ARG(QWindow *, window));
44 source = source->parent();
56 DragImage(
const QPixmap &pixmap,
const QMimeData *mimeData, QWindow *window);
63 void generateDragImage(
const QPixmap &pixmap,
const QMimeData *mimeData);
64 void generateDragImageFromText(
const QMimeData *mimeData);
65 void generateDefaultDragImage();
66 void generateDragImageFromPixmap(
const QPixmap &pixmap);
73 DragState(QDrag *drag, QWindow *window,
std::function<
void()> quitEventLoopClosure);
98 Q_ASSERT_X(!m_dragState, Q_FUNC_INFO,
"Drag already in progress");
100 QWindow *window = windowForDrag(drag);
102 Qt::DropAction dragResult = Qt::IgnoreAction;
103 if (!qstdweb::haveAsyncify())
106 auto dragState =
std::make_shared<
DragState>(drag, window, [
this]() { QSimpleDrag::cancelDrag(); });
108 if (!m_isInEnterDrag)
109 m_dragState = dragState;
112 drag->setPixmap(QPixmap());
113 else if (drag->pixmap().size() == QSize(0, 0))
114 drag->setPixmap(dragState->dragImage->pixmap());
116 dragResult = QSimpleDrag::drag(drag);
125 "The event is not a DragStart event");
128 if (!m_dragState || m_dragState->window != event->targetWindow) {
132 setExecutedDropAction(event->dropAction);
135 if (shapedPixmapWindow())
136 shapedPixmapWindow()->setVisible(
false);
138 event->dataTransfer.setDragImage(m_dragState->dragImage->htmlElement(),
139 m_dragState->drag->hotSpot());
140 event->dataTransfer.setDataFromMimeData(*m_dragState->drag->mimeData());
145 event->webEvent.call<
void>(
"preventDefault");
147 auto mimeDataPreview = event->dataTransfer.toMimeDataPreview();
149 const Qt::DropActions actions = m_dragState
150 ? m_dragState->drag->supportedActions()
151 : (Qt::DropAction::CopyAction | Qt::DropAction::MoveAction
152 | Qt::DropAction::LinkAction);
154 const auto dragResponse = QWindowSystemInterface::handleDrag(
155 event->targetWindow, &*mimeDataPreview, event->pointInPage.toPoint(), actions,
156 event->mouseButton, event->modifiers);
158 if (dragResponse.isAccepted()) {
159 setExecutedDropAction(dragResponse.acceptedAction());
160 event->dataTransfer.setDropAction(dragResponse.acceptedAction());
162 setExecutedDropAction(Qt::DropAction::IgnoreAction);
163 event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction);
169 event->webEvent.call<
void>(
"preventDefault");
173 const auto screenElementPos = dom::mapPoint(
175 const auto screenPos =
177 const QPoint targetWindowPos = event->targetWindow->mapFromGlobal(screenPos).toPoint();
179 const Qt::DropActions actions = m_dragState
180 ? m_dragState->drag->supportedActions()
181 : (Qt::DropAction::CopyAction | Qt::DropAction::MoveAction
182 | Qt::DropAction::LinkAction);
183 Qt::MouseButton mouseButton = event->mouseButton;
184 QFlags<Qt::KeyboardModifier> modifiers = event->modifiers;
190 setExecutedDropAction(event->dropAction);
193 const auto dropCallback = [
this, dragState, wasmWindow, targetWindowPos,
194 actions, mouseButton, modifiers](QMimeData *mimeData) {
197 const QPlatformDropQtResponse dropResponse =
198 QWindowSystemInterface::handleDrop(wasmWindow->window(), mimeData,
199 targetWindowPos, actions,
200 mouseButton, modifiers);
202 if (dragState && dropResponse.isAccepted())
203 dragState->dropAction = dropResponse.acceptedAction();
209 QSimpleDrag::cancelDrag();
212 event->dataTransfer.toMimeDataWithFile(dropCallback);
218 QPointer<QWindow> sourceWindow = m_sourceWindow;
220 event->webEvent.call<
void>(
"preventDefault");
223 m_dragState->dropAction = event->dropAction;
225 setExecutedDropAction(event->dropAction);
228 m_dragState->quitEventLoopClosure();
234 const auto pointInScreen = platformScreen->mapFromLocal(
235 dom::mapPoint(mouseEvent.target(), platformScreen
->element(), mouseEvent.localPoint));
236 const auto geometryF = platformScreen
->geometry().toRectF();
237 QPointF targetPointClippedToScreen(
238 qBound(geometryF.left(), pointInScreen.x(), geometryF.right()),
239 qBound(geometryF.top(), pointInScreen.y(), geometryF.bottom()));
241 QTimer::singleShot(0, [sourceWindow, targetPointClippedToScreen, mouseEvent]() {
243 const QEvent::Type eventType = QEvent::MouseButtonRelease;
244 QWindowSystemInterface::handleMouseEvent(
245 sourceWindow, QWasmIntegration::getTimestamp(),
246 sourceWindow->mapFromGlobal(targetPointClippedToScreen),
247 targetPointClippedToScreen, mouseEvent.mouseButtons, mouseEvent.mouseButton,
248 eventType, mouseEvent.modifiers);
255 event->webEvent.call<
void>(
"preventDefault");
258 if (QDragManager::self() && QDragManager::self()->object())
263 m_dragState->dropAction = event->dropAction;
265 setExecutedDropAction(event->dropAction);
267 m_isInEnterDrag =
true;
268 QDrag *drag =
new QDrag(
this);
269 drag->setMimeData(event->dataTransfer.toMimeDataPreview());
270 drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
271 m_isInEnterDrag =
false;
276 event->webEvent.call<
void>(
"preventDefault");
278 m_dragState->dropAction = event->dropAction;
279 setExecutedDropAction(event->dropAction);
281 event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction);
293 m_temporaryImageElementParent = QWasmWindow::fromWindow(window)->containerElement();
294 generateDragImage(pixmap, mimeData);
296 m_imageDomElement.set(
"className",
"hidden-drag-image");
299 if (m_temporaryImageElementParent.isUndefined())
301 else if (m_temporaryImageElementParent[
"childElementCount"].as<
int>() == 0)
302 m_temporaryImageElementParent.call<
void>(
"appendChild", m_imageDomElement);
304 m_temporaryImageElementParent.call<
void>(
"insertBefore", m_imageDomElement, m_temporaryImageElementParent[
"children"][0]);
309 if (!m_temporaryImageElementParent.isUndefined())
310 m_temporaryImageElementParent.call<
void>(
"removeChild", m_imageDomElement);
314 const QPixmap &pixmap,
315 const QMimeData *mimeData)
317 if (!pixmap.isNull())
318 generateDragImageFromPixmap(pixmap);
319 else if (mimeData && mimeData->hasFormat(
"text/plain"))
320 generateDragImageFromText(mimeData);
322 generateDefaultDragImage();
325void QWasmDrag::
DragState::
DragImage::generateDragImageFromText(
const QMimeData *mimeData)
328 emscripten::val::global(
"document")
329 .call<emscripten::val>(
"createElement", emscripten::val(
"span"));
331 constexpr qsizetype MaxCharactersInDragImage = 100;
333 const auto text = QString::fromUtf8(mimeData->data(
"text/plain"));
334 dragImageElement.set(
336 text.first(qMin(qsizetype(MaxCharactersInDragImage), text.length())).toStdString());
340 QPixmap image(QSize(200,200));
341 if (!image.isNull()) {
342 QPainter painter(&image);
343 bounds = painter.boundingRect(0, 0, 200, 200, 0, text);
346 QImage image(bounds.size(), QImage::Format_RGBA8888);
347 if (!image.isNull()) {
348 QPainter painter(&image);
349 painter.fillRect(bounds, QColor(255,255,255, 255));
350 painter.setPen(Qt::black);
351 painter.drawText(bounds, text);
353 m_pixmap = QPixmap::fromImage(image);
355 m_imageDomElement = dragImageElement;
361 emscripten::val::global(
"document")
362 .call<emscripten::val>(
"createElement", emscripten::val(
"div"));
364 auto innerImgElement = emscripten::val::global(
"document")
365 .call<emscripten::val>(
"createElement", emscripten::val(
"img"));
366 innerImgElement.set(
"src",
367 "data:image/" + std::string(
"svg+xml") +
";base64,"
368 +
std::string(Base64IconStore::get()->getIcon(
369 Base64IconStore::IconType::QtLogo)));
371 constexpr char DragImageSize[] =
"50px";
373 dragImageElement[
"style"].set(
"width", DragImageSize);
374 innerImgElement[
"style"].set(
"width", DragImageSize);
375 dragImageElement[
"style"].set(
"display",
"flex");
377 dragImageElement.call<
void>(
"appendChild", innerImgElement);
379 QByteArray pixmap_ba =
380 QByteArray::fromBase64(
382 Base64IconStore::get()->getIcon(
383 Base64IconStore::IconType::QtLogo)).toLocal8Bit());
385 if (!m_pixmap.loadFromData(pixmap_ba))
386 qWarning() <<
" Load of Qt logo failed";
388 m_pixmap = m_pixmap.scaled(50, 50);
389 m_imageDomElement = dragImageElement;
392void QWasmDrag::
DragState::
DragImage::generateDragImageFromPixmap(
const QPixmap &pixmap)
395 emscripten::val::global(
"document")
396 .call<emscripten::val>(
"createElement", emscripten::val(
"canvas"));
397 dragImageElement.set(
"width", pixmap.width());
398 dragImageElement.set(
"height", pixmap.height());
400 dragImageElement[
"style"].set(
401 "width",
std::to_string(pixmap.width() / pixmap.devicePixelRatio()) +
"px");
402 dragImageElement[
"style"].set(
403 "height",
std::to_string(pixmap.height() / pixmap.devicePixelRatio()) +
"px");
405 auto context2d = dragImageElement.call<
emscripten::val>(
"getContext", emscripten::val(
"2d"));
406 auto imageData = context2d.call<
emscripten::val>(
409 dom::drawImageToWebImageDataArray(pixmap.toImage().convertedTo(QImage::Format::Format_RGBA8888),
410 imageData, QRect(0, 0, pixmap.width(), pixmap.height()));
411 context2d.call<
void>(
"putImageData", imageData, emscripten::val(0), emscripten::val(0));
414 m_imageDomElement = dragImageElement;
418 std::function<
void()> quitEventLoopClosure)
422 std::make_unique<DragState::DragImage>(
\inmodule QtCore\reentrant
DragImage(const QPixmap &pixmap, const QMimeData *mimeData, QWindow *window)
emscripten::val htmlElement()
Qt::DropAction drag(QDrag *drag) final
void onNativeDrop(DragEvent *event)
void onNativeDragEnter(DragEvent *event)
void onNativeDragLeave(DragEvent *event)
void onNativeDragOver(DragEvent *event)
static QWasmDrag * instance()
void onNativeDragStarted(DragEvent *event)
void onNativeDragFinished(DragEvent *event, QWasmScreen *platformScreen)
static QWasmIntegration * get()
emscripten::val element() const
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
static QWasmWindow * fromWindow(const QWindow *window)
friend class QWasmCompositor
QWasmScreen * platformScreen() const
Combined button and popup list for selecting options.
QWindow * windowForDrag(QDrag *drag)
DragState(QWasmDrag &&other)=delete
std::function< void()> quitEventLoopClosure
DragState(QDrag *drag, QWindow *window, std::function< void()> quitEventLoopClosure)
std::unique_ptr< DragImage > dragImage
Qt::DropAction dropAction
DragState(const QWasmDrag &other)=delete
DragState & operator=(QWasmDrag &&other)=delete
DragState & operator=(const QWasmDrag &other)=delete