1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquicklistview_p.h"
43 #include "qquickitemview_p_p.h"
44 #include "qquickvisualitemmodel_p.h"
46 #include <QtQml/qqmlexpression.h>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
53 #include <private/qquicksmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
55 #include "qplatformdefs.h"
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
63 //#define DEBUG_DELEGATE_LIFECYCLE
67 class QQuickListViewPrivate : public QQuickItemViewPrivate
69 Q_DECLARE_PUBLIC(QQuickListView)
71 static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
73 virtual Qt::Orientation layoutOrientation() const;
74 virtual bool isContentFlowReversed() const;
75 bool isRightToLeft() const;
76 bool isBottomToTop() const;
78 virtual qreal positionAt(int index) const;
79 virtual qreal endPositionAt(int index) const;
80 virtual qreal originPosition() const;
81 virtual qreal lastPosition() const;
83 FxViewItem *itemBefore(int modelIndex) const;
84 QString sectionAt(int modelIndex);
85 qreal snapPosAt(qreal pos);
86 FxViewItem *snapItemAt(qreal pos);
91 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer);
92 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
93 virtual void visibleItemsChanged();
95 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
96 virtual void initializeViewItem(FxViewItem *item);
97 virtual bool releaseItem(FxViewItem *item);
98 virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
99 virtual void repositionPackageItemAt(QQuickItem *item, int index);
100 virtual void resetFirstItemPosition(qreal pos = 0.0);
101 virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
103 virtual void createHighlight();
104 virtual void updateHighlight();
105 virtual void resetHighlightPosition();
107 virtual void setPosition(qreal pos);
108 virtual void layoutVisibleItems(int fromModelIndex = 0);
110 virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
111 virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
113 virtual void updateSections();
114 QQuickItem *getSectionItem(const QString §ion);
115 void releaseSectionItem(QQuickItem *item);
116 void updateInlineSection(FxListItemSG *);
117 void updateCurrentSection();
118 void updateStickySections();
120 virtual qreal headerSize() const;
121 virtual qreal footerSize() const;
122 virtual bool showHeaderForIndex(int index) const;
123 virtual bool showFooterForIndex(int index) const;
124 virtual void updateHeader();
125 virtual void updateFooter();
127 virtual void changedVisibleIndex(int newIndex);
128 virtual void initializeCurrentItem();
130 void updateAverage();
132 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
133 virtual void fixupPosition();
134 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
135 virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
136 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
138 QQuickListView::Orientation orient;
142 QQuickListView::SnapMode snapMode;
144 QSmoothedAnimation *highlightPosAnimator;
145 QSmoothedAnimation *highlightSizeAnimator;
146 int highlightResizeDuration;
148 QQuickViewSection *sectionCriteria;
149 QString currentSection;
150 static const int sectionCacheSize = 5;
151 QQuickItem *sectionCache[sectionCacheSize];
152 QQuickItem *currentSectionItem;
153 QString currentStickySection;
154 QQuickItem *nextSectionItem;
155 QString nextStickySection;
156 QString lastVisibleSection;
160 bool correctFlick : 1;
161 bool inFlickCorrection : 1;
163 QQuickListViewPrivate()
164 : orient(QQuickListView::Vertical)
166 , averageSize(100.0), spacing(0.0)
167 , snapMode(QQuickListView::NoSnap)
168 , highlightPosAnimator(0), highlightSizeAnimator(0)
169 , highlightResizeDuration(250)
170 , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
171 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
173 ~QQuickListViewPrivate() {
174 delete highlightPosAnimator;
175 delete highlightSizeAnimator;
178 friend class QQuickViewSection;
181 //----------------------------------------------------------------------------
183 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
184 : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
185 , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
189 void QQuickViewSection::setProperty(const QString &property)
191 if (property != m_property) {
192 m_property = property;
193 emit propertyChanged();
194 m_view->updateSections();
198 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
200 if (criteria != m_criteria) {
201 m_criteria = criteria;
202 emit criteriaChanged();
203 m_view->updateSections();
207 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
209 if (delegate != m_delegate) {
210 m_delegate = delegate;
211 emit delegateChanged();
212 m_view->updateSections();
216 QString QQuickViewSection::sectionString(const QString &value)
218 if (m_criteria == FirstCharacter)
219 return value.isEmpty() ? QString() : value.at(0);
224 void QQuickViewSection::setLabelPositioning(int l)
226 if (m_labelPositioning != l) {
227 m_labelPositioning = l;
228 emit labelPositioningChanged();
229 m_view->updateSections();
233 //----------------------------------------------------------------------------
235 class FxListItemSG : public FxViewItem
238 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
239 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
241 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
242 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
248 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
249 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
253 inline QQuickItem *section() const {
254 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
256 void setSection(QQuickItem *s) {
258 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
259 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
262 qreal position() const {
264 if (view->orientation() == QQuickListView::Vertical)
265 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
267 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
269 return itemPosition();
272 qreal itemPosition() const {
273 if (view->orientation() == QQuickListView::Vertical)
274 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
276 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
280 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
282 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
284 qreal itemSize() const {
285 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
287 qreal sectionSize() const {
289 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
292 qreal endPosition() const {
293 if (view->orientation() == QQuickListView::Vertical) {
294 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
296 : itemY() + item->height());
298 return (view->effectiveLayoutDirection() == Qt::RightToLeft
300 : itemX() + item->width());
303 void setPosition(qreal pos, bool immediate = false) {
304 // position the section immediately even if there is a transition
306 if (view->orientation() == QQuickListView::Vertical) {
307 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
308 section()->setY(-section()->height()-pos);
310 section()->setY(pos);
312 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
313 section()->setX(-section()->width()-pos);
315 section()->setX(pos);
318 moveTo(pointForPosition(pos), immediate);
320 void setSize(qreal size) {
321 if (view->orientation() == QQuickListView::Vertical)
322 item->setHeight(size);
324 item->setWidth(size);
326 bool contains(qreal x, qreal y) const {
327 return (x >= itemX() && x < itemX() + item->width() &&
328 y >= itemY() && y < itemY() + item->height());
331 QQuickListView *view;
334 QPointF pointForPosition(qreal pos) const {
335 if (view->orientation() == QQuickListView::Vertical) {
336 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
338 pos += section()->height();
339 return QPointF(itemX(), -item->height() - pos);
342 pos += section()->height();
343 return QPointF(itemX(), pos);
346 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
348 pos += section()->width();
349 return QPointF(-item->width() - pos, itemY());
352 pos += section()->width();
353 return QPointF(pos, itemY());
359 //----------------------------------------------------------------------------
361 bool QQuickListViewPrivate::isContentFlowReversed() const
363 return isRightToLeft() || isBottomToTop();
366 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
368 return static_cast<Qt::Orientation>(orient);
371 bool QQuickListViewPrivate::isRightToLeft() const
373 Q_Q(const QQuickListView);
374 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
377 bool QQuickListViewPrivate::isBottomToTop() const
379 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
382 // Returns the item before modelIndex, if created.
383 // May return an item marked for removal.
384 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
386 if (modelIndex < visibleIndex)
390 while (idx < visibleItems.count()) {
391 FxViewItem *item = visibleItems.at(idx);
392 if (item->index != -1)
393 lastIndex = item->index;
394 if (item->index == modelIndex)
395 return visibleItems.at(idx-1);
398 if (lastIndex == modelIndex-1)
399 return visibleItems.last();
403 void QQuickListViewPrivate::setPosition(qreal pos)
406 if (orient == QQuickListView::Vertical) {
408 q->QQuickFlickable::setContentY(-pos-size());
410 q->QQuickFlickable::setContentY(pos);
413 q->QQuickFlickable::setContentX(-pos-size());
415 q->QQuickFlickable::setContentX(pos);
419 qreal QQuickListViewPrivate::originPosition() const
422 if (!visibleItems.isEmpty()) {
423 pos = (*visibleItems.constBegin())->position();
424 if (visibleIndex > 0)
425 pos -= visibleIndex * (averageSize + spacing);
430 qreal QQuickListViewPrivate::lastPosition() const
433 if (!visibleItems.isEmpty()) {
434 int invisibleCount = visibleItems.count() - visibleIndex;
435 for (int i = visibleItems.count()-1; i >= 0; --i) {
436 if (visibleItems.at(i)->index != -1) {
437 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
441 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
442 } else if (model && model->count()) {
443 pos = (model->count() * averageSize + (model->count()-1) * spacing);
448 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
450 if (FxViewItem *item = visibleItem(modelIndex)) {
451 return item->position();
453 if (!visibleItems.isEmpty()) {
454 if (modelIndex < visibleIndex) {
455 int count = visibleIndex - modelIndex;
457 if (modelIndex == currentIndex && currentItem) {
458 cs = currentItem->size() + spacing;
461 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
463 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
464 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
470 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
472 if (FxViewItem *item = visibleItem(modelIndex))
473 return item->endPosition();
474 if (!visibleItems.isEmpty()) {
475 if (modelIndex < visibleIndex) {
476 int count = visibleIndex - modelIndex;
477 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
479 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
480 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
486 QString QQuickListViewPrivate::sectionAt(int modelIndex)
488 if (FxViewItem *item = visibleItem(modelIndex))
489 return item->attached->section();
492 if (sectionCriteria) {
493 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
494 section = sectionCriteria->sectionString(propValue);
500 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
502 if (FxViewItem *snapItem = snapItemAt(pos))
503 return snapItem->position();
504 if (visibleItems.count()) {
505 qreal firstPos = (*visibleItems.constBegin())->position();
506 qreal endPos = (*(--visibleItems.constEnd()))->position();
507 if (pos < firstPos) {
508 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
509 } else if (pos > endPos)
510 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
512 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
515 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
517 FxViewItem *snapItem = 0;
518 qreal prevItemSize = 0;
519 for (int i = 0; i < visibleItems.count(); ++i) {
520 FxViewItem *item = visibleItems.at(i);
521 if (item->index == -1)
523 qreal itemTop = item->position();
524 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
526 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
528 prevItemSize = item->size();
533 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
535 visiblePos = positionAt(newIndex);
536 visibleIndex = newIndex;
539 void QQuickListViewPrivate::init()
541 QQuickItemViewPrivate::init();
542 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
545 void QQuickListViewPrivate::clear()
547 for (int i = 0; i < sectionCacheSize; ++i) {
548 delete sectionCache[i];
552 releaseSectionItem(currentSectionItem);
553 currentSectionItem = 0;
554 releaseSectionItem(nextSectionItem);
556 lastVisibleSection = QString();
557 QQuickItemViewPrivate::clear();
560 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
564 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
565 listItem->index = modelIndex;
567 // initialise attached properties
568 if (sectionCriteria) {
569 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
570 listItem->attached->setSection(sectionCriteria->sectionString(propValue));
571 if (modelIndex > 0) {
572 if (FxViewItem *item = itemBefore(modelIndex))
573 listItem->attached->setPrevSection(item->attached->section());
575 listItem->attached->setPrevSection(sectionAt(modelIndex-1));
577 if (modelIndex < model->count()-1) {
578 if (FxViewItem *item = visibleItem(modelIndex+1))
579 listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section());
581 listItem->attached->setNextSection(sectionAt(modelIndex+1));
588 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
590 QQuickItemViewPrivate::initializeViewItem(item);
592 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
593 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
595 if (sectionCriteria && sectionCriteria->delegate()) {
596 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
597 updateInlineSection(static_cast<FxListItemSG*>(item));
601 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
606 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
608 bool released = QQuickItemViewPrivate::releaseItem(item);
609 if (released && att && att->m_sectionItem) {
610 // We hold no more references to this item
613 if (!sectionCache[i]) {
614 sectionCache[i] = att->m_sectionItem;
615 sectionCache[i]->setVisible(false);
616 att->m_sectionItem = 0;
620 } while (i < sectionCacheSize);
621 delete att->m_sectionItem;
622 att->m_sectionItem = 0;
628 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
630 qreal itemEnd = visiblePos;
631 if (visibleItems.count()) {
632 visiblePos = (*visibleItems.constBegin())->position();
633 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
636 int modelIndex = findLastVisibleIndex();
637 bool haveValidItems = modelIndex >= 0;
638 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
640 if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
641 || bufferTo < visiblePos - averageSize - spacing)) {
642 // We've jumped more than a page. Estimate which items are now
643 // visible and fill from there.
644 int count = (fillFrom - itemEnd) / (averageSize + spacing);
645 int newModelIdx = qBound(0, modelIndex + count, model->count());
646 count = newModelIdx - modelIndex;
648 for (int i = 0; i < visibleItems.count(); ++i)
649 releaseItem(visibleItems.at(i));
650 visibleItems.clear();
651 modelIndex = newModelIdx;
652 visibleIndex = modelIndex;
653 visiblePos = itemEnd + count * (averageSize + spacing);
654 itemEnd = visiblePos;
658 bool changed = false;
659 FxListItemSG *item = 0;
661 while (modelIndex < model->count() && pos <= fillTo) {
662 #ifdef DEBUG_DELEGATE_LIFECYCLE
663 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
665 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
667 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
668 item->setPosition(pos, true);
669 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
670 pos += item->size() + spacing;
671 visibleItems.append(item);
676 if (doBuffer && requestedIndex != -1) // already waiting for an item
679 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
680 #ifdef DEBUG_DELEGATE_LIFECYCLE
681 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
683 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
686 visiblePos -= item->size() + spacing;
687 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
688 item->setPosition(visiblePos, true);
689 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
690 visibleItems.prepend(item);
697 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
699 FxViewItem *item = 0;
700 bool changed = false;
702 // Remove items from the start of the view.
703 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
704 // removed, otherwise a zero-sized item is infinitely added and removed over and
707 while (visibleItems.count() > 1 && index < visibleItems.count()
708 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
709 if (item->attached->delayRemove())
712 if (item->size() > 0) {
713 #ifdef DEBUG_DELEGATE_LIFECYCLE
714 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
716 // remove this item and all zero-sized items before it
718 if (item->index != -1)
720 visibleItems.removeAt(index);
721 if (item->transitionScheduledOrRunning()) {
722 #ifdef DEBUG_DELEGATE_LIFECYCLE
723 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
725 item->releaseAfterTransition = true;
726 releasePendingTransition.append(item);
732 item = visibleItems.at(--index);
740 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
741 if (item->attached->delayRemove())
743 #ifdef DEBUG_DELEGATE_LIFECYCLE
744 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
746 visibleItems.removeLast();
747 if (item->transitionScheduledOrRunning()) {
748 #ifdef DEBUG_DELEGATE_LIFECYCLE
749 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
751 item->releaseAfterTransition = true;
752 releasePendingTransition.append(item);
762 void QQuickListViewPrivate::visibleItemsChanged()
764 if (visibleItems.count())
765 visiblePos = (*visibleItems.constBegin())->position();
767 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
768 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
772 updateCurrentSection();
773 updateUnrequestedPositions();
776 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
778 if (!visibleItems.isEmpty()) {
779 const qreal from = isContentFlowReversed() ? -position() - size() : position();
780 const qreal to = isContentFlowReversed() ? -position() : position() + size();
782 FxViewItem *firstItem = *visibleItems.constBegin();
783 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
784 qreal sum = firstItem->size();
785 qreal pos = firstItem->position() + firstItem->size() + spacing;
786 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
788 for (int i=1; i < visibleItems.count(); ++i) {
789 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
790 if (item->index >= fromModelIndex) {
791 item->setPosition(pos);
792 item->setVisible(item->endPosition() >= from && item->position() <= to);
794 pos += item->size() + spacing;
796 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
798 averageSize = qRound(sum / visibleItems.count());
800 // move current item if it is not a visible item.
801 if (currentIndex >= 0 && currentItem && !fixedCurrent)
802 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
806 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
808 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
811 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
814 qreal pos = position();
815 if (orient == QQuickListView::Vertical) {
816 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
818 item->setY(-positionAt(index)-item->height());
820 item->setY(positionAt(index));
823 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
825 item->setX(-positionAt(index)-item->width());
827 item->setX(positionAt(index));
832 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
834 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
835 item->setPosition(pos);
838 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
840 if (!visibleItems.count())
842 qreal diff = forwards - backwards;
843 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
846 void QQuickListViewPrivate::createHighlight()
849 bool changed = false;
851 if (trackedItem == highlight)
856 delete highlightPosAnimator;
857 delete highlightSizeAnimator;
858 highlightPosAnimator = 0;
859 highlightSizeAnimator = 0;
865 QQuickItem *item = createHighlightItem();
867 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
870 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
871 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
873 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
874 highlightPosAnimator = new QSmoothedAnimation;
875 highlightPosAnimator->target = QQmlProperty(item, posProp);
876 highlightPosAnimator->userDuration = highlightMoveDuration;
878 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
879 highlightSizeAnimator = new QSmoothedAnimation;
880 highlightSizeAnimator->userDuration = highlightResizeDuration;
881 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
883 highlight = newHighlight;
888 emit q->highlightItemChanged();
891 void QQuickListViewPrivate::updateHighlight()
893 applyPendingChanges();
895 if ((!currentItem && highlight) || (currentItem && !highlight))
897 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
898 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
899 // auto-update highlight
900 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
901 highlightPosAnimator->to = isContentFlowReversed()
902 ? -listItem->itemPosition()-listItem->itemSize()
903 : listItem->itemPosition();
904 highlightSizeAnimator->to = listItem->itemSize();
905 if (orient == QQuickListView::Vertical) {
906 if (highlight->item->width() == 0)
907 highlight->item->setWidth(currentItem->item->width());
909 if (highlight->item->height() == 0)
910 highlight->item->setHeight(currentItem->item->height());
913 highlightPosAnimator->restart();
914 highlightSizeAnimator->restart();
919 void QQuickListViewPrivate::resetHighlightPosition()
921 if (highlight && currentItem)
922 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
925 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
928 QQuickItem *sectionItem = 0;
929 int i = sectionCacheSize-1;
930 while (i >= 0 && !sectionCache[i])
933 sectionItem = sectionCache[i];
935 sectionItem->setVisible(true);
936 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
937 context->setContextProperty(QLatin1String("section"), section);
939 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
940 QQmlContext *context = new QQmlContext(
941 creationContext ? creationContext : qmlContext(q));
942 context->setContextProperty(QLatin1String("section"), section);
943 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
945 QQml_setParent_noEvent(context, nobj);
946 sectionItem = qobject_cast<QQuickItem *>(nobj);
950 sectionItem->setZ(2);
951 QQml_setParent_noEvent(sectionItem, contentItem);
952 sectionItem->setParentItem(contentItem);
957 sectionCriteria->delegate()->completeCreate();
963 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
969 if (!sectionCache[i]) {
970 sectionCache[i] = item;
971 sectionCache[i]->setVisible(false);
975 } while (i < sectionCacheSize);
979 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
981 if (!sectionCriteria || !sectionCriteria->delegate())
983 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
984 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
985 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
986 if (!listItem->section()) {
987 qreal pos = listItem->position();
988 listItem->setSection(getSectionItem(listItem->attached->m_section));
989 listItem->setPosition(pos);
991 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
992 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
994 } else if (listItem->section()) {
995 qreal pos = listItem->position();
996 releaseSectionItem(listItem->section());
997 listItem->setSection(0);
998 listItem->setPosition(pos);
1002 void QQuickListViewPrivate::updateStickySections()
1004 if (!sectionCriteria
1005 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1008 bool isFlowReversed = isContentFlowReversed();
1009 qreal viewPos = isFlowReversed ? -position()-size() : position();
1010 QQuickItem *sectionItem = 0;
1011 QQuickItem *lastSectionItem = 0;
1013 while (index < visibleItems.count()) {
1014 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1015 // Find the current section header and last visible section header
1016 // and hide them if they will overlap a static section header.
1017 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1018 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1020 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1021 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1023 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1024 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1025 section->setVisible(visBot && visTop);
1026 if (visTop && !sectionItem)
1027 sectionItem = section;
1028 if (isFlowReversed) {
1029 if (-sectionPos <= viewPos + size())
1030 lastSectionItem = section;
1032 if (sectionPos + sectionSize < viewPos + size())
1033 lastSectionItem = section;
1039 // Current section header
1040 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1041 if (!currentSectionItem) {
1042 currentSectionItem = getSectionItem(currentSection);
1043 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1044 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1045 context->setContextProperty(QLatin1String("section"), currentSection);
1047 currentStickySection = currentSection;
1048 if (!currentSectionItem)
1051 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1052 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1054 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1055 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1057 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1058 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1061 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1063 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1064 if (orient == QQuickListView::Vertical)
1065 currentSectionItem->setY(pos);
1067 currentSectionItem->setX(pos);
1068 } else if (currentSectionItem) {
1069 releaseSectionItem(currentSectionItem);
1070 currentSectionItem = 0;
1073 // Next section footer
1074 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1075 if (!nextSectionItem) {
1076 nextSectionItem = getSectionItem(nextSection);
1077 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1078 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1079 context->setContextProperty(QLatin1String("section"), nextSection);
1081 nextStickySection = nextSection;
1082 if (!nextSectionItem)
1085 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1086 nextSectionItem->setVisible(!nextSection.isEmpty());
1087 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1088 if (lastSectionItem) {
1089 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1090 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1093 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1094 if (orient == QQuickListView::Vertical)
1095 nextSectionItem->setY(pos);
1097 nextSectionItem->setX(pos);
1098 } else if (nextSectionItem) {
1099 releaseSectionItem(nextSectionItem);
1100 nextSectionItem = 0;
1104 void QQuickListViewPrivate::updateSections()
1106 Q_Q(QQuickListView);
1107 if (!q->isComponentComplete())
1110 QQuickItemViewPrivate::updateSections();
1112 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1113 QString prevSection;
1114 if (visibleIndex > 0)
1115 prevSection = sectionAt(visibleIndex-1);
1116 QQuickListViewAttached *prevAtt = 0;
1118 for (int i = 0; i < visibleItems.count(); ++i) {
1119 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1120 attached->setPrevSection(prevSection);
1121 if (visibleItems.at(i)->index != -1) {
1122 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1123 attached->setSection(sectionCriteria->sectionString(propValue));
1124 idx = visibleItems.at(i)->index;
1126 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1128 prevAtt->setNextSection(attached->section());
1129 prevSection = attached->section();
1133 if (idx > 0 && idx < model->count()-1)
1134 prevAtt->setNextSection(sectionAt(idx+1));
1136 prevAtt->setNextSection(QString());
1140 lastVisibleSection = QString();
1141 updateCurrentSection();
1142 updateStickySections();
1145 void QQuickListViewPrivate::updateCurrentSection()
1147 Q_Q(QQuickListView);
1148 if (!sectionCriteria || visibleItems.isEmpty()) {
1149 if (!currentSection.isEmpty()) {
1150 currentSection.clear();
1151 emit q->currentSectionChanged();
1155 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1156 qreal sectionThreshold = position();
1157 if (currentSectionItem && !inlineSections)
1158 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1160 int modelIndex = visibleIndex;
1161 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1162 if (visibleItems.at(index)->index != -1)
1163 modelIndex = visibleItems.at(index)->index;
1167 QString newSection = currentSection;
1168 if (index < visibleItems.count())
1169 newSection = visibleItems.at(index)->attached->section();
1171 newSection = (*visibleItems.constBegin())->attached->section();
1172 if (newSection != currentSection) {
1173 currentSection = newSection;
1174 updateStickySections();
1175 emit q->currentSectionChanged();
1178 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1179 // Don't want to scan for next section on every movement, so remember
1180 // the last section in the visible area and only scan for the next
1181 // section when that changes. Clearing lastVisibleSection will also
1183 QString lastSection = currentSection;
1184 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1185 if (nextSectionItem && !inlineSections)
1186 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1187 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1188 if (visibleItems.at(index)->index != -1)
1189 modelIndex = visibleItems.at(index)->index;
1190 lastSection = visibleItems.at(index)->attached->section();
1194 if (lastVisibleSection != lastSection) {
1195 nextSection = QString();
1196 lastVisibleSection = lastSection;
1197 for (int i = modelIndex; i < itemCount; ++i) {
1198 QString section = sectionAt(i);
1199 if (section != lastSection) {
1200 nextSection = section;
1201 updateStickySections();
1209 void QQuickListViewPrivate::initializeCurrentItem()
1211 QQuickItemViewPrivate::initializeCurrentItem();
1214 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1216 // don't reposition the item if it is already in the visibleItems list
1217 FxViewItem *actualItem = visibleItem(currentIndex);
1219 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1220 // We can calculate exact postion in this case
1221 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1223 // Create current item now and position as best we can.
1224 // Its position will be corrected when it becomes visible.
1225 listItem->setPosition(positionAt(currentIndex));
1229 if (visibleItems.isEmpty())
1230 averageSize = listItem->size();
1234 void QQuickListViewPrivate::updateAverage()
1236 if (!visibleItems.count())
1239 for (int i = 0; i < visibleItems.count(); ++i)
1240 sum += visibleItems.at(i)->size();
1241 averageSize = qRound(sum / visibleItems.count());
1244 qreal QQuickListViewPrivate::headerSize() const
1246 return header ? header->size() : 0.0;
1249 qreal QQuickListViewPrivate::footerSize() const
1251 return footer ? footer->size() : 0.0;
1254 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1259 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1261 return index == model->count()-1;
1264 void QQuickListViewPrivate::updateFooter()
1266 Q_Q(QQuickListView);
1267 bool created = false;
1269 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1272 footer = new FxListItemSG(item, q, true, true);
1276 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1277 if (visibleItems.count()) {
1278 qreal endPos = lastPosition();
1279 if (findLastVisibleIndex() == model->count()-1) {
1280 listItem->setPosition(endPos);
1282 qreal visiblePos = position() + q->height();
1283 if (endPos <= visiblePos || listItem->position() < endPos)
1284 listItem->setPosition(endPos);
1287 listItem->setPosition(visiblePos);
1291 emit q->footerItemChanged();
1294 void QQuickListViewPrivate::updateHeader()
1296 Q_Q(QQuickListView);
1297 bool created = false;
1299 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1302 header = new FxListItemSG(item, q, true, true);
1306 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1308 if (visibleItems.count()) {
1309 qreal startPos = originPosition();
1310 if (visibleIndex == 0) {
1311 listItem->setPosition(startPos - headerSize());
1313 if (position() <= startPos || listItem->position() > startPos - headerSize())
1314 listItem->setPosition(startPos - headerSize());
1317 listItem->setPosition(-headerSize());
1322 emit q->headerItemChanged();
1325 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1327 Q_Q(QQuickListView);
1328 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1329 if (!q->isComponentComplete())
1332 if (item != contentItem && (!highlight || item != highlight->item)) {
1333 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1334 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1336 // if visibleItems.first() has resized, adjust its pos since it is used to
1337 // position all subsequent items
1338 if (visibleItems.count() && item == visibleItems.first()->item) {
1339 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1340 if (orient == QQuickListView::Vertical) {
1341 qreal diff = newGeometry.height() - oldGeometry.height();
1342 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1343 listItem->setPosition(listItem->position() - diff, true);
1344 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1345 listItem->setPosition(listItem->position() + diff, true);
1347 qreal diff = newGeometry.width() - oldGeometry.width();
1348 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1349 listItem->setPosition(listItem->position() - diff, true);
1350 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1351 listItem->setPosition(listItem->position() + diff, true);
1360 void QQuickListViewPrivate::fixupPosition()
1362 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1363 || snapMode != QQuickListView::NoSnap)
1365 if (orient == QQuickListView::Vertical)
1371 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1373 if ((orient == QQuickListView::Horizontal && &data == &vData)
1374 || (orient == QQuickListView::Vertical && &data == &hData))
1377 correctFlick = false;
1378 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1379 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1381 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1383 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1384 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1385 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1386 // if we've been dragged < averageSize/2 then bias towards the next item
1387 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1389 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1390 bias = averageSize/2;
1391 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1392 bias = -averageSize/2;
1393 if (isContentFlowReversed())
1395 tempPosition -= bias;
1397 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1398 if (!topItem && strictHighlightRange && currentItem) {
1399 // StrictlyEnforceRange always keeps an item in range
1401 topItem = currentItem;
1403 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1404 if (!bottomItem && strictHighlightRange && currentItem) {
1405 // StrictlyEnforceRange always keeps an item in range
1407 bottomItem = currentItem;
1410 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1411 if (topItem && (isInBounds || strictHighlightRange)) {
1412 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1413 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1415 if (isContentFlowReversed())
1416 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1418 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1420 } else if (bottomItem && isInBounds) {
1421 if (isContentFlowReversed())
1422 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1424 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1426 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1430 qreal dist = qAbs(data.move + pos);
1432 timeline.reset(data.move);
1433 if (fixupMode != Immediate) {
1434 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1435 data.fixingUp = true;
1437 timeline.set(data.move, -pos);
1439 vTime = timeline.time();
1441 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1443 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1444 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1445 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1446 if (viewPos > pos - highlightRangeStart)
1447 viewPos = pos - highlightRangeStart;
1448 if (isContentFlowReversed())
1449 viewPos = -viewPos-size();
1451 timeline.reset(data.move);
1452 if (viewPos != position()) {
1453 if (fixupMode != Immediate) {
1454 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1455 data.fixingUp = true;
1457 timeline.set(data.move, -viewPos);
1460 vTime = timeline.time();
1462 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1464 data.inOvershoot = false;
1468 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1469 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1471 data.fixingUp = false;
1473 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1474 correctFlick = true;
1475 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1477 qreal maxDistance = 0;
1478 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1480 // -ve velocity means list is moving up/left
1482 if (data.move.value() < minExtent) {
1483 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1484 // if we've been dragged < averageSize/2 then bias towards the next item
1485 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1486 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1487 if (isContentFlowReversed())
1489 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1490 maxDistance = qAbs(data.flickTarget - data.move.value());
1491 velocity = maxVelocity;
1493 maxDistance = qAbs(minExtent - data.move.value());
1496 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1497 data.flickTarget = minExtent;
1499 if (data.move.value() > maxExtent) {
1500 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1501 // if we've been dragged < averageSize/2 then bias towards the next item
1502 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1503 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1504 if (isContentFlowReversed())
1506 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1507 maxDistance = qAbs(data.flickTarget - data.move.value());
1508 velocity = -maxVelocity;
1510 maxDistance = qAbs(maxExtent - data.move.value());
1513 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1514 data.flickTarget = maxExtent;
1516 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1517 if (maxDistance > 0 || overShoot) {
1518 // These modes require the list to stop exactly on an item boundary.
1519 // The initial flick will estimate the boundary to stop on.
1520 // Since list items can have variable sizes, the boundary will be
1521 // reevaluated and adjusted as we approach the boundary.
1523 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1529 if (!hData.flicking && !vData.flicking) {
1530 // the initial flick - estimate boundary
1531 qreal accel = deceleration;
1533 overshootDist = 0.0;
1534 // + averageSize/4 to encourage moving at least one item in the flick direction
1535 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1536 if (maxDistance > 0)
1537 dist = qMin(dist, maxDistance);
1540 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1541 if (snapMode != QQuickListView::SnapOneItem) {
1542 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1543 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1545 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1547 if (data.flickTarget >= minExtent) {
1548 overshootDist = overShootDistance(vSize);
1549 data.flickTarget += overshootDist;
1550 } else if (data.flickTarget <= maxExtent) {
1551 overshootDist = overShootDistance(vSize);
1552 data.flickTarget -= overshootDist;
1555 qreal adjDist = -data.flickTarget + data.move.value();
1556 if (qAbs(adjDist) > qAbs(dist)) {
1557 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1558 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1567 accel = v2 / (2.0f * qAbs(dist));
1568 } else if (overShoot) {
1569 data.flickTarget = data.move.value() - dist;
1570 if (data.flickTarget >= minExtent) {
1571 overshootDist = overShootDistance(vSize);
1572 data.flickTarget += overshootDist;
1573 } else if (data.flickTarget <= maxExtent) {
1574 overshootDist = overShootDistance(vSize);
1575 data.flickTarget -= overshootDist;
1578 timeline.reset(data.move);
1579 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1580 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1581 correctFlick = true;
1584 // reevaluate the target boundary.
1585 qreal newtarget = data.flickTarget;
1586 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1587 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1588 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1589 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1591 if (velocity < 0 && newtarget <= maxExtent)
1592 newtarget = maxExtent - overshootDist;
1593 else if (velocity > 0 && newtarget >= minExtent)
1594 newtarget = minExtent + overshootDist;
1595 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1596 if (qAbs(velocity) < MinimumFlickVelocity)
1597 correctFlick = false;
1600 data.flickTarget = newtarget;
1601 qreal dist = -newtarget + data.move.value();
1602 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1603 correctFlick = false;
1604 timeline.reset(data.move);
1605 fixup(data, minExtent, maxExtent);
1608 timeline.reset(data.move);
1609 timeline.accelDistance(data.move, v, -dist);
1610 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1614 correctFlick = false;
1615 timeline.reset(data.move);
1616 fixup(data, minExtent, maxExtent);
1621 //----------------------------------------------------------------------------
1624 \qmlclass ListView QQuickListView
1625 \inqmlmodule QtQuick 2
1626 \ingroup qtquick-views
1628 \brief Provides a list view of items provided by a model
1630 A ListView displays data from models created from built-in QML elements like ListModel
1631 and XmlListModel, or custom model classes defined in C++ that inherit from
1634 A ListView has a \l model, which defines the data to be displayed, and
1635 a \l delegate, which defines how the data should be displayed. Items in a
1636 ListView are laid out horizontally or vertically. List views are inherently
1637 flickable because ListView inherits from \l Flickable.
1639 \section1 Example Usage
1641 The following example shows the definition of a simple list model defined
1642 in a file called \c ContactModel.qml:
1644 \snippet qml/listview/ContactModel.qml 0
1646 Another component can display this model data in a ListView, like this:
1648 \snippet qml/listview/listview.qml import
1650 \snippet qml/listview/listview.qml classdocs simple
1652 \image listview-simple.png
1654 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1655 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1656 the delegate is able to access the model's \c name and \c number data directly.
1658 An improved list view is shown below. The delegate is visually improved and is moved
1659 into a separate \c contactDelegate component.
1661 \snippet qml/listview/listview.qml classdocs advanced
1662 \image listview-highlight.png
1664 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1665 and \c focus is set to \c true to enable keyboard navigation for the list view.
1666 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1668 Delegates are instantiated as needed and may be destroyed at any time.
1669 State should \e never be stored in a delegate.
1671 ListView attaches a number of properties to the root item of the delegate, for example
1672 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1673 this attached property directly as \c ListView.isCurrentItem, while the child
1674 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1676 \snippet qml/listview/listview.qml isCurrentItem
1678 \note Views do not enable \e clip automatically. If the view
1679 is not clipped by another item or the screen, it will be necessary
1680 to set \e {clip: true} in order to have the out of view items clipped
1684 \section1 ListView layouts
1686 The layout of the items in a ListView can be controlled by these properties:
1689 \li \l orientation - controls whether items flow horizontally or vertically.
1690 This value can be either Qt.Horizontal or Qt.Vertical.
1691 \li \l layoutDirection - controls the horizontal layout direction for a
1692 horizontally-oriented view: that is, whether items are laid out from the left side of
1693 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1694 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1695 view: that is, whether items are laid out from the top of the view down towards the bottom of
1696 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1699 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1700 table below shows the different layouts that a ListView can have, depending on the values of
1701 the properties listed above.
1706 \bold ListViews with Qt.Vertical orientation
1709 \image listview-layout-toptobottom.png
1711 \image listview-layout-bottomtotop.png
1714 \bold ListViews with Qt.Horizontal orientation
1717 \image listview-layout-lefttoright.png
1719 \image listview-layout-righttoleft.png
1722 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1724 QQuickListView::QQuickListView(QQuickItem *parent)
1725 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1729 QQuickListView::~QQuickListView()
1734 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1735 This attached property is true if this delegate is the current item; otherwise false.
1737 It is attached to each instance of the delegate.
1739 This property may be used to adjust the appearance of the current item, for example:
1741 \snippet qml/listview/listview.qml isCurrentItem
1745 \qmlattachedproperty ListView QtQuick2::ListView::view
1746 This attached property holds the view that manages this delegate instance.
1748 It is attached to each instance of the delegate.
1752 \qmlattachedproperty string QtQuick2::ListView::previousSection
1753 This attached property holds the section of the previous element.
1755 It is attached to each instance of the delegate.
1757 The section is evaluated using the \l {ListView::section.property}{section} properties.
1761 \qmlattachedproperty string QtQuick2::ListView::nextSection
1762 This attached property holds the section of the next element.
1764 It is attached to each instance of the delegate.
1766 The section is evaluated using the \l {ListView::section.property}{section} properties.
1770 \qmlattachedproperty string QtQuick2::ListView::section
1771 This attached property holds the section of this element.
1773 It is attached to each instance of the delegate.
1775 The section is evaluated using the \l {ListView::section.property}{section} properties.
1779 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1781 This attached property holds whether the delegate may be destroyed. It
1782 is attached to each instance of the delegate. The default value is false.
1784 It is sometimes necessary to delay the destruction of an item
1785 until an animation completes. The example delegate below ensures that the
1786 animation completes before the item is removed from the list.
1788 \snippet qml/listview/listview.qml delayRemove
1790 If a \l remove transition has been specified, it will not be applied until
1791 delayRemove is returned to \c false.
1795 \qmlattachedsignal QtQuick2::ListView::onAdd()
1796 This attached signal handler is called immediately after an item is added to the view.
1798 If an \l add transition is specified, it is applied immediately after
1799 this signal handler is called.
1803 \qmlattachedsignal QtQuick2::ListView::onRemove()
1804 This attached handler is called immediately before an item is removed from the view.
1806 If a \l remove transition has been specified, it is applied after
1807 this signal handler is called, providing that delayRemove is false.
1811 \qmlproperty model QtQuick2::ListView::model
1812 This property holds the model providing data for the list.
1814 The model provides the set of data that is used to create the items
1815 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1816 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1817 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1819 \sa {qmlmodels}{Data Models}
1823 \qmlproperty Component QtQuick2::ListView::delegate
1825 The delegate provides a template defining each item instantiated by the view.
1826 The index is exposed as an accessible \c index property. Properties of the
1827 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1829 The number of elements in the delegate has a direct effect on the
1830 flicking performance of the view. If at all possible, place functionality
1831 that is not needed for the normal display of the delegate in a \l Loader which
1832 can load additional elements when needed.
1834 The ListView will lay out the items based on the size of the root item
1837 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1840 \note Delegates are instantiated as needed and may be destroyed at any time.
1841 State should \e never be stored in a delegate.
1844 \qmlproperty int QtQuick2::ListView::currentIndex
1845 \qmlproperty Item QtQuick2::ListView::currentItem
1847 The \c currentIndex property holds the index of the current item, and
1848 \c currentItem holds the current item. Setting the currentIndex to -1
1849 will clear the highlight and set currentItem to null.
1851 If highlightFollowsCurrentItem is \c true, setting either of these
1852 properties will smoothly scroll the ListView so that the current
1853 item becomes visible.
1855 Note that the position of the current item
1856 may only be approximate until it becomes visible in the view.
1860 \qmlproperty Item QtQuick2::ListView::highlightItem
1862 This holds the highlight item created from the \l highlight component.
1864 The \c highlightItem is managed by the view unless
1865 \l highlightFollowsCurrentItem is set to false.
1867 \sa highlight, highlightFollowsCurrentItem
1871 \qmlproperty int QtQuick2::ListView::count
1872 This property holds the number of items in the view.
1876 \qmlproperty Component QtQuick2::ListView::highlight
1877 This property holds the component to use as the highlight.
1879 An instance of the highlight component is created for each list.
1880 The geometry of the resulting component instance is managed by the list
1881 so as to stay with the current item, unless the highlightFollowsCurrentItem
1884 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1888 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1889 This property holds whether the highlight is managed by the view.
1891 If this property is true (the default value), the highlight is moved smoothly
1892 to follow the current item. Otherwise, the
1893 highlight is not moved by the view, and any movement must be implemented
1896 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1898 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1900 Note that the highlight animation also affects the way that the view
1901 is scrolled. This is because the view moves to maintain the
1902 highlight within the preferred highlight range (or visible viewport).
1906 //###Possibly rename these properties, since they are very useful even without a highlight?
1908 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1909 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1910 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1912 These properties define the preferred range of the highlight (for the current item)
1913 within the view. The \c preferredHighlightBegin value must be less than the
1914 \c preferredHighlightEnd value.
1916 These properties affect the position of the current item when the list is scrolled.
1917 For example, if the currently selected item should stay in the middle of the
1918 list when the view is scrolled, set the \c preferredHighlightBegin and
1919 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1920 item would be. If the \c currentItem is changed programmatically, the list will
1921 automatically scroll so that the current item is in the middle of the view.
1922 Furthermore, the behavior of the current item index will occur whether or not a
1925 Valid values for \c highlightRangeMode are:
1928 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1929 However, the highlight can move outside of the range at the ends of the list or due
1930 to mouse interaction.
1931 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1932 The current item changes if a keyboard or mouse action would cause the highlight to move
1933 outside of the range.
1934 \li ListView.NoHighlightRange - this is the default value.
1937 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1939 Q_D(QQuickListView);
1940 if (d->autoHighlight != autoHighlight) {
1941 if (!autoHighlight) {
1942 if (d->highlightPosAnimator)
1943 d->highlightPosAnimator->stop();
1944 if (d->highlightSizeAnimator)
1945 d->highlightSizeAnimator->stop();
1947 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1952 \qmlproperty real QtQuick2::ListView::spacing
1954 This property holds the spacing between items.
1956 The default value is 0.
1958 qreal QQuickListView::spacing() const
1960 Q_D(const QQuickListView);
1964 void QQuickListView::setSpacing(qreal spacing)
1966 Q_D(QQuickListView);
1967 if (spacing != d->spacing) {
1968 d->spacing = spacing;
1969 d->forceLayout = true;
1971 emit spacingChanged();
1976 \qmlproperty enumeration QtQuick2::ListView::orientation
1977 This property holds the orientation of the list.
1982 \li ListView.Horizontal - Items are laid out horizontally
1983 \li ListView.Vertical (default) - Items are laid out vertically
1988 \li Horizontal orientation:
1989 \image ListViewHorizontal.png
1992 \li Vertical orientation:
1993 \image listview-highlight.png
1996 QQuickListView::Orientation QQuickListView::orientation() const
1998 Q_D(const QQuickListView);
2002 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2004 Q_D(QQuickListView);
2005 if (d->orient != orientation) {
2006 d->orient = orientation;
2007 if (d->orient == Vertical) {
2008 setContentWidth(-1);
2009 setFlickableDirection(VerticalFlick);
2012 setContentHeight(-1);
2013 setFlickableDirection(HorizontalFlick);
2017 emit orientationChanged();
2022 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
2023 This property holds the layout direction of a horizontally-oriented list.
2028 \li Qt.LeftToRight (default) - Items will be laid out from left to right.
2029 \li Qt.RightToLeft - Items will be laid out from right to let.
2032 Setting this property has no effect if the \l orientation is Qt.Vertical.
2034 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2039 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
2040 This property holds the effective layout direction of a horizontally-oriented list.
2042 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2043 the visual layout direction of the horizontal list will be mirrored. However, the
2044 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2046 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2051 \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
2052 This property holds the layout direction of a vertically-oriented list.
2057 \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
2058 \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
2061 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2063 \sa ListView::layoutDirection
2068 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
2069 This property holds whether the list wraps key navigation.
2071 If this is true, key navigation that would move the current item selection
2072 past the end of the list instead wraps around and moves the selection to
2073 the start of the list, and vice-versa.
2075 By default, key navigation is not wrapped.
2080 \qmlproperty int QtQuick2::ListView::cacheBuffer
2081 This property determines whether delegates are retained outside the
2082 visible area of the view.
2084 If this value is non-zero, the view may keep as many delegates
2085 instantiated as it can fit within the buffer specified. For example,
2086 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2087 set to 40, then up to 2 delegates above and 2 delegates below the visible
2088 area may be created/retained. The buffered delegates are created asynchronously,
2089 allowing creation to occur across multiple frames and reducing the
2090 likelihood of skipping frames. In order to improve painting performance
2091 delegates outside the visible area are not painted.
2093 The default value of this property is platform dependent, but will usually
2094 be a non-zero value.
2096 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2097 instantiated delegates.
2099 Setting this value can improve the smoothness of scrolling behavior at the expense
2100 of additional memory usage. It is not a substitute for creating efficient
2101 delegates; the fewer elements in a delegate, the faster a view can be
2107 \qmlproperty string QtQuick2::ListView::section.property
2108 \qmlproperty enumeration QtQuick2::ListView::section.criteria
2109 \qmlproperty Component QtQuick2::ListView::section.delegate
2110 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
2112 These properties determine the expression to be evaluated and appearance
2113 of the section labels.
2115 \c section.property holds the name of the property that is the basis
2118 \c section.criteria holds the criteria for forming each section based on
2119 \c section.property. This value can be one of:
2122 \li ViewSection.FullString (default) - sections are created based on the
2123 \c section.property value.
2124 \li ViewSection.FirstCharacter - sections are created based on the first
2125 character of the \c section.property value (for example, 'A', 'B', 'C'
2126 sections, etc. for an address book)
2129 A case insensitive comparison is used when determining section
2132 \c section.delegate holds the delegate component for each section.
2134 \c section.labelPositioning determines whether the current and/or
2135 next section labels stick to the start/end of the view, and whether
2136 the labels are shown inline. This value can be a combination of:
2139 \li ViewSection.InlineLabels - section labels are shown inline between
2140 the item delegates separating sections (default).
2141 \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
2142 start of the view as it is moved.
2143 \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
2144 sections) sticks to the end of the view as it is moved. \note Enabling
2145 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
2146 section, which has performance implications, especially for slower models.
2149 Each item in the list has attached properties named \c ListView.section,
2150 \c ListView.previousSection and \c ListView.nextSection.
2152 For example, here is a ListView that displays a list of animals, separated
2153 into sections. Each item in the ListView is placed in a different section
2154 depending on the "size" property of the model item. The \c sectionHeading
2155 delegate component provides the light blue bar that marks the beginning of
2159 \snippet examples/quick/modelviews/listview/sections.qml 0
2161 \image qml-listview-sections-example.png
2163 \note Adding sections to a ListView does not automatically re-order the
2164 list items by the section criteria.
2165 If the model is not ordered by section, then it is possible that
2166 the sections created will not be unique; each boundary between
2167 differing sections will result in a section header being created
2168 even if that section exists elsewhere.
2170 \sa {quick/modelviews/listview}{ListView examples}
2172 QQuickViewSection *QQuickListView::sectionCriteria()
2174 Q_D(QQuickListView);
2175 if (!d->sectionCriteria) {
2176 d->sectionCriteria = new QQuickViewSection(this);
2177 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2179 return d->sectionCriteria;
2183 \qmlproperty string QtQuick2::ListView::currentSection
2184 This property holds the section that is currently at the beginning of the view.
2186 QString QQuickListView::currentSection() const
2188 Q_D(const QQuickListView);
2189 return d->currentSection;
2193 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2194 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2196 These properties hold the move and resize animation duration of
2197 the highlight delegate.
2199 \l highlightFollowsCurrentItem must be true for these properties
2202 The default value for highlightMoveDuration is 150ms and the
2203 default value for highlightResizeDuration is 250ms.
2205 \sa highlightFollowsCurrentItem
2207 void QQuickListView::setHighlightMoveDuration(int duration)
2209 Q_D(QQuickListView);
2210 if (d->highlightMoveDuration != duration) {
2211 if (d->highlightPosAnimator)
2212 d->highlightPosAnimator->userDuration = duration;
2213 QQuickItemView::setHighlightMoveDuration(duration);
2217 int QQuickListView::highlightResizeDuration() const
2219 Q_D(const QQuickListView);
2220 return d->highlightResizeDuration;
2223 void QQuickListView::setHighlightResizeDuration(int duration)
2225 Q_D(QQuickListView);
2226 if (d->highlightResizeDuration != duration) {
2227 d->highlightResizeDuration = duration;
2228 if (d->highlightSizeAnimator)
2229 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2230 emit highlightResizeDurationChanged();
2235 \qmlproperty enumeration QtQuick2::ListView::snapMode
2237 This property determines how the view scrolling will settle following a drag or flick.
2238 The possible values are:
2241 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2242 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2244 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2245 visible item at the time the mouse button is released. This mode is particularly
2246 useful for moving one page at a time.
2249 \c snapMode does not affect the \l currentIndex. To update the
2250 \l currentIndex as the list is moved, set \l highlightRangeMode
2251 to \c ListView.StrictlyEnforceRange.
2253 \sa highlightRangeMode
2255 QQuickListView::SnapMode QQuickListView::snapMode() const
2257 Q_D(const QQuickListView);
2261 void QQuickListView::setSnapMode(SnapMode mode)
2263 Q_D(QQuickListView);
2264 if (d->snapMode != mode) {
2266 emit snapModeChanged();
2272 \qmlproperty Component QtQuick2::ListView::footer
2273 This property holds the component to use as the footer.
2275 An instance of the footer component is created for each view. The
2276 footer is positioned at the end of the view, after any items.
2278 \sa header, footerItem
2283 \qmlproperty Component QtQuick2::ListView::header
2284 This property holds the component to use as the header.
2286 An instance of the header component is created for each view. The
2287 header is positioned at the beginning of the view, before any items.
2289 \sa footer, headertem
2293 \qmlproperty Item QtQuick2::ListView::headerItem
2294 This holds the header item created from the \l header component.
2296 An instance of the header component is created for each view. The
2297 header is positioned at the beginning of the view, before any items.
2299 \sa header, footerItem
2303 \qmlproperty Item QtQuick2::ListView::footerItem
2304 This holds the footer item created from the \l footer component.
2306 An instance of the footer component is created for each view. The
2307 footer is positioned at the end of the view, after any items.
2309 \sa footer, headerItem
2313 \qmlproperty Transition QtQuick2::ListView::populate
2315 This property holds the transition to apply to the items that are initially created
2318 It is applied to all items that are created when:
2321 \li The view is first created
2322 \li The view's \l model changes
2323 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2326 For example, here is a view that specifies such a transition:
2331 populate: Transition {
2332 NumberAnimation { properties: "x,y"; duration: 1000 }
2337 When the view is initialized, the view will create all the necessary items for the view,
2338 then animate them to their correct positions within the view over one second.
2340 For more details and examples on how to use view transitions, see the ViewTransition
2343 \sa add, ViewTransition
2347 \qmlproperty Transition QtQuick2::ListView::add
2349 This property holds the transition to apply to items that are added to the view.
2351 For example, here is a view that specifies such a transition:
2357 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2362 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2363 to its final x,y position within the view, over one second. The transition only applies to
2364 the new items that are added to the view; it does not apply to the items below that are
2365 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2366 or \l addDisplaced properties.
2368 For more details and examples on how to use view transitions, see the ViewTransition
2371 \note This transition is not applied to the items that are created when the view is initially
2372 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2375 \sa addDisplaced, populate, ViewTransition
2379 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2381 This property holds the transition to apply to items within the view that are displaced by
2382 the addition of other items to the view.
2384 For example, here is a view that specifies such a transition:
2389 addDisplaced: Transition {
2390 NumberAnimation { properties: "x,y"; duration: 1000 }
2395 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2396 them to move down (or sideways, if horizontally orientated) within the view. As this
2397 displacement occurs, the items' movement to their new x,y positions within the view will be
2398 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2399 the new item that has been added to the view; to animate the added items, set the \l add
2402 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2403 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2404 if it is not necessary to specify different transitions depending on whether an item is displaced
2405 by an add, move or remove operation, consider setting the \l displaced property instead.
2407 For more details and examples on how to use view transitions, see the ViewTransition
2410 \note This transition is not applied to the items that are created when the view is initially
2411 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2414 \sa displaced, add, populate, ViewTransition
2418 \qmlproperty Transition QtQuick2::ListView::move
2420 This property holds the transition to apply to items in the view that are being moved due
2421 to a move operation in the view's \l model.
2423 For example, here is a view that specifies such a transition:
2429 NumberAnimation { properties: "x,y"; duration: 1000 }
2434 Whenever the \l model performs a move operation to move a particular set of indexes, the
2435 respective items in the view will be animated to their new positions in the view over one
2436 second. The transition only applies to the items that are the subject of the move operation
2437 in the model; it does not apply to items below them that are displaced by the move operation.
2438 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2440 For more details and examples on how to use view transitions, see the ViewTransition
2443 \sa moveDisplaced, ViewTransition
2447 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2449 This property holds the transition to apply to items that are displaced by a move operation in
2450 the view's \l model.
2452 For example, here is a view that specifies such a transition:
2457 moveDisplaced: Transition {
2458 NumberAnimation { properties: "x,y"; duration: 1000 }
2463 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2464 between the source and destination indexes of the move operation are displaced, causing them
2465 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2466 displacement occurs, the items' movement to their new x,y positions within the view will be
2467 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2468 the items that are the actual subjects of the move operation; to animate the moved items, set
2469 the \l move property.
2471 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2472 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2473 if it is not necessary to specify different transitions depending on whether an item is displaced
2474 by an add, move or remove operation, consider setting the \l displaced property instead.
2476 For more details and examples on how to use view transitions, see the ViewTransition
2479 \sa displaced, move, ViewTransition
2483 \qmlproperty Transition QtQuick2::ListView::remove
2485 This property holds the transition to apply to items that are removed from the view.
2487 For example, here is a view that specifies such a transition:
2492 remove: Transition {
2494 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2495 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2501 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2502 over one second, and in parallel will also change its opacity to 0. The transition
2503 only applies to the items that are removed from the view; it does not apply to the items below
2504 them that are displaced by the removal of the items. To animate the displaced items, set the
2505 \l displaced or \l removeDisplaced properties.
2507 Note that by the time the transition is applied, the item has already been removed from the
2508 model; any references to the model data for the removed index will not be valid.
2510 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2511 remove transition will not be applied until \l delayRemove becomes false again.
2513 For more details and examples on how to use view transitions, see the ViewTransition
2516 \sa removeDisplaced, ViewTransition
2520 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2522 This property holds the transition to apply to items in the view that are displaced by the
2523 removal of other items in the view.
2525 For example, here is a view that specifies such a transition:
2530 removeDisplaced: Transition {
2531 NumberAnimation { properties: "x,y"; duration: 1000 }
2536 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2537 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2538 displacement occurs, the items' movement to their new x,y positions within the view will be
2539 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2540 the item that has actually been removed from the view; to animate the removed items, set the
2543 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2544 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2545 if it is not necessary to specify different transitions depending on whether an item is displaced
2546 by an add, move or remove operation, consider setting the \l displaced property instead.
2548 For more details and examples on how to use view transitions, see the ViewTransition
2551 \sa displaced, remove, ViewTransition
2555 \qmlproperty Transition QtQuick2::ListView::displaced
2556 This property holds the generic transition to apply to items that have been displaced by
2557 any model operation that affects the view.
2559 This is a convenience for specifying the generic transition to be applied to any items
2560 that are displaced by an add, move or remove operation, without having to specify the
2561 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2562 is a view that specifies a displaced transition:
2567 displaced: Transition {
2568 NumberAnimation { properties: "x,y"; duration: 1000 }
2573 When any item is added, moved or removed within the above view, the items below it are
2574 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2575 view. As this displacement occurs, the items' movement to their new x,y positions within
2576 the view will be animated by a NumberAnimation over one second, as specified.
2578 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2579 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2580 instead of the generic displaced transition when the relevant operation occurs, providing that
2581 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2582 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2584 For more details and examples on how to use view transitions, see the ViewTransition
2587 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2590 void QQuickListView::viewportMoved(Qt::Orientations orient)
2592 Q_D(QQuickListView);
2593 QQuickItemView::viewportMoved(orient);
2596 // Recursion can occur due to refill changing the content size.
2597 if (d->inViewportMoved)
2599 d->inViewportMoved = true;
2602 if (d->isBottomToTop())
2603 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2605 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2607 if (d->isRightToLeft())
2608 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2610 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2613 d->refillOrLayout();
2615 // Set visibility of items to eliminate cost of items outside the visible area.
2616 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2617 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2618 for (int i = 0; i < d->visibleItems.count(); ++i) {
2619 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2620 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2623 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2625 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2626 d->moveReason = QQuickListViewPrivate::Mouse;
2627 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2628 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2629 // reposition highlight
2630 qreal pos = d->highlight->position();
2631 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2632 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2633 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2634 if (pos < viewPos + d->highlightRangeStart)
2635 pos = viewPos + d->highlightRangeStart;
2636 if (pos != d->highlight->position()) {
2637 d->highlightPosAnimator->stop();
2638 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2640 d->updateHighlight();
2643 // update current index
2644 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2645 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2646 d->updateCurrent(snapItem->index);
2651 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2652 d->inFlickCorrection = true;
2653 // Near an end and it seems that the extent has changed?
2654 // Recalculate the flick so that we don't end up in an odd position.
2655 if (yflick() && !d->vData.inOvershoot) {
2656 if (d->vData.velocity > 0) {
2657 const qreal minY = minYExtent();
2658 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2659 && minY != d->vData.flickTarget)
2660 d->flickY(-d->vData.smoothVelocity.value());
2661 } else if (d->vData.velocity < 0) {
2662 const qreal maxY = maxYExtent();
2663 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2664 && maxY != d->vData.flickTarget)
2665 d->flickY(-d->vData.smoothVelocity.value());
2669 if (xflick() && !d->hData.inOvershoot) {
2670 if (d->hData.velocity > 0) {
2671 const qreal minX = minXExtent();
2672 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2673 && minX != d->hData.flickTarget)
2674 d->flickX(-d->hData.smoothVelocity.value());
2675 } else if (d->hData.velocity < 0) {
2676 const qreal maxX = maxXExtent();
2677 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2678 && maxX != d->hData.flickTarget)
2679 d->flickX(-d->hData.smoothVelocity.value());
2682 d->inFlickCorrection = false;
2684 if (d->sectionCriteria) {
2685 d->updateCurrentSection();
2686 d->updateStickySections();
2688 d->inViewportMoved = false;
2691 void QQuickListView::keyPressEvent(QKeyEvent *event)
2693 Q_D(QQuickListView);
2694 if (d->model && d->model->count() && d->interactive) {
2695 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2696 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2697 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2698 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2699 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2700 decrementCurrentIndex();
2703 } else if (d->wrap) {
2707 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2708 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2709 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2710 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2711 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2712 incrementCurrentIndex();
2715 } else if (d->wrap) {
2722 QQuickItemView::keyPressEvent(event);
2725 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2727 Q_D(QQuickListView);
2728 if (d->isRightToLeft()) {
2729 // maintain position relative to the right edge
2730 int dx = newGeometry.width() - oldGeometry.width();
2731 setContentX(contentX() - dx);
2732 } else if (d->isBottomToTop()) {
2733 // maintain position relative to the bottom edge
2734 int dy = newGeometry.height() - oldGeometry.height();
2735 setContentY(contentY() - dy);
2737 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2740 void QQuickListView::initItem(int index, QQuickItem *item)
2742 QQuickItemView::initItem(index, item);
2743 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
2744 qmlAttachedPropertiesObject<QQuickListView>(item));
2746 attached->setView(this);
2751 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2753 Increments the current index. The current index will wrap
2754 if keyNavigationWraps is true and it is currently at the end.
2755 This method has no effect if the \l count is zero.
2757 \b Note: methods should only be called after the Component has completed.
2759 void QQuickListView::incrementCurrentIndex()
2761 Q_D(QQuickListView);
2762 int count = d->model ? d->model->count() : 0;
2763 if (count && (currentIndex() < count - 1 || d->wrap)) {
2764 d->moveReason = QQuickListViewPrivate::SetIndex;
2765 int index = currentIndex()+1;
2766 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2771 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2773 Decrements the current index. The current index will wrap
2774 if keyNavigationWraps is true and it is currently at the beginning.
2775 This method has no effect if the \l count is zero.
2777 \b Note: methods should only be called after the Component has completed.
2779 void QQuickListView::decrementCurrentIndex()
2781 Q_D(QQuickListView);
2782 int count = d->model ? d->model->count() : 0;
2783 if (count && (currentIndex() > 0 || d->wrap)) {
2784 d->moveReason = QQuickListViewPrivate::SetIndex;
2785 int index = currentIndex()-1;
2786 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2790 void QQuickListView::updateSections()
2792 Q_D(QQuickListView);
2793 if (isComponentComplete() && d->model) {
2794 QList<QByteArray> roles;
2795 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2796 roles << d->sectionCriteria->property().toUtf8();
2797 d->model->setWatchedRoles(roles);
2798 d->updateSections();
2800 d->forceLayout = true;
2806 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2808 int modelIndex = change.index;
2809 int count = change.count;
2811 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2812 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2815 int i = visibleItems.count() - 1;
2816 while (i > 0 && visibleItems.at(i)->index == -1)
2818 if (i == 0 && visibleItems.first()->index == -1) {
2819 // there are no visible items except items marked for removal
2820 index = visibleItems.count();
2821 } else if (visibleItems.at(i)->index + 1 == modelIndex
2822 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2823 // Special case of appending an item to the model.
2824 index = visibleItems.count();
2826 if (modelIndex < visibleIndex) {
2827 // Insert before visible items
2828 visibleIndex += count;
2829 for (int i = 0; i < visibleItems.count(); ++i) {
2830 FxViewItem *item = visibleItems.at(i);
2831 if (item->index != -1 && item->index >= modelIndex)
2832 item->index += count;
2839 // index can be the next item past the end of the visible items list (i.e. appended)
2841 if (visibleItems.count()) {
2842 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2843 : visibleItems.last()->endPosition()+spacing;
2846 int prevVisibleCount = visibleItems.count();
2847 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2848 // Insert items before the visible item.
2849 int insertionIdx = index;
2851 int from = tempPos - buffer;
2853 for (i = count-1; i >= 0; --i) {
2854 if (pos > from && insertionIdx < visibleIndex) {
2855 // item won't be visible, just note the size for repositioning
2856 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2857 pos -= averageSize + spacing;
2859 // item is before first visible e.g. in cache buffer
2860 FxViewItem *item = 0;
2861 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2862 item->index = modelIndex + i;
2864 item = createItem(modelIndex + i);
2868 visibleItems.insert(insertionIdx, item);
2869 if (insertionIdx == 0)
2870 insertResult->changedFirstItem = true;
2871 if (!change.isMove()) {
2872 addedItems->append(item);
2873 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2875 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2876 pos -= item->size() + spacing;
2882 int to = buffer+tempPos+size();
2883 for (i = 0; i < count && pos <= to; ++i) {
2884 FxViewItem *item = 0;
2885 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2886 item->index = modelIndex + i;
2887 bool newItem = !item;
2889 item = createItem(modelIndex + i);
2893 visibleItems.insert(index, item);
2895 insertResult->changedFirstItem = true;
2896 if (change.isMove()) {
2897 // we know this is a move target, since move displaced items that are
2898 // shuffled into view due to a move would be added in refill()
2899 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2900 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2902 addedItems->append(item);
2903 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2905 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2906 pos += item->size() + spacing;
2911 for (; index < visibleItems.count(); ++index) {
2912 FxViewItem *item = visibleItems.at(index);
2913 if (item->index != -1) {
2914 item->index += count;
2915 if (change.isMove())
2916 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2918 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2922 updateVisibleIndex();
2924 return visibleItems.count() > prevVisibleCount;
2927 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2929 Q_UNUSED(insertionResult);
2934 int markerItemIndex = -1;
2935 for (int i=0; i<visibleItems.count(); i++) {
2936 if (visibleItems[i]->index == afterModelIndex) {
2937 markerItemIndex = i;
2941 if (markerItemIndex < 0)
2944 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2945 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
2946 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
2948 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2949 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
2950 if (!listItem->transitionScheduledOrRunning()) {
2951 qreal pos = listItem->position();
2952 listItem->setPosition(pos - sizeRemoved);
2953 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2954 listItem->setPosition(pos);
2960 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2962 Positions the view such that the \a index is at the position specified by
2966 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2967 \li ListView.Center - position item in the center of the view.
2968 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2969 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
2970 bring the item into view.
2971 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
2972 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2975 If positioning the view at \a index would cause empty space to be displayed at
2976 the beginning or end of the view, the view will be positioned at the boundary.
2978 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2979 at a particular index. This is unreliable since removing items from the start
2980 of the list does not cause all other items to be repositioned, and because
2981 the actual start of the view can vary based on the size of the delegates.
2982 The correct way to bring an item into view is with \c positionViewAtIndex.
2984 \b Note: methods should only be called after the Component has completed. To position
2985 the view at startup, this method should be called by Component.onCompleted. For
2986 example, to position the view at the end:
2989 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2994 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
2995 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
2997 Positions the view at the beginning or end, taking into account any header or footer.
2999 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3000 at a particular index. This is unreliable since removing items from the start
3001 of the list does not cause all other items to be repositioned, and because
3002 the actual start of the view can vary based on the size of the delegates.
3004 \b Note: methods should only be called after the Component has completed. To position
3005 the view at startup, this method should be called by Component.onCompleted. For
3006 example, to position the view at the end on startup:
3009 Component.onCompleted: positionViewAtEnd()
3014 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3016 Returns the index of the visible item containing the point \a x, \a y in content
3017 coordinates. If there is no item at the point specified, or the item is
3018 not visible -1 is returned.
3020 If the item is outside the visible area, -1 is returned, regardless of
3021 whether an item will exist at that point when scrolled into view.
3023 \b Note: methods should only be called after the Component has completed.
3027 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3029 Returns the visible item containing the point \a x, \a y in content
3030 coordinates. If there is no item at the point specified, or the item is
3031 not visible null is returned.
3033 If the item is outside the visible area, null is returned, regardless of
3034 whether an item will exist at that point when scrolled into view.
3036 \b Note: methods should only be called after the Component has completed.
3039 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3041 return new QQuickListViewAttached(obj);