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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativelistview_p.h"
44 #include "private/qdeclarativeflickable_p_p.h"
45 #include "private/qdeclarativevisualitemmodel_p.h"
47 #include "private/qdeclarativesmoothedanimation_p_p.h"
48 #include <qdeclarativeexpression.h>
49 #include <qdeclarativeengine.h>
50 #include <qdeclarativeguard_p.h>
51 #include <qdeclarativeinfo.h>
53 #include <qlistmodelinterface_p.h>
59 void QDeclarativeViewSection::setProperty(const QString &property)
61 if (property != m_property) {
62 m_property = property;
63 emit propertyChanged();
67 void QDeclarativeViewSection::setCriteria(QDeclarativeViewSection::SectionCriteria criteria)
69 if (criteria != m_criteria) {
70 m_criteria = criteria;
71 emit criteriaChanged();
75 void QDeclarativeViewSection::setDelegate(QDeclarativeComponent *delegate)
77 if (delegate != m_delegate) {
78 m_delegate = delegate;
79 emit delegateChanged();
83 QString QDeclarativeViewSection::sectionString(const QString &value)
85 if (m_criteria == FirstCharacter)
86 return value.isEmpty() ? QString() : value.at(0);
91 //----------------------------------------------------------------------------
96 FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) : item(i), section(0), view(v) {
97 attached = static_cast<QDeclarativeListViewAttached*>(qmlAttachedPropertiesObject<QDeclarativeListView>(item));
99 attached->setView(view);
102 qreal position() const {
104 if (view->orientation() == QDeclarativeListView::Vertical)
107 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
109 return itemPosition();
113 qreal itemPosition() const {
114 if (view->orientation() == QDeclarativeListView::Vertical)
117 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
121 return (view->orientation() == QDeclarativeListView::Vertical ? item->height()+section->height() : item->width()+section->width());
123 return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width());
125 qreal itemSize() const {
126 return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width());
128 qreal sectionSize() const {
130 return (view->orientation() == QDeclarativeListView::Vertical ? section->height() : section->width());
133 qreal endPosition() const {
134 if (view->orientation() == QDeclarativeListView::Vertical) {
135 return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
137 return (view->effectiveLayoutDirection() == Qt::RightToLeft
138 ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
139 : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
142 void setPosition(qreal pos) {
143 if (view->orientation() == QDeclarativeListView::Vertical) {
146 pos += section->height();
150 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
152 section->setX(-section->width()-pos);
153 pos += section->width();
155 item->setX(-item->width()-pos);
159 pos += section->width();
165 void setSize(qreal size) {
166 if (view->orientation() == QDeclarativeListView::Vertical)
167 item->setHeight(size);
169 item->setWidth(size);
171 bool contains(qreal x, qreal y) const {
172 return (x >= item->x() && x < item->x() + item->width() &&
173 y >= item->y() && y < item->y() + item->height());
176 QDeclarativeItem *item;
177 QDeclarativeItem *section;
178 QDeclarativeListView *view;
179 QDeclarativeListViewAttached *attached;
183 //----------------------------------------------------------------------------
185 class QDeclarativeListViewPrivate : public QDeclarativeFlickablePrivate
187 Q_DECLARE_PUBLIC(QDeclarativeListView)
190 QDeclarativeListViewPrivate()
191 : currentItem(0), orient(QDeclarativeListView::Vertical), layoutDirection(Qt::LeftToRight)
192 , visiblePos(0), visibleIndex(0)
193 , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
194 , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
195 , highlightRangeStartValid(false), highlightRangeEndValid(false)
196 , highlightComponent(0), highlight(0), trackedItem(0)
197 , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
198 , sectionCriteria(0), spacing(0.0)
199 , highlightMoveSpeed(400), highlightMoveDuration(-1)
200 , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarativeListView::NoHighlightRange)
201 , snapMode(QDeclarativeListView::NoSnap), overshootDist(0.0)
202 , footerComponent(0), footer(0), headerComponent(0), header(0)
203 , bufferMode(BufferBefore | BufferAfter)
204 , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
205 , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
206 , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
207 , inViewportMoved(false)
208 , minExtentDirty(true), maxExtentDirty(true)
213 FxListItem *createItem(int modelIndex);
214 void releaseItem(FxListItem *item);
216 FxListItem *visibleItem(int modelIndex) const {
217 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
218 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
219 FxListItem *item = visibleItems.at(i);
220 if (item->index == modelIndex)
227 FxListItem *firstVisibleItem() const {
228 const qreal pos = isRightToLeft() ? -position()-size() : position();
229 for (int i = 0; i < visibleItems.count(); ++i) {
230 FxListItem *item = visibleItems.at(i);
231 if (item->index != -1 && item->endPosition() > pos)
234 return visibleItems.count() ? visibleItems.first() : 0;
237 FxListItem *nextVisibleItem() const {
238 const qreal pos = isRightToLeft() ? -position()-size() : position();
239 bool foundFirst = false;
240 for (int i = 0; i < visibleItems.count(); ++i) {
241 FxListItem *item = visibleItems.at(i);
242 if (item->index != -1) {
245 else if (item->position() < pos && item->endPosition() > pos)
252 // Returns the item before modelIndex, if created.
253 // May return an item marked for removal.
254 FxListItem *itemBefore(int modelIndex) const {
255 if (modelIndex < visibleIndex)
259 while (idx < visibleItems.count()) {
260 FxListItem *item = visibleItems.at(idx);
261 if (item->index != -1)
262 lastIndex = item->index;
263 if (item->index == modelIndex)
264 return visibleItems.at(idx-1);
267 if (lastIndex == modelIndex-1)
268 return visibleItems.last();
273 Q_Q(QDeclarativeListView);
274 if (q->isComponentComplete()) {
277 q->scene()->removeItem(header->item);
278 header->item->deleteLater();
284 q->scene()->removeItem(footer->item);
285 footer->item->deleteLater();
294 updateCurrent(currentIndex);
298 void mirrorChange() {
299 Q_Q(QDeclarativeListView);
301 emit q->effectiveLayoutDirectionChanged();
304 bool isRightToLeft() const {
305 Q_Q(const QDeclarativeListView);
306 return orient == QDeclarativeListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
309 qreal position() const {
310 Q_Q(const QDeclarativeListView);
311 return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX();
314 void setPosition(qreal pos) {
315 Q_Q(QDeclarativeListView);
316 if (orient == QDeclarativeListView::Vertical) {
317 q->QDeclarativeFlickable::setContentY(pos);
320 q->QDeclarativeFlickable::setContentX(-pos-size());
322 q->QDeclarativeFlickable::setContentX(pos);
326 Q_Q(const QDeclarativeListView);
327 return orient == QDeclarativeListView::Vertical ? q->height() : q->width();
330 qreal originPosition() const {
332 if (!visibleItems.isEmpty()) {
333 pos = (*visibleItems.constBegin())->position();
334 if (visibleIndex > 0)
335 pos -= visibleIndex * (averageSize + spacing);
340 qreal lastPosition() const {
342 if (!visibleItems.isEmpty()) {
343 int invisibleCount = visibleItems.count() - visibleIndex;
344 for (int i = visibleItems.count()-1; i >= 0; --i) {
345 if (visibleItems.at(i)->index != -1) {
346 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
350 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
351 } else if (model && model->count()) {
352 pos = model->count() * averageSize + (model->count()-1) * spacing;
357 qreal startPosition() const {
358 return isRightToLeft() ? -lastPosition()-1 : originPosition();
361 qreal endPosition() const {
362 return isRightToLeft() ? -originPosition()-1 : lastPosition();
365 qreal positionAt(int modelIndex) const {
366 if (FxListItem *item = visibleItem(modelIndex))
367 return item->position();
368 if (!visibleItems.isEmpty()) {
369 if (modelIndex < visibleIndex) {
370 int count = visibleIndex - modelIndex;
372 if (modelIndex == currentIndex && currentItem) {
373 cs = currentItem->size() + spacing;
376 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
378 int idx = visibleItems.count() - 1;
379 while (idx >= 0 && visibleItems.at(idx)->index == -1)
384 idx = visibleItems.at(idx)->index;
385 int count = modelIndex - idx - 1;
387 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
393 qreal endPositionAt(int modelIndex) const {
394 if (FxListItem *item = visibleItem(modelIndex))
395 return item->endPosition();
396 if (!visibleItems.isEmpty()) {
397 if (modelIndex < visibleIndex) {
398 int count = visibleIndex - modelIndex;
399 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
401 int idx = visibleItems.count() - 1;
402 while (idx >= 0 && visibleItems.at(idx)->index == -1)
407 idx = visibleItems.at(idx)->index;
408 int count = modelIndex - idx - 1;
409 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
415 QString sectionAt(int modelIndex) {
416 if (FxListItem *item = visibleItem(modelIndex))
417 return item->attached->section();
420 if (sectionCriteria) {
421 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
422 section = sectionCriteria->sectionString(propValue);
428 bool isValid() const {
429 return model && model->count() && model->isValid();
432 qreal snapPosAt(qreal pos) {
433 if (FxListItem *snapItem = snapItemAt(pos))
434 return snapItem->position();
435 if (visibleItems.count()) {
436 qreal firstPos = visibleItems.first()->position();
437 qreal endPos = visibleItems.last()->position();
438 if (pos < firstPos) {
439 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
440 } else if (pos > endPos)
441 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
443 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
446 FxListItem *snapItemAt(qreal pos) {
447 FxListItem *snapItem = 0;
448 for (int i = 0; i < visibleItems.count(); ++i) {
449 FxListItem *item = visibleItems[i];
450 if (item->index == -1)
452 qreal itemTop = item->position();
453 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
455 if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
461 int lastVisibleIndex() const {
463 for (int i = visibleItems.count()-1; i >= 0; --i) {
464 FxListItem *listItem = visibleItems.at(i);
465 if (listItem->index != -1) {
466 lastIndex = listItem->index;
473 // map a model index to visibleItems index.
474 int mapFromModel(int modelIndex) const {
475 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
477 for (int i = 0; i < visibleItems.count(); ++i) {
478 FxListItem *listItem = visibleItems.at(i);
479 if (listItem->index == modelIndex)
481 if (listItem->index > modelIndex)
484 return -1; // Not in visibleList
487 void updateViewport() {
488 Q_Q(QDeclarativeListView);
489 if (orient == QDeclarativeListView::Vertical) {
490 q->setContentHeight(endPosition() - startPosition() + 1);
492 q->setContentWidth(endPosition() - startPosition() + 1);
496 void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
497 Q_Q(QDeclarativeListView);
498 QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
499 if (!q->isComponentComplete())
501 if (item != contentItem && (!highlight || item != highlight->item)) {
502 if ((orient == QDeclarativeListView::Vertical && newGeometry.height() != oldGeometry.height())
503 || (orient == QDeclarativeListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
507 if ((header && header->item == item) || (footer && footer->item == item)) {
513 if (currentItem && currentItem->item == item)
515 if (trackedItem && trackedItem->item == item)
516 q->trackedPositionChanged();
519 // for debugging only
520 void checkVisible() const {
522 for (int i = 0; i < visibleItems.count(); ++i) {
523 FxListItem *listItem = visibleItems.at(i);
524 if (listItem->index == -1) {
526 } else if (listItem->index != visibleIndex + i - skip) {
527 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
532 void refill(qreal from, qreal to, bool doBuffer = false);
533 void scheduleLayout();
535 void updateUnrequestedIndexes();
536 void updateUnrequestedPositions();
537 void updateTrackedItem();
538 void createHighlight();
539 void updateHighlight();
540 void createSection(FxListItem *);
541 void updateSections();
542 void updateCurrentSection();
543 void updateCurrent(int);
544 void updateAverage();
547 void fixupPosition();
548 void positionViewAtIndex(int index, int mode);
549 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
550 virtual void flick(QDeclarativeFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
551 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
553 QDeclarativeGuard<QDeclarativeVisualModel> model;
554 QVariant modelVariant;
555 QList<FxListItem*> visibleItems;
556 QHash<QDeclarativeItem*,int> unrequestedItems;
557 FxListItem *currentItem;
558 QDeclarativeListView::Orientation orient;
559 Qt::LayoutDirection layoutDirection;
566 qreal highlightRangeStart;
567 qreal highlightRangeEnd;
568 bool highlightRangeStartValid;
569 bool highlightRangeEndValid;
570 QDeclarativeComponent *highlightComponent;
571 FxListItem *highlight;
572 FxListItem *trackedItem;
573 enum MovementReason { Other, SetIndex, Mouse };
574 MovementReason moveReason;
576 QSmoothedAnimation *highlightPosAnimator;
577 QSmoothedAnimation *highlightSizeAnimator;
578 QDeclarativeViewSection *sectionCriteria;
579 QString currentSection;
580 static const int sectionCacheSize = 4;
581 QDeclarativeItem *sectionCache[sectionCacheSize];
583 qreal highlightMoveSpeed;
584 int highlightMoveDuration;
585 qreal highlightResizeSpeed;
586 int highlightResizeDuration;
587 QDeclarativeListView::HighlightRangeMode highlightRange;
588 QDeclarativeListView::SnapMode snapMode;
590 QDeclarativeComponent *footerComponent;
592 QDeclarativeComponent *headerComponent;
594 enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
596 mutable qreal minExtent;
597 mutable qreal maxExtent;
601 bool autoHighlight : 1;
602 bool haveHighlightRange : 1;
603 bool correctFlick : 1;
604 bool inFlickCorrection : 1;
605 bool lazyRelease : 1;
606 bool deferredRelease : 1;
607 bool layoutScheduled : 1;
608 bool currentIndexCleared : 1;
609 bool inViewportMoved : 1;
610 mutable bool minExtentDirty : 1;
611 mutable bool maxExtentDirty : 1;
614 void QDeclarativeListViewPrivate::init()
616 Q_Q(QDeclarativeListView);
617 q->setFlag(QGraphicsItem::ItemIsFocusScope);
618 addItemChangeListener(this, Geometry);
619 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
620 q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick);
621 ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize);
624 void QDeclarativeListViewPrivate::clear()
627 for (int i = 0; i < visibleItems.count(); ++i)
628 releaseItem(visibleItems.at(i));
629 visibleItems.clear();
630 for (int i = 0; i < sectionCacheSize; ++i) {
631 delete sectionCache[i];
634 visiblePos = header ? header->size() : 0;
636 releaseItem(currentItem);
640 minExtentDirty = true;
641 maxExtentDirty = true;
645 FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex)
647 Q_Q(QDeclarativeListView);
649 requestedIndex = modelIndex;
650 FxListItem *listItem = 0;
651 if (QDeclarativeItem *item = model->item(modelIndex, false)) {
652 listItem = new FxListItem(item, q);
653 listItem->index = modelIndex;
654 // initialise attached properties
655 if (sectionCriteria) {
656 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
657 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
658 if (modelIndex > 0) {
659 if (FxListItem *item = itemBefore(modelIndex))
660 listItem->attached->m_prevSection = item->attached->section();
662 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
664 if (modelIndex < model->count()-1) {
665 if (FxListItem *item = visibleItem(modelIndex+1))
666 listItem->attached->m_nextSection = item->attached->section();
668 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
671 if (model->completePending()) {
673 listItem->item->setZValue(1);
674 listItem->item->setParentItem(q->contentItem());
675 model->completeItem();
677 listItem->item->setParentItem(q->contentItem());
679 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
680 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
681 if (sectionCriteria && sectionCriteria->delegate()) {
682 if (listItem->attached->m_prevSection != listItem->attached->m_section)
683 createSection(listItem);
685 unrequestedItems.remove(listItem->item);
692 void QDeclarativeListViewPrivate::releaseItem(FxListItem *item)
694 Q_Q(QDeclarativeListView);
697 if (trackedItem == item)
699 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item->item));
700 itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
701 if (model->release(item->item) == 0) {
702 // item was not destroyed, and we no longer reference it.
703 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
708 if (!sectionCache[i]) {
709 sectionCache[i] = item->section;
710 sectionCache[i]->setVisible(false);
715 } while (i < sectionCacheSize);
716 delete item->section;
721 void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
723 Q_Q(QDeclarativeListView);
724 if (!isValid() || !q->isComponentComplete())
726 itemCount = model->count();
727 qreal bufferFrom = from - buffer;
728 qreal bufferTo = to + buffer;
729 qreal fillFrom = from;
731 if (doBuffer && (bufferMode & BufferAfter))
733 if (doBuffer && (bufferMode & BufferBefore))
734 fillFrom = bufferFrom;
736 int modelIndex = visibleIndex;
737 qreal itemEnd = visiblePos-1;
738 if (!visibleItems.isEmpty()) {
739 visiblePos = (*visibleItems.constBegin())->position();
740 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
741 int i = visibleItems.count() - 1;
742 while (i > 0 && visibleItems.at(i)->index == -1)
744 if (visibleItems.at(i)->index != -1)
745 modelIndex = visibleItems.at(i)->index + 1;
748 if (visibleItems.count() && (fillFrom > itemEnd+averageSize+spacing
749 || fillTo < visiblePos - averageSize - spacing)) {
750 // We've jumped more than a page. Estimate which items are now
751 // visible and fill from there.
752 int count = (fillFrom - itemEnd) / (averageSize + spacing);
753 for (int i = 0; i < visibleItems.count(); ++i)
754 releaseItem(visibleItems.at(i));
755 visibleItems.clear();
757 if (modelIndex >= model->count()) {
758 count -= modelIndex - model->count() + 1;
759 modelIndex = model->count() - 1;
760 } else if (modelIndex < 0) {
764 visibleIndex = modelIndex;
765 visiblePos = itemEnd + count * (averageSize + spacing) + 1;
766 itemEnd = visiblePos-1;
769 bool changed = false;
770 FxListItem *item = 0;
771 qreal pos = itemEnd + 1;
772 while (modelIndex < model->count() && pos <= fillTo) {
773 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
774 if (!(item = createItem(modelIndex)))
776 item->setPosition(pos);
777 pos += item->size() + spacing;
778 visibleItems.append(item);
781 if (doBuffer) // never buffer more than one item per frame
784 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
785 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
786 if (!(item = createItem(visibleIndex-1)))
789 visiblePos -= item->size() + spacing;
790 item->setPosition(visiblePos);
791 visibleItems.prepend(item);
793 if (doBuffer) // never buffer more than one item per frame
797 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
798 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
799 if (item->attached->delayRemove())
801 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
802 if (item->index != -1)
804 visibleItems.removeFirst();
808 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
809 if (item->attached->delayRemove())
811 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
812 visibleItems.removeLast();
816 deferredRelease = false;
818 deferredRelease = true;
821 minExtentDirty = true;
822 maxExtentDirty = true;
823 if (visibleItems.count())
824 visiblePos = (*visibleItems.constBegin())->position();
826 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
827 currentItem->setPosition(positionAt(currentIndex));
832 updateCurrentSection();
838 updateUnrequestedPositions();
839 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
840 refill(from, to, true);
845 void QDeclarativeListViewPrivate::scheduleLayout()
847 Q_Q(QDeclarativeListView);
848 if (!layoutScheduled) {
849 layoutScheduled = true;
850 QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority);
854 void QDeclarativeListViewPrivate::layout()
856 Q_Q(QDeclarativeListView);
857 layoutScheduled = false;
858 if (!isValid() && !visibleItems.count()) {
863 if (!visibleItems.isEmpty()) {
864 bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
865 qreal sum = visibleItems.first()->size();
866 qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
867 for (int i=1; i < visibleItems.count(); ++i) {
868 FxListItem *item = visibleItems.at(i);
869 item->setPosition(pos);
870 pos += item->size() + spacing;
872 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
874 averageSize = qRound(sum / visibleItems.count());
875 // move current item if it is not a visible item.
876 if (currentIndex >= 0 && currentItem && !fixedCurrent)
877 currentItem->setPosition(positionAt(currentIndex));
880 minExtentDirty = true;
881 maxExtentDirty = true;
883 if (!q->isMoving() && !q->isFlicking()) {
894 void QDeclarativeListViewPrivate::updateUnrequestedIndexes()
896 Q_Q(QDeclarativeListView);
897 QHash<QDeclarativeItem*,int>::iterator it;
898 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
899 *it = model->indexOf(it.key(), q);
902 void QDeclarativeListViewPrivate::updateUnrequestedPositions()
904 Q_Q(QDeclarativeListView);
905 if (unrequestedItems.count()) {
906 qreal pos = position();
907 QHash<QDeclarativeItem*,int>::const_iterator it;
908 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
909 QDeclarativeItem *item = it.key();
910 if (orient == QDeclarativeListView::Vertical) {
911 if (item->y() + item->height() > pos && item->y() < pos + q->height())
912 item->setY(positionAt(*it));
914 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
916 item->setX(-positionAt(*it)-item->width());
918 item->setX(positionAt(*it));
925 void QDeclarativeListViewPrivate::updateTrackedItem()
927 Q_Q(QDeclarativeListView);
928 FxListItem *item = currentItem;
933 q->trackedPositionChanged();
936 void QDeclarativeListViewPrivate::createHighlight()
938 Q_Q(QDeclarativeListView);
939 bool changed = false;
941 if (trackedItem == highlight)
943 delete highlight->item;
946 delete highlightPosAnimator;
947 delete highlightSizeAnimator;
948 highlightPosAnimator = 0;
949 highlightSizeAnimator = 0;
954 QDeclarativeItem *item = 0;
955 if (highlightComponent) {
956 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
957 QObject *nobj = highlightComponent->create(highlightContext);
959 QDeclarative_setParent_noEvent(highlightContext, nobj);
960 item = qobject_cast<QDeclarativeItem *>(nobj);
964 delete highlightContext;
967 item = new QDeclarativeItem;
970 QDeclarative_setParent_noEvent(item, q->contentItem());
971 item->setParentItem(q->contentItem());
972 highlight = new FxListItem(item, q);
973 if (currentItem && autoHighlight) {
974 if (orient == QDeclarativeListView::Vertical) {
975 highlight->item->setHeight(currentItem->item->height());
977 highlight->item->setWidth(currentItem->item->width());
979 highlight->setPosition(currentItem->itemPosition());
981 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
982 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
983 const QLatin1String posProp(orient == QDeclarativeListView::Vertical ? "y" : "x");
984 highlightPosAnimator = new QSmoothedAnimation(q);
985 highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
986 highlightPosAnimator->velocity = highlightMoveSpeed;
987 highlightPosAnimator->userDuration = highlightMoveDuration;
988 const QLatin1String sizeProp(orient == QDeclarativeListView::Vertical ? "height" : "width");
989 highlightSizeAnimator = new QSmoothedAnimation(q);
990 highlightSizeAnimator->velocity = highlightResizeSpeed;
991 highlightSizeAnimator->userDuration = highlightResizeDuration;
992 highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
994 highlightPosAnimator->restart();
995 highlightSizeAnimator->restart();
1001 emit q->highlightItemChanged();
1004 void QDeclarativeListViewPrivate::updateHighlight()
1006 if ((!currentItem && highlight) || (currentItem && !highlight))
1008 if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
1009 // auto-update highlight
1010 highlightPosAnimator->to = isRightToLeft()
1011 ? -currentItem->itemPosition()-currentItem->itemSize()
1012 : currentItem->itemPosition();
1013 highlightSizeAnimator->to = currentItem->itemSize();
1014 if (orient == QDeclarativeListView::Vertical) {
1015 if (highlight->item->width() == 0)
1016 highlight->item->setWidth(currentItem->item->width());
1018 if (highlight->item->height() == 0)
1019 highlight->item->setHeight(currentItem->item->height());
1021 highlightPosAnimator->restart();
1022 highlightSizeAnimator->restart();
1024 updateTrackedItem();
1027 void QDeclarativeListViewPrivate::createSection(FxListItem *listItem)
1029 Q_Q(QDeclarativeListView);
1030 if (!sectionCriteria || !sectionCriteria->delegate())
1032 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
1033 if (!listItem->section) {
1034 qreal pos = listItem->position();
1035 int i = sectionCacheSize-1;
1036 while (i >= 0 && !sectionCache[i])
1039 listItem->section = sectionCache[i];
1040 sectionCache[i] = 0;
1041 listItem->section->setVisible(true);
1042 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1043 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1045 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1046 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1047 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
1049 QDeclarative_setParent_noEvent(context, nobj);
1050 listItem->section = qobject_cast<QDeclarativeItem *>(nobj);
1051 if (!listItem->section) {
1054 listItem->section->setZValue(1);
1055 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
1056 listItem->section->setParentItem(q->contentItem());
1061 sectionCriteria->delegate()->completeCreate();
1063 listItem->setPosition(pos);
1065 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1066 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1068 } else if (listItem->section) {
1069 qreal pos = listItem->position();
1072 if (!sectionCache[i]) {
1073 sectionCache[i] = listItem->section;
1074 sectionCache[i]->setVisible(false);
1075 listItem->section = 0;
1079 } while (i < sectionCacheSize);
1080 delete listItem->section;
1081 listItem->section = 0;
1082 listItem->setPosition(pos);
1086 void QDeclarativeListViewPrivate::updateSections()
1088 if (sectionCriteria && !visibleItems.isEmpty()) {
1089 QString prevSection;
1090 if (visibleIndex > 0)
1091 prevSection = sectionAt(visibleIndex-1);
1092 QDeclarativeListViewAttached *prevAtt = 0;
1094 for (int i = 0; i < visibleItems.count(); ++i) {
1095 QDeclarativeListViewAttached *attached = visibleItems.at(i)->attached;
1096 attached->setPrevSection(prevSection);
1097 if (visibleItems.at(i)->index != -1) {
1098 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1099 attached->setSection(sectionCriteria->sectionString(propValue));
1100 idx = visibleItems.at(i)->index;
1102 createSection(visibleItems.at(i));
1104 prevAtt->setNextSection(attached->section());
1105 prevSection = attached->section();
1109 if (idx > 0 && idx < model->count()-1)
1110 prevAtt->setNextSection(sectionAt(idx+1));
1112 prevAtt->setNextSection(QString());
1117 void QDeclarativeListViewPrivate::updateCurrentSection()
1119 Q_Q(QDeclarativeListView);
1120 if (!sectionCriteria || visibleItems.isEmpty()) {
1121 if (!currentSection.isEmpty()) {
1122 currentSection.clear();
1123 emit q->currentSectionChanged();
1128 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1131 QString newSection = currentSection;
1132 if (index < visibleItems.count())
1133 newSection = visibleItems.at(index)->attached->section();
1135 newSection = visibleItems.first()->attached->section();
1136 if (newSection != currentSection) {
1137 currentSection = newSection;
1138 emit q->currentSectionChanged();
1142 void QDeclarativeListViewPrivate::updateCurrent(int modelIndex)
1144 Q_Q(QDeclarativeListView);
1145 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1147 currentItem->attached->setIsCurrentItem(false);
1148 releaseItem(currentItem);
1150 currentIndex = modelIndex;
1151 emit q->currentIndexChanged();
1153 } else if (currentIndex != modelIndex) {
1154 currentIndex = modelIndex;
1155 emit q->currentIndexChanged();
1160 if (currentItem && currentIndex == modelIndex) {
1164 FxListItem *oldCurrentItem = currentItem;
1165 currentIndex = modelIndex;
1166 currentItem = createItem(modelIndex);
1167 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1168 oldCurrentItem->attached->setIsCurrentItem(false);
1170 if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1171 // We can calculate exact postion in this case
1172 currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1174 // Create current item now and position as best we can.
1175 // Its position will be corrected when it becomes visible.
1176 currentItem->setPosition(positionAt(modelIndex));
1178 currentItem->item->setFocus(true);
1179 currentItem->attached->setIsCurrentItem(true);
1180 // Avoid showing section delegate twice. We still need the section heading so that
1181 // currentItem positioning works correctly.
1182 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1183 if (currentItem->section)
1184 currentItem->section->setVisible(false);
1185 if (visibleItems.isEmpty())
1186 averageSize = currentItem->size();
1189 emit q->currentIndexChanged();
1190 // Release the old current item
1191 releaseItem(oldCurrentItem);
1194 void QDeclarativeListViewPrivate::updateAverage()
1196 if (!visibleItems.count())
1199 for (int i = 0; i < visibleItems.count(); ++i)
1200 sum += visibleItems.at(i)->size();
1201 averageSize = qRound(sum / visibleItems.count());
1204 void QDeclarativeListViewPrivate::updateFooter()
1206 Q_Q(QDeclarativeListView);
1207 if (!footer && footerComponent) {
1208 QDeclarativeItem *item = 0;
1209 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1210 QObject *nobj = footerComponent->create(context);
1212 QDeclarative_setParent_noEvent(context, nobj);
1213 item = qobject_cast<QDeclarativeItem *>(nobj);
1220 QDeclarative_setParent_noEvent(item, q->contentItem());
1221 item->setParentItem(q->contentItem());
1223 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1224 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1225 footer = new FxListItem(item, q);
1229 if (visibleItems.count()) {
1230 qreal endPos = lastPosition() + 1;
1231 if (lastVisibleIndex() == model->count()-1) {
1232 footer->setPosition(endPos);
1234 qreal visiblePos = position() + q->height();
1235 if (endPos <= visiblePos || footer->position() < endPos)
1236 footer->setPosition(endPos);
1239 footer->setPosition(visiblePos);
1244 void QDeclarativeListViewPrivate::updateHeader()
1246 Q_Q(QDeclarativeListView);
1247 if (!header && headerComponent) {
1248 QDeclarativeItem *item = 0;
1249 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1250 QObject *nobj = headerComponent->create(context);
1252 QDeclarative_setParent_noEvent(context, nobj);
1253 item = qobject_cast<QDeclarativeItem *>(nobj);
1260 QDeclarative_setParent_noEvent(item, q->contentItem());
1261 item->setParentItem(q->contentItem());
1263 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1264 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1265 header = new FxListItem(item, q);
1269 if (visibleItems.count()) {
1270 qreal startPos = originPosition();
1271 if (visibleIndex == 0) {
1272 header->setPosition(startPos - header->size());
1274 if (position() <= startPos || header->position() > startPos - header->size())
1275 header->setPosition(startPos - header->size());
1279 visiblePos = header->size();
1280 header->setPosition(0);
1285 void QDeclarativeListViewPrivate::fixupPosition()
1287 if ((haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange)
1288 || snapMode != QDeclarativeListView::NoSnap)
1290 if (orient == QDeclarativeListView::Vertical)
1296 void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1298 if ((orient == QDeclarativeListView::Horizontal && &data == &vData)
1299 || (orient == QDeclarativeListView::Vertical && &data == &hData))
1302 correctFlick = false;
1303 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1305 qreal highlightStart;
1308 if (isRightToLeft()) {
1309 // Handle Right-To-Left exceptions
1310 viewPos = -position()-size();
1311 highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1312 highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1314 viewPos = position();
1315 highlightStart = highlightRangeStart;
1316 highlightEnd = highlightRangeEnd;
1319 if (currentItem && haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange
1320 && moveReason != QDeclarativeListViewPrivate::SetIndex) {
1322 qreal pos = currentItem->itemPosition();
1323 if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1324 viewPos = pos + currentItem->itemSize() - highlightEnd;
1325 if (viewPos > pos - highlightStart)
1326 viewPos = pos - highlightStart;
1327 if (isRightToLeft())
1328 viewPos = -viewPos-size();
1330 timeline.reset(data.move);
1331 if (viewPos != position()) {
1332 if (fixupMode != Immediate) {
1333 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1334 data.fixingUp = true;
1336 timeline.set(data.move, -viewPos);
1339 vTime = timeline.time();
1340 } else if (snapMode != QDeclarativeListView::NoSnap && moveReason != QDeclarativeListViewPrivate::SetIndex) {
1341 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1342 FxListItem *topItem = snapItemAt(tempPosition+highlightStart);
1343 FxListItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1345 bool isInBounds = -position() > maxExtent && -position() < minExtent;
1346 if (topItem && isInBounds) {
1347 if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2) {
1348 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1350 if (isRightToLeft())
1351 pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1353 pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1355 } else if (bottomItem && isInBounds) {
1356 if (isRightToLeft())
1357 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1359 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1361 QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1365 qreal dist = qAbs(data.move + pos);
1367 timeline.reset(data.move);
1368 if (fixupMode != Immediate) {
1369 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1370 data.fixingUp = true;
1372 timeline.set(data.move, -pos);
1374 vTime = timeline.time();
1377 QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1379 data.inOvershoot = false;
1383 void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1384 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1386 Q_Q(QDeclarativeListView);
1388 data.fixingUp = false;
1390 if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) {
1391 correctFlick = true;
1392 QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1395 qreal maxDistance = 0;
1396 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1397 // -ve velocity means list is moving up/left
1399 if (data.move.value() < minExtent) {
1400 if (snapMode == QDeclarativeListView::SnapOneItem) {
1401 if (FxListItem *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1402 maxDistance = qAbs(item->position() + dataValue);
1404 maxDistance = qAbs(minExtent - data.move.value());
1407 if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1408 data.flickTarget = minExtent;
1410 if (data.move.value() > maxExtent) {
1411 if (snapMode == QDeclarativeListView::SnapOneItem) {
1412 if (FxListItem *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1413 maxDistance = qAbs(item->position() + dataValue);
1415 maxDistance = qAbs(maxExtent - data.move.value());
1418 if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1419 data.flickTarget = maxExtent;
1422 bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
1423 qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1425 if (maxDistance > 0 || overShoot) {
1426 // These modes require the list to stop exactly on an item boundary.
1427 // The initial flick will estimate the boundary to stop on.
1428 // Since list items can have variable sizes, the boundary will be
1429 // reevaluated and adjusted as we approach the boundary.
1431 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1437 if (!flickingHorizontally && !flickingVertically) {
1438 // the initial flick - estimate boundary
1439 qreal accel = deceleration;
1441 overshootDist = 0.0;
1442 // + averageSize/4 to encourage moving at least one item in the flick direction
1443 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1444 if (maxDistance > 0)
1445 dist = qMin(dist, maxDistance);
1448 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) {
1449 qreal distTemp = isRightToLeft() ? -dist : dist;
1450 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1451 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1453 if (data.flickTarget >= minExtent) {
1454 overshootDist = overShootDistance(vSize);
1455 data.flickTarget += overshootDist;
1456 } else if (data.flickTarget <= maxExtent) {
1457 overshootDist = overShootDistance(vSize);
1458 data.flickTarget -= overshootDist;
1461 qreal adjDist = -data.flickTarget + data.move.value();
1462 if (qAbs(adjDist) > qAbs(dist)) {
1463 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1464 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1473 accel = v2 / (2.0f * qAbs(dist));
1474 } else if (overShoot) {
1475 data.flickTarget = data.move.value() - dist;
1476 if (data.flickTarget >= minExtent) {
1477 overshootDist = overShootDistance(vSize);
1478 data.flickTarget += overshootDist;
1479 } else if (data.flickTarget <= maxExtent) {
1480 overshootDist = overShootDistance(vSize);
1481 data.flickTarget -= overshootDist;
1485 timeline.reset(data.move);
1486 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1487 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1488 if (!flickingHorizontally && q->xflick()) {
1489 flickingHorizontally = true;
1490 emit q->flickingChanged();
1491 emit q->flickingHorizontallyChanged();
1492 emit q->flickStarted();
1494 if (!flickingVertically && q->yflick()) {
1495 flickingVertically = true;
1496 emit q->flickingChanged();
1497 emit q->flickingVerticallyChanged();
1498 emit q->flickStarted();
1500 correctFlick = true;
1502 // reevaluate the target boundary.
1503 qreal newtarget = data.flickTarget;
1504 if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) {
1505 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1506 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1507 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1509 if (velocity < 0 && newtarget <= maxExtent)
1510 newtarget = maxExtent - overshootDist;
1511 else if (velocity > 0 && newtarget >= minExtent)
1512 newtarget = minExtent + overshootDist;
1513 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1514 if (qAbs(velocity) < MinimumFlickVelocity)
1515 correctFlick = false;
1518 data.flickTarget = newtarget;
1519 qreal dist = -newtarget + data.move.value();
1520 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1521 correctFlick = false;
1522 timeline.reset(data.move);
1523 fixup(data, minExtent, maxExtent);
1527 timeline.reset(data.move);
1528 timeline.accelDistance(data.move, v, -dist);
1529 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1532 correctFlick = false;
1533 timeline.reset(data.move);
1534 fixup(data, minExtent, maxExtent);
1538 //----------------------------------------------------------------------------
1541 \qmlclass ListView QDeclarativeListView
1542 \ingroup qml-view-elements
1545 \brief The ListView item provides a list view of items provided by a model.
1547 A ListView displays data from models created from built-in QML elements like ListModel
1548 and XmlListModel, or custom model classes defined in C++ that inherit from
1551 A ListView has a \l model, which defines the data to be displayed, and
1552 a \l delegate, which defines how the data should be displayed. Items in a
1553 ListView are laid out horizontally or vertically. List views are inherently
1554 flickable because ListView inherits from \l Flickable.
1556 \section1 Example Usage
1558 The following example shows the definition of a simple list model defined
1559 in a file called \c ContactModel.qml:
1561 \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1563 Another component can display this model data in a ListView, like this:
1565 \snippet doc/src/snippets/declarative/listview/listview.qml import
1567 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1569 \image listview-simple.png
1571 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1572 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1573 the delegate is able to access the model's \c name and \c number data directly.
1575 An improved list view is shown below. The delegate is visually improved and is moved
1576 into a separate \c contactDelegate component.
1578 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1579 \image listview-highlight.png
1581 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1582 and \c focus is set to \c true to enable keyboard navigation for the list view.
1583 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1585 Delegates are instantiated as needed and may be destroyed at any time.
1586 State should \e never be stored in a delegate.
1588 ListView attaches a number of properties to the root item of the delegate, for example
1589 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1590 this attached property directly as \c ListView.isCurrentItem, while the child
1591 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1593 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1595 \note Views do not enable \e clip automatically. If the view
1596 is not clipped by another item or the screen, it will be necessary
1597 to set \e {clip: true} in order to have the out of view items clipped
1600 \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1603 QDeclarativeListView::QDeclarativeListView(QDeclarativeItem *parent)
1604 : QDeclarativeFlickable(*(new QDeclarativeListViewPrivate), parent)
1606 Q_D(QDeclarativeListView);
1610 QDeclarativeListView::~QDeclarativeListView()
1612 Q_D(QDeclarativeListView);
1621 \qmlattachedproperty bool ListView::isCurrentItem
1622 This attached property is true if this delegate is the current item; otherwise false.
1624 It is attached to each instance of the delegate.
1626 This property may be used to adjust the appearance of the current item, for example:
1628 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1632 \qmlattachedproperty ListView ListView::view
1633 This attached property holds the view that manages this delegate instance.
1635 It is attached to each instance of the delegate.
1639 \qmlattachedproperty string ListView::previousSection
1640 This attached property holds the section of the previous element.
1642 It is attached to each instance of the delegate.
1644 The section is evaluated using the \l {ListView::section.property}{section} properties.
1648 \qmlattachedproperty string ListView::nextSection
1649 This attached property holds the section of the next element.
1651 It is attached to each instance of the delegate.
1653 The section is evaluated using the \l {ListView::section.property}{section} properties.
1657 \qmlattachedproperty string ListView::section
1658 This attached property holds the section of this element.
1660 It is attached to each instance of the delegate.
1662 The section is evaluated using the \l {ListView::section.property}{section} properties.
1666 \qmlattachedproperty bool ListView::delayRemove
1667 This attached property holds whether the delegate may be destroyed.
1669 It is attached to each instance of the delegate.
1671 It is sometimes necessary to delay the destruction of an item
1672 until an animation completes.
1674 The example delegate below ensures that the animation completes before
1675 the item is removed from the list.
1677 \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1681 \qmlattachedsignal ListView::onAdd()
1682 This attached handler is called immediately after an item is added to the view.
1686 \qmlattachedsignal ListView::onRemove()
1687 This attached handler is called immediately before an item is removed from the view.
1691 \qmlproperty model ListView::model
1692 This property holds the model providing data for the list.
1694 The model provides the set of data that is used to create the items
1695 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1696 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1697 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1699 \sa {qmlmodels}{Data Models}
1701 QVariant QDeclarativeListView::model() const
1703 Q_D(const QDeclarativeListView);
1704 return d->modelVariant;
1707 void QDeclarativeListView::setModel(const QVariant &model)
1709 Q_D(QDeclarativeListView);
1710 if (d->modelVariant == model)
1713 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1714 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1715 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1716 disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1717 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1718 disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1719 disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1722 QDeclarativeVisualModel *oldModel = d->model;
1725 d->modelVariant = model;
1726 QObject *object = qvariant_cast<QObject*>(model);
1727 QDeclarativeVisualModel *vim = 0;
1728 if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
1731 d->ownModel = false;
1736 d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
1739 d->model = oldModel;
1741 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1742 dataModel->setModel(model);
1745 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
1746 if (isComponentComplete()) {
1749 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1752 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1753 d->updateCurrent(d->currentIndex);
1754 if (d->highlight && d->currentItem) {
1755 if (d->autoHighlight)
1756 d->highlight->setPosition(d->currentItem->position());
1757 d->updateTrackedItem();
1760 d->updateViewport();
1762 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1763 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1764 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1765 connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1766 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1767 connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1768 connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1769 emit countChanged();
1771 emit modelChanged();
1775 \qmlproperty Component ListView::delegate
1777 The delegate provides a template defining each item instantiated by the view.
1778 The index is exposed as an accessible \c index property. Properties of the
1779 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1781 The number of elements in the delegate has a direct effect on the
1782 flicking performance of the view. If at all possible, place functionality
1783 that is not needed for the normal display of the delegate in a \l Loader which
1784 can load additional elements when needed.
1786 The ListView will lay out the items based on the size of the root item
1789 It is recommended that the delagate's size be a whole number to avoid sub-pixel
1792 \note Delegates are instantiated as needed and may be destroyed at any time.
1793 State should \e never be stored in a delegate.
1795 QDeclarativeComponent *QDeclarativeListView::delegate() const
1797 Q_D(const QDeclarativeListView);
1799 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1800 return dataModel->delegate();
1806 void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate)
1808 Q_D(QDeclarativeListView);
1809 if (delegate == this->delegate())
1812 d->model = new QDeclarativeVisualDataModel(qmlContext(this));
1815 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
1816 int oldCount = dataModel->count();
1817 dataModel->setDelegate(delegate);
1818 if (isComponentComplete()) {
1819 for (int i = 0; i < d->visibleItems.count(); ++i)
1820 d->releaseItem(d->visibleItems.at(i));
1821 d->visibleItems.clear();
1822 d->releaseItem(d->currentItem);
1826 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1827 d->updateCurrent(d->currentIndex);
1828 if (d->highlight && d->currentItem) {
1829 if (d->autoHighlight)
1830 d->highlight->setPosition(d->currentItem->position());
1831 d->updateTrackedItem();
1833 d->updateViewport();
1835 if (oldCount != dataModel->count())
1836 emit countChanged();
1838 emit delegateChanged();
1842 \qmlproperty int ListView::currentIndex
1843 \qmlproperty Item ListView::currentItem
1845 The \c currentIndex property holds the index of the current item, and
1846 \c currentItem holds the current item. Setting the currentIndex to -1
1847 will clear the highlight and set currentItem to null.
1849 If highlightFollowsCurrentItem is \c true, setting either of these
1850 properties will smoothly scroll the ListView so that the current
1851 item becomes visible.
1853 Note that the position of the current item
1854 may only be approximate until it becomes visible in the view.
1856 int QDeclarativeListView::currentIndex() const
1858 Q_D(const QDeclarativeListView);
1859 return d->currentIndex;
1862 void QDeclarativeListView::setCurrentIndex(int index)
1864 Q_D(QDeclarativeListView);
1865 if (d->requestedIndex >= 0) // currently creating item
1867 d->currentIndexCleared = (index == -1);
1868 if (index == d->currentIndex)
1870 if (isComponentComplete() && d->isValid()) {
1871 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1872 d->updateCurrent(index);
1873 } else if (d->currentIndex != index) {
1874 d->currentIndex = index;
1875 emit currentIndexChanged();
1879 QDeclarativeItem *QDeclarativeListView::currentItem()
1881 Q_D(QDeclarativeListView);
1882 if (!d->currentItem)
1884 return d->currentItem->item;
1888 \qmlproperty Item ListView::highlightItem
1890 This holds the highlight item created from the \l highlight component.
1892 The \c highlightItem is managed by the view unless
1893 \l highlightFollowsCurrentItem is set to false.
1895 \sa highlight, highlightFollowsCurrentItem
1897 QDeclarativeItem *QDeclarativeListView::highlightItem()
1899 Q_D(QDeclarativeListView);
1902 return d->highlight->item;
1906 \qmlproperty int ListView::count
1907 This property holds the number of items in the view.
1909 int QDeclarativeListView::count() const
1911 Q_D(const QDeclarativeListView);
1913 return d->model->count();
1918 \qmlproperty Component ListView::highlight
1919 This property holds the component to use as the highlight.
1921 An instance of the highlight component is created for each list.
1922 The geometry of the resulting component instance is managed by the list
1923 so as to stay with the current item, unless the highlightFollowsCurrentItem
1926 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1928 QDeclarativeComponent *QDeclarativeListView::highlight() const
1930 Q_D(const QDeclarativeListView);
1931 return d->highlightComponent;
1934 void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight)
1936 Q_D(QDeclarativeListView);
1937 if (highlight != d->highlightComponent) {
1938 d->highlightComponent = highlight;
1939 d->createHighlight();
1941 d->updateHighlight();
1942 emit highlightChanged();
1947 \qmlproperty bool ListView::highlightFollowsCurrentItem
1948 This property holds whether the highlight is managed by the view.
1950 If this property is true (the default value), the highlight is moved smoothly
1951 to follow the current item. Otherwise, the
1952 highlight is not moved by the view, and any movement must be implemented
1955 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1957 \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1959 Note that the highlight animation also affects the way that the view
1960 is scrolled. This is because the view moves to maintain the
1961 highlight within the preferred highlight range (or visible viewport).
1963 \sa highlight, highlightMoveSpeed
1965 bool QDeclarativeListView::highlightFollowsCurrentItem() const
1967 Q_D(const QDeclarativeListView);
1968 return d->autoHighlight;
1971 void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1973 Q_D(QDeclarativeListView);
1974 if (d->autoHighlight != autoHighlight) {
1975 d->autoHighlight = autoHighlight;
1976 if (autoHighlight) {
1977 d->updateHighlight();
1979 if (d->highlightPosAnimator)
1980 d->highlightPosAnimator->stop();
1981 if (d->highlightSizeAnimator)
1982 d->highlightSizeAnimator->stop();
1984 emit highlightFollowsCurrentItemChanged();
1988 //###Possibly rename these properties, since they are very useful even without a highlight?
1990 \qmlproperty real ListView::preferredHighlightBegin
1991 \qmlproperty real ListView::preferredHighlightEnd
1992 \qmlproperty enumeration ListView::highlightRangeMode
1994 These properties define the preferred range of the highlight (for the current item)
1995 within the view. The \c preferredHighlightBegin value must be less than the
1996 \c preferredHighlightEnd value.
1998 These properties affect the position of the current item when the list is scrolled.
1999 For example, if the currently selected item should stay in the middle of the
2000 list when the view is scrolled, set the \c preferredHighlightBegin and
2001 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
2002 item would be. If the \c currentItem is changed programmatically, the list will
2003 automatically scroll so that the current item is in the middle of the view.
2004 Furthermore, the behavior of the current item index will occur whether or not a
2007 Valid values for \c highlightRangeMode are:
2010 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2011 However, the highlight can move outside of the range at the ends of the list or due
2012 to mouse interaction.
2013 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2014 The current item changes if a keyboard or mouse action would cause the highlight to move
2015 outside of the range.
2016 \o ListView.NoHighlightRange - this is the default value.
2019 qreal QDeclarativeListView::preferredHighlightBegin() const
2021 Q_D(const QDeclarativeListView);
2022 return d->highlightRangeStart;
2025 void QDeclarativeListView::setPreferredHighlightBegin(qreal start)
2027 Q_D(QDeclarativeListView);
2028 d->highlightRangeStartValid = true;
2029 if (d->highlightRangeStart == start)
2031 d->highlightRangeStart = start;
2032 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2033 emit preferredHighlightBeginChanged();
2036 void QDeclarativeListView::resetPreferredHighlightBegin()
2038 Q_D(QDeclarativeListView);
2039 d->highlightRangeStartValid = false;
2040 if (d->highlightRangeStart == 0)
2042 d->highlightRangeStart = 0;
2043 emit preferredHighlightBeginChanged();
2046 qreal QDeclarativeListView::preferredHighlightEnd() const
2048 Q_D(const QDeclarativeListView);
2049 return d->highlightRangeEnd;
2052 void QDeclarativeListView::setPreferredHighlightEnd(qreal end)
2054 Q_D(QDeclarativeListView);
2055 d->highlightRangeEndValid = true;
2056 if (d->highlightRangeEnd == end)
2058 d->highlightRangeEnd = end;
2059 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2060 emit preferredHighlightEndChanged();
2063 void QDeclarativeListView::resetPreferredHighlightEnd()
2065 Q_D(QDeclarativeListView);
2066 d->highlightRangeEndValid = false;
2067 if (d->highlightRangeEnd == 0)
2069 d->highlightRangeEnd = 0;
2070 emit preferredHighlightEndChanged();
2073 QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
2075 Q_D(const QDeclarativeListView);
2076 return d->highlightRange;
2079 void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode)
2081 Q_D(QDeclarativeListView);
2082 if (d->highlightRange == mode)
2084 d->highlightRange = mode;
2085 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2086 emit highlightRangeModeChanged();
2090 \qmlproperty real ListView::spacing
2092 This property holds the spacing between items.
2094 The default value is 0.
2096 qreal QDeclarativeListView::spacing() const
2098 Q_D(const QDeclarativeListView);
2102 void QDeclarativeListView::setSpacing(qreal spacing)
2104 Q_D(QDeclarativeListView);
2105 if (spacing != d->spacing) {
2106 d->spacing = spacing;
2108 emit spacingChanged();
2113 \qmlproperty enumeration ListView::orientation
2114 This property holds the orientation of the list.
2119 \o ListView.Horizontal - Items are laid out horizontally
2120 \o ListView.Vertical (default) - Items are laid out vertically
2125 \o Horizontal orientation:
2126 \image ListViewHorizontal.png
2129 \o Vertical orientation:
2130 \image listview-highlight.png
2133 QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
2135 Q_D(const QDeclarativeListView);
2139 void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation)
2141 Q_D(QDeclarativeListView);
2142 if (d->orient != orientation) {
2143 d->orient = orientation;
2144 if (d->orient == QDeclarativeListView::Vertical) {
2145 setContentWidth(-1);
2146 setFlickableDirection(VerticalFlick);
2149 setContentHeight(-1);
2150 setFlickableDirection(HorizontalFlick);
2154 emit orientationChanged();
2159 \qmlproperty enumeration ListView::layoutDirection
2160 This property holds the layout direction of the horizontal list.
2165 \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2166 \o Qt.RightToLeft - Items will be laid out from right to let.
2169 \sa ListView::effectiveLayoutDirection
2172 Qt::LayoutDirection QDeclarativeListView::layoutDirection() const
2174 Q_D(const QDeclarativeListView);
2175 return d->layoutDirection;
2178 void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2180 Q_D(QDeclarativeListView);
2181 if (d->layoutDirection != layoutDirection) {
2182 d->layoutDirection = layoutDirection;
2184 emit layoutDirectionChanged();
2185 emit effectiveLayoutDirectionChanged();
2190 \qmlproperty enumeration ListView::effectiveLayoutDirection
2191 This property holds the effective layout direction of the horizontal list.
2193 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2194 the visual layout direction of the horizontal list will be mirrored. However, the
2195 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2197 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2200 Qt::LayoutDirection QDeclarativeListView::effectiveLayoutDirection() const
2202 Q_D(const QDeclarativeListView);
2203 if (d->effectiveLayoutMirror)
2204 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2206 return d->layoutDirection;
2210 \qmlproperty bool ListView::keyNavigationWraps
2211 This property holds whether the list wraps key navigation.
2213 If this is true, key navigation that would move the current item selection
2214 past the end of the list instead wraps around and moves the selection to
2215 the start of the list, and vice-versa.
2217 By default, key navigation is not wrapped.
2219 bool QDeclarativeListView::isWrapEnabled() const
2221 Q_D(const QDeclarativeListView);
2225 void QDeclarativeListView::setWrapEnabled(bool wrap)
2227 Q_D(QDeclarativeListView);
2228 if (d->wrap == wrap)
2231 emit keyNavigationWrapsChanged();
2235 \qmlproperty int ListView::cacheBuffer
2236 This property determines whether delegates are retained outside the
2237 visible area of the view.
2239 If this value is non-zero, the view keeps as many delegates
2240 instantiated as it can fit within the buffer specified. For example,
2241 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2242 set to 40, then up to 2 delegates above and 2 delegates below the visible
2243 area may be retained.
2245 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2246 instantiated delegates.
2248 Setting this value can improve the smoothness of scrolling behavior at the expense
2249 of additional memory usage. It is not a substitute for creating efficient
2250 delegates; the fewer elements in a delegate, the faster a view can be
2253 int QDeclarativeListView::cacheBuffer() const
2255 Q_D(const QDeclarativeListView);
2259 void QDeclarativeListView::setCacheBuffer(int b)
2261 Q_D(QDeclarativeListView);
2262 if (d->buffer != b) {
2264 if (isComponentComplete()) {
2265 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
2268 emit cacheBufferChanged();
2273 \qmlproperty string ListView::section.property
2274 \qmlproperty enumeration ListView::section.criteria
2275 \qmlproperty Component ListView::section.delegate
2277 These properties hold the expression to be evaluated for the \l section attached property.
2279 The \l section attached property enables a ListView to be visually
2280 separated into different parts. These properties determine how sections
2283 \c section.property holds the name of the property that is the basis
2286 \c section.criteria holds the criteria for forming each section based on
2287 \c section.property. This value can be one of:
2290 \o ViewSection.FullString (default) - sections are created based on the
2291 \c section.property value.
2292 \o ViewSection.FirstCharacter - sections are created based on the first
2293 character of the \c section.property value (for example, 'A', 'B', 'C'
2294 sections, etc. for an address book)
2297 \c section.delegate holds the delegate component for each section.
2299 Each item in the list has attached properties named \c ListView.section,
2300 \c ListView.previousSection and \c ListView.nextSection. These may be
2301 used to place a section header for related items.
2303 For example, here is a ListView that displays a list of animals, separated
2304 into sections. Each item in the ListView is placed in a different section
2305 depending on the "size" property of the model item. The \c sectionHeading
2306 delegate component provides the light blue bar that marks the beginning of
2310 \snippet examples/declarative/modelviews/listview/sections.qml 0
2312 \image qml-listview-sections-example.png
2314 \note Adding sections to a ListView does not automatically re-order the
2315 list items by the section criteria.
2316 If the model is not ordered by section, then it is possible that
2317 the sections created will not be unique; each boundary between
2318 differing sections will result in a section header being created
2319 even if that section exists elsewhere.
2321 \sa {declarative/modelviews/listview}{ListView examples}
2323 QDeclarativeViewSection *QDeclarativeListView::sectionCriteria()
2325 Q_D(QDeclarativeListView);
2326 if (!d->sectionCriteria) {
2327 d->sectionCriteria = new QDeclarativeViewSection(this);
2328 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2330 return d->sectionCriteria;
2334 \qmlproperty string ListView::currentSection
2335 This property holds the section that is currently at the beginning of the view.
2337 QString QDeclarativeListView::currentSection() const
2339 Q_D(const QDeclarativeListView);
2340 return d->currentSection;
2344 \qmlproperty real ListView::highlightMoveSpeed
2345 \qmlproperty int ListView::highlightMoveDuration
2346 \qmlproperty real ListView::highlightResizeSpeed
2347 \qmlproperty int ListView::highlightResizeDuration
2349 These properties hold the move and resize animation speed of the highlight delegate.
2351 \l highlightFollowsCurrentItem must be true for these properties
2354 The default value for the speed properties is 400 pixels/second.
2355 The default value for the duration properties is -1, i.e. the
2356 highlight will take as much time as necessary to move at the set speed.
2358 These properties have the same characteristics as a SmoothedAnimation.
2360 \sa highlightFollowsCurrentItem
2362 qreal QDeclarativeListView::highlightMoveSpeed() const
2364 Q_D(const QDeclarativeListView);\
2365 return d->highlightMoveSpeed;
2368 void QDeclarativeListView::setHighlightMoveSpeed(qreal speed)
2370 Q_D(QDeclarativeListView);\
2371 if (d->highlightMoveSpeed != speed) {
2372 d->highlightMoveSpeed = speed;
2373 if (d->highlightPosAnimator)
2374 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2375 emit highlightMoveSpeedChanged();
2379 int QDeclarativeListView::highlightMoveDuration() const
2381 Q_D(const QDeclarativeListView);
2382 return d->highlightMoveDuration;
2385 void QDeclarativeListView::setHighlightMoveDuration(int duration)
2387 Q_D(QDeclarativeListView);\
2388 if (d->highlightMoveDuration != duration) {
2389 d->highlightMoveDuration = duration;
2390 if (d->highlightPosAnimator)
2391 d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2392 emit highlightMoveDurationChanged();
2396 qreal QDeclarativeListView::highlightResizeSpeed() const
2398 Q_D(const QDeclarativeListView);\
2399 return d->highlightResizeSpeed;
2402 void QDeclarativeListView::setHighlightResizeSpeed(qreal speed)
2404 Q_D(QDeclarativeListView);\
2405 if (d->highlightResizeSpeed != speed) {
2406 d->highlightResizeSpeed = speed;
2407 if (d->highlightSizeAnimator)
2408 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2409 emit highlightResizeSpeedChanged();
2413 int QDeclarativeListView::highlightResizeDuration() const
2415 Q_D(const QDeclarativeListView);
2416 return d->highlightResizeDuration;
2419 void QDeclarativeListView::setHighlightResizeDuration(int duration)
2421 Q_D(QDeclarativeListView);\
2422 if (d->highlightResizeDuration != duration) {
2423 d->highlightResizeDuration = duration;
2424 if (d->highlightSizeAnimator)
2425 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2426 emit highlightResizeDurationChanged();
2431 \qmlproperty enumeration ListView::snapMode
2433 This property determines how the view scrolling will settle following a drag or flick.
2434 The possible values are:
2437 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2438 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2440 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2441 visible item at the time the mouse button is released. This mode is particularly
2442 useful for moving one page at a time.
2445 \c snapMode does not affect the \l currentIndex. To update the
2446 \l currentIndex as the list is moved, set \l highlightRangeMode
2447 to \c ListView.StrictlyEnforceRange.
2449 \sa highlightRangeMode
2451 QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
2453 Q_D(const QDeclarativeListView);
2457 void QDeclarativeListView::setSnapMode(SnapMode mode)
2459 Q_D(QDeclarativeListView);
2460 if (d->snapMode != mode) {
2462 emit snapModeChanged();
2467 \qmlproperty Component ListView::footer
2468 This property holds the component to use as the footer.
2470 An instance of the footer component is created for each view. The
2471 footer is positioned at the end of the view, after any items.
2475 QDeclarativeComponent *QDeclarativeListView::footer() const
2477 Q_D(const QDeclarativeListView);
2478 return d->footerComponent;
2481 void QDeclarativeListView::setFooter(QDeclarativeComponent *footer)
2483 Q_D(QDeclarativeListView);
2484 if (d->footerComponent != footer) {
2487 scene()->removeItem(d->footer->item);
2488 d->footer->item->deleteLater();
2492 d->footerComponent = footer;
2493 d->minExtentDirty = true;
2494 d->maxExtentDirty = true;
2495 if (isComponentComplete()) {
2497 d->updateViewport();
2500 emit footerChanged();
2505 \qmlproperty Component ListView::header
2506 This property holds the component to use as the header.
2508 An instance of the header component is created for each view. The
2509 header is positioned at the beginning of the view, before any items.
2513 QDeclarativeComponent *QDeclarativeListView::header() const
2515 Q_D(const QDeclarativeListView);
2516 return d->headerComponent;
2519 void QDeclarativeListView::setHeader(QDeclarativeComponent *header)
2521 Q_D(QDeclarativeListView);
2522 if (d->headerComponent != header) {
2525 scene()->removeItem(d->header->item);
2526 d->header->item->deleteLater();
2530 d->headerComponent = header;
2531 d->minExtentDirty = true;
2532 d->maxExtentDirty = true;
2533 if (isComponentComplete()) {
2536 d->updateViewport();
2539 emit headerChanged();
2543 void QDeclarativeListView::setContentX(qreal pos)
2545 Q_D(QDeclarativeListView);
2546 // Positioning the view manually should override any current movement state
2547 d->moveReason = QDeclarativeListViewPrivate::Other;
2548 QDeclarativeFlickable::setContentX(pos);
2551 void QDeclarativeListView::setContentY(qreal pos)
2553 Q_D(QDeclarativeListView);
2554 // Positioning the view manually should override any current movement state
2555 d->moveReason = QDeclarativeListViewPrivate::Other;
2556 QDeclarativeFlickable::setContentY(pos);
2559 bool QDeclarativeListView::event(QEvent *event)
2561 Q_D(QDeclarativeListView);
2562 if (event->type() == QEvent::User) {
2567 return QDeclarativeFlickable::event(event);
2570 void QDeclarativeListView::viewportMoved()
2572 Q_D(QDeclarativeListView);
2573 QDeclarativeFlickable::viewportMoved();
2576 // Recursion can occur due to refill changing the content size.
2577 if (d->inViewportMoved)
2579 d->inViewportMoved = true;
2580 d->lazyRelease = true;
2582 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2583 d->moveReason = QDeclarativeListViewPrivate::Mouse;
2584 if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) {
2585 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2586 // reposition highlight
2587 qreal pos = d->highlight->position();
2589 qreal highlightStart;
2591 if (d->isRightToLeft()) {
2592 // Handle Right-To-Left exceptions
2593 viewPos = -d->position()-d->size();
2594 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2595 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2597 viewPos = d->position();
2598 highlightStart = d->highlightRangeStart;
2599 highlightEnd = d->highlightRangeEnd;
2601 if (pos > viewPos + highlightEnd - d->highlight->size())
2602 pos = viewPos + highlightEnd - d->highlight->size();
2603 if (pos < viewPos + highlightStart)
2604 pos = viewPos + highlightStart;
2605 d->highlightPosAnimator->stop();
2606 d->highlight->setPosition(qRound(pos));
2608 // update current index
2609 if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) {
2610 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2611 d->updateCurrent(snapItem->index);
2616 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2617 d->inFlickCorrection = true;
2618 // Near an end and it seems that the extent has changed?
2619 // Recalculate the flick so that we don't end up in an odd position.
2620 if (yflick() && !d->vData.inOvershoot) {
2621 if (d->vData.velocity > 0) {
2622 const qreal minY = minYExtent();
2623 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2624 && minY != d->vData.flickTarget)
2625 d->flickY(-d->vData.smoothVelocity.value());
2626 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2627 } else if (d->vData.velocity < 0) {
2628 const qreal maxY = maxYExtent();
2629 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2630 && maxY != d->vData.flickTarget)
2631 d->flickY(-d->vData.smoothVelocity.value());
2632 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2636 if (xflick() && !d->hData.inOvershoot) {
2637 if (d->hData.velocity > 0) {
2638 const qreal minX = minXExtent();
2639 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2640 && minX != d->hData.flickTarget)
2641 d->flickX(-d->hData.smoothVelocity.value());
2642 d->bufferMode = d->isRightToLeft()
2643 ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::BufferBefore;
2644 } else if (d->hData.velocity < 0) {
2645 const qreal maxX = maxXExtent();
2646 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2647 && maxX != d->hData.flickTarget)
2648 d->flickX(-d->hData.smoothVelocity.value());
2649 d->bufferMode = d->isRightToLeft()
2650 ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter;
2653 d->inFlickCorrection = false;
2655 d->inViewportMoved = false;
2658 qreal QDeclarativeListView::minYExtent() const
2660 Q_D(const QDeclarativeListView);
2661 if (d->orient == QDeclarativeListView::Horizontal)
2662 return QDeclarativeFlickable::minYExtent();
2663 if (d->minExtentDirty) {
2664 d->minExtent = -d->startPosition();
2665 if (d->header && d->visibleItems.count())
2666 d->minExtent += d->header->size();
2667 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2668 d->minExtent += d->highlightRangeStart;
2669 if (d->sectionCriteria) {
2670 if (d->visibleItem(0))
2671 d->minExtent -= d->visibleItem(0)->sectionSize();
2673 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2675 d->minExtentDirty = false;
2678 return d->minExtent;
2681 qreal QDeclarativeListView::maxYExtent() const
2683 Q_D(const QDeclarativeListView);
2684 if (d->orient == QDeclarativeListView::Horizontal)
2686 if (d->maxExtentDirty) {
2687 if (!d->model || !d->model->count()) {
2688 d->maxExtent = d->header ? -d->header->size() : 0;
2689 d->maxExtent += height();
2690 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2691 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2692 if (d->highlightRangeEnd != d->highlightRangeStart)
2693 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2695 d->maxExtent = -(d->endPosition() - height() + 1);
2698 d->maxExtent -= d->footer->size();
2699 qreal minY = minYExtent();
2700 if (d->maxExtent > minY)
2701 d->maxExtent = minY;
2702 d->maxExtentDirty = false;
2704 return d->maxExtent;
2707 qreal QDeclarativeListView::minXExtent() const
2709 Q_D(const QDeclarativeListView);
2710 if (d->orient == QDeclarativeListView::Vertical)
2711 return QDeclarativeFlickable::minXExtent();
2712 if (d->minExtentDirty) {
2713 d->minExtent = -d->startPosition();
2715 qreal highlightStart;
2717 qreal endPositionFirstItem = 0;
2718 if (d->isRightToLeft()) {
2719 if (d->model && d->model->count())
2720 endPositionFirstItem = d->positionAt(d->model->count()-1);
2722 d->minExtent += d->header->size();
2723 highlightStart = d->highlightRangeStartValid
2724 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2725 : d->size() - (d->lastPosition()-endPositionFirstItem);
2726 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2728 d->minExtent += d->footer->size();
2729 qreal maxX = maxXExtent();
2730 if (d->minExtent < maxX)
2731 d->minExtent = maxX;
2733 endPositionFirstItem = d->endPositionAt(0);
2734 highlightStart = d->highlightRangeStart;
2735 highlightEnd = d->highlightRangeEnd;
2736 if (d->header && d->visibleItems.count())
2737 d->minExtent += d->header->size();
2739 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2740 d->minExtent += highlightStart;
2741 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2743 d->minExtentDirty = false;
2746 return d->minExtent;
2749 qreal QDeclarativeListView::maxXExtent() const
2751 Q_D(const QDeclarativeListView);
2752 if (d->orient == QDeclarativeListView::Vertical)
2754 if (d->maxExtentDirty) {
2755 qreal highlightStart;
2757 qreal lastItemPosition = 0;
2759 if (d->isRightToLeft()) {
2760 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2761 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2762 lastItemPosition = d->endPosition();
2764 highlightStart = d->highlightRangeStart;
2765 highlightEnd = d->highlightRangeEnd;
2766 if (d->model && d->model->count())
2767 lastItemPosition = d->positionAt(d->model->count()-1);
2769 if (!d->model || !d->model->count()) {
2770 if (!d->isRightToLeft())
2771 d->maxExtent = d->header ? -d->header->size() : 0;
2772 d->maxExtent += width();
2773 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2774 d->maxExtent = -(lastItemPosition - highlightStart);
2775 if (highlightEnd != highlightStart) {
2776 d->maxExtent = d->isRightToLeft()
2777 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2778 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2781 d->maxExtent = -(d->endPosition() - width() + 1);
2783 if (d->isRightToLeft()) {
2784 if (d->header && d->visibleItems.count())
2785 d->maxExtent -= d->header->size();
2788 d->maxExtent -= d->footer->size();
2789 qreal minX = minXExtent();
2790 if (d->maxExtent > minX)
2791 d->maxExtent = minX;
2793 d->maxExtentDirty = false;
2795 return d->maxExtent;
2798 void QDeclarativeListView::keyPressEvent(QKeyEvent *event)
2800 Q_D(QDeclarativeListView);
2801 keyPressPreHandler(event);
2802 if (event->isAccepted())
2805 if (d->model && d->model->count() && d->interactive) {
2806 if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2807 || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2808 || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) {
2809 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2810 decrementCurrentIndex();
2813 } else if (d->wrap) {
2817 } else if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2818 || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2819 || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) {
2820 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2821 incrementCurrentIndex();
2824 } else if (d->wrap) {
2831 QDeclarativeFlickable::keyPressEvent(event);
2834 void QDeclarativeListView::geometryChanged(const QRectF &newGeometry,
2835 const QRectF &oldGeometry)
2837 Q_D(QDeclarativeListView);
2838 d->maxExtentDirty = true;
2839 d->minExtentDirty = true;
2840 if (d->isRightToLeft() && d->orient == QDeclarativeListView::Horizontal) {
2841 // maintain position relative to the right edge
2842 int dx = newGeometry.width() - oldGeometry.width();
2843 setContentX(contentX() - dx);
2845 QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry);
2850 \qmlmethod ListView::incrementCurrentIndex()
2852 Increments the current index. The current index will wrap
2853 if keyNavigationWraps is true and it is currently at the end.
2854 This method has no effect if the \l count is zero.
2856 \bold Note: methods should only be called after the Component has completed.
2858 void QDeclarativeListView::incrementCurrentIndex()
2860 Q_D(QDeclarativeListView);
2861 int count = d->model ? d->model->count() : 0;
2862 if (count && (currentIndex() < count - 1 || d->wrap)) {
2863 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2864 int index = currentIndex()+1;
2865 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2870 \qmlmethod ListView::decrementCurrentIndex()
2872 Decrements the current index. The current index will wrap
2873 if keyNavigationWraps is true and it is currently at the beginning.
2874 This method has no effect if the \l count is zero.
2876 \bold Note: methods should only be called after the Component has completed.
2878 void QDeclarativeListView::decrementCurrentIndex()
2880 Q_D(QDeclarativeListView);
2881 int count = d->model ? d->model->count() : 0;
2882 if (count && (currentIndex() > 0 || d->wrap)) {
2883 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2884 int index = currentIndex()-1;
2885 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2889 void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode)
2891 Q_Q(QDeclarativeListView);
2894 if (mode < QDeclarativeListView::Beginning || mode > QDeclarativeListView::Contain)
2896 int idx = qMax(qMin(index, model->count()-1), 0);
2898 if (layoutScheduled)
2900 qreal pos = isRightToLeft() ? -position() - size() : position();
2901 FxListItem *item = visibleItem(idx);
2903 if (orient == QDeclarativeListView::Vertical)
2904 maxExtent = -q->maxYExtent();
2906 maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2909 int itemPos = positionAt(idx);
2910 // save the currently visible items in case any of them end up visible again
2911 QList<FxListItem*> oldVisible = visibleItems;
2912 visibleItems.clear();
2913 visiblePos = itemPos;
2915 setPosition(qMin(qreal(itemPos), maxExtent));
2916 // now release the reference to all the old visible items.
2917 for (int i = 0; i < oldVisible.count(); ++i)
2918 releaseItem(oldVisible.at(i));
2919 item = visibleItem(idx);
2922 const qreal itemPos = item->position();
2924 case QDeclarativeListView::Beginning:
2926 if (index < 0 && header)
2927 pos -= header->size();
2929 case QDeclarativeListView::Center:
2930 pos = itemPos - (size() - item->size())/2;
2932 case QDeclarativeListView::End:
2933 pos = itemPos - size() + item->size();
2934 if (index >= model->count() && footer)
2935 pos += footer->size();
2937 case QDeclarativeListView::Visible:
2938 if (itemPos > pos + size())
2939 pos = itemPos - size() + item->size();
2940 else if (item->endPosition() < pos)
2943 case QDeclarativeListView::Contain:
2944 if (item->endPosition() > pos + size())
2945 pos = itemPos - size() + item->size();
2949 pos = qMin(pos, maxExtent);
2951 if (orient == QDeclarativeListView::Vertical) {
2952 minExtent = -q->minYExtent();
2954 minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2956 pos = qMax(pos, minExtent);
2957 moveReason = QDeclarativeListViewPrivate::Other;
2961 if (autoHighlight) {
2962 highlight->setPosition(currentItem->itemPosition());
2963 highlight->setSize(currentItem->itemSize());
2972 \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode)
2974 Positions the view such that the \a index is at the position specified by
2978 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2979 \o ListView.Center - position item in the center of the view.
2980 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2981 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2982 bring the item into view.
2983 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
2984 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2987 If positioning the view at \a index would cause empty space to be displayed at
2988 the beginning or end of the view, the view will be positioned at the boundary.
2990 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2991 at a particular index. This is unreliable since removing items from the start
2992 of the list does not cause all other items to be repositioned, and because
2993 the actual start of the view can vary based on the size of the delegates.
2994 The correct way to bring an item into view is with \c positionViewAtIndex.
2996 \bold Note: methods should only be called after the Component has completed. To position
2997 the view at startup, this method should be called by Component.onCompleted. For
2998 example, to position the view at the end:
3001 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3004 void QDeclarativeListView::positionViewAtIndex(int index, int mode)
3006 Q_D(QDeclarativeListView);
3007 if (!d->isValid() || index < 0 || index >= d->model->count())
3009 d->positionViewAtIndex(index, mode);
3013 \qmlmethod ListView::positionViewAtBeginning()
3014 \qmlmethod ListView::positionViewAtEnd()
3017 Positions the view at the beginning or end, taking into account any header or footer.
3019 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3020 at a particular index. This is unreliable since removing items from the start
3021 of the list does not cause all other items to be repositioned, and because
3022 the actual start of the view can vary based on the size of the delegates.
3024 \bold Note: methods should only be called after the Component has completed. To position
3025 the view at startup, this method should be called by Component.onCompleted. For
3026 example, to position the view at the end on startup:
3029 Component.onCompleted: positionViewAtEnd()
3032 void QDeclarativeListView::positionViewAtBeginning()
3034 Q_D(QDeclarativeListView);
3037 d->positionViewAtIndex(-1, Beginning);
3040 void QDeclarativeListView::positionViewAtEnd()
3042 Q_D(QDeclarativeListView);
3045 d->positionViewAtIndex(d->model->count(), End);
3049 \qmlmethod int ListView::indexAt(int x, int y)
3051 Returns the index of the visible item containing the point \a x, \a y in content
3052 coordinates. If there is no item at the point specified, or the item is
3053 not visible -1 is returned.
3055 If the item is outside the visible area, -1 is returned, regardless of
3056 whether an item will exist at that point when scrolled into view.
3058 \bold Note: methods should only be called after the Component has completed.
3060 int QDeclarativeListView::indexAt(qreal x, qreal y) const
3062 Q_D(const QDeclarativeListView);
3063 for (int i = 0; i < d->visibleItems.count(); ++i) {
3064 const FxListItem *listItem = d->visibleItems.at(i);
3065 if(listItem->contains(x, y))
3066 return listItem->index;
3072 void QDeclarativeListView::componentComplete()
3074 Q_D(QDeclarativeListView);
3075 QDeclarativeFlickable::componentComplete();
3081 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3082 if (d->currentIndex < 0 && !d->currentIndexCleared)
3083 d->updateCurrent(0);
3085 d->updateCurrent(d->currentIndex);
3086 if (d->highlight && d->currentItem) {
3087 if (d->autoHighlight)
3088 d->highlight->setPosition(d->currentItem->position());
3089 d->updateTrackedItem();
3091 d->moveReason = QDeclarativeListViewPrivate::Other;
3096 void QDeclarativeListView::updateSections()
3098 Q_D(QDeclarativeListView);
3099 if (isComponentComplete() && d->model) {
3100 QList<QByteArray> roles;
3101 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3102 roles << d->sectionCriteria->property().toUtf8();
3103 d->model->setWatchedRoles(roles);
3104 d->updateSections();
3110 void QDeclarativeListView::refill()
3112 Q_D(QDeclarativeListView);
3113 if (d->isRightToLeft())
3114 d->refill(-d->position()-d->size()+1, -d->position());
3116 d->refill(d->position(), d->position()+d->size()-1);
3119 void QDeclarativeListView::trackedPositionChanged()
3121 Q_D(QDeclarativeListView);
3122 if (!d->trackedItem || !d->currentItem)
3124 if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) {
3125 qreal trackedPos = qCeil(d->trackedItem->position());
3126 qreal trackedSize = d->trackedItem->size();
3127 if (d->trackedItem != d->currentItem) {
3128 trackedPos -= d->currentItem->sectionSize();
3129 trackedSize += d->currentItem->sectionSize();
3132 qreal highlightStart;
3134 if (d->isRightToLeft()) {
3135 viewPos = -d->position()-d->size();
3136 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3137 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3139 viewPos = d->position();
3140 highlightStart = d->highlightRangeStart;
3141 highlightEnd = d->highlightRangeEnd;
3143 qreal pos = viewPos;
3144 if (d->haveHighlightRange) {
3145 if (d->highlightRange == StrictlyEnforceRange) {
3146 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3147 pos = trackedPos - highlightEnd + d->trackedItem->size();
3148 if (trackedPos < pos + highlightStart)
3149 pos = trackedPos - highlightStart;
3151 if (trackedPos < d->startPosition() + highlightStart) {
3152 pos = d->startPosition();
3153 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3154 pos = d->endPosition() - d->size() + 1;
3155 if (pos < d->startPosition())
3156 pos = d->startPosition();
3158 if (trackedPos < viewPos + highlightStart) {
3159 pos = trackedPos - highlightStart;
3160 } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
3161 pos = trackedPos - highlightEnd + trackedSize;
3166 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
3167 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
3168 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
3169 && d->currentItem->endPosition() >= viewPos + d->size()) {
3170 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
3171 pos = d->trackedItem->endPosition() - d->size() + 1;
3172 if (trackedSize > d->size())
3175 pos = d->currentItem->endPosition() - d->size() + 1;
3176 if (d->currentItem->size() > d->size())
3177 pos = d->currentItem->position();
3181 if (viewPos != pos) {
3183 d->calcVelocity = true;
3184 d->setPosition(pos);
3185 d->calcVelocity = false;
3190 void QDeclarativeListView::itemsInserted(int modelIndex, int count)
3192 Q_D(QDeclarativeListView);
3193 if (!isComponentComplete())
3195 d->updateUnrequestedIndexes();
3196 d->moveReason = QDeclarativeListViewPrivate::Other;
3198 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
3199 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
3202 int i = d->visibleItems.count() - 1;
3203 while (i > 0 && d->visibleItems.at(i)->index == -1)
3205 if (i == 0 && d->visibleItems.first()->index == -1) {
3206 // there are no visible items except items marked for removal
3207 index = d->visibleItems.count();
3208 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
3209 && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
3210 // Special case of appending an item to the model.
3211 index = d->visibleItems.count();
3213 if (modelIndex < d->visibleIndex) {
3214 // Insert before visible items
3215 d->visibleIndex += count;
3216 for (int i = 0; i < d->visibleItems.count(); ++i) {
3217 FxListItem *listItem = d->visibleItems.at(i);
3218 if (listItem->index != -1 && listItem->index >= modelIndex)
3219 listItem->index += count;
3222 if (d->currentIndex >= modelIndex) {
3223 // adjust current item index
3224 d->currentIndex += count;
3226 d->currentItem->index = d->currentIndex;
3227 emit currentIndexChanged();
3229 d->scheduleLayout();
3230 d->itemCount += count;
3231 emit countChanged();
3236 // index can be the next item past the end of the visible items list (i.e. appended)
3238 if (d->visibleItems.count()) {
3239 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
3240 : d->visibleItems.last()->endPosition()+d->spacing+1;
3241 } else if (d->itemCount == 0 && d->header) {
3242 pos = d->header->size();
3245 int initialPos = pos;
3247 QList<FxListItem*> added;
3248 bool addedVisible = false;
3249 FxListItem *firstVisible = d->firstVisibleItem();
3250 if (firstVisible && pos < firstVisible->position()) {
3251 // Insert items before the visible item.
3252 int insertionIdx = index;
3254 int from = tempPos - d->buffer;
3255 for (i = count-1; i >= 0 && pos > from; --i) {
3256 if (!addedVisible) {
3257 d->scheduleLayout();
3258 addedVisible = true;
3260 FxListItem *item = d->createItem(modelIndex + i);
3261 d->visibleItems.insert(insertionIdx, item);
3262 pos -= item->size() + d->spacing;
3263 item->setPosition(pos);
3267 // If we didn't insert all our new items - anything
3268 // before the current index is not visible - remove it.
3269 while (insertionIdx--) {
3270 FxListItem *item = d->visibleItems.takeFirst();
3271 if (item->index != -1)
3273 d->releaseItem(item);
3276 // adjust pos of items before inserted items.
3277 for (int i = insertionIdx-1; i >= 0; i--) {
3278 FxListItem *listItem = d->visibleItems.at(i);
3279 listItem->setPosition(listItem->position() - (initialPos - pos));
3284 int to = d->buffer+tempPos+d->size()-1;
3285 for (i = 0; i < count && pos <= to; ++i) {
3286 if (!addedVisible) {
3287 d->scheduleLayout();
3288 addedVisible = true;
3290 FxListItem *item = d->createItem(modelIndex + i);
3291 d->visibleItems.insert(index, item);
3292 item->setPosition(pos);
3294 pos += item->size() + d->spacing;
3298 // We didn't insert all our new items, which means anything
3299 // beyond the current index is not visible - remove it.
3300 while (d->visibleItems.count() > index)
3301 d->releaseItem(d->visibleItems.takeLast());
3303 diff = pos - initialPos;
3305 if (d->itemCount && d->currentIndex >= modelIndex) {
3306 // adjust current item index
3307 d->currentIndex += count;
3308 if (d->currentItem) {
3309 d->currentItem->index = d->currentIndex;
3310 d->currentItem->setPosition(d->currentItem->position() + diff);
3312 emit currentIndexChanged();
3313 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
3314 d->updateCurrent(0);
3316 // Update the indexes of the following visible items.
3317 for (; index < d->visibleItems.count(); ++index) {
3318 FxListItem *listItem = d->visibleItems.at(index);
3319 if (d->currentItem && listItem->item != d->currentItem->item)
3320 listItem->setPosition(listItem->position() + diff);
3321 if (listItem->index != -1)
3322 listItem->index += count;
3324 // everything is in order now - emit add() signal
3325 for (int j = 0; j < added.count(); ++j)
3326 added.at(j)->attached->emitAdd();
3328 d->updateSections();
3329 d->itemCount += count;
3330 emit countChanged();
3333 void QDeclarativeListView::itemsRemoved(int modelIndex, int count)
3335 Q_D(QDeclarativeListView);
3336 if (!isComponentComplete())
3338 d->moveReason = QDeclarativeListViewPrivate::Other;
3339 d->updateUnrequestedIndexes();
3340 d->itemCount -= count;
3342 FxListItem *firstVisible = d->firstVisibleItem();
3343 int preRemovedSize = 0;
3344 bool removedVisible = false;
3345 // Remove the items from the visible list, skipping anything already marked for removal
3346 QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3347 while (it != d->visibleItems.end()) {
3348 FxListItem *item = *it;
3349 if (item->index == -1 || item->index < modelIndex) {
3350 // already removed, or before removed items
3352 } else if (item->index >= modelIndex + count) {
3353 // after removed items
3354 item->index -= count;
3358 if (!removedVisible) {
3359 d->scheduleLayout();
3360 removedVisible = true;
3362 item->attached->emitRemove();
3363 if (item->attached->delayRemove()) {
3365 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
3368 if (item == firstVisible)
3370 if (firstVisible && item->position() < firstVisible->position())
3371 preRemovedSize += item->size();
3372 it = d->visibleItems.erase(it);
3373 d->releaseItem(item);
3378 if (firstVisible && d->visibleItems.first() != firstVisible)
3379 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
3382 if (d->currentIndex >= modelIndex + count) {
3383 d->currentIndex -= count;
3385 d->currentItem->index -= count;
3386 emit currentIndexChanged();
3387 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
3388 // current item has been removed.
3389 d->currentItem->attached->setIsCurrentItem(false);
3390 d->releaseItem(d->currentItem);
3392 d->currentIndex = -1;
3394 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
3396 emit currentIndexChanged();
3399 // update visibleIndex
3400 bool haveVisibleIndex = false;
3401 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3402 if ((*it)->index != -1) {
3403 d->visibleIndex = (*it)->index;
3404 haveVisibleIndex = true;
3409 if (removedVisible && !haveVisibleIndex) {
3410 d->timeline.clear();
3411 if (d->itemCount == 0) {
3412 d->visibleIndex = 0;
3413 d->visiblePos = d->header ? d->header->size() : 0;
3419 if (modelIndex < d->visibleIndex)
3420 d->visibleIndex = modelIndex+1;
3421 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3425 d->updateSections();
3426 emit countChanged();
3429 void QDeclarativeListView::destroyRemoved()
3431 Q_D(QDeclarativeListView);
3432 for (QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3433 it != d->visibleItems.end();) {
3434 FxListItem *listItem = *it;
3435 if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3436 d->releaseItem(listItem);
3437 it = d->visibleItems.erase(it);
3443 // Correct the positioning of the items
3444 d->updateSections();
3448 void QDeclarativeListView::itemsMoved(int from, int to, int count)
3450 Q_D(QDeclarativeListView);
3451 if (!isComponentComplete())
3453 d->updateUnrequestedIndexes();
3455 if (d->visibleItems.isEmpty()) {
3460 d->moveReason = QDeclarativeListViewPrivate::Other;
3461 FxListItem *firstVisible = d->firstVisibleItem();
3462 qreal firstItemPos = firstVisible->position();
3463 QHash<int,FxListItem*> moved;
3466 QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3467 while (it != d->visibleItems.end()) {
3468 FxListItem *item = *it;
3469 if (item->index >= from && item->index < from + count) {
3470 // take the items that are moving
3471 item->index += (to-from);
3472 moved.insert(item->index, item);
3473 if (item->position() < firstItemPos)
3474 moveBy += item->size();
3475 it = d->visibleItems.erase(it);
3477 // move everything after the moved items.
3478 if (item->index > from && item->index != -1)
3479 item->index -= count;
3484 int remaining = count;
3485 int endIndex = d->visibleIndex;
3486 it = d->visibleItems.begin();
3487 while (it != d->visibleItems.end()) {
3488 FxListItem *item = *it;
3489 if (remaining && item->index >= to && item->index < to + count) {
3490 // place items in the target position, reusing any existing items
3491 FxListItem *movedItem = moved.take(item->index);
3493 movedItem = d->createItem(item->index);
3494 if (item->index <= firstVisible->index)
3495 moveBy -= movedItem->size();
3496 it = d->visibleItems.insert(it, movedItem);
3500 if (item->index != -1) {
3501 if (item->index >= to) {
3502 // update everything after the moved items.
3503 item->index += count;
3505 endIndex = item->index;
3511 // If we have moved items to the end of the visible items
3512 // then add any existing moved items that we have
3513 while (FxListItem *item = moved.take(endIndex+1)) {
3514 d->visibleItems.append(item);
3518 // update visibleIndex
3519 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3520 if ((*it)->index != -1) {
3521 d->visibleIndex = (*it)->index;
3526 // Fix current index
3527 if (d->currentIndex >= 0 && d->currentItem) {
3528 int oldCurrent = d->currentIndex;
3529 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3530 if (oldCurrent != d->currentIndex) {
3531 d->currentItem->index = d->currentIndex;
3532 emit currentIndexChanged();
3536 // Whatever moved items remain are no longer visible items.
3537 while (moved.count()) {
3538 int idx = moved.begin().key();
3539 FxListItem *item = moved.take(idx);
3540 if (d->currentItem && item->item == d->currentItem->item)
3541 item->setPosition(d->positionAt(idx));
3542 d->releaseItem(item);
3545 // Ensure we don't cause an ugly list scroll.
3546 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3548 d->updateSections();
3552 void QDeclarativeListView::itemsChanged(int, int)
3554 Q_D(QDeclarativeListView);
3555 d->updateSections();
3559 void QDeclarativeListView::modelReset()
3561 Q_D(QDeclarativeListView);
3562 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3564 if (d->highlight && d->currentItem) {
3565 if (d->autoHighlight)
3566 d->highlight->setPosition(d->currentItem->position());
3567 d->updateTrackedItem();
3569 d->moveReason = QDeclarativeListViewPrivate::Other;
3570 emit countChanged();
3573 void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item)
3575 Q_D(QDeclarativeListView);
3576 if (d->requestedIndex != index) {
3577 item->setParentItem(contentItem());
3578 d->unrequestedItems.insert(item, index);
3579 if (d->orient == QDeclarativeListView::Vertical) {
3580 item->setY(d->positionAt(index));
3582 if (d->isRightToLeft())
3583 item->setX(-d->positionAt(index)-item->width());
3585 item->setX(d->positionAt(index));
3590 void QDeclarativeListView::destroyingItem(QDeclarativeItem *item)
3592 Q_D(QDeclarativeListView);
3593 d->unrequestedItems.remove(item);
3596 void QDeclarativeListView::animStopped()
3598 Q_D(QDeclarativeListView);
3599 d->bufferMode = QDeclarativeListViewPrivate::NoBuffer;
3600 if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange)
3601 d->updateHighlight();
3604 QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj)
3606 return new QDeclarativeListViewAttached(obj);