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