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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quick/items/qquickdrag.cpp