aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickitemview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickitemview.cpp')
-rw-r--r--src/quick/items/qquickitemview.cpp458
1 files changed, 289 insertions, 169 deletions
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 21d644dad5..1518a484d3 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickitemview_p_p.h"
#include "qquickitemviewfxitem_p_p.h"
@@ -57,8 +21,6 @@ FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemVie
, view(v)
, attached(attached)
{
- if (attached) // can be null for default components (see createComponentItem)
- attached->setView(view);
}
QQuickItemViewChangeSet::QQuickItemViewChangeSet()
@@ -162,7 +124,7 @@ QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
QQuickItemView::~QQuickItemView()
{
Q_D(QQuickItemView);
- d->clear();
+ d->clear(true);
if (d->ownModel)
delete d->model;
delete d->header;
@@ -197,6 +159,10 @@ 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*)));
+ if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
+ disconnect(delegateModel, SIGNAL(itemPooled(int,QObject*)), this, SLOT(onItemPooled(int,QObject*)));
+ disconnect(delegateModel, SIGNAL(itemReused(int,QObject*)), this, SLOT(onItemReused(int,QObject*)));
+ }
}
QQmlInstanceModel *oldModel = d->model;
@@ -232,24 +198,35 @@ 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*)));
+ if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
+ connect(delegateModel, SIGNAL(itemPooled(int,QObject*)), this, SLOT(onItemPooled(int,QObject*)));
+ connect(delegateModel, 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();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner && d->transitioner->populateTransition) {
d->transitioner->setPopulateTransitionEnabled(true);
d->forceLayoutPolish();
}
+#endif
}
connect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
this, SLOT(modelUpdated(QQmlChangeSet,bool)));
+ if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
+ QObjectPrivate::connect(dataModel, &QQmlDelegateModel::delegateChanged, d, &QQuickItemViewPrivate::applyDelegateChange);
emit countChanged();
}
emit modelChanged();
+ d->moveReason = QQuickItemViewPrivate::Other;
}
QQmlComponent *QQuickItemView::delegate() const
@@ -277,22 +254,6 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate)
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
int oldCount = dataModel->count();
dataModel->setDelegate(delegate);
- if (isComponentComplete()) {
- d->releaseVisibleItems();
- d->releaseItem(d->currentItem);
- d->currentItem = nullptr;
- d->updateSectionCriteria();
- d->refill();
- d->moveReason = QQuickItemViewPrivate::SetIndex;
- d->updateCurrent(d->currentIndex);
- if (d->highlight && d->currentItem) {
- if (d->autoHighlight)
- d->resetHighlightPosition();
- d->updateTrackedItem();
- }
- d->moveReason = QQuickItemViewPrivate::Other;
- d->updateViewport();
- }
if (oldCount != dataModel->count())
emit countChanged();
}
@@ -599,6 +560,7 @@ void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
if (isComponentComplete()) {
d->updateViewport();
+ d->moveReason = QQuickItemViewPrivate::Other;
d->fixupPosition();
}
emit highlightRangeModeChanged();
@@ -621,8 +583,10 @@ void QQuickItemView::setPreferredHighlightBegin(qreal start)
d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
if (isComponentComplete()) {
d->updateViewport();
- if (!isMoving() && !isFlicking())
+ if (!isMoving() && !isFlicking()) {
+ d->moveReason = QQuickItemViewPrivate::Other;
d->fixupPosition();
+ }
}
emit preferredHighlightBeginChanged();
}
@@ -636,8 +600,10 @@ void QQuickItemView::resetPreferredHighlightBegin()
d->highlightRangeStart = 0;
if (isComponentComplete()) {
d->updateViewport();
- if (!isMoving() && !isFlicking())
+ if (!isMoving() && !isFlicking()) {
+ d->moveReason = QQuickItemViewPrivate::Other;
d->fixupPosition();
+ }
}
emit preferredHighlightBeginChanged();
}
@@ -658,8 +624,10 @@ void QQuickItemView::setPreferredHighlightEnd(qreal end)
d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
if (isComponentComplete()) {
d->updateViewport();
- if (!isMoving() && !isFlicking())
+ if (!isMoving() && !isFlicking()) {
+ d->moveReason = QQuickItemViewPrivate::Other;
d->fixupPosition();
+ }
}
emit preferredHighlightEndChanged();
}
@@ -673,8 +641,10 @@ void QQuickItemView::resetPreferredHighlightEnd()
d->highlightRangeEnd = 0;
if (isComponentComplete()) {
d->updateViewport();
- if (!isMoving() && !isFlicking())
+ if (!isMoving() && !isFlicking()) {
+ d->moveReason = QQuickItemViewPrivate::Other;
d->fixupPosition();
+ }
}
emit preferredHighlightEndChanged();
}
@@ -694,6 +664,29 @@ 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();
+}
+
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *QQuickItemView::populateTransition() const
{
Q_D(const QQuickItemView);
@@ -821,6 +814,7 @@ void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
emit displacedTransitionChanged();
}
}
+#endif
void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
{
@@ -848,7 +842,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) {
@@ -951,6 +945,13 @@ QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
return item ? item->item : nullptr;
}
+QQuickItem *QQuickItemView::itemAtIndex(int index) const
+{
+ Q_D(const QQuickItemView);
+ const FxViewItem *item = d->visibleItem(index);
+ return item ? item->item : nullptr;
+}
+
void QQuickItemView::forceLayout()
{
Q_D(QQuickItemView);
@@ -967,7 +968,7 @@ void QQuickItemViewPrivate::applyPendingChanges()
int QQuickItemViewPrivate::findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector<QQmlChangeSet::Change> &changes) const
{
- for (int i=0; i<changes.count(); i++) {
+ for (int i=0; i<changes.size(); i++) {
for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
if (changes[i].moveKey(j) == key)
return j;
@@ -1082,11 +1083,28 @@ qreal QQuickItemViewPrivate::calculatedMaxExtent() const
return maxExtent;
}
+void QQuickItemViewPrivate::applyDelegateChange()
+{
+ releaseVisibleItems(QQmlDelegateModel::NotReusable);
+ releaseCurrentItem(QQmlDelegateModel::NotReusable);
+ updateSectionCriteria();
+ refill();
+ moveReason = QQuickItemViewPrivate::SetIndex;
+ updateCurrent(currentIndex);
+ if (highlight && currentItem) {
+ if (autoHighlight)
+ resetHighlightPosition();
+ updateTrackedItem();
+ }
+ moveReason = QQuickItemViewPrivate::Other;
+ updateViewport();
+}
+
// for debugging only
void QQuickItemViewPrivate::checkVisible() const
{
int skip = 0;
- for (int i = 0; i < visibleItems.count(); ++i) {
+ for (int i = 0; i < visibleItems.size(); ++i) {
FxViewItem *item = visibleItems.at(i);
if (item->index == -1) {
++skip;
@@ -1133,11 +1151,13 @@ void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry
// don't allow item movement transitions to trigger a re-layout and
// start new transitions
bool prevInLayout = inLayout;
+#if QT_CONFIG(quick_viewtransitions)
if (!inLayout) {
FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : nullptr;
if (actualItem && actualItem->transitionRunning())
inLayout = true;
}
+#endif
updateHighlight();
inLayout = prevInLayout;
}
@@ -1150,17 +1170,20 @@ void QQuickItemView::destroyRemoved()
{
Q_D(QQuickItemView);
+#if QT_CONFIG(quick_viewtransitions)
bool hasRemoveTransition = false;
bool hasRemoveTransitionAsTarget = false;
if (d->transitioner) {
hasRemoveTransition = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false);
hasRemoveTransitionAsTarget = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true);
}
+#endif
for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
it != d->visibleItems.end();) {
FxViewItem *item = *it;
if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) {
+#if QT_CONFIG(quick_viewtransitions)
if (hasRemoveTransitionAsTarget) {
// don't remove from visibleItems until next layout()
d->runDelayedRemoveTransition = true;
@@ -1169,9 +1192,12 @@ void QQuickItemView::destroyRemoved()
} else {
if (hasRemoveTransition)
d->runDelayedRemoveTransition = true;
- d->releaseItem(item);
+#endif
+ d->releaseItem(item, d->reusableFlag);
it = d->visibleItems.erase(it);
+#if QT_CONFIG(quick_viewtransitions)
}
+#endif
} else {
++it;
}
@@ -1186,8 +1212,10 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
Q_D(QQuickItemView);
if (reset) {
cancelFlick();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
d->moveReason = QQuickItemViewPrivate::SetIndex;
d->regenerate();
if (d->highlight && d->currentItem) {
@@ -1197,8 +1225,10 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
}
d->moveReason = QQuickItemViewPrivate::Other;
emit countChanged();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner && d->transitioner->populateTransition)
d->forceLayoutPolish();
+#endif
} else {
if (d->inLayout) {
d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
@@ -1236,7 +1266,9 @@ void QQuickItemView::trackedPositionChanged()
return;
}
- if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
+ const bool needMoveToTrackHighlight = d->autoHighlight || d->highlightRange != NoHighlightRange;
+
+ if (d->moveReason == QQuickItemViewPrivate::SetIndex && needMoveToTrackHighlight) {
qreal trackedPos = d->trackedItem->position();
qreal trackedSize = d->trackedItem->size();
qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
@@ -1311,13 +1343,13 @@ void QQuickItemView::trackedPositionChanged()
}
}
-void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickItemView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickItemView);
d->markExtentsDirty();
if (isComponentComplete() && (d->isValid() || !d->visibleItems.isEmpty()))
d->forceLayoutPolish();
- QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
+ QQuickFlickable::geometryChange(newGeometry, oldGeometry);
}
qreal QQuickItemView::minYExtent() const
@@ -1338,7 +1370,7 @@ qreal QQuickItemView::maxYExtent() const
{
Q_D(const QQuickItemView);
if (d->layoutOrientation() == Qt::Horizontal)
- return height();
+ return QQuickFlickable::maxYExtent();
if (d->vData.maxExtentDirty) {
d->maxExtent = d->maxExtentForAxis(d->vData, false);
@@ -1366,7 +1398,7 @@ qreal QQuickItemView::maxXExtent() const
{
Q_D(const QQuickItemView);
if (d->layoutOrientation() == Qt::Vertical)
- return width();
+ return QQuickFlickable::maxXExtent();
if (d->hData.maxExtentDirty) {
d->maxExtent = d->maxExtentForAxis(d->hData, true);
@@ -1434,8 +1466,10 @@ void QQuickItemView::componentComplete()
d->updateFooter();
d->updateViewport();
d->setPosition(d->contentStartOffset());
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
if (d->isValid()) {
d->refill();
@@ -1463,7 +1497,6 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
, displayMarginBeginning(0), displayMarginEnd(0)
, layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
- , moveReason(Other)
, visibleIndex(0)
, currentIndex(-1), currentItem(nullptr)
, trackedItem(nullptr), requestedIndex(-1)
@@ -1472,7 +1505,9 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, highlightRangeStart(0), highlightRangeEnd(0)
, highlightMoveDuration(150)
, headerComponent(nullptr), header(nullptr), footerComponent(nullptr), footer(nullptr)
+#if QT_CONFIG(quick_viewtransitions)
, transitioner(nullptr)
+#endif
, minExtent(0), maxExtent(0)
, ownModel(false), wrap(false)
, keyNavigationEnabled(true)
@@ -1480,7 +1515,10 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
, haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
, fillCacheBuffer(false), inRequest(false)
- , runDelayedRemoveTransition(false), delegateValidated(false)
+#if QT_CONFIG(quick_viewtransitions)
+ , runDelayedRemoveTransition(false)
+#endif
+ , delegateValidated(false), isClearing(false)
{
bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
bufferPause.setLoopCount(1);
@@ -1489,9 +1527,11 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
QQuickItemViewPrivate::~QQuickItemViewPrivate()
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
transitioner->setChangeListener(nullptr);
delete transitioner;
+#endif
}
bool QQuickItemViewPrivate::isValid() const
@@ -1549,8 +1589,8 @@ int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
}
FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
- if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
- for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
+ if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.size()) {
+ for (int i = modelIndex - visibleIndex; i < visibleItems.size(); ++i) {
FxViewItem *item = visibleItems.at(i);
if (item->index == modelIndex)
return item;
@@ -1559,15 +1599,13 @@ FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
return nullptr;
}
-// should rename to firstItemInView() to avoid confusion with other "*visible*" methods
-// that don't look at the view position and size
-FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
+FxViewItem *QQuickItemViewPrivate::firstItemInView() const {
const qreal pos = isContentFlowReversed() ? -position()-size() : position();
for (FxViewItem *item : visibleItems) {
if (item->index != -1 && item->endPosition() > pos)
return item;
}
- return visibleItems.count() ? visibleItems.first() : 0;
+ return visibleItems.size() ? visibleItems.first() : 0;
}
int QQuickItemViewPrivate::findLastIndexInView() const
@@ -1586,9 +1624,9 @@ int QQuickItemViewPrivate::findLastIndexInView() const
// e.g. doing a removal animation
int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
{
- if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
+ if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.size())
return -1;
- for (int i = 0; i < visibleItems.count(); ++i) {
+ for (int i = 0; i < visibleItems.size(); ++i) {
FxViewItem *item = visibleItems.at(i);
if (item->index == modelIndex)
return i;
@@ -1615,8 +1653,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex)
if (currentItem) {
if (currentItem->attached)
currentItem->attached->setIsCurrentItem(false);
- releaseItem(currentItem);
- currentItem = nullptr;
+ releaseCurrentItem(reusableFlag);
currentIndex = modelIndex;
emit q->currentIndexChanged();
emit q->currentItemChanged();
@@ -1652,31 +1689,36 @@ 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()
+void QQuickItemViewPrivate::clear(bool onDestruction)
{
Q_Q(QQuickItemView);
+
+ isClearing = true;
+ auto cleanup = qScopeGuard([this] { isClearing = false; });
+
currentChanges.reset();
bufferedChanges.reset();
timeline.clear();
- releaseVisibleItems();
+ releaseVisibleItems(QQmlInstanceModel::NotReusable);
visibleIndex = 0;
- for (FxViewItem *item : qAsConst(releasePendingTransition)) {
+#if QT_CONFIG(quick_viewtransitions)
+ for (FxViewItem *item : std::as_const(releasePendingTransition)) {
item->releaseAfterTransition = false;
- releaseItem(item);
+ releaseItem(item, QQmlInstanceModel::NotReusable);
}
releasePendingTransition.clear();
+#endif
- auto oldCurrentItem = currentItem;
- releaseItem(currentItem);
- currentItem = nullptr;
- if (oldCurrentItem)
+ const bool hadCurrentItem = currentItem != nullptr;
+ releaseCurrentItem(QQmlDelegateModel::NotReusable);
+ if (hadCurrentItem)
emit q->currentItemChanged();
- createHighlight();
+ createHighlight(onDestruction);
trackedItem = nullptr;
if (requestedIndex >= 0) {
@@ -1719,6 +1761,8 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
Q_Q(QQuickItemView);
if (!model || !model->isValid() || !q->isComponentComplete())
return;
+ if (q->size().isEmpty() && visibleItems.isEmpty())
+ return;
if (!model->count()) {
updateHeader();
updateFooter();
@@ -1728,10 +1772,10 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
do {
bufferPause.stop();
- if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()) {
+ if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) {
currentChanges.reset();
bufferedChanges.reset();
- releaseVisibleItems();
+ releaseVisibleItems(reusableFlag);
}
int prevCount = itemCount;
@@ -1742,7 +1786,6 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
qreal fillTo = to;
bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
- bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
if (added) {
@@ -1758,6 +1801,8 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
}
}
+ bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
+
if (added || removed) {
markExtentsDirty();
updateBeginningEnd();
@@ -1770,6 +1815,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)
@@ -1814,23 +1860,27 @@ void QQuickItemViewPrivate::layout()
// viewBounds contains bounds before any add/remove/move operation to the view
QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
- if (!isValid() && !visibleItems.count()) {
+ if (!isValid() && !visibleItems.size()) {
clear();
setPosition(contentStartOffset());
updateViewport();
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
transitioner->setPopulateTransitionEnabled(false);
+#endif
inLayout = false;
return;
}
+#if QT_CONFIG(quick_viewtransitions)
if (runDelayedRemoveTransition && transitioner
&& transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
// assume that any items moving now are moving due to the remove - if they schedule
// a different transition, that will override this one anyway
- for (int i=0; i<visibleItems.count(); i++)
+ for (int i=0; i<visibleItems.size(); i++)
visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
}
+#endif
ChangeResult insertionPosChanges;
ChangeResult removalPosChanges;
@@ -1844,20 +1894,25 @@ void QQuickItemViewPrivate::layout()
}
forceLayout = false;
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
// Give the view one more chance to refill itself,
// in case its size is changed such that more delegates become visible after component completed
refill();
- for (FxViewItem *item : qAsConst(visibleItems)) {
+ for (FxViewItem *item : std::as_const(visibleItems)) {
if (!item->transitionScheduledOrRunning())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
}
}
+#endif
updateSections();
layoutVisibleItems();
+ storeFirstVisibleItemPosition();
+#if QT_CONFIG(quick_viewtransitions)
int lastIndexInView = findLastIndexInView();
+#endif
refill();
markExtentsDirty();
updateHighlight();
@@ -1872,6 +1927,7 @@ void QQuickItemViewPrivate::layout()
updateViewport();
updateUnrequestedPositions();
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner) {
// items added in the last refill() may need to be transitioned in - e.g. a remove
// causes items to slide up into view
@@ -1883,33 +1939,43 @@ 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.size(); 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.size();
+ 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++)
+ for (int i=0; i<visibleItems.size(); i++)
visibleItems[i]->startTransition(transitioner);
- for (int i=0; i<releasePendingTransition.count(); i++)
+ for (int i=0; i<releasePendingTransition.size(); i++)
releasePendingTransition[i]->startTransition(transitioner);
transitioner->setPopulateTransitionEnabled(false);
transitioner->resetTargetLists();
}
+#endif
+
+ if (!currentItem)
+ updateCurrent(currentIndex);
+#if QT_CONFIG(quick_viewtransitions)
runDelayedRemoveTransition = false;
+#endif
inLayout = false;
}
@@ -1926,29 +1992,29 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
updateUnrequestedIndexes();
- FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
+ FxViewItem *prevVisibleItemsFirst = visibleItems.size() ? *visibleItems.constBegin() : nullptr;
int prevItemCount = itemCount;
- int prevVisibleItemsCount = visibleItems.count();
+ int prevVisibleItemsCount = visibleItems.size();
bool visibleAffected = false;
bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
|| !currentChanges.pendingChanges.inserts().isEmpty();
- FxViewItem *prevFirstVisible = firstVisibleItem();
- QQmlNullableValue<qreal> prevViewPos;
- int prevFirstVisibleIndex = -1;
- if (prevFirstVisible) {
- prevViewPos = prevFirstVisible->position();
- prevFirstVisibleIndex = prevFirstVisible->index;
+ FxViewItem *prevFirstItemInView = firstItemInView();
+ QQmlNullableValue<qreal> prevFirstItemInViewPos;
+ int prevFirstItemInViewIndex = -1;
+ if (prevFirstItemInView) {
+ prevFirstItemInViewPos = prevFirstItemInView->position();
+ prevFirstItemInViewIndex = prevFirstItemInView->index;
}
- qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.constFirst()->position() : 0.0;
+ qreal prevVisibleItemsFirstPos = visibleItems.size() ? firstVisibleItemPosition : 0.0;
- totalInsertionResult->visiblePos = prevViewPos;
- totalRemovalResult->visiblePos = prevViewPos;
+ totalInsertionResult->visiblePos = prevFirstItemInViewPos;
+ totalRemovalResult->visiblePos = prevFirstItemInViewPos;
const QVector<QQmlChangeSet::Change> &removals = currentChanges.pendingChanges.removes();
const QVector<QQmlChangeSet::Change> &insertions = currentChanges.pendingChanges.inserts();
- ChangeResult insertionResult(prevViewPos);
- ChangeResult removalResult(prevViewPos);
+ ChangeResult insertionResult(prevFirstItemInViewPos);
+ ChangeResult removalResult(prevFirstItemInViewPos);
int removedCount = 0;
for (const QQmlChangeSet::Change &r : removals) {
@@ -1957,7 +2023,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
visibleAffected = true;
if (!visibleAffected && needsRefillForAddedOrRemovedIndex(r.index))
visibleAffected = true;
- const int correctedFirstVisibleIndex = prevFirstVisibleIndex - removalResult.countChangeBeforeVisible;
+ const int correctedFirstVisibleIndex = prevFirstItemInViewIndex - removalResult.countChangeBeforeVisible;
if (correctedFirstVisibleIndex >= 0 && r.index < correctedFirstVisibleIndex) {
if (r.index + r.count < correctedFirstVisibleIndex)
removalResult.countChangeBeforeVisible += r.count;
@@ -1965,6 +2031,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - r.index);
}
}
+#if QT_CONFIG(quick_viewtransitions)
if (runDelayedRemoveTransition) {
QQmlChangeSet::Change removal;
for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
@@ -1978,21 +2045,23 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
}
}
}
+#endif
*totalRemovalResult += removalResult;
if (!removals.isEmpty()) {
updateVisibleIndex();
// set positions correctly for the next insertion
if (!insertions.isEmpty()) {
- repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
+ repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
layoutVisibleItems(removals.first().index);
+ storeFirstVisibleItemPosition();
}
}
QList<FxViewItem *> newItems;
QList<MovedItem> movingIntoView;
- for (int i=0; i<insertions.count(); i++) {
+ for (int i=0; i<insertions.size(); i++) {
bool wasEmpty = visibleItems.isEmpty();
if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
visibleAffected = true;
@@ -2003,25 +2072,27 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
*totalInsertionResult += insertionResult;
// set positions correctly for the next insertion
- if (i < insertions.count() - 1) {
- repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
+ if (i < insertions.size() - 1) {
+ repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
layoutVisibleItems(insertions[i].index);
+ storeFirstVisibleItemPosition();
}
itemCount += insertions[i].count;
}
- for (FxViewItem *item : qAsConst(newItems)) {
+ for (FxViewItem *item : std::as_const(newItems)) {
if (item->attached)
item->attached->emitAdd();
}
+#if QT_CONFIG(quick_viewtransitions)
// for each item that was moved directly into the view as a result of a move(),
// find the index it was moved from in order to set its initial position, so that we
// can transition it from this "original" position to its new position in the view
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
- for (const MovedItem &m : qAsConst(movingIntoView)) {
+ for (const MovedItem &m : std::as_const(movingIntoView)) {
int fromIndex = findMoveKeyIndex(m.moveKey, removals);
if (fromIndex >= 0) {
- if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
+ if (prevFirstItemInViewIndex >= 0 && fromIndex < prevFirstItemInViewIndex)
repositionItemAt(m.item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
else
repositionItemAt(m.item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
@@ -2029,16 +2100,19 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
}
}
}
+#endif
// reposition visibleItems.first() correctly so that the content y doesn't jump
if (removedCount != prevVisibleItemsCount)
- repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
+ repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
// Whatever removed/moved items remain are no longer visible items.
+#if QT_CONFIG(quick_viewtransitions)
prepareRemoveTransitions(&currentChanges.removedItems);
- for (QHash<QQmlChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
+#endif
+ for (auto it = currentChanges.removedItems.begin();
it != currentChanges.removedItems.end(); ++it) {
- releaseItem(it.value());
+ releaseItem(it.value(), reusableFlag);
}
currentChanges.removedItems.clear();
@@ -2046,10 +2120,9 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
if (currentChanges.currentRemoved && currentItem) {
if (currentItem->item && currentItem->attached)
currentItem->attached->setIsCurrentItem(false);
- auto oldCurrentItem = currentItem;
- releaseItem(currentItem);
- currentItem = nullptr;
- if (oldCurrentItem)
+ const bool hadCurrentItem = currentItem != nullptr;
+ releaseCurrentItem(reusableFlag);
+ if (hadCurrentItem)
emit q->currentItemChanged();
}
if (!currentIndexCleared)
@@ -2074,7 +2147,7 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo
Q_Q(QQuickItemView);
bool visibleAffected = false;
- if (visibleItems.count() && removal.index + removal.count > visibleItems.constLast()->index) {
+ if (visibleItems.size() && removal.index + removal.count > visibleItems.constLast()->index) {
if (removal.index > visibleItems.constLast()->index)
removeResult->countChangeAfterVisibleItems += removal.count;
else
@@ -2092,10 +2165,12 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo
} else if (item->index >= removal.index + removal.count) {
// after removed items
item->index -= removal.count;
+#if QT_CONFIG(quick_viewtransitions)
if (removal.isMove())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
else
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
+#endif
++it;
} else {
// removed item
@@ -2128,11 +2203,13 @@ 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);
+#if QT_CONFIG(quick_viewtransitions)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
+#endif
} 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;
@@ -2152,7 +2229,7 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs
const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
// reposition visibleItems.first() correctly so that the content y doesn't jump
- if (visibleItems.count()) {
+ if (visibleItems.size()) {
if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
resetFirstItemPosition(prevVisibleItemsFirstPos);
@@ -2183,6 +2260,7 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs
}
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickItemViewPrivate::createTransitioner()
{
if (!transitioner) {
@@ -2199,19 +2277,18 @@ void QQuickItemViewPrivate::prepareVisibleItemTransitions()
// must call for every visible item to init or discard transitions
QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
- for (int i=0; i<visibleItems.count(); i++)
+ for (int i=0; i<visibleItems.size(); i++)
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;
@@ -2252,13 +2329,14 @@ bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, co
void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
{
- for (int i=0; i<releasePendingTransition.count(); i++) {
+ for (int i=0; i<releasePendingTransition.size(); i++) {
if (releasePendingTransition.at(i)->transitionableItem == item) {
- releaseItem(releasePendingTransition.takeAt(i));
+ releaseItem(releasePendingTransition.takeAt(i), reusableFlag);
return;
}
}
}
+#endif
/*
This may return 0 if the item is being created asynchronously.
@@ -2272,17 +2350,21 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc
if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous)
return nullptr;
- for (int i=0; i<releasePendingTransition.count(); i++) {
+#if QT_CONFIG(quick_viewtransitions)
+ for (int i=0; i<releasePendingTransition.size(); i++) {
if (releasePendingTransition.at(i)->index == modelIndex
&& !releasePendingTransition.at(i)->isPendingRemoval()) {
releasePendingTransition[i]->releaseAfterTransition = false;
return releasePendingTransition.takeAt(i);
}
}
+#endif
inRequest = true;
- QObject* object = model->object(modelIndex, incubationMode);
+ // The model will run this same range check internally but produce a warning and return nullptr.
+ // Since we handle this result graciously in our code, we preempt this warning by checking the range ourselves.
+ QObject* object = modelIndex < model->count() ? model->object(modelIndex, incubationMode) : nullptr;
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
if (!item) {
@@ -2360,23 +2442,47 @@ 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 || !model)
+ if (!item)
return true;
if (trackedItem == item)
trackedItem = nullptr;
item->trackGeometry(false);
- QQmlInstanceModel::ReleaseFlags flags = model->release(item->item);
- if (item->item) {
- if (flags == 0) {
+ QQmlInstanceModel::ReleaseFlags flags = {};
+ if (model && 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);
- unrequestedItems.insert(item->item, model->indexOf(item->item, q));
+ 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);
+ }
+ if (!isClearing)
+ 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;
@@ -2394,17 +2500,14 @@ QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component,
QQuickItem *item = nullptr;
if (component) {
- QQmlContext *creationContext = component->creationContext();
- QQmlContext *context = new QQmlContext(
- creationContext ? creationContext : qmlContext(q));
- QObject *nobj = component->beginCreate(context);
- if (nobj) {
- QQml_setParent_noEvent(context, nobj);
+ QQmlContext *context = component->creationContext();
+ if (!context)
+ context = qmlContext(q);
+
+ if (QObject *nobj = component->beginCreate(context)) {
item = qobject_cast<QQuickItem *>(nobj);
if (!item)
delete nobj;
- } else {
- delete context;
}
} else if (createDefault) {
item = new QQuickItem;
@@ -2414,18 +2517,35 @@ QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component,
item->setZ(zValue);
QQml_setParent_noEvent(item, q->contentItem());
item->setParentItem(q->contentItem());
+
+ initializeComponentItem(item);
}
if (component)
component->completeCreate();
return item;
}
+/*!
+ \internal
+
+ Allows derived classes to do any initialization required for \a item
+ before completeCreate() is called on it. For example, any attached
+ properties required by the item can be set.
+
+ This is similar to initItem(), but as that has logic specific to
+ delegate items, we use a separate function for non-delegates.
+*/
+void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ Q_UNUSED(item);
+}
+
void QQuickItemViewPrivate::updateTrackedItem()
{
Q_Q(QQuickItemView);
FxViewItem *item = currentItem;
if (highlight)
- item = highlight;
+ item = highlight.get();
trackedItem = item;
if (trackedItem)