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, 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 static_cast<QQuickListViewAttached*>(attached)->setView(view);
245 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
246 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
252 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
253 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
257 inline QQuickItem *section() const {
258 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
260 void setSection(QQuickItem *s) {
262 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
263 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
266 qreal position() const {
268 if (view->orientation() == QQuickListView::Vertical)
269 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
271 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
273 return itemPosition();
276 qreal itemPosition() const {
277 if (view->orientation() == QQuickListView::Vertical)
278 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
280 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
284 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
286 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
288 qreal itemSize() const {
289 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
291 qreal sectionSize() const {
293 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
296 qreal endPosition() const {
297 if (view->orientation() == QQuickListView::Vertical) {
298 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
300 : itemY() + item->height());
302 return (view->effectiveLayoutDirection() == Qt::RightToLeft
304 : itemX() + item->width());
307 void setPosition(qreal pos, bool immediate = false) {
308 // position the section immediately even if there is a transition
310 if (view->orientation() == QQuickListView::Vertical) {
311 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
312 section()->setY(-section()->height()-pos);
314 section()->setY(pos);
316 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
317 section()->setX(-section()->width()-pos);
319 section()->setX(pos);
322 moveTo(pointForPosition(pos), immediate);
324 void setSize(qreal size) {
325 if (view->orientation() == QQuickListView::Vertical)
326 item->setHeight(size);
328 item->setWidth(size);
330 bool contains(qreal x, qreal y) const {
331 return (x >= itemX() && x < itemX() + item->width() &&
332 y >= itemY() && y < itemY() + item->height());
335 QQuickListView *view;
338 QPointF pointForPosition(qreal pos) const {
339 if (view->orientation() == QQuickListView::Vertical) {
340 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
342 pos += section()->height();
343 return QPointF(itemX(), -item->height() - pos);
346 pos += section()->height();
347 return QPointF(itemX(), pos);
350 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
352 pos += section()->width();
353 return QPointF(-item->width() - pos, itemY());
356 pos += section()->width();
357 return QPointF(pos, itemY());
363 //----------------------------------------------------------------------------
365 bool QQuickListViewPrivate::isContentFlowReversed() const
367 return isRightToLeft() || isBottomToTop();
370 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
372 return static_cast<Qt::Orientation>(orient);
375 bool QQuickListViewPrivate::isRightToLeft() const
377 Q_Q(const QQuickListView);
378 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
381 bool QQuickListViewPrivate::isBottomToTop() const
383 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
386 // Returns the item before modelIndex, if created.
387 // May return an item marked for removal.
388 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
390 if (modelIndex < visibleIndex)
394 while (idx < visibleItems.count()) {
395 FxViewItem *item = visibleItems.at(idx);
396 if (item->index != -1)
397 lastIndex = item->index;
398 if (item->index == modelIndex)
399 return visibleItems.at(idx-1);
402 if (lastIndex == modelIndex-1)
403 return visibleItems.last();
407 void QQuickListViewPrivate::setPosition(qreal pos)
410 if (orient == QQuickListView::Vertical) {
412 q->QQuickFlickable::setContentY(-pos-size());
414 q->QQuickFlickable::setContentY(pos);
417 q->QQuickFlickable::setContentX(-pos-size());
419 q->QQuickFlickable::setContentX(pos);
423 qreal QQuickListViewPrivate::originPosition() const
426 if (!visibleItems.isEmpty()) {
427 pos = (*visibleItems.constBegin())->position();
428 if (visibleIndex > 0)
429 pos -= visibleIndex * (averageSize + spacing);
434 qreal QQuickListViewPrivate::lastPosition() const
437 if (!visibleItems.isEmpty()) {
438 int invisibleCount = visibleItems.count() - visibleIndex;
439 for (int i = visibleItems.count()-1; i >= 0; --i) {
440 if (visibleItems.at(i)->index != -1) {
441 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
445 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
446 } else if (model && model->count()) {
447 pos = (model->count() * averageSize + (model->count()-1) * spacing);
452 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
454 if (FxViewItem *item = visibleItem(modelIndex)) {
455 return item->position();
457 if (!visibleItems.isEmpty()) {
458 if (modelIndex < visibleIndex) {
459 int count = visibleIndex - modelIndex;
461 if (modelIndex == currentIndex && currentItem) {
462 cs = currentItem->size() + spacing;
465 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
467 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
468 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
474 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
476 if (FxViewItem *item = visibleItem(modelIndex))
477 return item->endPosition();
478 if (!visibleItems.isEmpty()) {
479 if (modelIndex < visibleIndex) {
480 int count = visibleIndex - modelIndex;
481 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
483 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
484 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
490 QString QQuickListViewPrivate::sectionAt(int modelIndex)
492 if (FxViewItem *item = visibleItem(modelIndex))
493 return item->attached->section();
496 if (sectionCriteria) {
497 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
498 section = sectionCriteria->sectionString(propValue);
504 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
506 if (FxViewItem *snapItem = snapItemAt(pos))
507 return snapItem->position();
508 if (visibleItems.count()) {
509 qreal firstPos = (*visibleItems.constBegin())->position();
510 qreal endPos = (*(--visibleItems.constEnd()))->position();
511 if (pos < firstPos) {
512 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
513 } else if (pos > endPos)
514 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
516 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
519 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
521 FxViewItem *snapItem = 0;
522 qreal prevItemSize = 0;
523 for (int i = 0; i < visibleItems.count(); ++i) {
524 FxViewItem *item = visibleItems.at(i);
525 if (item->index == -1)
527 qreal itemTop = item->position();
528 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
530 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
532 prevItemSize = item->size();
537 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
539 visiblePos = positionAt(newIndex);
540 visibleIndex = newIndex;
543 void QQuickListViewPrivate::init()
545 QQuickItemViewPrivate::init();
546 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
549 void QQuickListViewPrivate::clear()
551 for (int i = 0; i < sectionCacheSize; ++i) {
552 delete sectionCache[i];
556 releaseSectionItem(currentSectionItem);
557 currentSectionItem = 0;
558 releaseSectionItem(nextSectionItem);
560 lastVisibleSection = QString();
561 QQuickItemViewPrivate::clear();
564 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
568 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
569 listItem->index = modelIndex;
571 // initialise attached properties
572 if (sectionCriteria) {
573 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
574 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
575 if (modelIndex > 0) {
576 if (FxViewItem *item = itemBefore(modelIndex))
577 listItem->attached->m_prevSection = item->attached->section();
579 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
581 if (modelIndex < model->count()-1) {
582 if (FxViewItem *item = visibleItem(modelIndex+1))
583 listItem->attached->m_nextSection = static_cast<QQuickListViewAttached*>(item->attached)->section();
585 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
592 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
594 QQuickItemViewPrivate::initializeViewItem(item);
596 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
597 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
599 if (sectionCriteria && sectionCriteria->delegate()) {
600 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
601 updateInlineSection(static_cast<FxListItemSG*>(item));
605 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
610 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
612 bool released = QQuickItemViewPrivate::releaseItem(item);
613 if (released && att && att->m_sectionItem) {
614 // We hold no more references to this item
617 if (!sectionCache[i]) {
618 sectionCache[i] = att->m_sectionItem;
619 sectionCache[i]->setVisible(false);
620 att->m_sectionItem = 0;
624 } while (i < sectionCacheSize);
625 delete att->m_sectionItem;
626 att->m_sectionItem = 0;
632 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
634 qreal itemEnd = visiblePos;
635 if (visibleItems.count()) {
636 visiblePos = (*visibleItems.constBegin())->position();
637 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
640 int modelIndex = findLastVisibleIndex();
641 bool haveValidItems = modelIndex >= 0;
642 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
644 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
645 || fillTo < visiblePos - averageSize - spacing)) {
646 // We've jumped more than a page. Estimate which items are now
647 // visible and fill from there.
648 int count = (fillFrom - itemEnd) / (averageSize + spacing);
649 int newModelIdx = qBound(0, modelIndex + count, model->count());
650 count = newModelIdx - modelIndex;
652 for (int i = 0; i < visibleItems.count(); ++i)
653 releaseItem(visibleItems.at(i));
654 visibleItems.clear();
655 modelIndex = newModelIdx;
656 visibleIndex = modelIndex;
657 visiblePos = itemEnd + count * (averageSize + spacing);
658 itemEnd = visiblePos;
662 bool changed = false;
663 FxListItemSG *item = 0;
665 while (modelIndex < model->count() && pos <= fillTo) {
666 #ifdef DEBUG_DELEGATE_LIFECYCLE
667 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
669 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
671 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
672 item->setPosition(pos, true);
673 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
674 pos += item->size() + spacing;
675 visibleItems.append(item);
680 if (doBuffer && requestedIndex != -1) // already waiting for an item
683 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
684 #ifdef DEBUG_DELEGATE_LIFECYCLE
685 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
687 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
690 visiblePos -= item->size() + spacing;
691 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
692 item->setPosition(visiblePos, true);
693 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
694 visibleItems.prepend(item);
701 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
703 FxViewItem *item = 0;
704 bool changed = false;
706 // Remove items from the start of the view.
707 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
708 // removed, otherwise a zero-sized item is infinitely added and removed over and
711 while (visibleItems.count() > 1 && index < visibleItems.count()
712 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
713 if (item->attached->delayRemove())
716 if (item->size() > 0) {
717 #ifdef DEBUG_DELEGATE_LIFECYCLE
718 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
720 // remove this item and all zero-sized items before it
722 if (item->index != -1)
724 visibleItems.removeAt(index);
725 if (item->transitionScheduledOrRunning()) {
726 #ifdef DEBUG_DELEGATE_LIFECYCLE
727 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
729 item->releaseAfterTransition = true;
730 releasePendingTransition.append(item);
736 item = visibleItems.at(--index);
744 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
745 if (item->attached->delayRemove())
747 #ifdef DEBUG_DELEGATE_LIFECYCLE
748 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
750 visibleItems.removeLast();
751 if (item->transitionScheduledOrRunning()) {
752 #ifdef DEBUG_DELEGATE_LIFECYCLE
753 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
755 item->releaseAfterTransition = true;
756 releasePendingTransition.append(item);
766 void QQuickListViewPrivate::visibleItemsChanged()
768 if (visibleItems.count())
769 visiblePos = (*visibleItems.constBegin())->position();
771 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
772 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
776 updateCurrentSection();
777 updateUnrequestedPositions();
780 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
782 if (!visibleItems.isEmpty()) {
783 const qreal from = isContentFlowReversed() ? -position() - size() : position();
784 const qreal to = isContentFlowReversed() ? -position() : position() + size();
786 FxViewItem *firstItem = *visibleItems.constBegin();
787 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
788 qreal sum = firstItem->size();
789 qreal pos = firstItem->position() + firstItem->size() + spacing;
790 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
792 for (int i=1; i < visibleItems.count(); ++i) {
793 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
794 if (item->index >= fromModelIndex) {
795 item->setPosition(pos);
796 item->setVisible(item->endPosition() >= from && item->position() <= to);
798 pos += item->size() + spacing;
800 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
802 averageSize = qRound(sum / visibleItems.count());
804 // move current item if it is not a visible item.
805 if (currentIndex >= 0 && currentItem && !fixedCurrent)
806 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
810 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
812 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
815 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
818 qreal pos = position();
819 if (orient == QQuickListView::Vertical) {
820 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
822 item->setY(-positionAt(index)-item->height());
824 item->setY(positionAt(index));
827 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
829 item->setX(-positionAt(index)-item->width());
831 item->setX(positionAt(index));
836 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
838 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
839 item->setPosition(pos);
842 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
844 if (!visibleItems.count())
846 qreal diff = forwards - backwards;
847 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
850 void QQuickListViewPrivate::createHighlight()
853 bool changed = false;
855 if (trackedItem == highlight)
860 delete highlightPosAnimator;
861 delete highlightSizeAnimator;
862 highlightPosAnimator = 0;
863 highlightSizeAnimator = 0;
869 QQuickItem *item = createHighlightItem();
871 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
874 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
875 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
877 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
878 highlightPosAnimator = new QSmoothedAnimation;
879 highlightPosAnimator->target = QQmlProperty(item, posProp);
880 highlightPosAnimator->velocity = highlightMoveSpeed;
881 highlightPosAnimator->userDuration = highlightMoveDuration;
883 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
884 highlightSizeAnimator = new QSmoothedAnimation;
885 highlightSizeAnimator->velocity = highlightResizeSpeed;
886 highlightSizeAnimator->userDuration = highlightResizeDuration;
887 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
889 highlight = newHighlight;
894 emit q->highlightItemChanged();
897 void QQuickListViewPrivate::updateHighlight()
899 applyPendingChanges();
901 if ((!currentItem && highlight) || (currentItem && !highlight))
903 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
904 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
905 // auto-update highlight
906 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
907 highlightPosAnimator->to = isContentFlowReversed()
908 ? -listItem->itemPosition()-listItem->itemSize()
909 : listItem->itemPosition();
910 highlightSizeAnimator->to = listItem->itemSize();
911 if (orient == QQuickListView::Vertical) {
912 if (highlight->item->width() == 0)
913 highlight->item->setWidth(currentItem->item->width());
915 if (highlight->item->height() == 0)
916 highlight->item->setHeight(currentItem->item->height());
919 highlightPosAnimator->restart();
920 highlightSizeAnimator->restart();
925 void QQuickListViewPrivate::resetHighlightPosition()
927 if (highlight && currentItem)
928 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
931 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
934 QQuickItem *sectionItem = 0;
935 int i = sectionCacheSize-1;
936 while (i >= 0 && !sectionCache[i])
939 sectionItem = sectionCache[i];
941 sectionItem->setVisible(true);
942 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
943 context->setContextProperty(QLatin1String("section"), section);
945 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
946 QQmlContext *context = new QQmlContext(
947 creationContext ? creationContext : qmlContext(q));
948 context->setContextProperty(QLatin1String("section"), section);
949 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
951 QQml_setParent_noEvent(context, nobj);
952 sectionItem = qobject_cast<QQuickItem *>(nobj);
956 sectionItem->setZ(2);
957 QQml_setParent_noEvent(sectionItem, contentItem);
958 sectionItem->setParentItem(contentItem);
963 sectionCriteria->delegate()->completeCreate();
969 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
975 if (!sectionCache[i]) {
976 sectionCache[i] = item;
977 sectionCache[i]->setVisible(false);
981 } while (i < sectionCacheSize);
985 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
987 if (!sectionCriteria || !sectionCriteria->delegate())
989 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
990 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
991 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
992 if (!listItem->section()) {
993 qreal pos = listItem->position();
994 listItem->setSection(getSectionItem(listItem->attached->m_section));
995 listItem->setPosition(pos);
997 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
998 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1000 } else if (listItem->section()) {
1001 qreal pos = listItem->position();
1002 releaseSectionItem(listItem->section());
1003 listItem->setSection(0);
1004 listItem->setPosition(pos);
1008 void QQuickListViewPrivate::updateStickySections()
1010 if (!sectionCriteria
1011 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1014 bool isFlowReversed = isContentFlowReversed();
1015 qreal viewPos = isFlowReversed ? -position()-size() : position();
1016 QQuickItem *sectionItem = 0;
1017 QQuickItem *lastSectionItem = 0;
1019 while (index < visibleItems.count()) {
1020 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1021 // Find the current section header and last visible section header
1022 // and hide them if they will overlap a static section header.
1023 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1024 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1026 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1027 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1029 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1030 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1031 section->setVisible(visBot && visTop);
1032 if (visTop && !sectionItem)
1033 sectionItem = section;
1034 if (isFlowReversed) {
1035 if (-sectionPos <= viewPos + size())
1036 lastSectionItem = section;
1038 if (sectionPos + sectionSize < viewPos + size())
1039 lastSectionItem = section;
1045 // Current section header
1046 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1047 if (!currentSectionItem) {
1048 currentSectionItem = getSectionItem(currentSection);
1049 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1050 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1051 context->setContextProperty(QLatin1String("section"), currentSection);
1053 currentStickySection = currentSection;
1054 if (!currentSectionItem)
1057 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1058 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1060 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1061 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1063 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1064 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1067 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1069 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1070 if (orient == QQuickListView::Vertical)
1071 currentSectionItem->setY(pos);
1073 currentSectionItem->setX(pos);
1074 } else if (currentSectionItem) {
1075 releaseSectionItem(currentSectionItem);
1076 currentSectionItem = 0;
1079 // Next section footer
1080 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1081 if (!nextSectionItem) {
1082 nextSectionItem = getSectionItem(nextSection);
1083 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1084 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1085 context->setContextProperty(QLatin1String("section"), nextSection);
1087 nextStickySection = nextSection;
1088 if (!nextSectionItem)
1091 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1092 nextSectionItem->setVisible(!nextSection.isEmpty());
1093 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1094 if (lastSectionItem) {
1095 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1096 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1099 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1100 if (orient == QQuickListView::Vertical)
1101 nextSectionItem->setY(pos);
1103 nextSectionItem->setX(pos);
1104 } else if (nextSectionItem) {
1105 releaseSectionItem(nextSectionItem);
1106 nextSectionItem = 0;
1110 void QQuickListViewPrivate::updateSections()
1112 Q_Q(QQuickListView);
1113 if (!q->isComponentComplete())
1116 QQuickItemViewPrivate::updateSections();
1118 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1119 QString prevSection;
1120 if (visibleIndex > 0)
1121 prevSection = sectionAt(visibleIndex-1);
1122 QQuickListViewAttached *prevAtt = 0;
1124 for (int i = 0; i < visibleItems.count(); ++i) {
1125 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1126 attached->setPrevSection(prevSection);
1127 if (visibleItems.at(i)->index != -1) {
1128 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1129 attached->setSection(sectionCriteria->sectionString(propValue));
1130 idx = visibleItems.at(i)->index;
1132 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1134 prevAtt->setNextSection(attached->section());
1135 prevSection = attached->section();
1139 if (idx > 0 && idx < model->count()-1)
1140 prevAtt->setNextSection(sectionAt(idx+1));
1142 prevAtt->setNextSection(QString());
1146 lastVisibleSection = QString();
1147 updateCurrentSection();
1148 updateStickySections();
1151 void QQuickListViewPrivate::updateCurrentSection()
1153 Q_Q(QQuickListView);
1154 if (!sectionCriteria || visibleItems.isEmpty()) {
1155 if (!currentSection.isEmpty()) {
1156 currentSection.clear();
1157 emit q->currentSectionChanged();
1161 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1162 qreal sectionThreshold = position();
1163 if (currentSectionItem && !inlineSections)
1164 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1166 int modelIndex = visibleIndex;
1167 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1168 if (visibleItems.at(index)->index != -1)
1169 modelIndex = visibleItems.at(index)->index;
1173 QString newSection = currentSection;
1174 if (index < visibleItems.count())
1175 newSection = visibleItems.at(index)->attached->section();
1177 newSection = (*visibleItems.constBegin())->attached->section();
1178 if (newSection != currentSection) {
1179 currentSection = newSection;
1180 updateStickySections();
1181 emit q->currentSectionChanged();
1184 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1185 // Don't want to scan for next section on every movement, so remember
1186 // the last section in the visible area and only scan for the next
1187 // section when that changes. Clearing lastVisibleSection will also
1189 QString lastSection = currentSection;
1190 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1191 if (nextSectionItem && !inlineSections)
1192 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1193 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1194 if (visibleItems.at(index)->index != -1)
1195 modelIndex = visibleItems.at(index)->index;
1196 lastSection = visibleItems.at(index)->attached->section();
1200 if (lastVisibleSection != lastSection) {
1201 nextSection = QString();
1202 lastVisibleSection = lastSection;
1203 for (int i = modelIndex; i < itemCount; ++i) {
1204 QString section = sectionAt(i);
1205 if (section != lastSection) {
1206 nextSection = section;
1207 updateStickySections();
1215 void QQuickListViewPrivate::initializeCurrentItem()
1217 QQuickItemViewPrivate::initializeCurrentItem();
1220 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1222 // don't reposition the item if it is already in the visibleItems list
1223 FxViewItem *actualItem = visibleItem(currentIndex);
1225 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1226 // We can calculate exact postion in this case
1227 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1229 // Create current item now and position as best we can.
1230 // Its position will be corrected when it becomes visible.
1231 listItem->setPosition(positionAt(currentIndex));
1235 if (visibleItems.isEmpty())
1236 averageSize = listItem->size();
1240 void QQuickListViewPrivate::updateAverage()
1242 if (!visibleItems.count())
1245 for (int i = 0; i < visibleItems.count(); ++i)
1246 sum += visibleItems.at(i)->size();
1247 averageSize = qRound(sum / visibleItems.count());
1250 qreal QQuickListViewPrivate::headerSize() const
1252 return header ? header->size() : 0.0;
1255 qreal QQuickListViewPrivate::footerSize() const
1257 return footer ? footer->size() : 0.0;
1260 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1265 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1267 return index == model->count()-1;
1270 void QQuickListViewPrivate::updateFooter()
1272 Q_Q(QQuickListView);
1273 bool created = false;
1275 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1278 footer = new FxListItemSG(item, q, true, true);
1282 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1283 if (visibleItems.count()) {
1284 qreal endPos = lastPosition();
1285 if (findLastVisibleIndex() == model->count()-1) {
1286 listItem->setPosition(endPos);
1288 qreal visiblePos = position() + q->height();
1289 if (endPos <= visiblePos || listItem->position() < endPos)
1290 listItem->setPosition(endPos);
1293 listItem->setPosition(visiblePos);
1297 emit q->footerItemChanged();
1300 void QQuickListViewPrivate::updateHeader()
1302 Q_Q(QQuickListView);
1303 bool created = false;
1305 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1308 header = new FxListItemSG(item, q, true, true);
1312 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1314 if (visibleItems.count()) {
1315 qreal startPos = originPosition();
1316 if (visibleIndex == 0) {
1317 listItem->setPosition(startPos - headerSize());
1319 if (position() <= startPos || listItem->position() > startPos - headerSize())
1320 listItem->setPosition(startPos - headerSize());
1323 listItem->setPosition(-headerSize());
1328 emit q->headerItemChanged();
1331 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1333 Q_Q(QQuickListView);
1334 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1335 if (!q->isComponentComplete())
1338 if (item != contentItem && (!highlight || item != highlight->item)) {
1339 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1340 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1342 // if visibleItems.first() has resized, adjust its pos since it is used to
1343 // position all subsequent items
1344 if (visibleItems.count() && item == visibleItems.first()->item) {
1345 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1346 if (orient == QQuickListView::Vertical) {
1347 qreal diff = newGeometry.height() - oldGeometry.height();
1348 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1349 listItem->setPosition(listItem->position() - diff, true);
1350 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1351 listItem->setPosition(listItem->position() + diff, true);
1353 qreal diff = newGeometry.width() - oldGeometry.width();
1354 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1355 listItem->setPosition(listItem->position() - diff, true);
1356 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1357 listItem->setPosition(listItem->position() + diff, true);
1366 void QQuickListViewPrivate::fixupPosition()
1368 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1369 || snapMode != QQuickListView::NoSnap)
1371 if (orient == QQuickListView::Vertical)
1377 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1379 if ((orient == QQuickListView::Horizontal && &data == &vData)
1380 || (orient == QQuickListView::Vertical && &data == &hData))
1383 correctFlick = false;
1384 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1385 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1387 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1389 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1390 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1391 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1392 // if we've been dragged < averageSize/2 then bias towards the next item
1393 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1395 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1396 bias = averageSize/2;
1397 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1398 bias = -averageSize/2;
1399 if (isContentFlowReversed())
1401 tempPosition -= bias;
1403 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1404 if (!topItem && strictHighlightRange && currentItem) {
1405 // StrictlyEnforceRange always keeps an item in range
1407 topItem = currentItem;
1409 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1410 if (!bottomItem && strictHighlightRange && currentItem) {
1411 // StrictlyEnforceRange always keeps an item in range
1413 bottomItem = currentItem;
1416 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1417 if (topItem && (isInBounds || strictHighlightRange)) {
1418 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1419 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1421 if (isContentFlowReversed())
1422 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1424 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1426 } else if (bottomItem && isInBounds) {
1427 if (isContentFlowReversed())
1428 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1430 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1432 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1436 qreal dist = qAbs(data.move + pos);
1438 timeline.reset(data.move);
1439 if (fixupMode != Immediate) {
1440 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1441 data.fixingUp = true;
1443 timeline.set(data.move, -pos);
1445 vTime = timeline.time();
1447 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1449 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1450 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1451 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1452 if (viewPos > pos - highlightRangeStart)
1453 viewPos = pos - highlightRangeStart;
1454 if (isContentFlowReversed())
1455 viewPos = -viewPos-size();
1457 timeline.reset(data.move);
1458 if (viewPos != position()) {
1459 if (fixupMode != Immediate) {
1460 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1461 data.fixingUp = true;
1463 timeline.set(data.move, -viewPos);
1466 vTime = timeline.time();
1468 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1470 data.inOvershoot = false;
1474 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1475 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1477 data.fixingUp = false;
1479 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1480 correctFlick = true;
1481 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1483 qreal maxDistance = 0;
1484 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1486 // -ve velocity means list is moving up/left
1488 if (data.move.value() < minExtent) {
1489 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1490 // if we've been dragged < averageSize/2 then bias towards the next item
1491 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1492 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1493 if (isContentFlowReversed())
1495 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1496 maxDistance = qAbs(data.flickTarget - data.move.value());
1497 velocity = maxVelocity;
1499 maxDistance = qAbs(minExtent - data.move.value());
1502 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1503 data.flickTarget = minExtent;
1505 if (data.move.value() > maxExtent) {
1506 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1507 // if we've been dragged < averageSize/2 then bias towards the next item
1508 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1509 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1510 if (isContentFlowReversed())
1512 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1513 maxDistance = qAbs(data.flickTarget - data.move.value());
1514 velocity = -maxVelocity;
1516 maxDistance = qAbs(maxExtent - data.move.value());
1519 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1520 data.flickTarget = maxExtent;
1522 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1523 if (maxDistance > 0 || overShoot) {
1524 // These modes require the list to stop exactly on an item boundary.
1525 // The initial flick will estimate the boundary to stop on.
1526 // Since list items can have variable sizes, the boundary will be
1527 // reevaluated and adjusted as we approach the boundary.
1529 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1535 if (!hData.flicking && !vData.flicking) {
1536 // the initial flick - estimate boundary
1537 qreal accel = deceleration;
1539 overshootDist = 0.0;
1540 // + averageSize/4 to encourage moving at least one item in the flick direction
1541 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1542 if (maxDistance > 0)
1543 dist = qMin(dist, maxDistance);
1546 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1547 if (snapMode != QQuickListView::SnapOneItem) {
1548 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1549 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1551 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1553 if (data.flickTarget >= minExtent) {
1554 overshootDist = overShootDistance(vSize);
1555 data.flickTarget += overshootDist;
1556 } else if (data.flickTarget <= maxExtent) {
1557 overshootDist = overShootDistance(vSize);
1558 data.flickTarget -= overshootDist;
1561 qreal adjDist = -data.flickTarget + data.move.value();
1562 if (qAbs(adjDist) > qAbs(dist)) {
1563 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1564 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1573 accel = v2 / (2.0f * qAbs(dist));
1574 } else if (overShoot) {
1575 data.flickTarget = data.move.value() - dist;
1576 if (data.flickTarget >= minExtent) {
1577 overshootDist = overShootDistance(vSize);
1578 data.flickTarget += overshootDist;
1579 } else if (data.flickTarget <= maxExtent) {
1580 overshootDist = overShootDistance(vSize);
1581 data.flickTarget -= overshootDist;
1584 timeline.reset(data.move);
1585 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1586 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1587 correctFlick = true;
1590 // reevaluate the target boundary.
1591 qreal newtarget = data.flickTarget;
1592 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1593 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1594 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1595 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1597 if (velocity < 0 && newtarget <= maxExtent)
1598 newtarget = maxExtent - overshootDist;
1599 else if (velocity > 0 && newtarget >= minExtent)
1600 newtarget = minExtent + overshootDist;
1601 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1602 if (qAbs(velocity) < MinimumFlickVelocity)
1603 correctFlick = false;
1606 data.flickTarget = newtarget;
1607 qreal dist = -newtarget + data.move.value();
1608 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1609 correctFlick = false;
1610 timeline.reset(data.move);
1611 fixup(data, minExtent, maxExtent);
1614 timeline.reset(data.move);
1615 timeline.accelDistance(data.move, v, -dist);
1616 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1620 correctFlick = false;
1621 timeline.reset(data.move);
1622 fixup(data, minExtent, maxExtent);
1627 //----------------------------------------------------------------------------
1630 \qmlclass ListView QQuickListView
1631 \inqmlmodule QtQuick 2
1632 \ingroup qtquick-views
1634 \brief Provides a list view of items provided by a model
1636 A ListView displays data from models created from built-in QML elements like ListModel
1637 and XmlListModel, or custom model classes defined in C++ that inherit from
1640 A ListView has a \l model, which defines the data to be displayed, and
1641 a \l delegate, which defines how the data should be displayed. Items in a
1642 ListView are laid out horizontally or vertically. List views are inherently
1643 flickable because ListView inherits from \l Flickable.
1645 \section1 Example Usage
1647 The following example shows the definition of a simple list model defined
1648 in a file called \c ContactModel.qml:
1650 \snippet qml/listview/ContactModel.qml 0
1652 Another component can display this model data in a ListView, like this:
1654 \snippet qml/listview/listview.qml import
1656 \snippet qml/listview/listview.qml classdocs simple
1658 \image listview-simple.png
1660 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1661 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1662 the delegate is able to access the model's \c name and \c number data directly.
1664 An improved list view is shown below. The delegate is visually improved and is moved
1665 into a separate \c contactDelegate component.
1667 \snippet qml/listview/listview.qml classdocs advanced
1668 \image listview-highlight.png
1670 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1671 and \c focus is set to \c true to enable keyboard navigation for the list view.
1672 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1674 Delegates are instantiated as needed and may be destroyed at any time.
1675 State should \e never be stored in a delegate.
1677 ListView attaches a number of properties to the root item of the delegate, for example
1678 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1679 this attached property directly as \c ListView.isCurrentItem, while the child
1680 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1682 \snippet qml/listview/listview.qml isCurrentItem
1684 \note Views do not enable \e clip automatically. If the view
1685 is not clipped by another item or the screen, it will be necessary
1686 to set \e {clip: true} in order to have the out of view items clipped
1690 \section1 ListView layouts
1692 The layout of the items in a ListView can be controlled by these properties:
1695 \li \l orientation - controls whether items flow horizontally or vertically.
1696 This value can be either Qt.Horizontal or Qt.Vertical.
1697 \li \l layoutDirection - controls the horizontal layout direction for a
1698 horizontally-oriented view: that is, whether items are laid out from the left side of
1699 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1700 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1701 view: that is, whether items are laid out from the top of the view down towards the bottom of
1702 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1705 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1706 table below shows the different layouts that a ListView can have, depending on the values of
1707 the properties listed above.
1712 \bold ListViews with Qt.Vertical orientation
1715 \image listview-layout-toptobottom.png
1717 \image listview-layout-bottomtotop.png
1720 \bold ListViews with Qt.Horizontal orientation
1723 \image listview-layout-lefttoright.png
1725 \image listview-layout-righttoleft.png
1728 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1730 QQuickListView::QQuickListView(QQuickItem *parent)
1731 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1735 QQuickListView::~QQuickListView()
1740 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1741 This attached property is true if this delegate is the current item; otherwise false.
1743 It is attached to each instance of the delegate.
1745 This property may be used to adjust the appearance of the current item, for example:
1747 \snippet qml/listview/listview.qml isCurrentItem
1751 \qmlattachedproperty ListView QtQuick2::ListView::view
1752 This attached property holds the view that manages this delegate instance.
1754 It is attached to each instance of the delegate.
1758 \qmlattachedproperty string QtQuick2::ListView::previousSection
1759 This attached property holds the section of the previous element.
1761 It is attached to each instance of the delegate.
1763 The section is evaluated using the \l {ListView::section.property}{section} properties.
1767 \qmlattachedproperty string QtQuick2::ListView::nextSection
1768 This attached property holds the section of the next element.
1770 It is attached to each instance of the delegate.
1772 The section is evaluated using the \l {ListView::section.property}{section} properties.
1776 \qmlattachedproperty string QtQuick2::ListView::section
1777 This attached property holds the section of this element.
1779 It is attached to each instance of the delegate.
1781 The section is evaluated using the \l {ListView::section.property}{section} properties.
1785 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1787 This attached property holds whether the delegate may be destroyed. It
1788 is attached to each instance of the delegate. The default value is false.
1790 It is sometimes necessary to delay the destruction of an item
1791 until an animation completes. The example delegate below ensures that the
1792 animation completes before the item is removed from the list.
1794 \snippet qml/listview/listview.qml delayRemove
1796 If a \l remove transition has been specified, it will not be applied until
1797 delayRemove is returned to \c false.
1801 \qmlattachedsignal QtQuick2::ListView::onAdd()
1802 This attached signal handler is called immediately after an item is added to the view.
1804 If an \l add transition is specified, it is applied immediately after
1805 this signal handler is called.
1809 \qmlattachedsignal QtQuick2::ListView::onRemove()
1810 This attached handler is called immediately before an item is removed from the view.
1812 If a \l remove transition has been specified, it is applied after
1813 this signal handler is called, providing that delayRemove is false.
1817 \qmlproperty model QtQuick2::ListView::model
1818 This property holds the model providing data for the list.
1820 The model provides the set of data that is used to create the items
1821 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1822 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1823 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1825 \sa {qmlmodels}{Data Models}
1829 \qmlproperty Component QtQuick2::ListView::delegate
1831 The delegate provides a template defining each item instantiated by the view.
1832 The index is exposed as an accessible \c index property. Properties of the
1833 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1835 The number of elements in the delegate has a direct effect on the
1836 flicking performance of the view. If at all possible, place functionality
1837 that is not needed for the normal display of the delegate in a \l Loader which
1838 can load additional elements when needed.
1840 The ListView will lay out the items based on the size of the root item
1843 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1846 \note Delegates are instantiated as needed and may be destroyed at any time.
1847 State should \e never be stored in a delegate.
1850 \qmlproperty int QtQuick2::ListView::currentIndex
1851 \qmlproperty Item QtQuick2::ListView::currentItem
1853 The \c currentIndex property holds the index of the current item, and
1854 \c currentItem holds the current item. Setting the currentIndex to -1
1855 will clear the highlight and set currentItem to null.
1857 If highlightFollowsCurrentItem is \c true, setting either of these
1858 properties will smoothly scroll the ListView so that the current
1859 item becomes visible.
1861 Note that the position of the current item
1862 may only be approximate until it becomes visible in the view.
1866 \qmlproperty Item QtQuick2::ListView::highlightItem
1868 This holds the highlight item created from the \l highlight component.
1870 The \c highlightItem is managed by the view unless
1871 \l highlightFollowsCurrentItem is set to false.
1873 \sa highlight, highlightFollowsCurrentItem
1877 \qmlproperty int QtQuick2::ListView::count
1878 This property holds the number of items in the view.
1882 \qmlproperty Component QtQuick2::ListView::highlight
1883 This property holds the component to use as the highlight.
1885 An instance of the highlight component is created for each list.
1886 The geometry of the resulting component instance is managed by the list
1887 so as to stay with the current item, unless the highlightFollowsCurrentItem
1890 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1894 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1895 This property holds whether the highlight is managed by the view.
1897 If this property is true (the default value), the highlight is moved smoothly
1898 to follow the current item. Otherwise, the
1899 highlight is not moved by the view, and any movement must be implemented
1902 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1904 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1906 Note that the highlight animation also affects the way that the view
1907 is scrolled. This is because the view moves to maintain the
1908 highlight within the preferred highlight range (or visible viewport).
1910 \sa highlight, highlightMoveSpeed
1912 //###Possibly rename these properties, since they are very useful even without a highlight?
1914 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1915 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1916 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1918 These properties define the preferred range of the highlight (for the current item)
1919 within the view. The \c preferredHighlightBegin value must be less than the
1920 \c preferredHighlightEnd value.
1922 These properties affect the position of the current item when the list is scrolled.
1923 For example, if the currently selected item should stay in the middle of the
1924 list when the view is scrolled, set the \c preferredHighlightBegin and
1925 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1926 item would be. If the \c currentItem is changed programmatically, the list will
1927 automatically scroll so that the current item is in the middle of the view.
1928 Furthermore, the behavior of the current item index will occur whether or not a
1931 Valid values for \c highlightRangeMode are:
1934 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1935 However, the highlight can move outside of the range at the ends of the list or due
1936 to mouse interaction.
1937 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1938 The current item changes if a keyboard or mouse action would cause the highlight to move
1939 outside of the range.
1940 \li ListView.NoHighlightRange - this is the default value.
1943 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1945 Q_D(QQuickListView);
1946 if (d->autoHighlight != autoHighlight) {
1947 if (!autoHighlight) {
1948 if (d->highlightPosAnimator)
1949 d->highlightPosAnimator->stop();
1950 if (d->highlightSizeAnimator)
1951 d->highlightSizeAnimator->stop();
1953 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1958 \qmlproperty real QtQuick2::ListView::spacing
1960 This property holds the spacing between items.
1962 The default value is 0.
1964 qreal QQuickListView::spacing() const
1966 Q_D(const QQuickListView);
1970 void QQuickListView::setSpacing(qreal spacing)
1972 Q_D(QQuickListView);
1973 if (spacing != d->spacing) {
1974 d->spacing = spacing;
1975 d->forceLayout = true;
1977 emit spacingChanged();
1982 \qmlproperty enumeration QtQuick2::ListView::orientation
1983 This property holds the orientation of the list.
1988 \li ListView.Horizontal - Items are laid out horizontally
1989 \li ListView.Vertical (default) - Items are laid out vertically
1994 \li Horizontal orientation:
1995 \image ListViewHorizontal.png
1998 \li Vertical orientation:
1999 \image listview-highlight.png
2002 QQuickListView::Orientation QQuickListView::orientation() const
2004 Q_D(const QQuickListView);
2008 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2010 Q_D(QQuickListView);
2011 if (d->orient != orientation) {
2012 d->orient = orientation;
2013 if (d->orient == Vertical) {
2014 setContentWidth(-1);
2015 setFlickableDirection(VerticalFlick);
2018 setContentHeight(-1);
2019 setFlickableDirection(HorizontalFlick);
2023 emit orientationChanged();
2028 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
2029 This property holds the layout direction of a horizontally-oriented list.
2034 \li Qt.LeftToRight (default) - Items will be laid out from left to right.
2035 \li Qt.RightToLeft - Items will be laid out from right to let.
2038 Setting this property has no effect if the \l orientation is Qt.Vertical.
2040 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2045 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
2046 This property holds the effective layout direction of a horizontally-oriented list.
2048 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2049 the visual layout direction of the horizontal list will be mirrored. However, the
2050 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2052 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2057 \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
2058 This property holds the layout direction of a vertically-oriented list.
2063 \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
2064 \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
2067 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2069 \sa ListView::layoutDirection
2074 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
2075 This property holds whether the list wraps key navigation.
2077 If this is true, key navigation that would move the current item selection
2078 past the end of the list instead wraps around and moves the selection to
2079 the start of the list, and vice-versa.
2081 By default, key navigation is not wrapped.
2086 \qmlproperty int QtQuick2::ListView::cacheBuffer
2087 This property determines whether delegates are retained outside the
2088 visible area of the view.
2090 If this value is non-zero, the view may keep as many delegates
2091 instantiated as it can fit within the buffer specified. For example,
2092 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2093 set to 40, then up to 2 delegates above and 2 delegates below the visible
2094 area may be created/retained. The buffered delegates are created asynchronously,
2095 allowing creation to occur across multiple frames and reducing the
2096 likelihood of skipping frames. In order to improve painting performance
2097 delegates outside the visible area are not painted.
2099 The default value of this property is platform dependent, but will usually
2100 be a non-zero value.
2102 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2103 instantiated delegates.
2105 Setting this value can improve the smoothness of scrolling behavior at the expense
2106 of additional memory usage. It is not a substitute for creating efficient
2107 delegates; the fewer elements in a delegate, the faster a view can be
2113 \qmlproperty string QtQuick2::ListView::section.property
2114 \qmlproperty enumeration QtQuick2::ListView::section.criteria
2115 \qmlproperty Component QtQuick2::ListView::section.delegate
2116 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
2118 These properties determine the expression to be evaluated and appearance
2119 of the section labels.
2121 \c section.property holds the name of the property that is the basis
2124 \c section.criteria holds the criteria for forming each section based on
2125 \c section.property. This value can be one of:
2128 \li ViewSection.FullString (default) - sections are created based on the
2129 \c section.property value.
2130 \li ViewSection.FirstCharacter - sections are created based on the first
2131 character of the \c section.property value (for example, 'A', 'B', 'C'
2132 sections, etc. for an address book)
2135 A case insensitive comparison is used when determining section
2138 \c section.delegate holds the delegate component for each section.
2140 \c section.labelPositioning determines whether the current and/or
2141 next section labels stick to the start/end of the view, and whether
2142 the labels are shown inline. This value can be a combination of:
2145 \li ViewSection.InlineLabels - section labels are shown inline between
2146 the item delegates separating sections (default).
2147 \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
2148 start of the view as it is moved.
2149 \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
2150 sections) sticks to the end of the view as it is moved. \note Enabling
2151 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
2152 section, which has performance implications, especially for slower models.
2155 Each item in the list has attached properties named \c ListView.section,
2156 \c ListView.previousSection and \c ListView.nextSection.
2158 For example, here is a ListView that displays a list of animals, separated
2159 into sections. Each item in the ListView is placed in a different section
2160 depending on the "size" property of the model item. The \c sectionHeading
2161 delegate component provides the light blue bar that marks the beginning of
2165 \snippet examples/quick/modelviews/listview/sections.qml 0
2167 \image qml-listview-sections-example.png
2169 \note Adding sections to a ListView does not automatically re-order the
2170 list items by the section criteria.
2171 If the model is not ordered by section, then it is possible that
2172 the sections created will not be unique; each boundary between
2173 differing sections will result in a section header being created
2174 even if that section exists elsewhere.
2176 \sa {quick/modelviews/listview}{ListView examples}
2178 QQuickViewSection *QQuickListView::sectionCriteria()
2180 Q_D(QQuickListView);
2181 if (!d->sectionCriteria) {
2182 d->sectionCriteria = new QQuickViewSection(this);
2183 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2185 return d->sectionCriteria;
2189 \qmlproperty string QtQuick2::ListView::currentSection
2190 This property holds the section that is currently at the beginning of the view.
2192 QString QQuickListView::currentSection() const
2194 Q_D(const QQuickListView);
2195 return d->currentSection;
2199 \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
2200 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2201 \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
2202 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2204 These properties hold the move and resize animation speed of the highlight delegate.
2206 \l highlightFollowsCurrentItem must be true for these properties
2209 The default value for the speed properties is 400 pixels/second.
2210 The default value for the duration properties is -1, i.e. the
2211 highlight will take as much time as necessary to move at the set speed.
2213 These properties have the same characteristics as a SmoothedAnimation.
2215 \sa highlightFollowsCurrentItem
2217 qreal QQuickListView::highlightMoveSpeed() const
2219 Q_D(const QQuickListView);
2220 return d->highlightMoveSpeed;
2223 void QQuickListView::setHighlightMoveSpeed(qreal speed)
2225 Q_D(QQuickListView);
2226 if (d->highlightMoveSpeed != speed) {
2227 d->highlightMoveSpeed = speed;
2228 if (d->highlightPosAnimator)
2229 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2230 emit highlightMoveSpeedChanged();
2234 void QQuickListView::setHighlightMoveDuration(int duration)
2236 Q_D(QQuickListView);
2237 if (d->highlightMoveDuration != duration) {
2238 if (d->highlightPosAnimator)
2239 d->highlightPosAnimator->userDuration = duration;
2240 QQuickItemView::setHighlightMoveDuration(duration);
2244 qreal QQuickListView::highlightResizeSpeed() const
2246 Q_D(const QQuickListView);
2247 return d->highlightResizeSpeed;
2250 void QQuickListView::setHighlightResizeSpeed(qreal speed)
2252 Q_D(QQuickListView);
2253 if (d->highlightResizeSpeed != speed) {
2254 d->highlightResizeSpeed = speed;
2255 if (d->highlightSizeAnimator)
2256 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2257 emit highlightResizeSpeedChanged();
2261 int QQuickListView::highlightResizeDuration() const
2263 Q_D(const QQuickListView);
2264 return d->highlightResizeDuration;
2267 void QQuickListView::setHighlightResizeDuration(int duration)
2269 Q_D(QQuickListView);
2270 if (d->highlightResizeDuration != duration) {
2271 d->highlightResizeDuration = duration;
2272 if (d->highlightSizeAnimator)
2273 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2274 emit highlightResizeDurationChanged();
2279 \qmlproperty enumeration QtQuick2::ListView::snapMode
2281 This property determines how the view scrolling will settle following a drag or flick.
2282 The possible values are:
2285 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2286 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2288 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2289 visible item at the time the mouse button is released. This mode is particularly
2290 useful for moving one page at a time.
2293 \c snapMode does not affect the \l currentIndex. To update the
2294 \l currentIndex as the list is moved, set \l highlightRangeMode
2295 to \c ListView.StrictlyEnforceRange.
2297 \sa highlightRangeMode
2299 QQuickListView::SnapMode QQuickListView::snapMode() const
2301 Q_D(const QQuickListView);
2305 void QQuickListView::setSnapMode(SnapMode mode)
2307 Q_D(QQuickListView);
2308 if (d->snapMode != mode) {
2310 emit snapModeChanged();
2316 \qmlproperty Component QtQuick2::ListView::footer
2317 This property holds the component to use as the footer.
2319 An instance of the footer component is created for each view. The
2320 footer is positioned at the end of the view, after any items.
2322 \sa header, footerItem
2327 \qmlproperty Component QtQuick2::ListView::header
2328 This property holds the component to use as the header.
2330 An instance of the header component is created for each view. The
2331 header is positioned at the beginning of the view, before any items.
2333 \sa footer, headertem
2337 \qmlproperty Item QtQuick2::ListView::headerItem
2338 This holds the header item created from the \l header component.
2340 An instance of the header component is created for each view. The
2341 header is positioned at the beginning of the view, before any items.
2343 \sa header, footerItem
2347 \qmlproperty Item QtQuick2::ListView::footerItem
2348 This holds the footer item created from the \l footer component.
2350 An instance of the footer component is created for each view. The
2351 footer is positioned at the end of the view, after any items.
2353 \sa footer, headerItem
2357 \qmlproperty Transition QtQuick2::ListView::populate
2359 This property holds the transition to apply to the items that are initially created
2362 It is applied to all items that are created when:
2365 \li The view is first created
2366 \li The view's \l model changes
2367 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2370 For example, here is a view that specifies such a transition:
2375 populate: Transition {
2376 NumberAnimation { properties: "x,y"; duration: 1000 }
2381 When the view is initialized, the view will create all the necessary items for the view,
2382 then animate them to their correct positions within the view over one second.
2384 For more details and examples on how to use view transitions, see the ViewTransition
2387 \sa add, ViewTransition
2391 \qmlproperty Transition QtQuick2::ListView::add
2393 This property holds the transition to apply to items that are added to the view.
2395 For example, here is a view that specifies such a transition:
2401 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2406 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2407 to its final x,y position within the view, over one second. The transition only applies to
2408 the new items that are added to the view; it does not apply to the items below that are
2409 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2410 or \l addDisplaced properties.
2412 For more details and examples on how to use view transitions, see the ViewTransition
2415 \note This transition is not applied to the items that are created when the view is initially
2416 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2419 \sa addDisplaced, populate, ViewTransition
2423 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2425 This property holds the transition to apply to items within the view that are displaced by
2426 the addition of other items to the view.
2428 For example, here is a view that specifies such a transition:
2433 addDisplaced: Transition {
2434 NumberAnimation { properties: "x,y"; duration: 1000 }
2439 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2440 them to move down (or sideways, if horizontally orientated) within the view. As this
2441 displacement occurs, the items' movement to their new x,y positions within the view will be
2442 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2443 the new item that has been added to the view; to animate the added items, set the \l add
2446 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2447 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2448 if it is not necessary to specify different transitions depending on whether an item is displaced
2449 by an add, move or remove operation, consider setting the \l displaced property instead.
2451 For more details and examples on how to use view transitions, see the ViewTransition
2454 \note This transition is not applied to the items that are created when the view is initially
2455 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2458 \sa displaced, add, populate, ViewTransition
2462 \qmlproperty Transition QtQuick2::ListView::move
2464 This property holds the transition to apply to items in the view that are being moved due
2465 to a move operation in the view's \l model.
2467 For example, here is a view that specifies such a transition:
2473 NumberAnimation { properties: "x,y"; duration: 1000 }
2478 Whenever the \l model performs a move operation to move a particular set of indexes, the
2479 respective items in the view will be animated to their new positions in the view over one
2480 second. The transition only applies to the items that are the subject of the move operation
2481 in the model; it does not apply to items below them that are displaced by the move operation.
2482 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2484 For more details and examples on how to use view transitions, see the ViewTransition
2487 \sa moveDisplaced, ViewTransition
2491 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2493 This property holds the transition to apply to items that are displaced by a move operation in
2494 the view's \l model.
2496 For example, here is a view that specifies such a transition:
2501 moveDisplaced: Transition {
2502 NumberAnimation { properties: "x,y"; duration: 1000 }
2507 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2508 between the source and destination indexes of the move operation are displaced, causing them
2509 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2510 displacement occurs, the items' movement to their new x,y positions within the view will be
2511 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2512 the items that are the actual subjects of the move operation; to animate the moved items, set
2513 the \l move property.
2515 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2516 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2517 if it is not necessary to specify different transitions depending on whether an item is displaced
2518 by an add, move or remove operation, consider setting the \l displaced property instead.
2520 For more details and examples on how to use view transitions, see the ViewTransition
2523 \sa displaced, move, ViewTransition
2527 \qmlproperty Transition QtQuick2::ListView::remove
2529 This property holds the transition to apply to items that are removed from the view.
2531 For example, here is a view that specifies such a transition:
2536 remove: Transition {
2538 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2539 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2545 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2546 over one second, and in parallel will also change its opacity to 0. The transition
2547 only applies to the items that are removed from the view; it does not apply to the items below
2548 them that are displaced by the removal of the items. To animate the displaced items, set the
2549 \l displaced or \l removeDisplaced properties.
2551 Note that by the time the transition is applied, the item has already been removed from the
2552 model; any references to the model data for the removed index will not be valid.
2554 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2555 remove transition will not be applied until \l delayRemove becomes false again.
2557 For more details and examples on how to use view transitions, see the ViewTransition
2560 \sa removeDisplaced, ViewTransition
2564 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2566 This property holds the transition to apply to items in the view that are displaced by the
2567 removal of other items in the view.
2569 For example, here is a view that specifies such a transition:
2574 removeDisplaced: Transition {
2575 NumberAnimation { properties: "x,y"; duration: 1000 }
2580 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2581 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2582 displacement occurs, the items' movement to their new x,y positions within the view will be
2583 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2584 the item that has actually been removed from the view; to animate the removed items, set the
2587 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2588 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2589 if it is not necessary to specify different transitions depending on whether an item is displaced
2590 by an add, move or remove operation, consider setting the \l displaced property instead.
2592 For more details and examples on how to use view transitions, see the ViewTransition
2595 \sa displaced, remove, ViewTransition
2599 \qmlproperty Transition QtQuick2::ListView::displaced
2600 This property holds the generic transition to apply to items that have been displaced by
2601 any model operation that affects the view.
2603 This is a convenience for specifying the generic transition to be applied to any items
2604 that are displaced by an add, move or remove operation, without having to specify the
2605 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2606 is a view that specifies a displaced transition:
2611 displaced: Transition {
2612 NumberAnimation { properties: "x,y"; duration: 1000 }
2617 When any item is added, moved or removed within the above view, the items below it are
2618 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2619 view. As this displacement occurs, the items' movement to their new x,y positions within
2620 the view will be animated by a NumberAnimation over one second, as specified.
2622 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2623 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2624 instead of the generic displaced transition when the relevant operation occurs, providing that
2625 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2626 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2628 For more details and examples on how to use view transitions, see the ViewTransition
2631 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2634 void QQuickListView::viewportMoved(Qt::Orientations orient)
2636 Q_D(QQuickListView);
2637 QQuickItemView::viewportMoved(orient);
2640 // Recursion can occur due to refill changing the content size.
2641 if (d->inViewportMoved)
2643 d->inViewportMoved = true;
2646 if (d->isBottomToTop())
2647 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2649 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2651 if (d->isRightToLeft())
2652 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2654 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2657 d->refillOrLayout();
2659 // Set visibility of items to eliminate cost of items outside the visible area.
2660 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2661 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2662 for (int i = 0; i < d->visibleItems.count(); ++i) {
2663 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2664 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2667 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2669 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2670 d->moveReason = QQuickListViewPrivate::Mouse;
2671 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2672 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2673 // reposition highlight
2674 qreal pos = d->highlight->position();
2675 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2676 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2677 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2678 if (pos < viewPos + d->highlightRangeStart)
2679 pos = viewPos + d->highlightRangeStart;
2680 if (pos != d->highlight->position()) {
2681 d->highlightPosAnimator->stop();
2682 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2684 d->updateHighlight();
2687 // update current index
2688 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2689 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2690 d->updateCurrent(snapItem->index);
2695 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2696 d->inFlickCorrection = true;
2697 // Near an end and it seems that the extent has changed?
2698 // Recalculate the flick so that we don't end up in an odd position.
2699 if (yflick() && !d->vData.inOvershoot) {
2700 if (d->vData.velocity > 0) {
2701 const qreal minY = minYExtent();
2702 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2703 && minY != d->vData.flickTarget)
2704 d->flickY(-d->vData.smoothVelocity.value());
2705 } else if (d->vData.velocity < 0) {
2706 const qreal maxY = maxYExtent();
2707 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2708 && maxY != d->vData.flickTarget)
2709 d->flickY(-d->vData.smoothVelocity.value());
2713 if (xflick() && !d->hData.inOvershoot) {
2714 if (d->hData.velocity > 0) {
2715 const qreal minX = minXExtent();
2716 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2717 && minX != d->hData.flickTarget)
2718 d->flickX(-d->hData.smoothVelocity.value());
2719 } else if (d->hData.velocity < 0) {
2720 const qreal maxX = maxXExtent();
2721 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2722 && maxX != d->hData.flickTarget)
2723 d->flickX(-d->hData.smoothVelocity.value());
2726 d->inFlickCorrection = false;
2728 if (d->sectionCriteria) {
2729 d->updateCurrentSection();
2730 d->updateStickySections();
2732 d->inViewportMoved = false;
2735 void QQuickListView::keyPressEvent(QKeyEvent *event)
2737 Q_D(QQuickListView);
2738 if (d->model && d->model->count() && d->interactive) {
2739 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2740 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2741 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2742 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2743 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2744 decrementCurrentIndex();
2747 } else if (d->wrap) {
2751 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2752 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2753 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2754 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2755 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2756 incrementCurrentIndex();
2759 } else if (d->wrap) {
2766 QQuickItemView::keyPressEvent(event);
2769 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2771 Q_D(QQuickListView);
2772 if (d->isRightToLeft()) {
2773 // maintain position relative to the right edge
2774 int dx = newGeometry.width() - oldGeometry.width();
2775 setContentX(contentX() - dx);
2776 } else if (d->isBottomToTop()) {
2777 // maintain position relative to the bottom edge
2778 int dy = newGeometry.height() - oldGeometry.height();
2779 setContentY(contentY() - dy);
2781 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2786 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2788 Increments the current index. The current index will wrap
2789 if keyNavigationWraps is true and it is currently at the end.
2790 This method has no effect if the \l count is zero.
2792 \b Note: methods should only be called after the Component has completed.
2794 void QQuickListView::incrementCurrentIndex()
2796 Q_D(QQuickListView);
2797 int count = d->model ? d->model->count() : 0;
2798 if (count && (currentIndex() < count - 1 || d->wrap)) {
2799 d->moveReason = QQuickListViewPrivate::SetIndex;
2800 int index = currentIndex()+1;
2801 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2806 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2808 Decrements the current index. The current index will wrap
2809 if keyNavigationWraps is true and it is currently at the beginning.
2810 This method has no effect if the \l count is zero.
2812 \b Note: methods should only be called after the Component has completed.
2814 void QQuickListView::decrementCurrentIndex()
2816 Q_D(QQuickListView);
2817 int count = d->model ? d->model->count() : 0;
2818 if (count && (currentIndex() > 0 || d->wrap)) {
2819 d->moveReason = QQuickListViewPrivate::SetIndex;
2820 int index = currentIndex()-1;
2821 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2825 void QQuickListView::updateSections()
2827 Q_D(QQuickListView);
2828 if (isComponentComplete() && d->model) {
2829 QList<QByteArray> roles;
2830 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2831 roles << d->sectionCriteria->property().toUtf8();
2832 d->model->setWatchedRoles(roles);
2833 d->updateSections();
2835 d->forceLayout = true;
2841 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2843 int modelIndex = change.index;
2844 int count = change.count;
2846 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2847 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2850 int i = visibleItems.count() - 1;
2851 while (i > 0 && visibleItems.at(i)->index == -1)
2853 if (i == 0 && visibleItems.first()->index == -1) {
2854 // there are no visible items except items marked for removal
2855 index = visibleItems.count();
2856 } else if (visibleItems.at(i)->index + 1 == modelIndex
2857 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2858 // Special case of appending an item to the model.
2859 index = visibleItems.count();
2861 if (modelIndex < visibleIndex) {
2862 // Insert before visible items
2863 visibleIndex += count;
2864 for (int i = 0; i < visibleItems.count(); ++i) {
2865 FxViewItem *item = visibleItems.at(i);
2866 if (item->index != -1 && item->index >= modelIndex)
2867 item->index += count;
2874 // index can be the next item past the end of the visible items list (i.e. appended)
2876 if (visibleItems.count()) {
2877 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2878 : visibleItems.last()->endPosition()+spacing;
2881 int prevVisibleCount = visibleItems.count();
2882 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2883 // Insert items before the visible item.
2884 int insertionIdx = index;
2886 int from = tempPos - buffer;
2888 for (i = count-1; i >= 0; --i) {
2889 if (pos > from && insertionIdx < visibleIndex) {
2890 // item won't be visible, just note the size for repositioning
2891 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2892 pos -= averageSize + spacing;
2894 // item is before first visible e.g. in cache buffer
2895 FxViewItem *item = 0;
2896 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2897 item->index = modelIndex + i;
2899 item = createItem(modelIndex + i);
2903 visibleItems.insert(insertionIdx, item);
2904 if (insertionIdx == 0)
2905 insertResult->changedFirstItem = true;
2906 if (!change.isMove()) {
2907 addedItems->append(item);
2908 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2910 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2911 pos -= item->size() + spacing;
2917 int to = buffer+tempPos+size();
2918 for (i = 0; i < count && pos <= to; ++i) {
2919 FxViewItem *item = 0;
2920 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2921 item->index = modelIndex + i;
2922 bool newItem = !item;
2924 item = createItem(modelIndex + i);
2928 visibleItems.insert(index, item);
2930 insertResult->changedFirstItem = true;
2931 if (change.isMove()) {
2932 // we know this is a move target, since move displaced items that are
2933 // shuffled into view due to a move would be added in refill()
2934 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2935 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2937 addedItems->append(item);
2938 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2940 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2941 pos += item->size() + spacing;
2946 for (; index < visibleItems.count(); ++index) {
2947 FxViewItem *item = visibleItems.at(index);
2948 if (item->index != -1) {
2949 item->index += count;
2950 if (change.isMove())
2951 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2953 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2957 updateVisibleIndex();
2959 return visibleItems.count() > prevVisibleCount;
2962 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2964 Q_UNUSED(insertionResult);
2969 int markerItemIndex = -1;
2970 for (int i=0; i<visibleItems.count(); i++) {
2971 if (visibleItems[i]->index == afterModelIndex) {
2972 markerItemIndex = i;
2976 if (markerItemIndex < 0)
2979 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2980 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
2981 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
2983 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2984 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
2985 if (!listItem->transitionScheduledOrRunning()) {
2986 qreal pos = listItem->position();
2987 listItem->setPosition(pos - sizeRemoved);
2988 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2989 listItem->setPosition(pos);
2995 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2997 Positions the view such that the \a index is at the position specified by
3001 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
3002 \li ListView.Center - position item in the center of the view.
3003 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3004 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
3005 bring the item into view.
3006 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
3007 the view the item is positioned at the top (or left for horizontal orientation) of the view.
3010 If positioning the view at \a index would cause empty space to be displayed at
3011 the beginning or end of the view, the view will be positioned at the boundary.
3013 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3014 at a particular index. This is unreliable since removing items from the start
3015 of the list does not cause all other items to be repositioned, and because
3016 the actual start of the view can vary based on the size of the delegates.
3017 The correct way to bring an item into view is with \c positionViewAtIndex.
3019 \b Note: methods should only be called after the Component has completed. To position
3020 the view at startup, this method should be called by Component.onCompleted. For
3021 example, to position the view at the end:
3024 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3029 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
3030 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
3032 Positions the view at the beginning or end, taking into account any header or footer.
3034 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3035 at a particular index. This is unreliable since removing items from the start
3036 of the list does not cause all other items to be repositioned, and because
3037 the actual start of the view can vary based on the size of the delegates.
3039 \b Note: methods should only be called after the Component has completed. To position
3040 the view at startup, this method should be called by Component.onCompleted. For
3041 example, to position the view at the end on startup:
3044 Component.onCompleted: positionViewAtEnd()
3049 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3051 Returns the index of the visible item containing the point \a x, \a y in content
3052 coordinates. If there is no item at the point specified, or the item is
3053 not visible -1 is returned.
3055 If the item is outside the visible area, -1 is returned, regardless of
3056 whether an item will exist at that point when scrolled into view.
3058 \b Note: methods should only be called after the Component has completed.
3062 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3064 Returns the visible item containing the point \a x, \a y in content
3065 coordinates. If there is no item at the point specified, or the item is
3066 not visible null is returned.
3068 If the item is outside the visible area, null is returned, regardless of
3069 whether an item will exist at that point when scrolled into view.
3071 \b Note: methods should only be called after the Component has completed.
3074 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3076 return new QQuickListViewAttached(obj);