Add SnapPosition mode to positionViewAtIndex() in List/GridView.
authorMartin Jones <martin.jones@nokia.com>
Thu, 26 Jul 2012 03:14:50 +0000 (13:14 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 27 Jul 2012 01:08:24 +0000 (03:08 +0200)
This allows the view to be positioned on a snap boundary, which is
generally what is wanted when strict highlight mode s set or snapping
is enabled.

Task-number: QTBUG-26605
Change-Id: I6288dc8be4ff16c412b56ab449b6a9fb7b7ea889
Reviewed-by: Bea Lam <bea.lam@nokia.com>
src/quick/items/qquickgridview.cpp
src/quick/items/qquickitemview.cpp
src/quick/items/qquickitemview_p.h
src/quick/items/qquicklistview.cpp
tests/auto/quick/qquickgridview/data/layouts.qml
tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
tests/auto/quick/qquicklistview/data/listviewtest.qml
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp

index 8f330b9..518692a 100644 (file)
@@ -2490,6 +2490,9 @@ bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) co
     bring the item into view.
     \li GridView.Contain - ensure the entire item is visible.  If the item is larger than
     the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
+    \li GridView.SnapPosition - position the item at \l preferredHighlightBegin.  This mode
+    is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
+    via \l snapMode.
     \endlist
 
     If positioning the view at the index would cause empty space to be displayed at
index 8f1687f..240027e 100644 (file)
@@ -613,6 +613,10 @@ void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
         return;
     d->highlightRange = mode;
     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+    if (isComponentComplete()) {
+        d->updateViewport();
+        d->fixupPosition();
+    }
     emit highlightRangeModeChanged();
 }
 
@@ -631,6 +635,10 @@ void QQuickItemView::setPreferredHighlightBegin(qreal start)
         return;
     d->highlightRangeStart = start;
     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+    if (isComponentComplete()) {
+        d->updateViewport();
+        d->fixupPosition();
+    }
     emit preferredHighlightBeginChanged();
 }
 
@@ -641,6 +649,10 @@ void QQuickItemView::resetPreferredHighlightBegin()
     if (d->highlightRangeStart == 0)
         return;
     d->highlightRangeStart = 0;
+    if (isComponentComplete()) {
+        d->updateViewport();
+        d->fixupPosition();
+    }
     emit preferredHighlightBeginChanged();
 }
 
@@ -658,6 +670,10 @@ void QQuickItemView::setPreferredHighlightEnd(qreal end)
         return;
     d->highlightRangeEnd = end;
     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+    if (isComponentComplete()) {
+        d->updateViewport();
+        d->fixupPosition();
+    }
     emit preferredHighlightEndChanged();
 }
 
@@ -668,6 +684,10 @@ void QQuickItemView::resetPreferredHighlightEnd()
     if (d->highlightRangeEnd == 0)
         return;
     d->highlightRangeEnd = 0;
+    if (isComponentComplete()) {
+        d->updateViewport();
+        d->fixupPosition();
+    }
     emit preferredHighlightEndChanged();
 }
 
@@ -819,7 +839,7 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
     Q_Q(QQuickItemView);
     if (!isValid())
         return;
-    if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain)
+    if (mode < QQuickItemView::Beginning || mode > QQuickItemView::SnapPosition)
         return;
 
     applyPendingChanges();
@@ -871,6 +891,10 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
                 pos = itemPos - size() + item->size();
             if (itemPos < pos)
                 pos = itemPos;
+            break;
+        case QQuickItemView::SnapPosition:
+            pos = itemPos - highlightRangeStart;
+            break;
         }
         pos = qMin(pos, maxExtent);
         qreal minExtent;
index e1cb020..75d573c 100644 (file)
@@ -199,7 +199,7 @@ public:
     int highlightMoveDuration() const;
     virtual void setHighlightMoveDuration(int);
 
-    enum PositionMode { Beginning, Center, End, Visible, Contain };
+    enum PositionMode { Beginning, Center, End, Visible, Contain, SnapPosition };
 
     Q_INVOKABLE void positionViewAtIndex(int index, int mode);
     Q_INVOKABLE int indexAt(qreal x, qreal y) const;
index 18b9947..61574ee 100644 (file)
@@ -3031,6 +3031,9 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
     bring the item into view.
     \li ListView.Contain - ensure the entire item is visible.  If the item is larger than
     the view the item is positioned at the top (or left for horizontal orientation) of the view.
+    \li ListView.SnapPosition - position the item at \l preferredHighlightBegin.  This mode
+    is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
+    via \l snapMode.
     \endlist
 
     If positioning the view at \a index would cause empty space to be displayed at
index f528841..1ae0606 100644 (file)
@@ -7,6 +7,7 @@ Rectangle {
 
     property bool showHeader: false
     property bool showFooter: false
+    property bool enforceRange: false
 
     Component {
         id: myDelegate
@@ -42,7 +43,7 @@ Rectangle {
 
     Component {
         id: headerFooter
-        Rectangle { width: 30; height: 320; color: "blue" }
+        Rectangle { width: 30; height: 30; color: "blue" }
     }
 
     GridView {
@@ -55,6 +56,9 @@ Rectangle {
         flow: (testTopToBottom == false) ? GridView.LeftToRight : GridView.TopToBottom
         layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
         verticalLayoutDirection: (testBottomToTop == true) ? GridView.BottomToTop : GridView.TopToBottom
+        highlightRangeMode: enforceRange ? GridView.StrictlyEnforceRange : GridView.NoHighlightRange
+        preferredHighlightBegin: enforceRange ? 120 : 0
+        preferredHighlightEnd: enforceRange ? 120 : 0
         model: testModel
         delegate: myDelegate
         header: root.showHeader ? headerFooter : null
index c8991cc..a5f37b7 100644 (file)
@@ -62,6 +62,7 @@
 Q_DECLARE_METATYPE(QQuickGridView::Flow)
 Q_DECLARE_METATYPE(Qt::LayoutDirection)
 Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection)
+Q_DECLARE_METATYPE(QQuickItemView::PositionMode)
 Q_DECLARE_METATYPE(Qt::Key)
 
 using namespace QQuickViewTestUtil;
@@ -108,8 +109,9 @@ private slots:
     void propertyChanges();
     void componentChanges();
     void modelChanges();
+    void positionViewAtBeginningEnd();
     void positionViewAtIndex();
-    void positionViewAtIndex_rightToLeft();
+    void positionViewAtIndex_data();
     void mirroring();
     void snapping();
     void resetModel();
@@ -2449,7 +2451,7 @@ void tst_QQuickGridView::modelChanges()
     delete window;
 }
 
-void tst_QQuickGridView::positionViewAtIndex()
+void tst_QQuickGridView::positionViewAtBeginningEnd()
 {
     QQuickView *window = createView();
 
@@ -2473,157 +2475,33 @@ void tst_QQuickGridView::positionViewAtIndex()
     QTRY_VERIFY(contentItem != 0);
     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
-    // Confirm items positioned correctly
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), (i%3)*80.);
-        QTRY_COMPARE(item->y(), (i/3)*60.);
-    }
-
-    // Position on a currently visible item
-    gridview->positionViewAtIndex(4, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->indexAt(120, 90), 4);
-    QTRY_COMPARE(gridview->contentY(), 60.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), (i%3)*80.);
-        QTRY_COMPARE(item->y(), (i/3)*60.);
-    }
-
-    // Position on an item beyond the visible items
-    gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->indexAt(40, 450), 21);
-    QTRY_COMPARE(gridview->contentY(), 420.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), (i%3)*80.);
-        QTRY_COMPARE(item->y(), (i/3)*60.);
-    }
-
-    // Position on an item that would leave empty space if positioned at the top
-    gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->indexAt(120, 630), 31);
-    QTRY_COMPARE(gridview->contentY(), 520.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), (i%3)*80.);
-        QTRY_COMPARE(item->y(), (i/3)*60.);
-    }
-
-    // Position at the beginning again
-    gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->indexAt(0, 0), 0);
-    QTRY_COMPARE(gridview->indexAt(40, 30), 0);
-    QTRY_COMPARE(gridview->indexAt(80, 60), 4);
+    // positionViewAtBeginning
+    gridview->setContentY(150);
+    gridview->positionViewAtBeginning();
     QTRY_COMPARE(gridview->contentY(), 0.);
 
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), (i%3)*80.);
-        QTRY_COMPARE(item->y(), (i/3)*60.);
-    }
-
-    // Position at End
-    gridview->positionViewAtIndex(30, QQuickGridView::End);
-    QTRY_COMPARE(gridview->contentY(), 340.);
-
-    // Position in Center
-    gridview->positionViewAtIndex(15, QQuickGridView::Center);
-    QTRY_COMPARE(gridview->contentY(), 170.);
-
-    // Ensure at least partially visible
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentY(), 170.);
-
-    gridview->setContentY(302);
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentY(), 302.);
-
-    gridview->setContentY(360);
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentY(), 300.);
-
-    gridview->setContentY(60);
-    gridview->positionViewAtIndex(20, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentY(), 60.);
-
-    gridview->setContentY(20);
-    gridview->positionViewAtIndex(20, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentY(), 100.);
-
-    // Ensure completely visible
-    gridview->setContentY(120);
-    gridview->positionViewAtIndex(20, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentY(), 120.);
+    gridview->setContentY(80);
+    window->rootObject()->setProperty("showHeader", true);
+    gridview->positionViewAtBeginning();
+    QTRY_COMPARE(gridview->contentY(), -30.);
 
-    gridview->setContentY(302);
-    gridview->positionViewAtIndex(15, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentY(), 300.);
+    // positionViewAtEnd
+    gridview->setContentY(150);
+    gridview->positionViewAtEnd();
+    QTRY_COMPARE(gridview->contentY(), 520.);   // 14*60 - 320   (14 rows)
 
-    gridview->setContentY(60);
-    gridview->positionViewAtIndex(20, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentY(), 100.);
+    gridview->setContentY(80);
+    window->rootObject()->setProperty("showFooter", true);
+    gridview->positionViewAtEnd();
+    QTRY_COMPARE(gridview->contentY(), 550.);
 
     // Test for Top To Bottom layout
     ctxt->setContextProperty("testTopToBottom", QVariant(true));
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), (i/5)*80.);
-        QTRY_COMPARE(item->y(), (i%5)*60.);
-    }
-
-    // Position at End
-    gridview->positionViewAtIndex(30, QQuickGridView::End);
-    QTRY_COMPARE(gridview->contentX(), 320.);
-    QTRY_COMPARE(gridview->contentY(), 0.);
-
-    // Position in Center
-    gridview->positionViewAtIndex(15, QQuickGridView::Center);
-    QTRY_COMPARE(gridview->contentX(), 160.);
-
-    // Ensure at least partially visible
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), 160.);
-
-    gridview->setContentX(170);
-    gridview->positionViewAtIndex(25, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), 170.);
-
-    gridview->positionViewAtIndex(30, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), 320.);
-
-    gridview->setContentX(170);
-    gridview->positionViewAtIndex(25, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentX(), 240.);
+    window->rootObject()->setProperty("showHeader", false);
+    window->rootObject()->setProperty("showFooter", false);
 
     // positionViewAtBeginning
+    gridview->setContentX(150);
     gridview->positionViewAtBeginning();
     QTRY_COMPARE(gridview->contentX(), 0.);
 
@@ -2652,6 +2530,132 @@ void tst_QQuickGridView::positionViewAtIndex()
     delete window;
 }
 
+void tst_QQuickGridView::positionViewAtIndex()
+{
+    QFETCH(bool, enforceRange);
+    QFETCH(bool, topToBottom);
+    QFETCH(bool, rightToLeft);
+    QFETCH(qreal, initContentPos);
+    QFETCH(int, index);
+    QFETCH(QQuickGridView::PositionMode, mode);
+    QFETCH(qreal, contentPos);
+
+    QQuickView *window = getView();
+
+    QaimModel model;
+    for (int i = 0; i < 40; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QQmlContext *ctxt = window->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    ctxt->setContextProperty("testRightToLeft", QVariant(rightToLeft));
+    ctxt->setContextProperty("testTopToBottom", QVariant(topToBottom));
+    ctxt->setContextProperty("testBottomToTop", QVariant(false));
+
+    window->setSource(testFileUrl("layouts.qml"));
+    window->show();
+    qApp->processEvents();
+
+    QQuickGridView *gridview = findItem<QQuickGridView>(window->rootObject(), "grid");
+    QTRY_VERIFY(gridview != 0);
+    QQuickItem *contentItem = gridview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    window->rootObject()->setProperty("enforceRange", enforceRange);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    if (topToBottom)
+        gridview->setContentX(initContentPos);
+    else
+        gridview->setContentY(initContentPos);
+
+    gridview->positionViewAtIndex(index, mode);
+    if (topToBottom)
+        QTRY_COMPARE(gridview->contentX(), contentPos);
+    else
+        QTRY_COMPARE(gridview->contentY(), contentPos);
+
+    // Confirm items positioned correctly
+    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+    for (int i = index; i < model.count() && i < itemCount-index-1; ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (!item) qWarning() << "Item" << i << "not found";
+        QTRY_VERIFY(item);
+        if (topToBottom) {
+            if (rightToLeft) {
+                QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
+                QTRY_COMPARE(item->y(), qreal((i%5)*60));
+            } else {
+                QTRY_COMPARE(item->x(), (i/5)*80.);
+                QTRY_COMPARE(item->y(), (i%5)*60.);
+            }
+        } else {
+            QTRY_COMPARE(item->x(), (i%3)*80.);
+            QTRY_COMPARE(item->y(), (i/3)*60.);
+        }
+    }
+
+    releaseView(window);
+}
+
+void tst_QQuickGridView::positionViewAtIndex_data()
+{
+    QTest::addColumn<bool>("enforceRange");
+    QTest::addColumn<bool>("topToBottom");
+    QTest::addColumn<bool>("rightToLeft");
+    QTest::addColumn<qreal>("initContentPos");
+    QTest::addColumn<int>("index");
+    QTest::addColumn<QQuickGridView::PositionMode>("mode");
+    QTest::addColumn<qreal>("contentPos");
+
+    QTest::newRow("no range, 4 at Beginning") << false << false << false << 0. << 4 << QQuickGridView::Beginning << 60.;
+    QTest::newRow("no range, 4 at End") << false << false << false << 0. << 4 << QQuickGridView::End << 0.;
+    QTest::newRow("no range, 21 at Beginning") << false << false << false << 0. << 21 << QQuickGridView::Beginning << 420.;
+    // Position on an item that would leave empty space if positioned at the top
+    QTest::newRow("no range, 31 at Beginning") << false << false << false << 0. << 31 << QQuickGridView::Beginning << 520.;
+    QTest::newRow("no range, 30 at End") << false << false << false << 0. << 30 << QQuickGridView::End << 340.;
+    QTest::newRow("no range, 15 at Center") << false << false << false << 0. << 15 << QQuickGridView::Center << 170.;
+    // Ensure at least partially visible
+    QTest::newRow("no range, 15 visible => Visible") << false << false << false << 302. << 15 << QQuickGridView::Visible << 302.;
+    QTest::newRow("no range, 15 after visible => Visible") << false << false << false << 360. << 15 << QQuickGridView::Visible << 300.;
+    QTest::newRow("no range, 20 visible => Visible") << false << false << false << 60. << 20 << QQuickGridView::Visible << 60.;
+    QTest::newRow("no range, 20 before visible => Visible") << false << false << false << 20. << 20 << QQuickGridView::Visible << 100.;
+    // Ensure completely visible
+    QTest::newRow("no range, 20 visible => Contain") << false << false << false << 120. << 20 << QQuickGridView::Contain << 120.;
+    QTest::newRow("no range, 15 partially visible => Contain") << false << false << false << 302. << 15 << QQuickGridView::Contain << 300.;
+    QTest::newRow("no range, 20 partially visible => Contain") << false << false << false << 60. << 20 << QQuickGridView::Contain << 100.;
+
+    QTest::newRow("strict range, 4 at End") << true << false << false << 0. << 4 << QQuickGridView::End << -120.;
+    QTest::newRow("strict range, 38 at Beginning") << true << false << false << 0. << 38 << QQuickGridView::Beginning << 660.;
+    QTest::newRow("strict range, 15 at Center") << true << false << false << 0. << 15 << QQuickGridView::Center << 180.;
+    QTest::newRow("strict range, 4 at SnapPosition") << true << false << false << 0. << 4 << QQuickGridView::SnapPosition << -60.;
+    QTest::newRow("strict range, 10 at SnapPosition") << true << false << false << 0. << 10 << QQuickGridView::SnapPosition << 60.;
+    QTest::newRow("strict range, 38 at SnapPosition") << true << false << false << 0. << 38 << QQuickGridView::SnapPosition << 600.;
+
+    // TopToBottom
+    QTest::newRow("no range, ttb, 30 at End") << false << true << false << 0. << 30 << QQuickGridView::End << 320.;
+    QTest::newRow("no range, ttb, 15 at Center") << false << true << false << 0. << 15 << QQuickGridView::Center << 160.;
+    QTest::newRow("no range, ttb, 15 visible => Visible") << false << true << false << 160. << 15 << QQuickGridView::Visible << 160.;
+    QTest::newRow("no range, ttb, 25 partially visible => Visible") << false << true << false << 170. << 25 << QQuickGridView::Visible << 170.;
+    QTest::newRow("no range, ttb, 30 before visible => Visible") << false << true << false << 170. << 30 << QQuickGridView::Visible << 320.;
+    QTest::newRow("no range, ttb, 25 partially visible => Contain") << false << true << false << 170. << 25 << QQuickGridView::Contain << 240.;
+
+    // RightToLeft
+    QTest::newRow("no range, rtl, ttb, 6 at Beginning") << false << true << true << 0. << 6 << QQuickGridView::Beginning << -320.;
+    QTest::newRow("no range, rtl, ttb, 21 at Beginning") << false << true << true << 0. << 21 << QQuickGridView::Beginning << -560.;
+    // Position on an item that would leave empty space if positioned at the top
+    QTest::newRow("no range, rtl, ttb, 31 at Beginning") << false << true << true << 0. << 31 << QQuickGridView::Beginning << -640.;
+    QTest::newRow("no range, rtl, ttb, 0 at Beginning") << false << true << true << -400. << 0 << QQuickGridView::Beginning << -240.;
+    QTest::newRow("no range, rtl, ttb, 30 at End") << false << true << true << 0. << 30 << QQuickGridView::End << -560.;
+    QTest::newRow("no range, rtl, ttb, 15 at Center") << false << true << true << 0. << 15 << QQuickGridView::Center << -400.;
+    QTest::newRow("no range, rtl, ttb, 15 visible => Visible") << false << true << true << -555. << 15 << QQuickGridView::Visible << -555.;
+    QTest::newRow("no range, rtl, ttb, 15 not visible => Visible") << false << true << true << -239. << 15 << QQuickGridView::Visible << -320.;
+    QTest::newRow("no range, rtl, ttb, 15 partially visible => Visible") << false << true << true << -300. << 15 << QQuickGridView::Visible << -300.;
+    QTest::newRow("no range, rtl, ttb, 20 visible => Contain") << false << true << true << -400. << 20 << QQuickGridView::Contain << -400.;
+    QTest::newRow("no range, rtl, ttb, 15 partially visible => Contain") << false << true << true << -315. << 15 << QQuickGridView::Contain << -320.;
+}
+
 void tst_QQuickGridView::snapping()
 {
     QQuickView *window = createView();
@@ -2750,139 +2754,6 @@ void tst_QQuickGridView::mirroring()
     delete windowB;
 }
 
-void tst_QQuickGridView::positionViewAtIndex_rightToLeft()
-{
-    QQuickView *window = createView();
-
-    QaimModel model;
-    for (int i = 0; i < 40; i++)
-        model.addItem("Item" + QString::number(i), "");
-
-    QQmlContext *ctxt = window->rootContext();
-    ctxt->setContextProperty("testModel", &model);
-    ctxt->setContextProperty("testTopToBottom", QVariant(true));
-    ctxt->setContextProperty("testRightToLeft", QVariant(true));
-    ctxt->setContextProperty("testBottomToTop", QVariant(false));
-
-    window->setSource(testFileUrl("layouts.qml"));
-    qApp->processEvents();
-
-    QQuickGridView *gridview = findItem<QQuickGridView>(window->rootObject(), "grid");
-    QTRY_VERIFY(gridview != 0);
-
-    QQuickItem *contentItem = gridview->contentItem();
-    QTRY_VERIFY(contentItem != 0);
-
-    // Confirm items positioned correctly
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
-        QTRY_COMPARE(item->y(), qreal((i%5)*60));
-    }
-
-    // Position on a currently visible item
-    gridview->positionViewAtIndex(6, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->contentX(), -320.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
-        QTRY_COMPARE(item->y(), qreal((i%5)*60));
-    }
-
-    // Position on an item beyond the visible items
-    gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->contentX(), -560.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
-        QTRY_COMPARE(item->y(), qreal((i%5)*60));
-    }
-
-    // Position on an item that would leave empty space if positioned at the top
-    gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->contentX(), -640.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
-        QTRY_COMPARE(item->y(), qreal((i%5)*60));
-    }
-
-    // Position at the beginning again
-    gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
-    QTRY_COMPARE(gridview->contentX(), -240.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
-        QTRY_COMPARE(item->y(), qreal((i%5)*60));
-    }
-
-    // Position at End
-    gridview->positionViewAtIndex(30, QQuickGridView::End);
-    QTRY_COMPARE(gridview->contentX(), -560.);
-
-    // Position in Center
-    gridview->positionViewAtIndex(15, QQuickGridView::Center);
-    QTRY_COMPARE(gridview->contentX(), -400.);
-
-    // Ensure at least partially visible
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), -400.);
-
-    gridview->setContentX(-555.);
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), -555.);
-
-    gridview->setContentX(-239);
-    gridview->positionViewAtIndex(15, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), -320.);
-
-    gridview->setContentX(-239);
-    gridview->positionViewAtIndex(20, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), -400.);
-
-    gridview->setContentX(-640);
-    gridview->positionViewAtIndex(20, QQuickGridView::Visible);
-    QTRY_COMPARE(gridview->contentX(), -560.);
-
-    // Ensure completely visible
-    gridview->setContentX(-400);
-    gridview->positionViewAtIndex(20, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentX(), -400.);
-
-    gridview->setContentX(-315);
-    gridview->positionViewAtIndex(15, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentX(), -320.);
-
-    gridview->setContentX(-640);
-    gridview->positionViewAtIndex(20, QQuickGridView::Contain);
-    QTRY_COMPARE(gridview->contentX(), -560.);
-
-    delete window;
-}
-
 void tst_QQuickGridView::resetModel()
 {
     QQuickView *window = createView();
index 8da859f..159483a 100644 (file)
@@ -9,6 +9,7 @@ Rectangle {
     property int count: list.count
     property bool showHeader: false
     property bool showFooter: false
+    property bool enforceRange: false
     property real hr: list.visibleArea.heightRatio
     function heightRatio() {
         return list.visibleArea.heightRatio
@@ -126,6 +127,9 @@ Rectangle {
         highlight: testObject.invalidHighlight ? invalidHl : myHighlight
         highlightMoveVelocity: 1000
         highlightResizeVelocity: 1000
+        preferredHighlightBegin: enforceRange ? 120 : 0
+        preferredHighlightEnd: enforceRange ? 120 : 0
+        highlightRangeMode: enforceRange ? ListView.StrictlyEnforceRange : ListView.NoHighlightRange
         cacheBuffer: testObject.cacheBuffer
         header: root.showHeader ? headerFooter : null
         footer: root.showFooter ? headerFooter : null
index 18b4553..fabba8f 100644 (file)
@@ -58,6 +58,7 @@
 
 Q_DECLARE_METATYPE(Qt::LayoutDirection)
 Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection)
+Q_DECLARE_METATYPE(QQuickItemView::PositionMode)
 Q_DECLARE_METATYPE(QQuickListView::Orientation)
 Q_DECLARE_METATYPE(Qt::Key)
 
@@ -136,7 +137,9 @@ private slots:
     void sectionPropertyChange();
     void sectionDelegateChange();
     void cacheBuffer();
+    void positionViewAtBeginningEnd();
     void positionViewAtIndex();
+    void positionViewAtIndex_data();
     void resetModel();
     void propertyChanges();
     void componentChanges();
@@ -2949,7 +2952,7 @@ void tst_QQuickListView::cacheBuffer()
     delete testObject;
 }
 
-void tst_QQuickListView::positionViewAtIndex()
+void tst_QQuickListView::positionViewAtBeginningEnd()
 {
     QQuickView *window = createView();
 
@@ -2972,120 +2975,7 @@ void tst_QQuickListView::positionViewAtIndex()
     QTRY_VERIFY(contentItem != 0);
     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
-    // Confirm items positioned correctly
-    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(), i*20.);
-    }
-
-    // Position on a currently visible item
-    listview->positionViewAtIndex(3, QQuickListView::Beginning);
-    QTRY_COMPARE(listview->contentY(), 60.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(), i*20.);
-    }
-
-    // Position on an item beyond the visible items
-    listview->positionViewAtIndex(22, QQuickListView::Beginning);
-    QTRY_COMPARE(listview->contentY(), 440.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(), i*20.);
-    }
-
-    // Position on an item that would leave empty space if positioned at the top
-    listview->positionViewAtIndex(28, QQuickListView::Beginning);
-    QTRY_COMPARE(listview->contentY(), 480.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(), i*20.);
-    }
-
-    // Position at the beginning again
-    listview->positionViewAtIndex(0, QQuickListView::Beginning);
-    QTRY_COMPARE(listview->contentY(), 0.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(), i*20.);
-    }
-
-    // Position at End using last index
-    listview->positionViewAtIndex(model.count()-1, QQuickListView::End);
-    QTRY_COMPARE(listview->contentY(), 480.);
-
-    // Confirm items positioned correctly
-    itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
-    for (int i = 24; i < model.count(); ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(), i*20.);
-    }
-
-    // Position at End
-    listview->positionViewAtIndex(20, QQuickListView::End);
-    QTRY_COMPARE(listview->contentY(), 100.);
-
-    // Position in Center
-    listview->positionViewAtIndex(15, QQuickListView::Center);
-    QTRY_COMPARE(listview->contentY(), 150.);
-
-    // Ensure at least partially visible
-    listview->positionViewAtIndex(15, QQuickListView::Visible);
-    QTRY_COMPARE(listview->contentY(), 150.);
-
-    listview->setContentY(302);
-    listview->positionViewAtIndex(15, QQuickListView::Visible);
-    QTRY_COMPARE(listview->contentY(), 302.);
-
-    listview->setContentY(320);
-    listview->positionViewAtIndex(15, QQuickListView::Visible);
-    QTRY_COMPARE(listview->contentY(), 300.);
-
-    listview->setContentY(85);
-    listview->positionViewAtIndex(20, QQuickListView::Visible);
-    QTRY_COMPARE(listview->contentY(), 85.);
-
-    listview->setContentY(75);
-    listview->positionViewAtIndex(20, QQuickListView::Visible);
-    QTRY_COMPARE(listview->contentY(), 100.);
-
-    // Ensure completely visible
-    listview->setContentY(120);
-    listview->positionViewAtIndex(20, QQuickListView::Contain);
-    QTRY_COMPARE(listview->contentY(), 120.);
-
-    listview->setContentY(302);
-    listview->positionViewAtIndex(15, QQuickListView::Contain);
-    QTRY_COMPARE(listview->contentY(), 300.);
-
-    listview->setContentY(85);
-    listview->positionViewAtIndex(20, QQuickListView::Contain);
-    QTRY_COMPARE(listview->contentY(), 100.);
+    listview->setContentY(100);
 
     // positionAtBeginnging
     listview->positionViewAtBeginning();
@@ -3117,6 +3007,94 @@ void tst_QQuickListView::positionViewAtIndex()
     delete testObject;
 }
 
+void tst_QQuickListView::positionViewAtIndex()
+{
+    QFETCH(bool, enforceRange);
+    QFETCH(qreal, initContentY);
+    QFETCH(int, index);
+    QFETCH(QQuickListView::PositionMode, mode);
+    QFETCH(qreal, contentY);
+
+    QQuickView *window = getView();
+
+    QaimModel model;
+    for (int i = 0; i < 40; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QQmlContext *ctxt = window->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+
+    TestObject *testObject = new TestObject;
+    ctxt->setContextProperty("testObject", testObject);
+    window->show();
+    window->setSource(testFileUrl("listviewtest.qml"));
+    qApp->processEvents();
+
+    QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list");
+    QTRY_VERIFY(listview != 0);
+    QQuickItem *contentItem = listview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+
+    window->rootObject()->setProperty("enforceRange", enforceRange);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+
+    listview->setContentY(initContentY);
+
+    listview->positionViewAtIndex(index, mode);
+    QTRY_COMPARE(listview->contentY(), contentY);
+
+    // Confirm items positioned correctly
+    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+    for (int i = index; i < model.count() && i < itemCount-index-1; ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        if (!item) qWarning() << "Item" << i << "not found";
+        QTRY_VERIFY(item);
+        QTRY_COMPARE(item->y(), i*20.);
+    }
+
+    releaseView(window);
+}
+
+void tst_QQuickListView::positionViewAtIndex_data()
+{
+    QTest::addColumn<bool>("enforceRange");
+    QTest::addColumn<qreal>("initContentY");
+    QTest::addColumn<int>("index");
+    QTest::addColumn<QQuickListView::PositionMode>("mode");
+    QTest::addColumn<qreal>("contentY");
+
+    QTest::newRow("no range, 3 at Beginning") << false << 0. << 3 << QQuickListView::Beginning << 60.;
+    QTest::newRow("no range, 3 at End") << false << 0. << 3 << QQuickListView::End << 0.;
+    QTest::newRow("no range, 22 at Beginning") << false << 0. << 22 << QQuickListView::Beginning << 440.;
+    // Position on an item that would leave empty space if positioned at the top
+    QTest::newRow("no range, 28 at Beginning") << false << 0. << 28 << QQuickListView::Beginning << 480.;
+    // Position at End using last index
+    QTest::newRow("no range, last at End") << false << 0. << 39 << QQuickListView::End << 480.;
+    // Position at End
+    QTest::newRow("no range, 20 at End") << false << 0. << 20 << QQuickListView::End << 100.;
+    // Position in Center
+    QTest::newRow("no range, 15 at Center") << false << 0. << 15 << QQuickListView::Center << 150.;
+    // Ensure at least partially visible
+    QTest::newRow("no range, 15 visible => Visible") << false << 150. << 15 << QQuickListView::Visible << 150.;
+    QTest::newRow("no range, 15 partially visible => Visible") << false << 302. << 15 << QQuickListView::Visible << 302.;
+    QTest::newRow("no range, 15 before visible => Visible") << false << 320. << 15 << QQuickListView::Visible << 300.;
+    QTest::newRow("no range, 20 visible => Visible") << false << 85. << 20 << QQuickListView::Visible << 85.;
+    QTest::newRow("no range, 20 before visible => Visible") << false << 75. << 20 << QQuickListView::Visible << 100.;
+    QTest::newRow("no range, 20 after visible => Visible") << false << 480. << 20 << QQuickListView::Visible << 400.;
+    // Ensure completely visible
+    QTest::newRow("no range, 20 visible => Contain") << false << 120. << 20 << QQuickListView::Contain << 120.;
+    QTest::newRow("no range, 15 partially visible => Contain") << false << 302. << 15 << QQuickListView::Contain << 300.;
+    QTest::newRow("no range, 20 partially visible => Contain") << false << 85. << 20 << QQuickListView::Contain << 100.;
+
+    QTest::newRow("strict range, 3 at End") << true << 0. << 3 << QQuickListView::End << -120.;
+    QTest::newRow("strict range, 38 at Beginning") << true << 0. << 38 << QQuickListView::Beginning << 660.;
+    QTest::newRow("strict range, 15 at Center") << true << 0. << 15 << QQuickListView::Center << 140.;
+    QTest::newRow("strict range, 3 at SnapPosition") << true << 0. << 3 << QQuickListView::SnapPosition << -60.;
+    QTest::newRow("strict range, 10 at SnapPosition") << true << 0. << 10 << QQuickListView::SnapPosition << 80.;
+    QTest::newRow("strict range, 38 at SnapPosition") << true << 0. << 38 << QQuickListView::SnapPosition << 640.;
+}
+
 void tst_QQuickListView::resetModel()
 {
     QQuickView *window = createView();