Fix content pos adjustment when resizing first item
authorBea Lam <bea.lam@nokia.com>
Mon, 30 Apr 2012 02:55:45 +0000 (12:55 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 1 May 2012 05:34:12 +0000 (07:34 +0200)
If visibleItems.first() changes size in a ListView, the size change
should be made in the direction of the content flow. This was not
working correctly for layouts other than the default vertical+LTR
layout. This patch fixes the issue for other layouts and also fixes
resizing of implicitly sized delegates (e.g. positioners) within the
default layout.

Change-Id: Ib1d84ce000a3a341fe617cf644420dc5d589d577
Reviewed-by: Andrew den Exter <andrew.den-exter@nokia.com>
src/quick/items/qquicklistview.cpp
tests/auto/quick/qquicklistview/data/repositionResizedDelegate.qml [new file with mode: 0644]
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp

index df50d3e..bbfaeb0 100644 (file)
@@ -1336,9 +1336,29 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &
     QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
     if (!q->isComponentComplete())
         return;
+
     if (item != contentItem && (!highlight || item != highlight->item)) {
         if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
             || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
+
+            // if visibleItems.first() has resized, adjust its pos since it is used to
+            // position all subsequent items
+            if (visibleItems.count() && item == visibleItems.first()->item) {
+                FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
+                if (orient == Qt::Vertical) {
+                    qreal diff = newGeometry.height() - oldGeometry.height();
+                    if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
+                        listItem->setPosition(listItem->position() - diff, true);
+                    else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
+                        listItem->setPosition(listItem->position() + diff, true);
+                } else {
+                    qreal diff = newGeometry.width() - oldGeometry.width();
+                    if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
+                        listItem->setPosition(listItem->position() - diff, true);
+                    else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
+                        listItem->setPosition(listItem->position() + diff, true);
+                }
+            }
             forceLayout = true;
             q->polish();
         }
diff --git a/tests/auto/quick/qquicklistview/data/repositionResizedDelegate.qml b/tests/auto/quick/qquicklistview/data/repositionResizedDelegate.qml
new file mode 100644 (file)
index 0000000..d79ca10
--- /dev/null
@@ -0,0 +1,53 @@
+import QtQuick 2.0
+
+ListView {
+    id: root
+
+    width: 200; height: 200
+
+    orientation: (testHorizontal == true) ? Qt.Horizontal : Qt.Vertical
+    layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
+    verticalLayoutDirection: (testBottomToTop == true) ? ListView.BottomToTop : ListView.TopToBottom
+
+    model: VisualItemModel {
+        Rectangle {
+            objectName: "red"
+            width: 200; height: 200; color: "red"
+            Text { text: parent.x + ", " + parent.y }
+        }
+        Grid {
+            id: grid
+            objectName: "positioner"
+            columns: root.orientation == Qt.Vertical ? 1 : 2
+            Repeater {
+                id: rpt
+                model: 1
+                Rectangle {
+                    width: 120; height: 120; color: "orange"; border.width: 1
+                    Column {
+                        Text { text: grid.x + ", " + grid.y }
+                        Text { text: index }
+                    }
+                }
+            }
+        }
+        Rectangle {
+            objectName: "yellow"
+            width: 200; height: 200; color: "yellow"
+            Text { text: parent.x + ", " + parent.y }
+        }
+    }
+
+    focus: true
+
+    function incrementRepeater() {
+        rpt.model += 1
+    }
+
+    function decrementRepeater() {
+        rpt.model -= 1
+    }
+
+    Text { anchors.right: parent.right; anchors.bottom: parent.bottom; text: root.contentX + ", " + root.contentY }
+}
+
index e4d81d9..b38bb0d 100644 (file)
@@ -163,6 +163,8 @@ private slots:
     void QTBUG_14821();
     void resizeDelegate();
     void resizeFirstDelegate();
+    void repositionResizedDelegate();
+    void repositionResizedDelegate_data();
     void QTBUG_16037();
     void indexAt_itemAt_data();
     void indexAt_itemAt();
@@ -4056,6 +4058,111 @@ void tst_QQuickListView::resizeFirstDelegate()
     delete canvas;
 }
 
+void tst_QQuickListView::repositionResizedDelegate()
+{
+    QFETCH(QQuickListView::Orientation, orientation);
+    QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
+    QFETCH(QPointF, contentPos_itemFirstHalfVisible);
+    QFETCH(QPointF, contentPos_itemStart);
+    QFETCH(QPointF, contentPos_itemSecondHalfVisible);
+    QFETCH(QRectF, origPositionerRect);
+    QFETCH(QRectF, resizedPositionerRect);
+
+    QQuickView *canvas = getView();
+    QQmlContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testHorizontal", orientation == QQuickListView::Horizontal);
+    ctxt->setContextProperty("testRightToLeft", layoutDirection == Qt::RightToLeft);
+    ctxt->setContextProperty("testBottomToTop", verticalLayoutDirection == QQuickListView::BottomToTop);
+    canvas->setSource(testFileUrl("repositionResizedDelegate.qml"));
+    canvas->show();
+    qApp->processEvents();
+
+    QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
+    QTRY_VERIFY(listview != 0);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+
+    QQuickItem *positioner = findItem<QQuickItem>(canvas->rootObject(), "positioner");
+    QVERIFY(positioner);
+    QTRY_COMPARE(positioner->boundingRect().size(), origPositionerRect.size());
+    QTRY_COMPARE(positioner->pos(), origPositionerRect.topLeft());
+    QSignalSpy spy(listview, orientation == QQuickListView::Vertical ? SIGNAL(contentYChanged()) : SIGNAL(contentXChanged()));
+    int prevSpyCount = 0;
+
+    // When an item is resized while it is partially visible, it should resize in the
+    // direction of the content flow. If a RightToLeft or BottomToTop layout is used,
+    // the item should also be re-positioned so its end position stays the same.
+
+    listview->setContentX(contentPos_itemFirstHalfVisible.x());
+    listview->setContentY(contentPos_itemFirstHalfVisible.y());
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+    prevSpyCount = spy.count();
+    QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "incrementRepeater"));
+    QTRY_COMPARE(positioner->boundingRect().size(), resizedPositionerRect.size());
+    QTRY_COMPARE(positioner->pos(), resizedPositionerRect.topLeft());
+    QCOMPARE(listview->contentX(), contentPos_itemFirstHalfVisible.x());
+    QCOMPARE(listview->contentY(), contentPos_itemFirstHalfVisible.y());
+    QCOMPARE(spy.count(), prevSpyCount);
+
+    QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "decrementRepeater"));
+    QTRY_COMPARE(positioner->boundingRect().size(), origPositionerRect.size());
+    QTRY_COMPARE(positioner->pos(), origPositionerRect.topLeft());
+    QCOMPARE(listview->contentX(), contentPos_itemFirstHalfVisible.x());
+    QCOMPARE(listview->contentY(), contentPos_itemFirstHalfVisible.y());
+
+    listview->setContentX(contentPos_itemSecondHalfVisible.x());
+    listview->setContentY(contentPos_itemSecondHalfVisible.y());
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+    prevSpyCount = spy.count();
+
+    QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "incrementRepeater"));
+    positioner = findItem<QQuickItem>(canvas->rootObject(), "positioner");
+    QTRY_COMPARE(positioner->boundingRect().size(), resizedPositionerRect.size());
+    QTRY_COMPARE(positioner->pos(), resizedPositionerRect.topLeft());
+    QCOMPARE(listview->contentX(), contentPos_itemSecondHalfVisible.x());
+    QCOMPARE(listview->contentY(), contentPos_itemSecondHalfVisible.y());
+    qApp->processEvents();
+    QCOMPARE(spy.count(), prevSpyCount);
+
+    releaseView(canvas);
+}
+
+void tst_QQuickListView::repositionResizedDelegate_data()
+{
+    QTest::addColumn<QQuickListView::Orientation>("orientation");
+    QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<QQuickListView::VerticalLayoutDirection>("verticalLayoutDirection");
+    QTest::addColumn<QPointF>("contentPos_itemFirstHalfVisible");
+    QTest::addColumn<QPointF>("contentPos_itemStart");
+    QTest::addColumn<QPointF>("contentPos_itemSecondHalfVisible");
+    QTest::addColumn<QRectF>("origPositionerRect");
+    QTest::addColumn<QRectF>("resizedPositionerRect");
+
+    QTest::newRow("vertical")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(0, 60) << QPointF(0, 200) << QPointF(0, 200 + 60)
+            << QRectF(0, 200, 120, 120)
+            << QRectF(0, 200, 120, 120 * 2);
+
+    QTest::newRow("vertical, BottomToTop")
+            << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
+            << QPointF(0, -200 - 60) << QPointF(0, -200 - 200) << QPointF(0, -200 - 260)
+            << QRectF(0, -200 - 120, 120, 120)
+            << QRectF(0, -200 - 120*2, 120, 120 * 2);
+
+    QTest::newRow("horizontal")
+            << QQuickListView::Horizontal<< Qt::LeftToRight << QQuickItemView::TopToBottom
+            << QPointF(60, 0) << QPointF(200, 0) << QPointF(260, 0)
+            << QRectF(200, 0, 120, 120)
+            << QRectF(200, 0, 120 * 2, 120);
+
+    QTest::newRow("horizontal, rtl")
+            << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
+            << QPointF(-200 - 60, 0) << QPointF(-200 - 200, 0) << QPointF(-200 - 260, 0)
+            << QRectF(-200 - 120, 0, 120, 120)
+            << QRectF(-200 - 120 * 2, 0, 120 * 2, 120);
+}
+
 void tst_QQuickListView::QTBUG_16037()
 {
     QQuickView *canvas = createView();