aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquicklistview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquicklistview.cpp')
-rw-r--r--src/quick/items/qquicklistview.cpp102
1 files changed, 93 insertions, 9 deletions
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 0c924c0e0e..b954f1b107 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -382,6 +382,83 @@ private:
}
};
+/*! \internal
+ \brief A helper class for iterating over a model that might change
+
+ When populating the ListView from a model under normal
+ circumstances, we would iterate over the range of model indices
+ correspondning to the visual range, and basically call
+ createItem(index++) in order to create each item.
+
+ This will also emit Component.onCompleted() for each item, which
+ might do some weird things... For instance, it might remove itself
+ from the model, and this might change model count and the indices
+ of the other subsequent entries in the model.
+
+ This class takes such changes to the model into consideration while
+ iterating, and will adjust the iterator index and keep track of
+ whether the iterator has reached the end of the range.
+
+ It keeps track of changes to the model by connecting to
+ QQmlInstanceModel::modelUpdated() from its constructor.
+ When destroyed, it will automatically disconnect. You can
+ explicitly disconnect earlier by calling \fn disconnect().
+*/
+class MutableModelIterator {
+public:
+ MutableModelIterator(QQmlInstanceModel *model, int iBegin, int iEnd)
+ : removedAtIndex(false)
+ , backwards(iEnd < iBegin)
+ {
+ conn = QObject::connect(model, &QQmlInstanceModel::modelUpdated,
+ [&] (const QQmlChangeSet &changeSet, bool /*reset*/)
+ {
+ for (const QQmlChangeSet::Change &rem : changeSet.removes()) {
+ idxEnd -= rem.count;
+ if (rem.start() <= index) {
+ index -= rem.count;
+ if (index < rem.start() + rem.count)
+ removedAtIndex = true; // model index was removed
+ }
+ }
+ for (const QQmlChangeSet::Change &ins : changeSet.inserts()) {
+ idxEnd += ins.count;
+ if (ins.start() <= index)
+ index += ins.count;
+ }
+ }
+ );
+ index = iBegin;
+ idxEnd = iEnd;
+ }
+
+ bool hasNext() const {
+ return backwards ? index > idxEnd : index < idxEnd;
+ }
+
+ void next() { index += (backwards ? -1 : +1); }
+
+ ~MutableModelIterator()
+ {
+ disconnect();
+ }
+
+ void disconnect()
+ {
+ if (conn) {
+ QObject::disconnect(conn);
+ conn = QMetaObject::Connection(); // set to nullptr
+ }
+ }
+ int index = 0;
+ int idxEnd;
+ unsigned removedAtIndex : 1;
+ unsigned backwards : 1;
+private:
+ QMetaObject::Connection conn;
+};
+
+
//----------------------------------------------------------------------------
bool QQuickListViewPrivate::isContentFlowReversed() const
@@ -3602,7 +3679,6 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
// Insert items before the visible item.
int insertionIdx = index;
- int i = 0;
qreal from = tempPos - displayMarginBeginning - buffer;
if (insertionIdx < visibleIndex) {
@@ -3611,15 +3687,18 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
insertResult->sizeChangesBeforeVisiblePos += count * (averageSize + spacing);
}
} else {
- for (i = count-1; i >= 0 && pos >= from; --i) {
+ MutableModelIterator it(model, modelIndex + count - 1, modelIndex -1);
+ for (; it.hasNext() && pos >= from; it.next()) {
// item is before first visible e.g. in cache buffer
FxViewItem *item = nullptr;
- if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
- item->index = modelIndex + i;
+ if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
+ item->index = it.index;
if (!item)
- item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
+ item = createItem(it.index, QQmlIncubator::Synchronous);
if (!item)
return false;
+ if (it.removedAtIndex)
+ continue;
visibleAffected = true;
visibleItems.insert(insertionIdx, item);
@@ -3652,16 +3731,20 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
}
} else {
- for (int i = 0; i < count && pos <= lastVisiblePos; ++i) {
+ MutableModelIterator it(model, modelIndex, modelIndex + count);
+ for (; it.hasNext() && pos <= lastVisiblePos; it.next()) {
visibleAffected = true;
FxViewItem *item = nullptr;
- if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
- item->index = modelIndex + i;
+ if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
+ item->index = it.index;
bool newItem = !item;
+ it.removedAtIndex = false;
if (!item)
- item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
+ item = createItem(it.index, QQmlIncubator::Synchronous);
if (!item)
return false;
+ if (it.removedAtIndex)
+ continue;
visibleItems.insert(index, item);
if (index == 0)
@@ -3682,6 +3765,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
pos += item->size() + spacing;
++index;
}
+ it.disconnect();
if (0 < index && index < visibleItems.count()) {
FxViewItem *prevItem = visibleItems.at(index - 1);