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 <private/qlistmodelinterface_p.h>
55 #include "qplatformdefs.h"
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
63 //#define DEBUG_DELEGATE_LIFECYCLE
67 class QQuickListViewPrivate : public QQuickItemViewPrivate
69 Q_DECLARE_PUBLIC(QQuickListView)
71 static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
73 virtual Qt::Orientation layoutOrientation() const;
74 virtual bool isContentFlowReversed() const;
75 bool isRightToLeft() const;
76 bool isBottomToTop() const;
78 virtual qreal positionAt(int index) const;
79 virtual qreal endPositionAt(int index) const;
80 virtual qreal originPosition() const;
81 virtual qreal lastPosition() const;
83 FxViewItem *itemBefore(int modelIndex) const;
84 QString sectionAt(int modelIndex);
85 qreal snapPosAt(qreal pos);
86 FxViewItem *snapItemAt(qreal pos);
91 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer);
92 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
93 virtual void visibleItemsChanged();
95 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
96 virtual void initializeViewItem(FxViewItem *item);
97 virtual bool releaseItem(FxViewItem *item);
98 virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
99 virtual void repositionPackageItemAt(QQuickItem *item, int index);
100 virtual void resetFirstItemPosition(qreal pos = 0.0);
101 virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
103 virtual void createHighlight();
104 virtual void updateHighlight();
105 virtual void resetHighlightPosition();
107 virtual void setPosition(qreal pos);
108 virtual void layoutVisibleItems(int fromModelIndex = 0);
110 virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
111 virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
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 m_view->updateSections();
201 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
203 if (criteria != m_criteria) {
204 m_criteria = criteria;
205 emit criteriaChanged();
206 m_view->updateSections();
210 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
212 if (delegate != m_delegate) {
214 m_view->releaseSectionItems();
215 m_delegate = delegate;
216 emit delegateChanged();
217 m_view->updateSections();
221 QString QQuickViewSection::sectionString(const QString &value)
223 if (m_criteria == FirstCharacter)
224 return value.isEmpty() ? QString() : value.at(0);
229 void QQuickViewSection::setLabelPositioning(int l)
231 if (m_labelPositioning != l) {
232 m_labelPositioning = l;
233 emit labelPositioningChanged();
234 m_view->updateSections();
238 //----------------------------------------------------------------------------
240 class FxListItemSG : public FxViewItem
243 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
244 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
246 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
247 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
253 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
254 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
258 inline QQuickItem *section() const {
259 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
261 void setSection(QQuickItem *s) {
263 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
264 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
267 qreal position() const {
269 if (view->orientation() == QQuickListView::Vertical)
270 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
272 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
274 return itemPosition();
277 qreal itemPosition() const {
278 if (view->orientation() == QQuickListView::Vertical)
279 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
281 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
285 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
287 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
289 qreal itemSize() const {
290 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
292 qreal sectionSize() const {
294 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
297 qreal endPosition() const {
298 if (view->orientation() == QQuickListView::Vertical) {
299 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
301 : itemY() + item->height());
303 return (view->effectiveLayoutDirection() == Qt::RightToLeft
305 : itemX() + item->width());
308 void setPosition(qreal pos, bool immediate = false) {
309 // position the section immediately even if there is a transition
311 if (view->orientation() == QQuickListView::Vertical) {
312 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
313 section()->setY(-section()->height()-pos);
315 section()->setY(pos);
317 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
318 section()->setX(-section()->width()-pos);
320 section()->setX(pos);
323 moveTo(pointForPosition(pos), immediate);
325 void setSize(qreal size) {
326 if (view->orientation() == QQuickListView::Vertical)
327 item->setHeight(size);
329 item->setWidth(size);
331 bool contains(qreal x, qreal y) const {
332 return (x >= itemX() && x < itemX() + item->width() &&
333 y >= itemY() && y < itemY() + item->height());
336 QQuickListView *view;
339 QPointF pointForPosition(qreal pos) const {
340 if (view->orientation() == QQuickListView::Vertical) {
341 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
343 pos += section()->height();
344 return QPointF(itemX(), -item->height() - pos);
347 pos += section()->height();
348 return QPointF(itemX(), pos);
351 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
353 pos += section()->width();
354 return QPointF(-item->width() - pos, itemY());
357 pos += section()->width();
358 return QPointF(pos, itemY());
364 //----------------------------------------------------------------------------
366 bool QQuickListViewPrivate::isContentFlowReversed() const
368 return isRightToLeft() || isBottomToTop();
371 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
373 return static_cast<Qt::Orientation>(orient);
376 bool QQuickListViewPrivate::isRightToLeft() const
378 Q_Q(const QQuickListView);
379 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
382 bool QQuickListViewPrivate::isBottomToTop() const
384 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
387 // Returns the item before modelIndex, if created.
388 // May return an item marked for removal.
389 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
391 if (modelIndex < visibleIndex)
395 while (idx < visibleItems.count()) {
396 FxViewItem *item = visibleItems.at(idx);
397 if (item->index != -1)
398 lastIndex = item->index;
399 if (item->index == modelIndex)
400 return visibleItems.at(idx-1);
403 if (lastIndex == modelIndex-1)
404 return visibleItems.last();
408 void QQuickListViewPrivate::setPosition(qreal pos)
411 if (orient == QQuickListView::Vertical) {
413 q->QQuickFlickable::setContentY(-pos-size());
415 q->QQuickFlickable::setContentY(pos);
418 q->QQuickFlickable::setContentX(-pos-size());
420 q->QQuickFlickable::setContentX(pos);
424 qreal QQuickListViewPrivate::originPosition() const
427 if (!visibleItems.isEmpty()) {
428 pos = (*visibleItems.constBegin())->position();
429 if (visibleIndex > 0)
430 pos -= visibleIndex * (averageSize + spacing);
435 qreal QQuickListViewPrivate::lastPosition() const
438 if (!visibleItems.isEmpty()) {
439 int invisibleCount = visibleItems.count() - visibleIndex;
440 for (int i = visibleItems.count()-1; i >= 0; --i) {
441 if (visibleItems.at(i)->index != -1) {
442 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
446 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
447 } else if (model && model->count()) {
448 pos = (model->count() * averageSize + (model->count()-1) * spacing);
453 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
455 if (FxViewItem *item = visibleItem(modelIndex)) {
456 return item->position();
458 if (!visibleItems.isEmpty()) {
459 if (modelIndex < visibleIndex) {
460 int count = visibleIndex - modelIndex;
462 if (modelIndex == currentIndex && currentItem) {
463 cs = currentItem->size() + spacing;
466 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
468 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
469 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
475 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
477 if (FxViewItem *item = visibleItem(modelIndex))
478 return item->endPosition();
479 if (!visibleItems.isEmpty()) {
480 if (modelIndex < visibleIndex) {
481 int count = visibleIndex - modelIndex;
482 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
484 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
485 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
491 QString QQuickListViewPrivate::sectionAt(int modelIndex)
493 if (FxViewItem *item = visibleItem(modelIndex))
494 return item->attached->section();
497 if (sectionCriteria) {
498 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
499 section = sectionCriteria->sectionString(propValue);
505 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
507 if (FxViewItem *snapItem = snapItemAt(pos))
508 return snapItem->position();
509 if (visibleItems.count()) {
510 qreal firstPos = (*visibleItems.constBegin())->position();
511 qreal endPos = (*(--visibleItems.constEnd()))->position();
512 if (pos < firstPos) {
513 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
514 } else if (pos > endPos)
515 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
517 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
520 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
522 FxViewItem *snapItem = 0;
523 qreal prevItemSize = 0;
524 for (int i = 0; i < visibleItems.count(); ++i) {
525 FxViewItem *item = visibleItems.at(i);
526 if (item->index == -1)
528 qreal itemTop = item->position();
529 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
531 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
533 prevItemSize = item->size();
538 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
540 visiblePos = positionAt(newIndex);
541 visibleIndex = newIndex;
544 void QQuickListViewPrivate::init()
546 QQuickItemViewPrivate::init();
547 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
550 void QQuickListViewPrivate::clear()
552 for (int i = 0; i < sectionCacheSize; ++i) {
553 delete sectionCache[i];
557 releaseSectionItem(currentSectionItem);
558 currentSectionItem = 0;
559 releaseSectionItem(nextSectionItem);
561 lastVisibleSection = QString();
562 QQuickItemViewPrivate::clear();
565 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
569 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
570 listItem->index = modelIndex;
572 // initialise attached properties
573 if (sectionCriteria) {
574 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
575 listItem->attached->setSection(sectionCriteria->sectionString(propValue));
576 if (modelIndex > 0) {
577 if (FxViewItem *item = itemBefore(modelIndex))
578 listItem->attached->setPrevSection(item->attached->section());
580 listItem->attached->setPrevSection(sectionAt(modelIndex-1));
582 if (modelIndex < model->count()-1) {
583 if (FxViewItem *item = visibleItem(modelIndex+1))
584 listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section());
586 listItem->attached->setNextSection(sectionAt(modelIndex+1));
593 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
595 QQuickItemViewPrivate::initializeViewItem(item);
597 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
598 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
600 if (sectionCriteria && sectionCriteria->delegate()) {
601 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
602 updateInlineSection(static_cast<FxListItemSG*>(item));
606 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
611 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
613 bool released = QQuickItemViewPrivate::releaseItem(item);
614 if (released && att && att->m_sectionItem) {
615 // We hold no more references to this item
618 if (!sectionCache[i]) {
619 sectionCache[i] = att->m_sectionItem;
620 sectionCache[i]->setVisible(false);
621 att->m_sectionItem = 0;
625 } while (i < sectionCacheSize);
626 delete att->m_sectionItem;
627 att->m_sectionItem = 0;
633 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
635 qreal itemEnd = visiblePos;
636 if (visibleItems.count()) {
637 visiblePos = (*visibleItems.constBegin())->position();
638 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
641 int modelIndex = findLastVisibleIndex();
642 bool haveValidItems = modelIndex >= 0;
643 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
645 if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
646 || bufferTo < visiblePos - averageSize - spacing)) {
647 // We've jumped more than a page. Estimate which items are now
648 // visible and fill from there.
649 int count = (fillFrom - itemEnd) / (averageSize + spacing);
650 int newModelIdx = qBound(0, modelIndex + count, model->count());
651 count = newModelIdx - modelIndex;
653 for (int i = 0; i < visibleItems.count(); ++i)
654 releaseItem(visibleItems.at(i));
655 visibleItems.clear();
656 modelIndex = newModelIdx;
657 visibleIndex = modelIndex;
658 visiblePos = itemEnd + count * (averageSize + spacing);
659 itemEnd = visiblePos;
663 bool changed = false;
664 FxListItemSG *item = 0;
666 while (modelIndex < model->count() && pos <= fillTo) {
667 #ifdef DEBUG_DELEGATE_LIFECYCLE
668 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
670 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
672 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
673 item->setPosition(pos, true);
674 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
675 pos += item->size() + spacing;
676 visibleItems.append(item);
681 if (doBuffer && requestedIndex != -1) // already waiting for an item
684 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
685 #ifdef DEBUG_DELEGATE_LIFECYCLE
686 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
688 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
691 visiblePos -= item->size() + spacing;
692 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
693 item->setPosition(visiblePos, true);
694 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
695 visibleItems.prepend(item);
702 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
704 FxViewItem *item = 0;
705 bool changed = false;
707 // Remove items from the start of the view.
708 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
709 // removed, otherwise a zero-sized item is infinitely added and removed over and
712 while (visibleItems.count() > 1 && index < visibleItems.count()
713 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
714 if (item->attached->delayRemove())
717 if (item->size() > 0) {
718 #ifdef DEBUG_DELEGATE_LIFECYCLE
719 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
721 // remove this item and all zero-sized items before it
723 if (item->index != -1)
725 visibleItems.removeAt(index);
726 if (item->transitionScheduledOrRunning()) {
727 #ifdef DEBUG_DELEGATE_LIFECYCLE
728 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
730 item->releaseAfterTransition = true;
731 releasePendingTransition.append(item);
737 item = visibleItems.at(--index);
745 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
746 if (item->attached->delayRemove())
748 #ifdef DEBUG_DELEGATE_LIFECYCLE
749 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
751 visibleItems.removeLast();
752 if (item->transitionScheduledOrRunning()) {
753 #ifdef DEBUG_DELEGATE_LIFECYCLE
754 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
756 item->releaseAfterTransition = true;
757 releasePendingTransition.append(item);
767 void QQuickListViewPrivate::visibleItemsChanged()
769 if (visibleItems.count())
770 visiblePos = (*visibleItems.constBegin())->position();
772 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
773 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
777 updateCurrentSection();
778 updateUnrequestedPositions();
781 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
783 if (!visibleItems.isEmpty()) {
784 const qreal from = isContentFlowReversed() ? -position() - size() : position();
785 const qreal to = isContentFlowReversed() ? -position() : position() + size();
787 FxViewItem *firstItem = *visibleItems.constBegin();
788 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
789 qreal sum = firstItem->size();
790 qreal pos = firstItem->position() + firstItem->size() + spacing;
791 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
793 for (int i=1; i < visibleItems.count(); ++i) {
794 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
795 if (item->index >= fromModelIndex) {
796 item->setPosition(pos);
797 item->setVisible(item->endPosition() >= from && item->position() <= to);
799 pos += item->size() + spacing;
801 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
803 averageSize = qRound(sum / visibleItems.count());
805 // move current item if it is not a visible item.
806 if (currentIndex >= 0 && currentItem && !fixedCurrent)
807 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
811 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
813 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
816 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
819 qreal pos = position();
820 if (orient == QQuickListView::Vertical) {
821 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
823 item->setY(-positionAt(index)-item->height());
825 item->setY(positionAt(index));
828 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
830 item->setX(-positionAt(index)-item->width());
832 item->setX(positionAt(index));
837 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
839 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
840 item->setPosition(pos);
843 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
845 if (!visibleItems.count())
847 qreal diff = forwards - backwards;
848 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
851 void QQuickListViewPrivate::createHighlight()
854 bool changed = false;
856 if (trackedItem == highlight)
861 delete highlightPosAnimator;
862 delete highlightSizeAnimator;
863 highlightPosAnimator = 0;
864 highlightSizeAnimator = 0;
870 QQuickItem *item = createHighlightItem();
872 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
875 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
876 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
878 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
879 highlightPosAnimator = new QSmoothedAnimation;
880 highlightPosAnimator->target = QQmlProperty(item, posProp);
881 highlightPosAnimator->velocity = highlightMoveVelocity;
882 highlightPosAnimator->userDuration = highlightMoveDuration;
884 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
885 highlightSizeAnimator = new QSmoothedAnimation;
886 highlightSizeAnimator->velocity = highlightResizeVelocity;
887 highlightSizeAnimator->userDuration = highlightResizeDuration;
888 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
890 highlight = newHighlight;
895 emit q->highlightItemChanged();
898 void QQuickListViewPrivate::updateHighlight()
900 applyPendingChanges();
902 if ((!currentItem && highlight) || (currentItem && !highlight))
904 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
905 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
906 // auto-update highlight
907 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
908 highlightPosAnimator->to = isContentFlowReversed()
909 ? -listItem->itemPosition()-listItem->itemSize()
910 : listItem->itemPosition();
911 highlightSizeAnimator->to = listItem->itemSize();
912 if (orient == QQuickListView::Vertical) {
913 if (highlight->item->width() == 0)
914 highlight->item->setWidth(currentItem->item->width());
916 if (highlight->item->height() == 0)
917 highlight->item->setHeight(currentItem->item->height());
920 highlightPosAnimator->restart();
921 highlightSizeAnimator->restart();
926 void QQuickListViewPrivate::resetHighlightPosition()
928 if (highlight && currentItem)
929 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
932 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
935 QQuickItem *sectionItem = 0;
936 int i = sectionCacheSize-1;
937 while (i >= 0 && !sectionCache[i])
940 sectionItem = sectionCache[i];
942 sectionItem->setVisible(true);
943 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
944 context->setContextProperty(QLatin1String("section"), section);
946 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
947 QQmlContext *context = new QQmlContext(
948 creationContext ? creationContext : qmlContext(q));
949 context->setContextProperty(QLatin1String("section"), section);
950 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
952 QQml_setParent_noEvent(context, nobj);
953 sectionItem = qobject_cast<QQuickItem *>(nobj);
957 sectionItem->setZ(2);
958 QQml_setParent_noEvent(sectionItem, contentItem);
959 sectionItem->setParentItem(contentItem);
964 sectionCriteria->delegate()->completeCreate();
970 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
976 if (!sectionCache[i]) {
977 sectionCache[i] = item;
978 sectionCache[i]->setVisible(false);
982 } while (i < sectionCacheSize);
987 void QQuickListViewPrivate::releaseSectionItems()
989 for (int i = 0; i < visibleItems.count(); ++i) {
990 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
991 if (listItem->section()) {
992 qreal pos = listItem->position();
993 releaseSectionItem(listItem->section());
994 listItem->setSection(0);
995 listItem->setPosition(pos);
998 for (int i = 0; i < sectionCacheSize; ++i) {
999 delete sectionCache[i];
1000 sectionCache[i] = 0;
1004 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
1006 if (!sectionCriteria || !sectionCriteria->delegate())
1008 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
1009 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
1010 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
1011 if (!listItem->section()) {
1012 qreal pos = listItem->position();
1013 listItem->setSection(getSectionItem(listItem->attached->m_section));
1014 listItem->setPosition(pos);
1016 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
1017 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1019 } else if (listItem->section()) {
1020 qreal pos = listItem->position();
1021 releaseSectionItem(listItem->section());
1022 listItem->setSection(0);
1023 listItem->setPosition(pos);
1027 void QQuickListViewPrivate::updateStickySections()
1029 if (!sectionCriteria
1030 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1033 bool isFlowReversed = isContentFlowReversed();
1034 qreal viewPos = isFlowReversed ? -position()-size() : position();
1035 QQuickItem *sectionItem = 0;
1036 QQuickItem *lastSectionItem = 0;
1038 while (index < visibleItems.count()) {
1039 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1040 // Find the current section header and last visible section header
1041 // and hide them if they will overlap a static section header.
1042 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1043 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1045 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1046 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1048 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1049 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1050 section->setVisible(visBot && visTop);
1051 if (visTop && !sectionItem)
1052 sectionItem = section;
1053 if (isFlowReversed) {
1054 if (-sectionPos <= viewPos + size())
1055 lastSectionItem = section;
1057 if (sectionPos + sectionSize < viewPos + size())
1058 lastSectionItem = section;
1064 // Current section header
1065 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1066 if (!currentSectionItem) {
1067 currentSectionItem = getSectionItem(currentSection);
1068 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1069 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1070 context->setContextProperty(QLatin1String("section"), currentSection);
1072 currentStickySection = currentSection;
1073 if (!currentSectionItem)
1076 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1077 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1079 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1080 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1082 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1083 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1086 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1088 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1089 if (orient == QQuickListView::Vertical)
1090 currentSectionItem->setY(pos);
1092 currentSectionItem->setX(pos);
1093 } else if (currentSectionItem) {
1094 releaseSectionItem(currentSectionItem);
1095 currentSectionItem = 0;
1098 // Next section footer
1099 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1100 if (!nextSectionItem) {
1101 nextSectionItem = getSectionItem(nextSection);
1102 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1103 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1104 context->setContextProperty(QLatin1String("section"), nextSection);
1106 nextStickySection = nextSection;
1107 if (!nextSectionItem)
1110 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1111 nextSectionItem->setVisible(!nextSection.isEmpty());
1112 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1113 if (lastSectionItem) {
1114 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1115 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1118 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1119 if (orient == QQuickListView::Vertical)
1120 nextSectionItem->setY(pos);
1122 nextSectionItem->setX(pos);
1123 } else if (nextSectionItem) {
1124 releaseSectionItem(nextSectionItem);
1125 nextSectionItem = 0;
1129 void QQuickListViewPrivate::updateSections()
1131 Q_Q(QQuickListView);
1132 if (!q->isComponentComplete())
1135 QQuickItemViewPrivate::updateSections();
1137 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1138 QString prevSection;
1139 if (visibleIndex > 0)
1140 prevSection = sectionAt(visibleIndex-1);
1141 QQuickListViewAttached *prevAtt = 0;
1143 for (int i = 0; i < visibleItems.count(); ++i) {
1144 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1145 attached->setPrevSection(prevSection);
1146 if (visibleItems.at(i)->index != -1) {
1147 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1148 attached->setSection(sectionCriteria->sectionString(propValue));
1149 idx = visibleItems.at(i)->index;
1151 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1153 prevAtt->setNextSection(attached->section());
1154 prevSection = attached->section();
1158 if (idx > 0 && idx < model->count()-1)
1159 prevAtt->setNextSection(sectionAt(idx+1));
1161 prevAtt->setNextSection(QString());
1165 lastVisibleSection = QString();
1166 updateCurrentSection();
1167 updateStickySections();
1172 void QQuickListViewPrivate::updateCurrentSection()
1174 Q_Q(QQuickListView);
1175 if (!sectionCriteria || visibleItems.isEmpty()) {
1176 if (!currentSection.isEmpty()) {
1177 currentSection.clear();
1178 emit q->currentSectionChanged();
1182 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1183 qreal sectionThreshold = position();
1184 if (currentSectionItem && !inlineSections)
1185 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1187 int modelIndex = visibleIndex;
1188 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1189 if (visibleItems.at(index)->index != -1)
1190 modelIndex = visibleItems.at(index)->index;
1194 QString newSection = currentSection;
1195 if (index < visibleItems.count())
1196 newSection = visibleItems.at(index)->attached->section();
1198 newSection = (*visibleItems.constBegin())->attached->section();
1199 if (newSection != currentSection) {
1200 currentSection = newSection;
1201 updateStickySections();
1202 emit q->currentSectionChanged();
1205 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1206 // Don't want to scan for next section on every movement, so remember
1207 // the last section in the visible area and only scan for the next
1208 // section when that changes. Clearing lastVisibleSection will also
1210 QString lastSection = currentSection;
1211 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1212 if (nextSectionItem && !inlineSections)
1213 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1214 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1215 if (visibleItems.at(index)->index != -1)
1216 modelIndex = visibleItems.at(index)->index;
1217 lastSection = visibleItems.at(index)->attached->section();
1221 if (lastVisibleSection != lastSection) {
1222 nextSection = QString();
1223 lastVisibleSection = lastSection;
1224 for (int i = modelIndex; i < itemCount; ++i) {
1225 QString section = sectionAt(i);
1226 if (section != lastSection) {
1227 nextSection = section;
1228 updateStickySections();
1236 void QQuickListViewPrivate::initializeCurrentItem()
1238 QQuickItemViewPrivate::initializeCurrentItem();
1241 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1243 // don't reposition the item if it is already in the visibleItems list
1244 FxViewItem *actualItem = visibleItem(currentIndex);
1246 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1247 // We can calculate exact postion in this case
1248 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1250 // Create current item now and position as best we can.
1251 // Its position will be corrected when it becomes visible.
1252 listItem->setPosition(positionAt(currentIndex));
1256 if (visibleItems.isEmpty())
1257 averageSize = listItem->size();
1261 void QQuickListViewPrivate::updateAverage()
1263 if (!visibleItems.count())
1266 for (int i = 0; i < visibleItems.count(); ++i)
1267 sum += visibleItems.at(i)->size();
1268 averageSize = qRound(sum / visibleItems.count());
1271 qreal QQuickListViewPrivate::headerSize() const
1273 return header ? header->size() : 0.0;
1276 qreal QQuickListViewPrivate::footerSize() const
1278 return footer ? footer->size() : 0.0;
1281 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1286 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1288 return index == model->count()-1;
1291 void QQuickListViewPrivate::updateFooter()
1293 Q_Q(QQuickListView);
1294 bool created = false;
1296 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1299 footer = new FxListItemSG(item, q, true, true);
1303 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1304 if (visibleItems.count()) {
1305 qreal endPos = lastPosition();
1306 if (findLastVisibleIndex() == model->count()-1) {
1307 listItem->setPosition(endPos);
1309 qreal visiblePos = position() + q->height();
1310 if (endPos <= visiblePos || listItem->position() < endPos)
1311 listItem->setPosition(endPos);
1314 listItem->setPosition(visiblePos);
1318 emit q->footerItemChanged();
1321 void QQuickListViewPrivate::updateHeader()
1323 Q_Q(QQuickListView);
1324 bool created = false;
1326 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1329 header = new FxListItemSG(item, q, true, true);
1333 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1335 if (visibleItems.count()) {
1336 qreal startPos = originPosition();
1337 if (visibleIndex == 0) {
1338 listItem->setPosition(startPos - headerSize());
1340 if (position() <= startPos || listItem->position() > startPos - headerSize())
1341 listItem->setPosition(startPos - headerSize());
1344 listItem->setPosition(-headerSize());
1349 emit q->headerItemChanged();
1352 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1354 Q_Q(QQuickListView);
1355 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1356 if (!q->isComponentComplete())
1359 if (item != contentItem && (!highlight || item != highlight->item)) {
1360 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1361 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1363 // if visibleItems.first() has resized, adjust its pos since it is used to
1364 // position all subsequent items
1365 if (visibleItems.count() && item == visibleItems.first()->item) {
1366 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1367 if (orient == QQuickListView::Vertical) {
1368 qreal diff = newGeometry.height() - oldGeometry.height();
1369 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1370 listItem->setPosition(listItem->position() - diff, true);
1371 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1372 listItem->setPosition(listItem->position() + diff, true);
1374 qreal diff = newGeometry.width() - oldGeometry.width();
1375 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1376 listItem->setPosition(listItem->position() - diff, true);
1377 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1378 listItem->setPosition(listItem->position() + diff, true);
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 //----------------------------------------------------------------------------
1651 \qmlclass ListView QQuickListView
1652 \inqmlmodule QtQuick 2
1653 \ingroup qtquick-views
1655 \brief Provides a list view of items provided by a model
1657 A ListView displays data from models created from built-in QML elements like ListModel
1658 and XmlListModel, or custom model classes defined in C++ that inherit from
1661 A ListView has a \l model, which defines the data to be displayed, and
1662 a \l delegate, which defines how the data should be displayed. Items in a
1663 ListView are laid out horizontally or vertically. List views are inherently
1664 flickable because ListView inherits from \l Flickable.
1666 \section1 Example Usage
1668 The following example shows the definition of a simple list model defined
1669 in a file called \c ContactModel.qml:
1671 \snippet qml/listview/ContactModel.qml 0
1673 Another component can display this model data in a ListView, like this:
1675 \snippet qml/listview/listview.qml import
1677 \snippet qml/listview/listview.qml classdocs simple
1679 \image listview-simple.png
1681 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1682 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1683 the delegate is able to access the model's \c name and \c number data directly.
1685 An improved list view is shown below. The delegate is visually improved and is moved
1686 into a separate \c contactDelegate component.
1688 \snippet qml/listview/listview.qml classdocs advanced
1689 \image listview-highlight.png
1691 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1692 and \c focus is set to \c true to enable keyboard navigation for the list view.
1693 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1695 Delegates are instantiated as needed and may be destroyed at any time.
1696 State should \e never be stored in a delegate.
1698 ListView attaches a number of properties to the root item of the delegate, for example
1699 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1700 this attached property directly as \c ListView.isCurrentItem, while the child
1701 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1703 \snippet qml/listview/listview.qml isCurrentItem
1705 \note Views do not enable \e clip automatically. If the view
1706 is not clipped by another item or the screen, it will be necessary
1707 to set \e {clip: true} in order to have the out of view items clipped
1711 \section1 ListView layouts
1713 The layout of the items in a ListView can be controlled by these properties:
1716 \li \l orientation - controls whether items flow horizontally or vertically.
1717 This value can be either Qt.Horizontal or Qt.Vertical.
1718 \li \l layoutDirection - controls the horizontal layout direction for a
1719 horizontally-oriented view: that is, whether items are laid out from the left side of
1720 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1721 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1722 view: that is, whether items are laid out from the top of the view down towards the bottom of
1723 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1726 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1727 table below shows the different layouts that a ListView can have, depending on the values of
1728 the properties listed above.
1733 \bold ListViews with Qt.Vertical orientation
1736 \image listview-layout-toptobottom.png
1738 \image listview-layout-bottomtotop.png
1741 \bold ListViews with Qt.Horizontal orientation
1744 \image listview-layout-lefttoright.png
1746 \image listview-layout-righttoleft.png
1749 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1751 QQuickListView::QQuickListView(QQuickItem *parent)
1752 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1756 QQuickListView::~QQuickListView()
1761 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1762 This attached property is true if this delegate is the current item; otherwise false.
1764 It is attached to each instance of the delegate.
1766 This property may be used to adjust the appearance of the current item, for example:
1768 \snippet qml/listview/listview.qml isCurrentItem
1772 \qmlattachedproperty ListView QtQuick2::ListView::view
1773 This attached property holds the view that manages this delegate instance.
1775 It is attached to each instance of the delegate.
1779 \qmlattachedproperty string QtQuick2::ListView::previousSection
1780 This attached property holds the section of the previous element.
1782 It is attached to each instance of the delegate.
1784 The section is evaluated using the \l {ListView::section.property}{section} properties.
1788 \qmlattachedproperty string QtQuick2::ListView::nextSection
1789 This attached property holds the section of the next element.
1791 It is attached to each instance of the delegate.
1793 The section is evaluated using the \l {ListView::section.property}{section} properties.
1797 \qmlattachedproperty string QtQuick2::ListView::section
1798 This attached property holds the section of this element.
1800 It is attached to each instance of the delegate.
1802 The section is evaluated using the \l {ListView::section.property}{section} properties.
1806 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1808 This attached property holds whether the delegate may be destroyed. It
1809 is attached to each instance of the delegate. The default value is false.
1811 It is sometimes necessary to delay the destruction of an item
1812 until an animation completes. The example delegate below ensures that the
1813 animation completes before the item is removed from the list.
1815 \snippet qml/listview/listview.qml delayRemove
1817 If a \l remove transition has been specified, it will not be applied until
1818 delayRemove is returned to \c false.
1822 \qmlattachedsignal QtQuick2::ListView::onAdd()
1823 This attached signal handler is called immediately after an item is added to the view.
1825 If an \l add transition is specified, it is applied immediately after
1826 this signal handler is called.
1830 \qmlattachedsignal QtQuick2::ListView::onRemove()
1831 This attached handler is called immediately before an item is removed from the view.
1833 If a \l remove transition has been specified, it is applied after
1834 this signal handler is called, providing that delayRemove is false.
1838 \qmlproperty model QtQuick2::ListView::model
1839 This property holds the model providing data for the list.
1841 The model provides the set of data that is used to create the items
1842 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1843 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1844 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1846 \sa {qmlmodels}{Data Models}
1850 \qmlproperty Component QtQuick2::ListView::delegate
1852 The delegate provides a template defining each item instantiated by the view.
1853 The index is exposed as an accessible \c index property. Properties of the
1854 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1856 The number of elements in the delegate has a direct effect on the
1857 flicking performance of the view. If at all possible, place functionality
1858 that is not needed for the normal display of the delegate in a \l Loader which
1859 can load additional elements when needed.
1861 The ListView will lay out the items based on the size of the root item
1864 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1867 \note Delegates are instantiated as needed and may be destroyed at any time.
1868 State should \e never be stored in a delegate.
1871 \qmlproperty int QtQuick2::ListView::currentIndex
1872 \qmlproperty Item QtQuick2::ListView::currentItem
1874 The \c currentIndex property holds the index of the current item, and
1875 \c currentItem holds the current item. Setting the currentIndex to -1
1876 will clear the highlight and set currentItem to null.
1878 If highlightFollowsCurrentItem is \c true, setting either of these
1879 properties will smoothly scroll the ListView so that the current
1880 item becomes visible.
1882 Note that the position of the current item
1883 may only be approximate until it becomes visible in the view.
1887 \qmlproperty Item QtQuick2::ListView::highlightItem
1889 This holds the highlight item created from the \l highlight component.
1891 The \c highlightItem is managed by the view unless
1892 \l highlightFollowsCurrentItem is set to false.
1894 \sa highlight, highlightFollowsCurrentItem
1898 \qmlproperty int QtQuick2::ListView::count
1899 This property holds the number of items in the view.
1903 \qmlproperty Component QtQuick2::ListView::highlight
1904 This property holds the component to use as the highlight.
1906 An instance of the highlight component is created for each list.
1907 The geometry of the resulting component instance is managed by the list
1908 so as to stay with the current item, unless the highlightFollowsCurrentItem
1911 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1915 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1916 This property holds whether the highlight is managed by the view.
1918 If this property is true (the default value), the highlight is moved smoothly
1919 to follow the current item. Otherwise, the
1920 highlight is not moved by the view, and any movement must be implemented
1923 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1925 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1927 Note that the highlight animation also affects the way that the view
1928 is scrolled. This is because the view moves to maintain the
1929 highlight within the preferred highlight range (or visible viewport).
1931 \sa highlight, highlightMoveVelocity
1933 //###Possibly rename these properties, since they are very useful even without a highlight?
1935 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1936 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1937 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1939 These properties define the preferred range of the highlight (for the current item)
1940 within the view. The \c preferredHighlightBegin value must be less than the
1941 \c preferredHighlightEnd value.
1943 These properties affect the position of the current item when the list is scrolled.
1944 For example, if the currently selected item should stay in the middle of the
1945 list when the view is scrolled, set the \c preferredHighlightBegin and
1946 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1947 item would be. If the \c currentItem is changed programmatically, the list will
1948 automatically scroll so that the current item is in the middle of the view.
1949 Furthermore, the behavior of the current item index will occur whether or not a
1952 Valid values for \c highlightRangeMode are:
1955 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1956 However, the highlight can move outside of the range at the ends of the list or due
1957 to mouse interaction.
1958 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1959 The current item changes if a keyboard or mouse action would cause the highlight to move
1960 outside of the range.
1961 \li ListView.NoHighlightRange - this is the default value.
1964 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1966 Q_D(QQuickListView);
1967 if (d->autoHighlight != autoHighlight) {
1968 if (!autoHighlight) {
1969 if (d->highlightPosAnimator)
1970 d->highlightPosAnimator->stop();
1971 if (d->highlightSizeAnimator)
1972 d->highlightSizeAnimator->stop();
1974 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1979 \qmlproperty real QtQuick2::ListView::spacing
1981 This property holds the spacing between items.
1983 The default value is 0.
1985 qreal QQuickListView::spacing() const
1987 Q_D(const QQuickListView);
1991 void QQuickListView::setSpacing(qreal spacing)
1993 Q_D(QQuickListView);
1994 if (spacing != d->spacing) {
1995 d->spacing = spacing;
1996 d->forceLayout = true;
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 elements 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 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2206 return d->sectionCriteria;
2210 \qmlproperty string QtQuick2::ListView::currentSection
2211 This property holds the section that is currently at the beginning of the view.
2213 QString QQuickListView::currentSection() const
2215 Q_D(const QQuickListView);
2216 return d->currentSection;
2220 \qmlproperty real QtQuick2::ListView::highlightMoveVelocity
2221 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2222 \qmlproperty real QtQuick2::ListView::highlightResizeVelocity
2223 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2225 These properties control the speed of the move and resize animations for the
2228 \l highlightFollowsCurrentItem must be true for these properties
2231 The default value for the velocity properties is 400 pixels/second.
2232 The default value for the duration properties is -1, i.e. the
2233 highlight will take as much time as necessary to move at the set speed.
2235 These properties have the same characteristics as a SmoothedAnimation.
2237 \sa highlightFollowsCurrentItem
2239 qreal QQuickListView::highlightMoveVelocity() const
2241 Q_D(const QQuickListView);
2242 return d->highlightMoveVelocity;
2245 void QQuickListView::setHighlightMoveVelocity(qreal speed)
2247 Q_D(QQuickListView);
2248 if (d->highlightMoveVelocity != speed) {
2249 d->highlightMoveVelocity = speed;
2250 if (d->highlightPosAnimator)
2251 d->highlightPosAnimator->velocity = d->highlightMoveVelocity;
2252 emit highlightMoveVelocityChanged();
2256 void QQuickListView::setHighlightMoveDuration(int duration)
2258 Q_D(QQuickListView);
2259 if (d->highlightMoveDuration != duration) {
2260 if (d->highlightPosAnimator)
2261 d->highlightPosAnimator->userDuration = duration;
2262 QQuickItemView::setHighlightMoveDuration(duration);
2266 qreal QQuickListView::highlightResizeVelocity() const
2268 Q_D(const QQuickListView);
2269 return d->highlightResizeVelocity;
2272 void QQuickListView::setHighlightResizeVelocity(qreal speed)
2274 Q_D(QQuickListView);
2275 if (d->highlightResizeVelocity != speed) {
2276 d->highlightResizeVelocity = speed;
2277 if (d->highlightSizeAnimator)
2278 d->highlightSizeAnimator->velocity = d->highlightResizeVelocity;
2279 emit highlightResizeVelocityChanged();
2283 int QQuickListView::highlightResizeDuration() const
2285 Q_D(const QQuickListView);
2286 return d->highlightResizeDuration;
2289 void QQuickListView::setHighlightResizeDuration(int duration)
2291 Q_D(QQuickListView);
2292 if (d->highlightResizeDuration != duration) {
2293 d->highlightResizeDuration = duration;
2294 if (d->highlightSizeAnimator)
2295 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2296 emit highlightResizeDurationChanged();
2301 \qmlproperty enumeration QtQuick2::ListView::snapMode
2303 This property determines how the view scrolling will settle following a drag or flick.
2304 The possible values are:
2307 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2308 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2310 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2311 visible item at the time the mouse button is released. This mode is particularly
2312 useful for moving one page at a time.
2315 \c snapMode does not affect the \l currentIndex. To update the
2316 \l currentIndex as the list is moved, set \l highlightRangeMode
2317 to \c ListView.StrictlyEnforceRange.
2319 \sa highlightRangeMode
2321 QQuickListView::SnapMode QQuickListView::snapMode() const
2323 Q_D(const QQuickListView);
2327 void QQuickListView::setSnapMode(SnapMode mode)
2329 Q_D(QQuickListView);
2330 if (d->snapMode != mode) {
2332 emit snapModeChanged();
2338 \qmlproperty Component QtQuick2::ListView::footer
2339 This property holds the component to use as the footer.
2341 An instance of the footer component is created for each view. The
2342 footer is positioned at the end of the view, after any items.
2344 \sa header, footerItem
2349 \qmlproperty Component QtQuick2::ListView::header
2350 This property holds the component to use as the header.
2352 An instance of the header component is created for each view. The
2353 header is positioned at the beginning of the view, before any items.
2355 \sa footer, headertem
2359 \qmlproperty Item QtQuick2::ListView::headerItem
2360 This holds the header item created from the \l header component.
2362 An instance of the header component is created for each view. The
2363 header is positioned at the beginning of the view, before any items.
2365 \sa header, footerItem
2369 \qmlproperty Item QtQuick2::ListView::footerItem
2370 This holds the footer item created from the \l footer component.
2372 An instance of the footer component is created for each view. The
2373 footer is positioned at the end of the view, after any items.
2375 \sa footer, headerItem
2379 \qmlproperty Transition QtQuick2::ListView::populate
2381 This property holds the transition to apply to the items that are initially created
2384 It is applied to all items that are created when:
2387 \li The view is first created
2388 \li The view's \l model changes
2389 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2392 For example, here is a view that specifies such a transition:
2397 populate: Transition {
2398 NumberAnimation { properties: "x,y"; duration: 1000 }
2403 When the view is initialized, the view will create all the necessary items for the view,
2404 then animate them to their correct positions within the view over one second.
2406 For more details and examples on how to use view transitions, see the ViewTransition
2409 \sa add, ViewTransition
2413 \qmlproperty Transition QtQuick2::ListView::add
2415 This property holds the transition to apply to items that are added to the view.
2417 For example, here is a view that specifies such a transition:
2423 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2428 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2429 to its final x,y position within the view, over one second. The transition only applies to
2430 the new items that are added to the view; it does not apply to the items below that are
2431 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2432 or \l addDisplaced properties.
2434 For more details and examples on how to use view transitions, see the ViewTransition
2437 \note This transition is not applied to the items that are created when the view is initially
2438 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2441 \sa addDisplaced, populate, ViewTransition
2445 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2447 This property holds the transition to apply to items within the view that are displaced by
2448 the addition of other items to the view.
2450 For example, here is a view that specifies such a transition:
2455 addDisplaced: Transition {
2456 NumberAnimation { properties: "x,y"; duration: 1000 }
2461 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2462 them to move down (or sideways, if horizontally orientated) within the view. As this
2463 displacement occurs, the items' movement to their new x,y positions within the view will be
2464 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2465 the new item that has been added to the view; to animate the added items, set the \l add
2468 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2469 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2470 if it is not necessary to specify different transitions depending on whether an item is displaced
2471 by an add, move or remove operation, consider setting the \l displaced property instead.
2473 For more details and examples on how to use view transitions, see the ViewTransition
2476 \note This transition is not applied to the items that are created when the view is initially
2477 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2480 \sa displaced, add, populate, ViewTransition
2484 \qmlproperty Transition QtQuick2::ListView::move
2486 This property holds the transition to apply to items in the view that are being moved due
2487 to a move operation in the view's \l model.
2489 For example, here is a view that specifies such a transition:
2495 NumberAnimation { properties: "x,y"; duration: 1000 }
2500 Whenever the \l model performs a move operation to move a particular set of indexes, the
2501 respective items in the view will be animated to their new positions in the view over one
2502 second. The transition only applies to the items that are the subject of the move operation
2503 in the model; it does not apply to items below them that are displaced by the move operation.
2504 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2506 For more details and examples on how to use view transitions, see the ViewTransition
2509 \sa moveDisplaced, ViewTransition
2513 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2515 This property holds the transition to apply to items that are displaced by a move operation in
2516 the view's \l model.
2518 For example, here is a view that specifies such a transition:
2523 moveDisplaced: Transition {
2524 NumberAnimation { properties: "x,y"; duration: 1000 }
2529 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2530 between the source and destination indexes of the move operation are displaced, causing them
2531 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2532 displacement occurs, the items' movement to their new x,y positions within the view will be
2533 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2534 the items that are the actual subjects of the move operation; to animate the moved items, set
2535 the \l move property.
2537 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2538 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2539 if it is not necessary to specify different transitions depending on whether an item is displaced
2540 by an add, move or remove operation, consider setting the \l displaced property instead.
2542 For more details and examples on how to use view transitions, see the ViewTransition
2545 \sa displaced, move, ViewTransition
2549 \qmlproperty Transition QtQuick2::ListView::remove
2551 This property holds the transition to apply to items that are removed from the view.
2553 For example, here is a view that specifies such a transition:
2558 remove: Transition {
2560 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2561 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2567 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2568 over one second, and in parallel will also change its opacity to 0. The transition
2569 only applies to the items that are removed from the view; it does not apply to the items below
2570 them that are displaced by the removal of the items. To animate the displaced items, set the
2571 \l displaced or \l removeDisplaced properties.
2573 Note that by the time the transition is applied, the item has already been removed from the
2574 model; any references to the model data for the removed index will not be valid.
2576 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2577 remove transition will not be applied until \l delayRemove becomes false again.
2579 For more details and examples on how to use view transitions, see the ViewTransition
2582 \sa removeDisplaced, ViewTransition
2586 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2588 This property holds the transition to apply to items in the view that are displaced by the
2589 removal of other items in the view.
2591 For example, here is a view that specifies such a transition:
2596 removeDisplaced: Transition {
2597 NumberAnimation { properties: "x,y"; duration: 1000 }
2602 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2603 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2604 displacement occurs, the items' movement to their new x,y positions within the view will be
2605 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2606 the item that has actually been removed from the view; to animate the removed items, set the
2609 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2610 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2611 if it is not necessary to specify different transitions depending on whether an item is displaced
2612 by an add, move or remove operation, consider setting the \l displaced property instead.
2614 For more details and examples on how to use view transitions, see the ViewTransition
2617 \sa displaced, remove, ViewTransition
2621 \qmlproperty Transition QtQuick2::ListView::displaced
2622 This property holds the generic transition to apply to items that have been displaced by
2623 any model operation that affects the view.
2625 This is a convenience for specifying the generic transition to be applied to any items
2626 that are displaced by an add, move or remove operation, without having to specify the
2627 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2628 is a view that specifies a displaced transition:
2633 displaced: Transition {
2634 NumberAnimation { properties: "x,y"; duration: 1000 }
2639 When any item is added, moved or removed within the above view, the items below it are
2640 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2641 view. As this displacement occurs, the items' movement to their new x,y positions within
2642 the view will be animated by a NumberAnimation over one second, as specified.
2644 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2645 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2646 instead of the generic displaced transition when the relevant operation occurs, providing that
2647 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2648 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2650 For more details and examples on how to use view transitions, see the ViewTransition
2653 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2656 void QQuickListView::viewportMoved(Qt::Orientations orient)
2658 Q_D(QQuickListView);
2659 QQuickItemView::viewportMoved(orient);
2662 // Recursion can occur due to refill changing the content size.
2663 if (d->inViewportMoved)
2665 d->inViewportMoved = true;
2668 if (d->isBottomToTop())
2669 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2671 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2673 if (d->isRightToLeft())
2674 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2676 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2679 d->refillOrLayout();
2681 // Set visibility of items to eliminate cost of items outside the visible area.
2682 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2683 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2684 for (int i = 0; i < d->visibleItems.count(); ++i) {
2685 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2686 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2689 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2691 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2692 d->moveReason = QQuickListViewPrivate::Mouse;
2693 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2694 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2695 // reposition highlight
2696 qreal pos = d->highlight->position();
2697 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2698 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2699 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2700 if (pos < viewPos + d->highlightRangeStart)
2701 pos = viewPos + d->highlightRangeStart;
2702 if (pos != d->highlight->position()) {
2703 d->highlightPosAnimator->stop();
2704 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2706 d->updateHighlight();
2709 // update current index
2710 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2711 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2712 d->updateCurrent(snapItem->index);
2717 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2718 d->inFlickCorrection = true;
2719 // Near an end and it seems that the extent has changed?
2720 // Recalculate the flick so that we don't end up in an odd position.
2721 if (yflick() && !d->vData.inOvershoot) {
2722 if (d->vData.velocity > 0) {
2723 const qreal minY = minYExtent();
2724 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2725 && minY != d->vData.flickTarget)
2726 d->flickY(-d->vData.smoothVelocity.value());
2727 } else if (d->vData.velocity < 0) {
2728 const qreal maxY = maxYExtent();
2729 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2730 && maxY != d->vData.flickTarget)
2731 d->flickY(-d->vData.smoothVelocity.value());
2735 if (xflick() && !d->hData.inOvershoot) {
2736 if (d->hData.velocity > 0) {
2737 const qreal minX = minXExtent();
2738 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2739 && minX != d->hData.flickTarget)
2740 d->flickX(-d->hData.smoothVelocity.value());
2741 } else if (d->hData.velocity < 0) {
2742 const qreal maxX = maxXExtent();
2743 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2744 && maxX != d->hData.flickTarget)
2745 d->flickX(-d->hData.smoothVelocity.value());
2748 d->inFlickCorrection = false;
2750 if (d->sectionCriteria) {
2751 d->updateCurrentSection();
2752 d->updateStickySections();
2754 d->inViewportMoved = false;
2757 void QQuickListView::keyPressEvent(QKeyEvent *event)
2759 Q_D(QQuickListView);
2760 if (d->model && d->model->count() && d->interactive) {
2761 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2762 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2763 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2764 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2765 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2766 decrementCurrentIndex();
2769 } else if (d->wrap) {
2773 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2774 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2775 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2776 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2777 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2778 incrementCurrentIndex();
2781 } else if (d->wrap) {
2788 QQuickItemView::keyPressEvent(event);
2791 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2793 Q_D(QQuickListView);
2794 if (d->isRightToLeft()) {
2795 // maintain position relative to the right edge
2796 int dx = newGeometry.width() - oldGeometry.width();
2797 setContentX(contentX() - dx);
2798 } else if (d->isBottomToTop()) {
2799 // maintain position relative to the bottom edge
2800 int dy = newGeometry.height() - oldGeometry.height();
2801 setContentY(contentY() - dy);
2803 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2806 void QQuickListView::initItem(int index, QQuickItem *item)
2808 QQuickItemView::initItem(index, item);
2809 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
2810 qmlAttachedPropertiesObject<QQuickListView>(item));
2812 attached->setView(this);
2817 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2819 Increments the current index. The current index will wrap
2820 if keyNavigationWraps is true and it is currently at the end.
2821 This method has no effect if the \l count is zero.
2823 \b Note: methods should only be called after the Component has completed.
2825 void QQuickListView::incrementCurrentIndex()
2827 Q_D(QQuickListView);
2828 int count = d->model ? d->model->count() : 0;
2829 if (count && (currentIndex() < count - 1 || d->wrap)) {
2830 d->moveReason = QQuickListViewPrivate::SetIndex;
2831 int index = currentIndex()+1;
2832 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2837 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2839 Decrements the current index. The current index will wrap
2840 if keyNavigationWraps is true and it is currently at the beginning.
2841 This method has no effect if the \l count is zero.
2843 \b Note: methods should only be called after the Component has completed.
2845 void QQuickListView::decrementCurrentIndex()
2847 Q_D(QQuickListView);
2848 int count = d->model ? d->model->count() : 0;
2849 if (count && (currentIndex() > 0 || d->wrap)) {
2850 d->moveReason = QQuickListViewPrivate::SetIndex;
2851 int index = currentIndex()-1;
2852 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2856 void QQuickListView::updateSections()
2858 Q_D(QQuickListView);
2859 if (isComponentComplete() && d->model) {
2860 QList<QByteArray> roles;
2861 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2862 roles << d->sectionCriteria->property().toUtf8();
2863 d->model->setWatchedRoles(roles);
2864 d->updateSections();
2866 d->forceLayout = true;
2872 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2874 int modelIndex = change.index;
2875 int count = change.count;
2877 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2878 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2881 int i = visibleItems.count() - 1;
2882 while (i > 0 && visibleItems.at(i)->index == -1)
2884 if (i == 0 && visibleItems.first()->index == -1) {
2885 // there are no visible items except items marked for removal
2886 index = visibleItems.count();
2887 } else if (visibleItems.at(i)->index + 1 == modelIndex
2888 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2889 // Special case of appending an item to the model.
2890 index = visibleItems.count();
2892 if (modelIndex < visibleIndex) {
2893 // Insert before visible items
2894 visibleIndex += count;
2895 for (int i = 0; i < visibleItems.count(); ++i) {
2896 FxViewItem *item = visibleItems.at(i);
2897 if (item->index != -1 && item->index >= modelIndex)
2898 item->index += count;
2905 // index can be the next item past the end of the visible items list (i.e. appended)
2907 if (visibleItems.count()) {
2908 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2909 : visibleItems.last()->endPosition()+spacing;
2912 int prevVisibleCount = visibleItems.count();
2913 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2914 // Insert items before the visible item.
2915 int insertionIdx = index;
2917 int from = tempPos - buffer;
2919 for (i = count-1; i >= 0; --i) {
2920 if (pos > from && insertionIdx < visibleIndex) {
2921 // item won't be visible, just note the size for repositioning
2922 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2923 pos -= averageSize + spacing;
2925 // item is before first visible e.g. in cache buffer
2926 FxViewItem *item = 0;
2927 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2928 item->index = modelIndex + i;
2930 item = createItem(modelIndex + i);
2934 visibleItems.insert(insertionIdx, item);
2935 if (insertionIdx == 0)
2936 insertResult->changedFirstItem = true;
2937 if (!change.isMove()) {
2938 addedItems->append(item);
2939 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2941 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2942 pos -= item->size() + spacing;
2948 int to = buffer+tempPos+size();
2949 for (i = 0; i < count && pos <= to; ++i) {
2950 FxViewItem *item = 0;
2951 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2952 item->index = modelIndex + i;
2953 bool newItem = !item;
2955 item = createItem(modelIndex + i);
2959 visibleItems.insert(index, item);
2961 insertResult->changedFirstItem = true;
2962 if (change.isMove()) {
2963 // we know this is a move target, since move displaced items that are
2964 // shuffled into view due to a move would be added in refill()
2965 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2966 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2968 addedItems->append(item);
2969 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2971 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2972 pos += item->size() + spacing;
2977 for (; index < visibleItems.count(); ++index) {
2978 FxViewItem *item = visibleItems.at(index);
2979 if (item->index != -1) {
2980 item->index += count;
2981 if (change.isMove())
2982 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2984 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2988 updateVisibleIndex();
2990 return visibleItems.count() > prevVisibleCount;
2993 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2995 Q_UNUSED(insertionResult);
3000 int markerItemIndex = -1;
3001 for (int i=0; i<visibleItems.count(); i++) {
3002 if (visibleItems[i]->index == afterModelIndex) {
3003 markerItemIndex = i;
3007 if (markerItemIndex < 0)
3010 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
3011 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
3012 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
3014 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
3015 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
3016 if (!listItem->transitionScheduledOrRunning()) {
3017 qreal pos = listItem->position();
3018 listItem->setPosition(pos - sizeRemoved);
3019 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
3020 listItem->setPosition(pos);
3026 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
3028 Positions the view such that the \a index is at the position specified by
3032 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
3033 \li ListView.Center - position item in the center of the view.
3034 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3035 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
3036 bring the item into view.
3037 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
3038 the view the item is positioned at the top (or left for horizontal orientation) of the view.
3041 If positioning the view at \a index would cause empty space to be displayed at
3042 the beginning or end of the view, the view will be positioned at the boundary.
3044 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3045 at a particular index. This is unreliable since removing items from the start
3046 of the list does not cause all other items to be repositioned, and because
3047 the actual start of the view can vary based on the size of the delegates.
3048 The correct way to bring an item into view is with \c positionViewAtIndex.
3050 \b Note: methods should only be called after the Component has completed. To position
3051 the view at startup, this method should be called by Component.onCompleted. For
3052 example, to position the view at the end:
3055 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3060 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
3061 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
3063 Positions the view at the beginning or end, taking into account any header or footer.
3065 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3066 at a particular index. This is unreliable since removing items from the start
3067 of the list does not cause all other items to be repositioned, and because
3068 the actual start of the view can vary based on the size of the delegates.
3070 \b Note: methods should only be called after the Component has completed. To position
3071 the view at startup, this method should be called by Component.onCompleted. For
3072 example, to position the view at the end on startup:
3075 Component.onCompleted: positionViewAtEnd()
3080 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3082 Returns the index of the visible item containing the point \a x, \a y in content
3083 coordinates. If there is no item at the point specified, or the item is
3084 not visible -1 is returned.
3086 If the item is outside the visible area, -1 is returned, regardless of
3087 whether an item will exist at that point when scrolled into view.
3089 \b Note: methods should only be called after the Component has completed.
3093 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3095 Returns the visible item containing the point \a x, \a y in content
3096 coordinates. If there is no item at the point specified, or the item is
3097 not visible null is returned.
3099 If the item is outside the visible area, null is returned, regardless of
3100 whether an item will exist at that point when scrolled into view.
3102 \b Note: methods should only be called after the Component has completed.
3105 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3107 return new QQuickListViewAttached(obj);