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

source code of qtdeclarative/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp