11#include <qpa/qwindowsysteminterface.h>
13#include <QtCore/private/qstdweb_p.h>
14#include <QtCore/qeventloop.h>
15#include <QtCore/qmimedata.h>
16#include <QtCore/qtimer.h>
17#include <QtGui/QPainter>
18#include <QtGui/QImage>
19#include <QtGui/QPixmap>
22#include <private/qshapedpixmapdndwindow_p.h>
23#include <private/qdnd_p.h>
35 QObject *source = drag->source();
36 QWindow *window =
nullptr;
37 while (source && !window) {
38 window = qobject_cast<QWindow *>(source);
39 if (!window && source->metaObject()->indexOfMethod(
"_q_closestWindowHandle()") != -1)
40 QMetaObject::invokeMethod(source,
"_q_closestWindowHandle",
41 Q_RETURN_ARG(QWindow *, window));
43 source = source->parent();
55 DragImage(
const QPixmap &pixmap,
const QMimeData *mimeData, QWindow *window);
62 void generateDragImage(
const QPixmap &pixmap,
const QMimeData *mimeData);
63 void generateDragImageFromText(
const QMimeData *mimeData);
64 void generateDefaultDragImage();
65 void generateDragImageFromPixmap(
const QPixmap &pixmap);
72 DragState(QDrag *drag, QWindow *window,
std::function<
void()> quitEventLoopClosure);
97 Q_ASSERT_X(!m_dragState, Q_FUNC_INFO,
"Drag already in progress");
99 QWindow *window = windowForDrag(drag);
101 Qt::DropAction dragResult = Qt::IgnoreAction;
102 if (!qstdweb::haveAsyncify())
105 auto dragState =
std::make_shared<
DragState>(drag, window, [
this]() { QSimpleDrag::cancelDrag(); });
107 if (!m_isInEnterDrag)
108 m_dragState = dragState;
111 drag->setPixmap(QPixmap());
112 else if (drag->pixmap().size() == QSize(0, 0))
113 drag->setPixmap(dragState->dragImage->pixmap());
115 dragResult = QSimpleDrag::drag(drag);
124 "The event is not a DragStart event");
127 if (!m_dragState || m_dragState->window != event->targetWindow) {
131 setExecutedDropAction(event->dropAction);
134 if (shapedPixmapWindow())
135 shapedPixmapWindow()->setVisible(
false);
137 event->dataTransfer.setDragImage(m_dragState->dragImage->htmlElement(),
138 m_dragState->drag->hotSpot());
139 event->dataTransfer.setDataFromMimeData(*m_dragState->drag->mimeData());
144 event->webEvent.call<
void>(
"preventDefault");
146 auto mimeDataPreview = event->dataTransfer.toMimeDataPreview();
148 const Qt::DropActions actions = m_dragState
149 ? m_dragState->drag->supportedActions()
150 : (Qt::DropAction::CopyAction | Qt::DropAction::MoveAction
151 | Qt::DropAction::LinkAction);
153 const auto dragResponse = QWindowSystemInterface::handleDrag(
154 event->targetWindow, &*mimeDataPreview, event->pointInPage.toPoint(), actions,
155 event->mouseButton, event->modifiers);
157 if (dragResponse.isAccepted()) {
158 setExecutedDropAction(dragResponse.acceptedAction());
159 event->dataTransfer.setDropAction(dragResponse.acceptedAction());
161 setExecutedDropAction(Qt::DropAction::IgnoreAction);
162 event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction);
168 event->webEvent.call<
void>(
"preventDefault");
172 const auto screenElementPos = dom::mapPoint(
174 const auto screenPos =
176 const QPoint targetWindowPos = event->targetWindow->mapFromGlobal(screenPos).toPoint();
178 const Qt::DropActions actions = m_dragState
179 ? m_dragState->drag->supportedActions()
180 : (Qt::DropAction::CopyAction | Qt::DropAction::MoveAction
181 | Qt::DropAction::LinkAction);
182 Qt::MouseButton mouseButton = event->mouseButton;
183 QFlags<Qt::KeyboardModifier> modifiers = event->modifiers;
189 setExecutedDropAction(event->dropAction);
192 const auto dropCallback = [
this, dragState, wasmWindow, targetWindowPos,
193 actions, mouseButton, modifiers](QMimeData *mimeData) {
196 const QPlatformDropQtResponse dropResponse =
197 QWindowSystemInterface::handleDrop(wasmWindow->window(), mimeData,
198 targetWindowPos, actions,
199 mouseButton, modifiers);
201 if (dragState && dropResponse.isAccepted())
202 dragState->dropAction = dropResponse.acceptedAction();
208 QSimpleDrag::cancelDrag();
211 event->dataTransfer.toMimeDataWithFile(dropCallback);
217 QPointer<QWindow> sourceWindow = m_sourceWindow;
219 event->webEvent.call<
void>(
"preventDefault");
222 m_dragState->dropAction = event->dropAction;
224 setExecutedDropAction(event->dropAction);
227 m_dragState->quitEventLoopClosure();
233 const auto pointInScreen = platformScreen->mapFromLocal(
234 dom::mapPoint(mouseEvent.target(), platformScreen
->element(), mouseEvent.localPoint));
235 const auto geometryF = platformScreen
->geometry().toRectF();
236 QPointF targetPointClippedToScreen(
237 qBound(geometryF.left(), pointInScreen.x(), geometryF.right()),
238 qBound(geometryF.top(), pointInScreen.y(), geometryF.bottom()));
240 QTimer::singleShot(0, [sourceWindow, targetPointClippedToScreen, mouseEvent]() {
242 const QEvent::Type eventType = QEvent::MouseButtonRelease;
243 QWindowSystemInterface::handleMouseEvent(
244 sourceWindow, QWasmIntegration::getTimestamp(),
245 sourceWindow->mapFromGlobal(targetPointClippedToScreen),
246 targetPointClippedToScreen, mouseEvent.mouseButtons, mouseEvent.mouseButton,
247 eventType, mouseEvent.modifiers);
254 event->webEvent.call<
void>(
"preventDefault");
257 if (QDragManager::self() && QDragManager::self()->object())
262 m_dragState->dropAction = event->dropAction;
264 setExecutedDropAction(event->dropAction);
266 m_isInEnterDrag =
true;
267 QDrag *drag =
new QDrag(
this);
268 drag->setMimeData(event->dataTransfer.toMimeDataPreview());
269 drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
270 m_isInEnterDrag =
false;
275 event->webEvent.call<
void>(
"preventDefault");
277 m_dragState->dropAction = event->dropAction;
278 setExecutedDropAction(event->dropAction);
280 event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction);
292 m_temporaryImageElementParent = QWasmWindow::fromWindow(window)->containerElement();
293 generateDragImage(pixmap, mimeData);
295 m_imageDomElement.set(
"className",
"hidden-drag-image");
298 if (m_temporaryImageElementParent.isUndefined())
300 else if (m_temporaryImageElementParent[
"childElementCount"].as<
int>() == 0)
301 m_temporaryImageElementParent.call<
void>(
"appendChild", m_imageDomElement);
303 m_temporaryImageElementParent.call<
void>(
"insertBefore", m_imageDomElement, m_temporaryImageElementParent[
"children"][0]);
308 if (!m_temporaryImageElementParent.isUndefined())
309 m_temporaryImageElementParent.call<
void>(
"removeChild", m_imageDomElement);
313 const QPixmap &pixmap,
314 const QMimeData *mimeData)
316 if (!pixmap.isNull())
317 generateDragImageFromPixmap(pixmap);
318 else if (mimeData && mimeData->hasFormat(
"text/plain"))
319 generateDragImageFromText(mimeData);
321 generateDefaultDragImage();
324void QWasmDrag::
DragState::
DragImage::generateDragImageFromText(
const QMimeData *mimeData)
327 emscripten::val::global(
"document")
328 .call<emscripten::val>(
"createElement", emscripten::val(
"span"));
330 constexpr qsizetype MaxCharactersInDragImage = 100;
332 const auto text = QString::fromUtf8(mimeData->data(
"text/plain"));
333 dragImageElement.set(
335 text.first(qMin(qsizetype(MaxCharactersInDragImage), text.length())).toStdString());
339 QPixmap image(QSize(200,200));
340 if (!image.isNull()) {
341 QPainter painter(&image);
342 bounds = painter.boundingRect(0, 0, 200, 200, 0, text);
345 QImage image(bounds.size(), QImage::Format_RGBA8888);
346 if (!image.isNull()) {
347 QPainter painter(&image);
348 painter.fillRect(bounds, QColor(255,255,255, 255));
349 painter.setPen(Qt::black);
350 painter.drawText(bounds, text);
352 m_pixmap = QPixmap::fromImage(image);
354 m_imageDomElement = dragImageElement;
360 emscripten::val::global(
"document")
361 .call<emscripten::val>(
"createElement", emscripten::val(
"div"));
363 auto innerImgElement = emscripten::val::global(
"document")
364 .call<emscripten::val>(
"createElement", emscripten::val(
"img"));
365 innerImgElement.set(
"src",
366 "data:image/" + std::string(
"svg+xml") +
";base64,"
367 +
std::string(Base64IconStore::get()->getIcon(
368 Base64IconStore::IconType::QtLogo)));
370 constexpr char DragImageSize[] =
"50px";
372 dragImageElement[
"style"].set(
"width", DragImageSize);
373 innerImgElement[
"style"].set(
"width", DragImageSize);
374 dragImageElement[
"style"].set(
"display",
"flex");
376 dragImageElement.call<
void>(
"appendChild", innerImgElement);
378 QByteArray pixmap_ba =
379 QByteArray::fromBase64(
381 Base64IconStore::get()->getIcon(
382 Base64IconStore::IconType::QtLogo)).toLocal8Bit());
384 if (!m_pixmap.loadFromData(pixmap_ba))
385 qWarning() <<
" Load of Qt logo failed";
387 m_pixmap = m_pixmap.scaled(50, 50);
388 m_imageDomElement = dragImageElement;
391void QWasmDrag::
DragState::
DragImage::generateDragImageFromPixmap(
const QPixmap &pixmap)
394 emscripten::val::global(
"document")
395 .call<emscripten::val>(
"createElement", emscripten::val(
"canvas"));
396 dragImageElement.set(
"width", pixmap.width());
397 dragImageElement.set(
"height", pixmap.height());
399 dragImageElement[
"style"].set(
400 "width",
std::to_string(pixmap.width() / pixmap.devicePixelRatio()) +
"px");
401 dragImageElement[
"style"].set(
402 "height",
std::to_string(pixmap.height() / pixmap.devicePixelRatio()) +
"px");
404 auto context2d = dragImageElement.call<
emscripten::val>(
"getContext", emscripten::val(
"2d"));
405 auto imageData = context2d.call<
emscripten::val>(
408 dom::drawImageToWebImageDataArray(pixmap.toImage().convertedTo(QImage::Format::Format_RGBA8888),
409 imageData, QRect(0, 0, pixmap.width(), pixmap.height()));
410 context2d.call<
void>(
"putImageData", imageData, emscripten::val(0), emscripten::val(0));
413 m_imageDomElement = dragImageElement;
417 std::function<
void()> quitEventLoopClosure)
421 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