1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquicklistview_p.h"
43 #include "qquickitemview_p_p.h"
44 #include "qquickvisualitemmodel_p.h"
46 #include <QtQml/qqmlexpression.h>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
53 #include <private/qquicksmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
55 #include "qplatformdefs.h"
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
63 //#define DEBUG_DELEGATE_LIFECYCLE
67 class QQuickListViewPrivate : public QQuickItemViewPrivate
69 Q_DECLARE_PUBLIC(QQuickListView)
71 static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
73 virtual Qt::Orientation layoutOrientation() const;
74 virtual bool isContentFlowReversed() const;
75 bool isRightToLeft() const;
76 bool isBottomToTop() const;
78 virtual qreal positionAt(int index) const;
79 virtual qreal endPositionAt(int index) const;
80 virtual qreal originPosition() const;
81 virtual qreal lastPosition() const;
83 FxViewItem *itemBefore(int modelIndex) const;
84 QString sectionAt(int modelIndex);
85 qreal snapPosAt(qreal pos);
86 FxViewItem *snapItemAt(qreal pos);
91 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer);
92 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
93 virtual void visibleItemsChanged();
95 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
96 virtual void initializeViewItem(FxViewItem *item);
97 virtual bool releaseItem(FxViewItem *item);
98 virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
99 virtual void repositionPackageItemAt(QQuickItem *item, int index);
100 virtual void resetFirstItemPosition(qreal pos = 0.0);
101 virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
103 virtual void createHighlight();
104 virtual void updateHighlight();
105 virtual void resetHighlightPosition();
107 virtual void setPosition(qreal pos);
108 virtual void layoutVisibleItems(int fromModelIndex = 0);
110 virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
111 virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
113 virtual void updateSections();
114 QQuickItem *getSectionItem(const QString §ion);
115 void releaseSectionItem(QQuickItem *item);
116 void updateInlineSection(FxListItemSG *);
117 void updateCurrentSection();
118 void updateStickySections();
120 virtual qreal headerSize() const;
121 virtual qreal footerSize() const;
122 virtual bool showHeaderForIndex(int index) const;
123 virtual bool showFooterForIndex(int index) const;
124 virtual void updateHeader();
125 virtual void updateFooter();
127 virtual void changedVisibleIndex(int newIndex);
128 virtual void initializeCurrentItem();
130 void updateAverage();
132 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
133 virtual void fixupPosition();
134 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
135 virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
136 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
138 QQuickListView::Orientation orient;
142 QQuickListView::SnapMode snapMode;
144 QSmoothedAnimation *highlightPosAnimator;
145 QSmoothedAnimation *highlightSizeAnimator;
146 qreal highlightMoveSpeed;
147 qreal highlightResizeSpeed;
148 int highlightResizeDuration;
150 QQuickViewSection *sectionCriteria;
151 QString currentSection;
152 static const int sectionCacheSize = 5;
153 QQuickItem *sectionCache[sectionCacheSize];
154 QQuickItem *currentSectionItem;
155 QString currentStickySection;
156 QQuickItem *nextSectionItem;
157 QString nextStickySection;
158 QString lastVisibleSection;
162 bool correctFlick : 1;
163 bool inFlickCorrection : 1;
165 QQuickListViewPrivate()
166 : orient(QQuickListView::Vertical)
168 , averageSize(100.0), spacing(0.0)
169 , snapMode(QQuickListView::NoSnap)
170 , highlightPosAnimator(0), highlightSizeAnimator(0)
171 , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
172 , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
173 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
175 ~QQuickListViewPrivate() {
176 delete highlightPosAnimator;
177 delete highlightSizeAnimator;
180 friend class QQuickViewSection;
183 //----------------------------------------------------------------------------
185 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
186 : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
187 , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
191 void QQuickViewSection::setProperty(const QString &property)
193 if (property != m_property) {
194 m_property = property;
195 emit propertyChanged();
196 m_view->updateSections();
200 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
202 if (criteria != m_criteria) {
203 m_criteria = criteria;
204 emit criteriaChanged();
205 m_view->updateSections();
209 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
211 if (delegate != m_delegate) {
212 m_delegate = delegate;
213 emit delegateChanged();
214 m_view->updateSections();
218 QString QQuickViewSection::sectionString(const QString &value)
220 if (m_criteria == FirstCharacter)
221 return value.isEmpty() ? QString() : value.at(0);
226 void QQuickViewSection::setLabelPositioning(int l)
228 if (m_labelPositioning != l) {
229 m_labelPositioning = l;
230 emit labelPositioningChanged();
231 m_view->updateSections();
235 //----------------------------------------------------------------------------
237 class FxListItemSG : public FxViewItem
240 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
241 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
243 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
244 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
250 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
251 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
255 inline QQuickItem *section() const {
256 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
258 void setSection(QQuickItem *s) {
260 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
261 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
264 qreal position() const {
266 if (view->orientation() == QQuickListView::Vertical)
267 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
269 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
271 return itemPosition();
274 qreal itemPosition() const {
275 if (view->orientation() == QQuickListView::Vertical)
276 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
278 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
282 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
284 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
286 qreal itemSize() const {
287 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
289 qreal sectionSize() const {
291 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
294 qreal endPosition() const {
295 if (view->orientation() == QQuickListView::Vertical) {
296 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
298 : itemY() + item->height());
300 return (view->effectiveLayoutDirection() == Qt::RightToLeft
302 : itemX() + item->width());
305 void setPosition(qreal pos, bool immediate = false) {
306 // position the section immediately even if there is a transition
308 if (view->orientation() == QQuickListView::Vertical) {
309 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
310 section()->setY(-section()->height()-pos);
312 section()->setY(pos);
314 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
315 section()->setX(-section()->width()-pos);
317 section()->setX(pos);
320 moveTo(pointForPosition(pos), immediate);
322 void setSize(qreal size) {
323 if (view->orientation() == QQuickListView::Vertical)
324 item->setHeight(size);
326 item->setWidth(size);
328 bool contains(qreal x, qreal y) const {
329 return (x >= itemX() && x < itemX() + item->width() &&
330 y >= itemY() && y < itemY() + item->height());
333 QQuickListView *view;
336 QPointF pointForPosition(qreal pos) const {
337 if (view->orientation() == QQuickListView::Vertical) {
338 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
340 pos += section()->height();
341 return QPointF(itemX(), -item->height() - pos);
344 pos += section()->height();
345 return QPointF(itemX(), pos);
348 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
350 pos += section()->width();
351 return QPointF(-item->width() - pos, itemY());
354 pos += section()->width();
355 return QPointF(pos, itemY());
361 //----------------------------------------------------------------------------
363 bool QQuickListViewPrivate::isContentFlowReversed() const
365 return isRightToLeft() || isBottomToTop();
368 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
370 return static_cast<Qt::Orientation>(orient);
373 bool QQuickListViewPrivate::isRightToLeft() const
375 Q_Q(const QQuickListView);
376 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
379 bool QQuickListViewPrivate::isBottomToTop() const
381 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
384 // Returns the item before modelIndex, if created.
385 // May return an item marked for removal.
386 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
388 if (modelIndex < visibleIndex)
392 while (idx < visibleItems.count()) {
393 FxViewItem *item = visibleItems.at(idx);
394 if (item->index != -1)
395 lastIndex = item->index;
396 if (item->index == modelIndex)
397 return visibleItems.at(idx-1);
400 if (lastIndex == modelIndex-1)
401 return visibleItems.last();
405 void QQuickListViewPrivate::setPosition(qreal pos)
408 if (orient == QQuickListView::Vertical) {
410 q->QQuickFlickable::setContentY(-pos-size());
412 q->QQuickFlickable::setContentY(pos);
415 q->QQuickFlickable::setContentX(-pos-size());
417 q->QQuickFlickable::setContentX(pos);
421 qreal QQuickListViewPrivate::originPosition() const
424 if (!visibleItems.isEmpty()) {
425 pos = (*visibleItems.constBegin())->position();
426 if (visibleIndex > 0)
427 pos -= visibleIndex * (averageSize + spacing);
432 qreal QQuickListViewPrivate::lastPosition() const
435 if (!visibleItems.isEmpty()) {
436 int invisibleCount = visibleItems.count() - visibleIndex;
437 for (int i = visibleItems.count()-1; i >= 0; --i) {
438 if (visibleItems.at(i)->index != -1) {
439 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
443 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
444 } else if (model && model->count()) {
445 pos = (model->count() * averageSize + (model->count()-1) * spacing);
450 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
452 if (FxViewItem *item = visibleItem(modelIndex)) {
453 return item->position();
455 if (!visibleItems.isEmpty()) {
456 if (modelIndex < visibleIndex) {
457 int count = visibleIndex - modelIndex;
459 if (modelIndex == currentIndex && currentItem) {
460 cs = currentItem->size() + spacing;
463 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
465 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
466 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
472 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
474 if (FxViewItem *item = visibleItem(modelIndex))
475 return item->endPosition();
476 if (!visibleItems.isEmpty()) {
477 if (modelIndex < visibleIndex) {
478 int count = visibleIndex - modelIndex;
479 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
481 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
482 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
488 QString QQuickListViewPrivate::sectionAt(int modelIndex)
490 if (FxViewItem *item = visibleItem(modelIndex))
491 return item->attached->section();
494 if (sectionCriteria) {
495 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
496 section = sectionCriteria->sectionString(propValue);
502 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
504 if (FxViewItem *snapItem = snapItemAt(pos))
505 return snapItem->position();
506 if (visibleItems.count()) {
507 qreal firstPos = (*visibleItems.constBegin())->position();
508 qreal endPos = (*(--visibleItems.constEnd()))->position();
509 if (pos < firstPos) {
510 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
511 } else if (pos > endPos)
512 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
514 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
517 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
519 FxViewItem *snapItem = 0;
520 qreal prevItemSize = 0;
521 for (int i = 0; i < visibleItems.count(); ++i) {
522 FxViewItem *item = visibleItems.at(i);
523 if (item->index == -1)
525 qreal itemTop = item->position();
526 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
528 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
530 prevItemSize = item->size();
535 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
537 visiblePos = positionAt(newIndex);
538 visibleIndex = newIndex;
541 void QQuickListViewPrivate::init()
543 QQuickItemViewPrivate::init();
544 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
547 void QQuickListViewPrivate::clear()
549 for (int i = 0; i < sectionCacheSize; ++i) {
550 delete sectionCache[i];
554 releaseSectionItem(currentSectionItem);
555 currentSectionItem = 0;
556 releaseSectionItem(nextSectionItem);
558 lastVisibleSection = QString();
559 QQuickItemViewPrivate::clear();
562 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
566 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
567 listItem->index = modelIndex;
569 // initialise attached properties
570 if (sectionCriteria) {
571 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
572 listItem->attached->setSection(sectionCriteria->sectionString(propValue));
573 if (modelIndex > 0) {
574 if (FxViewItem *item = itemBefore(modelIndex))
575 listItem->attached->setPrevSection(item->attached->section());
577 listItem->attached->setPrevSection(sectionAt(modelIndex-1));
579 if (modelIndex < model->count()-1) {
580 if (FxViewItem *item = visibleItem(modelIndex+1))
581 listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section());
583 listItem->attached->setNextSection(sectionAt(modelIndex+1));
590 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
592 QQuickItemViewPrivate::initializeViewItem(item);
594 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
595 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
597 if (sectionCriteria && sectionCriteria->delegate()) {
598 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
599 updateInlineSection(static_cast<FxListItemSG*>(item));
603 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
608 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
610 bool released = QQuickItemViewPrivate::releaseItem(item);
611 if (released && att && att->m_sectionItem) {
612 // We hold no more references to this item
615 if (!sectionCache[i]) {
616 sectionCache[i] = att->m_sectionItem;
617 sectionCache[i]->setVisible(false);
618 att->m_sectionItem = 0;
622 } while (i < sectionCacheSize);
623 delete att->m_sectionItem;
624 att->m_sectionItem = 0;
630 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
632 qreal itemEnd = visiblePos;
633 if (visibleItems.count()) {
634 visiblePos = (*visibleItems.constBegin())->position();
635 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
638 int modelIndex = findLastVisibleIndex();
639 bool haveValidItems = modelIndex >= 0;
640 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
642 if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
643 || bufferTo < visiblePos - averageSize - spacing)) {
644 // We've jumped more than a page. Estimate which items are now
645 // visible and fill from there.
646 int count = (fillFrom - itemEnd) / (averageSize + spacing);
647 int newModelIdx = qBound(0, modelIndex + count, model->count());
648 count = newModelIdx - modelIndex;
650 for (int i = 0; i < visibleItems.count(); ++i)
651 releaseItem(visibleItems.at(i));
652 visibleItems.clear();
653 modelIndex = newModelIdx;
654 visibleIndex = modelIndex;
655 visiblePos = itemEnd + count * (averageSize + spacing);
656 itemEnd = visiblePos;
660 bool changed = false;
661 FxListItemSG *item = 0;
663 while (modelIndex < model->count() && pos <= fillTo) {
664 #ifdef DEBUG_DELEGATE_LIFECYCLE
665 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
667 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
669 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
670 item->setPosition(pos, true);
671 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
672 pos += item->size() + spacing;
673 visibleItems.append(item);
678 if (doBuffer && requestedIndex != -1) // already waiting for an item
681 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
682 #ifdef DEBUG_DELEGATE_LIFECYCLE
683 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
685 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
688 visiblePos -= item->size() + spacing;
689 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
690 item->setPosition(visiblePos, true);
691 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
692 visibleItems.prepend(item);
699 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
701 FxViewItem *item = 0;
702 bool changed = false;
704 // Remove items from the start of the view.
705 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
706 // removed, otherwise a zero-sized item is infinitely added and removed over and
709 while (visibleItems.count() > 1 && index < visibleItems.count()
710 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
711 if (item->attached->delayRemove())
714 if (item->size() > 0) {
715 #ifdef DEBUG_DELEGATE_LIFECYCLE
716 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
718 // remove this item and all zero-sized items before it
720 if (item->index != -1)
722 visibleItems.removeAt(index);
723 if (item->transitionScheduledOrRunning()) {
724 #ifdef DEBUG_DELEGATE_LIFECYCLE
725 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
727 item->releaseAfterTransition = true;
728 releasePendingTransition.append(item);
734 item = visibleItems.at(--index);
742 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
743 if (item->attached->delayRemove())
745 #ifdef DEBUG_DELEGATE_LIFECYCLE
746 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
748 visibleItems.removeLast();
749 if (item->transitionScheduledOrRunning()) {
750 #ifdef DEBUG_DELEGATE_LIFECYCLE
751 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
753 item->releaseAfterTransition = true;
754 releasePendingTransition.append(item);
764 void QQuickListViewPrivate::visibleItemsChanged()
766 if (visibleItems.count())
767 visiblePos = (*visibleItems.constBegin())->position();
769 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
770 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
774 updateCurrentSection();
775 updateUnrequestedPositions();
778 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
780 if (!visibleItems.isEmpty()) {
781 const qreal from = isContentFlowReversed() ? -position() - size() : position();
782 const qreal to = isContentFlowReversed() ? -position() : position() + size();
784 FxViewItem *firstItem = *visibleItems.constBegin();
785 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
786 qreal sum = firstItem->size();
787 qreal pos = firstItem->position() + firstItem->size() + spacing;
788 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
790 for (int i=1; i < visibleItems.count(); ++i) {
791 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
792 if (item->index >= fromModelIndex) {
793 item->setPosition(pos);
794 item->setVisible(item->endPosition() >= from && item->position() <= to);
796 pos += item->size() + spacing;
798 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
800 averageSize = qRound(sum / visibleItems.count());
802 // move current item if it is not a visible item.
803 if (currentIndex >= 0 && currentItem && !fixedCurrent)
804 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
808 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
810 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
813 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
816 qreal pos = position();
817 if (orient == QQuickListView::Vertical) {
818 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
820 item->setY(-positionAt(index)-item->height());
822 item->setY(positionAt(index));
825 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
827 item->setX(-positionAt(index)-item->width());
829 item->setX(positionAt(index));
834 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
836 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
837 item->setPosition(pos);
840 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
842 if (!visibleItems.count())
844 qreal diff = forwards - backwards;
845 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
848 void QQuickListViewPrivate::createHighlight()
851 bool changed = false;
853 if (trackedItem == highlight)
858 delete highlightPosAnimator;
859 delete highlightSizeAnimator;
860 highlightPosAnimator = 0;
861 highlightSizeAnimator = 0;
867 QQuickItem *item = createHighlightItem();
869 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
872 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
873 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
875 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
876 highlightPosAnimator = new QSmoothedAnimation;
877 highlightPosAnimator->target = QQmlProperty(item, posProp);
878 highlightPosAnimator->velocity = highlightMoveSpeed;
879 highlightPosAnimator->userDuration = highlightMoveDuration;
881 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
882 highlightSizeAnimator = new QSmoothedAnimation;
883 highlightSizeAnimator->velocity = highlightResizeSpeed;
884 highlightSizeAnimator->userDuration = highlightResizeDuration;
885 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
887 highlight = newHighlight;
892 emit q->highlightItemChanged();
895 void QQuickListViewPrivate::updateHighlight()
897 applyPendingChanges();
899 if ((!currentItem && highlight) || (currentItem && !highlight))
901 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
902 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
903 // auto-update highlight
904 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
905 highlightPosAnimator->to = isContentFlowReversed()
906 ? -listItem->itemPosition()-listItem->itemSize()
907 : listItem->itemPosition();
908 highlightSizeAnimator->to = listItem->itemSize();
909 if (orient == QQuickListView::Vertical) {
910 if (highlight->item->width() == 0)
911 highlight->item->setWidth(currentItem->item->width());
913 if (highlight->item->height() == 0)
914 highlight->item->setHeight(currentItem->item->height());
917 highlightPosAnimator->restart();
918 highlightSizeAnimator->restart();
923 void QQuickListViewPrivate::resetHighlightPosition()
925 if (highlight && currentItem)
926 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
929 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
932 QQuickItem *sectionItem = 0;
933 int i = sectionCacheSize-1;
934 while (i >= 0 && !sectionCache[i])
937 sectionItem = sectionCache[i];
939 sectionItem->setVisible(true);
940 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
941 context->setContextProperty(QLatin1String("section"), section);
943 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
944 QQmlContext *context = new QQmlContext(
945 creationContext ? creationContext : qmlContext(q));
946 context->setContextProperty(QLatin1String("section"), section);
947 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
949 QQml_setParent_noEvent(context, nobj);
950 sectionItem = qobject_cast<QQuickItem *>(nobj);
954 sectionItem->setZ(2);
955 QQml_setParent_noEvent(sectionItem, contentItem);
956 sectionItem->setParentItem(contentItem);
961 sectionCriteria->delegate()->completeCreate();
967 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
973 if (!sectionCache[i]) {
974 sectionCache[i] = item;
975 sectionCache[i]->setVisible(false);
979 } while (i < sectionCacheSize);
983 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
985 if (!sectionCriteria || !sectionCriteria->delegate())
987 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
988 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
989 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
990 if (!listItem->section()) {
991 qreal pos = listItem->position();
992 listItem->setSection(getSectionItem(listItem->attached->m_section));
993 listItem->setPosition(pos);
995 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
996 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
998 } else if (listItem->section()) {
999 qreal pos = listItem->position();
1000 releaseSectionItem(listItem->section());
1001 listItem->setSection(0);
1002 listItem->setPosition(pos);
1006 void QQuickListViewPrivate::updateStickySections()
1008 if (!sectionCriteria
1009 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1012 bool isFlowReversed = isContentFlowReversed();
1013 qreal viewPos = isFlowReversed ? -position()-size() : position();
1014 QQuickItem *sectionItem = 0;
1015 QQuickItem *lastSectionItem = 0;
1017 while (index < visibleItems.count()) {
1018 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1019 // Find the current section header and last visible section header
1020 // and hide them if they will overlap a static section header.
1021 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1022 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1024 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1025 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1027 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1028 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1029 section->setVisible(visBot && visTop);
1030 if (visTop && !sectionItem)
1031 sectionItem = section;
1032 if (isFlowReversed) {
1033 if (-sectionPos <= viewPos + size())
1034 lastSectionItem = section;
1036 if (sectionPos + sectionSize < viewPos + size())
1037 lastSectionItem = section;
1043 // Current section header
1044 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1045 if (!currentSectionItem) {
1046 currentSectionItem = getSectionItem(currentSection);
1047 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1048 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1049 context->setContextProperty(QLatin1String("section"), currentSection);
1051 currentStickySection = currentSection;
1052 if (!currentSectionItem)
1055 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1056 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1058 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1059 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1061 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1062 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1065 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1067 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1068 if (orient == QQuickListView::Vertical)
1069 currentSectionItem->setY(pos);
1071 currentSectionItem->setX(pos);
1072 } else if (currentSectionItem) {
1073 releaseSectionItem(currentSectionItem);
1074 currentSectionItem = 0;
1077 // Next section footer
1078 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1079 if (!nextSectionItem) {
1080 nextSectionItem = getSectionItem(nextSection);
1081 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1082 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1083 context->setContextProperty(QLatin1String("section"), nextSection);
1085 nextStickySection = nextSection;
1086 if (!nextSectionItem)
1089 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1090 nextSectionItem->setVisible(!nextSection.isEmpty());
1091 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1092 if (lastSectionItem) {
1093 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1094 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1097 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1098 if (orient == QQuickListView::Vertical)
1099 nextSectionItem->setY(pos);
1101 nextSectionItem->setX(pos);
1102 } else if (nextSectionItem) {
1103 releaseSectionItem(nextSectionItem);
1104 nextSectionItem = 0;
1108 void QQuickListViewPrivate::updateSections()
1110 Q_Q(QQuickListView);
1111 if (!q->isComponentComplete())
1114 QQuickItemViewPrivate::updateSections();
1116 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1117 QString prevSection;
1118 if (visibleIndex > 0)
1119 prevSection = sectionAt(visibleIndex-1);
1120 QQuickListViewAttached *prevAtt = 0;
1122 for (int i = 0; i < visibleItems.count(); ++i) {
1123 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1124 attached->setPrevSection(prevSection);
1125 if (visibleItems.at(i)->index != -1) {
1126 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1127 attached->setSection(sectionCriteria->sectionString(propValue));
1128 idx = visibleItems.at(i)->index;
1130 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1132 prevAtt->setNextSection(attached->section());
1133 prevSection = attached->section();
1137 if (idx > 0 && idx < model->count()-1)
1138 prevAtt->setNextSection(sectionAt(idx+1));
1140 prevAtt->setNextSection(QString());
1144 lastVisibleSection = QString();
1145 updateCurrentSection();
1146 updateStickySections();
1149 void QQuickListViewPrivate::updateCurrentSection()
1151 Q_Q(QQuickListView);
1152 if (!sectionCriteria || visibleItems.isEmpty()) {
1153 if (!currentSection.isEmpty()) {
1154 currentSection.clear();
1155 emit q->currentSectionChanged();
1159 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1160 qreal sectionThreshold = position();
1161 if (currentSectionItem && !inlineSections)
1162 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1164 int modelIndex = visibleIndex;
1165 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1166 if (visibleItems.at(index)->index != -1)
1167 modelIndex = visibleItems.at(index)->index;
1171 QString newSection = currentSection;
1172 if (index < visibleItems.count())
1173 newSection = visibleItems.at(index)->attached->section();
1175 newSection = (*visibleItems.constBegin())->attached->section();
1176 if (newSection != currentSection) {
1177 currentSection = newSection;
1178 updateStickySections();
1179 emit q->currentSectionChanged();
1182 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1183 // Don't want to scan for next section on every movement, so remember
1184 // the last section in the visible area and only scan for the next
1185 // section when that changes. Clearing lastVisibleSection will also
1187 QString lastSection = currentSection;
1188 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1189 if (nextSectionItem && !inlineSections)
1190 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1191 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1192 if (visibleItems.at(index)->index != -1)
1193 modelIndex = visibleItems.at(index)->index;
1194 lastSection = visibleItems.at(index)->attached->section();
1198 if (lastVisibleSection != lastSection) {
1199 nextSection = QString();
1200 lastVisibleSection = lastSection;
1201 for (int i = modelIndex; i < itemCount; ++i) {
1202 QString section = sectionAt(i);
1203 if (section != lastSection) {
1204 nextSection = section;
1205 updateStickySections();
1213 void QQuickListViewPrivate::initializeCurrentItem()
1215 QQuickItemViewPrivate::initializeCurrentItem();
1218 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1220 // don't reposition the item if it is already in the visibleItems list
1221 FxViewItem *actualItem = visibleItem(currentIndex);
1223 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1224 // We can calculate exact postion in this case
1225 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1227 // Create current item now and position as best we can.
1228 // Its position will be corrected when it becomes visible.
1229 listItem->setPosition(positionAt(currentIndex));
1233 if (visibleItems.isEmpty())
1234 averageSize = listItem->size();
1238 void QQuickListViewPrivate::updateAverage()
1240 if (!visibleItems.count())
1243 for (int i = 0; i < visibleItems.count(); ++i)
1244 sum += visibleItems.at(i)->size();
1245 averageSize = qRound(sum / visibleItems.count());
1248 qreal QQuickListViewPrivate::headerSize() const
1250 return header ? header->size() : 0.0;
1253 qreal QQuickListViewPrivate::footerSize() const
1255 return footer ? footer->size() : 0.0;
1258 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1263 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1265 return index == model->count()-1;
1268 void QQuickListViewPrivate::updateFooter()
1270 Q_Q(QQuickListView);
1271 bool created = false;
1273 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1276 footer = new FxListItemSG(item, q, true, true);
1280 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1281 if (visibleItems.count()) {
1282 qreal endPos = lastPosition();
1283 if (findLastVisibleIndex() == model->count()-1) {
1284 listItem->setPosition(endPos);
1286 qreal visiblePos = position() + q->height();
1287 if (endPos <= visiblePos || listItem->position() < endPos)
1288 listItem->setPosition(endPos);
1291 listItem->setPosition(visiblePos);
1295 emit q->footerItemChanged();
1298 void QQuickListViewPrivate::updateHeader()
1300 Q_Q(QQuickListView);
1301 bool created = false;
1303 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1306 header = new FxListItemSG(item, q, true, true);
1310 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1312 if (visibleItems.count()) {
1313 qreal startPos = originPosition();
1314 if (visibleIndex == 0) {
1315 listItem->setPosition(startPos - headerSize());
1317 if (position() <= startPos || listItem->position() > startPos - headerSize())
1318 listItem->setPosition(startPos - headerSize());
1321 listItem->setPosition(-headerSize());
1326 emit q->headerItemChanged();
1329 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1331 Q_Q(QQuickListView);
1332 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1333 if (!q->isComponentComplete())
1336 if (item != contentItem && (!highlight || item != highlight->item)) {
1337 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1338 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1340 // if visibleItems.first() has resized, adjust its pos since it is used to
1341 // position all subsequent items
1342 if (visibleItems.count() && item == visibleItems.first()->item) {
1343 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1344 if (orient == QQuickListView::Vertical) {
1345 qreal diff = newGeometry.height() - oldGeometry.height();
1346 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1347 listItem->setPosition(listItem->position() - diff, true);
1348 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1349 listItem->setPosition(listItem->position() + diff, true);
1351 qreal diff = newGeometry.width() - oldGeometry.width();
1352 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1353 listItem->setPosition(listItem->position() - diff, true);
1354 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1355 listItem->setPosition(listItem->position() + diff, true);
1364 void QQuickListViewPrivate::fixupPosition()
1366 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1367 || snapMode != QQuickListView::NoSnap)
1369 if (orient == QQuickListView::Vertical)
1375 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1377 if ((orient == QQuickListView::Horizontal && &data == &vData)
1378 || (orient == QQuickListView::Vertical && &data == &hData))
1381 correctFlick = false;
1382 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1383 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1385 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1387 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1388 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1389 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1390 // if we've been dragged < averageSize/2 then bias towards the next item
1391 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1393 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1394 bias = averageSize/2;
1395 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1396 bias = -averageSize/2;
1397 if (isContentFlowReversed())
1399 tempPosition -= bias;
1401 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1402 if (!topItem && strictHighlightRange && currentItem) {
1403 // StrictlyEnforceRange always keeps an item in range
1405 topItem = currentItem;
1407 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1408 if (!bottomItem && strictHighlightRange && currentItem) {
1409 // StrictlyEnforceRange always keeps an item in range
1411 bottomItem = currentItem;
1414 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1415 if (topItem && (isInBounds || strictHighlightRange)) {
1416 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1417 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1419 if (isContentFlowReversed())
1420 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1422 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1424 } else if (bottomItem && isInBounds) {
1425 if (isContentFlowReversed())
1426 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1428 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1430 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1434 qreal dist = qAbs(data.move + pos);
1436 timeline.reset(data.move);
1437 if (fixupMode != Immediate) {
1438 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1439 data.fixingUp = true;
1441 timeline.set(data.move, -pos);
1443 vTime = timeline.time();
1445 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1447 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1448 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1449 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1450 if (viewPos > pos - highlightRangeStart)
1451 viewPos = pos - highlightRangeStart;
1452 if (isContentFlowReversed())
1453 viewPos = -viewPos-size();
1455 timeline.reset(data.move);
1456 if (viewPos != position()) {
1457 if (fixupMode != Immediate) {
1458 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1459 data.fixingUp = true;
1461 timeline.set(data.move, -viewPos);
1464 vTime = timeline.time();
1466 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1468 data.inOvershoot = false;
1472 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1473 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1475 data.fixingUp = false;
1477 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1478 correctFlick = true;
1479 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1481 qreal maxDistance = 0;
1482 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1484 // -ve velocity means list is moving up/left
1486 if (data.move.value() < minExtent) {
1487 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1488 // if we've been dragged < averageSize/2 then bias towards the next item
1489 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1490 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1491 if (isContentFlowReversed())
1493 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1494 maxDistance = qAbs(data.flickTarget - data.move.value());
1495 velocity = maxVelocity;
1497 maxDistance = qAbs(minExtent - data.move.value());
1500 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1501 data.flickTarget = minExtent;
1503 if (data.move.value() > maxExtent) {
1504 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1505 // if we've been dragged < averageSize/2 then bias towards the next item
1506 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1507 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1508 if (isContentFlowReversed())
1510 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1511 maxDistance = qAbs(data.flickTarget - data.move.value());
1512 velocity = -maxVelocity;
1514 maxDistance = qAbs(maxExtent - data.move.value());
1517 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1518 data.flickTarget = maxExtent;
1520 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1521 if (maxDistance > 0 || overShoot) {
1522 // These modes require the list to stop exactly on an item boundary.
1523 // The initial flick will estimate the boundary to stop on.
1524 // Since list items can have variable sizes, the boundary will be
1525 // reevaluated and adjusted as we approach the boundary.
1527 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1533 if (!hData.flicking && !vData.flicking) {
1534 // the initial flick - estimate boundary
1535 qreal accel = deceleration;
1537 overshootDist = 0.0;
1538 // + averageSize/4 to encourage moving at least one item in the flick direction
1539 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1540 if (maxDistance > 0)
1541 dist = qMin(dist, maxDistance);
1544 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1545 if (snapMode != QQuickListView::SnapOneItem) {
1546 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1547 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1549 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1551 if (data.flickTarget >= minExtent) {
1552 overshootDist = overShootDistance(vSize);
1553 data.flickTarget += overshootDist;
1554 } else if (data.flickTarget <= maxExtent) {
1555 overshootDist = overShootDistance(vSize);
1556 data.flickTarget -= overshootDist;
1559 qreal adjDist = -data.flickTarget + data.move.value();
1560 if (qAbs(adjDist) > qAbs(dist)) {
1561 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1562 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1571 accel = v2 / (2.0f * qAbs(dist));
1572 } else if (overShoot) {
1573 data.flickTarget = data.move.value() - dist;
1574 if (data.flickTarget >= minExtent) {
1575 overshootDist = overShootDistance(vSize);
1576 data.flickTarget += overshootDist;
1577 } else if (data.flickTarget <= maxExtent) {
1578 overshootDist = overShootDistance(vSize);
1579 data.flickTarget -= overshootDist;
1582 timeline.reset(data.move);
1583 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1584 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1585 correctFlick = true;
1588 // reevaluate the target boundary.
1589 qreal newtarget = data.flickTarget;
1590 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1591 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1592 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1593 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1595 if (velocity < 0 && newtarget <= maxExtent)
1596 newtarget = maxExtent - overshootDist;
1597 else if (velocity > 0 && newtarget >= minExtent)
1598 newtarget = minExtent + overshootDist;
1599 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1600 if (qAbs(velocity) < MinimumFlickVelocity)
1601 correctFlick = false;
1604 data.flickTarget = newtarget;
1605 qreal dist = -newtarget + data.move.value();
1606 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1607 correctFlick = false;
1608 timeline.reset(data.move);
1609 fixup(data, minExtent, maxExtent);
1612 timeline.reset(data.move);
1613 timeline.accelDistance(data.move, v, -dist);
1614 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1618 correctFlick = false;
1619 timeline.reset(data.move);
1620 fixup(data, minExtent, maxExtent);
1625 //----------------------------------------------------------------------------
1628 \qmlclass ListView QQuickListView
1629 \inqmlmodule QtQuick 2
1630 \ingroup qtquick-views
1632 \brief Provides a list view of items provided by a model
1634 A ListView displays data from models created from built-in QML elements like ListModel
1635 and XmlListModel, or custom model classes defined in C++ that inherit from
1638 A ListView has a \l model, which defines the data to be displayed, and
1639 a \l delegate, which defines how the data should be displayed. Items in a
1640 ListView are laid out horizontally or vertically. List views are inherently
1641 flickable because ListView inherits from \l Flickable.
1643 \section1 Example Usage
1645 The following example shows the definition of a simple list model defined
1646 in a file called \c ContactModel.qml:
1648 \snippet qml/listview/ContactModel.qml 0
1650 Another component can display this model data in a ListView, like this:
1652 \snippet qml/listview/listview.qml import
1654 \snippet qml/listview/listview.qml classdocs simple
1656 \image listview-simple.png
1658 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1659 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1660 the delegate is able to access the model's \c name and \c number data directly.
1662 An improved list view is shown below. The delegate is visually improved and is moved
1663 into a separate \c contactDelegate component.
1665 \snippet qml/listview/listview.qml classdocs advanced
1666 \image listview-highlight.png
1668 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1669 and \c focus is set to \c true to enable keyboard navigation for the list view.
1670 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1672 Delegates are instantiated as needed and may be destroyed at any time.
1673 State should \e never be stored in a delegate.
1675 ListView attaches a number of properties to the root item of the delegate, for example
1676 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1677 this attached property directly as \c ListView.isCurrentItem, while the child
1678 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1680 \snippet qml/listview/listview.qml isCurrentItem
1682 \note Views do not enable \e clip automatically. If the view
1683 is not clipped by another item or the screen, it will be necessary
1684 to set \e {clip: true} in order to have the out of view items clipped
1688 \section1 ListView layouts
1690 The layout of the items in a ListView can be controlled by these properties:
1693 \li \l orientation - controls whether items flow horizontally or vertically.
1694 This value can be either Qt.Horizontal or Qt.Vertical.
1695 \li \l layoutDirection - controls the horizontal layout direction for a
1696 horizontally-oriented view: that is, whether items are laid out from the left side of
1697 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1698 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1699 view: that is, whether items are laid out from the top of the view down towards the bottom of
1700 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1703 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1704 table below shows the different layouts that a ListView can have, depending on the values of
1705 the properties listed above.
1710 \bold ListViews with Qt.Vertical orientation
1713 \image listview-layout-toptobottom.png
1715 \image listview-layout-bottomtotop.png
1718 \bold ListViews with Qt.Horizontal orientation
1721 \image listview-layout-lefttoright.png
1723 \image listview-layout-righttoleft.png
1726 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1728 QQuickListView::QQuickListView(QQuickItem *parent)
1729 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1733 QQuickListView::~QQuickListView()
1738 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1739 This attached property is true if this delegate is the current item; otherwise false.
1741 It is attached to each instance of the delegate.
1743 This property may be used to adjust the appearance of the current item, for example:
1745 \snippet qml/listview/listview.qml isCurrentItem
1749 \qmlattachedproperty ListView QtQuick2::ListView::view
1750 This attached property holds the view that manages this delegate instance.
1752 It is attached to each instance of the delegate.
1756 \qmlattachedproperty string QtQuick2::ListView::previousSection
1757 This attached property holds the section of the previous element.
1759 It is attached to each instance of the delegate.
1761 The section is evaluated using the \l {ListView::section.property}{section} properties.
1765 \qmlattachedproperty string QtQuick2::ListView::nextSection
1766 This attached property holds the section of the next element.
1768 It is attached to each instance of the delegate.
1770 The section is evaluated using the \l {ListView::section.property}{section} properties.
1774 \qmlattachedproperty string QtQuick2::ListView::section
1775 This attached property holds the section of this element.
1777 It is attached to each instance of the delegate.
1779 The section is evaluated using the \l {ListView::section.property}{section} properties.
1783 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1785 This attached property holds whether the delegate may be destroyed. It
1786 is attached to each instance of the delegate. The default value is false.
1788 It is sometimes necessary to delay the destruction of an item
1789 until an animation completes. The example delegate below ensures that the
1790 animation completes before the item is removed from the list.
1792 \snippet qml/listview/listview.qml delayRemove
1794 If a \l remove transition has been specified, it will not be applied until
1795 delayRemove is returned to \c false.
1799 \qmlattachedsignal QtQuick2::ListView::onAdd()
1800 This attached signal handler is called immediately after an item is added to the view.
1802 If an \l add transition is specified, it is applied immediately after
1803 this signal handler is called.
1807 \qmlattachedsignal QtQuick2::ListView::onRemove()
1808 This attached handler is called immediately before an item is removed from the view.
1810 If a \l remove transition has been specified, it is applied after
1811 this signal handler is called, providing that delayRemove is false.
1815 \qmlproperty model QtQuick2::ListView::model
1816 This property holds the model providing data for the list.
1818 The model provides the set of data that is used to create the items
1819 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1820 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1821 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1823 \sa {qmlmodels}{Data Models}
1827 \qmlproperty Component QtQuick2::ListView::delegate
1829 The delegate provides a template defining each item instantiated by the view.
1830 The index is exposed as an accessible \c index property. Properties of the
1831 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1833 The number of elements in the delegate has a direct effect on the
1834 flicking performance of the view. If at all possible, place functionality
1835 that is not needed for the normal display of the delegate in a \l Loader which
1836 can load additional elements when needed.
1838 The ListView will lay out the items based on the size of the root item
1841 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1844 \note Delegates are instantiated as needed and may be destroyed at any time.
1845 State should \e never be stored in a delegate.
1848 \qmlproperty int QtQuick2::ListView::currentIndex
1849 \qmlproperty Item QtQuick2::ListView::currentItem
1851 The \c currentIndex property holds the index of the current item, and
1852 \c currentItem holds the current item. Setting the currentIndex to -1
1853 will clear the highlight and set currentItem to null.
1855 If highlightFollowsCurrentItem is \c true, setting either of these
1856 properties will smoothly scroll the ListView so that the current
1857 item becomes visible.
1859 Note that the position of the current item
1860 may only be approximate until it becomes visible in the view.
1864 \qmlproperty Item QtQuick2::ListView::highlightItem
1866 This holds the highlight item created from the \l highlight component.
1868 The \c highlightItem is managed by the view unless
1869 \l highlightFollowsCurrentItem is set to false.
1871 \sa highlight, highlightFollowsCurrentItem
1875 \qmlproperty int QtQuick2::ListView::count
1876 This property holds the number of items in the view.
1880 \qmlproperty Component QtQuick2::ListView::highlight
1881 This property holds the component to use as the highlight.
1883 An instance of the highlight component is created for each list.
1884 The geometry of the resulting component instance is managed by the list
1885 so as to stay with the current item, unless the highlightFollowsCurrentItem
1888 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1892 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1893 This property holds whether the highlight is managed by the view.
1895 If this property is true (the default value), the highlight is moved smoothly
1896 to follow the current item. Otherwise, the
1897 highlight is not moved by the view, and any movement must be implemented
1900 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1902 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1904 Note that the highlight animation also affects the way that the view
1905 is scrolled. This is because the view moves to maintain the
1906 highlight within the preferred highlight range (or visible viewport).
1908 \sa highlight, highlightMoveSpeed
1910 //###Possibly rename these properties, since they are very useful even without a highlight?
1912 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1913 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1914 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1916 These properties define the preferred range of the highlight (for the current item)
1917 within the view. The \c preferredHighlightBegin value must be less than the
1918 \c preferredHighlightEnd value.
1920 These properties affect the position of the current item when the list is scrolled.
1921 For example, if the currently selected item should stay in the middle of the
1922 list when the view is scrolled, set the \c preferredHighlightBegin and
1923 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1924 item would be. If the \c currentItem is changed programmatically, the list will
1925 automatically scroll so that the current item is in the middle of the view.
1926 Furthermore, the behavior of the current item index will occur whether or not a
1929 Valid values for \c highlightRangeMode are:
1932 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1933 However, the highlight can move outside of the range at the ends of the list or due
1934 to mouse interaction.
1935 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1936 The current item changes if a keyboard or mouse action would cause the highlight to move
1937 outside of the range.
1938 \li ListView.NoHighlightRange - this is the default value.
1941 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1943 Q_D(QQuickListView);
1944 if (d->autoHighlight != autoHighlight) {
1945 if (!autoHighlight) {
1946 if (d->highlightPosAnimator)
1947 d->highlightPosAnimator->stop();
1948 if (d->highlightSizeAnimator)
1949 d->highlightSizeAnimator->stop();
1951 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1956 \qmlproperty real QtQuick2::ListView::spacing
1958 This property holds the spacing between items.
1960 The default value is 0.
1962 qreal QQuickListView::spacing() const
1964 Q_D(const QQuickListView);
1968 void QQuickListView::setSpacing(qreal spacing)
1970 Q_D(QQuickListView);
1971 if (spacing != d->spacing) {
1972 d->spacing = spacing;
1973 d->forceLayout = true;
1975 emit spacingChanged();
1980 \qmlproperty enumeration QtQuick2::ListView::orientation
1981 This property holds the orientation of the list.
1986 \li ListView.Horizontal - Items are laid out horizontally
1987 \li ListView.Vertical (default) - Items are laid out vertically
1992 \li Horizontal orientation:
1993 \image ListViewHorizontal.png
1996 \li Vertical orientation:
1997 \image listview-highlight.png
2000 QQuickListView::Orientation QQuickListView::orientation() const
2002 Q_D(const QQuickListView);
2006 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2008 Q_D(QQuickListView);
2009 if (d->orient != orientation) {
2010 d->orient = orientation;
2011 if (d->orient == Vertical) {
2012 setContentWidth(-1);
2013 setFlickableDirection(VerticalFlick);
2016 setContentHeight(-1);
2017 setFlickableDirection(HorizontalFlick);
2021 emit orientationChanged();
2026 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
2027 This property holds the layout direction of a horizontally-oriented list.
2032 \li Qt.LeftToRight (default) - Items will be laid out from left to right.
2033 \li Qt.RightToLeft - Items will be laid out from right to let.
2036 Setting this property has no effect if the \l orientation is Qt.Vertical.
2038 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2043 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
2044 This property holds the effective layout direction of a horizontally-oriented list.
2046 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2047 the visual layout direction of the horizontal list will be mirrored. However, the
2048 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2050 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2055 \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
2056 This property holds the layout direction of a vertically-oriented list.
2061 \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
2062 \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
2065 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2067 \sa ListView::layoutDirection
2072 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
2073 This property holds whether the list wraps key navigation.
2075 If this is true, key navigation that would move the current item selection
2076 past the end of the list instead wraps around and moves the selection to
2077 the start of the list, and vice-versa.
2079 By default, key navigation is not wrapped.
2084 \qmlproperty int QtQuick2::ListView::cacheBuffer
2085 This property determines whether delegates are retained outside the
2086 visible area of the view.
2088 If this value is non-zero, the view may keep as many delegates
2089 instantiated as it can fit within the buffer specified. For example,
2090 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2091 set to 40, then up to 2 delegates above and 2 delegates below the visible
2092 area may be created/retained. The buffered delegates are created asynchronously,
2093 allowing creation to occur across multiple frames and reducing the
2094 likelihood of skipping frames. In order to improve painting performance
2095 delegates outside the visible area are not painted.
2097 The default value of this property is platform dependent, but will usually
2098 be a non-zero value.
2100 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2101 instantiated delegates.
2103 Setting this value can improve the smoothness of scrolling behavior at the expense
2104 of additional memory usage. It is not a substitute for creating efficient
2105 delegates; the fewer elements in a delegate, the faster a view can be
2111 \qmlproperty string QtQuick2::ListView::section.property
2112 \qmlproperty enumeration QtQuick2::ListView::section.criteria
2113 \qmlproperty Component QtQuick2::ListView::section.delegate
2114 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
2116 These properties determine the expression to be evaluated and appearance
2117 of the section labels.
2119 \c section.property holds the name of the property that is the basis
2122 \c section.criteria holds the criteria for forming each section based on
2123 \c section.property. This value can be one of:
2126 \li ViewSection.FullString (default) - sections are created based on the
2127 \c section.property value.
2128 \li ViewSection.FirstCharacter - sections are created based on the first
2129 character of the \c section.property value (for example, 'A', 'B', 'C'
2130 sections, etc. for an address book)
2133 A case insensitive comparison is used when determining section
2136 \c section.delegate holds the delegate component for each section.
2138 \c section.labelPositioning determines whether the current and/or
2139 next section labels stick to the start/end of the view, and whether
2140 the labels are shown inline. This value can be a combination of:
2143 \li ViewSection.InlineLabels - section labels are shown inline between
2144 the item delegates separating sections (default).
2145 \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
2146 start of the view as it is moved.
2147 \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
2148 sections) sticks to the end of the view as it is moved. \note Enabling
2149 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
2150 section, which has performance implications, especially for slower models.
2153 Each item in the list has attached properties named \c ListView.section,
2154 \c ListView.previousSection and \c ListView.nextSection.
2156 For example, here is a ListView that displays a list of animals, separated
2157 into sections. Each item in the ListView is placed in a different section
2158 depending on the "size" property of the model item. The \c sectionHeading
2159 delegate component provides the light blue bar that marks the beginning of
2163 \snippet examples/quick/modelviews/listview/sections.qml 0
2165 \image qml-listview-sections-example.png
2167 \note Adding sections to a ListView does not automatically re-order the
2168 list items by the section criteria.
2169 If the model is not ordered by section, then it is possible that
2170 the sections created will not be unique; each boundary between
2171 differing sections will result in a section header being created
2172 even if that section exists elsewhere.
2174 \sa {quick/modelviews/listview}{ListView examples}
2176 QQuickViewSection *QQuickListView::sectionCriteria()
2178 Q_D(QQuickListView);
2179 if (!d->sectionCriteria) {
2180 d->sectionCriteria = new QQuickViewSection(this);
2181 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2183 return d->sectionCriteria;
2187 \qmlproperty string QtQuick2::ListView::currentSection
2188 This property holds the section that is currently at the beginning of the view.
2190 QString QQuickListView::currentSection() const
2192 Q_D(const QQuickListView);
2193 return d->currentSection;
2197 \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
2198 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2199 \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
2200 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2202 These properties hold the move and resize animation speed of the highlight delegate.
2204 \l highlightFollowsCurrentItem must be true for these properties
2207 The default value for the speed properties is 400 pixels/second.
2208 The default value for the duration properties is -1, i.e. the
2209 highlight will take as much time as necessary to move at the set speed.
2211 These properties have the same characteristics as a SmoothedAnimation.
2213 \sa highlightFollowsCurrentItem
2215 qreal QQuickListView::highlightMoveSpeed() const
2217 Q_D(const QQuickListView);
2218 return d->highlightMoveSpeed;
2221 void QQuickListView::setHighlightMoveSpeed(qreal speed)
2223 Q_D(QQuickListView);
2224 if (d->highlightMoveSpeed != speed) {
2225 d->highlightMoveSpeed = speed;
2226 if (d->highlightPosAnimator)
2227 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2228 emit highlightMoveSpeedChanged();
2232 void QQuickListView::setHighlightMoveDuration(int duration)
2234 Q_D(QQuickListView);
2235 if (d->highlightMoveDuration != duration) {
2236 if (d->highlightPosAnimator)
2237 d->highlightPosAnimator->userDuration = duration;
2238 QQuickItemView::setHighlightMoveDuration(duration);
2242 qreal QQuickListView::highlightResizeSpeed() const
2244 Q_D(const QQuickListView);
2245 return d->highlightResizeSpeed;
2248 void QQuickListView::setHighlightResizeSpeed(qreal speed)
2250 Q_D(QQuickListView);
2251 if (d->highlightResizeSpeed != speed) {
2252 d->highlightResizeSpeed = speed;
2253 if (d->highlightSizeAnimator)
2254 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2255 emit highlightResizeSpeedChanged();
2259 int QQuickListView::highlightResizeDuration() const
2261 Q_D(const QQuickListView);
2262 return d->highlightResizeDuration;
2265 void QQuickListView::setHighlightResizeDuration(int duration)
2267 Q_D(QQuickListView);
2268 if (d->highlightResizeDuration != duration) {
2269 d->highlightResizeDuration = duration;
2270 if (d->highlightSizeAnimator)
2271 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2272 emit highlightResizeDurationChanged();
2277 \qmlproperty enumeration QtQuick2::ListView::snapMode
2279 This property determines how the view scrolling will settle following a drag or flick.
2280 The possible values are:
2283 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2284 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2286 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2287 visible item at the time the mouse button is released. This mode is particularly
2288 useful for moving one page at a time.
2291 \c snapMode does not affect the \l currentIndex. To update the
2292 \l currentIndex as the list is moved, set \l highlightRangeMode
2293 to \c ListView.StrictlyEnforceRange.
2295 \sa highlightRangeMode
2297 QQuickListView::SnapMode QQuickListView::snapMode() const
2299 Q_D(const QQuickListView);
2303 void QQuickListView::setSnapMode(SnapMode mode)
2305 Q_D(QQuickListView);
2306 if (d->snapMode != mode) {
2308 emit snapModeChanged();
2314 \qmlproperty Component QtQuick2::ListView::footer
2315 This property holds the component to use as the footer.
2317 An instance of the footer component is created for each view. The
2318 footer is positioned at the end of the view, after any items.
2320 \sa header, footerItem
2325 \qmlproperty Component QtQuick2::ListView::header
2326 This property holds the component to use as the header.
2328 An instance of the header component is created for each view. The
2329 header is positioned at the beginning of the view, before any items.
2331 \sa footer, headertem
2335 \qmlproperty Item QtQuick2::ListView::headerItem
2336 This holds the header item created from the \l header component.
2338 An instance of the header component is created for each view. The
2339 header is positioned at the beginning of the view, before any items.
2341 \sa header, footerItem
2345 \qmlproperty Item QtQuick2::ListView::footerItem
2346 This holds the footer item created from the \l footer component.
2348 An instance of the footer component is created for each view. The
2349 footer is positioned at the end of the view, after any items.
2351 \sa footer, headerItem
2355 \qmlproperty Transition QtQuick2::ListView::populate
2357 This property holds the transition to apply to the items that are initially created
2360 It is applied to all items that are created when:
2363 \li The view is first created
2364 \li The view's \l model changes
2365 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2368 For example, here is a view that specifies such a transition:
2373 populate: Transition {
2374 NumberAnimation { properties: "x,y"; duration: 1000 }
2379 When the view is initialized, the view will create all the necessary items for the view,
2380 then animate them to their correct positions within the view over one second.
2382 For more details and examples on how to use view transitions, see the ViewTransition
2385 \sa add, ViewTransition
2389 \qmlproperty Transition QtQuick2::ListView::add
2391 This property holds the transition to apply to items that are added to the view.
2393 For example, here is a view that specifies such a transition:
2399 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2404 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2405 to its final x,y position within the view, over one second. The transition only applies to
2406 the new items that are added to the view; it does not apply to the items below that are
2407 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2408 or \l addDisplaced properties.
2410 For more details and examples on how to use view transitions, see the ViewTransition
2413 \note This transition is not applied to the items that are created when the view is initially
2414 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2417 \sa addDisplaced, populate, ViewTransition
2421 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2423 This property holds the transition to apply to items within the view that are displaced by
2424 the addition of other items to the view.
2426 For example, here is a view that specifies such a transition:
2431 addDisplaced: Transition {
2432 NumberAnimation { properties: "x,y"; duration: 1000 }
2437 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2438 them to move down (or sideways, if horizontally orientated) within the view. As this
2439 displacement occurs, the items' movement to their new x,y positions within the view will be
2440 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2441 the new item that has been added to the view; to animate the added items, set the \l add
2444 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2445 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2446 if it is not necessary to specify different transitions depending on whether an item is displaced
2447 by an add, move or remove operation, consider setting the \l displaced property instead.
2449 For more details and examples on how to use view transitions, see the ViewTransition
2452 \note This transition is not applied to the items that are created when the view is initially
2453 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2456 \sa displaced, add, populate, ViewTransition
2460 \qmlproperty Transition QtQuick2::ListView::move
2462 This property holds the transition to apply to items in the view that are being moved due
2463 to a move operation in the view's \l model.
2465 For example, here is a view that specifies such a transition:
2471 NumberAnimation { properties: "x,y"; duration: 1000 }
2476 Whenever the \l model performs a move operation to move a particular set of indexes, the
2477 respective items in the view will be animated to their new positions in the view over one
2478 second. The transition only applies to the items that are the subject of the move operation
2479 in the model; it does not apply to items below them that are displaced by the move operation.
2480 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2482 For more details and examples on how to use view transitions, see the ViewTransition
2485 \sa moveDisplaced, ViewTransition
2489 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2491 This property holds the transition to apply to items that are displaced by a move operation in
2492 the view's \l model.
2494 For example, here is a view that specifies such a transition:
2499 moveDisplaced: Transition {
2500 NumberAnimation { properties: "x,y"; duration: 1000 }
2505 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2506 between the source and destination indexes of the move operation are displaced, causing them
2507 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2508 displacement occurs, the items' movement to their new x,y positions within the view will be
2509 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2510 the items that are the actual subjects of the move operation; to animate the moved items, set
2511 the \l move property.
2513 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2514 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2515 if it is not necessary to specify different transitions depending on whether an item is displaced
2516 by an add, move or remove operation, consider setting the \l displaced property instead.
2518 For more details and examples on how to use view transitions, see the ViewTransition
2521 \sa displaced, move, ViewTransition
2525 \qmlproperty Transition QtQuick2::ListView::remove
2527 This property holds the transition to apply to items that are removed from the view.
2529 For example, here is a view that specifies such a transition:
2534 remove: Transition {
2536 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2537 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2543 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2544 over one second, and in parallel will also change its opacity to 0. The transition
2545 only applies to the items that are removed from the view; it does not apply to the items below
2546 them that are displaced by the removal of the items. To animate the displaced items, set the
2547 \l displaced or \l removeDisplaced properties.
2549 Note that by the time the transition is applied, the item has already been removed from the
2550 model; any references to the model data for the removed index will not be valid.
2552 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2553 remove transition will not be applied until \l delayRemove becomes false again.
2555 For more details and examples on how to use view transitions, see the ViewTransition
2558 \sa removeDisplaced, ViewTransition
2562 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2564 This property holds the transition to apply to items in the view that are displaced by the
2565 removal of other items in the view.
2567 For example, here is a view that specifies such a transition:
2572 removeDisplaced: Transition {
2573 NumberAnimation { properties: "x,y"; duration: 1000 }
2578 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2579 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2580 displacement occurs, the items' movement to their new x,y positions within the view will be
2581 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2582 the item that has actually been removed from the view; to animate the removed items, set the
2585 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2586 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2587 if it is not necessary to specify different transitions depending on whether an item is displaced
2588 by an add, move or remove operation, consider setting the \l displaced property instead.
2590 For more details and examples on how to use view transitions, see the ViewTransition
2593 \sa displaced, remove, ViewTransition
2597 \qmlproperty Transition QtQuick2::ListView::displaced
2598 This property holds the generic transition to apply to items that have been displaced by
2599 any model operation that affects the view.
2601 This is a convenience for specifying the generic transition to be applied to any items
2602 that are displaced by an add, move or remove operation, without having to specify the
2603 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2604 is a view that specifies a displaced transition:
2609 displaced: Transition {
2610 NumberAnimation { properties: "x,y"; duration: 1000 }
2615 When any item is added, moved or removed within the above view, the items below it are
2616 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2617 view. As this displacement occurs, the items' movement to their new x,y positions within
2618 the view will be animated by a NumberAnimation over one second, as specified.
2620 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2621 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2622 instead of the generic displaced transition when the relevant operation occurs, providing that
2623 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2624 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2626 For more details and examples on how to use view transitions, see the ViewTransition
2629 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2632 void QQuickListView::viewportMoved(Qt::Orientations orient)
2634 Q_D(QQuickListView);
2635 QQuickItemView::viewportMoved(orient);
2638 // Recursion can occur due to refill changing the content size.
2639 if (d->inViewportMoved)
2641 d->inViewportMoved = true;
2644 if (d->isBottomToTop())
2645 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2647 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2649 if (d->isRightToLeft())
2650 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2652 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2655 d->refillOrLayout();
2657 // Set visibility of items to eliminate cost of items outside the visible area.
2658 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2659 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2660 for (int i = 0; i < d->visibleItems.count(); ++i) {
2661 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2662 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2665 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2667 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2668 d->moveReason = QQuickListViewPrivate::Mouse;
2669 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2670 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2671 // reposition highlight
2672 qreal pos = d->highlight->position();
2673 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2674 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2675 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2676 if (pos < viewPos + d->highlightRangeStart)
2677 pos = viewPos + d->highlightRangeStart;
2678 if (pos != d->highlight->position()) {
2679 d->highlightPosAnimator->stop();
2680 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2682 d->updateHighlight();
2685 // update current index
2686 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2687 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2688 d->updateCurrent(snapItem->index);
2693 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2694 d->inFlickCorrection = true;
2695 // Near an end and it seems that the extent has changed?
2696 // Recalculate the flick so that we don't end up in an odd position.
2697 if (yflick() && !d->vData.inOvershoot) {
2698 if (d->vData.velocity > 0) {
2699 const qreal minY = minYExtent();
2700 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2701 && minY != d->vData.flickTarget)
2702 d->flickY(-d->vData.smoothVelocity.value());
2703 } else if (d->vData.velocity < 0) {
2704 const qreal maxY = maxYExtent();
2705 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2706 && maxY != d->vData.flickTarget)
2707 d->flickY(-d->vData.smoothVelocity.value());
2711 if (xflick() && !d->hData.inOvershoot) {
2712 if (d->hData.velocity > 0) {
2713 const qreal minX = minXExtent();
2714 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2715 && minX != d->hData.flickTarget)
2716 d->flickX(-d->hData.smoothVelocity.value());
2717 } else if (d->hData.velocity < 0) {
2718 const qreal maxX = maxXExtent();
2719 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2720 && maxX != d->hData.flickTarget)
2721 d->flickX(-d->hData.smoothVelocity.value());
2724 d->inFlickCorrection = false;
2726 if (d->sectionCriteria) {
2727 d->updateCurrentSection();
2728 d->updateStickySections();
2730 d->inViewportMoved = false;
2733 void QQuickListView::keyPressEvent(QKeyEvent *event)
2735 Q_D(QQuickListView);
2736 if (d->model && d->model->count() && d->interactive) {
2737 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2738 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2739 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2740 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2741 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2742 decrementCurrentIndex();
2745 } else if (d->wrap) {
2749 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2750 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2751 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2752 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2753 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2754 incrementCurrentIndex();
2757 } else if (d->wrap) {
2764 QQuickItemView::keyPressEvent(event);
2767 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2769 Q_D(QQuickListView);
2770 if (d->isRightToLeft()) {
2771 // maintain position relative to the right edge
2772 int dx = newGeometry.width() - oldGeometry.width();
2773 setContentX(contentX() - dx);
2774 } else if (d->isBottomToTop()) {
2775 // maintain position relative to the bottom edge
2776 int dy = newGeometry.height() - oldGeometry.height();
2777 setContentY(contentY() - dy);
2779 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2782 void QQuickListView::initItem(int index, QQuickItem *item)
2784 QQuickItemView::initItem(index, item);
2785 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
2786 qmlAttachedPropertiesObject<QQuickListView>(item));
2788 attached->setView(this);
2793 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2795 Increments the current index. The current index will wrap
2796 if keyNavigationWraps is true and it is currently at the end.
2797 This method has no effect if the \l count is zero.
2799 \b Note: methods should only be called after the Component has completed.
2801 void QQuickListView::incrementCurrentIndex()
2803 Q_D(QQuickListView);
2804 int count = d->model ? d->model->count() : 0;
2805 if (count && (currentIndex() < count - 1 || d->wrap)) {
2806 d->moveReason = QQuickListViewPrivate::SetIndex;
2807 int index = currentIndex()+1;
2808 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2813 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2815 Decrements the current index. The current index will wrap
2816 if keyNavigationWraps is true and it is currently at the beginning.
2817 This method has no effect if the \l count is zero.
2819 \b Note: methods should only be called after the Component has completed.
2821 void QQuickListView::decrementCurrentIndex()
2823 Q_D(QQuickListView);
2824 int count = d->model ? d->model->count() : 0;
2825 if (count && (currentIndex() > 0 || d->wrap)) {
2826 d->moveReason = QQuickListViewPrivate::SetIndex;
2827 int index = currentIndex()-1;
2828 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2832 void QQuickListView::updateSections()
2834 Q_D(QQuickListView);
2835 if (isComponentComplete() && d->model) {
2836 QList<QByteArray> roles;
2837 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2838 roles << d->sectionCriteria->property().toUtf8();
2839 d->model->setWatchedRoles(roles);
2840 d->updateSections();
2842 d->forceLayout = true;
2848 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2850 int modelIndex = change.index;
2851 int count = change.count;
2853 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2854 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2857 int i = visibleItems.count() - 1;
2858 while (i > 0 && visibleItems.at(i)->index == -1)
2860 if (i == 0 && visibleItems.first()->index == -1) {
2861 // there are no visible items except items marked for removal
2862 index = visibleItems.count();
2863 } else if (visibleItems.at(i)->index + 1 == modelIndex
2864 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2865 // Special case of appending an item to the model.
2866 index = visibleItems.count();
2868 if (modelIndex < visibleIndex) {
2869 // Insert before visible items
2870 visibleIndex += count;
2871 for (int i = 0; i < visibleItems.count(); ++i) {
2872 FxViewItem *item = visibleItems.at(i);
2873 if (item->index != -1 && item->index >= modelIndex)
2874 item->index += count;
2881 // index can be the next item past the end of the visible items list (i.e. appended)
2883 if (visibleItems.count()) {
2884 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2885 : visibleItems.last()->endPosition()+spacing;
2888 int prevVisibleCount = visibleItems.count();
2889 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2890 // Insert items before the visible item.
2891 int insertionIdx = index;
2893 int from = tempPos - buffer;
2895 for (i = count-1; i >= 0; --i) {
2896 if (pos > from && insertionIdx < visibleIndex) {
2897 // item won't be visible, just note the size for repositioning
2898 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2899 pos -= averageSize + spacing;
2901 // item is before first visible e.g. in cache buffer
2902 FxViewItem *item = 0;
2903 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2904 item->index = modelIndex + i;
2906 item = createItem(modelIndex + i);
2910 visibleItems.insert(insertionIdx, item);
2911 if (insertionIdx == 0)
2912 insertResult->changedFirstItem = true;
2913 if (!change.isMove()) {
2914 addedItems->append(item);
2915 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2917 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2918 pos -= item->size() + spacing;
2924 int to = buffer+tempPos+size();
2925 for (i = 0; i < count && pos <= to; ++i) {
2926 FxViewItem *item = 0;
2927 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2928 item->index = modelIndex + i;
2929 bool newItem = !item;
2931 item = createItem(modelIndex + i);
2935 visibleItems.insert(index, item);
2937 insertResult->changedFirstItem = true;
2938 if (change.isMove()) {
2939 // we know this is a move target, since move displaced items that are
2940 // shuffled into view due to a move would be added in refill()
2941 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2942 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2944 addedItems->append(item);
2945 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2947 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2948 pos += item->size() + spacing;
2953 for (; index < visibleItems.count(); ++index) {
2954 FxViewItem *item = visibleItems.at(index);
2955 if (item->index != -1) {
2956 item->index += count;
2957 if (change.isMove())
2958 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2960 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2964 updateVisibleIndex();
2966 return visibleItems.count() > prevVisibleCount;
2969 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2971 Q_UNUSED(insertionResult);
2976 int markerItemIndex = -1;
2977 for (int i=0; i<visibleItems.count(); i++) {
2978 if (visibleItems[i]->index == afterModelIndex) {
2979 markerItemIndex = i;
2983 if (markerItemIndex < 0)
2986 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2987 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
2988 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
2990 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2991 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
2992 if (!listItem->transitionScheduledOrRunning()) {
2993 qreal pos = listItem->position();
2994 listItem->setPosition(pos - sizeRemoved);
2995 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2996 listItem->setPosition(pos);
3002 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
3004 Positions the view such that the \a index is at the position specified by
3008 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
3009 \li ListView.Center - position item in the center of the view.
3010 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3011 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
3012 bring the item into view.
3013 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
3014 the view the item is positioned at the top (or left for horizontal orientation) of the view.
3017 If positioning the view at \a index would cause empty space to be displayed at
3018 the beginning or end of the view, the view will be positioned at the boundary.
3020 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3021 at a particular index. This is unreliable since removing items from the start
3022 of the list does not cause all other items to be repositioned, and because
3023 the actual start of the view can vary based on the size of the delegates.
3024 The correct way to bring an item into view is with \c positionViewAtIndex.
3026 \b Note: methods should only be called after the Component has completed. To position
3027 the view at startup, this method should be called by Component.onCompleted. For
3028 example, to position the view at the end:
3031 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3036 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
3037 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
3039 Positions the view at the beginning or end, taking into account any header or footer.
3041 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3042 at a particular index. This is unreliable since removing items from the start
3043 of the list does not cause all other items to be repositioned, and because
3044 the actual start of the view can vary based on the size of the delegates.
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 on startup:
3051 Component.onCompleted: positionViewAtEnd()
3056 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3058 Returns the index of the visible item containing the point \a x, \a y in content
3059 coordinates. If there is no item at the point specified, or the item is
3060 not visible -1 is returned.
3062 If the item is outside the visible area, -1 is returned, regardless of
3063 whether an item will exist at that point when scrolled into view.
3065 \b Note: methods should only be called after the Component has completed.
3069 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3071 Returns the visible item containing the point \a x, \a y in content
3072 coordinates. If there is no item at the point specified, or the item is
3073 not visible null is returned.
3075 If the item is outside the visible area, null is returned, regardless of
3076 whether an item will exist at that point when scrolled into view.
3078 \b Note: methods should only be called after the Component has completed.
3081 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3083 return new QQuickListViewAttached(obj);