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)
69 , dragRestarted(false)
72 , overrideActions(false)
76 void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &);
77 void itemParentChanged(QQuickItem *, QQuickItem *parent);
78 void updatePosition();
80 void deliverEnterEvent();
81 void deliverMoveEvent();
82 void deliverLeaveEvent();
83 void deliverEvent(QQuickCanvas *canvas, QEvent *event);
84 void start() { start(supportedActions); }
85 void start(Qt::DropActions supportedActions);
86 void setTarget(QQuickItem *item);
88 QQuickDragGrabber dragGrabber;
90 QQmlGuard<QObject> source;
91 QQmlGuard<QObject> target;
92 QQmlGuard<QQuickCanvas> canvas;
93 QQuickItem *attachedItem;
94 QQuickDragMimeData *mimeData;
95 Qt::DropAction proposedAction;
96 Qt::DropActions supportedActions;
100 bool dragRestarted : 1;
102 bool eventQueued : 1;
103 bool overrideActions : 1;
109 \qmlclass Drag QQuickDrag
110 \inqmlmodule QtQuick 2
111 \ingroup qtquick-input
112 \brief For specifying drag and drop events for moved Items
114 Using the Drag attached property any Item can made a source of drag and drop
115 events within a scene.
117 When a drag is \l active on an item any change in that items position will
118 generate a drag events that will be sent to any DropArea that intersects
119 the with new position of the item. Other items which implement drag and
120 drop event handlers can also receive these events.
122 The following snippet shows how an item can be dragged with a MouseArea.
123 However, dragging is not limited to mouse drags, anything that can move an item
124 can generate drag events, this can include touch events, animations and bindings.
126 \snippet qml/drag.qml 0
128 A drag can be terminated either by canceling it with Drag.cancel() or setting
129 Drag.active to false, or it can be terminated with a drop event by calling
130 Drag.drop(). If the drop event is accepted Drag.drop() will return the
131 \l {supportedActions}{drop action} chosen by the recipient of the event,
132 otherwise it will return Qt.IgnoreAction.
136 void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
138 if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
143 void QQuickDragAttachedPrivate::itemParentChanged(QQuickItem *, QQuickItem *)
145 if (!active || dragRestarted)
148 QQuickCanvas *newCanvas = attachedItem->canvas();
150 if (canvas != newCanvas)
156 void QQuickDragAttachedPrivate::updatePosition()
158 Q_Q(QQuickDragAttached);
162 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
166 void QQuickDragAttachedPrivate::restartDrag()
168 Q_Q(QQuickDragAttached);
169 dragRestarted = true;
172 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
176 void QQuickDragAttachedPrivate::deliverEnterEvent()
178 dragRestarted = false;
181 canvas = attachedItem->canvas();
183 mimeData->m_source = source;
184 if (!overrideActions)
185 mimeData->m_supportedActions = supportedActions;
186 mimeData->m_keys = keys;
189 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
190 QDragEnterEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
191 QQuickDropEventEx::setProposedAction(&event, proposedAction);
192 deliverEvent(canvas, &event);
196 void QQuickDragAttachedPrivate::deliverMoveEvent()
198 Q_Q(QQuickDragAttached);
202 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
203 QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
204 QQuickDropEventEx::setProposedAction(&event, proposedAction);
205 deliverEvent(canvas, &event);
206 if (target != dragGrabber.target()) {
207 target = dragGrabber.target();
208 emit q->targetChanged();
213 void QQuickDragAttachedPrivate::deliverLeaveEvent()
216 QDragLeaveEvent event;
217 deliverEvent(canvas, &event);
222 void QQuickDragAttachedPrivate::deliverEvent(QQuickCanvas *canvas, QEvent *event)
226 QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, event);
230 bool QQuickDragAttached::event(QEvent *event)
232 Q_D(QQuickDragAttached);
234 if (event->type() == QEvent::User) {
235 d->eventQueued = false;
236 if (d->dragRestarted) {
237 d->deliverLeaveEvent();
238 d->deliverEnterEvent();
240 if (d->target != d->dragGrabber.target()) {
241 d->target = d->dragGrabber.target();
242 emit targetChanged();
244 } else if (d->itemMoved) {
245 d->deliverMoveEvent();
249 return QObject::event(event);
253 QQuickDragAttached::QQuickDragAttached(QObject *parent)
254 : QObject(*new QQuickDragAttachedPrivate, parent)
256 Q_D(QQuickDragAttached);
257 d->attachedItem = qobject_cast<QQuickItem *>(parent);
258 d->source = d->attachedItem;
261 QQuickDragAttached::~QQuickDragAttached()
263 Q_D(QQuickDragAttached);
268 \qmlattachedproperty bool QtQuick2::Drag::active
270 This property holds whether a drag event sequence is currently active.
272 Setting this property to true will send a QDragEnter event to the scene
273 with the item's current position. Setting it to false will send a
276 While a drag is active any change in an item's position will send a QDragMove
277 event with item's new position to the scene.
280 bool QQuickDragAttached::isActive() const
282 Q_D(const QQuickDragAttached);
286 void QQuickDragAttached::setActive(bool active)
288 Q_D(QQuickDragAttached);
289 if (d->active != active) {
291 qmlInfo(this) << "active cannot be changed from within a drag event handler";
293 d->start(d->supportedActions);
300 \qmlattachedproperty Object QtQuick2::Drag::source
302 This property holds an object that is identified to recipients of drag events as
303 the source of the events. By default this is the item Drag property is attached to.
305 Changing the source while a drag is active will reset the sequence of drag events by
306 sending a drag leave event followed by a drag enter event with the new source.
309 QObject *QQuickDragAttached::source() const
311 Q_D(const QQuickDragAttached);
315 void QQuickDragAttached::setSource(QObject *item)
317 Q_D(QQuickDragAttached);
318 if (d->source != item) {
322 emit sourceChanged();
326 void QQuickDragAttached::resetSource()
328 Q_D(QQuickDragAttached);
329 if (d->source != d->attachedItem) {
330 d->source = d->attachedItem;
333 emit sourceChanged();
338 \qmlattachedproperty Object QtQuick2::Drag::target
340 While a drag is active this property holds the last object to accept an
341 enter event from the dragged item, if the current drag position doesn't
342 intersect any accepting targets it is null.
344 When a drag is not active this property holds the object that accepted
345 the drop event that ended the drag, if no object accepted the drop or
346 the drag was canceled the target will then be null.
349 QObject *QQuickDragAttached::target() const
351 Q_D(const QQuickDragAttached);
356 \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot
358 This property holds the drag position relative to the top left of the item.
360 By default this is (0, 0).
362 Changes to hotSpot trigger a new drag move with the updated position.
365 QPointF QQuickDragAttached::hotSpot() const
367 Q_D(const QQuickDragAttached);
371 void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
373 Q_D(QQuickDragAttached);
374 if (d->hotSpot != hotSpot) {
375 d->hotSpot = hotSpot;
380 emit hotSpotChanged();
385 \qmlattachedproperty stringlist QtQuick2::Drag::keys
387 This property holds a list of keys that can be used by a DropArea to filter drag events.
389 Changing the keys while a drag is active will reset the sequence of drag events by
390 sending a drag leave event followed by a drag enter event with the new source.
393 QStringList QQuickDragAttached::keys() const
395 Q_D(const QQuickDragAttached);
399 void QQuickDragAttached::setKeys(const QStringList &keys)
401 Q_D(QQuickDragAttached);
402 if (d->keys != keys) {
411 \qmlattachedproperty flags QtQuick2::Drag::supportedActions
413 This property holds return values of Drag.drop() supported by the drag source.
415 Changing the supportedActions while a drag is active will reset the sequence of drag
416 events by sending a drag leave event followed by a drag enter event with the new source.
419 Qt::DropActions QQuickDragAttached::supportedActions() const
421 Q_D(const QQuickDragAttached);
422 return d->supportedActions;
425 void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
427 Q_D(QQuickDragAttached);
428 if (d->supportedActions != actions) {
429 d->supportedActions = actions;
432 emit supportedActionsChanged();
437 \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction
439 This property holds an action that is recommended by the drag source as a
440 return value from Drag.drop().
442 Changes to proposedAction will trigger a move event with the updated proposal.
445 Qt::DropAction QQuickDragAttached::proposedAction() const
447 Q_D(const QQuickDragAttached);
448 return d->proposedAction;
451 void QQuickDragAttached::setProposedAction(Qt::DropAction action)
453 Q_D(QQuickDragAttached);
454 if (d->proposedAction != action) {
455 d->proposedAction = action;
456 // The proposed action shouldn't affect whether a drag is accepted
457 // so leave/enter events are excessive, but the target should still
461 emit proposedActionChanged();
465 void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
467 Q_Q(QQuickDragAttached);
471 mimeData = new QQuickDragMimeData;
473 QQuickItemPrivate::get(attachedItem)->addItemChangeListener(
474 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent);
478 mimeData->m_supportedActions = supportedActions;
481 dragRestarted = false;
485 if (target != dragGrabber.target()) {
486 target = dragGrabber.target();
487 emit q->targetChanged();
490 emit q->activeChanged();
494 \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions)
496 Starts sending drag events.
498 The optional \a supportedActions argument can be used to override the \l supportedActions
499 property for the started sequence.
502 void QQuickDragAttached::start(QQmlV8Function *args)
504 Q_D(QQuickDragAttached);
506 qmlInfo(this) << "start() cannot be called from within a drag event handler";
513 d->overrideActions = false;
514 Qt::DropActions supportedActions = d->supportedActions;
515 // check arguments for supportedActions, maybe data?
516 if (args->Length() >= 1) {
517 v8::Local<v8::Value> v = (*args)[0];
519 supportedActions = Qt::DropActions(v->Int32Value());
520 d->overrideActions = true;
524 d->start(supportedActions);
528 \qmlattachedmethod enum QtQuick2::Drag::drop()
530 Ends a drag sequence by sending a drop event to the target item.
532 Returns the action accepted by the target item. If the target item or a parent doesn't accept
533 the drop event then Qt.IgnoreAction will be returned.
535 The returned drop action may be one of:
538 \li Qt.CopyAction Copy the data to the target
539 \li Qt.MoveAction Move the data from the source to the target
540 \li Qt.LinkAction Create a link from the source to the target.
541 \li Qt.IgnoreAction Ignore the action (do nothing with the data).
546 int QQuickDragAttached::drop()
548 Q_D(QQuickDragAttached);
549 Qt::DropAction acceptedAction = Qt::IgnoreAction;
552 qmlInfo(this) << "drop() cannot be called from within a drag event handler";
553 return acceptedAction;
557 d->deliverMoveEvent();
560 return acceptedAction;
566 QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
569 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
570 QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
571 d->deliverEvent(d->canvas, &event);
573 if (event.isAccepted()) {
574 acceptedAction = event.dropAction();
575 target = d->dragGrabber.target();
579 if (d->target != target) {
581 emit targetChanged();
584 emit activeChanged();
585 return acceptedAction;
589 \qmlattachedmethod void QtQuick2::Drag::cancel()
591 Ends a drag sequence.
594 void QQuickDragAttached::cancel()
596 Q_D(QQuickDragAttached);
599 qmlInfo(this) << "cancel() cannot be called from within a drag event handler";
606 d->deliverLeaveEvent();
610 emit targetChanged();
613 emit activeChanged();