1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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 <QtDeclarative/qdeclarativeexpression.h>
47 #include <QtDeclarative/qdeclarativeengine.h>
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
53 #include <private/qdeclarativesmoothedanimation_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;
77 virtual qreal positionAt(int index) const;
78 virtual qreal endPositionAt(int index) const;
79 virtual qreal originPosition() const;
80 virtual qreal lastPosition() const;
82 FxViewItem *itemBefore(int modelIndex) const;
83 QString sectionAt(int modelIndex);
84 qreal snapPosAt(qreal pos);
85 FxViewItem *snapItemAt(qreal pos);
90 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
91 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
92 virtual void visibleItemsChanged();
94 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
95 virtual void initializeViewItem(FxViewItem *item);
96 virtual void releaseItem(FxViewItem *item);
97 virtual void repositionPackageItemAt(QQuickItem *item, int index);
98 virtual void resetFirstItemPosition(qreal pos = 0.0);
99 virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
101 virtual void createHighlight();
102 virtual void updateHighlight();
103 virtual void resetHighlightPosition();
105 virtual void setPosition(qreal pos);
106 virtual void layoutVisibleItems(int fromModelIndex = 0);
107 virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems);
109 virtual void updateSections();
110 QQuickItem *getSectionItem(const QString §ion);
111 void releaseSectionItem(QQuickItem *item);
112 void updateInlineSection(FxListItemSG *);
113 void updateCurrentSection();
114 void updateStickySections();
116 virtual qreal headerSize() const;
117 virtual qreal footerSize() const;
118 virtual bool showHeaderForIndex(int index) const;
119 virtual bool showFooterForIndex(int index) const;
120 virtual void updateHeader();
121 virtual void updateFooter();
123 virtual void changedVisibleIndex(int newIndex);
124 virtual void initializeCurrentItem();
126 void updateAverage();
128 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
129 virtual void fixupPosition();
130 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
131 virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
132 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
134 QQuickListView::Orientation orient;
138 QQuickListView::SnapMode snapMode;
140 QSmoothedAnimation *highlightPosAnimator;
141 QSmoothedAnimation *highlightSizeAnimator;
142 qreal highlightMoveSpeed;
143 qreal highlightResizeSpeed;
144 int highlightResizeDuration;
146 QQuickViewSection *sectionCriteria;
147 QString currentSection;
148 static const int sectionCacheSize = 5;
149 QQuickItem *sectionCache[sectionCacheSize];
150 QQuickItem *currentSectionItem;
151 QString currentStickySection;
152 QQuickItem *nextSectionItem;
153 QString nextStickySection;
154 QString lastVisibleSection;
158 bool correctFlick : 1;
159 bool inFlickCorrection : 1;
161 QQuickListViewPrivate()
162 : orient(QQuickListView::Vertical)
164 , averageSize(100.0), spacing(0.0)
165 , snapMode(QQuickListView::NoSnap)
166 , highlightPosAnimator(0), highlightSizeAnimator(0)
167 , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
168 , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
169 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
172 friend class QQuickViewSection;
175 //----------------------------------------------------------------------------
177 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
178 : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
179 , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
183 void QQuickViewSection::setProperty(const QString &property)
185 if (property != m_property) {
186 m_property = property;
187 emit propertyChanged();
188 m_view->updateSections();
192 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
194 if (criteria != m_criteria) {
195 m_criteria = criteria;
196 emit criteriaChanged();
197 m_view->updateSections();
201 void QQuickViewSection::setDelegate(QDeclarativeComponent *delegate)
203 if (delegate != m_delegate) {
204 m_delegate = delegate;
205 emit delegateChanged();
206 m_view->updateSections();
210 QString QQuickViewSection::sectionString(const QString &value)
212 if (m_criteria == FirstCharacter)
213 return value.isEmpty() ? QString() : value.at(0);
218 void QQuickViewSection::setLabelPositioning(int l)
220 if (m_labelPositioning != l) {
221 m_labelPositioning = l;
222 emit labelPositioningChanged();
223 m_view->updateSections();
227 //----------------------------------------------------------------------------
229 class FxListItemSG : public FxViewItem
232 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own) : FxViewItem(i, own), section(0), view(v) {
233 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
235 static_cast<QQuickListViewAttached*>(attached)->setView(view);
240 qreal position() const {
242 if (view->orientation() == QQuickListView::Vertical)
245 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
247 return itemPosition();
250 qreal itemPosition() const {
251 if (view->orientation() == QQuickListView::Vertical)
254 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
258 return (view->orientation() == QQuickListView::Vertical ? item->height()+section->height() : item->width()+section->width());
260 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
262 qreal itemSize() const {
263 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
265 qreal sectionSize() const {
267 return (view->orientation() == QQuickListView::Vertical ? section->height() : section->width());
270 qreal endPosition() const {
271 if (view->orientation() == QQuickListView::Vertical) {
272 return item->y() + item->height();
274 return (view->effectiveLayoutDirection() == Qt::RightToLeft
276 : item->x() + item->width());
279 void setPosition(qreal pos) {
280 if (view->orientation() == QQuickListView::Vertical) {
283 pos += section->height();
287 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
289 section->setX(-section->width()-pos);
290 pos += section->width();
292 item->setX(-item->width()-pos);
296 pos += section->width();
302 void setSize(qreal size) {
303 if (view->orientation() == QQuickListView::Vertical)
304 item->setHeight(size);
306 item->setWidth(size);
308 bool contains(qreal x, qreal y) const {
309 return (x >= item->x() && x < item->x() + item->width() &&
310 y >= item->y() && y < item->y() + item->height());
314 QQuickListView *view;
317 //----------------------------------------------------------------------------
319 bool QQuickListViewPrivate::isContentFlowReversed() const
321 return isRightToLeft();
324 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
326 return static_cast<Qt::Orientation>(orient);
329 bool QQuickListViewPrivate::isRightToLeft() const
331 Q_Q(const QQuickListView);
332 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
335 // Returns the item before modelIndex, if created.
336 // May return an item marked for removal.
337 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
339 if (modelIndex < visibleIndex)
343 while (idx < visibleItems.count()) {
344 FxViewItem *item = visibleItems.at(idx);
345 if (item->index != -1)
346 lastIndex = item->index;
347 if (item->index == modelIndex)
348 return visibleItems.at(idx-1);
351 if (lastIndex == modelIndex-1)
352 return visibleItems.last();
356 void QQuickListViewPrivate::setPosition(qreal pos)
359 if (orient == QQuickListView::Vertical) {
360 q->QQuickFlickable::setContentY(pos);
363 q->QQuickFlickable::setContentX(-pos-size());
365 q->QQuickFlickable::setContentX(pos);
369 qreal QQuickListViewPrivate::originPosition() const
372 if (!visibleItems.isEmpty()) {
373 pos = (*visibleItems.constBegin())->position();
374 if (visibleIndex > 0)
375 pos -= visibleIndex * (averageSize + spacing);
380 qreal QQuickListViewPrivate::lastPosition() const
383 if (!visibleItems.isEmpty()) {
384 int invisibleCount = visibleItems.count() - visibleIndex;
385 for (int i = visibleItems.count()-1; i >= 0; --i) {
386 if (visibleItems.at(i)->index != -1) {
387 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
391 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
392 } else if (model && model->count()) {
393 pos = (model->count() * averageSize + (model->count()-1) * spacing);
398 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
400 if (FxViewItem *item = visibleItem(modelIndex))
401 return item->position();
402 if (!visibleItems.isEmpty()) {
403 if (modelIndex < visibleIndex) {
404 int count = visibleIndex - modelIndex;
406 if (modelIndex == currentIndex && currentItem) {
407 cs = currentItem->size() + spacing;
410 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
412 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
413 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
419 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
421 if (FxViewItem *item = visibleItem(modelIndex))
422 return item->endPosition();
423 if (!visibleItems.isEmpty()) {
424 if (modelIndex < visibleIndex) {
425 int count = visibleIndex - modelIndex;
426 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
428 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
429 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
435 QString QQuickListViewPrivate::sectionAt(int modelIndex)
437 if (FxViewItem *item = visibleItem(modelIndex))
438 return item->attached->section();
441 if (sectionCriteria) {
442 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
443 section = sectionCriteria->sectionString(propValue);
449 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
451 if (FxViewItem *snapItem = snapItemAt(pos))
452 return snapItem->position();
453 if (visibleItems.count()) {
454 qreal firstPos = (*visibleItems.constBegin())->position();
455 qreal endPos = (*(--visibleItems.constEnd()))->position();
456 if (pos < firstPos) {
457 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
458 } else if (pos > endPos)
459 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
461 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
464 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
466 FxViewItem *snapItem = 0;
467 qreal prevItemSize = 0;
468 for (int i = 0; i < visibleItems.count(); ++i) {
469 FxViewItem *item = visibleItems.at(i);
470 if (item->index == -1)
472 qreal itemTop = item->position();
473 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
475 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
477 prevItemSize = item->size();
482 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
484 visiblePos = positionAt(newIndex);
485 visibleIndex = newIndex;
488 void QQuickListViewPrivate::init()
490 QQuickItemViewPrivate::init();
491 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
494 void QQuickListViewPrivate::clear()
496 for (int i = 0; i < sectionCacheSize; ++i) {
497 delete sectionCache[i];
501 currentSectionItem = 0;
503 lastVisibleSection = QString();
504 QQuickItemViewPrivate::clear();
507 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
511 FxListItemSG *listItem = new FxListItemSG(item, q, false);
512 listItem->index = modelIndex;
514 // initialise attached properties
515 if (sectionCriteria) {
516 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
517 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
518 if (modelIndex > 0) {
519 if (FxViewItem *item = itemBefore(modelIndex))
520 listItem->attached->m_prevSection = item->attached->section();
522 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
524 if (modelIndex < model->count()-1) {
525 if (FxViewItem *item = visibleItem(modelIndex+1))
526 listItem->attached->m_nextSection = static_cast<QQuickListViewAttached*>(item->attached)->section();
528 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
535 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
537 QQuickItemViewPrivate::initializeViewItem(item);
539 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
540 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
542 if (sectionCriteria && sectionCriteria->delegate()) {
543 if (item->attached->m_prevSection != item->attached->m_section)
544 updateInlineSection(static_cast<FxListItemSG*>(item));
548 void QQuickListViewPrivate::releaseItem(FxViewItem *item)
551 FxListItemSG* listItem = static_cast<FxListItemSG*>(item);
552 if (listItem->section) {
555 if (!sectionCache[i]) {
556 sectionCache[i] = listItem->section;
557 sectionCache[i]->setVisible(false);
558 listItem->section = 0;
562 } while (i < sectionCacheSize);
563 delete listItem->section;
566 QQuickItemViewPrivate::releaseItem(item);
569 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
571 qreal itemEnd = visiblePos;
572 if (visibleItems.count()) {
573 visiblePos = (*visibleItems.constBegin())->position();
574 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
577 int modelIndex = findLastVisibleIndex();
578 bool haveValidItems = modelIndex >= 0;
579 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
581 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
582 || fillTo < visiblePos - averageSize - spacing)) {
583 // We've jumped more than a page. Estimate which items are now
584 // visible and fill from there.
585 int count = (fillFrom - itemEnd) / (averageSize + spacing);
586 for (int i = 0; i < visibleItems.count(); ++i)
587 releaseItem(visibleItems.at(i));
588 visibleItems.clear();
590 if (modelIndex >= model->count()) {
591 count -= modelIndex - model->count() + 1;
592 modelIndex = model->count() - 1;
593 } else if (modelIndex < 0) {
597 visibleIndex = modelIndex;
598 visiblePos = itemEnd + count * (averageSize + spacing);
599 itemEnd = visiblePos;
602 bool changed = false;
603 FxListItemSG *item = 0;
605 while (modelIndex < model->count() && pos <= fillTo) {
606 #ifdef DEBUG_DELEGATE_LIFECYCLE
607 qDebug() << "refill: append item" << modelIndex << "pos" << pos;
609 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
611 item->setPosition(pos);
612 item->item->setVisible(!doBuffer);
613 pos += item->size() + spacing;
614 visibleItems.append(item);
619 if (doBuffer && requestedIndex != -1) // already waiting for an item
622 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
623 #ifdef DEBUG_DELEGATE_LIFECYCLE
624 qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
626 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
629 visiblePos -= item->size() + spacing;
630 item->setPosition(visiblePos);
631 item->item->setVisible(!doBuffer);
632 visibleItems.prepend(item);
639 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
641 FxViewItem *item = 0;
642 bool changed = false;
644 // Remove items from the start of the view.
645 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
646 // removed, otherwise a zero-sized item is infinitely added and removed over and
649 while (visibleItems.count() > 1 && index < visibleItems.count()
650 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
651 if (item->attached->delayRemove())
653 if (item->size() > 0) {
654 #ifdef DEBUG_DELEGATE_LIFECYCLE
655 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
657 // remove this item and all zero-sized items before it
659 if (item->index != -1)
661 visibleItems.removeAt(index);
665 item = visibleItems.at(--index);
673 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
674 if (item->attached->delayRemove())
676 #ifdef DEBUG_DELEGATE_LIFECYCLE
677 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
679 visibleItems.removeLast();
687 void QQuickListViewPrivate::visibleItemsChanged()
689 if (visibleItems.count())
690 visiblePos = (*visibleItems.constBegin())->position();
692 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
693 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
697 updateCurrentSection();
701 updateUnrequestedPositions();
704 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
706 if (!visibleItems.isEmpty()) {
707 const qreal from = isContentFlowReversed() ? -position() - size() : position();
708 const qreal to = isContentFlowReversed() ? -position() : position() + size();
710 FxViewItem *firstItem = *visibleItems.constBegin();
711 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
712 qreal sum = firstItem->size();
713 qreal pos = firstItem->position() + firstItem->size() + spacing;
714 firstItem->item->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
715 for (int i=1; i < visibleItems.count(); ++i) {
716 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
717 if (item->index >= fromModelIndex) {
718 item->setPosition(pos);
719 item->item->setVisible(item->endPosition() >= from && item->position() <= to);
721 pos += item->size() + spacing;
723 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
725 averageSize = qRound(sum / visibleItems.count());
727 // move current item if it is not a visible item.
728 if (currentIndex >= 0 && currentItem && !fixedCurrent) {
729 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
734 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
737 qreal pos = position();
738 if (orient == QQuickListView::Vertical) {
739 if (item->y() + item->height() > pos && item->y() < pos + q->height())
740 item->setY(positionAt(index));
742 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
744 item->setX(-positionAt(index)-item->width());
746 item->setX(positionAt(index));
751 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
753 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
754 item->setPosition(pos);
757 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
759 if (!visibleItems.count())
761 qreal diff = forwards - backwards;
762 static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
765 void QQuickListViewPrivate::createHighlight()
768 bool changed = false;
770 if (trackedItem == highlight)
775 delete highlightPosAnimator;
776 delete highlightSizeAnimator;
777 highlightPosAnimator = 0;
778 highlightSizeAnimator = 0;
784 QQuickItem *item = createHighlightItem();
786 FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
789 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
790 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
792 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
793 highlightPosAnimator = new QSmoothedAnimation(q);
794 highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
795 highlightPosAnimator->velocity = highlightMoveSpeed;
796 highlightPosAnimator->userDuration = highlightMoveDuration;
798 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
799 highlightSizeAnimator = new QSmoothedAnimation(q);
800 highlightSizeAnimator->velocity = highlightResizeSpeed;
801 highlightSizeAnimator->userDuration = highlightResizeDuration;
802 highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
804 highlight = newHighlight;
809 emit q->highlightItemChanged();
812 void QQuickListViewPrivate::updateHighlight()
814 applyPendingChanges();
816 if ((!currentItem && highlight) || (currentItem && !highlight))
818 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
819 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
820 // auto-update highlight
821 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
822 highlightPosAnimator->to = isRightToLeft()
823 ? -listItem->itemPosition()-listItem->itemSize()
824 : listItem->itemPosition();
825 highlightSizeAnimator->to = listItem->itemSize();
826 if (orient == QQuickListView::Vertical) {
827 if (highlight->item->width() == 0)
828 highlight->item->setWidth(currentItem->item->width());
830 if (highlight->item->height() == 0)
831 highlight->item->setHeight(currentItem->item->height());
834 highlightPosAnimator->restart();
835 highlightSizeAnimator->restart();
840 void QQuickListViewPrivate::resetHighlightPosition()
842 if (highlight && currentItem)
843 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
846 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
849 QQuickItem *sectionItem = 0;
850 int i = sectionCacheSize-1;
851 while (i >= 0 && !sectionCache[i])
854 sectionItem = sectionCache[i];
856 sectionItem->setVisible(true);
857 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(sectionItem)->parentContext();
858 context->setContextProperty(QLatin1String("section"), section);
860 QDeclarativeContext *creationContext = sectionCriteria->delegate()->creationContext();
861 QDeclarativeContext *context = new QDeclarativeContext(
862 creationContext ? creationContext : qmlContext(q));
863 context->setContextProperty(QLatin1String("section"), section);
864 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
866 QDeclarative_setParent_noEvent(context, nobj);
867 sectionItem = qobject_cast<QQuickItem *>(nobj);
871 sectionItem->setZ(2);
872 QDeclarative_setParent_noEvent(sectionItem, contentItem);
873 sectionItem->setParentItem(contentItem);
878 sectionCriteria->delegate()->completeCreate();
884 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
888 if (!sectionCache[i]) {
889 sectionCache[i] = item;
890 sectionCache[i]->setVisible(false);
894 } while (i < sectionCacheSize);
898 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
900 if (!sectionCriteria || !sectionCriteria->delegate())
902 if (listItem->attached->m_prevSection != listItem->attached->m_section
903 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
904 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
905 if (!listItem->section) {
906 qreal pos = listItem->position();
907 listItem->section = getSectionItem(listItem->attached->m_section);
908 listItem->setPosition(pos);
910 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
911 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
913 } else if (listItem->section) {
914 qreal pos = listItem->position();
915 releaseSectionItem(listItem->section);
916 listItem->section = 0;
917 listItem->setPosition(pos);
921 void QQuickListViewPrivate::updateStickySections()
923 if (!sectionCriteria || visibleItems.isEmpty()
924 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
927 bool isRtl = isRightToLeft();
928 qreal viewPos = isRightToLeft() ? -position()-size() : position();
929 QQuickItem *sectionItem = 0;
930 QQuickItem *lastSectionItem = 0;
932 while (index < visibleItems.count()) {
933 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section) {
934 // Find the current section header and last visible section header
935 // and hide them if they will overlap a static section header.
936 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
937 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
939 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
940 visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
942 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
943 visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
944 section->setVisible(visBot && visTop);
945 if (visTop && !sectionItem)
946 sectionItem = section;
948 if (-sectionPos <= viewPos + size())
949 lastSectionItem = section;
951 if (sectionPos + sectionSize < viewPos + size())
952 lastSectionItem = section;
958 // Current section header
959 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) {
960 if (!currentSectionItem) {
961 currentSectionItem = getSectionItem(currentSection);
962 } else if (currentStickySection != currentSection) {
963 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(currentSectionItem)->parentContext();
964 context->setContextProperty(QLatin1String("section"), currentSection);
966 currentStickySection = currentSection;
967 if (!currentSectionItem)
970 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
971 bool atBeginning = orient == QQuickListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
972 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
973 qreal pos = isRtl ? position() + size() - sectionSize : viewPos;
975 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
976 pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
979 pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
981 pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
982 if (orient == QQuickListView::Vertical)
983 currentSectionItem->setY(pos);
985 currentSectionItem->setX(pos);
986 } else if (currentSectionItem) {
987 releaseSectionItem(currentSectionItem);
988 currentSectionItem = 0;
991 // Next section footer
992 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
993 if (!nextSectionItem) {
994 nextSectionItem = getSectionItem(nextSection);
995 } else if (nextStickySection != nextSection) {
996 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(nextSectionItem)->parentContext();
997 context->setContextProperty(QLatin1String("section"), nextSection);
999 nextStickySection = nextSection;
1000 if (!nextSectionItem)
1003 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1004 nextSectionItem->setVisible(!nextSection.isEmpty());
1005 qreal pos = isRtl ? position() : viewPos + size() - sectionSize;
1006 if (lastSectionItem) {
1007 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1008 pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1011 pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1012 if (orient == QQuickListView::Vertical)
1013 nextSectionItem->setY(pos);
1015 nextSectionItem->setX(pos);
1016 } else if (nextSectionItem) {
1017 releaseSectionItem(nextSectionItem);
1018 nextSectionItem = 0;
1022 void QQuickListViewPrivate::updateSections()
1024 Q_Q(QQuickListView);
1025 if (!q->isComponentComplete())
1028 QQuickItemViewPrivate::updateSections();
1030 if (sectionCriteria && !visibleItems.isEmpty()) {
1031 QString prevSection;
1032 if (visibleIndex > 0)
1033 prevSection = sectionAt(visibleIndex-1);
1034 QQuickListViewAttached *prevAtt = 0;
1036 for (int i = 0; i < visibleItems.count(); ++i) {
1037 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1038 attached->setPrevSection(prevSection);
1039 if (visibleItems.at(i)->index != -1) {
1040 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1041 attached->setSection(sectionCriteria->sectionString(propValue));
1042 idx = visibleItems.at(i)->index;
1044 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1046 prevAtt->setNextSection(attached->section());
1047 prevSection = attached->section();
1051 if (idx > 0 && idx < model->count()-1)
1052 prevAtt->setNextSection(sectionAt(idx+1));
1054 prevAtt->setNextSection(QString());
1058 lastVisibleSection = QString();
1059 updateCurrentSection();
1060 updateStickySections();
1063 void QQuickListViewPrivate::updateCurrentSection()
1065 Q_Q(QQuickListView);
1066 if (!sectionCriteria || visibleItems.isEmpty()) {
1067 if (!currentSection.isEmpty()) {
1068 currentSection.clear();
1069 emit q->currentSectionChanged();
1073 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1074 qreal sectionThreshold = position();
1075 if (currentSectionItem && !inlineSections)
1076 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1078 int modelIndex = visibleIndex;
1079 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1080 if (visibleItems.at(index)->index != -1)
1081 modelIndex = visibleItems.at(index)->index;
1085 QString newSection = currentSection;
1086 if (index < visibleItems.count())
1087 newSection = visibleItems.at(index)->attached->section();
1089 newSection = (*visibleItems.constBegin())->attached->section();
1090 if (newSection != currentSection) {
1091 currentSection = newSection;
1092 updateStickySections();
1093 emit q->currentSectionChanged();
1096 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1097 // Don't want to scan for next section on every movement, so remember
1098 // the last section in the visible area and only scan for the next
1099 // section when that changes. Clearing lastVisibleSection will also
1101 QString lastSection = currentSection;
1102 qreal endPos = isRightToLeft() ? -position() : position() + size();
1103 if (nextSectionItem && !inlineSections)
1104 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1105 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1106 if (visibleItems.at(index)->index != -1)
1107 modelIndex = visibleItems.at(index)->index;
1108 lastSection = visibleItems.at(index)->attached->section();
1112 if (lastVisibleSection != lastSection) {
1113 nextSection = QString();
1114 lastVisibleSection = lastSection;
1115 for (int i = modelIndex; i < itemCount; ++i) {
1116 QString section = sectionAt(i);
1117 if (section != lastSection) {
1118 nextSection = section;
1119 updateStickySections();
1127 void QQuickListViewPrivate::initializeCurrentItem()
1129 QQuickItemViewPrivate::initializeCurrentItem();
1132 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1134 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1135 // We can calculate exact postion in this case
1136 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1138 // Create current item now and position as best we can.
1139 // Its position will be corrected when it becomes visible.
1140 listItem->setPosition(positionAt(currentIndex));
1143 // Avoid showing section delegate twice. We still need the section heading so that
1144 // currentItem positioning works correctly.
1145 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1146 if (listItem->section)
1147 listItem->section->setVisible(false);
1149 if (visibleItems.isEmpty())
1150 averageSize = listItem->size();
1154 void QQuickListViewPrivate::updateAverage()
1156 if (!visibleItems.count())
1159 for (int i = 0; i < visibleItems.count(); ++i)
1160 sum += visibleItems.at(i)->size();
1161 averageSize = qRound(sum / visibleItems.count());
1164 qreal QQuickListViewPrivate::headerSize() const
1166 return header ? header->size() : 0.0;
1169 qreal QQuickListViewPrivate::footerSize() const
1171 return footer ? footer->size() : 0.0;
1174 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1179 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1181 return index == model->count()-1;
1184 void QQuickListViewPrivate::updateFooter()
1186 Q_Q(QQuickListView);
1187 bool created = false;
1189 QQuickItem *item = createComponentItem(footerComponent, true);
1193 footer = new FxListItemSG(item, q, true);
1197 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1198 if (visibleItems.count()) {
1199 qreal endPos = lastPosition();
1200 if (findLastVisibleIndex() == model->count()-1) {
1201 listItem->setPosition(endPos);
1203 qreal visiblePos = position() + q->height();
1204 if (endPos <= visiblePos || listItem->position() < endPos)
1205 listItem->setPosition(endPos);
1208 listItem->setPosition(visiblePos);
1212 emit q->footerItemChanged();
1215 void QQuickListViewPrivate::updateHeader()
1217 Q_Q(QQuickListView);
1218 bool created = false;
1220 QQuickItem *item = createComponentItem(headerComponent, true);
1224 header = new FxListItemSG(item, q, true);
1228 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1230 if (visibleItems.count()) {
1231 qreal startPos = originPosition();
1232 if (visibleIndex == 0) {
1233 listItem->setPosition(startPos - headerSize());
1235 if (position() <= startPos || listItem->position() > startPos - headerSize())
1236 listItem->setPosition(startPos - headerSize());
1239 listItem->setPosition(-headerSize());
1244 emit q->headerItemChanged();
1247 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1249 Q_Q(QQuickListView);
1250 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1251 if (!q->isComponentComplete())
1253 if (item != contentItem && (!highlight || item != highlight->item)) {
1254 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1255 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1262 void QQuickListViewPrivate::fixupPosition()
1264 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1265 || snapMode != QQuickListView::NoSnap)
1267 if (orient == QQuickListView::Vertical)
1273 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1275 if ((orient == QQuickListView::Horizontal && &data == &vData)
1276 || (orient == QQuickListView::Vertical && &data == &hData))
1279 correctFlick = false;
1280 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1281 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1283 qreal viewPos = isRightToLeft() ? -position()-size() : position();
1285 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1286 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1287 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1288 // if we've been dragged < averageSize/2 then bias towards the next item
1289 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1291 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1292 bias = averageSize/2;
1293 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1294 bias = -averageSize/2;
1295 if (isRightToLeft())
1297 tempPosition -= bias;
1299 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1300 if (!topItem && strictHighlightRange && currentItem) {
1301 // StrictlyEnforceRange always keeps an item in range
1303 topItem = currentItem;
1305 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1306 if (!bottomItem && strictHighlightRange && currentItem) {
1307 // StrictlyEnforceRange always keeps an item in range
1309 bottomItem = currentItem;
1312 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1313 if (topItem && (isInBounds || strictHighlightRange)) {
1314 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1315 pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1317 if (isRightToLeft())
1318 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1320 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1322 } else if (bottomItem && isInBounds) {
1323 if (isRightToLeft())
1324 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1326 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1328 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1332 qreal dist = qAbs(data.move + pos);
1334 timeline.reset(data.move);
1335 if (fixupMode != Immediate) {
1336 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1337 data.fixingUp = true;
1339 timeline.set(data.move, -pos);
1341 vTime = timeline.time();
1343 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1345 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1346 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1347 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1348 if (viewPos > pos - highlightRangeStart)
1349 viewPos = pos - highlightRangeStart;
1350 if (isRightToLeft())
1351 viewPos = -viewPos-size();
1353 timeline.reset(data.move);
1354 if (viewPos != position()) {
1355 if (fixupMode != Immediate) {
1356 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1357 data.fixingUp = true;
1359 timeline.set(data.move, -viewPos);
1362 vTime = timeline.time();
1364 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1366 data.inOvershoot = false;
1370 void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1371 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1373 Q_Q(QQuickListView);
1375 data.fixingUp = false;
1377 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1378 correctFlick = true;
1379 QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1382 qreal maxDistance = 0;
1383 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1385 // -ve velocity means list is moving up/left
1387 if (data.move.value() < minExtent) {
1388 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1389 // if we've been dragged < averageSize/2 then bias towards the next item
1390 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1391 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1392 if (isRightToLeft())
1394 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1395 maxDistance = qAbs(data.flickTarget - data.move.value());
1396 velocity = maxVelocity;
1398 maxDistance = qAbs(minExtent - data.move.value());
1401 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1402 data.flickTarget = minExtent;
1404 if (data.move.value() > maxExtent) {
1405 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1406 // if we've been dragged < averageSize/2 then bias towards the next item
1407 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1408 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1409 if (isRightToLeft())
1411 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1412 maxDistance = qAbs(data.flickTarget - data.move.value());
1413 velocity = -maxVelocity;
1415 maxDistance = qAbs(maxExtent - data.move.value());
1418 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1419 data.flickTarget = maxExtent;
1421 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1422 if (maxDistance > 0 || overShoot) {
1423 // These modes require the list to stop exactly on an item boundary.
1424 // The initial flick will estimate the boundary to stop on.
1425 // Since list items can have variable sizes, the boundary will be
1426 // reevaluated and adjusted as we approach the boundary.
1428 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1434 if (!hData.flicking && !vData.flicking) {
1435 // the initial flick - estimate boundary
1436 qreal accel = deceleration;
1438 overshootDist = 0.0;
1439 // + averageSize/4 to encourage moving at least one item in the flick direction
1440 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1441 if (maxDistance > 0)
1442 dist = qMin(dist, maxDistance);
1445 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1446 if (snapMode != QQuickListView::SnapOneItem) {
1447 qreal distTemp = isRightToLeft() ? -dist : dist;
1448 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1450 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1452 if (data.flickTarget >= minExtent) {
1453 overshootDist = overShootDistance(vSize);
1454 data.flickTarget += overshootDist;
1455 } else if (data.flickTarget <= maxExtent) {
1456 overshootDist = overShootDistance(vSize);
1457 data.flickTarget -= overshootDist;
1460 qreal adjDist = -data.flickTarget + data.move.value();
1461 if (qAbs(adjDist) > qAbs(dist)) {
1462 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1463 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1472 accel = v2 / (2.0f * qAbs(dist));
1473 } else if (overShoot) {
1474 data.flickTarget = data.move.value() - dist;
1475 if (data.flickTarget >= minExtent) {
1476 overshootDist = overShootDistance(vSize);
1477 data.flickTarget += overshootDist;
1478 } else if (data.flickTarget <= maxExtent) {
1479 overshootDist = overShootDistance(vSize);
1480 data.flickTarget -= overshootDist;
1483 timeline.reset(data.move);
1484 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1485 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1486 if (!hData.flicking && q->xflick()) {
1487 hData.flicking = true;
1488 emit q->flickingChanged();
1489 emit q->flickingHorizontallyChanged();
1490 emit q->flickStarted();
1492 if (!vData.flicking && q->yflick()) {
1493 vData.flicking = true;
1494 emit q->flickingChanged();
1495 emit q->flickingVerticallyChanged();
1496 emit q->flickStarted();
1498 correctFlick = true;
1500 // reevaluate the target boundary.
1501 qreal newtarget = data.flickTarget;
1502 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1503 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1504 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1505 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1507 if (velocity < 0 && newtarget <= maxExtent)
1508 newtarget = maxExtent - overshootDist;
1509 else if (velocity > 0 && newtarget >= minExtent)
1510 newtarget = minExtent + overshootDist;
1511 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1512 if (qAbs(velocity) < MinimumFlickVelocity)
1513 correctFlick = false;
1516 data.flickTarget = newtarget;
1517 qreal dist = -newtarget + data.move.value();
1518 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1519 correctFlick = false;
1520 timeline.reset(data.move);
1521 fixup(data, minExtent, maxExtent);
1524 timeline.reset(data.move);
1525 timeline.accelDistance(data.move, v, -dist);
1526 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1529 correctFlick = false;
1530 timeline.reset(data.move);
1531 fixup(data, minExtent, maxExtent);
1535 //----------------------------------------------------------------------------
1538 \qmlclass ListView QQuickListView
1539 \inqmlmodule QtQuick 2
1540 \ingroup qml-view-elements
1542 \brief The ListView item provides a list view of items provided by a model.
1544 A ListView displays data from models created from built-in QML elements like ListModel
1545 and XmlListModel, or custom model classes defined in C++ that inherit from
1548 A ListView has a \l model, which defines the data to be displayed, and
1549 a \l delegate, which defines how the data should be displayed. Items in a
1550 ListView are laid out horizontally or vertically. List views are inherently
1551 flickable because ListView inherits from \l Flickable.
1553 \section1 Example Usage
1555 The following example shows the definition of a simple list model defined
1556 in a file called \c ContactModel.qml:
1558 \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1560 Another component can display this model data in a ListView, like this:
1562 \snippet doc/src/snippets/declarative/listview/listview.qml import
1564 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1566 \image listview-simple.png
1568 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1569 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1570 the delegate is able to access the model's \c name and \c number data directly.
1572 An improved list view is shown below. The delegate is visually improved and is moved
1573 into a separate \c contactDelegate component.
1575 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1576 \image listview-highlight.png
1578 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1579 and \c focus is set to \c true to enable keyboard navigation for the list view.
1580 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1582 Delegates are instantiated as needed and may be destroyed at any time.
1583 State should \e never be stored in a delegate.
1585 ListView attaches a number of properties to the root item of the delegate, for example
1586 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1587 this attached property directly as \c ListView.isCurrentItem, while the child
1588 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1590 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1592 \note Views do not enable \e clip automatically. If the view
1593 is not clipped by another item or the screen, it will be necessary
1594 to set \e {clip: true} in order to have the out of view items clipped
1597 \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1599 QQuickListView::QQuickListView(QQuickItem *parent)
1600 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1604 QQuickListView::~QQuickListView()
1609 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1610 This attached property is true if this delegate is the current item; otherwise false.
1612 It is attached to each instance of the delegate.
1614 This property may be used to adjust the appearance of the current item, for example:
1616 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1620 \qmlattachedproperty ListView QtQuick2::ListView::view
1621 This attached property holds the view that manages this delegate instance.
1623 It is attached to each instance of the delegate.
1627 \qmlattachedproperty string QtQuick2::ListView::previousSection
1628 This attached property holds the section of the previous element.
1630 It is attached to each instance of the delegate.
1632 The section is evaluated using the \l {ListView::section.property}{section} properties.
1636 \qmlattachedproperty string QtQuick2::ListView::nextSection
1637 This attached property holds the section of the next element.
1639 It is attached to each instance of the delegate.
1641 The section is evaluated using the \l {ListView::section.property}{section} properties.
1645 \qmlattachedproperty string QtQuick2::ListView::section
1646 This attached property holds the section of this element.
1648 It is attached to each instance of the delegate.
1650 The section is evaluated using the \l {ListView::section.property}{section} properties.
1654 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1655 This attached property holds whether the delegate may be destroyed.
1657 It is attached to each instance of the delegate.
1659 It is sometimes necessary to delay the destruction of an item
1660 until an animation completes.
1662 The example delegate below ensures that the animation completes before
1663 the item is removed from the list.
1665 \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1669 \qmlattachedsignal QtQuick2::ListView::onAdd()
1670 This attached handler is called immediately after an item is added to the view.
1674 \qmlattachedsignal QtQuick2::ListView::onRemove()
1675 This attached handler is called immediately before an item is removed from the view.
1679 \qmlproperty model QtQuick2::ListView::model
1680 This property holds the model providing data for the list.
1682 The model provides the set of data that is used to create the items
1683 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1684 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1685 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1687 \sa {qmlmodels}{Data Models}
1691 \qmlproperty Component QtQuick2::ListView::delegate
1693 The delegate provides a template defining each item instantiated by the view.
1694 The index is exposed as an accessible \c index property. Properties of the
1695 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1697 The number of elements in the delegate has a direct effect on the
1698 flicking performance of the view. If at all possible, place functionality
1699 that is not needed for the normal display of the delegate in a \l Loader which
1700 can load additional elements when needed.
1702 The ListView will lay out the items based on the size of the root item
1705 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1708 \note Delegates are instantiated as needed and may be destroyed at any time.
1709 State should \e never be stored in a delegate.
1712 \qmlproperty int QtQuick2::ListView::currentIndex
1713 \qmlproperty Item QtQuick2::ListView::currentItem
1715 The \c currentIndex property holds the index of the current item, and
1716 \c currentItem holds the current item. Setting the currentIndex to -1
1717 will clear the highlight and set currentItem to null.
1719 If highlightFollowsCurrentItem is \c true, setting either of these
1720 properties will smoothly scroll the ListView so that the current
1721 item becomes visible.
1723 Note that the position of the current item
1724 may only be approximate until it becomes visible in the view.
1728 \qmlproperty Item QtQuick2::ListView::highlightItem
1730 This holds the highlight item created from the \l highlight component.
1732 The \c highlightItem is managed by the view unless
1733 \l highlightFollowsCurrentItem is set to false.
1735 \sa highlight, highlightFollowsCurrentItem
1739 \qmlproperty int QtQuick2::ListView::count
1740 This property holds the number of items in the view.
1744 \qmlproperty Component QtQuick2::ListView::highlight
1745 This property holds the component to use as the highlight.
1747 An instance of the highlight component is created for each list.
1748 The geometry of the resulting component instance is managed by the list
1749 so as to stay with the current item, unless the highlightFollowsCurrentItem
1752 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1756 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1757 This property holds whether the highlight is managed by the view.
1759 If this property is true (the default value), the highlight is moved smoothly
1760 to follow the current item. Otherwise, the
1761 highlight is not moved by the view, and any movement must be implemented
1764 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1766 \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1768 Note that the highlight animation also affects the way that the view
1769 is scrolled. This is because the view moves to maintain the
1770 highlight within the preferred highlight range (or visible viewport).
1772 \sa highlight, highlightMoveSpeed
1774 //###Possibly rename these properties, since they are very useful even without a highlight?
1776 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1777 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1778 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1780 These properties define the preferred range of the highlight (for the current item)
1781 within the view. The \c preferredHighlightBegin value must be less than the
1782 \c preferredHighlightEnd value.
1784 These properties affect the position of the current item when the list is scrolled.
1785 For example, if the currently selected item should stay in the middle of the
1786 list when the view is scrolled, set the \c preferredHighlightBegin and
1787 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1788 item would be. If the \c currentItem is changed programmatically, the list will
1789 automatically scroll so that the current item is in the middle of the view.
1790 Furthermore, the behavior of the current item index will occur whether or not a
1793 Valid values for \c highlightRangeMode are:
1796 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1797 However, the highlight can move outside of the range at the ends of the list or due
1798 to mouse interaction.
1799 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1800 The current item changes if a keyboard or mouse action would cause the highlight to move
1801 outside of the range.
1802 \o ListView.NoHighlightRange - this is the default value.
1805 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1807 Q_D(QQuickListView);
1808 if (d->autoHighlight != autoHighlight) {
1809 if (!autoHighlight) {
1810 if (d->highlightPosAnimator)
1811 d->highlightPosAnimator->stop();
1812 if (d->highlightSizeAnimator)
1813 d->highlightSizeAnimator->stop();
1815 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1820 \qmlproperty real QtQuick2::ListView::spacing
1822 This property holds the spacing between items.
1824 The default value is 0.
1826 qreal QQuickListView::spacing() const
1828 Q_D(const QQuickListView);
1832 void QQuickListView::setSpacing(qreal spacing)
1834 Q_D(QQuickListView);
1835 if (spacing != d->spacing) {
1836 d->spacing = spacing;
1837 d->forceLayout = true;
1839 emit spacingChanged();
1844 \qmlproperty enumeration QtQuick2::ListView::orientation
1845 This property holds the orientation of the list.
1850 \o ListView.Horizontal - Items are laid out horizontally
1851 \o ListView.Vertical (default) - Items are laid out vertically
1856 \o Horizontal orientation:
1857 \image ListViewHorizontal.png
1860 \o Vertical orientation:
1861 \image listview-highlight.png
1864 QQuickListView::Orientation QQuickListView::orientation() const
1866 Q_D(const QQuickListView);
1870 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
1872 Q_D(QQuickListView);
1873 if (d->orient != orientation) {
1874 d->orient = orientation;
1875 if (d->orient == Vertical) {
1876 setContentWidth(-1);
1877 setFlickableDirection(VerticalFlick);
1880 setContentHeight(-1);
1881 setFlickableDirection(HorizontalFlick);
1885 emit orientationChanged();
1890 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
1891 This property holds the layout direction of the horizontal list.
1896 \o Qt.LeftToRight (default) - Items will be laid out from left to right.
1897 \o Qt.RightToLeft - Items will be laid out from right to let.
1900 \sa ListView::effectiveLayoutDirection
1905 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
1906 This property holds the effective layout direction of the horizontal list.
1908 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1909 the visual layout direction of the horizontal list will be mirrored. However, the
1910 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
1912 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1916 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
1917 This property holds whether the list wraps key navigation.
1919 If this is true, key navigation that would move the current item selection
1920 past the end of the list instead wraps around and moves the selection to
1921 the start of the list, and vice-versa.
1923 By default, key navigation is not wrapped.
1928 \qmlproperty int QtQuick2::ListView::cacheBuffer
1929 This property determines whether delegates are retained outside the
1930 visible area of the view.
1932 If this value is non-zero, the view may keep as many delegates
1933 instantiated as it can fit within the buffer specified. For example,
1934 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1935 set to 40, then up to 2 delegates above and 2 delegates below the visible
1936 area may be created/retained. The buffered delegates are created asynchronously,
1937 allowing creation to occur across multiple frames and reducing the
1938 likelihood of skipping frames. In order to improve painting performance
1939 delegates outside the visible area have their \l visible property set to
1940 false until they are moved into the visible area.
1942 Note that cacheBuffer is not a pixel buffer - it only maintains additional
1943 instantiated delegates.
1945 Setting this value can improve the smoothness of scrolling behavior at the expense
1946 of additional memory usage. It is not a substitute for creating efficient
1947 delegates; the fewer elements in a delegate, the faster a view can be
1953 \qmlproperty string QtQuick2::ListView::section.property
1954 \qmlproperty enumeration QtQuick2::ListView::section.criteria
1955 \qmlproperty Component QtQuick2::ListView::section.delegate
1956 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
1958 These properties determine the expression to be evaluated and appearance
1959 of the section labels.
1961 \c section.property holds the name of the property that is the basis
1964 \c section.criteria holds the criteria for forming each section based on
1965 \c section.property. This value can be one of:
1968 \o ViewSection.FullString (default) - sections are created based on the
1969 \c section.property value.
1970 \o ViewSection.FirstCharacter - sections are created based on the first
1971 character of the \c section.property value (for example, 'A', 'B', 'C'
1972 sections, etc. for an address book)
1975 \c section.delegate holds the delegate component for each section.
1977 \c section.labelPositioning determines whether the current and/or
1978 next section labels stick to the start/end of the view, and whether
1979 the labels are shown inline. This value can be a combination of:
1982 \o ViewSection.InlineLabels - section labels are shown inline between
1983 the item delegates separating sections (default).
1984 \o ViewSection.CurrentLabelAtStart - the current section label sticks to the
1985 start of the view as it is moved.
1986 \o ViewSection.NextLabelAtEnd - the next section label (beyond all visible
1987 sections) sticks to the end of the view as it is moved. \note Enabling
1988 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
1989 section, which has performance implications, especially for slower models.
1992 Each item in the list has attached properties named \c ListView.section,
1993 \c ListView.previousSection and \c ListView.nextSection.
1995 For example, here is a ListView that displays a list of animals, separated
1996 into sections. Each item in the ListView is placed in a different section
1997 depending on the "size" property of the model item. The \c sectionHeading
1998 delegate component provides the light blue bar that marks the beginning of
2002 \snippet examples/declarative/modelviews/listview/sections.qml 0
2004 \image qml-listview-sections-example.png
2006 \note Adding sections to a ListView does not automatically re-order the
2007 list items by the section criteria.
2008 If the model is not ordered by section, then it is possible that
2009 the sections created will not be unique; each boundary between
2010 differing sections will result in a section header being created
2011 even if that section exists elsewhere.
2013 \sa {declarative/modelviews/listview}{ListView examples}
2015 QQuickViewSection *QQuickListView::sectionCriteria()
2017 Q_D(QQuickListView);
2018 if (!d->sectionCriteria) {
2019 d->sectionCriteria = new QQuickViewSection(this);
2020 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2022 return d->sectionCriteria;
2026 \qmlproperty string QtQuick2::ListView::currentSection
2027 This property holds the section that is currently at the beginning of the view.
2029 QString QQuickListView::currentSection() const
2031 Q_D(const QQuickListView);
2032 return d->currentSection;
2036 \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
2037 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2038 \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
2039 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2041 These properties hold the move and resize animation speed of the highlight delegate.
2043 \l highlightFollowsCurrentItem must be true for these properties
2046 The default value for the speed properties is 400 pixels/second.
2047 The default value for the duration properties is -1, i.e. the
2048 highlight will take as much time as necessary to move at the set speed.
2050 These properties have the same characteristics as a SmoothedAnimation.
2052 \sa highlightFollowsCurrentItem
2054 qreal QQuickListView::highlightMoveSpeed() const
2056 Q_D(const QQuickListView);
2057 return d->highlightMoveSpeed;
2060 void QQuickListView::setHighlightMoveSpeed(qreal speed)
2062 Q_D(QQuickListView);
2063 if (d->highlightMoveSpeed != speed) {
2064 d->highlightMoveSpeed = speed;
2065 if (d->highlightPosAnimator)
2066 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2067 emit highlightMoveSpeedChanged();
2071 void QQuickListView::setHighlightMoveDuration(int duration)
2073 Q_D(QQuickListView);
2074 if (d->highlightMoveDuration != duration) {
2075 if (d->highlightPosAnimator)
2076 d->highlightPosAnimator->userDuration = duration;
2077 QQuickItemView::setHighlightMoveDuration(duration);
2081 qreal QQuickListView::highlightResizeSpeed() const
2083 Q_D(const QQuickListView);
2084 return d->highlightResizeSpeed;
2087 void QQuickListView::setHighlightResizeSpeed(qreal speed)
2089 Q_D(QQuickListView);
2090 if (d->highlightResizeSpeed != speed) {
2091 d->highlightResizeSpeed = speed;
2092 if (d->highlightSizeAnimator)
2093 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2094 emit highlightResizeSpeedChanged();
2098 int QQuickListView::highlightResizeDuration() const
2100 Q_D(const QQuickListView);
2101 return d->highlightResizeDuration;
2104 void QQuickListView::setHighlightResizeDuration(int duration)
2106 Q_D(QQuickListView);
2107 if (d->highlightResizeDuration != duration) {
2108 d->highlightResizeDuration = duration;
2109 if (d->highlightSizeAnimator)
2110 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2111 emit highlightResizeDurationChanged();
2116 \qmlproperty enumeration QtQuick2::ListView::snapMode
2118 This property determines how the view scrolling will settle following a drag or flick.
2119 The possible values are:
2122 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2123 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2125 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2126 visible item at the time the mouse button is released. This mode is particularly
2127 useful for moving one page at a time.
2130 \c snapMode does not affect the \l currentIndex. To update the
2131 \l currentIndex as the list is moved, set \l highlightRangeMode
2132 to \c ListView.StrictlyEnforceRange.
2134 \sa highlightRangeMode
2136 QQuickListView::SnapMode QQuickListView::snapMode() const
2138 Q_D(const QQuickListView);
2142 void QQuickListView::setSnapMode(SnapMode mode)
2144 Q_D(QQuickListView);
2145 if (d->snapMode != mode) {
2147 emit snapModeChanged();
2153 \qmlproperty Component QtQuick2::ListView::footer
2154 This property holds the component to use as the footer.
2156 An instance of the footer component is created for each view. The
2157 footer is positioned at the end of the view, after any items.
2159 \sa header, footerItem
2164 \qmlproperty Component QtQuick2::ListView::header
2165 This property holds the component to use as the header.
2167 An instance of the header component is created for each view. The
2168 header is positioned at the beginning of the view, before any items.
2170 \sa footer, headertem
2174 \qmlproperty Item QtQuick2::ListView::headerItem
2175 This holds the header item created from the \l header component.
2177 An instance of the header component is created for each view. The
2178 header is positioned at the beginning of the view, before any items.
2180 \sa header, footerItem
2184 \qmlproperty Item QtQuick2::ListView::footerItem
2185 This holds the footer item created from the \l footer component.
2187 An instance of the footer component is created for each view. The
2188 footer is positioned at the end of the view, after any items.
2190 \sa footer, headerItem
2193 void QQuickListView::viewportMoved()
2195 Q_D(QQuickListView);
2196 QQuickItemView::viewportMoved();
2199 // Recursion can occur due to refill changing the content size.
2200 if (d->inViewportMoved)
2202 d->inViewportMoved = true;
2205 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2206 else if (d->isRightToLeft())
2207 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2209 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2213 // Set visibility of items to eliminate cost of items outside the visible area.
2214 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2215 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2216 for (int i = 0; i < d->visibleItems.count(); ++i) {
2217 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2218 item->item->setVisible(item->endPosition() >= from && item->position() <= to);
2221 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2222 d->moveReason = QQuickListViewPrivate::Mouse;
2223 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2224 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2225 // reposition highlight
2226 qreal pos = d->highlight->position();
2227 qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
2228 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2229 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2230 if (pos < viewPos + d->highlightRangeStart)
2231 pos = viewPos + d->highlightRangeStart;
2232 if (pos != d->highlight->position()) {
2233 d->highlightPosAnimator->stop();
2234 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2236 d->updateHighlight();
2239 // update current index
2240 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2241 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2242 d->updateCurrent(snapItem->index);
2247 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2248 d->inFlickCorrection = true;
2249 // Near an end and it seems that the extent has changed?
2250 // Recalculate the flick so that we don't end up in an odd position.
2251 if (yflick() && !d->vData.inOvershoot) {
2252 if (d->vData.velocity > 0) {
2253 const qreal minY = minYExtent();
2254 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2255 && minY != d->vData.flickTarget)
2256 d->flickY(-d->vData.smoothVelocity.value());
2257 } else if (d->vData.velocity < 0) {
2258 const qreal maxY = maxYExtent();
2259 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2260 && maxY != d->vData.flickTarget)
2261 d->flickY(-d->vData.smoothVelocity.value());
2265 if (xflick() && !d->hData.inOvershoot) {
2266 if (d->hData.velocity > 0) {
2267 const qreal minX = minXExtent();
2268 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2269 && minX != d->hData.flickTarget)
2270 d->flickX(-d->hData.smoothVelocity.value());
2271 } else if (d->hData.velocity < 0) {
2272 const qreal maxX = maxXExtent();
2273 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2274 && maxX != d->hData.flickTarget)
2275 d->flickX(-d->hData.smoothVelocity.value());
2278 d->inFlickCorrection = false;
2280 if (d->sectionCriteria) {
2281 d->updateCurrentSection();
2282 d->updateStickySections();
2284 d->inViewportMoved = false;
2287 void QQuickListView::keyPressEvent(QKeyEvent *event)
2289 Q_D(QQuickListView);
2290 if (d->model && d->model->count() && d->interactive) {
2291 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2292 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2293 || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Up)) {
2294 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2295 decrementCurrentIndex();
2298 } else if (d->wrap) {
2302 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2303 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2304 || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Down)) {
2305 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2306 incrementCurrentIndex();
2309 } else if (d->wrap) {
2316 QQuickItemView::keyPressEvent(event);
2319 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2321 Q_D(QQuickListView);
2322 if (d->isRightToLeft() && d->orient == QQuickListView::Horizontal) {
2323 // maintain position relative to the right edge
2324 int dx = newGeometry.width() - oldGeometry.width();
2325 setContentX(contentX() - dx);
2327 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2332 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2334 Increments the current index. The current index will wrap
2335 if keyNavigationWraps is true and it is currently at the end.
2336 This method has no effect if the \l count is zero.
2338 \bold Note: methods should only be called after the Component has completed.
2340 void QQuickListView::incrementCurrentIndex()
2342 Q_D(QQuickListView);
2343 int count = d->model ? d->model->count() : 0;
2344 if (count && (currentIndex() < count - 1 || d->wrap)) {
2345 d->moveReason = QQuickListViewPrivate::SetIndex;
2346 int index = currentIndex()+1;
2347 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2352 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2354 Decrements the current index. The current index will wrap
2355 if keyNavigationWraps is true and it is currently at the beginning.
2356 This method has no effect if the \l count is zero.
2358 \bold Note: methods should only be called after the Component has completed.
2360 void QQuickListView::decrementCurrentIndex()
2362 Q_D(QQuickListView);
2363 int count = d->model ? d->model->count() : 0;
2364 if (count && (currentIndex() > 0 || d->wrap)) {
2365 d->moveReason = QQuickListViewPrivate::SetIndex;
2366 int index = currentIndex()-1;
2367 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2371 void QQuickListView::updateSections()
2373 Q_D(QQuickListView);
2374 if (isComponentComplete() && d->model) {
2375 QList<QByteArray> roles;
2376 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2377 roles << d->sectionCriteria->property().toUtf8();
2378 d->model->setWatchedRoles(roles);
2379 d->updateSections();
2381 d->forceLayout = true;
2387 bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems)
2389 int modelIndex = change.index;
2390 int count = change.count;
2392 qreal tempPos = isRightToLeft() ? -position()-size() : position();
2393 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2396 int i = visibleItems.count() - 1;
2397 while (i > 0 && visibleItems.at(i)->index == -1)
2399 if (i == 0 && visibleItems.first()->index == -1) {
2400 // there are no visible items except items marked for removal
2401 index = visibleItems.count();
2402 } else if (visibleItems.at(i)->index + 1 == modelIndex
2403 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2404 // Special case of appending an item to the model.
2405 index = visibleItems.count();
2407 if (modelIndex < visibleIndex) {
2408 // Insert before visible items
2409 visibleIndex += count;
2410 for (int i = 0; i < visibleItems.count(); ++i) {
2411 FxViewItem *item = visibleItems.at(i);
2412 if (item->index != -1 && item->index >= modelIndex)
2413 item->index += count;
2420 // index can be the next item past the end of the visible items list (i.e. appended)
2422 if (visibleItems.count()) {
2423 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2424 : visibleItems.last()->endPosition()+spacing;
2427 int prevVisibleCount = visibleItems.count();
2428 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2429 // Insert items before the visible item.
2430 int insertionIdx = index;
2432 int from = tempPos - buffer;
2434 for (i = count-1; i >= 0; --i) {
2435 if (pos > from && insertionIdx < visibleIndex) {
2436 // item won't be visible, just note the size for repositioning
2437 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2438 pos -= averageSize + spacing;
2440 // item is before first visible e.g. in cache buffer
2441 FxViewItem *item = 0;
2442 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2443 item->index = modelIndex + i;
2445 item = createItem(modelIndex + i);
2449 visibleItems.insert(insertionIdx, item);
2450 if (insertionIdx == 0)
2451 insertResult->changedFirstItem = true;
2452 if (!change.isMove())
2453 addedItems->append(item);
2454 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2455 pos -= item->size() + spacing;
2461 int to = buffer+tempPos+size();
2462 for (i = 0; i < count && pos <= to; ++i) {
2463 FxViewItem *item = 0;
2464 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2465 item->index = modelIndex + i;
2467 item = createItem(modelIndex + i);
2471 visibleItems.insert(index, item);
2473 insertResult->changedFirstItem = true;
2474 if (!change.isMove())
2475 addedItems->append(item);
2476 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2477 pos += item->size() + spacing;
2482 for (; index < visibleItems.count(); ++index) {
2483 FxViewItem *item = visibleItems.at(index);
2484 if (item->index != -1)
2485 item->index += count;
2488 updateVisibleIndex();
2490 return visibleItems.count() > prevVisibleCount;
2495 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2497 Positions the view such that the \a index is at the position specified by
2501 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2502 \o ListView.Center - position item in the center of the view.
2503 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2504 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2505 bring the item into view.
2506 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
2507 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2510 If positioning the view at \a index would cause empty space to be displayed at
2511 the beginning or end of the view, the view will be positioned at the boundary.
2513 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2514 at a particular index. This is unreliable since removing items from the start
2515 of the list does not cause all other items to be repositioned, and because
2516 the actual start of the view can vary based on the size of the delegates.
2517 The correct way to bring an item into view is with \c positionViewAtIndex.
2519 \bold Note: methods should only be called after the Component has completed. To position
2520 the view at startup, this method should be called by Component.onCompleted. For
2521 example, to position the view at the end:
2524 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2529 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
2530 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
2532 Positions the view at the beginning or end, taking into account any header or footer.
2534 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2535 at a particular index. This is unreliable since removing items from the start
2536 of the list does not cause all other items to be repositioned, and because
2537 the actual start of the view can vary based on the size of the delegates.
2539 \bold Note: methods should only be called after the Component has completed. To position
2540 the view at startup, this method should be called by Component.onCompleted. For
2541 example, to position the view at the end on startup:
2544 Component.onCompleted: positionViewAtEnd()
2549 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
2551 Returns the index of the visible item containing the point \a x, \a y in content
2552 coordinates. If there is no item at the point specified, or the item is
2553 not visible -1 is returned.
2555 If the item is outside the visible area, -1 is returned, regardless of
2556 whether an item will exist at that point when scrolled into view.
2558 \bold Note: methods should only be called after the Component has completed.
2562 \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
2564 Returns the visible item containing the point \a x, \a y in content
2565 coordinates. If there is no item at the point specified, or the item is
2566 not visible null is returned.
2568 If the item is outside the visible area, null is returned, regardless of
2569 whether an item will exist at that point when scrolled into view.
2571 \bold Note: methods should only be called after the Component has completed.
2574 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
2576 return new QQuickListViewAttached(obj);