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>
44 #include "qplatformdefs.h"
48 // Default cacheBuffer for all views.
49 #ifndef QML_VIEW_DEFAULTCACHEBUFFER
50 #define QML_VIEW_DEFAULTCACHEBUFFER 320
53 FxViewItem::FxViewItem(QQuickItem *i, bool own, bool trackGeometry)
55 , transitionableItem(0)
58 , releaseAfterTransition(false)
59 , trackGeom(trackGeometry)
63 FxViewItem::~FxViewItem()
65 delete transitionableItem;
66 if (ownItem && item) {
67 item->setParentItem(0);
73 qreal FxViewItem::itemX() const
75 return transitionableItem ? transitionableItem->itemX() : item->x();
78 qreal FxViewItem::itemY() const
80 return transitionableItem ? transitionableItem->itemY() : item->y();
83 void FxViewItem::moveTo(const QPointF &pos, bool immediate)
85 if (transitionableItem)
86 transitionableItem->moveTo(pos, immediate);
91 void FxViewItem::setVisible(bool visible)
93 if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
95 QQuickItemPrivate::get(item)->setCulled(!visible);
98 QQuickItemViewTransitioner::TransitionType FxViewItem::scheduledTransitionType() const
100 return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
103 bool FxViewItem::transitionScheduledOrRunning() const
105 return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
108 bool FxViewItem::transitionRunning() const
110 return transitionableItem ? transitionableItem->transitionRunning() : false;
113 bool FxViewItem::isPendingRemoval() const
115 return transitionableItem ? transitionableItem->isPendingRemoval() : false;
118 void FxViewItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
122 if (!transitionableItem)
123 transitionableItem = new QQuickItemViewTransitionableItem(item);
124 transitioner->transitionNextReposition(transitionableItem, type, asTarget);
127 bool FxViewItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
129 return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
132 void FxViewItem::startTransition(QQuickItemViewTransitioner *transitioner)
134 if (transitionableItem)
135 transitionableItem->startTransition(transitioner, index);
139 QQuickItemViewChangeSet::QQuickItemViewChangeSet()
145 bool QQuickItemViewChangeSet::hasPendingChanges() const
147 return !pendingChanges.isEmpty();
150 void QQuickItemViewChangeSet::applyChanges(const QQuickChangeSet &changeSet)
152 pendingChanges.apply(changeSet);
157 foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) {
158 itemCount -= r.count;
159 if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
160 newCurrentIndex -= r.count;
161 currentChanged = true;
162 } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) {
163 // current item has been removed.
166 moveOffset = newCurrentIndex - r.index;
168 currentRemoved = true;
169 newCurrentIndex = -1;
171 newCurrentIndex = qMin(r.index, itemCount - 1);
173 currentChanged = true;
176 foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) {
178 if (itemCount && newCurrentIndex >= i.index) {
179 newCurrentIndex += i.count;
180 currentChanged = true;
181 } else if (newCurrentIndex < 0) {
183 currentChanged = true;
184 } else if (newCurrentIndex == 0 && !itemCount) {
185 // this is the first item, set the initial current index
186 currentChanged = true;
188 } else if (moveId == i.moveId) {
189 newCurrentIndex = i.index + moveOffset;
191 itemCount += i.count;
195 void QQuickItemViewChangeSet::applyBufferedChanges(const QQuickItemViewChangeSet &other)
197 if (!other.hasPendingChanges())
200 pendingChanges.apply(other.pendingChanges);
201 itemCount = other.itemCount;
202 newCurrentIndex = other.newCurrentIndex;
203 currentChanged = other.currentChanged;
204 currentRemoved = other.currentRemoved;
207 void QQuickItemViewChangeSet::prepare(int currentIndex, int count)
214 newCurrentIndex = currentIndex;
217 void QQuickItemViewChangeSet::reset()
220 newCurrentIndex = -1;
221 pendingChanges.clear();
222 removedItems.clear();
224 currentChanged = false;
225 currentRemoved = false;
228 //-----------------------------------
230 QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
231 : QQuickFlickable(dd, parent)
237 QQuickItemView::~QQuickItemView()
248 QQuickItem *QQuickItemView::currentItem() const
250 Q_D(const QQuickItemView);
253 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
254 return d->currentItem->item;
257 QVariant QQuickItemView::model() const
259 Q_D(const QQuickItemView);
260 return d->modelVariant;
263 void QQuickItemView::setModel(const QVariant &model)
266 if (d->modelVariant == model)
269 disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
270 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
271 disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
272 disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
273 disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
276 QQuickVisualModel *oldModel = d->model;
280 d->setPosition(d->contentStartOffset());
281 d->modelVariant = model;
283 QObject *object = qvariant_cast<QObject*>(model);
284 QQuickVisualModel *vim = 0;
285 if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
293 d->model = new QQuickVisualDataModel(qmlContext(this), this);
295 if (isComponentComplete())
296 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
300 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
301 dataModel->setModel(model);
305 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
306 connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
307 connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
308 connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
309 if (isComponentComplete()) {
312 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
315 d->moveReason = QQuickItemViewPrivate::SetIndex;
316 d->updateCurrent(d->currentIndex);
317 if (d->highlight && d->currentItem) {
318 if (d->autoHighlight)
319 d->resetHighlightPosition();
320 d->updateTrackedItem();
322 d->moveReason = QQuickItemViewPrivate::Other;
326 if (d->transitioner && d->transitioner->populateTransition) {
327 d->forceLayout = true;
328 d->transitioner->setPopulateTransitionEnabled(true);
332 connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
333 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
339 QQmlComponent *QQuickItemView::delegate() const
341 Q_D(const QQuickItemView);
343 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
344 return dataModel->delegate();
350 void QQuickItemView::setDelegate(QQmlComponent *delegate)
353 if (delegate == this->delegate())
356 d->model = new QQuickVisualDataModel(qmlContext(this));
359 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
360 int oldCount = dataModel->count();
361 dataModel->setDelegate(delegate);
362 if (isComponentComplete()) {
363 for (int i = 0; i < d->visibleItems.count(); ++i)
364 d->releaseItem(d->visibleItems.at(i));
365 d->visibleItems.clear();
366 d->releaseItem(d->currentItem);
370 d->moveReason = QQuickItemViewPrivate::SetIndex;
371 d->updateCurrent(d->currentIndex);
372 if (d->highlight && d->currentItem) {
373 if (d->autoHighlight)
374 d->resetHighlightPosition();
375 d->updateTrackedItem();
377 d->moveReason = QQuickItemViewPrivate::Other;
380 if (oldCount != dataModel->count())
383 emit delegateChanged();
387 int QQuickItemView::count() const
389 Q_D(const QQuickItemView);
392 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
393 return d->model->count();
396 int QQuickItemView::currentIndex() const
398 Q_D(const QQuickItemView);
399 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
400 return d->currentIndex;
403 void QQuickItemView::setCurrentIndex(int index)
406 if (d->inRequest) // currently creating item
408 d->currentIndexCleared = (index == -1);
410 d->applyPendingChanges();
411 if (index == d->currentIndex)
413 if (isComponentComplete() && d->isValid()) {
414 d->moveReason = QQuickItemViewPrivate::SetIndex;
415 d->updateCurrent(index);
416 } else if (d->currentIndex != index) {
417 d->currentIndex = index;
418 emit currentIndexChanged();
423 bool QQuickItemView::isWrapEnabled() const
425 Q_D(const QQuickItemView);
429 void QQuickItemView::setWrapEnabled(bool wrap)
435 emit keyNavigationWrapsChanged();
438 int QQuickItemView::cacheBuffer() const
440 Q_D(const QQuickItemView);
444 void QQuickItemView::setCacheBuffer(int b)
447 if (d->buffer != b) {
449 if (isComponentComplete()) {
450 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
453 emit cacheBufferChanged();
458 Qt::LayoutDirection QQuickItemView::layoutDirection() const
460 Q_D(const QQuickItemView);
461 return d->layoutDirection;
464 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
467 if (d->layoutDirection != layoutDirection) {
468 d->layoutDirection = layoutDirection;
470 emit layoutDirectionChanged();
471 emit effectiveLayoutDirectionChanged();
475 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
477 Q_D(const QQuickItemView);
478 if (d->effectiveLayoutMirror)
479 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
481 return d->layoutDirection;
484 QQuickItemView::VerticalLayoutDirection QQuickItemView::verticalLayoutDirection() const
486 Q_D(const QQuickItemView);
487 return d->verticalLayoutDirection;
490 void QQuickItemView::setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection)
493 if (d->verticalLayoutDirection != layoutDirection) {
494 d->verticalLayoutDirection = layoutDirection;
496 emit verticalLayoutDirectionChanged();
500 QQmlComponent *QQuickItemView::header() const
502 Q_D(const QQuickItemView);
503 return d->headerComponent;
506 QQuickItem *QQuickItemView::headerItem() const
508 Q_D(const QQuickItemView);
509 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
510 return d->header ? d->header->item : 0;
513 void QQuickItemView::setHeader(QQmlComponent *headerComponent)
516 if (d->headerComponent != headerComponent) {
517 d->applyPendingChanges();
520 d->headerComponent = headerComponent;
522 d->markExtentsDirty();
524 if (isComponentComplete()) {
530 emit headerItemChanged();
532 emit headerChanged();
536 QQmlComponent *QQuickItemView::footer() const
538 Q_D(const QQuickItemView);
539 return d->footerComponent;
542 QQuickItem *QQuickItemView::footerItem() const
544 Q_D(const QQuickItemView);
545 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
546 return d->footer ? d->footer->item : 0;
549 void QQuickItemView::setFooter(QQmlComponent *footerComponent)
552 if (d->footerComponent != footerComponent) {
553 d->applyPendingChanges();
556 d->footerComponent = footerComponent;
558 if (isComponentComplete()) {
563 emit footerItemChanged();
565 emit footerChanged();
569 QQmlComponent *QQuickItemView::highlight() const
571 Q_D(const QQuickItemView);
572 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
573 return d->highlightComponent;
576 void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
579 if (highlightComponent != d->highlightComponent) {
580 d->applyPendingChanges();
581 d->highlightComponent = highlightComponent;
582 d->createHighlight();
584 d->updateHighlight();
585 emit highlightChanged();
589 QQuickItem *QQuickItemView::highlightItem() const
591 Q_D(const QQuickItemView);
592 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
593 return d->highlight ? d->highlight->item : 0;
596 bool QQuickItemView::highlightFollowsCurrentItem() const
598 Q_D(const QQuickItemView);
599 return d->autoHighlight;
602 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
605 if (d->autoHighlight != autoHighlight) {
606 d->autoHighlight = autoHighlight;
608 d->updateHighlight();
609 emit highlightFollowsCurrentItemChanged();
613 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
615 Q_D(const QQuickItemView);
616 return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
619 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
622 if (d->highlightRange == mode)
624 d->highlightRange = mode;
625 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
626 emit highlightRangeModeChanged();
629 //###Possibly rename these properties, since they are very useful even without a highlight?
630 qreal QQuickItemView::preferredHighlightBegin() const
632 Q_D(const QQuickItemView);
633 return d->highlightRangeStart;
636 void QQuickItemView::setPreferredHighlightBegin(qreal start)
639 d->highlightRangeStartValid = true;
640 if (d->highlightRangeStart == start)
642 d->highlightRangeStart = start;
643 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
644 emit preferredHighlightBeginChanged();
647 void QQuickItemView::resetPreferredHighlightBegin()
650 d->highlightRangeStartValid = false;
651 if (d->highlightRangeStart == 0)
653 d->highlightRangeStart = 0;
654 emit preferredHighlightBeginChanged();
657 qreal QQuickItemView::preferredHighlightEnd() const
659 Q_D(const QQuickItemView);
660 return d->highlightRangeEnd;
663 void QQuickItemView::setPreferredHighlightEnd(qreal end)
666 d->highlightRangeEndValid = true;
667 if (d->highlightRangeEnd == end)
669 d->highlightRangeEnd = end;
670 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
671 emit preferredHighlightEndChanged();
674 void QQuickItemView::resetPreferredHighlightEnd()
677 d->highlightRangeEndValid = false;
678 if (d->highlightRangeEnd == 0)
680 d->highlightRangeEnd = 0;
681 emit preferredHighlightEndChanged();
684 int QQuickItemView::highlightMoveDuration() const
686 Q_D(const QQuickItemView);
687 return d->highlightMoveDuration;
690 void QQuickItemView::setHighlightMoveDuration(int duration)
693 if (d->highlightMoveDuration != duration) {
694 d->highlightMoveDuration = duration;
695 emit highlightMoveDurationChanged();
699 QQuickTransition *QQuickItemView::populateTransition() const
701 Q_D(const QQuickItemView);
702 return d->transitioner ? d->transitioner->populateTransition : 0;
705 void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
708 d->createTransitioner();
709 if (d->transitioner->populateTransition != transition) {
710 d->transitioner->populateTransition = transition;
711 emit populateTransitionChanged();
715 QQuickTransition *QQuickItemView::addTransition() const
717 Q_D(const QQuickItemView);
718 return d->transitioner ? d->transitioner->addTransition : 0;
721 void QQuickItemView::setAddTransition(QQuickTransition *transition)
724 d->createTransitioner();
725 if (d->transitioner->addTransition != transition) {
726 d->transitioner->addTransition = transition;
727 emit addTransitionChanged();
731 QQuickTransition *QQuickItemView::addDisplacedTransition() const
733 Q_D(const QQuickItemView);
734 return d->transitioner ? d->transitioner->addDisplacedTransition : 0;
737 void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
740 d->createTransitioner();
741 if (d->transitioner->addDisplacedTransition != transition) {
742 d->transitioner->addDisplacedTransition = transition;
743 emit addDisplacedTransitionChanged();
747 QQuickTransition *QQuickItemView::moveTransition() const
749 Q_D(const QQuickItemView);
750 return d->transitioner ? d->transitioner->moveTransition : 0;
753 void QQuickItemView::setMoveTransition(QQuickTransition *transition)
756 d->createTransitioner();
757 if (d->transitioner->moveTransition != transition) {
758 d->transitioner->moveTransition = transition;
759 emit moveTransitionChanged();
763 QQuickTransition *QQuickItemView::moveDisplacedTransition() const
765 Q_D(const QQuickItemView);
766 return d->transitioner ? d->transitioner->moveDisplacedTransition : 0;
769 void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
772 d->createTransitioner();
773 if (d->transitioner->moveDisplacedTransition != transition) {
774 d->transitioner->moveDisplacedTransition = transition;
775 emit moveDisplacedTransitionChanged();
779 QQuickTransition *QQuickItemView::removeTransition() const
781 Q_D(const QQuickItemView);
782 return d->transitioner ? d->transitioner->removeTransition : 0;
785 void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
788 d->createTransitioner();
789 if (d->transitioner->removeTransition != transition) {
790 d->transitioner->removeTransition = transition;
791 emit removeTransitionChanged();
795 QQuickTransition *QQuickItemView::removeDisplacedTransition() const
797 Q_D(const QQuickItemView);
798 return d->transitioner ? d->transitioner->removeDisplacedTransition : 0;
801 void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
804 d->createTransitioner();
805 if (d->transitioner->removeDisplacedTransition != transition) {
806 d->transitioner->removeDisplacedTransition = transition;
807 emit removeDisplacedTransitionChanged();
811 QQuickTransition *QQuickItemView::displacedTransition() const
813 Q_D(const QQuickItemView);
814 return d->transitioner ? d->transitioner->displacedTransition : 0;
817 void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
820 d->createTransitioner();
821 if (d->transitioner->displacedTransition != transition) {
822 d->transitioner->displacedTransition = transition;
823 emit displacedTransitionChanged();
827 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
832 if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain)
835 applyPendingChanges();
836 int idx = qMax(qMin(index, model->count()-1), 0);
838 qreal pos = isContentFlowReversed() ? -position() - size() : position();
839 FxViewItem *item = visibleItem(idx);
841 if (layoutOrientation() == Qt::Vertical)
842 maxExtent = isContentFlowReversed() ? q->minYExtent()-size(): -q->maxYExtent();
844 maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
846 int itemPos = positionAt(idx);
847 changedVisibleIndex(idx);
848 // save the currently visible items in case any of them end up visible again
849 QList<FxViewItem *> oldVisible = visibleItems;
850 visibleItems.clear();
851 setPosition(qMin(qreal(itemPos), maxExtent));
852 // now release the reference to all the old visible items.
853 for (int i = 0; i < oldVisible.count(); ++i)
854 releaseItem(oldVisible.at(i));
855 item = visibleItem(idx);
858 const qreal itemPos = item->position();
860 case QQuickItemView::Beginning:
862 if (index < 0 && header)
865 case QQuickItemView::Center:
866 pos = itemPos - (size() - item->size())/2;
868 case QQuickItemView::End:
869 pos = itemPos - size() + item->size();
870 if (index >= model->count() && footer)
873 case QQuickItemView::Visible:
874 if (itemPos > pos + size())
875 pos = itemPos - size() + item->size();
876 else if (item->endPosition() <= pos)
879 case QQuickItemView::Contain:
880 if (item->endPosition() >= pos + size())
881 pos = itemPos - size() + item->size();
885 pos = qMin(pos, maxExtent);
887 if (layoutOrientation() == Qt::Vertical)
888 minExtent = isContentFlowReversed() ? q->maxYExtent()-size(): -q->minYExtent();
890 minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
891 pos = qMax(pos, minExtent);
892 moveReason = QQuickItemViewPrivate::Other;
898 resetHighlightPosition();
905 void QQuickItemView::positionViewAtIndex(int index, int mode)
908 if (!d->isValid() || index < 0 || index >= d->model->count())
910 d->positionViewAtIndex(index, mode);
914 void QQuickItemView::positionViewAtBeginning()
919 d->positionViewAtIndex(-1, Beginning);
922 void QQuickItemView::positionViewAtEnd()
927 d->positionViewAtIndex(d->model->count(), End);
930 int QQuickItemView::indexAt(qreal x, qreal y) const
932 Q_D(const QQuickItemView);
933 for (int i = 0; i < d->visibleItems.count(); ++i) {
934 const FxViewItem *item = d->visibleItems.at(i);
935 if (item->contains(x, y))
942 QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
944 Q_D(const QQuickItemView);
945 for (int i = 0; i < d->visibleItems.count(); ++i) {
946 const FxViewItem *item = d->visibleItems.at(i);
947 if (item->contains(x, y))
954 void QQuickItemViewPrivate::applyPendingChanges()
957 if (q->isComponentComplete() && currentChanges.hasPendingChanges())
961 int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector<QQuickChangeSet::Remove> &changes) const
963 for (int i=0; i<changes.count(); i++) {
964 for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
965 if (changes[i].moveKey(j) == key)
972 qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
974 Q_Q(const QQuickItemView);
976 qreal highlightStart;
978 qreal endPositionFirstItem = 0;
979 qreal extent = -startPosition() + axisData.startMargin;
980 if (isContentFlowReversed()) {
981 if (model && model->count())
982 endPositionFirstItem = positionAt(model->count()-1);
984 extent += headerSize();
985 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
986 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
987 extent += footerSize();
988 qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
989 if (extent < maxExtentAlongAxis)
990 extent = maxExtentAlongAxis;
992 endPositionFirstItem = endPositionAt(0);
993 highlightStart = highlightRangeStart;
994 highlightEnd = highlightRangeEnd;
995 extent += headerSize();
997 if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
998 extent += highlightStart;
999 FxViewItem *firstItem = visibleItem(0);
1001 extent -= firstItem->sectionSize();
1002 extent = isContentFlowReversed()
1003 ? qMin(extent, endPositionFirstItem + highlightEnd)
1004 : qMax(extent, -(endPositionFirstItem - highlightEnd));
1009 qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
1011 Q_Q(const QQuickItemView);
1013 qreal highlightStart;
1015 qreal lastItemPosition = 0;
1017 if (isContentFlowReversed()) {
1018 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1019 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1020 lastItemPosition = endPosition();
1022 highlightStart = highlightRangeStart;
1023 highlightEnd = highlightRangeEnd;
1024 if (model && model->count())
1025 lastItemPosition = positionAt(model->count()-1);
1027 if (!model || !model->count()) {
1028 if (!isContentFlowReversed())
1029 maxExtent = header ? -headerSize() : 0;
1030 extent += forXAxis ? q->width() : q->height();
1031 } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1032 extent = -(lastItemPosition - highlightStart);
1033 if (highlightEnd != highlightStart) {
1034 extent = isContentFlowReversed()
1035 ? qMax(extent, -(endPosition() - highlightEnd))
1036 : qMin(extent, -(endPosition() - highlightEnd));
1039 extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
1041 if (isContentFlowReversed()) {
1042 extent -= headerSize();
1043 extent -= axisData.endMargin;
1045 extent -= footerSize();
1046 extent -= axisData.endMargin;
1047 qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
1048 if (extent > minExtentAlongAxis)
1049 extent = minExtentAlongAxis;
1055 // for debugging only
1056 void QQuickItemViewPrivate::checkVisible() const
1059 for (int i = 0; i < visibleItems.count(); ++i) {
1060 FxViewItem *item = visibleItems.at(i);
1061 if (item->index == -1) {
1063 } else if (item->index != visibleIndex + i - skip) {
1064 qFatal("index %d %d %d", visibleIndex, i, item->index);
1069 // for debugging only
1070 void QQuickItemViewPrivate::showVisibleItems() const
1072 qDebug() << "Visible items:";
1073 for (int i = 0; i < visibleItems.count(); ++i) {
1074 qDebug() << "\t" << visibleItems[i]->index
1075 << visibleItems[i]->item->objectName()
1076 << visibleItems[i]->position();
1080 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1082 Q_Q(QQuickItemView);
1083 QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1084 if (!q->isComponentComplete())
1087 if (header && header->item == item) {
1090 if (!q->isMoving() && !q->isFlicking())
1092 } else if (footer && footer->item == item) {
1095 if (!q->isMoving() && !q->isFlicking())
1099 if (currentItem && currentItem->item == item) {
1100 // don't allow item movement transitions to trigger a re-layout and
1101 // start new transitions
1102 bool prevInLayout = inLayout;
1104 FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : 0;
1105 if (actualItem && actualItem->transitionRunning())
1109 inLayout = prevInLayout;
1112 if (trackedItem && trackedItem->item == item)
1113 q->trackedPositionChanged();
1116 void QQuickItemView::destroyRemoved()
1118 Q_D(QQuickItemView);
1119 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1120 it != d->visibleItems.end();) {
1121 FxViewItem *item = *it;
1122 if (item->index == -1 && item->attached->delayRemove() == false) {
1123 if (d->transitioner && d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)) {
1124 // don't remove from visibleItems until next layout()
1125 d->runDelayedRemoveTransition = true;
1126 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1129 d->releaseItem(item);
1130 it = d->visibleItems.erase(it);
1137 // Correct the positioning of the items
1138 d->updateSections();
1139 d->forceLayout = true;
1143 void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1145 Q_D(QQuickItemView);
1147 if (d->transitioner)
1148 d->transitioner->setPopulateTransitionEnabled(true);
1149 d->moveReason = QQuickItemViewPrivate::SetIndex;
1151 if (d->highlight && d->currentItem) {
1152 if (d->autoHighlight)
1153 d->resetHighlightPosition();
1154 d->updateTrackedItem();
1156 d->moveReason = QQuickItemViewPrivate::Other;
1157 emit countChanged();
1158 if (d->transitioner && d->transitioner->populateTransition) {
1159 d->forceLayout = true;
1164 d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
1165 d->bufferedChanges.applyChanges(changeSet);
1167 if (d->bufferedChanges.hasPendingChanges()) {
1168 d->currentChanges.applyBufferedChanges(d->bufferedChanges);
1169 d->bufferedChanges.reset();
1171 d->currentChanges.prepare(d->currentIndex, d->itemCount);
1172 d->currentChanges.applyChanges(changeSet);
1178 void QQuickItemView::animStopped()
1180 Q_D(QQuickItemView);
1181 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1182 d->refillOrLayout();
1183 if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1184 d->updateHighlight();
1188 void QQuickItemView::trackedPositionChanged()
1190 Q_D(QQuickItemView);
1191 if (!d->trackedItem || !d->currentItem)
1193 if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
1194 qreal trackedPos = d->trackedItem->position();
1195 qreal trackedSize = d->trackedItem->size();
1196 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1197 qreal pos = viewPos;
1198 if (d->haveHighlightRange) {
1199 if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1200 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1201 if (trackedPos < pos + d->highlightRangeStart)
1202 pos = trackedPos - d->highlightRangeStart;
1203 if (d->highlightRange != StrictlyEnforceRange) {
1204 if (pos > d->endPosition() - d->size())
1205 pos = d->endPosition() - d->size();
1206 if (pos < d->startPosition())
1207 pos = d->startPosition();
1210 if (d->trackedItem != d->currentItem) {
1211 // also make section header visible
1212 trackedPos -= d->currentItem->sectionSize();
1213 trackedSize += d->currentItem->sectionSize();
1215 qreal trackedEndPos = d->trackedItem->endPosition();
1216 qreal toItemPos = d->currentItem->position();
1217 qreal toItemEndPos = d->currentItem->endPosition();
1218 if (d->showHeaderForIndex(d->currentIndex)) {
1219 qreal startOffset = -d->contentStartOffset();
1220 trackedPos -= startOffset;
1221 trackedEndPos -= startOffset;
1222 toItemPos -= startOffset;
1223 toItemEndPos -= startOffset;
1224 } else if (d->showFooterForIndex(d->currentIndex)) {
1225 qreal endOffset = d->footerSize();
1226 if (d->layoutOrientation() == Qt::Vertical) {
1227 if (d->isContentFlowReversed())
1228 endOffset += d->vData.startMargin;
1230 endOffset += d->vData.endMargin;
1232 if (d->isContentFlowReversed())
1233 endOffset += d->hData.startMargin;
1235 endOffset += d->hData.endMargin;
1237 trackedPos += endOffset;
1238 trackedEndPos += endOffset;
1239 toItemPos += endOffset;
1240 toItemEndPos += endOffset;
1243 if (trackedEndPos >= viewPos + d->size()
1244 && toItemEndPos >= viewPos + d->size()) {
1245 if (trackedEndPos <= toItemEndPos) {
1246 pos = trackedEndPos - d->size();
1247 if (trackedSize > d->size())
1250 pos = toItemEndPos - d->size();
1251 if (d->currentItem->size() > d->size())
1252 pos = d->currentItem->position();
1255 if (trackedPos < pos && toItemPos < pos)
1256 pos = qMax(trackedPos, toItemPos);
1258 if (viewPos != pos) {
1260 d->calcVelocity = true;
1261 d->setPosition(pos);
1262 d->calcVelocity = false;
1267 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1269 Q_D(QQuickItemView);
1270 d->markExtentsDirty();
1271 if (isComponentComplete() && d->isValid()) {
1272 d->forceLayout = true;
1275 QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1278 qreal QQuickItemView::minYExtent() const
1280 Q_D(const QQuickItemView);
1281 if (d->layoutOrientation() == Qt::Horizontal)
1282 return QQuickFlickable::minYExtent();
1284 if (d->vData.minExtentDirty) {
1285 d->minExtent = d->minExtentForAxis(d->vData, false);
1286 d->vData.minExtentDirty = false;
1289 return d->minExtent;
1292 qreal QQuickItemView::maxYExtent() const
1294 Q_D(const QQuickItemView);
1295 if (d->layoutOrientation() == Qt::Horizontal)
1298 if (d->vData.maxExtentDirty) {
1299 d->maxExtent = d->maxExtentForAxis(d->vData, false);
1300 d->vData.maxExtentDirty = false;
1303 return d->maxExtent;
1306 qreal QQuickItemView::minXExtent() const
1308 Q_D(const QQuickItemView);
1309 if (d->layoutOrientation() == Qt::Vertical)
1310 return QQuickFlickable::minXExtent();
1312 if (d->hData.minExtentDirty) {
1313 d->minExtent = d->minExtentForAxis(d->hData, true);
1314 d->hData.minExtentDirty = false;
1317 return d->minExtent;
1320 qreal QQuickItemView::maxXExtent() const
1322 Q_D(const QQuickItemView);
1323 if (d->layoutOrientation() == Qt::Vertical)
1326 if (d->hData.maxExtentDirty) {
1327 d->maxExtent = d->maxExtentForAxis(d->hData, true);
1328 d->hData.maxExtentDirty = false;
1331 return d->maxExtent;
1334 void QQuickItemView::setContentX(qreal pos)
1336 Q_D(QQuickItemView);
1337 // Positioning the view manually should override any current movement state
1338 d->moveReason = QQuickItemViewPrivate::Other;
1339 QQuickFlickable::setContentX(pos);
1342 void QQuickItemView::setContentY(qreal pos)
1344 Q_D(QQuickItemView);
1345 // Positioning the view manually should override any current movement state
1346 d->moveReason = QQuickItemViewPrivate::Other;
1347 QQuickFlickable::setContentY(pos);
1350 qreal QQuickItemView::originX() const
1352 Q_D(const QQuickItemView);
1353 if (d->layoutOrientation() == Qt::Horizontal
1354 && effectiveLayoutDirection() == Qt::RightToLeft
1355 && contentWidth() < width()) {
1356 return d->lastPosition() - d->footerSize();
1358 return QQuickFlickable::originX();
1361 qreal QQuickItemView::originY() const
1363 Q_D(const QQuickItemView);
1364 if (d->layoutOrientation() == Qt::Vertical
1365 && d->verticalLayoutDirection == QQuickItemView::BottomToTop
1366 && contentHeight() < height()) {
1367 return d->lastPosition() - d->footerSize();
1369 return QQuickFlickable::originY();
1372 void QQuickItemView::updatePolish()
1374 Q_D(QQuickItemView);
1375 QQuickFlickable::updatePolish();
1379 void QQuickItemView::componentComplete()
1381 Q_D(QQuickItemView);
1382 if (d->model && d->ownModel)
1383 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1385 QQuickFlickable::componentComplete();
1390 d->updateViewport();
1391 d->setPosition(d->contentStartOffset());
1392 if (d->transitioner)
1393 d->transitioner->setPopulateTransitionEnabled(true);
1397 d->moveReason = QQuickItemViewPrivate::SetIndex;
1398 if (d->currentIndex < 0 && !d->currentIndexCleared)
1399 d->updateCurrent(0);
1401 d->updateCurrent(d->currentIndex);
1402 if (d->highlight && d->currentItem) {
1403 if (d->autoHighlight)
1404 d->resetHighlightPosition();
1405 d->updateTrackedItem();
1407 d->moveReason = QQuickItemViewPrivate::Other;
1410 if (d->model && d->model->count())
1411 emit countChanged();
1416 QQuickItemViewPrivate::QQuickItemViewPrivate()
1418 , buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
1419 , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
1422 , currentIndex(-1), currentItem(0)
1423 , trackedItem(0), requestedIndex(-1)
1424 , highlightComponent(0), highlight(0)
1425 , highlightRange(QQuickItemView::NoHighlightRange)
1426 , highlightRangeStart(0), highlightRangeEnd(0)
1427 , highlightMoveDuration(150)
1428 , headerComponent(0), header(0), footerComponent(0), footer(0)
1430 , minExtent(0), maxExtent(0)
1431 , ownModel(false), wrap(false)
1432 , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1433 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1434 , fillCacheBuffer(false), inRequest(false)
1435 , runDelayedRemoveTransition(false)
1437 bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
1438 bufferPause.setLoopCount(1);
1439 bufferPause.setDuration(16);
1442 QQuickItemViewPrivate::~QQuickItemViewPrivate()
1445 transitioner->setChangeListener(0);
1446 delete transitioner;
1449 bool QQuickItemViewPrivate::isValid() const
1451 return model && model->count() && model->isValid();
1454 qreal QQuickItemViewPrivate::position() const
1456 Q_Q(const QQuickItemView);
1457 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1460 qreal QQuickItemViewPrivate::size() const
1462 Q_Q(const QQuickItemView);
1463 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1466 qreal QQuickItemViewPrivate::startPosition() const
1468 return isContentFlowReversed() ? -lastPosition() : originPosition();
1471 qreal QQuickItemViewPrivate::endPosition() const
1473 return isContentFlowReversed() ? -originPosition() : lastPosition();
1476 qreal QQuickItemViewPrivate::contentStartOffset() const
1478 qreal pos = -headerSize();
1479 if (layoutOrientation() == Qt::Vertical) {
1480 if (isContentFlowReversed())
1481 pos -= vData.endMargin;
1483 pos -= vData.startMargin;
1485 if (isContentFlowReversed())
1486 pos -= hData.endMargin;
1488 pos -= hData.startMargin;
1493 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1495 if (visibleItems.count()) {
1496 int i = visibleItems.count() - 1;
1497 while (i > 0 && visibleItems.at(i)->index == -1)
1499 if (visibleItems.at(i)->index != -1)
1500 return visibleItems.at(i)->index;
1502 return defaultValue;
1505 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1506 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1507 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1508 FxViewItem *item = visibleItems.at(i);
1509 if (item->index == modelIndex)
1516 // should rename to firstItemInView() to avoid confusion with other "*visible*" methods
1517 // that don't look at the view position and size
1518 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
1519 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1520 for (int i = 0; i < visibleItems.count(); ++i) {
1521 FxViewItem *item = visibleItems.at(i);
1522 if (item->index != -1 && item->endPosition() > pos)
1525 return visibleItems.count() ? visibleItems.first() : 0;
1528 int QQuickItemViewPrivate::findLastIndexInView() const
1530 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1531 for (int i=visibleItems.count() - 1; i>=0; i--) {
1532 if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
1533 return visibleItems.at(i)->index;
1538 // Map a model index to visibleItems list index.
1539 // These may differ if removed items are still present in the visible list,
1540 // e.g. doing a removal animation
1541 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1543 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1545 for (int i = 0; i < visibleItems.count(); ++i) {
1546 FxViewItem *item = visibleItems.at(i);
1547 if (item->index == modelIndex)
1549 if (item->index > modelIndex)
1552 return -1; // Not in visibleList
1555 void QQuickItemViewPrivate::init()
1557 Q_Q(QQuickItemView);
1558 q->setFlag(QQuickItem::ItemIsFocusScope);
1559 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1560 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1563 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1565 Q_Q(QQuickItemView);
1566 applyPendingChanges();
1567 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1569 currentItem->attached->setIsCurrentItem(false);
1570 releaseItem(currentItem);
1572 currentIndex = modelIndex;
1573 emit q->currentIndexChanged();
1574 emit q->currentItemChanged();
1576 } else if (currentIndex != modelIndex) {
1577 currentIndex = modelIndex;
1578 emit q->currentIndexChanged();
1583 if (currentItem && currentIndex == modelIndex) {
1588 FxViewItem *oldCurrentItem = currentItem;
1589 int oldCurrentIndex = currentIndex;
1590 currentIndex = modelIndex;
1591 currentItem = createItem(modelIndex, false);
1592 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1593 oldCurrentItem->attached->setIsCurrentItem(false);
1595 currentItem->item->setFocus(true);
1596 currentItem->attached->setIsCurrentItem(true);
1597 initializeCurrentItem();
1601 if (oldCurrentIndex != currentIndex)
1602 emit q->currentIndexChanged();
1603 if (oldCurrentItem != currentItem)
1604 emit q->currentItemChanged();
1605 releaseItem(oldCurrentItem);
1608 void QQuickItemViewPrivate::clear()
1610 currentChanges.reset();
1613 for (int i = 0; i < visibleItems.count(); ++i)
1614 releaseItem(visibleItems.at(i));
1615 visibleItems.clear();
1618 for (int i = 0; i < releasePendingTransition.count(); ++i) {
1619 releasePendingTransition.at(i)->releaseAfterTransition = false;
1620 releaseItem(releasePendingTransition.at(i));
1622 releasePendingTransition.clear();
1624 releaseItem(currentItem);
1629 if (requestedIndex >= 0) {
1631 model->cancel(requestedIndex);
1632 requestedIndex = -1;
1640 void QQuickItemViewPrivate::mirrorChange()
1642 Q_Q(QQuickItemView);
1644 emit q->effectiveLayoutDirectionChanged();
1647 void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1649 Q_Q(QQuickItemView);
1650 fillCacheBuffer = true;
1654 void QQuickItemViewPrivate::refill()
1656 qreal s = qMax(size(), qreal(0.));
1657 if (isContentFlowReversed())
1658 refill(-position()-s, -position());
1660 refill(position(), position()+s);
1663 void QQuickItemViewPrivate::refill(qreal from, qreal to)
1665 Q_Q(QQuickItemView);
1666 if (!isValid() || !q->isComponentComplete())
1670 currentChanges.reset();
1672 int prevCount = itemCount;
1673 itemCount = model->count();
1674 qreal bufferFrom = from - buffer;
1675 qreal bufferTo = to + buffer;
1676 qreal fillFrom = from;
1679 bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
1680 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1682 if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1684 // We've already created a new delegate this frame.
1685 // Just schedule a buffer refill.
1686 bufferPause.start();
1688 if (bufferMode & BufferAfter)
1690 if (bufferMode & BufferBefore)
1691 fillFrom = bufferFrom;
1692 added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, true);
1696 if (added || removed) {
1698 updateBeginningEnd();
1699 visibleItemsChanged();
1705 if (prevCount != itemCount)
1706 emit q->countChanged();
1709 void QQuickItemViewPrivate::regenerate()
1711 Q_Q(QQuickItemView);
1712 if (q->isComponentComplete()) {
1713 currentChanges.reset();
1722 setPosition(contentStartOffset());
1724 updateCurrent(currentIndex);
1728 void QQuickItemViewPrivate::updateViewport()
1730 Q_Q(QQuickItemView);
1731 qreal extra = headerSize() + footerSize();
1732 qreal contentSize = isValid() ? (endPosition() - startPosition()) : 0.0;
1733 if (layoutOrientation() == Qt::Vertical)
1734 q->setContentHeight(contentSize + extra);
1736 q->setContentWidth(contentSize + extra);
1739 void QQuickItemViewPrivate::layout()
1741 Q_Q(QQuickItemView);
1747 if (!isValid() && !visibleItems.count()) {
1749 setPosition(contentStartOffset());
1751 transitioner->setPopulateTransitionEnabled(false);
1756 if (runDelayedRemoveTransition && transitioner
1757 && transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1758 // assume that any items moving now are moving due to the remove - if they schedule
1759 // a different transition, that will override this one anyway
1760 for (int i=0; i<visibleItems.count(); i++)
1761 visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
1764 ChangeResult insertionPosChanges;
1765 ChangeResult removalPosChanges;
1766 if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
1767 if (fillCacheBuffer) {
1768 fillCacheBuffer = false;
1774 forceLayout = false;
1776 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
1777 for (int i=0; i<visibleItems.count(); i++) {
1778 if (!visibleItems.at(i)->transitionScheduledOrRunning())
1779 visibleItems.at(i)->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
1782 layoutVisibleItems();
1784 int lastIndexInView = findLastIndexInView();
1789 if (!q->isMoving() && !q->isFlicking()) {
1797 updateUnrequestedPositions();
1800 // items added in the last refill() may need to be transitioned in - e.g. a remove
1801 // causes items to slide up into view
1802 if (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false)
1803 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1804 translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
1807 prepareVisibleItemTransitions();
1809 QRectF viewBounds(0, position(), q->width(), q->height());
1810 for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin();
1811 it != releasePendingTransition.end(); ) {
1812 FxViewItem *item = *it;
1813 if (prepareNonVisibleItemTransition(item, viewBounds)) {
1817 it = releasePendingTransition.erase(it);
1821 for (int i=0; i<visibleItems.count(); i++)
1822 visibleItems[i]->startTransition(transitioner);
1823 for (int i=0; i<releasePendingTransition.count(); i++)
1824 releasePendingTransition[i]->startTransition(transitioner);
1826 transitioner->setPopulateTransitionEnabled(false);
1827 transitioner->resetTargetLists();
1830 runDelayedRemoveTransition = false;
1834 bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
1836 Q_Q(QQuickItemView);
1837 if (!q->isComponentComplete() || !hasPendingChanges())
1840 if (bufferedChanges.hasPendingChanges()) {
1841 currentChanges.applyBufferedChanges(bufferedChanges);
1842 bufferedChanges.reset();
1845 updateUnrequestedIndexes();
1846 moveReason = QQuickItemViewPrivate::Other;
1848 FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
1849 int prevItemCount = itemCount;
1850 int prevVisibleItemsCount = visibleItems.count();
1851 bool visibleAffected = false;
1852 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
1853 || !currentChanges.pendingChanges.inserts().isEmpty();
1855 FxViewItem *prevFirstVisible = firstVisibleItem();
1856 QQmlNullableValue<qreal> prevViewPos;
1857 int prevFirstVisibleIndex = -1;
1858 if (prevFirstVisible) {
1859 prevViewPos = prevFirstVisible->position();
1860 prevFirstVisibleIndex = prevFirstVisible->index;
1862 qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
1864 totalInsertionResult->visiblePos = prevViewPos;
1865 totalRemovalResult->visiblePos = prevViewPos;
1867 const QVector<QQuickChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
1868 const QVector<QQuickChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
1869 ChangeResult insertionResult(prevViewPos);
1870 ChangeResult removalResult(prevViewPos);
1872 int removedCount = 0;
1873 for (int i=0; i<removals.count(); i++) {
1874 itemCount -= removals[i].count;
1875 if (applyRemovalChange(removals[i], &removalResult, &removedCount))
1876 visibleAffected = true;
1877 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
1878 visibleAffected = true;
1879 if (prevFirstVisibleIndex >= 0 && removals[i].index < prevFirstVisibleIndex) {
1880 if (removals[i].index + removals[i].count < prevFirstVisibleIndex)
1881 removalResult.countChangeBeforeVisible += removals[i].count;
1883 removalResult.countChangeBeforeVisible += (prevFirstVisibleIndex - removals[i].index);
1886 if (runDelayedRemoveTransition) {
1887 QQuickChangeSet::Remove removal;
1888 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
1889 FxViewItem *item = *it;
1890 if (item->index == -1 && !item->attached->delayRemove()) {
1891 removeItem(item, removal, &removalResult);
1893 it = visibleItems.erase(it);
1899 *totalRemovalResult += removalResult;
1900 if (!removals.isEmpty()) {
1901 updateVisibleIndex();
1903 // set positions correctly for the next insertion
1904 if (!insertions.isEmpty()) {
1905 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1906 layoutVisibleItems(removals.first().index);
1910 QList<FxViewItem *> newItems;
1911 QList<MovedItem> movingIntoView;
1913 for (int i=0; i<insertions.count(); i++) {
1914 bool wasEmpty = visibleItems.isEmpty();
1915 if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
1916 visibleAffected = true;
1917 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
1918 visibleAffected = true;
1919 if (wasEmpty && !visibleItems.isEmpty())
1920 resetFirstItemPosition();
1921 *totalInsertionResult += insertionResult;
1923 // set positions correctly for the next insertion
1924 if (i < insertions.count() - 1) {
1925 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1926 layoutVisibleItems(insertions[i].index);
1928 itemCount += insertions[i].count;
1930 for (int i=0; i<newItems.count(); i++)
1931 newItems.at(i)->attached->emitAdd();
1933 // for each item that was moved directly into the view as a result of a move(),
1934 // find the index it was moved from in order to set its initial position, so that we
1935 // can transition it from this "original" position to its new position in the view
1936 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
1937 for (int i=0; i<movingIntoView.count(); i++) {
1938 int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
1939 if (fromIndex >= 0) {
1940 if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
1941 repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
1943 repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
1944 movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
1949 // reposition visibleItems.first() correctly so that the content y doesn't jump
1950 if (removedCount != prevVisibleItemsCount)
1951 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1953 // Whatever removed/moved items remain are no longer visible items.
1954 prepareRemoveTransitions(¤tChanges.removedItems);
1955 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
1956 it != currentChanges.removedItems.end(); ++it) {
1957 releaseItem(it.value());
1959 currentChanges.removedItems.clear();
1961 if (currentChanges.currentChanged) {
1962 if (currentChanges.currentRemoved && currentItem) {
1963 currentItem->attached->setIsCurrentItem(false);
1964 releaseItem(currentItem);
1967 if (!currentIndexCleared)
1968 updateCurrent(currentChanges.newCurrentIndex);
1971 if (!visibleAffected)
1972 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
1973 currentChanges.reset();
1976 if (prevItemCount != itemCount)
1977 emit q->countChanged();
1978 if (!visibleAffected && viewportChanged)
1981 return visibleAffected;
1984 bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount)
1986 Q_Q(QQuickItemView);
1987 bool visibleAffected = false;
1989 if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
1990 if (removal.index > visibleItems.last()->index)
1991 removeResult->countChangeAfterVisibleItems += removal.count;
1993 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
1996 QList<FxViewItem*>::Iterator it = visibleItems.begin();
1997 while (it != visibleItems.end()) {
1998 FxViewItem *item = *it;
1999 if (item->index == -1 || item->index < removal.index) {
2000 // already removed, or before removed items
2001 if (!visibleAffected && item->index < removal.index)
2002 visibleAffected = true;
2004 } else if (item->index >= removal.index + removal.count) {
2005 // after removed items
2006 item->index -= removal.count;
2007 if (removal.isMove())
2008 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2010 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2014 visibleAffected = true;
2015 if (!removal.isMove())
2016 item->attached->emitRemove();
2018 if (item->attached->delayRemove() && !removal.isMove()) {
2020 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2023 removeItem(item, removal, removeResult);
2024 if (!removal.isMove())
2026 it = visibleItems.erase(it);
2031 return visibleAffected;
2034 void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult)
2036 if (removeResult->visiblePos.isValid()) {
2037 if (item->position() < removeResult->visiblePos)
2038 removeResult->sizeChangesBeforeVisiblePos += item->size();
2040 removeResult->sizeChangesAfterVisiblePos += item->size();
2042 if (removal.isMove()) {
2043 currentChanges.removedItems.insert(removal.moveKey(item->index), item);
2044 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2046 // track item so it is released later
2047 currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item);
2049 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2050 removeResult->changedFirstItem = true;
2053 void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2054 qreal prevVisibleItemsFirstPos,
2055 FxViewItem *prevFirstVisible,
2056 ChangeResult *insertionResult,
2057 ChangeResult *removalResult)
2059 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2061 // reposition visibleItems.first() correctly so that the content y doesn't jump
2062 if (visibleItems.count()) {
2063 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2064 resetFirstItemPosition(prevVisibleItemsFirstPos);
2066 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2067 && prevFirstVisible != *visibleItems.constBegin()) {
2068 // the previous visibleItems.first() was also the first visible item, and it has been
2069 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2070 if (!insertionResult->changedFirstItem)
2071 resetFirstItemPosition(prevVisibleItemsFirstPos);
2073 } else if (prevViewPos.isValid()) {
2074 qreal moveForwardsBy = 0;
2075 qreal moveBackwardsBy = 0;
2077 // shift visibleItems.first() relative to the number of added/removed items
2078 if (visibleItems.first()->position() > prevViewPos) {
2079 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2080 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2081 } else if (visibleItems.first()->position() < prevViewPos) {
2082 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2083 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2085 adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2087 insertionResult->reset();
2088 removalResult->reset();
2092 void QQuickItemViewPrivate::createTransitioner()
2094 if (!transitioner) {
2095 transitioner = new QQuickItemViewTransitioner;
2096 transitioner->setChangeListener(this);
2100 void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2102 Q_Q(QQuickItemView);
2106 // must call for every visible item to init or discard transitions
2107 QRectF viewBounds(0, position(), q->width(), q->height());
2108 for (int i=0; i<visibleItems.count(); i++)
2109 visibleItems[i]->prepareTransition(transitioner, viewBounds);
2112 void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQuickChangeSet::MoveKey, FxViewItem *> *removedItems)
2117 if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)
2118 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2119 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin();
2120 it != removedItems->end(); ) {
2121 bool isRemove = it.key().moveId < 0;
2123 FxViewItem *item = *it;
2124 item->releaseAfterTransition = true;
2125 releasePendingTransition.append(item);
2126 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
2127 it = removedItems->erase(it);
2135 bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2137 // Called for items that have been removed from visibleItems and may now be
2138 // transitioned out of the view. This applies to items that are being directly
2139 // removed, or moved to outside of the view, as well as those that are
2140 // displaced to a position outside of the view due to an insert or move.
2145 if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2146 repositionItemAt(item, item->index, 0);
2148 if (item->prepareTransition(transitioner, viewBounds)) {
2149 item->releaseAfterTransition = true;
2155 void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2157 for (int i=0; i<releasePendingTransition.count(); i++) {
2158 if (releasePendingTransition[i]->transitionableItem == item) {
2159 releaseItem(releasePendingTransition.takeAt(i));
2166 This may return 0 if the item is being created asynchronously.
2167 When the item becomes available, refill() will be called and the item
2168 will be returned on the next call to createItem().
2170 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
2172 Q_Q(QQuickItemView);
2174 if (requestedIndex == modelIndex && asynchronous)
2177 for (int i=0; i<releasePendingTransition.count(); i++) {
2178 if (releasePendingTransition[i]->index == modelIndex
2179 && !releasePendingTransition[i]->isPendingRemoval()) {
2180 releasePendingTransition[i]->releaseAfterTransition = false;
2181 return releasePendingTransition.takeAt(i);
2186 requestedIndex = modelIndex;
2189 if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
2190 item->setParentItem(q->contentItem());
2191 if (requestedIndex == modelIndex)
2192 requestedIndex = -1;
2193 FxViewItem *viewItem = newViewItem(modelIndex, item);
2195 viewItem->index = modelIndex;
2196 // do other set up for the new item that should not happen
2197 // until after bindings are evaluated
2198 initializeViewItem(viewItem);
2199 unrequestedItems.remove(item);
2209 void QQuickItemView::createdItem(int index, QQuickItem *item)
2211 Q_D(QQuickItemView);
2213 if (!d->inRequest) {
2214 d->unrequestedItems.insert(item, index);
2215 d->requestedIndex = -1;
2216 if (d->hasPendingChanges())
2220 if (d->unrequestedItems.contains(item))
2221 d->repositionPackageItemAt(item, index);
2222 else if (index == d->currentIndex)
2223 d->updateCurrent(index);
2227 void QQuickItemView::initItem(int, QQuickItem *item)
2230 item->setParentItem(contentItem());
2231 QQuickItemPrivate::get(item)->setCulled(true);
2234 void QQuickItemView::destroyingItem(QQuickItem *item)
2236 Q_D(QQuickItemView);
2237 d->unrequestedItems.remove(item);
2240 bool QQuickItemViewPrivate::releaseItem(FxViewItem *item)
2242 Q_Q(QQuickItemView);
2243 if (!item || !model)
2245 if (trackedItem == item)
2247 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
2248 itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
2249 QQuickVisualModel::ReleaseFlags flags = model->release(item->item);
2251 // item was not destroyed, and we no longer reference it.
2252 QQuickItemPrivate::get(item->item)->setCulled(true);
2253 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
2256 return flags != QQuickVisualModel::Referenced;
2259 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2261 return createComponentItem(highlightComponent, 0.0, true);
2264 QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault)
2266 Q_Q(QQuickItemView);
2268 QQuickItem *item = 0;
2270 QQmlContext *creationContext = component->creationContext();
2271 QQmlContext *context = new QQmlContext(
2272 creationContext ? creationContext : qmlContext(q));
2273 QObject *nobj = component->beginCreate(context);
2275 QQml_setParent_noEvent(context, nobj);
2276 item = qobject_cast<QQuickItem *>(nobj);
2282 } else if (createDefault) {
2283 item = new QQuickItem;
2287 QQml_setParent_noEvent(item, q->contentItem());
2288 item->setParentItem(q->contentItem());
2291 component->completeCreate();
2295 void QQuickItemViewPrivate::updateTrackedItem()
2297 Q_Q(QQuickItemView);
2298 FxViewItem *item = currentItem;
2304 q->trackedPositionChanged();
2307 void QQuickItemViewPrivate::updateUnrequestedIndexes()
2309 Q_Q(QQuickItemView);
2310 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2311 *it = model->indexOf(it.key(), q);
2314 void QQuickItemViewPrivate::updateUnrequestedPositions()
2316 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2317 repositionPackageItemAt(it.key(), it.value());
2320 void QQuickItemViewPrivate::updateVisibleIndex()
2323 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
2324 if ((*it)->index != -1) {
2325 visibleIndex = (*it)->index;