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