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()) {
310 d->updateSectionCriteria();
312 d->currentIndex = -1;
316 if (d->transitioner && d->transitioner->populateTransition) {
317 d->transitioner->setPopulateTransitionEnabled(true);
318 d->forceLayoutPolish();
322 connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
323 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
329 QQmlComponent *QQuickItemView::delegate() const
331 Q_D(const QQuickItemView);
333 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
334 return dataModel->delegate();
340 void QQuickItemView::setDelegate(QQmlComponent *delegate)
343 if (delegate == this->delegate())
346 d->model = new QQuickVisualDataModel(qmlContext(this));
349 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
350 int oldCount = dataModel->count();
351 dataModel->setDelegate(delegate);
352 if (isComponentComplete()) {
353 for (int i = 0; i < d->visibleItems.count(); ++i)
354 d->releaseItem(d->visibleItems.at(i));
355 d->visibleItems.clear();
356 d->releaseItem(d->currentItem);
358 d->updateSectionCriteria();
360 d->moveReason = QQuickItemViewPrivate::SetIndex;
361 d->updateCurrent(d->currentIndex);
362 if (d->highlight && d->currentItem) {
363 if (d->autoHighlight)
364 d->resetHighlightPosition();
365 d->updateTrackedItem();
367 d->moveReason = QQuickItemViewPrivate::Other;
370 if (oldCount != dataModel->count())
373 emit delegateChanged();
377 int QQuickItemView::count() const
379 Q_D(const QQuickItemView);
382 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
383 return d->model->count();
386 int QQuickItemView::currentIndex() const
388 Q_D(const QQuickItemView);
389 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
390 return d->currentIndex;
393 void QQuickItemView::setCurrentIndex(int index)
396 if (d->inRequest) // currently creating item
398 d->currentIndexCleared = (index == -1);
400 d->applyPendingChanges();
401 if (index == d->currentIndex)
403 if (isComponentComplete() && d->isValid()) {
404 d->moveReason = QQuickItemViewPrivate::SetIndex;
405 d->updateCurrent(index);
406 } else if (d->currentIndex != index) {
407 d->currentIndex = index;
408 emit currentIndexChanged();
413 bool QQuickItemView::isWrapEnabled() const
415 Q_D(const QQuickItemView);
419 void QQuickItemView::setWrapEnabled(bool wrap)
425 emit keyNavigationWrapsChanged();
428 int QQuickItemView::cacheBuffer() const
430 Q_D(const QQuickItemView);
434 void QQuickItemView::setCacheBuffer(int b)
437 if (d->buffer != b) {
439 if (isComponentComplete()) {
440 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
443 emit cacheBufferChanged();
448 Qt::LayoutDirection QQuickItemView::layoutDirection() const
450 Q_D(const QQuickItemView);
451 return d->layoutDirection;
454 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
457 if (d->layoutDirection != layoutDirection) {
458 d->layoutDirection = layoutDirection;
460 emit layoutDirectionChanged();
461 emit effectiveLayoutDirectionChanged();
465 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
467 Q_D(const QQuickItemView);
468 if (d->effectiveLayoutMirror)
469 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
471 return d->layoutDirection;
474 QQuickItemView::VerticalLayoutDirection QQuickItemView::verticalLayoutDirection() const
476 Q_D(const QQuickItemView);
477 return d->verticalLayoutDirection;
480 void QQuickItemView::setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection)
483 if (d->verticalLayoutDirection != layoutDirection) {
484 d->verticalLayoutDirection = layoutDirection;
486 emit verticalLayoutDirectionChanged();
490 QQmlComponent *QQuickItemView::header() const
492 Q_D(const QQuickItemView);
493 return d->headerComponent;
496 QQuickItem *QQuickItemView::headerItem() const
498 Q_D(const QQuickItemView);
499 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
500 return d->header ? d->header->item : 0;
503 void QQuickItemView::setHeader(QQmlComponent *headerComponent)
506 if (d->headerComponent != headerComponent) {
507 d->applyPendingChanges();
510 d->headerComponent = headerComponent;
512 d->markExtentsDirty();
514 if (isComponentComplete()) {
520 emit headerItemChanged();
522 emit headerChanged();
526 QQmlComponent *QQuickItemView::footer() const
528 Q_D(const QQuickItemView);
529 return d->footerComponent;
532 QQuickItem *QQuickItemView::footerItem() const
534 Q_D(const QQuickItemView);
535 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
536 return d->footer ? d->footer->item : 0;
539 void QQuickItemView::setFooter(QQmlComponent *footerComponent)
542 if (d->footerComponent != footerComponent) {
543 d->applyPendingChanges();
546 d->footerComponent = footerComponent;
548 if (isComponentComplete()) {
553 emit footerItemChanged();
555 emit footerChanged();
559 QQmlComponent *QQuickItemView::highlight() const
561 Q_D(const QQuickItemView);
562 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
563 return d->highlightComponent;
566 void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
569 if (highlightComponent != d->highlightComponent) {
570 d->applyPendingChanges();
571 d->highlightComponent = highlightComponent;
572 d->createHighlight();
574 d->updateHighlight();
575 emit highlightChanged();
579 QQuickItem *QQuickItemView::highlightItem() const
581 Q_D(const QQuickItemView);
582 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
583 return d->highlight ? d->highlight->item : 0;
586 bool QQuickItemView::highlightFollowsCurrentItem() const
588 Q_D(const QQuickItemView);
589 return d->autoHighlight;
592 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
595 if (d->autoHighlight != autoHighlight) {
596 d->autoHighlight = autoHighlight;
598 d->updateHighlight();
599 emit highlightFollowsCurrentItemChanged();
603 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
605 Q_D(const QQuickItemView);
606 return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
609 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
612 if (d->highlightRange == mode)
614 d->highlightRange = mode;
615 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
616 if (isComponentComplete()) {
620 emit highlightRangeModeChanged();
623 //###Possibly rename these properties, since they are very useful even without a highlight?
624 qreal QQuickItemView::preferredHighlightBegin() const
626 Q_D(const QQuickItemView);
627 return d->highlightRangeStart;
630 void QQuickItemView::setPreferredHighlightBegin(qreal start)
633 d->highlightRangeStartValid = true;
634 if (d->highlightRangeStart == start)
636 d->highlightRangeStart = start;
637 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
638 if (isComponentComplete()) {
642 emit preferredHighlightBeginChanged();
645 void QQuickItemView::resetPreferredHighlightBegin()
648 d->highlightRangeStartValid = false;
649 if (d->highlightRangeStart == 0)
651 d->highlightRangeStart = 0;
652 if (isComponentComplete()) {
656 emit preferredHighlightBeginChanged();
659 qreal QQuickItemView::preferredHighlightEnd() const
661 Q_D(const QQuickItemView);
662 return d->highlightRangeEnd;
665 void QQuickItemView::setPreferredHighlightEnd(qreal end)
668 d->highlightRangeEndValid = true;
669 if (d->highlightRangeEnd == end)
671 d->highlightRangeEnd = end;
672 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
673 if (isComponentComplete()) {
677 emit preferredHighlightEndChanged();
680 void QQuickItemView::resetPreferredHighlightEnd()
683 d->highlightRangeEndValid = false;
684 if (d->highlightRangeEnd == 0)
686 d->highlightRangeEnd = 0;
687 if (isComponentComplete()) {
691 emit preferredHighlightEndChanged();
694 int QQuickItemView::highlightMoveDuration() const
696 Q_D(const QQuickItemView);
697 return d->highlightMoveDuration;
700 void QQuickItemView::setHighlightMoveDuration(int duration)
703 if (d->highlightMoveDuration != duration) {
704 d->highlightMoveDuration = duration;
705 emit highlightMoveDurationChanged();
709 QQuickTransition *QQuickItemView::populateTransition() const
711 Q_D(const QQuickItemView);
712 return d->transitioner ? d->transitioner->populateTransition : 0;
715 void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
718 d->createTransitioner();
719 if (d->transitioner->populateTransition != transition) {
720 d->transitioner->populateTransition = transition;
721 emit populateTransitionChanged();
725 QQuickTransition *QQuickItemView::addTransition() const
727 Q_D(const QQuickItemView);
728 return d->transitioner ? d->transitioner->addTransition : 0;
731 void QQuickItemView::setAddTransition(QQuickTransition *transition)
734 d->createTransitioner();
735 if (d->transitioner->addTransition != transition) {
736 d->transitioner->addTransition = transition;
737 emit addTransitionChanged();
741 QQuickTransition *QQuickItemView::addDisplacedTransition() const
743 Q_D(const QQuickItemView);
744 return d->transitioner ? d->transitioner->addDisplacedTransition : 0;
747 void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
750 d->createTransitioner();
751 if (d->transitioner->addDisplacedTransition != transition) {
752 d->transitioner->addDisplacedTransition = transition;
753 emit addDisplacedTransitionChanged();
757 QQuickTransition *QQuickItemView::moveTransition() const
759 Q_D(const QQuickItemView);
760 return d->transitioner ? d->transitioner->moveTransition : 0;
763 void QQuickItemView::setMoveTransition(QQuickTransition *transition)
766 d->createTransitioner();
767 if (d->transitioner->moveTransition != transition) {
768 d->transitioner->moveTransition = transition;
769 emit moveTransitionChanged();
773 QQuickTransition *QQuickItemView::moveDisplacedTransition() const
775 Q_D(const QQuickItemView);
776 return d->transitioner ? d->transitioner->moveDisplacedTransition : 0;
779 void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
782 d->createTransitioner();
783 if (d->transitioner->moveDisplacedTransition != transition) {
784 d->transitioner->moveDisplacedTransition = transition;
785 emit moveDisplacedTransitionChanged();
789 QQuickTransition *QQuickItemView::removeTransition() const
791 Q_D(const QQuickItemView);
792 return d->transitioner ? d->transitioner->removeTransition : 0;
795 void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
798 d->createTransitioner();
799 if (d->transitioner->removeTransition != transition) {
800 d->transitioner->removeTransition = transition;
801 emit removeTransitionChanged();
805 QQuickTransition *QQuickItemView::removeDisplacedTransition() const
807 Q_D(const QQuickItemView);
808 return d->transitioner ? d->transitioner->removeDisplacedTransition : 0;
811 void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
814 d->createTransitioner();
815 if (d->transitioner->removeDisplacedTransition != transition) {
816 d->transitioner->removeDisplacedTransition = transition;
817 emit removeDisplacedTransitionChanged();
821 QQuickTransition *QQuickItemView::displacedTransition() const
823 Q_D(const QQuickItemView);
824 return d->transitioner ? d->transitioner->displacedTransition : 0;
827 void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
830 d->createTransitioner();
831 if (d->transitioner->displacedTransition != transition) {
832 d->transitioner->displacedTransition = transition;
833 emit displacedTransitionChanged();
837 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
842 if (mode < QQuickItemView::Beginning || mode > QQuickItemView::SnapPosition)
845 applyPendingChanges();
846 int idx = qMax(qMin(index, model->count()-1), 0);
848 qreal pos = isContentFlowReversed() ? -position() - size() : position();
849 FxViewItem *item = visibleItem(idx);
851 if (layoutOrientation() == Qt::Vertical)
852 maxExtent = isContentFlowReversed() ? q->minYExtent()-size(): -q->maxYExtent();
854 maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
856 int itemPos = positionAt(idx);
857 changedVisibleIndex(idx);
858 // save the currently visible items in case any of them end up visible again
859 QList<FxViewItem *> oldVisible = visibleItems;
860 visibleItems.clear();
861 setPosition(qMin(qreal(itemPos), maxExtent));
862 // now release the reference to all the old visible items.
863 for (int i = 0; i < oldVisible.count(); ++i)
864 releaseItem(oldVisible.at(i));
865 item = visibleItem(idx);
868 const qreal itemPos = item->position();
870 case QQuickItemView::Beginning:
872 if (index < 0 && header)
875 case QQuickItemView::Center:
876 pos = itemPos - (size() - item->size())/2;
878 case QQuickItemView::End:
879 pos = itemPos - size() + item->size();
880 if (index >= model->count() && footer)
883 case QQuickItemView::Visible:
884 if (itemPos > pos + size())
885 pos = itemPos - size() + item->size();
886 else if (item->endPosition() <= pos)
889 case QQuickItemView::Contain:
890 if (item->endPosition() >= pos + size())
891 pos = itemPos - size() + item->size();
895 case QQuickItemView::SnapPosition:
896 pos = itemPos - highlightRangeStart;
899 pos = qMin(pos, maxExtent);
901 if (layoutOrientation() == Qt::Vertical)
902 minExtent = isContentFlowReversed() ? q->maxYExtent()-size(): -q->minYExtent();
904 minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
905 pos = qMax(pos, minExtent);
906 moveReason = QQuickItemViewPrivate::Other;
912 resetHighlightPosition();
919 void QQuickItemView::positionViewAtIndex(int index, int mode)
922 if (!d->isValid() || index < 0 || index >= d->model->count())
924 d->positionViewAtIndex(index, mode);
928 void QQuickItemView::positionViewAtBeginning()
933 d->positionViewAtIndex(-1, Beginning);
936 void QQuickItemView::positionViewAtEnd()
941 d->positionViewAtIndex(d->model->count(), End);
944 int QQuickItemView::indexAt(qreal x, qreal y) const
946 Q_D(const QQuickItemView);
947 for (int i = 0; i < d->visibleItems.count(); ++i) {
948 const FxViewItem *item = d->visibleItems.at(i);
949 if (item->contains(x, y))
956 QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
958 Q_D(const QQuickItemView);
959 for (int i = 0; i < d->visibleItems.count(); ++i) {
960 const FxViewItem *item = d->visibleItems.at(i);
961 if (item->contains(x, y))
968 void QQuickItemViewPrivate::applyPendingChanges()
971 if (q->isComponentComplete() && currentChanges.hasPendingChanges())
975 int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector<QQuickChangeSet::Remove> &changes) const
977 for (int i=0; i<changes.count(); i++) {
978 for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
979 if (changes[i].moveKey(j) == key)
986 qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
988 Q_Q(const QQuickItemView);
990 qreal highlightStart;
992 qreal endPositionFirstItem = 0;
993 qreal extent = -startPosition() + axisData.startMargin;
994 if (isContentFlowReversed()) {
995 if (model && model->count())
996 endPositionFirstItem = positionAt(model->count()-1);
998 extent += headerSize();
999 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1000 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1001 extent += footerSize();
1002 qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
1003 if (extent < maxExtentAlongAxis)
1004 extent = maxExtentAlongAxis;
1006 endPositionFirstItem = endPositionAt(0);
1007 highlightStart = highlightRangeStart;
1008 highlightEnd = highlightRangeEnd;
1009 extent += headerSize();
1011 if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1012 extent += highlightStart;
1013 FxViewItem *firstItem = visibleItem(0);
1015 extent -= firstItem->sectionSize();
1016 extent = isContentFlowReversed()
1017 ? qMin(extent, endPositionFirstItem + highlightEnd)
1018 : qMax(extent, -(endPositionFirstItem - highlightEnd));
1023 qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
1025 Q_Q(const QQuickItemView);
1027 qreal highlightStart;
1029 qreal lastItemPosition = 0;
1031 if (isContentFlowReversed()) {
1032 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1033 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1034 lastItemPosition = endPosition();
1036 highlightStart = highlightRangeStart;
1037 highlightEnd = highlightRangeEnd;
1038 if (model && model->count())
1039 lastItemPosition = positionAt(model->count()-1);
1041 if (!model || !model->count()) {
1042 if (!isContentFlowReversed())
1043 maxExtent = header ? -headerSize() : 0;
1044 extent += forXAxis ? q->width() : q->height();
1045 } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1046 extent = -(lastItemPosition - highlightStart);
1047 if (highlightEnd != highlightStart) {
1048 extent = isContentFlowReversed()
1049 ? qMax(extent, -(endPosition() - highlightEnd))
1050 : qMin(extent, -(endPosition() - highlightEnd));
1053 extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
1055 if (isContentFlowReversed()) {
1056 extent -= headerSize();
1057 extent -= axisData.endMargin;
1059 extent -= footerSize();
1060 extent -= axisData.endMargin;
1061 qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
1062 if (extent > minExtentAlongAxis)
1063 extent = minExtentAlongAxis;
1069 // for debugging only
1070 void QQuickItemViewPrivate::checkVisible() const
1073 for (int i = 0; i < visibleItems.count(); ++i) {
1074 FxViewItem *item = visibleItems.at(i);
1075 if (item->index == -1) {
1077 } else if (item->index != visibleIndex + i - skip) {
1078 qFatal("index %d %d %d", visibleIndex, i, item->index);
1083 // for debugging only
1084 void QQuickItemViewPrivate::showVisibleItems() const
1086 qDebug() << "Visible items:";
1087 for (int i = 0; i < visibleItems.count(); ++i) {
1088 qDebug() << "\t" << visibleItems[i]->index
1089 << visibleItems[i]->item->objectName()
1090 << visibleItems[i]->position();
1094 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1096 Q_Q(QQuickItemView);
1097 QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1098 if (!q->isComponentComplete())
1101 if (header && header->item == item) {
1104 if (!q->isMoving() && !q->isFlicking())
1106 } else if (footer && footer->item == item) {
1109 if (!q->isMoving() && !q->isFlicking())
1113 if (currentItem && currentItem->item == item) {
1114 // don't allow item movement transitions to trigger a re-layout and
1115 // start new transitions
1116 bool prevInLayout = inLayout;
1118 FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : 0;
1119 if (actualItem && actualItem->transitionRunning())
1123 inLayout = prevInLayout;
1126 if (trackedItem && trackedItem->item == item)
1127 q->trackedPositionChanged();
1130 void QQuickItemView::destroyRemoved()
1132 Q_D(QQuickItemView);
1133 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1134 it != d->visibleItems.end();) {
1135 FxViewItem *item = *it;
1136 if (item->index == -1 && item->attached->delayRemove() == false) {
1137 if (d->transitioner && d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)) {
1138 // don't remove from visibleItems until next layout()
1139 d->runDelayedRemoveTransition = true;
1140 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1143 d->releaseItem(item);
1144 it = d->visibleItems.erase(it);
1151 // Correct the positioning of the items
1152 d->forceLayoutPolish();
1155 void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1157 Q_D(QQuickItemView);
1159 if (d->transitioner)
1160 d->transitioner->setPopulateTransitionEnabled(true);
1161 d->moveReason = QQuickItemViewPrivate::SetIndex;
1163 if (d->highlight && d->currentItem) {
1164 if (d->autoHighlight)
1165 d->resetHighlightPosition();
1166 d->updateTrackedItem();
1168 d->moveReason = QQuickItemViewPrivate::Other;
1169 emit countChanged();
1170 if (d->transitioner && d->transitioner->populateTransition)
1171 d->forceLayoutPolish();
1174 d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
1175 d->bufferedChanges.applyChanges(changeSet);
1177 if (d->bufferedChanges.hasPendingChanges()) {
1178 d->currentChanges.applyBufferedChanges(d->bufferedChanges);
1179 d->bufferedChanges.reset();
1181 d->currentChanges.prepare(d->currentIndex, d->itemCount);
1182 d->currentChanges.applyChanges(changeSet);
1188 void QQuickItemView::animStopped()
1190 Q_D(QQuickItemView);
1191 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1192 d->refillOrLayout();
1193 if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1194 d->updateHighlight();
1198 void QQuickItemView::trackedPositionChanged()
1200 Q_D(QQuickItemView);
1201 if (!d->trackedItem || !d->currentItem)
1203 if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
1204 qreal trackedPos = d->trackedItem->position();
1205 qreal trackedSize = d->trackedItem->size();
1206 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1207 qreal pos = viewPos;
1208 if (d->haveHighlightRange) {
1209 if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1210 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1211 if (trackedPos < pos + d->highlightRangeStart)
1212 pos = trackedPos - d->highlightRangeStart;
1213 if (d->highlightRange != StrictlyEnforceRange) {
1214 if (pos > d->endPosition() - d->size())
1215 pos = d->endPosition() - d->size();
1216 if (pos < d->startPosition())
1217 pos = d->startPosition();
1220 if (d->trackedItem != d->currentItem) {
1221 // also make section header visible
1222 trackedPos -= d->currentItem->sectionSize();
1223 trackedSize += d->currentItem->sectionSize();
1225 qreal trackedEndPos = d->trackedItem->endPosition();
1226 qreal toItemPos = d->currentItem->position();
1227 qreal toItemEndPos = d->currentItem->endPosition();
1228 if (d->showHeaderForIndex(d->currentIndex)) {
1229 qreal startOffset = -d->contentStartOffset();
1230 trackedPos -= startOffset;
1231 trackedEndPos -= startOffset;
1232 toItemPos -= startOffset;
1233 toItemEndPos -= startOffset;
1234 } else if (d->showFooterForIndex(d->currentIndex)) {
1235 qreal endOffset = d->footerSize();
1236 if (d->layoutOrientation() == Qt::Vertical) {
1237 if (d->isContentFlowReversed())
1238 endOffset += d->vData.startMargin;
1240 endOffset += d->vData.endMargin;
1242 if (d->isContentFlowReversed())
1243 endOffset += d->hData.startMargin;
1245 endOffset += d->hData.endMargin;
1247 trackedPos += endOffset;
1248 trackedEndPos += endOffset;
1249 toItemPos += endOffset;
1250 toItemEndPos += endOffset;
1253 if (trackedEndPos >= viewPos + d->size()
1254 && toItemEndPos >= viewPos + d->size()) {
1255 if (trackedEndPos <= toItemEndPos) {
1256 pos = trackedEndPos - d->size();
1257 if (trackedSize > d->size())
1260 pos = toItemEndPos - d->size();
1261 if (d->currentItem->size() > d->size())
1262 pos = d->currentItem->position();
1265 if (trackedPos < pos && toItemPos < pos)
1266 pos = qMax(trackedPos, toItemPos);
1268 if (viewPos != pos) {
1270 d->calcVelocity = true;
1271 d->setPosition(pos);
1272 d->calcVelocity = false;
1277 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1279 Q_D(QQuickItemView);
1280 d->markExtentsDirty();
1281 if (isComponentComplete() && d->isValid())
1282 d->forceLayoutPolish();
1283 QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1286 qreal QQuickItemView::minYExtent() const
1288 Q_D(const QQuickItemView);
1289 if (d->layoutOrientation() == Qt::Horizontal)
1290 return QQuickFlickable::minYExtent();
1292 if (d->vData.minExtentDirty) {
1293 d->minExtent = d->minExtentForAxis(d->vData, false);
1294 d->vData.minExtentDirty = false;
1297 return d->minExtent;
1300 qreal QQuickItemView::maxYExtent() const
1302 Q_D(const QQuickItemView);
1303 if (d->layoutOrientation() == Qt::Horizontal)
1306 if (d->vData.maxExtentDirty) {
1307 d->maxExtent = d->maxExtentForAxis(d->vData, false);
1308 d->vData.maxExtentDirty = false;
1311 return d->maxExtent;
1314 qreal QQuickItemView::minXExtent() const
1316 Q_D(const QQuickItemView);
1317 if (d->layoutOrientation() == Qt::Vertical)
1318 return QQuickFlickable::minXExtent();
1320 if (d->hData.minExtentDirty) {
1321 d->minExtent = d->minExtentForAxis(d->hData, true);
1322 d->hData.minExtentDirty = false;
1325 return d->minExtent;
1328 qreal QQuickItemView::maxXExtent() const
1330 Q_D(const QQuickItemView);
1331 if (d->layoutOrientation() == Qt::Vertical)
1334 if (d->hData.maxExtentDirty) {
1335 d->maxExtent = d->maxExtentForAxis(d->hData, true);
1336 d->hData.maxExtentDirty = false;
1339 return d->maxExtent;
1342 void QQuickItemView::setContentX(qreal pos)
1344 Q_D(QQuickItemView);
1345 // Positioning the view manually should override any current movement state
1346 d->moveReason = QQuickItemViewPrivate::Other;
1347 QQuickFlickable::setContentX(pos);
1350 void QQuickItemView::setContentY(qreal pos)
1352 Q_D(QQuickItemView);
1353 // Positioning the view manually should override any current movement state
1354 d->moveReason = QQuickItemViewPrivate::Other;
1355 QQuickFlickable::setContentY(pos);
1358 qreal QQuickItemView::originX() const
1360 Q_D(const QQuickItemView);
1361 if (d->layoutOrientation() == Qt::Horizontal
1362 && effectiveLayoutDirection() == Qt::RightToLeft
1363 && contentWidth() < width()) {
1364 return d->lastPosition() - d->footerSize();
1366 return QQuickFlickable::originX();
1369 qreal QQuickItemView::originY() const
1371 Q_D(const QQuickItemView);
1372 if (d->layoutOrientation() == Qt::Vertical
1373 && d->verticalLayoutDirection == QQuickItemView::BottomToTop
1374 && contentHeight() < height()) {
1375 return d->lastPosition() - d->footerSize();
1377 return QQuickFlickable::originY();
1380 void QQuickItemView::updatePolish()
1382 Q_D(QQuickItemView);
1383 QQuickFlickable::updatePolish();
1387 void QQuickItemView::componentComplete()
1389 Q_D(QQuickItemView);
1390 if (d->model && d->ownModel)
1391 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1393 QQuickFlickable::componentComplete();
1395 d->updateSectionCriteria();
1398 d->updateViewport();
1399 d->setPosition(d->contentStartOffset());
1400 if (d->transitioner)
1401 d->transitioner->setPopulateTransitionEnabled(true);
1405 d->moveReason = QQuickItemViewPrivate::SetIndex;
1406 if (d->currentIndex < 0 && !d->currentIndexCleared)
1407 d->updateCurrent(0);
1409 d->updateCurrent(d->currentIndex);
1410 if (d->highlight && d->currentItem) {
1411 if (d->autoHighlight)
1412 d->resetHighlightPosition();
1413 d->updateTrackedItem();
1415 d->moveReason = QQuickItemViewPrivate::Other;
1418 if (d->model && d->model->count())
1419 emit countChanged();
1424 QQuickItemViewPrivate::QQuickItemViewPrivate()
1426 , buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
1427 , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
1430 , currentIndex(-1), currentItem(0)
1431 , trackedItem(0), requestedIndex(-1)
1432 , highlightComponent(0), highlight(0)
1433 , highlightRange(QQuickItemView::NoHighlightRange)
1434 , highlightRangeStart(0), highlightRangeEnd(0)
1435 , highlightMoveDuration(150)
1436 , headerComponent(0), header(0), footerComponent(0), footer(0)
1438 , minExtent(0), maxExtent(0)
1439 , ownModel(false), wrap(false)
1440 , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1441 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1442 , fillCacheBuffer(false), inRequest(false)
1443 , runDelayedRemoveTransition(false)
1445 bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
1446 bufferPause.setLoopCount(1);
1447 bufferPause.setDuration(16);
1450 QQuickItemViewPrivate::~QQuickItemViewPrivate()
1453 transitioner->setChangeListener(0);
1454 delete transitioner;
1457 bool QQuickItemViewPrivate::isValid() const
1459 return model && model->count() && model->isValid();
1462 qreal QQuickItemViewPrivate::position() const
1464 Q_Q(const QQuickItemView);
1465 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1468 qreal QQuickItemViewPrivate::size() const
1470 Q_Q(const QQuickItemView);
1471 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1474 qreal QQuickItemViewPrivate::startPosition() const
1476 return isContentFlowReversed() ? -lastPosition() : originPosition();
1479 qreal QQuickItemViewPrivate::endPosition() const
1481 return isContentFlowReversed() ? -originPosition() : lastPosition();
1484 qreal QQuickItemViewPrivate::contentStartOffset() const
1486 qreal pos = -headerSize();
1487 if (layoutOrientation() == Qt::Vertical) {
1488 if (isContentFlowReversed())
1489 pos -= vData.endMargin;
1491 pos -= vData.startMargin;
1493 if (isContentFlowReversed())
1494 pos -= hData.endMargin;
1496 pos -= hData.startMargin;
1501 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1503 if (visibleItems.count()) {
1504 int i = visibleItems.count() - 1;
1505 while (i > 0 && visibleItems.at(i)->index == -1)
1507 if (visibleItems.at(i)->index != -1)
1508 return visibleItems.at(i)->index;
1510 return defaultValue;
1513 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1514 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1515 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1516 FxViewItem *item = visibleItems.at(i);
1517 if (item->index == modelIndex)
1524 // should rename to firstItemInView() to avoid confusion with other "*visible*" methods
1525 // that don't look at the view position and size
1526 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
1527 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1528 for (int i = 0; i < visibleItems.count(); ++i) {
1529 FxViewItem *item = visibleItems.at(i);
1530 if (item->index != -1 && item->endPosition() > pos)
1533 return visibleItems.count() ? visibleItems.first() : 0;
1536 int QQuickItemViewPrivate::findLastIndexInView() const
1538 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1539 for (int i=visibleItems.count() - 1; i>=0; i--) {
1540 if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
1541 return visibleItems.at(i)->index;
1546 // Map a model index to visibleItems list index.
1547 // These may differ if removed items are still present in the visible list,
1548 // e.g. doing a removal animation
1549 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1551 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1553 for (int i = 0; i < visibleItems.count(); ++i) {
1554 FxViewItem *item = visibleItems.at(i);
1555 if (item->index == modelIndex)
1557 if (item->index > modelIndex)
1560 return -1; // Not in visibleList
1563 void QQuickItemViewPrivate::init()
1565 Q_Q(QQuickItemView);
1566 q->setFlag(QQuickItem::ItemIsFocusScope);
1567 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1568 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1571 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1573 Q_Q(QQuickItemView);
1574 applyPendingChanges();
1575 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1577 currentItem->attached->setIsCurrentItem(false);
1578 releaseItem(currentItem);
1580 currentIndex = modelIndex;
1581 emit q->currentIndexChanged();
1582 emit q->currentItemChanged();
1584 } else if (currentIndex != modelIndex) {
1585 currentIndex = modelIndex;
1586 emit q->currentIndexChanged();
1591 if (currentItem && currentIndex == modelIndex) {
1596 FxViewItem *oldCurrentItem = currentItem;
1597 int oldCurrentIndex = currentIndex;
1598 currentIndex = modelIndex;
1599 currentItem = createItem(modelIndex, false);
1600 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1601 oldCurrentItem->attached->setIsCurrentItem(false);
1603 currentItem->item->setFocus(true);
1604 currentItem->attached->setIsCurrentItem(true);
1605 initializeCurrentItem();
1609 if (oldCurrentIndex != currentIndex)
1610 emit q->currentIndexChanged();
1611 if (oldCurrentItem != currentItem)
1612 emit q->currentItemChanged();
1613 releaseItem(oldCurrentItem);
1616 void QQuickItemViewPrivate::clear()
1618 currentChanges.reset();
1621 for (int i = 0; i < visibleItems.count(); ++i)
1622 releaseItem(visibleItems.at(i));
1623 visibleItems.clear();
1626 for (int i = 0; i < releasePendingTransition.count(); ++i) {
1627 releasePendingTransition.at(i)->releaseAfterTransition = false;
1628 releaseItem(releasePendingTransition.at(i));
1630 releasePendingTransition.clear();
1632 releaseItem(currentItem);
1637 if (requestedIndex >= 0) {
1639 model->cancel(requestedIndex);
1640 requestedIndex = -1;
1648 void QQuickItemViewPrivate::mirrorChange()
1650 Q_Q(QQuickItemView);
1652 emit q->effectiveLayoutDirectionChanged();
1655 void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1657 Q_Q(QQuickItemView);
1658 fillCacheBuffer = true;
1662 void QQuickItemViewPrivate::refill()
1664 qreal s = qMax(size(), qreal(0.));
1665 if (isContentFlowReversed())
1666 refill(-position()-s, -position());
1668 refill(position(), position()+s);
1671 void QQuickItemViewPrivate::refill(qreal from, qreal to)
1673 Q_Q(QQuickItemView);
1674 if (!isValid() || !q->isComponentComplete())
1678 currentChanges.reset();
1680 int prevCount = itemCount;
1681 itemCount = model->count();
1682 qreal bufferFrom = from - buffer;
1683 qreal bufferTo = to + buffer;
1684 qreal fillFrom = from;
1687 bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
1688 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1690 if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1692 // We've already created a new delegate this frame.
1693 // Just schedule a buffer refill.
1694 bufferPause.start();
1696 if (bufferMode & BufferAfter)
1698 if (bufferMode & BufferBefore)
1699 fillFrom = bufferFrom;
1700 added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, true);
1704 if (added || removed) {
1706 updateBeginningEnd();
1707 visibleItemsChanged();
1713 if (prevCount != itemCount)
1714 emit q->countChanged();
1717 void QQuickItemViewPrivate::regenerate()
1719 Q_Q(QQuickItemView);
1720 if (q->isComponentComplete()) {
1721 currentChanges.reset();
1730 setPosition(contentStartOffset());
1732 updateCurrent(currentIndex);
1736 void QQuickItemViewPrivate::updateViewport()
1738 Q_Q(QQuickItemView);
1739 qreal extra = headerSize() + footerSize();
1740 qreal contentSize = isValid() ? (endPosition() - startPosition()) : 0.0;
1741 if (layoutOrientation() == Qt::Vertical)
1742 q->setContentHeight(contentSize + extra);
1744 q->setContentWidth(contentSize + extra);
1747 void QQuickItemViewPrivate::layout()
1749 Q_Q(QQuickItemView);
1755 if (!isValid() && !visibleItems.count()) {
1757 setPosition(contentStartOffset());
1759 transitioner->setPopulateTransitionEnabled(false);
1764 if (runDelayedRemoveTransition && transitioner
1765 && transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1766 // assume that any items moving now are moving due to the remove - if they schedule
1767 // a different transition, that will override this one anyway
1768 for (int i=0; i<visibleItems.count(); i++)
1769 visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
1772 ChangeResult insertionPosChanges;
1773 ChangeResult removalPosChanges;
1774 if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
1775 if (fillCacheBuffer) {
1776 fillCacheBuffer = false;
1782 forceLayout = false;
1784 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
1785 for (int i=0; i<visibleItems.count(); i++) {
1786 if (!visibleItems.at(i)->transitionScheduledOrRunning())
1787 visibleItems.at(i)->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
1792 layoutVisibleItems();
1794 int lastIndexInView = findLastIndexInView();
1799 if (!q->isMoving() && !q->isFlicking()) {
1807 updateUnrequestedPositions();
1810 // items added in the last refill() may need to be transitioned in - e.g. a remove
1811 // causes items to slide up into view
1812 if (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false)
1813 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1814 translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
1817 prepareVisibleItemTransitions();
1819 QRectF viewBounds(0, position(), q->width(), q->height());
1820 for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin();
1821 it != releasePendingTransition.end(); ) {
1822 FxViewItem *item = *it;
1823 if (prepareNonVisibleItemTransition(item, viewBounds)) {
1827 it = releasePendingTransition.erase(it);
1831 for (int i=0; i<visibleItems.count(); i++)
1832 visibleItems[i]->startTransition(transitioner);
1833 for (int i=0; i<releasePendingTransition.count(); i++)
1834 releasePendingTransition[i]->startTransition(transitioner);
1836 transitioner->setPopulateTransitionEnabled(false);
1837 transitioner->resetTargetLists();
1840 runDelayedRemoveTransition = false;
1844 bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
1846 Q_Q(QQuickItemView);
1847 if (!q->isComponentComplete() || !hasPendingChanges())
1850 if (bufferedChanges.hasPendingChanges()) {
1851 currentChanges.applyBufferedChanges(bufferedChanges);
1852 bufferedChanges.reset();
1855 updateUnrequestedIndexes();
1856 moveReason = QQuickItemViewPrivate::Other;
1858 FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
1859 int prevItemCount = itemCount;
1860 int prevVisibleItemsCount = visibleItems.count();
1861 bool visibleAffected = false;
1862 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
1863 || !currentChanges.pendingChanges.inserts().isEmpty();
1865 FxViewItem *prevFirstVisible = firstVisibleItem();
1866 QQmlNullableValue<qreal> prevViewPos;
1867 int prevFirstVisibleIndex = -1;
1868 if (prevFirstVisible) {
1869 prevViewPos = prevFirstVisible->position();
1870 prevFirstVisibleIndex = prevFirstVisible->index;
1872 qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
1874 totalInsertionResult->visiblePos = prevViewPos;
1875 totalRemovalResult->visiblePos = prevViewPos;
1877 const QVector<QQuickChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
1878 const QVector<QQuickChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
1879 ChangeResult insertionResult(prevViewPos);
1880 ChangeResult removalResult(prevViewPos);
1882 int removedCount = 0;
1883 for (int i=0; i<removals.count(); i++) {
1884 itemCount -= removals[i].count;
1885 if (applyRemovalChange(removals[i], &removalResult, &removedCount))
1886 visibleAffected = true;
1887 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
1888 visibleAffected = true;
1889 const int correctedFirstVisibleIndex = prevFirstVisibleIndex - removalResult.countChangeBeforeVisible;
1890 if (correctedFirstVisibleIndex >= 0 && removals[i].index < correctedFirstVisibleIndex) {
1891 if (removals[i].index + removals[i].count < correctedFirstVisibleIndex)
1892 removalResult.countChangeBeforeVisible += removals[i].count;
1894 removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - removals[i].index);
1897 if (runDelayedRemoveTransition) {
1898 QQuickChangeSet::Remove removal;
1899 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
1900 FxViewItem *item = *it;
1901 if (item->index == -1 && !item->attached->delayRemove()) {
1902 removeItem(item, removal, &removalResult);
1904 it = visibleItems.erase(it);
1910 *totalRemovalResult += removalResult;
1911 if (!removals.isEmpty()) {
1912 updateVisibleIndex();
1914 // set positions correctly for the next insertion
1915 if (!insertions.isEmpty()) {
1916 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1917 layoutVisibleItems(removals.first().index);
1921 QList<FxViewItem *> newItems;
1922 QList<MovedItem> movingIntoView;
1924 for (int i=0; i<insertions.count(); i++) {
1925 bool wasEmpty = visibleItems.isEmpty();
1926 if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
1927 visibleAffected = true;
1928 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
1929 visibleAffected = true;
1930 if (wasEmpty && !visibleItems.isEmpty())
1931 resetFirstItemPosition();
1932 *totalInsertionResult += insertionResult;
1934 // set positions correctly for the next insertion
1935 if (i < insertions.count() - 1) {
1936 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1937 layoutVisibleItems(insertions[i].index);
1939 itemCount += insertions[i].count;
1941 for (int i=0; i<newItems.count(); i++)
1942 newItems.at(i)->attached->emitAdd();
1944 // for each item that was moved directly into the view as a result of a move(),
1945 // find the index it was moved from in order to set its initial position, so that we
1946 // can transition it from this "original" position to its new position in the view
1947 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
1948 for (int i=0; i<movingIntoView.count(); i++) {
1949 int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
1950 if (fromIndex >= 0) {
1951 if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
1952 repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
1954 repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
1955 movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
1960 // reposition visibleItems.first() correctly so that the content y doesn't jump
1961 if (removedCount != prevVisibleItemsCount)
1962 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1964 // Whatever removed/moved items remain are no longer visible items.
1965 prepareRemoveTransitions(¤tChanges.removedItems);
1966 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
1967 it != currentChanges.removedItems.end(); ++it) {
1968 releaseItem(it.value());
1970 currentChanges.removedItems.clear();
1972 if (currentChanges.currentChanged) {
1973 if (currentChanges.currentRemoved && currentItem) {
1974 currentItem->attached->setIsCurrentItem(false);
1975 releaseItem(currentItem);
1978 if (!currentIndexCleared)
1979 updateCurrent(currentChanges.newCurrentIndex);
1982 if (!visibleAffected)
1983 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
1984 currentChanges.reset();
1987 if (prevItemCount != itemCount)
1988 emit q->countChanged();
1989 if (!visibleAffected && viewportChanged)
1992 return visibleAffected;
1995 bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount)
1997 Q_Q(QQuickItemView);
1998 bool visibleAffected = false;
2000 if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
2001 if (removal.index > visibleItems.last()->index)
2002 removeResult->countChangeAfterVisibleItems += removal.count;
2004 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
2007 QList<FxViewItem*>::Iterator it = visibleItems.begin();
2008 while (it != visibleItems.end()) {
2009 FxViewItem *item = *it;
2010 if (item->index == -1 || item->index < removal.index) {
2011 // already removed, or before removed items
2012 if (!visibleAffected && item->index < removal.index)
2013 visibleAffected = true;
2015 } else if (item->index >= removal.index + removal.count) {
2016 // after removed items
2017 item->index -= removal.count;
2018 if (removal.isMove())
2019 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2021 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2025 visibleAffected = true;
2026 if (!removal.isMove())
2027 item->attached->emitRemove();
2029 if (item->attached->delayRemove() && !removal.isMove()) {
2031 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2034 removeItem(item, removal, removeResult);
2035 if (!removal.isMove())
2037 it = visibleItems.erase(it);
2042 return visibleAffected;
2045 void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult)
2047 if (removeResult->visiblePos.isValid()) {
2048 if (item->position() < removeResult->visiblePos)
2049 removeResult->sizeChangesBeforeVisiblePos += item->size();
2051 removeResult->sizeChangesAfterVisiblePos += item->size();
2053 if (removal.isMove()) {
2054 currentChanges.removedItems.insert(removal.moveKey(item->index), item);
2055 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2057 // track item so it is released later
2058 currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item);
2060 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2061 removeResult->changedFirstItem = true;
2064 void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2065 qreal prevVisibleItemsFirstPos,
2066 FxViewItem *prevFirstVisible,
2067 ChangeResult *insertionResult,
2068 ChangeResult *removalResult)
2070 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2072 // reposition visibleItems.first() correctly so that the content y doesn't jump
2073 if (visibleItems.count()) {
2074 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2075 resetFirstItemPosition(prevVisibleItemsFirstPos);
2077 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2078 && prevFirstVisible != *visibleItems.constBegin()) {
2079 // the previous visibleItems.first() was also the first visible item, and it has been
2080 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2081 if (!insertionResult->changedFirstItem)
2082 resetFirstItemPosition(prevVisibleItemsFirstPos);
2084 } else if (prevViewPos.isValid()) {
2085 qreal moveForwardsBy = 0;
2086 qreal moveBackwardsBy = 0;
2088 // shift visibleItems.first() relative to the number of added/removed items
2089 if (visibleItems.first()->position() > prevViewPos) {
2090 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2091 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2092 } else if (visibleItems.first()->position() < prevViewPos) {
2093 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2094 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2096 adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2098 insertionResult->reset();
2099 removalResult->reset();
2103 void QQuickItemViewPrivate::createTransitioner()
2105 if (!transitioner) {
2106 transitioner = new QQuickItemViewTransitioner;
2107 transitioner->setChangeListener(this);
2111 void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2113 Q_Q(QQuickItemView);
2117 // must call for every visible item to init or discard transitions
2118 QRectF viewBounds(0, position(), q->width(), q->height());
2119 for (int i=0; i<visibleItems.count(); i++)
2120 visibleItems[i]->prepareTransition(transitioner, viewBounds);
2123 void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQuickChangeSet::MoveKey, FxViewItem *> *removedItems)
2128 if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)
2129 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2130 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin();
2131 it != removedItems->end(); ) {
2132 bool isRemove = it.key().moveId < 0;
2134 FxViewItem *item = *it;
2135 item->releaseAfterTransition = true;
2136 releasePendingTransition.append(item);
2137 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
2138 it = removedItems->erase(it);
2146 bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2148 // Called for items that have been removed from visibleItems and may now be
2149 // transitioned out of the view. This applies to items that are being directly
2150 // removed, or moved to outside of the view, as well as those that are
2151 // displaced to a position outside of the view due to an insert or move.
2156 if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2157 repositionItemAt(item, item->index, 0);
2159 if (item->prepareTransition(transitioner, viewBounds)) {
2160 item->releaseAfterTransition = true;
2166 void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2168 for (int i=0; i<releasePendingTransition.count(); i++) {
2169 if (releasePendingTransition[i]->transitionableItem == item) {
2170 releaseItem(releasePendingTransition.takeAt(i));
2177 This may return 0 if the item is being created asynchronously.
2178 When the item becomes available, refill() will be called and the item
2179 will be returned on the next call to createItem().
2181 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
2183 Q_Q(QQuickItemView);
2185 if (requestedIndex == modelIndex && asynchronous)
2188 for (int i=0; i<releasePendingTransition.count(); i++) {
2189 if (releasePendingTransition[i]->index == modelIndex
2190 && !releasePendingTransition[i]->isPendingRemoval()) {
2191 releasePendingTransition[i]->releaseAfterTransition = false;
2192 return releasePendingTransition.takeAt(i);
2197 requestedIndex = modelIndex;
2200 if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
2201 item->setParentItem(q->contentItem());
2202 if (requestedIndex == modelIndex)
2203 requestedIndex = -1;
2204 FxViewItem *viewItem = newViewItem(modelIndex, item);
2206 viewItem->index = modelIndex;
2207 // do other set up for the new item that should not happen
2208 // until after bindings are evaluated
2209 initializeViewItem(viewItem);
2210 unrequestedItems.remove(item);
2220 void QQuickItemView::createdItem(int index, QQuickItem *item)
2222 Q_D(QQuickItemView);
2224 if (!d->inRequest) {
2225 d->unrequestedItems.insert(item, index);
2226 d->requestedIndex = -1;
2227 if (d->hasPendingChanges())
2231 if (d->unrequestedItems.contains(item))
2232 d->repositionPackageItemAt(item, index);
2233 else if (index == d->currentIndex)
2234 d->updateCurrent(index);
2238 void QQuickItemView::initItem(int, QQuickItem *item)
2241 item->setParentItem(contentItem());
2242 QQuickItemPrivate::get(item)->setCulled(true);
2245 void QQuickItemView::destroyingItem(QQuickItem *item)
2247 Q_D(QQuickItemView);
2248 d->unrequestedItems.remove(item);
2251 bool QQuickItemViewPrivate::releaseItem(FxViewItem *item)
2253 Q_Q(QQuickItemView);
2254 if (!item || !model)
2256 if (trackedItem == item)
2258 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
2259 itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
2260 QQuickVisualModel::ReleaseFlags flags = model->release(item->item);
2262 // item was not destroyed, and we no longer reference it.
2263 QQuickItemPrivate::get(item->item)->setCulled(true);
2264 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
2267 return flags != QQuickVisualModel::Referenced;
2270 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2272 return createComponentItem(highlightComponent, 0.0, true);
2275 QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault)
2277 Q_Q(QQuickItemView);
2279 QQuickItem *item = 0;
2281 QQmlContext *creationContext = component->creationContext();
2282 QQmlContext *context = new QQmlContext(
2283 creationContext ? creationContext : qmlContext(q));
2284 QObject *nobj = component->beginCreate(context);
2286 QQml_setParent_noEvent(context, nobj);
2287 item = qobject_cast<QQuickItem *>(nobj);
2293 } else if (createDefault) {
2294 item = new QQuickItem;
2298 QQml_setParent_noEvent(item, q->contentItem());
2299 item->setParentItem(q->contentItem());
2302 component->completeCreate();
2306 void QQuickItemViewPrivate::updateTrackedItem()
2308 Q_Q(QQuickItemView);
2309 FxViewItem *item = currentItem;
2315 q->trackedPositionChanged();
2318 void QQuickItemViewPrivate::updateUnrequestedIndexes()
2320 Q_Q(QQuickItemView);
2321 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2322 *it = model->indexOf(it.key(), q);
2325 void QQuickItemViewPrivate::updateUnrequestedPositions()
2327 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2328 repositionPackageItemAt(it.key(), it.value());
2331 void QQuickItemViewPrivate::updateVisibleIndex()
2334 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
2335 if ((*it)->index != -1) {
2336 visibleIndex = (*it)->index;