1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickitemview_p_p.h"
43 #include <QtQuick/private/qquicktransition_p.h>
48 FxViewItem::FxViewItem(QQuickItem *i, bool own)
49 : item(i), ownItem(own), index(-1), releaseAfterTransition(false)
51 , nextTransitionType(FxViewItemTransitionManager::NoTransition)
52 , isTransitionTarget(false)
53 , nextTransitionToSet(false)
57 FxViewItem::~FxViewItem()
60 transition->m_item = 0;
63 if (ownItem && item) {
64 item->setParentItem(0);
70 qreal FxViewItem::itemX() const
72 if (nextTransitionType != FxViewItemTransitionManager::NoTransition)
73 return nextTransitionToSet ? nextTransitionTo.x() : item->x();
74 else if (transition && transition->isActive())
75 return transition->m_toPos.x();
80 qreal FxViewItem::itemY() const
82 // If item is transitioning to some pos, return that dest pos.
83 // If item was redirected to some new pos before the current transition finished,
84 // return that new pos.
85 if (nextTransitionType != FxViewItemTransitionManager::NoTransition)
86 return nextTransitionToSet ? nextTransitionTo.y() : item->y();
87 else if (transition && transition->isActive())
88 return transition->m_toPos.y();
93 void FxViewItem::setVisible(bool visible)
95 if (!visible && transitionScheduledOrRunning())
97 item->setVisible(visible);
100 void FxViewItem::setNextTransition(FxViewItemTransitionManager::TransitionType type, bool isTargetItem)
102 // Don't reset nextTransitionToSet - once it is set, it cannot be changed
103 // until the animation finishes since the itemX() and itemY() may be used
104 // to calculate positions for transitions for other items in the view.
105 nextTransitionType = type;
106 isTransitionTarget = isTargetItem;
109 bool FxViewItem::transitionScheduledOrRunning() const
111 return (transition && transition->isActive())
112 || nextTransitionType != FxViewItemTransitionManager::NoTransition;
115 bool FxViewItem::prepareTransition(const QRectF &viewBounds)
117 bool doTransition = false;
119 switch (nextTransitionType) {
120 case FxViewItemTransitionManager::NoTransition:
124 case FxViewItemTransitionManager::PopulateTransition:
128 case FxViewItemTransitionManager::AddTransition:
129 case FxViewItemTransitionManager::RemoveTransition:
130 // For Add targets, do transition if item is moving into visible area
131 // For Remove targets, do transition if item is currently in visible area
132 if (isTransitionTarget) {
133 doTransition = (nextTransitionType == FxViewItemTransitionManager::AddTransition)
134 ? viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))
135 : viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()));
137 item->setPos(nextTransitionTo);
139 if (viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()))
140 || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))) {
141 doTransition = (nextTransitionTo != item->pos());
143 item->setPos(nextTransitionTo);
147 case FxViewItemTransitionManager::MoveTransition:
148 // do transition if moving from or into visible area
149 if (nextTransitionTo != item->pos()) {
150 doTransition = viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()))
151 || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()));
153 item->setPos(nextTransitionTo);
159 resetTransitionData();
163 void FxViewItem::startTransition()
165 if (nextTransitionType == FxViewItemTransitionManager::NoTransition)
168 if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
170 transition = new FxViewItemTransitionManager;
173 // if item is not already moving somewhere, set it to not move anywhere
174 // so that removed items do not move to the default (0,0)
175 if (!nextTransitionToSet)
178 transition->startTransition(this, nextTransitionType, nextTransitionTo, isTransitionTarget);
179 nextTransitionType = FxViewItemTransitionManager::NoTransition;
182 void FxViewItem::stopTransition()
185 transition->cancel();
189 resetTransitionData();
190 finishedTransition();
193 void FxViewItem::finishedTransition()
195 nextTransitionToSet = false;
196 nextTransitionTo = QPointF();
198 if (releaseAfterTransition) {
199 QQuickItemViewPrivate *vp = static_cast<QQuickItemViewPrivate*>(QObjectPrivate::get(itemView()));
200 vp->releasePendingTransition.removeOne(this);
201 vp->releaseItem(this);
205 void FxViewItem::resetTransitionData()
207 nextTransitionType = FxViewItemTransitionManager::NoTransition;
208 isTransitionTarget = false;
209 nextTransitionTo = QPointF();
210 nextTransitionToSet = false;
213 bool FxViewItem::isPendingRemoval() const
215 if (nextTransitionType == FxViewItemTransitionManager::RemoveTransition)
216 return isTransitionTarget;
217 if (transition && transition->isActive() && transition->m_type == FxViewItemTransitionManager::RemoveTransition)
218 return transition->m_isTarget;
222 void FxViewItem::moveTo(const QPointF &pos)
224 if (transitionScheduledOrRunning()) {
225 nextTransitionTo = pos;
226 nextTransitionToSet = true;
233 FxViewItemTransitionManager::FxViewItemTransitionManager()
234 : m_active(false), m_item(0), m_type(FxViewItemTransitionManager::NoTransition), m_isTarget(false)
238 FxViewItemTransitionManager::~FxViewItemTransitionManager()
242 bool FxViewItemTransitionManager::isActive() const
247 void FxViewItemTransitionManager::startTransition(FxViewItem *item, FxViewItemTransitionManager::TransitionType type, const QPointF &to, bool isTargetItem)
250 qWarning("startTransition(): invalid item");
254 QQuickItemViewPrivate *vp = static_cast<QQuickItemViewPrivate*>(QObjectPrivate::get(item->itemView()));
256 QQuickTransition *trans = 0;
260 case PopulateTransition:
261 trans = vp->populateTransition;
264 trans = isTargetItem ? vp->addTransition : vp->addDisplacedTransition;
267 trans = isTargetItem ? vp->moveTransition : vp->moveDisplacedTransition;
269 case RemoveTransition:
270 trans = isTargetItem ? vp->removeTransition : vp->removeDisplacedTransition;
275 qWarning("QQuickItemView: invalid view transition!");
283 m_isTarget = isTargetItem;
285 QQuickViewTransitionAttached *attached =
286 static_cast<QQuickViewTransitionAttached*>(qmlAttachedPropertiesObject<QQuickViewTransitionAttached>(trans));
288 attached->m_index = item->index;
289 attached->m_item = item->item;
290 attached->m_destination = to;
294 case PopulateTransition:
296 attached->m_targetIndexes = vp->addTransitionIndexes;
297 attached->m_targetItems = vp->addTransitionTargets;
300 attached->m_targetIndexes = vp->moveTransitionIndexes;
301 attached->m_targetItems = vp->moveTransitionTargets;
303 case RemoveTransition:
304 attached->m_targetIndexes = vp->removeTransitionIndexes;
305 attached->m_targetItems = vp->removeTransitionTargets;
308 emit attached->indexChanged();
309 emit attached->itemChanged();
310 emit attached->destinationChanged();
311 emit attached->targetIndexesChanged();
312 emit attached->targetItemsChanged();
315 QQuickStateOperation::ActionList actions;
316 actions << QQuickAction(item->item, QLatin1String("x"), QVariant(to.x()));
317 actions << QQuickAction(item->item, QLatin1String("y"), QVariant(to.y()));
319 QQuickTransitionManager::transition(actions, trans, item->item);
322 void FxViewItemTransitionManager::finished()
324 QQuickTransitionManager::finished();
329 m_item->finishedTransition();
333 m_type = NoTransition;
338 QQuickItemViewChangeSet::QQuickItemViewChangeSet()
344 bool QQuickItemViewChangeSet::hasPendingChanges() const
346 return !pendingChanges.isEmpty();
349 void QQuickItemViewChangeSet::applyChanges(const QQuickChangeSet &changeSet)
351 pendingChanges.apply(changeSet);
356 foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) {
357 itemCount -= r.count;
358 if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
359 newCurrentIndex -= r.count;
360 currentChanged = true;
361 } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) {
362 // current item has been removed.
365 moveOffset = newCurrentIndex - r.index;
367 currentRemoved = true;
368 newCurrentIndex = -1;
370 newCurrentIndex = qMin(r.index, itemCount - 1);
372 currentChanged = true;
375 foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) {
377 if (itemCount && newCurrentIndex >= i.index) {
378 newCurrentIndex += i.count;
379 currentChanged = true;
380 } else if (newCurrentIndex < 0) {
382 currentChanged = true;
383 } else if (newCurrentIndex == 0 && !itemCount) {
384 // this is the first item, set the initial current index
385 currentChanged = true;
387 } else if (moveId == i.moveId) {
388 newCurrentIndex = i.index + moveOffset;
390 itemCount += i.count;
394 void QQuickItemViewChangeSet::prepare(int currentIndex, int count)
401 newCurrentIndex = currentIndex;
404 void QQuickItemViewChangeSet::reset()
407 newCurrentIndex = -1;
408 pendingChanges.clear();
409 removedItems.clear();
411 currentChanged = false;
412 currentRemoved = false;
416 QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
417 : QObject(parent), m_index(-1), m_item(0)
421 \qmlclass ViewTransition QQuickViewTransitionAttached
422 \inqmlmodule QtQuick 2
423 \ingroup qml-view-elements
424 \brief The ViewTransition attached property provides details on items under transition in a view.
426 With ListView and GridView, it is possible to specify transitions that should be applied whenever
427 the items in the view change as a result of modifications to the view's model. They both have the
428 following properties that can be set to the appropriate transitions to be run for various
432 \o \c add and \c addDisplaced - the transitions to run when items are added to the view
433 \o \c remove and \c removeDisplaced - the transitions to run when items are removed from the view
434 \o \c move and \c moveDisplaced - the transitions to run when items are moved within the view
435 (i.e. as a result of a move operation in the model)
436 \o \c populate - the transition to run when a view is created, or when the model changes
439 Such view transitions additionally have access to a ViewTransition attached property that
440 provides details of the items that are under transition and the operation that triggered the
441 transition. Since view transitions are run once per item, these details can be used to customise
442 each transition for each individual item.
444 The ViewTransition attached property provides the following properties specific to the item to
445 which the transition is applied:
448 \o ViewTransition.item - the item that is under transition
449 \o ViewTransition.index - the index of this item
450 \o ViewTransition.destination - the (x,y) point to which this item is moving for the relevant view operation
453 In addition, ViewTransition provides properties specific to the items which are the target
454 of the operation that triggered the transition:
457 \o ViewTransition.targetIndexes - the indexes of the target items
458 \o ViewTransition.targetItems - the target items themselves
461 View transitions can be written without referring to any of the attributes listed
462 above. These attributes merely provide extra details that are useful for customising view
465 Following is an introduction to view transitions and the ways in which the ViewTransition
466 attached property can be used to augment view transitions.
469 \section2 View transitions: a simple example
471 Here is a basic example of the use of view transitions. The view below specifies transitions for
472 the \c add and \c addDisplaced properties, which will be run when items are added to the view:
474 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0
476 When the space key is pressed, adding an item to the model, the new item will fade in and
477 increase in scale over 400 milliseconds as it is added to the view. Also, any item that is
478 displaced by the addition of a new item will animate to its new position in the view over
479 400 milliseconds, as specified by the \c addDisplaced transition.
481 If five items were inserted in succession at index 0, the effect would be this:
483 \image viewtransitions-basic.gif
485 Notice that the NumberAnimation objects above do not need to specify a \c target to animate
486 the appropriate item. Also, the NumberAnimation in the \c addTransition does not need to specify
487 the \c to value to move the item to its correct position in the view. This is because the view
488 implicitly sets the \c target and \c to values with the correct item and final item position
489 values if these properties are not explicitly defined.
491 At its simplest, a view transition may just animate an item to its new position following a
492 view operation, just as the \c addDisplaced transition does above, or animate some item properties,
493 as in the \c add transition above. Additionally, a view transition may make use of the
494 ViewTransition attached property to customise animation behavior for different items. Following
495 are some examples of how this can be achieved.
498 \section2 Using the ViewTransition attached property
500 As stated, the various ViewTransition properties provide details specific to the individual item
501 being transitioned as well as the operation that triggered the transition. In the animation above,
502 five items are inserted in succession at index 0. When the fifth and final insertion takes place,
503 adding "Item 4" to the view, the \c add transition is run once (for the inserted item) and the
504 \c addDisplaced transition is run four times (once for each of the four existing items in the view).
506 At this point, if we examined the \c addDisplaced transition that was run for the bottom displaced
507 item ("Item 0"), the ViewTransition property values provided to this transition would be as follows:
515 \o ViewTransition.item
516 \o "Item 0" delegate instance
517 \o The "Item 0" \l Rectangle object itself
519 \o ViewTransition.index
521 \o The index of "Item 0" within the model following the add operation
523 \o ViewTransition.destination
524 \o \l point value of (0, 120)
525 \o The position that "Item 0" is moving to
527 \o ViewTransition.targetIndexes
528 \o \c int array, just contains the integer "0" (zero)
529 \o The index of "Item 4", the new item added to the view
531 \o ViewTransition.targetItems
532 \o object array, just contains the "Item 4" delegate instance
533 \o The "Item 4" \l Rectangle object - the new item added to the view
536 The ViewTransition.targetIndexes and ViewTransition.targetItems lists provide the items and
537 indexes of all delegate instances that are the targets of the relevant operation. For an add
538 operation, these are all the items that are added into the view; for a remove, these are all
539 the items removed from the view, and so on. (Note these lists will only contain references to
540 items that have been created within the view or its cached items; targets that are not within
541 the visible area of the view or within the item cache will not be accessible.)
543 So, while the ViewTransition.item, ViewTransition.index and ViewTransition.destination values
544 vary for each individual transition that is run, the ViewTransition.targetIndexes and
545 ViewTransition.targetItems values are the same for every \c add and \c addDisplaced transition
546 that is triggered by a particular add operation.
549 \section3 Delaying animations based on index
551 Since each view transition is run once for each item affected by the transition, the ViewTransition
552 properties can be used within a transition to define custom behavior for each item's transition.
553 For example, the ListView in the previous example could use this information to create a ripple-type
554 effect on the movement of the displaced items.
556 This can be achieved by modifying the \c addDisplaced transition so that it delays the animation of
557 each displaced item based on the difference between its index (provided by ViewTransition.index)
558 and the first removed index (provided by ViewTransition.targetIndexes):
560 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-delayedbyindex.qml 0
562 Each displaced item delays its animation by an additional 100 milliseconds, producing a subtle
563 ripple-type effect when items are displaced by the add, like this:
565 \image viewtransitions-delayedbyindex.gif
568 \section3 Animating items to intermediate positions
570 The ViewTransition.item property gives a reference to the item to which the transition is being
571 applied. This can be used to access any of the item's attributes, custom \c property values,
574 Below is a modification of the \c addDisplaced transition from the previous example. It adds a
575 ParallelAnimation with nested NumberAnimation objects that reference ViewTransition.item to access
576 each item's \c x and \c y values at the start of their transitions. This allows each item to
577 animate to an intermediate position relative to its starting point for the transition, before
578 animating to its final position in the view:
580 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-intermediatemove.qml 0
582 Now, a displaced item will first move to a position of (20, 50) relative to its starting
583 position, and then to its final, correct position in the view:
585 \image viewtransitions-intermediatemove.gif
587 Since the final NumberAnimation does not specify a \c to value, the view implicitly sets this
588 value to the item's final position in the view, and so this last animation will move this item
589 to the correct place. If the transition requires the final position of the item for some calculation,
590 this is accessible through ViewTransition.destination.
592 Instead of using multiple NumberAnimations, you could use a PathAnimation to animate an item over
593 a curved path. For example, the \c add transition in the previous example could be augmented with
594 a PathAnimation as follows: to animate newly added items along a path:
596 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-pathanim.qml 0
598 This animates newly added items along a path. Notice that each path is specified relative to
599 each item's final destination point, so that items inserted at different indexes start their
600 paths from different positions:
602 \image viewtransitions-pathanim.gif
605 \section2 Handling interrupted animations
607 A view transition may be interrupted at any time if a different view transition needs to be
608 applied while the original transition is in progress. For example, say Item A is inserted at index 0
609 and undergoes an "add" transition; then, Item B is inserted at index 0 in quick succession before
610 Item A's transition has finished. Since Item B is inserted before Item A, it will displace Item
611 A, causing the view to interrupt Item A's "add" transition mid-way and start an "addDisplaced"
612 transition on Item A instead.
614 For simple animations that simply animate an item's movement to its final destination, this
615 interruption is unlikely to require additional consideration. However, if a transition changes other
616 properties, this interruption may cause unwanted side effects. Consider the first example on this
617 page, repeated below for convenience:
619 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0
621 If multiple items are added in rapid succession, without waiting for a previous transition
622 to finish, this is the result:
624 \image viewtransitions-interruptedbad.gif
626 Each newly added item undergoes an \c add transition, but before the transition can finish,
627 another item is added, displacing the previously added item. Because of this, the \c add
628 transition on the previously added item is interrupted and an \c addDisplaced transition is
629 started on the item instead. Due to the interruption, the \c opacity and \c scale animations
630 have not completed, thus producing items with opacity and scale that are below 1.0.
632 To fix this, the \c addDisplaced transition should additionally ensure the item properties are
633 set to the end values specified in the \c add transition, effectively resetting these values
634 whenever an item is displaced. In this case, it means setting the item opacity and scale to 1.0:
636 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-interruptedgood.qml 0
638 Now, when an item's \c add transition is interrupted, its opacity and scale are animated to 1.0
639 upon displacement, avoiding the erroneous visual effects from before:
641 \image viewtransitions-interruptedgood.gif
643 The same principle applies to any combination of view transitions. An added item may be moved
644 before its add transition finishes, or a moved item may be removed before its moved transition
645 finishes, and so on; so, the rule of thumb is that every transition should handle the same set of
649 \section2 Restrictions regarding ScriptAction
651 When a view transition is initialized, any property bindings that refer to the ViewTransition
652 attached property are evaluated in preparation for the transition. Due to the nature of the
653 internal construction of a view transition, the attributes of the ViewTransition attached
654 property are only valid for the relevant item when the transition is initialized, and may not be
655 valid when the transition is actually run.
657 Therefore, a ScriptAction within a view transition should not refer to the ViewTransition
658 attached property, as it may not refer to the expected values at the time that the ScriptAction
659 is actually invoked. Consider the following example:
661 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactionbad.qml 0
663 When the space key is pressed, three items are moved from index 5 to index 1. For each moved
664 item, the \c moveTransition sequence presumably animates the item's color to "yellow", then
665 animates it to its final position, then changes the item color back to "lightsteelblue" using a
666 ScriptAction. However, when run, the transition does not produce the intended result:
668 \image viewtransitions-scriptactionbad.gif
670 Only the last moved item is returned to the "lightsteelblue" color; the others remain yellow. This
671 is because the ScriptAction is not run until after the transition has already been initialized, by
672 which time the ViewTransition.item value has changed to refer to a different item; the item that
673 the script had intended to refer to is not the one held by ViewTransition.item at the time the
674 ScriptAction is actually invoked.
676 In this instance, to avoid this issue, the view could set the property using a PropertyAction
679 \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactiongood.qml 0
681 When the transition is initialized, the PropertyAction \c target will be set to the respective
682 ViewTransition.item for the transition and will later run with the correct item target as
687 \qmlattachedproperty list QtQuick2::ViewTransition::index
689 This attached property holds the index of the item that is being
692 Note that if the item is being moved, this property holds the index that
693 the item is moving to, not from.
697 \qmlattachedproperty list QtQuick2::ViewTransition::item
699 This attached property holds the the item that is being transitioned.
701 \warning This item should not be kept and referred to outside of the transition
702 as it may become invalid as the view changes.
706 \qmlattachedproperty list QtQuick2::ViewTransition::destination
708 This attached property holds the final destination position for the transitioned
709 item within the view.
711 This property value is a \l point with \c x and \c y properties.
715 \qmlattachedproperty list QtQuick2::ViewTransition::targetIndexes
717 This attached property holds a list of the indexes of the items in view
718 that are the target of the relevant operation.
720 The targets are the items that are the subject of the operation. For
721 an add operation, these are the items being added; for a remove, these
722 are the items being removed; for a move, these are the items being
725 For example, if the transition was triggered by an insert operation
726 that added two items at index 1 and 2, this targetIndexes list would
727 have the value [1,2].
729 \note The targetIndexes list only contains the indexes of items that are actually
730 in view, or will be in the view once the relevant operation completes.
732 \sa QtQuick2::ViewTransition::targetIndexes
736 \qmlattachedproperty list QtQuick2::ViewTransition::targetItems
738 This attached property holds the list of items in view that are the
739 target of the relevant operation.
741 The targets are the items that are the subject of the operation. For
742 an add operation, these are the items being added; for a remove, these
743 are the items being removed; for a move, these are the items being
746 For example, if the transition was triggered by an insert operation
747 that added two items at index 1 and 2, this targetItems list would
748 contain these two items.
750 \note The targetItems list only contains items that are actually
751 in view, or will be in the view once the relevant operation completes.
753 \warning The objects in this list should not be kept and referred to
754 outside of the transition as the items may become invalid. The targetItems
755 are only valid when the Transition is initially created; this also means
756 they should not be used by ScriptAction objects in the Transition, which are
757 not evaluated until the transition is run.
759 \sa QtQuick2::ViewTransition::targetIndexes
761 QQmlListProperty<QObject> QQuickViewTransitionAttached::targetItems()
763 return QQmlListProperty<QObject>(this, m_targetItems);
766 QQuickViewTransitionAttached *QQuickViewTransitionAttached::qmlAttachedProperties(QObject *obj)
768 return new QQuickViewTransitionAttached(obj);
772 //-----------------------------------
774 QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
775 : QQuickFlickable(dd, parent)
781 QQuickItemView::~QQuickItemView()
792 QQuickItem *QQuickItemView::currentItem() const
794 Q_D(const QQuickItemView);
797 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
798 return d->currentItem->item;
801 QVariant QQuickItemView::model() const
803 Q_D(const QQuickItemView);
804 return d->modelVariant;
807 void QQuickItemView::setModel(const QVariant &model)
810 if (d->modelVariant == model)
813 disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
814 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
815 disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
816 disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
817 disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
820 QQuickVisualModel *oldModel = d->model;
823 d->setPosition(d->contentStartOffset());
825 d->modelVariant = model;
827 QObject *object = qvariant_cast<QObject*>(model);
828 QQuickVisualModel *vim = 0;
829 if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
837 d->model = new QQuickVisualDataModel(qmlContext(this), this);
839 if (isComponentComplete())
840 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
844 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
845 dataModel->setModel(model);
849 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
850 connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
851 connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
852 connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
853 if (isComponentComplete()) {
856 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
859 d->moveReason = QQuickItemViewPrivate::SetIndex;
860 d->updateCurrent(d->currentIndex);
861 if (d->highlight && d->currentItem) {
862 if (d->autoHighlight)
863 d->resetHighlightPosition();
864 d->updateTrackedItem();
866 d->moveReason = QQuickItemViewPrivate::Other;
870 if (d->populateTransition) {
871 d->forceLayout = true;
872 d->usePopulateTransition = true;
876 connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
877 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
883 QQmlComponent *QQuickItemView::delegate() const
885 Q_D(const QQuickItemView);
887 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
888 return dataModel->delegate();
894 void QQuickItemView::setDelegate(QQmlComponent *delegate)
897 if (delegate == this->delegate())
900 d->model = new QQuickVisualDataModel(qmlContext(this));
903 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
904 int oldCount = dataModel->count();
905 dataModel->setDelegate(delegate);
906 if (isComponentComplete()) {
907 for (int i = 0; i < d->visibleItems.count(); ++i)
908 d->releaseItem(d->visibleItems.at(i));
909 d->visibleItems.clear();
910 d->releaseItem(d->currentItem);
914 d->moveReason = QQuickItemViewPrivate::SetIndex;
915 d->updateCurrent(d->currentIndex);
916 if (d->highlight && d->currentItem) {
917 if (d->autoHighlight)
918 d->resetHighlightPosition();
919 d->updateTrackedItem();
921 d->moveReason = QQuickItemViewPrivate::Other;
924 if (oldCount != dataModel->count())
927 emit delegateChanged();
931 int QQuickItemView::count() const
933 Q_D(const QQuickItemView);
936 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
937 return d->model->count();
940 int QQuickItemView::currentIndex() const
942 Q_D(const QQuickItemView);
943 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
944 return d->currentIndex;
947 void QQuickItemView::setCurrentIndex(int index)
950 if (d->requestedIndex >= 0 && !d->requestedAsync) // currently creating item
952 d->currentIndexCleared = (index == -1);
954 d->applyPendingChanges();
955 if (index == d->currentIndex)
957 if (isComponentComplete() && d->isValid()) {
958 d->moveReason = QQuickItemViewPrivate::SetIndex;
959 d->updateCurrent(index);
960 } else if (d->currentIndex != index) {
961 d->currentIndex = index;
962 emit currentIndexChanged();
967 bool QQuickItemView::isWrapEnabled() const
969 Q_D(const QQuickItemView);
973 void QQuickItemView::setWrapEnabled(bool wrap)
979 emit keyNavigationWrapsChanged();
982 int QQuickItemView::cacheBuffer() const
984 Q_D(const QQuickItemView);
988 void QQuickItemView::setCacheBuffer(int b)
991 if (d->buffer != b) {
993 if (isComponentComplete()) {
994 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
997 emit cacheBufferChanged();
1002 Qt::LayoutDirection QQuickItemView::layoutDirection() const
1004 Q_D(const QQuickItemView);
1005 return d->layoutDirection;
1008 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1010 Q_D(QQuickItemView);
1011 if (d->layoutDirection != layoutDirection) {
1012 d->layoutDirection = layoutDirection;
1014 emit layoutDirectionChanged();
1015 emit effectiveLayoutDirectionChanged();
1019 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
1021 Q_D(const QQuickItemView);
1022 if (d->effectiveLayoutMirror)
1023 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
1025 return d->layoutDirection;
1029 QQmlComponent *QQuickItemView::header() const
1031 Q_D(const QQuickItemView);
1032 return d->headerComponent;
1035 QQuickItem *QQuickItemView::headerItem() const
1037 Q_D(const QQuickItemView);
1038 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1039 return d->header ? d->header->item : 0;
1042 void QQuickItemView::setHeader(QQmlComponent *headerComponent)
1044 Q_D(QQuickItemView);
1045 if (d->headerComponent != headerComponent) {
1046 d->applyPendingChanges();
1049 d->headerComponent = headerComponent;
1051 d->markExtentsDirty();
1053 if (isComponentComplete()) {
1056 d->updateViewport();
1059 emit headerItemChanged();
1061 emit headerChanged();
1065 QQmlComponent *QQuickItemView::footer() const
1067 Q_D(const QQuickItemView);
1068 return d->footerComponent;
1071 QQuickItem *QQuickItemView::footerItem() const
1073 Q_D(const QQuickItemView);
1074 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1075 return d->footer ? d->footer->item : 0;
1078 void QQuickItemView::setFooter(QQmlComponent *footerComponent)
1080 Q_D(QQuickItemView);
1081 if (d->footerComponent != footerComponent) {
1082 d->applyPendingChanges();
1085 d->footerComponent = footerComponent;
1087 if (isComponentComplete()) {
1089 d->updateViewport();
1092 emit footerItemChanged();
1094 emit footerChanged();
1098 QQmlComponent *QQuickItemView::highlight() const
1100 Q_D(const QQuickItemView);
1101 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1102 return d->highlightComponent;
1105 void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
1107 Q_D(QQuickItemView);
1108 if (highlightComponent != d->highlightComponent) {
1109 d->applyPendingChanges();
1110 d->highlightComponent = highlightComponent;
1111 d->createHighlight();
1113 d->updateHighlight();
1114 emit highlightChanged();
1118 QQuickItem *QQuickItemView::highlightItem() const
1120 Q_D(const QQuickItemView);
1121 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1122 return d->highlight ? d->highlight->item : 0;
1125 bool QQuickItemView::highlightFollowsCurrentItem() const
1127 Q_D(const QQuickItemView);
1128 return d->autoHighlight;
1131 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
1133 Q_D(QQuickItemView);
1134 if (d->autoHighlight != autoHighlight) {
1135 d->autoHighlight = autoHighlight;
1137 d->updateHighlight();
1138 emit highlightFollowsCurrentItemChanged();
1142 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
1144 Q_D(const QQuickItemView);
1145 return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
1148 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
1150 Q_D(QQuickItemView);
1151 if (d->highlightRange == mode)
1153 d->highlightRange = mode;
1154 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1155 emit highlightRangeModeChanged();
1158 //###Possibly rename these properties, since they are very useful even without a highlight?
1159 qreal QQuickItemView::preferredHighlightBegin() const
1161 Q_D(const QQuickItemView);
1162 return d->highlightRangeStart;
1165 void QQuickItemView::setPreferredHighlightBegin(qreal start)
1167 Q_D(QQuickItemView);
1168 d->highlightRangeStartValid = true;
1169 if (d->highlightRangeStart == start)
1171 d->highlightRangeStart = start;
1172 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1173 emit preferredHighlightBeginChanged();
1176 void QQuickItemView::resetPreferredHighlightBegin()
1178 Q_D(QQuickItemView);
1179 d->highlightRangeStartValid = false;
1180 if (d->highlightRangeStart == 0)
1182 d->highlightRangeStart = 0;
1183 emit preferredHighlightBeginChanged();
1186 qreal QQuickItemView::preferredHighlightEnd() const
1188 Q_D(const QQuickItemView);
1189 return d->highlightRangeEnd;
1192 void QQuickItemView::setPreferredHighlightEnd(qreal end)
1194 Q_D(QQuickItemView);
1195 d->highlightRangeEndValid = true;
1196 if (d->highlightRangeEnd == end)
1198 d->highlightRangeEnd = end;
1199 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1200 emit preferredHighlightEndChanged();
1203 void QQuickItemView::resetPreferredHighlightEnd()
1205 Q_D(QQuickItemView);
1206 d->highlightRangeEndValid = false;
1207 if (d->highlightRangeEnd == 0)
1209 d->highlightRangeEnd = 0;
1210 emit preferredHighlightEndChanged();
1213 int QQuickItemView::highlightMoveDuration() const
1215 Q_D(const QQuickItemView);
1216 return d->highlightMoveDuration;
1219 void QQuickItemView::setHighlightMoveDuration(int duration)
1221 Q_D(QQuickItemView);
1222 if (d->highlightMoveDuration != duration) {
1223 d->highlightMoveDuration = duration;
1224 emit highlightMoveDurationChanged();
1228 QQuickTransition *QQuickItemView::populateTransition() const
1230 Q_D(const QQuickItemView);
1231 return d->populateTransition;
1234 void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
1236 Q_D(QQuickItemView);
1237 if (d->populateTransition != transition) {
1238 d->populateTransition = transition;
1239 emit populateTransitionChanged();
1243 QQuickTransition *QQuickItemView::addTransition() const
1245 Q_D(const QQuickItemView);
1246 return d->addTransition;
1249 void QQuickItemView::setAddTransition(QQuickTransition *transition)
1251 Q_D(QQuickItemView);
1252 if (d->addTransition != transition) {
1253 d->addTransition = transition;
1254 emit addTransitionChanged();
1258 QQuickTransition *QQuickItemView::addDisplacedTransition() const
1260 Q_D(const QQuickItemView);
1261 return d->addDisplacedTransition;
1264 void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
1266 Q_D(QQuickItemView);
1267 if (d->addDisplacedTransition != transition) {
1268 d->addDisplacedTransition = transition;
1269 emit addDisplacedTransitionChanged();
1273 QQuickTransition *QQuickItemView::moveTransition() const
1275 Q_D(const QQuickItemView);
1276 return d->moveTransition;
1279 void QQuickItemView::setMoveTransition(QQuickTransition *transition)
1281 Q_D(QQuickItemView);
1282 if (d->moveTransition != transition) {
1283 d->moveTransition = transition;
1284 emit moveTransitionChanged();
1288 QQuickTransition *QQuickItemView::moveDisplacedTransition() const
1290 Q_D(const QQuickItemView);
1291 return d->moveDisplacedTransition;
1294 void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
1296 Q_D(QQuickItemView);
1297 if (d->moveDisplacedTransition != transition) {
1298 d->moveDisplacedTransition = transition;
1299 emit moveDisplacedTransitionChanged();
1303 QQuickTransition *QQuickItemView::removeTransition() const
1305 Q_D(const QQuickItemView);
1306 return d->removeTransition;
1309 void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
1311 Q_D(QQuickItemView);
1312 if (d->removeTransition != transition) {
1313 d->removeTransition = transition;
1314 emit removeTransitionChanged();
1318 QQuickTransition *QQuickItemView::removeDisplacedTransition() const
1320 Q_D(const QQuickItemView);
1321 return d->removeDisplacedTransition;
1324 void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
1326 Q_D(QQuickItemView);
1327 if (d->removeDisplacedTransition != transition) {
1328 d->removeDisplacedTransition = transition;
1329 emit removeDisplacedTransitionChanged();
1333 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
1335 Q_Q(QQuickItemView);
1338 if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain)
1341 applyPendingChanges();
1342 int idx = qMax(qMin(index, model->count()-1), 0);
1344 qreal pos = isContentFlowReversed() ? -position() - size() : position();
1345 FxViewItem *item = visibleItem(idx);
1347 if (layoutOrientation() == Qt::Vertical)
1348 maxExtent = -q->maxYExtent();
1350 maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
1352 int itemPos = positionAt(idx);
1353 changedVisibleIndex(idx);
1354 // save the currently visible items in case any of them end up visible again
1355 QList<FxViewItem *> oldVisible = visibleItems;
1356 visibleItems.clear();
1357 setPosition(qMin(qreal(itemPos), maxExtent));
1358 // now release the reference to all the old visible items.
1359 for (int i = 0; i < oldVisible.count(); ++i)
1360 releaseItem(oldVisible.at(i));
1361 item = visibleItem(idx);
1364 const qreal itemPos = item->position();
1366 case QQuickItemView::Beginning:
1368 if (index < 0 && header)
1369 pos -= headerSize();
1371 case QQuickItemView::Center:
1372 pos = itemPos - (size() - item->size())/2;
1374 case QQuickItemView::End:
1375 pos = itemPos - size() + item->size();
1376 if (index >= model->count() && footer)
1377 pos += footerSize();
1379 case QQuickItemView::Visible:
1380 if (itemPos > pos + size())
1381 pos = itemPos - size() + item->size();
1382 else if (item->endPosition() <= pos)
1385 case QQuickItemView::Contain:
1386 if (item->endPosition() >= pos + size())
1387 pos = itemPos - size() + item->size();
1391 pos = qMin(pos, maxExtent);
1393 if (layoutOrientation() == Qt::Vertical)
1394 minExtent = -q->minYExtent();
1396 minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
1397 pos = qMax(pos, minExtent);
1398 moveReason = QQuickItemViewPrivate::Other;
1404 resetHighlightPosition();
1411 void QQuickItemView::positionViewAtIndex(int index, int mode)
1413 Q_D(QQuickItemView);
1414 if (!d->isValid() || index < 0 || index >= d->model->count())
1416 d->positionViewAtIndex(index, mode);
1420 void QQuickItemView::positionViewAtBeginning()
1422 Q_D(QQuickItemView);
1425 d->positionViewAtIndex(-1, Beginning);
1428 void QQuickItemView::positionViewAtEnd()
1430 Q_D(QQuickItemView);
1433 d->positionViewAtIndex(d->model->count(), End);
1436 int QQuickItemView::indexAt(qreal x, qreal y) const
1438 Q_D(const QQuickItemView);
1439 for (int i = 0; i < d->visibleItems.count(); ++i) {
1440 const FxViewItem *item = d->visibleItems.at(i);
1441 if (item->contains(x, y))
1448 QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
1450 Q_D(const QQuickItemView);
1451 for (int i = 0; i < d->visibleItems.count(); ++i) {
1452 const FxViewItem *item = d->visibleItems.at(i);
1453 if (item->contains(x, y))
1460 void QQuickItemViewPrivate::applyPendingChanges()
1462 Q_Q(QQuickItemView);
1463 if (q->isComponentComplete() && currentChanges.hasPendingChanges())
1467 bool QQuickItemViewPrivate::canTransition(FxViewItemTransitionManager::TransitionType type, bool asTarget) const
1470 case FxViewItemTransitionManager::NoTransition:
1472 case FxViewItemTransitionManager::PopulateTransition:
1473 return usePopulateTransition
1474 && populateTransition && populateTransition->enabled();
1475 case FxViewItemTransitionManager::AddTransition:
1477 return addTransition && addTransition->enabled();
1479 return addDisplacedTransition && addDisplacedTransition->enabled();
1480 case FxViewItemTransitionManager::MoveTransition:
1482 return moveTransition && moveTransition->enabled();
1484 return moveDisplacedTransition && moveDisplacedTransition->enabled();
1485 case FxViewItemTransitionManager::RemoveTransition:
1487 return removeTransition && removeTransition->enabled();
1489 return removeDisplacedTransition && removeDisplacedTransition->enabled();
1494 bool QQuickItemViewPrivate::hasItemTransitions() const
1496 return canTransition(FxViewItemTransitionManager::PopulateTransition, true)
1497 || canTransition(FxViewItemTransitionManager::AddTransition, true)
1498 || canTransition(FxViewItemTransitionManager::AddTransition, false)
1499 || canTransition(FxViewItemTransitionManager::MoveTransition, true)
1500 || canTransition(FxViewItemTransitionManager::MoveTransition, false)
1501 || canTransition(FxViewItemTransitionManager::RemoveTransition, true)
1502 || canTransition(FxViewItemTransitionManager::RemoveTransition, false);
1505 void QQuickItemViewPrivate::transitionNextReposition(FxViewItem *item, FxViewItemTransitionManager::TransitionType type, bool isTarget)
1507 bool matchedTransition = false;
1508 if (type == FxViewItemTransitionManager::AddTransition) {
1509 // don't run add transitions for added items while populating
1510 matchedTransition = !usePopulateTransition && canTransition(type, isTarget);
1512 matchedTransition = canTransition(type, isTarget);
1515 if (matchedTransition) {
1516 item->setNextTransition(type, isTarget);
1518 // the requested transition type is not valid, but the item is scheduled/in another
1519 // transition, so cancel it to allow the item to move directly to the correct pos
1520 if (item->transitionScheduledOrRunning())
1521 item->stopTransition();
1525 int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector<QQuickChangeSet::Remove> &changes) const
1527 for (int i=0; i<changes.count(); i++) {
1528 for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
1529 if (changes[i].moveKey(j) == key)
1536 // for debugging only
1537 void QQuickItemViewPrivate::checkVisible() const
1540 for (int i = 0; i < visibleItems.count(); ++i) {
1541 FxViewItem *item = visibleItems.at(i);
1542 if (item->index == -1) {
1544 } else if (item->index != visibleIndex + i - skip) {
1545 qFatal("index %d %d %d", visibleIndex, i, item->index);
1550 // for debugging only
1551 void QQuickItemViewPrivate::showVisibleItems() const
1553 qDebug() << "Visible items:";
1554 for (int i = 0; i < visibleItems.count(); ++i) {
1555 qDebug() << "\t" << visibleItems[i]->index
1556 << visibleItems[i]->item->objectName()
1557 << visibleItems[i]->position();
1561 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1563 Q_Q(QQuickItemView);
1564 QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1565 if (!q->isComponentComplete())
1568 if (header && header->item == item) {
1571 if (!q->isMoving() && !q->isFlicking())
1573 } else if (footer && footer->item == item) {
1576 if (!q->isMoving() && !q->isFlicking())
1580 if (currentItem && currentItem->item == item) {
1581 // don't allow item movement transitions to trigger a re-layout and
1582 // start new transitions
1583 bool prevDisableLayout = disableLayout;
1584 if (!disableLayout) {
1585 FxViewItem *actualItem = hasItemTransitions() ? visibleItem(currentIndex) : 0;
1586 if (actualItem && actualItem->transition && actualItem->transition->isRunning())
1587 disableLayout = true;
1590 disableLayout = prevDisableLayout;
1593 if (trackedItem && trackedItem->item == item)
1594 q->trackedPositionChanged();
1597 void QQuickItemView::destroyRemoved()
1599 Q_D(QQuickItemView);
1600 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1601 it != d->visibleItems.end();) {
1602 FxViewItem *item = *it;
1603 if (item->index == -1 && item->attached->delayRemove() == false) {
1604 if (d->canTransition(FxViewItemTransitionManager::RemoveTransition, true)) {
1605 // don't remove from visibleItems until next layout()
1606 d->runDelayedRemoveTransition = true;
1607 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1610 d->releaseItem(item);
1611 it = d->visibleItems.erase(it);
1618 // Correct the positioning of the items
1619 d->updateSections();
1620 d->forceLayout = true;
1624 void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1626 Q_D(QQuickItemView);
1628 d->usePopulateTransition = true;
1629 d->moveReason = QQuickItemViewPrivate::SetIndex;
1631 if (d->highlight && d->currentItem) {
1632 if (d->autoHighlight)
1633 d->resetHighlightPosition();
1634 d->updateTrackedItem();
1636 d->moveReason = QQuickItemViewPrivate::Other;
1637 emit countChanged();
1638 if (d->populateTransition) {
1639 d->forceLayout = true;
1643 d->currentChanges.prepare(d->currentIndex, d->itemCount);
1644 d->currentChanges.applyChanges(changeSet);
1649 void QQuickItemView::animStopped()
1651 Q_D(QQuickItemView);
1652 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1654 if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1655 d->updateHighlight();
1659 void QQuickItemView::trackedPositionChanged()
1661 Q_D(QQuickItemView);
1662 if (!d->trackedItem || !d->currentItem)
1664 if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
1665 qreal trackedPos = d->trackedItem->position();
1666 qreal trackedSize = d->trackedItem->size();
1667 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1668 qreal pos = viewPos;
1669 if (d->haveHighlightRange) {
1670 if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1671 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1672 if (trackedPos < pos + d->highlightRangeStart)
1673 pos = trackedPos - d->highlightRangeStart;
1674 if (d->highlightRange != StrictlyEnforceRange) {
1675 if (pos > d->endPosition() - d->size())
1676 pos = d->endPosition() - d->size();
1677 if (pos < d->startPosition())
1678 pos = d->startPosition();
1681 if (d->trackedItem != d->currentItem) {
1682 // also make section header visible
1683 trackedPos -= d->currentItem->sectionSize();
1684 trackedSize += d->currentItem->sectionSize();
1686 qreal trackedEndPos = d->trackedItem->endPosition();
1687 qreal toItemPos = d->currentItem->position();
1688 qreal toItemEndPos = d->currentItem->endPosition();
1689 if (d->showHeaderForIndex(d->currentIndex)) {
1690 qreal startOffset = -d->contentStartOffset();
1691 trackedPos -= startOffset;
1692 trackedEndPos -= startOffset;
1693 toItemPos -= startOffset;
1694 toItemEndPos -= startOffset;
1695 } else if (d->showFooterForIndex(d->currentIndex)) {
1696 qreal endOffset = d->footerSize();
1697 if (d->layoutOrientation() == Qt::Vertical)
1698 endOffset += d->vData.endMargin;
1699 else if (d->isContentFlowReversed())
1700 endOffset += d->hData.endMargin;
1702 endOffset += d->hData.startMargin;
1703 trackedPos += endOffset;
1704 trackedEndPos += endOffset;
1705 toItemPos += endOffset;
1706 toItemEndPos += endOffset;
1709 if (trackedEndPos >= viewPos + d->size()
1710 && toItemEndPos >= viewPos + d->size()) {
1711 if (trackedEndPos <= toItemEndPos) {
1712 pos = trackedEndPos - d->size();
1713 if (trackedSize > d->size())
1716 pos = toItemEndPos - d->size();
1717 if (d->currentItem->size() > d->size())
1718 pos = d->currentItem->position();
1721 if (trackedPos < pos && toItemPos < pos)
1722 pos = qMax(trackedPos, toItemPos);
1724 if (viewPos != pos) {
1726 d->calcVelocity = true;
1727 d->setPosition(pos);
1728 d->calcVelocity = false;
1733 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1735 Q_D(QQuickItemView);
1736 d->markExtentsDirty();
1737 if (isComponentComplete() && d->isValid()) {
1738 d->forceLayout = true;
1741 QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1745 qreal QQuickItemView::minYExtent() const
1747 Q_D(const QQuickItemView);
1748 if (d->layoutOrientation() == Qt::Horizontal)
1749 return QQuickFlickable::minYExtent();
1751 if (d->vData.minExtentDirty) {
1752 d->minExtent = d->vData.startMargin-d->startPosition();
1754 d->minExtent += d->headerSize();
1755 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1756 d->minExtent += d->highlightRangeStart;
1757 if (d->visibleItem(0))
1758 d->minExtent -= d->visibleItem(0)->sectionSize();
1759 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
1761 d->vData.minExtentDirty = false;
1764 return d->minExtent;
1767 qreal QQuickItemView::maxYExtent() const
1769 Q_D(const QQuickItemView);
1770 if (d->layoutOrientation() == Qt::Horizontal)
1773 if (d->vData.maxExtentDirty) {
1774 if (!d->model || !d->model->count()) {
1775 d->maxExtent = d->header ? -d->headerSize() : 0;
1776 d->maxExtent += height();
1777 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1778 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
1779 if (d->highlightRangeEnd != d->highlightRangeStart)
1780 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
1782 d->maxExtent = -(d->endPosition() - height());
1786 d->maxExtent -= d->footerSize();
1787 d->maxExtent -= d->vData.endMargin;
1788 qreal minY = minYExtent();
1789 if (d->maxExtent > minY)
1790 d->maxExtent = minY;
1791 d->vData.maxExtentDirty = false;
1793 return d->maxExtent;
1796 qreal QQuickItemView::minXExtent() const
1798 Q_D(const QQuickItemView);
1799 if (d->layoutOrientation() == Qt::Vertical)
1800 return QQuickFlickable::minXExtent();
1802 if (d->hData.minExtentDirty) {
1803 d->minExtent = -d->startPosition();
1804 qreal highlightStart;
1806 qreal endPositionFirstItem = 0;
1807 if (d->isContentFlowReversed()) {
1808 d->minExtent += d->hData.endMargin;
1809 if (d->model && d->model->count())
1810 endPositionFirstItem = d->positionAt(d->model->count()-1);
1812 d->minExtent += d->headerSize();
1813 highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
1814 highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
1816 d->minExtent += d->footerSize();
1817 qreal maxX = maxXExtent();
1818 if (d->minExtent < maxX)
1819 d->minExtent = maxX;
1821 d->minExtent += d->hData.startMargin;
1822 endPositionFirstItem = d->endPositionAt(0);
1823 highlightStart = d->highlightRangeStart;
1824 highlightEnd = d->highlightRangeEnd;
1826 d->minExtent += d->headerSize();
1828 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1829 d->minExtent += highlightStart;
1830 d->minExtent = d->isContentFlowReversed()
1831 ? qMin(d->minExtent, endPositionFirstItem + highlightEnd)
1832 : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
1834 d->hData.minExtentDirty = false;
1837 return d->minExtent;
1840 qreal QQuickItemView::maxXExtent() const
1842 Q_D(const QQuickItemView);
1843 if (d->layoutOrientation() == Qt::Vertical)
1846 if (d->hData.maxExtentDirty) {
1847 qreal highlightStart;
1849 qreal lastItemPosition = 0;
1851 if (d->isContentFlowReversed()) {
1852 highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
1853 highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
1854 lastItemPosition = d->endPosition();
1856 highlightStart = d->highlightRangeStart;
1857 highlightEnd = d->highlightRangeEnd;
1858 if (d->model && d->model->count())
1859 lastItemPosition = d->positionAt(d->model->count()-1);
1861 if (!d->model || !d->model->count()) {
1862 if (!d->isContentFlowReversed())
1863 d->maxExtent = d->header ? -d->headerSize() : 0;
1864 d->maxExtent += width();
1865 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1866 d->maxExtent = -(lastItemPosition - highlightStart);
1867 if (highlightEnd != highlightStart) {
1868 d->maxExtent = d->isContentFlowReversed()
1869 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
1870 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
1873 d->maxExtent = -(d->endPosition() - width());
1875 if (d->isContentFlowReversed()) {
1877 d->maxExtent -= d->headerSize();
1878 d->maxExtent -= d->hData.startMargin;
1881 d->maxExtent -= d->footerSize();
1882 d->maxExtent -= d->hData.endMargin;
1883 qreal minX = minXExtent();
1884 if (d->maxExtent > minX)
1885 d->maxExtent = minX;
1887 d->hData.maxExtentDirty = false;
1890 return d->maxExtent;
1893 void QQuickItemView::setContentX(qreal pos)
1895 Q_D(QQuickItemView);
1896 // Positioning the view manually should override any current movement state
1897 d->moveReason = QQuickItemViewPrivate::Other;
1898 QQuickFlickable::setContentX(pos);
1901 void QQuickItemView::setContentY(qreal pos)
1903 Q_D(QQuickItemView);
1904 // Positioning the view manually should override any current movement state
1905 d->moveReason = QQuickItemViewPrivate::Other;
1906 QQuickFlickable::setContentY(pos);
1909 qreal QQuickItemView::xOrigin() const
1911 Q_D(const QQuickItemView);
1912 if (d->isContentFlowReversed())
1913 return -maxXExtent() + d->size() - d->hData.startMargin;
1915 return -minXExtent() + d->hData.startMargin;
1918 void QQuickItemView::updatePolish()
1920 Q_D(QQuickItemView);
1921 QQuickFlickable::updatePolish();
1925 void QQuickItemView::componentComplete()
1927 Q_D(QQuickItemView);
1928 if (d->model && d->ownModel)
1929 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1931 QQuickFlickable::componentComplete();
1936 d->updateViewport();
1937 d->setPosition(d->contentStartOffset());
1938 d->usePopulateTransition = true;
1942 d->moveReason = QQuickItemViewPrivate::SetIndex;
1943 if (d->currentIndex < 0 && !d->currentIndexCleared)
1944 d->updateCurrent(0);
1946 d->updateCurrent(d->currentIndex);
1947 if (d->highlight && d->currentItem) {
1948 if (d->autoHighlight)
1949 d->resetHighlightPosition();
1950 d->updateTrackedItem();
1952 d->moveReason = QQuickItemViewPrivate::Other;
1955 if (d->model && d->model->count())
1956 emit countChanged();
1961 QQuickItemViewPrivate::QQuickItemViewPrivate()
1963 , buffer(0), bufferMode(BufferBefore | BufferAfter)
1964 , layoutDirection(Qt::LeftToRight)
1967 , currentIndex(-1), currentItem(0)
1968 , trackedItem(0), requestedIndex(-1), requestedItem(0)
1969 , highlightComponent(0), highlight(0)
1970 , highlightRange(QQuickItemView::NoHighlightRange)
1971 , highlightRangeStart(0), highlightRangeEnd(0)
1972 , highlightMoveDuration(150)
1973 , headerComponent(0), header(0), footerComponent(0), footer(0)
1974 , populateTransition(0)
1975 , addTransition(0), addDisplacedTransition(0)
1976 , moveTransition(0), moveDisplacedTransition(0)
1977 , removeTransition(0), removeDisplacedTransition(0)
1978 , minExtent(0), maxExtent(0)
1979 , ownModel(false), wrap(false)
1980 , disableLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1981 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1982 , fillCacheBuffer(false), inRequest(false), requestedAsync(false)
1983 , usePopulateTransition(false), runDelayedRemoveTransition(false)
1987 bool QQuickItemViewPrivate::isValid() const
1989 return model && model->count() && model->isValid();
1992 qreal QQuickItemViewPrivate::position() const
1994 Q_Q(const QQuickItemView);
1995 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1998 qreal QQuickItemViewPrivate::size() const
2000 Q_Q(const QQuickItemView);
2001 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
2004 qreal QQuickItemViewPrivate::startPosition() const
2006 return isContentFlowReversed() ? -lastPosition() : originPosition();
2009 qreal QQuickItemViewPrivate::endPosition() const
2011 return isContentFlowReversed() ? -originPosition() : lastPosition();
2014 qreal QQuickItemViewPrivate::contentStartOffset() const
2016 qreal pos = -headerSize();
2017 if (layoutOrientation() == Qt::Vertical)
2018 pos -= vData.startMargin;
2019 else if (isContentFlowReversed())
2020 pos -= hData.endMargin;
2022 pos -= hData.startMargin;
2027 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
2029 if (visibleItems.count()) {
2030 int i = visibleItems.count() - 1;
2031 while (i > 0 && visibleItems.at(i)->index == -1)
2033 if (visibleItems.at(i)->index != -1)
2034 return visibleItems.at(i)->index;
2036 return defaultValue;
2039 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
2040 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
2041 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
2042 FxViewItem *item = visibleItems.at(i);
2043 if (item->index == modelIndex)
2050 // should rename to firstItemInView() to avoid confusion with other "*visible*" methods
2051 // that don't look at the view position and size
2052 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
2053 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
2054 for (int i = 0; i < visibleItems.count(); ++i) {
2055 FxViewItem *item = visibleItems.at(i);
2056 if (item->index != -1 && item->endPosition() > pos)
2059 return visibleItems.count() ? visibleItems.first() : 0;
2062 int QQuickItemViewPrivate::findLastIndexInView() const
2064 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2065 for (int i=visibleItems.count() - 1; i>=0; i--) {
2066 if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
2067 return visibleItems.at(i)->index;
2072 // Map a model index to visibleItems list index.
2073 // These may differ if removed items are still present in the visible list,
2074 // e.g. doing a removal animation
2075 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
2077 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
2079 for (int i = 0; i < visibleItems.count(); ++i) {
2080 FxViewItem *item = visibleItems.at(i);
2081 if (item->index == modelIndex)
2083 if (item->index > modelIndex)
2086 return -1; // Not in visibleList
2089 void QQuickItemViewPrivate::init()
2091 Q_Q(QQuickItemView);
2092 QQuickItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
2093 q->setFlag(QQuickItem::ItemIsFocusScope);
2094 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
2095 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
2098 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
2100 Q_Q(QQuickItemView);
2101 applyPendingChanges();
2102 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
2104 currentItem->attached->setIsCurrentItem(false);
2105 releaseItem(currentItem);
2107 currentIndex = modelIndex;
2108 emit q->currentIndexChanged();
2109 emit q->currentItemChanged();
2111 } else if (currentIndex != modelIndex) {
2112 currentIndex = modelIndex;
2113 emit q->currentIndexChanged();
2118 if (currentItem && currentIndex == modelIndex) {
2123 FxViewItem *oldCurrentItem = currentItem;
2124 int oldCurrentIndex = currentIndex;
2125 currentIndex = modelIndex;
2126 currentItem = createItem(modelIndex, false);
2127 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
2128 oldCurrentItem->attached->setIsCurrentItem(false);
2130 currentItem->item->setFocus(true);
2131 currentItem->attached->setIsCurrentItem(true);
2132 initializeCurrentItem();
2136 if (oldCurrentIndex != currentIndex)
2137 emit q->currentIndexChanged();
2138 if (oldCurrentItem != currentItem)
2139 emit q->currentItemChanged();
2140 releaseItem(oldCurrentItem);
2143 void QQuickItemViewPrivate::clear()
2145 currentChanges.reset();
2148 for (int i = 0; i < visibleItems.count(); ++i)
2149 releaseItem(visibleItems.at(i));
2150 visibleItems.clear();
2153 for (int i = 0; i < releasePendingTransition.count(); ++i) {
2154 releasePendingTransition.at(i)->releaseAfterTransition = false;
2155 releaseItem(releasePendingTransition.at(i));
2157 releasePendingTransition.clear();
2159 releaseItem(currentItem);
2169 void QQuickItemViewPrivate::mirrorChange()
2171 Q_Q(QQuickItemView);
2173 emit q->effectiveLayoutDirectionChanged();
2176 void QQuickItemViewPrivate::refill()
2178 qreal s = qMax(size(), qreal(0.));
2179 if (isContentFlowReversed())
2180 refill(-position()-s, -position());
2182 refill(position(), position()+s);
2185 void QQuickItemViewPrivate::refill(qreal from, qreal to)
2187 Q_Q(QQuickItemView);
2188 if (!isValid() || !q->isComponentComplete())
2191 currentChanges.reset();
2193 int prevCount = itemCount;
2194 itemCount = model->count();
2195 qreal bufferFrom = from - buffer;
2196 qreal bufferTo = to + buffer;
2197 qreal fillFrom = from;
2200 bool added = addVisibleItems(fillFrom, fillTo, false);
2201 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
2203 if (buffer && bufferMode != NoBuffer) {
2204 if (bufferMode & BufferAfter)
2206 if (bufferMode & BufferBefore)
2207 fillFrom = bufferFrom;
2208 added |= addVisibleItems(fillFrom, fillTo, true);
2211 if (added || removed) {
2213 updateBeginningEnd();
2214 visibleItemsChanged();
2220 if (prevCount != itemCount)
2221 emit q->countChanged();
2224 void QQuickItemViewPrivate::regenerate()
2226 Q_Q(QQuickItemView);
2227 if (q->isComponentComplete()) {
2228 currentChanges.reset();
2237 setPosition(contentStartOffset());
2239 updateCurrent(currentIndex);
2243 void QQuickItemViewPrivate::updateViewport()
2245 Q_Q(QQuickItemView);
2247 if (layoutOrientation() == Qt::Vertical)
2248 q->setContentHeight(endPosition() - startPosition());
2250 q->setContentWidth(endPosition() - startPosition());
2254 void QQuickItemViewPrivate::layout()
2256 Q_Q(QQuickItemView);
2260 if (!isValid() && !visibleItems.count()) {
2262 setPosition(contentStartOffset());
2263 usePopulateTransition = false;
2267 if (runDelayedRemoveTransition && canTransition(FxViewItemTransitionManager::RemoveTransition, false)) {
2268 // assume that any items moving now are moving due to the remove - if they schedule
2269 // a different transition, that will override this one anyway
2270 for (int i=0; i<visibleItems.count(); i++)
2271 transitionNextReposition(visibleItems[i], FxViewItemTransitionManager::RemoveTransition, false);
2274 ChangeResult insertionPosChanges;
2275 ChangeResult removalPosChanges;
2276 if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
2277 if (fillCacheBuffer) {
2278 fillCacheBuffer = false;
2283 forceLayout = false;
2285 if (canTransition(FxViewItemTransitionManager::PopulateTransition, true)) {
2286 for (int i=0; i<visibleItems.count(); i++)
2287 transitionNextReposition(visibleItems.at(i), FxViewItemTransitionManager::PopulateTransition, true);
2289 layoutVisibleItems();
2291 int lastIndexInView = findLastIndexInView();
2296 if (!q->isMoving() && !q->isFlicking()) {
2304 updateUnrequestedPositions();
2306 if (hasItemTransitions()) {
2307 // items added in the last refill() may need to be transitioned in - e.g. a remove
2308 // causes items to slide up into view
2309 if (canTransition(FxViewItemTransitionManager::MoveTransition, false)
2310 || canTransition(FxViewItemTransitionManager::RemoveTransition, false)) {
2311 translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
2314 prepareVisibleItemTransitions();
2316 QRectF viewBounds(0, position(), q->width(), q->height());
2317 for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin();
2318 it != releasePendingTransition.end(); ) {
2319 FxViewItem *item = *it;
2320 if ( (item->transition && item->transition->isActive())
2321 || prepareNonVisibleItemTransition(item, viewBounds)) {
2325 it = releasePendingTransition.erase(it);
2329 for (int i=0; i<visibleItems.count(); i++)
2330 visibleItems[i]->startTransition();
2331 for (int i=0; i<releasePendingTransition.count(); i++)
2332 releasePendingTransition[i]->startTransition();
2334 usePopulateTransition = false;
2335 runDelayedRemoveTransition = false;
2338 bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
2340 Q_Q(QQuickItemView);
2341 if (!q->isComponentComplete() || (!currentChanges.hasPendingChanges() && !runDelayedRemoveTransition) || disableLayout)
2344 disableLayout = true;
2346 updateUnrequestedIndexes();
2347 moveReason = QQuickItemViewPrivate::Other;
2349 FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
2350 int prevItemCount = itemCount;
2351 int prevVisibleItemsCount = visibleItems.count();
2352 bool visibleAffected = false;
2353 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
2354 || !currentChanges.pendingChanges.inserts().isEmpty();
2356 FxViewItem *prevFirstVisible = firstVisibleItem();
2357 QQmlNullableValue<qreal> prevViewPos;
2358 int prevFirstVisibleIndex = -1;
2359 if (prevFirstVisible) {
2360 prevViewPos = prevFirstVisible->position();
2361 prevFirstVisibleIndex = prevFirstVisible->index;
2363 qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
2365 totalInsertionResult->visiblePos = prevViewPos;
2366 totalRemovalResult->visiblePos = prevViewPos;
2368 const QVector<QQuickChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
2369 const QVector<QQuickChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
2370 ChangeResult insertionResult(prevViewPos);
2371 ChangeResult removalResult(prevViewPos);
2373 int removedCount = 0;
2374 for (int i=0; i<removals.count(); i++) {
2375 itemCount -= removals[i].count;
2376 if (applyRemovalChange(removals[i], &removalResult, &removedCount))
2377 visibleAffected = true;
2378 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
2379 visibleAffected = true;
2380 if (prevFirstVisibleIndex >= 0 && removals[i].index < prevFirstVisibleIndex) {
2381 if (removals[i].index + removals[i].count < prevFirstVisibleIndex)
2382 removalResult.countChangeBeforeVisible += removals[i].count;
2384 removalResult.countChangeBeforeVisible += (prevFirstVisibleIndex - removals[i].index);
2387 if (runDelayedRemoveTransition) {
2388 QQuickChangeSet::Remove removal;
2389 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
2390 FxViewItem *item = *it;
2391 if (item->index == -1 && !item->attached->delayRemove()) {
2392 removeItem(item, removal, &removalResult);
2394 it = visibleItems.erase(it);
2400 *totalRemovalResult += removalResult;
2401 if (!removals.isEmpty()) {
2402 updateVisibleIndex();
2404 // set positions correctly for the next insertion
2405 if (!insertions.isEmpty()) {
2406 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
2407 layoutVisibleItems(removals.first().index);
2411 QList<FxViewItem *> newItems;
2412 QList<MovedItem> movingIntoView;
2414 for (int i=0; i<insertions.count(); i++) {
2415 bool wasEmpty = visibleItems.isEmpty();
2416 if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
2417 visibleAffected = true;
2418 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
2419 visibleAffected = true;
2420 if (wasEmpty && !visibleItems.isEmpty())
2421 resetFirstItemPosition();
2422 *totalInsertionResult += insertionResult;
2424 // set positions correctly for the next insertion
2425 if (i < insertions.count() - 1) {
2426 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
2427 layoutVisibleItems(insertions[i].index);
2429 itemCount += insertions[i].count;
2431 for (int i=0; i<newItems.count(); i++)
2432 newItems.at(i)->attached->emitAdd();
2434 // for each item that was moved directly into the view as a result of a move(),
2435 // find the index it was moved from in order to set its initial position, so that we
2436 // can transition it from this "original" position to its new position in the view
2437 if (canTransition(FxViewItemTransitionManager::MoveTransition, true)) {
2438 for (int i=0; i<movingIntoView.count(); i++) {
2439 int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
2440 if (fromIndex >= 0) {
2441 if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
2442 repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
2444 repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
2445 transitionNextReposition(movingIntoView[i].item, FxViewItemTransitionManager::MoveTransition, true);
2450 // reposition visibleItems.first() correctly so that the content y doesn't jump
2451 if (removedCount != prevVisibleItemsCount)
2452 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
2454 // Whatever removed/moved items remain are no longer visible items.
2455 prepareRemoveTransitions(¤tChanges.removedItems);
2456 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
2457 it != currentChanges.removedItems.end(); ++it) {
2458 releaseItem(it.value());
2460 currentChanges.removedItems.clear();
2462 if (currentChanges.currentChanged) {
2463 if (currentChanges.currentRemoved && currentItem) {
2464 currentItem->attached->setIsCurrentItem(false);
2465 releaseItem(currentItem);
2468 if (!currentIndexCleared)
2469 updateCurrent(currentChanges.newCurrentIndex);
2472 if (!visibleAffected)
2473 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
2474 currentChanges.reset();
2477 if (prevItemCount != itemCount)
2478 emit q->countChanged();
2479 if (!visibleAffected && viewportChanged)
2482 disableLayout = false;
2483 return visibleAffected;
2486 bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount)
2488 Q_Q(QQuickItemView);
2489 bool visibleAffected = false;
2491 if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
2492 if (removal.index > visibleItems.last()->index)
2493 removeResult->countChangeAfterVisibleItems += removal.count;
2495 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
2498 QList<FxViewItem*>::Iterator it = visibleItems.begin();
2499 while (it != visibleItems.end()) {
2500 FxViewItem *item = *it;
2501 if (item->index == -1 || item->index < removal.index) {
2502 // already removed, or before removed items
2503 if (!visibleAffected && item->index < removal.index)
2504 visibleAffected = true;
2506 } else if (item->index >= removal.index + removal.count) {
2507 // after removed items
2508 item->index -= removal.count;
2509 if (removal.isMove())
2510 transitionNextReposition(item, FxViewItemTransitionManager::MoveTransition, false);
2512 transitionNextReposition(item, FxViewItemTransitionManager::RemoveTransition, false);
2516 visibleAffected = true;
2517 if (!removal.isMove())
2518 item->attached->emitRemove();
2520 if (item->attached->delayRemove() && !removal.isMove()) {
2522 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2525 removeItem(item, removal, removeResult);
2526 if (!removal.isMove())
2528 it = visibleItems.erase(it);
2533 return visibleAffected;
2536 void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult)
2538 if (removeResult->visiblePos.isValid()) {
2539 if (item->position() < removeResult->visiblePos)
2540 removeResult->sizeChangesBeforeVisiblePos += item->size();
2542 removeResult->sizeChangesAfterVisiblePos += item->size();
2544 if (removal.isMove()) {
2545 currentChanges.removedItems.insert(removal.moveKey(item->index), item);
2546 transitionNextReposition(item, FxViewItemTransitionManager::MoveTransition, true);
2548 // track item so it is released later
2549 currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item);
2551 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2552 removeResult->changedFirstItem = true;
2555 void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2556 qreal prevVisibleItemsFirstPos,
2557 FxViewItem *prevFirstVisible,
2558 ChangeResult *insertionResult,
2559 ChangeResult *removalResult)
2561 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2563 // reposition visibleItems.first() correctly so that the content y doesn't jump
2564 if (visibleItems.count()) {
2565 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2566 resetFirstItemPosition(prevVisibleItemsFirstPos);
2568 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2569 && prevFirstVisible != *visibleItems.constBegin()) {
2570 // the previous visibleItems.first() was also the first visible item, and it has been
2571 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2572 if (!insertionResult->changedFirstItem)
2573 resetFirstItemPosition(prevVisibleItemsFirstPos);
2575 } else if (prevViewPos.isValid()) {
2576 qreal moveForwardsBy = 0;
2577 qreal moveBackwardsBy = 0;
2579 // shift visibleItems.first() relative to the number of added/removed items
2580 if (visibleItems.first()->position() > prevViewPos) {
2581 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2582 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2583 } else if (visibleItems.first()->position() < prevViewPos) {
2584 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2585 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2587 adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2589 insertionResult->reset();
2590 removalResult->reset();
2594 void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2596 Q_Q(QQuickItemView);
2597 if (!hasItemTransitions())
2600 addTransitionIndexes.clear();
2601 addTransitionTargets.clear();
2602 moveTransitionIndexes.clear();
2603 moveTransitionTargets.clear();
2605 QRectF viewBounds(0, position(), q->width(), q->height());
2606 for (int i=0; i<visibleItems.count(); i++) {
2607 // must call for every visible item to init or discard transitions
2608 if (!visibleItems[i]->prepareTransition(viewBounds))
2610 if (visibleItems[i]->isTransitionTarget) {
2611 switch (visibleItems[i]->nextTransitionType) {
2612 case FxViewItemTransitionManager::NoTransition:
2614 case FxViewItemTransitionManager::PopulateTransition:
2615 case FxViewItemTransitionManager::AddTransition:
2616 addTransitionIndexes.append(visibleItems[i]->index);
2617 addTransitionTargets.append(visibleItems[i]->item);
2619 case FxViewItemTransitionManager::MoveTransition:
2620 moveTransitionIndexes.append(visibleItems[i]->index);
2621 moveTransitionTargets.append(visibleItems[i]->item);
2623 case FxViewItemTransitionManager::RemoveTransition:
2624 // removed targets won't be in visibleItems, handle these
2625 // in prepareNonVisibleItemTransition()
2632 void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQuickChangeSet::MoveKey, FxViewItem *> *removedItems)
2634 removeTransitionIndexes.clear();
2635 removeTransitionTargets.clear();
2637 if (canTransition(FxViewItemTransitionManager::RemoveTransition, true)) {
2638 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin();
2639 it != removedItems->end(); ) {
2640 bool isRemove = it.key().moveId < 0;
2642 FxViewItem *item = *it;
2643 item->releaseAfterTransition = true;
2644 releasePendingTransition.append(item);
2645 transitionNextReposition(item, FxViewItemTransitionManager::RemoveTransition, true);
2646 it = removedItems->erase(it);
2654 bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2656 // Called for items that have been removed from visibleItems and may now be
2657 // transitioned out of the view. This applies to items that are being directly
2658 // removed, or moved to outside of the view, as well as those that are
2659 // displaced to a position outside of the view due to an insert or move.
2661 if (item->nextTransitionType == FxViewItemTransitionManager::MoveTransition)
2662 repositionItemAt(item, item->index, 0);
2663 if (!item->prepareTransition(viewBounds))
2666 if (item->isTransitionTarget) {
2667 if (item->nextTransitionType == FxViewItemTransitionManager::MoveTransition) {
2668 moveTransitionIndexes.append(item->index);
2669 moveTransitionTargets.append(item->item);
2670 } else if (item->nextTransitionType == FxViewItemTransitionManager::RemoveTransition) {
2671 removeTransitionIndexes.append(item->index);
2672 removeTransitionTargets.append(item->item);
2676 item->releaseAfterTransition = true;
2681 This may return 0 if the item is being created asynchronously.
2682 When the item becomes available, refill() will be called and the item
2683 will be returned on the next call to createItem().
2685 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
2687 Q_Q(QQuickItemView);
2689 if (requestedIndex == modelIndex && (asynchronous || requestedAsync == asynchronous))
2692 if (requestedIndex != -1 && requestedIndex != modelIndex) {
2693 if (requestedItem && requestedItem->item)
2694 requestedItem->item->setParentItem(0);
2695 delete requestedItem;
2699 for (int i=0; i<releasePendingTransition.count(); i++) {
2700 if (releasePendingTransition[i]->index == modelIndex
2701 && !releasePendingTransition[i]->isPendingRemoval()) {
2702 releasePendingTransition[i]->releaseAfterTransition = false;
2703 return releasePendingTransition.takeAt(i);
2707 requestedIndex = modelIndex;
2708 requestedAsync = asynchronous;
2711 if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
2712 item->setParentItem(q->contentItem());
2713 QQml_setParent_noEvent(item, q->contentItem());
2714 requestedIndex = -1;
2715 FxViewItem *viewItem = requestedItem;
2717 viewItem = newViewItem(modelIndex, item); // already in cache, so viewItem not initialized in initItem()
2719 viewItem->index = modelIndex;
2720 // do other set up for the new item that should not happen
2721 // until after bindings are evaluated
2722 initializeViewItem(viewItem);
2723 unrequestedItems.remove(item);
2734 void QQuickItemView::createdItem(int index, QQuickItem *item)
2736 Q_D(QQuickItemView);
2737 if (d->requestedIndex != index) {
2738 item->setParentItem(contentItem());
2739 d->unrequestedItems.insert(item, index);
2740 item->setVisible(false);
2741 d->repositionPackageItemAt(item, index);
2743 d->requestedIndex = -1;
2744 if (!d->inRequest) {
2745 if (index == d->currentIndex)
2746 d->updateCurrent(index);
2752 void QQuickItemView::initItem(int index, QQuickItem *item)
2754 Q_D(QQuickItemView);
2756 if (d->requestedIndex == index) {
2757 if (d->requestedAsync)
2758 item->setVisible(false);
2759 item->setParentItem(contentItem());
2760 QQml_setParent_noEvent(item, contentItem());
2761 d->requestedItem = d->newViewItem(index, item);
2765 void QQuickItemView::destroyingItem(QQuickItem *item)
2767 Q_D(QQuickItemView);
2768 d->unrequestedItems.remove(item);
2771 void QQuickItemViewPrivate::releaseItem(FxViewItem *item)
2773 Q_Q(QQuickItemView);
2774 if (!item || !model)
2776 if (trackedItem == item)
2778 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
2779 itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
2780 if (model->release(item->item) == 0) {
2781 // item was not destroyed, and we no longer reference it.
2782 item->item->setVisible(false);
2783 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
2788 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2790 return createComponentItem(highlightComponent, true, true);
2793 QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, bool receiveItemGeometryChanges, bool createDefault)
2795 Q_Q(QQuickItemView);
2797 QQuickItem *item = 0;
2799 QQmlContext *creationContext = component->creationContext();
2800 QQmlContext *context = new QQmlContext(
2801 creationContext ? creationContext : qmlContext(q));
2802 QObject *nobj = component->create(context);
2804 QQml_setParent_noEvent(context, nobj);
2805 item = qobject_cast<QQuickItem *>(nobj);
2811 } else if (createDefault) {
2812 item = new QQuickItem;
2815 QQml_setParent_noEvent(item, q->contentItem());
2816 item->setParentItem(q->contentItem());
2817 if (receiveItemGeometryChanges) {
2818 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2819 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
2825 void QQuickItemViewPrivate::updateTrackedItem()
2827 Q_Q(QQuickItemView);
2828 FxViewItem *item = currentItem;
2834 q->trackedPositionChanged();
2837 void QQuickItemViewPrivate::updateUnrequestedIndexes()
2839 Q_Q(QQuickItemView);
2840 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2841 *it = model->indexOf(it.key(), q);
2844 void QQuickItemViewPrivate::updateUnrequestedPositions()
2846 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2847 repositionPackageItemAt(it.key(), it.value());
2850 void QQuickItemViewPrivate::updateVisibleIndex()
2853 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
2854 if ((*it)->index != -1) {
2855 visibleIndex = (*it)->index;