95void QQuickDragAttachedPrivate::itemDestroyed(QQuickItem *item)
97 Q_Q(QQuickDragAttached);
99 listenForAttachedItemDestruction(
false);
100 if (!executingNativeDrag)
103 Q_ASSERT(item == attachedItem);
112 if (QPlatformDrag *platformDrag = QGuiApplicationPrivate::platformIntegration()->drag())
113 platformDrag->cancelDrag();
115 executingNativeDrag =
false;
119 emit q->targetChanged();
121 emit q->dragFinished(Qt::IgnoreAction);
123 emit q->activeChanged();
126void QQuickDragAttachedPrivate::listenForAttachedItemDestruction(
bool listen)
128 if (listen == listeningForDestruction || !attachedItem)
131 auto *itemPrivate = QQuickItemPrivate::get(attachedItem);
133 itemPrivate->addItemChangeListener(
this, QQuickItemPrivate::Destroyed);
135 itemPrivate->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed);
136 listeningForDestruction = listen;
159void QQuickDragAttachedPrivate::deliverEnterEvent()
161 dragRestarted =
false;
164 window = attachedItem->window();
166 mimeData->m_source = source;
167 if (!overrideActions)
168 mimeData->m_supportedActions = supportedActions;
169 mimeData->m_keys = keys;
172 QDragEnterEvent event(attachedItem->mapToScene(hotSpot), mimeData->m_supportedActions,
173 mimeData, Qt::NoButton, Qt::NoModifier);
174 QQuickDropEventEx::setProposedAction(&event, proposedAction);
175 deliverEvent(window, &event);
179void QQuickDragAttachedPrivate::deliverMoveEvent()
181 Q_Q(QQuickDragAttached);
185 QDragMoveEvent event(attachedItem->mapToScene(hotSpot), mimeData->m_supportedActions,
186 mimeData, Qt::NoButton, Qt::NoModifier);
187 QQuickDropEventEx::setProposedAction(&event, proposedAction);
188 deliverEvent(window, &event);
189 if (target != dragGrabber.target()) {
190 target = dragGrabber.target();
191 emit q->targetChanged();
238QQuickDragAttached::QQuickDragAttached(QObject *parent)
239 : QObject(*
new QQuickDragAttachedPrivate, parent)
241 Q_D(QQuickDragAttached);
242 d->attachedItem = qobject_cast<QQuickItem *>(parent);
243 d->source = d->attachedItem;
274void QQuickDragAttached::setActive(
bool active)
276 Q_D(QQuickDragAttached);
277 if (d->active != active) {
279 qmlWarning(
this) <<
"active cannot be changed from within a drag event handler";
280 else if (d->executingNativeDrag) {
285 if (d->dragType == QQuickDrag::Internal) {
286 d->start(d->supportedActions);
289 emit activeChanged();
290 if (d->dragType == QQuickDrag::Automatic) {
291 bool grabbingItemPixmap =
false;
295 if (
auto grab = d->attachedItem->grabToImage()) {
296 grabbingItemPixmap =
true;
297 QObject::connect(grab.data(), &QQuickItemGrabResult::ready,
this,
300 const QPixmap pixmap = QPixmap::fromImage(grab->image());
301 d->startDrag(d->supportedActions, pixmap);
306 if (!grabbingItemPixmap) {
311 QMetaObject::invokeMethod(
this, [
this] {
312 Q_D(QQuickDragAttached);
314 d->startDrag(d->supportedActions);
315 }, Qt::QueuedConnection);
468QSize QQuickDragAttached::imageSourceSize()
const
470 Q_D(
const QQuickDragAttached);
471 int width = d->imageSourceSize.width();
472 int height = d->imageSourceSize.height();
476 width = d->pixmapLoader.width();
481 height = d->pixmapLoader.height();
485 return QSize(width, height);
488void QQuickDragAttached::setImageSourceSize(
const QSize &size)
490 Q_D(QQuickDragAttached);
491 if (d->imageSourceSize != size) {
492 d->imageSourceSize = size;
494 if (!d->imageSource.isEmpty())
497 Q_EMIT imageSourceSizeChanged();
640void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
642 Q_Q(QQuickDragAttached);
646 mimeData =
new QQuickDragMimeData;
648 QQuickItemPrivate::get(attachedItem)->addItemChangeListener(
649 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent);
653 mimeData->m_supportedActions = supportedActions;
656 dragRestarted =
false;
660 if (target != dragGrabber.target()) {
661 target = dragGrabber.target();
662 emit q->targetChanged();
665 emit q->activeChanged();
678void QQuickDragAttached::start(QQmlV4FunctionPtr args)
680 Q_D(QQuickDragAttached);
682 qmlWarning(
this) <<
"start() cannot be called from within a drag event handler";
689 d->overrideActions =
false;
690 Qt::DropActions supportedActions = d->supportedActions;
692 if (args->length() >= 1) {
693 QV4::Scope scope(args->v4engine());
694 QV4::ScopedValue v(scope, (*args)[0]);
696 supportedActions = Qt::DropActions(v->integerValue());
697 d->overrideActions =
true;
701 d->start(supportedActions);
720int QQuickDragAttached::drop()
722 Q_D(QQuickDragAttached);
723 Qt::DropAction acceptedAction = Qt::IgnoreAction;
726 qmlWarning(
this) <<
"drop() cannot be called from within a drag event handler";
727 return acceptedAction;
731 d->deliverMoveEvent();
734 return acceptedAction;
737 QObject *target =
nullptr;
740 QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
743 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
744 QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
745 d->deliverEvent(d->window, &event);
747 if (event.isAccepted()) {
748 acceptedAction = event.dropAction();
749 target = d->dragGrabber.target();
753 if (d->target != target) {
755 emit targetChanged();
758 emit activeChanged();
759 return acceptedAction;
808QMimeData *QQuickDragAttachedPrivate::createMimeData()
const
810 Q_Q(
const QQuickDragAttached);
811 QMimeData *mimeData =
new QMimeData();
813 for (
const auto [mimeType, value] : externalMimeData.asKeyValueRange()) {
814 switch (value.typeId()) {
815 case QMetaType::QByteArray:
817 mimeData->setData(mimeType, value.toByteArray());
819 case QMetaType::QString: {
820 const QString text = value.toString();
821 if (mimeType == u"text/plain"_s) {
822 mimeData->setText(text);
823 }
else if (mimeType == u"text/html"_s) {
824 mimeData->setHtml(text);
825 }
else if (mimeType == u"text/uri-list"_s) {
828 const auto lines = text.split(u"\r\n"_s, Qt::SkipEmptyParts);
829 for (
const auto &line : lines) {
830 const QUrl url(line);
834 qmlWarning(q) << line <<
" is not a valid URI";
837 mimeData->setUrls(urls);
838 }
else if (mimeType.startsWith(u"text/"_s)) {
839 if (qsizetype charsetIdx = mimeType.lastIndexOf(u";charset="_s); charsetIdx != -1) {
840 charsetIdx +=
sizeof(
";charset=") - 1;
841 const QByteArray encoding = mimeType.mid(charsetIdx).toUtf8();
842 QStringEncoder encoder(encoding);
843 if (encoder.isValid())
844 mimeData->setData(mimeType, encoder.encode(text));
846 qmlWarning(q) <<
"Don't know how to encode text as " << mimeType;
848 mimeData->setData(mimeType, text.toUtf8());
851 mimeData->setData(mimeType, text.toUtf8());
855 case QMetaType::QVariantList:
856 case QMetaType::QStringList:
857 if (mimeType == u"text/uri-list"_s) {
858 const QVariantList values = value.toList();
860 urls.reserve(values.size());
862 for (qsizetype index = 0; index < values.size(); ++index) {
863 const QUrl url = values.at(index).value<QUrl>();
868 qmlWarning(q) <<
"Value '" << values.at(index) <<
"' at index " << index
869 <<
" is not a valid URI";
873 mimeData->setUrls(urls);
876 case QMetaType::QColor:
877 if (mimeType == u"application/x-color"_s)
878 mimeData->setColorData(value);
880 case QMetaType::QImage:
881 if (
const QByteArray mimeTypeUtf8 = mimeType.toUtf8();
882 QImageWriter::supportedMimeTypes().contains(mimeTypeUtf8)) {
883 const auto imageFormats = QImageWriter::imageFormatsForMimeType(mimeTypeUtf8);
884 if (imageFormats.isEmpty()) {
885 mimeData->setImageData(value);
888 const QImage image = value.value<QImage>();
891 QBuffer buffer(&bytes);
892 QImageWriter encoder(&buffer, imageFormats.first());
893 encoder.write(image);
895 mimeData->setData(mimeType, bytes);
900 qmlWarning(q) <<
"Don't know how to encode variant of type " << value.metaType()
901 <<
" as mime type " << mimeType;
903 mimeData->setData(mimeType, value.toString().toUtf8());
911void QQuickDragAttachedPrivate::loadPixmap()
913 Q_Q(QQuickDragAttached);
915 QUrl loadUrl = imageSource;
916 const QQmlContext *context = qmlContext(q->parent());
918 loadUrl = context->resolvedUrl(imageSource);
919 pixmapLoader.load(context ? context->engine() :
nullptr, loadUrl, QRect(), q->imageSourceSize());
922Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedActions,
925 Q_Q(QQuickDragAttached);
927 QDrag *drag =
new QDrag(source ? source : q);
929 drag->setMimeData(createMimeData());
930 if (!pixmap.isNull())
931 drag->setPixmap(pixmap);
932 else if (pixmapLoader.isReady())
933 drag->setPixmap(QPixmap::fromImage(pixmapLoader.image()));
935 drag->setHotSpot(hotSpot.toPoint());
943 listenForAttachedItemDestruction(
true);
944 QPointer<QQuickDragAttached> self(q);
945 QPointer<QDrag> dragGuard(drag);
947 emit q->dragStarted();
949 executingNativeDrag =
true;
950 Qt::DropAction dropAction = drag->exec(supportedActions);
953 return Qt::IgnoreAction;
955 executingNativeDrag =
false;
956 listenForAttachedItemDestruction(
false);
959 && !QGuiApplicationPrivate::platformIntegration()->drag()->ownsDragObject()) {
960 dragGuard->deleteLater();
967 emit q->targetChanged();
970 emit q->dragFinished(dropAction);
973 emit q->activeChanged();
988void QQuickDragAttached::startDrag(QQmlV4FunctionPtr args)
990 Q_D(QQuickDragAttached);
993 qmlWarning(
this) <<
"startDrag() cannot be called from within a drag event handler";
998 qmlWarning(
this) <<
"startDrag() drag must be active";
1002 Qt::DropActions supportedActions = d->supportedActions;
1005 if (args->length() >= 1) {
1006 QV4::Scope scope(args->v4engine());
1007 QV4::ScopedValue v(scope, (*args)[0]);
1009 supportedActions = Qt::DropActions(v->integerValue());
1013 Qt::DropAction dropAction = d->startDrag(supportedActions);
1015 args->setReturnValue(QV4::Encode((
int)dropAction));
1018QQuickDrag::QQuickDrag(QObject *parent)
1019: QObject(parent), _target(
nullptr), _axis(XAndYAxis), _xmin(-FLT_MAX),
1020_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(
false), _filterChildren(
false),
1021 _smoothed(
true), _threshold(QGuiApplication::styleHints()->startDragDistance())