Qt 6.x
The Qt SDK
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
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
27
28using namespace Qt::StringLiterals;
29
30
62 const QRectF &)
63{
64 if (!change.positionChange() || !active || itemMoved)
65 return;
67}
68
70{
71 if (!active || dragRestarted)
72 return;
73
74 QQuickWindow *newWindow = attachedItem->window();
75
76 if (window != newWindow)
78 else if (window)
80}
81
83{
85 itemMoved = true;
86 if (!eventQueued) {
87 eventQueued = true;
89 }
90}
91
93{
95 dragRestarted = true;
96 if (!eventQueued) {
97 eventQueued = true;
99 }
100}
101
103{
104 dragRestarted = false;
105 itemMoved = false;
106
108
109 mimeData->m_source = source;
110 if (!overrideActions)
111 mimeData->m_supportedActions = supportedActions;
112 mimeData->m_keys = keys;
113
114 if (window) {
116 QDragEnterEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
119 }
120}
121
123{
125
126 itemMoved = false;
127 if (window) {
129 QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
132 if (target != dragGrabber.target()) {
134 emit q->targetChanged();
135 }
136 }
137}
138
140{
141 if (window) {
142 QDragLeaveEvent event;
144 window = nullptr;
145 }
146}
147
149{
151 inEvent = true;
153 inEvent = false;
154}
155
157{
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();
171 }
172 } else if (d->itemMoved) {
173 d->deliverMoveEvent();
174 }
175 return true;
176 } else {
177 return QObject::event(event);
178 }
179}
180
183{
185 d->attachedItem = qobject_cast<QQuickItem *>(parent);
186 d->source = d->attachedItem;
187}
188
190{
192 delete d->mimeData;
193}
194
212{
213 Q_D(const QQuickDragAttached);
214 return d->active;
215}
216
218{
220 if (d->active != active) {
221 if (d->inEvent)
222 qmlWarning(this) << "active cannot be changed from within a drag event handler";
223 else if (active) {
224 if (d->dragType == QQuickDrag::Internal) {
225 d->start(d->supportedActions);
226 } else {
227 d->active = true;
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(d->supportedActions);
233 }
234 }
235 }
236 else
237 cancel();
238 }
239}
240
253{
254 Q_D(const QQuickDragAttached);
255 return d->source;
256}
257
259{
261 if (d->source != item) {
262 d->source = item;
263 if (d->active)
264 d->restartDrag();
266 }
267}
268
270{
272 if (d->source != d->attachedItem) {
273 d->source = d->attachedItem;
274 if (d->active)
275 d->restartDrag();
277 }
278}
279
293{
294 Q_D(const QQuickDragAttached);
295 return d->target;
296}
297
309{
310 Q_D(const QQuickDragAttached);
311 return d->hotSpot;
312}
313
315{
317 if (d->hotSpot != hotSpot) {
318 d->hotSpot = hotSpot;
319
320 if (d->active)
321 d->updatePosition();
322
324 }
325}
326
343{
344 Q_D(const QQuickDragAttached);
345 return d->imageSource;
346}
347
349{
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
361 }
362}
363
374{
375 Q_D(const QQuickDragAttached);
376 return d->keys;
377}
378
380{
382 if (d->keys != keys) {
383 d->keys = keys;
384 if (d->active)
385 d->restartDrag();
387 }
388}
389
401{
402 Q_D(const QQuickDragAttached);
403 return d->externalMimeData;
404}
405
407{
409 if (d->externalMimeData != mimeData) {
410 d->externalMimeData = mimeData;
412 }
413}
414
425{
426 Q_D(const QQuickDragAttached);
427 return d->supportedActions;
428}
429
430void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
431{
433 if (d->supportedActions != actions) {
434 d->supportedActions = actions;
435 if (d->active)
436 d->restartDrag();
438 }
439}
440
451{
452 Q_D(const QQuickDragAttached);
453 return d->proposedAction;
454}
455
457{
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();
467 }
468}
469
489{
490 Q_D(const QQuickDragAttached);
491 return d->dragType;
492}
493
495{
497 if (d->dragType != dragType) {
498 d->dragType = dragType;
500 }
501}
502
503void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
504{
507
508 if (!mimeData)
510 if (!listening) {
513 listening = true;
514 }
515
516 mimeData->m_supportedActions = supportedActions;
517 active = true;
518 itemMoved = false;
519 dragRestarted = false;
520
522
523 if (target != dragGrabber.target()) {
525 emit q->targetChanged();
526 }
527
528 emit q->activeChanged();
529}
530
542{
544 if (d->inEvent) {
545 qmlWarning(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
584{
586 Qt::DropAction acceptedAction = Qt::IgnoreAction;
587
588 if (d->inEvent) {
589 qmlWarning(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(d->hotSpot).toPoint();
604
605 QDropEvent event(
606 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
608 d->deliverEvent(d->window, &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;
619 }
620
622 return acceptedAction;
623}
624
632{
634
635 if (d->inEvent) {
636 qmlWarning(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;
648 }
649
651}
652
672{
673 Q_Q(const QQuickDragAttached);
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, value.toByteArray());
681 break;
682 case QMetaType::QString: {
683 const QString text = value.toString();
684 if (mimeType == u"text/plain"_s) {
686 } else if (mimeType == u"text/html"_s) {
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(q) << text << " is not a valid URI";
694 } else if (mimeType.startsWith(u"text/"_s)) {
695 if (qsizetype charsetIdx = mimeType.lastIndexOf(u";charset="_s); charsetIdx != -1) {
696 charsetIdx += sizeof(";charset=") - 1;
697 const QByteArray encoding = mimeType.mid(charsetIdx).toUtf8();
698 QStringEncoder encoder(encoding);
699 if (encoder.isValid())
700 mimeData->setData(mimeType, encoder.encode(text));
701 else
702 qmlWarning(q) << "Don't know how to encode text as " << mimeType;
703 } else {
705 }
706 } else {
707 qmlWarning(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) {
716 QList<QUrl> urls;
717 urls.reserve(values.size());
718 bool error = false;
719 for (qsizetype index = 0; index < values.size(); ++index) {
720 const QUrl url = values.at(index).value<QUrl>();
721 if (url.isValid()) {
722 urls += url;
723 } else {
724 error = true;
725 qmlWarning(q) << "Value '" << values.at(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();
736 const auto imageFormats = QImageWriter::imageFormatsForMimeType(mimeTypeUtf8);
737 if (imageFormats.isEmpty()) { // shouldn't happen, but we can fall back
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, bytes);
749 break;
750 }
752 default:
753 qmlWarning(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, value.toString().toUtf8());
757 break;
758 }
759 }
760
761 return mimeData;
762}
763
765{
767
768 QDrag *drag = new QDrag(source ? source : q);
769
771 if (pixmapLoader.isReady())
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
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
808{
810
811 if (d->inEvent) {
812 qmlWarning(this) << "startDrag() cannot be called from within a drag event handler";
813 return;
814 }
815
816 if (!d->active) {
817 qmlWarning(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
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
845{
846}
847
849{
850 return _target;
851}
852
854{
855 if (_target == t)
856 return;
857 _target = t;
859}
860
862{
863 if (_target == nullptr)
864 return;
865 _target = nullptr;
867}
868
870{
871 return _axis;
872}
873
875{
876 if (_axis == a)
877 return;
878 _axis = a;
880}
881
883{
884 return _xmin;
885}
886
888{
889 if (_xmin == m)
890 return;
891 _xmin = m;
893}
894
896{
897 return _xmax;
898}
899
901{
902 if (_xmax == m)
903 return;
904 _xmax = m;
906}
907
909{
910 return _ymin;
911}
912
914{
915 if (_ymin == m)
916 return;
917 _ymin = m;
919}
920
922{
923 return _ymax;
924}
925
927{
928 if (_ymax == m)
929 return;
930 _ymax = m;
932}
933
935{
936 return _smoothed;
937}
938
939void QQuickDrag::setSmoothed(bool smooth)
940{
941 if (_smoothed != smooth) {
942 _smoothed = smooth;
944 }
945}
946
948{
949 return _threshold;
950}
951
953{
954 if (_threshold != value) {
955 _threshold = value;
957 }
958}
959
961{
962 setThreshold(QGuiApplication::styleHints()->startDragDistance());
963}
964
966{
967 return _active;
968}
969
971{
972 if (_active == drag)
973 return;
974 _active = drag;
976}
977
979{
980 return _filterChildren;
981}
982
984{
985 if (_filterChildren == filter)
986 return;
987 _filterChildren = filter;
989}
990
992{
993 return new QQuickDragAttached(obj);
994}
995
997
998#include "moc_qquickdrag_p.cpp"
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
\inmodule QtGui
Definition qdrag.h:22
void setHotSpot(const QPoint &hotspot)
Sets the position of the hot spot relative to the top-left corner of the pixmap used to the point spe...
Definition qdrag.cpp:146
Qt::DropAction exec(Qt::DropActions supportedActions=Qt::MoveAction)
Definition qdrag.cpp:199
void setMimeData(QMimeData *data)
Sets the data to be sent to the given MIME data.
Definition qdrag.cpp:99
void setPixmap(const QPixmap &)
Sets pixmap as the pixmap used to represent the data in a drag and drop operation.
Definition qdrag.cpp:123
\inmodule QtCore
Definition qcoreevent.h:45
static QPlatformIntegration * platformIntegration()
\macro qGuiApp
static QStyleHints * styleHints()
Returns the application's style hints.
The QImageWriter class provides a format independent interface for writing images to files or other d...
bool write(const QImage &image)
Writes the image image to the assigned device or file name.
static QList< QByteArray > supportedMimeTypes()
Returns the list of MIME types supported by QImageWriter.
static QList< QByteArray > imageFormatsForMimeType(const QByteArray &mimeType)
\inmodule QtGui
Definition qimage.h:37
QList< T > toList() const noexcept
Definition qlist.h:716
qsizetype length() const noexcept
Definition qlist.h:388
void reserve(qsizetype size)
Definition qlist.h:746
auto asKeyValueRange() &
Definition qmap.h:613
\inmodule QtCore
Definition qmimedata.h:16
void setHtml(const QString &html)
Sets html as the HTML (MIME type text/html) used to represent the data.
void setData(const QString &mimetype, const QByteArray &data)
Sets the data associated with the MIME type given by mimeType to the specified data.
void setText(const QString &text)
Sets text as the plain text (MIME type text/plain) used to represent the data.
void setImageData(const QVariant &image)
Sets the data in the object to the given image.
void setUrls(const QList< QUrl > &urls)
Sets the URLs stored in the MIME data object to those specified by urls.
\inmodule QtCore
Definition qobject.h:90
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1363
void deleteLater()
\threadsafe
Definition qobject.cpp:2352
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1445
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
Definition qpoint.h:394
\inmodule QtCore\reentrant
Definition qpoint.h:23
Qt::DropAction proposedAction
QQuickDragMimeData * mimeData
QMimeData * createMimeData() const
\qmlattachedsignal QtQuick::Drag::dragStarted()
void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) override
\qmltype Drag \instantiates QQuickDrag \inqmlmodule QtQuick
Qt::DropAction startDrag(Qt::DropActions supportedActions)
QQuickDragGrabber dragGrabber
QPointer< QObject > source
void itemParentChanged(QQuickItem *, QQuickItem *parent) override
Qt::DropActions supportedActions
void start(Qt::DropActions supportedActions)
void deliverEvent(QQuickWindow *window, QEvent *event)
QPointer< QQuickWindow > window
void startDrag(QQmlV4Function *)
\qmlattachedmethod void QtQuick::Drag::startDrag(flags supportedActions)
void setSupportedActions(Qt::DropActions actions)
void setImageSource(const QUrl &url)
QQuickDrag::DragType dragType
void setSource(QObject *item)
void cancel()
\qmlattachedmethod void QtQuick::Drag::cancel()
void setMimeData(const QVariantMap &mimeData)
void setDragType(QQuickDrag::DragType dragType)
Qt::DropAction proposedAction
Qt::DropActions supportedActions
void start(QQmlV4Function *)
\qmlattachedmethod void QtQuick::Drag::start(flags supportedActions)
void setActive(bool active)
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
QQuickDragAttached(QObject *parent)
void proposedActionChanged()
void setKeys(const QStringList &keys)
void setProposedAction(Qt::DropAction action)
QVariantMap mimeData
Q_INVOKABLE int drop()
\qmlattachedmethod enumeration QtQuick::Drag::drop()
bool isActive() const
\qmlattachedproperty bool QtQuick::Drag::active
void setHotSpot(const QPointF &hotSpot)
void supportedActionsChanged()
QObject * target() const
void setXmax(qreal)
void setXmin(qreal)
qreal threshold
void setSmoothed(bool smooth)
void activeChanged()
void maximumXChanged()
void minimumXChanged()
void targetChanged()
void setAxis(Axis)
void setActive(bool)
void resetTarget()
qreal xmin() const
void thresholdChanged()
qreal ymin() const
void setYmax(qreal)
void smoothedChanged()
void setFilterChildren(bool)
qreal ymax() const
void minimumYChanged()
void setTarget(QQuickItem *target)
bool filterChildren
void axisChanged()
QQuickItem * target
qreal xmax() const
void filterChildrenChanged()
void setYmin(qreal)
QQuickDrag(QObject *parent=nullptr)
static QQuickDragAttached * qmlAttachedProperties(QObject *obj)
void resetThreshold()
void maximumYChanged()
void setThreshold(qreal)
void setProposedAction(Qt::DropAction action)
void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
QPointF mapToScene(const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within the scene's coor...
QQuickWindow * window() const
Returns the window in which this item is rendered.
QImage image() const
static QQuickWindowPrivate * get(QQuickWindow *c)
QQuickDeliveryAgentPrivate * deliveryAgentPrivate() const
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:483
bool isValid() const noexcept
Returns true if this is a valid string converter that can be used for encoding or decoding text.
\inmodule QtCore
Q_WEAK_OVERLOAD DecodedData< const QString & > encode(const QString &str)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toUtf8() const &
Definition qstring.h:563
\inmodule QtCore
Definition qurl.h:94
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1874
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
QString text
Combined button and popup list for selecting options.
@ NoButton
Definition qnamespace.h:56
@ NoModifier
DropAction
@ IgnoreAction
Definition image.cpp:4
#define Q_FALLTHROUGH()
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * mimeType
GLenum GLsizei GLsizei GLint * values
[15]
GLsizei const GLfloat * v
[13]
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLenum GLuint buffer
GLenum target
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLsizei GLsizei GLchar * source
struct _cl_event * event
GLhandleARB obj
[2]
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:76
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:483
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_EMIT
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:70
double qreal
Definition qtypes.h:92
QStringList keys
QUrl url("example.com")
[constructor-url-reference]
QMimeData * mimeData
QObject::connect nullptr
QGraphicsItem * item
aWidget window() -> setWindowTitle("New Window Title")
[2]
QJSValueList args
bool contains(const AT &t) const noexcept
Definition qlist.h:44
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent