Fix positioning issues and change content y repositioning behaviour
authorBea Lam <bea.lam@nokia.com>
Wed, 4 Jan 2012 23:59:30 +0000 (09:59 +1000)
committerQt by Nokia <qt-info@nokia.com>
Mon, 9 Jan 2012 06:14:31 +0000 (07:14 +0100)
Refactor the code for re-positioning the visibleItems.first() in
QSGItemView::applyModelChanges() and fix various positioning issues.

The positioning behaviour for removing items at the start of the view
has  changed. This action will no longer cause the content y to move
down; previously the content y would always move down unless another
item was moving backwards to the first visible position. This will make
it easier to implement built-in removal transitions for the views, since
removed items cannot be animated if the content y jumps down past them.

Additionally, moving items from before a GridView should not cause the
top item in the view to move upwards and out of the view.

This also adds additional remove tests and fixes the
tst_QQuickGridView::moved() test which wasn't waiting for the polish
event after setting the content y, which caused some of the tests to
pass even though their test data was wrong.

Change-Id: Idad11a73a18f88208e73a52111ed839458e05f2d
Reviewed-by: Martin Jones <martin.jones@nokia.com>
src/quick/items/qquickgridview.cpp
src/quick/items/qquickitemview.cpp
src/quick/items/qquickitemview_p_p.h
src/quick/items/qquicklistview.cpp
tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp
tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp

index 582f462..b29c61f 100644 (file)
@@ -173,7 +173,7 @@ public:
     virtual void repositionPackageItemAt(QQuickItem *item, int index);
     virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem);
     virtual void resetFirstItemPosition();
-    virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards);
+    virtual void adjustFirstItem(qreal forwards, qreal backwards);
 
     virtual void createHighlight();
     virtual void updateHighlight();
@@ -181,7 +181,7 @@ public:
 
     virtual void setPosition(qreal pos);
     virtual void layoutVisibleItems();
-    bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *, InsertionsResult *);
+    virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &insert, ChangeResult *changeResult, bool *newVisibleItemsFirst, QList<FxViewItem *> *addedItems);
     virtual bool needsRefillForAddedOrRemovedIndex(int index) const;
 
     virtual qreal headerSize() const;
@@ -598,11 +598,14 @@ void QQuickGridViewPrivate::resetFirstItemPosition()
     item->setPosition(0, 0);
 }
 
-void QQuickGridViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards)
+void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards)
 {
+    if (!visibleItems.count())
+        return;
+
     int moveCount = (forwards / rowSize()) - (backwards / rowSize());
 
-    FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(item);
+    FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first());
     gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
 }
 
@@ -783,8 +786,7 @@ void QQuickGridViewPrivate::initializeCurrentItem()
 {
     if (currentItem && currentIndex >= 0) {
         FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(currentItem);
-        if (gridItem)
-            gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
+        gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
     }
 }
 
@@ -1768,7 +1770,7 @@ void QQuickGridView::moveCurrentIndexRight()
     }
 }
 
-bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult)
+bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, ChangeResult *insertResult, bool *newVisibleItemsFirst, QList<FxViewItem *> *addedItems)
 {
     Q_Q(QQuickGridView);
 
@@ -1828,8 +1830,8 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
             item->index += count;
     }
 
-    int prevAddedCount = insertResult->addedItems.count();
-    if (firstVisible && rowPos < firstVisible->position()) {
+    int prevVisibleCount = visibleItems.count();
+    if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
         // Insert items before the visible item.
         int insertionIdx = index;
         int i = count - 1;
@@ -1838,15 +1840,12 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
         while (i >= 0) {
             if (rowPos > from && insertionIdx < visibleIndex) {
                 // item won't be visible, just note the size for repositioning
-                insertResult->sizeAddedBeforeVisible += rowSize();
+                insertResult->sizeChangesBeforeVisiblePos += rowSize();
             } else {
                 // item is before first visible e.g. in cache buffer
                 FxViewItem *item = 0;
-                if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
-                    if (item->index > modelIndex + i)
-                        insertResult->movedBackwards.append(item);
+                if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
                     item->index = modelIndex + i;
-                }
                 if (!item)
                     item = createItem(modelIndex + i);
                 if (!item)
@@ -1854,10 +1853,9 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
 
                 item->item->setVisible(true);
                 visibleItems.insert(insertionIdx, item);
-                if (!change.isMove()) {
-                    insertResult->addedItems.append(item);
-                    insertResult->sizeAddedBeforeVisible += rowSize();
-                }
+                if (!change.isMove())
+                    addedItems->append(item);
+                insertResult->sizeChangesBeforeVisiblePos += rowSize();
             }
 
             if (--colNum < 0 ) {
@@ -1873,11 +1871,8 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
         int to = buffer+tempPos+size()-1;
         while (i < count && rowPos <= to + rowSize()*(columns - (colPos/colSize()))/qreal(columns)) {
             FxViewItem *item = 0;
-            if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
-                if (item->index > modelIndex + i)
-                    insertResult->movedBackwards.append(item);
+            if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
                 item->index = modelIndex + i;
-            }
             if (!item)
                 item = createItem(modelIndex + i);
             if (!item)
@@ -1885,8 +1880,12 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
 
             item->item->setVisible(true);
             visibleItems.insert(index, item);
+            if (index == 0)
+                *newVisibleItemsFirst = true;
             if (!change.isMove())
-                insertResult->addedItems.append(item);
+                addedItems->append(item);
+            insertResult->sizeChangesAfterVisiblePos += rowSize();
+
             if (++colNum >= columns) {
                 colNum = 0;
                 rowPos += rowSize();
@@ -1899,7 +1898,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
 
     updateVisibleIndex();
 
-    return insertResult->addedItems.count() > prevAddedCount;
+    return visibleItems.count() > prevVisibleCount;
 }
 
 bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
index 879c02f..8f46661 100644 (file)
@@ -75,7 +75,7 @@ void QQuickItemViewChangeSet::applyChanges(const QDeclarativeChangeSet &changeSe
     pendingChanges.apply(changeSet);
 
     int moveId = -1;
-    int moveOffset;
+    int moveOffset = 0;
 
     foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) {
         itemCount -= r.count;
@@ -1221,7 +1221,6 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex)
 {
     Q_Q(QQuickItemView);
     applyPendingChanges();
-
     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
         if (currentItem) {
             currentItem->attached->setIsCurrentItem(false);
@@ -1425,57 +1424,25 @@ bool QQuickItemViewPrivate::applyModelChanges()
     updateUnrequestedIndexes();
     moveReason = QQuickItemViewPrivate::Other;
 
-    int prevCount = itemCount;
+    FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0;
+    int prevItemCount = itemCount;
+    int prevVisibleItemsCount = visibleItems.count();
     bool visibleAffected = false;
     bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
             || !currentChanges.pendingChanges.inserts().isEmpty();
 
-    FxViewItem *firstVisible = firstVisibleItem();
-    FxViewItem *origVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0;
-    int firstItemIndex = firstVisible ? firstVisible->index : -1;
-    qreal removedBeforeFirstVisibleBy = 0;
+    FxViewItem *prevFirstVisible = firstVisibleItem();
+    QDeclarativeNullableValue<qreal> prevFirstVisiblePos;
+    if (prevFirstVisible)
+        prevFirstVisiblePos = prevFirstVisible->position();
 
     const QVector<QDeclarativeChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
+    ChangeResult removalResult(prevFirstVisiblePos);
+    int removedCount = 0;
     for (int i=0; i<removals.count(); i++) {
         itemCount -= removals[i].count;
-
-        // Remove the items from the visible list, skipping anything already marked for removal
-        QList<FxViewItem*>::Iterator it = visibleItems.begin();
-        while (it != visibleItems.end()) {
-            FxViewItem *item = *it;
-            if (item->index == -1 || item->index < removals[i].index) {
-                // already removed, or before removed items
-                if (!visibleAffected && item->index < removals[i].index)
-                    visibleAffected = true;
-                ++it;
-            } else if (item->index >= removals[i].index + removals[i].count) {
-                // after removed items
-                item->index -= removals[i].count;
-                ++it;
-            } else {
-                // removed item
-                visibleAffected = true;
-                if (!removals[i].isMove())
-                    item->attached->emitRemove();
-
-                if (item->attached->delayRemove() && !removals[i].isMove()) {
-                    item->index = -1;
-                    QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
-                    ++it;
-                } else {
-                    if (firstVisible && item->position() < firstVisible->position() && item != visibleItems.first())
-                        removedBeforeFirstVisibleBy += item->size();
-                    if (removals[i].isMove()) {
-                        currentChanges.removedItems.insert(removals[i].moveKey(item->index), item);
-                    } else {
-                        if (item == firstVisible)
-                            firstVisible = 0;
-                        currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item);
-                    }
-                    it = visibleItems.erase(it);
-                }
-            }
-        }
+        if (applyRemovalChange(removals[i], &removalResult, &removedCount))
+            visibleAffected = true;
         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
             visibleAffected = true;
     }
@@ -1483,59 +1450,47 @@ bool QQuickItemViewPrivate::applyModelChanges()
         updateVisibleIndex();
 
     const QVector<QDeclarativeChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
-    InsertionsResult insertResult;
-    bool allInsertionsBeforeVisible = true;
-
+    ChangeResult insertionResult(prevFirstVisiblePos);
+    bool newVisibleItemsFirst = false;
+    QList<FxViewItem *> newItems;
     for (int i=0; i<insertions.count(); i++) {
         bool wasEmpty = visibleItems.isEmpty();
-        if (applyInsertionChange(insertions[i], firstVisible, &insertResult))
+        if (applyInsertionChange(insertions[i], &insertionResult, &newVisibleItemsFirst, &newItems))
             visibleAffected = true;
         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
             visibleAffected = true;
-        if (insertions[i].index >= visibleIndex)
-            allInsertionsBeforeVisible = false;
         if (wasEmpty && !visibleItems.isEmpty())
             resetFirstItemPosition();
         itemCount += insertions[i].count;
     }
-    for (int i=0; i<insertResult.addedItems.count(); ++i)
-        insertResult.addedItems.at(i)->attached->emitAdd();
-
-    // if the first visible item has moved, ensure another one takes its place
-    // so that we avoid shifting all content forwards
-    // (if an item is removed from before the first visible, the first visible should not move upwards)
-    bool movedBackToFirstVisible = false;
-    if (firstVisible && firstItemIndex >= 0) {
-        for (int i=0; i<insertResult.movedBackwards.count(); i++) {
-            if (insertResult.movedBackwards[i]->index == firstItemIndex) {
-                // an item has moved backwards up to the first visible's position
-                resetItemPosition(insertResult.movedBackwards[i], firstVisible);
-                insertResult.movedBackwards.removeAt(i);
-                movedBackToFirstVisible = true;
-                break;
+    for (int i=0; i<newItems.count(); i++)
+        newItems.at(i)->attached->emitAdd();
+
+    // reposition visibleItems.first() correctly so that the content y doesn't jump
+    if (visibleItems.count() && removedCount != prevVisibleItemsCount) {
+        if (newVisibleItemsFirst && prevVisibleItemsFirst)
+            resetItemPosition(visibleItems.first(), prevVisibleItemsFirst);
+
+        if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
+                && prevFirstVisible != visibleItems.first()) {
+            // the previous visibleItems.first() was also the first visible item, and it has been
+            // moved/removed, so move the new visibleItems.first() to the pos of the previous one
+            if (!newVisibleItemsFirst)
+                resetItemPosition(visibleItems.first(), prevFirstVisible);
+
+        } else if (prevFirstVisiblePos.isValid()) {
+            qreal moveForwardsBy = 0;
+            qreal moveBackwardsBy = 0;
+
+            // shift visibleItems.first() relative to the number of added/removed items
+            if (visibleItems.first()->position() > prevFirstVisiblePos) {
+                moveForwardsBy = insertionResult.sizeChangesAfterVisiblePos;
+                moveBackwardsBy = removalResult.sizeChangesAfterVisiblePos;
+            } else if (visibleItems.first()->position() < prevFirstVisiblePos) {
+                moveForwardsBy = removalResult.sizeChangesBeforeVisiblePos;
+                moveBackwardsBy = insertionResult.sizeChangesBeforeVisiblePos;
             }
-        }
-        if (!movedBackToFirstVisible && !allInsertionsBeforeVisible) {
-            // first visible item has moved forward, another visible item takes its place
-            FxViewItem *item = visibleItem(firstItemIndex);
-            if (item)
-                resetItemPosition(item, firstVisible);
-        }
-    }
-
-    // Ensure we don't cause an ugly list scroll
-    if (firstVisible && visibleItems.count() && visibleItems.first() != firstVisible) {
-        // ensure first item is placed at correct postion if moving backward
-        // since it will be used to position all subsequent items
-        if (insertResult.movedBackwards.count() && origVisibleItemsFirst)
-            resetItemPosition(visibleItems.first(), origVisibleItemsFirst);
-
-        // correct the first item position (unless it has already been fixed)
-        if (!movedBackToFirstVisible) {
-            qreal moveBackwardsBy = insertResult.sizeAddedBeforeVisible;
-            for (int i=0; i<insertResult.movedBackwards.count(); i++)
-                moveBackwardsBy += insertResult.movedBackwards[i]->size();
-            moveItemBy(visibleItems.first(), removedBeforeFirstVisibleBy, moveBackwardsBy);
+            adjustFirstItem(moveForwardsBy, moveBackwardsBy);
         }
     }
 
@@ -1558,7 +1513,7 @@ bool QQuickItemViewPrivate::applyModelChanges()
     currentChanges.reset();
 
     updateSections();
-    if (prevCount != itemCount)
+    if (prevItemCount != itemCount)
         emit q->countChanged();
 
     if (!visibleAffected)
@@ -1570,6 +1525,58 @@ bool QQuickItemViewPrivate::applyModelChanges()
     return visibleAffected;
 }
 
+bool QQuickItemViewPrivate::applyRemovalChange(const QDeclarativeChangeSet::Remove &removal, ChangeResult *insertResult, int *removedCount)
+{
+    Q_Q(QQuickItemView);
+    bool visibleAffected = false;
+
+    QList<FxViewItem*>::Iterator it = visibleItems.begin();
+    while (it != visibleItems.end()) {
+        FxViewItem *item = *it;
+        if (item->index == -1 || item->index < removal.index) {
+            // already removed, or before removed items
+            if (!visibleAffected && item->index < removal.index)
+                visibleAffected = true;
+            ++it;
+        } else if (item->index >= removal.index + removal.count) {
+            // after removed items
+            item->index -= removal.count;
+            ++it;
+        } else {
+            // removed item
+            visibleAffected = true;
+            if (!removal.isMove())
+                item->attached->emitRemove();
+
+            if (item->attached->delayRemove() && !removal.isMove()) {
+                item->index = -1;
+                QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
+                ++it;
+            } else {
+                if (insertResult->visiblePos.isValid()) {
+                    if (item->position() < insertResult->visiblePos) {
+                        // sizeRemovedBeforeFirstVisible measures the size between the visibleItems.first()
+                        // and the firstVisible, so don't count it if removing visibleItems.first()
+                        if (item != visibleItems.first())
+                            insertResult->sizeChangesBeforeVisiblePos += item->size();
+                    } else {
+                        insertResult->sizeChangesAfterVisiblePos += item->size();
+                    }
+                }
+                if (removal.isMove()) {
+                    currentChanges.removedItems.insert(removal.moveKey(item->index), item);
+                } else {
+                    // track item so it is released later
+                    currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item);
+                    (*removedCount)++;
+                }
+                it = visibleItems.erase(it);
+            }
+        }
+    }
+    return visibleAffected;
+}
+
 /*
   This may return 0 if the item is being created asynchronously.
   When the item becomes available, refill() will be called and the item
index cfceca6..a81aa6f 100644 (file)
@@ -99,12 +99,13 @@ class QQuickItemViewPrivate : public QQuickFlickablePrivate
 public:
     QQuickItemViewPrivate();
 
-    struct InsertionsResult {
-        QList<FxViewItem *> addedItems;
-        QList<FxViewItem *> movedBackwards;
-        qreal sizeAddedBeforeVisible;
+    struct ChangeResult {
+        QDeclarativeNullableValue<qreal> visiblePos;
+        qreal sizeChangesBeforeVisiblePos;
+        qreal sizeChangesAfterVisiblePos;
 
-        InsertionsResult() : sizeAddedBeforeVisible(0) {}
+        ChangeResult(const QDeclarativeNullableValue<qreal> &p)
+            : visiblePos(p), sizeChangesBeforeVisiblePos(0), sizeChangesAfterVisiblePos(0) {}
     };
 
     enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
@@ -145,6 +146,7 @@ public:
     void positionViewAtIndex(int index, int mode);
     void applyPendingChanges();
     bool applyModelChanges();
+    bool applyRemovalChange(const QDeclarativeChangeSet::Remove &removal, ChangeResult *changeResult, int *removedCount);
 
     void checkVisible() const;
 
@@ -236,11 +238,12 @@ protected:
     virtual void repositionPackageItemAt(QQuickItem *item, int index) = 0;
     virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem) = 0;
     virtual void resetFirstItemPosition() = 0;
-    virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) = 0;
+    virtual void adjustFirstItem(qreal forwards, qreal backwards) = 0;
 
     virtual void layoutVisibleItems() = 0;
     virtual void changedVisibleIndex(int newIndex) = 0;
-    virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *, InsertionsResult *) = 0;
+    virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &insert, ChangeResult *changeResult, bool *newVisibleItemsFirst, QList<FxViewItem *> *newItems) = 0;
+
     virtual bool needsRefillForAddedOrRemovedIndex(int) const { return false; }
 
     virtual void initializeViewItem(FxViewItem *) {}
index b2e9c61..e2c4288 100644 (file)
@@ -95,7 +95,7 @@ public:
     virtual void repositionPackageItemAt(QQuickItem *item, int index);
     virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem);
     virtual void resetFirstItemPosition();
-    virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards);
+    virtual void adjustFirstItem(qreal forwards, qreal backwards);
 
     virtual void createHighlight();
     virtual void updateHighlight();
@@ -103,7 +103,7 @@ public:
 
     virtual void setPosition(qreal pos);
     virtual void layoutVisibleItems();
-    bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *firstVisible, InsertionsResult *);
+    virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &insert, ChangeResult *changeResult, bool *newVisibleItemsFirst, QList<FxViewItem *> *addedItems);
 
     virtual void updateSections();
     QQuickItem *getSectionItem(const QString &section);
@@ -747,10 +747,12 @@ void QQuickListViewPrivate::resetFirstItemPosition()
     item->setPosition(0);
 }
 
-void QQuickListViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards)
+void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards)
 {
+    if (!visibleItems.count())
+        return;
     qreal diff = forwards - backwards;
-    static_cast<FxListItemSG*>(item)->setPosition(item->position() + diff);
+    static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
 }
 
 void QQuickListViewPrivate::createHighlight()
@@ -2374,7 +2376,7 @@ void QQuickListView::updateSections()
     }
 }
 
-bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult)
+bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, ChangeResult *insertResult, bool *newVisibleItemsFirst, QList<FxViewItem *> *addedItems)
 {
     int modelIndex = change.index;
     int count = change.count;
@@ -2414,8 +2416,8 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
                                                 : visibleItems.last()->endPosition()+spacing;
     }
 
-    int prevAddedCount = insertResult->addedItems.count();
-    if (firstVisible && pos < firstVisible->position()) {
+    int prevVisibleCount = visibleItems.count();
+    if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
         // Insert items before the visible item.
         int insertionIdx = index;
         int i = 0;
@@ -2424,26 +2426,24 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
         for (i = count-1; i >= 0; --i) {
             if (pos > from && insertionIdx < visibleIndex) {
                 // item won't be visible, just note the size for repositioning
-                insertResult->sizeAddedBeforeVisible += averageSize + spacing;
+                insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
                 pos -= averageSize + spacing;
             } else {
                 // item is before first visible e.g. in cache buffer
                 FxViewItem *item = 0;
-                if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
-                    if (item->index > modelIndex + i)
-                        insertResult->movedBackwards.append(item);
+                if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
                     item->index = modelIndex + i;
-                }
                 if (!item)
                     item = createItem(modelIndex + i);
                 if (!item)
                     return false;
 
                 visibleItems.insert(insertionIdx, item);
-                if (!change.isMove()) {
-                    insertResult->addedItems.append(item);
-                    insertResult->sizeAddedBeforeVisible += item->size();
-                }
+                if (insertionIdx == 0)
+                    *newVisibleItemsFirst = true;
+                if (!change.isMove())
+                    addedItems->append(item);
+                insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
                 pos -= item->size() + spacing;
             }
             index++;
@@ -2453,19 +2453,19 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
         int to = buffer+tempPos+size();
         for (i = 0; i < count && pos <= to; ++i) {
             FxViewItem *item = 0;
-            if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
-                if (item->index > modelIndex + i)
-                    insertResult->movedBackwards.append(item);
+            if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
                 item->index = modelIndex + i;
-            }
             if (!item)
                 item = createItem(modelIndex + i);
             if (!item)
                 return false;
 
             visibleItems.insert(index, item);
+            if (index == 0)
+                *newVisibleItemsFirst = true;
             if (!change.isMove())
-                insertResult->addedItems.append(item);
+                addedItems->append(item);
+            insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
             pos += item->size() + spacing;
             ++index;
         }
@@ -2479,7 +2479,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In
 
     updateVisibleIndex();
 
-    return insertResult->addedItems.count() > prevAddedCount;
+    return visibleItems.count() > prevVisibleCount;
 }
 
 
index fa42ddb..fe469b5 100644 (file)
@@ -73,6 +73,8 @@ private slots:
     void insertBeforeVisible();
     void insertBeforeVisible_data();
     void removed();
+    void removed_more();
+    void removed_more_data();
     void addOrRemoveBeforeVisible();
     void addOrRemoveBeforeVisible_data();
     void clear();
@@ -629,6 +631,7 @@ void tst_QQuickGridView::insertBeforeVisible()
     int firstVisibleIndex = 20;     // move to an index where the top item is not visible
     gridview->setContentY(firstVisibleIndex * 20.0);
     gridview->setCurrentIndex(firstVisibleIndex);
+
     qApp->processEvents();
     QTRY_COMPARE(gridview->currentIndex(), firstVisibleIndex);
     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
@@ -831,6 +834,181 @@ void tst_QQuickGridView::removed()
     delete canvas;
 }
 
+void tst_QQuickGridView::removed_more()
+{
+    QFETCH(qreal, contentY);
+    QFETCH(int, removeIndex);
+    QFETCH(int, removeCount);
+    QFETCH(qreal, itemsOffsetAfterMove);
+
+    QQuickText *name;
+    QQuickText *number;
+    QQuickView *canvas = createView();
+    canvas->show();
+
+    TestModel model;
+    for (int i = 0; i < 30; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QDeclarativeContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    ctxt->setContextProperty("testRightToLeft", QVariant(false));
+    ctxt->setContextProperty("testTopToBottom", QVariant(false));
+    canvas->setSource(testFileUrl("gridview1.qml"));
+    qApp->processEvents();
+
+    QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
+    QTRY_VERIFY(gridview != 0);
+    QQuickItem *contentItem = gridview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
+
+    gridview->setContentY(contentY);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    model.removeItems(removeIndex, removeCount);
+    QTRY_COMPARE(gridview->property("count").toInt(), model.count());
+
+    // check visibleItems.first() is in correct position
+    QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
+//    qApp->exec();
+    QVERIFY(item0);
+    QCOMPARE(item0->y(), itemsOffsetAfterMove);
+
+    int firstVisibleIndex = -1;
+    QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    for (int i=0; i<items.count(); i++) {
+        if (items[i]->y() >= contentY) {
+            QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+            firstVisibleIndex = e.evaluate().toInt();
+            break;
+        }
+    }
+    QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+    // Confirm items positioned correctly and indexes correct
+    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+
+        QTRY_COMPARE(item->x(), (i%3)*80.0);
+        QTRY_COMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
+
+        name = findItem<QQuickText>(contentItem, "textName", i);
+        QVERIFY(name != 0);
+        QTRY_COMPARE(name->text(), model.name(i));
+        number = findItem<QQuickText>(contentItem, "textNumber", i);
+        QVERIFY(number != 0);
+        QTRY_COMPARE(number->text(), model.number(i));
+    }
+
+    delete canvas;
+}
+
+void tst_QQuickGridView::removed_more_data()
+{
+    QTest::addColumn<qreal>("contentY");
+    QTest::addColumn<int>("removeIndex");
+    QTest::addColumn<int>("removeCount");
+    QTest::addColumn<qreal>("itemsOffsetAfterMove");
+
+    QTest::newRow("remove 1, before visible items")
+            << 120.0     // show 6-23
+            << 3 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, all before visible items")
+            << 120.0
+            << 1 << 3
+            << 60.0;    // removed top row, slide down by 1 row
+
+    QTest::newRow("remove multiple, all before visible items, remove item 0")
+            << 120.0
+            << 0 << 4
+            << 60.0;    // removed top row, slide down by 1 row
+
+
+    // remove 3,4,5 before the visible pos, first row moves down to just before the visible pos,
+    // items 6,7 are removed from view, item 8 slides up to original pos of item 6 (120px)
+    QTest::newRow("remove multiple, mix of items from before and within visible items")
+            << 120.0
+            << 3 << 5
+            << 60.0;    // adjust for the 1 row removed before the visible
+
+    QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
+            << 120.0
+            << 0 << 8
+            << 60.0 * 2;    // adjust for the 2 rows removed before the visible
+
+
+    QTest::newRow("remove 1, from start of visible, content at start")
+            << 0.0
+            << 0 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from start of visible, content at start")
+            << 0.0
+            << 0 << 3
+            << 0.0;
+
+    QTest::newRow("remove 1, from start of visible, content not at start")
+            << 120.0     // show 6-23
+            << 4 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from start of visible, content not at start")
+            << 120.0     // show 6-23
+            << 4 << 3
+            << 0.0;
+
+
+    QTest::newRow("remove 1, from middle of visible, content at start")
+            << 0.0
+            << 10 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from middle of visible, content at start")
+            << 0.0
+            << 10 << 5
+            << 0.0;
+
+    QTest::newRow("remove 1, from middle of visible, content not at start")
+            << 120.0     // show 6-23
+            << 10 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from middle of visible, content not at start")
+            << 120.0     // show 6-23
+            << 10 << 5
+            << 0.0;
+
+
+    QTest::newRow("remove 1, after visible, content at start")
+            << 0.0
+            << 16 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, after visible, content at start")
+            << 0.0
+            << 16 << 5
+            << 0.0;
+
+    QTest::newRow("remove 1, after visible, content not at start")
+            << 120.0     // show 6-23
+            << 16+4 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, after visible, content not at start")
+            << 120.0     // show 6-23
+            << 16+4 << 5
+            << 0.0;
+
+    QTest::newRow("remove multiple, mix of items from within and after visible items")
+            << 120.0     // show 6-23
+            << 20 << 5
+            << 0.0;
+}
+
 void tst_QQuickGridView::addOrRemoveBeforeVisible()
 {
     // QTBUG-21588: ensure re-layout is done on grid after adding or removing
@@ -956,6 +1134,11 @@ void tst_QQuickGridView::clear()
 
 void tst_QQuickGridView::moved()
 {
+    if (QTest::currentDataTag() == QLatin1String("move 1 forwards, from non-visible -> visible")
+            || QTest::currentDataTag() == QLatin1String("move 1 forwards, from non-visible -> visible (move first item)")) {
+        QSKIP("QTBUG-23455");
+    }
+
     QFETCH(qreal, contentY);
     QFETCH(int, from);
     QFETCH(int, to);
@@ -989,9 +1172,9 @@ void tst_QQuickGridView::moved()
     QTRY_VERIFY(currentItem != 0);
 
     gridview->setContentY(contentY);
-    model.moveItems(from, to, count);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
-    // wait for items to move
+    model.moveItems(from, to, count);
     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
 
     // Confirm items positioned correctly and indexes correct
@@ -1030,20 +1213,26 @@ void tst_QQuickGridView::moved_data()
     // model starts with 30 items, each 80x60, in area 240x320
     // 18 items should be visible at a time
 
+    // The first visible item should not move upwards and out of the view
+    // if items are moved/removed before it.
+
+
     QTest::newRow("move 1 forwards, within visible items")
             << 0.0
             << 1 << 8 << 1
             << 0.0;
 
+    // skipped QTBUG-23455
     QTest::newRow("move 1 forwards, from non-visible -> visible")
             << 120.0     // show 6-23
             << 1 << 23 << 1
-            << 0.0;     // only 1 item was removed from the 1st row, so it doesn't move down
+            << 0.0;
 
+    // skipped QTBUG-23455
     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
             << 120.0     // // show 6-23
             << 0 << 6 << 1
-            << 0.0;     // only 1 item was removed from the 1st row, so it doesn't move down
+            << 0.0;
 
     QTest::newRow("move 1 forwards, from visible -> non-visible")
             << 0.0
index 7312e44..98d4f49 100644 (file)
@@ -85,8 +85,12 @@ private slots:
     void qAbstractItemModel_inserted_more_data();
 
     void qListModelInterface_removed();
+    void qListModelInterface_removed_more();
+    void qListModelInterface_removed_more_data();
     void qListModelInterface_package_removed();
     void qAbstractItemModel_removed();
+    void qAbstractItemModel_removed_more();
+    void qAbstractItemModel_removed_more_data();
 
     void qListModelInterface_moved();
     void qListModelInterface_moved_data();
@@ -167,6 +171,7 @@ private:
     template <class T> void inserted(const QUrl &source);
     template <class T> void inserted_more();
     template <class T> void removed(const QUrl &source, bool animated);
+    template <class T> void removed_more(const QUrl &source);
     template <class T> void moved(const QUrl &source);
     template <class T> void clear(const QUrl &source);
     template <class T> void sections(const QUrl &source);
@@ -180,6 +185,7 @@ private:
     void dumpTree(QQuickItem *parent, int depth = 0);
 
     void inserted_more_data();
+    void removed_more_data();
     void moved_data();
 };
 
@@ -677,7 +683,7 @@ void tst_QQuickListView::inserted(const QUrl &source)
     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
     QVERIFY(item);
     QCOMPARE(item->y(), 0.);
-    QVERIFY(listview->contentY() == 0);
+    QTRY_VERIFY(listview->contentY() == 0);
 
     delete canvas;
     delete testObject;
@@ -870,6 +876,7 @@ void tst_QQuickListView::insertBeforeVisible()
     int firstVisibleIndex = 20;     // move to an index where the top item is not visible
     listview->setContentY(firstVisibleIndex * 20.0);
     listview->setCurrentIndex(firstVisibleIndex);
+
     qApp->processEvents();
     QTRY_COMPARE(listview->currentIndex(), firstVisibleIndex);
     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
@@ -971,7 +978,7 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
     }
 
     // Remove first item (which is the current item);
-    model.removeItem(0);  // post: top item starts at 20
+    model.removeItem(0);
     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     name = findItem<QQuickText>(contentItem, "textName", 0);
@@ -987,7 +994,7 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
         QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(),i*20.0 + 20.0);
+        QTRY_COMPARE(item->y(),i*20.0);
     }
 
     // Remove items not visible
@@ -1000,14 +1007,14 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
         QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(),i*20.0+20.0);
+        QTRY_COMPARE(item->y(),i*20.0);
     }
 
     // Remove items before visible
     listview->setContentY(80);
     listview->setCurrentIndex(10);
 
-    model.removeItem(1); // post: top item will be at 40
+    model.removeItem(1); // post: top item will be at 20
     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     // Confirm items positioned correctly
@@ -1015,7 +1022,7 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
         QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(),40+i*20.0);
+        QTRY_COMPARE(item->y(),20+i*20.0);
     }
 
     // Remove current index
@@ -1026,7 +1033,7 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
     QTRY_COMPARE(listview->currentIndex(), 9);
     QTRY_VERIFY(listview->currentItem() != oldCurrent);
 
-    listview->setContentY(40); // That's the top now
+    listview->setContentY(20); // That's the top now
     // let transitions settle.
     QTest::qWait(300);
 
@@ -1036,7 +1043,7 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
         QTRY_VERIFY(item);
-        QTRY_COMPARE(item->y(),40+i*20.0);
+        QTRY_COMPARE(item->y(),20+i*20.0);
     }
 
     // remove current item beyond visible items.
@@ -1105,6 +1112,184 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
 }
 
 template <class T>
+void tst_QQuickListView::removed_more(const QUrl &source)
+{
+    QFETCH(qreal, contentY);
+    QFETCH(int, removeIndex);
+    QFETCH(int, removeCount);
+    QFETCH(qreal, itemsOffsetAfterMove);
+
+    QQuickText *name;
+    QQuickText *number;
+    QQuickView *canvas = createView();
+    canvas->show();
+
+    T model;
+    for (int i = 0; i < 30; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QDeclarativeContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+
+    TestObject *testObject = new TestObject;
+    ctxt->setContextProperty("testObject", testObject);
+
+    canvas->setSource(source);
+    qApp->processEvents();
+
+    QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
+    QTRY_VERIFY(listview != 0);
+    QQuickItem *contentItem = listview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
+
+    listview->setContentY(contentY);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+
+    // wait for refill (after refill, items above the firstVisibleIndex-1 should not be rendered)
+    int firstVisibleIndex = contentY / 20;
+    if (firstVisibleIndex - 2 >= 0)
+        QTRY_VERIFY(!findItem<QQuickText>(contentItem, "textName", firstVisibleIndex - 2));
+
+    model.removeItems(removeIndex, removeCount);
+    QTRY_COMPARE(listview->property("count").toInt(), model.count());
+
+    // check visibleItems.first() is in correct position
+    QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
+    QVERIFY(item0);
+    QCOMPARE(item0->y(), itemsOffsetAfterMove);
+
+    QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    for (int i=0; i<items.count(); i++) {
+        if (items[i]->y() >= contentY) {
+            QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+            firstVisibleIndex = e.evaluate().toInt();
+            break;
+        }
+    }
+    QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+    // Confirm items positioned correctly and indexes correct
+    int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+        QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
+        name = findItem<QQuickText>(contentItem, "textName", i);
+        QVERIFY(name != 0);
+        QTRY_COMPARE(name->text(), model.name(i));
+        number = findItem<QQuickText>(contentItem, "textNumber", i);
+        QVERIFY(number != 0);
+        QTRY_COMPARE(number->text(), model.number(i));
+    }
+
+    delete canvas;
+    delete testObject;
+}
+
+void tst_QQuickListView::removed_more_data()
+{
+    QTest::addColumn<qreal>("contentY");
+    QTest::addColumn<int>("removeIndex");
+    QTest::addColumn<int>("removeCount");
+    QTest::addColumn<qreal>("itemsOffsetAfterMove");
+
+    QTest::newRow("remove 1, before visible items")
+            << 80.0     // show 4-19
+            << 3 << 1
+            << 20.0;   // visible items slide down by 1 item so that first visible does not move
+
+    QTest::newRow("remove multiple, all before visible items")
+            << 80.0
+            << 1 << 3
+            << 20.0 * 3;
+
+    QTest::newRow("remove multiple, all before visible items, remove item 0")
+            << 80.0
+            << 0 << 4
+            << 20.0 * 4;
+
+    // remove 1,2,3 before the visible pos, 0 moves down to just before the visible pos,
+    // items 4,5 are removed from view, item 6 slides up to original pos of item 4 (80px)
+    QTest::newRow("remove multiple, mix of items from before and within visible items")
+            << 80.0
+            << 1 << 5
+            << 20.0 * 3;    // adjust for the 3 items removed before the visible
+
+    QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
+            << 80.0
+            << 0 << 6
+            << 20.0 * 4;    // adjust for the 3 items removed before the visible
+
+
+    QTest::newRow("remove 1, from start of visible, content at start")
+            << 0.0
+            << 0 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from start of visible, content at start")
+            << 0.0
+            << 0 << 3
+            << 0.0;
+
+    QTest::newRow("remove 1, from start of visible, content not at start")
+            << 80.0     // show 4-19
+            << 4 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from start of visible, content not at start")
+            << 80.0     // show 4-19
+            << 4 << 3
+            << 0.0;
+
+
+    QTest::newRow("remove 1, from middle of visible, content at start")
+            << 0.0
+            << 10 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from middle of visible, content at start")
+            << 0.0
+            << 10 << 5
+            << 0.0;
+
+    QTest::newRow("remove 1, from middle of visible, content not at start")
+            << 80.0     // show 4-19
+            << 10 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, from middle of visible, content not at start")
+            << 80.0     // show 4-19
+            << 10 << 5
+            << 0.0;
+
+
+    QTest::newRow("remove 1, after visible, content at start")
+            << 0.0
+            << 16 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, after visible, content at start")
+            << 0.0
+            << 16 << 5
+            << 0.0;
+
+    QTest::newRow("remove 1, after visible, content not at middle")
+            << 80.0     // show 4-19
+            << 16+4 << 1
+            << 0.0;
+
+    QTest::newRow("remove multiple, after visible, content not at start")
+            << 80.0     // show 4-19
+            << 16+4 << 5
+            << 0.0;
+
+    QTest::newRow("remove multiple, mix of items from within and after visible items")
+            << 80.0
+            << 18 << 5
+            << 0.0;
+}
+
+template <class T>
 void tst_QQuickListView::clear(const QUrl &source)
 {
     QQuickView *canvas = createView();
@@ -1182,10 +1367,10 @@ void tst_QQuickListView::moved(const QUrl &source)
     QTRY_VERIFY(currentItem != 0);
 
     listview->setContentY(contentY);
-    model.moveItems(from, to, count);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
-    // wait for items to move
-    QTest::qWait(100);
+    model.moveItems(from, to, count);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
 
     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
     int firstVisibleIndex = -1;
@@ -4210,6 +4395,16 @@ void tst_QQuickListView::qListModelInterface_removed()
     removed<TestModel>(testFileUrl("listviewtest.qml"), true);
 }
 
+void tst_QQuickListView::qListModelInterface_removed_more()
+{
+    removed_more<TestModel>(testFileUrl("listviewtest.qml"));
+}
+
+void tst_QQuickListView::qListModelInterface_removed_more_data()
+{
+    removed_more_data();
+}
+
 void tst_QQuickListView::qListModelInterface_package_removed()
 {
     removed<TestModel>(testFileUrl("listviewtest-package.qml"), false);
@@ -4222,6 +4417,16 @@ void tst_QQuickListView::qAbstractItemModel_removed()
     removed<TestModel2>(testFileUrl("listviewtest.qml"), true);
 }
 
+void tst_QQuickListView::qAbstractItemModel_removed_more()
+{
+    removed_more<TestModel2>(testFileUrl("listviewtest.qml"));
+}
+
+void tst_QQuickListView::qAbstractItemModel_removed_more_data()
+{
+    removed_more_data();
+}
+
 void tst_QQuickListView::qListModelInterface_moved()
 {
     moved<TestModel>(testFileUrl("listviewtest.qml"));
@@ -4659,9 +4864,9 @@ void tst_QQuickListView::unrequestedVisibility()
     model.moveItems(17, 16, 1);
     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
 
-    QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
+    QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
     QCOMPARE(item->isVisible(), false);
-    QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 6));
+    QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
     QCOMPARE(item->isVisible(), true);
     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
     QCOMPARE(item->isVisible(), true);