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 updateSectionCriteria();
114 virtual void updateSections();
115 QQuickItem *getSectionItem(const QString §ion);
116 void releaseSectionItem(QQuickItem *item);
117 void releaseSectionItems();
118 void updateInlineSection(FxListItemSG *);
119 void updateCurrentSection();
120 void updateStickySections();
122 virtual qreal headerSize() const;
123 virtual qreal footerSize() const;
124 virtual bool showHeaderForIndex(int index) const;
125 virtual bool showFooterForIndex(int index) const;
126 virtual void updateHeader();
127 virtual void updateFooter();
129 virtual void changedVisibleIndex(int newIndex);
130 virtual void initializeCurrentItem();
132 void updateAverage();
134 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
135 virtual void fixupPosition();
136 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
137 virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
138 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
140 QQuickListView::Orientation orient;
144 QQuickListView::SnapMode snapMode;
146 QSmoothedAnimation *highlightPosAnimator;
147 QSmoothedAnimation *highlightSizeAnimator;
148 qreal highlightMoveVelocity;
149 qreal highlightResizeVelocity;
150 int highlightResizeDuration;
152 QQuickViewSection *sectionCriteria;
153 QString currentSection;
154 static const int sectionCacheSize = 5;
155 QQuickItem *sectionCache[sectionCacheSize];
156 QQuickItem *currentSectionItem;
157 QString currentStickySection;
158 QQuickItem *nextSectionItem;
159 QString nextStickySection;
160 QString lastVisibleSection;
164 bool correctFlick : 1;
165 bool inFlickCorrection : 1;
167 QQuickListViewPrivate()
168 : orient(QQuickListView::Vertical)
170 , averageSize(100.0), spacing(0.0)
171 , snapMode(QQuickListView::NoSnap)
172 , highlightPosAnimator(0), highlightSizeAnimator(0)
173 , highlightMoveVelocity(400), highlightResizeVelocity(400), highlightResizeDuration(-1)
174 , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
175 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
177 ~QQuickListViewPrivate() {
178 delete highlightPosAnimator;
179 delete highlightSizeAnimator;
182 friend class QQuickViewSection;
185 //----------------------------------------------------------------------------
187 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
188 : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
189 , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
193 void QQuickViewSection::setProperty(const QString &property)
195 if (property != m_property) {
196 m_property = property;
197 emit propertyChanged();
198 // notify view that the contents of the sections must be recalculated
199 m_view->updateSectionCriteria();
203 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
205 if (criteria != m_criteria) {
206 m_criteria = criteria;
207 emit criteriaChanged();
208 // notify view that the contents of the sections must be recalculated
209 m_view->updateSectionCriteria();
213 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
215 if (delegate != m_delegate) {
217 m_view->releaseSectionItems();
218 m_delegate = delegate;
219 emit delegateChanged();
220 m_view->forceLayoutPolish();
224 QString QQuickViewSection::sectionString(const QString &value)
226 if (m_criteria == FirstCharacter)
227 return value.isEmpty() ? QString() : value.at(0);
232 void QQuickViewSection::setLabelPositioning(int l)
234 if (m_labelPositioning != l) {
235 m_labelPositioning = l;
236 emit labelPositioningChanged();
237 m_view->forceLayoutPolish();
241 //----------------------------------------------------------------------------
243 class FxListItemSG : public FxViewItem
246 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
247 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
249 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
250 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
256 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
257 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
261 inline QQuickItem *section() const {
262 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
264 void setSection(QQuickItem *s) {
266 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
267 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
270 qreal position() const {
272 if (view->orientation() == QQuickListView::Vertical)
273 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
275 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
277 return itemPosition();
280 qreal itemPosition() const {
281 if (view->orientation() == QQuickListView::Vertical)
282 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
284 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
288 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
290 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
292 qreal itemSize() const {
293 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
295 qreal sectionSize() const {
297 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
300 qreal endPosition() const {
301 if (view->orientation() == QQuickListView::Vertical) {
302 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
304 : itemY() + item->height());
306 return (view->effectiveLayoutDirection() == Qt::RightToLeft
308 : itemX() + item->width());
311 void setPosition(qreal pos, bool immediate = false) {
312 // position the section immediately even if there is a transition
314 if (view->orientation() == QQuickListView::Vertical) {
315 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
316 section()->setY(-section()->height()-pos);
318 section()->setY(pos);
320 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
321 section()->setX(-section()->width()-pos);
323 section()->setX(pos);
326 moveTo(pointForPosition(pos), immediate);
328 void setSize(qreal size) {
329 if (view->orientation() == QQuickListView::Vertical)
330 item->setHeight(size);
332 item->setWidth(size);
334 bool contains(qreal x, qreal y) const {
335 return (x >= itemX() && x < itemX() + item->width() &&
336 y >= itemY() && y < itemY() + item->height());
339 QQuickListView *view;
342 QPointF pointForPosition(qreal pos) const {
343 if (view->orientation() == QQuickListView::Vertical) {
344 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
346 pos += section()->height();
347 return QPointF(itemX(), -item->height() - pos);
350 pos += section()->height();
351 return QPointF(itemX(), pos);
354 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
356 pos += section()->width();
357 return QPointF(-item->width() - pos, itemY());
360 pos += section()->width();
361 return QPointF(pos, itemY());
367 //----------------------------------------------------------------------------
369 bool QQuickListViewPrivate::isContentFlowReversed() const
371 return isRightToLeft() || isBottomToTop();
374 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
376 return static_cast<Qt::Orientation>(orient);
379 bool QQuickListViewPrivate::isRightToLeft() const
381 Q_Q(const QQuickListView);
382 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
385 bool QQuickListViewPrivate::isBottomToTop() const
387 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
390 // Returns the item before modelIndex, if created.
391 // May return an item marked for removal.
392 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
394 if (modelIndex < visibleIndex)
398 while (idx < visibleItems.count()) {
399 FxViewItem *item = visibleItems.at(idx);
400 if (item->index != -1)
401 lastIndex = item->index;
402 if (item->index == modelIndex)
403 return visibleItems.at(idx-1);
406 if (lastIndex == modelIndex-1)
407 return visibleItems.last();
411 void QQuickListViewPrivate::setPosition(qreal pos)
414 if (orient == QQuickListView::Vertical) {
416 q->QQuickFlickable::setContentY(-pos-size());
418 q->QQuickFlickable::setContentY(pos);
421 q->QQuickFlickable::setContentX(-pos-size());
423 q->QQuickFlickable::setContentX(pos);
427 qreal QQuickListViewPrivate::originPosition() const
430 if (!visibleItems.isEmpty()) {
431 pos = (*visibleItems.constBegin())->position();
432 if (visibleIndex > 0)
433 pos -= visibleIndex * (averageSize + spacing);
438 qreal QQuickListViewPrivate::lastPosition() const
441 if (!visibleItems.isEmpty()) {
442 int invisibleCount = visibleItems.count() - visibleIndex;
443 for (int i = visibleItems.count()-1; i >= 0; --i) {
444 if (visibleItems.at(i)->index != -1) {
445 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
449 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
450 } else if (model && model->count()) {
451 pos = (model->count() * averageSize + (model->count()-1) * spacing);
456 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
458 if (FxViewItem *item = visibleItem(modelIndex)) {
459 return item->position();
461 if (!visibleItems.isEmpty()) {
462 if (modelIndex < visibleIndex) {
463 int count = visibleIndex - modelIndex;
465 if (modelIndex == currentIndex && currentItem) {
466 cs = currentItem->size() + spacing;
469 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
471 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
472 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
478 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
480 if (FxViewItem *item = visibleItem(modelIndex))
481 return item->endPosition();
482 if (!visibleItems.isEmpty()) {
483 if (modelIndex < visibleIndex) {
484 int count = visibleIndex - modelIndex;
485 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
487 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
488 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
494 QString QQuickListViewPrivate::sectionAt(int modelIndex)
496 if (FxViewItem *item = visibleItem(modelIndex))
497 return item->attached->section();
500 if (sectionCriteria) {
501 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
502 section = sectionCriteria->sectionString(propValue);
508 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
510 if (FxViewItem *snapItem = snapItemAt(pos))
511 return snapItem->position();
512 if (visibleItems.count()) {
513 qreal firstPos = (*visibleItems.constBegin())->position();
514 qreal endPos = (*(--visibleItems.constEnd()))->position();
515 if (pos < firstPos) {
516 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
517 } else if (pos > endPos)
518 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
520 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
523 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
525 FxViewItem *snapItem = 0;
526 qreal prevItemSize = 0;
527 for (int i = 0; i < visibleItems.count(); ++i) {
528 FxViewItem *item = visibleItems.at(i);
529 if (item->index == -1)
531 qreal itemTop = item->position();
532 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
534 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
536 prevItemSize = item->size();
541 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
543 visiblePos = positionAt(newIndex);
544 visibleIndex = newIndex;
547 void QQuickListViewPrivate::init()
549 QQuickItemViewPrivate::init();
550 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
553 void QQuickListViewPrivate::clear()
555 for (int i = 0; i < sectionCacheSize; ++i) {
556 delete sectionCache[i];
560 releaseSectionItem(currentSectionItem);
561 currentSectionItem = 0;
562 releaseSectionItem(nextSectionItem);
564 lastVisibleSection = QString();
565 QQuickItemViewPrivate::clear();
568 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
572 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
573 listItem->index = modelIndex;
575 // initialise attached properties
576 if (sectionCriteria) {
577 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
578 listItem->attached->setSection(sectionCriteria->sectionString(propValue));
579 if (modelIndex > 0) {
580 if (FxViewItem *item = itemBefore(modelIndex))
581 listItem->attached->setPrevSection(item->attached->section());
583 listItem->attached->setPrevSection(sectionAt(modelIndex-1));
585 if (modelIndex < model->count()-1) {
586 if (FxViewItem *item = visibleItem(modelIndex+1))
587 listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section());
589 listItem->attached->setNextSection(sectionAt(modelIndex+1));
596 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
598 QQuickItemViewPrivate::initializeViewItem(item);
600 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
601 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
603 if (sectionCriteria && sectionCriteria->delegate()) {
604 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
605 updateInlineSection(static_cast<FxListItemSG*>(item));
609 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
614 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
616 bool released = QQuickItemViewPrivate::releaseItem(item);
617 if (released && att && att->m_sectionItem) {
618 // We hold no more references to this item
621 if (!sectionCache[i]) {
622 sectionCache[i] = att->m_sectionItem;
623 sectionCache[i]->setVisible(false);
624 att->m_sectionItem = 0;
628 } while (i < sectionCacheSize);
629 delete att->m_sectionItem;
630 att->m_sectionItem = 0;
636 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
638 qreal itemEnd = visiblePos;
639 if (visibleItems.count()) {
640 visiblePos = (*visibleItems.constBegin())->position();
641 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
644 int modelIndex = findLastVisibleIndex();
645 bool haveValidItems = modelIndex >= 0;
646 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
648 if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
649 || bufferTo < visiblePos - averageSize - spacing)) {
650 // We've jumped more than a page. Estimate which items are now
651 // visible and fill from there.
652 int count = (fillFrom - itemEnd) / (averageSize + spacing);
653 int newModelIdx = qBound(0, modelIndex + count, model->count());
654 count = newModelIdx - modelIndex;
656 for (int i = 0; i < visibleItems.count(); ++i)
657 releaseItem(visibleItems.at(i));
658 visibleItems.clear();
659 modelIndex = newModelIdx;
660 visibleIndex = modelIndex;
661 visiblePos = itemEnd + count * (averageSize + spacing);
662 itemEnd = visiblePos;
666 bool changed = false;
667 FxListItemSG *item = 0;
669 while (modelIndex < model->count() && pos <= fillTo) {
670 #ifdef DEBUG_DELEGATE_LIFECYCLE
671 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
673 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
675 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
676 item->setPosition(pos, true);
677 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
678 pos += item->size() + spacing;
679 visibleItems.append(item);
684 if (doBuffer && requestedIndex != -1) // already waiting for an item
687 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
688 #ifdef DEBUG_DELEGATE_LIFECYCLE
689 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
691 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
694 visiblePos -= item->size() + spacing;
695 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
696 item->setPosition(visiblePos, true);
697 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
698 visibleItems.prepend(item);
705 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
707 FxViewItem *item = 0;
708 bool changed = false;
710 // Remove items from the start of the view.
711 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
712 // removed, otherwise a zero-sized item is infinitely added and removed over and
715 while (visibleItems.count() > 1 && index < visibleItems.count()
716 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
717 if (item->attached->delayRemove())
720 if (item->size() > 0) {
721 #ifdef DEBUG_DELEGATE_LIFECYCLE
722 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
724 // remove this item and all zero-sized items before it
726 if (item->index != -1)
728 visibleItems.removeAt(index);
729 if (item->transitionScheduledOrRunning()) {
730 #ifdef DEBUG_DELEGATE_LIFECYCLE
731 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
733 item->releaseAfterTransition = true;
734 releasePendingTransition.append(item);
740 item = visibleItems.at(--index);
748 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
749 if (item->attached->delayRemove())
751 #ifdef DEBUG_DELEGATE_LIFECYCLE
752 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
754 visibleItems.removeLast();
755 if (item->transitionScheduledOrRunning()) {
756 #ifdef DEBUG_DELEGATE_LIFECYCLE
757 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
759 item->releaseAfterTransition = true;
760 releasePendingTransition.append(item);
770 void QQuickListViewPrivate::visibleItemsChanged()
772 if (visibleItems.count())
773 visiblePos = (*visibleItems.constBegin())->position();
775 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
776 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
780 updateCurrentSection();
781 updateUnrequestedPositions();
784 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
786 if (!visibleItems.isEmpty()) {
787 const qreal from = isContentFlowReversed() ? -position() - size() : position();
788 const qreal to = isContentFlowReversed() ? -position() : position() + size();
790 FxViewItem *firstItem = *visibleItems.constBegin();
791 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
792 qreal sum = firstItem->size();
793 qreal pos = firstItem->position() + firstItem->size() + spacing;
794 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
796 for (int i=1; i < visibleItems.count(); ++i) {
797 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
798 if (item->index >= fromModelIndex) {
799 item->setPosition(pos);
800 item->setVisible(item->endPosition() >= from && item->position() <= to);
802 pos += item->size() + spacing;
804 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
806 averageSize = qRound(sum / visibleItems.count());
808 // move current item if it is not a visible item.
809 if (currentIndex >= 0 && currentItem && !fixedCurrent)
810 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
812 updateCurrentSection();
813 updateStickySections();
817 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
819 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
822 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
825 qreal pos = position();
826 if (orient == QQuickListView::Vertical) {
827 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
829 item->setY(-positionAt(index)-item->height());
831 item->setY(positionAt(index));
834 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
836 item->setX(-positionAt(index)-item->width());
838 item->setX(positionAt(index));
843 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
845 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
846 item->setPosition(pos);
849 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
851 if (!visibleItems.count())
853 qreal diff = forwards - backwards;
854 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
857 void QQuickListViewPrivate::createHighlight()
860 bool changed = false;
862 if (trackedItem == highlight)
867 delete highlightPosAnimator;
868 delete highlightSizeAnimator;
869 highlightPosAnimator = 0;
870 highlightSizeAnimator = 0;
876 QQuickItem *item = createHighlightItem();
878 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
881 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
882 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
884 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
885 highlightPosAnimator = new QSmoothedAnimation;
886 highlightPosAnimator->target = QQmlProperty(item, posProp);
887 highlightPosAnimator->velocity = highlightMoveVelocity;
888 highlightPosAnimator->userDuration = highlightMoveDuration;
890 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
891 highlightSizeAnimator = new QSmoothedAnimation;
892 highlightSizeAnimator->velocity = highlightResizeVelocity;
893 highlightSizeAnimator->userDuration = highlightResizeDuration;
894 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
896 highlight = newHighlight;
901 emit q->highlightItemChanged();
904 void QQuickListViewPrivate::updateHighlight()
906 applyPendingChanges();
908 if ((!currentItem && highlight) || (currentItem && !highlight))
910 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
911 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
912 // auto-update highlight
913 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
914 highlightPosAnimator->to = isContentFlowReversed()
915 ? -listItem->itemPosition()-listItem->itemSize()
916 : listItem->itemPosition();
917 highlightSizeAnimator->to = listItem->itemSize();
918 if (orient == QQuickListView::Vertical) {
919 if (highlight->item->width() == 0)
920 highlight->item->setWidth(currentItem->item->width());
922 if (highlight->item->height() == 0)
923 highlight->item->setHeight(currentItem->item->height());
926 highlightPosAnimator->restart();
927 highlightSizeAnimator->restart();
932 void QQuickListViewPrivate::resetHighlightPosition()
934 if (highlight && currentItem)
935 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
938 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
941 QQuickItem *sectionItem = 0;
942 int i = sectionCacheSize-1;
943 while (i >= 0 && !sectionCache[i])
946 sectionItem = sectionCache[i];
948 sectionItem->setVisible(true);
949 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
950 context->setContextProperty(QLatin1String("section"), section);
952 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
953 QQmlContext *context = new QQmlContext(
954 creationContext ? creationContext : qmlContext(q));
955 context->setContextProperty(QLatin1String("section"), section);
956 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
958 QQml_setParent_noEvent(context, nobj);
959 sectionItem = qobject_cast<QQuickItem *>(nobj);
963 sectionItem->setZ(2);
964 QQml_setParent_noEvent(sectionItem, contentItem);
965 sectionItem->setParentItem(contentItem);
970 sectionCriteria->delegate()->completeCreate();
976 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
982 if (!sectionCache[i]) {
983 sectionCache[i] = item;
984 sectionCache[i]->setVisible(false);
988 } while (i < sectionCacheSize);
993 void QQuickListViewPrivate::releaseSectionItems()
995 for (int i = 0; i < visibleItems.count(); ++i) {
996 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
997 if (listItem->section()) {
998 qreal pos = listItem->position();
999 releaseSectionItem(listItem->section());
1000 listItem->setSection(0);
1001 listItem->setPosition(pos);
1004 for (int i = 0; i < sectionCacheSize; ++i) {
1005 delete sectionCache[i];
1006 sectionCache[i] = 0;
1010 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
1012 if (!sectionCriteria || !sectionCriteria->delegate())
1014 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
1015 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
1016 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
1017 if (!listItem->section()) {
1018 qreal pos = listItem->position();
1019 listItem->setSection(getSectionItem(listItem->attached->m_section));
1020 listItem->setPosition(pos);
1022 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
1023 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1025 } else if (listItem->section()) {
1026 qreal pos = listItem->position();
1027 releaseSectionItem(listItem->section());
1028 listItem->setSection(0);
1029 listItem->setPosition(pos);
1033 void QQuickListViewPrivate::updateStickySections()
1035 if (!sectionCriteria
1036 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1039 bool isFlowReversed = isContentFlowReversed();
1040 qreal viewPos = isFlowReversed ? -position()-size() : position();
1041 QQuickItem *sectionItem = 0;
1042 QQuickItem *lastSectionItem = 0;
1044 while (index < visibleItems.count()) {
1045 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1046 // Find the current section header and last visible section header
1047 // and hide them if they will overlap a static section header.
1048 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1049 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1051 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1052 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1054 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1055 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1056 section->setVisible(visBot && visTop);
1057 if (visTop && !sectionItem)
1058 sectionItem = section;
1059 if (isFlowReversed) {
1060 if (-sectionPos <= viewPos + size())
1061 lastSectionItem = section;
1063 if (sectionPos + sectionSize < viewPos + size())
1064 lastSectionItem = section;
1070 // Current section header
1071 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1072 if (!currentSectionItem) {
1073 currentSectionItem = getSectionItem(currentSection);
1074 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1075 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1076 context->setContextProperty(QLatin1String("section"), currentSection);
1078 currentStickySection = currentSection;
1079 if (!currentSectionItem)
1082 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1083 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1085 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1086 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1088 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1089 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1092 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1094 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1095 if (orient == QQuickListView::Vertical)
1096 currentSectionItem->setY(pos);
1098 currentSectionItem->setX(pos);
1099 } else if (currentSectionItem) {
1100 releaseSectionItem(currentSectionItem);
1101 currentSectionItem = 0;
1104 // Next section footer
1105 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1106 if (!nextSectionItem) {
1107 nextSectionItem = getSectionItem(nextSection);
1108 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1109 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1110 context->setContextProperty(QLatin1String("section"), nextSection);
1112 nextStickySection = nextSection;
1113 if (!nextSectionItem)
1116 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1117 nextSectionItem->setVisible(!nextSection.isEmpty());
1118 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1119 if (lastSectionItem) {
1120 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1121 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1124 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1125 if (orient == QQuickListView::Vertical)
1126 nextSectionItem->setY(pos);
1128 nextSectionItem->setX(pos);
1129 } else if (nextSectionItem) {
1130 releaseSectionItem(nextSectionItem);
1131 nextSectionItem = 0;
1135 void QQuickListViewPrivate::updateSections()
1137 Q_Q(QQuickListView);
1138 if (!q->isComponentComplete())
1141 QQuickItemViewPrivate::updateSections();
1143 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1144 QString prevSection;
1145 if (visibleIndex > 0)
1146 prevSection = sectionAt(visibleIndex-1);
1147 QQuickListViewAttached *prevAtt = 0;
1149 for (int i = 0; i < visibleItems.count(); ++i) {
1150 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1151 attached->setPrevSection(prevSection);
1152 if (visibleItems.at(i)->index != -1) {
1153 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1154 attached->setSection(sectionCriteria->sectionString(propValue));
1155 idx = visibleItems.at(i)->index;
1157 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1159 prevAtt->setNextSection(attached->section());
1160 prevSection = attached->section();
1164 if (idx > 0 && idx < model->count()-1)
1165 prevAtt->setNextSection(sectionAt(idx+1));
1167 prevAtt->setNextSection(QString());
1171 lastVisibleSection = QString();
1174 void QQuickListViewPrivate::updateCurrentSection()
1176 Q_Q(QQuickListView);
1177 if (!sectionCriteria || visibleItems.isEmpty()) {
1178 if (!currentSection.isEmpty()) {
1179 currentSection.clear();
1180 emit q->currentSectionChanged();
1184 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1185 qreal sectionThreshold = position();
1186 if (currentSectionItem && !inlineSections)
1187 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1189 int modelIndex = visibleIndex;
1190 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1191 if (visibleItems.at(index)->index != -1)
1192 modelIndex = visibleItems.at(index)->index;
1196 QString newSection = currentSection;
1197 if (index < visibleItems.count())
1198 newSection = visibleItems.at(index)->attached->section();
1200 newSection = (*visibleItems.constBegin())->attached->section();
1201 if (newSection != currentSection) {
1202 currentSection = newSection;
1203 updateStickySections();
1204 emit q->currentSectionChanged();
1207 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1208 // Don't want to scan for next section on every movement, so remember
1209 // the last section in the visible area and only scan for the next
1210 // section when that changes. Clearing lastVisibleSection will also
1212 QString lastSection = currentSection;
1213 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1214 if (nextSectionItem && !inlineSections)
1215 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1216 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1217 if (visibleItems.at(index)->index != -1)
1218 modelIndex = visibleItems.at(index)->index;
1219 lastSection = visibleItems.at(index)->attached->section();
1223 if (lastVisibleSection != lastSection) {
1224 nextSection = QString();
1225 lastVisibleSection = lastSection;
1226 for (int i = modelIndex; i < itemCount; ++i) {
1227 QString section = sectionAt(i);
1228 if (section != lastSection) {
1229 nextSection = section;
1230 updateStickySections();
1238 void QQuickListViewPrivate::initializeCurrentItem()
1240 QQuickItemViewPrivate::initializeCurrentItem();
1243 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1245 // don't reposition the item if it is already in the visibleItems list
1246 FxViewItem *actualItem = visibleItem(currentIndex);
1248 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1249 // We can calculate exact postion in this case
1250 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1252 // Create current item now and position as best we can.
1253 // Its position will be corrected when it becomes visible.
1254 listItem->setPosition(positionAt(currentIndex));
1258 if (visibleItems.isEmpty())
1259 averageSize = listItem->size();
1263 void QQuickListViewPrivate::updateAverage()
1265 if (!visibleItems.count())
1268 for (int i = 0; i < visibleItems.count(); ++i)
1269 sum += visibleItems.at(i)->size();
1270 averageSize = qRound(sum / visibleItems.count());
1273 qreal QQuickListViewPrivate::headerSize() const
1275 return header ? header->size() : 0.0;
1278 qreal QQuickListViewPrivate::footerSize() const
1280 return footer ? footer->size() : 0.0;
1283 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1288 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1290 return index == model->count()-1;
1293 void QQuickListViewPrivate::updateFooter()
1295 Q_Q(QQuickListView);
1296 bool created = false;
1298 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1301 footer = new FxListItemSG(item, q, true, true);
1305 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1306 if (visibleItems.count()) {
1307 qreal endPos = lastPosition();
1308 if (findLastVisibleIndex() == model->count()-1) {
1309 listItem->setPosition(endPos);
1311 qreal visiblePos = position() + q->height();
1312 if (endPos <= visiblePos || listItem->position() < endPos)
1313 listItem->setPosition(endPos);
1316 listItem->setPosition(visiblePos);
1320 emit q->footerItemChanged();
1323 void QQuickListViewPrivate::updateHeader()
1325 Q_Q(QQuickListView);
1326 bool created = false;
1328 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1331 header = new FxListItemSG(item, q, true, true);
1335 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1337 if (visibleItems.count()) {
1338 qreal startPos = originPosition();
1339 if (visibleIndex == 0) {
1340 listItem->setPosition(startPos - headerSize());
1342 if (position() <= startPos || listItem->position() > startPos - headerSize())
1343 listItem->setPosition(startPos - headerSize());
1346 listItem->setPosition(-headerSize());
1351 emit q->headerItemChanged();
1354 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1356 Q_Q(QQuickListView);
1357 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1358 if (!q->isComponentComplete())
1361 if (item != contentItem && (!highlight || item != highlight->item)) {
1362 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1363 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1365 // if visibleItems.first() has resized, adjust its pos since it is used to
1366 // position all subsequent items
1367 if (visibleItems.count() && item == visibleItems.first()->item) {
1368 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1369 if (orient == QQuickListView::Vertical) {
1370 qreal diff = newGeometry.height() - oldGeometry.height();
1371 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1372 listItem->setPosition(listItem->position() - diff, true);
1373 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1374 listItem->setPosition(listItem->position() + diff, true);
1376 qreal diff = newGeometry.width() - oldGeometry.width();
1377 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1378 listItem->setPosition(listItem->position() - diff, true);
1379 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1380 listItem->setPosition(listItem->position() + diff, true);
1383 forceLayoutPolish();
1388 void QQuickListViewPrivate::fixupPosition()
1390 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1391 || snapMode != QQuickListView::NoSnap)
1393 if (orient == QQuickListView::Vertical)
1399 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1401 if ((orient == QQuickListView::Horizontal && &data == &vData)
1402 || (orient == QQuickListView::Vertical && &data == &hData))
1405 correctFlick = false;
1406 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1407 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1409 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1411 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1412 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1413 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1414 // if we've been dragged < averageSize/2 then bias towards the next item
1415 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1417 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1418 bias = averageSize/2;
1419 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1420 bias = -averageSize/2;
1421 if (isContentFlowReversed())
1423 tempPosition -= bias;
1425 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1426 if (!topItem && strictHighlightRange && currentItem) {
1427 // StrictlyEnforceRange always keeps an item in range
1429 topItem = currentItem;
1431 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1432 if (!bottomItem && strictHighlightRange && currentItem) {
1433 // StrictlyEnforceRange always keeps an item in range
1435 bottomItem = currentItem;
1438 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1439 if (topItem && (isInBounds || strictHighlightRange)) {
1440 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1441 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1443 if (isContentFlowReversed())
1444 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1446 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1448 } else if (bottomItem && isInBounds) {
1449 if (isContentFlowReversed())
1450 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1452 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1454 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1458 qreal dist = qAbs(data.move + pos);
1460 timeline.reset(data.move);
1461 if (fixupMode != Immediate) {
1462 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1463 data.fixingUp = true;
1465 timeline.set(data.move, -pos);
1467 vTime = timeline.time();
1469 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1471 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1472 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1473 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1474 if (viewPos > pos - highlightRangeStart)
1475 viewPos = pos - highlightRangeStart;
1476 if (isContentFlowReversed())
1477 viewPos = -viewPos-size();
1479 timeline.reset(data.move);
1480 if (viewPos != position()) {
1481 if (fixupMode != Immediate) {
1482 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1483 data.fixingUp = true;
1485 timeline.set(data.move, -viewPos);
1488 vTime = timeline.time();
1490 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1492 data.inOvershoot = false;
1496 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1497 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1499 data.fixingUp = false;
1501 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1502 correctFlick = true;
1503 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1505 qreal maxDistance = 0;
1506 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1508 // -ve velocity means list is moving up/left
1510 if (data.move.value() < minExtent) {
1511 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1512 // if we've been dragged < averageSize/2 then bias towards the next item
1513 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1514 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1515 if (isContentFlowReversed())
1517 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1518 maxDistance = qAbs(data.flickTarget - data.move.value());
1519 velocity = maxVelocity;
1521 maxDistance = qAbs(minExtent - data.move.value());
1524 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1525 data.flickTarget = minExtent;
1527 if (data.move.value() > maxExtent) {
1528 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1529 // if we've been dragged < averageSize/2 then bias towards the next item
1530 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1531 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1532 if (isContentFlowReversed())
1534 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1535 maxDistance = qAbs(data.flickTarget - data.move.value());
1536 velocity = -maxVelocity;
1538 maxDistance = qAbs(maxExtent - data.move.value());
1541 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1542 data.flickTarget = maxExtent;
1544 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1545 if (maxDistance > 0 || overShoot) {
1546 // These modes require the list to stop exactly on an item boundary.
1547 // The initial flick will estimate the boundary to stop on.
1548 // Since list items can have variable sizes, the boundary will be
1549 // reevaluated and adjusted as we approach the boundary.
1551 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1557 if (!hData.flicking && !vData.flicking) {
1558 // the initial flick - estimate boundary
1559 qreal accel = deceleration;
1561 overshootDist = 0.0;
1562 // + averageSize/4 to encourage moving at least one item in the flick direction
1563 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1564 if (maxDistance > 0)
1565 dist = qMin(dist, maxDistance);
1568 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1569 if (snapMode != QQuickListView::SnapOneItem) {
1570 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1571 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1573 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1575 if (data.flickTarget >= minExtent) {
1576 overshootDist = overShootDistance(vSize);
1577 data.flickTarget += overshootDist;
1578 } else if (data.flickTarget <= maxExtent) {
1579 overshootDist = overShootDistance(vSize);
1580 data.flickTarget -= overshootDist;
1583 qreal adjDist = -data.flickTarget + data.move.value();
1584 if (qAbs(adjDist) > qAbs(dist)) {
1585 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1586 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1595 accel = v2 / (2.0f * qAbs(dist));
1596 } else if (overShoot) {
1597 data.flickTarget = data.move.value() - dist;
1598 if (data.flickTarget >= minExtent) {
1599 overshootDist = overShootDistance(vSize);
1600 data.flickTarget += overshootDist;
1601 } else if (data.flickTarget <= maxExtent) {
1602 overshootDist = overShootDistance(vSize);
1603 data.flickTarget -= overshootDist;
1606 timeline.reset(data.move);
1607 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1608 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1609 correctFlick = true;
1612 // reevaluate the target boundary.
1613 qreal newtarget = data.flickTarget;
1614 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1615 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1616 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1617 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1619 if (velocity < 0 && newtarget <= maxExtent)
1620 newtarget = maxExtent - overshootDist;
1621 else if (velocity > 0 && newtarget >= minExtent)
1622 newtarget = minExtent + overshootDist;
1623 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1624 if (qAbs(velocity) < MinimumFlickVelocity)
1625 correctFlick = false;
1628 data.flickTarget = newtarget;
1629 qreal dist = -newtarget + data.move.value();
1630 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1631 correctFlick = false;
1632 timeline.reset(data.move);
1633 fixup(data, minExtent, maxExtent);
1636 timeline.reset(data.move);
1637 timeline.accelDistance(data.move, v, -dist);
1638 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1642 correctFlick = false;
1643 timeline.reset(data.move);
1644 fixup(data, minExtent, maxExtent);
1649 //----------------------------------------------------------------------------
1652 \qmlclass ListView QQuickListView
1653 \inqmlmodule QtQuick 2
1654 \ingroup qtquick-views
1656 \brief Provides a list view of items provided by a model
1658 A ListView displays data from models created from built-in QML elements like ListModel
1659 and XmlListModel, or custom model classes defined in C++ that inherit from
1662 A ListView has a \l model, which defines the data to be displayed, and
1663 a \l delegate, which defines how the data should be displayed. Items in a
1664 ListView are laid out horizontally or vertically. List views are inherently
1665 flickable because ListView inherits from \l Flickable.
1667 \section1 Example Usage
1669 The following example shows the definition of a simple list model defined
1670 in a file called \c ContactModel.qml:
1672 \snippet qml/listview/ContactModel.qml 0
1674 Another component can display this model data in a ListView, like this:
1676 \snippet qml/listview/listview.qml import
1678 \snippet qml/listview/listview.qml classdocs simple
1680 \image listview-simple.png
1682 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1683 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1684 the delegate is able to access the model's \c name and \c number data directly.
1686 An improved list view is shown below. The delegate is visually improved and is moved
1687 into a separate \c contactDelegate component.
1689 \snippet qml/listview/listview.qml classdocs advanced
1690 \image listview-highlight.png
1692 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1693 and \c focus is set to \c true to enable keyboard navigation for the list view.
1694 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1696 Delegates are instantiated as needed and may be destroyed at any time.
1697 State should \e never be stored in a delegate.
1699 ListView attaches a number of properties to the root item of the delegate, for example
1700 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1701 this attached property directly as \c ListView.isCurrentItem, while the child
1702 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1704 \snippet qml/listview/listview.qml isCurrentItem
1706 \note Views do not enable \e clip automatically. If the view
1707 is not clipped by another item or the screen, it will be necessary
1708 to set \e {clip: true} in order to have the out of view items clipped
1712 \section1 ListView layouts
1714 The layout of the items in a ListView can be controlled by these properties:
1717 \li \l orientation - controls whether items flow horizontally or vertically.
1718 This value can be either Qt.Horizontal or Qt.Vertical.
1719 \li \l layoutDirection - controls the horizontal layout direction for a
1720 horizontally-oriented view: that is, whether items are laid out from the left side of
1721 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1722 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1723 view: that is, whether items are laid out from the top of the view down towards the bottom of
1724 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1727 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1728 table below shows the different layouts that a ListView can have, depending on the values of
1729 the properties listed above.
1734 \bold ListViews with Qt.Vertical orientation
1737 \image listview-layout-toptobottom.png
1739 \image listview-layout-bottomtotop.png
1742 \bold ListViews with Qt.Horizontal orientation
1745 \image listview-layout-lefttoright.png
1747 \image listview-layout-righttoleft.png
1750 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1752 QQuickListView::QQuickListView(QQuickItem *parent)
1753 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1757 QQuickListView::~QQuickListView()
1762 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1763 This attached property is true if this delegate is the current item; otherwise false.
1765 It is attached to each instance of the delegate.
1767 This property may be used to adjust the appearance of the current item, for example:
1769 \snippet qml/listview/listview.qml isCurrentItem
1773 \qmlattachedproperty ListView QtQuick2::ListView::view
1774 This attached property holds the view that manages this delegate instance.
1776 It is attached to each instance of the delegate.
1780 \qmlattachedproperty string QtQuick2::ListView::previousSection
1781 This attached property holds the section of the previous element.
1783 It is attached to each instance of the delegate.
1785 The section is evaluated using the \l {ListView::section.property}{section} properties.
1789 \qmlattachedproperty string QtQuick2::ListView::nextSection
1790 This attached property holds the section of the next element.
1792 It is attached to each instance of the delegate.
1794 The section is evaluated using the \l {ListView::section.property}{section} properties.
1798 \qmlattachedproperty string QtQuick2::ListView::section
1799 This attached property holds the section of this element.
1801 It is attached to each instance of the delegate.
1803 The section is evaluated using the \l {ListView::section.property}{section} properties.
1807 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1809 This attached property holds whether the delegate may be destroyed. It
1810 is attached to each instance of the delegate. The default value is false.
1812 It is sometimes necessary to delay the destruction of an item
1813 until an animation completes. The example delegate below ensures that the
1814 animation completes before the item is removed from the list.
1816 \snippet qml/listview/listview.qml delayRemove
1818 If a \l remove transition has been specified, it will not be applied until
1819 delayRemove is returned to \c false.
1823 \qmlattachedsignal QtQuick2::ListView::onAdd()
1824 This attached signal handler is called immediately after an item is added to the view.
1826 If an \l add transition is specified, it is applied immediately after
1827 this signal handler is called.
1831 \qmlattachedsignal QtQuick2::ListView::onRemove()
1832 This attached handler is called immediately before an item is removed from the view.
1834 If a \l remove transition has been specified, it is applied after
1835 this signal handler is called, providing that delayRemove is false.
1839 \qmlproperty model QtQuick2::ListView::model
1840 This property holds the model providing data for the list.
1842 The model provides the set of data that is used to create the items
1843 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1844 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1845 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1847 \sa {qmlmodels}{Data Models}
1851 \qmlproperty Component QtQuick2::ListView::delegate
1853 The delegate provides a template defining each item instantiated by the view.
1854 The index is exposed as an accessible \c index property. Properties of the
1855 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1857 The number of elements in the delegate has a direct effect on the
1858 flicking performance of the view. If at all possible, place functionality
1859 that is not needed for the normal display of the delegate in a \l Loader which
1860 can load additional elements when needed.
1862 The ListView will lay out the items based on the size of the root item
1865 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1868 \note Delegates are instantiated as needed and may be destroyed at any time.
1869 State should \e never be stored in a delegate.
1872 \qmlproperty int QtQuick2::ListView::currentIndex
1873 \qmlproperty Item QtQuick2::ListView::currentItem
1875 The \c currentIndex property holds the index of the current item, and
1876 \c currentItem holds the current item. Setting the currentIndex to -1
1877 will clear the highlight and set currentItem to null.
1879 If highlightFollowsCurrentItem is \c true, setting either of these
1880 properties will smoothly scroll the ListView so that the current
1881 item becomes visible.
1883 Note that the position of the current item
1884 may only be approximate until it becomes visible in the view.
1888 \qmlproperty Item QtQuick2::ListView::highlightItem
1890 This holds the highlight item created from the \l highlight component.
1892 The \c highlightItem is managed by the view unless
1893 \l highlightFollowsCurrentItem is set to false.
1895 \sa highlight, highlightFollowsCurrentItem
1899 \qmlproperty int QtQuick2::ListView::count
1900 This property holds the number of items in the view.
1904 \qmlproperty Component QtQuick2::ListView::highlight
1905 This property holds the component to use as the highlight.
1907 An instance of the highlight component is created for each list.
1908 The geometry of the resulting component instance is managed by the list
1909 so as to stay with the current item, unless the highlightFollowsCurrentItem
1912 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1916 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1917 This property holds whether the highlight is managed by the view.
1919 If this property is true (the default value), the highlight is moved smoothly
1920 to follow the current item. Otherwise, the
1921 highlight is not moved by the view, and any movement must be implemented
1924 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1926 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1928 Note that the highlight animation also affects the way that the view
1929 is scrolled. This is because the view moves to maintain the
1930 highlight within the preferred highlight range (or visible viewport).
1932 \sa highlight, highlightMoveVelocity
1934 //###Possibly rename these properties, since they are very useful even without a highlight?
1936 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1937 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1938 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1940 These properties define the preferred range of the highlight (for the current item)
1941 within the view. The \c preferredHighlightBegin value must be less than the
1942 \c preferredHighlightEnd value.
1944 These properties affect the position of the current item when the list is scrolled.
1945 For example, if the currently selected item should stay in the middle of the
1946 list when the view is scrolled, set the \c preferredHighlightBegin and
1947 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1948 item would be. If the \c currentItem is changed programmatically, the list will
1949 automatically scroll so that the current item is in the middle of the view.
1950 Furthermore, the behavior of the current item index will occur whether or not a
1953 Valid values for \c highlightRangeMode are:
1956 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1957 However, the highlight can move outside of the range at the ends of the list or due
1958 to mouse interaction.
1959 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1960 The current item changes if a keyboard or mouse action would cause the highlight to move
1961 outside of the range.
1962 \li ListView.NoHighlightRange - this is the default value.
1965 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1967 Q_D(QQuickListView);
1968 if (d->autoHighlight != autoHighlight) {
1969 if (!autoHighlight) {
1970 if (d->highlightPosAnimator)
1971 d->highlightPosAnimator->stop();
1972 if (d->highlightSizeAnimator)
1973 d->highlightSizeAnimator->stop();
1975 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1980 \qmlproperty real QtQuick2::ListView::spacing
1982 This property holds the spacing between items.
1984 The default value is 0.
1986 qreal QQuickListView::spacing() const
1988 Q_D(const QQuickListView);
1992 void QQuickListView::setSpacing(qreal spacing)
1994 Q_D(QQuickListView);
1995 if (spacing != d->spacing) {
1996 d->spacing = spacing;
1997 d->forceLayoutPolish();
1998 emit spacingChanged();
2003 \qmlproperty enumeration QtQuick2::ListView::orientation
2004 This property holds the orientation of the list.
2009 \li ListView.Horizontal - Items are laid out horizontally
2010 \li ListView.Vertical (default) - Items are laid out vertically
2015 \li Horizontal orientation:
2016 \image ListViewHorizontal.png
2019 \li Vertical orientation:
2020 \image listview-highlight.png
2023 QQuickListView::Orientation QQuickListView::orientation() const
2025 Q_D(const QQuickListView);
2029 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2031 Q_D(QQuickListView);
2032 if (d->orient != orientation) {
2033 d->orient = orientation;
2034 if (d->orient == Vertical) {
2035 setContentWidth(-1);
2036 setFlickableDirection(VerticalFlick);
2039 setContentHeight(-1);
2040 setFlickableDirection(HorizontalFlick);
2044 emit orientationChanged();
2049 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
2050 This property holds the layout direction of a horizontally-oriented list.
2055 \li Qt.LeftToRight (default) - Items will be laid out from left to right.
2056 \li Qt.RightToLeft - Items will be laid out from right to let.
2059 Setting this property has no effect if the \l orientation is Qt.Vertical.
2061 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2066 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
2067 This property holds the effective layout direction of a horizontally-oriented list.
2069 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2070 the visual layout direction of the horizontal list will be mirrored. However, the
2071 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2073 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2078 \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
2079 This property holds the layout direction of a vertically-oriented list.
2084 \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
2085 \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
2088 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2090 \sa ListView::layoutDirection
2095 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
2096 This property holds whether the list wraps key navigation.
2098 If this is true, key navigation that would move the current item selection
2099 past the end of the list instead wraps around and moves the selection to
2100 the start of the list, and vice-versa.
2102 By default, key navigation is not wrapped.
2107 \qmlproperty int QtQuick2::ListView::cacheBuffer
2108 This property determines whether delegates are retained outside the
2109 visible area of the view.
2111 If this value is non-zero, the view may keep as many delegates
2112 instantiated as it can fit within the buffer specified. For example,
2113 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2114 set to 40, then up to 2 delegates above and 2 delegates below the visible
2115 area may be created/retained. The buffered delegates are created asynchronously,
2116 allowing creation to occur across multiple frames and reducing the
2117 likelihood of skipping frames. In order to improve painting performance
2118 delegates outside the visible area are not painted.
2120 The default value of this property is platform dependent, but will usually
2121 be a non-zero value.
2123 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2124 instantiated delegates.
2126 Setting this value can improve the smoothness of scrolling behavior at the expense
2127 of additional memory usage. It is not a substitute for creating efficient
2128 delegates; the fewer 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 return d->sectionCriteria;
2208 \qmlproperty string QtQuick2::ListView::currentSection
2209 This property holds the section that is currently at the beginning of the view.
2211 QString QQuickListView::currentSection() const
2213 Q_D(const QQuickListView);
2214 return d->currentSection;
2218 \qmlproperty real QtQuick2::ListView::highlightMoveVelocity
2219 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2220 \qmlproperty real QtQuick2::ListView::highlightResizeVelocity
2221 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2223 These properties control the speed of the move and resize animations for the
2226 \l highlightFollowsCurrentItem must be true for these properties
2229 The default value for the velocity properties is 400 pixels/second.
2230 The default value for the duration properties is -1, i.e. the
2231 highlight will take as much time as necessary to move at the set speed.
2233 These properties have the same characteristics as a SmoothedAnimation.
2235 \sa highlightFollowsCurrentItem
2237 qreal QQuickListView::highlightMoveVelocity() const
2239 Q_D(const QQuickListView);
2240 return d->highlightMoveVelocity;
2243 void QQuickListView::setHighlightMoveVelocity(qreal speed)
2245 Q_D(QQuickListView);
2246 if (d->highlightMoveVelocity != speed) {
2247 d->highlightMoveVelocity = speed;
2248 if (d->highlightPosAnimator)
2249 d->highlightPosAnimator->velocity = d->highlightMoveVelocity;
2250 emit highlightMoveVelocityChanged();
2254 void QQuickListView::setHighlightMoveDuration(int duration)
2256 Q_D(QQuickListView);
2257 if (d->highlightMoveDuration != duration) {
2258 if (d->highlightPosAnimator)
2259 d->highlightPosAnimator->userDuration = duration;
2260 QQuickItemView::setHighlightMoveDuration(duration);
2264 qreal QQuickListView::highlightResizeVelocity() const
2266 Q_D(const QQuickListView);
2267 return d->highlightResizeVelocity;
2270 void QQuickListView::setHighlightResizeVelocity(qreal speed)
2272 Q_D(QQuickListView);
2273 if (d->highlightResizeVelocity != speed) {
2274 d->highlightResizeVelocity = speed;
2275 if (d->highlightSizeAnimator)
2276 d->highlightSizeAnimator->velocity = d->highlightResizeVelocity;
2277 emit highlightResizeVelocityChanged();
2281 int QQuickListView::highlightResizeDuration() const
2283 Q_D(const QQuickListView);
2284 return d->highlightResizeDuration;
2287 void QQuickListView::setHighlightResizeDuration(int duration)
2289 Q_D(QQuickListView);
2290 if (d->highlightResizeDuration != duration) {
2291 d->highlightResizeDuration = duration;
2292 if (d->highlightSizeAnimator)
2293 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2294 emit highlightResizeDurationChanged();
2299 \qmlproperty enumeration QtQuick2::ListView::snapMode
2301 This property determines how the view scrolling will settle following a drag or flick.
2302 The possible values are:
2305 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2306 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2308 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2309 visible item at the time the mouse button is released. This mode is particularly
2310 useful for moving one page at a time.
2313 \c snapMode does not affect the \l currentIndex. To update the
2314 \l currentIndex as the list is moved, set \l highlightRangeMode
2315 to \c ListView.StrictlyEnforceRange.
2317 \sa highlightRangeMode
2319 QQuickListView::SnapMode QQuickListView::snapMode() const
2321 Q_D(const QQuickListView);
2325 void QQuickListView::setSnapMode(SnapMode mode)
2327 Q_D(QQuickListView);
2328 if (d->snapMode != mode) {
2330 emit snapModeChanged();
2336 \qmlproperty Component QtQuick2::ListView::footer
2337 This property holds the component to use as the footer.
2339 An instance of the footer component is created for each view. The
2340 footer is positioned at the end of the view, after any items.
2342 \sa header, footerItem
2347 \qmlproperty Component QtQuick2::ListView::header
2348 This property holds the component to use as the header.
2350 An instance of the header component is created for each view. The
2351 header is positioned at the beginning of the view, before any items.
2353 \sa footer, headertem
2357 \qmlproperty Item QtQuick2::ListView::headerItem
2358 This holds the header item created from the \l header component.
2360 An instance of the header component is created for each view. The
2361 header is positioned at the beginning of the view, before any items.
2363 \sa header, footerItem
2367 \qmlproperty Item QtQuick2::ListView::footerItem
2368 This holds the footer item created from the \l footer component.
2370 An instance of the footer component is created for each view. The
2371 footer is positioned at the end of the view, after any items.
2373 \sa footer, headerItem
2377 \qmlproperty Transition QtQuick2::ListView::populate
2379 This property holds the transition to apply to the items that are initially created
2382 It is applied to all items that are created when:
2385 \li The view is first created
2386 \li The view's \l model changes
2387 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2390 For example, here is a view that specifies such a transition:
2395 populate: Transition {
2396 NumberAnimation { properties: "x,y"; duration: 1000 }
2401 When the view is initialized, the view will create all the necessary items for the view,
2402 then animate them to their correct positions within the view over one second.
2404 For more details and examples on how to use view transitions, see the ViewTransition
2407 \sa add, ViewTransition
2411 \qmlproperty Transition QtQuick2::ListView::add
2413 This property holds the transition to apply to items that are added to the view.
2415 For example, here is a view that specifies such a transition:
2421 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2426 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2427 to its final x,y position within the view, over one second. The transition only applies to
2428 the new items that are added to the view; it does not apply to the items below that are
2429 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2430 or \l addDisplaced properties.
2432 For more details and examples on how to use view transitions, see the ViewTransition
2435 \note This transition is not applied to the items that are created when the view is initially
2436 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2439 \sa addDisplaced, populate, ViewTransition
2443 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2445 This property holds the transition to apply to items within the view that are displaced by
2446 the addition of other items to the view.
2448 For example, here is a view that specifies such a transition:
2453 addDisplaced: Transition {
2454 NumberAnimation { properties: "x,y"; duration: 1000 }
2459 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2460 them to move down (or sideways, if horizontally orientated) within the view. As this
2461 displacement occurs, the items' movement to their new x,y positions within the view will be
2462 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2463 the new item that has been added to the view; to animate the added items, set the \l add
2466 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2467 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2468 if it is not necessary to specify different transitions depending on whether an item is displaced
2469 by an add, move or remove operation, consider setting the \l displaced property instead.
2471 For more details and examples on how to use view transitions, see the ViewTransition
2474 \note This transition is not applied to the items that are created when the view is initially
2475 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2478 \sa displaced, add, populate, ViewTransition
2482 \qmlproperty Transition QtQuick2::ListView::move
2484 This property holds the transition to apply to items in the view that are being moved due
2485 to a move operation in the view's \l model.
2487 For example, here is a view that specifies such a transition:
2493 NumberAnimation { properties: "x,y"; duration: 1000 }
2498 Whenever the \l model performs a move operation to move a particular set of indexes, the
2499 respective items in the view will be animated to their new positions in the view over one
2500 second. The transition only applies to the items that are the subject of the move operation
2501 in the model; it does not apply to items below them that are displaced by the move operation.
2502 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2504 For more details and examples on how to use view transitions, see the ViewTransition
2507 \sa moveDisplaced, ViewTransition
2511 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2513 This property holds the transition to apply to items that are displaced by a move operation in
2514 the view's \l model.
2516 For example, here is a view that specifies such a transition:
2521 moveDisplaced: Transition {
2522 NumberAnimation { properties: "x,y"; duration: 1000 }
2527 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2528 between the source and destination indexes of the move operation are displaced, causing them
2529 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2530 displacement occurs, the items' movement to their new x,y positions within the view will be
2531 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2532 the items that are the actual subjects of the move operation; to animate the moved items, set
2533 the \l move property.
2535 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2536 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2537 if it is not necessary to specify different transitions depending on whether an item is displaced
2538 by an add, move or remove operation, consider setting the \l displaced property instead.
2540 For more details and examples on how to use view transitions, see the ViewTransition
2543 \sa displaced, move, ViewTransition
2547 \qmlproperty Transition QtQuick2::ListView::remove
2549 This property holds the transition to apply to items that are removed from the view.
2551 For example, here is a view that specifies such a transition:
2556 remove: Transition {
2558 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2559 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2565 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2566 over one second, and in parallel will also change its opacity to 0. The transition
2567 only applies to the items that are removed from the view; it does not apply to the items below
2568 them that are displaced by the removal of the items. To animate the displaced items, set the
2569 \l displaced or \l removeDisplaced properties.
2571 Note that by the time the transition is applied, the item has already been removed from the
2572 model; any references to the model data for the removed index will not be valid.
2574 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2575 remove transition will not be applied until \l delayRemove becomes false again.
2577 For more details and examples on how to use view transitions, see the ViewTransition
2580 \sa removeDisplaced, ViewTransition
2584 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2586 This property holds the transition to apply to items in the view that are displaced by the
2587 removal of other items in the view.
2589 For example, here is a view that specifies such a transition:
2594 removeDisplaced: Transition {
2595 NumberAnimation { properties: "x,y"; duration: 1000 }
2600 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2601 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2602 displacement occurs, the items' movement to their new x,y positions within the view will be
2603 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2604 the item that has actually been removed from the view; to animate the removed items, set the
2607 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2608 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2609 if it is not necessary to specify different transitions depending on whether an item is displaced
2610 by an add, move or remove operation, consider setting the \l displaced property instead.
2612 For more details and examples on how to use view transitions, see the ViewTransition
2615 \sa displaced, remove, ViewTransition
2619 \qmlproperty Transition QtQuick2::ListView::displaced
2620 This property holds the generic transition to apply to items that have been displaced by
2621 any model operation that affects the view.
2623 This is a convenience for specifying the generic transition to be applied to any items
2624 that are displaced by an add, move or remove operation, without having to specify the
2625 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2626 is a view that specifies a displaced transition:
2631 displaced: Transition {
2632 NumberAnimation { properties: "x,y"; duration: 1000 }
2637 When any item is added, moved or removed within the above view, the items below it are
2638 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2639 view. As this displacement occurs, the items' movement to their new x,y positions within
2640 the view will be animated by a NumberAnimation over one second, as specified.
2642 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2643 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2644 instead of the generic displaced transition when the relevant operation occurs, providing that
2645 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2646 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2648 For more details and examples on how to use view transitions, see the ViewTransition
2651 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2654 void QQuickListView::viewportMoved(Qt::Orientations orient)
2656 Q_D(QQuickListView);
2657 QQuickItemView::viewportMoved(orient);
2660 // Recursion can occur due to refill changing the content size.
2661 if (d->inViewportMoved)
2663 d->inViewportMoved = true;
2666 if (d->isBottomToTop())
2667 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2669 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2671 if (d->isRightToLeft())
2672 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2674 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2677 d->refillOrLayout();
2679 // Set visibility of items to eliminate cost of items outside the visible area.
2680 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2681 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2682 for (int i = 0; i < d->visibleItems.count(); ++i) {
2683 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2684 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2687 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2689 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2690 d->moveReason = QQuickListViewPrivate::Mouse;
2691 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2692 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2693 // reposition highlight
2694 qreal pos = d->highlight->position();
2695 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2696 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2697 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2698 if (pos < viewPos + d->highlightRangeStart)
2699 pos = viewPos + d->highlightRangeStart;
2700 if (pos != d->highlight->position()) {
2701 d->highlightPosAnimator->stop();
2702 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2704 d->updateHighlight();
2707 // update current index
2708 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2709 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2710 d->updateCurrent(snapItem->index);
2715 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2716 d->inFlickCorrection = true;
2717 // Near an end and it seems that the extent has changed?
2718 // Recalculate the flick so that we don't end up in an odd position.
2719 if (yflick() && !d->vData.inOvershoot) {
2720 if (d->vData.velocity > 0) {
2721 const qreal minY = minYExtent();
2722 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2723 && minY != d->vData.flickTarget)
2724 d->flickY(-d->vData.smoothVelocity.value());
2725 } else if (d->vData.velocity < 0) {
2726 const qreal maxY = maxYExtent();
2727 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2728 && maxY != d->vData.flickTarget)
2729 d->flickY(-d->vData.smoothVelocity.value());
2733 if (xflick() && !d->hData.inOvershoot) {
2734 if (d->hData.velocity > 0) {
2735 const qreal minX = minXExtent();
2736 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2737 && minX != d->hData.flickTarget)
2738 d->flickX(-d->hData.smoothVelocity.value());
2739 } else if (d->hData.velocity < 0) {
2740 const qreal maxX = maxXExtent();
2741 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2742 && maxX != d->hData.flickTarget)
2743 d->flickX(-d->hData.smoothVelocity.value());
2746 d->inFlickCorrection = false;
2748 if (d->sectionCriteria) {
2749 d->updateCurrentSection();
2750 d->updateStickySections();
2752 d->inViewportMoved = false;
2755 void QQuickListView::keyPressEvent(QKeyEvent *event)
2757 Q_D(QQuickListView);
2758 if (d->model && d->model->count() && d->interactive) {
2759 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2760 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2761 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2762 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2763 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2764 decrementCurrentIndex();
2767 } else if (d->wrap) {
2771 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2772 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2773 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2774 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2775 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2776 incrementCurrentIndex();
2779 } else if (d->wrap) {
2786 QQuickItemView::keyPressEvent(event);
2789 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2791 Q_D(QQuickListView);
2792 if (d->isRightToLeft()) {
2793 // maintain position relative to the right edge
2794 int dx = newGeometry.width() - oldGeometry.width();
2795 setContentX(contentX() - dx);
2796 } else if (d->isBottomToTop()) {
2797 // maintain position relative to the bottom edge
2798 int dy = newGeometry.height() - oldGeometry.height();
2799 setContentY(contentY() - dy);
2801 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2804 void QQuickListView::initItem(int index, QQuickItem *item)
2806 QQuickItemView::initItem(index, item);
2807 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
2808 qmlAttachedPropertiesObject<QQuickListView>(item));
2810 attached->setView(this);
2815 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2817 Increments the current index. The current index will wrap
2818 if keyNavigationWraps is true and it is currently at the end.
2819 This method has no effect if the \l count is zero.
2821 \b Note: methods should only be called after the Component has completed.
2823 void QQuickListView::incrementCurrentIndex()
2825 Q_D(QQuickListView);
2826 int count = d->model ? d->model->count() : 0;
2827 if (count && (currentIndex() < count - 1 || d->wrap)) {
2828 d->moveReason = QQuickListViewPrivate::SetIndex;
2829 int index = currentIndex()+1;
2830 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2835 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2837 Decrements the current index. The current index will wrap
2838 if keyNavigationWraps is true and it is currently at the beginning.
2839 This method has no effect if the \l count is zero.
2841 \b Note: methods should only be called after the Component has completed.
2843 void QQuickListView::decrementCurrentIndex()
2845 Q_D(QQuickListView);
2846 int count = d->model ? d->model->count() : 0;
2847 if (count && (currentIndex() > 0 || d->wrap)) {
2848 d->moveReason = QQuickListViewPrivate::SetIndex;
2849 int index = currentIndex()-1;
2850 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2854 void QQuickListViewPrivate::updateSectionCriteria()
2856 Q_Q(QQuickListView);
2857 if (q->isComponentComplete() && model) {
2858 QList<QByteArray> roles;
2859 if (sectionCriteria && !sectionCriteria->property().isEmpty())
2860 roles << sectionCriteria->property().toUtf8();
2861 model->setWatchedRoles(roles);
2864 forceLayoutPolish();
2868 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2870 int modelIndex = change.index;
2871 int count = change.count;
2873 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2874 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2877 int i = visibleItems.count() - 1;
2878 while (i > 0 && visibleItems.at(i)->index == -1)
2880 if (i == 0 && visibleItems.first()->index == -1) {
2881 // there are no visible items except items marked for removal
2882 index = visibleItems.count();
2883 } else if (visibleItems.at(i)->index + 1 == modelIndex
2884 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2885 // Special case of appending an item to the model.
2886 index = visibleItems.count();
2888 if (modelIndex < visibleIndex) {
2889 // Insert before visible items
2890 visibleIndex += count;
2891 for (int i = 0; i < visibleItems.count(); ++i) {
2892 FxViewItem *item = visibleItems.at(i);
2893 if (item->index != -1 && item->index >= modelIndex)
2894 item->index += count;
2901 // index can be the next item past the end of the visible items list (i.e. appended)
2903 if (visibleItems.count()) {
2904 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2905 : visibleItems.last()->endPosition()+spacing;
2908 int prevVisibleCount = visibleItems.count();
2909 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2910 // Insert items before the visible item.
2911 int insertionIdx = index;
2913 int from = tempPos - buffer;
2915 for (i = count-1; i >= 0; --i) {
2916 if (pos > from && insertionIdx < visibleIndex) {
2917 // item won't be visible, just note the size for repositioning
2918 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2919 pos -= averageSize + spacing;
2921 // item is before first visible e.g. in cache buffer
2922 FxViewItem *item = 0;
2923 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2924 item->index = modelIndex + i;
2926 item = createItem(modelIndex + i);
2930 visibleItems.insert(insertionIdx, item);
2931 if (insertionIdx == 0)
2932 insertResult->changedFirstItem = true;
2933 if (!change.isMove()) {
2934 addedItems->append(item);
2935 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2937 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2938 pos -= item->size() + spacing;
2944 int to = buffer+tempPos+size();
2945 for (i = 0; i < count && pos <= to; ++i) {
2946 FxViewItem *item = 0;
2947 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2948 item->index = modelIndex + i;
2949 bool newItem = !item;
2951 item = createItem(modelIndex + i);
2955 visibleItems.insert(index, item);
2957 insertResult->changedFirstItem = true;
2958 if (change.isMove()) {
2959 // we know this is a move target, since move displaced items that are
2960 // shuffled into view due to a move would be added in refill()
2961 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2962 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2964 addedItems->append(item);
2965 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2967 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2968 pos += item->size() + spacing;
2973 for (; index < visibleItems.count(); ++index) {
2974 FxViewItem *item = visibleItems.at(index);
2975 if (item->index != -1) {
2976 item->index += count;
2977 if (change.isMove())
2978 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2980 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2984 updateVisibleIndex();
2986 return visibleItems.count() > prevVisibleCount;
2989 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2991 Q_UNUSED(insertionResult);
2996 int markerItemIndex = -1;
2997 for (int i=0; i<visibleItems.count(); i++) {
2998 if (visibleItems[i]->index == afterModelIndex) {
2999 markerItemIndex = i;
3003 if (markerItemIndex < 0)
3006 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
3007 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
3008 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
3010 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
3011 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
3012 if (!listItem->transitionScheduledOrRunning()) {
3013 qreal pos = listItem->position();
3014 listItem->setPosition(pos - sizeRemoved);
3015 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
3016 listItem->setPosition(pos);
3022 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
3024 Positions the view such that the \a index is at the position specified by
3028 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
3029 \li ListView.Center - position item in the center of the view.
3030 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3031 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
3032 bring the item into view.
3033 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
3034 the view the item is positioned at the top (or left for horizontal orientation) of the view.
3037 If positioning the view at \a index would cause empty space to be displayed at
3038 the beginning or end of the view, the view will be positioned at the boundary.
3040 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3041 at a particular index. This is unreliable since removing items from the start
3042 of the list does not cause all other items to be repositioned, and because
3043 the actual start of the view can vary based on the size of the delegates.
3044 The correct way to bring an item into view is with \c positionViewAtIndex.
3046 \b Note: methods should only be called after the Component has completed. To position
3047 the view at startup, this method should be called by Component.onCompleted. For
3048 example, to position the view at the end:
3051 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3056 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
3057 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
3059 Positions the view at the beginning or end, taking into account any header or footer.
3061 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3062 at a particular index. This is unreliable since removing items from the start
3063 of the list does not cause all other items to be repositioned, and because
3064 the actual start of the view can vary based on the size of the delegates.
3066 \b Note: methods should only be called after the Component has completed. To position
3067 the view at startup, this method should be called by Component.onCompleted. For
3068 example, to position the view at the end on startup:
3071 Component.onCompleted: positionViewAtEnd()
3076 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3078 Returns the index of the visible item containing the point \a x, \a y in content
3079 coordinates. If there is no item at the point specified, or the item is
3080 not visible -1 is returned.
3082 If the item is outside the visible area, -1 is returned, regardless of
3083 whether an item will exist at that point when scrolled into view.
3085 \b Note: methods should only be called after the Component has completed.
3089 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3091 Returns the visible item containing the point \a x, \a y in content
3092 coordinates. If there is no item at the point specified, or the item is
3093 not visible null is returned.
3095 If the item is outside the visible area, null is returned, regardless of
3096 whether an item will exist at that point when scrolled into view.
3098 \b Note: methods should only be called after the Component has completed.
3101 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3103 return new QQuickListViewAttached(obj);