d8575353ea4b11e58d1a4ecadeae0e40137fb356
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickpathview / tst_qquickpathview.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 <QtQuick/qquickview.h>
44 #include <QtQml/qqmlengine.h>
45 #include <QtQml/qqmlcomponent.h>
46 #include <QtQml/qqmlcontext.h>
47 #include <QtQml/qqmlexpression.h>
48 #include <QtQml/qqmlincubator.h>
49 #include <QtQuick/private/qquickpathview_p.h>
50 #include <QtQuick/private/qquickpath_p.h>
51 #include <QtQuick/private/qquicktext_p.h>
52 #include <QtQuick/private/qquickrectangle_p.h>
53 #include <QtQml/private/qquicklistmodel_p.h>
54 #include <QtQml/private/qqmlvaluetype_p.h>
55 #include <QStringListModel>
56 #include <QFile>
57
58 #include "../../shared/util.h"
59 #include "../shared/viewtestutil.h"
60 #include "../shared/visualtestutil.h"
61
62 using namespace QQuickViewTestUtil;
63 using namespace QQuickVisualTestUtil;
64
65 Q_DECLARE_METATYPE(QQuickPathView::HighlightRangeMode)
66
67 #ifndef QT_NO_WIDGETS
68 #include <QStandardItemModel>
69 static void initStandardTreeModel(QStandardItemModel *model)
70 {
71     QStandardItem *item;
72     item = new QStandardItem(QLatin1String("Row 1 Item"));
73     model->insertRow(0, item);
74
75     item = new QStandardItem(QLatin1String("Row 2 Item"));
76     item->setCheckable(true);
77     model->insertRow(1, item);
78
79     QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
80     item->setChild(0, childItem);
81
82     item = new QStandardItem(QLatin1String("Row 3 Item"));
83     item->setIcon(QIcon());
84     model->insertRow(2, item);
85 }
86 #endif
87
88
89 class tst_QQuickPathView : public QQmlDataTest
90 {
91     Q_OBJECT
92 public:
93     tst_QQuickPathView();
94
95 private slots:
96     void initValues();
97     void items();
98     void dataModel();
99     void pathview2();
100     void pathview3();
101     void insertModel_data();
102     void insertModel();
103     void removeModel_data();
104     void removeModel();
105     void moveModel_data();
106     void moveModel();
107     void consecutiveModelChanges_data();
108     void consecutiveModelChanges();
109     void path();
110     void pathMoved();
111     void offset_data();
112     void offset();
113     void setCurrentIndex();
114     void resetModel();
115     void propertyChanges();
116     void pathChanges();
117     void componentChanges();
118     void modelChanges();
119     void pathUpdateOnStartChanged();
120     void package();
121     void emptyModel();
122     void closed();
123     void pathUpdate();
124     void visualDataModel();
125     void undefinedPath();
126     void mouseDrag();
127 #ifndef QT_NO_WIDGETS
128     void treeModel();
129 #endif
130     void changePreferredHighlight();
131     void missingPercent();
132     void creationContext();
133     void currentOffsetOnInsertion();
134     void asynchronous();
135     void cancelDrag();
136     void maximumFlickVelocity();
137     void snapToItem();
138     void snapToItem_data();
139     void snapOneItem();
140     void snapOneItem_data();
141 };
142
143 class TestObject : public QObject
144 {
145     Q_OBJECT
146
147     Q_PROPERTY(bool error READ error WRITE setError)
148     Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
149     Q_PROPERTY(int pathItemCount READ pathItemCount NOTIFY pathItemCountChanged)
150
151 public:
152     TestObject() : QObject(), mError(true), mUseModel(true), mPathItemCount(-1) {}
153
154     bool error() const { return mError; }
155     void setError(bool err) { mError = err; }
156
157     bool useModel() const { return mUseModel; }
158     void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
159
160     int pathItemCount() const { return mPathItemCount; }
161     void setPathItemCount(int count) { mPathItemCount = count; emit pathItemCountChanged(); }
162
163 signals:
164     void useModelChanged();
165     void pathItemCountChanged();
166
167 private:
168     bool mError;
169     bool mUseModel;
170     int mPathItemCount;
171 };
172
173 tst_QQuickPathView::tst_QQuickPathView()
174 {
175 }
176
177 void tst_QQuickPathView::initValues()
178 {
179     QQmlEngine engine;
180     QQmlComponent c(&engine, testFileUrl("pathview1.qml"));
181     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
182
183     QVERIFY(obj != 0);
184     QVERIFY(obj->path() == 0);
185     QVERIFY(obj->delegate() == 0);
186     QCOMPARE(obj->model(), QVariant());
187     QCOMPARE(obj->currentIndex(), 0);
188     QCOMPARE(obj->offset(), 0.);
189     QCOMPARE(obj->preferredHighlightBegin(), 0.);
190     QCOMPARE(obj->dragMargin(), 0.);
191     QCOMPARE(obj->count(), 0);
192     QCOMPARE(obj->pathItemCount(), -1);
193
194     delete obj;
195 }
196
197 void tst_QQuickPathView::items()
198 {
199     QQuickView *canvas = createView();
200
201     QaimModel model;
202     model.addItem("Fred", "12345");
203     model.addItem("John", "2345");
204     model.addItem("Bob", "54321");
205     model.addItem("Bill", "4321");
206
207     QQmlContext *ctxt = canvas->rootContext();
208     ctxt->setContextProperty("testModel", &model);
209
210     canvas->setSource(testFileUrl("pathview0.qml"));
211     qApp->processEvents();
212
213     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
214     QVERIFY(pathview != 0);
215
216     QCOMPARE(pathview->count(), model.count());
217     QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
218     QCOMPARE(pathview->childItems().count(), model.count()+1); // assumes all are visible, including highlight
219
220     for (int i = 0; i < model.count(); ++i) {
221         QQuickText *name = findItem<QQuickText>(pathview, "textName", i);
222         QVERIFY(name != 0);
223         QCOMPARE(name->text(), model.name(i));
224         QQuickText *number = findItem<QQuickText>(pathview, "textNumber", i);
225         QVERIFY(number != 0);
226         QCOMPARE(number->text(), model.number(i));
227     }
228
229     QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
230     QVERIFY(path);
231
232     QVERIFY(pathview->highlightItem());
233     QPointF start = path->pointAt(0.0);
234     QPointF offset;
235     offset.setX(pathview->highlightItem()->width()/2);
236     offset.setY(pathview->highlightItem()->height()/2);
237     QCOMPARE(pathview->highlightItem()->pos() + offset, start);
238
239     delete canvas;
240 }
241
242 void tst_QQuickPathView::pathview2()
243 {
244     QQmlEngine engine;
245     QQmlComponent c(&engine, testFileUrl("pathview2.qml"));
246     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
247
248     QVERIFY(obj != 0);
249     QVERIFY(obj->path() != 0);
250     QVERIFY(obj->delegate() != 0);
251     QVERIFY(obj->model() != QVariant());
252     QCOMPARE(obj->currentIndex(), 0);
253     QCOMPARE(obj->offset(), 0.);
254     QCOMPARE(obj->preferredHighlightBegin(), 0.);
255     QCOMPARE(obj->dragMargin(), 0.);
256     QCOMPARE(obj->count(), 8);
257     QCOMPARE(obj->pathItemCount(), 10);
258
259     delete obj;
260 }
261
262 void tst_QQuickPathView::pathview3()
263 {
264     QQmlEngine engine;
265     QQmlComponent c(&engine, testFileUrl("pathview3.qml"));
266     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
267
268     QVERIFY(obj != 0);
269     QVERIFY(obj->path() != 0);
270     QVERIFY(obj->delegate() != 0);
271     QVERIFY(obj->model() != QVariant());
272     QCOMPARE(obj->currentIndex(), 7);
273     QCOMPARE(obj->offset(), 1.0);
274     QCOMPARE(obj->preferredHighlightBegin(), 0.5);
275     QCOMPARE(obj->dragMargin(), 24.);
276     QCOMPARE(obj->count(), 8);
277     QCOMPARE(obj->pathItemCount(), 4);
278
279     delete obj;
280 }
281
282 void tst_QQuickPathView::insertModel_data()
283 {
284     QTest::addColumn<int>("mode");
285     QTest::addColumn<int>("idx");
286     QTest::addColumn<int>("count");
287     QTest::addColumn<qreal>("offset");
288     QTest::addColumn<int>("currentIndex");
289
290     // We have 8 items, with currentIndex == 4
291     QTest::newRow("insert after current")
292         << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 5. << 4;
293     QTest::newRow("insert before current")
294         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4. << 5;
295     QTest::newRow("insert multiple after current")
296         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 6. << 4;
297     QTest::newRow("insert multiple before current")
298         << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4. << 6;
299     QTest::newRow("insert at end")
300         << int(QQuickPathView::StrictlyEnforceRange) << 8 << 1 << 5. << 4;
301     QTest::newRow("insert at beginning")
302         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4. << 5;
303     QTest::newRow("insert at current")
304         << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 4. << 5;
305
306     QTest::newRow("no range - insert after current")
307         << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 5. << 4;
308     QTest::newRow("no range - insert before current")
309         << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4. << 5;
310     QTest::newRow("no range - insert multiple after current")
311         << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 6. << 4;
312     QTest::newRow("no range - insert multiple before current")
313         << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4. << 6;
314     QTest::newRow("no range - insert at end")
315         << int(QQuickPathView::NoHighlightRange) << 8 << 1 << 5. << 4;
316     QTest::newRow("no range - insert at beginning")
317         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4. << 5;
318     QTest::newRow("no range - insert at current")
319         << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4. << 5;
320 }
321
322 void tst_QQuickPathView::insertModel()
323 {
324     QFETCH(int, mode);
325     QFETCH(int, idx);
326     QFETCH(int, count);
327     QFETCH(qreal, offset);
328     QFETCH(int, currentIndex);
329
330     QQuickView *canvas = createView();
331     canvas->show();
332
333     QaimModel model;
334     model.addItem("Ben", "12345");
335     model.addItem("Bohn", "2345");
336     model.addItem("Bob", "54321");
337     model.addItem("Bill", "4321");
338     model.addItem("Jinny", "679");
339     model.addItem("Milly", "73378");
340     model.addItem("Jimmy", "3535");
341     model.addItem("Barb", "9039");
342
343     QQmlContext *ctxt = canvas->rootContext();
344     ctxt->setContextProperty("testModel", &model);
345
346     canvas->setSource(testFileUrl("pathview0.qml"));
347     qApp->processEvents();
348
349     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
350     QVERIFY(pathview != 0);
351
352     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
353
354     pathview->setCurrentIndex(4);
355     if (mode == QQuickPathView::StrictlyEnforceRange)
356         QTRY_COMPARE(pathview->offset(), 4.0);
357     else
358         pathview->setOffset(4);
359
360     QList<QPair<QString, QString> > items;
361     for (int i = 0; i < count; ++i)
362         items.append(qMakePair(QString("New"), QString::number(i)));
363
364     model.insertItems(idx, items);
365     QTRY_COMPARE(pathview->offset(), offset);
366
367     QCOMPARE(pathview->currentIndex(), currentIndex);
368
369     delete canvas;
370 }
371
372 void tst_QQuickPathView::removeModel_data()
373 {
374     QTest::addColumn<int>("mode");
375     QTest::addColumn<int>("idx");
376     QTest::addColumn<int>("count");
377     QTest::addColumn<qreal>("offset");
378     QTest::addColumn<int>("currentIndex");
379
380     // We have 8 items, with currentIndex == 4
381     QTest::newRow("remove after current")
382         << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 3. << 4;
383     QTest::newRow("remove before current")
384         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4. << 3;
385     QTest::newRow("remove multiple after current")
386         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 2. << 4;
387     QTest::newRow("remove multiple before current")
388         << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4. << 2;
389     QTest::newRow("remove last")
390         << int(QQuickPathView::StrictlyEnforceRange) << 7 << 1 << 3. << 4;
391     QTest::newRow("remove first")
392         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4. << 3;
393     QTest::newRow("remove current")
394         << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 3. << 4;
395     QTest::newRow("remove all")
396         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 8 << 0. << 0;
397
398     QTest::newRow("no range - remove after current")
399         << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 3. << 4;
400     QTest::newRow("no range - remove before current")
401         << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4. << 3;
402     QTest::newRow("no range - remove multiple after current")
403         << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 2. << 4;
404     QTest::newRow("no range - remove multiple before current")
405         << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4. << 2;
406     QTest::newRow("no range - remove last")
407         << int(QQuickPathView::NoHighlightRange) << 7 << 1 << 3. << 4;
408     QTest::newRow("no range - remove first")
409         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4. << 3;
410     QTest::newRow("no range - remove current offset")
411         << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4. << 4;
412     QTest::newRow("no range - remove all")
413         << int(QQuickPathView::NoHighlightRange) << 0 << 8 << 0. << 0;
414 }
415
416 void tst_QQuickPathView::removeModel()
417 {
418     QFETCH(int, mode);
419     QFETCH(int, idx);
420     QFETCH(int, count);
421     QFETCH(qreal, offset);
422     QFETCH(int, currentIndex);
423
424     QQuickView *canvas = createView();
425     canvas->show();
426
427     QaimModel model;
428     model.addItem("Ben", "12345");
429     model.addItem("Bohn", "2345");
430     model.addItem("Bob", "54321");
431     model.addItem("Bill", "4321");
432     model.addItem("Jinny", "679");
433     model.addItem("Milly", "73378");
434     model.addItem("Jimmy", "3535");
435     model.addItem("Barb", "9039");
436
437     QQmlContext *ctxt = canvas->rootContext();
438     ctxt->setContextProperty("testModel", &model);
439
440     canvas->setSource(testFileUrl("pathview0.qml"));
441     qApp->processEvents();
442
443     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
444     QVERIFY(pathview != 0);
445
446     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
447
448     pathview->setCurrentIndex(4);
449     if (mode == QQuickPathView::StrictlyEnforceRange)
450         QTRY_COMPARE(pathview->offset(), 4.0);
451     else
452         pathview->setOffset(4);
453
454     model.removeItems(idx, count);
455     QTRY_COMPARE(pathview->offset(), offset);
456
457     QCOMPARE(pathview->currentIndex(), currentIndex);
458
459     delete canvas;
460 }
461
462
463 void tst_QQuickPathView::moveModel_data()
464 {
465     QTest::addColumn<int>("mode");
466     QTest::addColumn<int>("from");
467     QTest::addColumn<int>("to");
468     QTest::addColumn<int>("count");
469     QTest::addColumn<qreal>("offset");
470     QTest::addColumn<int>("currentIndex");
471
472     // We have 8 items, with currentIndex == 4
473     QTest::newRow("move after current")
474         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 1 << 4. << 4;
475     QTest::newRow("move before current")
476         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 3 << 1 << 4. << 4;
477     QTest::newRow("move before current to after")
478         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 6 << 1 << 5. << 3;
479     QTest::newRow("move multiple after current")
480         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 2 << 4. << 4;
481     QTest::newRow("move multiple before current")
482         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 2 << 4. << 4;
483     QTest::newRow("move before current to end")
484         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 7 << 1 << 5. << 3;
485     QTest::newRow("move last to beginning")
486         << int(QQuickPathView::StrictlyEnforceRange) << 7 << 0 << 1 << 3. << 5;
487     QTest::newRow("move current")
488         << int(QQuickPathView::StrictlyEnforceRange) << 4 << 6 << 1 << 2. << 6;
489
490     QTest::newRow("no range - move after current")
491         << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 1 << 4. << 4;
492     QTest::newRow("no range - move before current")
493         << int(QQuickPathView::NoHighlightRange) << 2 << 3 << 1 << 4. << 4;
494     QTest::newRow("no range - move before current to after")
495         << int(QQuickPathView::NoHighlightRange) << 2 << 6 << 1 << 5. << 3;
496     QTest::newRow("no range - move multiple after current")
497         << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 2 << 4. << 4;
498     QTest::newRow("no range - move multiple before current")
499         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 2 << 4. << 4;
500     QTest::newRow("no range - move before current to end")
501         << int(QQuickPathView::NoHighlightRange) << 2 << 7 << 1 << 5. << 3;
502     QTest::newRow("no range - move last to beginning")
503         << int(QQuickPathView::NoHighlightRange) << 7 << 0 << 1 << 3. << 5;
504     QTest::newRow("no range - move current")
505         << int(QQuickPathView::NoHighlightRange) << 4 << 6 << 1 << 4. << 6;
506     QTest::newRow("no range - move multiple incl. current")
507         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 5 << 4. << 5;
508 }
509
510 void tst_QQuickPathView::moveModel()
511 {
512     QFETCH(int, mode);
513     QFETCH(int, from);
514     QFETCH(int, to);
515     QFETCH(int, count);
516     QFETCH(qreal, offset);
517     QFETCH(int, currentIndex);
518
519     QQuickView *canvas = createView();
520     canvas->show();
521
522     QaimModel model;
523     model.addItem("Ben", "12345");
524     model.addItem("Bohn", "2345");
525     model.addItem("Bob", "54321");
526     model.addItem("Bill", "4321");
527     model.addItem("Jinny", "679");
528     model.addItem("Milly", "73378");
529     model.addItem("Jimmy", "3535");
530     model.addItem("Barb", "9039");
531
532     QQmlContext *ctxt = canvas->rootContext();
533     ctxt->setContextProperty("testModel", &model);
534
535     canvas->setSource(testFileUrl("pathview0.qml"));
536     qApp->processEvents();
537
538     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
539     QVERIFY(pathview != 0);
540
541     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
542
543     pathview->setCurrentIndex(4);
544     if (mode == QQuickPathView::StrictlyEnforceRange)
545         QTRY_COMPARE(pathview->offset(), 4.0);
546     else
547         pathview->setOffset(4);
548
549     model.moveItems(from, to, count);
550     QTRY_COMPARE(pathview->offset(), offset);
551
552     QCOMPARE(pathview->currentIndex(), currentIndex);
553
554     delete canvas;
555 }
556
557 void tst_QQuickPathView::consecutiveModelChanges_data()
558 {
559     QTest::addColumn<QQuickPathView::HighlightRangeMode>("mode");
560     QTest::addColumn<QList<ListChange> >("changes");
561     QTest::addColumn<int>("count");
562     QTest::addColumn<qreal>("offset");
563     QTest::addColumn<int>("currentIndex");
564
565     QTest::newRow("no range - insert after, insert before")
566             << QQuickPathView::NoHighlightRange
567             << (QList<ListChange>()
568                 << ListChange::insert(7, 2)
569                 << ListChange::insert(1, 3))
570             << 13
571             << 6.
572             << 7;
573     QTest::newRow("no range - remove after, remove before")
574             << QQuickPathView::NoHighlightRange
575             << (QList<ListChange>()
576                 << ListChange::remove(6, 2)
577                 << ListChange::remove(1, 3))
578             << 3
579             << 2.
580             << 1;
581
582     QTest::newRow("no range - remove after, insert before")
583             << QQuickPathView::NoHighlightRange
584             << (QList<ListChange>()
585                 << ListChange::remove(5, 2)
586                 << ListChange::insert(1, 3))
587             << 9
588             << 2.
589             << 7;
590
591     QTest::newRow("no range - insert after, remove before")
592             << QQuickPathView::NoHighlightRange
593             << (QList<ListChange>()
594                 << ListChange::insert(6, 2)
595                 << ListChange::remove(1, 3))
596             << 7
597             << 6.
598             << 1;
599
600     QTest::newRow("no range - insert, remove all, polish, insert")
601             << QQuickPathView::NoHighlightRange
602             << (QList<ListChange>()
603                 << ListChange::insert(3, 1)
604                 << ListChange::remove(0, 9)
605                 << ListChange::polish()
606                 << ListChange::insert(0, 3))
607             << 3
608             << 0.
609             << 0;
610 }
611
612 void tst_QQuickPathView::consecutiveModelChanges()
613 {
614     QFETCH(QQuickPathView::HighlightRangeMode, mode);
615     QFETCH(QList<ListChange>, changes);
616     QFETCH(int, count);
617     QFETCH(qreal, offset);
618     QFETCH(int, currentIndex);
619
620     QQuickView *canvas = createView();
621     canvas->show();
622
623     QaimModel model;
624     model.addItem("Ben", "12345");
625     model.addItem("Bohn", "2345");
626     model.addItem("Bob", "54321");
627     model.addItem("Bill", "4321");
628     model.addItem("Jinny", "679");
629     model.addItem("Milly", "73378");
630     model.addItem("Jimmy", "3535");
631     model.addItem("Barb", "9039");
632
633     QQmlContext *ctxt = canvas->rootContext();
634     ctxt->setContextProperty("testModel", &model);
635
636     canvas->setSource(testFileUrl("pathview0.qml"));
637     qApp->processEvents();
638
639     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
640     QVERIFY(pathview != 0);
641
642     pathview->setHighlightRangeMode(mode);
643
644     pathview->setCurrentIndex(4);
645     if (mode == QQuickPathView::StrictlyEnforceRange)
646         QTRY_COMPARE(pathview->offset(), 4.0);
647     else
648         pathview->setOffset(4);
649
650     for (int i=0; i<changes.count(); i++) {
651         switch (changes[i].type) {
652             case ListChange::Inserted:
653             {
654                 QList<QPair<QString, QString> > items;
655                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
656                     items << qMakePair(QString("new item %1").arg(j), QString::number(j));
657                 model.insertItems(changes[i].index, items);
658                 break;
659             }
660             case ListChange::Removed:
661                 model.removeItems(changes[i].index, changes[i].count);
662                 break;
663             case ListChange::Moved:
664                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
665                 break;
666             case ListChange::SetCurrent:
667                 pathview->setCurrentIndex(changes[i].index);
668                 break;
669         case ListChange::Polish:
670                 QQUICK_VERIFY_POLISH(pathview);
671                 break;
672             default:
673                 continue;
674         }
675     }
676     QQUICK_VERIFY_POLISH(pathview);
677
678     QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), count);
679     QCOMPARE(pathview->count(), count);
680     QTRY_COMPARE(pathview->offset(), offset);
681
682     QCOMPARE(pathview->currentIndex(), currentIndex);
683
684     delete canvas;
685 }
686
687 void tst_QQuickPathView::path()
688 {
689     QQmlEngine engine;
690     QQmlComponent c(&engine, testFileUrl("pathtest.qml"));
691     QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
692
693     QVERIFY(obj != 0);
694     QCOMPARE(obj->startX(), 120.);
695     QCOMPARE(obj->startY(), 100.);
696     QVERIFY(obj->path() != QPainterPath());
697
698     QQmlListReference list(obj, "pathElements");
699     QCOMPARE(list.count(), 5);
700
701     QQuickPathAttribute* attr = qobject_cast<QQuickPathAttribute*>(list.at(0));
702     QVERIFY(attr != 0);
703     QCOMPARE(attr->name(), QString("scale"));
704     QCOMPARE(attr->value(), 1.0);
705
706     QQuickPathQuad* quad = qobject_cast<QQuickPathQuad*>(list.at(1));
707     QVERIFY(quad != 0);
708     QCOMPARE(quad->x(), 120.);
709     QCOMPARE(quad->y(), 25.);
710     QCOMPARE(quad->controlX(), 260.);
711     QCOMPARE(quad->controlY(), 75.);
712
713     QQuickPathPercent* perc = qobject_cast<QQuickPathPercent*>(list.at(2));
714     QVERIFY(perc != 0);
715     QCOMPARE(perc->value(), 0.3);
716
717     QQuickPathLine* line = qobject_cast<QQuickPathLine*>(list.at(3));
718     QVERIFY(line != 0);
719     QCOMPARE(line->x(), 120.);
720     QCOMPARE(line->y(), 100.);
721
722     QQuickPathCubic* cubic = qobject_cast<QQuickPathCubic*>(list.at(4));
723     QVERIFY(cubic != 0);
724     QCOMPARE(cubic->x(), 180.);
725     QCOMPARE(cubic->y(), 0.);
726     QCOMPARE(cubic->control1X(), -10.);
727     QCOMPARE(cubic->control1Y(), 90.);
728     QCOMPARE(cubic->control2X(), 210.);
729     QCOMPARE(cubic->control2Y(), 90.);
730
731     delete obj;
732 }
733
734 void tst_QQuickPathView::dataModel()
735 {
736     QQuickView *canvas = createView();
737     canvas->show();
738
739     QQmlContext *ctxt = canvas->rootContext();
740     TestObject *testObject = new TestObject;
741     ctxt->setContextProperty("testObject", testObject);
742
743     QaimModel model;
744     model.addItem("red", "1");
745     model.addItem("green", "2");
746     model.addItem("blue", "3");
747     model.addItem("purple", "4");
748     model.addItem("gray", "5");
749     model.addItem("brown", "6");
750     model.addItem("yellow", "7");
751     model.addItem("thistle", "8");
752     model.addItem("cyan", "9");
753     model.addItem("peachpuff", "10");
754     model.addItem("powderblue", "11");
755     model.addItem("gold", "12");
756     model.addItem("sandybrown", "13");
757
758     ctxt->setContextProperty("testData", &model);
759
760     canvas->setSource(testFileUrl("datamodel.qml"));
761     qApp->processEvents();
762
763     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
764     QVERIFY(pathview != 0);
765
766     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
767     QVERIFY(testObject->error() == false);
768
769     QQuickItem *item = findItem<QQuickItem>(pathview, "wrapper", 0);
770     QVERIFY(item);
771     QCOMPARE(item->x(), 110.0);
772     QCOMPARE(item->y(), 10.0);
773
774     model.insertItem(4, "orange", "10");
775     QTest::qWait(100);
776
777     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
778     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 14);
779
780     QVERIFY(pathview->currentIndex() == 0);
781     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
782
783     QQuickText *text = findItem<QQuickText>(pathview, "myText", 4);
784     QVERIFY(text);
785     QCOMPARE(text->text(), model.name(4));
786
787     model.removeItem(2);
788     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
789     text = findItem<QQuickText>(pathview, "myText", 2);
790     QVERIFY(text);
791     QCOMPARE(text->text(), model.name(2));
792     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
793
794     testObject->setPathItemCount(5);
795     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
796     QVERIFY(testObject->error() == false);
797
798     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
799
800     QQuickRectangle *testItem = findItem<QQuickRectangle>(pathview, "wrapper", 4);
801     QVERIFY(testItem != 0);
802     testItem = findItem<QQuickRectangle>(pathview, "wrapper", 5);
803     QVERIFY(testItem == 0);
804
805     pathview->setCurrentIndex(1);
806     QTRY_COMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
807     QTest::qWait(100);
808
809     model.insertItem(2, "pink", "2");
810     QTest::qWait(100);
811
812     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
813     QVERIFY(pathview->currentIndex() == 1);
814     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
815
816     text = findItem<QQuickText>(pathview, "myText", 2);
817     QVERIFY(text);
818     QCOMPARE(text->text(), model.name(2));
819
820     model.removeItem(3);
821     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
822     text = findItem<QQuickText>(pathview, "myText", 3);
823     QVERIFY(text);
824     QCOMPARE(text->text(), model.name(3));
825     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
826
827     model.moveItem(3, 5);
828     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
829     QList<QQuickItem*> items = findItems<QQuickItem>(pathview, "wrapper");
830     foreach (QQuickItem *item, items) {
831         QVERIFY(item->property("onPath").toBool());
832     }
833     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
834
835     // QTBUG-14199
836     pathview->setOffset(7);
837     pathview->setOffset(0);
838     QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
839
840     pathview->setCurrentIndex(model.count()-1);
841     QTRY_COMPARE(pathview->offset(), 1.0);
842     model.removeItem(model.count()-1);
843     QCOMPARE(pathview->currentIndex(), model.count()-1);
844
845     delete canvas;
846     delete testObject;
847 }
848
849 void tst_QQuickPathView::pathMoved()
850 {
851     QQuickView *canvas = createView();
852     canvas->show();
853
854     QaimModel model;
855     model.addItem("Ben", "12345");
856     model.addItem("Bohn", "2345");
857     model.addItem("Bob", "54321");
858     model.addItem("Bill", "4321");
859
860     QQmlContext *ctxt = canvas->rootContext();
861     ctxt->setContextProperty("testModel", &model);
862
863     canvas->setSource(testFileUrl("pathview0.qml"));
864     qApp->processEvents();
865
866     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
867     QVERIFY(pathview != 0);
868
869     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
870     QVERIFY(firstItem);
871     QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
872     QVERIFY(path);
873     QPointF start = path->pointAt(0.0);
874     QPointF offset;//Center of item is at point, but pos is from corner
875     offset.setX(firstItem->width()/2);
876     offset.setY(firstItem->height()/2);
877     QTRY_COMPARE(firstItem->pos() + offset, start);
878     pathview->setOffset(1.0);
879
880     for (int i=0; i<model.count(); i++) {
881         QQuickRectangle *curItem = findItem<QQuickRectangle>(pathview, "wrapper", i);
882         QPointF itemPos(path->pointAt(0.25 + i*0.25));
883         QCOMPARE(curItem->pos() + offset, QPointF(itemPos.x(), itemPos.y()));
884     }
885
886     QCOMPARE(pathview->currentIndex(), 3);
887
888     pathview->setOffset(0.0);
889     QCOMPARE(firstItem->pos() + offset, start);
890     QCOMPARE(pathview->currentIndex(), 0);
891
892     // Change delegate size
893     pathview->setOffset(0.1);
894     pathview->setOffset(0.0);
895     canvas->rootObject()->setProperty("delegateWidth", 30);
896     QCOMPARE(firstItem->width(), 30.0);
897     offset.setX(firstItem->width()/2);
898     QTRY_COMPARE(firstItem->pos() + offset, start);
899
900     // Change delegate scale
901     pathview->setOffset(0.1);
902     pathview->setOffset(0.0);
903     canvas->rootObject()->setProperty("delegateScale", 1.2);
904     QTRY_COMPARE(firstItem->pos() + offset, start);
905
906     delete canvas;
907 }
908
909 void tst_QQuickPathView::offset_data()
910 {
911     QTest::addColumn<qreal>("offset");
912     QTest::addColumn<int>("currentIndex");
913
914     QTest::newRow("0.0") << 0.0 << 0;
915     QTest::newRow("1.0") << 7.0 << 1;
916     QTest::newRow("5.0") << 5.0 << 3;
917     QTest::newRow("4.6") << 4.6 << 3;
918     QTest::newRow("4.4") << 4.4 << 4;
919     QTest::newRow("5.4") << 5.4 << 3;
920     QTest::newRow("5.6") << 5.6 << 2;
921 }
922
923 void tst_QQuickPathView::offset()
924 {
925     QFETCH(qreal, offset);
926     QFETCH(int, currentIndex);
927
928     QQmlEngine engine;
929     QQmlComponent c(&engine, testFileUrl("pathview3.qml"));
930     QQuickPathView *view = qobject_cast<QQuickPathView*>(c.create());
931
932     view->setOffset(offset);
933     QCOMPARE(view->currentIndex(), currentIndex);
934
935     delete view;
936 }
937
938 void tst_QQuickPathView::setCurrentIndex()
939 {
940     QQuickView *canvas = createView();
941     canvas->show();
942
943     QaimModel model;
944     model.addItem("Ben", "12345");
945     model.addItem("Bohn", "2345");
946     model.addItem("Bob", "54321");
947     model.addItem("Bill", "4321");
948
949     QQmlContext *ctxt = canvas->rootContext();
950     ctxt->setContextProperty("testModel", &model);
951
952     canvas->setSource(testFileUrl("pathview0.qml"));
953     qApp->processEvents();
954
955     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
956     QVERIFY(pathview != 0);
957
958     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
959     QVERIFY(firstItem);
960     QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
961     QVERIFY(path);
962     QPointF start = path->pointAt(0.0);
963     QPointF offset;//Center of item is at point, but pos is from corner
964     offset.setX(firstItem->width()/2);
965     offset.setY(firstItem->height()/2);
966     QCOMPARE(firstItem->pos() + offset, start);
967     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 0);
968     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 0);
969
970     pathview->setCurrentIndex(2);
971
972     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
973     QTRY_COMPARE(firstItem->pos() + offset, start);
974     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 2);
975     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 2);
976     QCOMPARE(pathview->currentItem(), firstItem);
977     QCOMPARE(firstItem->property("onPath"), QVariant(true));
978
979     pathview->decrementCurrentIndex();
980     QTRY_COMPARE(pathview->currentIndex(), 1);
981     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
982     QVERIFY(firstItem);
983     QTRY_COMPARE(firstItem->pos() + offset, start);
984     QCOMPARE(pathview->currentItem(), firstItem);
985     QCOMPARE(firstItem->property("onPath"), QVariant(true));
986
987     pathview->decrementCurrentIndex();
988     QTRY_COMPARE(pathview->currentIndex(), 0);
989     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
990     QVERIFY(firstItem);
991     QTRY_COMPARE(firstItem->pos() + offset, start);
992     QCOMPARE(pathview->currentItem(), firstItem);
993     QCOMPARE(firstItem->property("onPath"), QVariant(true));
994
995     pathview->decrementCurrentIndex();
996     QTRY_COMPARE(pathview->currentIndex(), 3);
997     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
998     QVERIFY(firstItem);
999     QTRY_COMPARE(firstItem->pos() + offset, start);
1000     QCOMPARE(pathview->currentItem(), firstItem);
1001     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1002
1003     pathview->incrementCurrentIndex();
1004     QTRY_COMPARE(pathview->currentIndex(), 0);
1005     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1006     QVERIFY(firstItem);
1007     QTRY_COMPARE(firstItem->pos() + offset, start);
1008     QCOMPARE(pathview->currentItem(), firstItem);
1009     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1010
1011     // Test positive indexes are wrapped.
1012     pathview->setCurrentIndex(6);
1013     QTRY_COMPARE(pathview->currentIndex(), 2);
1014     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
1015     QVERIFY(firstItem);
1016     QTRY_COMPARE(firstItem->pos() + offset, start);
1017     QCOMPARE(pathview->currentItem(), firstItem);
1018     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1019
1020     // Test negative indexes are wrapped.
1021     pathview->setCurrentIndex(-3);
1022     QTRY_COMPARE(pathview->currentIndex(), 1);
1023     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
1024     QVERIFY(firstItem);
1025     QTRY_COMPARE(firstItem->pos() + offset, start);
1026     QCOMPARE(pathview->currentItem(), firstItem);
1027     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1028
1029     // move an item, set move duration to 0, and change currentIndex to moved item. QTBUG-22786
1030     model.moveItem(0, 3);
1031     pathview->setHighlightMoveDuration(0);
1032     pathview->setCurrentIndex(3);
1033     QCOMPARE(pathview->currentIndex(), 3);
1034     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
1035     QVERIFY(firstItem);
1036     QCOMPARE(pathview->currentItem(), firstItem);
1037     QTRY_COMPARE(firstItem->pos() + offset, start);
1038     model.moveItem(3, 0);
1039     pathview->setCurrentIndex(0);
1040     pathview->setHighlightMoveDuration(300);
1041
1042     // Check the current item is still created when outside the bounds of pathItemCount.
1043     pathview->setPathItemCount(2);
1044     pathview->setHighlightRangeMode(QQuickPathView::NoHighlightRange);
1045     QVERIFY(findItem<QQuickRectangle>(pathview, "wrapper", 0));
1046     QVERIFY(findItem<QQuickRectangle>(pathview, "wrapper", 1));
1047     QVERIFY(!findItem<QQuickRectangle>(pathview, "wrapper", 2));
1048     QVERIFY(!findItem<QQuickRectangle>(pathview, "wrapper", 3));
1049
1050     pathview->setCurrentIndex(2);
1051     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
1052     QCOMPARE(pathview->currentItem(), firstItem);
1053     QCOMPARE(firstItem->property("onPath"), QVariant(false));
1054
1055     pathview->decrementCurrentIndex();
1056     QTRY_COMPARE(pathview->currentIndex(), 1);
1057     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
1058     QVERIFY(firstItem);
1059     QCOMPARE(pathview->currentItem(), firstItem);
1060     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1061
1062     pathview->decrementCurrentIndex();
1063     QTRY_COMPARE(pathview->currentIndex(), 0);
1064     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1065     QVERIFY(firstItem);
1066     QCOMPARE(pathview->currentItem(), firstItem);
1067     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1068
1069     pathview->decrementCurrentIndex();
1070     QTRY_COMPARE(pathview->currentIndex(), 3);
1071     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
1072     QVERIFY(firstItem);
1073     QCOMPARE(pathview->currentItem(), firstItem);
1074     QCOMPARE(firstItem->property("onPath"), QVariant(false));
1075
1076     pathview->incrementCurrentIndex();
1077     QTRY_COMPARE(pathview->currentIndex(), 0);
1078     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1079     QVERIFY(firstItem);
1080     QCOMPARE(pathview->currentItem(), firstItem);
1081     QCOMPARE(firstItem->property("onPath"), QVariant(true));
1082
1083     delete canvas;
1084 }
1085
1086 void tst_QQuickPathView::resetModel()
1087 {
1088     QQuickView *canvas = createView();
1089
1090     QStringList strings;
1091     strings << "one" << "two" << "three";
1092     QStringListModel model(strings);
1093
1094     QQmlContext *ctxt = canvas->rootContext();
1095     ctxt->setContextProperty("testModel", &model);
1096
1097     canvas->setSource(testFileUrl("displaypath.qml"));
1098     qApp->processEvents();
1099
1100     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1101     QVERIFY(pathview != 0);
1102
1103     QCOMPARE(pathview->count(), model.rowCount());
1104
1105     for (int i = 0; i < model.rowCount(); ++i) {
1106         QQuickText *display = findItem<QQuickText>(pathview, "displayText", i);
1107         QVERIFY(display != 0);
1108         QCOMPARE(display->text(), strings.at(i));
1109     }
1110
1111     strings.clear();
1112     strings << "four" << "five" << "six" << "seven";
1113     model.setStringList(strings);
1114
1115     QCOMPARE(pathview->count(), model.rowCount());
1116
1117     for (int i = 0; i < model.rowCount(); ++i) {
1118         QQuickText *display = findItem<QQuickText>(pathview, "displayText", i);
1119         QVERIFY(display != 0);
1120         QCOMPARE(display->text(), strings.at(i));
1121     }
1122
1123     delete canvas;
1124 }
1125
1126 void tst_QQuickPathView::propertyChanges()
1127 {
1128     QQuickView *canvas = createView();
1129     QVERIFY(canvas);
1130     canvas->setSource(testFileUrl("propertychanges.qml"));
1131
1132     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1133     QVERIFY(pathView);
1134
1135     QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
1136     QSignalSpy dragMarginSpy(pathView, SIGNAL(dragMarginChanged()));
1137
1138     QCOMPARE(pathView->preferredHighlightBegin(), 0.1);
1139     QCOMPARE(pathView->dragMargin(), 5.0);
1140
1141     pathView->setPreferredHighlightBegin(0.4);
1142     pathView->setPreferredHighlightEnd(0.4);
1143     pathView->setDragMargin(20.0);
1144
1145     QCOMPARE(pathView->preferredHighlightBegin(), 0.4);
1146     QCOMPARE(pathView->preferredHighlightEnd(), 0.4);
1147     QCOMPARE(pathView->dragMargin(), 20.0);
1148
1149     QCOMPARE(snapPositionSpy.count(), 1);
1150     QCOMPARE(dragMarginSpy.count(), 1);
1151
1152     pathView->setPreferredHighlightBegin(0.4);
1153     pathView->setPreferredHighlightEnd(0.4);
1154     pathView->setDragMargin(20.0);
1155
1156     QCOMPARE(snapPositionSpy.count(), 1);
1157     QCOMPARE(dragMarginSpy.count(), 1);
1158
1159     QSignalSpy maximumFlickVelocitySpy(pathView, SIGNAL(maximumFlickVelocityChanged()));
1160     pathView->setMaximumFlickVelocity(1000);
1161     QCOMPARE(maximumFlickVelocitySpy.count(), 1);
1162     pathView->setMaximumFlickVelocity(1000);
1163     QCOMPARE(maximumFlickVelocitySpy.count(), 1);
1164
1165     delete canvas;
1166 }
1167
1168 void tst_QQuickPathView::pathChanges()
1169 {
1170     QQuickView *canvas = createView();
1171     QVERIFY(canvas);
1172     canvas->setSource(testFileUrl("propertychanges.qml"));
1173
1174     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1175     QVERIFY(pathView);
1176
1177     QQuickPath *path = canvas->rootObject()->findChild<QQuickPath*>("path");
1178     QVERIFY(path);
1179
1180     QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
1181     QSignalSpy startYSpy(path, SIGNAL(startYChanged()));
1182
1183     QCOMPARE(path->startX(), 220.0);
1184     QCOMPARE(path->startY(), 200.0);
1185
1186     path->setStartX(240.0);
1187     path->setStartY(220.0);
1188
1189     QCOMPARE(path->startX(), 240.0);
1190     QCOMPARE(path->startY(), 220.0);
1191
1192     QCOMPARE(startXSpy.count(),1);
1193     QCOMPARE(startYSpy.count(),1);
1194
1195     path->setStartX(240);
1196     path->setStartY(220);
1197
1198     QCOMPARE(startXSpy.count(),1);
1199     QCOMPARE(startYSpy.count(),1);
1200
1201     QQuickPath *alternatePath = canvas->rootObject()->findChild<QQuickPath*>("alternatePath");
1202     QVERIFY(alternatePath);
1203
1204     QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
1205
1206     QCOMPARE(pathView->path(), path);
1207
1208     pathView->setPath(alternatePath);
1209     QCOMPARE(pathView->path(), alternatePath);
1210     QCOMPARE(pathSpy.count(),1);
1211
1212     pathView->setPath(alternatePath);
1213     QCOMPARE(pathSpy.count(),1);
1214
1215     QQuickPathAttribute *pathAttribute = canvas->rootObject()->findChild<QQuickPathAttribute*>("pathAttribute");
1216     QVERIFY(pathAttribute);
1217
1218     QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
1219     QCOMPARE(pathAttribute->name(), QString("opacity"));
1220
1221     pathAttribute->setName("scale");
1222     QCOMPARE(pathAttribute->name(), QString("scale"));
1223     QCOMPARE(nameSpy.count(),1);
1224
1225     pathAttribute->setName("scale");
1226     QCOMPARE(nameSpy.count(),1);
1227     delete canvas;
1228 }
1229
1230 void tst_QQuickPathView::componentChanges()
1231 {
1232     QQuickView *canvas = createView();
1233     QVERIFY(canvas);
1234     canvas->setSource(testFileUrl("propertychanges.qml"));
1235
1236     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1237     QVERIFY(pathView);
1238
1239     QQmlComponent delegateComponent(canvas->engine());
1240     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
1241
1242     QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
1243
1244     pathView->setDelegate(&delegateComponent);
1245     QCOMPARE(pathView->delegate(), &delegateComponent);
1246     QCOMPARE(delegateSpy.count(),1);
1247
1248     pathView->setDelegate(&delegateComponent);
1249     QCOMPARE(delegateSpy.count(),1);
1250     delete canvas;
1251 }
1252
1253 void tst_QQuickPathView::modelChanges()
1254 {
1255     QQuickView *canvas = createView();
1256     QVERIFY(canvas);
1257     canvas->setSource(testFileUrl("propertychanges.qml"));
1258
1259     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1260     QVERIFY(pathView);
1261
1262     QQuickListModel *alternateModel = canvas->rootObject()->findChild<QQuickListModel*>("alternateModel");
1263     QVERIFY(alternateModel);
1264     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
1265     QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
1266
1267     pathView->setModel(modelVariant);
1268     QCOMPARE(pathView->model(), modelVariant);
1269     QCOMPARE(modelSpy.count(),1);
1270
1271     pathView->setModel(modelVariant);
1272     QCOMPARE(modelSpy.count(),1);
1273
1274     pathView->setModel(QVariant());
1275     QCOMPARE(modelSpy.count(),2);
1276
1277     delete canvas;
1278 }
1279
1280 void tst_QQuickPathView::pathUpdateOnStartChanged()
1281 {
1282     QQuickView *canvas = createView();
1283     QVERIFY(canvas);
1284     canvas->setSource(testFileUrl("pathUpdateOnStartChanged.qml"));
1285
1286     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1287     QVERIFY(pathView);
1288
1289     QQuickPath *path = canvas->rootObject()->findChild<QQuickPath*>("path");
1290     QVERIFY(path);
1291     QCOMPARE(path->startX(), 400.0);
1292     QCOMPARE(path->startY(), 300.0);
1293
1294     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1295     QVERIFY(item);
1296     QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
1297     QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
1298
1299     delete canvas;
1300 }
1301
1302 void tst_QQuickPathView::package()
1303 {
1304     QQuickView *canvas = createView();
1305     QVERIFY(canvas);
1306     canvas->setSource(testFileUrl("pathview_package.qml"));
1307     canvas->show();
1308     QTest::qWaitForWindowShown(canvas);
1309
1310     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("photoPathView");
1311     QVERIFY(pathView);
1312
1313 #ifdef Q_OS_MAC
1314     QSKIP("QTBUG-21590 view does not reliably receive polish without a running animation");
1315 #endif
1316
1317     QQuickItem *item = findItem<QQuickItem>(pathView, "pathItem");
1318     QVERIFY(item);
1319     QVERIFY(item->scale() != 1.0);
1320
1321     delete canvas;
1322 }
1323
1324 //QTBUG-13017
1325 void tst_QQuickPathView::emptyModel()
1326 {
1327     QQuickView *canvas = createView();
1328
1329     QStringListModel model;
1330
1331     QQmlContext *ctxt = canvas->rootContext();
1332     ctxt->setContextProperty("emptyModel", &model);
1333
1334     canvas->setSource(testFileUrl("emptymodel.qml"));
1335     qApp->processEvents();
1336
1337     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1338     QVERIFY(pathview != 0);
1339
1340     QCOMPARE(pathview->offset(), qreal(0.0));
1341
1342     delete canvas;
1343 }
1344
1345 void tst_QQuickPathView::closed()
1346 {
1347     QQmlEngine engine;
1348
1349     {
1350         QQmlComponent c(&engine, testFileUrl("openPath.qml"));
1351         QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
1352         QVERIFY(obj);
1353         QCOMPARE(obj->isClosed(), false);
1354         delete obj;
1355     }
1356
1357     {
1358         QQmlComponent c(&engine, testFileUrl("closedPath.qml"));
1359         QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
1360         QVERIFY(obj);
1361         QCOMPARE(obj->isClosed(), true);
1362         delete obj;
1363     }
1364 }
1365
1366 // QTBUG-14239
1367 void tst_QQuickPathView::pathUpdate()
1368 {
1369     QQuickView *canvas = createView();
1370     QVERIFY(canvas);
1371     canvas->setSource(testFileUrl("pathUpdate.qml"));
1372
1373     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1374     QVERIFY(pathView);
1375
1376     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1377     QVERIFY(item);
1378     QCOMPARE(item->x(), 150.0);
1379
1380     delete canvas;
1381 }
1382
1383 void tst_QQuickPathView::visualDataModel()
1384 {
1385     QQmlEngine engine;
1386     QQmlComponent c(&engine, testFileUrl("vdm.qml"));
1387
1388     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1389     QVERIFY(obj != 0);
1390
1391     QCOMPARE(obj->count(), 3);
1392
1393     delete obj;
1394 }
1395
1396 void tst_QQuickPathView::undefinedPath()
1397 {
1398     QQmlEngine engine;
1399
1400     // QPainterPath warnings are only received if QT_NO_DEBUG is not defined
1401     if (QLibraryInfo::isDebugBuild()) {
1402         QString warning1("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
1403         QTest::ignoreMessage(QtWarningMsg,qPrintable(warning1));
1404
1405         QString warning2("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
1406         QTest::ignoreMessage(QtWarningMsg,qPrintable(warning2));
1407     }
1408
1409     QQmlComponent c(&engine, testFileUrl("undefinedpath.qml"));
1410
1411     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1412     QVERIFY(obj != 0);
1413
1414     QCOMPARE(obj->count(), 3);
1415
1416     delete obj;
1417 }
1418
1419 void tst_QQuickPathView::mouseDrag()
1420 {
1421     QQuickView *canvas = createView();
1422     canvas->setSource(testFileUrl("dragpath.qml"));
1423     canvas->show();
1424     canvas->requestActivateWindow();
1425     QTest::qWaitForWindowShown(canvas);
1426     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1427
1428     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1429     QVERIFY(pathview != 0);
1430
1431     QSignalSpy movingSpy(pathview, SIGNAL(movingChanged()));
1432     QSignalSpy moveStartedSpy(pathview, SIGNAL(movementStarted()));
1433     QSignalSpy moveEndedSpy(pathview, SIGNAL(movementEnded()));
1434     QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged()));
1435     QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted()));
1436     QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded()));
1437
1438     int current = pathview->currentIndex();
1439
1440     QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
1441     QTest::qWait(100);
1442
1443     {
1444         QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1445         QGuiApplication::sendEvent(canvas, &mv);
1446     }
1447     // first move beyond threshold does not trigger drag
1448     QVERIFY(!pathview->isMoving());
1449     QVERIFY(!pathview->isDragging());
1450     QCOMPARE(movingSpy.count(), 0);
1451     QCOMPARE(moveStartedSpy.count(), 0);
1452     QCOMPARE(moveEndedSpy.count(), 0);
1453     QCOMPARE(draggingSpy.count(), 0);
1454     QCOMPARE(dragStartedSpy.count(), 0);
1455     QCOMPARE(dragEndedSpy.count(), 0);
1456
1457     {
1458         QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1459         QGuiApplication::sendEvent(canvas, &mv);
1460     }
1461     // next move beyond threshold does trigger drag
1462     QVERIFY(pathview->isMoving());
1463     QVERIFY(pathview->isDragging());
1464     QCOMPARE(movingSpy.count(), 1);
1465     QCOMPARE(moveStartedSpy.count(), 1);
1466     QCOMPARE(moveEndedSpy.count(), 0);
1467     QCOMPARE(draggingSpy.count(), 1);
1468     QCOMPARE(dragStartedSpy.count(), 1);
1469     QCOMPARE(dragEndedSpy.count(), 0);
1470
1471     QVERIFY(pathview->currentIndex() != current);
1472
1473     QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
1474     QVERIFY(!pathview->isDragging());
1475     QCOMPARE(draggingSpy.count(), 2);
1476     QCOMPARE(dragStartedSpy.count(), 1);
1477     QCOMPARE(dragEndedSpy.count(), 1);
1478     QTRY_COMPARE(movingSpy.count(), 2);
1479     QTRY_COMPARE(moveEndedSpy.count(), 1);
1480     QCOMPARE(moveStartedSpy.count(), 1);
1481
1482     delete canvas;
1483 }
1484
1485 #ifndef QT_NO_WIDGETS
1486 void tst_QQuickPathView::treeModel()
1487 {
1488     QQuickView *canvas = createView();
1489     canvas->show();
1490
1491     QStandardItemModel model;
1492     initStandardTreeModel(&model);
1493     canvas->engine()->rootContext()->setContextProperty("myModel", &model);
1494
1495     canvas->setSource(testFileUrl("treemodel.qml"));
1496
1497     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1498     QVERIFY(pathview != 0);
1499     QCOMPARE(pathview->count(), 3);
1500
1501     QQuickText *item = findItem<QQuickText>(pathview, "wrapper", 0);
1502     QVERIFY(item);
1503     QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
1504
1505     QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
1506     QCOMPARE(pathview->count(), 1);
1507
1508     QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0));
1509     QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
1510
1511     delete canvas;
1512 }
1513 #endif
1514
1515 void tst_QQuickPathView::changePreferredHighlight()
1516 {
1517     QQuickView *canvas = createView();
1518     canvas->setGeometry(0,0,400,200);
1519     canvas->setSource(testFileUrl("dragpath.qml"));
1520     canvas->show();
1521     canvas->requestActivateWindow();
1522     QTest::qWaitForWindowShown(canvas);
1523     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1524
1525     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1526     QVERIFY(pathview != 0);
1527
1528     int current = pathview->currentIndex();
1529     QCOMPARE(current, 0);
1530
1531     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1532     QVERIFY(firstItem);
1533     QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
1534     QVERIFY(path);
1535     QPointF start = path->pointAt(0.5);
1536     QPointF offset;//Center of item is at point, but pos is from corner
1537     offset.setX(firstItem->width()/2);
1538     offset.setY(firstItem->height()/2);
1539     QTRY_COMPARE(firstItem->pos() + offset, start);
1540
1541     pathview->setPreferredHighlightBegin(0.8);
1542     pathview->setPreferredHighlightEnd(0.8);
1543     start = path->pointAt(0.8);
1544     QTRY_COMPARE(firstItem->pos() + offset, start);
1545     QCOMPARE(pathview->currentIndex(), 0);
1546
1547     delete canvas;
1548 }
1549
1550 void tst_QQuickPathView::creationContext()
1551 {
1552     QQuickView canvas;
1553     canvas.setGeometry(0,0,240,320);
1554     canvas.setSource(testFileUrl("creationContext.qml"));
1555
1556     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
1557     QVERIFY(rootItem);
1558     QVERIFY(rootItem->property("count").toInt() > 0);
1559
1560     QQuickItem *item;
1561     QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem", 0));
1562     QCOMPARE(item->property("text").toString(), QString("Hello!"));
1563 }
1564
1565 // QTBUG-21320
1566 void tst_QQuickPathView::currentOffsetOnInsertion()
1567 {
1568     QQuickView *canvas = createView();
1569     canvas->show();
1570
1571     QaimModel model;
1572
1573     QQmlContext *ctxt = canvas->rootContext();
1574     ctxt->setContextProperty("testModel", &model);
1575
1576     canvas->setSource(testFileUrl("pathline.qml"));
1577     qApp->processEvents();
1578
1579     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1580     QVERIFY(pathview != 0);
1581
1582     pathview->setPreferredHighlightBegin(0.5);
1583     pathview->setPreferredHighlightEnd(0.5);
1584
1585     QCOMPARE(pathview->count(), model.count());
1586
1587     model.addItem("item0", "0");
1588
1589     QCOMPARE(pathview->count(), model.count());
1590
1591     QQuickRectangle *item = 0;
1592     QTRY_VERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 0));
1593
1594     QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
1595     QVERIFY(path);
1596
1597     QPointF start = path->pointAt(0.5);
1598     QPointF offset;//Center of item is at point, but pos is from corner
1599     offset.setX(item->width()/2);
1600     offset.setY(item->height()/2);
1601     QCOMPARE(item->pos() + offset, start);
1602
1603     QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged()));
1604
1605     // insert an item at the beginning
1606     model.insertItem(0, "item1", "1");
1607     qApp->processEvents();
1608
1609     QCOMPARE(currentIndexSpy.count(), 1);
1610
1611     // currentIndex is now 1
1612     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1613
1614     // verify that current item (item 1) is still at offset 0.5
1615     QCOMPARE(item->pos() + offset, start);
1616
1617     // insert another item at the beginning
1618     model.insertItem(0, "item2", "2");
1619     qApp->processEvents();
1620
1621     QCOMPARE(currentIndexSpy.count(), 2);
1622
1623     // currentIndex is now 2
1624     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 2));
1625
1626     // verify that current item (item 2) is still at offset 0.5
1627     QCOMPARE(item->pos() + offset, start);
1628
1629     // verify that remove before current maintains current item
1630     model.removeItem(0);
1631     qApp->processEvents();
1632
1633     QCOMPARE(currentIndexSpy.count(), 3);
1634
1635     // currentIndex is now 1
1636     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1637
1638     // verify that current item (item 1) is still at offset 0.5
1639     QCOMPARE(item->pos() + offset, start);
1640
1641     delete canvas;
1642 }
1643
1644 void tst_QQuickPathView::asynchronous()
1645 {
1646     QQuickView *canvas = createView();
1647     canvas->show();
1648     QQmlIncubationController controller;
1649     canvas->engine()->setIncubationController(&controller);
1650
1651     canvas->setSource(testFileUrl("asyncloader.qml"));
1652
1653     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
1654     QVERIFY(rootObject);
1655
1656     QQuickPathView *pathview = 0;
1657     while (!pathview) {
1658         bool b = false;
1659         controller.incubateWhile(&b);
1660         pathview = rootObject->findChild<QQuickPathView*>("view");
1661     }
1662
1663     // items will be created one at a time
1664     for (int i = 0; i < 5; ++i) {
1665         QVERIFY(findItem<QQuickItem>(pathview, "wrapper", i) == 0);
1666         QQuickItem *item = 0;
1667         while (!item) {
1668             bool b = false;
1669             controller.incubateWhile(&b);
1670             item = findItem<QQuickItem>(pathview, "wrapper", i);
1671         }
1672     }
1673
1674     {
1675         bool b = true;
1676         controller.incubateWhile(&b);
1677     }
1678
1679     // verify positioning
1680     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1681     QVERIFY(firstItem);
1682     QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
1683     QVERIFY(path);
1684     QPointF start = path->pointAt(0.0);
1685     QPointF offset;//Center of item is at point, but pos is from corner
1686     offset.setX(firstItem->width()/2);
1687     offset.setY(firstItem->height()/2);
1688     QTRY_COMPARE(firstItem->pos() + offset, start);
1689     pathview->setOffset(1.0);
1690
1691     for (int i=0; i<5; i++) {
1692         QQuickItem *curItem = findItem<QQuickItem>(pathview, "wrapper", i);
1693         QPointF itemPos(path->pointAt(0.2 + i*0.2));
1694         QCOMPARE(curItem->pos() + offset, itemPos);
1695     }
1696
1697     delete canvas;
1698 }
1699
1700 void tst_QQuickPathView::missingPercent()
1701 {
1702     QQmlEngine engine;
1703     QQmlComponent c(&engine, testFileUrl("missingPercent.qml"));
1704     QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
1705     QVERIFY(obj);
1706     QCOMPARE(obj->attributeAt("_qfx_percent", 1.0), qreal(1.0));
1707     delete obj;
1708 }
1709
1710 void tst_QQuickPathView::cancelDrag()
1711 {
1712     QQuickView *canvas = createView();
1713     canvas->setSource(testFileUrl("dragpath.qml"));
1714     canvas->show();
1715     canvas->requestActivateWindow();
1716     QTest::qWaitForWindowShown(canvas);
1717     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1718
1719     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1720     QVERIFY(pathview != 0);
1721
1722     QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged()));
1723     QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted()));
1724     QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded()));
1725
1726     // drag between snap points
1727     QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
1728     QTest::qWait(100);
1729     QTest::mouseMove(canvas, QPoint(30, 100));
1730     QTest::mouseMove(canvas, QPoint(85, 100));
1731
1732     QTRY_VERIFY(pathview->offset() != qFloor(pathview->offset()));
1733     QTRY_VERIFY(pathview->isMoving());
1734     QVERIFY(pathview->isDragging());
1735     QCOMPARE(draggingSpy.count(), 1);
1736     QCOMPARE(dragStartedSpy.count(), 1);
1737     QCOMPARE(dragEndedSpy.count(), 0);
1738
1739     // steal mouse grab - cancels PathView dragging
1740     QQuickItem *item = canvas->rootObject()->findChild<QQuickItem*>("text");
1741     item->grabMouse();
1742
1743     // returns to a snap point.
1744     QTRY_VERIFY(pathview->offset() == qFloor(pathview->offset()));
1745     QTRY_VERIFY(!pathview->isMoving());
1746     QVERIFY(!pathview->isDragging());
1747     QCOMPARE(draggingSpy.count(), 2);
1748     QCOMPARE(dragStartedSpy.count(), 1);
1749     QCOMPARE(dragEndedSpy.count(), 1);
1750
1751     QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
1752
1753     delete canvas;
1754 }
1755
1756 void tst_QQuickPathView::maximumFlickVelocity()
1757 {
1758     QQuickView *canvas = createView();
1759     canvas->setSource(testFileUrl("dragpath.qml"));
1760     canvas->show();
1761     canvas->requestActivateWindow();
1762     QTest::qWaitForWindowShown(canvas);
1763     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1764
1765     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1766     QVERIFY(pathview != 0);
1767
1768     pathview->setMaximumFlickVelocity(700);
1769     flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1770     QVERIFY(pathview->isMoving());
1771     QVERIFY(pathview->isFlicking());
1772     QTRY_VERIFY(!pathview->isMoving());
1773
1774     double dist1 = 100 - pathview->offset();
1775
1776     pathview->setOffset(0.);
1777     pathview->setMaximumFlickVelocity(300);
1778     flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1779     QVERIFY(pathview->isMoving());
1780     QVERIFY(pathview->isFlicking());
1781     QTRY_VERIFY(!pathview->isMoving());
1782
1783     double dist2 = 100 - pathview->offset();
1784
1785     pathview->setOffset(0.);
1786     pathview->setMaximumFlickVelocity(500);
1787     flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1788     QVERIFY(pathview->isMoving());
1789     QVERIFY(pathview->isFlicking());
1790     QTRY_VERIFY(!pathview->isMoving());
1791
1792     double dist3 = 100 - pathview->offset();
1793
1794     QVERIFY(dist1 > dist2);
1795     QVERIFY(dist3 > dist2);
1796     QVERIFY(dist2 < dist1);
1797
1798     delete canvas;
1799 }
1800
1801 void tst_QQuickPathView::snapToItem()
1802 {
1803     QFETCH(bool, enforceRange);
1804
1805     QQuickView *canvas = createView();
1806     canvas->setSource(testFileUrl("panels.qml"));
1807     QQuickPathView *pathview = canvas->rootObject()->findChild<QQuickPathView*>("view");
1808     QVERIFY(pathview != 0);
1809
1810     canvas->rootObject()->setProperty("enforceRange", enforceRange);
1811     QTRY_VERIFY(!pathview->isMoving()); // ensure stable
1812
1813     int currentIndex = pathview->currentIndex();
1814
1815     QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged()));
1816
1817     flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1818
1819     QVERIFY(pathview->isMoving());
1820     QTRY_VERIFY(!pathview->isMoving());
1821
1822     QVERIFY(pathview->offset() == qFloor(pathview->offset()));
1823
1824     if (enforceRange)
1825         QVERIFY(pathview->currentIndex() != currentIndex);
1826     else
1827         QVERIFY(pathview->currentIndex() == currentIndex);
1828 }
1829
1830 void tst_QQuickPathView::snapToItem_data()
1831 {
1832     QTest::addColumn<bool>("enforceRange");
1833
1834     QTest::newRow("no enforce range") << false;
1835     QTest::newRow("enforce range") << true;
1836 }
1837
1838 void tst_QQuickPathView::snapOneItem()
1839 {
1840     QFETCH(bool, enforceRange);
1841
1842     QQuickView *canvas = createView();
1843     canvas->setSource(testFileUrl("panels.qml"));
1844     canvas->show();
1845     canvas->requestActivateWindow();
1846     QTest::qWaitForWindowShown(canvas);
1847     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1848
1849     QQuickPathView *pathview = canvas->rootObject()->findChild<QQuickPathView*>("view");
1850     QVERIFY(pathview != 0);
1851
1852     canvas->rootObject()->setProperty("enforceRange", enforceRange);
1853
1854     QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged()));
1855
1856     canvas->rootObject()->setProperty("snapOne", true);
1857     QVERIFY(snapModeSpy.count() == 1);
1858     QTRY_VERIFY(!pathview->isMoving()); // ensure stable
1859
1860     int currentIndex = pathview->currentIndex();
1861
1862     double startOffset = pathview->offset();
1863     flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1864
1865     QVERIFY(pathview->isMoving());
1866     QTRY_VERIFY(!pathview->isMoving());
1867
1868     // must have moved only one item
1869     QCOMPARE(pathview->offset(), fmodf(3.0 + startOffset - 1.0, 3.0));
1870
1871     if (enforceRange)
1872         QVERIFY(pathview->currentIndex() == currentIndex+1);
1873     else
1874         QVERIFY(pathview->currentIndex() == currentIndex);
1875 }
1876
1877 void tst_QQuickPathView::snapOneItem_data()
1878 {
1879     QTest::addColumn<bool>("enforceRange");
1880
1881     QTest::newRow("no enforce range") << false;
1882     QTest::newRow("enforce range") << true;
1883 }
1884
1885
1886 QTEST_MAIN(tst_QQuickPathView)
1887
1888 #include "tst_qquickpathview.moc"