1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <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>
58 #include "../../shared/util.h"
59 #include "../shared/viewtestutil.h"
60 #include "../shared/visualtestutil.h"
62 using namespace QQuickViewTestUtil;
63 using namespace QQuickVisualTestUtil;
65 Q_DECLARE_METATYPE(QQuickPathView::HighlightRangeMode)
68 #include <QStandardItemModel>
69 static void initStandardTreeModel(QStandardItemModel *model)
72 item = new QStandardItem(QLatin1String("Row 1 Item"));
73 model->insertRow(0, item);
75 item = new QStandardItem(QLatin1String("Row 2 Item"));
76 item->setCheckable(true);
77 model->insertRow(1, item);
79 QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
80 item->setChild(0, childItem);
82 item = new QStandardItem(QLatin1String("Row 3 Item"));
83 item->setIcon(QIcon());
84 model->insertRow(2, item);
89 class tst_QQuickPathView : public QQmlDataTest
101 void insertModel_data();
103 void removeModel_data();
105 void moveModel_data();
107 void consecutiveModelChanges_data();
108 void consecutiveModelChanges();
113 void setCurrentIndex();
115 void propertyChanges();
117 void componentChanges();
119 void pathUpdateOnStartChanged();
124 void visualDataModel();
125 void undefinedPath();
127 #ifndef QT_NO_WIDGETS
130 void changePreferredHighlight();
131 void missingPercent();
132 void creationContext();
133 void currentOffsetOnInsertion();
136 void maximumFlickVelocity();
138 void snapToItem_data();
140 void snapOneItem_data();
143 class TestObject : public QObject
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)
152 TestObject() : QObject(), mError(true), mUseModel(true), mPathItemCount(-1) {}
154 bool error() const { return mError; }
155 void setError(bool err) { mError = err; }
157 bool useModel() const { return mUseModel; }
158 void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
160 int pathItemCount() const { return mPathItemCount; }
161 void setPathItemCount(int count) { mPathItemCount = count; emit pathItemCountChanged(); }
164 void useModelChanged();
165 void pathItemCountChanged();
173 tst_QQuickPathView::tst_QQuickPathView()
177 void tst_QQuickPathView::initValues()
180 QQmlComponent c(&engine, testFileUrl("pathview1.qml"));
181 QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
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);
197 void tst_QQuickPathView::items()
199 QQuickView *canvas = createView();
202 model.addItem("Fred", "12345");
203 model.addItem("John", "2345");
204 model.addItem("Bob", "54321");
205 model.addItem("Bill", "4321");
207 QQmlContext *ctxt = canvas->rootContext();
208 ctxt->setContextProperty("testModel", &model);
210 canvas->setSource(testFileUrl("pathview0.qml"));
211 qApp->processEvents();
213 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
214 QVERIFY(pathview != 0);
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
220 for (int i = 0; i < model.count(); ++i) {
221 QQuickText *name = findItem<QQuickText>(pathview, "textName", i);
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));
229 QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
232 QVERIFY(pathview->highlightItem());
233 QPointF start = path->pointAt(0.0);
235 offset.setX(pathview->highlightItem()->width()/2);
236 offset.setY(pathview->highlightItem()->height()/2);
237 QCOMPARE(pathview->highlightItem()->pos() + offset, start);
242 void tst_QQuickPathView::pathview2()
245 QQmlComponent c(&engine, testFileUrl("pathview2.qml"));
246 QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
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);
262 void tst_QQuickPathView::pathview3()
265 QQmlComponent c(&engine, testFileUrl("pathview3.qml"));
266 QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
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);
282 void tst_QQuickPathView::insertModel_data()
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");
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;
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;
322 void tst_QQuickPathView::insertModel()
327 QFETCH(qreal, offset);
328 QFETCH(int, currentIndex);
330 QQuickView *canvas = createView();
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");
343 QQmlContext *ctxt = canvas->rootContext();
344 ctxt->setContextProperty("testModel", &model);
346 canvas->setSource(testFileUrl("pathview0.qml"));
347 qApp->processEvents();
349 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
350 QVERIFY(pathview != 0);
352 pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
354 pathview->setCurrentIndex(4);
355 if (mode == QQuickPathView::StrictlyEnforceRange)
356 QTRY_COMPARE(pathview->offset(), 4.0);
358 pathview->setOffset(4);
360 QList<QPair<QString, QString> > items;
361 for (int i = 0; i < count; ++i)
362 items.append(qMakePair(QString("New"), QString::number(i)));
364 model.insertItems(idx, items);
365 QTRY_COMPARE(pathview->offset(), offset);
367 QCOMPARE(pathview->currentIndex(), currentIndex);
372 void tst_QQuickPathView::removeModel_data()
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");
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;
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;
416 void tst_QQuickPathView::removeModel()
421 QFETCH(qreal, offset);
422 QFETCH(int, currentIndex);
424 QQuickView *canvas = createView();
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");
437 QQmlContext *ctxt = canvas->rootContext();
438 ctxt->setContextProperty("testModel", &model);
440 canvas->setSource(testFileUrl("pathview0.qml"));
441 qApp->processEvents();
443 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
444 QVERIFY(pathview != 0);
446 pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
448 pathview->setCurrentIndex(4);
449 if (mode == QQuickPathView::StrictlyEnforceRange)
450 QTRY_COMPARE(pathview->offset(), 4.0);
452 pathview->setOffset(4);
454 model.removeItems(idx, count);
455 QTRY_COMPARE(pathview->offset(), offset);
457 QCOMPARE(pathview->currentIndex(), currentIndex);
463 void tst_QQuickPathView::moveModel_data()
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");
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;
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;
510 void tst_QQuickPathView::moveModel()
516 QFETCH(qreal, offset);
517 QFETCH(int, currentIndex);
519 QQuickView *canvas = createView();
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");
532 QQmlContext *ctxt = canvas->rootContext();
533 ctxt->setContextProperty("testModel", &model);
535 canvas->setSource(testFileUrl("pathview0.qml"));
536 qApp->processEvents();
538 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
539 QVERIFY(pathview != 0);
541 pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
543 pathview->setCurrentIndex(4);
544 if (mode == QQuickPathView::StrictlyEnforceRange)
545 QTRY_COMPARE(pathview->offset(), 4.0);
547 pathview->setOffset(4);
549 model.moveItems(from, to, count);
550 QTRY_COMPARE(pathview->offset(), offset);
552 QCOMPARE(pathview->currentIndex(), currentIndex);
557 void tst_QQuickPathView::consecutiveModelChanges_data()
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");
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))
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))
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))
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))
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))
612 void tst_QQuickPathView::consecutiveModelChanges()
614 QFETCH(QQuickPathView::HighlightRangeMode, mode);
615 QFETCH(QList<ListChange>, changes);
617 QFETCH(qreal, offset);
618 QFETCH(int, currentIndex);
620 QQuickView *canvas = createView();
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");
633 QQmlContext *ctxt = canvas->rootContext();
634 ctxt->setContextProperty("testModel", &model);
636 canvas->setSource(testFileUrl("pathview0.qml"));
637 qApp->processEvents();
639 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
640 QVERIFY(pathview != 0);
642 pathview->setHighlightRangeMode(mode);
644 pathview->setCurrentIndex(4);
645 if (mode == QQuickPathView::StrictlyEnforceRange)
646 QTRY_COMPARE(pathview->offset(), 4.0);
648 pathview->setOffset(4);
650 for (int i=0; i<changes.count(); i++) {
651 switch (changes[i].type) {
652 case ListChange::Inserted:
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);
660 case ListChange::Removed:
661 model.removeItems(changes[i].index, changes[i].count);
663 case ListChange::Moved:
664 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
666 case ListChange::SetCurrent:
667 pathview->setCurrentIndex(changes[i].index);
669 case ListChange::Polish:
670 QQUICK_VERIFY_POLISH(pathview);
676 QQUICK_VERIFY_POLISH(pathview);
678 QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), count);
679 QCOMPARE(pathview->count(), count);
680 QTRY_COMPARE(pathview->offset(), offset);
682 QCOMPARE(pathview->currentIndex(), currentIndex);
687 void tst_QQuickPathView::path()
690 QQmlComponent c(&engine, testFileUrl("pathtest.qml"));
691 QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
694 QCOMPARE(obj->startX(), 120.);
695 QCOMPARE(obj->startY(), 100.);
696 QVERIFY(obj->path() != QPainterPath());
698 QQmlListReference list(obj, "pathElements");
699 QCOMPARE(list.count(), 5);
701 QQuickPathAttribute* attr = qobject_cast<QQuickPathAttribute*>(list.at(0));
703 QCOMPARE(attr->name(), QString("scale"));
704 QCOMPARE(attr->value(), 1.0);
706 QQuickPathQuad* quad = qobject_cast<QQuickPathQuad*>(list.at(1));
708 QCOMPARE(quad->x(), 120.);
709 QCOMPARE(quad->y(), 25.);
710 QCOMPARE(quad->controlX(), 260.);
711 QCOMPARE(quad->controlY(), 75.);
713 QQuickPathPercent* perc = qobject_cast<QQuickPathPercent*>(list.at(2));
715 QCOMPARE(perc->value(), 0.3);
717 QQuickPathLine* line = qobject_cast<QQuickPathLine*>(list.at(3));
719 QCOMPARE(line->x(), 120.);
720 QCOMPARE(line->y(), 100.);
722 QQuickPathCubic* cubic = qobject_cast<QQuickPathCubic*>(list.at(4));
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.);
734 void tst_QQuickPathView::dataModel()
736 QQuickView *canvas = createView();
739 QQmlContext *ctxt = canvas->rootContext();
740 TestObject *testObject = new TestObject;
741 ctxt->setContextProperty("testObject", testObject);
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");
758 ctxt->setContextProperty("testData", &model);
760 canvas->setSource(testFileUrl("datamodel.qml"));
761 qApp->processEvents();
763 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
764 QVERIFY(pathview != 0);
766 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
767 QVERIFY(testObject->error() == false);
769 QQuickItem *item = findItem<QQuickItem>(pathview, "wrapper", 0);
771 QCOMPARE(item->x(), 110.0);
772 QCOMPARE(item->y(), 10.0);
774 model.insertItem(4, "orange", "10");
777 QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
778 QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 14);
780 QVERIFY(pathview->currentIndex() == 0);
781 QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
783 QQuickText *text = findItem<QQuickText>(pathview, "myText", 4);
785 QCOMPARE(text->text(), model.name(4));
788 QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
789 text = findItem<QQuickText>(pathview, "myText", 2);
791 QCOMPARE(text->text(), model.name(2));
792 QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
794 testObject->setPathItemCount(5);
795 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
796 QVERIFY(testObject->error() == false);
798 QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
800 QQuickRectangle *testItem = findItem<QQuickRectangle>(pathview, "wrapper", 4);
801 QVERIFY(testItem != 0);
802 testItem = findItem<QQuickRectangle>(pathview, "wrapper", 5);
803 QVERIFY(testItem == 0);
805 pathview->setCurrentIndex(1);
806 QTRY_COMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
809 model.insertItem(2, "pink", "2");
812 QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
813 QVERIFY(pathview->currentIndex() == 1);
814 QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
816 text = findItem<QQuickText>(pathview, "myText", 2);
818 QCOMPARE(text->text(), model.name(2));
821 QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
822 text = findItem<QQuickText>(pathview, "myText", 3);
824 QCOMPARE(text->text(), model.name(3));
825 QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
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());
833 QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
836 pathview->setOffset(7);
837 pathview->setOffset(0);
838 QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
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);
849 void tst_QQuickPathView::pathMoved()
851 QQuickView *canvas = createView();
855 model.addItem("Ben", "12345");
856 model.addItem("Bohn", "2345");
857 model.addItem("Bob", "54321");
858 model.addItem("Bill", "4321");
860 QQmlContext *ctxt = canvas->rootContext();
861 ctxt->setContextProperty("testModel", &model);
863 canvas->setSource(testFileUrl("pathview0.qml"));
864 qApp->processEvents();
866 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
867 QVERIFY(pathview != 0);
869 QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
871 QQuickPath *path = qobject_cast<QQuickPath*>(pathview->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);
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()));
886 QCOMPARE(pathview->currentIndex(), 3);
888 pathview->setOffset(0.0);
889 QCOMPARE(firstItem->pos() + offset, start);
890 QCOMPARE(pathview->currentIndex(), 0);
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);
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);
909 void tst_QQuickPathView::offset_data()
911 QTest::addColumn<qreal>("offset");
912 QTest::addColumn<int>("currentIndex");
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;
923 void tst_QQuickPathView::offset()
925 QFETCH(qreal, offset);
926 QFETCH(int, currentIndex);
929 QQmlComponent c(&engine, testFileUrl("pathview3.qml"));
930 QQuickPathView *view = qobject_cast<QQuickPathView*>(c.create());
932 view->setOffset(offset);
933 QCOMPARE(view->currentIndex(), currentIndex);
938 void tst_QQuickPathView::setCurrentIndex()
940 QQuickView *canvas = createView();
944 model.addItem("Ben", "12345");
945 model.addItem("Bohn", "2345");
946 model.addItem("Bob", "54321");
947 model.addItem("Bill", "4321");
949 QQmlContext *ctxt = canvas->rootContext();
950 ctxt->setContextProperty("testModel", &model);
952 canvas->setSource(testFileUrl("pathview0.qml"));
953 qApp->processEvents();
955 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
956 QVERIFY(pathview != 0);
958 QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
960 QQuickPath *path = qobject_cast<QQuickPath*>(pathview->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);
970 pathview->setCurrentIndex(2);
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));
979 pathview->decrementCurrentIndex();
980 QTRY_COMPARE(pathview->currentIndex(), 1);
981 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
983 QTRY_COMPARE(firstItem->pos() + offset, start);
984 QCOMPARE(pathview->currentItem(), firstItem);
985 QCOMPARE(firstItem->property("onPath"), QVariant(true));
987 pathview->decrementCurrentIndex();
988 QTRY_COMPARE(pathview->currentIndex(), 0);
989 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
991 QTRY_COMPARE(firstItem->pos() + offset, start);
992 QCOMPARE(pathview->currentItem(), firstItem);
993 QCOMPARE(firstItem->property("onPath"), QVariant(true));
995 pathview->decrementCurrentIndex();
996 QTRY_COMPARE(pathview->currentIndex(), 3);
997 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
999 QTRY_COMPARE(firstItem->pos() + offset, start);
1000 QCOMPARE(pathview->currentItem(), firstItem);
1001 QCOMPARE(firstItem->property("onPath"), QVariant(true));
1003 pathview->incrementCurrentIndex();
1004 QTRY_COMPARE(pathview->currentIndex(), 0);
1005 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1007 QTRY_COMPARE(firstItem->pos() + offset, start);
1008 QCOMPARE(pathview->currentItem(), firstItem);
1009 QCOMPARE(firstItem->property("onPath"), QVariant(true));
1011 // Test positive indexes are wrapped.
1012 pathview->setCurrentIndex(6);
1013 QTRY_COMPARE(pathview->currentIndex(), 2);
1014 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
1016 QTRY_COMPARE(firstItem->pos() + offset, start);
1017 QCOMPARE(pathview->currentItem(), firstItem);
1018 QCOMPARE(firstItem->property("onPath"), QVariant(true));
1020 // Test negative indexes are wrapped.
1021 pathview->setCurrentIndex(-3);
1022 QTRY_COMPARE(pathview->currentIndex(), 1);
1023 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
1025 QTRY_COMPARE(firstItem->pos() + offset, start);
1026 QCOMPARE(pathview->currentItem(), firstItem);
1027 QCOMPARE(firstItem->property("onPath"), QVariant(true));
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);
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);
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));
1050 pathview->setCurrentIndex(2);
1051 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
1052 QCOMPARE(pathview->currentItem(), firstItem);
1053 QCOMPARE(firstItem->property("onPath"), QVariant(false));
1055 pathview->decrementCurrentIndex();
1056 QTRY_COMPARE(pathview->currentIndex(), 1);
1057 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
1059 QCOMPARE(pathview->currentItem(), firstItem);
1060 QCOMPARE(firstItem->property("onPath"), QVariant(true));
1062 pathview->decrementCurrentIndex();
1063 QTRY_COMPARE(pathview->currentIndex(), 0);
1064 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1066 QCOMPARE(pathview->currentItem(), firstItem);
1067 QCOMPARE(firstItem->property("onPath"), QVariant(true));
1069 pathview->decrementCurrentIndex();
1070 QTRY_COMPARE(pathview->currentIndex(), 3);
1071 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
1073 QCOMPARE(pathview->currentItem(), firstItem);
1074 QCOMPARE(firstItem->property("onPath"), QVariant(false));
1076 pathview->incrementCurrentIndex();
1077 QTRY_COMPARE(pathview->currentIndex(), 0);
1078 firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1080 QCOMPARE(pathview->currentItem(), firstItem);
1081 QCOMPARE(firstItem->property("onPath"), QVariant(true));
1086 void tst_QQuickPathView::resetModel()
1088 QQuickView *canvas = createView();
1090 QStringList strings;
1091 strings << "one" << "two" << "three";
1092 QStringListModel model(strings);
1094 QQmlContext *ctxt = canvas->rootContext();
1095 ctxt->setContextProperty("testModel", &model);
1097 canvas->setSource(testFileUrl("displaypath.qml"));
1098 qApp->processEvents();
1100 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1101 QVERIFY(pathview != 0);
1103 QCOMPARE(pathview->count(), model.rowCount());
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));
1112 strings << "four" << "five" << "six" << "seven";
1113 model.setStringList(strings);
1115 QCOMPARE(pathview->count(), model.rowCount());
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));
1126 void tst_QQuickPathView::propertyChanges()
1128 QQuickView *canvas = createView();
1130 canvas->setSource(testFileUrl("propertychanges.qml"));
1132 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1135 QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
1136 QSignalSpy dragMarginSpy(pathView, SIGNAL(dragMarginChanged()));
1138 QCOMPARE(pathView->preferredHighlightBegin(), 0.1);
1139 QCOMPARE(pathView->dragMargin(), 5.0);
1141 pathView->setPreferredHighlightBegin(0.4);
1142 pathView->setPreferredHighlightEnd(0.4);
1143 pathView->setDragMargin(20.0);
1145 QCOMPARE(pathView->preferredHighlightBegin(), 0.4);
1146 QCOMPARE(pathView->preferredHighlightEnd(), 0.4);
1147 QCOMPARE(pathView->dragMargin(), 20.0);
1149 QCOMPARE(snapPositionSpy.count(), 1);
1150 QCOMPARE(dragMarginSpy.count(), 1);
1152 pathView->setPreferredHighlightBegin(0.4);
1153 pathView->setPreferredHighlightEnd(0.4);
1154 pathView->setDragMargin(20.0);
1156 QCOMPARE(snapPositionSpy.count(), 1);
1157 QCOMPARE(dragMarginSpy.count(), 1);
1159 QSignalSpy maximumFlickVelocitySpy(pathView, SIGNAL(maximumFlickVelocityChanged()));
1160 pathView->setMaximumFlickVelocity(1000);
1161 QCOMPARE(maximumFlickVelocitySpy.count(), 1);
1162 pathView->setMaximumFlickVelocity(1000);
1163 QCOMPARE(maximumFlickVelocitySpy.count(), 1);
1168 void tst_QQuickPathView::pathChanges()
1170 QQuickView *canvas = createView();
1172 canvas->setSource(testFileUrl("propertychanges.qml"));
1174 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1177 QQuickPath *path = canvas->rootObject()->findChild<QQuickPath*>("path");
1180 QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
1181 QSignalSpy startYSpy(path, SIGNAL(startYChanged()));
1183 QCOMPARE(path->startX(), 220.0);
1184 QCOMPARE(path->startY(), 200.0);
1186 path->setStartX(240.0);
1187 path->setStartY(220.0);
1189 QCOMPARE(path->startX(), 240.0);
1190 QCOMPARE(path->startY(), 220.0);
1192 QCOMPARE(startXSpy.count(),1);
1193 QCOMPARE(startYSpy.count(),1);
1195 path->setStartX(240);
1196 path->setStartY(220);
1198 QCOMPARE(startXSpy.count(),1);
1199 QCOMPARE(startYSpy.count(),1);
1201 QQuickPath *alternatePath = canvas->rootObject()->findChild<QQuickPath*>("alternatePath");
1202 QVERIFY(alternatePath);
1204 QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
1206 QCOMPARE(pathView->path(), path);
1208 pathView->setPath(alternatePath);
1209 QCOMPARE(pathView->path(), alternatePath);
1210 QCOMPARE(pathSpy.count(),1);
1212 pathView->setPath(alternatePath);
1213 QCOMPARE(pathSpy.count(),1);
1215 QQuickPathAttribute *pathAttribute = canvas->rootObject()->findChild<QQuickPathAttribute*>("pathAttribute");
1216 QVERIFY(pathAttribute);
1218 QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
1219 QCOMPARE(pathAttribute->name(), QString("opacity"));
1221 pathAttribute->setName("scale");
1222 QCOMPARE(pathAttribute->name(), QString("scale"));
1223 QCOMPARE(nameSpy.count(),1);
1225 pathAttribute->setName("scale");
1226 QCOMPARE(nameSpy.count(),1);
1230 void tst_QQuickPathView::componentChanges()
1232 QQuickView *canvas = createView();
1234 canvas->setSource(testFileUrl("propertychanges.qml"));
1236 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1239 QQmlComponent delegateComponent(canvas->engine());
1240 delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
1242 QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
1244 pathView->setDelegate(&delegateComponent);
1245 QCOMPARE(pathView->delegate(), &delegateComponent);
1246 QCOMPARE(delegateSpy.count(),1);
1248 pathView->setDelegate(&delegateComponent);
1249 QCOMPARE(delegateSpy.count(),1);
1253 void tst_QQuickPathView::modelChanges()
1255 QQuickView *canvas = createView();
1257 canvas->setSource(testFileUrl("propertychanges.qml"));
1259 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1262 QQuickListModel *alternateModel = canvas->rootObject()->findChild<QQuickListModel*>("alternateModel");
1263 QVERIFY(alternateModel);
1264 QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
1265 QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
1267 pathView->setModel(modelVariant);
1268 QCOMPARE(pathView->model(), modelVariant);
1269 QCOMPARE(modelSpy.count(),1);
1271 pathView->setModel(modelVariant);
1272 QCOMPARE(modelSpy.count(),1);
1274 pathView->setModel(QVariant());
1275 QCOMPARE(modelSpy.count(),2);
1280 void tst_QQuickPathView::pathUpdateOnStartChanged()
1282 QQuickView *canvas = createView();
1284 canvas->setSource(testFileUrl("pathUpdateOnStartChanged.qml"));
1286 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1289 QQuickPath *path = canvas->rootObject()->findChild<QQuickPath*>("path");
1291 QCOMPARE(path->startX(), 400.0);
1292 QCOMPARE(path->startY(), 300.0);
1294 QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1296 QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
1297 QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
1302 void tst_QQuickPathView::package()
1304 QQuickView *canvas = createView();
1306 canvas->setSource(testFileUrl("pathview_package.qml"));
1308 QTest::qWaitForWindowShown(canvas);
1310 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("photoPathView");
1314 QSKIP("QTBUG-21590 view does not reliably receive polish without a running animation");
1317 QQuickItem *item = findItem<QQuickItem>(pathView, "pathItem");
1319 QVERIFY(item->scale() != 1.0);
1325 void tst_QQuickPathView::emptyModel()
1327 QQuickView *canvas = createView();
1329 QStringListModel model;
1331 QQmlContext *ctxt = canvas->rootContext();
1332 ctxt->setContextProperty("emptyModel", &model);
1334 canvas->setSource(testFileUrl("emptymodel.qml"));
1335 qApp->processEvents();
1337 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1338 QVERIFY(pathview != 0);
1340 QCOMPARE(pathview->offset(), qreal(0.0));
1345 void tst_QQuickPathView::closed()
1350 QQmlComponent c(&engine, testFileUrl("openPath.qml"));
1351 QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
1353 QCOMPARE(obj->isClosed(), false);
1358 QQmlComponent c(&engine, testFileUrl("closedPath.qml"));
1359 QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
1361 QCOMPARE(obj->isClosed(), true);
1367 void tst_QQuickPathView::pathUpdate()
1369 QQuickView *canvas = createView();
1371 canvas->setSource(testFileUrl("pathUpdate.qml"));
1373 QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1376 QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1378 QCOMPARE(item->x(), 150.0);
1383 void tst_QQuickPathView::visualDataModel()
1386 QQmlComponent c(&engine, testFileUrl("vdm.qml"));
1388 QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1391 QCOMPARE(obj->count(), 3);
1396 void tst_QQuickPathView::undefinedPath()
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));
1405 QString warning2("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
1406 QTest::ignoreMessage(QtWarningMsg,qPrintable(warning2));
1409 QQmlComponent c(&engine, testFileUrl("undefinedpath.qml"));
1411 QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1414 QCOMPARE(obj->count(), 3);
1419 void tst_QQuickPathView::mouseDrag()
1421 QQuickView *canvas = createView();
1422 canvas->setSource(testFileUrl("dragpath.qml"));
1424 canvas->requestActivateWindow();
1425 QTest::qWaitForWindowShown(canvas);
1426 QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1428 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1429 QVERIFY(pathview != 0);
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()));
1438 int current = pathview->currentIndex();
1440 QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
1444 QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1445 QGuiApplication::sendEvent(canvas, &mv);
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);
1458 QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1459 QGuiApplication::sendEvent(canvas, &mv);
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);
1471 QVERIFY(pathview->currentIndex() != current);
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);
1485 #ifndef QT_NO_WIDGETS
1486 void tst_QQuickPathView::treeModel()
1488 QQuickView *canvas = createView();
1491 QStandardItemModel model;
1492 initStandardTreeModel(&model);
1493 canvas->engine()->rootContext()->setContextProperty("myModel", &model);
1495 canvas->setSource(testFileUrl("treemodel.qml"));
1497 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1498 QVERIFY(pathview != 0);
1499 QCOMPARE(pathview->count(), 3);
1501 QQuickText *item = findItem<QQuickText>(pathview, "wrapper", 0);
1503 QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
1505 QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
1506 QCOMPARE(pathview->count(), 1);
1508 QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0));
1509 QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
1515 void tst_QQuickPathView::changePreferredHighlight()
1517 QQuickView *canvas = createView();
1518 canvas->setGeometry(0,0,400,200);
1519 canvas->setSource(testFileUrl("dragpath.qml"));
1521 canvas->requestActivateWindow();
1522 QTest::qWaitForWindowShown(canvas);
1523 QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1525 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1526 QVERIFY(pathview != 0);
1528 int current = pathview->currentIndex();
1529 QCOMPARE(current, 0);
1531 QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1533 QQuickPath *path = qobject_cast<QQuickPath*>(pathview->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);
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);
1550 void tst_QQuickPathView::creationContext()
1553 canvas.setGeometry(0,0,240,320);
1554 canvas.setSource(testFileUrl("creationContext.qml"));
1556 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
1558 QVERIFY(rootItem->property("count").toInt() > 0);
1561 QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem", 0));
1562 QCOMPARE(item->property("text").toString(), QString("Hello!"));
1566 void tst_QQuickPathView::currentOffsetOnInsertion()
1568 QQuickView *canvas = createView();
1573 QQmlContext *ctxt = canvas->rootContext();
1574 ctxt->setContextProperty("testModel", &model);
1576 canvas->setSource(testFileUrl("pathline.qml"));
1577 qApp->processEvents();
1579 QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1580 QVERIFY(pathview != 0);
1582 pathview->setPreferredHighlightBegin(0.5);
1583 pathview->setPreferredHighlightEnd(0.5);
1585 QCOMPARE(pathview->count(), model.count());
1587 model.addItem("item0", "0");
1589 QCOMPARE(pathview->count(), model.count());
1591 QQuickRectangle *item = 0;
1592 QTRY_VERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 0));
1594 QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
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);
1603 QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged()));
1605 // insert an item at the beginning
1606 model.insertItem(0, "item1", "1");
1607 qApp->processEvents();
1609 QCOMPARE(currentIndexSpy.count(), 1);
1611 // currentIndex is now 1
1612 QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1614 // verify that current item (item 1) is still at offset 0.5
1615 QCOMPARE(item->pos() + offset, start);
1617 // insert another item at the beginning
1618 model.insertItem(0, "item2", "2");
1619 qApp->processEvents();
1621 QCOMPARE(currentIndexSpy.count(), 2);
1623 // currentIndex is now 2
1624 QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 2));
1626 // verify that current item (item 2) is still at offset 0.5
1627 QCOMPARE(item->pos() + offset, start);
1629 // verify that remove before current maintains current item
1630 model.removeItem(0);
1631 qApp->processEvents();
1633 QCOMPARE(currentIndexSpy.count(), 3);
1635 // currentIndex is now 1
1636 QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1638 // verify that current item (item 1) is still at offset 0.5
1639 QCOMPARE(item->pos() + offset, start);
1644 void tst_QQuickPathView::asynchronous()
1646 QQuickView *canvas = createView();
1648 QQmlIncubationController controller;
1649 canvas->engine()->setIncubationController(&controller);
1651 canvas->setSource(testFileUrl("asyncloader.qml"));
1653 QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
1654 QVERIFY(rootObject);
1656 QQuickPathView *pathview = 0;
1659 controller.incubateWhile(&b);
1660 pathview = rootObject->findChild<QQuickPathView*>("view");
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;
1669 controller.incubateWhile(&b);
1670 item = findItem<QQuickItem>(pathview, "wrapper", i);
1676 controller.incubateWhile(&b);
1679 // verify positioning
1680 QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1682 QQuickPath *path = qobject_cast<QQuickPath*>(pathview->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);
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);
1700 void tst_QQuickPathView::missingPercent()
1703 QQmlComponent c(&engine, testFileUrl("missingPercent.qml"));
1704 QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
1706 QCOMPARE(obj->attributeAt("_qfx_percent", 1.0), qreal(1.0));
1710 void tst_QQuickPathView::cancelDrag()
1712 QQuickView *canvas = createView();
1713 canvas->setSource(testFileUrl("dragpath.qml"));
1715 canvas->requestActivateWindow();
1716 QTest::qWaitForWindowShown(canvas);
1717 QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1719 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1720 QVERIFY(pathview != 0);
1722 QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged()));
1723 QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted()));
1724 QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded()));
1726 // drag between snap points
1727 QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
1729 QTest::mouseMove(canvas, QPoint(30, 100));
1730 QTest::mouseMove(canvas, QPoint(85, 100));
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);
1739 // steal mouse grab - cancels PathView dragging
1740 QQuickItem *item = canvas->rootObject()->findChild<QQuickItem*>("text");
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);
1751 QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
1756 void tst_QQuickPathView::maximumFlickVelocity()
1758 QQuickView *canvas = createView();
1759 canvas->setSource(testFileUrl("dragpath.qml"));
1761 canvas->requestActivateWindow();
1762 QTest::qWaitForWindowShown(canvas);
1763 QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1765 QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1766 QVERIFY(pathview != 0);
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());
1774 double dist1 = 100 - pathview->offset();
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());
1783 double dist2 = 100 - pathview->offset();
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());
1792 double dist3 = 100 - pathview->offset();
1794 QVERIFY(dist1 > dist2);
1795 QVERIFY(dist3 > dist2);
1796 QVERIFY(dist2 < dist1);
1801 void tst_QQuickPathView::snapToItem()
1803 QFETCH(bool, enforceRange);
1805 QQuickView *canvas = createView();
1806 canvas->setSource(testFileUrl("panels.qml"));
1807 QQuickPathView *pathview = canvas->rootObject()->findChild<QQuickPathView*>("view");
1808 QVERIFY(pathview != 0);
1810 canvas->rootObject()->setProperty("enforceRange", enforceRange);
1811 QTRY_VERIFY(!pathview->isMoving()); // ensure stable
1813 int currentIndex = pathview->currentIndex();
1815 QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged()));
1817 flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1819 QVERIFY(pathview->isMoving());
1820 QTRY_VERIFY(!pathview->isMoving());
1822 QVERIFY(pathview->offset() == qFloor(pathview->offset()));
1825 QVERIFY(pathview->currentIndex() != currentIndex);
1827 QVERIFY(pathview->currentIndex() == currentIndex);
1830 void tst_QQuickPathView::snapToItem_data()
1832 QTest::addColumn<bool>("enforceRange");
1834 QTest::newRow("no enforce range") << false;
1835 QTest::newRow("enforce range") << true;
1838 void tst_QQuickPathView::snapOneItem()
1840 QFETCH(bool, enforceRange);
1842 QQuickView *canvas = createView();
1843 canvas->setSource(testFileUrl("panels.qml"));
1845 canvas->requestActivateWindow();
1846 QTest::qWaitForWindowShown(canvas);
1847 QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1849 QQuickPathView *pathview = canvas->rootObject()->findChild<QQuickPathView*>("view");
1850 QVERIFY(pathview != 0);
1852 canvas->rootObject()->setProperty("enforceRange", enforceRange);
1854 QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged()));
1856 canvas->rootObject()->setProperty("snapOne", true);
1857 QVERIFY(snapModeSpy.count() == 1);
1858 QTRY_VERIFY(!pathview->isMoving()); // ensure stable
1860 int currentIndex = pathview->currentIndex();
1862 double startOffset = pathview->offset();
1863 flick(canvas, QPoint(200,10), QPoint(10,10), 180);
1865 QVERIFY(pathview->isMoving());
1866 QTRY_VERIFY(!pathview->isMoving());
1868 // must have moved only one item
1869 QCOMPARE(pathview->offset(), fmodf(3.0 + startOffset - 1.0, 3.0));
1872 QVERIFY(pathview->currentIndex() == currentIndex+1);
1874 QVERIFY(pathview->currentIndex() == currentIndex);
1877 void tst_QQuickPathView::snapOneItem_data()
1879 QTest::addColumn<bool>("enforceRange");
1881 QTest::newRow("no enforce range") << false;
1882 QTest::newRow("enforce range") << true;
1886 QTEST_MAIN(tst_QQuickPathView)
1888 #include "tst_qquickpathview.moc"