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 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->transitioner->setPopulateTransitionEnabled(true);
328 d->forceLayoutPolish();
331 connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
332 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
338 QQmlComponent *QQuickItemView::delegate() const
340 Q_D(const QQuickItemView);
342 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
343 return dataModel->delegate();
349 void QQuickItemView::setDelegate(QQmlComponent *delegate)
352 if (delegate == this->delegate())
355 d->model = new QQuickVisualDataModel(qmlContext(this));
358 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
359 int oldCount = dataModel->count();
360 dataModel->setDelegate(delegate);
361 if (isComponentComplete()) {
362 for (int i = 0; i < d->visibleItems.count(); ++i)
363 d->releaseItem(d->visibleItems.at(i));
364 d->visibleItems.clear();
365 d->releaseItem(d->currentItem);
367 d->updateSectionCriteria();
369 d->moveReason = QQuickItemViewPrivate::SetIndex;
370 d->updateCurrent(d->currentIndex);
371 if (d->highlight && d->currentItem) {
372 if (d->autoHighlight)
373 d->resetHighlightPosition();
374 d->updateTrackedItem();
376 d->moveReason = QQuickItemViewPrivate::Other;
379 if (oldCount != dataModel->count())
382 emit delegateChanged();
386 int QQuickItemView::count() const
388 Q_D(const QQuickItemView);
391 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
392 return d->model->count();
395 int QQuickItemView::currentIndex() const
397 Q_D(const QQuickItemView);
398 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
399 return d->currentIndex;
402 void QQuickItemView::setCurrentIndex(int index)
405 if (d->inRequest) // currently creating item
407 d->currentIndexCleared = (index == -1);
409 d->applyPendingChanges();
410 if (index == d->currentIndex)
412 if (isComponentComplete() && d->isValid()) {
413 d->moveReason = QQuickItemViewPrivate::SetIndex;
414 d->updateCurrent(index);
415 } else if (d->currentIndex != index) {
416 d->currentIndex = index;
417 emit currentIndexChanged();
422 bool QQuickItemView::isWrapEnabled() const
424 Q_D(const QQuickItemView);
428 void QQuickItemView::setWrapEnabled(bool wrap)
434 emit keyNavigationWrapsChanged();
437 int QQuickItemView::cacheBuffer() const
439 Q_D(const QQuickItemView);
443 void QQuickItemView::setCacheBuffer(int b)
446 if (d->buffer != b) {
448 if (isComponentComplete()) {
449 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
452 emit cacheBufferChanged();
457 Qt::LayoutDirection QQuickItemView::layoutDirection() const
459 Q_D(const QQuickItemView);
460 return d->layoutDirection;
463 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
466 if (d->layoutDirection != layoutDirection) {
467 d->layoutDirection = layoutDirection;
469 emit layoutDirectionChanged();
470 emit effectiveLayoutDirectionChanged();
474 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
476 Q_D(const QQuickItemView);
477 if (d->effectiveLayoutMirror)
478 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
480 return d->layoutDirection;
483 QQuickItemView::VerticalLayoutDirection QQuickItemView::verticalLayoutDirection() const
485 Q_D(const QQuickItemView);
486 return d->verticalLayoutDirection;
489 void QQuickItemView::setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection)
492 if (d->verticalLayoutDirection != layoutDirection) {
493 d->verticalLayoutDirection = layoutDirection;
495 emit verticalLayoutDirectionChanged();
499 QQmlComponent *QQuickItemView::header() const
501 Q_D(const QQuickItemView);
502 return d->headerComponent;
505 QQuickItem *QQuickItemView::headerItem() const
507 Q_D(const QQuickItemView);
508 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
509 return d->header ? d->header->item : 0;
512 void QQuickItemView::setHeader(QQmlComponent *headerComponent)
515 if (d->headerComponent != headerComponent) {
516 d->applyPendingChanges();
519 d->headerComponent = headerComponent;
521 d->markExtentsDirty();
523 if (isComponentComplete()) {
529 emit headerItemChanged();
531 emit headerChanged();
535 QQmlComponent *QQuickItemView::footer() const
537 Q_D(const QQuickItemView);
538 return d->footerComponent;
541 QQuickItem *QQuickItemView::footerItem() const
543 Q_D(const QQuickItemView);
544 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
545 return d->footer ? d->footer->item : 0;
548 void QQuickItemView::setFooter(QQmlComponent *footerComponent)
551 if (d->footerComponent != footerComponent) {
552 d->applyPendingChanges();
555 d->footerComponent = footerComponent;
557 if (isComponentComplete()) {
562 emit footerItemChanged();
564 emit footerChanged();
568 QQmlComponent *QQuickItemView::highlight() const
570 Q_D(const QQuickItemView);
571 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
572 return d->highlightComponent;
575 void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
578 if (highlightComponent != d->highlightComponent) {
579 d->applyPendingChanges();
580 d->highlightComponent = highlightComponent;
581 d->createHighlight();
583 d->updateHighlight();
584 emit highlightChanged();
588 QQuickItem *QQuickItemView::highlightItem() const
590 Q_D(const QQuickItemView);
591 const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
592 return d->highlight ? d->highlight->item : 0;
595 bool QQuickItemView::highlightFollowsCurrentItem() const
597 Q_D(const QQuickItemView);
598 return d->autoHighlight;
601 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
604 if (d->autoHighlight != autoHighlight) {
605 d->autoHighlight = autoHighlight;
607 d->updateHighlight();
608 emit highlightFollowsCurrentItemChanged();
612 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
614 Q_D(const QQuickItemView);
615 return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
618 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
621 if (d->highlightRange == mode)
623 d->highlightRange = mode;
624 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
625 emit highlightRangeModeChanged();
628 //###Possibly rename these properties, since they are very useful even without a highlight?
629 qreal QQuickItemView::preferredHighlightBegin() const
631 Q_D(const QQuickItemView);
632 return d->highlightRangeStart;
635 void QQuickItemView::setPreferredHighlightBegin(qreal start)
638 d->highlightRangeStartValid = true;
639 if (d->highlightRangeStart == start)
641 d->highlightRangeStart = start;
642 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
643 emit preferredHighlightBeginChanged();
646 void QQuickItemView::resetPreferredHighlightBegin()
649 d->highlightRangeStartValid = false;
650 if (d->highlightRangeStart == 0)
652 d->highlightRangeStart = 0;
653 emit preferredHighlightBeginChanged();
656 qreal QQuickItemView::preferredHighlightEnd() const
658 Q_D(const QQuickItemView);
659 return d->highlightRangeEnd;
662 void QQuickItemView::setPreferredHighlightEnd(qreal end)
665 d->highlightRangeEndValid = true;
666 if (d->highlightRangeEnd == end)
668 d->highlightRangeEnd = end;
669 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
670 emit preferredHighlightEndChanged();
673 void QQuickItemView::resetPreferredHighlightEnd()
676 d->highlightRangeEndValid = false;
677 if (d->highlightRangeEnd == 0)
679 d->highlightRangeEnd = 0;
680 emit preferredHighlightEndChanged();
683 int QQuickItemView::highlightMoveDuration() const
685 Q_D(const QQuickItemView);
686 return d->highlightMoveDuration;
689 void QQuickItemView::setHighlightMoveDuration(int duration)
692 if (d->highlightMoveDuration != duration) {
693 d->highlightMoveDuration = duration;
694 emit highlightMoveDurationChanged();
698 QQuickTransition *QQuickItemView::populateTransition() const
700 Q_D(const QQuickItemView);
701 return d->transitioner ? d->transitioner->populateTransition : 0;
704 void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
707 d->createTransitioner();
708 if (d->transitioner->populateTransition != transition) {
709 d->transitioner->populateTransition = transition;
710 emit populateTransitionChanged();
714 QQuickTransition *QQuickItemView::addTransition() const
716 Q_D(const QQuickItemView);
717 return d->transitioner ? d->transitioner->addTransition : 0;
720 void QQuickItemView::setAddTransition(QQuickTransition *transition)
723 d->createTransitioner();
724 if (d->transitioner->addTransition != transition) {
725 d->transitioner->addTransition = transition;
726 emit addTransitionChanged();
730 QQuickTransition *QQuickItemView::addDisplacedTransition() const
732 Q_D(const QQuickItemView);
733 return d->transitioner ? d->transitioner->addDisplacedTransition : 0;
736 void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
739 d->createTransitioner();
740 if (d->transitioner->addDisplacedTransition != transition) {
741 d->transitioner->addDisplacedTransition = transition;
742 emit addDisplacedTransitionChanged();
746 QQuickTransition *QQuickItemView::moveTransition() const
748 Q_D(const QQuickItemView);
749 return d->transitioner ? d->transitioner->moveTransition : 0;
752 void QQuickItemView::setMoveTransition(QQuickTransition *transition)
755 d->createTransitioner();
756 if (d->transitioner->moveTransition != transition) {
757 d->transitioner->moveTransition = transition;
758 emit moveTransitionChanged();
762 QQuickTransition *QQuickItemView::moveDisplacedTransition() const
764 Q_D(const QQuickItemView);
765 return d->transitioner ? d->transitioner->moveDisplacedTransition : 0;
768 void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
771 d->createTransitioner();
772 if (d->transitioner->moveDisplacedTransition != transition) {
773 d->transitioner->moveDisplacedTransition = transition;
774 emit moveDisplacedTransitionChanged();
778 QQuickTransition *QQuickItemView::removeTransition() const
780 Q_D(const QQuickItemView);
781 return d->transitioner ? d->transitioner->removeTransition : 0;
784 void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
787 d->createTransitioner();
788 if (d->transitioner->removeTransition != transition) {
789 d->transitioner->removeTransition = transition;
790 emit removeTransitionChanged();
794 QQuickTransition *QQuickItemView::removeDisplacedTransition() const
796 Q_D(const QQuickItemView);
797 return d->transitioner ? d->transitioner->removeDisplacedTransition : 0;
800 void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
803 d->createTransitioner();
804 if (d->transitioner->removeDisplacedTransition != transition) {
805 d->transitioner->removeDisplacedTransition = transition;
806 emit removeDisplacedTransitionChanged();
810 QQuickTransition *QQuickItemView::displacedTransition() const
812 Q_D(const QQuickItemView);
813 return d->transitioner ? d->transitioner->displacedTransition : 0;
816 void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
819 d->createTransitioner();
820 if (d->transitioner->displacedTransition != transition) {
821 d->transitioner->displacedTransition = transition;
822 emit displacedTransitionChanged();
826 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
831 if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain)
834 applyPendingChanges();
835 int idx = qMax(qMin(index, model->count()-1), 0);
837 qreal pos = isContentFlowReversed() ? -position() - size() : position();
838 FxViewItem *item = visibleItem(idx);
840 if (layoutOrientation() == Qt::Vertical)
841 maxExtent = isContentFlowReversed() ? q->minYExtent()-size(): -q->maxYExtent();
843 maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
845 int itemPos = positionAt(idx);
846 changedVisibleIndex(idx);
847 // save the currently visible items in case any of them end up visible again
848 QList<FxViewItem *> oldVisible = visibleItems;
849 visibleItems.clear();
850 setPosition(qMin(qreal(itemPos), maxExtent));
851 // now release the reference to all the old visible items.
852 for (int i = 0; i < oldVisible.count(); ++i)
853 releaseItem(oldVisible.at(i));
854 item = visibleItem(idx);
857 const qreal itemPos = item->position();
859 case QQuickItemView::Beginning:
861 if (index < 0 && header)
864 case QQuickItemView::Center:
865 pos = itemPos - (size() - item->size())/2;
867 case QQuickItemView::End:
868 pos = itemPos - size() + item->size();
869 if (index >= model->count() && footer)
872 case QQuickItemView::Visible:
873 if (itemPos > pos + size())
874 pos = itemPos - size() + item->size();
875 else if (item->endPosition() <= pos)
878 case QQuickItemView::Contain:
879 if (item->endPosition() >= pos + size())
880 pos = itemPos - size() + item->size();
884 pos = qMin(pos, maxExtent);
886 if (layoutOrientation() == Qt::Vertical)
887 minExtent = isContentFlowReversed() ? q->maxYExtent()-size(): -q->minYExtent();
889 minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
890 pos = qMax(pos, minExtent);
891 moveReason = QQuickItemViewPrivate::Other;
897 resetHighlightPosition();
904 void QQuickItemView::positionViewAtIndex(int index, int mode)
907 if (!d->isValid() || index < 0 || index >= d->model->count())
909 d->positionViewAtIndex(index, mode);
913 void QQuickItemView::positionViewAtBeginning()
918 d->positionViewAtIndex(-1, Beginning);
921 void QQuickItemView::positionViewAtEnd()
926 d->positionViewAtIndex(d->model->count(), End);
929 int QQuickItemView::indexAt(qreal x, qreal y) const
931 Q_D(const QQuickItemView);
932 for (int i = 0; i < d->visibleItems.count(); ++i) {
933 const FxViewItem *item = d->visibleItems.at(i);
934 if (item->contains(x, y))
941 QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
943 Q_D(const QQuickItemView);
944 for (int i = 0; i < d->visibleItems.count(); ++i) {
945 const FxViewItem *item = d->visibleItems.at(i);
946 if (item->contains(x, y))
953 void QQuickItemViewPrivate::applyPendingChanges()
956 if (q->isComponentComplete() && currentChanges.hasPendingChanges())
960 int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector<QQuickChangeSet::Remove> &changes) const
962 for (int i=0; i<changes.count(); i++) {
963 for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
964 if (changes[i].moveKey(j) == key)
971 qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
973 Q_Q(const QQuickItemView);
975 qreal highlightStart;
977 qreal endPositionFirstItem = 0;
978 qreal extent = -startPosition() + axisData.startMargin;
979 if (isContentFlowReversed()) {
980 if (model && model->count())
981 endPositionFirstItem = positionAt(model->count()-1);
983 extent += headerSize();
984 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
985 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
986 extent += footerSize();
987 qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
988 if (extent < maxExtentAlongAxis)
989 extent = maxExtentAlongAxis;
991 endPositionFirstItem = endPositionAt(0);
992 highlightStart = highlightRangeStart;
993 highlightEnd = highlightRangeEnd;
994 extent += headerSize();
996 if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
997 extent += highlightStart;
998 FxViewItem *firstItem = visibleItem(0);
1000 extent -= firstItem->sectionSize();
1001 extent = isContentFlowReversed()
1002 ? qMin(extent, endPositionFirstItem + highlightEnd)
1003 : qMax(extent, -(endPositionFirstItem - highlightEnd));
1008 qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
1010 Q_Q(const QQuickItemView);
1012 qreal highlightStart;
1014 qreal lastItemPosition = 0;
1016 if (isContentFlowReversed()) {
1017 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1018 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1019 lastItemPosition = endPosition();
1021 highlightStart = highlightRangeStart;
1022 highlightEnd = highlightRangeEnd;
1023 if (model && model->count())
1024 lastItemPosition = positionAt(model->count()-1);
1026 if (!model || !model->count()) {
1027 if (!isContentFlowReversed())
1028 maxExtent = header ? -headerSize() : 0;
1029 extent += forXAxis ? q->width() : q->height();
1030 } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1031 extent = -(lastItemPosition - highlightStart);
1032 if (highlightEnd != highlightStart) {
1033 extent = isContentFlowReversed()
1034 ? qMax(extent, -(endPosition() - highlightEnd))
1035 : qMin(extent, -(endPosition() - highlightEnd));
1038 extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
1040 if (isContentFlowReversed()) {
1041 extent -= headerSize();
1042 extent -= axisData.endMargin;
1044 extent -= footerSize();
1045 extent -= axisData.endMargin;
1046 qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
1047 if (extent > minExtentAlongAxis)
1048 extent = minExtentAlongAxis;
1054 // for debugging only
1055 void QQuickItemViewPrivate::checkVisible() const
1058 for (int i = 0; i < visibleItems.count(); ++i) {
1059 FxViewItem *item = visibleItems.at(i);
1060 if (item->index == -1) {
1062 } else if (item->index != visibleIndex + i - skip) {
1063 qFatal("index %d %d %d", visibleIndex, i, item->index);
1068 // for debugging only
1069 void QQuickItemViewPrivate::showVisibleItems() const
1071 qDebug() << "Visible items:";
1072 for (int i = 0; i < visibleItems.count(); ++i) {
1073 qDebug() << "\t" << visibleItems[i]->index
1074 << visibleItems[i]->item->objectName()
1075 << visibleItems[i]->position();
1079 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1081 Q_Q(QQuickItemView);
1082 QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1083 if (!q->isComponentComplete())
1086 if (header && header->item == item) {
1089 if (!q->isMoving() && !q->isFlicking())
1091 } else if (footer && footer->item == item) {
1094 if (!q->isMoving() && !q->isFlicking())
1098 if (currentItem && currentItem->item == item) {
1099 // don't allow item movement transitions to trigger a re-layout and
1100 // start new transitions
1101 bool prevInLayout = inLayout;
1103 FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : 0;
1104 if (actualItem && actualItem->transitionRunning())
1108 inLayout = prevInLayout;
1111 if (trackedItem && trackedItem->item == item)
1112 q->trackedPositionChanged();
1115 void QQuickItemView::destroyRemoved()
1117 Q_D(QQuickItemView);
1118 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1119 it != d->visibleItems.end();) {
1120 FxViewItem *item = *it;
1121 if (item->index == -1 && item->attached->delayRemove() == false) {
1122 if (d->transitioner && d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)) {
1123 // don't remove from visibleItems until next layout()
1124 d->runDelayedRemoveTransition = true;
1125 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1128 d->releaseItem(item);
1129 it = d->visibleItems.erase(it);
1136 // Correct the positioning of the items
1137 d->forceLayoutPolish();
1140 void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1142 Q_D(QQuickItemView);
1144 if (d->transitioner)
1145 d->transitioner->setPopulateTransitionEnabled(true);
1146 d->moveReason = QQuickItemViewPrivate::SetIndex;
1148 if (d->highlight && d->currentItem) {
1149 if (d->autoHighlight)
1150 d->resetHighlightPosition();
1151 d->updateTrackedItem();
1153 d->moveReason = QQuickItemViewPrivate::Other;
1154 emit countChanged();
1155 if (d->transitioner && d->transitioner->populateTransition)
1156 d->forceLayoutPolish();
1159 d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
1160 d->bufferedChanges.applyChanges(changeSet);
1162 if (d->bufferedChanges.hasPendingChanges()) {
1163 d->currentChanges.applyBufferedChanges(d->bufferedChanges);
1164 d->bufferedChanges.reset();
1166 d->currentChanges.prepare(d->currentIndex, d->itemCount);
1167 d->currentChanges.applyChanges(changeSet);
1173 void QQuickItemView::animStopped()
1175 Q_D(QQuickItemView);
1176 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1177 d->refillOrLayout();
1178 if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1179 d->updateHighlight();
1183 void QQuickItemView::trackedPositionChanged()
1185 Q_D(QQuickItemView);
1186 if (!d->trackedItem || !d->currentItem)
1188 if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
1189 qreal trackedPos = d->trackedItem->position();
1190 qreal trackedSize = d->trackedItem->size();
1191 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1192 qreal pos = viewPos;
1193 if (d->haveHighlightRange) {
1194 if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1195 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1196 if (trackedPos < pos + d->highlightRangeStart)
1197 pos = trackedPos - d->highlightRangeStart;
1198 if (d->highlightRange != StrictlyEnforceRange) {
1199 if (pos > d->endPosition() - d->size())
1200 pos = d->endPosition() - d->size();
1201 if (pos < d->startPosition())
1202 pos = d->startPosition();
1205 if (d->trackedItem != d->currentItem) {
1206 // also make section header visible
1207 trackedPos -= d->currentItem->sectionSize();
1208 trackedSize += d->currentItem->sectionSize();
1210 qreal trackedEndPos = d->trackedItem->endPosition();
1211 qreal toItemPos = d->currentItem->position();
1212 qreal toItemEndPos = d->currentItem->endPosition();
1213 if (d->showHeaderForIndex(d->currentIndex)) {
1214 qreal startOffset = -d->contentStartOffset();
1215 trackedPos -= startOffset;
1216 trackedEndPos -= startOffset;
1217 toItemPos -= startOffset;
1218 toItemEndPos -= startOffset;
1219 } else if (d->showFooterForIndex(d->currentIndex)) {
1220 qreal endOffset = d->footerSize();
1221 if (d->layoutOrientation() == Qt::Vertical) {
1222 if (d->isContentFlowReversed())
1223 endOffset += d->vData.startMargin;
1225 endOffset += d->vData.endMargin;
1227 if (d->isContentFlowReversed())
1228 endOffset += d->hData.startMargin;
1230 endOffset += d->hData.endMargin;
1232 trackedPos += endOffset;
1233 trackedEndPos += endOffset;
1234 toItemPos += endOffset;
1235 toItemEndPos += endOffset;
1238 if (trackedEndPos >= viewPos + d->size()
1239 && toItemEndPos >= viewPos + d->size()) {
1240 if (trackedEndPos <= toItemEndPos) {
1241 pos = trackedEndPos - d->size();
1242 if (trackedSize > d->size())
1245 pos = toItemEndPos - d->size();
1246 if (d->currentItem->size() > d->size())
1247 pos = d->currentItem->position();
1250 if (trackedPos < pos && toItemPos < pos)
1251 pos = qMax(trackedPos, toItemPos);
1253 if (viewPos != pos) {
1255 d->calcVelocity = true;
1256 d->setPosition(pos);
1257 d->calcVelocity = false;
1262 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1264 Q_D(QQuickItemView);
1265 d->markExtentsDirty();
1266 if (isComponentComplete() && d->isValid())
1267 d->forceLayoutPolish();
1268 QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1271 qreal QQuickItemView::minYExtent() const
1273 Q_D(const QQuickItemView);
1274 if (d->layoutOrientation() == Qt::Horizontal)
1275 return QQuickFlickable::minYExtent();
1277 if (d->vData.minExtentDirty) {
1278 d->minExtent = d->minExtentForAxis(d->vData, false);
1279 d->vData.minExtentDirty = false;
1282 return d->minExtent;
1285 qreal QQuickItemView::maxYExtent() const
1287 Q_D(const QQuickItemView);
1288 if (d->layoutOrientation() == Qt::Horizontal)
1291 if (d->vData.maxExtentDirty) {
1292 d->maxExtent = d->maxExtentForAxis(d->vData, false);
1293 d->vData.maxExtentDirty = false;
1296 return d->maxExtent;
1299 qreal QQuickItemView::minXExtent() const
1301 Q_D(const QQuickItemView);
1302 if (d->layoutOrientation() == Qt::Vertical)
1303 return QQuickFlickable::minXExtent();
1305 if (d->hData.minExtentDirty) {
1306 d->minExtent = d->minExtentForAxis(d->hData, true);
1307 d->hData.minExtentDirty = false;
1310 return d->minExtent;
1313 qreal QQuickItemView::maxXExtent() const
1315 Q_D(const QQuickItemView);
1316 if (d->layoutOrientation() == Qt::Vertical)
1319 if (d->hData.maxExtentDirty) {
1320 d->maxExtent = d->maxExtentForAxis(d->hData, true);
1321 d->hData.maxExtentDirty = false;
1324 return d->maxExtent;
1327 void QQuickItemView::setContentX(qreal pos)
1329 Q_D(QQuickItemView);
1330 // Positioning the view manually should override any current movement state
1331 d->moveReason = QQuickItemViewPrivate::Other;
1332 QQuickFlickable::setContentX(pos);
1335 void QQuickItemView::setContentY(qreal pos)
1337 Q_D(QQuickItemView);
1338 // Positioning the view manually should override any current movement state
1339 d->moveReason = QQuickItemViewPrivate::Other;
1340 QQuickFlickable::setContentY(pos);
1343 qreal QQuickItemView::originX() const
1345 Q_D(const QQuickItemView);
1346 if (d->layoutOrientation() == Qt::Horizontal
1347 && effectiveLayoutDirection() == Qt::RightToLeft
1348 && contentWidth() < width()) {
1349 return d->lastPosition() - d->footerSize();
1351 return QQuickFlickable::originX();
1354 qreal QQuickItemView::originY() const
1356 Q_D(const QQuickItemView);
1357 if (d->layoutOrientation() == Qt::Vertical
1358 && d->verticalLayoutDirection == QQuickItemView::BottomToTop
1359 && contentHeight() < height()) {
1360 return d->lastPosition() - d->footerSize();
1362 return QQuickFlickable::originY();
1365 void QQuickItemView::updatePolish()
1367 Q_D(QQuickItemView);
1368 QQuickFlickable::updatePolish();
1372 void QQuickItemView::componentComplete()
1374 Q_D(QQuickItemView);
1375 if (d->model && d->ownModel)
1376 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1378 QQuickFlickable::componentComplete();
1380 d->updateSectionCriteria();
1383 d->updateViewport();
1384 d->setPosition(d->contentStartOffset());
1385 if (d->transitioner)
1386 d->transitioner->setPopulateTransitionEnabled(true);
1390 d->moveReason = QQuickItemViewPrivate::SetIndex;
1391 if (d->currentIndex < 0 && !d->currentIndexCleared)
1392 d->updateCurrent(0);
1394 d->updateCurrent(d->currentIndex);
1395 if (d->highlight && d->currentItem) {
1396 if (d->autoHighlight)
1397 d->resetHighlightPosition();
1398 d->updateTrackedItem();
1400 d->moveReason = QQuickItemViewPrivate::Other;
1403 if (d->model && d->model->count())
1404 emit countChanged();
1409 QQuickItemViewPrivate::QQuickItemViewPrivate()
1411 , buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
1412 , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
1415 , currentIndex(-1), currentItem(0)
1416 , trackedItem(0), requestedIndex(-1)
1417 , highlightComponent(0), highlight(0)
1418 , highlightRange(QQuickItemView::NoHighlightRange)
1419 , highlightRangeStart(0), highlightRangeEnd(0)
1420 , highlightMoveDuration(150)
1421 , headerComponent(0), header(0), footerComponent(0), footer(0)
1423 , minExtent(0), maxExtent(0)
1424 , ownModel(false), wrap(false)
1425 , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1426 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1427 , fillCacheBuffer(false), inRequest(false)
1428 , runDelayedRemoveTransition(false)
1430 bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
1431 bufferPause.setLoopCount(1);
1432 bufferPause.setDuration(16);
1435 QQuickItemViewPrivate::~QQuickItemViewPrivate()
1438 transitioner->setChangeListener(0);
1439 delete transitioner;
1442 bool QQuickItemViewPrivate::isValid() const
1444 return model && model->count() && model->isValid();
1447 qreal QQuickItemViewPrivate::position() const
1449 Q_Q(const QQuickItemView);
1450 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1453 qreal QQuickItemViewPrivate::size() const
1455 Q_Q(const QQuickItemView);
1456 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1459 qreal QQuickItemViewPrivate::startPosition() const
1461 return isContentFlowReversed() ? -lastPosition() : originPosition();
1464 qreal QQuickItemViewPrivate::endPosition() const
1466 return isContentFlowReversed() ? -originPosition() : lastPosition();
1469 qreal QQuickItemViewPrivate::contentStartOffset() const
1471 qreal pos = -headerSize();
1472 if (layoutOrientation() == Qt::Vertical) {
1473 if (isContentFlowReversed())
1474 pos -= vData.endMargin;
1476 pos -= vData.startMargin;
1478 if (isContentFlowReversed())
1479 pos -= hData.endMargin;
1481 pos -= hData.startMargin;
1486 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1488 if (visibleItems.count()) {
1489 int i = visibleItems.count() - 1;
1490 while (i > 0 && visibleItems.at(i)->index == -1)
1492 if (visibleItems.at(i)->index != -1)
1493 return visibleItems.at(i)->index;
1495 return defaultValue;
1498 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1499 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1500 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1501 FxViewItem *item = visibleItems.at(i);
1502 if (item->index == modelIndex)
1509 // should rename to firstItemInView() to avoid confusion with other "*visible*" methods
1510 // that don't look at the view position and size
1511 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
1512 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1513 for (int i = 0; i < visibleItems.count(); ++i) {
1514 FxViewItem *item = visibleItems.at(i);
1515 if (item->index != -1 && item->endPosition() > pos)
1518 return visibleItems.count() ? visibleItems.first() : 0;
1521 int QQuickItemViewPrivate::findLastIndexInView() const
1523 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1524 for (int i=visibleItems.count() - 1; i>=0; i--) {
1525 if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
1526 return visibleItems.at(i)->index;
1531 // Map a model index to visibleItems list index.
1532 // These may differ if removed items are still present in the visible list,
1533 // e.g. doing a removal animation
1534 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1536 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1538 for (int i = 0; i < visibleItems.count(); ++i) {
1539 FxViewItem *item = visibleItems.at(i);
1540 if (item->index == modelIndex)
1542 if (item->index > modelIndex)
1545 return -1; // Not in visibleList
1548 void QQuickItemViewPrivate::init()
1550 Q_Q(QQuickItemView);
1551 q->setFlag(QQuickItem::ItemIsFocusScope);
1552 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1553 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1556 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1558 Q_Q(QQuickItemView);
1559 applyPendingChanges();
1560 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1562 currentItem->attached->setIsCurrentItem(false);
1563 releaseItem(currentItem);
1565 currentIndex = modelIndex;
1566 emit q->currentIndexChanged();
1567 emit q->currentItemChanged();
1569 } else if (currentIndex != modelIndex) {
1570 currentIndex = modelIndex;
1571 emit q->currentIndexChanged();
1576 if (currentItem && currentIndex == modelIndex) {
1581 FxViewItem *oldCurrentItem = currentItem;
1582 int oldCurrentIndex = currentIndex;
1583 currentIndex = modelIndex;
1584 currentItem = createItem(modelIndex, false);
1585 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1586 oldCurrentItem->attached->setIsCurrentItem(false);
1588 currentItem->item->setFocus(true);
1589 currentItem->attached->setIsCurrentItem(true);
1590 initializeCurrentItem();
1594 if (oldCurrentIndex != currentIndex)
1595 emit q->currentIndexChanged();
1596 if (oldCurrentItem != currentItem)
1597 emit q->currentItemChanged();
1598 releaseItem(oldCurrentItem);
1601 void QQuickItemViewPrivate::clear()
1603 currentChanges.reset();
1606 for (int i = 0; i < visibleItems.count(); ++i)
1607 releaseItem(visibleItems.at(i));
1608 visibleItems.clear();
1611 for (int i = 0; i < releasePendingTransition.count(); ++i) {
1612 releasePendingTransition.at(i)->releaseAfterTransition = false;
1613 releaseItem(releasePendingTransition.at(i));
1615 releasePendingTransition.clear();
1617 releaseItem(currentItem);
1622 if (requestedIndex >= 0) {
1624 model->cancel(requestedIndex);
1625 requestedIndex = -1;
1633 void QQuickItemViewPrivate::mirrorChange()
1635 Q_Q(QQuickItemView);
1637 emit q->effectiveLayoutDirectionChanged();
1640 void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1642 Q_Q(QQuickItemView);
1643 fillCacheBuffer = true;
1647 void QQuickItemViewPrivate::refill()
1649 qreal s = qMax(size(), qreal(0.));
1650 if (isContentFlowReversed())
1651 refill(-position()-s, -position());
1653 refill(position(), position()+s);
1656 void QQuickItemViewPrivate::refill(qreal from, qreal to)
1658 Q_Q(QQuickItemView);
1659 if (!isValid() || !q->isComponentComplete())
1663 currentChanges.reset();
1665 int prevCount = itemCount;
1666 itemCount = model->count();
1667 qreal bufferFrom = from - buffer;
1668 qreal bufferTo = to + buffer;
1669 qreal fillFrom = from;
1672 bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
1673 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1675 if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1677 // We've already created a new delegate this frame.
1678 // Just schedule a buffer refill.
1679 bufferPause.start();
1681 if (bufferMode & BufferAfter)
1683 if (bufferMode & BufferBefore)
1684 fillFrom = bufferFrom;
1685 added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, true);
1689 if (added || removed) {
1691 updateBeginningEnd();
1692 visibleItemsChanged();
1698 if (prevCount != itemCount)
1699 emit q->countChanged();
1702 void QQuickItemViewPrivate::regenerate()
1704 Q_Q(QQuickItemView);
1705 if (q->isComponentComplete()) {
1706 currentChanges.reset();
1715 setPosition(contentStartOffset());
1717 updateCurrent(currentIndex);
1721 void QQuickItemViewPrivate::updateViewport()
1723 Q_Q(QQuickItemView);
1724 qreal extra = headerSize() + footerSize();
1725 qreal contentSize = isValid() ? (endPosition() - startPosition()) : 0.0;
1726 if (layoutOrientation() == Qt::Vertical)
1727 q->setContentHeight(contentSize + extra);
1729 q->setContentWidth(contentSize + extra);
1732 void QQuickItemViewPrivate::layout()
1734 Q_Q(QQuickItemView);
1740 if (!isValid() && !visibleItems.count()) {
1742 setPosition(contentStartOffset());
1744 transitioner->setPopulateTransitionEnabled(false);
1749 if (runDelayedRemoveTransition && transitioner
1750 && transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1751 // assume that any items moving now are moving due to the remove - if they schedule
1752 // a different transition, that will override this one anyway
1753 for (int i=0; i<visibleItems.count(); i++)
1754 visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
1757 ChangeResult insertionPosChanges;
1758 ChangeResult removalPosChanges;
1759 if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
1760 if (fillCacheBuffer) {
1761 fillCacheBuffer = false;
1767 forceLayout = false;
1769 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
1770 for (int i=0; i<visibleItems.count(); i++) {
1771 if (!visibleItems.at(i)->transitionScheduledOrRunning())
1772 visibleItems.at(i)->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
1777 layoutVisibleItems();
1779 int lastIndexInView = findLastIndexInView();
1784 if (!q->isMoving() && !q->isFlicking()) {
1792 updateUnrequestedPositions();
1795 // items added in the last refill() may need to be transitioned in - e.g. a remove
1796 // causes items to slide up into view
1797 if (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false)
1798 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1799 translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
1802 prepareVisibleItemTransitions();
1804 QRectF viewBounds(0, position(), q->width(), q->height());
1805 for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin();
1806 it != releasePendingTransition.end(); ) {
1807 FxViewItem *item = *it;
1808 if (prepareNonVisibleItemTransition(item, viewBounds)) {
1812 it = releasePendingTransition.erase(it);
1816 for (int i=0; i<visibleItems.count(); i++)
1817 visibleItems[i]->startTransition(transitioner);
1818 for (int i=0; i<releasePendingTransition.count(); i++)
1819 releasePendingTransition[i]->startTransition(transitioner);
1821 transitioner->setPopulateTransitionEnabled(false);
1822 transitioner->resetTargetLists();
1825 runDelayedRemoveTransition = false;
1829 bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
1831 Q_Q(QQuickItemView);
1832 if (!q->isComponentComplete() || !hasPendingChanges())
1835 if (bufferedChanges.hasPendingChanges()) {
1836 currentChanges.applyBufferedChanges(bufferedChanges);
1837 bufferedChanges.reset();
1840 updateUnrequestedIndexes();
1841 moveReason = QQuickItemViewPrivate::Other;
1843 FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
1844 int prevItemCount = itemCount;
1845 int prevVisibleItemsCount = visibleItems.count();
1846 bool visibleAffected = false;
1847 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
1848 || !currentChanges.pendingChanges.inserts().isEmpty();
1850 FxViewItem *prevFirstVisible = firstVisibleItem();
1851 QQmlNullableValue<qreal> prevViewPos;
1852 int prevFirstVisibleIndex = -1;
1853 if (prevFirstVisible) {
1854 prevViewPos = prevFirstVisible->position();
1855 prevFirstVisibleIndex = prevFirstVisible->index;
1857 qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
1859 totalInsertionResult->visiblePos = prevViewPos;
1860 totalRemovalResult->visiblePos = prevViewPos;
1862 const QVector<QQuickChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
1863 const QVector<QQuickChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
1864 ChangeResult insertionResult(prevViewPos);
1865 ChangeResult removalResult(prevViewPos);
1867 int removedCount = 0;
1868 for (int i=0; i<removals.count(); i++) {
1869 itemCount -= removals[i].count;
1870 if (applyRemovalChange(removals[i], &removalResult, &removedCount))
1871 visibleAffected = true;
1872 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
1873 visibleAffected = true;
1874 if (prevFirstVisibleIndex >= 0 && removals[i].index < prevFirstVisibleIndex) {
1875 if (removals[i].index + removals[i].count < prevFirstVisibleIndex)
1876 removalResult.countChangeBeforeVisible += removals[i].count;
1878 removalResult.countChangeBeforeVisible += (prevFirstVisibleIndex - removals[i].index);
1881 if (runDelayedRemoveTransition) {
1882 QQuickChangeSet::Remove removal;
1883 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
1884 FxViewItem *item = *it;
1885 if (item->index == -1 && !item->attached->delayRemove()) {
1886 removeItem(item, removal, &removalResult);
1888 it = visibleItems.erase(it);
1894 *totalRemovalResult += removalResult;
1895 if (!removals.isEmpty()) {
1896 updateVisibleIndex();
1898 // set positions correctly for the next insertion
1899 if (!insertions.isEmpty()) {
1900 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1901 layoutVisibleItems(removals.first().index);
1905 QList<FxViewItem *> newItems;
1906 QList<MovedItem> movingIntoView;
1908 for (int i=0; i<insertions.count(); i++) {
1909 bool wasEmpty = visibleItems.isEmpty();
1910 if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
1911 visibleAffected = true;
1912 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
1913 visibleAffected = true;
1914 if (wasEmpty && !visibleItems.isEmpty())
1915 resetFirstItemPosition();
1916 *totalInsertionResult += insertionResult;
1918 // set positions correctly for the next insertion
1919 if (i < insertions.count() - 1) {
1920 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1921 layoutVisibleItems(insertions[i].index);
1923 itemCount += insertions[i].count;
1925 for (int i=0; i<newItems.count(); i++)
1926 newItems.at(i)->attached->emitAdd();
1928 // for each item that was moved directly into the view as a result of a move(),
1929 // find the index it was moved from in order to set its initial position, so that we
1930 // can transition it from this "original" position to its new position in the view
1931 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
1932 for (int i=0; i<movingIntoView.count(); i++) {
1933 int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
1934 if (fromIndex >= 0) {
1935 if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
1936 repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
1938 repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
1939 movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
1944 // reposition visibleItems.first() correctly so that the content y doesn't jump
1945 if (removedCount != prevVisibleItemsCount)
1946 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1948 // Whatever removed/moved items remain are no longer visible items.
1949 prepareRemoveTransitions(¤tChanges.removedItems);
1950 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
1951 it != currentChanges.removedItems.end(); ++it) {
1952 releaseItem(it.value());
1954 currentChanges.removedItems.clear();
1956 if (currentChanges.currentChanged) {
1957 if (currentChanges.currentRemoved && currentItem) {
1958 currentItem->attached->setIsCurrentItem(false);
1959 releaseItem(currentItem);
1962 if (!currentIndexCleared)
1963 updateCurrent(currentChanges.newCurrentIndex);
1966 if (!visibleAffected)
1967 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
1968 currentChanges.reset();
1971 if (prevItemCount != itemCount)
1972 emit q->countChanged();
1973 if (!visibleAffected && viewportChanged)
1976 return visibleAffected;
1979 bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount)
1981 Q_Q(QQuickItemView);
1982 bool visibleAffected = false;
1984 if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
1985 if (removal.index > visibleItems.last()->index)
1986 removeResult->countChangeAfterVisibleItems += removal.count;
1988 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
1991 QList<FxViewItem*>::Iterator it = visibleItems.begin();
1992 while (it != visibleItems.end()) {
1993 FxViewItem *item = *it;
1994 if (item->index == -1 || item->index < removal.index) {
1995 // already removed, or before removed items
1996 if (!visibleAffected && item->index < removal.index)
1997 visibleAffected = true;
1999 } else if (item->index >= removal.index + removal.count) {
2000 // after removed items
2001 item->index -= removal.count;
2002 if (removal.isMove())
2003 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2005 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2009 visibleAffected = true;
2010 if (!removal.isMove())
2011 item->attached->emitRemove();
2013 if (item->attached->delayRemove() && !removal.isMove()) {
2015 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2018 removeItem(item, removal, removeResult);
2019 if (!removal.isMove())
2021 it = visibleItems.erase(it);
2026 return visibleAffected;
2029 void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult)
2031 if (removeResult->visiblePos.isValid()) {
2032 if (item->position() < removeResult->visiblePos)
2033 removeResult->sizeChangesBeforeVisiblePos += item->size();
2035 removeResult->sizeChangesAfterVisiblePos += item->size();
2037 if (removal.isMove()) {
2038 currentChanges.removedItems.insert(removal.moveKey(item->index), item);
2039 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2041 // track item so it is released later
2042 currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item);
2044 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2045 removeResult->changedFirstItem = true;
2048 void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2049 qreal prevVisibleItemsFirstPos,
2050 FxViewItem *prevFirstVisible,
2051 ChangeResult *insertionResult,
2052 ChangeResult *removalResult)
2054 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2056 // reposition visibleItems.first() correctly so that the content y doesn't jump
2057 if (visibleItems.count()) {
2058 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2059 resetFirstItemPosition(prevVisibleItemsFirstPos);
2061 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2062 && prevFirstVisible != *visibleItems.constBegin()) {
2063 // the previous visibleItems.first() was also the first visible item, and it has been
2064 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2065 if (!insertionResult->changedFirstItem)
2066 resetFirstItemPosition(prevVisibleItemsFirstPos);
2068 } else if (prevViewPos.isValid()) {
2069 qreal moveForwardsBy = 0;
2070 qreal moveBackwardsBy = 0;
2072 // shift visibleItems.first() relative to the number of added/removed items
2073 if (visibleItems.first()->position() > prevViewPos) {
2074 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2075 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2076 } else if (visibleItems.first()->position() < prevViewPos) {
2077 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2078 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2080 adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2082 insertionResult->reset();
2083 removalResult->reset();
2087 void QQuickItemViewPrivate::createTransitioner()
2089 if (!transitioner) {
2090 transitioner = new QQuickItemViewTransitioner;
2091 transitioner->setChangeListener(this);
2095 void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2097 Q_Q(QQuickItemView);
2101 // must call for every visible item to init or discard transitions
2102 QRectF viewBounds(0, position(), q->width(), q->height());
2103 for (int i=0; i<visibleItems.count(); i++)
2104 visibleItems[i]->prepareTransition(transitioner, viewBounds);
2107 void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQuickChangeSet::MoveKey, FxViewItem *> *removedItems)
2112 if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)
2113 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2114 for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin();
2115 it != removedItems->end(); ) {
2116 bool isRemove = it.key().moveId < 0;
2118 FxViewItem *item = *it;
2119 item->releaseAfterTransition = true;
2120 releasePendingTransition.append(item);
2121 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
2122 it = removedItems->erase(it);
2130 bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2132 // Called for items that have been removed from visibleItems and may now be
2133 // transitioned out of the view. This applies to items that are being directly
2134 // removed, or moved to outside of the view, as well as those that are
2135 // displaced to a position outside of the view due to an insert or move.
2140 if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2141 repositionItemAt(item, item->index, 0);
2143 if (item->prepareTransition(transitioner, viewBounds)) {
2144 item->releaseAfterTransition = true;
2150 void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2152 for (int i=0; i<releasePendingTransition.count(); i++) {
2153 if (releasePendingTransition[i]->transitionableItem == item) {
2154 releaseItem(releasePendingTransition.takeAt(i));
2161 This may return 0 if the item is being created asynchronously.
2162 When the item becomes available, refill() will be called and the item
2163 will be returned on the next call to createItem().
2165 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
2167 Q_Q(QQuickItemView);
2169 if (requestedIndex == modelIndex && asynchronous)
2172 for (int i=0; i<releasePendingTransition.count(); i++) {
2173 if (releasePendingTransition[i]->index == modelIndex
2174 && !releasePendingTransition[i]->isPendingRemoval()) {
2175 releasePendingTransition[i]->releaseAfterTransition = false;
2176 return releasePendingTransition.takeAt(i);
2181 requestedIndex = modelIndex;
2184 if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
2185 item->setParentItem(q->contentItem());
2186 if (requestedIndex == modelIndex)
2187 requestedIndex = -1;
2188 FxViewItem *viewItem = newViewItem(modelIndex, item);
2190 viewItem->index = modelIndex;
2191 // do other set up for the new item that should not happen
2192 // until after bindings are evaluated
2193 initializeViewItem(viewItem);
2194 unrequestedItems.remove(item);
2204 void QQuickItemView::createdItem(int index, QQuickItem *item)
2206 Q_D(QQuickItemView);
2208 if (!d->inRequest) {
2209 d->unrequestedItems.insert(item, index);
2210 d->requestedIndex = -1;
2211 if (d->hasPendingChanges())
2215 if (d->unrequestedItems.contains(item))
2216 d->repositionPackageItemAt(item, index);
2217 else if (index == d->currentIndex)
2218 d->updateCurrent(index);
2222 void QQuickItemView::initItem(int, QQuickItem *item)
2225 item->setParentItem(contentItem());
2226 QQuickItemPrivate::get(item)->setCulled(true);
2229 void QQuickItemView::destroyingItem(QQuickItem *item)
2231 Q_D(QQuickItemView);
2232 d->unrequestedItems.remove(item);
2235 bool QQuickItemViewPrivate::releaseItem(FxViewItem *item)
2237 Q_Q(QQuickItemView);
2238 if (!item || !model)
2240 if (trackedItem == item)
2242 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
2243 itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
2244 QQuickVisualModel::ReleaseFlags flags = model->release(item->item);
2246 // item was not destroyed, and we no longer reference it.
2247 QQuickItemPrivate::get(item->item)->setCulled(true);
2248 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
2251 return flags != QQuickVisualModel::Referenced;
2254 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2256 return createComponentItem(highlightComponent, 0.0, true);
2259 QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault)
2261 Q_Q(QQuickItemView);
2263 QQuickItem *item = 0;
2265 QQmlContext *creationContext = component->creationContext();
2266 QQmlContext *context = new QQmlContext(
2267 creationContext ? creationContext : qmlContext(q));
2268 QObject *nobj = component->beginCreate(context);
2270 QQml_setParent_noEvent(context, nobj);
2271 item = qobject_cast<QQuickItem *>(nobj);
2277 } else if (createDefault) {
2278 item = new QQuickItem;
2282 QQml_setParent_noEvent(item, q->contentItem());
2283 item->setParentItem(q->contentItem());
2286 component->completeCreate();
2290 void QQuickItemViewPrivate::updateTrackedItem()
2292 Q_Q(QQuickItemView);
2293 FxViewItem *item = currentItem;
2299 q->trackedPositionChanged();
2302 void QQuickItemViewPrivate::updateUnrequestedIndexes()
2304 Q_Q(QQuickItemView);
2305 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2306 *it = model->indexOf(it.key(), q);
2309 void QQuickItemViewPrivate::updateUnrequestedPositions()
2311 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2312 repositionPackageItemAt(it.key(), it.value());
2315 void QQuickItemViewPrivate::updateVisibleIndex()
2318 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
2319 if ((*it)->index != -1) {
2320 visibleIndex = (*it)->index;