/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
**
**
**
+**
** $QT_END_LICENSE$
**
****************************************************************************/
pendingChanges.apply(changeSet);
int moveId = -1;
- int moveOffset;
+ int moveOffset = 0;
foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) {
itemCount -= r.count;
// Correct the positioning of the items
d->updateSections();
d->forceLayout = true;
- d->layout();
+ polish();
}
void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
{
Q_D(QQuickItemView);
d->markExtentsDirty();
+ if (isComponentComplete() && d->isValid()) {
+ d->forceLayout = true;
+ polish();
+ }
QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
}
, highlightMoveDuration(150)
, headerComponent(0), header(0), footerComponent(0), footer(0)
, minExtent(0), maxExtent(0)
- , ownModel(false), wrap(false), deferredRelease(false)
+ , ownModel(false), wrap(false)
, inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
, haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
, fillCacheBuffer(false), inRequest(false), requestedAsync(false)
Q_Q(QQuickItemView);
QQuickItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
q->setFlag(QQuickItem::ItemIsFocusScope);
- addItemChangeListener(this, Geometry);
QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
q->setFlickableDirection(QQuickFlickable::VerticalFlick);
}
{
Q_Q(QQuickItemView);
applyPendingChanges();
-
if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
if (currentItem) {
currentItem->attached->setIsCurrentItem(false);
void QQuickItemViewPrivate::refill()
{
+ qreal s = qMax(size(), qreal(0.));
if (isContentFlowReversed())
- refill(-position()-size(), -position());
+ refill(-position()-s, -position());
else
- refill(position(), position()+size());
+ refill(position(), position()+s);
}
-void QQuickItemViewPrivate::refill(qreal from, qreal to, bool doBuffer)
+void QQuickItemViewPrivate::refill(qreal from, qreal to)
{
Q_Q(QQuickItemView);
if (!isValid() || !q->isComponentComplete())
qreal bufferTo = to + buffer;
qreal fillFrom = from;
qreal fillTo = to;
- if (doBuffer && (bufferMode & BufferAfter))
- fillTo = bufferTo;
- if (doBuffer && (bufferMode & BufferBefore))
- fillFrom = bufferFrom;
-
- // Item creation and release is staggered in order to avoid
- // creating/releasing multiple items in one frame
- // while flicking (as much as possible).
- bool changed = addVisibleItems(fillFrom, fillTo, doBuffer);
+ bool added = addVisibleItems(fillFrom, fillTo, false);
+ bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
- if (!changed || deferredRelease) { // avoid destroying items in the same frame that we create
- if (removeNonVisibleItems(bufferFrom, bufferTo))
- changed = true;
- deferredRelease = false;
- } else {
- deferredRelease = true;
+ if (buffer && bufferMode != NoBuffer) {
+ if (bufferMode & BufferAfter)
+ fillTo = bufferTo;
+ if (bufferMode & BufferBefore)
+ fillFrom = bufferFrom;
+ added |= addVisibleItems(fillFrom, fillTo, true);
}
- if (changed) {
+ if (added || removed) {
markExtentsDirty();
+ updateBeginningEnd();
visibleItemsChanged();
- } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
- refill(from, to, true);
- }
-
- if (!q->isMoving() && changed) {
- fillCacheBuffer = true;
- q->polish();
}
if (prevCount != itemCount)
}
if (!applyModelChanges() && !forceLayout) {
- if (fillCacheBuffer)
+ if (fillCacheBuffer) {
+ fillCacheBuffer = false;
refill();
+ }
return;
}
forceLayout = false;
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> prevViewPos;
+ if (prevFirstVisible)
+ prevViewPos = prevFirstVisible->position();
+ qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
const QVector<QDeclarativeChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
+ const QVector<QDeclarativeChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
+ ChangeResult removalResult(prevViewPos);
+ ChangeResult insertionResult(prevViewPos);
+
+ 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;
}
- if (!removals.isEmpty())
+ if (!removals.isEmpty()) {
updateVisibleIndex();
- const QVector<QDeclarativeChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
- InsertionsResult insertResult;
- bool allInsertionsBeforeVisible = true;
+ // set positions correctly for the next insertion
+ if (!insertions.isEmpty()) {
+ repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
+ layoutVisibleItems(removals.first().index);
+ }
+ }
+ 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, &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;
- }
- }
- 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);
+ // set positions correctly for the next insertion
+ if (i < insertions.count() - 1) {
+ repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
+ layoutVisibleItems(insertions[i].index);
}
+
+ itemCount += insertions[i].count;
}
+ 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 (removedCount != prevVisibleItemsCount)
+ repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
// Whatever removed/moved items remain are no longer visible items.
for (QHash<QDeclarativeChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
currentChanges.reset();
updateSections();
- if (prevCount != itemCount)
+ if (prevItemCount != itemCount)
emit q->countChanged();
if (!visibleAffected)
return visibleAffected;
}
+bool QQuickItemViewPrivate::applyRemovalChange(const QDeclarativeChangeSet::Remove &removal, ChangeResult *removeResult, 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 (removeResult->visiblePos.isValid()) {
+ if (item->position() < removeResult->visiblePos)
+ removeResult->sizeChangesBeforeVisiblePos += item->size();
+ else
+ removeResult->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)++;
+ }
+ if (!removeResult->changedFirstItem && item == visibleItems.first())
+ removeResult->changedFirstItem = true;
+ it = visibleItems.erase(it);
+ }
+ }
+ }
+
+ if (removal.index + removal.count < visibleIndex)
+ removeResult->changeBeforeVisible -= removal.count;
+
+ return visibleAffected;
+}
+
+void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
+ qreal prevVisibleItemsFirstPos,
+ FxViewItem *prevFirstVisible,
+ ChangeResult *insertionResult,
+ ChangeResult *removalResult)
+{
+ const QDeclarativeNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
+
+ // reposition visibleItems.first() correctly so that the content y doesn't jump
+ if (visibleItems.count()) {
+ if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
+ resetFirstItemPosition(prevVisibleItemsFirstPos);
+
+ if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
+ && prevFirstVisible != *visibleItems.constBegin()) {
+ // 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 (!insertionResult->changedFirstItem)
+ resetFirstItemPosition(prevVisibleItemsFirstPos);
+
+ } else if (prevViewPos.isValid()) {
+ qreal moveForwardsBy = 0;
+ qreal moveBackwardsBy = 0;
+
+ // shift visibleItems.first() relative to the number of added/removed items
+ if (visibleItems.first()->position() > prevViewPos) {
+ moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
+ moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
+ } else if (visibleItems.first()->position() < prevViewPos) {
+ moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
+ moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
+ }
+ adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->changeBeforeVisible + removalResult->changeBeforeVisible);
+ }
+ insertionResult->reset();
+ removalResult->reset();
+ }
+}
+
/*
This may return 0 if the item is being created asynchronously.
When the item becomes available, refill() will be called and the item
return 0;
if (requestedIndex != -1 && requestedIndex != modelIndex) {
+ if (requestedItem && requestedItem->item)
+ requestedItem->item->setParentItem(0);
delete requestedItem;
requestedItem = 0;
}
item->setParentItem(q->contentItem());
QDeclarative_setParent_noEvent(item, q->contentItem());
requestedIndex = -1;
- fillCacheBuffer = false;
FxViewItem *viewItem = requestedItem;
if (!viewItem)
viewItem = newViewItem(modelIndex, item); // already in cache, so viewItem not initialized in initItem()
if (index == d->currentIndex)
d->updateCurrent(index);
d->refill();
- } else {
- d->fillCacheBuffer = true;
- polish();
}
}
}