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 "qquicklistview_p.h"
43 #include "qquickitemview_p_p.h"
44 #include "qquickvisualitemmodel_p.h"
46 #include <QtQml/qqmlexpression.h>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
53 #include <private/qquicksmoothedanimation_p_p.h>
54 #include "qplatformdefs.h"
58 #ifndef QML_FLICK_SNAPONETHRESHOLD
59 #define QML_FLICK_SNAPONETHRESHOLD 30
62 //#define DEBUG_DELEGATE_LIFECYCLE
66 class QQuickListViewPrivate : public QQuickItemViewPrivate
68 Q_DECLARE_PUBLIC(QQuickListView)
70 static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
72 virtual Qt::Orientation layoutOrientation() const;
73 virtual bool isContentFlowReversed() const;
74 bool isRightToLeft() const;
75 bool isBottomToTop() const;
77 virtual qreal positionAt(int index) const;
78 virtual qreal endPositionAt(int index) const;
79 virtual qreal originPosition() const;
80 virtual qreal lastPosition() const;
82 FxViewItem *itemBefore(int modelIndex) const;
83 QString sectionAt(int modelIndex);
84 qreal snapPosAt(qreal pos);
85 FxViewItem *snapItemAt(qreal pos);
90 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer);
91 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
92 virtual void visibleItemsChanged();
94 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
95 virtual void initializeViewItem(FxViewItem *item);
96 virtual bool releaseItem(FxViewItem *item);
97 virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
98 virtual void repositionPackageItemAt(QQuickItem *item, int index);
99 virtual void resetFirstItemPosition(qreal pos = 0.0);
100 virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
102 virtual void createHighlight();
103 virtual void updateHighlight();
104 virtual void resetHighlightPosition();
106 virtual void setPosition(qreal pos);
107 virtual void layoutVisibleItems(int fromModelIndex = 0);
109 virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
110 virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
112 virtual void updateSectionCriteria();
113 virtual void updateSections();
114 QQuickItem *getSectionItem(const QString §ion);
115 void releaseSectionItem(QQuickItem *item);
116 void releaseSectionItems();
117 void updateInlineSection(FxListItemSG *);
118 void updateCurrentSection();
119 void updateStickySections();
121 virtual qreal headerSize() const;
122 virtual qreal footerSize() const;
123 virtual bool showHeaderForIndex(int index) const;
124 virtual bool showFooterForIndex(int index) const;
125 virtual void updateHeader();
126 virtual void updateFooter();
128 virtual void changedVisibleIndex(int newIndex);
129 virtual void initializeCurrentItem();
131 void updateAverage();
133 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
134 virtual void fixupPosition();
135 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
136 virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
137 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
139 QQuickListView::Orientation orient;
143 QQuickListView::SnapMode snapMode;
145 QSmoothedAnimation *highlightPosAnimator;
146 QSmoothedAnimation *highlightSizeAnimator;
147 qreal highlightMoveVelocity;
148 qreal highlightResizeVelocity;
149 int highlightResizeDuration;
151 QQuickViewSection *sectionCriteria;
152 QString currentSection;
153 static const int sectionCacheSize = 5;
154 QQuickItem *sectionCache[sectionCacheSize];
155 QQuickItem *currentSectionItem;
156 QString currentStickySection;
157 QQuickItem *nextSectionItem;
158 QString nextStickySection;
159 QString lastVisibleSection;
163 bool correctFlick : 1;
164 bool inFlickCorrection : 1;
166 QQuickListViewPrivate()
167 : orient(QQuickListView::Vertical)
169 , averageSize(100.0), spacing(0.0)
170 , snapMode(QQuickListView::NoSnap)
171 , highlightPosAnimator(0), highlightSizeAnimator(0)
172 , highlightMoveVelocity(400), highlightResizeVelocity(400), highlightResizeDuration(-1)
173 , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
174 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
176 ~QQuickListViewPrivate() {
177 delete highlightPosAnimator;
178 delete highlightSizeAnimator;
181 friend class QQuickViewSection;
184 //----------------------------------------------------------------------------
186 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
187 : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
188 , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
192 void QQuickViewSection::setProperty(const QString &property)
194 if (property != m_property) {
195 m_property = property;
196 emit propertyChanged();
197 // notify view that the contents of the sections must be recalculated
198 m_view->updateSectionCriteria();
202 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
204 if (criteria != m_criteria) {
205 m_criteria = criteria;
206 emit criteriaChanged();
207 // notify view that the contents of the sections must be recalculated
208 m_view->updateSectionCriteria();
212 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
214 if (delegate != m_delegate) {
216 m_view->releaseSectionItems();
217 m_delegate = delegate;
218 emit delegateChanged();
219 m_view->forceLayoutPolish();
223 QString QQuickViewSection::sectionString(const QString &value)
225 if (m_criteria == FirstCharacter)
226 return value.isEmpty() ? QString() : value.at(0);
231 void QQuickViewSection::setLabelPositioning(int l)
233 if (m_labelPositioning != l) {
234 m_labelPositioning = l;
235 emit labelPositioningChanged();
236 m_view->forceLayoutPolish();
240 //----------------------------------------------------------------------------
242 class FxListItemSG : public FxViewItem
245 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
246 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
248 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
249 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
255 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
256 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
260 inline QQuickItem *section() const {
261 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
263 void setSection(QQuickItem *s) {
265 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
266 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
269 qreal position() const {
271 if (view->orientation() == QQuickListView::Vertical)
272 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
274 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
276 return itemPosition();
279 qreal itemPosition() const {
280 if (view->orientation() == QQuickListView::Vertical)
281 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
283 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
287 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
289 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
291 qreal itemSize() const {
292 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
294 qreal sectionSize() const {
296 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
299 qreal endPosition() const {
300 if (view->orientation() == QQuickListView::Vertical) {
301 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
303 : itemY() + item->height());
305 return (view->effectiveLayoutDirection() == Qt::RightToLeft
307 : itemX() + item->width());
310 void setPosition(qreal pos, bool immediate = false) {
311 // position the section immediately even if there is a transition
313 if (view->orientation() == QQuickListView::Vertical) {
314 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
315 section()->setY(-section()->height()-pos);
317 section()->setY(pos);
319 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
320 section()->setX(-section()->width()-pos);
322 section()->setX(pos);
325 moveTo(pointForPosition(pos), immediate);
327 void setSize(qreal size) {
328 if (view->orientation() == QQuickListView::Vertical)
329 item->setHeight(size);
331 item->setWidth(size);
333 bool contains(qreal x, qreal y) const {
334 return (x >= itemX() && x < itemX() + item->width() &&
335 y >= itemY() && y < itemY() + item->height());
338 QQuickListView *view;
341 QPointF pointForPosition(qreal pos) const {
342 if (view->orientation() == QQuickListView::Vertical) {
343 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
345 pos += section()->height();
346 return QPointF(itemX(), -item->height() - pos);
349 pos += section()->height();
350 return QPointF(itemX(), pos);
353 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
355 pos += section()->width();
356 return QPointF(-item->width() - pos, itemY());
359 pos += section()->width();
360 return QPointF(pos, itemY());
366 //----------------------------------------------------------------------------
368 bool QQuickListViewPrivate::isContentFlowReversed() const
370 return isRightToLeft() || isBottomToTop();
373 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
375 return static_cast<Qt::Orientation>(orient);
378 bool QQuickListViewPrivate::isRightToLeft() const
380 Q_Q(const QQuickListView);
381 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
384 bool QQuickListViewPrivate::isBottomToTop() const
386 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
389 // Returns the item before modelIndex, if created.
390 // May return an item marked for removal.
391 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
393 if (modelIndex < visibleIndex)
397 while (idx < visibleItems.count()) {
398 FxViewItem *item = visibleItems.at(idx);
399 if (item->index != -1)
400 lastIndex = item->index;
401 if (item->index == modelIndex)
402 return visibleItems.at(idx-1);
405 if (lastIndex == modelIndex-1)
406 return visibleItems.last();
410 void QQuickListViewPrivate::setPosition(qreal pos)
413 if (orient == QQuickListView::Vertical) {
415 q->QQuickFlickable::setContentY(-pos-size());
417 q->QQuickFlickable::setContentY(pos);
420 q->QQuickFlickable::setContentX(-pos-size());
422 q->QQuickFlickable::setContentX(pos);
426 qreal QQuickListViewPrivate::originPosition() const
429 if (!visibleItems.isEmpty()) {
430 pos = (*visibleItems.constBegin())->position();
431 if (visibleIndex > 0)
432 pos -= visibleIndex * (averageSize + spacing);
437 qreal QQuickListViewPrivate::lastPosition() const
440 if (!visibleItems.isEmpty()) {
441 int invisibleCount = visibleItems.count() - visibleIndex;
442 for (int i = visibleItems.count()-1; i >= 0; --i) {
443 if (visibleItems.at(i)->index != -1) {
444 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
448 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
449 } else if (model && model->count()) {
450 pos = (model->count() * averageSize + (model->count()-1) * spacing);
455 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
457 if (FxViewItem *item = visibleItem(modelIndex)) {
458 return item->position();
460 if (!visibleItems.isEmpty()) {
461 if (modelIndex < visibleIndex) {
462 int count = visibleIndex - modelIndex;
464 if (modelIndex == currentIndex && currentItem) {
465 cs = currentItem->size() + spacing;
468 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
470 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
471 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
477 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
479 if (FxViewItem *item = visibleItem(modelIndex))
480 return item->endPosition();
481 if (!visibleItems.isEmpty()) {
482 if (modelIndex < visibleIndex) {
483 int count = visibleIndex - modelIndex;
484 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
486 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
487 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
493 QString QQuickListViewPrivate::sectionAt(int modelIndex)
495 if (FxViewItem *item = visibleItem(modelIndex))
496 return item->attached->section();
499 if (sectionCriteria) {
500 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
501 section = sectionCriteria->sectionString(propValue);
507 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
509 if (FxViewItem *snapItem = snapItemAt(pos))
510 return snapItem->position();
511 if (visibleItems.count()) {
512 qreal firstPos = (*visibleItems.constBegin())->position();
513 qreal endPos = (*(--visibleItems.constEnd()))->position();
514 if (pos < firstPos) {
515 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
516 } else if (pos > endPos)
517 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
519 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
522 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
524 FxViewItem *snapItem = 0;
525 qreal prevItemSize = 0;
526 for (int i = 0; i < visibleItems.count(); ++i) {
527 FxViewItem *item = visibleItems.at(i);
528 if (item->index == -1)
530 qreal itemTop = item->position();
531 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
533 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
535 prevItemSize = item->size();
540 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
542 visiblePos = positionAt(newIndex);
543 visibleIndex = newIndex;
546 void QQuickListViewPrivate::init()
548 QQuickItemViewPrivate::init();
549 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
552 void QQuickListViewPrivate::clear()
554 for (int i = 0; i < sectionCacheSize; ++i) {
555 delete sectionCache[i];
559 releaseSectionItem(currentSectionItem);
560 currentSectionItem = 0;
561 releaseSectionItem(nextSectionItem);
563 lastVisibleSection = QString();
564 QQuickItemViewPrivate::clear();
567 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
571 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
572 listItem->index = modelIndex;
574 // initialise attached properties
575 if (sectionCriteria) {
576 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
577 listItem->attached->setSection(sectionCriteria->sectionString(propValue));
578 if (modelIndex > 0) {
579 if (FxViewItem *item = itemBefore(modelIndex))
580 listItem->attached->setPrevSection(item->attached->section());
582 listItem->attached->setPrevSection(sectionAt(modelIndex-1));
584 if (modelIndex < model->count()-1) {
585 if (FxViewItem *item = visibleItem(modelIndex+1))
586 listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section());
588 listItem->attached->setNextSection(sectionAt(modelIndex+1));
595 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
597 QQuickItemViewPrivate::initializeViewItem(item);
599 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
600 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
602 if (sectionCriteria && sectionCriteria->delegate()) {
603 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
604 updateInlineSection(static_cast<FxListItemSG*>(item));
608 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
613 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
615 bool released = QQuickItemViewPrivate::releaseItem(item);
616 if (released && att && att->m_sectionItem) {
617 // We hold no more references to this item
620 if (!sectionCache[i]) {
621 sectionCache[i] = att->m_sectionItem;
622 sectionCache[i]->setVisible(false);
623 att->m_sectionItem = 0;
627 } while (i < sectionCacheSize);
628 delete att->m_sectionItem;
629 att->m_sectionItem = 0;
635 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
637 qreal itemEnd = visiblePos;
638 if (visibleItems.count()) {
639 visiblePos = (*visibleItems.constBegin())->position();
640 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
643 int modelIndex = findLastVisibleIndex();
644 bool haveValidItems = modelIndex >= 0;
645 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
647 if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
648 || bufferTo < visiblePos - averageSize - spacing)) {
649 // We've jumped more than a page. Estimate which items are now
650 // visible and fill from there.
651 int count = (fillFrom - itemEnd) / (averageSize + spacing);
652 int newModelIdx = qBound(0, modelIndex + count, model->count());
653 count = newModelIdx - modelIndex;
655 for (int i = 0; i < visibleItems.count(); ++i)
656 releaseItem(visibleItems.at(i));
657 visibleItems.clear();
658 modelIndex = newModelIdx;
659 visibleIndex = modelIndex;
660 visiblePos = itemEnd + count * (averageSize + spacing);
661 itemEnd = visiblePos;
665 bool changed = false;
666 FxListItemSG *item = 0;
668 while (modelIndex < model->count() && pos <= fillTo) {
669 #ifdef DEBUG_DELEGATE_LIFECYCLE
670 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
672 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
674 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
675 item->setPosition(pos, true);
676 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
677 pos += item->size() + spacing;
678 visibleItems.append(item);
683 if (doBuffer && requestedIndex != -1) // already waiting for an item
686 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
687 #ifdef DEBUG_DELEGATE_LIFECYCLE
688 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
690 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
693 visiblePos -= item->size() + spacing;
694 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
695 item->setPosition(visiblePos, true);
696 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
697 visibleItems.prepend(item);
704 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
706 FxViewItem *item = 0;
707 bool changed = false;
709 // Remove items from the start of the view.
710 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
711 // removed, otherwise a zero-sized item is infinitely added and removed over and
714 while (visibleItems.count() > 1 && index < visibleItems.count()
715 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
716 if (item->attached->delayRemove())
719 if (item->size() > 0) {
720 #ifdef DEBUG_DELEGATE_LIFECYCLE
721 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
723 // remove this item and all zero-sized items before it
725 if (item->index != -1)
727 visibleItems.removeAt(index);
728 if (item->transitionScheduledOrRunning()) {
729 #ifdef DEBUG_DELEGATE_LIFECYCLE
730 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
732 item->releaseAfterTransition = true;
733 releasePendingTransition.append(item);
739 item = visibleItems.at(--index);
747 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
748 if (item->attached->delayRemove())
750 #ifdef DEBUG_DELEGATE_LIFECYCLE
751 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
753 visibleItems.removeLast();
754 if (item->transitionScheduledOrRunning()) {
755 #ifdef DEBUG_DELEGATE_LIFECYCLE
756 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
758 item->releaseAfterTransition = true;
759 releasePendingTransition.append(item);
769 void QQuickListViewPrivate::visibleItemsChanged()
771 if (visibleItems.count())
772 visiblePos = (*visibleItems.constBegin())->position();
774 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
775 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
779 updateCurrentSection();
780 updateUnrequestedPositions();
783 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
785 if (!visibleItems.isEmpty()) {
786 const qreal from = isContentFlowReversed() ? -position() - size() : position();
787 const qreal to = isContentFlowReversed() ? -position() : position() + size();
789 FxViewItem *firstItem = *visibleItems.constBegin();
790 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
791 qreal sum = firstItem->size();
792 qreal pos = firstItem->position() + firstItem->size() + spacing;
793 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
795 for (int i=1; i < visibleItems.count(); ++i) {
796 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
797 if (item->index >= fromModelIndex) {
798 item->setPosition(pos);
799 item->setVisible(item->endPosition() >= from && item->position() <= to);
801 pos += item->size() + spacing;
803 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
805 averageSize = qRound(sum / visibleItems.count());
807 // move current item if it is not a visible item.
808 if (currentIndex >= 0 && currentItem && !fixedCurrent)
809 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
811 updateCurrentSection();
812 updateStickySections();
816 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
818 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
821 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
824 qreal pos = position();
825 if (orient == QQuickListView::Vertical) {
826 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
828 item->setY(-positionAt(index)-item->height());
830 item->setY(positionAt(index));
833 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
835 item->setX(-positionAt(index)-item->width());
837 item->setX(positionAt(index));
842 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
844 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
845 item->setPosition(pos);
848 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
850 if (!visibleItems.count())
852 qreal diff = forwards - backwards;
853 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
856 void QQuickListViewPrivate::createHighlight()
859 bool changed = false;
861 if (trackedItem == highlight)
866 delete highlightPosAnimator;
867 delete highlightSizeAnimator;
868 highlightPosAnimator = 0;
869 highlightSizeAnimator = 0;
875 QQuickItem *item = createHighlightItem();
877 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
880 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
881 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
883 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
884 highlightPosAnimator = new QSmoothedAnimation;
885 highlightPosAnimator->target = QQmlProperty(item, posProp);
886 highlightPosAnimator->velocity = highlightMoveVelocity;
887 highlightPosAnimator->userDuration = highlightMoveDuration;
889 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
890 highlightSizeAnimator = new QSmoothedAnimation;
891 highlightSizeAnimator->velocity = highlightResizeVelocity;
892 highlightSizeAnimator->userDuration = highlightResizeDuration;
893 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
895 highlight = newHighlight;
900 emit q->highlightItemChanged();
903 void QQuickListViewPrivate::updateHighlight()
905 applyPendingChanges();
907 if ((!currentItem && highlight) || (currentItem && !highlight))
909 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
910 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
911 // auto-update highlight
912 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
913 highlightPosAnimator->to = isContentFlowReversed()
914 ? -listItem->itemPosition()-listItem->itemSize()
915 : listItem->itemPosition();
916 highlightSizeAnimator->to = listItem->itemSize();
917 if (orient == QQuickListView::Vertical) {
918 if (highlight->item->width() == 0)
919 highlight->item->setWidth(currentItem->item->width());
921 if (highlight->item->height() == 0)
922 highlight->item->setHeight(currentItem->item->height());
925 highlightPosAnimator->restart();
926 highlightSizeAnimator->restart();
931 void QQuickListViewPrivate::resetHighlightPosition()
933 if (highlight && currentItem)
934 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
937 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
940 QQuickItem *sectionItem = 0;
941 int i = sectionCacheSize-1;
942 while (i >= 0 && !sectionCache[i])
945 sectionItem = sectionCache[i];
947 sectionItem->setVisible(true);
948 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
949 context->setContextProperty(QLatin1String("section"), section);
951 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
952 QQmlContext *context = new QQmlContext(
953 creationContext ? creationContext : qmlContext(q));
954 context->setContextProperty(QLatin1String("section"), section);
955 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
957 QQml_setParent_noEvent(context, nobj);
958 sectionItem = qobject_cast<QQuickItem *>(nobj);
962 sectionItem->setZ(2);
963 QQml_setParent_noEvent(sectionItem, contentItem);
964 sectionItem->setParentItem(contentItem);
969 sectionCriteria->delegate()->completeCreate();
975 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
981 if (!sectionCache[i]) {
982 sectionCache[i] = item;
983 sectionCache[i]->setVisible(false);
987 } while (i < sectionCacheSize);
992 void QQuickListViewPrivate::releaseSectionItems()
994 for (int i = 0; i < visibleItems.count(); ++i) {
995 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
996 if (listItem->section()) {
997 qreal pos = listItem->position();
998 releaseSectionItem(listItem->section());
999 listItem->setSection(0);
1000 listItem->setPosition(pos);
1003 for (int i = 0; i < sectionCacheSize; ++i) {
1004 delete sectionCache[i];
1005 sectionCache[i] = 0;
1009 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
1011 if (!sectionCriteria || !sectionCriteria->delegate())
1013 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
1014 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
1015 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
1016 if (!listItem->section()) {
1017 qreal pos = listItem->position();
1018 listItem->setSection(getSectionItem(listItem->attached->m_section));
1019 listItem->setPosition(pos);
1021 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
1022 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1024 } else if (listItem->section()) {
1025 qreal pos = listItem->position();
1026 releaseSectionItem(listItem->section());
1027 listItem->setSection(0);
1028 listItem->setPosition(pos);
1032 void QQuickListViewPrivate::updateStickySections()
1034 if (!sectionCriteria
1035 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1038 bool isFlowReversed = isContentFlowReversed();
1039 qreal viewPos = isFlowReversed ? -position()-size() : position();
1040 QQuickItem *sectionItem = 0;
1041 QQuickItem *lastSectionItem = 0;
1043 while (index < visibleItems.count()) {
1044 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1045 // Find the current section header and last visible section header
1046 // and hide them if they will overlap a static section header.
1047 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1048 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1050 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1051 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1053 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1054 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1055 section->setVisible(visBot && visTop);
1056 if (visTop && !sectionItem)
1057 sectionItem = section;
1058 if (isFlowReversed) {
1059 if (-sectionPos <= viewPos + size())
1060 lastSectionItem = section;
1062 if (sectionPos + sectionSize < viewPos + size())
1063 lastSectionItem = section;
1069 // Current section header
1070 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1071 if (!currentSectionItem) {
1072 currentSectionItem = getSectionItem(currentSection);
1073 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1074 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1075 context->setContextProperty(QLatin1String("section"), currentSection);
1077 currentStickySection = currentSection;
1078 if (!currentSectionItem)
1081 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1082 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1084 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1085 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1087 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1088 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1091 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1093 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1094 if (orient == QQuickListView::Vertical)
1095 currentSectionItem->setY(pos);
1097 currentSectionItem->setX(pos);
1098 } else if (currentSectionItem) {
1099 releaseSectionItem(currentSectionItem);
1100 currentSectionItem = 0;
1103 // Next section footer
1104 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1105 if (!nextSectionItem) {
1106 nextSectionItem = getSectionItem(nextSection);
1107 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1108 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1109 context->setContextProperty(QLatin1String("section"), nextSection);
1111 nextStickySection = nextSection;
1112 if (!nextSectionItem)
1115 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1116 nextSectionItem->setVisible(!nextSection.isEmpty());
1117 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1118 if (lastSectionItem) {
1119 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1120 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1123 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1124 if (orient == QQuickListView::Vertical)
1125 nextSectionItem->setY(pos);
1127 nextSectionItem->setX(pos);
1128 } else if (nextSectionItem) {
1129 releaseSectionItem(nextSectionItem);
1130 nextSectionItem = 0;
1134 void QQuickListViewPrivate::updateSections()
1136 Q_Q(QQuickListView);
1137 if (!q->isComponentComplete())
1140 QQuickItemViewPrivate::updateSections();
1142 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1143 QString prevSection;
1144 if (visibleIndex > 0)
1145 prevSection = sectionAt(visibleIndex-1);
1146 QQuickListViewAttached *prevAtt = 0;
1148 for (int i = 0; i < visibleItems.count(); ++i) {
1149 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1150 attached->setPrevSection(prevSection);
1151 if (visibleItems.at(i)->index != -1) {
1152 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1153 attached->setSection(sectionCriteria->sectionString(propValue));
1154 idx = visibleItems.at(i)->index;
1156 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1158 prevAtt->setNextSection(attached->section());
1159 prevSection = attached->section();
1163 if (idx > 0 && idx < model->count()-1)
1164 prevAtt->setNextSection(sectionAt(idx+1));
1166 prevAtt->setNextSection(QString());
1170 lastVisibleSection = QString();
1173 void QQuickListViewPrivate::updateCurrentSection()
1175 Q_Q(QQuickListView);
1176 if (!sectionCriteria || visibleItems.isEmpty()) {
1177 if (!currentSection.isEmpty()) {
1178 currentSection.clear();
1179 emit q->currentSectionChanged();
1183 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1184 qreal sectionThreshold = position();
1185 if (currentSectionItem && !inlineSections)
1186 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1188 int modelIndex = visibleIndex;
1189 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1190 if (visibleItems.at(index)->index != -1)
1191 modelIndex = visibleItems.at(index)->index;
1195 QString newSection = currentSection;
1196 if (index < visibleItems.count())
1197 newSection = visibleItems.at(index)->attached->section();
1199 newSection = (*visibleItems.constBegin())->attached->section();
1200 if (newSection != currentSection) {
1201 currentSection = newSection;
1202 updateStickySections();
1203 emit q->currentSectionChanged();
1206 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1207 // Don't want to scan for next section on every movement, so remember
1208 // the last section in the visible area and only scan for the next
1209 // section when that changes. Clearing lastVisibleSection will also
1211 QString lastSection = currentSection;
1212 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1213 if (nextSectionItem && !inlineSections)
1214 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1215 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1216 if (visibleItems.at(index)->index != -1)
1217 modelIndex = visibleItems.at(index)->index;
1218 lastSection = visibleItems.at(index)->attached->section();
1222 if (lastVisibleSection != lastSection) {
1223 nextSection = QString();
1224 lastVisibleSection = lastSection;
1225 for (int i = modelIndex; i < itemCount; ++i) {
1226 QString section = sectionAt(i);
1227 if (section != lastSection) {
1228 nextSection = section;
1229 updateStickySections();
1237 void QQuickListViewPrivate::initializeCurrentItem()
1239 QQuickItemViewPrivate::initializeCurrentItem();
1242 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1244 // don't reposition the item if it is already in the visibleItems list
1245 FxViewItem *actualItem = visibleItem(currentIndex);
1247 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1248 // We can calculate exact postion in this case
1249 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1251 // Create current item now and position as best we can.
1252 // Its position will be corrected when it becomes visible.
1253 listItem->setPosition(positionAt(currentIndex));
1257 if (visibleItems.isEmpty())
1258 averageSize = listItem->size();
1262 void QQuickListViewPrivate::updateAverage()
1264 if (!visibleItems.count())
1267 for (int i = 0; i < visibleItems.count(); ++i)
1268 sum += visibleItems.at(i)->size();
1269 averageSize = qRound(sum / visibleItems.count());
1272 qreal QQuickListViewPrivate::headerSize() const
1274 return header ? header->size() : 0.0;
1277 qreal QQuickListViewPrivate::footerSize() const
1279 return footer ? footer->size() : 0.0;
1282 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1287 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1289 return index == model->count()-1;
1292 void QQuickListViewPrivate::updateFooter()
1294 Q_Q(QQuickListView);
1295 bool created = false;
1297 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1300 footer = new FxListItemSG(item, q, true, true);
1304 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1305 if (visibleItems.count()) {
1306 qreal endPos = lastPosition();
1307 if (findLastVisibleIndex() == model->count()-1) {
1308 listItem->setPosition(endPos);
1310 qreal visiblePos = position() + q->height();
1311 if (endPos <= visiblePos || listItem->position() < endPos)
1312 listItem->setPosition(endPos);
1315 listItem->setPosition(visiblePos);
1319 emit q->footerItemChanged();
1322 void QQuickListViewPrivate::updateHeader()
1324 Q_Q(QQuickListView);
1325 bool created = false;
1327 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1330 header = new FxListItemSG(item, q, true, true);
1334 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1336 if (visibleItems.count()) {
1337 qreal startPos = originPosition();
1338 if (visibleIndex == 0) {
1339 listItem->setPosition(startPos - headerSize());
1341 if (position() <= startPos || listItem->position() > startPos - headerSize())
1342 listItem->setPosition(startPos - headerSize());
1345 listItem->setPosition(-headerSize());
1350 emit q->headerItemChanged();
1353 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1355 Q_Q(QQuickListView);
1356 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1357 if (!q->isComponentComplete())
1360 if (item != contentItem && (!highlight || item != highlight->item)) {
1361 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1362 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1364 // if visibleItems.first() has resized, adjust its pos since it is used to
1365 // position all subsequent items
1366 if (visibleItems.count() && item == visibleItems.first()->item) {
1367 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1368 if (orient == QQuickListView::Vertical) {
1369 qreal diff = newGeometry.height() - oldGeometry.height();
1370 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1371 listItem->setPosition(listItem->position() - diff, true);
1372 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1373 listItem->setPosition(listItem->position() + diff, true);
1375 qreal diff = newGeometry.width() - oldGeometry.width();
1376 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1377 listItem->setPosition(listItem->position() - diff, true);
1378 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1379 listItem->setPosition(listItem->position() + diff, true);
1382 forceLayoutPolish();
1387 void QQuickListViewPrivate::fixupPosition()
1389 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1390 || snapMode != QQuickListView::NoSnap)
1392 if (orient == QQuickListView::Vertical)
1398 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1400 if ((orient == QQuickListView::Horizontal && &data == &vData)
1401 || (orient == QQuickListView::Vertical && &data == &hData))
1404 correctFlick = false;
1405 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1406 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1408 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1410 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1411 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1412 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1413 // if we've been dragged < averageSize/2 then bias towards the next item
1414 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1416 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1417 bias = averageSize/2;
1418 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1419 bias = -averageSize/2;
1420 if (isContentFlowReversed())
1422 tempPosition -= bias;
1424 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1425 if (!topItem && strictHighlightRange && currentItem) {
1426 // StrictlyEnforceRange always keeps an item in range
1428 topItem = currentItem;
1430 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1431 if (!bottomItem && strictHighlightRange && currentItem) {
1432 // StrictlyEnforceRange always keeps an item in range
1434 bottomItem = currentItem;
1437 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1438 if (topItem && (isInBounds || strictHighlightRange)) {
1439 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1440 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1442 if (isContentFlowReversed())
1443 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1445 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1447 } else if (bottomItem && isInBounds) {
1448 if (isContentFlowReversed())
1449 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1451 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1453 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1457 qreal dist = qAbs(data.move + pos);
1459 timeline.reset(data.move);
1460 if (fixupMode != Immediate) {
1461 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1462 data.fixingUp = true;
1464 timeline.set(data.move, -pos);
1466 vTime = timeline.time();
1468 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1470 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1471 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1472 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1473 if (viewPos > pos - highlightRangeStart)
1474 viewPos = pos - highlightRangeStart;
1475 if (isContentFlowReversed())
1476 viewPos = -viewPos-size();
1478 timeline.reset(data.move);
1479 if (viewPos != position()) {
1480 if (fixupMode != Immediate) {
1481 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1482 data.fixingUp = true;
1484 timeline.set(data.move, -viewPos);
1487 vTime = timeline.time();
1489 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1491 data.inOvershoot = false;
1495 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1496 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1498 data.fixingUp = false;
1500 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1501 correctFlick = true;
1502 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1504 qreal maxDistance = 0;
1505 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1507 // -ve velocity means list is moving up/left
1509 if (data.move.value() < minExtent) {
1510 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1511 // if we've been dragged < averageSize/2 then bias towards the next item
1512 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1513 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1514 if (isContentFlowReversed())
1516 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1517 maxDistance = qAbs(data.flickTarget - data.move.value());
1518 velocity = maxVelocity;
1520 maxDistance = qAbs(minExtent - data.move.value());
1523 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1524 data.flickTarget = minExtent;
1526 if (data.move.value() > maxExtent) {
1527 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1528 // if we've been dragged < averageSize/2 then bias towards the next item
1529 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1530 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1531 if (isContentFlowReversed())
1533 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1534 maxDistance = qAbs(data.flickTarget - data.move.value());
1535 velocity = -maxVelocity;
1537 maxDistance = qAbs(maxExtent - data.move.value());
1540 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1541 data.flickTarget = maxExtent;
1543 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1544 if (maxDistance > 0 || overShoot) {
1545 // These modes require the list to stop exactly on an item boundary.
1546 // The initial flick will estimate the boundary to stop on.
1547 // Since list items can have variable sizes, the boundary will be
1548 // reevaluated and adjusted as we approach the boundary.
1550 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1556 if (!hData.flicking && !vData.flicking) {
1557 // the initial flick - estimate boundary
1558 qreal accel = deceleration;
1560 overshootDist = 0.0;
1561 // + averageSize/4 to encourage moving at least one item in the flick direction
1562 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1563 if (maxDistance > 0)
1564 dist = qMin(dist, maxDistance);
1567 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1568 if (snapMode != QQuickListView::SnapOneItem) {
1569 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1570 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1572 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1574 if (data.flickTarget >= minExtent) {
1575 overshootDist = overShootDistance(vSize);
1576 data.flickTarget += overshootDist;
1577 } else if (data.flickTarget <= maxExtent) {
1578 overshootDist = overShootDistance(vSize);
1579 data.flickTarget -= overshootDist;
1582 qreal adjDist = -data.flickTarget + data.move.value();
1583 if (qAbs(adjDist) > qAbs(dist)) {
1584 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1585 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1594 accel = v2 / (2.0f * qAbs(dist));
1595 } else if (overShoot) {
1596 data.flickTarget = data.move.value() - dist;
1597 if (data.flickTarget >= minExtent) {
1598 overshootDist = overShootDistance(vSize);
1599 data.flickTarget += overshootDist;
1600 } else if (data.flickTarget <= maxExtent) {
1601 overshootDist = overShootDistance(vSize);
1602 data.flickTarget -= overshootDist;
1605 timeline.reset(data.move);
1606 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1607 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1608 correctFlick = true;
1611 // reevaluate the target boundary.
1612 qreal newtarget = data.flickTarget;
1613 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1614 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1615 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1616 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1618 if (velocity < 0 && newtarget <= maxExtent)
1619 newtarget = maxExtent - overshootDist;
1620 else if (velocity > 0 && newtarget >= minExtent)
1621 newtarget = minExtent + overshootDist;
1622 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1623 if (qAbs(velocity) < MinimumFlickVelocity)
1624 correctFlick = false;
1627 data.flickTarget = newtarget;
1628 qreal dist = -newtarget + data.move.value();
1629 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1630 correctFlick = false;
1631 timeline.reset(data.move);
1632 fixup(data, minExtent, maxExtent);
1635 timeline.reset(data.move);
1636 timeline.accelDistance(data.move, v, -dist);
1637 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1641 correctFlick = false;
1642 timeline.reset(data.move);
1643 fixup(data, minExtent, maxExtent);
1648 //----------------------------------------------------------------------------
1652 \instantiates QQuickListView
1653 \inqmlmodule QtQuick 2
1654 \ingroup qtquick-views
1656 \brief Provides a list view of items provided by a model
1658 A ListView displays data from models created from built-in QML types like ListModel
1659 and XmlListModel, or custom model classes defined in C++ that inherit from
1662 A ListView has a \l model, which defines the data to be displayed, and
1663 a \l delegate, which defines how the data should be displayed. Items in a
1664 ListView are laid out horizontally or vertically. List views are inherently
1665 flickable because ListView inherits from \l Flickable.
1667 \section1 Example Usage
1669 The following example shows the definition of a simple list model defined
1670 in a file called \c ContactModel.qml:
1672 \snippet qml/listview/ContactModel.qml 0
1674 Another component can display this model data in a ListView, like this:
1676 \snippet qml/listview/listview.qml import
1678 \snippet qml/listview/listview.qml classdocs simple
1680 \image listview-simple.png
1682 Here, the ListView creates a \c ContactModel component for its model, and a \l Text item
1683 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1684 the delegate is able to access the model's \c name and \c number data directly.
1686 An improved list view is shown below. The delegate is visually improved and is moved
1687 into a separate \c contactDelegate component.
1689 \snippet qml/listview/listview.qml classdocs advanced
1690 \image listview-highlight.png
1692 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1693 and \c focus is set to \c true to enable keyboard navigation for the list view.
1694 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1696 Delegates are instantiated as needed and may be destroyed at any time.
1697 State should \e never be stored in a delegate.
1699 ListView attaches a number of properties to the root item of the delegate, for example
1700 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1701 this attached property directly as \c ListView.isCurrentItem, while the child
1702 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1704 \snippet qml/listview/listview.qml isCurrentItem
1706 \note Views do not enable \e clip automatically. If the view
1707 is not clipped by another item or the screen, it will be necessary
1708 to set \e {clip: true} in order to have the out of view items clipped
1712 \section1 ListView layouts
1714 The layout of the items in a ListView can be controlled by these properties:
1717 \li \l orientation - controls whether items flow horizontally or vertically.
1718 This value can be either Qt.Horizontal or Qt.Vertical.
1719 \li \l layoutDirection - controls the horizontal layout direction for a
1720 horizontally-oriented view: that is, whether items are laid out from the left side of
1721 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1722 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1723 view: that is, whether items are laid out from the top of the view down towards the bottom of
1724 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1727 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1728 table below shows the different layouts that a ListView can have, depending on the values of
1729 the properties listed above.
1734 \b ListViews with Qt.Vertical orientation
1737 \image listview-layout-toptobottom.png
1739 \image listview-layout-bottomtotop.png
1742 \b ListViews with Qt.Horizontal orientation
1745 \image listview-layout-lefttoright.png
1747 \image listview-layout-righttoleft.png
1750 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1752 QQuickListView::QQuickListView(QQuickItem *parent)
1753 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1757 QQuickListView::~QQuickListView()
1762 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1763 This attached property is true if this delegate is the current item; otherwise false.
1765 It is attached to each instance of the delegate.
1767 This property may be used to adjust the appearance of the current item, for example:
1769 \snippet qml/listview/listview.qml isCurrentItem
1773 \qmlattachedproperty ListView QtQuick2::ListView::view
1774 This attached property holds the view that manages this delegate instance.
1776 It is attached to each instance of the delegate.
1780 \qmlattachedproperty string QtQuick2::ListView::previousSection
1781 This attached property holds the section of the previous element.
1783 It is attached to each instance of the delegate.
1785 The section is evaluated using the \l {ListView::section.property}{section} properties.
1789 \qmlattachedproperty string QtQuick2::ListView::nextSection
1790 This attached property holds the section of the next element.
1792 It is attached to each instance of the delegate.
1794 The section is evaluated using the \l {ListView::section.property}{section} properties.
1798 \qmlattachedproperty string QtQuick2::ListView::section
1799 This attached property holds the section of this element.
1801 It is attached to each instance of the delegate.
1803 The section is evaluated using the \l {ListView::section.property}{section} properties.
1807 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1809 This attached property holds whether the delegate may be destroyed. It
1810 is attached to each instance of the delegate. The default value is false.
1812 It is sometimes necessary to delay the destruction of an item
1813 until an animation completes. The example delegate below ensures that the
1814 animation completes before the item is removed from the list.
1816 \snippet qml/listview/listview.qml delayRemove
1818 If a \l remove transition has been specified, it will not be applied until
1819 delayRemove is returned to \c false.
1823 \qmlattachedsignal QtQuick2::ListView::onAdd()
1824 This attached signal handler is called immediately after an item is added to the view.
1826 If an \l add transition is specified, it is applied immediately after
1827 this signal handler is called.
1831 \qmlattachedsignal QtQuick2::ListView::onRemove()
1832 This attached handler is called immediately before an item is removed from the view.
1834 If a \l remove transition has been specified, it is applied after
1835 this signal handler is called, providing that delayRemove is false.
1839 \qmlproperty model QtQuick2::ListView::model
1840 This property holds the model providing data for the list.
1842 The model provides the set of data that is used to create the items
1843 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1844 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1845 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1847 \sa {qmlmodels}{Data Models}
1851 \qmlproperty Component QtQuick2::ListView::delegate
1853 The delegate provides a template defining each item instantiated by the view.
1854 The index is exposed as an accessible \c index property. Properties of the
1855 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1857 The number of objects and bindings in the delegate has a direct effect on the
1858 flicking performance of the view. If at all possible, place functionality
1859 that is not needed for the normal display of the delegate in a \l Loader which
1860 can load additional components when needed.
1862 The ListView will lay out the items based on the size of the root item
1865 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1868 \note Delegates are instantiated as needed and may be destroyed at any time.
1869 State should \e never be stored in a delegate.
1872 \qmlproperty int QtQuick2::ListView::currentIndex
1873 \qmlproperty Item QtQuick2::ListView::currentItem
1875 The \c currentIndex property holds the index of the current item, and
1876 \c currentItem holds the current item. Setting the currentIndex to -1
1877 will clear the highlight and set currentItem to null.
1879 If highlightFollowsCurrentItem is \c true, setting either of these
1880 properties will smoothly scroll the ListView so that the current
1881 item becomes visible.
1883 Note that the position of the current item
1884 may only be approximate until it becomes visible in the view.
1888 \qmlproperty Item QtQuick2::ListView::highlightItem
1890 This holds the highlight item created from the \l highlight component.
1892 The \c highlightItem is managed by the view unless
1893 \l highlightFollowsCurrentItem is set to false.
1895 \sa highlight, highlightFollowsCurrentItem
1899 \qmlproperty int QtQuick2::ListView::count
1900 This property holds the number of items in the view.
1904 \qmlproperty Component QtQuick2::ListView::highlight
1905 This property holds the component to use as the highlight.
1907 An instance of the highlight component is created for each list.
1908 The geometry of the resulting component instance is managed by the list
1909 so as to stay with the current item, unless the highlightFollowsCurrentItem
1912 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1916 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1917 This property holds whether the highlight is managed by the view.
1919 If this property is true (the default value), the highlight is moved smoothly
1920 to follow the current item. Otherwise, the
1921 highlight is not moved by the view, and any movement must be implemented
1924 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1926 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1928 Note that the highlight animation also affects the way that the view
1929 is scrolled. This is because the view moves to maintain the
1930 highlight within the preferred highlight range (or visible viewport).
1932 \sa highlight, highlightMoveVelocity
1934 //###Possibly rename these properties, since they are very useful even without a highlight?
1936 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1937 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1938 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1940 These properties define the preferred range of the highlight (for the current item)
1941 within the view. The \c preferredHighlightBegin value must be less than the
1942 \c preferredHighlightEnd value.
1944 These properties affect the position of the current item when the list is scrolled.
1945 For example, if the currently selected item should stay in the middle of the
1946 list when the view is scrolled, set the \c preferredHighlightBegin and
1947 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1948 item would be. If the \c currentItem is changed programmatically, the list will
1949 automatically scroll so that the current item is in the middle of the view.
1950 Furthermore, the behavior of the current item index will occur whether or not a
1953 Valid values for \c highlightRangeMode are:
1956 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1957 However, the highlight can move outside of the range at the ends of the list or due
1958 to mouse interaction.
1959 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1960 The current item changes if a keyboard or mouse action would cause the highlight to move
1961 outside of the range.
1962 \li ListView.NoHighlightRange - this is the default value.
1965 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1967 Q_D(QQuickListView);
1968 if (d->autoHighlight != autoHighlight) {
1969 if (!autoHighlight) {
1970 if (d->highlightPosAnimator)
1971 d->highlightPosAnimator->stop();
1972 if (d->highlightSizeAnimator)
1973 d->highlightSizeAnimator->stop();
1975 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1980 \qmlproperty real QtQuick2::ListView::spacing
1982 This property holds the spacing between items.
1984 The default value is 0.
1986 qreal QQuickListView::spacing() const
1988 Q_D(const QQuickListView);
1992 void QQuickListView::setSpacing(qreal spacing)
1994 Q_D(QQuickListView);
1995 if (spacing != d->spacing) {
1996 d->spacing = spacing;
1997 d->forceLayoutPolish();
1998 emit spacingChanged();
2003 \qmlproperty enumeration QtQuick2::ListView::orientation
2004 This property holds the orientation of the list.
2009 \li ListView.Horizontal - Items are laid out horizontally
2010 \li ListView.Vertical (default) - Items are laid out vertically
2015 \li Horizontal orientation:
2016 \image ListViewHorizontal.png
2019 \li Vertical orientation:
2020 \image listview-highlight.png
2023 QQuickListView::Orientation QQuickListView::orientation() const
2025 Q_D(const QQuickListView);
2029 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2031 Q_D(QQuickListView);
2032 if (d->orient != orientation) {
2033 d->orient = orientation;
2034 if (d->orient == Vertical) {
2035 setContentWidth(-1);
2036 setFlickableDirection(VerticalFlick);
2039 setContentHeight(-1);
2040 setFlickableDirection(HorizontalFlick);
2044 emit orientationChanged();
2049 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
2050 This property holds the layout direction of a horizontally-oriented list.
2055 \li Qt.LeftToRight (default) - Items will be laid out from left to right.
2056 \li Qt.RightToLeft - Items will be laid out from right to let.
2059 Setting this property has no effect if the \l orientation is Qt.Vertical.
2061 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2066 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
2067 This property holds the effective layout direction of a horizontally-oriented list.
2069 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2070 the visual layout direction of the horizontal list will be mirrored. However, the
2071 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2073 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2078 \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
2079 This property holds the layout direction of a vertically-oriented list.
2084 \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
2085 \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
2088 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2090 \sa ListView::layoutDirection
2095 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
2096 This property holds whether the list wraps key navigation.
2098 If this is true, key navigation that would move the current item selection
2099 past the end of the list instead wraps around and moves the selection to
2100 the start of the list, and vice-versa.
2102 By default, key navigation is not wrapped.
2107 \qmlproperty int QtQuick2::ListView::cacheBuffer
2108 This property determines whether delegates are retained outside the
2109 visible area of the view.
2111 If this value is non-zero, the view may keep as many delegates
2112 instantiated as it can fit within the buffer specified. For example,
2113 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2114 set to 40, then up to 2 delegates above and 2 delegates below the visible
2115 area may be created/retained. The buffered delegates are created asynchronously,
2116 allowing creation to occur across multiple frames and reducing the
2117 likelihood of skipping frames. In order to improve painting performance
2118 delegates outside the visible area are not painted.
2120 The default value of this property is platform dependent, but will usually
2121 be a non-zero value.
2123 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2124 instantiated delegates.
2126 Setting this value can improve the smoothness of scrolling behavior at the expense
2127 of additional memory usage. It is not a substitute for creating efficient
2128 delegates; the fewer objects and bindings in a delegate, the faster a view can be
2134 \qmlproperty string QtQuick2::ListView::section.property
2135 \qmlproperty enumeration QtQuick2::ListView::section.criteria
2136 \qmlproperty Component QtQuick2::ListView::section.delegate
2137 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
2139 These properties determine the expression to be evaluated and appearance
2140 of the section labels.
2142 \c section.property holds the name of the property that is the basis
2145 \c section.criteria holds the criteria for forming each section based on
2146 \c section.property. This value can be one of:
2149 \li ViewSection.FullString (default) - sections are created based on the
2150 \c section.property value.
2151 \li ViewSection.FirstCharacter - sections are created based on the first
2152 character of the \c section.property value (for example, 'A', 'B', 'C'
2153 sections, etc. for an address book)
2156 A case insensitive comparison is used when determining section
2159 \c section.delegate holds the delegate component for each section.
2161 \c section.labelPositioning determines whether the current and/or
2162 next section labels stick to the start/end of the view, and whether
2163 the labels are shown inline. This value can be a combination of:
2166 \li ViewSection.InlineLabels - section labels are shown inline between
2167 the item delegates separating sections (default).
2168 \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
2169 start of the view as it is moved.
2170 \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
2171 sections) sticks to the end of the view as it is moved. \note Enabling
2172 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
2173 section, which has performance implications, especially for slower models.
2176 Each item in the list has attached properties named \c ListView.section,
2177 \c ListView.previousSection and \c ListView.nextSection.
2179 For example, here is a ListView that displays a list of animals, separated
2180 into sections. Each item in the ListView is placed in a different section
2181 depending on the "size" property of the model item. The \c sectionHeading
2182 delegate component provides the light blue bar that marks the beginning of
2186 \snippet examples/quick/modelviews/listview/sections.qml 0
2188 \image qml-listview-sections-example.png
2190 \note Adding sections to a ListView does not automatically re-order the
2191 list items by the section criteria.
2192 If the model is not ordered by section, then it is possible that
2193 the sections created will not be unique; each boundary between
2194 differing sections will result in a section header being created
2195 even if that section exists elsewhere.
2197 \sa {quick/modelviews/listview}{ListView examples}
2199 QQuickViewSection *QQuickListView::sectionCriteria()
2201 Q_D(QQuickListView);
2202 if (!d->sectionCriteria)
2203 d->sectionCriteria = new QQuickViewSection(this);
2204 return d->sectionCriteria;
2208 \qmlproperty string QtQuick2::ListView::currentSection
2209 This property holds the section that is currently at the beginning of the view.
2211 QString QQuickListView::currentSection() const
2213 Q_D(const QQuickListView);
2214 return d->currentSection;
2218 \qmlproperty real QtQuick2::ListView::highlightMoveVelocity
2219 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2220 \qmlproperty real QtQuick2::ListView::highlightResizeVelocity
2221 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2223 These properties control the speed of the move and resize animations for the
2226 \l highlightFollowsCurrentItem must be true for these properties
2229 The default value for the velocity properties is 400 pixels/second.
2230 The default value for the duration properties is -1, i.e. the
2231 highlight will take as much time as necessary to move at the set speed.
2233 These properties have the same characteristics as a SmoothedAnimation.
2235 \sa highlightFollowsCurrentItem
2237 qreal QQuickListView::highlightMoveVelocity() const
2239 Q_D(const QQuickListView);
2240 return d->highlightMoveVelocity;
2243 void QQuickListView::setHighlightMoveVelocity(qreal speed)
2245 Q_D(QQuickListView);
2246 if (d->highlightMoveVelocity != speed) {
2247 d->highlightMoveVelocity = speed;
2248 if (d->highlightPosAnimator)
2249 d->highlightPosAnimator->velocity = d->highlightMoveVelocity;
2250 emit highlightMoveVelocityChanged();
2254 void QQuickListView::setHighlightMoveDuration(int duration)
2256 Q_D(QQuickListView);
2257 if (d->highlightMoveDuration != duration) {
2258 if (d->highlightPosAnimator)
2259 d->highlightPosAnimator->userDuration = duration;
2260 QQuickItemView::setHighlightMoveDuration(duration);
2264 qreal QQuickListView::highlightResizeVelocity() const
2266 Q_D(const QQuickListView);
2267 return d->highlightResizeVelocity;
2270 void QQuickListView::setHighlightResizeVelocity(qreal speed)
2272 Q_D(QQuickListView);
2273 if (d->highlightResizeVelocity != speed) {
2274 d->highlightResizeVelocity = speed;
2275 if (d->highlightSizeAnimator)
2276 d->highlightSizeAnimator->velocity = d->highlightResizeVelocity;
2277 emit highlightResizeVelocityChanged();
2281 int QQuickListView::highlightResizeDuration() const
2283 Q_D(const QQuickListView);
2284 return d->highlightResizeDuration;
2287 void QQuickListView::setHighlightResizeDuration(int duration)
2289 Q_D(QQuickListView);
2290 if (d->highlightResizeDuration != duration) {
2291 d->highlightResizeDuration = duration;
2292 if (d->highlightSizeAnimator)
2293 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2294 emit highlightResizeDurationChanged();
2299 \qmlproperty enumeration QtQuick2::ListView::snapMode
2301 This property determines how the view scrolling will settle following a drag or flick.
2302 The possible values are:
2305 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2306 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2308 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2309 visible item at the time the mouse button is released. This mode is particularly
2310 useful for moving one page at a time.
2313 \c snapMode does not affect the \l currentIndex. To update the
2314 \l currentIndex as the list is moved, set \l highlightRangeMode
2315 to \c ListView.StrictlyEnforceRange.
2317 \sa highlightRangeMode
2319 QQuickListView::SnapMode QQuickListView::snapMode() const
2321 Q_D(const QQuickListView);
2325 void QQuickListView::setSnapMode(SnapMode mode)
2327 Q_D(QQuickListView);
2328 if (d->snapMode != mode) {
2330 emit snapModeChanged();
2336 \qmlproperty Component QtQuick2::ListView::footer
2337 This property holds the component to use as the footer.
2339 An instance of the footer component is created for each view. The
2340 footer is positioned at the end of the view, after any items.
2342 \sa header, footerItem
2347 \qmlproperty Component QtQuick2::ListView::header
2348 This property holds the component to use as the header.
2350 An instance of the header component is created for each view. The
2351 header is positioned at the beginning of the view, before any items.
2353 \sa footer, headertem
2357 \qmlproperty Item QtQuick2::ListView::headerItem
2358 This holds the header item created from the \l header component.
2360 An instance of the header component is created for each view. The
2361 header is positioned at the beginning of the view, before any items.
2363 \sa header, footerItem
2367 \qmlproperty Item QtQuick2::ListView::footerItem
2368 This holds the footer item created from the \l footer component.
2370 An instance of the footer component is created for each view. The
2371 footer is positioned at the end of the view, after any items.
2373 \sa footer, headerItem
2377 \qmlproperty Transition QtQuick2::ListView::populate
2379 This property holds the transition to apply to the items that are initially created
2382 It is applied to all items that are created when:
2385 \li The view is first created
2386 \li The view's \l model changes
2387 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2390 For example, here is a view that specifies such a transition:
2395 populate: Transition {
2396 NumberAnimation { properties: "x,y"; duration: 1000 }
2401 When the view is initialized, the view will create all the necessary items for the view,
2402 then animate them to their correct positions within the view over one second.
2404 For more details and examples on how to use view transitions, see the ViewTransition
2407 \sa add, ViewTransition
2411 \qmlproperty Transition QtQuick2::ListView::add
2413 This property holds the transition to apply to items that are added to the view.
2415 For example, here is a view that specifies such a transition:
2421 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2426 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2427 to its final x,y position within the view, over one second. The transition only applies to
2428 the new items that are added to the view; it does not apply to the items below that are
2429 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2430 or \l addDisplaced properties.
2432 For more details and examples on how to use view transitions, see the ViewTransition
2435 \note This transition is not applied to the items that are created when the view is initially
2436 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2439 \sa addDisplaced, populate, ViewTransition
2443 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2445 This property holds the transition to apply to items within the view that are displaced by
2446 the addition of other items to the view.
2448 For example, here is a view that specifies such a transition:
2453 addDisplaced: Transition {
2454 NumberAnimation { properties: "x,y"; duration: 1000 }
2459 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2460 them to move down (or sideways, if horizontally orientated) within the view. As this
2461 displacement occurs, the items' movement to their new x,y positions within the view will be
2462 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2463 the new item that has been added to the view; to animate the added items, set the \l add
2466 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2467 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2468 if it is not necessary to specify different transitions depending on whether an item is displaced
2469 by an add, move or remove operation, consider setting the \l displaced property instead.
2471 For more details and examples on how to use view transitions, see the ViewTransition
2474 \note This transition is not applied to the items that are created when the view is initially
2475 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2478 \sa displaced, add, populate, ViewTransition
2482 \qmlproperty Transition QtQuick2::ListView::move
2484 This property holds the transition to apply to items in the view that are being moved due
2485 to a move operation in the view's \l model.
2487 For example, here is a view that specifies such a transition:
2493 NumberAnimation { properties: "x,y"; duration: 1000 }
2498 Whenever the \l model performs a move operation to move a particular set of indexes, the
2499 respective items in the view will be animated to their new positions in the view over one
2500 second. The transition only applies to the items that are the subject of the move operation
2501 in the model; it does not apply to items below them that are displaced by the move operation.
2502 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2504 For more details and examples on how to use view transitions, see the ViewTransition
2507 \sa moveDisplaced, ViewTransition
2511 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2513 This property holds the transition to apply to items that are displaced by a move operation in
2514 the view's \l model.
2516 For example, here is a view that specifies such a transition:
2521 moveDisplaced: Transition {
2522 NumberAnimation { properties: "x,y"; duration: 1000 }
2527 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2528 between the source and destination indexes of the move operation are displaced, causing them
2529 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2530 displacement occurs, the items' movement to their new x,y positions within the view will be
2531 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2532 the items that are the actual subjects of the move operation; to animate the moved items, set
2533 the \l move property.
2535 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2536 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2537 if it is not necessary to specify different transitions depending on whether an item is displaced
2538 by an add, move or remove operation, consider setting the \l displaced property instead.
2540 For more details and examples on how to use view transitions, see the ViewTransition
2543 \sa displaced, move, ViewTransition
2547 \qmlproperty Transition QtQuick2::ListView::remove
2549 This property holds the transition to apply to items that are removed from the view.
2551 For example, here is a view that specifies such a transition:
2556 remove: Transition {
2558 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2559 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2565 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2566 over one second, and in parallel will also change its opacity to 0. The transition
2567 only applies to the items that are removed from the view; it does not apply to the items below
2568 them that are displaced by the removal of the items. To animate the displaced items, set the
2569 \l displaced or \l removeDisplaced properties.
2571 Note that by the time the transition is applied, the item has already been removed from the
2572 model; any references to the model data for the removed index will not be valid.
2574 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2575 remove transition will not be applied until \l delayRemove becomes false again.
2577 For more details and examples on how to use view transitions, see the ViewTransition
2580 \sa removeDisplaced, ViewTransition
2584 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2586 This property holds the transition to apply to items in the view that are displaced by the
2587 removal of other items in the view.
2589 For example, here is a view that specifies such a transition:
2594 removeDisplaced: Transition {
2595 NumberAnimation { properties: "x,y"; duration: 1000 }
2600 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2601 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2602 displacement occurs, the items' movement to their new x,y positions within the view will be
2603 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2604 the item that has actually been removed from the view; to animate the removed items, set the
2607 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2608 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2609 if it is not necessary to specify different transitions depending on whether an item is displaced
2610 by an add, move or remove operation, consider setting the \l displaced property instead.
2612 For more details and examples on how to use view transitions, see the ViewTransition
2615 \sa displaced, remove, ViewTransition
2619 \qmlproperty Transition QtQuick2::ListView::displaced
2620 This property holds the generic transition to apply to items that have been displaced by
2621 any model operation that affects the view.
2623 This is a convenience for specifying the generic transition to be applied to any items
2624 that are displaced by an add, move or remove operation, without having to specify the
2625 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2626 is a view that specifies a displaced transition:
2631 displaced: Transition {
2632 NumberAnimation { properties: "x,y"; duration: 1000 }
2637 When any item is added, moved or removed within the above view, the items below it are
2638 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2639 view. As this displacement occurs, the items' movement to their new x,y positions within
2640 the view will be animated by a NumberAnimation over one second, as specified.
2642 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2643 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2644 instead of the generic displaced transition when the relevant operation occurs, providing that
2645 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2646 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2648 For more details and examples on how to use view transitions, see the ViewTransition
2651 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2654 void QQuickListView::viewportMoved(Qt::Orientations orient)
2656 Q_D(QQuickListView);
2657 QQuickItemView::viewportMoved(orient);
2660 // Recursion can occur due to refill changing the content size.
2661 if (d->inViewportMoved)
2663 d->inViewportMoved = true;
2666 if (d->isBottomToTop())
2667 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2669 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2671 if (d->isRightToLeft())
2672 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2674 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2677 d->refillOrLayout();
2679 // Set visibility of items to eliminate cost of items outside the visible area.
2680 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2681 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2682 for (int i = 0; i < d->visibleItems.count(); ++i) {
2683 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2684 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2687 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2689 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2690 d->moveReason = QQuickListViewPrivate::Mouse;
2691 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2692 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2693 // reposition highlight
2694 qreal pos = d->highlight->position();
2695 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2696 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2697 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2698 if (pos < viewPos + d->highlightRangeStart)
2699 pos = viewPos + d->highlightRangeStart;
2700 if (pos != d->highlight->position()) {
2701 d->highlightPosAnimator->stop();
2702 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2704 d->updateHighlight();
2707 // update current index
2708 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2709 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2710 d->updateCurrent(snapItem->index);
2715 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2716 d->inFlickCorrection = true;
2717 // Near an end and it seems that the extent has changed?
2718 // Recalculate the flick so that we don't end up in an odd position.
2719 if (yflick() && !d->vData.inOvershoot) {
2720 if (d->vData.velocity > 0) {
2721 const qreal minY = minYExtent();
2722 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2723 && minY != d->vData.flickTarget)
2724 d->flickY(-d->vData.smoothVelocity.value());
2725 } else if (d->vData.velocity < 0) {
2726 const qreal maxY = maxYExtent();
2727 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2728 && maxY != d->vData.flickTarget)
2729 d->flickY(-d->vData.smoothVelocity.value());
2733 if (xflick() && !d->hData.inOvershoot) {
2734 if (d->hData.velocity > 0) {
2735 const qreal minX = minXExtent();
2736 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2737 && minX != d->hData.flickTarget)
2738 d->flickX(-d->hData.smoothVelocity.value());
2739 } else if (d->hData.velocity < 0) {
2740 const qreal maxX = maxXExtent();
2741 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2742 && maxX != d->hData.flickTarget)
2743 d->flickX(-d->hData.smoothVelocity.value());
2746 d->inFlickCorrection = false;
2748 if (d->sectionCriteria) {
2749 d->updateCurrentSection();
2750 d->updateStickySections();
2752 d->inViewportMoved = false;
2755 void QQuickListView::keyPressEvent(QKeyEvent *event)
2757 Q_D(QQuickListView);
2758 if (d->model && d->model->count() && d->interactive) {
2759 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2760 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2761 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2762 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2763 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2764 decrementCurrentIndex();
2767 } else if (d->wrap) {
2771 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2772 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2773 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2774 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2775 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2776 incrementCurrentIndex();
2779 } else if (d->wrap) {
2786 QQuickItemView::keyPressEvent(event);
2789 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2791 Q_D(QQuickListView);
2792 if (d->isRightToLeft()) {
2793 // maintain position relative to the right edge
2794 int dx = newGeometry.width() - oldGeometry.width();
2795 setContentX(contentX() - dx);
2796 } else if (d->isBottomToTop()) {
2797 // maintain position relative to the bottom edge
2798 int dy = newGeometry.height() - oldGeometry.height();
2799 setContentY(contentY() - dy);
2801 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2804 void QQuickListView::initItem(int index, QQuickItem *item)
2806 QQuickItemView::initItem(index, item);
2807 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
2808 qmlAttachedPropertiesObject<QQuickListView>(item));
2810 attached->setView(this);
2815 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2817 Increments the current index. The current index will wrap
2818 if keyNavigationWraps is true and it is currently at the end.
2819 This method has no effect if the \l count is zero.
2821 \b Note: methods should only be called after the Component has completed.
2823 void QQuickListView::incrementCurrentIndex()
2825 Q_D(QQuickListView);
2826 int count = d->model ? d->model->count() : 0;
2827 if (count && (currentIndex() < count - 1 || d->wrap)) {
2828 d->moveReason = QQuickListViewPrivate::SetIndex;
2829 int index = currentIndex()+1;
2830 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2835 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2837 Decrements the current index. The current index will wrap
2838 if keyNavigationWraps is true and it is currently at the beginning.
2839 This method has no effect if the \l count is zero.
2841 \b Note: methods should only be called after the Component has completed.
2843 void QQuickListView::decrementCurrentIndex()
2845 Q_D(QQuickListView);
2846 int count = d->model ? d->model->count() : 0;
2847 if (count && (currentIndex() > 0 || d->wrap)) {
2848 d->moveReason = QQuickListViewPrivate::SetIndex;
2849 int index = currentIndex()-1;
2850 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2854 void QQuickListViewPrivate::updateSectionCriteria()
2856 Q_Q(QQuickListView);
2857 if (q->isComponentComplete() && model) {
2858 QList<QByteArray> roles;
2859 if (sectionCriteria && !sectionCriteria->property().isEmpty())
2860 roles << sectionCriteria->property().toUtf8();
2861 model->setWatchedRoles(roles);
2864 forceLayoutPolish();
2868 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2870 int modelIndex = change.index;
2871 int count = change.count;
2873 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2874 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2877 int i = visibleItems.count() - 1;
2878 while (i > 0 && visibleItems.at(i)->index == -1)
2880 if (i == 0 && visibleItems.first()->index == -1) {
2881 // there are no visible items except items marked for removal
2882 index = visibleItems.count();
2883 } else if (visibleItems.at(i)->index + 1 == modelIndex
2884 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2885 // Special case of appending an item to the model.
2886 index = visibleItems.count();
2888 if (modelIndex < visibleIndex) {
2889 // Insert before visible items
2890 visibleIndex += count;
2891 for (int i = 0; i < visibleItems.count(); ++i) {
2892 FxViewItem *item = visibleItems.at(i);
2893 if (item->index != -1 && item->index >= modelIndex)
2894 item->index += count;
2901 // index can be the next item past the end of the visible items list (i.e. appended)
2903 if (visibleItems.count()) {
2904 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2905 : visibleItems.last()->endPosition()+spacing;
2908 int prevVisibleCount = visibleItems.count();
2909 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2910 // Insert items before the visible item.
2911 int insertionIdx = index;
2913 int from = tempPos - buffer;
2915 for (i = count-1; i >= 0; --i) {
2916 if (pos > from && insertionIdx < visibleIndex) {
2917 // item won't be visible, just note the size for repositioning
2918 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2919 pos -= averageSize + spacing;
2921 // item is before first visible e.g. in cache buffer
2922 FxViewItem *item = 0;
2923 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2924 item->index = modelIndex + i;
2926 item = createItem(modelIndex + i);
2930 visibleItems.insert(insertionIdx, item);
2931 if (insertionIdx == 0)
2932 insertResult->changedFirstItem = true;
2933 if (!change.isMove()) {
2934 addedItems->append(item);
2935 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2937 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2938 pos -= item->size() + spacing;
2944 int to = buffer+tempPos+size();
2945 for (i = 0; i < count && pos <= to; ++i) {
2946 FxViewItem *item = 0;
2947 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2948 item->index = modelIndex + i;
2949 bool newItem = !item;
2951 item = createItem(modelIndex + i);
2955 visibleItems.insert(index, item);
2957 insertResult->changedFirstItem = true;
2958 if (change.isMove()) {
2959 // we know this is a move target, since move displaced items that are
2960 // shuffled into view due to a move would be added in refill()
2961 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2962 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2964 addedItems->append(item);
2965 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2967 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2968 pos += item->size() + spacing;
2973 for (; index < visibleItems.count(); ++index) {
2974 FxViewItem *item = visibleItems.at(index);
2975 if (item->index != -1) {
2976 item->index += count;
2977 if (change.isMove())
2978 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2980 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2984 updateVisibleIndex();
2986 return visibleItems.count() > prevVisibleCount;
2989 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2991 Q_UNUSED(insertionResult);
2996 int markerItemIndex = -1;
2997 for (int i=0; i<visibleItems.count(); i++) {
2998 if (visibleItems[i]->index == afterModelIndex) {
2999 markerItemIndex = i;
3003 if (markerItemIndex < 0)
3006 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
3007 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
3008 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
3010 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
3011 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
3012 if (!listItem->transitionScheduledOrRunning()) {
3013 qreal pos = listItem->position();
3014 listItem->setPosition(pos - sizeRemoved);
3015 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
3016 listItem->setPosition(pos);
3022 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
3024 Positions the view such that the \a index is at the position specified by
3028 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
3029 \li ListView.Center - position item in the center of the view.
3030 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3031 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
3032 bring the item into view.
3033 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
3034 the view the item is positioned at the top (or left for horizontal orientation) of the view.
3035 \li ListView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
3036 is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
3040 If positioning the view at \a index would cause empty space to be displayed at
3041 the beginning or end of the view, the view will be positioned at the boundary.
3043 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3044 at a particular index. This is unreliable since removing items from the start
3045 of the list does not cause all other items to be repositioned, and because
3046 the actual start of the view can vary based on the size of the delegates.
3047 The correct way to bring an item into view is with \c positionViewAtIndex.
3049 \b Note: methods should only be called after the Component has completed. To position
3050 the view at startup, this method should be called by Component.onCompleted. For
3051 example, to position the view at the end:
3054 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3059 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
3060 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
3062 Positions the view at the beginning or end, taking into account any header or footer.
3064 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3065 at a particular index. This is unreliable since removing items from the start
3066 of the list does not cause all other items to be repositioned, and because
3067 the actual start of the view can vary based on the size of the delegates.
3069 \b Note: methods should only be called after the Component has completed. To position
3070 the view at startup, this method should be called by Component.onCompleted. For
3071 example, to position the view at the end on startup:
3074 Component.onCompleted: positionViewAtEnd()
3079 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3081 Returns the index of the visible item containing the point \a x, \a y in content
3082 coordinates. If there is no item at the point specified, or the item is
3083 not visible -1 is returned.
3085 If the item is outside the visible area, -1 is returned, regardless of
3086 whether an item will exist at that point when scrolled into view.
3088 \b Note: methods should only be called after the Component has completed.
3092 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3094 Returns the visible item containing the point \a x, \a y in content
3095 coordinates. If there is no item at the point specified, or the item is
3096 not visible null is returned.
3098 If the item is outside the visible area, null is returned, regardless of
3099 whether an item will exist at that point when scrolled into view.
3101 \b Note: methods should only be called after the Component has completed.
3104 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3106 return new QQuickListViewAttached(obj);