aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickdrag.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickdrag.cpp')
-rw-r--r--src/quick/items/qquickdrag.cpp296
1 files changed, 172 insertions, 124 deletions
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index e6ffcee2aa..dbe443b2b3 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -1,123 +1,32 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickdrag_p.h"
+#include "qquickdrag_p_p.h"
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <QtCore/qbuffer.h>
#include <QtCore/qmimedata.h>
+#include <QtCore/qstringconverter.h>
#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QtGui/qstylehints.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/qimagewriter.h>
#include <qpa/qplatformdrag.h>
#include <QtGui/qdrag.h>
QT_BEGIN_NAMESPACE
-class QQuickDragAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
-{
- Q_DECLARE_PUBLIC(QQuickDragAttached)
- QML_ANONYMOUS
- QML_ADDED_IN_VERSION(2, 0)
-
-public:
- static QQuickDragAttachedPrivate *get(QQuickDragAttached *attached) {
- return static_cast<QQuickDragAttachedPrivate *>(QObjectPrivate::get(attached)); }
-
- QQuickDragAttachedPrivate()
- : attachedItem(nullptr)
- , mimeData(nullptr)
- , proposedAction(Qt::MoveAction)
- , supportedActions(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction)
- , active(false)
- , listening(false)
- , inEvent(false)
- , dragRestarted(false)
- , itemMoved(false)
- , eventQueued(false)
- , overrideActions(false)
- , dragType(QQuickDrag::Internal)
- {
- }
-
- void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) override;
- void itemParentChanged(QQuickItem *, QQuickItem *parent) override;
- void updatePosition();
- void restartDrag();
- void deliverEnterEvent();
- void deliverMoveEvent();
- void deliverLeaveEvent();
- void deliverEvent(QQuickWindow *window, QEvent *event);
- void start(Qt::DropActions supportedActions);
- Qt::DropAction startDrag(Qt::DropActions supportedActions);
- void setTarget(QQuickItem *item);
-
- QQuickDragGrabber dragGrabber;
-
- QPointer<QObject> source;
- QPointer<QObject> target;
- QPointer<QQuickWindow> window;
- QQuickItem *attachedItem;
- QQuickDragMimeData *mimeData;
- Qt::DropAction proposedAction;
- Qt::DropActions supportedActions;
- bool active : 1;
- bool listening : 1;
- bool inEvent : 1;
- bool dragRestarted : 1;
- bool itemMoved : 1;
- bool eventQueued : 1;
- bool overrideActions : 1;
- QPointF hotSpot;
- QUrl imageSource;
- QQuickPixmap pixmapLoader;
- QStringList keys;
- QVariantMap externalMimeData;
- QQuickDrag::DragType dragType;
-};
+using namespace Qt::StringLiterals;
+
/*!
\qmltype Drag
@@ -146,7 +55,7 @@ public:
\l {supportedActions}{drop action} chosen by the recipient of the event,
otherwise it will return Qt.IgnoreAction.
- \sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
+ \sa {Qt Quick Examples - Drag and Drop}
*/
void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change,
@@ -445,7 +354,7 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
if (url.isEmpty()) {
d->pixmapLoader.clear();
} else {
- d->pixmapLoader.load(qmlEngine(parent()), url);
+ d->loadPixmap();
}
Q_EMIT imageSourceChanged();
@@ -453,6 +362,48 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
}
/*!
+ \qmlattachedproperty QUrl QtQuick::Drag::imageSourceSize
+ \since 6.8
+
+ This property holds the size of the image that will be used to represent
+ the data during the drag and drop operation. Changing this property after
+ the drag operation has started will have no effect.
+
+ This property sets the maximum number of pixels stored for the loaded
+ image so that large images do not use more memory than necessary.
+ See \l {QtQuick::Image::sourceSize}{Image.sourceSize} for more details.
+
+ The example below shows an SVG image rendered at one size, and re-renders
+ it at a different size for the drag image:
+
+ \snippet qml/externalDragScaledImage.qml 0
+
+ \sa imageSource, Item::grabToImage()
+*/
+
+QSize QQuickDragAttached::imageSourceSize() const
+{
+ Q_D(const QQuickDragAttached);
+ int width = d->imageSourceSize.width();
+ int height = d->imageSourceSize.height();
+ return QSize(width != -1 ? width : d->pixmapLoader.width(),
+ height != -1 ? height : d->pixmapLoader.height());
+}
+
+void QQuickDragAttached::setImageSourceSize(const QSize &size)
+{
+ Q_D(QQuickDragAttached);
+ if (d->imageSourceSize != size) {
+ d->imageSourceSize = size;
+
+ if (!d->imageSource.isEmpty())
+ d->loadPixmap();
+
+ Q_EMIT imageSourceSizeChanged();
+ }
+}
+
+/*!
\qmlattachedproperty stringlist QtQuick::Drag::keys
This property holds a list of keys that can be used by a DropArea to filter drag events.
@@ -479,10 +430,13 @@ void QQuickDragAttached::setKeys(const QStringList &keys)
}
/*!
- \qmlattachedproperty stringlist QtQuick::Drag::mimeData
+ \qmlattachedproperty var QtQuick::Drag::mimeData
\since 5.2
- This property holds a map of mimeData that is used during startDrag.
+ This property holds a map from mime type to data that is used during startDrag.
+ The mime data needs to be of a type that matches the mime type (e.g. a string if
+ the mime type is "text/plain", or an image if the mime type is "image/png"), or
+ an \c ArrayBuffer with the data encoded according to the mime type.
*/
QVariantMap QQuickDragAttached::mimeData() const
@@ -565,11 +519,9 @@ void QQuickDragAttached::setProposedAction(Qt::DropAction action)
A drag can also be started manually using \l startDrag.
- \list
- \li Drag.None - do not start drags automatically
- \li Drag.Automatic - start drags automatically
- \li Drag.Internal (default) - start backwards compatible drags automatically
- \endlist
+ \value Drag.None do not start drags automatically
+ \value Drag.Automatic start drags automatically
+ \value Drag.Internal (default) start backwards compatible drags automatically
When using \c Drag.Automatic you should also define \l mimeData and bind the
\l active property to the active property of MouseArea : \l {MouseArea::drag.active}
@@ -628,7 +580,7 @@ void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
property for the started sequence.
*/
-void QQuickDragAttached::start(QQmlV4Function *args)
+void QQuickDragAttached::start(QQmlV4FunctionPtr args)
{
Q_D(QQuickDragAttached);
if (d->inEvent) {
@@ -664,12 +616,10 @@ void QQuickDragAttached::start(QQmlV4Function *args)
The returned drop action may be one of:
- \list
- \li Qt.CopyAction Copy the data to the target
- \li Qt.MoveAction Move the data from the source to the target
- \li Qt.LinkAction Create a link from the source to the target.
- \li Qt.IgnoreAction Ignore the action (do nothing with the data).
- \endlist
+ \value Qt.CopyAction Copy the data to the target
+ \value Qt.MoveAction Move the data from the source to the target
+ \value Qt.LinkAction Create a link from the source to the target.
+ \value Qt.IgnoreAction Ignore the action (do nothing with the data).
*/
int QQuickDragAttached::drop()
@@ -760,20 +710,118 @@ void QQuickDragAttached::cancel()
\sa drop()
*/
+QMimeData *QQuickDragAttachedPrivate::createMimeData() const
+{
+ Q_Q(const QQuickDragAttached);
+ QMimeData *mimeData = new QMimeData();
+
+ for (const auto [mimeType, value] : externalMimeData.asKeyValueRange()) {
+ switch (value.typeId()) {
+ case QMetaType::QByteArray:
+ // byte array assumed to already be correctly encoded
+ mimeData->setData(mimeType, value.toByteArray());
+ break;
+ case QMetaType::QString: {
+ const QString text = value.toString();
+ if (mimeType == u"text/plain"_s) {
+ mimeData->setText(text);
+ } else if (mimeType == u"text/html"_s) {
+ mimeData->setHtml(text);
+ } else if (mimeType == u"text/uri-list"_s) {
+ const QUrl url(text);
+ if (url.isValid())
+ mimeData->setUrls({url});
+ else
+ qmlWarning(q) << text << " is not a valid URI";
+ } else if (mimeType.startsWith(u"text/"_s)) {
+ if (qsizetype charsetIdx = mimeType.lastIndexOf(u";charset="_s); charsetIdx != -1) {
+ charsetIdx += sizeof(";charset=") - 1;
+ const QByteArray encoding = mimeType.mid(charsetIdx).toUtf8();
+ QStringEncoder encoder(encoding);
+ if (encoder.isValid())
+ mimeData->setData(mimeType, encoder.encode(text));
+ else
+ qmlWarning(q) << "Don't know how to encode text as " << mimeType;
+ } else {
+ mimeData->setData(mimeType, text.toUtf8());
+ }
+ } else {
+ mimeData->setData(mimeType, text.toUtf8());
+ }
+ break;
+ }
+ case QMetaType::QVariantList:
+ case QMetaType::QStringList:
+ if (mimeType == u"text/uri-list"_s) {
+ const QVariantList values = value.toList();
+ QList<QUrl> urls;
+ urls.reserve(values.size());
+ bool error = false;
+ for (qsizetype index = 0; index < values.size(); ++index) {
+ const QUrl url = values.at(index).value<QUrl>();
+ if (url.isValid()) {
+ urls += url;
+ } else {
+ error = true;
+ qmlWarning(q) << "Value '" << values.at(index) << "' at index " << index
+ << " is not a valid URI";
+ }
+ }
+ if (!error)
+ mimeData->setUrls(urls);
+ }
+ break;
+ case QMetaType::QImage:
+ if (const QByteArray mimeTypeUtf8 = mimeType.toUtf8();
+ QImageWriter::supportedMimeTypes().contains(mimeTypeUtf8)) {
+ const auto imageFormats = QImageWriter::imageFormatsForMimeType(mimeTypeUtf8);
+ if (imageFormats.isEmpty()) { // shouldn't happen, but we can fall back
+ mimeData->setImageData(value);
+ break;
+ }
+ const QImage image = value.value<QImage>();
+ QByteArray bytes;
+ {
+ QBuffer buffer(&bytes);
+ QImageWriter encoder(&buffer, imageFormats.first());
+ encoder.write(image);
+ }
+ mimeData->setData(mimeType, bytes);
+ break;
+ }
+ Q_FALLTHROUGH();
+ default:
+ qmlWarning(q) << "Don't know how to encode variant of type " << value.metaType()
+ << " as mime type " << mimeType;
+ // compatibility with pre-6.5 - probably a bad idea
+ mimeData->setData(mimeType, value.toString().toUtf8());
+ break;
+ }
+ }
+
+ return mimeData;
+}
+
+void QQuickDragAttachedPrivate::loadPixmap()
+{
+ Q_Q(QQuickDragAttached);
+
+ QUrl loadUrl = imageSource;
+ const QQmlContext *context = qmlContext(q->parent());
+ if (context)
+ loadUrl = context->resolvedUrl(imageSource);
+ pixmapLoader.load(context ? context->engine() : nullptr, loadUrl, QRect(), q->imageSourceSize());
+}
+
Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedActions)
{
Q_Q(QQuickDragAttached);
QDrag *drag = new QDrag(source ? source : q);
- QMimeData *mimeData = new QMimeData();
- for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it)
- mimeData->setData(it.key(), it.value().toString().toUtf8());
-
- drag->setMimeData(mimeData);
- if (pixmapLoader.isReady()) {
+ drag->setMimeData(createMimeData());
+ if (pixmapLoader.isReady())
drag->setPixmap(QPixmap::fromImage(pixmapLoader.image()));
- }
drag->setHotSpot(hotSpot.toPoint());
emit q->dragStarted();
@@ -808,7 +856,7 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct
property for the started sequence.
*/
-void QQuickDragAttached::startDrag(QQmlV4Function *args)
+void QQuickDragAttached::startDrag(QQmlV4FunctionPtr args)
{
Q_D(QQuickDragAttached);