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