1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickdrag_p.h"
44 #include <private/qquickitem_p.h>
45 #include <QtQuick/private/qquickevents_p_p.h>
46 #include <private/qquickitemchangelistener_p.h>
47 #include <private/qv8engine_p.h>
48 #include <QtCore/qcoreapplication.h>
49 #include <QtQml/qqmlinfo.h>
50 #include <QtGui/qevent.h>
54 class QQuickDragAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
56 Q_DECLARE_PUBLIC(QQuickDragAttached)
58 static QQuickDragAttachedPrivate *get(QQuickDragAttached *attached) {
59 return static_cast<QQuickDragAttachedPrivate *>(QObjectPrivate::get(attached)); }
61 QQuickDragAttachedPrivate()
64 , proposedAction(Qt::MoveAction)
65 , supportedActions(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction)
74 void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &);
75 void deliverMoveEvent();
76 void deliverEvent(QQuickCanvas *canvas, QEvent *event);
77 void start() { start(supportedActions); }
78 void start(Qt::DropActions supportedActions);
79 void setTarget(QQuickItem *item);
81 QQuickDragGrabber dragGrabber;
83 QQmlGuard<QObject> source;
84 QQmlGuard<QObject> target;
85 QQuickItem *attachedItem;
86 QQuickDragMimeData *mimeData;
87 Qt::DropAction proposedAction;
88 Qt::DropActions supportedActions;
99 \qmlclass Drag QQuickDrag
100 \inqmlmodule QtQuick 2
101 \ingroup qtquick-interaction
102 \brief For specifying drag and drop events for moved Items
104 Using the Drag attached property any Item can made a source of drag and drop
105 events within a scene.
107 When a drag is \l active on an item any change in that items position will
108 generate a drag events that will be sent to any DropArea that intersects
109 the with new position of the item. Other items which implement drag and
110 drop event handlers can also receive these events.
112 The following snippet shows how an item can be dragged with a MouseArea.
113 However, dragging is not limited to mouse drags, anything that can move an item
114 can generate drag events, this can include touch events, animations and bindings.
116 \snippet qml/drag.qml 0
118 A drag can be terminated either by canceling it with Drag.cancel() or setting
119 Drag.active to false, or it can be terminated with a drop event by calling
120 Drag.drop(). If the drop event is accepted Drag.drop() will return the
121 \l {supportedActions}{drop action} chosen by the recipient of the event,
122 otherwise it will return Qt.IgnoreAction.
126 void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
128 Q_Q(QQuickDragAttached);
129 if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
136 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
140 void QQuickDragAttachedPrivate::deliverMoveEvent()
142 Q_Q(QQuickDragAttached);
145 if (QQuickCanvas *canvas = attachedItem->canvas()) {
146 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
147 QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
148 QQuickDropEventEx::setProposedAction(&event, proposedAction);
149 deliverEvent(canvas, &event);
150 if (target != dragGrabber.target()) {
151 target = dragGrabber.target();
152 emit q->targetChanged();
157 void QQuickDragAttachedPrivate::deliverEvent(QQuickCanvas *canvas, QEvent *event)
161 QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, event);
165 bool QQuickDragAttached::event(QEvent *event)
167 Q_D(QQuickDragAttached);
169 if (event->type() == QEvent::User) {
170 d->eventQueued = false;
172 d->deliverMoveEvent();
175 return QObject::event(event);
179 QQuickDragAttached::QQuickDragAttached(QObject *parent)
180 : QObject(*new QQuickDragAttachedPrivate, parent)
182 Q_D(QQuickDragAttached);
183 d->attachedItem = qobject_cast<QQuickItem *>(parent);
184 d->source = d->attachedItem;
187 QQuickDragAttached::~QQuickDragAttached()
189 Q_D(QQuickDragAttached);
194 \qmlattachedproperty bool QtQuick2::Drag::active
196 This property holds whether a drag event sequence is currently active.
198 Setting this property to true will send a QDragEnter event to the scene
199 with the item's current position. Setting it to false will send a
202 While a drag is active any change in an item's position will send a QDragMove
203 event with item's new position to the scene.
206 bool QQuickDragAttached::isActive() const
208 Q_D(const QQuickDragAttached);
212 void QQuickDragAttached::setActive(bool active)
214 Q_D(QQuickDragAttached);
215 if (d->active != active) {
217 qmlInfo(this) << "active cannot be changed from within a drag event handler";
219 d->start(d->supportedActions);
226 \qmlattachedproperty Object QtQuick2::Drag::source
228 This property holds an object that is identified to recipients of drag events as
229 the source of the events. By default this is the item Drag property is attached to.
231 Changes to source while a Drag is active don't take effect until a new drag is started.
234 QObject *QQuickDragAttached::source() const
236 Q_D(const QQuickDragAttached);
240 void QQuickDragAttached::setSource(QObject *item)
242 Q_D(QQuickDragAttached);
243 if (d->source != item) {
245 emit sourceChanged();
249 void QQuickDragAttached::resetSource()
251 Q_D(QQuickDragAttached);
252 if (d->source != d->attachedItem) {
253 d->source = d->attachedItem;
254 emit sourceChanged();
259 \qmlattachedproperty Object QtQuick2::Drag::target
261 While a drag is active this property holds the last object to accept an
262 enter event from the dragged item, if the current drag position doesn't
263 intersect any accepting targets it is null.
265 When a drag is not active this property holds the object that accepted
266 the drop event that ended the drag, if no object accepted the drop or
267 the drag was canceled the target will then be null.
270 QObject *QQuickDragAttached::target() const
272 Q_D(const QQuickDragAttached);
277 \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot
279 This property holds the drag position relative to the top left of the item.
281 By default this is (0, 0).
283 Changes to hotSpot will take effect when the next event is sent.
286 QPointF QQuickDragAttached::hotSpot() const
288 Q_D(const QQuickDragAttached);
292 void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
294 Q_D(QQuickDragAttached);
295 if (d->hotSpot != hotSpot) {
296 d->hotSpot = hotSpot;
301 if (!d->eventQueued) {
302 d->eventQueued = true;
303 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
307 emit hotSpotChanged();
312 \qmlattachedproperty stringlist QtQuick2::Drag::keys
314 This property holds a list of keys that can be used by a DropArea to filter drag events.
316 Changes to keys while a Drag is active don't take effect until a new drag is started.
319 QStringList QQuickDragAttached::keys() const
321 Q_D(const QQuickDragAttached);
325 void QQuickDragAttached::setKeys(const QStringList &keys)
327 Q_D(QQuickDragAttached);
328 if (d->keys != keys) {
335 \qmlattachedproperty flags QtQuick2::Drag::supportedActions
337 This property holds return values of Drag.drop() supported by the drag source.
339 Changes to supportedActions while a Drag is active don't take effect
340 until a new drag is started.
343 Qt::DropActions QQuickDragAttached::supportedActions() const
345 Q_D(const QQuickDragAttached);
346 return d->supportedActions;
349 void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
351 Q_D(QQuickDragAttached);
352 if (d->supportedActions != actions) {
353 d->supportedActions = actions;
354 emit supportedActionsChanged();
359 \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction
361 This property holds an action that is recommended by the drag source as a
362 return value from Drag.drop().
364 Changes to proposedAction will take effect when the next event is sent.
367 Qt::DropAction QQuickDragAttached::proposedAction() const
369 Q_D(const QQuickDragAttached);
370 return d->proposedAction;
373 void QQuickDragAttached::setProposedAction(Qt::DropAction action)
375 Q_D(QQuickDragAttached);
376 if (d->proposedAction != action) {
377 d->proposedAction = action;
378 emit proposedActionChanged();
379 // send a move event with the new default action if active?
383 void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
385 Q_Q(QQuickDragAttached);
388 if (QQuickCanvas *canvas = attachedItem ? attachedItem->canvas() : 0) {
390 mimeData = new QQuickDragMimeData;
392 QQuickItemPrivate::get(attachedItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
396 mimeData->m_source = source;
397 mimeData->m_supportedActions = supportedActions;
398 mimeData->m_keys = keys;
401 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
402 QDragEnterEvent event(scenePos, supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
403 QQuickDropEventEx::setProposedAction(&event, proposedAction);
404 deliverEvent(canvas, &event);
406 emit q->activeChanged();
407 if (target != dragGrabber.target()) {
408 target = dragGrabber.target();
409 emit q->targetChanged();
415 \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions)
417 Starts sending drag events.
419 The optional \a supportedActions argument can be used to override the \l supportedActions
420 property for the started sequence.
423 void QQuickDragAttached::start(QQmlV8Function *args)
425 Q_D(QQuickDragAttached);
427 qmlInfo(this) << "start() cannot be called from within a drag event handler";
434 Qt::DropActions supportedActions = d->supportedActions;
435 // check arguments for supportedActions, maybe data?
436 if (args->Length() >= 1) {
437 v8::Local<v8::Value> v = (*args)[0];
439 supportedActions = Qt::DropActions(v->Int32Value());
442 d->start(supportedActions);
446 \qmlattachedmethod enum QtQuick2::Drag::drop()
448 Ends a drag sequence by sending a drop event to the target item.
450 Returns the action accepted by the target item. If the target item or a parent doesn't accept
451 the drop event then Qt.IgnoreAction will be returned.
453 The returned drop action may be one of:
456 \li Qt.CopyAction Copy the data to the target
457 \li Qt.MoveAction Move the data from the source to the target
458 \li Qt.LinkAction Create a link from the source to the target.
459 \li Qt.IgnoreAction Ignore the action (do nothing with the data).
464 int QQuickDragAttached::drop()
466 Q_D(QQuickDragAttached);
467 Qt::DropAction acceptedAction = Qt::IgnoreAction;
470 qmlInfo(this) << "drop() cannot be called from within a drag event handler";
471 return acceptedAction;
475 d->deliverMoveEvent();
478 return acceptedAction;
483 if (QQuickCanvas *canvas = d->attachedItem->canvas()) {
484 QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
487 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
488 QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
489 d->deliverEvent(canvas, &event);
491 if (event.isAccepted()) {
492 acceptedAction = event.dropAction();
493 target = d->dragGrabber.target();
497 if (d->target != target) {
499 emit targetChanged();
502 emit activeChanged();
503 return acceptedAction;
507 \qmlattachedmethod void QtQuick2::Drag::cancel()
509 Ends a drag sequence.
512 void QQuickDragAttached::cancel()
514 Q_D(QQuickDragAttached);
517 qmlInfo(this) << "cancel() cannot be called from within a drag event handler";
524 d->itemMoved = false;
526 if (QQuickCanvas *canvas = d->attachedItem->canvas()) {
527 QDragLeaveEvent event;
528 d->deliverEvent(canvas, &event);
533 emit targetChanged();
535 emit activeChanged();