1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgitemview_p_p.h"
47 FxViewItem::FxViewItem(QSGItem *i, bool own)
48 : item(i), ownItem(own), index(-1)
52 FxViewItem::~FxViewItem()
54 if (ownItem && item) {
55 item->setParentItem(0);
61 QSGItemView::QSGItemView(QSGFlickablePrivate &dd, QSGItem *parent)
62 : QSGFlickable(dd, parent)
68 QSGItemView::~QSGItemView()
79 QSGItem *QSGItemView::currentItem() const
81 Q_D(const QSGItemView);
84 return d->currentItem->item;
87 QVariant QSGItemView::model() const
89 Q_D(const QSGItemView);
90 return d->modelVariant;
93 void QSGItemView::setModel(const QVariant &model)
96 if (d->modelVariant == model)
99 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
100 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
101 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
102 disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
103 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
104 disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
105 disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
108 QSGVisualModel *oldModel = d->model;
111 d->setPosition(d->contentStartPosition());
113 d->modelVariant = model;
115 QObject *object = qvariant_cast<QObject*>(model);
116 QSGVisualModel *vim = 0;
117 if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
125 d->model = new QSGVisualDataModel(qmlContext(this), this);
130 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
131 dataModel->setModel(model);
135 d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter;
136 if (isComponentComplete()) {
139 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
142 d->moveReason = QSGItemViewPrivate::SetIndex;
143 d->updateCurrent(d->currentIndex);
144 if (d->highlight && d->currentItem) {
145 if (d->autoHighlight)
146 d->resetHighlightPosition();
147 d->updateTrackedItem();
149 d->moveReason = QSGItemViewPrivate::Other;
153 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
154 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
155 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
156 connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
157 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
158 connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
159 connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
165 QDeclarativeComponent *QSGItemView::delegate() const
167 Q_D(const QSGItemView);
169 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
170 return dataModel->delegate();
176 void QSGItemView::setDelegate(QDeclarativeComponent *delegate)
179 if (delegate == this->delegate())
182 d->model = new QSGVisualDataModel(qmlContext(this));
185 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
186 int oldCount = dataModel->count();
187 dataModel->setDelegate(delegate);
188 if (isComponentComplete()) {
189 for (int i = 0; i < d->visibleItems.count(); ++i)
190 d->releaseItem(d->visibleItems.at(i));
191 d->visibleItems.clear();
192 d->releaseItem(d->currentItem);
196 d->moveReason = QSGItemViewPrivate::SetIndex;
197 d->updateCurrent(d->currentIndex);
198 if (d->highlight && d->currentItem) {
199 if (d->autoHighlight)
200 d->resetHighlightPosition();
201 d->updateTrackedItem();
203 d->moveReason = QSGItemViewPrivate::Other;
206 if (oldCount != dataModel->count())
209 emit delegateChanged();
213 int QSGItemView::count() const
215 Q_D(const QSGItemView);
217 return d->model->count();
221 int QSGItemView::currentIndex() const
223 Q_D(const QSGItemView);
224 return d->currentIndex;
227 void QSGItemView::setCurrentIndex(int index)
230 if (d->requestedIndex >= 0) // currently creating item
232 d->currentIndexCleared = (index == -1);
233 if (index == d->currentIndex)
235 if (isComponentComplete() && d->isValid()) {
236 d->moveReason = QSGItemViewPrivate::SetIndex;
237 d->updateCurrent(index);
238 } else if (d->currentIndex != index) {
239 d->currentIndex = index;
240 emit currentIndexChanged();
245 bool QSGItemView::isWrapEnabled() const
247 Q_D(const QSGItemView);
251 void QSGItemView::setWrapEnabled(bool wrap)
257 emit keyNavigationWrapsChanged();
260 int QSGItemView::cacheBuffer() const
262 Q_D(const QSGItemView);
266 void QSGItemView::setCacheBuffer(int b)
269 if (d->buffer != b) {
271 if (isComponentComplete()) {
272 d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter;
275 emit cacheBufferChanged();
280 Qt::LayoutDirection QSGItemView::layoutDirection() const
282 Q_D(const QSGItemView);
283 return d->layoutDirection;
286 void QSGItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
289 if (d->layoutDirection != layoutDirection) {
290 d->layoutDirection = layoutDirection;
292 emit layoutDirectionChanged();
293 emit effectiveLayoutDirectionChanged();
297 Qt::LayoutDirection QSGItemView::effectiveLayoutDirection() const
299 Q_D(const QSGItemView);
300 if (d->effectiveLayoutMirror)
301 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
303 return d->layoutDirection;
307 QDeclarativeComponent *QSGItemView::header() const
309 Q_D(const QSGItemView);
310 return d->headerComponent;
313 QSGItem *QSGItemView::headerItem() const
315 Q_D(const QSGItemView);
316 return d->header ? d->header->item : 0;
319 void QSGItemView::setHeader(QDeclarativeComponent *headerComponent)
322 if (d->headerComponent != headerComponent) {
325 d->headerComponent = headerComponent;
327 d->minExtentDirty = true;
328 d->maxExtentDirty = true;
330 if (isComponentComplete()) {
336 emit headerItemChanged();
338 emit headerChanged();
342 QDeclarativeComponent *QSGItemView::footer() const
344 Q_D(const QSGItemView);
345 return d->footerComponent;
348 QSGItem *QSGItemView::footerItem() const
350 Q_D(const QSGItemView);
351 return d->footer ? d->footer->item : 0;
354 void QSGItemView::setFooter(QDeclarativeComponent *footerComponent)
357 if (d->footerComponent != footerComponent) {
360 d->footerComponent = footerComponent;
362 if (isComponentComplete()) {
367 emit footerItemChanged();
369 emit footerChanged();
373 QDeclarativeComponent *QSGItemView::highlight() const
375 Q_D(const QSGItemView);
376 return d->highlightComponent;
379 void QSGItemView::setHighlight(QDeclarativeComponent *highlightComponent)
382 if (highlightComponent != d->highlightComponent) {
383 d->highlightComponent = highlightComponent;
384 d->createHighlight();
386 d->updateHighlight();
387 emit highlightChanged();
391 QSGItem *QSGItemView::highlightItem() const
393 Q_D(const QSGItemView);
396 return d->highlight->item;
399 bool QSGItemView::highlightFollowsCurrentItem() const
401 Q_D(const QSGItemView);
402 return d->autoHighlight;
405 void QSGItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
408 if (d->autoHighlight != autoHighlight) {
409 d->autoHighlight = autoHighlight;
411 d->updateHighlight();
412 emit highlightFollowsCurrentItemChanged();
416 QSGItemView::HighlightRangeMode QSGItemView::highlightRangeMode() const
418 Q_D(const QSGItemView);
419 return static_cast<QSGItemView::HighlightRangeMode>(d->highlightRange);
422 void QSGItemView::setHighlightRangeMode(HighlightRangeMode mode)
425 if (d->highlightRange == mode)
427 d->highlightRange = mode;
428 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
429 emit highlightRangeModeChanged();
432 //###Possibly rename these properties, since they are very useful even without a highlight?
433 qreal QSGItemView::preferredHighlightBegin() const
435 Q_D(const QSGItemView);
436 return d->highlightRangeStart;
439 void QSGItemView::setPreferredHighlightBegin(qreal start)
442 d->highlightRangeStartValid = true;
443 if (d->highlightRangeStart == start)
445 d->highlightRangeStart = start;
446 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
447 emit preferredHighlightBeginChanged();
450 void QSGItemView::resetPreferredHighlightBegin()
453 d->highlightRangeStartValid = false;
454 if (d->highlightRangeStart == 0)
456 d->highlightRangeStart = 0;
457 emit preferredHighlightBeginChanged();
460 qreal QSGItemView::preferredHighlightEnd() const
462 Q_D(const QSGItemView);
463 return d->highlightRangeEnd;
466 void QSGItemView::setPreferredHighlightEnd(qreal end)
469 d->highlightRangeEndValid = true;
470 if (d->highlightRangeEnd == end)
472 d->highlightRangeEnd = end;
473 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
474 emit preferredHighlightEndChanged();
477 void QSGItemView::resetPreferredHighlightEnd()
480 d->highlightRangeEndValid = false;
481 if (d->highlightRangeEnd == 0)
483 d->highlightRangeEnd = 0;
484 emit preferredHighlightEndChanged();
487 int QSGItemView::highlightMoveDuration() const
489 Q_D(const QSGItemView);
490 return d->highlightMoveDuration;
493 void QSGItemView::setHighlightMoveDuration(int duration)
496 if (d->highlightMoveDuration != duration) {
497 d->highlightMoveDuration = duration;
498 emit highlightMoveDurationChanged();
502 void QSGItemViewPrivate::positionViewAtIndex(int index, int mode)
507 if (mode < QSGItemView::Beginning || mode > QSGItemView::Contain)
509 int idx = qMax(qMin(index, model->count()-1), 0);
513 qreal pos = isContentFlowReversed() ? -position() - size() : position();
514 FxViewItem *item = visibleItem(idx);
516 if (layoutOrientation() == Qt::Vertical)
517 maxExtent = -q->maxYExtent();
519 maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
521 int itemPos = positionAt(idx);
522 changedVisibleIndex(idx);
523 // save the currently visible items in case any of them end up visible again
524 QList<FxViewItem *> oldVisible = visibleItems;
525 visibleItems.clear();
526 setPosition(qMin(qreal(itemPos), maxExtent));
527 // now release the reference to all the old visible items.
528 for (int i = 0; i < oldVisible.count(); ++i)
529 releaseItem(oldVisible.at(i));
530 item = visibleItem(idx);
533 const qreal itemPos = item->position();
535 case QSGItemView::Beginning:
537 if (index < 0 && header)
540 case QSGItemView::Center:
541 pos = itemPos - (size() - item->size())/2;
543 case QSGItemView::End:
544 pos = itemPos - size() + item->size();
545 if (index >= model->count() && footer)
548 case QSGItemView::Visible:
549 if (itemPos > pos + size())
550 pos = itemPos - size() + item->size();
551 else if (item->endPosition() <= pos)
554 case QSGItemView::Contain:
555 if (item->endPosition() >= pos + size())
556 pos = itemPos - size() + item->size();
560 pos = qMin(pos, maxExtent);
562 if (layoutOrientation() == Qt::Vertical)
563 minExtent = -q->minYExtent();
565 minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
566 pos = qMax(pos, minExtent);
567 moveReason = QSGItemViewPrivate::Other;
573 resetHighlightPosition();
580 void QSGItemView::positionViewAtIndex(int index, int mode)
583 if (!d->isValid() || index < 0 || index >= d->model->count())
585 d->positionViewAtIndex(index, mode);
589 void QSGItemView::positionViewAtBeginning()
594 d->positionViewAtIndex(-1, Beginning);
597 void QSGItemView::positionViewAtEnd()
602 d->positionViewAtIndex(d->model->count(), End);
605 int QSGItemView::indexAt(qreal x, qreal y) const
607 Q_D(const QSGItemView);
608 for (int i = 0; i < d->visibleItems.count(); ++i) {
609 const FxViewItem *item = d->visibleItems.at(i);
610 if (item->contains(x, y))
618 // for debugging only
619 void QSGItemViewPrivate::checkVisible() const
622 for (int i = 0; i < visibleItems.count(); ++i) {
623 FxViewItem *item = visibleItems.at(i);
624 if (item->index == -1) {
626 } else if (item->index != visibleIndex + i - skip) {
627 qFatal("index %d %d %d", visibleIndex, i, item->index);
634 void QSGItemViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
637 QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
638 if (!q->isComponentComplete())
641 if (header && header->item == item)
643 else if (footer && footer->item == item)
646 if (currentItem && currentItem->item == item)
648 if (trackedItem && trackedItem->item == item)
649 q->trackedPositionChanged();
652 void QSGItemView::destroyRemoved()
655 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
656 it != d->visibleItems.end();) {
657 FxViewItem *item = *it;
658 if (item->index == -1 && item->attached->delayRemove() == false) {
659 d->releaseItem(item);
660 it = d->visibleItems.erase(it);
666 // Correct the positioning of the items
671 void QSGItemView::itemsChanged(int, int)
678 void QSGItemView::modelReset()
681 d->moveReason = QSGItemViewPrivate::SetIndex;
683 if (d->highlight && d->currentItem) {
684 if (d->autoHighlight)
685 d->resetHighlightPosition();
686 d->updateTrackedItem();
688 d->moveReason = QSGItemViewPrivate::Other;
693 void QSGItemView::createdItem(int index, QSGItem *item)
696 if (d->requestedIndex != index) {
697 item->setParentItem(contentItem());
698 d->unrequestedItems.insert(item, index);
699 d->repositionPackageItemAt(item, index);
703 void QSGItemView::destroyingItem(QSGItem *item)
706 d->unrequestedItems.remove(item);
709 void QSGItemView::animStopped()
712 d->bufferMode = QSGItemViewPrivate::NoBuffer;
713 if (d->haveHighlightRange && d->highlightRange == QSGItemView::StrictlyEnforceRange)
714 d->updateHighlight();
718 void QSGItemView::trackedPositionChanged()
721 if (!d->trackedItem || !d->currentItem)
723 if (d->moveReason == QSGItemViewPrivate::SetIndex) {
724 qreal trackedPos = d->trackedItem->position();
725 qreal trackedSize = d->trackedItem->size();
726 if (d->trackedItem != d->currentItem) {
727 trackedSize += d->currentItem->sectionSize();
730 qreal highlightStart;
732 if (d->isContentFlowReversed()) {
733 viewPos = -d->position()-d->size();
734 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
735 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
737 viewPos = d->position();
738 highlightStart = d->highlightRangeStart;
739 highlightEnd = d->highlightRangeEnd;
742 if (d->haveHighlightRange) {
743 if (d->highlightRange == StrictlyEnforceRange) {
744 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
745 pos = trackedPos - highlightEnd + d->trackedItem->size();
746 if (trackedPos < pos + highlightStart)
747 pos = trackedPos - highlightStart;
749 if (trackedPos > pos + highlightEnd - trackedSize)
750 pos = trackedPos - highlightEnd + trackedSize;
751 if (trackedPos < pos + highlightStart)
752 pos = trackedPos - highlightStart;
753 if (pos > d->endPosition() - d->size())
754 pos = d->endPosition() - d->size();
755 if (pos < d->startPosition())
756 pos = d->startPosition();
759 qreal trackedEndPos = d->trackedItem->endPosition();
760 qreal toItemPos = d->currentItem->position();
761 qreal toItemEndPos = d->currentItem->endPosition();
763 if (d->header && d->showHeaderForIndex(d->currentIndex)) {
764 trackedPos -= d->headerSize();
765 trackedEndPos -= d->headerSize();
766 toItemPos -= d->headerSize();
767 toItemEndPos -= d->headerSize();
768 } else if (d->footer && d->showFooterForIndex(d->currentIndex)) {
769 trackedPos += d->footerSize();
770 trackedEndPos += d->footerSize();
771 toItemPos += d->footerSize();
772 toItemEndPos += d->footerSize();
775 if (trackedPos < viewPos && toItemPos < viewPos) {
776 pos = qMax(trackedPos, toItemPos);
777 } else if (trackedEndPos >= viewPos + d->size()
778 && toItemEndPos >= viewPos + d->size()) {
779 if (trackedEndPos <= toItemEndPos) {
780 pos = trackedEndPos - d->size();
781 if (trackedSize > d->size())
784 pos = toItemEndPos - d->size();
785 if (d->currentItem->size() > d->size())
786 pos = d->currentItem->position();
790 if (viewPos != pos) {
792 d->calcVelocity = true;
794 d->calcVelocity = false;
800 void QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
803 d->maxExtentDirty = true;
804 d->minExtentDirty = true;
805 QSGFlickable::geometryChanged(newGeometry, oldGeometry);
809 qreal QSGItemView::minYExtent() const
811 Q_D(const QSGItemView);
812 if (d->layoutOrientation() == Qt::Horizontal)
813 return QSGFlickable::minYExtent();
815 if (d->minExtentDirty) {
816 d->minExtent = -d->startPosition();
817 if (d->header && d->visibleItems.count())
818 d->minExtent += d->headerSize();
819 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
820 d->minExtent += d->highlightRangeStart;
821 if (d->visibleItem(0))
822 d->minExtent -= d->visibleItem(0)->sectionSize();
823 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
825 d->minExtentDirty = false;
831 qreal QSGItemView::maxYExtent() const
833 Q_D(const QSGItemView);
834 if (d->layoutOrientation() == Qt::Horizontal)
837 if (d->maxExtentDirty) {
838 if (!d->model || !d->model->count()) {
839 d->maxExtent = d->header ? -d->headerSize() : 0;
840 d->maxExtent += height();
841 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
842 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
843 if (d->highlightRangeEnd != d->highlightRangeStart)
844 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
846 d->maxExtent = -(d->endPosition() - height());
850 d->maxExtent -= d->footerSize();
851 qreal minY = minYExtent();
852 if (d->maxExtent > minY)
854 d->maxExtentDirty = false;
859 qreal QSGItemView::minXExtent() const
861 Q_D(const QSGItemView);
862 if (d->layoutOrientation() == Qt::Vertical)
863 return QSGFlickable::minXExtent();
865 if (d->minExtentDirty) {
866 d->minExtent = -d->startPosition();
867 qreal highlightStart;
869 qreal endPositionFirstItem = 0;
870 if (d->isContentFlowReversed()) {
871 if (d->model && d->model->count())
872 endPositionFirstItem = d->positionAt(d->model->count()-1);
874 d->minExtent += d->headerSize();
875 highlightStart = d->highlightRangeStartValid
876 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
877 : d->size() - (d->lastPosition()-endPositionFirstItem);
878 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
880 d->minExtent += d->footerSize();
881 qreal maxX = maxXExtent();
882 if (d->minExtent < maxX)
885 endPositionFirstItem = d->endPositionAt(0);
886 highlightStart = d->highlightRangeStart;
887 highlightEnd = d->highlightRangeEnd;
888 if (d->header && d->visibleItems.count())
889 d->minExtent += d->headerSize();
891 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
892 d->minExtent += highlightStart;
893 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
895 d->minExtentDirty = false;
901 qreal QSGItemView::maxXExtent() const
903 Q_D(const QSGItemView);
904 if (d->layoutOrientation() == Qt::Vertical)
907 if (d->maxExtentDirty) {
908 qreal highlightStart;
910 qreal lastItemPosition = 0;
912 if (d->isContentFlowReversed()) {
913 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
914 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
915 lastItemPosition = d->endPosition();
917 highlightStart = d->highlightRangeStart;
918 highlightEnd = d->highlightRangeEnd;
919 if (d->model && d->model->count())
920 lastItemPosition = d->positionAt(d->model->count()-1);
922 if (!d->model || !d->model->count()) {
923 if (!d->isContentFlowReversed())
924 d->maxExtent = d->header ? -d->headerSize() : 0;
925 d->maxExtent += width();
926 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
927 d->maxExtent = -(lastItemPosition - highlightStart);
928 if (highlightEnd != highlightStart) {
929 d->maxExtent = d->isContentFlowReversed()
930 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
931 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
934 d->maxExtent = -(d->endPosition() - width());
936 if (d->isContentFlowReversed()) {
937 if (d->header && d->visibleItems.count())
938 d->maxExtent -= d->headerSize();
941 d->maxExtent -= d->footerSize();
942 qreal minX = minXExtent();
943 if (d->maxExtent > minX)
946 d->maxExtentDirty = false;
952 void QSGItemView::setContentX(qreal pos)
955 // Positioning the view manually should override any current movement state
956 d->moveReason = QSGItemViewPrivate::Other;
957 QSGFlickable::setContentX(pos);
960 void QSGItemView::setContentY(qreal pos)
963 // Positioning the view manually should override any current movement state
964 d->moveReason = QSGItemViewPrivate::Other;
965 QSGFlickable::setContentY(pos);
969 void QSGItemView::updatePolish()
972 QSGFlickable::updatePolish();
976 void QSGItemView::componentComplete()
979 QSGFlickable::componentComplete();
985 d->setPosition(d->contentStartPosition());
988 d->moveReason = QSGItemViewPrivate::SetIndex;
989 if (d->currentIndex < 0 && !d->currentIndexCleared)
992 d->updateCurrent(d->currentIndex);
993 if (d->highlight && d->currentItem) {
994 if (d->autoHighlight)
995 d->resetHighlightPosition();
996 d->updateTrackedItem();
998 d->moveReason = QSGItemViewPrivate::Other;
1005 QSGItemViewPrivate::QSGItemViewPrivate()
1007 , buffer(0), bufferMode(BufferBefore | BufferAfter)
1008 , layoutDirection(Qt::LeftToRight)
1011 , currentIndex(-1), currentItem(0)
1012 , trackedItem(0), requestedIndex(-1)
1013 , highlightComponent(0), highlight(0)
1014 , highlightRange(QSGItemView::NoHighlightRange)
1015 , highlightRangeStart(0), highlightRangeEnd(0)
1016 , highlightMoveDuration(150)
1017 , headerComponent(0), header(0), footerComponent(0), footer(0)
1018 , minExtent(0), maxExtent(0)
1019 , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false)
1020 , layoutScheduled(false), inViewportMoved(false), currentIndexCleared(false)
1021 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1022 , minExtentDirty(true), maxExtentDirty(true)
1026 bool QSGItemViewPrivate::isValid() const
1028 return model && model->count() && model->isValid();
1031 qreal QSGItemViewPrivate::position() const
1033 Q_Q(const QSGItemView);
1034 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1037 qreal QSGItemViewPrivate::size() const
1039 Q_Q(const QSGItemView);
1040 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1043 qreal QSGItemViewPrivate::startPosition() const
1045 return isContentFlowReversed() ? -lastPosition() : originPosition();
1048 qreal QSGItemViewPrivate::endPosition() const
1050 return isContentFlowReversed() ? -originPosition() : lastPosition();
1053 qreal QSGItemViewPrivate::contentStartPosition() const
1055 return -headerSize();
1058 int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1060 if (visibleItems.count()) {
1061 int i = visibleItems.count() - 1;
1062 while (i > 0 && visibleItems.at(i)->index == -1)
1064 if (visibleItems.at(i)->index != -1)
1065 return visibleItems.at(i)->index;
1067 return defaultValue;
1070 FxViewItem *QSGItemViewPrivate::visibleItem(int modelIndex) const {
1071 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1072 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1073 FxViewItem *item = visibleItems.at(i);
1074 if (item->index == modelIndex)
1081 FxViewItem *QSGItemViewPrivate::firstVisibleItem() const {
1082 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1083 for (int i = 0; i < visibleItems.count(); ++i) {
1084 FxViewItem *item = visibleItems.at(i);
1085 if (item->index != -1 && item->endPosition() >= pos)
1088 return visibleItems.count() ? visibleItems.first() : 0;
1091 // Map a model index to visibleItems list index.
1092 // These may differ if removed items are still present in the visible list,
1093 // e.g. doing a removal animation
1094 int QSGItemViewPrivate::mapFromModel(int modelIndex) const
1096 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1098 for (int i = 0; i < visibleItems.count(); ++i) {
1099 FxViewItem *item = visibleItems.at(i);
1100 if (item->index == modelIndex)
1101 return i + visibleIndex;
1102 if (item->index > modelIndex)
1105 return -1; // Not in visibleList
1108 void QSGItemViewPrivate::adjustMoveParameters(int *from, int *to, int *count) const
1111 // Only move forwards - flip if backwards moving
1116 *count = tfrom - tto;
1120 void QSGItemViewPrivate::init()
1123 QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
1124 q->setFlag(QSGItem::ItemIsFocusScope);
1125 addItemChangeListener(this, Geometry);
1126 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1127 q->setFlickableDirection(QSGFlickable::VerticalFlick);
1130 void QSGItemViewPrivate::updateCurrent(int modelIndex)
1133 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1135 currentItem->attached->setIsCurrentItem(false);
1136 releaseItem(currentItem);
1138 currentIndex = modelIndex;
1139 emit q->currentIndexChanged();
1141 } else if (currentIndex != modelIndex) {
1142 currentIndex = modelIndex;
1143 emit q->currentIndexChanged();
1148 if (currentItem && currentIndex == modelIndex) {
1153 FxViewItem *oldCurrentItem = currentItem;
1154 currentIndex = modelIndex;
1155 currentItem = createItem(modelIndex);
1156 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1157 oldCurrentItem->attached->setIsCurrentItem(false);
1159 currentItem->item->setFocus(true);
1160 currentItem->attached->setIsCurrentItem(true);
1161 initializeCurrentItem();
1165 emit q->currentIndexChanged();
1166 releaseItem(oldCurrentItem);
1169 void QSGItemViewPrivate::clear()
1173 for (int i = 0; i < visibleItems.count(); ++i)
1174 releaseItem(visibleItems.at(i));
1175 visibleItems.clear();
1178 releaseItem(currentItem);
1183 minExtentDirty = true;
1184 maxExtentDirty = true;
1189 void QSGItemViewPrivate::mirrorChange()
1193 emit q->effectiveLayoutDirectionChanged();
1196 void QSGItemViewPrivate::refill()
1198 if (isContentFlowReversed())
1199 refill(-position()-size(), -position());
1201 refill(position(), position()+size());
1204 void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer)
1207 if (!isValid() || !q->isComponentComplete())
1210 itemCount = model->count();
1211 qreal bufferFrom = from - buffer;
1212 qreal bufferTo = to + buffer;
1213 qreal fillFrom = from;
1215 if (doBuffer && (bufferMode & BufferAfter))
1217 if (doBuffer && (bufferMode & BufferBefore))
1218 fillFrom = bufferFrom;
1220 // Item creation and release is staggered in order to avoid
1221 // creating/releasing multiple items in one frame
1222 // while flicking (as much as possible).
1224 bool changed = addVisibleItems(fillFrom, fillTo, doBuffer);
1226 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
1227 if (removeNonVisibleItems(bufferFrom, bufferTo))
1229 deferredRelease = false;
1231 deferredRelease = true;
1235 minExtentDirty = true;
1236 maxExtentDirty = true;
1237 visibleItemsChanged();
1238 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
1239 refill(from, to, true);
1242 lazyRelease = false;
1245 void QSGItemViewPrivate::regenerate()
1248 if (q->isComponentComplete()) {
1257 setPosition(contentStartPosition());
1259 updateCurrent(currentIndex);
1263 void QSGItemViewPrivate::scheduleLayout()
1266 if (!layoutScheduled) {
1267 layoutScheduled = true;
1272 void QSGItemViewPrivate::updateViewport()
1276 if (layoutOrientation() == Qt::Vertical)
1277 q->setContentHeight(endPosition() - startPosition());
1279 q->setContentWidth(endPosition() - startPosition());
1283 void QSGItemViewPrivate::layout()
1286 layoutScheduled = false;
1287 if (!isValid() && !visibleItems.count()) {
1289 setPosition(contentStartPosition());
1293 layoutVisibleItems();
1296 minExtentDirty = true;
1297 maxExtentDirty = true;
1300 if (!q->isMoving() && !q->isFlicking()) {
1308 updateUnrequestedPositions();
1311 FxViewItem *QSGItemViewPrivate::createItem(int modelIndex)
1315 requestedIndex = modelIndex;
1316 FxViewItem *viewItem = 0;
1318 if (QSGItem *item = model->item(modelIndex, false)) {
1319 viewItem = newViewItem(modelIndex, item);
1321 viewItem->index = modelIndex;
1322 if (model->completePending()) {
1324 viewItem->item->setZ(1);
1325 viewItem->item->setParentItem(q->contentItem());
1326 model->completeItem();
1328 viewItem->item->setParentItem(q->contentItem());
1330 // do other set up for the new item that should not happen
1331 // until after bindings are evaluated
1332 initializeViewItem(viewItem);
1334 unrequestedItems.remove(viewItem->item);
1337 requestedIndex = -1;
1342 void QSGItemViewPrivate::releaseItem(FxViewItem *item)
1345 if (!item || !model)
1347 if (trackedItem == item)
1349 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
1350 itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
1351 if (model->release(item->item) == 0) {
1352 // item was not destroyed, and we no longer reference it.
1353 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
1358 QSGItem *QSGItemViewPrivate::createHighlightItem()
1360 return createComponentItem(highlightComponent, true, true);
1363 QSGItem *QSGItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault)
1369 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1370 QObject *nobj = component->create(context);
1372 QDeclarative_setParent_noEvent(context, nobj);
1373 item = qobject_cast<QSGItem *>(nobj);
1379 } else if (createDefault) {
1383 QDeclarative_setParent_noEvent(item, q->contentItem());
1384 item->setParentItem(q->contentItem());
1385 if (receiveItemGeometryChanges) {
1386 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1387 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1393 void QSGItemViewPrivate::updateTrackedItem()
1396 FxViewItem *item = currentItem;
1402 q->trackedPositionChanged();
1405 void QSGItemViewPrivate::updateUnrequestedIndexes()
1408 for (QHash<QSGItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1409 *it = model->indexOf(it.key(), q);
1412 void QSGItemViewPrivate::updateUnrequestedPositions()
1414 for (QHash<QSGItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1415 repositionPackageItemAt(it.key(), it.value());