diff options
Diffstat (limited to 'src/quick/items/qquickitemview.cpp')
-rw-r--r-- | src/quick/items/qquickitemview.cpp | 130 |
1 files changed, 94 insertions, 36 deletions
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 661f19509a..031ac6337a 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -197,6 +197,8 @@ void QQuickItemView::setModel(const QVariant &m) disconnect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); disconnect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*))); disconnect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*))); + disconnect(d->model, SIGNAL(itemPooled(int, QObject *)), this, SLOT(onItemPooled(int, QObject *))); + disconnect(d->model, SIGNAL(itemReused(int, QObject *)), this, SLOT(onItemReused(int, QObject *))); } QQmlInstanceModel *oldModel = d->model; @@ -232,10 +234,14 @@ void QQuickItemView::setModel(const QVariant &m) connect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*))); connect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); connect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*))); + connect(d->model, SIGNAL(itemPooled(int, QObject *)), this, SLOT(onItemPooled(int, QObject *))); + connect(d->model, SIGNAL(itemReused(int, QObject *)), this, SLOT(onItemReused(int, QObject *))); if (isComponentComplete()) { d->updateSectionCriteria(); d->refill(); - d->currentIndex = -1; + /* Setting currentIndex to -2 ensures that we always enter the "currentIndex changed" + code path in setCurrentIndex, updating bindings depending on currentIndex.*/ + d->currentIndex = -2; setCurrentIndex(d->model->count() > 0 ? 0 : -1); d->updateViewport(); @@ -252,6 +258,7 @@ void QQuickItemView::setModel(const QVariant &m) emit countChanged(); } emit modelChanged(); + d->moveReason = QQuickItemViewPrivate::Other; } QQmlComponent *QQuickItemView::delegate() const @@ -691,6 +698,28 @@ void QQuickItemView::setHighlightMoveDuration(int duration) } } +bool QQuickItemView::reuseItems() const +{ + return bool(d_func()->reusableFlag == QQmlDelegateModel::Reusable); +} + +void QQuickItemView::setReuseItems(bool reuse) +{ + Q_D(QQuickItemView); + if (reuseItems() == reuse) + return; + + d->reusableFlag = reuse ? QQmlDelegateModel::Reusable : QQmlDelegateModel::NotReusable; + + if (!reuse && d->model) { + // When we're told to not reuse items, we + // immediately, as documented, drain the pool. + d->model->drainReusableItemsPool(0); + } + + emit reuseItemsChanged(); +} + QQuickTransition *QQuickItemView::populateTransition() const { Q_D(const QQuickItemView); @@ -845,7 +874,7 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) setPosition(qMin(itemPos, maxExtent)); // now release the reference to all the old visible items. for (FxViewItem *item : oldVisible) - releaseItem(item); + releaseItem(item, reusableFlag); item = visibleItem(idx); } if (item) { @@ -1088,8 +1117,8 @@ qreal QQuickItemViewPrivate::calculatedMaxExtent() const void QQuickItemViewPrivate::applyDelegateChange() { - releaseVisibleItems(); - releaseItem(currentItem); + releaseVisibleItems(QQmlDelegateModel::NotReusable); + releaseItem(currentItem, QQmlDelegateModel::NotReusable); currentItem = nullptr; updateSectionCriteria(); refill(); @@ -1191,7 +1220,7 @@ void QQuickItemView::destroyRemoved() } else { if (hasRemoveTransition) d->runDelayedRemoveTransition = true; - d->releaseItem(item); + d->releaseItem(item, d->reusableFlag); it = d->visibleItems.erase(it); } } else { @@ -1635,7 +1664,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) if (currentItem) { if (currentItem->attached) currentItem->attached->setIsCurrentItem(false); - releaseItem(currentItem); + releaseItem(currentItem, reusableFlag); currentItem = nullptr; currentIndex = modelIndex; emit q->currentIndexChanged(); @@ -1672,7 +1701,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) if (oldCurrentItem != currentItem && (!oldCurrentItem || !currentItem || oldCurrentItem->item != currentItem->item)) emit q->currentItemChanged(); - releaseItem(oldCurrentItem); + releaseItem(oldCurrentItem, reusableFlag); } void QQuickItemViewPrivate::clear(bool onDestruction) @@ -1682,17 +1711,17 @@ void QQuickItemViewPrivate::clear(bool onDestruction) bufferedChanges.reset(); timeline.clear(); - releaseVisibleItems(); + releaseVisibleItems(QQmlInstanceModel::NotReusable); visibleIndex = 0; for (FxViewItem *item : qAsConst(releasePendingTransition)) { item->releaseAfterTransition = false; - releaseItem(item); + releaseItem(item, QQmlInstanceModel::NotReusable); } releasePendingTransition.clear(); auto oldCurrentItem = currentItem; - releaseItem(currentItem); + releaseItem(currentItem, QQmlDelegateModel::NotReusable); currentItem = nullptr; if (oldCurrentItem) emit q->currentItemChanged(); @@ -1751,7 +1780,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to) if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()) { currentChanges.reset(); bufferedChanges.reset(); - releaseVisibleItems(); + releaseVisibleItems(reusableFlag); } int prevCount = itemCount; @@ -1790,6 +1819,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to) if (prevCount != itemCount) emit q->countChanged(); } while (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()); + storeFirstVisibleItemPosition(); } void QQuickItemViewPrivate::regenerate(bool orientationChanged) @@ -1876,6 +1906,7 @@ void QQuickItemViewPrivate::layout() updateSections(); layoutVisibleItems(); + storeFirstVisibleItemPosition(); int lastIndexInView = findLastIndexInView(); refill(); @@ -1903,21 +1934,25 @@ void QQuickItemViewPrivate::layout() prepareVisibleItemTransitions(); - for (auto it = releasePendingTransition.begin(); it != releasePendingTransition.end(); ) { - auto old_count = releasePendingTransition.count(); - auto success = prepareNonVisibleItemTransition(*it, viewBounds); - // prepareNonVisibleItemTransition() may invalidate iterators while in fast flicking - // invisible animating items are kicked in or out the viewPort - // use old_count to test if the abrupt erasure occurs - if (old_count > releasePendingTransition.count()) { + // We cannot use iterators here as erasing from a container invalidates them. + for (int i = 0, count = releasePendingTransition.count(); i < count;) { + auto success = prepareNonVisibleItemTransition(releasePendingTransition[i], viewBounds); + // prepareNonVisibleItemTransition() may remove items while in fast flicking. + // Invisible animating items are kicked in or out the viewPort. + // Recheck count to test if the item got removed. In that case the same index points + // to a different item now. + const int old_count = count; + count = releasePendingTransition.count(); + if (old_count > count) continue; - } + if (!success) { - releaseItem(*it); - it = releasePendingTransition.erase(it); - continue; + releaseItem(releasePendingTransition[i], reusableFlag); + releasePendingTransition.remove(i); + --count; + } else { + ++i; } - ++it; } for (int i=0; i<visibleItems.count(); i++) @@ -1960,7 +1995,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult prevFirstItemInViewPos = prevFirstItemInView->position(); prevFirstItemInViewIndex = prevFirstItemInView->index; } - qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.constFirst()->position() : 0.0; + qreal prevVisibleItemsFirstPos = visibleItems.count() ? firstVisibleItemPosition : 0.0; totalInsertionResult->visiblePos = prevFirstItemInViewPos; totalRemovalResult->visiblePos = prevFirstItemInViewPos; @@ -2006,6 +2041,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult if (!insertions.isEmpty()) { repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult); layoutVisibleItems(removals.first().index); + storeFirstVisibleItemPosition(); } } @@ -2026,6 +2062,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult if (i < insertions.count() - 1) { repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult); layoutVisibleItems(insertions[i].index); + storeFirstVisibleItemPosition(); } itemCount += insertions[i].count; } @@ -2056,9 +2093,9 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult // Whatever removed/moved items remain are no longer visible items. prepareRemoveTransitions(¤tChanges.removedItems); - for (QHash<QQmlChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin(); + for (auto it = currentChanges.removedItems.begin(); it != currentChanges.removedItems.end(); ++it) { - releaseItem(it.value()); + releaseItem(it.value(), reusableFlag); } currentChanges.removedItems.clear(); @@ -2067,7 +2104,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult if (currentItem->item && currentItem->attached) currentItem->attached->setIsCurrentItem(false); auto oldCurrentItem = currentItem; - releaseItem(currentItem); + releaseItem(currentItem, reusableFlag); currentItem = nullptr; if (oldCurrentItem) emit q->currentItemChanged(); @@ -2148,11 +2185,11 @@ void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Ch removeResult->sizeChangesAfterVisiblePos += item->size(); } if (removal.isMove()) { - currentChanges.removedItems.insert(removal.moveKey(item->index), item); + currentChanges.removedItems.replace(removal.moveKey(item->index), item); item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); } else { // track item so it is released later - currentChanges.removedItems.insertMulti(QQmlChangeSet::MoveKey(), item); + currentChanges.removedItems.insert(QQmlChangeSet::MoveKey(), item); } if (!removeResult->changedFirstItem && item == *visibleItems.constBegin()) removeResult->changedFirstItem = true; @@ -2223,15 +2260,14 @@ void QQuickItemViewPrivate::prepareVisibleItemTransitions() visibleItems[i]->prepareTransition(transitioner, viewBounds); } -void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems) +void QQuickItemViewPrivate::prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems) { if (!transitioner) return; if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true) || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) { - for (QHash<QQmlChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin(); - it != removedItems->end(); ) { + for (auto it = removedItems->begin(); it != removedItems->end(); ) { bool isRemove = it.key().moveId < 0; if (isRemove) { FxViewItem *item = *it; @@ -2274,7 +2310,7 @@ void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitiona { for (int i=0; i<releasePendingTransition.count(); i++) { if (releasePendingTransition.at(i)->transitionableItem == item) { - releaseItem(releasePendingTransition.takeAt(i)); + releaseItem(releasePendingTransition.takeAt(i), reusableFlag); return; } } @@ -2380,7 +2416,23 @@ void QQuickItemView::destroyingItem(QObject *object) } } -bool QQuickItemViewPrivate::releaseItem(FxViewItem *item) +void QQuickItemView::onItemPooled(int modelIndex, QObject *object) +{ + Q_UNUSED(modelIndex); + + if (auto *attached = d_func()->getAttachedObject(object)) + emit attached->pooled(); +} + +void QQuickItemView::onItemReused(int modelIndex, QObject *object) +{ + Q_UNUSED(modelIndex); + + if (auto *attached = d_func()->getAttachedObject(object)) + emit attached->reused(); +} + +bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag) { Q_Q(QQuickItemView); if (!item) @@ -2391,13 +2443,19 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item) QQmlInstanceModel::ReleaseFlags flags = {}; if (model && item->item) { - flags = model->release(item->item); + flags = model->release(item->item, reusableFlag); if (!flags) { // item was not destroyed, and we no longer reference it. - QQuickItemPrivate::get(item->item)->setCulled(true); + if (item->item->parentItem() == contentItem) { + // Only cull the item if its parent item is still our contentItem. + // One case where this can happen is moving an item out of one ObjectModel and into another. + QQuickItemPrivate::get(item->item)->setCulled(true); + } unrequestedItems.insert(item->item, model->indexOf(item->item, q)); } else if (flags & QQmlInstanceModel::Destroyed) { item->item->setParentItem(nullptr); + } else if (flags & QQmlInstanceModel::Pooled) { + item->setVisible(false); } } delete item; |