From 53e317468626284fe26877659dff551ce3bc0e55 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 11 Apr 2013 16:57:19 -0700 Subject: [PATCH] No longer apply pending changes when accessing ItemView properties Applying changes in the getters can lead to binding loops, and is currently inconsistently applied. Removing the applyPendingChanges calls from remaining getters, and adding a forceLayout() function for cases where the immediate-apply behavior is needed. Task-number: QTBUG-30555 Parts-of-patch-by: Albert Astals Cid Change-Id: I64632601e02f2a53060296ab7739577a749d916f Reviewed-by: Andrew den Exter Reviewed-by: Albert Astals Cid --- src/quick/items/qquickgridview.cpp | 16 ++++ src/quick/items/qquickitemsmodule.cpp | 3 + src/quick/items/qquickitemview.cpp | 13 ++- src/quick/items/qquickitemview_p.h | 1 + src/quick/items/qquicklistview.cpp | 15 +++ tests/auto/qmltest/listview/tst_listview.qml | 3 +- .../quick/qquickgridview/tst_qquickgridview.cpp | 37 +++++-- .../quick/qquicklistview/data/delayedChanges.qml | 39 ++++++++ .../auto/quick/qquicklistview/data/emptymodel.qml | 4 +- .../quick/qquicklistview/tst_qquicklistview.cpp | 106 +++++++++++++++------ .../tst_qquickvisualdatamodel.cpp | 7 ++ 11 files changed, 198 insertions(+), 46 deletions(-) create mode 100644 tests/auto/quick/qquicklistview/data/delayedChanges.qml diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index e40d21b..f4e34da 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -2564,6 +2564,22 @@ bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) co \b Note: methods should only be called after the Component has completed. */ + +/*! + \qmlmethod QtQuick2::GridView::forceLayout() + + Responding to changes in the model is usually batched to happen only once + per frame. This means that inside script blocks it is possible for the + underlying model to have changed, but the GridView has not caught up yet. + + This method forces the GridView to immediately respond to any outstanding + changes in the model. + + \since 5.1 + + \b Note: methods should only be called after the Component has completed. +*/ + QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj) { return new QQuickGridViewAttached(obj); diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 741583a..f5bcf35 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -230,6 +230,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri, 2, 1,"Item"); qmlRegisterType(uri, 2, 1, "Grid"); + qmlRegisterUncreatableType(uri, 2, 1, "ItemView", QQuickItemView::tr("ItemView is an abstract base class")); + qmlRegisterType(uri, 2, 1, "ListView"); + qmlRegisterType(uri, 2, 1, "GridView"); qmlRegisterType(uri, 2, 1, "TextEdit"); } diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 03e16f2..d774091 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -249,7 +249,6 @@ QQuickItemView::~QQuickItemView() QQuickItem *QQuickItemView::currentItem() const { Q_D(const QQuickItemView); - const_cast(d)->applyPendingChanges(); return d->currentItem ? d->currentItem->item : 0; } @@ -379,14 +378,12 @@ int QQuickItemView::count() const Q_D(const QQuickItemView); if (!d->model) return 0; - const_cast(d)->applyPendingChanges(); return d->model->count(); } int QQuickItemView::currentIndex() const { Q_D(const QQuickItemView); - const_cast(d)->applyPendingChanges(); return d->currentIndex; } @@ -496,7 +493,6 @@ QQmlComponent *QQuickItemView::header() const QQuickItem *QQuickItemView::headerItem() const { Q_D(const QQuickItemView); - const_cast(d)->applyPendingChanges(); return d->header ? d->header->item : 0; } @@ -532,7 +528,6 @@ QQmlComponent *QQuickItemView::footer() const QQuickItem *QQuickItemView::footerItem() const { Q_D(const QQuickItemView); - const_cast(d)->applyPendingChanges(); return d->footer ? d->footer->item : 0; } @@ -559,7 +554,6 @@ void QQuickItemView::setFooter(QQmlComponent *footerComponent) QQmlComponent *QQuickItemView::highlight() const { Q_D(const QQuickItemView); - const_cast(d)->applyPendingChanges(); return d->highlightComponent; } @@ -579,7 +573,6 @@ void QQuickItemView::setHighlight(QQmlComponent *highlightComponent) QQuickItem *QQuickItemView::highlightItem() const { Q_D(const QQuickItemView); - const_cast(d)->applyPendingChanges(); return d->highlight ? d->highlight->item : 0; } @@ -965,6 +958,12 @@ QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const return 0; } +void QQuickItemView::forceLayout() +{ + Q_D(QQuickItemView); + d->applyPendingChanges(); +} + void QQuickItemViewPrivate::applyPendingChanges() { Q_Q(QQuickItemView); diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h index b0f9106..d7812bc 100644 --- a/src/quick/items/qquickitemview_p.h +++ b/src/quick/items/qquickitemview_p.h @@ -202,6 +202,7 @@ public: Q_INVOKABLE QQuickItem *itemAt(qreal x, qreal y) const; Q_INVOKABLE void positionViewAtBeginning(); Q_INVOKABLE void positionViewAtEnd(); + Q_REVISION(1) Q_INVOKABLE void forceLayout(); virtual void setContentX(qreal pos); virtual void setContentY(qreal pos); diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 53dc715..b99fba4 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -3108,6 +3108,21 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex \b Note: methods should only be called after the Component has completed. */ +/*! + \qmlmethod QtQuick2::ListView::forceLayout() + + Responding to changes in the model is usually batched to happen only once + per frame. This means that inside script blocks it is possible for the + underlying model to have changed, but the ListView has not caught up yet. + + This method forces the ListView to immediately respond to any outstanding + changes in the model. + + \since 5.1 + + \b Note: methods should only be called after the Component has completed. +*/ + QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj) { return new QQuickListViewAttached(obj); diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml index cbace62..52384fe 100644 --- a/tests/auto/qmltest/listview/tst_listview.qml +++ b/tests/auto/qmltest/listview/tst_listview.qml @@ -38,7 +38,7 @@ ** ****************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.1 import QtTest 1.0 Item { @@ -168,6 +168,7 @@ Item { modelalter.currentIndex = 1; compare(modelalter.currentItem.text, "AlterModelElement1") altermodel.clear() + modelalter.forceLayout() tryCompare(modelalter.count, 0) compare(modelalter.currentItem, null) } diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 0c9788a..d0ffba9 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -532,6 +532,7 @@ void tst_QQuickGridView::inserted_defaultLayout(QQuickGridView::Flow flow, for (int i=0; iforceLayout(); QTRY_COMPARE(gridview->property("count").toInt(), model.count()); // check visibleItems.first() is in correct position @@ -728,6 +729,7 @@ void tst_QQuickGridView::insertBeforeVisible() for (int i=0; iforceLayout(); QTRY_COMPARE(gridview->property("count").toInt(), model.count()); // now, moving to the top of the view should position the inserted items correctly @@ -958,6 +960,7 @@ void tst_QQuickGridView::removed_defaultLayout(QQuickGridView::Flow flow, QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); model.removeItems(removeIndex, removeCount); + gridview->forceLayout(); QTRY_COMPARE(gridview->property("count").toInt(), model.count()); QString firstName; @@ -1245,6 +1248,7 @@ void tst_QQuickGridView::clear() QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); model.clear(); + gridview->forceLayout(); QVERIFY(gridview->count() == 0); QVERIFY(gridview->currentItem() == 0); @@ -1254,6 +1258,7 @@ void tst_QQuickGridView::clear() // confirm sanity when adding an item to cleared list model.addItem("New", "1"); + gridview->forceLayout(); QTRY_COMPARE(gridview->count(), 1); QVERIFY(gridview->currentItem() != 0); QVERIFY(gridview->currentIndex() == 0); @@ -3483,6 +3488,7 @@ void tst_QQuickGridView::extents() QCOMPARE(gridview->originY(), origin_empty.y()); for (int i=0; i<30; i++) model.addItem("Item" + QString::number(i), ""); + gridview->forceLayout(); QTRY_COMPARE(gridview->count(), model.count()); QCOMPARE(gridview->originX(), origin_nonEmpty.x()); QCOMPARE(gridview->originY(), origin_nonEmpty.y()); @@ -3942,9 +3948,9 @@ void tst_QQuickGridView::onAdd() ctxt->setContextProperty("delegateHeight", delegateHeight); window->setSource(testFileUrl("attachedSignals.qml")); - QObject *object = window->rootObject(); - object->setProperty("width", window->width()); - object->setProperty("height", window->height()); + QQuickGridView *gridview = qobject_cast(window->rootObject()); + gridview->setProperty("width", window->width()); + gridview->setProperty("height", window->height()); qApp->processEvents(); QList > items; @@ -3952,10 +3958,11 @@ void tst_QQuickGridView::onAdd() items << qMakePair(QString("value %1").arg(i), QString::number(i)); model.addItems(items); - QTRY_COMPARE(model.count(), qobject_cast(window->rootObject())->count()); + gridview->forceLayout(); + QTRY_COMPARE(model.count(), gridview->count()); qApp->processEvents(); - QVariantList result = object->property("addedDelegates").toList(); + QVariantList result = gridview->property("addedDelegates").toList(); QTRY_COMPARE(result.count(), items.count()); for (int i=0; isetContextProperty("delegateWidth", delegateWidth); ctxt->setContextProperty("delegateHeight", delegateHeight); window->setSource(testFileUrl("attachedSignals.qml")); - QObject *object = window->rootObject(); + QQuickGridView *gridview = qobject_cast(window->rootObject()); model.removeItems(indexToRemove, removeCount); - QTRY_COMPARE(model.count(), qobject_cast(window->rootObject())->count()); - QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount)); + gridview->forceLayout(); + QTRY_COMPARE(model.count(), gridview->count()); + QCOMPARE(gridview->property("removedDelegateCount"), QVariant(removeCount)); releaseView(window); } @@ -4151,6 +4159,7 @@ void tst_QQuickGridView::margins() gridview->setContentX(-400); QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); model.removeItems(0, 4); + gridview->forceLayout(); QTRY_COMPARE(model.count(), gridview->count()); gridview->setContentX(-240+50); gridview->returnToBounds(); @@ -4458,6 +4467,7 @@ void tst_QQuickGridView::unaligned() // removing model.removeItems(7, 10); + gridview->forceLayout(); QTRY_COMPARE(model.count(), gridview->count()); for (int i = 0; i < 18; ++i) { QQuickItem *item = 0; @@ -4663,6 +4673,7 @@ void tst_QQuickGridView::addTransitions() // start animation if (!newData.isEmpty()) { model.insertItems(insertionIndex, newData); + gridview->forceLayout(); QTRY_COMPARE(model.count(), gridview->count()); } @@ -4866,6 +4877,7 @@ void tst_QQuickGridView::moveTransitions() // start animation model.moveItems(moveFrom, moveTo, moveCount); + gridview->forceLayout(); QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count()); QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(), @@ -5114,6 +5126,7 @@ void tst_QQuickGridView::removeTransitions() // start animation model.removeItems(removalIndex, removalCount); + gridview->forceLayout(); QTRY_COMPARE(model.count(), gridview->count()); if (shouldAnimateTargets || expectedDisplacedIndexes.isValid()) { @@ -5327,6 +5340,7 @@ void tst_QQuickGridView::displacedTransitions() case ListChange::Polish: break; } + gridview->forceLayout(); QVariantList resultTargetIndexes = gridview->property("displacedTargetIndexes").toList(); QVariantList resultTargetItems = gridview->property("displacedTargetItems").toList(); @@ -5534,6 +5548,7 @@ void tst_QQuickGridView::multipleTransitions() for (int j=changes[i].index; jforceLayout(); QTRY_COMPARE(model.count(), gridview->count()); if (i == changes.count() - 1) { QTRY_VERIFY(!gridview->property("runningAddTargets").toBool()); @@ -5545,6 +5560,7 @@ void tst_QQuickGridView::multipleTransitions() } case ListChange::Removed: model.removeItems(changes[i].index, changes[i].count); + gridview->forceLayout(); QTRY_COMPARE(model.count(), gridview->count()); if (i == changes.count() - 1) { QTRY_VERIFY(!gridview->property("runningRemoveTargets").toBool()); @@ -5555,6 +5571,7 @@ void tst_QQuickGridView::multipleTransitions() break; case ListChange::Moved: model.moveItems(changes[i].index, changes[i].to, changes[i].count); + gridview->forceLayout(); QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); if (i == changes.count() - 1) { QTRY_VERIFY(!gridview->property("runningMoveTargets").toBool()); @@ -5566,16 +5583,17 @@ void tst_QQuickGridView::multipleTransitions() case ListChange::SetCurrent: gridview->setCurrentIndex(changes[i].index); QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); + gridview->forceLayout(); break; case ListChange::SetContentY: gridview->setContentY(changes[i].pos); QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false); + gridview->forceLayout(); break; case ListChange::Polish: break; } } - QCOMPARE(gridview->count(), model.count()); QList items = findItems(contentItem, "wrapper"); int firstVisibleIndex = -1; @@ -5586,6 +5604,7 @@ void tst_QQuickGridView::multipleTransitions() break; } } + QTRY_COMPARE(gridview->count(), model.count()); QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex)); // verify all items moved to the correct final positions diff --git a/tests/auto/quick/qquicklistview/data/delayedChanges.qml b/tests/auto/quick/qquicklistview/data/delayedChanges.qml new file mode 100644 index 0000000..590af39 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/delayedChanges.qml @@ -0,0 +1,39 @@ +import QtQuick 2.1 + +Item { + width: 400 + height: 400 + function takeTwo() + { + listView.model.remove(0); + listView.model.remove(0); + } + function takeTwo_sync() + { + listView.model.remove(0); + listView.forceLayout(); + listView.model.remove(0); + listView.forceLayout(); + } + + ListView { + id: listView + height: parent.height + width: 400 + model: ListModel { + ListElement { name: "A" } + ListElement { name: "B" } + ListElement { name: "C" } + ListElement { name: "D" } + ListElement { name: "E" } + ListElement { name: "F" } + ListElement { name: "G" } + ListElement { name: "H" } + ListElement { name: "I" } + ListElement { name: "J" } + } + delegate: Text { + text: index + listView.count + } + } +} diff --git a/tests/auto/quick/qquicklistview/data/emptymodel.qml b/tests/auto/quick/qquicklistview/data/emptymodel.qml index 16bcd3f..3feec69 100644 --- a/tests/auto/quick/qquicklistview/data/emptymodel.qml +++ b/tests/auto/quick/qquicklistview/data/emptymodel.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.1 Rectangle { ListModel { id: model @@ -12,11 +12,13 @@ Rectangle { } function remove() { model.remove(0) + list.forceLayout() isCurrentItemNull = list.currentItem === null //check no seg fault } function add() { model.append({name: "hello"}) + list.forceLayout() isCurrentItemNull = list.currentItem === null } property bool isCurrentItemNull diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index f8c7de6..bec61ea 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -208,10 +208,11 @@ private slots: void parentBinding(); void defaultHighlightMoveDuration(); void accessEmptyCurrentItem_QTBUG_30227(); + void delayedChanges_QTBUG_30555(); private: - template void items(const QUrl &source, bool forceLayout); - template void changed(const QUrl &source, bool forceLayout); + template void items(const QUrl &source); + template void changed(const QUrl &source); template void inserted(const QUrl &source); template void inserted_more(QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom); template void removed(const QUrl &source, bool animated); @@ -329,7 +330,7 @@ void tst_QQuickListView::cleanupTestCase() } template -void tst_QQuickListView::items(const QUrl &source, bool forceLayout) +void tst_QQuickListView::items(const QUrl &source) { QQuickView *window = createView(); @@ -349,6 +350,7 @@ void tst_QQuickListView::items(const QUrl &source, bool forceLayout) QQuickListView *listview = findItem(window->rootObject(), "list"); QTRY_VERIFY(listview != 0); + listview->forceLayout(); QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); @@ -359,6 +361,7 @@ void tst_QQuickListView::items(const QUrl &source, bool forceLayout) QTRY_VERIFY(listview->highlightItem() != 0); QTRY_COMPARE(listview->count(), model.count()); QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count()); + listview->forceLayout(); QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item // current item should be first item @@ -398,8 +401,7 @@ void tst_QQuickListView::items(const QUrl &source, bool forceLayout) ctxt->setContextProperty("testModel", &model2); // Force a layout, necessary if ListView is completed before VisualDataModel. - if (forceLayout) - QCOMPARE(listview->property("count").toInt(), 0); + listview->forceLayout(); int itemCount = findItems(contentItem, "wrapper").count(); QTRY_VERIFY(itemCount == 0); @@ -413,7 +415,7 @@ void tst_QQuickListView::items(const QUrl &source, bool forceLayout) template -void tst_QQuickListView::changed(const QUrl &source, bool forceLayout) +void tst_QQuickListView::changed(const QUrl &source) { QQuickView *window = createView(); @@ -431,15 +433,15 @@ void tst_QQuickListView::changed(const QUrl &source, bool forceLayout) window->setSource(source); qApp->processEvents(); - QQuickFlickable *listview = findItem(window->rootObject(), "list"); + QQuickListView *listview = findItem(window->rootObject(), "list"); QTRY_VERIFY(listview != 0); + listview->forceLayout(); QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); // Force a layout, necessary if ListView is completed before VisualDataModel. - if (forceLayout) - QCOMPARE(listview->property("count").toInt(), model.count()); + listview->forceLayout(); model.modifyItem(1, "Will", "9876"); QQuickText *name = findItem(contentItem, "textName", 1); @@ -579,22 +581,24 @@ void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection v QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); - bool waitForPolish = (contentY != 0); if (verticalLayoutDirection == QQuickItemView::BottomToTop) { listview->setVerticalLayoutDirection(verticalLayoutDirection); QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); contentY = -listview->height() - contentY; } listview->setContentY(contentY); - if (waitForPolish) - QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); QList > newData; for (int i=0; ipolishScheduled, false); + QTRY_COMPARE(listview->property("count").toInt(), model.count()); + // check visibleItems.first() is in correct position QQuickItem *item0 = findItem(contentItem, "wrapper", 0); QVERIFY(item0); @@ -1019,17 +1023,17 @@ void tst_QQuickListView::removed_more(const QUrl &source, QQuickItemView::Vertic QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); - bool waitForPolish = (contentY != 0); if (verticalLayoutDirection == QQuickItemView::BottomToTop) { listview->setVerticalLayoutDirection(verticalLayoutDirection); QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); contentY = -listview->height() - contentY; } listview->setContentY(contentY); - if (waitForPolish) - QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); model.removeItems(removeIndex, removeCount); + //Wait for polish (updates list to the model changes) + QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); + QTRY_COMPARE(listview->property("count").toInt(), model.count()); // check visibleItems.first() is in correct position @@ -1219,6 +1223,7 @@ void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayou // confirm sanity when adding an item to cleared list model.addItem("New", "1"); + listview->forceLayout(); QTRY_VERIFY(listview->count() == 1); QVERIFY(listview->currentItem() != 0); QVERIFY(listview->currentIndex() == 0); @@ -1945,6 +1950,7 @@ void tst_QQuickListView::sections(const QUrl &source) // Remove section boundary model.removeItem(5); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); // New section header created @@ -1953,6 +1959,7 @@ void tst_QQuickListView::sections(const QUrl &source) QTRY_COMPARE(item->height(), 40.0); model.insertItem(3, "New Item", "0"); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); // Section header moved @@ -1966,6 +1973,7 @@ void tst_QQuickListView::sections(const QUrl &source) // insert item which will become a section header model.insertItem(6, "Replace header", "1"); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); item = findItem(contentItem, "wrapper", 6); @@ -2061,6 +2069,7 @@ void tst_QQuickListView::sectionsDelegate() // remove section boundary model.removeItem(5); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); for (int i = 0; i < 3; ++i) { QQuickItem *item = findItem(contentItem, @@ -2288,6 +2297,7 @@ void tst_QQuickListView::sectionsPositioning() listview->setContentY(120); QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); model.removeItem(5); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); for (int i = 1; i < 3; ++i) { QQuickItem *item = findVisibleChild(contentItem, @@ -2485,7 +2495,8 @@ void tst_QQuickListView::currentIndex_delayedItemCreation() QTRY_VERIFY(contentItem != 0); QSignalSpy spy(listview, SIGNAL(currentItemChanged())); - QCOMPARE(listview->currentIndex(), 0); + //QCOMPARE(listview->currentIndex(), 0); + listview->forceLayout(); QTRY_COMPARE(spy.count(), 1); releaseView(window); @@ -2536,6 +2547,7 @@ void tst_QQuickListView::currentIndex() for (int i = 0; i < 30; i++) model.addItem("Item" + QString::number(i), QString::number(i)); ctxt->setContextProperty("testModel", &model); + listview->forceLayout(); QCOMPARE(listview->currentIndex(), 0); QCOMPARE(listview->currentItem(), findItem(contentItem, "wrapper", 0)); @@ -2679,6 +2691,7 @@ void tst_QQuickListView::keyNavigation() QTRY_COMPARE(listview->currentIndex(), i+1); } QTest::keyRelease(window, forwardsKey); + listview->forceLayout(); QTRY_COMPARE(listview->currentIndex(), model.count()-1); QTRY_COMPARE(listview->contentX(), contentPosAtLastItem.x()); QTRY_COMPARE(listview->contentY(), contentPosAtLastItem.y()); @@ -2689,6 +2702,7 @@ void tst_QQuickListView::keyNavigation() QTRY_COMPARE(listview->currentIndex(), i-1); } QTest::keyRelease(window, backwardsKey); + listview->forceLayout(); QTRY_COMPARE(listview->currentIndex(), 0); QTRY_COMPARE(listview->contentX(), contentPosAtFirstItem.x()); QTRY_COMPARE(listview->contentY(), contentPosAtFirstItem.y()); @@ -3123,6 +3137,7 @@ void tst_QQuickListView::resetModel() strings << "four" << "five" << "six" << "seven"; model.setStringList(strings); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.rowCount()); for (int i = 0; i < model.rowCount(); ++i) { @@ -3470,6 +3485,7 @@ void tst_QQuickListView::header() QCOMPARE(item->position(), firstDelegatePos); model.clear(); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); QCOMPARE(header->position(), initialHeaderPos); // header should stay where it is if (orientation == QQuickListView::Vertical) @@ -3865,6 +3881,7 @@ void tst_QQuickListView::extents() QCOMPARE(listview->originY(), origin_empty.y()); for (int i=0; i<30; i++) model.addItem("Item" + QString::number(i), ""); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); QCOMPARE(listview->originX(), origin_nonEmpty.x()); QCOMPARE(listview->originY(), origin_nonEmpty.y()); @@ -4262,6 +4279,7 @@ void tst_QQuickListView::resizeFirstDelegate() for (int i = 0; i < 10; i++) model.addItem("Item" + QString::number(i), ""); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); item = findItem(contentItem, "wrapper", 1); @@ -4461,6 +4479,7 @@ void tst_QQuickListView::indexAt_itemAt() void tst_QQuickListView::incrementalModel() { QQuickView *window = createView(); + QSKIP("QTBUG-30716"); IncrementalModel model; QQmlContext *ctxt = window->rootContext(); @@ -4471,14 +4490,17 @@ void tst_QQuickListView::incrementalModel() QQuickListView *listview = findItem(window->rootObject(), "list"); QTRY_VERIFY(listview != 0); + listview->forceLayout(); QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); + listview->forceLayout(); QTRY_COMPARE(listview->count(), 20); listview->positionViewAtIndex(10, QQuickListView::Beginning); + listview->forceLayout(); QTRY_COMPARE(listview->count(), 25); delete window; @@ -4503,18 +4525,19 @@ void tst_QQuickListView::onAdd() ctxt->setContextProperty("delegateHeight", delegateHeight); window->setSource(testFileUrl("attachedSignals.qml")); - QObject *object = window->rootObject(); - object->setProperty("width", window->width()); - object->setProperty("height", window->height()); + QQuickListView* listview = qobject_cast(window->rootObject()); + listview->setProperty("width", window->width()); + listview->setProperty("height", window->height()); qApp->processEvents(); QList > items; for (int i=0; irootObject()->property("count").toInt(), model.count()); + listview->forceLayout(); + QTRY_COMPARE(listview->property("count").toInt(), model.count()); - QVariantList result = object->property("addedDelegates").toList(); + QVariantList result = listview->property("addedDelegates").toList(); QCOMPARE(result.count(), items.count()); for (int i=0; isetContextProperty("delegateHeight", delegateHeight); window->setSource(testFileUrl("attachedSignals.qml")); - QObject *object = window->rootObject(); + QQuickListView *listview = qobject_cast(window->rootObject()); model.removeItems(indexToRemove, removeCount); - QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count()); + listview->forceLayout(); + QTRY_COMPARE(listview->property("count").toInt(), model.count()); - QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount)); + QCOMPARE(listview->property("removedDelegateCount"), QVariant(removeCount)); releaseView(window); } @@ -4741,6 +4765,7 @@ void tst_QQuickListView::margins() // and originY is updated listview->setContentY(100); model.removeItem(1); + listview->forceLayout(); QTRY_COMPARE(listview->count(), model.count()); listview->setContentY(-50); QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); @@ -4979,22 +5004,22 @@ void tst_QQuickListView::snapToItem() void tst_QQuickListView::qAbstractItemModel_package_items() { - items(testFileUrl("listviewtest-package.qml"), true); + items(testFileUrl("listviewtest-package.qml")); } void tst_QQuickListView::qAbstractItemModel_items() { - items(testFileUrl("listviewtest.qml"), false); + items(testFileUrl("listviewtest.qml")); } void tst_QQuickListView::qAbstractItemModel_package_changed() { - changed(testFileUrl("listviewtest-package.qml"), true); + changed(testFileUrl("listviewtest-package.qml")); } void tst_QQuickListView::qAbstractItemModel_changed() { - changed(testFileUrl("listviewtest.qml"), false); + changed(testFileUrl("listviewtest.qml")); } void tst_QQuickListView::qAbstractItemModel_package_inserted() @@ -5555,6 +5580,7 @@ void tst_QQuickListView::populateTransitions() // clear the model window->rootContext()->setContextProperty("testModel", QVariant()); + listview->forceLayout(); QTRY_COMPARE(listview->count(), 0); QTRY_COMPARE(findItems(contentItem, "wrapper").count(), 0); listview->setProperty("countPopulateTransitions", 0); @@ -5683,6 +5709,7 @@ void tst_QQuickListView::addTransitions() if (!newData.isEmpty()) { model.insertItems(insertionIndex, newData); QTRY_COMPARE(model.count(), listview->count()); + listview->forceLayout(); } QList targetItems = findItems(contentItem, "wrapper", targetIndexes); @@ -6291,6 +6318,7 @@ void tst_QQuickListView::displacedTransitions() case ListChange::Polish: break; } + listview->forceLayout(); QVariantList resultTargetIndexes = listview->property("displacedTargetIndexes").toList(); QVariantList resultTargetItems = listview->property("displacedTargetItems").toList(); @@ -6539,6 +6567,7 @@ void tst_QQuickListView::multipleTransitions() break; } } + listview->forceLayout(); QCOMPARE(listview->count(), model.count()); // verify all items moved to the correct final positions @@ -6822,6 +6851,7 @@ void tst_QQuickListView::accessEmptyCurrentItem_QTBUG_30227() QQuickListView *listview = window->rootObject()->findChild(); QTRY_VERIFY(listview != 0); + listview->forceLayout(); QMetaObject::invokeMethod(window->rootObject(), "remove"); QVERIFY(window->rootObject()->property("isCurrentItemNull").toBool()); @@ -6830,6 +6860,26 @@ void tst_QQuickListView::accessEmptyCurrentItem_QTBUG_30227() QVERIFY(!window->rootObject()->property("isCurrentItemNull").toBool()); } +void tst_QQuickListView::delayedChanges_QTBUG_30555() +{ + QQuickView *window = createView(); + window->setSource(testFileUrl("delayedChanges.qml")); + + QQuickListView *listview = window->rootObject()->findChild(); + QTRY_VERIFY(listview != 0); + + QCOMPARE(listview->count(), 10); + + //Takes two just like in the bug report + QMetaObject::invokeMethod(window->rootObject(), "takeTwo"); + QTRY_COMPARE(listview->count(), 8); + + QMetaObject::invokeMethod(window->rootObject(), "takeTwo_sync"); + QCOMPARE(listview->count(), 6); + + delete window; +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index d16bf81..d374d71 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -615,6 +615,7 @@ void tst_qquickvisualdatamodel::childChanged() vdm->setRootIndex(QVariant::fromValue(model.indexFromItem(model.item(1,0)))); QCOMPARE(listview->count(), 1); + listview->forceLayout(); QQuickText *name = findItem(contentItem, "display", 0); QVERIFY(name); QCOMPARE(name->text(), QString("Row 2 Child Item")); @@ -628,6 +629,7 @@ void tst_qquickvisualdatamodel::childChanged() model.item(1,0)->appendRow(new QStandardItem(QLatin1String("Row 2 Child Item 2"))); QCOMPARE(listview->count(), 2); + listview->forceLayout(); name = findItem(contentItem, "display", 1); QVERIFY(name != 0); QCOMPARE(name->text(), QString("Row 2 Child Item 2")); @@ -638,6 +640,8 @@ void tst_qquickvisualdatamodel::childChanged() vdm->setRootIndex(QVariant::fromValue(QModelIndex())); QCOMPARE(listview->count(), 3); + + listview->forceLayout(); name = findItem(contentItem, "display", 0); QVERIFY(name); QCOMPARE(name->text(), QString("Row 1 Item")); @@ -989,6 +993,8 @@ void tst_qquickvisualdatamodel::packagesDestroyed() QQuickItem *rightContent = rightview->contentItem(); QTRY_VERIFY(rightContent != 0); + leftview->forceLayout(); + rightview->forceLayout(); QCOMPARE(leftview->currentIndex(), 0); QCOMPARE(rightview->currentIndex(), 0); @@ -3548,6 +3554,7 @@ void tst_qquickvisualdatamodel::resolve() evaluate(visualModel, setupExpression); QCOMPARE(evaluate(listView, "count"), unresolvedCount); + listView->forceLayout(); evaluate(visualModel, resolveExpression); QCOMPARE(evaluate(listView, "count"), inItems ? visualCount : modelCount); -- 2.7.4