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>
52 #ifndef QT_NO_DRAGANDDROP
56 class QQuickDragAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
58 Q_DECLARE_PUBLIC(QQuickDragAttached)
60 static QQuickDragAttachedPrivate *get(QQuickDragAttached *attached) {
61 return static_cast<QQuickDragAttachedPrivate *>(QObjectPrivate::get(attached)); }
63 QQuickDragAttachedPrivate()
66 , proposedAction(Qt::MoveAction)
67 , supportedActions(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction)
71 , dragRestarted(false)
74 , overrideActions(false)
78 void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &);
79 void itemParentChanged(QQuickItem *, QQuickItem *parent);
80 void updatePosition();
82 void deliverEnterEvent();
83 void deliverMoveEvent();
84 void deliverLeaveEvent();
85 void deliverEvent(QQuickCanvas *canvas, QEvent *event);
86 void start() { start(supportedActions); }
87 void start(Qt::DropActions supportedActions);
88 void setTarget(QQuickItem *item);
90 QQuickDragGrabber dragGrabber;
92 QQmlGuard<QObject> source;
93 QQmlGuard<QObject> target;
94 QQmlGuard<QQuickCanvas> canvas;
95 QQuickItem *attachedItem;
96 QQuickDragMimeData *mimeData;
97 Qt::DropAction proposedAction;
98 Qt::DropActions supportedActions;
102 bool dragRestarted : 1;
104 bool eventQueued : 1;
105 bool overrideActions : 1;
111 \qmlclass Drag QQuickDrag
112 \inqmlmodule QtQuick 2
113 \ingroup qtquick-input
114 \brief For specifying drag and drop events for moved Items
116 Using the Drag attached property any Item can made a source of drag and drop
117 events within a scene.
119 When a drag is \l active on an item any change in that items position will
120 generate a drag events that will be sent to any DropArea that intersects
121 the with new position of the item. Other items which implement drag and
122 drop event handlers can also receive these events.
124 The following snippet shows how an item can be dragged with a MouseArea.
125 However, dragging is not limited to mouse drags, anything that can move an item
126 can generate drag events, this can include touch events, animations and bindings.
128 \snippet qml/drag.qml 0
130 A drag can be terminated either by canceling it with Drag.cancel() or setting
131 Drag.active to false, or it can be terminated with a drop event by calling
132 Drag.drop(). If the drop event is accepted Drag.drop() will return the
133 \l {supportedActions}{drop action} chosen by the recipient of the event,
134 otherwise it will return Qt.IgnoreAction.
138 void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
140 if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
145 void QQuickDragAttachedPrivate::itemParentChanged(QQuickItem *, QQuickItem *)
147 if (!active || dragRestarted)
150 QQuickCanvas *newCanvas = attachedItem->canvas();
152 if (canvas != newCanvas)
158 void QQuickDragAttachedPrivate::updatePosition()
160 Q_Q(QQuickDragAttached);
164 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
168 void QQuickDragAttachedPrivate::restartDrag()
170 Q_Q(QQuickDragAttached);
171 dragRestarted = true;
174 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
178 void QQuickDragAttachedPrivate::deliverEnterEvent()
180 dragRestarted = false;
183 canvas = attachedItem->canvas();
185 mimeData->m_source = source;
186 if (!overrideActions)
187 mimeData->m_supportedActions = supportedActions;
188 mimeData->m_keys = keys;
191 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
192 QDragEnterEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
193 QQuickDropEventEx::setProposedAction(&event, proposedAction);
194 deliverEvent(canvas, &event);
198 void QQuickDragAttachedPrivate::deliverMoveEvent()
200 Q_Q(QQuickDragAttached);
204 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
205 QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
206 QQuickDropEventEx::setProposedAction(&event, proposedAction);
207 deliverEvent(canvas, &event);
208 if (target != dragGrabber.target()) {
209 target = dragGrabber.target();
210 emit q->targetChanged();
215 void QQuickDragAttachedPrivate::deliverLeaveEvent()
218 QDragLeaveEvent event;
219 deliverEvent(canvas, &event);
224 void QQuickDragAttachedPrivate::deliverEvent(QQuickCanvas *canvas, QEvent *event)
228 QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, event);
232 bool QQuickDragAttached::event(QEvent *event)
234 Q_D(QQuickDragAttached);
236 if (event->type() == QEvent::User) {
237 d->eventQueued = false;
238 if (d->dragRestarted) {
239 d->deliverLeaveEvent();
240 d->deliverEnterEvent();
242 if (d->target != d->dragGrabber.target()) {
243 d->target = d->dragGrabber.target();
244 emit targetChanged();
246 } else if (d->itemMoved) {
247 d->deliverMoveEvent();
251 return QObject::event(event);
255 QQuickDragAttached::QQuickDragAttached(QObject *parent)
256 : QObject(*new QQuickDragAttachedPrivate, parent)
258 Q_D(QQuickDragAttached);
259 d->attachedItem = qobject_cast<QQuickItem *>(parent);
260 d->source = d->attachedItem;
263 QQuickDragAttached::~QQuickDragAttached()
265 Q_D(QQuickDragAttached);
270 \qmlattachedproperty bool QtQuick2::Drag::active
272 This property holds whether a drag event sequence is currently active.
274 Setting this property to true will send a QDragEnter event to the scene
275 with the item's current position. Setting it to false will send a
278 While a drag is active any change in an item's position will send a QDragMove
279 event with item's new position to the scene.
282 bool QQuickDragAttached::isActive() const
284 Q_D(const QQuickDragAttached);
288 void QQuickDragAttached::setActive(bool active)
290 Q_D(QQuickDragAttached);
291 if (d->active != active) {
293 qmlInfo(this) << "active cannot be changed from within a drag event handler";
295 d->start(d->supportedActions);
302 \qmlattachedproperty Object QtQuick2::Drag::source
304 This property holds an object that is identified to recipients of drag events as
305 the source of the events. By default this is the item Drag property is attached to.
307 Changing the source while a drag is active will reset the sequence of drag events by
308 sending a drag leave event followed by a drag enter event with the new source.
311 QObject *QQuickDragAttached::source() const
313 Q_D(const QQuickDragAttached);
317 void QQuickDragAttached::setSource(QObject *item)
319 Q_D(QQuickDragAttached);
320 if (d->source != item) {
324 emit sourceChanged();
328 void QQuickDragAttached::resetSource()
330 Q_D(QQuickDragAttached);
331 if (d->source != d->attachedItem) {
332 d->source = d->attachedItem;
335 emit sourceChanged();
340 \qmlattachedproperty Object QtQuick2::Drag::target
342 While a drag is active this property holds the last object to accept an
343 enter event from the dragged item, if the current drag position doesn't
344 intersect any accepting targets it is null.
346 When a drag is not active this property holds the object that accepted
347 the drop event that ended the drag, if no object accepted the drop or
348 the drag was canceled the target will then be null.
351 QObject *QQuickDragAttached::target() const
353 Q_D(const QQuickDragAttached);
358 \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot
360 This property holds the drag position relative to the top left of the item.
362 By default this is (0, 0).
364 Changes to hotSpot trigger a new drag move with the updated position.
367 QPointF QQuickDragAttached::hotSpot() const
369 Q_D(const QQuickDragAttached);
373 void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
375 Q_D(QQuickDragAttached);
376 if (d->hotSpot != hotSpot) {
377 d->hotSpot = hotSpot;
382 emit hotSpotChanged();
387 \qmlattachedproperty stringlist QtQuick2::Drag::keys
389 This property holds a list of keys that can be used by a DropArea to filter drag events.
391 Changing the keys while a drag is active will reset the sequence of drag events by
392 sending a drag leave event followed by a drag enter event with the new source.
395 QStringList QQuickDragAttached::keys() const
397 Q_D(const QQuickDragAttached);
401 void QQuickDragAttached::setKeys(const QStringList &keys)
403 Q_D(QQuickDragAttached);
404 if (d->keys != keys) {
413 \qmlattachedproperty flags QtQuick2::Drag::supportedActions
415 This property holds return values of Drag.drop() supported by the drag source.
417 Changing the supportedActions while a drag is active will reset the sequence of drag
418 events by sending a drag leave event followed by a drag enter event with the new source.
421 Qt::DropActions QQuickDragAttached::supportedActions() const
423 Q_D(const QQuickDragAttached);
424 return d->supportedActions;
427 void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
429 Q_D(QQuickDragAttached);
430 if (d->supportedActions != actions) {
431 d->supportedActions = actions;
434 emit supportedActionsChanged();
439 \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction
441 This property holds an action that is recommended by the drag source as a
442 return value from Drag.drop().
444 Changes to proposedAction will trigger a move event with the updated proposal.
447 Qt::DropAction QQuickDragAttached::proposedAction() const
449 Q_D(const QQuickDragAttached);
450 return d->proposedAction;
453 void QQuickDragAttached::setProposedAction(Qt::DropAction action)
455 Q_D(QQuickDragAttached);
456 if (d->proposedAction != action) {
457 d->proposedAction = action;
458 // The proposed action shouldn't affect whether a drag is accepted
459 // so leave/enter events are excessive, but the target should still
463 emit proposedActionChanged();
467 void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
469 Q_Q(QQuickDragAttached);
473 mimeData = new QQuickDragMimeData;
475 QQuickItemPrivate::get(attachedItem)->addItemChangeListener(
476 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent);
480 mimeData->m_supportedActions = supportedActions;
483 dragRestarted = false;
487 if (target != dragGrabber.target()) {
488 target = dragGrabber.target();
489 emit q->targetChanged();
492 emit q->activeChanged();
496 \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions)
498 Starts sending drag events.
500 The optional \a supportedActions argument can be used to override the \l supportedActions
501 property for the started sequence.
504 void QQuickDragAttached::start(QQmlV8Function *args)
506 Q_D(QQuickDragAttached);
508 qmlInfo(this) << "start() cannot be called from within a drag event handler";
515 d->overrideActions = false;
516 Qt::DropActions supportedActions = d->supportedActions;
517 // check arguments for supportedActions, maybe data?
518 if (args->Length() >= 1) {
519 v8::Local<v8::Value> v = (*args)[0];
521 supportedActions = Qt::DropActions(v->Int32Value());
522 d->overrideActions = true;
526 d->start(supportedActions);
530 \qmlattachedmethod enum QtQuick2::Drag::drop()
532 Ends a drag sequence by sending a drop event to the target item.
534 Returns the action accepted by the target item. If the target item or a parent doesn't accept
535 the drop event then Qt.IgnoreAction will be returned.
537 The returned drop action may be one of:
540 \li Qt.CopyAction Copy the data to the target
541 \li Qt.MoveAction Move the data from the source to the target
542 \li Qt.LinkAction Create a link from the source to the target.
543 \li Qt.IgnoreAction Ignore the action (do nothing with the data).
548 int QQuickDragAttached::drop()
550 Q_D(QQuickDragAttached);
551 Qt::DropAction acceptedAction = Qt::IgnoreAction;
554 qmlInfo(this) << "drop() cannot be called from within a drag event handler";
555 return acceptedAction;
559 d->deliverMoveEvent();
562 return acceptedAction;
568 QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
571 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
572 QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
573 d->deliverEvent(d->canvas, &event);
575 if (event.isAccepted()) {
576 acceptedAction = event.dropAction();
577 target = d->dragGrabber.target();
581 if (d->target != target) {
583 emit targetChanged();
586 emit activeChanged();
587 return acceptedAction;
591 \qmlattachedmethod void QtQuick2::Drag::cancel()
593 Ends a drag sequence.
596 void QQuickDragAttached::cancel()
598 Q_D(QQuickDragAttached);
601 qmlInfo(this) << "cancel() cannot be called from within a drag event handler";
608 d->deliverLeaveEvent();
612 emit targetChanged();
615 emit activeChanged();
620 #endif // QT_NO_DRAGANDDROP