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/qquickpixmapcache_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 \instantiates 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->pixmapLoader.load(qmlEngine(parent()), url);
358 }
359
360 Q_EMIT imageSourceChanged();
361 }
362}
363
364/*!
365 \qmlattachedproperty stringlist QtQuick::Drag::keys
366
367 This property holds a list of keys that can be used by a DropArea to filter drag events.
368
369 Changing the keys while a drag is active will reset the sequence of drag events by
370 sending a drag leave event followed by a drag enter event with the new source.
371*/
372
373QStringList QQuickDragAttached::keys() const
374{
375 Q_D(const QQuickDragAttached);
376 return d->keys;
377}
378
379void QQuickDragAttached::setKeys(const QStringList &keys)
380{
381 Q_D(QQuickDragAttached);
382 if (d->keys != keys) {
383 d->keys = keys;
384 if (d->active)
385 d->restartDrag();
386 emit keysChanged();
387 }
388}
389
390/*!
391 \qmlattachedproperty var QtQuick::Drag::mimeData
392 \since 5.2
393
394 This property holds a map from mime type to data that is used during startDrag.
395 The mime data needs to be of a type that matches the mime type (e.g. a string if
396 the mime type is "text/plain", or an image if the mime type is "image/png"), or
397 an \c ArrayBuffer with the data encoded according to the mime type.
398*/
399
400QVariantMap QQuickDragAttached::mimeData() const
401{
402 Q_D(const QQuickDragAttached);
403 return d->externalMimeData;
404}
405
406void QQuickDragAttached::setMimeData(const QVariantMap &mimeData)
407{
408 Q_D(QQuickDragAttached);
409 if (d->externalMimeData != mimeData) {
410 d->externalMimeData = mimeData;
411 emit mimeDataChanged();
412 }
413}
414
415/*!
416 \qmlattachedproperty flags QtQuick::Drag::supportedActions
417
418 This property holds return values of Drag.drop() supported by the drag source.
419
420 Changing the supportedActions while a drag is active will reset the sequence of drag
421 events by sending a drag leave event followed by a drag enter event with the new source.
422*/
423
424Qt::DropActions QQuickDragAttached::supportedActions() const
425{
426 Q_D(const QQuickDragAttached);
427 return d->supportedActions;
428}
429
430void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
431{
432 Q_D(QQuickDragAttached);
433 if (d->supportedActions != actions) {
434 d->supportedActions = actions;
435 if (d->active)
436 d->restartDrag();
437 emit supportedActionsChanged();
438 }
439}
440
441/*!
442 \qmlattachedproperty enumeration QtQuick::Drag::proposedAction
443
444 This property holds an action that is recommended by the drag source as a
445 return value from Drag.drop().
446
447 Changes to proposedAction will trigger a move event with the updated proposal.
448*/
449
450Qt::DropAction QQuickDragAttached::proposedAction() const
451{
452 Q_D(const QQuickDragAttached);
453 return d->proposedAction;
454}
455
456void QQuickDragAttached::setProposedAction(Qt::DropAction action)
457{
458 Q_D(QQuickDragAttached);
459 if (d->proposedAction != action) {
460 d->proposedAction = action;
461 // The proposed action shouldn't affect whether a drag is accepted
462 // so leave/enter events are excessive, but the target should still
463 // updated.
464 if (d->active)
465 d->updatePosition();
466 emit proposedActionChanged();
467 }
468}
469
470/*!
471 \qmlattachedproperty enumeration QtQuick::Drag::dragType
472 \since 5.2
473
474 This property indicates whether to automatically start drags, do nothing, or
475 to use backwards compatible internal drags. The default is to use backwards
476 compatible internal drags.
477
478 A drag can also be started manually using \l startDrag.
479
480 \value Drag.None do not start drags automatically
481 \value Drag.Automatic start drags automatically
482 \value Drag.Internal (default) start backwards compatible drags automatically
483
484 When using \c Drag.Automatic you should also define \l mimeData and bind the
485 \l active property to the active property of MouseArea : \l {MouseArea::drag.active}
486*/
487
488QQuickDrag::DragType QQuickDragAttached::dragType() const
489{
490 Q_D(const QQuickDragAttached);
491 return d->dragType;
492}
493
494void QQuickDragAttached::setDragType(QQuickDrag::DragType dragType)
495{
496 Q_D(QQuickDragAttached);
497 if (d->dragType != dragType) {
498 d->dragType = dragType;
499 emit dragTypeChanged();
500 }
501}
502
503void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
504{
505 Q_Q(QQuickDragAttached);
506 Q_ASSERT(!active);
507
508 if (!mimeData)
509 mimeData = new QQuickDragMimeData;
510 if (!listening) {
511 QQuickItemPrivate::get(item: attachedItem)->addItemChangeListener(
512 listener: this, types: QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent);
513 listening = true;
514 }
515
516 mimeData->m_supportedActions = supportedActions;
517 active = true;
518 itemMoved = false;
519 dragRestarted = false;
520
521 deliverEnterEvent();
522
523 if (target != dragGrabber.target()) {
524 target = dragGrabber.target();
525 emit q->targetChanged();
526 }
527
528 emit q->activeChanged();
529}
530
531/*!
532 \qmlattachedmethod void QtQuick::Drag::start(flags supportedActions)
533
534 Starts sending drag events. Used for starting old-style internal drags. \l startDrag is the
535 new-style, preferred method of starting drags.
536
537 The optional \a supportedActions argument can be used to override the \l supportedActions
538 property for the started sequence.
539*/
540
541void QQuickDragAttached::start(QQmlV4Function *args)
542{
543 Q_D(QQuickDragAttached);
544 if (d->inEvent) {
545 qmlWarning(me: this) << "start() cannot be called from within a drag event handler";
546 return;
547 }
548
549 if (d->active)
550 cancel();
551
552 d->overrideActions = false;
553 Qt::DropActions supportedActions = d->supportedActions;
554 // check arguments for supportedActions, maybe data?
555 if (args->length() >= 1) {
556 QV4::Scope scope(args->v4engine());
557 QV4::ScopedValue v(scope, (*args)[0]);
558 if (v->isInt32()) {
559 supportedActions = Qt::DropActions(v->integerValue());
560 d->overrideActions = true;
561 }
562 }
563
564 d->start(supportedActions);
565}
566
567/*!
568 \qmlattachedmethod enumeration QtQuick::Drag::drop()
569
570 Ends a drag sequence by sending a drop event to the target item.
571
572 Returns the action accepted by the target item. If the target item or a parent doesn't accept
573 the drop event then Qt.IgnoreAction will be returned.
574
575 The returned drop action may be one of:
576
577 \value Qt.CopyAction Copy the data to the target
578 \value Qt.MoveAction Move the data from the source to the target
579 \value Qt.LinkAction Create a link from the source to the target.
580 \value Qt.IgnoreAction Ignore the action (do nothing with the data).
581*/
582
583int QQuickDragAttached::drop()
584{
585 Q_D(QQuickDragAttached);
586 Qt::DropAction acceptedAction = Qt::IgnoreAction;
587
588 if (d->inEvent) {
589 qmlWarning(me: this) << "drop() cannot be called from within a drag event handler";
590 return acceptedAction;
591 }
592
593 if (d->itemMoved)
594 d->deliverMoveEvent();
595
596 if (!d->active)
597 return acceptedAction;
598 d->active = false;
599
600 QObject *target = nullptr;
601
602 if (d->window) {
603 QPoint scenePos = d->attachedItem->mapToScene(point: d->hotSpot).toPoint();
604
605 QDropEvent event(
606 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
607 QQuickDropEventEx::setProposedAction(event: &event, action: d->proposedAction);
608 d->deliverEvent(window: d->window, event: &event);
609
610 if (event.isAccepted()) {
611 acceptedAction = event.dropAction();
612 target = d->dragGrabber.target();
613 }
614 }
615
616 if (d->target != target) {
617 d->target = target;
618 emit targetChanged();
619 }
620
621 emit activeChanged();
622 return acceptedAction;
623}
624
625/*!
626 \qmlattachedmethod void QtQuick::Drag::cancel()
627
628 Ends a drag sequence.
629*/
630
631void QQuickDragAttached::cancel()
632{
633 Q_D(QQuickDragAttached);
634
635 if (d->inEvent) {
636 qmlWarning(me: this) << "cancel() cannot be called from within a drag event handler";
637 return;
638 }
639
640 if (!d->active)
641 return;
642 d->active = false;
643 d->deliverLeaveEvent();
644
645 if (d->target) {
646 d->target = nullptr;
647 emit targetChanged();
648 }
649
650 emit activeChanged();
651}
652
653/*!
654 \qmlattachedsignal QtQuick::Drag::dragStarted()
655
656 This signal is emitted when a drag is started with the \l startDrag() method
657 or when it is started automatically using the \l dragType property.
658 */
659
660/*!
661 \qmlattachedsignal QtQuick::Drag::dragFinished(DropAction dropAction)
662
663 This signal is emitted when a drag finishes and the drag was started with the
664 \l startDrag() method or started automatically using the \l dragType property.
665
666 \a dropAction holds the action accepted by the target item.
667
668 \sa drop()
669 */
670
671QMimeData *QQuickDragAttachedPrivate::createMimeData() const
672{
673 Q_Q(const QQuickDragAttached);
674 QMimeData *mimeData = new QMimeData();
675
676 for (const auto [mimeType, value] : externalMimeData.asKeyValueRange()) {
677 switch (value.typeId()) {
678 case QMetaType::QByteArray:
679 // byte array assumed to already be correctly encoded
680 mimeData->setData(mimetype: mimeType, data: value.toByteArray());
681 break;
682 case QMetaType::QString: {
683 const QString text = value.toString();
684 if (mimeType == u"text/plain"_s) {
685 mimeData->setText(text);
686 } else if (mimeType == u"text/html"_s) {
687 mimeData->setHtml(text);
688 } else if (mimeType == u"text/uri-list"_s) {
689 const QUrl url(text);
690 if (url.isValid())
691 mimeData->setUrls({url});
692 else
693 qmlWarning(me: q) << text << " is not a valid URI";
694 } else if (mimeType.startsWith(s: u"text/"_s)) {
695 if (qsizetype charsetIdx = mimeType.lastIndexOf(s: u";charset="_s); charsetIdx != -1) {
696 charsetIdx += sizeof(";charset=") - 1;
697 const QByteArray encoding = mimeType.mid(position: charsetIdx).toUtf8();
698 QStringEncoder encoder(encoding);
699 if (encoder.isValid())
700 mimeData->setData(mimetype: mimeType, data: encoder.encode(str: text));
701 else
702 qmlWarning(me: q) << "Don't know how to encode text as " << mimeType;
703 } else {
704 mimeData->setData(mimetype: mimeType, data: text.toUtf8().constData());
705 }
706 } else {
707 qmlWarning(me: q) << "Mime data contains a string, but mime type " << mimeType
708 << " is not a supported text type";
709 }
710 break;
711 }
712 case QMetaType::QVariantList:
713 case QMetaType::QStringList:
714 if (mimeType == u"text/uri-list"_s) {
715 const QVariantList values = value.toList();
716 QList<QUrl> urls;
717 urls.reserve(size: values.size());
718 bool error = false;
719 for (qsizetype index = 0; index < values.size(); ++index) {
720 const QUrl url = values.at(i: index).value<QUrl>();
721 if (url.isValid()) {
722 urls += url;
723 } else {
724 error = true;
725 qmlWarning(me: q) << "Value '" << values.at(i: index) << "' at index " << index
726 << " is not a valid URI";
727 }
728 }
729 if (!error)
730 mimeData->setUrls(urls);
731 }
732 break;
733 case QMetaType::QImage:
734 if (const QByteArray mimeTypeUtf8 = mimeType.toUtf8();
735 QImageWriter::supportedMimeTypes().contains(t: mimeTypeUtf8)) {
736 const auto imageFormats = QImageWriter::imageFormatsForMimeType(mimeType: mimeTypeUtf8);
737 if (imageFormats.isEmpty()) { // shouldn't happen, but we can fall back
738 mimeData->setImageData(value);
739 break;
740 }
741 const QImage image = value.value<QImage>();
742 QByteArray bytes;
743 {
744 QBuffer buffer(&bytes);
745 QImageWriter encoder(&buffer, imageFormats.first());
746 encoder.write(image);
747 }
748 mimeData->setData(mimetype: mimeType, data: bytes);
749 break;
750 }
751 Q_FALLTHROUGH();
752 default:
753 qmlWarning(me: q) << "Don't know how to encode variant of type " << value.metaType()
754 << " as mime type " << mimeType;
755 // compatibility with pre-6.5 - probably a bad idea
756 mimeData->setData(mimetype: mimeType, data: value.toString().toUtf8());
757 break;
758 }
759 }
760
761 return mimeData;
762}
763
764Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedActions)
765{
766 Q_Q(QQuickDragAttached);
767
768 QDrag *drag = new QDrag(source ? source : q);
769
770 drag->setMimeData(createMimeData());
771 if (pixmapLoader.isReady())
772 drag->setPixmap(QPixmap::fromImage(image: pixmapLoader.image()));
773
774 drag->setHotSpot(hotSpot.toPoint());
775 emit q->dragStarted();
776
777 Qt::DropAction dropAction = drag->exec(supportedActions);
778
779 if (!QGuiApplicationPrivate::platformIntegration()->drag()->ownsDragObject())
780 drag->deleteLater();
781
782 deliverLeaveEvent();
783
784 if (target) {
785 target = nullptr;
786 emit q->targetChanged();
787 }
788
789 emit q->dragFinished(dropAction);
790
791 active = false;
792 emit q->activeChanged();
793
794 return dropAction;
795}
796
797
798/*!
799 \qmlattachedmethod void QtQuick::Drag::startDrag(flags supportedActions)
800
801 Starts sending drag events.
802
803 The optional \a supportedActions argument can be used to override the \l supportedActions
804 property for the started sequence.
805*/
806
807void QQuickDragAttached::startDrag(QQmlV4Function *args)
808{
809 Q_D(QQuickDragAttached);
810
811 if (d->inEvent) {
812 qmlWarning(me: this) << "startDrag() cannot be called from within a drag event handler";
813 return;
814 }
815
816 if (!d->active) {
817 qmlWarning(me: this) << "startDrag() drag must be active";
818 return;
819 }
820
821 Qt::DropActions supportedActions = d->supportedActions;
822
823 // check arguments for supportedActions
824 if (args->length() >= 1) {
825 QV4::Scope scope(args->v4engine());
826 QV4::ScopedValue v(scope, (*args)[0]);
827 if (v->isInt32()) {
828 supportedActions = Qt::DropActions(v->integerValue());
829 }
830 }
831
832 Qt::DropAction dropAction = d->startDrag(supportedActions);
833
834 args->setReturnValue(QV4::Encode((int)dropAction));
835}
836
837QQuickDrag::QQuickDrag(QObject *parent)
838: QObject(parent), _target(nullptr), _axis(XAndYAxis), _xmin(-FLT_MAX),
839_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false),
840 _smoothed(true), _threshold(QGuiApplication::styleHints()->startDragDistance())
841{
842}
843
844QQuickDrag::~QQuickDrag()
845{
846}
847
848QQuickItem *QQuickDrag::target() const
849{
850 return _target;
851}
852
853void QQuickDrag::setTarget(QQuickItem *t)
854{
855 if (_target == t)
856 return;
857 _target = t;
858 emit targetChanged();
859}
860
861void QQuickDrag::resetTarget()
862{
863 if (_target == nullptr)
864 return;
865 _target = nullptr;
866 emit targetChanged();
867}
868
869QQuickDrag::Axis QQuickDrag::axis() const
870{
871 return _axis;
872}
873
874void QQuickDrag::setAxis(QQuickDrag::Axis a)
875{
876 if (_axis == a)
877 return;
878 _axis = a;
879 emit axisChanged();
880}
881
882qreal QQuickDrag::xmin() const
883{
884 return _xmin;
885}
886
887void QQuickDrag::setXmin(qreal m)
888{
889 if (_xmin == m)
890 return;
891 _xmin = m;
892 emit minimumXChanged();
893}
894
895qreal QQuickDrag::xmax() const
896{
897 return _xmax;
898}
899
900void QQuickDrag::setXmax(qreal m)
901{
902 if (_xmax == m)
903 return;
904 _xmax = m;
905 emit maximumXChanged();
906}
907
908qreal QQuickDrag::ymin() const
909{
910 return _ymin;
911}
912
913void QQuickDrag::setYmin(qreal m)
914{
915 if (_ymin == m)
916 return;
917 _ymin = m;
918 emit minimumYChanged();
919}
920
921qreal QQuickDrag::ymax() const
922{
923 return _ymax;
924}
925
926void QQuickDrag::setYmax(qreal m)
927{
928 if (_ymax == m)
929 return;
930 _ymax = m;
931 emit maximumYChanged();
932}
933
934bool QQuickDrag::smoothed() const
935{
936 return _smoothed;
937}
938
939void QQuickDrag::setSmoothed(bool smooth)
940{
941 if (_smoothed != smooth) {
942 _smoothed = smooth;
943 emit smoothedChanged();
944 }
945}
946
947qreal QQuickDrag::threshold() const
948{
949 return _threshold;
950}
951
952void QQuickDrag::setThreshold(qreal value)
953{
954 if (_threshold != value) {
955 _threshold = value;
956 emit thresholdChanged();
957 }
958}
959
960void QQuickDrag::resetThreshold()
961{
962 setThreshold(QGuiApplication::styleHints()->startDragDistance());
963}
964
965bool QQuickDrag::active() const
966{
967 return _active;
968}
969
970void QQuickDrag::setActive(bool drag)
971{
972 if (_active == drag)
973 return;
974 _active = drag;
975 emit activeChanged();
976}
977
978bool QQuickDrag::filterChildren() const
979{
980 return _filterChildren;
981}
982
983void QQuickDrag::setFilterChildren(bool filter)
984{
985 if (_filterChildren == filter)
986 return;
987 _filterChildren = filter;
988 emit filterChildrenChanged();
989}
990
991QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj)
992{
993 return new QQuickDragAttached(obj);
994}
995
996QT_END_NAMESPACE
997
998#include "moc_qquickdrag_p.cpp"
999

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