12c1a03b3eb21f6d9d1f5c0a4faa45d95569d55e
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickdrag / tst_qquickdrag.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtTest/QSignalSpy>
44 #include <QtQuick/qquickitem.h>
45 #include <QtQuick/qquickview.h>
46 #include <QtQml/qqmlcontext.h>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlexpression.h>
49
50 template <typename T> static T evaluate(QObject *scope, const QString &expression)
51 {
52     QQmlExpression expr(qmlContext(scope), scope, expression);
53     QVariant result = expr.evaluate();
54     if (expr.hasError())
55         qWarning() << expr.error().toString();
56     return result.value<T>();
57 }
58
59 template <> void evaluate<void>(QObject *scope, const QString &expression)
60 {
61     QQmlExpression expr(qmlContext(scope), scope, expression);
62     expr.evaluate();
63     if (expr.hasError())
64         qWarning() << expr.error().toString();
65 }
66
67 Q_DECLARE_METATYPE(Qt::DropActions)
68
69 class TestDropTarget : public QQuickItem
70 {
71     Q_OBJECT
72 public:
73     TestDropTarget(QQuickItem *parent = 0)
74         : QQuickItem(parent)
75         , enterEvents(0)
76         , moveEvents(0)
77         , leaveEvents(0)
78         , dropEvents(0)
79         , acceptAction(Qt::MoveAction)
80         , defaultAction(Qt::IgnoreAction)
81         , proposedAction(Qt::IgnoreAction)
82         , accept(true)
83     {
84         setFlags(ItemAcceptsDrops);
85     }
86
87     void reset()
88     {
89         enterEvents = 0;
90         moveEvents = 0;
91         leaveEvents = 0;
92         dropEvents = 0;
93         defaultAction = Qt::IgnoreAction;
94         proposedAction = Qt::IgnoreAction;
95         supportedActions = Qt::IgnoreAction;
96     }
97
98     void dragEnterEvent(QDragEnterEvent *event)
99     {
100         ++enterEvents;
101         position = event->pos();
102         defaultAction = event->dropAction();
103         proposedAction = event->proposedAction();
104         supportedActions = event->possibleActions();
105         event->setAccepted(accept);
106     }
107
108     void dragMoveEvent(QDragMoveEvent *event)
109     {
110         ++moveEvents;
111         position = event->pos();
112         defaultAction = event->dropAction();
113         proposedAction = event->proposedAction();
114         supportedActions = event->possibleActions();
115         event->setAccepted(accept);
116     }
117
118     void dragLeaveEvent(QDragLeaveEvent *event)
119     {
120         ++leaveEvents;
121         event->setAccepted(accept);
122     }
123
124     void dropEvent(QDropEvent *event)
125     {
126         ++dropEvents;
127         position = event->pos();
128         defaultAction = event->dropAction();
129         proposedAction = event->proposedAction();
130         supportedActions = event->possibleActions();
131         event->setDropAction(acceptAction);
132         event->setAccepted(accept);
133     }
134
135     int enterEvents;
136     int moveEvents;
137     int leaveEvents;
138     int dropEvents;
139     Qt::DropAction acceptAction;
140     Qt::DropAction defaultAction;
141     Qt::DropAction proposedAction;
142     Qt::DropActions supportedActions;
143     QPointF position;
144     bool accept;
145 };
146
147 class tst_QQuickDrag: public QObject
148 {
149     Q_OBJECT
150 private slots:
151     void initTestCase();
152     void cleanupTestCase();
153
154     void active();
155     void drop();
156     void move();
157     void parentChange();
158     void hotSpot();
159     void supportedActions();
160     void proposedAction();
161     void keys();
162     void source();
163     void recursion_data();
164     void recursion();
165
166 private:
167     QQmlEngine engine;
168 };
169
170 void tst_QQuickDrag::initTestCase()
171 {
172
173 }
174
175 void tst_QQuickDrag::cleanupTestCase()
176 {
177
178 }
179
180 void tst_QQuickDrag::active()
181 {
182     QQuickCanvas canvas;
183     TestDropTarget dropTarget(canvas.rootItem());
184     dropTarget.setSize(QSizeF(100, 100));
185     QQmlComponent component(&engine);
186     component.setData(
187             "import QtQuick 2.0\n"
188             "Item {\n"
189                 "property bool dragActive: Drag.active\n"
190                 "property Item dragTarget: Drag.target\n"
191                 "x: 50; y: 50\n"
192                 "width: 10; height: 10\n"
193             "}", QUrl());
194     QScopedPointer<QObject> object(component.create());
195     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
196     QVERIFY(item);
197     item->setParentItem(&dropTarget);
198
199     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
200     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
201
202     evaluate<void>(item, "Drag.active = true");
203     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
204     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
205     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&dropTarget));
206     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&dropTarget));
207     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0);
208
209     dropTarget.reset();
210     evaluate<void>(item, "Drag.active = false");
211     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
212     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
213     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
214     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
215     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 1);
216
217     dropTarget.reset();
218     evaluate<void>(item, "Drag.cancel()");
219     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
220     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
221     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
222     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
223     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0);
224
225     dropTarget.reset();
226     evaluate<void>(item, "Drag.start()");
227     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
228     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
229     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&dropTarget));
230     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&dropTarget));
231     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0);
232
233     // Start while a drag is active, cancels the previous drag and starts a new one.
234     dropTarget.reset();
235     evaluate<void>(item, "Drag.start()");
236     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
237     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
238     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&dropTarget));
239     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&dropTarget));
240     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 1);
241
242     dropTarget.reset();
243     evaluate<void>(item, "Drag.cancel()");
244     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
245     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
246     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
247     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
248     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 1);
249
250     // Enter events aren't sent to items without the QQuickItem::ItemAcceptsDrops flag.
251     dropTarget.setFlags(QQuickItem::Flags());
252
253     dropTarget.reset();
254     evaluate<void>(item, "Drag.active = true");
255     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
256     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
257     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
258     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
259     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0);
260
261     dropTarget.reset();
262     evaluate<void>(item, "Drag.active = false");
263     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
264     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
265     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
266     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
267     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0);
268
269     dropTarget.setFlags(QQuickItem::ItemAcceptsDrops);
270
271     dropTarget.reset();
272     evaluate<void>(item, "Drag.active = true");
273     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
274     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
275     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&dropTarget));
276     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&dropTarget));
277     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0);
278
279     dropTarget.setFlags(QQuickItem::Flags());
280
281     dropTarget.reset();
282     evaluate<void>(item, "Drag.active = false");
283     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
284     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
285     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
286     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
287     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 1);
288
289     // Follow up events aren't sent to items if the enter event isn't accepted.
290     dropTarget.setFlags(QQuickItem::ItemAcceptsDrops);
291     dropTarget.accept = false;
292
293     dropTarget.reset();
294     evaluate<void>(item, "Drag.active = true");
295     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
296     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
297     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
298     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
299     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0);
300
301     dropTarget.reset();
302     evaluate<void>(item, "Drag.active = false");
303     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
304     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
305     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
306     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
307     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0);
308
309     dropTarget.accept = true;
310
311     dropTarget.reset();
312     evaluate<void>(item, "Drag.active = true");
313     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
314     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
315     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&dropTarget));
316     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&dropTarget));
317     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0);
318
319     dropTarget.accept = false;
320
321     dropTarget.reset();
322     evaluate<void>(item, "Drag.active = false");
323     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
324     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
325     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
326     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
327     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 1);
328
329     // Events are sent to hidden or disabled items.
330     dropTarget.accept = true;
331     dropTarget.setVisible(false);
332     dropTarget.reset();
333     evaluate<void>(item, "Drag.active = true");
334     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
335     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
336     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
337     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
338     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0);
339
340     evaluate<void>(item, "Drag.active = false");
341     dropTarget.setVisible(true);
342
343     dropTarget.setOpacity(0.0);
344     dropTarget.reset();
345     evaluate<void>(item, "Drag.active = true");
346     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
347     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
348     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&dropTarget));
349     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&dropTarget));
350     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0);
351
352     evaluate<void>(item, "Drag.active = false");
353     dropTarget.setOpacity(1.0);
354
355     dropTarget.setEnabled(false);
356     dropTarget.reset();
357     evaluate<void>(item, "Drag.active = true");
358     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
359     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
360     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
361     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
362     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0);
363
364     evaluate<void>(item, "Drag.active = false");
365     dropTarget.setEnabled(true);
366     dropTarget.reset();
367
368     // Queued move events are discarded if the drag is cancelled.
369     evaluate<void>(item, "Drag.active = true");
370     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
371     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
372     QCOMPARE(dropTarget.enterEvents, 1); QCOMPARE(dropTarget.leaveEvents, 0); QCOMPARE(dropTarget.moveEvents, 0);
373
374     dropTarget.reset();
375     item->setPos(QPointF(80, 80));
376     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
377     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
378     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0); QCOMPARE(dropTarget.moveEvents, 0);
379
380     evaluate<void>(item, "Drag.active = false");
381     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
382     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
383     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 1); QCOMPARE(dropTarget.moveEvents, 0);
384
385     dropTarget.reset();
386     QCoreApplication::processEvents();
387     QCOMPARE(dropTarget.enterEvents, 0); QCOMPARE(dropTarget.leaveEvents, 0); QCOMPARE(dropTarget.moveEvents, 0);
388 }
389
390 void tst_QQuickDrag::drop()
391 {
392     QQuickCanvas canvas;
393     TestDropTarget outerTarget(canvas.rootItem());
394     outerTarget.setSize(QSizeF(100, 100));
395     outerTarget.acceptAction = Qt::CopyAction;
396     TestDropTarget innerTarget(&outerTarget);
397     innerTarget.setSize(QSizeF(100, 100));
398     innerTarget.acceptAction = Qt::MoveAction;
399     QQmlComponent component(&engine);
400     component.setData(
401             "import QtQuick 2.0\n"
402             "Item {\n"
403                 "property bool dragActive: Drag.active\n"
404                 "property Item dragTarget: Drag.target\n"
405                 "x: 50; y: 50\n"
406                 "width: 10; height: 10\n"
407             "}", QUrl());
408     QScopedPointer<QObject> object(component.create());
409     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
410     QVERIFY(item);
411     item->setParentItem(&outerTarget);
412
413     innerTarget.reset(); outerTarget.reset();
414     evaluate<void>(item, "Drag.active = true");
415     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
416     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
417     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&innerTarget));
418     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&innerTarget));
419     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 0);
420     QCOMPARE(innerTarget.enterEvents, 1); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
421
422     innerTarget.reset(); outerTarget.reset();
423     QCOMPARE(evaluate<bool>(item, "Drag.drop() == Qt.MoveAction"), true);
424     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
425     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
426     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&innerTarget));
427     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&innerTarget));
428     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.dropEvents, 0);
429     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 1);
430
431     innerTarget.reset(); outerTarget.reset();
432     evaluate<void>(item, "Drag.active = true");
433     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
434     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
435     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&innerTarget));
436     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&innerTarget));
437     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 0);
438     QCOMPARE(innerTarget.enterEvents, 1); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
439
440     // Inner target declines the drop so it is propagated to the outer target.
441     innerTarget.accept = false;
442
443     innerTarget.reset(); outerTarget.reset();
444     QCOMPARE(evaluate<bool>(item, "Drag.drop() == Qt.CopyAction"), true);
445     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
446     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
447     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
448     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
449     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 1);
450     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 1);
451
452
453     // Inner target doesn't accept enter so drop goes directly to outer.
454     innerTarget.accept = true;
455     innerTarget.setFlags(QQuickItem::Flags());
456
457     innerTarget.reset(); outerTarget.reset();
458     evaluate<void>(item, "Drag.active = true");
459     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
460     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
461     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
462     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
463     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 0);
464     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
465
466     innerTarget.reset(); outerTarget.reset();
467     QCOMPARE(evaluate<bool>(item, "Drag.drop() == Qt.CopyAction"), true);
468     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
469     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
470     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
471     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
472     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 1);
473     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
474
475     // Neither target accepts drop so Qt::IgnoreAction is returned.
476     innerTarget.reset(); outerTarget.reset();
477     evaluate<void>(item, "Drag.active = true");
478     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
479     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
480     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
481     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
482     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 0);
483     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
484
485     outerTarget.accept = false;
486
487     innerTarget.reset(); outerTarget.reset();
488     QCOMPARE(evaluate<bool>(item, "Drag.drop() == Qt.IgnoreAction"), true);
489     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
490     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
491     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
492     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
493     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 1);
494     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
495
496     // drop doesn't send an event and returns Qt.IgnoreAction if not active.
497     innerTarget.accept = true;
498     outerTarget.accept = true;
499     innerTarget.reset(); outerTarget.reset();
500     QCOMPARE(evaluate<bool>(item, "Drag.drop() == Qt.IgnoreAction"), true);
501     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
502     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
503     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
504     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
505     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 0);
506     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0);
507
508     // Queued move event is delivered before a drop event.
509     innerTarget.reset(); outerTarget.reset();
510     evaluate<void>(item, "Drag.active = true");
511     item->setPos(QPointF(80, 80));
512     evaluate<void>(item, "Drag.drop()");
513     QCOMPARE(evaluate<bool>(item, "Drag.active"), false);
514     QCOMPARE(evaluate<bool>(item, "dragActive"), false);
515     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
516     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
517     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 1); QCOMPARE(outerTarget.moveEvents, 1);
518     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0); QCOMPARE(innerTarget.moveEvents, 0);
519
520     innerTarget.reset(); outerTarget.reset();
521     QCoreApplication::processEvents();
522     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.dropEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
523     QCOMPARE(innerTarget.enterEvents, 0); QCOMPARE(innerTarget.leaveEvents, 0); QCOMPARE(innerTarget.dropEvents, 0); QCOMPARE(innerTarget.moveEvents, 0);
524 }
525
526 void tst_QQuickDrag::move()
527 {
528     QQuickCanvas canvas;
529     TestDropTarget outerTarget(canvas.rootItem());
530     outerTarget.setSize(QSizeF(100, 100));
531     TestDropTarget leftTarget(&outerTarget);
532     leftTarget.setPos(QPointF(0, 35));
533     leftTarget.setSize(QSizeF(30, 30));
534     TestDropTarget rightTarget(&outerTarget);
535     rightTarget.setPos(QPointF(70, 35));
536     rightTarget.setSize(QSizeF(30, 30));
537     QQmlComponent component(&engine);
538     component.setData(
539             "import QtQuick 2.0\n"
540             "Item {\n"
541                 "property bool dragActive: Drag.active\n"
542                 "property Item dragTarget: Drag.target\n"
543                 "x: 50; y: 50\n"
544                 "width: 10; height: 10\n"
545             "}", QUrl());
546     QScopedPointer<QObject> object(component.create());
547     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
548     QVERIFY(item);
549     item->setParentItem(&outerTarget);
550
551     evaluate<void>(item, "Drag.active = true");
552     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
553     QCOMPARE(evaluate<bool>(item, "dragActive"), true);
554     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
555     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
556     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
557     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
558     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
559     QCOMPARE(outerTarget.position.x(), qreal(50)); QCOMPARE(outerTarget.position.y(), qreal(50));
560
561     // Move within the outer target.
562     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
563     item->setPos(QPointF(60, 50));
564     // Move event is delivered in the event loop.
565     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
566     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
567     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
568     QCoreApplication::processEvents();
569     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
570     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
571     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
572     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
573     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
574     QCOMPARE(outerTarget.position.x(), qreal(60)); QCOMPARE(outerTarget.position.y(), qreal(50));
575
576     // Move into the right target.
577     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
578     // Setting X and Y individually should still only generate on move.
579     item->setX(75);
580     item->setY(50);
581     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
582     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
583     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
584     QCoreApplication::processEvents();
585     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&rightTarget));
586     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&rightTarget));
587     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
588     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
589     QCOMPARE(rightTarget.enterEvents, 1); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
590     QCOMPARE(outerTarget.position.x(), qreal(75)); QCOMPARE(outerTarget.position.y(), qreal(50));
591     QCOMPARE(rightTarget.position.x(), qreal(5)); QCOMPARE(rightTarget.position.y(), qreal(15));
592
593     // Move into the left target.
594     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
595     item->setPos(QPointF(25, 50));
596     QCoreApplication::processEvents();
597     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&leftTarget));
598     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&leftTarget));
599     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
600     QCOMPARE(leftTarget .enterEvents, 1); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
601     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 1); QCOMPARE(rightTarget.moveEvents, 0);
602     QCOMPARE(outerTarget.position.x(), qreal(25)); QCOMPARE(outerTarget.position.y(), qreal(50));
603     QCOMPARE(leftTarget.position.x(), qreal(25)); QCOMPARE(leftTarget.position.y(), qreal(15));
604
605     // Move within the left target.
606     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
607     item->setPos(QPointF(25, 40));
608     QCoreApplication::processEvents();
609     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&leftTarget));
610     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&leftTarget));
611     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
612     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 1);
613     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
614     QCOMPARE(outerTarget.position.x(), qreal(25)); QCOMPARE(outerTarget.position.y(), qreal(40));
615     QCOMPARE(leftTarget.position.x(), qreal(25)); QCOMPARE(leftTarget.position.y(), qreal(5));
616
617     // Move out of all targets.
618     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
619     item->setPos(QPointF(110, 50));
620     QCoreApplication::processEvents();
621     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(0));
622     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(0));
623     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0);
624     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 1); QCOMPARE(leftTarget .moveEvents, 0);
625     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
626
627     // Stop the right target accepting drag events and move into it.
628     rightTarget.accept = false;
629
630     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
631     item->setPos(QPointF(80, 50));
632     QCoreApplication::processEvents();
633     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
634     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
635     QCOMPARE(outerTarget.enterEvents, 1); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
636     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
637     QCOMPARE(rightTarget.enterEvents, 1); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
638     QCOMPARE(outerTarget.position.x(), qreal(80)); QCOMPARE(outerTarget.position.y(), qreal(50));
639
640     // Stop the outer target accepting drag events after it has accepted an enter event.
641     outerTarget.accept = false;
642
643     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
644     item->setPos(QPointF(60, 50));
645     QCoreApplication::processEvents();
646     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
647     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
648     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
649     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
650     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
651     QCOMPARE(outerTarget.position.x(), qreal(60)); QCOMPARE(outerTarget.position.y(), qreal(50));
652
653     // Clear the QQuickItem::ItemAcceptsDrops flag from the outer target after it accepted an enter event.
654     outerTarget.setFlags(QQuickItem::Flags());
655
656     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
657     item->setPos(QPointF(40, 50));
658     QCoreApplication::processEvents();
659     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
660     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
661     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
662     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
663     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
664     QCOMPARE(outerTarget.position.x(), qreal(40)); QCOMPARE(outerTarget.position.y(), qreal(50));
665
666     // Clear the QQuickItem::ItemAcceptsDrops flag from the left target before it accepts an enter event.
667     leftTarget.setFlags(QQuickItem::Flags());
668
669     outerTarget.reset(); leftTarget.reset(); rightTarget.reset();
670     item->setPos(QPointF(25, 50));
671     QCoreApplication::processEvents();
672     QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&outerTarget));
673     QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&outerTarget));
674     QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
675     QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
676     QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
677     QCOMPARE(outerTarget.position.x(), qreal(25)); QCOMPARE(outerTarget.position.y(), qreal(50));
678 }
679
680 void tst_QQuickDrag::parentChange()
681 {
682     QQuickCanvas canvas1;
683     TestDropTarget dropTarget1(canvas1.rootItem());
684     dropTarget1.setSize(QSizeF(100, 100));
685
686     QQuickCanvas canvas2;
687     TestDropTarget dropTarget2(canvas2.rootItem());
688     dropTarget2.setSize(QSizeF(100, 100));
689
690     QQmlComponent component(&engine);
691     component.setData(
692             "import QtQuick 2.0\n"
693             "Item {\n"
694                 "property real hotSpotX: Drag.hotSpot.x\n"
695                 "property real hotSpotY: Drag.hotSpot.y\n"
696                 "x: 50; y: 50\n"
697                 "width: 10; height: 10\n"
698                 "Drag.active: true\n"
699             "}", QUrl());
700     QScopedPointer<QObject> object(component.create());
701     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
702     QVERIFY(item);
703
704     QCOMPARE(evaluate<bool>(item, "Drag.active"), true);
705
706     // Verify setting a parent item for an item with an active drag sends an enter event.
707     item->setParentItem(canvas1.rootItem());
708     QCOMPARE(dropTarget1.enterEvents, 0);
709     QCoreApplication::processEvents();
710     QCOMPARE(dropTarget1.enterEvents, 1);
711
712     // Changing the parent within the same canvas should send a move event.
713     item->setParentItem(&dropTarget1);
714     QCOMPARE(dropTarget1.enterEvents, 1);
715     QCOMPARE(dropTarget1.moveEvents, 0);
716     QCoreApplication::processEvents();
717     QCOMPARE(dropTarget1.enterEvents, 1);
718     QCOMPARE(dropTarget1.moveEvents, 1);
719
720     // Changing the parent to an item in another canvas sends a leave event in the old canvas
721     // and an enter on the new canvas.
722     item->setParentItem(canvas2.rootItem());
723     QCOMPARE(dropTarget1.enterEvents, 1);
724     QCOMPARE(dropTarget1.moveEvents, 1);
725     QCOMPARE(dropTarget1.leaveEvents, 0);
726     QCOMPARE(dropTarget2.enterEvents, 0);
727     QCoreApplication::processEvents();
728     QCOMPARE(dropTarget1.enterEvents, 1);
729     QCOMPARE(dropTarget1.moveEvents, 1);
730     QCOMPARE(dropTarget1.leaveEvents, 1);
731     QCOMPARE(dropTarget2.enterEvents, 1);
732
733     // Removing then parent item sends a leave event.
734     item->setParentItem(0);
735     QCOMPARE(dropTarget1.enterEvents, 1);
736     QCOMPARE(dropTarget1.moveEvents, 1);
737     QCOMPARE(dropTarget1.leaveEvents, 1);
738     QCOMPARE(dropTarget2.enterEvents, 1);
739     QCOMPARE(dropTarget2.leaveEvents, 0);
740     QCoreApplication::processEvents();
741     QCOMPARE(dropTarget1.enterEvents, 1);
742     QCOMPARE(dropTarget1.moveEvents, 1);
743     QCOMPARE(dropTarget1.leaveEvents, 1);
744     QCOMPARE(dropTarget2.enterEvents, 1);
745     QCOMPARE(dropTarget2.leaveEvents, 1);
746
747     // Go around again and verify no events if active is false.
748     evaluate<void>(item, "Drag.active = false");
749     item->setParentItem(canvas1.rootItem());
750     QCoreApplication::processEvents();
751
752     item->setParentItem(&dropTarget1);
753     QCoreApplication::processEvents();
754
755     item->setParentItem(canvas2.rootItem());
756     QCoreApplication::processEvents();
757
758     item->setParentItem(0);
759     QCoreApplication::processEvents();
760     QCOMPARE(dropTarget1.enterEvents, 1);
761     QCOMPARE(dropTarget1.moveEvents, 1);
762     QCOMPARE(dropTarget1.leaveEvents, 1);
763     QCOMPARE(dropTarget2.enterEvents, 1);
764     QCOMPARE(dropTarget2.leaveEvents, 1);
765 }
766
767 void tst_QQuickDrag::hotSpot()
768 {
769     QQuickCanvas canvas;
770     TestDropTarget dropTarget(canvas.rootItem());
771     dropTarget.setSize(QSizeF(100, 100));
772     QQmlComponent component(&engine);
773     component.setData(
774             "import QtQuick 2.0\n"
775             "Item {\n"
776                 "property real hotSpotX: Drag.hotSpot.x\n"
777                 "property real hotSpotY: Drag.hotSpot.y\n"
778                 "x: 50; y: 50\n"
779                 "width: 10; height: 10\n"
780             "}", QUrl());
781     QScopedPointer<QObject> object(component.create());
782     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
783     QVERIFY(item);
784     item->setParentItem(&dropTarget);
785
786     QCOMPARE(evaluate<qreal>(item, "Drag.hotSpot.x"), qreal(0));
787     QCOMPARE(evaluate<qreal>(item, "Drag.hotSpot.y"), qreal(0));
788     QCOMPARE(evaluate<qreal>(item, "hotSpotX"), qreal(0));
789     QCOMPARE(evaluate<qreal>(item, "hotSpotY"), qreal(0));
790
791     evaluate<void>(item, "{ Drag.start(); Drag.cancel() }");
792     QCOMPARE(dropTarget.position.x(), qreal(50));
793     QCOMPARE(dropTarget.position.y(), qreal(50));
794
795     evaluate<void>(item, "{ Drag.hotSpot.x = 5, Drag.hotSpot.y = 5 }");
796     QCOMPARE(evaluate<qreal>(item, "Drag.hotSpot.x"), qreal(5));
797     QCOMPARE(evaluate<qreal>(item, "Drag.hotSpot.y"), qreal(5));
798     QCOMPARE(evaluate<qreal>(item, "hotSpotX"), qreal(5));
799     QCOMPARE(evaluate<qreal>(item, "hotSpotY"), qreal(5));
800
801     evaluate<void>(item, "Drag.start()");
802     QCOMPARE(dropTarget.position.x(), qreal(55));
803     QCOMPARE(dropTarget.position.y(), qreal(55));
804
805     item->setPos(QPointF(30, 20));
806     QCoreApplication::processEvents();
807     QCOMPARE(dropTarget.position.x(), qreal(35));
808     QCOMPARE(dropTarget.position.y(), qreal(25));
809
810     evaluate<void>(item, "{ Drag.hotSpot.x = 10; Drag.hotSpot.y = 10 }");
811     QCOMPARE(evaluate<qreal>(item, "Drag.hotSpot.x"), qreal(10));
812     QCOMPARE(evaluate<qreal>(item, "Drag.hotSpot.y"), qreal(10));
813     QCOMPARE(evaluate<qreal>(item, "hotSpotX"), qreal(10));
814     QCOMPARE(evaluate<qreal>(item, "hotSpotY"), qreal(10));
815
816     // Setting the hotSpot will deliver a move event in the event loop.
817     QCOMPARE(dropTarget.position.x(), qreal(35));
818     QCOMPARE(dropTarget.position.y(), qreal(25));
819     QCoreApplication::processEvents();
820     QCOMPARE(dropTarget.position.x(), qreal(40));
821     QCOMPARE(dropTarget.position.y(), qreal(30));
822
823     item->setPos(QPointF(10, 20));
824     QCoreApplication::processEvents();
825     QCOMPARE(dropTarget.position.x(), qreal(20));
826     QCOMPARE(dropTarget.position.y(), qreal(30));
827
828     evaluate<void>(item, "{ Drag.hotSpot.x = 10; Drag.hotSpot.y = 10 }");
829 }
830
831 void tst_QQuickDrag::supportedActions()
832 {
833     QQuickCanvas canvas;
834     TestDropTarget dropTarget(canvas.rootItem());
835     dropTarget.setSize(QSizeF(100, 100));
836     QQmlComponent component(&engine);
837     component.setData(
838             "import QtQuick 2.0\n"
839             "Item {\n"
840                 "property int supportedActions: Drag.supportedActions\n"
841                 "x: 50; y: 50\n"
842                 "width: 10; height: 10\n"
843             "}", QUrl());
844     QScopedPointer<QObject> object(component.create());
845     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
846     QVERIFY(item);
847     item->setParentItem(&dropTarget);
848
849     QCOMPARE(evaluate<bool>(item, "Drag.supportedActions == Qt.CopyAction | Qt.MoveAction | Qt.LinkAction"), true);
850     QCOMPARE(evaluate<bool>(item, "supportedActions == Qt.CopyAction | Qt.MoveAction | Qt.LinkAction"), true);
851     evaluate<void>(item, "{ Drag.start(); Drag.cancel() }");
852     QCOMPARE(dropTarget.supportedActions, Qt::CopyAction | Qt::MoveAction | Qt::LinkAction);
853
854     dropTarget.reset();
855     evaluate<void>(item, "Drag.supportedActions = Qt.CopyAction | Qt.MoveAction");
856     QCOMPARE(evaluate<bool>(item, "Drag.supportedActions == Qt.CopyAction | Qt.MoveAction"), true);
857     QCOMPARE(evaluate<bool>(item, "supportedActions == Qt.CopyAction | Qt.MoveAction"), true);
858     evaluate<void>(item, "Drag.start()");
859     QCOMPARE(dropTarget.supportedActions, Qt::CopyAction | Qt::MoveAction);
860     QCOMPARE(dropTarget.leaveEvents, 0);
861     QCOMPARE(dropTarget.enterEvents, 1);
862
863     // Changing the supported actions will restart the drag, after a delay to avoid any
864     // recursion.
865     evaluate<void>(item, "Drag.supportedActions = Qt.MoveAction");
866     QCOMPARE(evaluate<bool>(item, "Drag.supportedActions == Qt.MoveAction"), true);
867     QCOMPARE(evaluate<bool>(item, "supportedActions == Qt.MoveAction"), true);
868     item->setPos(QPointF(60, 60));
869     QCOMPARE(dropTarget.supportedActions, Qt::CopyAction | Qt::MoveAction);
870     QCOMPARE(dropTarget.leaveEvents, 0);
871     QCOMPARE(dropTarget.enterEvents, 1);
872     QCoreApplication::processEvents();
873     QCOMPARE(dropTarget.supportedActions, Qt::MoveAction);
874     QCOMPARE(dropTarget.leaveEvents, 1);
875     QCOMPARE(dropTarget.enterEvents, 2);
876
877     // Calling start with proposed actions will override the current actions for the next sequence.
878     evaluate<void>(item, "Drag.start(Qt.CopyAction)");
879     QCOMPARE(evaluate<bool>(item, "Drag.supportedActions == Qt.MoveAction"), true);
880     QCOMPARE(evaluate<bool>(item, "supportedActions == Qt.MoveAction"), true);
881     QCOMPARE(dropTarget.supportedActions, Qt::CopyAction);
882
883     evaluate<void>(item, "Drag.start()");
884     QCOMPARE(evaluate<bool>(item, "Drag.supportedActions == Qt.MoveAction"), true);
885     QCOMPARE(evaluate<bool>(item, "supportedActions == Qt.MoveAction"), true);
886     QCOMPARE(dropTarget.supportedActions, Qt::MoveAction);
887 }
888
889 void tst_QQuickDrag::proposedAction()
890 {
891     QQuickCanvas canvas;
892     TestDropTarget dropTarget(canvas.rootItem());
893     dropTarget.setSize(QSizeF(100, 100));
894     QQmlComponent component(&engine);
895     component.setData(
896             "import QtQuick 2.0\n"
897             "Item {\n"
898                 "property int proposedAction: Drag.proposedAction\n"
899                 "x: 50; y: 50\n"
900                 "width: 10; height: 10\n"
901             "}", QUrl());
902     QScopedPointer<QObject> object(component.create());
903     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
904     QVERIFY(item);
905     item->setParentItem(&dropTarget);
906
907     QCOMPARE(evaluate<bool>(item, "Drag.proposedAction == Qt.MoveAction"), true);
908     QCOMPARE(evaluate<bool>(item, "proposedAction == Qt.MoveAction"), true);
909     evaluate<void>(item, "{ Drag.start(); Drag.cancel() }");
910     QCOMPARE(dropTarget.defaultAction, Qt::MoveAction);
911     QCOMPARE(dropTarget.proposedAction, Qt::MoveAction);
912
913     evaluate<void>(item, "Drag.proposedAction = Qt.CopyAction");
914     QCOMPARE(evaluate<bool>(item, "Drag.proposedAction == Qt.CopyAction"), true);
915     QCOMPARE(evaluate<bool>(item, "proposedAction == Qt.CopyAction"), true);
916     evaluate<void>(item, "Drag.start()");
917     QCOMPARE(dropTarget.defaultAction, Qt::CopyAction);
918     QCOMPARE(dropTarget.proposedAction, Qt::CopyAction);
919
920     // The proposed action can change during a drag.
921     evaluate<void>(item, "Drag.proposedAction = Qt.MoveAction");
922     QCOMPARE(evaluate<bool>(item, "Drag.proposedAction == Qt.MoveAction"), true);
923     QCOMPARE(evaluate<bool>(item, "proposedAction == Qt.MoveAction"), true);
924     QCoreApplication::processEvents();
925     QCOMPARE(dropTarget.defaultAction, Qt::MoveAction);
926     QCOMPARE(dropTarget.proposedAction, Qt::MoveAction);
927
928     evaluate<void>(item, "Drag.proposedAction = Qt.LinkAction");
929     QCOMPARE(evaluate<bool>(item, "Drag.proposedAction == Qt.LinkAction"), true);
930     QCOMPARE(evaluate<bool>(item, "proposedAction == Qt.LinkAction"), true);
931     evaluate<void>(item, "Drag.drop()");
932     QCOMPARE(dropTarget.defaultAction, Qt::LinkAction);
933     QCOMPARE(dropTarget.proposedAction, Qt::LinkAction);
934 }
935
936 void tst_QQuickDrag::keys()
937 {
938     QQmlComponent component(&engine);
939     component.setData(
940             "import QtQuick 2.0\n"
941             "Item {\n"
942                 "property variant keys: Drag.keys\n"
943                 "x: 50; y: 50\n"
944                 "width: 10; height: 10\n"
945             "}", QUrl());
946     QScopedPointer<QObject> object(component.create());
947     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
948     QVERIFY(item);
949
950     QCOMPARE(evaluate<QStringList>(item, "Drag.keys"), QStringList());
951     QCOMPARE(evaluate<QStringList>(item, "keys"), QStringList());
952     QCOMPARE(item->property("keys").toStringList(), QStringList());
953
954     evaluate<void>(item, "Drag.keys = [\"red\", \"blue\"]");
955     QCOMPARE(evaluate<QStringList>(item, "Drag.keys"), QStringList() << "red" << "blue");
956     QCOMPARE(evaluate<QStringList>(item, "keys"), QStringList() << "red" << "blue");
957     QCOMPARE(item->property("keys").toStringList(), QStringList() << "red" << "blue");
958
959     // Test changing the keys restarts a drag.
960     QQuickCanvas canvas;
961     item->setParentItem(canvas.rootItem());
962     TestDropTarget dropTarget(canvas.rootItem());
963     dropTarget.setSize(QSizeF(100, 100));
964
965     evaluate<void>(item, "Drag.start()");
966     QCOMPARE(dropTarget.leaveEvents, 0);
967     QCOMPARE(dropTarget.enterEvents, 1);
968
969     evaluate<void>(item, "Drag.keys = [\"green\"]");
970     QCOMPARE(dropTarget.leaveEvents, 0);
971     QCOMPARE(dropTarget.enterEvents, 1);
972     QCoreApplication::processEvents();
973     QCOMPARE(dropTarget.leaveEvents, 1);
974     QCOMPARE(dropTarget.enterEvents, 2);
975 }
976
977 void tst_QQuickDrag::source()
978 {
979
980     QQmlComponent component(&engine);
981     component.setData(
982             "import QtQuick 2.0\n"
983             "Item {\n"
984                 "property Item source: Drag.source\n"
985                 "x: 50; y: 50\n"
986                 "width: 10; height: 10\n"
987                 "Item { id: proxySource; objectName: \"proxySource\" }\n"
988             "}", QUrl());
989     QScopedPointer<QObject> object(component.create());
990     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
991     QVERIFY(item);
992
993     QCOMPARE(evaluate<QObject *>(item, "Drag.source"), static_cast<QObject *>(item));
994     QCOMPARE(evaluate<QObject *>(item, "source"), static_cast<QObject *>(item));
995
996     QQuickItem *proxySource = item->findChild<QQuickItem *>("proxySource");
997     QVERIFY(proxySource);
998
999     evaluate<void>(item, "Drag.source = proxySource");
1000     QCOMPARE(evaluate<QObject *>(item, "Drag.source"), static_cast<QObject *>(proxySource));
1001     QCOMPARE(evaluate<QObject *>(item, "source"), static_cast<QObject *>(proxySource));
1002
1003     evaluate<void>(item, "Drag.source = undefined");
1004     QCOMPARE(evaluate<QObject *>(item, "Drag.source"), static_cast<QObject *>(item));
1005     QCOMPARE(evaluate<QObject *>(item, "source"), static_cast<QObject *>(item));
1006
1007     // Test changing the source restarts a drag.
1008     QQuickCanvas canvas;
1009     item->setParentItem(canvas.rootItem());
1010     TestDropTarget dropTarget(canvas.rootItem());
1011     dropTarget.setSize(QSizeF(100, 100));
1012
1013     evaluate<void>(item, "Drag.start()");
1014     QCOMPARE(dropTarget.leaveEvents, 0);
1015     QCOMPARE(dropTarget.enterEvents, 1);
1016
1017     evaluate<void>(item, "Drag.source = proxySource");
1018     QCOMPARE(dropTarget.leaveEvents, 0);
1019     QCOMPARE(dropTarget.enterEvents, 1);
1020     QCoreApplication::processEvents();
1021     QCOMPARE(dropTarget.leaveEvents, 1);
1022     QCOMPARE(dropTarget.enterEvents, 2);
1023 }
1024
1025 class RecursingDropTarget : public TestDropTarget
1026 {
1027 public:
1028     RecursingDropTarget(const QString &script, int type, QQuickItem *parent)
1029         : TestDropTarget(parent), script(script), type(type), item(0) {}
1030
1031     void setItem(QQuickItem *i) { item = i; }
1032
1033 protected:
1034     void dragEnterEvent(QDragEnterEvent *event)
1035     {
1036         TestDropTarget::dragEnterEvent(event);
1037         if (type == QEvent::DragEnter && enterEvents < 2)
1038             evaluate<void>(item, script);
1039     }
1040
1041     void dragMoveEvent(QDragMoveEvent *event)
1042     {
1043         TestDropTarget::dragMoveEvent(event);
1044         if (type == QEvent::DragMove && moveEvents < 2)
1045             evaluate<void>(item, script);
1046     }
1047
1048     void dragLeaveEvent(QDragLeaveEvent *event)
1049     {
1050         TestDropTarget::dragLeaveEvent(event);
1051         if (type == QEvent::DragLeave && leaveEvents < 2)
1052             evaluate<void>(item, script);
1053     }
1054
1055     void dropEvent(QDropEvent *event)
1056     {
1057         TestDropTarget::dropEvent(event);
1058         if (type == QEvent::Drop && dropEvents < 2)
1059             evaluate<void>(item, script);
1060     }
1061
1062 private:
1063     QString script;
1064     int type;
1065     QQuickItem *item;
1066
1067 };
1068
1069 void tst_QQuickDrag::recursion_data()
1070 {
1071     QTest::addColumn<QString>("script");
1072     QTest::addColumn<int>("type");
1073     QTest::addColumn<QByteArray>("warning");
1074
1075     QTest::newRow("Drag.start() in Enter")
1076             << QString("Drag.start()")
1077             << int(QEvent::DragEnter)
1078             << QByteArray("<Unknown File>: QML QQuickDragAttached: start() cannot be called from within a drag event handler");
1079     QTest::newRow("Drag.cancel() in Enter")
1080             << QString("Drag.cancel()")
1081             << int(QEvent::DragEnter)
1082             << QByteArray("<Unknown File>: QML QQuickDragAttached: cancel() cannot be called from within a drag event handler");
1083     QTest::newRow("Drag.drop() in Enter")
1084             << QString("Drag.drop()")
1085             << int(QEvent::DragEnter)
1086             << QByteArray("<Unknown File>: QML QQuickDragAttached: drop() cannot be called from within a drag event handler");
1087     QTest::newRow("Drag.active = true in Enter")
1088             << QString("Drag.active = true")
1089             << int(QEvent::DragEnter)
1090             << QByteArray();
1091     QTest::newRow("Drag.active = false in Enter")
1092             << QString("Drag.active = false")
1093             << int(QEvent::DragEnter)
1094             << QByteArray("<Unknown File>: QML QQuickDragAttached: active cannot be changed from within a drag event handler");
1095     QTest::newRow("move in Enter")
1096             << QString("x = 23")
1097             << int(QEvent::DragEnter)
1098             << QByteArray();
1099
1100     QTest::newRow("Drag.start() in Move")
1101             << QString("Drag.start()")
1102             << int(QEvent::DragMove)
1103             << QByteArray("<Unknown File>: QML QQuickDragAttached: start() cannot be called from within a drag event handler");
1104     QTest::newRow("Drag.cancel() in Move")
1105             << QString("Drag.cancel()")
1106             << int(QEvent::DragMove)
1107             << QByteArray("<Unknown File>: QML QQuickDragAttached: cancel() cannot be called from within a drag event handler");
1108     QTest::newRow("Drag.drop() in Move")
1109             << QString("Drag.drop()")
1110             << int(QEvent::DragMove)
1111             << QByteArray("<Unknown File>: QML QQuickDragAttached: drop() cannot be called from within a drag event handler");
1112     QTest::newRow("Drag.active = true in Move")
1113             << QString("Drag.active = true")
1114             << int(QEvent::DragMove)
1115             << QByteArray();
1116     QTest::newRow("Drag.active = false in Move")
1117             << QString("Drag.active = false")
1118             << int(QEvent::DragMove)
1119             << QByteArray("<Unknown File>: QML QQuickDragAttached: active cannot be changed from within a drag event handler");
1120     QTest::newRow("move in Move")
1121             << QString("x = 23")
1122             << int(QEvent::DragMove)
1123             << QByteArray();
1124
1125     QTest::newRow("Drag.start() in Leave")
1126             << QString("Drag.start()")
1127             << int(QEvent::DragLeave)
1128             << QByteArray("<Unknown File>: QML QQuickDragAttached: start() cannot be called from within a drag event handler");
1129     QTest::newRow("Drag.cancel() in Leave")
1130             << QString("Drag.cancel()")
1131             << int(QEvent::DragLeave)
1132             << QByteArray("<Unknown File>: QML QQuickDragAttached: cancel() cannot be called from within a drag event handler");
1133     QTest::newRow("Drag.drop() in Leave")
1134             << QString("Drag.drop()")
1135             << int(QEvent::DragLeave)
1136             << QByteArray("<Unknown File>: QML QQuickDragAttached: drop() cannot be called from within a drag event handler");
1137     QTest::newRow("Drag.active = true in Leave")
1138             << QString("Drag.active = true")
1139             << int(QEvent::DragLeave)
1140             << QByteArray("<Unknown File>: QML QQuickDragAttached: active cannot be changed from within a drag event handler");
1141     QTest::newRow("Drag.active = false in Leave")
1142             << QString("Drag.active = false")
1143             << int(QEvent::DragLeave)
1144             << QByteArray();
1145     QTest::newRow("move in Leave")
1146             << QString("x = 23")
1147             << int(QEvent::DragLeave)
1148             << QByteArray();
1149
1150     QTest::newRow("Drag.start() in Drop")
1151             << QString("Drag.start()")
1152             << int(QEvent::Drop)
1153             << QByteArray("<Unknown File>: QML QQuickDragAttached: start() cannot be called from within a drag event handler");
1154     QTest::newRow("Drag.cancel() in Drop")
1155             << QString("Drag.cancel()")
1156             << int(QEvent::Drop)
1157             << QByteArray("<Unknown File>: QML QQuickDragAttached: cancel() cannot be called from within a drag event handler");
1158     QTest::newRow("Drag.drop() in Drop")
1159             << QString("Drag.drop()")
1160             << int(QEvent::Drop)
1161             << QByteArray("<Unknown File>: QML QQuickDragAttached: drop() cannot be called from within a drag event handler");
1162     QTest::newRow("Drag.active = true in Drop")
1163             << QString("Drag.active = true")
1164             << int(QEvent::Drop)
1165             << QByteArray("<Unknown File>: QML QQuickDragAttached: active cannot be changed from within a drag event handler");
1166     QTest::newRow("Drag.active = false in Drop")
1167             << QString("Drag.active = false")
1168             << int(QEvent::Drop)
1169             << QByteArray();
1170     QTest::newRow("move in Drop")
1171             << QString("x = 23")
1172             << int(QEvent::Drop)
1173             << QByteArray();
1174 }
1175
1176 void tst_QQuickDrag::recursion()
1177 {
1178     QFETCH(QString, script);
1179     QFETCH(int, type);
1180     QFETCH(QByteArray, warning);
1181
1182     if (!warning.isEmpty())
1183         QTest::ignoreMessage(QtWarningMsg, warning.constData());
1184
1185     QQuickCanvas canvas;
1186     RecursingDropTarget dropTarget(script, type, canvas.rootItem());
1187     dropTarget.setSize(QSizeF(100, 100));
1188     QQmlComponent component(&engine);
1189     component.setData(
1190             "import QtQuick 2.0\n"
1191             "Item {\n"
1192                 "x: 50; y: 50\n"
1193                 "width: 10; height: 10\n"
1194             "}", QUrl());
1195     QScopedPointer<QObject> object(component.create());
1196     QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
1197     QVERIFY(item);
1198     item->setParentItem(canvas.rootItem());
1199
1200     dropTarget.setItem(item);
1201
1202     evaluate<void>(item, "Drag.start()");
1203     QCOMPARE(dropTarget.enterEvents, 1);
1204     QCOMPARE(dropTarget.moveEvents, 0);
1205     QCOMPARE(dropTarget.dropEvents, 0);
1206     QCOMPARE(dropTarget.leaveEvents, 0);
1207
1208     evaluate<void>(item, "y = 15");
1209     QCoreApplication::processEvents();
1210     QCOMPARE(dropTarget.enterEvents, 1);
1211     QCOMPARE(dropTarget.moveEvents, 1);
1212     QCOMPARE(dropTarget.dropEvents, 0);
1213     QCOMPARE(dropTarget.leaveEvents, 0);
1214
1215     if (type == QEvent::Drop) {
1216         QCOMPARE(evaluate<bool>(item, "Drag.drop() == Qt.MoveAction"), true);
1217         QCOMPARE(dropTarget.enterEvents, 1);
1218         QCOMPARE(dropTarget.moveEvents, 1);
1219         QCOMPARE(dropTarget.dropEvents, 1);
1220         QCOMPARE(dropTarget.leaveEvents, 0);
1221     } else {
1222         evaluate<void>(item, "Drag.cancel()");
1223         QCOMPARE(dropTarget.enterEvents, 1);
1224         QCOMPARE(dropTarget.moveEvents, 1);
1225         QCOMPARE(dropTarget.dropEvents, 0);
1226         QCOMPARE(dropTarget.leaveEvents, 1);
1227     }
1228 }
1229
1230
1231 QTEST_MAIN(tst_QQuickDrag)
1232
1233 #include "tst_qquickdrag.moc"