commit e16ffd2ac3f842ec4e2e4ba8f925b03cdcdbee04 Author: Albert Astals Cid Date: Wed Jul 10 12:17:16 2013 +0200 Add the possibility of specifying the delegate creation range For itemviews that are inside itemviews using the contentY/height of the itemview is not a good idea since it means you create all the items at once. This adds two new properties that let you specify the range where delegate creation should happen Task-number: QTBUG-32338 Change-Id: Idc6c257ddbeac81412444deaa164ea847ffbc373 diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 37276d5..3dc1c0e 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -640,8 +640,8 @@ void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex) if (colPos != col * colSize()) { colPos = col * colSize(); firstItem->setPosition(colPos, rowPos); - firstItem->setVisible(firstItem->rowPos() + rowSize() >= from && firstItem->rowPos() <= to); } + firstItem->setVisible(firstItem->rowPos() + rowSize() >= from && firstItem->rowPos() <= to); for (int i = 1; i < visibleItems.count(); ++i) { FxGridItemSG *item = static_cast(visibleItems.at(i)); if (++col >= columns) { diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index f8f622a..eecbb01 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -717,6 +717,70 @@ void QQuickItemView::setHighlightMoveDuration(int duration) } } +qreal QQuickItemView::delegateCreationBegin() const +{ + Q_D(const QQuickItemView); + return d->delegateCreationBegin; +} + +void QQuickItemView::setDelegateCreationBegin(qreal begin) +{ + Q_D(QQuickItemView); + d->delegateCreationBeginValid = true; + if (d->delegateCreationBegin == begin) + return; + d->delegateCreationBegin = begin; + if (isComponentComplete()) { + d->forceLayoutPolish(); + } + emit delegateCreationBeginChanged(); +} + +void QQuickItemView::resetDelegateCreationBegin() +{ + Q_D(QQuickItemView); + d->delegateCreationBeginValid = false; + if (d->delegateCreationBegin == 0) + return; + d->delegateCreationBegin = 0; + if (isComponentComplete()) { + d->forceLayoutPolish(); + } + emit delegateCreationBeginChanged(); +} + +qreal QQuickItemView::delegateCreationEnd() const +{ + Q_D(const QQuickItemView); + return d->delegateCreationEnd; +} + +void QQuickItemView::setDelegateCreationEnd(qreal end) +{ + Q_D(QQuickItemView); + d->delegateCreationEndValid = true; + if (d->delegateCreationEnd == end) + return; + d->delegateCreationEnd = end; + if (isComponentComplete()) { + d->forceLayoutPolish(); + } + emit delegateCreationEndChanged(); +} + +void QQuickItemView::resetDelegateCreationEnd() +{ + Q_D(QQuickItemView); + d->delegateCreationEndValid = false; + if (d->delegateCreationEnd == 0) + return; + d->delegateCreationEnd = 0; + if (isComponentComplete()) { + d->forceLayoutPolish(); + } + emit delegateCreationEndChanged(); +} + QQuickTransition *QQuickItemView::populateTransition() const { Q_D(const QQuickItemView); @@ -1450,6 +1514,7 @@ QQuickItemViewPrivate::QQuickItemViewPrivate() , highlightRange(QQuickItemView::NoHighlightRange) , highlightRangeStart(0), highlightRangeEnd(0) , highlightMoveDuration(150) + , delegateCreationBegin(0), delegateCreationEnd(0) , headerComponent(0), header(0), footerComponent(0), footer(0) , transitioner(0) , minExtent(0), maxExtent(0) @@ -1458,6 +1523,7 @@ QQuickItemViewPrivate::QQuickItemViewPrivate() , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) , fillCacheBuffer(false), inRequest(false) , runDelayedRemoveTransition(false), delegateValidated(false) + , delegateCreationBeginValid(false), delegateCreationEndValid(false) { bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion); bufferPause.setLoopCount(1); @@ -1678,11 +1744,15 @@ void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *) void QQuickItemViewPrivate::refill() { - qreal s = qMax(size(), qreal(0.)); - if (isContentFlowReversed()) - refill(-position()-s, -position()); - else - refill(position(), position()+s); + if (delegateCreationBeginValid && delegateCreationEndValid) { + refill(delegateCreationBegin, delegateCreationEnd); + } else { + qreal s = qMax(size(), qreal(0.)); + if (isContentFlowReversed()) + refill(-position()-s, -position()); + else + refill(position(), position()+s); + } } void QQuickItemViewPrivate::refill(qreal from, qreal to) diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h index d7812bc..fd17ea2 100644 --- a/src/quick/items/qquickitemview_p.h +++ b/src/quick/items/qquickitemview_p.h @@ -89,6 +89,8 @@ class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin WRITE setDelegateCreationBegin NOTIFY delegateCreationBeginChanged RESET resetDelegateCreationBegin) + Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd WRITE setDelegateCreationEnd NOTIFY delegateCreationEndChanged RESET resetDelegateCreationEnd) Q_ENUMS(HighlightRangeMode) Q_ENUMS(PositionMode) @@ -195,6 +197,14 @@ public: int highlightMoveDuration() const; virtual void setHighlightMoveDuration(int); + qreal delegateCreationBegin() const; + void setDelegateCreationBegin(qreal); + Q_INVOKABLE void resetDelegateCreationBegin(); + + qreal delegateCreationEnd() const; + void setDelegateCreationEnd(qreal); + Q_INVOKABLE void resetDelegateCreationEnd(); + enum PositionMode { Beginning, Center, End, Visible, Contain, SnapPosition }; Q_INVOKABLE void positionViewAtIndex(int index, int mode); @@ -244,6 +254,8 @@ signals: void preferredHighlightBeginChanged(); void preferredHighlightEndChanged(); void highlightMoveDurationChanged(); + void delegateCreationBeginChanged(); + void delegateCreationEndChanged(); protected: virtual void updatePolish(); diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 32e94e0..9baaa3f 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -118,7 +118,7 @@ public: }; -class QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener +class Q_AUTOTEST_EXPORT QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener { Q_DECLARE_PUBLIC(QQuickItemView) public: @@ -278,6 +278,8 @@ public: qreal highlightRangeStart; qreal highlightRangeEnd; int highlightMoveDuration; + qreal delegateCreationBegin; + qreal delegateCreationEnd; QQmlComponent *headerComponent; FxViewItem *header; @@ -310,6 +312,8 @@ public: bool inRequest : 1; bool runDelayedRemoveTransition : 1; bool delegateValidated : 1; + bool delegateCreationBeginValid : 1; + bool delegateCreationEndValid : 1; protected: virtual Qt::Orientation layoutOrientation() const = 0; diff --git a/tests/auto/quick/qquickgridview/data/delegateCreationRange.qml b/tests/auto/quick/qquickgridview/data/delegateCreationRange.qml new file mode 100644 index 0000000..389b81c --- /dev/null +++ b/tests/auto/quick/qquickgridview/data/delegateCreationRange.qml @@ -0,0 +1,89 @@ + +import QtQuick 2.0 + +ListView { + id: list + width: 400 + height: 600 + model: ListModel { + ListElement { kind: "Bought" } + ListElement { kind: "Available To Buy" } + } + + delegate: GridView { + id: grid + objectName: "grid" + index + height: Math.ceil(count / (parent.width / cellWidth)) * cellHeight / 1 + width: parent.width + interactive: false + property int count: { + if (grid.foo == 0) return 50; + else return 100; + } + cellHeight: { + if (grid.foo == 0) return 200; + else return 100; + } + cellWidth: cellHeight + model: count + property int foo: index + property bool enableRange: true + onEnableRangeChanged: updatedDelegateCreationRange(); + + delegate: Item { + width: cellWidth + height: cellHeight + Rectangle { + width: parent.width - 20 + height: parent.height - 20 + anchors.centerIn: parent + color: { + if (grid.foo == 0) return Math.random() * 2 > 1 ? "green" : "yellow"; + else return Math.random() * 2 > 1 ? "red" : "blue"; + } + Text { + text: index + } + } + } + + delegateCreationBegin: enableRange ? 0 : undefined + delegateCreationEnd: enableRange ? 0 : undefined + + function updatedDelegateCreationRange() { + if (!enableRange) { + delegateCreationBegin = undefined + delegateCreationEnd = undefined + return; + } + + if (list.contentY + list.height <= y) { + // Not visible + delegateCreationBegin = 0 + delegateCreationEnd = 0 + } else if (y + height <= list.contentY) { + // Not visible + delegateCreationBegin = height + delegateCreationEnd = height + } else { + delegateCreationBegin = Math.max(list.contentY - y, 0) + delegateCreationEnd = Math.min(list.contentY + list.height - y, height) + } + } + + Component.onCompleted: updatedDelegateCreationRange(); + Connections { + target: list + onContentYChanged: updatedDelegateCreationRange(); + onHeightChanged: updatedDelegateCreationRange(); + } + } + + section.property: "kind" + section.delegate: Text { + height: 40 + font.pixelSize: 30 + text: section + } + +} \ No newline at end of file diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index d0ffba9..ab1dce1 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include #include #include "../../shared/util.h" @@ -207,6 +209,8 @@ private slots: void moved_topToBottom_RtL_BtT(); void moved_topToBottom_RtL_BtT_data(); + void testDelegateCreationRange(); + private: QList toIntList(const QVariantList &list); void matchIndexLists(const QVariantList &indexLists, const QList &expectedIndexes); @@ -6254,6 +6258,69 @@ void tst_QQuickGridView::moved_topToBottom_RtL_BtT_data() moved_defaultLayout_data(); } +void tst_QQuickGridView::testDelegateCreationRange() +{ + QQuickView *window = getView(); + window->setSource(testFileUrl("delegateCreationRange.qml")); + window->show(); + QTest::qWaitForWindowActive(window); + + QQuickListView *listview = dynamic_cast(window->rootObject()); + QVERIFY(listview != 0); + QQuickGridView *gridview0 = findItem(window->rootObject(), "grid0"); + QVERIFY(gridview0 != 0); + QQuickItemViewPrivate *gridview0Priv = dynamic_cast(QQuickItemPrivate::get(gridview0)); + QVERIFY(gridview0Priv != 0); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 11); + gridview0->setProperty("enableRange", false); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 50); + gridview0->setProperty("enableRange", true); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 11); + + listview->setContentY(1200); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 17); + gridview0->setProperty("enableRange", false); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 50); + gridview0->setProperty("enableRange", true); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 17); + + listview->setContentY(6000); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 5); + gridview0->setProperty("enableRange", false); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 50); + gridview0->setProperty("enableRange", true); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 5); + + QQuickGridView *gridview1 = findItem(window->rootObject(), "grid1"); + QVERIFY(gridview1 != 0); + QQuickItemViewPrivate *gridview1Priv = dynamic_cast(QQuickItemPrivate::get(gridview1)); + + QTRY_COMPARE(gridview1Priv->visibleItems.count(), 58); + gridview1->setProperty("enableRange", false); + QTRY_COMPARE(gridview1Priv->visibleItems.count(), 100); + gridview1->setProperty("enableRange", true); + QTRY_COMPARE(gridview1Priv->visibleItems.count(), 58); + + listview->setContentY(0); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 11); + gridview0->setProperty("enableRange", false); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 50); + gridview0->setProperty("enableRange", true); + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 11); + + listview->setContentY(800); + QTest::qWait(100); + listview->setContentY(600); + QTest::qWait(100); + listview->setContentY(400); + QTest::qWait(100); + listview->setContentY(200); + QTest::qWait(100); + listview->setContentY(0); + + QTRY_COMPARE(gridview0Priv->visibleItems.count(), 11); + QVERIFY(!QQuickItemPrivate::get(gridview0Priv->visibleItems[0]->item)->culled); +} QList tst_QQuickGridView::toIntList(const QVariantList &list) { diff --git a/tests/auto/quick/qquicklistview/data/delegateCreationRange.qml b/tests/auto/quick/qquicklistview/data/delegateCreationRange.qml new file mode 100644 index 0000000..c234339 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/delegateCreationRange.qml @@ -0,0 +1,84 @@ + +import QtQuick 2.0 + +ListView { + id: list + width: 400 + height: 600 + model: ListModel { + ListElement { kind: "Bought" } + ListElement { kind: "Available To Buy" } + } + + delegate: ListView { + id: innerList + objectName: "list" + index + height: count * itemHeight + width: parent.width + interactive: false + property int count: { + if (innerList.foo == 0) return 50; + else return 100; + } + property int itemHeight: innerList.foo == 0 ? 200 : 50; + model: count + property int foo: index + property bool enableRange: true + onEnableRangeChanged: updatedDelegateCreationRange(); + + delegate: Item { + width: parent.width + height: innerList.itemHeight + Rectangle { + width: parent.width - 20 + height: parent.height - 20 + anchors.centerIn: parent + color: { + if (innerList.foo == 0) return Math.random() * 2 > 1 ? "green" : "yellow"; + else return Math.random() * 2 > 1 ? "red" : "blue"; + } + Text { + text: index + } + } + } + + delegateCreationBegin: enableRange ? 0 : undefined + delegateCreationEnd: enableRange ? 0 : undefined + + function updatedDelegateCreationRange() { + if (!enableRange) { + delegateCreationBegin = undefined + delegateCreationEnd = undefined + return; + } + + if (list.contentY + list.height <= y) { + // Not visible + delegateCreationBegin = 0 + delegateCreationEnd = 0 + } else if (y + height <= list.contentY) { + // Not visible + delegateCreationBegin = height + delegateCreationEnd = height + } else { + delegateCreationBegin = Math.max(list.contentY - y, 0) + delegateCreationEnd = Math.min(list.contentY + list.height - y, height) + } + } + + Component.onCompleted: updatedDelegateCreationRange(); + Connections { + target: list + onContentYChanged: updatedDelegateCreationRange(); + onHeightChanged: updatedDelegateCreationRange(); + } + } + + section.property: "kind" + section.delegate: Text { + height: 40 + font.pixelSize: 30 + text: section + } +} \ No newline at end of file diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 2268f07..021fa4e 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -210,6 +211,8 @@ private slots: void accessEmptyCurrentItem_QTBUG_30227(); void delayedChanges_QTBUG_30555(); void outsideViewportChangeNotAffectingView(); + + void testDelegateCreationRange(); private: template void items(const QUrl &source); @@ -6918,6 +6921,73 @@ void tst_QQuickListView::outsideViewportChangeNotAffectingView() delete window; } +void tst_QQuickListView::testDelegateCreationRange() +{ + QQuickView *window = getView(); + window->setSource(testFileUrl("delegateCreationRange.qml")); + window->show(); + QTest::qWaitForWindowActive(window); + + QQuickListView *listview = dynamic_cast(window->rootObject()); + QVERIFY(listview != 0); + QQuickListView *listview0 = findItem(window->rootObject(), "list0"); + QVERIFY(listview0 != 0); + QQuickItemViewPrivate *listview0Priv = dynamic_cast(QQuickItemPrivate::get(listview0)); + QVERIFY(listview0Priv != 0); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 5); + listview0->setProperty("enableRange", false); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 50); + listview0->setProperty("enableRange", true); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 5); + + listview->setContentY(6000); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 7); + listview0->setProperty("enableRange", false); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 50); + listview0->setProperty("enableRange", true); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 7); + + listview->setContentY(11000); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 2); + listview0->setProperty("enableRange", false); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 50); + listview0->setProperty("enableRange", true); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 2); + + QQuickListView *listview1 = findItem(window->rootObject(), "list1"); + QVERIFY(listview1 != 0); + QQuickItemViewPrivate *listview1Priv = dynamic_cast(QQuickItemPrivate::get(listview1)); + + QTRY_COMPARE(listview1Priv->visibleItems.count(), 25); + listview1->setProperty("enableRange", false); + QTRY_COMPARE(listview1Priv->visibleItems.count(), 100); + listview1->setProperty("enableRange", true); + // For some reason in this case it goes back to 26 instead of 25 + // did not want to investigate it further since it would mean + // changing more things in ListView inner code + QTRY_COMPARE(listview1Priv->visibleItems.count(), 26); + + listview->setContentY(0); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 5); + listview0->setProperty("enableRange", false); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 50); + listview0->setProperty("enableRange", true); + QTRY_COMPARE(listview0Priv->visibleItems.count(), 5); + + listview->setContentY(800); + QTest::qWait(100); + listview->setContentY(600); + QTest::qWait(100); + listview->setContentY(400); + QTest::qWait(100); + listview->setContentY(200); + QTest::qWait(100); + listview->setContentY(0); + + QTRY_COMPARE(listview0Priv->visibleItems.count(), 5); + QVERIFY(!QQuickItemPrivate::get(listview0Priv->visibleItems[0]->item)->culled); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc"