1 /****************************************************************************
3 ** Copyright (C) 2011 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
65 class QQuickListViewPrivate : public QQuickItemViewPrivate
67 Q_DECLARE_PUBLIC(QQuickListView)
69 static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
71 virtual Qt::Orientation layoutOrientation() const;
72 virtual bool isContentFlowReversed() const;
73 bool isRightToLeft() const;
75 virtual qreal positionAt(int index) const;
76 virtual qreal endPositionAt(int index) const;
77 virtual qreal originPosition() const;
78 virtual qreal lastPosition() const;
80 FxViewItem *itemBefore(int modelIndex) const;
81 QString sectionAt(int modelIndex);
82 qreal snapPosAt(qreal pos);
83 FxViewItem *snapItemAt(qreal pos);
88 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
89 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
90 virtual void visibleItemsChanged();
92 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
93 virtual void initializeViewItem(FxViewItem *item);
94 virtual void releaseItem(FxViewItem *item);
95 virtual void repositionPackageItemAt(QQuickItem *item, int index);
96 virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem);
97 virtual void resetFirstItemPosition();
98 virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards);
100 virtual void createHighlight();
101 virtual void updateHighlight();
102 virtual void resetHighlightPosition();
104 virtual void setPosition(qreal pos);
105 virtual void layoutVisibleItems();
106 bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *firstVisible, InsertionsResult *);
108 virtual void updateSections();
109 QQuickItem *getSectionItem(const QString §ion);
110 void releaseSectionItem(QQuickItem *item);
111 void updateInlineSection(FxListItemSG *);
112 void updateCurrentSection();
113 void updateStickySections();
115 virtual qreal headerSize() const;
116 virtual qreal footerSize() const;
117 virtual bool showHeaderForIndex(int index) const;
118 virtual bool showFooterForIndex(int index) const;
119 virtual void updateHeader();
120 virtual void updateFooter();
122 virtual void changedVisibleIndex(int newIndex);
123 virtual void initializeCurrentItem();
125 void updateAverage();
127 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
128 virtual void fixupPosition();
129 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
130 virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
131 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
133 QQuickListView::Orientation orient;
137 QQuickListView::SnapMode snapMode;
139 QSmoothedAnimation *highlightPosAnimator;
140 QSmoothedAnimation *highlightSizeAnimator;
141 qreal highlightMoveSpeed;
142 qreal highlightResizeSpeed;
143 int highlightResizeDuration;
145 QQuickViewSection *sectionCriteria;
146 QString currentSection;
147 static const int sectionCacheSize = 5;
148 QQuickItem *sectionCache[sectionCacheSize];
149 QQuickItem *currentSectionItem;
150 QString currentStickySection;
151 QQuickItem *nextSectionItem;
152 QString nextStickySection;
153 QString lastVisibleSection;
157 bool correctFlick : 1;
158 bool inFlickCorrection : 1;
160 QQuickListViewPrivate()
161 : orient(QQuickListView::Vertical)
163 , averageSize(100.0), spacing(0.0)
164 , snapMode(QQuickListView::NoSnap)
165 , highlightPosAnimator(0), highlightSizeAnimator(0)
166 , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
167 , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
168 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
171 friend class QQuickViewSection;
174 //----------------------------------------------------------------------------
176 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
177 : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
178 , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
182 void QQuickViewSection::setProperty(const QString &property)
184 if (property != m_property) {
185 m_property = property;
186 emit propertyChanged();
187 m_view->updateSections();
191 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
193 if (criteria != m_criteria) {
194 m_criteria = criteria;
195 emit criteriaChanged();
196 m_view->updateSections();
200 void QQuickViewSection::setDelegate(QDeclarativeComponent *delegate)
202 if (delegate != m_delegate) {
203 m_delegate = delegate;
204 emit delegateChanged();
205 m_view->updateSections();
209 QString QQuickViewSection::sectionString(const QString &value)
211 if (m_criteria == FirstCharacter)
212 return value.isEmpty() ? QString() : value.at(0);
217 void QQuickViewSection::setLabelPositioning(int l)
219 if (m_labelPositioning != l) {
220 m_labelPositioning = l;
221 emit labelPositioningChanged();
222 m_view->updateSections();
226 //----------------------------------------------------------------------------
228 class FxListItemSG : public FxViewItem
231 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own) : FxViewItem(i, own), section(0), view(v) {
232 attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
234 static_cast<QQuickListViewAttached*>(attached)->setView(view);
239 qreal position() const {
241 if (view->orientation() == QQuickListView::Vertical)
244 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
246 return itemPosition();
249 qreal itemPosition() const {
250 if (view->orientation() == QQuickListView::Vertical)
253 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
257 return (view->orientation() == QQuickListView::Vertical ? item->height()+section->height() : item->width()+section->width());
259 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
261 qreal itemSize() const {
262 return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
264 qreal sectionSize() const {
266 return (view->orientation() == QQuickListView::Vertical ? section->height() : section->width());
269 qreal endPosition() const {
270 if (view->orientation() == QQuickListView::Vertical) {
271 return item->y() + item->height();
273 return (view->effectiveLayoutDirection() == Qt::RightToLeft
275 : item->x() + item->width());
278 void setPosition(qreal pos) {
279 if (view->orientation() == QQuickListView::Vertical) {
282 pos += section->height();
286 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
288 section->setX(-section->width()-pos);
289 pos += section->width();
291 item->setX(-item->width()-pos);
295 pos += section->width();
301 void setSize(qreal size) {
302 if (view->orientation() == QQuickListView::Vertical)
303 item->setHeight(size);
305 item->setWidth(size);
307 bool contains(qreal x, qreal y) const {
308 return (x >= item->x() && x < item->x() + item->width() &&
309 y >= item->y() && y < item->y() + item->height());
313 QQuickListView *view;
316 //----------------------------------------------------------------------------
318 bool QQuickListViewPrivate::isContentFlowReversed() const
320 return isRightToLeft();
323 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
325 return static_cast<Qt::Orientation>(orient);
328 bool QQuickListViewPrivate::isRightToLeft() const
330 Q_Q(const QQuickListView);
331 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
334 // Returns the item before modelIndex, if created.
335 // May return an item marked for removal.
336 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
338 if (modelIndex < visibleIndex)
342 while (idx < visibleItems.count()) {
343 FxViewItem *item = visibleItems.at(idx);
344 if (item->index != -1)
345 lastIndex = item->index;
346 if (item->index == modelIndex)
347 return visibleItems.at(idx-1);
350 if (lastIndex == modelIndex-1)
351 return visibleItems.last();
355 void QQuickListViewPrivate::setPosition(qreal pos)
358 if (orient == QQuickListView::Vertical) {
359 q->QQuickFlickable::setContentY(pos);
362 q->QQuickFlickable::setContentX(-pos-size());
364 q->QQuickFlickable::setContentX(pos);
368 qreal QQuickListViewPrivate::originPosition() const
371 if (!visibleItems.isEmpty()) {
372 pos = (*visibleItems.constBegin())->position();
373 if (visibleIndex > 0)
374 pos -= visibleIndex * (averageSize + spacing);
379 qreal QQuickListViewPrivate::lastPosition() const
382 if (!visibleItems.isEmpty()) {
383 int invisibleCount = visibleItems.count() - visibleIndex;
384 for (int i = visibleItems.count()-1; i >= 0; --i) {
385 if (visibleItems.at(i)->index != -1) {
386 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
390 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
391 } else if (model && model->count()) {
392 pos = (model->count() * averageSize + (model->count()-1) * spacing);
397 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
399 if (FxViewItem *item = visibleItem(modelIndex))
400 return item->position();
401 if (!visibleItems.isEmpty()) {
402 if (modelIndex < visibleIndex) {
403 int count = visibleIndex - modelIndex;
405 if (modelIndex == currentIndex && currentItem) {
406 cs = currentItem->size() + spacing;
409 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
411 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
412 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
418 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
420 if (FxViewItem *item = visibleItem(modelIndex))
421 return item->endPosition();
422 if (!visibleItems.isEmpty()) {
423 if (modelIndex < visibleIndex) {
424 int count = visibleIndex - modelIndex;
425 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
427 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
428 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
434 QString QQuickListViewPrivate::sectionAt(int modelIndex)
436 if (FxViewItem *item = visibleItem(modelIndex))
437 return item->attached->section();
440 if (sectionCriteria) {
441 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
442 section = sectionCriteria->sectionString(propValue);
448 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
450 if (FxViewItem *snapItem = snapItemAt(pos))
451 return snapItem->position();
452 if (visibleItems.count()) {
453 qreal firstPos = (*visibleItems.constBegin())->position();
454 qreal endPos = (*(--visibleItems.constEnd()))->position();
455 if (pos < firstPos) {
456 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
457 } else if (pos > endPos)
458 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
460 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
463 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
465 FxViewItem *snapItem = 0;
466 qreal prevItemSize = 0;
467 for (int i = 0; i < visibleItems.count(); ++i) {
468 FxViewItem *item = visibleItems.at(i);
469 if (item->index == -1)
471 qreal itemTop = item->position();
472 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
474 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
476 prevItemSize = item->size();
481 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
483 visiblePos = positionAt(newIndex);
484 visibleIndex = newIndex;
487 void QQuickListViewPrivate::init()
489 QQuickItemViewPrivate::init();
490 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
493 void QQuickListViewPrivate::clear()
495 for (int i = 0; i < sectionCacheSize; ++i) {
496 delete sectionCache[i];
500 currentSectionItem = 0;
502 lastVisibleSection = QString();
503 QQuickItemViewPrivate::clear();
506 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
510 FxListItemSG *listItem = new FxListItemSG(item, q, false);
511 listItem->index = modelIndex;
513 // initialise attached properties
514 if (sectionCriteria) {
515 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
516 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
517 if (modelIndex > 0) {
518 if (FxViewItem *item = itemBefore(modelIndex))
519 listItem->attached->m_prevSection = item->attached->section();
521 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
523 if (modelIndex < model->count()-1) {
524 if (FxViewItem *item = visibleItem(modelIndex+1))
525 listItem->attached->m_nextSection = static_cast<QQuickListViewAttached*>(item->attached)->section();
527 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
534 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
536 QQuickItemViewPrivate::initializeViewItem(item);
538 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
539 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
541 if (sectionCriteria && sectionCriteria->delegate()) {
542 if (item->attached->m_prevSection != item->attached->m_section)
543 updateInlineSection(static_cast<FxListItemSG*>(item));
547 void QQuickListViewPrivate::releaseItem(FxViewItem *item)
550 FxListItemSG* listItem = static_cast<FxListItemSG*>(item);
551 if (listItem->section) {
554 if (!sectionCache[i]) {
555 sectionCache[i] = listItem->section;
556 sectionCache[i]->setVisible(false);
557 listItem->section = 0;
561 } while (i < sectionCacheSize);
562 delete listItem->section;
565 QQuickItemViewPrivate::releaseItem(item);
568 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
570 qreal itemEnd = visiblePos;
571 if (visibleItems.count()) {
572 visiblePos = (*visibleItems.constBegin())->position();
573 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
576 int modelIndex = findLastVisibleIndex();
577 bool haveValidItems = modelIndex >= 0;
578 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
580 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
581 || fillTo < visiblePos - averageSize - spacing)) {
582 // We've jumped more than a page. Estimate which items are now
583 // visible and fill from there.
584 int count = (fillFrom - itemEnd) / (averageSize + spacing);
585 for (int i = 0; i < visibleItems.count(); ++i)
586 releaseItem(visibleItems.at(i));
587 visibleItems.clear();
589 if (modelIndex >= model->count()) {
590 count -= modelIndex - model->count() + 1;
591 modelIndex = model->count() - 1;
592 } else if (modelIndex < 0) {
596 visibleIndex = modelIndex;
597 visiblePos = itemEnd + count * (averageSize + spacing);
598 itemEnd = visiblePos;
601 bool changed = false;
602 FxListItemSG *item = 0;
604 while (modelIndex < model->count() && pos <= fillTo) {
605 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
606 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
608 item->setPosition(pos);
609 item->item->setVisible(!doBuffer);
610 pos += item->size() + spacing;
611 visibleItems.append(item);
615 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
616 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
617 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
620 visiblePos -= item->size() + spacing;
621 item->setPosition(visiblePos);
622 item->item->setVisible(!doBuffer);
623 visibleItems.prepend(item);
630 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
632 FxViewItem *item = 0;
633 bool changed = false;
635 // Remove items from the start of the view.
636 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
637 // removed, otherwise a zero-sized item is infinitely added and removed over and
640 while (visibleItems.count() > 1 && index < visibleItems.count()
641 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
642 if (item->attached->delayRemove())
644 if (item->size() > 0) {
645 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
647 // remove this item and all zero-sized items before it
649 if (item->index != -1)
651 visibleItems.removeAt(index);
655 item = visibleItems.at(--index);
663 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
664 if (item->attached->delayRemove())
666 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
667 visibleItems.removeLast();
675 void QQuickListViewPrivate::visibleItemsChanged()
677 if (visibleItems.count())
678 visiblePos = (*visibleItems.constBegin())->position();
680 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
681 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
685 updateCurrentSection();
689 updateUnrequestedPositions();
692 void QQuickListViewPrivate::layoutVisibleItems()
694 if (!visibleItems.isEmpty()) {
695 bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item;
696 qreal sum = (*visibleItems.constBegin())->size();
697 qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing;
698 for (int i=1; i < visibleItems.count(); ++i) {
699 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
700 item->setPosition(pos);
701 pos += item->size() + spacing;
703 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
705 averageSize = qRound(sum / visibleItems.count());
707 // move current item if it is not a visible item.
708 if (currentIndex >= 0 && currentItem && !fixedCurrent) {
709 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
714 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
717 qreal pos = position();
718 if (orient == QQuickListView::Vertical) {
719 if (item->y() + item->height() > pos && item->y() < pos + q->height())
720 item->setY(positionAt(index));
722 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
724 item->setX(-positionAt(index)-item->width());
726 item->setX(positionAt(index));
731 void QQuickListViewPrivate::resetItemPosition(FxViewItem *item, FxViewItem *toItem)
735 static_cast<FxListItemSG*>(item)->setPosition(toItem->position());
738 void QQuickListViewPrivate::resetFirstItemPosition()
740 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
741 item->setPosition(0);
744 void QQuickListViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards)
746 qreal diff = forwards - backwards;
747 static_cast<FxListItemSG*>(item)->setPosition(item->position() + diff);
750 void QQuickListViewPrivate::createHighlight()
753 bool changed = false;
755 if (trackedItem == highlight)
760 delete highlightPosAnimator;
761 delete highlightSizeAnimator;
762 highlightPosAnimator = 0;
763 highlightSizeAnimator = 0;
769 QQuickItem *item = createHighlightItem();
771 FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
774 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
775 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
777 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
778 highlightPosAnimator = new QSmoothedAnimation(q);
779 highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
780 highlightPosAnimator->velocity = highlightMoveSpeed;
781 highlightPosAnimator->userDuration = highlightMoveDuration;
783 const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
784 highlightSizeAnimator = new QSmoothedAnimation(q);
785 highlightSizeAnimator->velocity = highlightResizeSpeed;
786 highlightSizeAnimator->userDuration = highlightResizeDuration;
787 highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
789 highlight = newHighlight;
794 emit q->highlightItemChanged();
797 void QQuickListViewPrivate::updateHighlight()
799 applyPendingChanges();
801 if ((!currentItem && highlight) || (currentItem && !highlight))
803 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
804 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
805 // auto-update highlight
806 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
807 highlightPosAnimator->to = isRightToLeft()
808 ? -listItem->itemPosition()-listItem->itemSize()
809 : listItem->itemPosition();
810 highlightSizeAnimator->to = listItem->itemSize();
811 if (orient == QQuickListView::Vertical) {
812 if (highlight->item->width() == 0)
813 highlight->item->setWidth(currentItem->item->width());
815 if (highlight->item->height() == 0)
816 highlight->item->setHeight(currentItem->item->height());
819 highlightPosAnimator->restart();
820 highlightSizeAnimator->restart();
825 void QQuickListViewPrivate::resetHighlightPosition()
827 if (highlight && currentItem)
828 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
831 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion)
834 QQuickItem *sectionItem = 0;
835 int i = sectionCacheSize-1;
836 while (i >= 0 && !sectionCache[i])
839 sectionItem = sectionCache[i];
841 sectionItem->setVisible(true);
842 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(sectionItem)->parentContext();
843 context->setContextProperty(QLatin1String("section"), section);
845 QDeclarativeContext *creationContext = sectionCriteria->delegate()->creationContext();
846 QDeclarativeContext *context = new QDeclarativeContext(
847 creationContext ? creationContext : qmlContext(q));
848 context->setContextProperty(QLatin1String("section"), section);
849 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
851 QDeclarative_setParent_noEvent(context, nobj);
852 sectionItem = qobject_cast<QQuickItem *>(nobj);
856 sectionItem->setZ(2);
857 QDeclarative_setParent_noEvent(sectionItem, contentItem);
858 sectionItem->setParentItem(contentItem);
863 sectionCriteria->delegate()->completeCreate();
869 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
873 if (!sectionCache[i]) {
874 sectionCache[i] = item;
875 sectionCache[i]->setVisible(false);
879 } while (i < sectionCacheSize);
883 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
885 if (!sectionCriteria || !sectionCriteria->delegate())
887 if (listItem->attached->m_prevSection != listItem->attached->m_section
888 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
889 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
890 if (!listItem->section) {
891 qreal pos = listItem->position();
892 listItem->section = getSectionItem(listItem->attached->m_section);
893 listItem->setPosition(pos);
895 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
896 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
898 } else if (listItem->section) {
899 qreal pos = listItem->position();
900 releaseSectionItem(listItem->section);
901 listItem->section = 0;
902 listItem->setPosition(pos);
906 void QQuickListViewPrivate::updateStickySections()
908 if (!sectionCriteria || visibleItems.isEmpty()
909 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
912 bool isRtl = isRightToLeft();
913 qreal viewPos = isRightToLeft() ? -position()-size() : position();
914 QQuickItem *sectionItem = 0;
915 QQuickItem *lastSectionItem = 0;
917 while (index < visibleItems.count()) {
918 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section) {
919 // Find the current section header and last visible section header
920 // and hide them if they will overlap a static section header.
921 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
922 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
924 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
925 visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
927 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
928 visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
929 section->setVisible(visBot && visTop);
930 if (visTop && !sectionItem)
931 sectionItem = section;
933 if (-sectionPos <= viewPos + size())
934 lastSectionItem = section;
936 if (sectionPos + sectionSize < viewPos + size())
937 lastSectionItem = section;
943 // Current section header
944 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) {
945 if (!currentSectionItem) {
946 currentSectionItem = getSectionItem(currentSection);
947 } else if (currentStickySection != currentSection) {
948 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(currentSectionItem)->parentContext();
949 context->setContextProperty(QLatin1String("section"), currentSection);
951 currentStickySection = currentSection;
952 if (!currentSectionItem)
955 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
956 bool atBeginning = orient == QQuickListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
957 currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
958 qreal pos = isRtl ? position() + size() - sectionSize : viewPos;
960 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
961 pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
964 pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
966 pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
967 if (orient == QQuickListView::Vertical)
968 currentSectionItem->setY(pos);
970 currentSectionItem->setX(pos);
971 } else if (currentSectionItem) {
972 releaseSectionItem(currentSectionItem);
973 currentSectionItem = 0;
976 // Next section footer
977 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
978 if (!nextSectionItem) {
979 nextSectionItem = getSectionItem(nextSection);
980 } else if (nextStickySection != nextSection) {
981 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(nextSectionItem)->parentContext();
982 context->setContextProperty(QLatin1String("section"), nextSection);
984 nextStickySection = nextSection;
985 if (!nextSectionItem)
988 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
989 nextSectionItem->setVisible(!nextSection.isEmpty());
990 qreal pos = isRtl ? position() : viewPos + size() - sectionSize;
991 if (lastSectionItem) {
992 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
993 pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
996 pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
997 if (orient == QQuickListView::Vertical)
998 nextSectionItem->setY(pos);
1000 nextSectionItem->setX(pos);
1001 } else if (nextSectionItem) {
1002 releaseSectionItem(nextSectionItem);
1003 nextSectionItem = 0;
1007 void QQuickListViewPrivate::updateSections()
1009 Q_Q(QQuickListView);
1010 if (!q->isComponentComplete())
1013 QQuickItemViewPrivate::updateSections();
1015 if (sectionCriteria && !visibleItems.isEmpty()) {
1016 QString prevSection;
1017 if (visibleIndex > 0)
1018 prevSection = sectionAt(visibleIndex-1);
1019 QQuickListViewAttached *prevAtt = 0;
1021 for (int i = 0; i < visibleItems.count(); ++i) {
1022 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1023 attached->setPrevSection(prevSection);
1024 if (visibleItems.at(i)->index != -1) {
1025 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1026 attached->setSection(sectionCriteria->sectionString(propValue));
1027 idx = visibleItems.at(i)->index;
1029 updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1031 prevAtt->setNextSection(attached->section());
1032 prevSection = attached->section();
1036 if (idx > 0 && idx < model->count()-1)
1037 prevAtt->setNextSection(sectionAt(idx+1));
1039 prevAtt->setNextSection(QString());
1043 lastVisibleSection = QString();
1044 updateCurrentSection();
1045 updateStickySections();
1048 void QQuickListViewPrivate::updateCurrentSection()
1050 Q_Q(QQuickListView);
1051 if (!sectionCriteria || visibleItems.isEmpty()) {
1052 if (!currentSection.isEmpty()) {
1053 currentSection.clear();
1054 emit q->currentSectionChanged();
1058 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1059 qreal sectionThreshold = position();
1060 if (currentSectionItem && !inlineSections)
1061 sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1063 int modelIndex = visibleIndex;
1064 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1065 if (visibleItems.at(index)->index != -1)
1066 modelIndex = visibleItems.at(index)->index;
1070 QString newSection = currentSection;
1071 if (index < visibleItems.count())
1072 newSection = visibleItems.at(index)->attached->section();
1074 newSection = (*visibleItems.constBegin())->attached->section();
1075 if (newSection != currentSection) {
1076 currentSection = newSection;
1077 updateStickySections();
1078 emit q->currentSectionChanged();
1081 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1082 // Don't want to scan for next section on every movement, so remember
1083 // the last section in the visible area and only scan for the next
1084 // section when that changes. Clearing lastVisibleSection will also
1086 QString lastSection = currentSection;
1087 qreal endPos = isRightToLeft() ? -position() : position() + size();
1088 if (nextSectionItem && !inlineSections)
1089 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1090 while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1091 if (visibleItems.at(index)->index != -1)
1092 modelIndex = visibleItems.at(index)->index;
1093 lastSection = visibleItems.at(index)->attached->section();
1097 if (lastVisibleSection != lastSection) {
1098 nextSection = QString();
1099 lastVisibleSection = lastSection;
1100 for (int i = modelIndex; i < itemCount; ++i) {
1101 QString section = sectionAt(i);
1102 if (section != lastSection) {
1103 nextSection = section;
1104 updateStickySections();
1112 void QQuickListViewPrivate::initializeCurrentItem()
1114 QQuickItemViewPrivate::initializeCurrentItem();
1117 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1119 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1120 // We can calculate exact postion in this case
1121 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1123 // Create current item now and position as best we can.
1124 // Its position will be corrected when it becomes visible.
1125 listItem->setPosition(positionAt(currentIndex));
1128 // Avoid showing section delegate twice. We still need the section heading so that
1129 // currentItem positioning works correctly.
1130 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1131 if (listItem->section)
1132 listItem->section->setVisible(false);
1134 if (visibleItems.isEmpty())
1135 averageSize = listItem->size();
1139 void QQuickListViewPrivate::updateAverage()
1141 if (!visibleItems.count())
1144 for (int i = 0; i < visibleItems.count(); ++i)
1145 sum += visibleItems.at(i)->size();
1146 averageSize = qRound(sum / visibleItems.count());
1149 qreal QQuickListViewPrivate::headerSize() const
1151 return header ? header->size() : 0.0;
1154 qreal QQuickListViewPrivate::footerSize() const
1156 return footer ? footer->size() : 0.0;
1159 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1164 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1166 return index == model->count()-1;
1169 void QQuickListViewPrivate::updateFooter()
1171 Q_Q(QQuickListView);
1172 bool created = false;
1174 QQuickItem *item = createComponentItem(footerComponent, true);
1178 footer = new FxListItemSG(item, q, true);
1182 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1183 if (visibleItems.count()) {
1184 qreal endPos = lastPosition();
1185 if (findLastVisibleIndex() == model->count()-1) {
1186 listItem->setPosition(endPos);
1188 qreal visiblePos = position() + q->height();
1189 if (endPos <= visiblePos || listItem->position() < endPos)
1190 listItem->setPosition(endPos);
1193 listItem->setPosition(visiblePos);
1197 emit q->footerItemChanged();
1200 void QQuickListViewPrivate::updateHeader()
1202 Q_Q(QQuickListView);
1203 bool created = false;
1205 QQuickItem *item = createComponentItem(headerComponent, true);
1209 header = new FxListItemSG(item, q, true);
1213 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1215 if (visibleItems.count()) {
1216 qreal startPos = originPosition();
1217 if (visibleIndex == 0) {
1218 listItem->setPosition(startPos - headerSize());
1220 if (position() <= startPos || listItem->position() > startPos - headerSize())
1221 listItem->setPosition(startPos - headerSize());
1224 listItem->setPosition(-headerSize());
1229 emit q->headerItemChanged();
1232 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1234 Q_Q(QQuickListView);
1235 QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1236 if (!q->isComponentComplete())
1238 if (item != contentItem && (!highlight || item != highlight->item)) {
1239 if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1240 || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1247 void QQuickListViewPrivate::fixupPosition()
1249 if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1250 || snapMode != QQuickListView::NoSnap)
1252 if (orient == QQuickListView::Vertical)
1258 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1260 if ((orient == QQuickListView::Horizontal && &data == &vData)
1261 || (orient == QQuickListView::Vertical && &data == &hData))
1264 correctFlick = false;
1265 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1266 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1268 qreal viewPos = isRightToLeft() ? -position()-size() : position();
1270 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1271 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1272 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1273 // if we've been dragged < averageSize/2 then bias towards the next item
1274 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1276 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1277 bias = averageSize/2;
1278 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1279 bias = -averageSize/2;
1280 if (isRightToLeft())
1282 tempPosition -= bias;
1284 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1285 if (!topItem && strictHighlightRange && currentItem) {
1286 // StrictlyEnforceRange always keeps an item in range
1288 topItem = currentItem;
1290 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1291 if (!bottomItem && strictHighlightRange && currentItem) {
1292 // StrictlyEnforceRange always keeps an item in range
1294 bottomItem = currentItem;
1297 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1298 if (topItem && (isInBounds || strictHighlightRange)) {
1299 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1300 pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1302 if (isRightToLeft())
1303 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1305 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1307 } else if (bottomItem && isInBounds) {
1308 if (isRightToLeft())
1309 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1311 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1313 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1317 qreal dist = qAbs(data.move + pos);
1319 timeline.reset(data.move);
1320 if (fixupMode != Immediate) {
1321 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1322 data.fixingUp = true;
1324 timeline.set(data.move, -pos);
1326 vTime = timeline.time();
1328 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1330 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1331 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1332 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1333 if (viewPos > pos - highlightRangeStart)
1334 viewPos = pos - highlightRangeStart;
1335 if (isRightToLeft())
1336 viewPos = -viewPos-size();
1338 timeline.reset(data.move);
1339 if (viewPos != position()) {
1340 if (fixupMode != Immediate) {
1341 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1342 data.fixingUp = true;
1344 timeline.set(data.move, -viewPos);
1347 vTime = timeline.time();
1349 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1351 data.inOvershoot = false;
1355 void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1356 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1358 Q_Q(QQuickListView);
1360 data.fixingUp = false;
1362 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1363 correctFlick = true;
1364 QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1367 qreal maxDistance = 0;
1368 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1370 // -ve velocity means list is moving up/left
1372 if (data.move.value() < minExtent) {
1373 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1374 // if we've been dragged < averageSize/2 then bias towards the next item
1375 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1376 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1377 if (isRightToLeft())
1379 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1380 maxDistance = qAbs(data.flickTarget - data.move.value());
1381 velocity = maxVelocity;
1383 maxDistance = qAbs(minExtent - data.move.value());
1386 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1387 data.flickTarget = minExtent;
1389 if (data.move.value() > maxExtent) {
1390 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1391 // if we've been dragged < averageSize/2 then bias towards the next item
1392 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1393 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1394 if (isRightToLeft())
1396 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1397 maxDistance = qAbs(data.flickTarget - data.move.value());
1398 velocity = -maxVelocity;
1400 maxDistance = qAbs(maxExtent - data.move.value());
1403 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1404 data.flickTarget = maxExtent;
1406 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1407 if (maxDistance > 0 || overShoot) {
1408 // These modes require the list to stop exactly on an item boundary.
1409 // The initial flick will estimate the boundary to stop on.
1410 // Since list items can have variable sizes, the boundary will be
1411 // reevaluated and adjusted as we approach the boundary.
1413 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1419 if (!hData.flicking && !vData.flicking) {
1420 // the initial flick - estimate boundary
1421 qreal accel = deceleration;
1423 overshootDist = 0.0;
1424 // + averageSize/4 to encourage moving at least one item in the flick direction
1425 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1426 if (maxDistance > 0)
1427 dist = qMin(dist, maxDistance);
1430 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1431 if (snapMode != QQuickListView::SnapOneItem) {
1432 qreal distTemp = isRightToLeft() ? -dist : dist;
1433 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1435 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1437 if (data.flickTarget >= minExtent) {
1438 overshootDist = overShootDistance(vSize);
1439 data.flickTarget += overshootDist;
1440 } else if (data.flickTarget <= maxExtent) {
1441 overshootDist = overShootDistance(vSize);
1442 data.flickTarget -= overshootDist;
1445 qreal adjDist = -data.flickTarget + data.move.value();
1446 if (qAbs(adjDist) > qAbs(dist)) {
1447 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1448 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1457 accel = v2 / (2.0f * qAbs(dist));
1458 } else if (overShoot) {
1459 data.flickTarget = data.move.value() - dist;
1460 if (data.flickTarget >= minExtent) {
1461 overshootDist = overShootDistance(vSize);
1462 data.flickTarget += overshootDist;
1463 } else if (data.flickTarget <= maxExtent) {
1464 overshootDist = overShootDistance(vSize);
1465 data.flickTarget -= overshootDist;
1468 timeline.reset(data.move);
1469 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1470 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1471 if (!hData.flicking && q->xflick()) {
1472 hData.flicking = true;
1473 emit q->flickingChanged();
1474 emit q->flickingHorizontallyChanged();
1475 emit q->flickStarted();
1477 if (!vData.flicking && q->yflick()) {
1478 vData.flicking = true;
1479 emit q->flickingChanged();
1480 emit q->flickingVerticallyChanged();
1481 emit q->flickStarted();
1483 correctFlick = true;
1485 // reevaluate the target boundary.
1486 qreal newtarget = data.flickTarget;
1487 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1488 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1489 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1490 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1492 if (velocity < 0 && newtarget <= maxExtent)
1493 newtarget = maxExtent - overshootDist;
1494 else if (velocity > 0 && newtarget >= minExtent)
1495 newtarget = minExtent + overshootDist;
1496 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1497 if (qAbs(velocity) < MinimumFlickVelocity)
1498 correctFlick = false;
1501 data.flickTarget = newtarget;
1502 qreal dist = -newtarget + data.move.value();
1503 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1504 correctFlick = false;
1505 timeline.reset(data.move);
1506 fixup(data, minExtent, maxExtent);
1509 timeline.reset(data.move);
1510 timeline.accelDistance(data.move, v, -dist);
1511 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1514 correctFlick = false;
1515 timeline.reset(data.move);
1516 fixup(data, minExtent, maxExtent);
1520 //----------------------------------------------------------------------------
1523 \qmlclass ListView QQuickListView
1524 \inqmlmodule QtQuick 2
1525 \ingroup qml-view-elements
1527 \brief The ListView item provides a list view of items provided by a model.
1529 A ListView displays data from models created from built-in QML elements like ListModel
1530 and XmlListModel, or custom model classes defined in C++ that inherit from
1533 A ListView has a \l model, which defines the data to be displayed, and
1534 a \l delegate, which defines how the data should be displayed. Items in a
1535 ListView are laid out horizontally or vertically. List views are inherently
1536 flickable because ListView inherits from \l Flickable.
1538 \section1 Example Usage
1540 The following example shows the definition of a simple list model defined
1541 in a file called \c ContactModel.qml:
1543 \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1545 Another component can display this model data in a ListView, like this:
1547 \snippet doc/src/snippets/declarative/listview/listview.qml import
1549 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1551 \image listview-simple.png
1553 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1554 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1555 the delegate is able to access the model's \c name and \c number data directly.
1557 An improved list view is shown below. The delegate is visually improved and is moved
1558 into a separate \c contactDelegate component.
1560 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1561 \image listview-highlight.png
1563 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1564 and \c focus is set to \c true to enable keyboard navigation for the list view.
1565 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1567 Delegates are instantiated as needed and may be destroyed at any time.
1568 State should \e never be stored in a delegate.
1570 ListView attaches a number of properties to the root item of the delegate, for example
1571 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1572 this attached property directly as \c ListView.isCurrentItem, while the child
1573 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1575 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1577 \note Views do not enable \e clip automatically. If the view
1578 is not clipped by another item or the screen, it will be necessary
1579 to set \e {clip: true} in order to have the out of view items clipped
1582 \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1584 QQuickListView::QQuickListView(QQuickItem *parent)
1585 : QQuickItemView(*(new QQuickListViewPrivate), parent)
1589 QQuickListView::~QQuickListView()
1594 \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1595 This attached property is true if this delegate is the current item; otherwise false.
1597 It is attached to each instance of the delegate.
1599 This property may be used to adjust the appearance of the current item, for example:
1601 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1605 \qmlattachedproperty ListView QtQuick2::ListView::view
1606 This attached property holds the view that manages this delegate instance.
1608 It is attached to each instance of the delegate.
1612 \qmlattachedproperty string QtQuick2::ListView::previousSection
1613 This attached property holds the section of the previous element.
1615 It is attached to each instance of the delegate.
1617 The section is evaluated using the \l {ListView::section.property}{section} properties.
1621 \qmlattachedproperty string QtQuick2::ListView::nextSection
1622 This attached property holds the section of the next element.
1624 It is attached to each instance of the delegate.
1626 The section is evaluated using the \l {ListView::section.property}{section} properties.
1630 \qmlattachedproperty string QtQuick2::ListView::section
1631 This attached property holds the section of this element.
1633 It is attached to each instance of the delegate.
1635 The section is evaluated using the \l {ListView::section.property}{section} properties.
1639 \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1640 This attached property holds whether the delegate may be destroyed.
1642 It is attached to each instance of the delegate.
1644 It is sometimes necessary to delay the destruction of an item
1645 until an animation completes.
1647 The example delegate below ensures that the animation completes before
1648 the item is removed from the list.
1650 \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1654 \qmlattachedsignal QtQuick2::ListView::onAdd()
1655 This attached handler is called immediately after an item is added to the view.
1659 \qmlattachedsignal QtQuick2::ListView::onRemove()
1660 This attached handler is called immediately before an item is removed from the view.
1664 \qmlproperty model QtQuick2::ListView::model
1665 This property holds the model providing data for the list.
1667 The model provides the set of data that is used to create the items
1668 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1669 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1670 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1672 \sa {qmlmodels}{Data Models}
1676 \qmlproperty Component QtQuick2::ListView::delegate
1678 The delegate provides a template defining each item instantiated by the view.
1679 The index is exposed as an accessible \c index property. Properties of the
1680 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1682 The number of elements in the delegate has a direct effect on the
1683 flicking performance of the view. If at all possible, place functionality
1684 that is not needed for the normal display of the delegate in a \l Loader which
1685 can load additional elements when needed.
1687 The ListView will lay out the items based on the size of the root item
1690 It is recommended that the delegate's size be a whole number to avoid sub-pixel
1693 \note Delegates are instantiated as needed and may be destroyed at any time.
1694 State should \e never be stored in a delegate.
1697 \qmlproperty int QtQuick2::ListView::currentIndex
1698 \qmlproperty Item QtQuick2::ListView::currentItem
1700 The \c currentIndex property holds the index of the current item, and
1701 \c currentItem holds the current item. Setting the currentIndex to -1
1702 will clear the highlight and set currentItem to null.
1704 If highlightFollowsCurrentItem is \c true, setting either of these
1705 properties will smoothly scroll the ListView so that the current
1706 item becomes visible.
1708 Note that the position of the current item
1709 may only be approximate until it becomes visible in the view.
1713 \qmlproperty Item QtQuick2::ListView::highlightItem
1715 This holds the highlight item created from the \l highlight component.
1717 The \c highlightItem is managed by the view unless
1718 \l highlightFollowsCurrentItem is set to false.
1720 \sa highlight, highlightFollowsCurrentItem
1724 \qmlproperty int QtQuick2::ListView::count
1725 This property holds the number of items in the view.
1729 \qmlproperty Component QtQuick2::ListView::highlight
1730 This property holds the component to use as the highlight.
1732 An instance of the highlight component is created for each list.
1733 The geometry of the resulting component instance is managed by the list
1734 so as to stay with the current item, unless the highlightFollowsCurrentItem
1737 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1741 \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1742 This property holds whether the highlight is managed by the view.
1744 If this property is true (the default value), the highlight is moved smoothly
1745 to follow the current item. Otherwise, the
1746 highlight is not moved by the view, and any movement must be implemented
1749 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1751 \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1753 Note that the highlight animation also affects the way that the view
1754 is scrolled. This is because the view moves to maintain the
1755 highlight within the preferred highlight range (or visible viewport).
1757 \sa highlight, highlightMoveSpeed
1759 //###Possibly rename these properties, since they are very useful even without a highlight?
1761 \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1762 \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1763 \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1765 These properties define the preferred range of the highlight (for the current item)
1766 within the view. The \c preferredHighlightBegin value must be less than the
1767 \c preferredHighlightEnd value.
1769 These properties affect the position of the current item when the list is scrolled.
1770 For example, if the currently selected item should stay in the middle of the
1771 list when the view is scrolled, set the \c preferredHighlightBegin and
1772 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1773 item would be. If the \c currentItem is changed programmatically, the list will
1774 automatically scroll so that the current item is in the middle of the view.
1775 Furthermore, the behavior of the current item index will occur whether or not a
1778 Valid values for \c highlightRangeMode are:
1781 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1782 However, the highlight can move outside of the range at the ends of the list or due
1783 to mouse interaction.
1784 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1785 The current item changes if a keyboard or mouse action would cause the highlight to move
1786 outside of the range.
1787 \o ListView.NoHighlightRange - this is the default value.
1790 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1792 Q_D(QQuickListView);
1793 if (d->autoHighlight != autoHighlight) {
1794 if (!autoHighlight) {
1795 if (d->highlightPosAnimator)
1796 d->highlightPosAnimator->stop();
1797 if (d->highlightSizeAnimator)
1798 d->highlightSizeAnimator->stop();
1800 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1805 \qmlproperty real QtQuick2::ListView::spacing
1807 This property holds the spacing between items.
1809 The default value is 0.
1811 qreal QQuickListView::spacing() const
1813 Q_D(const QQuickListView);
1817 void QQuickListView::setSpacing(qreal spacing)
1819 Q_D(QQuickListView);
1820 if (spacing != d->spacing) {
1821 d->spacing = spacing;
1822 d->forceLayout = true;
1824 emit spacingChanged();
1829 \qmlproperty enumeration QtQuick2::ListView::orientation
1830 This property holds the orientation of the list.
1835 \o ListView.Horizontal - Items are laid out horizontally
1836 \o ListView.Vertical (default) - Items are laid out vertically
1841 \o Horizontal orientation:
1842 \image ListViewHorizontal.png
1845 \o Vertical orientation:
1846 \image listview-highlight.png
1849 QQuickListView::Orientation QQuickListView::orientation() const
1851 Q_D(const QQuickListView);
1855 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
1857 Q_D(QQuickListView);
1858 if (d->orient != orientation) {
1859 d->orient = orientation;
1860 if (d->orient == Vertical) {
1861 setContentWidth(-1);
1862 setFlickableDirection(VerticalFlick);
1865 setContentHeight(-1);
1866 setFlickableDirection(HorizontalFlick);
1870 emit orientationChanged();
1875 \qmlproperty enumeration QtQuick2::ListView::layoutDirection
1876 This property holds the layout direction of the horizontal list.
1881 \o Qt.LeftToRight (default) - Items will be laid out from left to right.
1882 \o Qt.RightToLeft - Items will be laid out from right to let.
1885 \sa ListView::effectiveLayoutDirection
1890 \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
1891 This property holds the effective layout direction of the horizontal list.
1893 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1894 the visual layout direction of the horizontal list will be mirrored. However, the
1895 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
1897 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1901 \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
1902 This property holds whether the list wraps key navigation.
1904 If this is true, key navigation that would move the current item selection
1905 past the end of the list instead wraps around and moves the selection to
1906 the start of the list, and vice-versa.
1908 By default, key navigation is not wrapped.
1913 \qmlproperty int QtQuick2::ListView::cacheBuffer
1914 This property determines whether delegates are retained outside the
1915 visible area of the view.
1917 If this value is non-zero, the view may keep as many delegates
1918 instantiated as it can fit within the buffer specified. For example,
1919 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1920 set to 40, then up to 2 delegates above and 2 delegates below the visible
1921 area may be created/retained. The buffered delegates are created asynchronously,
1922 allowing creation to occur across multiple frames and reducing the
1923 likelihood of skipping frames. In order to improve painting performance
1924 delegates outside the visible area have their \l visible property set to
1925 false until they are moved into the visible area.
1927 Note that cacheBuffer is not a pixel buffer - it only maintains additional
1928 instantiated delegates.
1930 Setting this value can improve the smoothness of scrolling behavior at the expense
1931 of additional memory usage. It is not a substitute for creating efficient
1932 delegates; the fewer elements in a delegate, the faster a view can be
1938 \qmlproperty string QtQuick2::ListView::section.property
1939 \qmlproperty enumeration QtQuick2::ListView::section.criteria
1940 \qmlproperty Component QtQuick2::ListView::section.delegate
1941 \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
1943 These properties determine the expression to be evaluated and appearance
1944 of the section labels.
1946 \c section.property holds the name of the property that is the basis
1949 \c section.criteria holds the criteria for forming each section based on
1950 \c section.property. This value can be one of:
1953 \o ViewSection.FullString (default) - sections are created based on the
1954 \c section.property value.
1955 \o ViewSection.FirstCharacter - sections are created based on the first
1956 character of the \c section.property value (for example, 'A', 'B', 'C'
1957 sections, etc. for an address book)
1960 \c section.delegate holds the delegate component for each section.
1962 \c section.labelPositioning determines whether the current and/or
1963 next section labels stick to the start/end of the view, and whether
1964 the labels are shown inline. This value can be a combination of:
1967 \o ViewSection.InlineLabels - section labels are shown inline between
1968 the item delegates separating sections (default).
1969 \o ViewSection.CurrentLabelAtStart - the current section label sticks to the
1970 start of the view as it is moved.
1971 \o ViewSection.NextLabelAtEnd - the next section label (beyond all visible
1972 sections) sticks to the end of the view as it is moved. \note Enabling
1973 \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
1974 section, which has performance implications, especially for slower models.
1977 Each item in the list has attached properties named \c ListView.section,
1978 \c ListView.previousSection and \c ListView.nextSection.
1980 For example, here is a ListView that displays a list of animals, separated
1981 into sections. Each item in the ListView is placed in a different section
1982 depending on the "size" property of the model item. The \c sectionHeading
1983 delegate component provides the light blue bar that marks the beginning of
1987 \snippet examples/declarative/modelviews/listview/sections.qml 0
1989 \image qml-listview-sections-example.png
1991 \note Adding sections to a ListView does not automatically re-order the
1992 list items by the section criteria.
1993 If the model is not ordered by section, then it is possible that
1994 the sections created will not be unique; each boundary between
1995 differing sections will result in a section header being created
1996 even if that section exists elsewhere.
1998 \sa {declarative/modelviews/listview}{ListView examples}
2000 QQuickViewSection *QQuickListView::sectionCriteria()
2002 Q_D(QQuickListView);
2003 if (!d->sectionCriteria) {
2004 d->sectionCriteria = new QQuickViewSection(this);
2005 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2007 return d->sectionCriteria;
2011 \qmlproperty string QtQuick2::ListView::currentSection
2012 This property holds the section that is currently at the beginning of the view.
2014 QString QQuickListView::currentSection() const
2016 Q_D(const QQuickListView);
2017 return d->currentSection;
2021 \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
2022 \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2023 \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
2024 \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2026 These properties hold the move and resize animation speed of the highlight delegate.
2028 \l highlightFollowsCurrentItem must be true for these properties
2031 The default value for the speed properties is 400 pixels/second.
2032 The default value for the duration properties is -1, i.e. the
2033 highlight will take as much time as necessary to move at the set speed.
2035 These properties have the same characteristics as a SmoothedAnimation.
2037 \sa highlightFollowsCurrentItem
2039 qreal QQuickListView::highlightMoveSpeed() const
2041 Q_D(const QQuickListView);
2042 return d->highlightMoveSpeed;
2045 void QQuickListView::setHighlightMoveSpeed(qreal speed)
2047 Q_D(QQuickListView);
2048 if (d->highlightMoveSpeed != speed) {
2049 d->highlightMoveSpeed = speed;
2050 if (d->highlightPosAnimator)
2051 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2052 emit highlightMoveSpeedChanged();
2056 void QQuickListView::setHighlightMoveDuration(int duration)
2058 Q_D(QQuickListView);
2059 if (d->highlightMoveDuration != duration) {
2060 if (d->highlightPosAnimator)
2061 d->highlightPosAnimator->userDuration = duration;
2062 QQuickItemView::setHighlightMoveDuration(duration);
2066 qreal QQuickListView::highlightResizeSpeed() const
2068 Q_D(const QQuickListView);
2069 return d->highlightResizeSpeed;
2072 void QQuickListView::setHighlightResizeSpeed(qreal speed)
2074 Q_D(QQuickListView);
2075 if (d->highlightResizeSpeed != speed) {
2076 d->highlightResizeSpeed = speed;
2077 if (d->highlightSizeAnimator)
2078 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2079 emit highlightResizeSpeedChanged();
2083 int QQuickListView::highlightResizeDuration() const
2085 Q_D(const QQuickListView);
2086 return d->highlightResizeDuration;
2089 void QQuickListView::setHighlightResizeDuration(int duration)
2091 Q_D(QQuickListView);
2092 if (d->highlightResizeDuration != duration) {
2093 d->highlightResizeDuration = duration;
2094 if (d->highlightSizeAnimator)
2095 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2096 emit highlightResizeDurationChanged();
2101 \qmlproperty enumeration QtQuick2::ListView::snapMode
2103 This property determines how the view scrolling will settle following a drag or flick.
2104 The possible values are:
2107 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2108 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2110 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2111 visible item at the time the mouse button is released. This mode is particularly
2112 useful for moving one page at a time.
2115 \c snapMode does not affect the \l currentIndex. To update the
2116 \l currentIndex as the list is moved, set \l highlightRangeMode
2117 to \c ListView.StrictlyEnforceRange.
2119 \sa highlightRangeMode
2121 QQuickListView::SnapMode QQuickListView::snapMode() const
2123 Q_D(const QQuickListView);
2127 void QQuickListView::setSnapMode(SnapMode mode)
2129 Q_D(QQuickListView);
2130 if (d->snapMode != mode) {
2132 emit snapModeChanged();
2138 \qmlproperty Component QtQuick2::ListView::footer
2139 This property holds the component to use as the footer.
2141 An instance of the footer component is created for each view. The
2142 footer is positioned at the end of the view, after any items.
2149 \qmlproperty Component QtQuick2::ListView::header
2150 This property holds the component to use as the header.
2152 An instance of the header component is created for each view. The
2153 header is positioned at the beginning of the view, before any items.
2158 void QQuickListView::viewportMoved()
2160 Q_D(QQuickListView);
2161 QQuickItemView::viewportMoved();
2164 // Recursion can occur due to refill changing the content size.
2165 if (d->inViewportMoved)
2167 d->inViewportMoved = true;
2169 // Set visibility of items to eliminate cost of items outside the visible area.
2170 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2171 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2172 for (int i = 0; i < d->visibleItems.count(); ++i) {
2173 FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2174 item->item->setVisible(item->endPosition() >= from && item->position() <= to);
2178 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2179 else if (d->isRightToLeft())
2180 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2182 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2185 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2186 d->moveReason = QQuickListViewPrivate::Mouse;
2187 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2188 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2189 // reposition highlight
2190 qreal pos = d->highlight->position();
2191 qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
2192 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2193 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2194 if (pos < viewPos + d->highlightRangeStart)
2195 pos = viewPos + d->highlightRangeStart;
2196 if (pos != d->highlight->position()) {
2197 d->highlightPosAnimator->stop();
2198 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2200 d->updateHighlight();
2203 // update current index
2204 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2205 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2206 d->updateCurrent(snapItem->index);
2211 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2212 d->inFlickCorrection = true;
2213 // Near an end and it seems that the extent has changed?
2214 // Recalculate the flick so that we don't end up in an odd position.
2215 if (yflick() && !d->vData.inOvershoot) {
2216 if (d->vData.velocity > 0) {
2217 const qreal minY = minYExtent();
2218 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2219 && minY != d->vData.flickTarget)
2220 d->flickY(-d->vData.smoothVelocity.value());
2221 } else if (d->vData.velocity < 0) {
2222 const qreal maxY = maxYExtent();
2223 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2224 && maxY != d->vData.flickTarget)
2225 d->flickY(-d->vData.smoothVelocity.value());
2229 if (xflick() && !d->hData.inOvershoot) {
2230 if (d->hData.velocity > 0) {
2231 const qreal minX = minXExtent();
2232 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2233 && minX != d->hData.flickTarget)
2234 d->flickX(-d->hData.smoothVelocity.value());
2235 } else if (d->hData.velocity < 0) {
2236 const qreal maxX = maxXExtent();
2237 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2238 && maxX != d->hData.flickTarget)
2239 d->flickX(-d->hData.smoothVelocity.value());
2242 d->inFlickCorrection = false;
2244 if (d->sectionCriteria) {
2245 d->updateCurrentSection();
2246 d->updateStickySections();
2248 d->inViewportMoved = false;
2251 void QQuickListView::keyPressEvent(QKeyEvent *event)
2253 Q_D(QQuickListView);
2254 if (d->model && d->model->count() && d->interactive) {
2255 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2256 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2257 || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Up)) {
2258 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2259 decrementCurrentIndex();
2262 } else if (d->wrap) {
2266 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2267 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2268 || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Down)) {
2269 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2270 incrementCurrentIndex();
2273 } else if (d->wrap) {
2280 QQuickItemView::keyPressEvent(event);
2283 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2285 Q_D(QQuickListView);
2286 if (d->isRightToLeft() && d->orient == QQuickListView::Horizontal) {
2287 // maintain position relative to the right edge
2288 int dx = newGeometry.width() - oldGeometry.width();
2289 setContentX(contentX() - dx);
2291 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2296 \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2298 Increments the current index. The current index will wrap
2299 if keyNavigationWraps is true and it is currently at the end.
2300 This method has no effect if the \l count is zero.
2302 \bold Note: methods should only be called after the Component has completed.
2304 void QQuickListView::incrementCurrentIndex()
2306 Q_D(QQuickListView);
2307 int count = d->model ? d->model->count() : 0;
2308 if (count && (currentIndex() < count - 1 || d->wrap)) {
2309 d->moveReason = QQuickListViewPrivate::SetIndex;
2310 int index = currentIndex()+1;
2311 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2316 \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2318 Decrements the current index. The current index will wrap
2319 if keyNavigationWraps is true and it is currently at the beginning.
2320 This method has no effect if the \l count is zero.
2322 \bold Note: methods should only be called after the Component has completed.
2324 void QQuickListView::decrementCurrentIndex()
2326 Q_D(QQuickListView);
2327 int count = d->model ? d->model->count() : 0;
2328 if (count && (currentIndex() > 0 || d->wrap)) {
2329 d->moveReason = QQuickListViewPrivate::SetIndex;
2330 int index = currentIndex()-1;
2331 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2335 void QQuickListView::updateSections()
2337 Q_D(QQuickListView);
2338 if (isComponentComplete() && d->model) {
2339 QList<QByteArray> roles;
2340 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2341 roles << d->sectionCriteria->property().toUtf8();
2342 d->model->setWatchedRoles(roles);
2343 d->updateSections();
2345 d->forceLayout = true;
2351 bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult)
2353 int modelIndex = change.index;
2354 int count = change.count;
2357 qreal tempPos = isRightToLeft() ? -position()-size() : position();
2358 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2361 int i = visibleItems.count() - 1;
2362 while (i > 0 && visibleItems.at(i)->index == -1)
2364 if (i == 0 && visibleItems.first()->index == -1) {
2365 // there are no visible items except items marked for removal
2366 index = visibleItems.count();
2367 } else if (visibleItems.at(i)->index + 1 == modelIndex
2368 && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2369 // Special case of appending an item to the model.
2370 index = visibleItems.count();
2372 if (modelIndex < visibleIndex) {
2373 // Insert before visible items
2374 visibleIndex += count;
2375 for (int i = 0; i < visibleItems.count(); ++i) {
2376 FxViewItem *item = visibleItems.at(i);
2377 if (item->index != -1 && item->index >= modelIndex)
2378 item->index += count;
2385 // index can be the next item past the end of the visible items list (i.e. appended)
2387 if (visibleItems.count()) {
2388 pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2389 : visibleItems.last()->endPosition()+spacing;
2392 int prevAddedCount = insertResult->addedItems.count();
2393 if (firstVisible && pos < firstVisible->position()) {
2394 // Insert items before the visible item.
2395 int insertionIdx = index;
2397 int from = tempPos - buffer;
2399 for (i = count-1; i >= 0; --i) {
2400 if (pos > from && insertionIdx < visibleIndex) {
2401 // item won't be visible, just note the size for repositioning
2402 insertResult->sizeAddedBeforeVisible += averageSize + spacing;
2403 pos -= averageSize + spacing;
2405 // item is before first visible e.g. in cache buffer
2406 FxViewItem *item = 0;
2407 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
2408 if (item->index > modelIndex + i)
2409 insertResult->movedBackwards.append(item);
2410 item->index = modelIndex + i;
2413 item = createItem(modelIndex + i);
2417 visibleItems.insert(insertionIdx, item);
2418 if (!change.isMove()) {
2419 insertResult->addedItems.append(item);
2420 insertResult->sizeAddedBeforeVisible += item->size();
2422 pos -= item->size() + spacing;
2428 int to = buffer+tempPos+size();
2429 for (i = 0; i < count && pos <= to; ++i) {
2430 FxViewItem *item = 0;
2431 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
2432 if (item->index > modelIndex + i)
2433 insertResult->movedBackwards.append(item);
2434 item->index = modelIndex + i;
2437 item = createItem(modelIndex + i);
2441 visibleItems.insert(index, item);
2442 if (!change.isMove())
2443 insertResult->addedItems.append(item);
2444 pos += item->size() + spacing;
2449 for (; index < visibleItems.count(); ++index) {
2450 FxViewItem *item = visibleItems.at(index);
2451 if (item->index != -1)
2452 item->index += count;
2455 updateVisibleIndex();
2457 return insertResult->addedItems.count() > prevAddedCount;
2462 \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2464 Positions the view such that the \a index is at the position specified by
2468 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2469 \o ListView.Center - position item in the center of the view.
2470 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2471 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2472 bring the item into view.
2473 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
2474 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2477 If positioning the view at \a index would cause empty space to be displayed at
2478 the beginning or end of the view, the view will be positioned at the boundary.
2480 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2481 at a particular index. This is unreliable since removing items from the start
2482 of the list does not cause all other items to be repositioned, and because
2483 the actual start of the view can vary based on the size of the delegates.
2484 The correct way to bring an item into view is with \c positionViewAtIndex.
2486 \bold Note: methods should only be called after the Component has completed. To position
2487 the view at startup, this method should be called by Component.onCompleted. For
2488 example, to position the view at the end:
2491 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2496 \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
2497 \qmlmethod QtQuick2::ListView::positionViewAtEnd()
2499 Positions the view at the beginning or end, taking into account any header or footer.
2501 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2502 at a particular index. This is unreliable since removing items from the start
2503 of the list does not cause all other items to be repositioned, and because
2504 the actual start of the view can vary based on the size of the delegates.
2506 \bold Note: methods should only be called after the Component has completed. To position
2507 the view at startup, this method should be called by Component.onCompleted. For
2508 example, to position the view at the end on startup:
2511 Component.onCompleted: positionViewAtEnd()
2516 \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
2518 Returns the index of the visible item containing the point \a x, \a y in content
2519 coordinates. If there is no item at the point specified, or the item is
2520 not visible -1 is returned.
2522 If the item is outside the visible area, -1 is returned, regardless of
2523 whether an item will exist at that point when scrolled into view.
2525 \bold Note: methods should only be called after the Component has completed.
2528 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
2530 return new QQuickListViewAttached(obj);