1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquicklistview_p.h"
43 #include "qquickitemview_p_p.h"
44 #include "qquickvisualitemmodel_p.h"
46 #include <QtQml/qqmlexpression.h>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
53 #include <private/qquicksmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
55 #include "qplatformdefs.h"
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
63 //#define DEBUG_DELEGATE_LIFECYCLE
67 class QQuickListViewPrivate : public QQuickItemViewPrivate
69 Q_DECLARE_PUBLIC(QQuickListView)
71 static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
73 virtual Qt::Orientation layoutOrientation() const;
74 virtual bool isContentFlowReversed() const;
75 bool isRightToLeft() const;
76 bool isBottomToTop() const;
78 virtual qreal positionAt(int index) const;
79 virtual qreal endPositionAt(int index) const;
80 virtual qreal originPosition() const;
81 virtual qreal lastPosition() const;
83 FxViewItem *itemBefore(int modelIndex) const;
84 QString sectionAt(int modelIndex);
85 qreal snapPosAt(qreal pos);
86 FxViewItem *snapItemAt(qreal pos);
91 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
92 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
93 virtual void visibleItemsChanged();
95 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
96 virtual void initializeViewItem(FxViewItem *item);
97 virtual bool releaseItem(FxViewItem *item);
98 virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
99 virtual void repositionPackageItemAt(QQuickItem *item, int index);
100 virtual void resetFirstItemPosition(qreal pos = 0.0);
101 virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
103 virtual void createHighlight();
104 virtual void updateHighlight();
105 virtual void resetHighlightPosition();
107 virtual void setPosition(qreal pos);
108 virtual void layoutVisibleItems(int fromModelIndex = 0);
110 virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
111 virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
113 virtual void updateSections();
114 QQuickItem *getSectionItem(const QString §ion);
115 void releaseSectionItem(QQuickItem *item);
116 void updateInlineSection(FxListItemSG *);
117 void updateCurrentSection();
118 void updateStickySections();
120 virtual qreal headerSize() const;
121 virtual qreal footerSize() const;
122 virtual bool showHeaderForIndex(int index) const;
123 virtual bool showFooterForIndex(int index) const;
124 virtual void updateHeader();
125 virtual void updateFooter();
127 virtual void changedVisibleIndex(int newIndex);
128 virtual void initializeCurrentItem();
130 void updateAverage();
132 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
133 virtual void fixupPosition();
134 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
135 virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
136 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
138 QQuickListView::Orientation orient;
142 QQuickListView::SnapMode snapMode;
144 QSmoothedAnimation *highlightPosAnimator;
145 QSmoothedAnimation *highlightSizeAnimator;
146 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 static_cast<QQuickListViewAttached*>(attached)->setView(view);
243 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
244 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
250 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
251 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
255 inline QQuickItem *section() const {
256 return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
258 void setSection(QQuickItem *s) {
260 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
261 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
264 qreal position() const {
266 if (view->orientation() == QQuickListView::Vertical)
267 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
269 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
271 return itemPosition();
274 qreal itemPosition() const {
275 if (view->orientation() == QQuickListView::Vertical)
276 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
278 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
282 return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
284 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
286 qreal itemSize() const {
287 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
289 qreal sectionSize() const {
291 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
294 qreal endPosition() const {
295 if (view->orientation() == QQuickListView::Vertical) {
296 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
298 : itemY() + item->height());
300 return (view->effectiveLayoutDirection() == Qt::RightToLeft
302 : itemX() + item->width());
305 void setPosition(qreal pos, bool immediate = false) {
306 // position the section immediately even if there is a transition
308 if (view->orientation() == QQuickListView::Vertical) {
309 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
310 section()->setY(-section()->height()-pos);
312 section()->setY(pos);
314 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
315 section()->setX(-section()->width()-pos);
317 section()->setX(pos);
320 moveTo(pointForPosition(pos), immediate);
322 void setSize(qreal size) {
323 if (view->orientation() == QQuickListView::Vertical)
324 item->setHeight(size);
326 item->setWidth(size);
328 bool contains(qreal x, qreal y) const {
329 return (x >= itemX() && x < itemX() + item->width() &&
330 y >= itemY() && y < itemY() + item->height());
333 QQuickListView *view;
336 QPointF pointForPosition(qreal pos) const {
337 if (view->orientation() == QQuickListView::Vertical) {
338 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
340 pos += section()->height();
341 return QPointF(itemX(), -item->height() - pos);
344 pos += section()->height();
345 return QPointF(itemX(), pos);
348 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
350 pos += section()->width();
351 return QPointF(-item->width() - pos, itemY());
354 pos += section()->width();
355 return QPointF(pos, itemY());
361 //----------------------------------------------------------------------------
363 bool QQuickListViewPrivate::isContentFlowReversed() const
365 return isRightToLeft() || isBottomToTop();
368 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
370 return static_cast<Qt::Orientation>(orient);
373 bool QQuickListViewPrivate::isRightToLeft() const
375 Q_Q(const QQuickListView);
376 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
379 bool QQuickListViewPrivate::isBottomToTop() const
381 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
384 // Returns the item before modelIndex, if created.
385 // May return an item marked for removal.
386 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
388 if (modelIndex < visibleIndex)
392 while (idx < visibleItems.count()) {
393 FxViewItem *item = visibleItems.at(idx);
394 if (item->index != -1)
395 lastIndex = item->index;
396 if (item->index == modelIndex)
397 return visibleItems.at(idx-1);
400 if (lastIndex == modelIndex-1)
401 return visibleItems.last();
405 void QQuickListViewPrivate::setPosition(qreal pos)
408 if (orient == QQuickListView::Vertical) {
410 q->QQuickFlickable::setContentY(-pos-size());
412 q->QQuickFlickable::setContentY(pos);
415 q->QQuickFlickable::setContentX(-pos-size());
417 q->QQuickFlickable::setContentX(pos);
421 qreal QQuickListViewPrivate::originPosition() const
424 if (!visibleItems.isEmpty()) {
425 pos = (*visibleItems.constBegin())->position();
426 if (visibleIndex > 0)
427 pos -= visibleIndex * (averageSize + spacing);
432 qreal QQuickListViewPrivate::lastPosition() const
435 if (!visibleItems.isEmpty()) {
436 int invisibleCount = visibleItems.count() - visibleIndex;
437 for (int i = visibleItems.count()-1; i >= 0; --i) {
438 if (visibleItems.at(i)->index != -1) {
439 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
443 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
444 } else if (model && model->count()) {
445 pos = (model->count() * averageSize + (model->count()-1) * spacing);
450 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
452 if (FxViewItem *item = visibleItem(modelIndex)) {
453 return item->position();
455 if (!visibleItems.isEmpty()) {
456 if (modelIndex < visibleIndex) {
457 int count = visibleIndex - modelIndex;
459 if (modelIndex == currentIndex && currentItem) {
460 cs = currentItem->size() + spacing;
463 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
465 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
466 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
472 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
474 if (FxViewItem *item = visibleItem(modelIndex))
475 return item->endPosition();
476 if (!visibleItems.isEmpty()) {
477 if (modelIndex < visibleIndex) {
478 int count = visibleIndex - modelIndex;
479 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
481 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
482 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
488 QString QQuickListViewPrivate::sectionAt(int modelIndex)
490 if (FxViewItem *item = visibleItem(modelIndex))
491 return item->attached->section();
494 if (sectionCriteria) {
495 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
496 section = sectionCriteria->sectionString(propValue);
502 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
504 if (FxViewItem *snapItem = snapItemAt(pos))
505 return snapItem->position();
506 if (visibleItems.count()) {
507 qreal firstPos = (*visibleItems.constBegin())->position();
508 qreal endPos = (*(--visibleItems.constEnd()))->position();
509 if (pos < firstPos) {
510 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
511 } else if (pos > endPos)
512 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
514 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
517 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
519 FxViewItem *snapItem = 0;
520 qreal prevItemSize = 0;
521 for (int i = 0; i < visibleItems.count(); ++i) {
522 FxViewItem *item = visibleItems.at(i);
523 if (item->index == -1)
525 qreal itemTop = item->position();
526 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
528 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
530 prevItemSize = item->size();
535 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
537 visiblePos = positionAt(newIndex);
538 visibleIndex = newIndex;
541 void QQuickListViewPrivate::init()
543 QQuickItemViewPrivate::init();
544 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
547 void QQuickListViewPrivate::clear()
549 for (int i = 0; i < sectionCacheSize; ++i) {
550 delete sectionCache[i];
554 releaseSectionItem(currentSectionItem);
555 currentSectionItem = 0;
556 releaseSectionItem(nextSectionItem);
558 lastVisibleSection = QString();
559 QQuickItemViewPrivate::clear();
562 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
566 FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
567 listItem->index = modelIndex;
569 // initialise attached properties
570 if (sectionCriteria) {
571 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
572 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
573 if (modelIndex > 0) {
574 if (FxViewItem *item = itemBefore(modelIndex))
575 listItem->attached->m_prevSection = item->attached->section();
577 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
579 if (modelIndex < model->count()-1) {
580 if (FxViewItem *item = visibleItem(modelIndex+1))
581 listItem->attached->m_nextSection = static_cast<QQuickListViewAttached*>(item->attached)->section();
583 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
590 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
592 QQuickItemViewPrivate::initializeViewItem(item);
594 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
595 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
597 if (sectionCriteria && sectionCriteria->delegate()) {
598 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
599 updateInlineSection(static_cast<FxListItemSG*>(item));
603 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
608 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
610 bool released = QQuickItemViewPrivate::releaseItem(item);
611 if (released && att && att->m_sectionItem) {
612 // We hold no more references to this item
615 if (!sectionCache[i]) {
616 sectionCache[i] = att->m_sectionItem;
617 sectionCache[i]->setVisible(false);
618 att->m_sectionItem = 0;
622 } while (i < sectionCacheSize);
623 delete att->m_sectionItem;
624 att->m_sectionItem = 0;
630 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
632 qreal itemEnd = visiblePos;
633 if (visibleItems.count()) {
634 visiblePos = (*visibleItems.constBegin())->position();
635 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
638 int modelIndex = findLastVisibleIndex();
639 bool haveValidItems = modelIndex >= 0;
640 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
642 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
643 || fillTo < visiblePos - averageSize - spacing)) {
644 // We've jumped more than a page. Estimate which items are now
645 // visible and fill from there.
646 int count = (fillFrom - itemEnd) / (averageSize + spacing);
647 int newModelIdx = qBound(0, modelIndex + count, model->count());
648 count = newModelIdx - modelIndex;
650 for (int i = 0; i < visibleItems.count(); ++i)
651 releaseItem(visibleItems.at(i));
652 visibleItems.clear();
653 modelIndex = newModelIdx;
654 visibleIndex = modelIndex;
655 visiblePos = itemEnd + count * (averageSize + spacing);
656 itemEnd = visiblePos;
660 bool changed = false;
661 FxListItemSG *item = 0;
663 while (modelIndex < model->count() && pos <= fillTo) {
664 #ifdef DEBUG_DELEGATE_LIFECYCLE
665 qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
667 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
669 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
670 item->setPosition(pos, true);
671 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
672 pos += item->size() + spacing;
673 visibleItems.append(item);
678 if (doBuffer && requestedIndex != -1) // already waiting for an item
681 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
682 #ifdef DEBUG_DELEGATE_LIFECYCLE
683 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
685 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
688 visiblePos -= item->size() + spacing;
689 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
690 item->setPosition(visiblePos, true);
691 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
692 visibleItems.prepend(item);
699 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
701 FxViewItem *item = 0;
702 bool changed = false;
704 // Remove items from the start of the view.
705 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
706 // removed, otherwise a zero-sized item is infinitely added and removed over and
709 while (visibleItems.count() > 1 && index < visibleItems.count()
710 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
711 if (item->attached->delayRemove())
714 if (item->size() > 0) {
715 #ifdef DEBUG_DELEGATE_LIFECYCLE
716 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
718 // remove this item and all zero-sized items before it
720 if (item->index != -1)
722 visibleItems.removeAt(index);
723 if (item->transitionScheduledOrRunning()) {
724 #ifdef DEBUG_DELEGATE_LIFECYCLE
725 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
727 item->releaseAfterTransition = true;
728 releasePendingTransition.append(item);
734 item = visibleItems.at(--index);
742 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
743 if (item->attached->delayRemove())
745 #ifdef DEBUG_DELEGATE_LIFECYCLE
746 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
748 visibleItems.removeLast();
749 if (item->transitionScheduledOrRunning()) {
750 #ifdef DEBUG_DELEGATE_LIFECYCLE
751 qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
753 item->releaseAfterTransition = true;
754 releasePendingTransition.append(item);
764 void QQuickListViewPrivate::visibleItemsChanged()
766 if (visibleItems.count())
767 visiblePos = (*visibleItems.constBegin())->position();
769 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
770 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
774 updateCurrentSection();
775 updateUnrequestedPositions();
778 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
780 if (!visibleItems.isEmpty()) {
781 const qreal from = isContentFlowReversed() ? -position() - size() : position();
782 const qreal to = isContentFlowReversed() ? -position() : position() + size();
784 FxViewItem *firstItem = *visibleItems.constBegin();
785 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
786 qreal sum = firstItem->size();
787 qreal pos = firstItem->position() + firstItem->size() + spacing;
788 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
790 for (int i=1; i < visibleItems.count(); ++i) {
791 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
792 if (item->index >= fromModelIndex) {
793 item->setPosition(pos);
794 item->setVisible(item->endPosition() >= from && item->position() <= to);
796 pos += item->size() + spacing;
798 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
800 averageSize = qRound(sum / visibleItems.count());
802 // move current item if it is not a visible item.
803 if (currentIndex >= 0 && currentItem && !fixedCurrent)
804 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
808 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
810 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
813 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
816 qreal pos = position();
817 if (orient == QQuickListView::Vertical) {
818 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
820 item->setY(-positionAt(index)-item->height());
822 item->setY(positionAt(index));
825 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
827 item->setX(-positionAt(index)-item->width());
829 item->setX(positionAt(index));
834 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
836 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
837 item->setPosition(pos);
840 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
842 if (!visibleItems.count())
844 qreal diff = forwards - backwards;
845 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
848 void QQuickListViewPrivate::createHighlight()
851 bool changed = false;
853 if (trackedItem == highlight)
858 delete highlightPosAnimator;
859 delete highlightSizeAnimator;
860 highlightPosAnimator = 0;
861 highlightSizeAnimator = 0;
867 QQuickItem *item = createHighlightItem();
869 FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
872 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
873 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
875 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
876 highlightPosAnimator = new QSmoothedAnimation;
877 highlightPosAnimator->target = QQmlProperty(item, posProp);
878 highlightPosAnimator->userDuration = highlightMoveDuration;
880 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
881 highlightSizeAnimator = new QSmoothedAnimation;
882 highlightSizeAnimator->userDuration = highlightResizeDuration;
883 highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
885 highlight = newHighlight;
890 emit q->highlightItemChanged();
893 void QQuickListViewPrivate::updateHighlight()
895 applyPendingChanges();
897 if ((!currentItem && highlight) || (currentItem && !highlight))
899 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
900 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
901 // auto-update highlight
902 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
903 highlightPosAnimator->to = isContentFlowReversed()
904 ? -listItem->itemPosition()-listItem->itemSize()
905 : listItem->itemPosition();
906 highlightSizeAnimator->to = listItem->itemSize();
907 if (orient == QQuickListView::Vertical) {
908 if (highlight->item->width() == 0)
909 highlight->item->setWidth(currentItem->item->width());
911 if (highlight->item->height() == 0)
912 highlight->item->setHeight(currentItem->item->height());
915 highlightPosAnimator->restart();
916 highlightSizeAnimator->restart();
921 void QQuickListViewPrivate::resetHighlightPosition()
923 if (highlight && currentItem)
924 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
927 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
930 QQuickItem *sectionItem = 0;
931 int i = sectionCacheSize-1;
932 while (i >= 0 && !sectionCache[i])
935 sectionItem = sectionCache[i];
937 sectionItem->setVisible(true);
938 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
939 context->setContextProperty(QLatin1String("section"), section);
941 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
942 QQmlContext *context = new QQmlContext(
943 creationContext ? creationContext : qmlContext(q));
944 context->setContextProperty(QLatin1String("section"), section);
945 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
947 QQml_setParent_noEvent(context, nobj);
948 sectionItem = qobject_cast<QQuickItem *>(nobj);
952 sectionItem->setZ(2);
953 QQml_setParent_noEvent(sectionItem, contentItem);
954 sectionItem->setParentItem(contentItem);
959 sectionCriteria->delegate()->completeCreate();
965 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
971 if (!sectionCache[i]) {
972 sectionCache[i] = item;
973 sectionCache[i]->setVisible(false);
977 } while (i < sectionCacheSize);
981 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
983 if (!sectionCriteria || !sectionCriteria->delegate())
985 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
986 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
987 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
988 if (!listItem->section()) {
989 qreal pos = listItem->position();
990 listItem->setSection(getSectionItem(listItem->attached->m_section));
991 listItem->setPosition(pos);
993 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
994 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
996 } else if (listItem->section()) {
997 qreal pos = listItem->position();
998 releaseSectionItem(listItem->section());
999 listItem->setSection(0);
1000 listItem->setPosition(pos);
1004 void QQuickListViewPrivate::updateStickySections()
1006 if (!sectionCriteria
1007 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1010 bool isFlowReversed = isContentFlowReversed();
1011 qreal viewPos = isFlowReversed ? -position()-size() : position();
1012 QQuickItem *sectionItem = 0;
1013 QQuickItem *lastSectionItem = 0;
1015 while (index < visibleItems.count()) {
1016 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1017 // Find the current section header and last visible section header
1018 // and hide them if they will overlap a static section header.
1019 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1020 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1022 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1023 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1025 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1026 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1027 section->setVisible(visBot && visTop);
1028 if (visTop && !sectionItem)
1029 sectionItem = section;
1030 if (isFlowReversed) {
1031 if (-sectionPos <= viewPos + size())
1032 lastSectionItem = section;
1034 if (sectionPos + sectionSize < viewPos + size())
1035 lastSectionItem = section;
1041 // Current section header
1042 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1043 if (!currentSectionItem) {
1044 currentSectionItem = getSectionItem(currentSection);
1045 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1046 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1047 context->setContextProperty(QLatin1String("section"), currentSection);
1049 currentStickySection = currentSection;
1050 if (!currentSectionItem)
1053 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1054 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1056 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1057 qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1059 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1060 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1063 pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1065 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1066 if (orient == QQuickListView::Vertical)
1067 currentSectionItem->setY(pos);
1069 currentSectionItem->setX(pos);
1070 } else if (currentSectionItem) {
1071 releaseSectionItem(currentSectionItem);
1072 currentSectionItem = 0;
1075 // Next section footer
1076 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1077 if (!nextSectionItem) {
1078 nextSectionItem = getSectionItem(nextSection);
1079 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1080 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1081 context->setContextProperty(QLatin1String("section"), nextSection);
1083 nextStickySection = nextSection;
1084 if (!nextSectionItem)
1087 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1088 nextSectionItem->setVisible(!nextSection.isEmpty());
1089 qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1090 if (lastSectionItem) {
1091 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1092 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1095 pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1096 if (orient == QQuickListView::Vertical)
1097 nextSectionItem->setY(pos);
1099 nextSectionItem->setX(pos);
1100 } else if (nextSectionItem) {
1101 releaseSectionItem(nextSectionItem);
1102 nextSectionItem = 0;
1106 void QQuickListViewPrivate::updateSections()
1108 Q_Q(QQuickListView);
1109 if (!q->isComponentComplete())
1112 QQuickItemViewPrivate::updateSections();
1114 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1115 QString prevSection;
1116 if (visibleIndex > 0)
1117 prevSection = sectionAt(visibleIndex-1);
1118 QQuickListViewAttached *prevAtt = 0;
1120 for (int i = 0; i < visibleItems.count(); ++i) {
1121 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1122 attached->setPrevSection(prevSection);
1123 if (visibleItems.at(i)->index != -1) {
1124 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1125 attached->setSection(sectionCriteria->sectionString(propValue));
1126 idx = visibleItems.at(i)->index;
1128 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1130 prevAtt->setNextSection(attached->section());
1131 prevSection = attached->section();
1135 if (idx > 0 && idx < model->count()-1)
1136 prevAtt->setNextSection(sectionAt(idx+1));
1138 prevAtt->setNextSection(QString());
1142 lastVisibleSection = QString();
1143 updateCurrentSection();
1144 updateStickySections();
1147 void QQuickListViewPrivate::updateCurrentSection()
1149 Q_Q(QQuickListView);
1150 if (!sectionCriteria || visibleItems.isEmpty()) {
1151 if (!currentSection.isEmpty()) {
1152 currentSection.clear();
1153 emit q->currentSectionChanged();
1157 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1158 qreal sectionThreshold = position();
1159 if (currentSectionItem && !inlineSections)
1160 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1162 int modelIndex = visibleIndex;
1163 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1164 if (visibleItems.at(index)->index != -1)
1165 modelIndex = visibleItems.at(index)->index;
1169 QString newSection = currentSection;
1170 if (index < visibleItems.count())
1171 newSection = visibleItems.at(index)->attached->section();
1173 newSection = (*visibleItems.constBegin())->attached->section();
1174 if (newSection != currentSection) {
1175 currentSection = newSection;
1176 updateStickySections();
1177 emit q->currentSectionChanged();
1180 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1181 // Don't want to scan for next section on every movement, so remember
1182 // the last section in the visible area and only scan for the next
1183 // section when that changes. Clearing lastVisibleSection will also
1185 QString lastSection = currentSection;
1186 qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1187 if (nextSectionItem && !inlineSections)
1188 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1189 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1190 if (visibleItems.at(index)->index != -1)
1191 modelIndex = visibleItems.at(index)->index;
1192 lastSection = visibleItems.at(index)->attached->section();
1196 if (lastVisibleSection != lastSection) {
1197 nextSection = QString();
1198 lastVisibleSection = lastSection;
1199 for (int i = modelIndex; i < itemCount; ++i) {
1200 QString section = sectionAt(i);
1201 if (section != lastSection) {
1202 nextSection = section;
1203 updateStickySections();
1211 void QQuickListViewPrivate::initializeCurrentItem()
1213 QQuickItemViewPrivate::initializeCurrentItem();
1216 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1218 // don't reposition the item if it is already in the visibleItems list
1219 FxViewItem *actualItem = visibleItem(currentIndex);
1221 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1222 // We can calculate exact postion in this case
1223 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1225 // Create current item now and position as best we can.
1226 // Its position will be corrected when it becomes visible.
1227 listItem->setPosition(positionAt(currentIndex));
1231 if (visibleItems.isEmpty())
1232 averageSize = listItem->size();
1236 void QQuickListViewPrivate::updateAverage()
1238 if (!visibleItems.count())
1241 for (int i = 0; i < visibleItems.count(); ++i)
1242 sum += visibleItems.at(i)->size();
1243 averageSize = qRound(sum / visibleItems.count());
1246 qreal QQuickListViewPrivate::headerSize() const
1248 return header ? header->size() : 0.0;
1251 qreal QQuickListViewPrivate::footerSize() const
1253 return footer ? footer->size() : 0.0;
1256 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1261 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1263 return index == model->count()-1;
1266 void QQuickListViewPrivate::updateFooter()
1268 Q_Q(QQuickListView);
1269 bool created = false;
1271 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1274 footer = new FxListItemSG(item, q, true, true);
1278 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1279 if (visibleItems.count()) {
1280 qreal endPos = lastPosition();
1281 if (findLastVisibleIndex() == model->count()-1) {
1282 listItem->setPosition(endPos);
1284 qreal visiblePos = position() + q->height();
1285 if (endPos <= visiblePos || listItem->position() < endPos)
1286 listItem->setPosition(endPos);
1289 listItem->setPosition(visiblePos);
1293 emit q->footerItemChanged();
1296 void QQuickListViewPrivate::updateHeader()
1298 Q_Q(QQuickListView);
1299 bool created = false;
1301 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1304 header = new FxListItemSG(item, q, true, true);
1308 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1310 if (visibleItems.count()) {
1311 qreal startPos = originPosition();
1312 if (visibleIndex == 0) {
1313 listItem->setPosition(startPos - headerSize());
1315 if (position() <= startPos || listItem->position() > startPos - headerSize())
1316 listItem->setPosition(startPos - headerSize());
1319 listItem->setPosition(-headerSize());
1324 emit q->headerItemChanged();
1327 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1329 Q_Q(QQuickListView);
1330 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1331 if (!q->isComponentComplete())
1334 if (item != contentItem && (!highlight || item != highlight->item)) {
1335 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1336 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1338 // if visibleItems.first() has resized, adjust its pos since it is used to
1339 // position all subsequent items
1340 if (visibleItems.count() && item == visibleItems.first()->item) {
1341 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1342 if (orient == QQuickListView::Vertical) {
1343 qreal diff = newGeometry.height() - oldGeometry.height();
1344 if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1345 listItem->setPosition(listItem->position() - diff, true);
1346 else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1347 listItem->setPosition(listItem->position() + diff, true);
1349 qreal diff = newGeometry.width() - oldGeometry.width();
1350 if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1351 listItem->setPosition(listItem->position() - diff, true);
1352 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1353 listItem->setPosition(listItem->position() + diff, true);
1362 void QQuickListViewPrivate::fixupPosition()
1364 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1365 || snapMode != QQuickListView::NoSnap)
1367 if (orient == QQuickListView::Vertical)
1373 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1375 if ((orient == QQuickListView::Horizontal && &data == &vData)
1376 || (orient == QQuickListView::Vertical && &data == &hData))
1379 correctFlick = false;
1380 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1381 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1383 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1385 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1386 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1387 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1388 // if we've been dragged < averageSize/2 then bias towards the next item
1389 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1391 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1392 bias = averageSize/2;
1393 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1394 bias = -averageSize/2;
1395 if (isContentFlowReversed())
1397 tempPosition -= bias;
1399 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1400 if (!topItem && strictHighlightRange && currentItem) {
1401 // StrictlyEnforceRange always keeps an item in range
1403 topItem = currentItem;
1405 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1406 if (!bottomItem && strictHighlightRange && currentItem) {
1407 // StrictlyEnforceRange always keeps an item in range
1409 bottomItem = currentItem;
1412 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1413 if (topItem && (isInBounds || strictHighlightRange)) {
1414 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1415 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1417 if (isContentFlowReversed())
1418 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1420 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1422 } else if (bottomItem && isInBounds) {
1423 if (isContentFlowReversed())
1424 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1426 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1428 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1432 qreal dist = qAbs(data.move + pos);
1434 timeline.reset(data.move);
1435 if (fixupMode != Immediate) {
1436 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1437 data.fixingUp = true;
1439 timeline.set(data.move, -pos);
1441 vTime = timeline.time();
1443 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1445 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1446 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1447 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1448 if (viewPos > pos - highlightRangeStart)
1449 viewPos = pos - highlightRangeStart;
1450 if (isContentFlowReversed())
1451 viewPos = -viewPos-size();
1453 timeline.reset(data.move);
1454 if (viewPos != position()) {
1455 if (fixupMode != Immediate) {
1456 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1457 data.fixingUp = true;
1459 timeline.set(data.move, -viewPos);
1462 vTime = timeline.time();
1464 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1466 data.inOvershoot = false;
1470 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1471 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1473 data.fixingUp = false;
1475 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1476 correctFlick = true;
1477 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1479 qreal maxDistance = 0;
1480 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1482 // -ve velocity means list is moving up/left
1484 if (data.move.value() < minExtent) {
1485 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1486 // if we've been dragged < averageSize/2 then bias towards the next item
1487 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1488 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1489 if (isContentFlowReversed())
1491 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1492 maxDistance = qAbs(data.flickTarget - data.move.value());
1493 velocity = maxVelocity;
1495 maxDistance = qAbs(minExtent - data.move.value());
1498 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1499 data.flickTarget = minExtent;
1501 if (data.move.value() > maxExtent) {
1502 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1503 // if we've been dragged < averageSize/2 then bias towards the next item
1504 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1505 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1506 if (isContentFlowReversed())
1508 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1509 maxDistance = qAbs(data.flickTarget - data.move.value());
1510 velocity = -maxVelocity;
1512 maxDistance = qAbs(maxExtent - data.move.value());
1515 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1516 data.flickTarget = maxExtent;
1518 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1519 if (maxDistance > 0 || overShoot) {
1520 // These modes require the list to stop exactly on an item boundary.
1521 // The initial flick will estimate the boundary to stop on.
1522 // Since list items can have variable sizes, the boundary will be
1523 // reevaluated and adjusted as we approach the boundary.
1525 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1531 if (!hData.flicking && !vData.flicking) {
1532 // the initial flick - estimate boundary
1533 qreal accel = deceleration;
1535 overshootDist = 0.0;
1536 // + averageSize/4 to encourage moving at least one item in the flick direction
1537 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1538 if (maxDistance > 0)
1539 dist = qMin(dist, maxDistance);
1542 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1543 if (snapMode != QQuickListView::SnapOneItem) {
1544 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1545 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1547 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1549 if (data.flickTarget >= minExtent) {
1550 overshootDist = overShootDistance(vSize);
1551 data.flickTarget += overshootDist;
1552 } else if (data.flickTarget <= maxExtent) {
1553 overshootDist = overShootDistance(vSize);
1554 data.flickTarget -= overshootDist;
1557 qreal adjDist = -data.flickTarget + data.move.value();
1558 if (qAbs(adjDist) > qAbs(dist)) {
1559 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1560 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1569 accel = v2 / (2.0f * qAbs(dist));
1570 } else if (overShoot) {
1571 data.flickTarget = data.move.value() - dist;
1572 if (data.flickTarget >= minExtent) {
1573 overshootDist = overShootDistance(vSize);
1574 data.flickTarget += overshootDist;
1575 } else if (data.flickTarget <= maxExtent) {
1576 overshootDist = overShootDistance(vSize);
1577 data.flickTarget -= overshootDist;
1580 timeline.reset(data.move);
1581 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1582 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1583 correctFlick = true;
1586 // reevaluate the target boundary.
1587 qreal newtarget = data.flickTarget;
1588 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1589 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1590 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1591 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1593 if (velocity < 0 && newtarget <= maxExtent)
1594 newtarget = maxExtent - overshootDist;
1595 else if (velocity > 0 && newtarget >= minExtent)
1596 newtarget = minExtent + overshootDist;
1597 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1598 if (qAbs(velocity) < MinimumFlickVelocity)
1599 correctFlick = false;
1602 data.flickTarget = newtarget;
1603 qreal dist = -newtarget + data.move.value();
1604 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1605 correctFlick = false;
1606 timeline.reset(data.move);
1607 fixup(data, minExtent, maxExtent);
1610 timeline.reset(data.move);
1611 timeline.accelDistance(data.move, v, -dist);
1612 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1616 correctFlick = false;
1617 timeline.reset(data.move);
1618 fixup(data, minExtent, maxExtent);
1623 //----------------------------------------------------------------------------
1626 \qmlclass ListView QQuickListView
1627 \inqmlmodule QtQuick 2
1628 \ingroup qtquick-views
1630 \brief Provides a list view of items provided by a model
1632 A ListView displays data from models created from built-in QML elements like ListModel
1633 and XmlListModel, or custom model classes defined in C++ that inherit from
1636 A ListView has a \l model, which defines the data to be displayed, and
1637 a \l delegate, which defines how the data should be displayed. Items in a
1638 ListView are laid out horizontally or vertically. List views are inherently
1639 flickable because ListView inherits from \l Flickable.
1641 \section1 Example Usage
1643 The following example shows the definition of a simple list model defined
1644 in a file called \c ContactModel.qml:
1646 \snippet qml/listview/ContactModel.qml 0
1648 Another component can display this model data in a ListView, like this:
1650 \snippet qml/listview/listview.qml import
1652 \snippet qml/listview/listview.qml classdocs simple
1654 \image listview-simple.png
1656 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1657 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1658 the delegate is able to access the model's \c name and \c number data directly.
1660 An improved list view is shown below. The delegate is visually improved and is moved
1661 into a separate \c contactDelegate component.
1663 \snippet qml/listview/listview.qml classdocs advanced
1664 \image listview-highlight.png
1666 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1667 and \c focus is set to \c true to enable keyboard navigation for the list view.
1668 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1670 Delegates are instantiated as needed and may be destroyed at any time.
1671 State should \e never be stored in a delegate.
1673 ListView attaches a number of properties to the root item of the delegate, for example
1674 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1675 this attached property directly as \c ListView.isCurrentItem, while the child
1676 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1678 \snippet qml/listview/listview.qml isCurrentItem
1680 \note Views do not enable \e clip automatically. If the view
1681 is not clipped by another item or the screen, it will be necessary
1682 to set \e {clip: true} in order to have the out of view items clipped
1686 \section1 ListView layouts
1688 The layout of the items in a ListView can be controlled by these properties:
1691 \li \l orientation - controls whether items flow horizontally or vertically.
1692 This value can be either Qt.Horizontal or Qt.Vertical.
1693 \li \l layoutDirection - controls the horizontal layout direction for a
1694 horizontally-oriented view: that is, whether items are laid out from the left side of
1695 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
1696 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
1697 view: that is, whether items are laid out from the top of the view down towards the bottom of
1698 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
1701 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
1702 table below shows the different layouts that a ListView can have, depending on the values of
1703 the properties listed above.
1708 \bold ListViews with Qt.Vertical orientation
1711 \image listview-layout-toptobottom.png
1713 \image listview-layout-bottomtotop.png
1716 \bold ListViews with Qt.Horizontal orientation
1719 \image listview-layout-lefttoright.png
1721 \image listview-layout-righttoleft.png
1724 \sa {QML Data Models}, GridView, {quick/modelviews/listview}{ListView examples}
1726 QQuickListView::QQuickListView(QQuickItem *parent)
1727 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1731 QQuickListView::~QQuickListView()
1736 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1737 This attached property is true if this delegate is the current item; otherwise false.
1739 It is attached to each instance of the delegate.
1741 This property may be used to adjust the appearance of the current item, for example:
1743 \snippet qml/listview/listview.qml isCurrentItem
1747 \qmlattachedproperty ListView QtQuick2::ListView::view
1748 This attached property holds the view that manages this delegate instance.
1750 It is attached to each instance of the delegate.
1754 \qmlattachedproperty string QtQuick2::ListView::previousSection
1755 This attached property holds the section of the previous element.
1757 It is attached to each instance of the delegate.
1759 The section is evaluated using the \l {ListView::section.property}{section} properties.
1763 \qmlattachedproperty string QtQuick2::ListView::nextSection
1764 This attached property holds the section of the next element.
1766 It is attached to each instance of the delegate.
1768 The section is evaluated using the \l {ListView::section.property}{section} properties.
1772 \qmlattachedproperty string QtQuick2::ListView::section
1773 This attached property holds the section of this element.
1775 It is attached to each instance of the delegate.
1777 The section is evaluated using the \l {ListView::section.property}{section} properties.
1781 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1783 This attached property holds whether the delegate may be destroyed. It
1784 is attached to each instance of the delegate. The default value is false.
1786 It is sometimes necessary to delay the destruction of an item
1787 until an animation completes. The example delegate below ensures that the
1788 animation completes before the item is removed from the list.
1790 \snippet qml/listview/listview.qml delayRemove
1792 If a \l remove transition has been specified, it will not be applied until
1793 delayRemove is returned to \c false.
1797 \qmlattachedsignal QtQuick2::ListView::onAdd()
1798 This attached signal handler is called immediately after an item is added to the view.
1800 If an \l add transition is specified, it is applied immediately after
1801 this signal handler is called.
1805 \qmlattachedsignal QtQuick2::ListView::onRemove()
1806 This attached handler is called immediately before an item is removed from the view.
1808 If a \l remove transition has been specified, it is applied after
1809 this signal handler is called, providing that delayRemove is false.
1813 \qmlproperty model QtQuick2::ListView::model
1814 This property holds the model providing data for the list.
1816 The model provides the set of data that is used to create the items
1817 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1818 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1819 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1821 \sa {qmlmodels}{Data Models}
1825 \qmlproperty Component QtQuick2::ListView::delegate
1827 The delegate provides a template defining each item instantiated by the view.
1828 The index is exposed as an accessible \c index property. Properties of the
1829 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1831 The number of elements in the delegate has a direct effect on the
1832 flicking performance of the view. If at all possible, place functionality
1833 that is not needed for the normal display of the delegate in a \l Loader which
1834 can load additional elements when needed.
1836 The ListView will lay out the items based on the size of the root item
1839 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1842 \note Delegates are instantiated as needed and may be destroyed at any time.
1843 State should \e never be stored in a delegate.
1846 \qmlproperty int QtQuick2::ListView::currentIndex
1847 \qmlproperty Item QtQuick2::ListView::currentItem
1849 The \c currentIndex property holds the index of the current item, and
1850 \c currentItem holds the current item. Setting the currentIndex to -1
1851 will clear the highlight and set currentItem to null.
1853 If highlightFollowsCurrentItem is \c true, setting either of these
1854 properties will smoothly scroll the ListView so that the current
1855 item becomes visible.
1857 Note that the position of the current item
1858 may only be approximate until it becomes visible in the view.
1862 \qmlproperty Item QtQuick2::ListView::highlightItem
1864 This holds the highlight item created from the \l highlight component.
1866 The \c highlightItem is managed by the view unless
1867 \l highlightFollowsCurrentItem is set to false.
1869 \sa highlight, highlightFollowsCurrentItem
1873 \qmlproperty int QtQuick2::ListView::count
1874 This property holds the number of items in the view.
1878 \qmlproperty Component QtQuick2::ListView::highlight
1879 This property holds the component to use as the highlight.
1881 An instance of the highlight component is created for each list.
1882 The geometry of the resulting component instance is managed by the list
1883 so as to stay with the current item, unless the highlightFollowsCurrentItem
1886 \sa highlightItem, highlightFollowsCurrentItem, {quick/modelviews/listview}{ListView examples}
1890 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1891 This property holds whether the highlight is managed by the view.
1893 If this property is true (the default value), the highlight is moved smoothly
1894 to follow the current item. Otherwise, the
1895 highlight is not moved by the view, and any movement must be implemented
1898 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1900 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
1902 Note that the highlight animation also affects the way that the view
1903 is scrolled. This is because the view moves to maintain the
1904 highlight within the preferred highlight range (or visible viewport).
1908 //###Possibly rename these properties, since they are very useful even without a highlight?
1910 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1911 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1912 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1914 These properties define the preferred range of the highlight (for the current item)
1915 within the view. The \c preferredHighlightBegin value must be less than the
1916 \c preferredHighlightEnd value.
1918 These properties affect the position of the current item when the list is scrolled.
1919 For example, if the currently selected item should stay in the middle of the
1920 list when the view is scrolled, set the \c preferredHighlightBegin and
1921 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1922 item would be. If the \c currentItem is changed programmatically, the list will
1923 automatically scroll so that the current item is in the middle of the view.
1924 Furthermore, the behavior of the current item index will occur whether or not a
1927 Valid values for \c highlightRangeMode are:
1930 \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1931 However, the highlight can move outside of the range at the ends of the list or due
1932 to mouse interaction.
1933 \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1934 The current item changes if a keyboard or mouse action would cause the highlight to move
1935 outside of the range.
1936 \li ListView.NoHighlightRange - this is the default value.
1939 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1941 Q_D(QQuickListView);
1942 if (d->autoHighlight != autoHighlight) {
1943 if (!autoHighlight) {
1944 if (d->highlightPosAnimator)
1945 d->highlightPosAnimator->stop();
1946 if (d->highlightSizeAnimator)
1947 d->highlightSizeAnimator->stop();
1949 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1954 \qmlproperty real QtQuick2::ListView::spacing
1956 This property holds the spacing between items.
1958 The default value is 0.
1960 qreal QQuickListView::spacing() const
1962 Q_D(const QQuickListView);
1966 void QQuickListView::setSpacing(qreal spacing)
1968 Q_D(QQuickListView);
1969 if (spacing != d->spacing) {
1970 d->spacing = spacing;
1971 d->forceLayout = true;
1973 emit spacingChanged();
1978 \qmlproperty enumeration QtQuick2::ListView::orientation
1979 This property holds the orientation of the list.
1984 \li ListView.Horizontal - Items are laid out horizontally
1985 \li ListView.Vertical (default) - Items are laid out vertically
1990 \li Horizontal orientation:
1991 \image ListViewHorizontal.png
1994 \li Vertical orientation:
1995 \image listview-highlight.png
1998 QQuickListView::Orientation QQuickListView::orientation() const
2000 Q_D(const QQuickListView);
2004 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2006 Q_D(QQuickListView);
2007 if (d->orient != orientation) {
2008 d->orient = orientation;
2009 if (d->orient == Vertical) {
2010 setContentWidth(-1);
2011 setFlickableDirection(VerticalFlick);
2014 setContentHeight(-1);
2015 setFlickableDirection(HorizontalFlick);
2019 emit orientationChanged();
2024 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
2025 This property holds the layout direction of a horizontally-oriented list.
2030 \li Qt.LeftToRight (default) - Items will be laid out from left to right.
2031 \li Qt.RightToLeft - Items will be laid out from right to let.
2034 Setting this property has no effect if the \l orientation is Qt.Vertical.
2036 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2041 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
2042 This property holds the effective layout direction of a horizontally-oriented list.
2044 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2045 the visual layout direction of the horizontal list will be mirrored. However, the
2046 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2048 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2053 \qmlproperty enumeration QtQuick2::ListView::verticalLayoutDirection
2054 This property holds the layout direction of a vertically-oriented list.
2059 \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
2060 \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
2063 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2065 \sa ListView::layoutDirection
2070 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
2071 This property holds whether the list wraps key navigation.
2073 If this is true, key navigation that would move the current item selection
2074 past the end of the list instead wraps around and moves the selection to
2075 the start of the list, and vice-versa.
2077 By default, key navigation is not wrapped.
2082 \qmlproperty int QtQuick2::ListView::cacheBuffer
2083 This property determines whether delegates are retained outside the
2084 visible area of the view.
2086 If this value is non-zero, the view may keep as many delegates
2087 instantiated as it can fit within the buffer specified. For example,
2088 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2089 set to 40, then up to 2 delegates above and 2 delegates below the visible
2090 area may be created/retained. The buffered delegates are created asynchronously,
2091 allowing creation to occur across multiple frames and reducing the
2092 likelihood of skipping frames. In order to improve painting performance
2093 delegates outside the visible area are not painted.
2095 The default value of this property is platform dependent, but will usually
2096 be a non-zero value.
2098 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2099 instantiated delegates.
2101 Setting this value can improve the smoothness of scrolling behavior at the expense
2102 of additional memory usage. It is not a substitute for creating efficient
2103 delegates; the fewer elements in a delegate, the faster a view can be
2109 \qmlproperty string QtQuick2::ListView::section.property
2110 \qmlproperty enumeration QtQuick2::ListView::section.criteria
2111 \qmlproperty Component QtQuick2::ListView::section.delegate
2112 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
2114 These properties determine the expression to be evaluated and appearance
2115 of the section labels.
2117 \c section.property holds the name of the property that is the basis
2120 \c section.criteria holds the criteria for forming each section based on
2121 \c section.property. This value can be one of:
2124 \li ViewSection.FullString (default) - sections are created based on the
2125 \c section.property value.
2126 \li ViewSection.FirstCharacter - sections are created based on the first
2127 character of the \c section.property value (for example, 'A', 'B', 'C'
2128 sections, etc. for an address book)
2131 A case insensitive comparison is used when determining section
2134 \c section.delegate holds the delegate component for each section.
2136 \c section.labelPositioning determines whether the current and/or
2137 next section labels stick to the start/end of the view, and whether
2138 the labels are shown inline. This value can be a combination of:
2141 \li ViewSection.InlineLabels - section labels are shown inline between
2142 the item delegates separating sections (default).
2143 \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
2144 start of the view as it is moved.
2145 \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
2146 sections) sticks to the end of the view as it is moved. \note Enabling
2147 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
2148 section, which has performance implications, especially for slower models.
2151 Each item in the list has attached properties named \c ListView.section,
2152 \c ListView.previousSection and \c ListView.nextSection.
2154 For example, here is a ListView that displays a list of animals, separated
2155 into sections. Each item in the ListView is placed in a different section
2156 depending on the "size" property of the model item. The \c sectionHeading
2157 delegate component provides the light blue bar that marks the beginning of
2161 \snippet examples/quick/modelviews/listview/sections.qml 0
2163 \image qml-listview-sections-example.png
2165 \note Adding sections to a ListView does not automatically re-order the
2166 list items by the section criteria.
2167 If the model is not ordered by section, then it is possible that
2168 the sections created will not be unique; each boundary between
2169 differing sections will result in a section header being created
2170 even if that section exists elsewhere.
2172 \sa {quick/modelviews/listview}{ListView examples}
2174 QQuickViewSection *QQuickListView::sectionCriteria()
2176 Q_D(QQuickListView);
2177 if (!d->sectionCriteria) {
2178 d->sectionCriteria = new QQuickViewSection(this);
2179 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2181 return d->sectionCriteria;
2185 \qmlproperty string QtQuick2::ListView::currentSection
2186 This property holds the section that is currently at the beginning of the view.
2188 QString QQuickListView::currentSection() const
2190 Q_D(const QQuickListView);
2191 return d->currentSection;
2195 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2196 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2198 These properties hold the move and resize animation duration of
2199 the highlight delegate.
2201 \l highlightFollowsCurrentItem must be true for these properties
2204 The default value for highlightMoveDuration is 150ms and the
2205 default value for highlightResizeDuration is 250ms.
2207 \sa highlightFollowsCurrentItem
2209 void QQuickListView::setHighlightMoveDuration(int duration)
2211 Q_D(QQuickListView);
2212 if (d->highlightMoveDuration != duration) {
2213 if (d->highlightPosAnimator)
2214 d->highlightPosAnimator->userDuration = duration;
2215 QQuickItemView::setHighlightMoveDuration(duration);
2219 int QQuickListView::highlightResizeDuration() const
2221 Q_D(const QQuickListView);
2222 return d->highlightResizeDuration;
2225 void QQuickListView::setHighlightResizeDuration(int duration)
2227 Q_D(QQuickListView);
2228 if (d->highlightResizeDuration != duration) {
2229 d->highlightResizeDuration = duration;
2230 if (d->highlightSizeAnimator)
2231 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2232 emit highlightResizeDurationChanged();
2237 \qmlproperty enumeration QtQuick2::ListView::snapMode
2239 This property determines how the view scrolling will settle following a drag or flick.
2240 The possible values are:
2243 \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2244 \li ListView.SnapToItem - the view settles with an item aligned with the start of
2246 \li ListView.SnapOneItem - the view settles no more than one item away from the first
2247 visible item at the time the mouse button is released. This mode is particularly
2248 useful for moving one page at a time.
2251 \c snapMode does not affect the \l currentIndex. To update the
2252 \l currentIndex as the list is moved, set \l highlightRangeMode
2253 to \c ListView.StrictlyEnforceRange.
2255 \sa highlightRangeMode
2257 QQuickListView::SnapMode QQuickListView::snapMode() const
2259 Q_D(const QQuickListView);
2263 void QQuickListView::setSnapMode(SnapMode mode)
2265 Q_D(QQuickListView);
2266 if (d->snapMode != mode) {
2268 emit snapModeChanged();
2274 \qmlproperty Component QtQuick2::ListView::footer
2275 This property holds the component to use as the footer.
2277 An instance of the footer component is created for each view. The
2278 footer is positioned at the end of the view, after any items.
2280 \sa header, footerItem
2285 \qmlproperty Component QtQuick2::ListView::header
2286 This property holds the component to use as the header.
2288 An instance of the header component is created for each view. The
2289 header is positioned at the beginning of the view, before any items.
2291 \sa footer, headertem
2295 \qmlproperty Item QtQuick2::ListView::headerItem
2296 This holds the header item created from the \l header component.
2298 An instance of the header component is created for each view. The
2299 header is positioned at the beginning of the view, before any items.
2301 \sa header, footerItem
2305 \qmlproperty Item QtQuick2::ListView::footerItem
2306 This holds the footer item created from the \l footer component.
2308 An instance of the footer component is created for each view. The
2309 footer is positioned at the end of the view, after any items.
2311 \sa footer, headerItem
2315 \qmlproperty Transition QtQuick2::ListView::populate
2317 This property holds the transition to apply to the items that are initially created
2320 It is applied to all items that are created when:
2323 \li The view is first created
2324 \li The view's \l model changes
2325 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2328 For example, here is a view that specifies such a transition:
2333 populate: Transition {
2334 NumberAnimation { properties: "x,y"; duration: 1000 }
2339 When the view is initialized, the view will create all the necessary items for the view,
2340 then animate them to their correct positions within the view over one second.
2342 For more details and examples on how to use view transitions, see the ViewTransition
2345 \sa add, ViewTransition
2349 \qmlproperty Transition QtQuick2::ListView::add
2351 This property holds the transition to apply to items that are added to the view.
2353 For example, here is a view that specifies such a transition:
2359 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2364 Whenever an item is added to the above view, the item will be animated from the position (100,100)
2365 to its final x,y position within the view, over one second. The transition only applies to
2366 the new items that are added to the view; it does not apply to the items below that are
2367 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2368 or \l addDisplaced properties.
2370 For more details and examples on how to use view transitions, see the ViewTransition
2373 \note This transition is not applied to the items that are created when the view is initially
2374 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2377 \sa addDisplaced, populate, ViewTransition
2381 \qmlproperty Transition QtQuick2::ListView::addDisplaced
2383 This property holds the transition to apply to items within the view that are displaced by
2384 the addition of other items to the view.
2386 For example, here is a view that specifies such a transition:
2391 addDisplaced: Transition {
2392 NumberAnimation { properties: "x,y"; duration: 1000 }
2397 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2398 them to move down (or sideways, if horizontally orientated) within the view. As this
2399 displacement occurs, the items' movement to their new x,y positions within the view will be
2400 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2401 the new item that has been added to the view; to animate the added items, set the \l add
2404 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2405 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2406 if it is not necessary to specify different transitions depending on whether an item is displaced
2407 by an add, move or remove operation, consider setting the \l displaced property instead.
2409 For more details and examples on how to use view transitions, see the ViewTransition
2412 \note This transition is not applied to the items that are created when the view is initially
2413 populated, or when the view's \l model changes. In those cases, the \l populate transition is
2416 \sa displaced, add, populate, ViewTransition
2420 \qmlproperty Transition QtQuick2::ListView::move
2422 This property holds the transition to apply to items in the view that are being moved due
2423 to a move operation in the view's \l model.
2425 For example, here is a view that specifies such a transition:
2431 NumberAnimation { properties: "x,y"; duration: 1000 }
2436 Whenever the \l model performs a move operation to move a particular set of indexes, the
2437 respective items in the view will be animated to their new positions in the view over one
2438 second. The transition only applies to the items that are the subject of the move operation
2439 in the model; it does not apply to items below them that are displaced by the move operation.
2440 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2442 For more details and examples on how to use view transitions, see the ViewTransition
2445 \sa moveDisplaced, ViewTransition
2449 \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2451 This property holds the transition to apply to items that are displaced by a move operation in
2452 the view's \l model.
2454 For example, here is a view that specifies such a transition:
2459 moveDisplaced: Transition {
2460 NumberAnimation { properties: "x,y"; duration: 1000 }
2465 Whenever the \l model performs a move operation to move a particular set of indexes, the items
2466 between the source and destination indexes of the move operation are displaced, causing them
2467 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2468 displacement occurs, the items' movement to their new x,y positions within the view will be
2469 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2470 the items that are the actual subjects of the move operation; to animate the moved items, set
2471 the \l move property.
2473 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2474 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2475 if it is not necessary to specify different transitions depending on whether an item is displaced
2476 by an add, move or remove operation, consider setting the \l displaced property instead.
2478 For more details and examples on how to use view transitions, see the ViewTransition
2481 \sa displaced, move, ViewTransition
2485 \qmlproperty Transition QtQuick2::ListView::remove
2487 This property holds the transition to apply to items that are removed from the view.
2489 For example, here is a view that specifies such a transition:
2494 remove: Transition {
2496 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2497 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2503 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2504 over one second, and in parallel will also change its opacity to 0. The transition
2505 only applies to the items that are removed from the view; it does not apply to the items below
2506 them that are displaced by the removal of the items. To animate the displaced items, set the
2507 \l displaced or \l removeDisplaced properties.
2509 Note that by the time the transition is applied, the item has already been removed from the
2510 model; any references to the model data for the removed index will not be valid.
2512 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2513 remove transition will not be applied until \l delayRemove becomes false again.
2515 For more details and examples on how to use view transitions, see the ViewTransition
2518 \sa removeDisplaced, ViewTransition
2522 \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2524 This property holds the transition to apply to items in the view that are displaced by the
2525 removal of other items in the view.
2527 For example, here is a view that specifies such a transition:
2532 removeDisplaced: Transition {
2533 NumberAnimation { properties: "x,y"; duration: 1000 }
2538 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2539 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2540 displacement occurs, the items' movement to their new x,y positions within the view will be
2541 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2542 the item that has actually been removed from the view; to animate the removed items, set the
2545 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2546 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2547 if it is not necessary to specify different transitions depending on whether an item is displaced
2548 by an add, move or remove operation, consider setting the \l displaced property instead.
2550 For more details and examples on how to use view transitions, see the ViewTransition
2553 \sa displaced, remove, ViewTransition
2557 \qmlproperty Transition QtQuick2::ListView::displaced
2558 This property holds the generic transition to apply to items that have been displaced by
2559 any model operation that affects the view.
2561 This is a convenience for specifying the generic transition to be applied to any items
2562 that are displaced by an add, move or remove operation, without having to specify the
2563 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2564 is a view that specifies a displaced transition:
2569 displaced: Transition {
2570 NumberAnimation { properties: "x,y"; duration: 1000 }
2575 When any item is added, moved or removed within the above view, the items below it are
2576 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2577 view. As this displacement occurs, the items' movement to their new x,y positions within
2578 the view will be animated by a NumberAnimation over one second, as specified.
2580 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2581 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2582 instead of the generic displaced transition when the relevant operation occurs, providing that
2583 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2584 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2586 For more details and examples on how to use view transitions, see the ViewTransition
2589 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2592 void QQuickListView::viewportMoved(Qt::Orientations orient)
2594 Q_D(QQuickListView);
2595 QQuickItemView::viewportMoved(orient);
2598 // Recursion can occur due to refill changing the content size.
2599 if (d->inViewportMoved)
2601 d->inViewportMoved = true;
2604 if (d->isBottomToTop())
2605 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2607 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2609 if (d->isRightToLeft())
2610 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2612 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2615 d->refillOrLayout();
2617 // Set visibility of items to eliminate cost of items outside the visible area.
2618 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2619 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2620 for (int i = 0; i < d->visibleItems.count(); ++i) {
2621 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2622 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
2625 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
2627 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2628 d->moveReason = QQuickListViewPrivate::Mouse;
2629 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2630 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2631 // reposition highlight
2632 qreal pos = d->highlight->position();
2633 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2634 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2635 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2636 if (pos < viewPos + d->highlightRangeStart)
2637 pos = viewPos + d->highlightRangeStart;
2638 if (pos != d->highlight->position()) {
2639 d->highlightPosAnimator->stop();
2640 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2642 d->updateHighlight();
2645 // update current index
2646 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2647 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2648 d->updateCurrent(snapItem->index);
2653 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2654 d->inFlickCorrection = true;
2655 // Near an end and it seems that the extent has changed?
2656 // Recalculate the flick so that we don't end up in an odd position.
2657 if (yflick() && !d->vData.inOvershoot) {
2658 if (d->vData.velocity > 0) {
2659 const qreal minY = minYExtent();
2660 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2661 && minY != d->vData.flickTarget)
2662 d->flickY(-d->vData.smoothVelocity.value());
2663 } else if (d->vData.velocity < 0) {
2664 const qreal maxY = maxYExtent();
2665 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2666 && maxY != d->vData.flickTarget)
2667 d->flickY(-d->vData.smoothVelocity.value());
2671 if (xflick() && !d->hData.inOvershoot) {
2672 if (d->hData.velocity > 0) {
2673 const qreal minX = minXExtent();
2674 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2675 && minX != d->hData.flickTarget)
2676 d->flickX(-d->hData.smoothVelocity.value());
2677 } else if (d->hData.velocity < 0) {
2678 const qreal maxX = maxXExtent();
2679 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2680 && maxX != d->hData.flickTarget)
2681 d->flickX(-d->hData.smoothVelocity.value());
2684 d->inFlickCorrection = false;
2686 if (d->sectionCriteria) {
2687 d->updateCurrentSection();
2688 d->updateStickySections();
2690 d->inViewportMoved = false;
2693 void QQuickListView::keyPressEvent(QKeyEvent *event)
2695 Q_D(QQuickListView);
2696 if (d->model && d->model->count() && d->interactive) {
2697 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2698 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2699 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
2700 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
2701 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2702 decrementCurrentIndex();
2705 } else if (d->wrap) {
2709 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2710 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2711 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
2712 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
2713 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2714 incrementCurrentIndex();
2717 } else if (d->wrap) {
2724 QQuickItemView::keyPressEvent(event);
2727 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2729 Q_D(QQuickListView);
2730 if (d->isRightToLeft()) {
2731 // maintain position relative to the right edge
2732 int dx = newGeometry.width() - oldGeometry.width();
2733 setContentX(contentX() - dx);
2734 } else if (d->isBottomToTop()) {
2735 // maintain position relative to the bottom edge
2736 int dy = newGeometry.height() - oldGeometry.height();
2737 setContentY(contentY() - dy);
2739 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2744 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2746 Increments the current index. The current index will wrap
2747 if keyNavigationWraps is true and it is currently at the end.
2748 This method has no effect if the \l count is zero.
2750 \b Note: methods should only be called after the Component has completed.
2752 void QQuickListView::incrementCurrentIndex()
2754 Q_D(QQuickListView);
2755 int count = d->model ? d->model->count() : 0;
2756 if (count && (currentIndex() < count - 1 || d->wrap)) {
2757 d->moveReason = QQuickListViewPrivate::SetIndex;
2758 int index = currentIndex()+1;
2759 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2764 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2766 Decrements the current index. The current index will wrap
2767 if keyNavigationWraps is true and it is currently at the beginning.
2768 This method has no effect if the \l count is zero.
2770 \b Note: methods should only be called after the Component has completed.
2772 void QQuickListView::decrementCurrentIndex()
2774 Q_D(QQuickListView);
2775 int count = d->model ? d->model->count() : 0;
2776 if (count && (currentIndex() > 0 || d->wrap)) {
2777 d->moveReason = QQuickListViewPrivate::SetIndex;
2778 int index = currentIndex()-1;
2779 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2783 void QQuickListView::updateSections()
2785 Q_D(QQuickListView);
2786 if (isComponentComplete() && d->model) {
2787 QList<QByteArray> roles;
2788 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2789 roles << d->sectionCriteria->property().toUtf8();
2790 d->model->setWatchedRoles(roles);
2791 d->updateSections();
2793 d->forceLayout = true;
2799 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2801 int modelIndex = change.index;
2802 int count = change.count;
2804 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
2805 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2808 int i = visibleItems.count() - 1;
2809 while (i > 0 && visibleItems.at(i)->index == -1)
2811 if (i == 0 && visibleItems.first()->index == -1) {
2812 // there are no visible items except items marked for removal
2813 index = visibleItems.count();
2814 } else if (visibleItems.at(i)->index + 1 == modelIndex
2815 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2816 // Special case of appending an item to the model.
2817 index = visibleItems.count();
2819 if (modelIndex < visibleIndex) {
2820 // Insert before visible items
2821 visibleIndex += count;
2822 for (int i = 0; i < visibleItems.count(); ++i) {
2823 FxViewItem *item = visibleItems.at(i);
2824 if (item->index != -1 && item->index >= modelIndex)
2825 item->index += count;
2832 // index can be the next item past the end of the visible items list (i.e. appended)
2834 if (visibleItems.count()) {
2835 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2836 : visibleItems.last()->endPosition()+spacing;
2839 int prevVisibleCount = visibleItems.count();
2840 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2841 // Insert items before the visible item.
2842 int insertionIdx = index;
2844 int from = tempPos - buffer;
2846 for (i = count-1; i >= 0; --i) {
2847 if (pos > from && insertionIdx < visibleIndex) {
2848 // item won't be visible, just note the size for repositioning
2849 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2850 pos -= averageSize + spacing;
2852 // item is before first visible e.g. in cache buffer
2853 FxViewItem *item = 0;
2854 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2855 item->index = modelIndex + i;
2857 item = createItem(modelIndex + i);
2861 visibleItems.insert(insertionIdx, item);
2862 if (insertionIdx == 0)
2863 insertResult->changedFirstItem = true;
2864 if (!change.isMove()) {
2865 addedItems->append(item);
2866 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2868 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2869 pos -= item->size() + spacing;
2875 int to = buffer+tempPos+size();
2876 for (i = 0; i < count && pos <= to; ++i) {
2877 FxViewItem *item = 0;
2878 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2879 item->index = modelIndex + i;
2880 bool newItem = !item;
2882 item = createItem(modelIndex + i);
2886 visibleItems.insert(index, item);
2888 insertResult->changedFirstItem = true;
2889 if (change.isMove()) {
2890 // we know this is a move target, since move displaced items that are
2891 // shuffled into view due to a move would be added in refill()
2892 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2893 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2895 addedItems->append(item);
2896 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2898 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2899 pos += item->size() + spacing;
2904 for (; index < visibleItems.count(); ++index) {
2905 FxViewItem *item = visibleItems.at(index);
2906 if (item->index != -1) {
2907 item->index += count;
2908 if (change.isMove())
2909 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2911 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2915 updateVisibleIndex();
2917 return visibleItems.count() > prevVisibleCount;
2920 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2922 Q_UNUSED(insertionResult);
2927 int markerItemIndex = -1;
2928 for (int i=0; i<visibleItems.count(); i++) {
2929 if (visibleItems[i]->index == afterModelIndex) {
2930 markerItemIndex = i;
2934 if (markerItemIndex < 0)
2937 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2938 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
2939 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
2941 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2942 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
2943 if (!listItem->transitionScheduledOrRunning()) {
2944 qreal pos = listItem->position();
2945 listItem->setPosition(pos - sizeRemoved);
2946 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2947 listItem->setPosition(pos);
2953 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2955 Positions the view such that the \a index is at the position specified by
2959 \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2960 \li ListView.Center - position item in the center of the view.
2961 \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2962 \li ListView.Visible - if any part of the item is visible then take no action, otherwise
2963 bring the item into view.
2964 \li ListView.Contain - ensure the entire item is visible. If the item is larger than
2965 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2968 If positioning the view at \a index would cause empty space to be displayed at
2969 the beginning or end of the view, the view will be positioned at the boundary.
2971 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2972 at a particular index. This is unreliable since removing items from the start
2973 of the list does not cause all other items to be repositioned, and because
2974 the actual start of the view can vary based on the size of the delegates.
2975 The correct way to bring an item into view is with \c positionViewAtIndex.
2977 \b Note: methods should only be called after the Component has completed. To position
2978 the view at startup, this method should be called by Component.onCompleted. For
2979 example, to position the view at the end:
2982 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2987 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
2988 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
2990 Positions the view at the beginning or end, taking into account any header or footer.
2992 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2993 at a particular index. This is unreliable since removing items from the start
2994 of the list does not cause all other items to be repositioned, and because
2995 the actual start of the view can vary based on the size of the delegates.
2997 \b Note: methods should only be called after the Component has completed. To position
2998 the view at startup, this method should be called by Component.onCompleted. For
2999 example, to position the view at the end on startup:
3002 Component.onCompleted: positionViewAtEnd()
3007 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3009 Returns the index of the visible item containing the point \a x, \a y in content
3010 coordinates. If there is no item at the point specified, or the item is
3011 not visible -1 is returned.
3013 If the item is outside the visible area, -1 is returned, regardless of
3014 whether an item will exist at that point when scrolled into view.
3016 \b Note: methods should only be called after the Component has completed.
3020 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3022 Returns the visible item containing the point \a x, \a y in content
3023 coordinates. If there is no item at the point specified, or the item is
3024 not visible null is returned.
3026 If the item is outside the visible area, null is returned, regardless of
3027 whether an item will exist at that point when scrolled into view.
3029 \b Note: methods should only be called after the Component has completed.
3032 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3034 return new QQuickListViewAttached(obj);