Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickdrag.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:interprocess-communication
4
5#include "qquickdrag_p.h"
7
8#include <private/qguiapplication_p.h>
9#include <qpa/qplatformintegration.h>
10#include <private/qquickitem_p.h>
11#include <QtQuick/private/qquickevents_p_p.h>
12#include <private/qquickitemchangelistener_p.h>
13#include <private/qquickpixmap_p.h>
14#include <private/qv4scopedvalue_p.h>
15#include <QtCore/qbuffer.h>
16#include <QtCore/qmimedata.h>
17#include <QtCore/qstringconverter.h>
18#include <QtQml/qqmlinfo.h>
19#include <QtGui/qevent.h>
20#include <QtGui/qstylehints.h>
21#include <QtGui/qguiapplication.h>
22#include <QtGui/qimagewriter.h>
23
24#include <qpa/qplatformdrag.h>
25#include <QtGui/qdrag.h>
26
28
29using namespace Qt::StringLiterals;
30
31
32/*!
33 \qmltype Drag
34 \nativetype QQuickDrag
35 \inqmlmodule QtQuick
36 \ingroup qtquick-input
37 \brief For specifying drag and drop events for moved Items.
38
39 Using the Drag attached property, any Item can be made a source of drag and drop
40 events within a scene.
41
42 When a drag is \l active on an item, any change in that item's position will
43 generate a drag event that will be sent to any DropArea that intersects
44 with the new position of the item. Other items which implement drag and
45 drop event handlers can also receive these events.
46
47 The following snippet shows how an item can be dragged with a MouseArea.
48 However, dragging is not limited to mouse drags; anything that can move an item
49 can generate drag events, including touch events, animations and bindings.
50
51 \snippet qml/drag.qml 0
52
53 A drag can be terminated either by canceling it with Drag.cancel() or setting
54 Drag.active to false, or it can be terminated with a drop event by calling
55 Drag.drop(). If the drop event is accepted, Drag.drop() will return the
56 \l {supportedActions}{drop action} chosen by the recipient of the event,
57 otherwise it will return Qt.IgnoreAction.
58
59 \sa {Qt Quick Examples - Drag and Drop}
60*/
61
62void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change,
63 const QRectF &)
64{
65 if (!change.positionChange() || !active || itemMoved)
66 return;
67 updatePosition();
68}
69
70void QQuickDragAttachedPrivate::itemParentChanged(QQuickItem *, QQuickItem *)
71{
72 if (!active || dragRestarted)
73 return;
74
75 QQuickWindow *newWindow = attachedItem->window();
76
77 if (window != newWindow)
78 restartDrag();
79 else if (window)
80 updatePosition();
81}
82
83void QQuickDragAttachedPrivate::updatePosition()
84{
85 Q_Q(QQuickDragAttached);
86 itemMoved = true;
87 if (!eventQueued) {
88 eventQueued = true;
89 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
90 }
91}
92
93void QQuickDragAttachedPrivate::restartDrag()
94{
95 Q_Q(QQuickDragAttached);
96 dragRestarted = true;
97 if (!eventQueued) {
98 eventQueued = true;
99 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
100 }
101}
102
103void QQuickDragAttachedPrivate::deliverEnterEvent()
104{
105 dragRestarted = false;
106 itemMoved = false;
107
108 window = attachedItem->window();
109
110 mimeData->m_source = source;
111 if (!overrideActions)
112 mimeData->m_supportedActions = supportedActions;
113 mimeData->m_keys = keys;
114
115 if (window) {
116 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
117 QDragEnterEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
118 QQuickDropEventEx::setProposedAction(&event, proposedAction);
119 deliverEvent(window, &event);
120 }
121}
122
123void QQuickDragAttachedPrivate::deliverMoveEvent()
124{
125 Q_Q(QQuickDragAttached);
126
127 itemMoved = false;
128 if (window) {
129 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
130 QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
131 QQuickDropEventEx::setProposedAction(&event, proposedAction);
132 deliverEvent(window, &event);
133 if (target != dragGrabber.target()) {
134 target = dragGrabber.target();
135 emit q->targetChanged();
136 }
137 }
138}
139
140void QQuickDragAttachedPrivate::deliverLeaveEvent()
141{
142 if (window) {
143 QDragLeaveEvent event;
144 deliverEvent(window, &event);
145 window = nullptr;
146 }
147}
148
149void QQuickDragAttachedPrivate::deliverEvent(QQuickWindow *window, QEvent *event)
150{
151 Q_ASSERT(!inEvent);
152 inEvent = true;
153 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->deliverDragEvent(&dragGrabber, event);
154 inEvent = false;
155}
156
157bool QQuickDragAttached::event(QEvent *event)
158{
159 Q_D(QQuickDragAttached);
160
161 if (event->type() == QEvent::User) {
162 d->eventQueued = false;
163 if (d->dragRestarted) {
164 d->deliverLeaveEvent();
165 if (!d->mimeData)
166 d->mimeData = new QQuickDragMimeData;
167 d->deliverEnterEvent();
168
169 if (d->target != d->dragGrabber.target()) {
170 d->target = d->dragGrabber.target();
171 emit targetChanged();
172 }
173 } else if (d->itemMoved) {
174 d->deliverMoveEvent();
175 }
176 return true;
177 } else {
178 return QObject::event(event);
179 }
180}
181
182QQuickDragAttached::QQuickDragAttached(QObject *parent)
183 : QObject(*new QQuickDragAttachedPrivate, parent)
184{
185 Q_D(QQuickDragAttached);
186 d->attachedItem = qobject_cast<QQuickItem *>(parent);
187 d->source = d->attachedItem;
188}
189
190QQuickDragAttached::~QQuickDragAttached()
191{
192 Q_D(QQuickDragAttached);
193 delete d->mimeData;
194}
195
196/*!
197 \qmlattachedproperty bool QtQuick::Drag::active
198
199 This property holds whether a drag event sequence is currently active.
200
201 Binding this property to the active property of \l MouseArea::drag will
202 cause \l startDrag to be called when the user starts dragging.
203
204 Setting this property to true will also send a QDragEnter event to the scene
205 with the item's current position. Setting it to false will send a
206 QDragLeave event.
207
208 While a drag is active any change in an item's position will send a QDragMove
209 event with item's new position to the scene.
210*/
211
212bool QQuickDragAttached::isActive() const
213{
214 Q_D(const QQuickDragAttached);
215 return d->active;
216}
217
218void QQuickDragAttached::setActive(bool active)
219{
220 Q_D(QQuickDragAttached);
221 if (d->active != active) {
222 if (d->inEvent)
223 qmlWarning(this) << "active cannot be changed from within a drag event handler";
224 else if (active) {
225 if (d->dragType == QQuickDrag::Internal) {
226 d->start(d->supportedActions);
227 } else {
228 d->active = true;
229 emit activeChanged();
230 if (d->dragType == QQuickDrag::Automatic) {
231 // There are different semantics than start() since startDrag()
232 // may be called after an internal drag is already started.
233 d->startDrag(d->supportedActions);
234 }
235 }
236 }
237 else
238 cancel();
239 }
240}
241
242/*!
243 \qmlattachedproperty Object QtQuick::Drag::source
244
245 This property holds an object that is identified to recipients of drag events as
246 the source of the events. By default this is the item that the Drag
247 property is attached to.
248
249 Changing the source while a drag is active will reset the sequence of drag events by
250 sending a drag leave event followed by a drag enter event with the new source.
251*/
252
253QObject *QQuickDragAttached::source() const
254{
255 Q_D(const QQuickDragAttached);
256 return d->source;
257}
258
259void QQuickDragAttached::setSource(QObject *item)
260{
261 Q_D(QQuickDragAttached);
262 if (d->source != item) {
263 d->source = item;
264 if (d->active)
265 d->restartDrag();
266 emit sourceChanged();
267 }
268}
269
270void QQuickDragAttached::resetSource()
271{
272 Q_D(QQuickDragAttached);
273 if (d->source != d->attachedItem) {
274 d->source = d->attachedItem;
275 if (d->active)
276 d->restartDrag();
277 emit sourceChanged();
278 }
279}
280
281/*!
282 \qmlattachedproperty Object QtQuick::Drag::target
283
284 While a drag is active this property holds the last object to accept an
285 enter event from the dragged item, if the current drag position doesn't
286 intersect any accepting targets it is null.
287
288 When a drag is not active this property holds the object that accepted
289 the drop event that ended the drag, if no object accepted the drop or
290 the drag was canceled the target will then be null.
291*/
292
293QObject *QQuickDragAttached::target() const
294{
295 Q_D(const QQuickDragAttached);
296 return d->target;
297}
298
299/*!
300 \qmlattachedproperty point QtQuick::Drag::hotSpot
301
302 This property holds the drag position relative to the top left of the item.
303
304 By default this is (0, 0).
305
306 Changes to hotSpot trigger a new drag move with the updated position.
307*/
308
309QPointF QQuickDragAttached::hotSpot() const
310{
311 Q_D(const QQuickDragAttached);
312 return d->hotSpot;
313}
314
315void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
316{
317 Q_D(QQuickDragAttached);
318 if (d->hotSpot != hotSpot) {
319 d->hotSpot = hotSpot;
320
321 if (d->active)
322 d->updatePosition();
323
324 emit hotSpotChanged();
325 }
326}
327
328/*!
329 \qmlattachedproperty url QtQuick::Drag::imageSource
330 \since 5.8
331
332 This property holds the URL of the image which will be used to represent
333 the data during the drag and drop operation. Changing this property after
334 the drag operation has started will have no effect.
335
336 The example below uses an item's contents as a drag image:
337
338 \snippet qml/externaldrag.qml 0
339
340 \sa Item::grabToImage()
341*/
342
343QUrl QQuickDragAttached::imageSource() const
344{
345 Q_D(const QQuickDragAttached);
346 return d->imageSource;
347}
348
349void QQuickDragAttached::setImageSource(const QUrl &url)
350{
351 Q_D(QQuickDragAttached);
352 if (d->imageSource != url) {
353 d->imageSource = url;
354
355 if (url.isEmpty()) {
356 d->pixmapLoader.clear();
357 } else {
358 d->loadPixmap();
359 }
360
361 Q_EMIT imageSourceChanged();
362 }
363}
364
365/*!
366 \qmlattachedproperty size QtQuick::Drag::imageSourceSize
367 \since 6.8
368
369 This property holds the size of the image that will be used to represent
370 the data during the drag and drop operation. Changing this property after
371 the drag operation has started will have no effect.
372
373 This property sets the maximum number of pixels stored for the loaded
374 image so that large images do not use more memory than necessary.
375 See \l {QtQuick::Image::sourceSize}{Image.sourceSize} for more details.
376
377 The example below shows an SVG image rendered at one size, and re-renders
378 it at a different size for the drag image:
379
380 \snippet qml/externalDragScaledImage.qml 0
381
382 \sa imageSource, Item::grabToImage()
383*/
384
385QSize QQuickDragAttached::imageSourceSize() const
386{
387 Q_D(const QQuickDragAttached);
388 int width = d->imageSourceSize.width();
389 int height = d->imageSourceSize.height();
390 // If width or height is invalid, check whether the size is valid from the loaded image.
391 // If it ends up 0x0 though, leave it as an invalid QSize instead (-1 x -1).
392 if (width == -1) {
393 width = d->pixmapLoader.width();
394 if (!width)
395 width = -1;
396 }
397 if (height == -1) {
398 height = d->pixmapLoader.height();
399 if (!height)
400 height = -1;
401 }
402 return QSize(width, height);
403}
404
405void QQuickDragAttached::setImageSourceSize(const QSize &size)
406{
407 Q_D(QQuickDragAttached);
408 if (d->imageSourceSize != size) {
409 d->imageSourceSize = size;
410
411 if (!d->imageSource.isEmpty())
412 d->loadPixmap();
413
414 Q_EMIT imageSourceSizeChanged();
415 }
416}
417
418/*!
419 \qmlattachedproperty stringlist QtQuick::Drag::keys
420
421 This property holds a list of keys that can be used by a DropArea to filter drag events.
422
423 Changing the keys while a drag is active will reset the sequence of drag events by
424 sending a drag leave event followed by a drag enter event with the new source.
425*/
426
427QStringList QQuickDragAttached::keys() const
428{
429 Q_D(const QQuickDragAttached);
430 return d->keys;
431}
432
433void QQuickDragAttached::setKeys(const QStringList &keys)
434{
435 Q_D(QQuickDragAttached);
436 if (d->keys != keys) {
437 d->keys = keys;
438 if (d->active)
439 d->restartDrag();
440 emit keysChanged();
441 }
442}
443
444/*!
445 \qmlattachedproperty var QtQuick::Drag::mimeData
446 \since 5.2
447
448 This property holds a map from mime type to data that is used during startDrag.
449 The mime data needs to be of a type that matches the mime type (e.g. a string if
450 the mime type is "text/plain", or an image if the mime type is "image/png"), or
451 an \c ArrayBuffer with the data encoded according to the mime type.
452*/
453
454QVariantMap QQuickDragAttached::mimeData() const
455{
456 Q_D(const QQuickDragAttached);
457 return d->externalMimeData;
458}
459
460void QQuickDragAttached::setMimeData(const QVariantMap &mimeData)
461{
462 Q_D(QQuickDragAttached);
463 if (d->externalMimeData != mimeData) {
464 d->externalMimeData = mimeData;
465 emit mimeDataChanged();
466 }
467}
468
469/*!
470 \qmlattachedproperty flags QtQuick::Drag::supportedActions
471
472 This property holds return values of Drag.drop() supported by the drag source.
473
474 Changing the supportedActions while a drag is active will reset the sequence of drag
475 events by sending a drag leave event followed by a drag enter event with the new source.
476*/
477
478Qt::DropActions QQuickDragAttached::supportedActions() const
479{
480 Q_D(const QQuickDragAttached);
481 return d->supportedActions;
482}
483
484void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
485{
486 Q_D(QQuickDragAttached);
487 if (d->supportedActions != actions) {
488 d->supportedActions = actions;
489 if (d->active)
490 d->restartDrag();
491 emit supportedActionsChanged();
492 }
493}
494
495/*!
496 \qmlattachedproperty enumeration QtQuick::Drag::proposedAction
497
498 This property holds an action that is recommended by the drag source as a
499 return value from Drag.drop().
500
501 Changes to proposedAction will trigger a move event with the updated proposal.
502*/
503
504Qt::DropAction QQuickDragAttached::proposedAction() const
505{
506 Q_D(const QQuickDragAttached);
507 return d->proposedAction;
508}
509
510void QQuickDragAttached::setProposedAction(Qt::DropAction action)
511{
512 Q_D(QQuickDragAttached);
513 if (d->proposedAction != action) {
514 d->proposedAction = action;
515 // The proposed action shouldn't affect whether a drag is accepted
516 // so leave/enter events are excessive, but the target should still
517 // updated.
518 if (d->active)
519 d->updatePosition();
520 emit proposedActionChanged();
521 }
522}
523
524/*!
525 \qmlattachedproperty enumeration QtQuick::Drag::dragType
526 \since 5.2
527
528 This property indicates whether to automatically start drags, do nothing, or
529 to use backwards compatible internal drags. The default is to use backwards
530 compatible internal drags.
531
532 A drag can also be started manually using \l startDrag.
533
534 \value Drag.None do not start drags automatically
535 \value Drag.Automatic start drags automatically
536 \value Drag.Internal (default) start backwards compatible drags automatically
537
538 When using \c Drag.Automatic you should also define \l mimeData and bind the
539 \l active property to the active property of MouseArea : \l {MouseArea::drag.active}
540*/
541
542QQuickDrag::DragType QQuickDragAttached::dragType() const
543{
544 Q_D(const QQuickDragAttached);
545 return d->dragType;
546}
547
548void QQuickDragAttached::setDragType(QQuickDrag::DragType dragType)
549{
550 Q_D(QQuickDragAttached);
551 if (d->dragType != dragType) {
552 d->dragType = dragType;
553 emit dragTypeChanged();
554 }
555}
556
557void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
558{
559 Q_Q(QQuickDragAttached);
560 Q_ASSERT(!active);
561
562 if (!mimeData)
563 mimeData = new QQuickDragMimeData;
564 if (!listening) {
565 QQuickItemPrivate::get(attachedItem)->addItemChangeListener(
566 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent);
567 listening = true;
568 }
569
570 mimeData->m_supportedActions = supportedActions;
571 active = true;
572 itemMoved = false;
573 dragRestarted = false;
574
575 deliverEnterEvent();
576
577 if (target != dragGrabber.target()) {
578 target = dragGrabber.target();
579 emit q->targetChanged();
580 }
581
582 emit q->activeChanged();
583}
584
585/*!
586 \qmlattachedmethod void QtQuick::Drag::start(flags supportedActions)
587
588 Starts sending drag events. Used for starting old-style internal drags. \l startDrag is the
589 new-style, preferred method of starting drags.
590
591 The optional \a supportedActions argument can be used to override the \l supportedActions
592 property for the started sequence.
593*/
594
595void QQuickDragAttached::start(QQmlV4FunctionPtr args)
596{
597 Q_D(QQuickDragAttached);
598 if (d->inEvent) {
599 qmlWarning(this) << "start() cannot be called from within a drag event handler";
600 return;
601 }
602
603 if (d->active)
604 cancel();
605
606 d->overrideActions = false;
607 Qt::DropActions supportedActions = d->supportedActions;
608 // check arguments for supportedActions, maybe data?
609 if (args->length() >= 1) {
610 QV4::Scope scope(args->v4engine());
611 QV4::ScopedValue v(scope, (*args)[0]);
612 if (v->isInt32()) {
613 supportedActions = Qt::DropActions(v->integerValue());
614 d->overrideActions = true;
615 }
616 }
617
618 d->start(supportedActions);
619}
620
621/*!
622 \qmlattachedmethod enumeration QtQuick::Drag::drop()
623
624 Ends a drag sequence by sending a drop event to the target item.
625
626 Returns the action accepted by the target item. If the target item or a parent doesn't accept
627 the drop event then Qt.IgnoreAction will be returned.
628
629 The returned drop action may be one of:
630
631 \value Qt.CopyAction Copy the data to the target
632 \value Qt.MoveAction Move the data from the source to the target
633 \value Qt.LinkAction Create a link from the source to the target.
634 \value Qt.IgnoreAction Ignore the action (do nothing with the data).
635*/
636
637int QQuickDragAttached::drop()
638{
639 Q_D(QQuickDragAttached);
640 Qt::DropAction acceptedAction = Qt::IgnoreAction;
641
642 if (d->inEvent) {
643 qmlWarning(this) << "drop() cannot be called from within a drag event handler";
644 return acceptedAction;
645 }
646
647 if (d->itemMoved)
648 d->deliverMoveEvent();
649
650 if (!d->active)
651 return acceptedAction;
652 d->active = false;
653
654 QObject *target = nullptr;
655
656 if (d->window) {
657 QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
658
659 QDropEvent event(
660 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
661 QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
662 d->deliverEvent(d->window, &event);
663
664 if (event.isAccepted()) {
665 acceptedAction = event.dropAction();
666 target = d->dragGrabber.target();
667 }
668 }
669
670 if (d->target != target) {
671 d->target = target;
672 emit targetChanged();
673 }
674
675 emit activeChanged();
676 return acceptedAction;
677}
678
679/*!
680 \qmlattachedmethod void QtQuick::Drag::cancel()
681
682 Ends a drag sequence.
683*/
684
685void QQuickDragAttached::cancel()
686{
687 Q_D(QQuickDragAttached);
688
689 if (d->inEvent) {
690 qmlWarning(this) << "cancel() cannot be called from within a drag event handler";
691 return;
692 }
693
694 if (!d->active)
695 return;
696 d->active = false;
697 d->deliverLeaveEvent();
698
699 if (d->target) {
700 d->target = nullptr;
701 emit targetChanged();
702 }
703
704 emit activeChanged();
705}
706
707/*!
708 \qmlattachedsignal QtQuick::Drag::dragStarted()
709
710 This signal is emitted when a drag is started with the \l startDrag() method
711 or when it is started automatically using the \l dragType property.
712 */
713
714/*!
715 \qmlattachedsignal QtQuick::Drag::dragFinished(DropAction dropAction)
716
717 This signal is emitted when a drag finishes and the drag was started with the
718 \l startDrag() method or started automatically using the \l dragType property.
719
720 \a dropAction holds the action accepted by the target item.
721
722 \sa drop()
723 */
724
725QMimeData *QQuickDragAttachedPrivate::createMimeData() const
726{
727 Q_Q(const QQuickDragAttached);
728 QMimeData *mimeData = new QMimeData();
729
730 for (const auto [mimeType, value] : externalMimeData.asKeyValueRange()) {
731 switch (value.typeId()) {
732 case QMetaType::QByteArray:
733 // byte array assumed to already be correctly encoded
734 mimeData->setData(mimeType, value.toByteArray());
735 break;
736 case QMetaType::QString: {
737 const QString text = value.toString();
738 if (mimeType == u"text/plain"_s) {
739 mimeData->setText(text);
740 } else if (mimeType == u"text/html"_s) {
741 mimeData->setHtml(text);
742 } else if (mimeType == u"text/uri-list"_s) {
743 QList<QUrl> urls;
744 // parse and split according to RFC2483
745 const auto lines = text.split(u"\r\n"_s, Qt::SkipEmptyParts);
746 for (const auto &line : lines) {
747 const QUrl url(line);
748 if (url.isValid())
749 urls.push_back(url);
750 else
751 qmlWarning(q) << line << " is not a valid URI";
752
753 }
754 mimeData->setUrls(urls);
755 } else if (mimeType.startsWith(u"text/"_s)) {
756 if (qsizetype charsetIdx = mimeType.lastIndexOf(u";charset="_s); charsetIdx != -1) {
757 charsetIdx += sizeof(";charset=") - 1;
758 const QByteArray encoding = mimeType.mid(charsetIdx).toUtf8();
759 QStringEncoder encoder(encoding);
760 if (encoder.isValid())
761 mimeData->setData(mimeType, encoder.encode(text));
762 else
763 qmlWarning(q) << "Don't know how to encode text as " << mimeType;
764 } else {
765 mimeData->setData(mimeType, text.toUtf8());
766 }
767 } else {
768 mimeData->setData(mimeType, text.toUtf8());
769 }
770 break;
771 }
772 case QMetaType::QVariantList:
773 case QMetaType::QStringList:
774 if (mimeType == u"text/uri-list"_s) {
775 const QVariantList values = value.toList();
776 QList<QUrl> urls;
777 urls.reserve(values.size());
778 bool error = false;
779 for (qsizetype index = 0; index < values.size(); ++index) {
780 const QUrl url = values.at(index).value<QUrl>();
781 if (url.isValid()) {
782 urls += url;
783 } else {
784 error = true;
785 qmlWarning(q) << "Value '" << values.at(index) << "' at index " << index
786 << " is not a valid URI";
787 }
788 }
789 if (!error)
790 mimeData->setUrls(urls);
791 }
792 break;
793 case QMetaType::QColor:
794 if (mimeType == u"application/x-color"_s)
795 mimeData->setColorData(value);
796 break;
797 case QMetaType::QImage:
798 if (const QByteArray mimeTypeUtf8 = mimeType.toUtf8();
799 QImageWriter::supportedMimeTypes().contains(mimeTypeUtf8)) {
800 const auto imageFormats = QImageWriter::imageFormatsForMimeType(mimeTypeUtf8);
801 if (imageFormats.isEmpty()) { // shouldn't happen, but we can fall back
802 mimeData->setImageData(value);
803 break;
804 }
805 const QImage image = value.value<QImage>();
806 QByteArray bytes;
807 {
808 QBuffer buffer(&bytes);
809 QImageWriter encoder(&buffer, imageFormats.first());
810 encoder.write(image);
811 }
812 mimeData->setData(mimeType, bytes);
813 break;
814 }
815 Q_FALLTHROUGH();
816 default:
817 qmlWarning(q) << "Don't know how to encode variant of type " << value.metaType()
818 << " as mime type " << mimeType;
819 // compatibility with pre-6.5 - probably a bad idea
820 mimeData->setData(mimeType, value.toString().toUtf8());
821 break;
822 }
823 }
824
825 return mimeData;
826}
827
828void QQuickDragAttachedPrivate::loadPixmap()
829{
830 Q_Q(QQuickDragAttached);
831
832 QUrl loadUrl = imageSource;
833 const QQmlContext *context = qmlContext(q->parent());
834 if (context)
835 loadUrl = context->resolvedUrl(imageSource);
836 pixmapLoader.load(context ? context->engine() : nullptr, loadUrl, QRect(), q->imageSourceSize());
837}
838
839Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedActions)
840{
841 Q_Q(QQuickDragAttached);
842
843 QDrag *drag = new QDrag(source ? source : q);
844
845 drag->setMimeData(createMimeData());
846 if (pixmapLoader.isReady())
847 drag->setPixmap(QPixmap::fromImage(pixmapLoader.image()));
848
849 drag->setHotSpot(hotSpot.toPoint());
850 emit q->dragStarted();
851
852 Qt::DropAction dropAction = drag->exec(supportedActions);
853
854 if (!QGuiApplicationPrivate::platformIntegration()->drag()->ownsDragObject())
855 drag->deleteLater();
856
857 deliverLeaveEvent();
858
859 if (target) {
860 target = nullptr;
861 emit q->targetChanged();
862 }
863
864 emit q->dragFinished(dropAction);
865
866 active = false;
867 emit q->activeChanged();
868
869 return dropAction;
870}
871
872
873/*!
874 \qmlattachedmethod void QtQuick::Drag::startDrag(flags supportedActions)
875
876 Starts sending drag events.
877
878 The optional \a supportedActions argument can be used to override the \l supportedActions
879 property for the started sequence.
880*/
881
882void QQuickDragAttached::startDrag(QQmlV4FunctionPtr args)
883{
884 Q_D(QQuickDragAttached);
885
886 if (d->inEvent) {
887 qmlWarning(this) << "startDrag() cannot be called from within a drag event handler";
888 return;
889 }
890
891 if (!d->active) {
892 qmlWarning(this) << "startDrag() drag must be active";
893 return;
894 }
895
896 Qt::DropActions supportedActions = d->supportedActions;
897
898 // check arguments for supportedActions
899 if (args->length() >= 1) {
900 QV4::Scope scope(args->v4engine());
901 QV4::ScopedValue v(scope, (*args)[0]);
902 if (v->isInt32()) {
903 supportedActions = Qt::DropActions(v->integerValue());
904 }
905 }
906
907 Qt::DropAction dropAction = d->startDrag(supportedActions);
908
909 args->setReturnValue(QV4::Encode((int)dropAction));
910}
911
912QQuickDrag::QQuickDrag(QObject *parent)
913: QObject(parent), _target(nullptr), _axis(XAndYAxis), _xmin(-FLT_MAX),
914_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false),
915 _smoothed(true), _threshold(QGuiApplication::styleHints()->startDragDistance())
916{
917}
918
919QQuickDrag::~QQuickDrag()
920{
921}
922
923QQuickItem *QQuickDrag::target() const
924{
925 return _target;
926}
927
928void QQuickDrag::setTarget(QQuickItem *t)
929{
930 if (_target == t)
931 return;
932 _target = t;
933 emit targetChanged();
934}
935
936void QQuickDrag::resetTarget()
937{
938 if (_target == nullptr)
939 return;
940 _target = nullptr;
941 emit targetChanged();
942}
943
944QQuickDrag::Axis QQuickDrag::axis() const
945{
946 return _axis;
947}
948
949void QQuickDrag::setAxis(QQuickDrag::Axis a)
950{
951 if (_axis == a)
952 return;
953 _axis = a;
954 emit axisChanged();
955}
956
957qreal QQuickDrag::xmin() const
958{
959 return _xmin;
960}
961
962void QQuickDrag::setXmin(qreal m)
963{
964 if (_xmin == m)
965 return;
966 _xmin = m;
967 emit minimumXChanged();
968}
969
970qreal QQuickDrag::xmax() const
971{
972 return _xmax;
973}
974
975void QQuickDrag::setXmax(qreal m)
976{
977 if (_xmax == m)
978 return;
979 _xmax = m;
980 emit maximumXChanged();
981}
982
983qreal QQuickDrag::ymin() const
984{
985 return _ymin;
986}
987
988void QQuickDrag::setYmin(qreal m)
989{
990 if (_ymin == m)
991 return;
992 _ymin = m;
993 emit minimumYChanged();
994}
995
996qreal QQuickDrag::ymax() const
997{
998 return _ymax;
999}
1000
1001void QQuickDrag::setYmax(qreal m)
1002{
1003 if (_ymax == m)
1004 return;
1005 _ymax = m;
1006 emit maximumYChanged();
1007}
1008
1009bool QQuickDrag::smoothed() const
1010{
1011 return _smoothed;
1012}
1013
1014void QQuickDrag::setSmoothed(bool smooth)
1015{
1016 if (_smoothed != smooth) {
1017 _smoothed = smooth;
1018 emit smoothedChanged();
1019 }
1020}
1021
1022qreal QQuickDrag::threshold() const
1023{
1024 return _threshold;
1025}
1026
1027void QQuickDrag::setThreshold(qreal value)
1028{
1029 if (_threshold != value) {
1030 _threshold = value;
1031 emit thresholdChanged();
1032 }
1033}
1034
1035void QQuickDrag::resetThreshold()
1036{
1037 setThreshold(QGuiApplication::styleHints()->startDragDistance());
1038}
1039
1040bool QQuickDrag::active() const
1041{
1042 return _active;
1043}
1044
1045void QQuickDrag::setActive(bool drag)
1046{
1047 if (_active == drag)
1048 return;
1049 _active = drag;
1050 emit activeChanged();
1051}
1052
1053bool QQuickDrag::filterChildren() const
1054{
1055 return _filterChildren;
1056}
1057
1058void QQuickDrag::setFilterChildren(bool filter)
1059{
1060 if (_filterChildren == filter)
1061 return;
1062 _filterChildren = filter;
1063 emit filterChildrenChanged();
1064}
1065
1066QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj)
1067{
1068 return new QQuickDragAttached(obj);
1069}
1070
1071QT_END_NAMESPACE
1072
1073#include "moc_qquickdrag_p.cpp"