diff options
Diffstat (limited to 'src/quick/items/qquickitemview.cpp')
-rw-r--r-- | src/quick/items/qquickitemview.cpp | 1126 |
1 files changed, 1091 insertions, 35 deletions
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index f09be2b54f..87a428a87c 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -40,17 +40,26 @@ ****************************************************************************/ #include "qquickitemview_p_p.h" +#include <QtQuick/private/qdeclarativetransition_p.h> QT_BEGIN_NAMESPACE FxViewItem::FxViewItem(QQuickItem *i, bool own) - : item(i), ownItem(own), index(-1) + : item(i), ownItem(own), index(-1), releaseAfterTransition(false) + , transition(0) + , nextTransitionType(FxViewItemTransitionManager::NoTransition) + , isTransitionTarget(false) + , nextTransitionToSet(false) { } FxViewItem::~FxViewItem() { + if (transition) + transition->m_item = 0; + delete transition; + if (ownItem && item) { item->setParentItem(0); item->deleteLater(); @@ -58,6 +67,273 @@ FxViewItem::~FxViewItem() } } +qreal FxViewItem::itemX() const +{ + if (nextTransitionType != FxViewItemTransitionManager::NoTransition) + return nextTransitionToSet ? nextTransitionTo.x() : item->x(); + else if (transition && transition->isActive()) + return transition->m_toPos.x(); + else + return item->x(); +} + +qreal FxViewItem::itemY() const +{ + // If item is transitioning to some pos, return that dest pos. + // If item was redirected to some new pos before the current transition finished, + // return that new pos. + if (nextTransitionType != FxViewItemTransitionManager::NoTransition) + return nextTransitionToSet ? nextTransitionTo.y() : item->y(); + else if (transition && transition->isActive()) + return transition->m_toPos.y(); + else + return item->y(); +} + +void FxViewItem::setVisible(bool visible) +{ + if (!visible && transitionScheduledOrRunning()) + return; + item->setVisible(visible); +} + +void FxViewItem::setNextTransition(FxViewItemTransitionManager::TransitionType type, bool isTargetItem) +{ + // Don't reset nextTransitionToSet - once it is set, it cannot be changed + // until the animation finishes since the itemX() and itemY() may be used + // to calculate positions for transitions for other items in the view. + nextTransitionType = type; + isTransitionTarget = isTargetItem; +} + +bool FxViewItem::transitionScheduledOrRunning() const +{ + return (transition && transition->isActive()) + || nextTransitionType != FxViewItemTransitionManager::NoTransition; +} + +bool FxViewItem::prepareTransition(const QRectF &viewBounds) +{ + bool doTransition = false; + + switch (nextTransitionType) { + case FxViewItemTransitionManager::NoTransition: + { + return false; + } + case FxViewItemTransitionManager::PopulateTransition: + { + return true; + } + case FxViewItemTransitionManager::AddTransition: + case FxViewItemTransitionManager::RemoveTransition: + // For Add targets, do transition if item is moving into visible area + // For Remove targets, do transition if item is currently in visible area + if (isTransitionTarget) { + doTransition = (nextTransitionType == FxViewItemTransitionManager::AddTransition) + ? viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height())) + : viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height())); + if (!doTransition) + item->setPos(nextTransitionTo); + } else { + if (viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height())) + || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))) { + doTransition = (nextTransitionTo != item->pos()); + } else { + item->setPos(nextTransitionTo); + } + } + break; + case FxViewItemTransitionManager::MoveTransition: + // do transition if moving from or into visible area + if (nextTransitionTo != item->pos()) { + doTransition = viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height())) + || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height())); + if (!doTransition) + item->setPos(nextTransitionTo); + } + break; + } + + if (!doTransition) + resetTransitionData(); + return doTransition; +} + +void FxViewItem::startTransition() +{ + if (nextTransitionType == FxViewItemTransitionManager::NoTransition) + return; + + if (!transition || transition->m_type != nextTransitionType || transition->m_type != isTransitionTarget) { + delete transition; + transition = new FxViewItemTransitionManager; + } + + // if item is not already moving somewhere, set it to not move anywhere + // so that removed items do not move to the default (0,0) + if (!nextTransitionToSet) + moveTo(item->pos()); + + transition->startTransition(this, nextTransitionType, nextTransitionTo, isTransitionTarget); + nextTransitionType = FxViewItemTransitionManager::NoTransition; +} + +void FxViewItem::stopTransition() +{ + if (transition) { + transition->cancel(); + delete transition; + transition = 0; + } + resetTransitionData(); + finishedTransition(); +} + +void FxViewItem::finishedTransition() +{ + nextTransitionToSet = false; + nextTransitionTo = QPointF(); + + if (releaseAfterTransition) { + QQuickItemViewPrivate *vp = static_cast<QQuickItemViewPrivate*>(QObjectPrivate::get(itemView())); + vp->releasePendingTransition.removeOne(this); + vp->releaseItem(this); + } +} + +void FxViewItem::resetTransitionData() +{ + nextTransitionType = FxViewItemTransitionManager::NoTransition; + isTransitionTarget = false; + nextTransitionTo = QPointF(); + nextTransitionToSet = false; +} + +bool FxViewItem::isPendingRemoval() const +{ + if (nextTransitionType == FxViewItemTransitionManager::RemoveTransition) + return isTransitionTarget; + if (transition && transition->isActive() && transition->m_type == FxViewItemTransitionManager::RemoveTransition) + return transition->m_isTarget; + return false; +} + +void FxViewItem::moveTo(const QPointF &pos) +{ + if (transitionScheduledOrRunning()) { + nextTransitionTo = pos; + nextTransitionToSet = true; + } else { + item->setPos(pos); + } +} + + +FxViewItemTransitionManager::FxViewItemTransitionManager() + : m_active(false), m_item(0), m_type(FxViewItemTransitionManager::NoTransition), m_isTarget(false) +{ +} + +FxViewItemTransitionManager::~FxViewItemTransitionManager() +{ +} + +bool FxViewItemTransitionManager::isActive() const +{ + return m_active; +} + +void FxViewItemTransitionManager::startTransition(FxViewItem *item, FxViewItemTransitionManager::TransitionType type, const QPointF &to, bool isTargetItem) +{ + if (!item) { + qWarning("startTransition(): invalid item"); + return; + } + + QQuickItemViewPrivate *vp = static_cast<QQuickItemViewPrivate*>(QObjectPrivate::get(item->itemView())); + + QDeclarativeTransition *trans = 0; + switch (type) { + case NoTransition: + break; + case PopulateTransition: + trans = vp->populateTransition; + break; + case AddTransition: + trans = isTargetItem ? vp->addTransition : vp->addDisplacedTransition; + break; + case MoveTransition: + trans = isTargetItem ? vp->moveTransition : vp->moveDisplacedTransition; + break; + case RemoveTransition: + trans = isTargetItem ? vp->removeTransition : vp->removeDisplacedTransition; + break; + } + + if (!trans) { + qWarning("QQuickItemView: invalid view transition!"); + return; + } + + m_active = true; + m_item = item; + m_toPos = to; + m_type = type; + m_isTarget = isTargetItem; + + QQuickViewTransitionAttached *attached = + static_cast<QQuickViewTransitionAttached*>(qmlAttachedPropertiesObject<QQuickViewTransitionAttached>(trans)); + if (attached) { + attached->m_index = item->index; + attached->m_item = item->item; + attached->m_destination = to; + switch (type) { + case NoTransition: + break; + case PopulateTransition: + case AddTransition: + attached->m_targetIndexes = vp->addTransitionIndexes; + attached->m_targetItems = vp->addTransitionTargets; + break; + case MoveTransition: + attached->m_targetIndexes = vp->moveTransitionIndexes; + attached->m_targetItems = vp->moveTransitionTargets; + break; + case RemoveTransition: + attached->m_targetIndexes = vp->removeTransitionIndexes; + attached->m_targetItems = vp->removeTransitionTargets; + break; + } + emit attached->indexChanged(); + emit attached->itemChanged(); + emit attached->destinationChanged(); + emit attached->targetIndexesChanged(); + emit attached->targetItemsChanged(); + } + + QDeclarativeStateOperation::ActionList actions; + actions << QDeclarativeAction(item->item, QLatin1String("x"), QVariant(to.x())); + actions << QDeclarativeAction(item->item, QLatin1String("y"), QVariant(to.y())); + + QDeclarativeTransitionManager::transition(actions, trans, item->item); +} + +void FxViewItemTransitionManager::finished() +{ + QDeclarativeTransitionManager::finished(); + + m_active = false; + + if (m_item) + m_item->finishedTransition(); + m_item = 0; + m_toPos.setX(0); + m_toPos.setY(0); + m_type = NoTransition; + m_isTarget = false; +} + QQuickItemViewChangeSet::QQuickItemViewChangeSet() : active(false) @@ -137,6 +413,364 @@ void QQuickItemViewChangeSet::reset() } +QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent) + : QObject(parent), m_index(-1), m_item(0) +{ +} +/*! + \qmlclass ViewTransition QQuickViewTransitionAttached + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The ViewTransition attached property provides details on items under transition in a view. + + With ListView and GridView, it is possible to specify transitions that should be applied whenever + the items in the view change as a result of modifications to the view's model. They both have the + following properties that can be set to the appropriate transitions to be run for various + operations: + + \list + \o \c add and \c addDisplaced - the transitions to run when items are added to the view + \o \c remove and \c removeDisplaced - the transitions to run when items are removed from the view + \o \c move and \c moveDisplaced - the transitions to run when items are moved within the view + (i.e. as a result of a move operation in the model) + \o \c populate - the transition to run when a view is created, or when the model changes + \endlist + + Such view transitions additionally have access to a ViewTransition attached property that + provides details of the items that are under transition and the operation that triggered the + transition. Since view transitions are run once per item, these details can be used to customise + each transition for each individual item. + + The ViewTransition attached property provides the following properties specific to the item to + which the transition is applied: + + \list + \o ViewTransition.item - the item that is under transition + \o ViewTransition.index - the index of this item + \o ViewTransition.destination - the (x,y) point to which this item is moving for the relevant view operation + \endlist + + In addition, ViewTransition provides properties specific to the items which are the target + of the operation that triggered the transition: + + \list + \o ViewTransition.targetIndexes - the indexes of the target items + \o ViewTransition.targetItems - the target items themselves + \endlist + + View transitions can be written without referring to any of the attributes listed + above. These attributes merely provide extra details that are useful for customising view + transitions. + + Following is an introduction to view transitions and the ways in which the ViewTransition + attached property can be used to augment view transitions. + + + \section2 View transitions: a simple example + + Here is a basic example of the use of view transitions. The view below specifies transitions for + the \c add and \c addDisplaced properties, which will be run when items are added to the view: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0 + + When the space key is pressed, adding an item to the model, the new item will fade in and + increase in scale over 400 milliseconds as it is added to the view. Also, any item that is + displaced by the addition of a new item will animate to its new position in the view over + 400 milliseconds, as specified by the \c addDisplaced transition. + + If five items were inserted in succession at index 0, the effect would be this: + + \image viewtransitions-basic.gif + + Notice that the NumberAnimation objects above do not need to specify a \c target to animate + the appropriate item. Also, the NumberAnimation in the \c addTransition does not need to specify + the \c to value to move the item to its correct position in the view. This is because the view + implicitly sets the \c target and \c to values with the correct item and final item position + values if these properties are not explicitly defined. + + At its simplest, a view transition may just animate an item to its new position following a + view operation, just as the \c addDisplaced transition does above, or animate some item properties, + as in the \c add transition above. Additionally, a view transition may make use of the + ViewTransition attached property to customise animation behavior for different items. Following + are some examples of how this can be achieved. + + + \section2 Using the ViewTransition attached property + + As stated, the various ViewTransition properties provide details specific to the individual item + being transitioned as well as the operation that triggered the transition. In the animation above, + five items are inserted in succession at index 0. When the fifth and final insertion takes place, + adding "Item 4" to the view, the \c add transition is run once (for the inserted item) and the + \c addDisplaced transition is run four times (once for each of the four existing items in the view). + + At this point, if we examined the \c addDisplaced transition that was run for the bottom displaced + item ("Item 0"), the ViewTransition property values provided to this transition would be as follows: + + \table + \header + \o Property + \o Value + \o Explanation + \row + \o ViewTransition.item + \o "Item 0" delegate instance + \o The "Item 0" \l Rectangle object itself + \row + \o ViewTransition.index + \o \c int value of 4 + \o The index of "Item 0" within the model following the add operation + \row + \o ViewTransition.destination + \o \l point value of (0, 120) + \o The position that "Item 0" is moving to + \row + \o ViewTransition.targetIndexes + \o \c int array, just contains the integer "0" (zero) + \o The index of "Item 4", the new item added to the view + \row + \o ViewTransition.targetItems + \o object array, just contains the "Item 4" delegate instance + \o The "Item 4" \l Rectangle object - the new item added to the view + \endtable + + The ViewTransition.targetIndexes and ViewTransition.targetItems lists provide the items and + indexes of all delegate instances that are the targets of the relevant operation. For an add + operation, these are all the items that are added into the view; for a remove, these are all + the items removed from the view, and so on. (Note these lists will only contain references to + items that have been created within the view or its cached items; targets that are not within + the visible area of the view or within the item cache will not be accessible.) + + So, while the ViewTransition.item, ViewTransition.index and ViewTransition.destination values + vary for each individual transition that is run, the ViewTransition.targetIndexes and + ViewTransition.targetItems values are the same for every \c add and \c addDisplaced transition + that is triggered by a particular add operation. + + + \section3 Delaying animations based on index + + Since each view transition is run once for each item affected by the transition, the ViewTransition + properties can be used within a transition to define custom behavior for each item's transition. + For example, the ListView in the previous example could use this information to create a ripple-type + effect on the movement of the displaced items. + + This can be achieved by modifying the \c addDisplaced transition so that it delays the animation of + each displaced item based on the difference between its index (provided by ViewTransition.index) + and the first removed index (provided by ViewTransition.targetIndexes): + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-delayedbyindex.qml 0 + + Each displaced item delays its animation by an additional 100 milliseconds, producing a subtle + ripple-type effect when items are displaced by the add, like this: + + \image viewtransitions-delayedbyindex.gif + + + \section3 Animating items to intermediate positions + + The ViewTransition.item property gives a reference to the item to which the transition is being + applied. This can be used to access any of the item's attributes, custom \c property values, + and so on. + + Below is a modification of the \c addDisplaced transition from the previous example. It adds a + ParallelAnimation with nested NumberAnimation objects that reference ViewTransition.item to access + each item's \c x and \c y values at the start of their transitions. This allows each item to + animate to an intermediate position relative to its starting point for the transition, before + animating to its final position in the view: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-intermediatemove.qml 0 + + Now, a displaced item will first move to a position of (20, 50) relative to its starting + position, and then to its final, correct position in the view: + + \image viewtransitions-intermediatemove.gif + + Since the final NumberAnimation does not specify a \c to value, the view implicitly sets this + value to the item's final position in the view, and so this last animation will move this item + to the correct place. If the transition requires the final position of the item for some calculation, + this is accessible through ViewTransition.destination. + + Instead of using multiple NumberAnimations, you could use a PathAnimation to animate an item over + a curved path. For example, the \c add transition in the previous example could be augmented with + a PathAnimation as follows: to animate newly added items along a path: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-pathanim.qml 0 + + This animates newly added items along a path. Notice that each path is specified relative to + each item's final destination point, so that items inserted at different indexes start their + paths from different positions: + + \image viewtransitions-pathanim.gif + + + \section2 Handling interrupted animations + + A view transition may be interrupted at any time if a different view transition needs to be + applied while the original transition is in progress. For example, say Item A is inserted at index 0 + and undergoes an "add" transition; then, Item B is inserted at index 0 in quick succession before + Item A's transition has finished. Since Item B is inserted before Item A, it will displace Item + A, causing the view to interrupt Item A's "add" transition mid-way and start an "addDisplaced" + transition on Item A instead. + + For simple animations that simply animate an item's movement to its final destination, this + interruption is unlikely to require additional consideration. However, if a transition changes other + properties, this interruption may cause unwanted side effects. Consider the first example on this + page, repeated below for convenience: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0 + + If multiple items are added in rapid succession, without waiting for a previous transition + to finish, this is the result: + + \image viewtransitions-interruptedbad.gif + + Each newly added item undergoes an \c add transition, but before the transition can finish, + another item is added, displacing the previously added item. Because of this, the \c add + transition on the previously added item is interrupted and an \c addDisplaced transition is + started on the item instead. Due to the interruption, the \c opacity and \c scale animations + have not completed, thus producing items with opacity and scale that are below 1.0. + + To fix this, the \c addDisplaced transition should additionally ensure the item properties are + set to the end values specified in the \c add transition, effectively resetting these values + whenever an item is displaced. In this case, it means setting the item opacity and scale to 1.0: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-interruptedgood.qml 0 + + Now, when an item's \c add transition is interrupted, its opacity and scale are animated to 1.0 + upon displacement, avoiding the erroneous visual effects from before: + + \image viewtransitions-interruptedgood.gif + + The same principle applies to any combination of view transitions. An added item may be moved + before its add transition finishes, or a moved item may be removed before its moved transition + finishes, and so on; so, the rule of thumb is that every transition should handle the same set of + properties. + + + \section2 Restrictions regarding ScriptAction + + When a view transition is initialized, any property bindings that refer to the ViewTransition + attached property are evaluated in preparation for the transition. Due to the nature of the + internal construction of a view transition, the attributes of the ViewTransition attached + property are only valid for the relevant item when the transition is initialized, and may not be + valid when the transition is actually run. + + Therefore, a ScriptAction within a view transition should not refer to the ViewTransition + attached property, as it may not refer to the expected values at the time that the ScriptAction + is actually invoked. Consider the following example: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactionbad.qml 0 + + When the space key is pressed, three items are moved from index 5 to index 1. For each moved + item, the \c moveTransition sequence presumably animates the item's color to "yellow", then + animates it to its final position, then changes the item color back to "lightsteelblue" using a + ScriptAction. However, when run, the transition does not produce the intended result: + + \image viewtransitions-scriptactionbad.gif + + Only the last moved item is returned to the "lightsteelblue" color; the others remain yellow. This + is because the ScriptAction is not run until after the transition has already been initialized, by + which time the ViewTransition.item value has changed to refer to a different item; the item that + the script had intended to refer to is not the one held by ViewTransition.item at the time the + ScriptAction is actually invoked. + + In this instance, to avoid this issue, the view could set the property using a PropertyAction + instead: + + \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactiongood.qml 0 + + When the transition is initialized, the PropertyAction \c target will be set to the respective + ViewTransition.item for the transition and will later run with the correct item target as + expected. + */ + +/*! + \qmlattachedproperty list QtQuick2::ViewTransition::index + + This attached property holds the index of the item that is being + transitioned. + + Note that if the item is being moved, this property holds the index that + the item is moving to, not from. +*/ + +/*! + \qmlattachedproperty list QtQuick2::ViewTransition::item + + This attached property holds the the item that is being transitioned. + + \warning This item should not be kept and referred to outside of the transition + as it may become invalid as the view changes. +*/ + +/*! + \qmlattachedproperty list QtQuick2::ViewTransition::destination + + This attached property holds the final destination position for the transitioned + item within the view. + + This property value is a \l point with \c x and \c y properties. +*/ + +/*! + \qmlattachedproperty list QtQuick2::ViewTransition::targetIndexes + + This attached property holds a list of the indexes of the items in view + that are the target of the relevant operation. + + The targets are the items that are the subject of the operation. For + an add operation, these are the items being added; for a remove, these + are the items being removed; for a move, these are the items being + moved. + + For example, if the transition was triggered by an insert operation + that added two items at index 1 and 2, this targetIndexes list would + have the value [1,2]. + + \note The targetIndexes list only contains the indexes of items that are actually + in view, or will be in the view once the relevant operation completes. + + \sa QtQuick2::ViewTransition::targetIndexes +*/ + +/*! + \qmlattachedproperty list QtQuick2::ViewTransition::targetItems + + This attached property holds the list of items in view that are the + target of the relevant operation. + + The targets are the items that are the subject of the operation. For + an add operation, these are the items being added; for a remove, these + are the items being removed; for a move, these are the items being + moved. + + For example, if the transition was triggered by an insert operation + that added two items at index 1 and 2, this targetItems list would + contain these two items. + + \note The targetItems list only contains items that are actually + in view, or will be in the view once the relevant operation completes. + + \warning The objects in this list should not be kept and referred to + outside of the transition as the items may become invalid. The targetItems + are only valid when the Transition is initially created; this also means + they should not be used by ScriptAction objects in the Transition, which are + not evaluated until the transition is run. + + \sa QtQuick2::ViewTransition::targetIndexes +*/ +QDeclarativeListProperty<QObject> QQuickViewTransitionAttached::targetItems() +{ + return QDeclarativeListProperty<QObject>(this, m_targetItems); +} + +QQuickViewTransitionAttached *QQuickViewTransitionAttached::qmlAttachedProperties(QObject *obj) +{ + return new QQuickViewTransitionAttached(obj); +} + + +//----------------------------------- + QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent) : QQuickFlickable(dd, parent) { @@ -232,6 +866,12 @@ void QQuickItemView::setModel(const QVariant &model) d->moveReason = QQuickItemViewPrivate::Other; } d->updateViewport(); + + if (d->populateTransition) { + d->forceLayout = true; + d->usePopulateTransition = true; + polish(); + } } connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); @@ -585,6 +1225,111 @@ void QQuickItemView::setHighlightMoveDuration(int duration) } } +QDeclarativeTransition *QQuickItemView::populateTransition() const +{ + Q_D(const QQuickItemView); + return d->populateTransition; +} + +void QQuickItemView::setPopulateTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->populateTransition != transition) { + d->populateTransition = transition; + emit populateTransitionChanged(); + } +} + +QDeclarativeTransition *QQuickItemView::addTransition() const +{ + Q_D(const QQuickItemView); + return d->addTransition; +} + +void QQuickItemView::setAddTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->addTransition != transition) { + d->addTransition = transition; + emit addTransitionChanged(); + } +} + +QDeclarativeTransition *QQuickItemView::addDisplacedTransition() const +{ + Q_D(const QQuickItemView); + return d->addDisplacedTransition; +} + +void QQuickItemView::setAddDisplacedTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->addDisplacedTransition != transition) { + d->addDisplacedTransition = transition; + emit addDisplacedTransitionChanged(); + } +} + +QDeclarativeTransition *QQuickItemView::moveTransition() const +{ + Q_D(const QQuickItemView); + return d->moveTransition; +} + +void QQuickItemView::setMoveTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->moveTransition != transition) { + d->moveTransition = transition; + emit moveTransitionChanged(); + } +} + +QDeclarativeTransition *QQuickItemView::moveDisplacedTransition() const +{ + Q_D(const QQuickItemView); + return d->moveDisplacedTransition; +} + +void QQuickItemView::setMoveDisplacedTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->moveDisplacedTransition != transition) { + d->moveDisplacedTransition = transition; + emit moveDisplacedTransitionChanged(); + } +} + +QDeclarativeTransition *QQuickItemView::removeTransition() const +{ + Q_D(const QQuickItemView); + return d->removeTransition; +} + +void QQuickItemView::setRemoveTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->removeTransition != transition) { + d->removeTransition = transition; + emit removeTransitionChanged(); + } +} + +QDeclarativeTransition *QQuickItemView::removeDisplacedTransition() const +{ + Q_D(const QQuickItemView); + return d->removeDisplacedTransition; +} + +void QQuickItemView::setRemoveDisplacedTransition(QDeclarativeTransition *transition) +{ + Q_D(QQuickItemView); + if (d->removeDisplacedTransition != transition) { + d->removeDisplacedTransition = transition; + emit removeDisplacedTransitionChanged(); + } +} + void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) { Q_Q(QQuickItemView); @@ -719,6 +1464,64 @@ void QQuickItemViewPrivate::applyPendingChanges() layout(); } +bool QQuickItemViewPrivate::hasItemTransitions() const +{ + return populateTransition + || addTransition || addDisplacedTransition + || moveTransition || moveDisplacedTransition + || removeTransition || removeDisplacedTransition; +} + +void QQuickItemViewPrivate::transitionNextReposition(FxViewItem *item, FxViewItemTransitionManager::TransitionType type, bool isTarget) +{ + switch (type) { + case FxViewItemTransitionManager::NoTransition: + return; + case FxViewItemTransitionManager::PopulateTransition: + if (populateTransition) { + item->setNextTransition(FxViewItemTransitionManager::PopulateTransition, isTarget); + return; + } + break; + case FxViewItemTransitionManager::AddTransition: + if (!usePopulateTransition) { + if ((isTarget && addTransition) || (!isTarget && addDisplacedTransition)) { + item->setNextTransition(type, isTarget); + return; + } + } + break; + case FxViewItemTransitionManager::MoveTransition: + if ((isTarget && moveTransition) || (!isTarget && moveDisplacedTransition)) { + item->setNextTransition(type, isTarget); + return; + } + break; + case FxViewItemTransitionManager::RemoveTransition: + if ((isTarget && removeTransition) || (!isTarget && removeDisplacedTransition)) { + item->setNextTransition(type, isTarget); + return; + } + break; + } + + // the requested transition type is not valid, but the item is scheduled/in another + // transition, so cancel it to allow the item to move directly to the correct pos + if (item->transitionScheduledOrRunning()) + item->stopTransition(); +} + +int QQuickItemViewPrivate::findMoveKeyIndex(QDeclarativeChangeSet::MoveKey key, const QVector<QDeclarativeChangeSet::Remove> &changes) const +{ + for (int i=0; i<changes.count(); i++) { + for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) { + if (changes[i].moveKey(j) == key) + return j; + } + } + return -1; +} + // for debugging only void QQuickItemViewPrivate::checkVisible() const { @@ -733,6 +1536,17 @@ void QQuickItemViewPrivate::checkVisible() const } } +// for debugging only +void QQuickItemViewPrivate::showVisibleItems() const +{ + qDebug() << "Visible items:"; + for (int i = 0; i < visibleItems.count(); ++i) { + qDebug() << "\t" << visibleItems[i]->index + << visibleItems[i]->item->objectName() + << visibleItems[i]->position(); + } +} + void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { Q_Q(QQuickItemView); @@ -752,8 +1566,19 @@ void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF & fixupPosition(); } - if (currentItem && currentItem->item == item) + if (currentItem && currentItem->item == item) { + // don't allow item movement transitions to trigger a re-layout and + // start new transitions + bool prevDisableLayout = disableLayout; + if (!disableLayout) { + FxViewItem *actualItem = hasItemTransitions() ? visibleItem(currentIndex) : 0; + if (actualItem && actualItem->transition && actualItem->transition->isRunning()) + disableLayout = true; + } updateHighlight(); + disableLayout = prevDisableLayout; + } + if (trackedItem && trackedItem->item == item) q->trackedPositionChanged(); } @@ -765,8 +1590,15 @@ void QQuickItemView::destroyRemoved() it != d->visibleItems.end();) { FxViewItem *item = *it; if (item->index == -1 && item->attached->delayRemove() == false) { - d->releaseItem(item); - it = d->visibleItems.erase(it); + if (d->removeTransition) { + // don't remove from visibleItems until next layout() + d->runDelayedRemoveTransition = true; + QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved())); + ++it; + } else { + d->releaseItem(item); + it = d->visibleItems.erase(it); + } } else { ++it; } @@ -782,6 +1614,7 @@ void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r { Q_D(QQuickItemView); if (reset) { + d->usePopulateTransition = true; d->moveReason = QQuickItemViewPrivate::SetIndex; d->regenerate(); if (d->highlight && d->currentItem) { @@ -790,8 +1623,11 @@ void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r d->updateTrackedItem(); } d->moveReason = QQuickItemViewPrivate::Other; - emit countChanged(); + if (d->populateTransition) { + d->forceLayout = true; + polish(); + } } else { d->currentChanges.prepare(d->currentIndex, d->itemCount); d->currentChanges.applyChanges(changeSet); @@ -1088,6 +1924,8 @@ void QQuickItemView::componentComplete() d->updateFooter(); d->updateViewport(); d->setPosition(d->contentStartOffset()); + d->usePopulateTransition = true; + if (d->isValid()) { d->refill(); d->moveReason = QQuickItemViewPrivate::SetIndex; @@ -1122,11 +1960,16 @@ QQuickItemViewPrivate::QQuickItemViewPrivate() , highlightRangeStart(0), highlightRangeEnd(0) , highlightMoveDuration(150) , headerComponent(0), header(0), footerComponent(0), footer(0) + , populateTransition(0) + , addTransition(0), addDisplacedTransition(0) + , moveTransition(0), moveDisplacedTransition(0) + , removeTransition(0), removeDisplacedTransition(0) , minExtent(0), maxExtent(0) , ownModel(false), wrap(false) - , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) + , disableLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) , fillCacheBuffer(false), inRequest(false), requestedAsync(false) + , usePopulateTransition(false), runDelayedRemoveTransition(false) { } @@ -1193,6 +2036,8 @@ FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const { return 0; } +// 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 { const qreal pos = isContentFlowReversed() ? -position()-size() : position(); for (int i = 0; i < visibleItems.count(); ++i) { @@ -1203,6 +2048,16 @@ FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const { return visibleItems.count() ? visibleItems.first() : 0; } +int QQuickItemViewPrivate::findLastIndexInView() const +{ + const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size(); + for (int i=visibleItems.count() - 1; i>=0; i--) { + if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1) + return visibleItems.at(i)->index; + } + return -1; +} + // Map a model index to visibleItems list index. // These may differ if removed items are still present in the visible list, // e.g. doing a removal animation @@ -1284,6 +2139,12 @@ void QQuickItemViewPrivate::clear() visibleItems.clear(); visibleIndex = 0; + for (int i = 0; i < releasePendingTransition.count(); ++i) { + releasePendingTransition.at(i)->releaseAfterTransition = false; + releaseItem(releasePendingTransition.at(i)); + } + releasePendingTransition.clear(); + releaseItem(currentItem); currentItem = 0; createHighlight(); @@ -1382,16 +2243,26 @@ void QQuickItemViewPrivate::updateViewport() void QQuickItemViewPrivate::layout() { Q_Q(QQuickItemView); - if (inApplyModelChanges) + if (disableLayout) return; if (!isValid() && !visibleItems.count()) { clear(); setPosition(contentStartOffset()); + usePopulateTransition = false; return; } - if (!applyModelChanges() && !forceLayout) { + if (runDelayedRemoveTransition && removeDisplacedTransition) { + // 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++) + transitionNextReposition(visibleItems[i], FxViewItemTransitionManager::RemoveTransition, false); + } + + ChangeResult insertionPosChanges; + ChangeResult removalPosChanges; + if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) { if (fillCacheBuffer) { fillCacheBuffer = false; refill(); @@ -1400,11 +2271,15 @@ void QQuickItemViewPrivate::layout() } forceLayout = false; + if (usePopulateTransition && populateTransition) { + for (int i=0; i<visibleItems.count(); i++) + transitionNextReposition(visibleItems.at(i), FxViewItemTransitionManager::PopulateTransition, true); + } layoutVisibleItems(); - refill(); + int lastIndexInView = findLastIndexInView(); + refill(); markExtentsDirty(); - updateHighlight(); if (!q->isMoving() && !q->isFlicking()) { @@ -1416,20 +2291,49 @@ void QQuickItemViewPrivate::layout() updateFooter(); updateViewport(); updateUnrequestedPositions(); + + if (hasItemTransitions()) { + // items added in the last refill() may need to be transitioned in - e.g. a remove + // causes items to slide up into view + if (moveDisplacedTransition || removeDisplacedTransition) + translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges); + + prepareVisibleItemTransitions(); + + QRectF viewBounds(0, position(), q->width(), q->height()); + for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin(); + it != releasePendingTransition.end(); ) { + FxViewItem *item = *it; + if ( (item->transition && item->transition->isActive()) + || prepareNonVisibleItemTransition(item, viewBounds)) { + ++it; + } else { + releaseItem(item); + it = releasePendingTransition.erase(it); + } + } + + for (int i=0; i<visibleItems.count(); i++) + visibleItems[i]->startTransition(); + for (int i=0; i<releasePendingTransition.count(); i++) + releasePendingTransition[i]->startTransition(); + } + usePopulateTransition = false; + runDelayedRemoveTransition = false; } -bool QQuickItemViewPrivate::applyModelChanges() +bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult) { Q_Q(QQuickItemView); - if (!q->isComponentComplete() || !currentChanges.hasPendingChanges() || inApplyModelChanges) + if (!q->isComponentComplete() || (!currentChanges.hasPendingChanges() && !runDelayedRemoveTransition) || disableLayout) return false; - inApplyModelChanges = true; + disableLayout = true; updateUnrequestedIndexes(); moveReason = QQuickItemViewPrivate::Other; - FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0; + FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0; int prevItemCount = itemCount; int prevVisibleItemsCount = visibleItems.count(); bool visibleAffected = false; @@ -1445,10 +2349,13 @@ bool QQuickItemViewPrivate::applyModelChanges() } qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0; + totalInsertionResult->visiblePos = prevViewPos; + totalRemovalResult->visiblePos = prevViewPos; + const QVector<QDeclarativeChangeSet::Remove> &removals = currentChanges.pendingChanges.removes(); const QVector<QDeclarativeChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts(); - ChangeResult removalResult(prevViewPos); ChangeResult insertionResult(prevViewPos); + ChangeResult removalResult(prevViewPos); int removedCount = 0; for (int i=0; i<removals.count(); i++) { @@ -1459,11 +2366,25 @@ bool QQuickItemViewPrivate::applyModelChanges() visibleAffected = true; if (prevFirstVisibleIndex >= 0 && removals[i].index < prevFirstVisibleIndex) { if (removals[i].index + removals[i].count < prevFirstVisibleIndex) - removalResult.changeBeforeVisible -= removals[i].count; + removalResult.countChangeBeforeVisible += removals[i].count; else - removalResult.changeBeforeVisible -= (prevFirstVisibleIndex - removals[i].index); + removalResult.countChangeBeforeVisible += (prevFirstVisibleIndex - removals[i].index); } } + if (runDelayedRemoveTransition) { + QDeclarativeChangeSet::Remove removal; + for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) { + FxViewItem *item = *it; + if (item->index == -1 && !item->attached->delayRemove()) { + removeItem(item, removal, &removalResult); + removedCount++; + it = visibleItems.erase(it); + } else { + ++it; + } + } + } + *totalRemovalResult += removalResult; if (!removals.isEmpty()) { updateVisibleIndex(); @@ -1475,31 +2396,50 @@ bool QQuickItemViewPrivate::applyModelChanges() } QList<FxViewItem *> newItems; + QList<MovedItem> movingIntoView; + for (int i=0; i<insertions.count(); i++) { bool wasEmpty = visibleItems.isEmpty(); - if (applyInsertionChange(insertions[i], &insertionResult, &newItems)) + if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView)) visibleAffected = true; if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index)) visibleAffected = true; if (wasEmpty && !visibleItems.isEmpty()) resetFirstItemPosition(); + *totalInsertionResult += insertionResult; // 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(); + // 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 (moveTransition) { + for (int i=0; i<movingIntoView.count(); i++) { + int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals); + if (fromIndex >= 0) { + if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex) + repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos); + else + repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos); + transitionNextReposition(movingIntoView[i].item, FxViewItemTransitionManager::MoveTransition, true); + } + } + } + // 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. + prepareRemoveTransitions(¤tChanges.removedItems); for (QHash<QDeclarativeChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin(); it != currentChanges.removedItems.end(); ++it) { releaseItem(it.value()); @@ -1526,7 +2466,7 @@ bool QQuickItemViewPrivate::applyModelChanges() if (!visibleAffected && viewportChanged) updateViewport(); - inApplyModelChanges = false; + disableLayout = false; return visibleAffected; } @@ -1535,6 +2475,13 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QDeclarativeChangeSet::Remo Q_Q(QQuickItemView); bool visibleAffected = false; + if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) { + if (removal.index > visibleItems.last()->index) + removeResult->countChangeAfterVisibleItems += removal.count; + else + removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index); + } + QList<FxViewItem*>::Iterator it = visibleItems.begin(); while (it != visibleItems.end()) { FxViewItem *item = *it; @@ -1546,6 +2493,10 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QDeclarativeChangeSet::Remo } else if (item->index >= removal.index + removal.count) { // after removed items item->index -= removal.count; + if (removal.isMove()) + transitionNextReposition(item, FxViewItemTransitionManager::MoveTransition, false); + else + transitionNextReposition(item, FxViewItemTransitionManager::RemoveTransition, false); ++it; } else { // removed item @@ -1558,21 +2509,9 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QDeclarativeChangeSet::Remo 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); + removeItem(item, removal, removeResult); + if (!removal.isMove()) (*removedCount)++; - } - if (!removeResult->changedFirstItem && item == visibleItems.first()) - removeResult->changedFirstItem = true; it = visibleItems.erase(it); } } @@ -1581,6 +2520,25 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QDeclarativeChangeSet::Remo return visibleAffected; } +void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QDeclarativeChangeSet::Remove &removal, ChangeResult *removeResult) +{ + 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); + transitionNextReposition(item, FxViewItemTransitionManager::MoveTransition, true); + } else { + // track item so it is released later + currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item); + } + if (!removeResult->changedFirstItem && item == *visibleItems.constBegin()) + removeResult->changedFirstItem = true; +} + void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst, qreal prevVisibleItemsFirstPos, FxViewItem *prevFirstVisible, @@ -1613,13 +2571,102 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos; moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos; } - adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->changeBeforeVisible + removalResult->changeBeforeVisible); + adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible); } insertionResult->reset(); removalResult->reset(); } } +void QQuickItemViewPrivate::prepareVisibleItemTransitions() +{ + Q_Q(QQuickItemView); + if (!hasItemTransitions()) + return; + + addTransitionIndexes.clear(); + addTransitionTargets.clear(); + moveTransitionIndexes.clear(); + moveTransitionTargets.clear(); + + QRectF viewBounds(0, position(), q->width(), q->height()); + for (int i=0; i<visibleItems.count(); i++) { + // must call for every visible item to init or discard transitions + if (!visibleItems[i]->prepareTransition(viewBounds)) + continue; + if (visibleItems[i]->isTransitionTarget) { + switch (visibleItems[i]->nextTransitionType) { + case FxViewItemTransitionManager::NoTransition: + break; + case FxViewItemTransitionManager::PopulateTransition: + case FxViewItemTransitionManager::AddTransition: + addTransitionIndexes.append(visibleItems[i]->index); + addTransitionTargets.append(visibleItems[i]->item); + break; + case FxViewItemTransitionManager::MoveTransition: + moveTransitionIndexes.append(visibleItems[i]->index); + moveTransitionTargets.append(visibleItems[i]->item); + break; + case FxViewItemTransitionManager::RemoveTransition: + // removed targets won't be in visibleItems, handle these + // in prepareNonVisibleItemTransition() + break; + } + } + } +} + +void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QDeclarativeChangeSet::MoveKey, FxViewItem *> *removedItems) +{ + if (!removeTransition && !removeDisplacedTransition) + return; + + removeTransitionIndexes.clear(); + removeTransitionTargets.clear(); + + if (removeTransition) { + for (QHash<QDeclarativeChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin(); + it != removedItems->end(); ) { + bool isRemove = it.key().moveId < 0; + if (isRemove) { + FxViewItem *item = *it; + item->releaseAfterTransition = true; + releasePendingTransition.append(item); + transitionNextReposition(item, FxViewItemTransitionManager::RemoveTransition, true); + it = removedItems->erase(it); + } else { + ++it; + } + } + } +} + +bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds) +{ + // Called for items that have been removed from visibleItems and may now be + // transitioned out of the view. This applies to items that are being directly + // removed, or moved to outside of the view, as well as those that are + // displaced to a position outside of the view due to an insert or move. + + if (item->nextTransitionType == FxViewItemTransitionManager::MoveTransition) + repositionItemAt(item, item->index, 0); + if (!item->prepareTransition(viewBounds)) + return false; + + if (item->isTransitionTarget) { + if (item->nextTransitionType == FxViewItemTransitionManager::MoveTransition) { + moveTransitionIndexes.append(item->index); + moveTransitionTargets.append(item->item); + } else if (item->nextTransitionType == FxViewItemTransitionManager::RemoveTransition) { + removeTransitionIndexes.append(item->index); + removeTransitionTargets.append(item->item); + } + } + + item->releaseAfterTransition = true; + return true; +} + /* This may return 0 if the item is being created asynchronously. When the item becomes available, refill() will be called and the item @@ -1628,6 +2675,7 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) { Q_Q(QQuickItemView); + if (requestedIndex == modelIndex && (asynchronous || requestedAsync == asynchronous)) return 0; @@ -1638,6 +2686,14 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) requestedItem = 0; } + for (int i=0; i<releasePendingTransition.count(); i++) { + if (releasePendingTransition[i]->index == modelIndex + && !releasePendingTransition[i]->isPendingRemoval()) { + releasePendingTransition[i]->releaseAfterTransition = false; + return releasePendingTransition.takeAt(i); + } + } + requestedIndex = modelIndex; requestedAsync = asynchronous; inRequest = true; |