QQuickCanvas renames
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickpathview / tst_qquickpathview.cpp
index 85d2c3b..fad068b 100644 (file)
@@ -53,7 +53,6 @@
 #include <QtQml/private/qquicklistmodel_p.h>
 #include <QtQml/private/qqmlvaluetype_p.h>
 #include <QStringListModel>
-#include <QStandardItemModel>
 #include <QFile>
 
 #include "../../shared/util.h"
 using namespace QQuickViewTestUtil;
 using namespace QQuickVisualTestUtil;
 
+Q_DECLARE_METATYPE(QQuickPathView::HighlightRangeMode)
 
+#ifndef QT_NO_WIDGETS
+#include <QStandardItemModel>
 static void initStandardTreeModel(QStandardItemModel *model)
 {
     QStandardItem *item;
@@ -81,6 +83,7 @@ static void initStandardTreeModel(QStandardItemModel *model)
     item->setIcon(QIcon());
     model->insertRow(2, item);
 }
+#endif
 
 
 class tst_QQuickPathView : public QQmlDataTest
@@ -101,8 +104,12 @@ private slots:
     void removeModel();
     void moveModel_data();
     void moveModel();
+    void consecutiveModelChanges_data();
+    void consecutiveModelChanges();
     void path();
     void pathMoved();
+    void offset_data();
+    void offset();
     void setCurrentIndex();
     void resetModel();
     void propertyChanges();
@@ -117,7 +124,9 @@ private slots:
     void visualDataModel();
     void undefinedPath();
     void mouseDrag();
+#ifndef QT_NO_WIDGETS
     void treeModel();
+#endif
     void changePreferredHighlight();
     void missingPercent();
     void creationContext();
@@ -125,6 +134,10 @@ private slots:
     void asynchronous();
     void cancelDrag();
     void maximumFlickVelocity();
+    void snapToItem();
+    void snapToItem_data();
+    void snapOneItem();
+    void snapOneItem_data();
 };
 
 class TestObject : public QObject
@@ -183,7 +196,7 @@ void tst_QQuickPathView::initValues()
 
 void tst_QQuickPathView::items()
 {
-    QQuickView *canvas = createView();
+    QQuickView *window = createView();
 
     QaimModel model;
     model.addItem("Fred", "12345");
@@ -191,17 +204,17 @@ void tst_QQuickPathView::items()
     model.addItem("Bob", "54321");
     model.addItem("Bill", "4321");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathview0.qml"));
+    window->setSource(testFileUrl("pathview0.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     QCOMPARE(pathview->count(), model.count());
-    QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
+    QCOMPARE(window->rootObject()->property("count").toInt(), model.count());
     QCOMPARE(pathview->childItems().count(), model.count()+1); // assumes all are visible, including highlight
 
     for (int i = 0; i < model.count(); ++i) {
@@ -223,7 +236,7 @@ void tst_QQuickPathView::items()
     offset.setY(pathview->highlightItem()->height()/2);
     QCOMPARE(pathview->highlightItem()->pos() + offset, start);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::pathview2()
@@ -256,7 +269,7 @@ void tst_QQuickPathView::pathview3()
     QVERIFY(obj->path() != 0);
     QVERIFY(obj->delegate() != 0);
     QVERIFY(obj->model() != QVariant());
-    QCOMPARE(obj->currentIndex(), 0);
+    QCOMPARE(obj->currentIndex(), 7);
     QCOMPARE(obj->offset(), 1.0);
     QCOMPARE(obj->preferredHighlightBegin(), 0.5);
     QCOMPARE(obj->dragMargin(), 24.);
@@ -272,37 +285,38 @@ void tst_QQuickPathView::insertModel_data()
     QTest::addColumn<int>("idx");
     QTest::addColumn<int>("count");
     QTest::addColumn<qreal>("offset");
+    QTest::addColumn<int>("currentIndex");
 
     // We have 8 items, with currentIndex == 4
     QTest::newRow("insert after current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 5.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 5. << 4;
     QTest::newRow("insert before current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4. << 5;
     QTest::newRow("insert multiple after current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 6.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 6. << 4;
     QTest::newRow("insert multiple before current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4. << 6;
     QTest::newRow("insert at end")
-        << int(QQuickPathView::StrictlyEnforceRange) << 8 << 1 << 5.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 8 << 1 << 5. << 4;
     QTest::newRow("insert at beginning")
-        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4. << 5;
     QTest::newRow("insert at current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 4. << 5;
 
     QTest::newRow("no range - insert after current")
-        << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 5.;
+        << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 5. << 4;
     QTest::newRow("no range - insert before current")
-        << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4. << 5;
     QTest::newRow("no range - insert multiple after current")
-        << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 6.;
+        << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 6. << 4;
     QTest::newRow("no range - insert multiple before current")
-        << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4. << 6;
     QTest::newRow("no range - insert at end")
-        << int(QQuickPathView::NoHighlightRange) << 8 << 1 << 5.;
+        << int(QQuickPathView::NoHighlightRange) << 8 << 1 << 5. << 4;
     QTest::newRow("no range - insert at beginning")
-        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4. << 5;
     QTest::newRow("no range - insert at current")
-        << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4. << 5;
 }
 
 void tst_QQuickPathView::insertModel()
@@ -311,9 +325,10 @@ void tst_QQuickPathView::insertModel()
     QFETCH(int, idx);
     QFETCH(int, count);
     QFETCH(qreal, offset);
+    QFETCH(int, currentIndex);
 
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QaimModel model;
     model.addItem("Ben", "12345");
@@ -325,13 +340,13 @@ void tst_QQuickPathView::insertModel()
     model.addItem("Jimmy", "3535");
     model.addItem("Barb", "9039");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathview0.qml"));
+    window->setSource(testFileUrl("pathview0.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
@@ -349,7 +364,9 @@ void tst_QQuickPathView::insertModel()
     model.insertItems(idx, items);
     QTRY_COMPARE(pathview->offset(), offset);
 
-    delete canvas;
+    QCOMPARE(pathview->currentIndex(), currentIndex);
+
+    delete window;
 }
 
 void tst_QQuickPathView::removeModel_data()
@@ -358,37 +375,42 @@ void tst_QQuickPathView::removeModel_data()
     QTest::addColumn<int>("idx");
     QTest::addColumn<int>("count");
     QTest::addColumn<qreal>("offset");
+    QTest::addColumn<int>("currentIndex");
 
     // We have 8 items, with currentIndex == 4
     QTest::newRow("remove after current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 3.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 3. << 4;
     QTest::newRow("remove before current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4. << 3;
     QTest::newRow("remove multiple after current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 2.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 2. << 4;
     QTest::newRow("remove multiple before current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4. << 2;
     QTest::newRow("remove last")
-        << int(QQuickPathView::StrictlyEnforceRange) << 7 << 1 << 3.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 7 << 1 << 3. << 4;
     QTest::newRow("remove first")
-        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4. << 3;
     QTest::newRow("remove current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 3.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 3. << 4;
+    QTest::newRow("remove all")
+        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 8 << 0. << 0;
 
     QTest::newRow("no range - remove after current")
-        << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 3.;
+        << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 3. << 4;
     QTest::newRow("no range - remove before current")
-        << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4. << 3;
     QTest::newRow("no range - remove multiple after current")
-        << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 2.;
+        << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 2. << 4;
     QTest::newRow("no range - remove multiple before current")
-        << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4. << 2;
     QTest::newRow("no range - remove last")
-        << int(QQuickPathView::NoHighlightRange) << 7 << 1 << 3.;
+        << int(QQuickPathView::NoHighlightRange) << 7 << 1 << 3. << 4;
     QTest::newRow("no range - remove first")
-        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4. << 3;
     QTest::newRow("no range - remove current offset")
-        << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4. << 4;
+    QTest::newRow("no range - remove all")
+        << int(QQuickPathView::NoHighlightRange) << 0 << 8 << 0. << 0;
 }
 
 void tst_QQuickPathView::removeModel()
@@ -397,9 +419,10 @@ void tst_QQuickPathView::removeModel()
     QFETCH(int, idx);
     QFETCH(int, count);
     QFETCH(qreal, offset);
+    QFETCH(int, currentIndex);
 
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QaimModel model;
     model.addItem("Ben", "12345");
@@ -411,13 +434,13 @@ void tst_QQuickPathView::removeModel()
     model.addItem("Jimmy", "3535");
     model.addItem("Barb", "9039");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathview0.qml"));
+    window->setSource(testFileUrl("pathview0.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
@@ -431,7 +454,9 @@ void tst_QQuickPathView::removeModel()
     model.removeItems(idx, count);
     QTRY_COMPARE(pathview->offset(), offset);
 
-    delete canvas;
+    QCOMPARE(pathview->currentIndex(), currentIndex);
+
+    delete window;
 }
 
 
@@ -442,43 +467,44 @@ void tst_QQuickPathView::moveModel_data()
     QTest::addColumn<int>("to");
     QTest::addColumn<int>("count");
     QTest::addColumn<qreal>("offset");
+    QTest::addColumn<int>("currentIndex");
 
     // We have 8 items, with currentIndex == 4
     QTest::newRow("move after current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 1 << 4. << 4;
     QTest::newRow("move before current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 3 << 1 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 3 << 1 << 4. << 4;
     QTest::newRow("move before current to after")
-        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 6 << 1 << 5.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 6 << 1 << 5. << 3;
     QTest::newRow("move multiple after current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 2 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 2 << 4. << 4;
     QTest::newRow("move multiple before current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 2 << 4.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 2 << 4. << 4;
     QTest::newRow("move before current to end")
-        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 7 << 1 << 5.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 2 << 7 << 1 << 5. << 3;
     QTest::newRow("move last to beginning")
-        << int(QQuickPathView::StrictlyEnforceRange) << 7 << 0 << 1 << 3.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 7 << 0 << 1 << 3. << 5;
     QTest::newRow("move current")
-        << int(QQuickPathView::StrictlyEnforceRange) << 4 << 6 << 1 << 2.;
+        << int(QQuickPathView::StrictlyEnforceRange) << 4 << 6 << 1 << 2. << 6;
 
     QTest::newRow("no range - move after current")
-        << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 1 << 4. << 4;
     QTest::newRow("no range - move before current")
-        << int(QQuickPathView::NoHighlightRange) << 2 << 3 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 2 << 3 << 1 << 4. << 4;
     QTest::newRow("no range - move before current to after")
-        << int(QQuickPathView::NoHighlightRange) << 2 << 6 << 1 << 5.;
+        << int(QQuickPathView::NoHighlightRange) << 2 << 6 << 1 << 5. << 3;
     QTest::newRow("no range - move multiple after current")
-        << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 2 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 2 << 4. << 4;
     QTest::newRow("no range - move multiple before current")
-        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 2 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 2 << 4. << 4;
     QTest::newRow("no range - move before current to end")
-        << int(QQuickPathView::NoHighlightRange) << 2 << 7 << 1 << 5.;
+        << int(QQuickPathView::NoHighlightRange) << 2 << 7 << 1 << 5. << 3;
     QTest::newRow("no range - move last to beginning")
-        << int(QQuickPathView::NoHighlightRange) << 7 << 0 << 1 << 3.;
+        << int(QQuickPathView::NoHighlightRange) << 7 << 0 << 1 << 3. << 5;
     QTest::newRow("no range - move current")
-        << int(QQuickPathView::NoHighlightRange) << 4 << 6 << 1 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 4 << 6 << 1 << 4. << 6;
     QTest::newRow("no range - move multiple incl. current")
-        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 5 << 4.;
+        << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 5 << 4. << 5;
 }
 
 void tst_QQuickPathView::moveModel()
@@ -488,9 +514,10 @@ void tst_QQuickPathView::moveModel()
     QFETCH(int, to);
     QFETCH(int, count);
     QFETCH(qreal, offset);
+    QFETCH(int, currentIndex);
 
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QaimModel model;
     model.addItem("Ben", "12345");
@@ -502,13 +529,13 @@ void tst_QQuickPathView::moveModel()
     model.addItem("Jimmy", "3535");
     model.addItem("Barb", "9039");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathview0.qml"));
+    window->setSource(testFileUrl("pathview0.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
@@ -522,7 +549,139 @@ void tst_QQuickPathView::moveModel()
     model.moveItems(from, to, count);
     QTRY_COMPARE(pathview->offset(), offset);
 
-    delete canvas;
+    QCOMPARE(pathview->currentIndex(), currentIndex);
+
+    delete window;
+}
+
+void tst_QQuickPathView::consecutiveModelChanges_data()
+{
+    QTest::addColumn<QQuickPathView::HighlightRangeMode>("mode");
+    QTest::addColumn<QList<ListChange> >("changes");
+    QTest::addColumn<int>("count");
+    QTest::addColumn<qreal>("offset");
+    QTest::addColumn<int>("currentIndex");
+
+    QTest::newRow("no range - insert after, insert before")
+            << QQuickPathView::NoHighlightRange
+            << (QList<ListChange>()
+                << ListChange::insert(7, 2)
+                << ListChange::insert(1, 3))
+            << 13
+            << 6.
+            << 7;
+    QTest::newRow("no range - remove after, remove before")
+            << QQuickPathView::NoHighlightRange
+            << (QList<ListChange>()
+                << ListChange::remove(6, 2)
+                << ListChange::remove(1, 3))
+            << 3
+            << 2.
+            << 1;
+
+    QTest::newRow("no range - remove after, insert before")
+            << QQuickPathView::NoHighlightRange
+            << (QList<ListChange>()
+                << ListChange::remove(5, 2)
+                << ListChange::insert(1, 3))
+            << 9
+            << 2.
+            << 7;
+
+    QTest::newRow("no range - insert after, remove before")
+            << QQuickPathView::NoHighlightRange
+            << (QList<ListChange>()
+                << ListChange::insert(6, 2)
+                << ListChange::remove(1, 3))
+            << 7
+            << 6.
+            << 1;
+
+    QTest::newRow("no range - insert, remove all, polish, insert")
+            << QQuickPathView::NoHighlightRange
+            << (QList<ListChange>()
+                << ListChange::insert(3, 1)
+                << ListChange::remove(0, 9)
+                << ListChange::polish()
+                << ListChange::insert(0, 3))
+            << 3
+            << 0.
+            << 0;
+}
+
+void tst_QQuickPathView::consecutiveModelChanges()
+{
+    QFETCH(QQuickPathView::HighlightRangeMode, mode);
+    QFETCH(QList<ListChange>, changes);
+    QFETCH(int, count);
+    QFETCH(qreal, offset);
+    QFETCH(int, currentIndex);
+
+    QQuickView *window = createView();
+    window->show();
+
+    QaimModel model;
+    model.addItem("Ben", "12345");
+    model.addItem("Bohn", "2345");
+    model.addItem("Bob", "54321");
+    model.addItem("Bill", "4321");
+    model.addItem("Jinny", "679");
+    model.addItem("Milly", "73378");
+    model.addItem("Jimmy", "3535");
+    model.addItem("Barb", "9039");
+
+    QQmlContext *ctxt = window->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+
+    window->setSource(testFileUrl("pathview0.qml"));
+    qApp->processEvents();
+
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
+    QVERIFY(pathview != 0);
+
+    pathview->setHighlightRangeMode(mode);
+
+    pathview->setCurrentIndex(4);
+    if (mode == QQuickPathView::StrictlyEnforceRange)
+        QTRY_COMPARE(pathview->offset(), 4.0);
+    else
+        pathview->setOffset(4);
+
+    for (int i=0; i<changes.count(); i++) {
+        switch (changes[i].type) {
+            case ListChange::Inserted:
+            {
+                QList<QPair<QString, QString> > items;
+                for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+                    items << qMakePair(QString("new item %1").arg(j), QString::number(j));
+                model.insertItems(changes[i].index, items);
+                break;
+            }
+            case ListChange::Removed:
+                model.removeItems(changes[i].index, changes[i].count);
+                break;
+            case ListChange::Moved:
+                model.moveItems(changes[i].index, changes[i].to, changes[i].count);
+                break;
+            case ListChange::SetCurrent:
+                pathview->setCurrentIndex(changes[i].index);
+                break;
+        case ListChange::Polish:
+                QQUICK_VERIFY_POLISH(pathview);
+                break;
+            default:
+                continue;
+        }
+    }
+    QQUICK_VERIFY_POLISH(pathview);
+
+    QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), count);
+    QCOMPARE(pathview->count(), count);
+    QTRY_COMPARE(pathview->offset(), offset);
+
+    QCOMPARE(pathview->currentIndex(), currentIndex);
+
+    delete window;
 }
 
 void tst_QQuickPathView::path()
@@ -574,10 +733,10 @@ void tst_QQuickPathView::path()
 
 void tst_QQuickPathView::dataModel()
 {
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     TestObject *testObject = new TestObject;
     ctxt->setContextProperty("testObject", testObject);
 
@@ -598,13 +757,13 @@ void tst_QQuickPathView::dataModel()
 
     ctxt->setContextProperty("testData", &model);
 
-    canvas->setSource(testFileUrl("datamodel.qml"));
+    window->setSource(testFileUrl("datamodel.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
 
-    QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+    QMetaObject::invokeMethod(window->rootObject(), "checkProperties");
     QVERIFY(testObject->error() == false);
 
     QQuickItem *item = findItem<QQuickItem>(pathview, "wrapper", 0);
@@ -615,7 +774,7 @@ void tst_QQuickPathView::dataModel()
     model.insertItem(4, "orange", "10");
     QTest::qWait(100);
 
-    QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
+    QCOMPARE(window->rootObject()->property("viewCount").toInt(), model.count());
     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 14);
 
     QVERIFY(pathview->currentIndex() == 0);
@@ -626,14 +785,14 @@ void tst_QQuickPathView::dataModel()
     QCOMPARE(text->text(), model.name(4));
 
     model.removeItem(2);
-    QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
+    QCOMPARE(window->rootObject()->property("viewCount").toInt(), model.count());
     text = findItem<QQuickText>(pathview, "myText", 2);
     QVERIFY(text);
     QCOMPARE(text->text(), model.name(2));
     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
 
     testObject->setPathItemCount(5);
-    QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+    QMetaObject::invokeMethod(window->rootObject(), "checkProperties");
     QVERIFY(testObject->error() == false);
 
     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
@@ -644,7 +803,7 @@ void tst_QQuickPathView::dataModel()
     QVERIFY(testItem == 0);
 
     pathview->setCurrentIndex(1);
-    QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
+    QTRY_COMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
     QTest::qWait(100);
 
     model.insertItem(2, "pink", "2");
@@ -679,17 +838,18 @@ void tst_QQuickPathView::dataModel()
     QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
 
     pathview->setCurrentIndex(model.count()-1);
+    QTRY_COMPARE(pathview->offset(), 1.0);
     model.removeItem(model.count()-1);
     QCOMPARE(pathview->currentIndex(), model.count()-1);
 
-    delete canvas;
+    delete window;
     delete testObject;
 }
 
 void tst_QQuickPathView::pathMoved()
 {
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QaimModel model;
     model.addItem("Ben", "12345");
@@ -697,13 +857,13 @@ void tst_QQuickPathView::pathMoved()
     model.addItem("Bob", "54321");
     model.addItem("Bill", "4321");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathview0.qml"));
+    window->setSource(testFileUrl("pathview0.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
@@ -723,13 +883,16 @@ void tst_QQuickPathView::pathMoved()
         QCOMPARE(curItem->pos() + offset, QPointF(itemPos.x(), itemPos.y()));
     }
 
+    QCOMPARE(pathview->currentIndex(), 3);
+
     pathview->setOffset(0.0);
     QCOMPARE(firstItem->pos() + offset, start);
+    QCOMPARE(pathview->currentIndex(), 0);
 
     // Change delegate size
     pathview->setOffset(0.1);
     pathview->setOffset(0.0);
-    canvas->rootObject()->setProperty("delegateWidth", 30);
+    window->rootObject()->setProperty("delegateWidth", 30);
     QCOMPARE(firstItem->width(), 30.0);
     offset.setX(firstItem->width()/2);
     QTRY_COMPARE(firstItem->pos() + offset, start);
@@ -737,16 +900,45 @@ void tst_QQuickPathView::pathMoved()
     // Change delegate scale
     pathview->setOffset(0.1);
     pathview->setOffset(0.0);
-    canvas->rootObject()->setProperty("delegateScale", 1.2);
+    window->rootObject()->setProperty("delegateScale", 1.2);
     QTRY_COMPARE(firstItem->pos() + offset, start);
 
-    delete canvas;
+    delete window;
+}
+
+void tst_QQuickPathView::offset_data()
+{
+    QTest::addColumn<qreal>("offset");
+    QTest::addColumn<int>("currentIndex");
+
+    QTest::newRow("0.0") << 0.0 << 0;
+    QTest::newRow("1.0") << 7.0 << 1;
+    QTest::newRow("5.0") << 5.0 << 3;
+    QTest::newRow("4.6") << 4.6 << 3;
+    QTest::newRow("4.4") << 4.4 << 4;
+    QTest::newRow("5.4") << 5.4 << 3;
+    QTest::newRow("5.6") << 5.6 << 2;
+}
+
+void tst_QQuickPathView::offset()
+{
+    QFETCH(qreal, offset);
+    QFETCH(int, currentIndex);
+
+    QQmlEngine engine;
+    QQmlComponent c(&engine, testFileUrl("pathview3.qml"));
+    QQuickPathView *view = qobject_cast<QQuickPathView*>(c.create());
+
+    view->setOffset(offset);
+    QCOMPARE(view->currentIndex(), currentIndex);
+
+    delete view;
 }
 
 void tst_QQuickPathView::setCurrentIndex()
 {
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QaimModel model;
     model.addItem("Ben", "12345");
@@ -754,13 +946,13 @@ void tst_QQuickPathView::setCurrentIndex()
     model.addItem("Bob", "54321");
     model.addItem("Bill", "4321");
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathview0.qml"));
+    window->setSource(testFileUrl("pathview0.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
@@ -772,15 +964,15 @@ void tst_QQuickPathView::setCurrentIndex()
     offset.setX(firstItem->width()/2);
     offset.setY(firstItem->height()/2);
     QCOMPARE(firstItem->pos() + offset, start);
-    QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 0);
-    QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 0);
+    QCOMPARE(window->rootObject()->property("currentA").toInt(), 0);
+    QCOMPARE(window->rootObject()->property("currentB").toInt(), 0);
 
     pathview->setCurrentIndex(2);
 
     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
     QTRY_COMPARE(firstItem->pos() + offset, start);
-    QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 2);
-    QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 2);
+    QCOMPARE(window->rootObject()->property("currentA").toInt(), 2);
+    QCOMPARE(window->rootObject()->property("currentB").toInt(), 2);
     QCOMPARE(pathview->currentItem(), firstItem);
     QCOMPARE(firstItem->property("onPath"), QVariant(true));
 
@@ -816,6 +1008,24 @@ void tst_QQuickPathView::setCurrentIndex()
     QCOMPARE(pathview->currentItem(), firstItem);
     QCOMPARE(firstItem->property("onPath"), QVariant(true));
 
+    // Test positive indexes are wrapped.
+    pathview->setCurrentIndex(6);
+    QTRY_COMPARE(pathview->currentIndex(), 2);
+    firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
+    QVERIFY(firstItem);
+    QTRY_COMPARE(firstItem->pos() + offset, start);
+    QCOMPARE(pathview->currentItem(), firstItem);
+    QCOMPARE(firstItem->property("onPath"), QVariant(true));
+
+    // Test negative indexes are wrapped.
+    pathview->setCurrentIndex(-3);
+    QTRY_COMPARE(pathview->currentIndex(), 1);
+    firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
+    QVERIFY(firstItem);
+    QTRY_COMPARE(firstItem->pos() + offset, start);
+    QCOMPARE(pathview->currentItem(), firstItem);
+    QCOMPARE(firstItem->property("onPath"), QVariant(true));
+
     // move an item, set move duration to 0, and change currentIndex to moved item. QTBUG-22786
     model.moveItem(0, 3);
     pathview->setHighlightMoveDuration(0);
@@ -870,24 +1080,24 @@ void tst_QQuickPathView::setCurrentIndex()
     QCOMPARE(pathview->currentItem(), firstItem);
     QCOMPARE(firstItem->property("onPath"), QVariant(true));
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::resetModel()
 {
-    QQuickView *canvas = createView();
+    QQuickView *window = createView();
 
     QStringList strings;
     strings << "one" << "two" << "three";
     QStringListModel model(strings);
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("displaypath.qml"));
+    window->setSource(testFileUrl("displaypath.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     QCOMPARE(pathview->count(), model.rowCount());
@@ -910,16 +1120,16 @@ void tst_QQuickPathView::resetModel()
         QCOMPARE(display->text(), strings.at(i));
     }
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::propertyChanges()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("propertychanges.qml"));
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("propertychanges.qml"));
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("pathView");
     QVERIFY(pathView);
 
     QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
@@ -952,19 +1162,19 @@ void tst_QQuickPathView::propertyChanges()
     pathView->setMaximumFlickVelocity(1000);
     QCOMPARE(maximumFlickVelocitySpy.count(), 1);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::pathChanges()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("propertychanges.qml"));
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("propertychanges.qml"));
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("pathView");
     QVERIFY(pathView);
 
-    QQuickPath *path = canvas->rootObject()->findChild<QQuickPath*>("path");
+    QQuickPath *path = window->rootObject()->findChild<QQuickPath*>("path");
     QVERIFY(path);
 
     QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
@@ -988,7 +1198,7 @@ void tst_QQuickPathView::pathChanges()
     QCOMPARE(startXSpy.count(),1);
     QCOMPARE(startYSpy.count(),1);
 
-    QQuickPath *alternatePath = canvas->rootObject()->findChild<QQuickPath*>("alternatePath");
+    QQuickPath *alternatePath = window->rootObject()->findChild<QQuickPath*>("alternatePath");
     QVERIFY(alternatePath);
 
     QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
@@ -1002,7 +1212,7 @@ void tst_QQuickPathView::pathChanges()
     pathView->setPath(alternatePath);
     QCOMPARE(pathSpy.count(),1);
 
-    QQuickPathAttribute *pathAttribute = canvas->rootObject()->findChild<QQuickPathAttribute*>("pathAttribute");
+    QQuickPathAttribute *pathAttribute = window->rootObject()->findChild<QQuickPathAttribute*>("pathAttribute");
     QVERIFY(pathAttribute);
 
     QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
@@ -1014,19 +1224,19 @@ void tst_QQuickPathView::pathChanges()
 
     pathAttribute->setName("scale");
     QCOMPARE(nameSpy.count(),1);
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::componentChanges()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("propertychanges.qml"));
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("propertychanges.qml"));
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("pathView");
     QVERIFY(pathView);
 
-    QQmlComponent delegateComponent(canvas->engine());
+    QQmlComponent delegateComponent(window->engine());
     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
 
     QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
@@ -1037,19 +1247,19 @@ void tst_QQuickPathView::componentChanges()
 
     pathView->setDelegate(&delegateComponent);
     QCOMPARE(delegateSpy.count(),1);
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::modelChanges()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("propertychanges.qml"));
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("propertychanges.qml"));
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("pathView");
     QVERIFY(pathView);
 
-    QQuickListModel *alternateModel = canvas->rootObject()->findChild<QQuickListModel*>("alternateModel");
+    QQuickListModel *alternateModel = window->rootObject()->findChild<QQuickListModel*>("alternateModel");
     QVERIFY(alternateModel);
     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
     QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
@@ -1064,19 +1274,19 @@ void tst_QQuickPathView::modelChanges()
     pathView->setModel(QVariant());
     QCOMPARE(modelSpy.count(),2);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::pathUpdateOnStartChanged()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("pathUpdateOnStartChanged.qml"));
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("pathUpdateOnStartChanged.qml"));
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("pathView");
     QVERIFY(pathView);
 
-    QQuickPath *path = canvas->rootObject()->findChild<QQuickPath*>("path");
+    QQuickPath *path = window->rootObject()->findChild<QQuickPath*>("path");
     QVERIFY(path);
     QCOMPARE(path->startX(), 400.0);
     QCOMPARE(path->startY(), 300.0);
@@ -1086,18 +1296,18 @@ void tst_QQuickPathView::pathUpdateOnStartChanged()
     QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
     QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::package()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("pathview_package.qml"));
-    canvas->show();
-    QTest::qWaitForWindowShown(canvas);
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("pathview_package.qml"));
+    window->show();
+    QTest::qWaitForWindowShown(window);
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("photoPathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("photoPathView");
     QVERIFY(pathView);
 
 #ifdef Q_OS_MAC
@@ -1108,28 +1318,28 @@ void tst_QQuickPathView::package()
     QVERIFY(item);
     QVERIFY(item->scale() != 1.0);
 
-    delete canvas;
+    delete window;
 }
 
 //QTBUG-13017
 void tst_QQuickPathView::emptyModel()
 {
-    QQuickView *canvas = createView();
+    QQuickView *window = createView();
 
     QStringListModel model;
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("emptyModel", &model);
 
-    canvas->setSource(testFileUrl("emptymodel.qml"));
+    window->setSource(testFileUrl("emptymodel.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
 
     QCOMPARE(pathview->offset(), qreal(0.0));
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::closed()
@@ -1156,18 +1366,18 @@ void tst_QQuickPathView::closed()
 // QTBUG-14239
 void tst_QQuickPathView::pathUpdate()
 {
-    QQuickView *canvas = createView();
-    QVERIFY(canvas);
-    canvas->setSource(testFileUrl("pathUpdate.qml"));
+    QQuickView *window = createView();
+    QVERIFY(window);
+    window->setSource(testFileUrl("pathUpdate.qml"));
 
-    QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
+    QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("pathView");
     QVERIFY(pathView);
 
     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
     QVERIFY(item);
     QCOMPARE(item->x(), 150.0);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::visualDataModel()
@@ -1208,49 +1418,83 @@ void tst_QQuickPathView::undefinedPath()
 
 void tst_QQuickPathView::mouseDrag()
 {
-    QQuickView *canvas = createView();
-    canvas->setSource(testFileUrl("dragpath.qml"));
-    canvas->show();
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_COMPARE(canvas, qGuiApp->focusWindow());
-
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickView *window = createView();
+    window->setSource(testFileUrl("dragpath.qml"));
+    window->show();
+    window->requestActivateWindow();
+    QTest::qWaitForWindowShown(window);
+    QTRY_COMPARE(window, qGuiApp->focusWindow());
+
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
 
+    QSignalSpy movingSpy(pathview, SIGNAL(movingChanged()));
+    QSignalSpy moveStartedSpy(pathview, SIGNAL(movementStarted()));
+    QSignalSpy moveEndedSpy(pathview, SIGNAL(movementEnded()));
+    QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged()));
+    QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted()));
+    QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded()));
+
     int current = pathview->currentIndex();
 
-    QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
+    QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10,100));
     QTest::qWait(100);
 
     {
         QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
-        QGuiApplication::sendEvent(canvas, &mv);
+        QGuiApplication::sendEvent(window, &mv);
     }
+    // first move beyond threshold does not trigger drag
+    QVERIFY(!pathview->isMoving());
+    QVERIFY(!pathview->isDragging());
+    QCOMPARE(movingSpy.count(), 0);
+    QCOMPARE(moveStartedSpy.count(), 0);
+    QCOMPARE(moveEndedSpy.count(), 0);
+    QCOMPARE(draggingSpy.count(), 0);
+    QCOMPARE(dragStartedSpy.count(), 0);
+    QCOMPARE(dragEndedSpy.count(), 0);
+
     {
         QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
-        QGuiApplication::sendEvent(canvas, &mv);
+        QGuiApplication::sendEvent(window, &mv);
     }
+    // next move beyond threshold does trigger drag
+    QVERIFY(pathview->isMoving());
+    QVERIFY(pathview->isDragging());
+    QCOMPARE(movingSpy.count(), 1);
+    QCOMPARE(moveStartedSpy.count(), 1);
+    QCOMPARE(moveEndedSpy.count(), 0);
+    QCOMPARE(draggingSpy.count(), 1);
+    QCOMPARE(dragStartedSpy.count(), 1);
+    QCOMPARE(dragEndedSpy.count(), 0);
 
     QVERIFY(pathview->currentIndex() != current);
 
-    QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
+    QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(40,100));
+    QVERIFY(!pathview->isDragging());
+    QCOMPARE(draggingSpy.count(), 2);
+    QCOMPARE(dragStartedSpy.count(), 1);
+    QCOMPARE(dragEndedSpy.count(), 1);
+    QTRY_COMPARE(movingSpy.count(), 2);
+    QTRY_COMPARE(moveEndedSpy.count(), 1);
+    QCOMPARE(moveStartedSpy.count(), 1);
 
-    delete canvas;
+    delete window;
 }
 
+#ifndef QT_NO_WIDGETS
 void tst_QQuickPathView::treeModel()
 {
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QStandardItemModel model;
     initStandardTreeModel(&model);
-    canvas->engine()->rootContext()->setContextProperty("myModel", &model);
+    window->engine()->rootContext()->setContextProperty("myModel", &model);
 
-    canvas->setSource(testFileUrl("treemodel.qml"));
+    window->setSource(testFileUrl("treemodel.qml"));
 
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
     QCOMPARE(pathview->count(), 3);
 
@@ -1264,20 +1508,21 @@ void tst_QQuickPathView::treeModel()
     QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0));
     QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
 
-    delete canvas;
+    delete window;
 }
+#endif
 
 void tst_QQuickPathView::changePreferredHighlight()
 {
-    QQuickView *canvas = createView();
-    canvas->setGeometry(0,0,400,200);
-    canvas->setSource(testFileUrl("dragpath.qml"));
-    canvas->show();
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_COMPARE(canvas, qGuiApp->focusWindow());
-
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickView *window = createView();
+    window->setGeometry(0,0,400,200);
+    window->setSource(testFileUrl("dragpath.qml"));
+    window->show();
+    window->requestActivateWindow();
+    QTest::qWaitForWindowShown(window);
+    QTRY_COMPARE(window, qGuiApp->focusWindow());
+
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
 
     int current = pathview->currentIndex();
@@ -1299,16 +1544,16 @@ void tst_QQuickPathView::changePreferredHighlight()
     QTRY_COMPARE(firstItem->pos() + offset, start);
     QCOMPARE(pathview->currentIndex(), 0);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::creationContext()
 {
-    QQuickView canvas;
-    canvas.setGeometry(0,0,240,320);
-    canvas.setSource(testFileUrl("creationContext.qml"));
+    QQuickView window;
+    window.setGeometry(0,0,240,320);
+    window.setSource(testFileUrl("creationContext.qml"));
 
-    QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
+    QQuickItem *rootItem = qobject_cast<QQuickItem *>(window.rootObject());
     QVERIFY(rootItem);
     QVERIFY(rootItem->property("count").toInt() > 0);
 
@@ -1320,18 +1565,18 @@ void tst_QQuickPathView::creationContext()
 // QTBUG-21320
 void tst_QQuickPathView::currentOffsetOnInsertion()
 {
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
 
     QaimModel model;
 
-    QQmlContext *ctxt = canvas->rootContext();
+    QQmlContext *ctxt = window->rootContext();
     ctxt->setContextProperty("testModel", &model);
 
-    canvas->setSource(testFileUrl("pathline.qml"));
+    window->setSource(testFileUrl("pathline.qml"));
     qApp->processEvents();
 
-    QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
+    QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "view");
     QVERIFY(pathview != 0);
 
     pathview->setPreferredHighlightBegin(0.5);
@@ -1393,19 +1638,19 @@ void tst_QQuickPathView::currentOffsetOnInsertion()
     // verify that current item (item 1) is still at offset 0.5
     QCOMPARE(item->pos() + offset, start);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::asynchronous()
 {
-    QQuickView *canvas = createView();
-    canvas->show();
+    QQuickView *window = createView();
+    window->show();
     QQmlIncubationController controller;
-    canvas->engine()->setIncubationController(&controller);
+    window->engine()->setIncubationController(&controller);
 
-    canvas->setSource(testFileUrl("asyncloader.qml"));
+    window->setSource(testFileUrl("asyncloader.qml"));
 
-    QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
+    QQuickItem *rootObject = qobject_cast<QQuickItem*>(window->rootObject());
     QVERIFY(rootObject);
 
     QQuickPathView *pathview = 0;
@@ -1449,7 +1694,7 @@ void tst_QQuickPathView::asynchronous()
         QCOMPARE(curItem->pos() + offset, itemPos);
     }
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::missingPercent()
@@ -1464,52 +1709,64 @@ void tst_QQuickPathView::missingPercent()
 
 void tst_QQuickPathView::cancelDrag()
 {
-    QQuickView *canvas = createView();
-    canvas->setSource(testFileUrl("dragpath.qml"));
-    canvas->show();
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_COMPARE(canvas, qGuiApp->focusWindow());
-
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickView *window = createView();
+    window->setSource(testFileUrl("dragpath.qml"));
+    window->show();
+    window->requestActivateWindow();
+    QTest::qWaitForWindowShown(window);
+    QTRY_COMPARE(window, qGuiApp->focusWindow());
+
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
 
+    QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged()));
+    QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted()));
+    QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded()));
+
     // drag between snap points
-    QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
+    QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10,100));
     QTest::qWait(100);
-    QTest::mouseMove(canvas, QPoint(30, 100));
-    QTest::mouseMove(canvas, QPoint(85, 100));
+    QTest::mouseMove(window, QPoint(30, 100));
+    QTest::mouseMove(window, QPoint(85, 100));
 
     QTRY_VERIFY(pathview->offset() != qFloor(pathview->offset()));
     QTRY_VERIFY(pathview->isMoving());
+    QVERIFY(pathview->isDragging());
+    QCOMPARE(draggingSpy.count(), 1);
+    QCOMPARE(dragStartedSpy.count(), 1);
+    QCOMPARE(dragEndedSpy.count(), 0);
 
     // steal mouse grab - cancels PathView dragging
-    QQuickItem *item = canvas->rootObject()->findChild<QQuickItem*>("text");
+    QQuickItem *item = window->rootObject()->findChild<QQuickItem*>("text");
     item->grabMouse();
 
     // returns to a snap point.
     QTRY_VERIFY(pathview->offset() == qFloor(pathview->offset()));
     QTRY_VERIFY(!pathview->isMoving());
+    QVERIFY(!pathview->isDragging());
+    QCOMPARE(draggingSpy.count(), 2);
+    QCOMPARE(dragStartedSpy.count(), 1);
+    QCOMPARE(dragEndedSpy.count(), 1);
 
-    QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
+    QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(40,100));
 
-    delete canvas;
+    delete window;
 }
 
 void tst_QQuickPathView::maximumFlickVelocity()
 {
-    QQuickView *canvas = createView();
-    canvas->setSource(testFileUrl("dragpath.qml"));
-    canvas->show();
-    canvas->requestActivateWindow();
-    QTest::qWaitForWindowShown(canvas);
-    QTRY_COMPARE(canvas, qGuiApp->focusWindow());
-
-    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
+    QQuickView *window = createView();
+    window->setSource(testFileUrl("dragpath.qml"));
+    window->show();
+    window->requestActivateWindow();
+    QTest::qWaitForWindowShown(window);
+    QTRY_COMPARE(window, qGuiApp->focusWindow());
+
+    QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
     QVERIFY(pathview != 0);
 
     pathview->setMaximumFlickVelocity(700);
-    flick(canvas, QPoint(200,10), QPoint(10,10), 180);
+    flick(window, QPoint(200,10), QPoint(10,10), 180);
     QVERIFY(pathview->isMoving());
     QVERIFY(pathview->isFlicking());
     QTRY_VERIFY(!pathview->isMoving());
@@ -1518,7 +1775,7 @@ void tst_QQuickPathView::maximumFlickVelocity()
 
     pathview->setOffset(0.);
     pathview->setMaximumFlickVelocity(300);
-    flick(canvas, QPoint(200,10), QPoint(10,10), 180);
+    flick(window, QPoint(200,10), QPoint(10,10), 180);
     QVERIFY(pathview->isMoving());
     QVERIFY(pathview->isFlicking());
     QTRY_VERIFY(!pathview->isMoving());
@@ -1527,7 +1784,7 @@ void tst_QQuickPathView::maximumFlickVelocity()
 
     pathview->setOffset(0.);
     pathview->setMaximumFlickVelocity(500);
-    flick(canvas, QPoint(200,10), QPoint(10,10), 180);
+    flick(window, QPoint(200,10), QPoint(10,10), 180);
     QVERIFY(pathview->isMoving());
     QVERIFY(pathview->isFlicking());
     QTRY_VERIFY(!pathview->isMoving());
@@ -1538,9 +1795,92 @@ void tst_QQuickPathView::maximumFlickVelocity()
     QVERIFY(dist3 > dist2);
     QVERIFY(dist2 < dist1);
 
-    delete canvas;
+    delete window;
 }
 
+void tst_QQuickPathView::snapToItem()
+{
+    QFETCH(bool, enforceRange);
+
+    QQuickView *window = createView();
+    window->setSource(testFileUrl("panels.qml"));
+    QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view");
+    QVERIFY(pathview != 0);
+
+    window->rootObject()->setProperty("enforceRange", enforceRange);
+    QTRY_VERIFY(!pathview->isMoving()); // ensure stable
+
+    int currentIndex = pathview->currentIndex();
+
+    QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged()));
+
+    flick(window, QPoint(200,10), QPoint(10,10), 180);
+
+    QVERIFY(pathview->isMoving());
+    QTRY_VERIFY(!pathview->isMoving());
+
+    QVERIFY(pathview->offset() == qFloor(pathview->offset()));
+
+    if (enforceRange)
+        QVERIFY(pathview->currentIndex() != currentIndex);
+    else
+        QVERIFY(pathview->currentIndex() == currentIndex);
+}
+
+void tst_QQuickPathView::snapToItem_data()
+{
+    QTest::addColumn<bool>("enforceRange");
+
+    QTest::newRow("no enforce range") << false;
+    QTest::newRow("enforce range") << true;
+}
+
+void tst_QQuickPathView::snapOneItem()
+{
+    QFETCH(bool, enforceRange);
+
+    QQuickView *window = createView();
+    window->setSource(testFileUrl("panels.qml"));
+    window->show();
+    window->requestActivateWindow();
+    QTest::qWaitForWindowShown(window);
+    QTRY_COMPARE(window, qGuiApp->focusWindow());
+
+    QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view");
+    QVERIFY(pathview != 0);
+
+    window->rootObject()->setProperty("enforceRange", enforceRange);
+
+    QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged()));
+
+    window->rootObject()->setProperty("snapOne", true);
+    QVERIFY(snapModeSpy.count() == 1);
+    QTRY_VERIFY(!pathview->isMoving()); // ensure stable
+
+    int currentIndex = pathview->currentIndex();
+
+    double startOffset = pathview->offset();
+    flick(window, QPoint(200,10), QPoint(10,10), 180);
+
+    QVERIFY(pathview->isMoving());
+    QTRY_VERIFY(!pathview->isMoving());
+
+    // must have moved only one item
+    QCOMPARE(pathview->offset(), fmodf(3.0 + startOffset - 1.0, 3.0));
+
+    if (enforceRange)
+        QVERIFY(pathview->currentIndex() == currentIndex+1);
+    else
+        QVERIFY(pathview->currentIndex() == currentIndex);
+}
+
+void tst_QQuickPathView::snapOneItem_data()
+{
+    QTest::addColumn<bool>("enforceRange");
+
+    QTest::newRow("no enforce range") << false;
+    QTest::newRow("enforce range") << true;
+}
 
 
 QTEST_MAIN(tst_QQuickPathView)