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 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
760 pos = qMax(trackedPos, d->currentItem->position());
761 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
762 && d->currentItem->endPosition() >= viewPos + d->size()) {
763 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
764 pos = d->trackedItem->endPosition() - d->size();
765 if (trackedSize > d->size())
768 pos = d->currentItem->endPosition() - d->size();
769 if (d->currentItem->size() > d->size())
770 pos = d->currentItem->position();
774 if (viewPos != pos) {
776 d->calcVelocity = true;
778 d->calcVelocity = false;
784 void QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
787 d->maxExtentDirty = true;
788 d->minExtentDirty = true;
789 QSGFlickable::geometryChanged(newGeometry, oldGeometry);
793 qreal QSGItemView::minYExtent() const
795 Q_D(const QSGItemView);
796 if (d->layoutOrientation() == Qt::Horizontal)
797 return QSGFlickable::minYExtent();
799 if (d->minExtentDirty) {
800 d->minExtent = -d->startPosition();
801 if (d->header && d->visibleItems.count())
802 d->minExtent += d->headerSize();
803 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
804 d->minExtent += d->highlightRangeStart;
805 if (d->visibleItem(0))
806 d->minExtent -= d->visibleItem(0)->sectionSize();
807 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
809 d->minExtentDirty = false;
815 qreal QSGItemView::maxYExtent() const
817 Q_D(const QSGItemView);
818 if (d->layoutOrientation() == Qt::Horizontal)
821 if (d->maxExtentDirty) {
822 if (!d->model || !d->model->count()) {
823 d->maxExtent = d->header ? -d->headerSize() : 0;
824 d->maxExtent += height();
825 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
826 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
827 if (d->highlightRangeEnd != d->highlightRangeStart)
828 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
830 d->maxExtent = -(d->endPosition() - height());
834 d->maxExtent -= d->footerSize();
835 qreal minY = minYExtent();
836 if (d->maxExtent > minY)
838 d->maxExtentDirty = false;
843 qreal QSGItemView::minXExtent() const
845 Q_D(const QSGItemView);
846 if (d->layoutOrientation() == Qt::Vertical)
847 return QSGFlickable::minXExtent();
849 if (d->minExtentDirty) {
850 d->minExtent = -d->startPosition();
851 qreal highlightStart;
853 qreal endPositionFirstItem = 0;
854 if (d->isContentFlowReversed()) {
855 if (d->model && d->model->count())
856 endPositionFirstItem = d->positionAt(d->model->count()-1);
858 d->minExtent += d->headerSize();
859 highlightStart = d->highlightRangeStartValid
860 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
861 : d->size() - (d->lastPosition()-endPositionFirstItem);
862 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
864 d->minExtent += d->footerSize();
865 qreal maxX = maxXExtent();
866 if (d->minExtent < maxX)
869 endPositionFirstItem = d->endPositionAt(0);
870 highlightStart = d->highlightRangeStart;
871 highlightEnd = d->highlightRangeEnd;
872 if (d->header && d->visibleItems.count())
873 d->minExtent += d->headerSize();
875 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
876 d->minExtent += highlightStart;
877 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
879 d->minExtentDirty = false;
885 qreal QSGItemView::maxXExtent() const
887 Q_D(const QSGItemView);
888 if (d->layoutOrientation() == Qt::Vertical)
891 if (d->maxExtentDirty) {
892 qreal highlightStart;
894 qreal lastItemPosition = 0;
896 if (d->isContentFlowReversed()) {
897 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
898 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
899 lastItemPosition = d->endPosition();
901 highlightStart = d->highlightRangeStart;
902 highlightEnd = d->highlightRangeEnd;
903 if (d->model && d->model->count())
904 lastItemPosition = d->positionAt(d->model->count()-1);
906 if (!d->model || !d->model->count()) {
907 if (!d->isContentFlowReversed())
908 d->maxExtent = d->header ? -d->headerSize() : 0;
909 d->maxExtent += width();
910 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
911 d->maxExtent = -(lastItemPosition - highlightStart);
912 if (highlightEnd != highlightStart) {
913 d->maxExtent = d->isContentFlowReversed()
914 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
915 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
918 d->maxExtent = -(d->endPosition() - width());
920 if (d->isContentFlowReversed()) {
921 if (d->header && d->visibleItems.count())
922 d->maxExtent -= d->headerSize();
925 d->maxExtent -= d->footerSize();
926 qreal minX = minXExtent();
927 if (d->maxExtent > minX)
930 d->maxExtentDirty = false;
936 void QSGItemView::setContentX(qreal pos)
939 // Positioning the view manually should override any current movement state
940 d->moveReason = QSGItemViewPrivate::Other;
941 QSGFlickable::setContentX(pos);
944 void QSGItemView::setContentY(qreal pos)
947 // Positioning the view manually should override any current movement state
948 d->moveReason = QSGItemViewPrivate::Other;
949 QSGFlickable::setContentY(pos);
953 void QSGItemView::updatePolish()
956 QSGFlickable::updatePolish();
960 void QSGItemView::componentComplete()
963 QSGFlickable::componentComplete();
969 d->setPosition(d->contentStartPosition());
972 d->moveReason = QSGItemViewPrivate::SetIndex;
973 if (d->currentIndex < 0 && !d->currentIndexCleared)
976 d->updateCurrent(d->currentIndex);
977 if (d->highlight && d->currentItem) {
978 if (d->autoHighlight)
979 d->resetHighlightPosition();
980 d->updateTrackedItem();
982 d->moveReason = QSGItemViewPrivate::Other;
989 QSGItemViewPrivate::QSGItemViewPrivate()
991 , buffer(0), bufferMode(BufferBefore | BufferAfter)
992 , layoutDirection(Qt::LeftToRight)
995 , currentIndex(-1), currentItem(0)
996 , trackedItem(0), requestedIndex(-1)
997 , highlightComponent(0), highlight(0)
998 , highlightRange(QSGItemView::NoHighlightRange)
999 , highlightRangeStart(0), highlightRangeEnd(0)
1000 , highlightMoveDuration(150)
1001 , headerComponent(0), header(0), footerComponent(0), footer(0)
1002 , minExtent(0), maxExtent(0)
1003 , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false)
1004 , layoutScheduled(false), inViewportMoved(false), currentIndexCleared(false)
1005 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1006 , minExtentDirty(true), maxExtentDirty(true)
1010 bool QSGItemViewPrivate::isValid() const
1012 return model && model->count() && model->isValid();
1015 qreal QSGItemViewPrivate::position() const
1017 Q_Q(const QSGItemView);
1018 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1021 qreal QSGItemViewPrivate::size() const
1023 Q_Q(const QSGItemView);
1024 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1027 qreal QSGItemViewPrivate::startPosition() const
1029 return isContentFlowReversed() ? -lastPosition() : originPosition();
1032 qreal QSGItemViewPrivate::endPosition() const
1034 return isContentFlowReversed() ? -originPosition() : lastPosition();
1037 qreal QSGItemViewPrivate::contentStartPosition() const
1039 return -headerSize();
1042 int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1044 if (visibleItems.count()) {
1045 int i = visibleItems.count() - 1;
1046 while (i > 0 && visibleItems.at(i)->index == -1)
1048 if (visibleItems.at(i)->index != -1)
1049 return visibleItems.at(i)->index;
1051 return defaultValue;
1054 FxViewItem *QSGItemViewPrivate::visibleItem(int modelIndex) const {
1055 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1056 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1057 FxViewItem *item = visibleItems.at(i);
1058 if (item->index == modelIndex)
1065 FxViewItem *QSGItemViewPrivate::firstVisibleItem() const {
1066 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1067 for (int i = 0; i < visibleItems.count(); ++i) {
1068 FxViewItem *item = visibleItems.at(i);
1069 if (item->index != -1 && item->endPosition() >= pos)
1072 return visibleItems.count() ? visibleItems.first() : 0;
1075 // Map a model index to visibleItems list index.
1076 // These may differ if removed items are still present in the visible list,
1077 // e.g. doing a removal animation
1078 int QSGItemViewPrivate::mapFromModel(int modelIndex) const
1080 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1082 for (int i = 0; i < visibleItems.count(); ++i) {
1083 FxViewItem *item = visibleItems.at(i);
1084 if (item->index == modelIndex)
1085 return i + visibleIndex;
1086 if (item->index > modelIndex)
1089 return -1; // Not in visibleList
1092 void QSGItemViewPrivate::adjustMoveParameters(int *from, int *to, int *count) const
1095 // Only move forwards - flip if backwards moving
1100 *count = tfrom - tto;
1104 void QSGItemViewPrivate::init()
1107 QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
1108 q->setFlag(QSGItem::ItemIsFocusScope);
1109 addItemChangeListener(this, Geometry);
1110 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1111 q->setFlickableDirection(QSGFlickable::VerticalFlick);
1114 void QSGItemViewPrivate::updateCurrent(int modelIndex)
1117 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1119 currentItem->attached->setIsCurrentItem(false);
1120 releaseItem(currentItem);
1122 currentIndex = modelIndex;
1123 emit q->currentIndexChanged();
1125 } else if (currentIndex != modelIndex) {
1126 currentIndex = modelIndex;
1127 emit q->currentIndexChanged();
1132 if (currentItem && currentIndex == modelIndex) {
1137 FxViewItem *oldCurrentItem = currentItem;
1138 currentIndex = modelIndex;
1139 currentItem = createItem(modelIndex);
1140 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1141 oldCurrentItem->attached->setIsCurrentItem(false);
1143 currentItem->item->setFocus(true);
1144 currentItem->attached->setIsCurrentItem(true);
1145 initializeCurrentItem();
1149 emit q->currentIndexChanged();
1150 releaseItem(oldCurrentItem);
1153 void QSGItemViewPrivate::clear()
1157 for (int i = 0; i < visibleItems.count(); ++i)
1158 releaseItem(visibleItems.at(i));
1159 visibleItems.clear();
1162 releaseItem(currentItem);
1167 minExtentDirty = true;
1168 maxExtentDirty = true;
1173 void QSGItemViewPrivate::mirrorChange()
1177 emit q->effectiveLayoutDirectionChanged();
1180 void QSGItemViewPrivate::refill()
1182 if (isContentFlowReversed())
1183 refill(-position()-size(), -position());
1185 refill(position(), position()+size());
1188 void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer)
1191 if (!isValid() || !q->isComponentComplete())
1194 itemCount = model->count();
1195 qreal bufferFrom = from - buffer;
1196 qreal bufferTo = to + buffer;
1197 qreal fillFrom = from;
1199 if (doBuffer && (bufferMode & BufferAfter))
1201 if (doBuffer && (bufferMode & BufferBefore))
1202 fillFrom = bufferFrom;
1204 // Item creation and release is staggered in order to avoid
1205 // creating/releasing multiple items in one frame
1206 // while flicking (as much as possible).
1208 bool changed = addVisibleItems(fillFrom, fillTo, doBuffer);
1210 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
1211 if (removeNonVisibleItems(bufferFrom, bufferTo))
1213 deferredRelease = false;
1215 deferredRelease = true;
1219 minExtentDirty = true;
1220 maxExtentDirty = true;
1221 visibleItemsChanged();
1222 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
1223 refill(from, to, true);
1226 lazyRelease = false;
1229 void QSGItemViewPrivate::regenerate()
1232 if (q->isComponentComplete()) {
1241 setPosition(contentStartPosition());
1243 updateCurrent(currentIndex);
1247 void QSGItemViewPrivate::scheduleLayout()
1250 if (!layoutScheduled) {
1251 layoutScheduled = true;
1256 void QSGItemViewPrivate::updateViewport()
1260 if (layoutOrientation() == Qt::Vertical)
1261 q->setContentHeight(endPosition() - startPosition());
1263 q->setContentWidth(endPosition() - startPosition());
1267 void QSGItemViewPrivate::layout()
1270 layoutScheduled = false;
1271 if (!isValid() && !visibleItems.count()) {
1273 setPosition(contentStartPosition());
1277 layoutVisibleItems();
1280 minExtentDirty = true;
1281 maxExtentDirty = true;
1284 if (!q->isMoving() && !q->isFlicking()) {
1292 updateUnrequestedPositions();
1295 FxViewItem *QSGItemViewPrivate::createItem(int modelIndex)
1299 requestedIndex = modelIndex;
1300 FxViewItem *viewItem = 0;
1302 if (QSGItem *item = model->item(modelIndex, false)) {
1303 viewItem = newViewItem(modelIndex, item);
1305 viewItem->index = modelIndex;
1306 if (model->completePending()) {
1308 viewItem->item->setZ(1);
1309 viewItem->item->setParentItem(q->contentItem());
1310 model->completeItem();
1312 viewItem->item->setParentItem(q->contentItem());
1314 // do other set up for the new item that should not happen
1315 // until after bindings are evaluated
1316 initializeViewItem(viewItem);
1318 unrequestedItems.remove(viewItem->item);
1321 requestedIndex = -1;
1326 void QSGItemViewPrivate::releaseItem(FxViewItem *item)
1329 if (!item || !model)
1331 if (trackedItem == item)
1333 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
1334 itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
1335 if (model->release(item->item) == 0) {
1336 // item was not destroyed, and we no longer reference it.
1337 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
1342 QSGItem *QSGItemViewPrivate::createHighlightItem()
1344 return createComponentItem(highlightComponent, true, true);
1347 QSGItem *QSGItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault)
1353 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1354 QObject *nobj = component->create(context);
1356 QDeclarative_setParent_noEvent(context, nobj);
1357 item = qobject_cast<QSGItem *>(nobj);
1363 } else if (createDefault) {
1367 QDeclarative_setParent_noEvent(item, q->contentItem());
1368 item->setParentItem(q->contentItem());
1369 if (receiveItemGeometryChanges) {
1370 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1371 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1377 void QSGItemViewPrivate::updateTrackedItem()
1380 FxViewItem *item = currentItem;
1386 q->trackedPositionChanged();
1389 void QSGItemViewPrivate::updateUnrequestedIndexes()
1392 for (QHash<QSGItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1393 *it = model->indexOf(it.key(), q);
1396 void QSGItemViewPrivate::updateUnrequestedPositions()
1398 for (QHash<QSGItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1399 repositionPackageItemAt(it.key(), it.value());