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(QQuickWindow *window, 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<QQuickWindow> window;
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;
112 \instantiates QQuickDrag
113 \inqmlmodule QtQuick 2
114 \ingroup qtquick-input
115 \brief For specifying drag and drop events for moved Items
117 Using the Drag attached property any Item can made a source of drag and drop
118 events within a scene.
120 When a drag is \l active on an item any change in that items position will
121 generate a drag events that will be sent to any DropArea that intersects
122 the with new position of the item. Other items which implement drag and
123 drop event handlers can also receive these events.
125 The following snippet shows how an item can be dragged with a MouseArea.
126 However, dragging is not limited to mouse drags, anything that can move an item
127 can generate drag events, this can include touch events, animations and bindings.
129 \snippet qml/drag.qml 0
131 A drag can be terminated either by canceling it with Drag.cancel() or setting
132 Drag.active to false, or it can be terminated with a drop event by calling
133 Drag.drop(). If the drop event is accepted Drag.drop() will return the
134 \l {supportedActions}{drop action} chosen by the recipient of the event,
135 otherwise it will return Qt.IgnoreAction.
139 void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
141 if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
146 void QQuickDragAttachedPrivate::itemParentChanged(QQuickItem *, QQuickItem *)
148 if (!active || dragRestarted)
151 QQuickWindow *newWindow = attachedItem->window();
153 if (window != newWindow)
159 void QQuickDragAttachedPrivate::updatePosition()
161 Q_Q(QQuickDragAttached);
165 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
169 void QQuickDragAttachedPrivate::restartDrag()
171 Q_Q(QQuickDragAttached);
172 dragRestarted = true;
175 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
179 void QQuickDragAttachedPrivate::deliverEnterEvent()
181 dragRestarted = false;
184 window = attachedItem->window();
186 mimeData->m_source = source;
187 if (!overrideActions)
188 mimeData->m_supportedActions = supportedActions;
189 mimeData->m_keys = keys;
192 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
193 QDragEnterEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
194 QQuickDropEventEx::setProposedAction(&event, proposedAction);
195 deliverEvent(window, &event);
199 void QQuickDragAttachedPrivate::deliverMoveEvent()
201 Q_Q(QQuickDragAttached);
205 QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
206 QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
207 QQuickDropEventEx::setProposedAction(&event, proposedAction);
208 deliverEvent(window, &event);
209 if (target != dragGrabber.target()) {
210 target = dragGrabber.target();
211 emit q->targetChanged();
216 void QQuickDragAttachedPrivate::deliverLeaveEvent()
219 QDragLeaveEvent event;
220 deliverEvent(window, &event);
225 void QQuickDragAttachedPrivate::deliverEvent(QQuickWindow *window, QEvent *event)
229 QQuickWindowPrivate::get(window)->deliverDragEvent(&dragGrabber, event);
233 bool QQuickDragAttached::event(QEvent *event)
235 Q_D(QQuickDragAttached);
237 if (event->type() == QEvent::User) {
238 d->eventQueued = false;
239 if (d->dragRestarted) {
240 d->deliverLeaveEvent();
241 d->deliverEnterEvent();
243 if (d->target != d->dragGrabber.target()) {
244 d->target = d->dragGrabber.target();
245 emit targetChanged();
247 } else if (d->itemMoved) {
248 d->deliverMoveEvent();
252 return QObject::event(event);
256 QQuickDragAttached::QQuickDragAttached(QObject *parent)
257 : QObject(*new QQuickDragAttachedPrivate, parent)
259 Q_D(QQuickDragAttached);
260 d->attachedItem = qobject_cast<QQuickItem *>(parent);
261 d->source = d->attachedItem;
264 QQuickDragAttached::~QQuickDragAttached()
266 Q_D(QQuickDragAttached);
271 \qmlattachedproperty bool QtQuick2::Drag::active
273 This property holds whether a drag event sequence is currently active.
275 Setting this property to true will send a QDragEnter event to the scene
276 with the item's current position. Setting it to false will send a
279 While a drag is active any change in an item's position will send a QDragMove
280 event with item's new position to the scene.
283 bool QQuickDragAttached::isActive() const
285 Q_D(const QQuickDragAttached);
289 void QQuickDragAttached::setActive(bool active)
291 Q_D(QQuickDragAttached);
292 if (d->active != active) {
294 qmlInfo(this) << "active cannot be changed from within a drag event handler";
296 d->start(d->supportedActions);
303 \qmlattachedproperty Object QtQuick2::Drag::source
305 This property holds an object that is identified to recipients of drag events as
306 the source of the events. By default this is the item Drag property is attached to.
308 Changing the source while a drag is active will reset the sequence of drag events by
309 sending a drag leave event followed by a drag enter event with the new source.
312 QObject *QQuickDragAttached::source() const
314 Q_D(const QQuickDragAttached);
318 void QQuickDragAttached::setSource(QObject *item)
320 Q_D(QQuickDragAttached);
321 if (d->source != item) {
325 emit sourceChanged();
329 void QQuickDragAttached::resetSource()
331 Q_D(QQuickDragAttached);
332 if (d->source != d->attachedItem) {
333 d->source = d->attachedItem;
336 emit sourceChanged();
341 \qmlattachedproperty Object QtQuick2::Drag::target
343 While a drag is active this property holds the last object to accept an
344 enter event from the dragged item, if the current drag position doesn't
345 intersect any accepting targets it is null.
347 When a drag is not active this property holds the object that accepted
348 the drop event that ended the drag, if no object accepted the drop or
349 the drag was canceled the target will then be null.
352 QObject *QQuickDragAttached::target() const
354 Q_D(const QQuickDragAttached);
359 \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot
361 This property holds the drag position relative to the top left of the item.
363 By default this is (0, 0).
365 Changes to hotSpot trigger a new drag move with the updated position.
368 QPointF QQuickDragAttached::hotSpot() const
370 Q_D(const QQuickDragAttached);
374 void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
376 Q_D(QQuickDragAttached);
377 if (d->hotSpot != hotSpot) {
378 d->hotSpot = hotSpot;
383 emit hotSpotChanged();
388 \qmlattachedproperty stringlist QtQuick2::Drag::keys
390 This property holds a list of keys that can be used by a DropArea to filter drag events.
392 Changing the keys while a drag is active will reset the sequence of drag events by
393 sending a drag leave event followed by a drag enter event with the new source.
396 QStringList QQuickDragAttached::keys() const
398 Q_D(const QQuickDragAttached);
402 void QQuickDragAttached::setKeys(const QStringList &keys)
404 Q_D(QQuickDragAttached);
405 if (d->keys != keys) {
414 \qmlattachedproperty flags QtQuick2::Drag::supportedActions
416 This property holds return values of Drag.drop() supported by the drag source.
418 Changing the supportedActions while a drag is active will reset the sequence of drag
419 events by sending a drag leave event followed by a drag enter event with the new source.
422 Qt::DropActions QQuickDragAttached::supportedActions() const
424 Q_D(const QQuickDragAttached);
425 return d->supportedActions;
428 void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
430 Q_D(QQuickDragAttached);
431 if (d->supportedActions != actions) {
432 d->supportedActions = actions;
435 emit supportedActionsChanged();
440 \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction
442 This property holds an action that is recommended by the drag source as a
443 return value from Drag.drop().
445 Changes to proposedAction will trigger a move event with the updated proposal.
448 Qt::DropAction QQuickDragAttached::proposedAction() const
450 Q_D(const QQuickDragAttached);
451 return d->proposedAction;
454 void QQuickDragAttached::setProposedAction(Qt::DropAction action)
456 Q_D(QQuickDragAttached);
457 if (d->proposedAction != action) {
458 d->proposedAction = action;
459 // The proposed action shouldn't affect whether a drag is accepted
460 // so leave/enter events are excessive, but the target should still
464 emit proposedActionChanged();
468 void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
470 Q_Q(QQuickDragAttached);
474 mimeData = new QQuickDragMimeData;
476 QQuickItemPrivate::get(attachedItem)->addItemChangeListener(
477 this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent);
481 mimeData->m_supportedActions = supportedActions;
484 dragRestarted = false;
488 if (target != dragGrabber.target()) {
489 target = dragGrabber.target();
490 emit q->targetChanged();
493 emit q->activeChanged();
497 \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions)
499 Starts sending drag events.
501 The optional \a supportedActions argument can be used to override the \l supportedActions
502 property for the started sequence.
505 void QQuickDragAttached::start(QQmlV8Function *args)
507 Q_D(QQuickDragAttached);
509 qmlInfo(this) << "start() cannot be called from within a drag event handler";
516 d->overrideActions = false;
517 Qt::DropActions supportedActions = d->supportedActions;
518 // check arguments for supportedActions, maybe data?
519 if (args->Length() >= 1) {
520 v8::Local<v8::Value> v = (*args)[0];
522 supportedActions = Qt::DropActions(v->Int32Value());
523 d->overrideActions = true;
527 d->start(supportedActions);
531 \qmlattachedmethod enumeration QtQuick2::Drag::drop()
533 Ends a drag sequence by sending a drop event to the target item.
535 Returns the action accepted by the target item. If the target item or a parent doesn't accept
536 the drop event then Qt.IgnoreAction will be returned.
538 The returned drop action may be one of:
541 \li Qt.CopyAction Copy the data to the target
542 \li Qt.MoveAction Move the data from the source to the target
543 \li Qt.LinkAction Create a link from the source to the target.
544 \li Qt.IgnoreAction Ignore the action (do nothing with the data).
549 int QQuickDragAttached::drop()
551 Q_D(QQuickDragAttached);
552 Qt::DropAction acceptedAction = Qt::IgnoreAction;
555 qmlInfo(this) << "drop() cannot be called from within a drag event handler";
556 return acceptedAction;
560 d->deliverMoveEvent();
563 return acceptedAction;
569 QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
572 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
573 QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
574 d->deliverEvent(d->window, &event);
576 if (event.isAccepted()) {
577 acceptedAction = event.dropAction();
578 target = d->dragGrabber.target();
582 if (d->target != target) {
584 emit targetChanged();
587 emit activeChanged();
588 return acceptedAction;
592 \qmlattachedmethod void QtQuick2::Drag::cancel()
594 Ends a drag sequence.
597 void QQuickDragAttached::cancel()
599 Q_D(QQuickDragAttached);
602 qmlInfo(this) << "cancel() cannot be called from within a drag event handler";
609 d->deliverLeaveEvent();
613 emit targetChanged();
616 emit activeChanged();
621 #endif // QT_NO_DRAGANDDROP