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 dataModel->setDelegate(delegate);
1817 if (isComponentComplete()) {
1818 for (int i = 0; i < d->visibleItems.count(); ++i)
1819 d->releaseItem(d->visibleItems.at(i));
1820 d->visibleItems.clear();
1821 d->releaseItem(d->currentItem);
1825 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1826 d->updateCurrent(d->currentIndex);
1827 if (d->highlight && d->currentItem) {
1828 if (d->autoHighlight)
1829 d->highlight->setPosition(d->currentItem->position());
1830 d->updateTrackedItem();
1832 d->updateViewport();
1835 emit delegateChanged();
1839 \qmlproperty int ListView::currentIndex
1840 \qmlproperty Item ListView::currentItem
1842 The \c currentIndex property holds the index of the current item, and
1843 \c currentItem holds the current item. Setting the currentIndex to -1
1844 will clear the highlight and set currentItem to null.
1846 If highlightFollowsCurrentItem is \c true, setting either of these
1847 properties will smoothly scroll the ListView so that the current
1848 item becomes visible.
1850 Note that the position of the current item
1851 may only be approximate until it becomes visible in the view.
1853 int QDeclarativeListView::currentIndex() const
1855 Q_D(const QDeclarativeListView);
1856 return d->currentIndex;
1859 void QDeclarativeListView::setCurrentIndex(int index)
1861 Q_D(QDeclarativeListView);
1862 if (d->requestedIndex >= 0) // currently creating item
1864 d->currentIndexCleared = (index == -1);
1865 if (index == d->currentIndex)
1867 if (isComponentComplete() && d->isValid()) {
1868 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1869 d->updateCurrent(index);
1870 } else if (d->currentIndex != index) {
1871 d->currentIndex = index;
1872 emit currentIndexChanged();
1876 QDeclarativeItem *QDeclarativeListView::currentItem()
1878 Q_D(QDeclarativeListView);
1879 if (!d->currentItem)
1881 return d->currentItem->item;
1885 \qmlproperty Item ListView::highlightItem
1887 This holds the highlight item created from the \l highlight component.
1889 The \c highlightItem is managed by the view unless
1890 \l highlightFollowsCurrentItem is set to false.
1892 \sa highlight, highlightFollowsCurrentItem
1894 QDeclarativeItem *QDeclarativeListView::highlightItem()
1896 Q_D(QDeclarativeListView);
1899 return d->highlight->item;
1903 \qmlproperty int ListView::count
1904 This property holds the number of items in the view.
1906 int QDeclarativeListView::count() const
1908 Q_D(const QDeclarativeListView);
1910 return d->model->count();
1915 \qmlproperty Component ListView::highlight
1916 This property holds the component to use as the highlight.
1918 An instance of the highlight component is created for each list.
1919 The geometry of the resulting component instance is managed by the list
1920 so as to stay with the current item, unless the highlightFollowsCurrentItem
1923 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1925 QDeclarativeComponent *QDeclarativeListView::highlight() const
1927 Q_D(const QDeclarativeListView);
1928 return d->highlightComponent;
1931 void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight)
1933 Q_D(QDeclarativeListView);
1934 if (highlight != d->highlightComponent) {
1935 d->highlightComponent = highlight;
1936 d->createHighlight();
1938 d->updateHighlight();
1939 emit highlightChanged();
1944 \qmlproperty bool ListView::highlightFollowsCurrentItem
1945 This property holds whether the highlight is managed by the view.
1947 If this property is true (the default value), the highlight is moved smoothly
1948 to follow the current item. Otherwise, the
1949 highlight is not moved by the view, and any movement must be implemented
1952 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1954 \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1956 Note that the highlight animation also affects the way that the view
1957 is scrolled. This is because the view moves to maintain the
1958 highlight within the preferred highlight range (or visible viewport).
1960 \sa highlight, highlightMoveSpeed
1962 bool QDeclarativeListView::highlightFollowsCurrentItem() const
1964 Q_D(const QDeclarativeListView);
1965 return d->autoHighlight;
1968 void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1970 Q_D(QDeclarativeListView);
1971 if (d->autoHighlight != autoHighlight) {
1972 d->autoHighlight = autoHighlight;
1973 if (autoHighlight) {
1974 d->updateHighlight();
1976 if (d->highlightPosAnimator)
1977 d->highlightPosAnimator->stop();
1978 if (d->highlightSizeAnimator)
1979 d->highlightSizeAnimator->stop();
1981 emit highlightFollowsCurrentItemChanged();
1985 //###Possibly rename these properties, since they are very useful even without a highlight?
1987 \qmlproperty real ListView::preferredHighlightBegin
1988 \qmlproperty real ListView::preferredHighlightEnd
1989 \qmlproperty enumeration ListView::highlightRangeMode
1991 These properties define the preferred range of the highlight (for the current item)
1992 within the view. The \c preferredHighlightBegin value must be less than the
1993 \c preferredHighlightEnd value.
1995 These properties affect the position of the current item when the list is scrolled.
1996 For example, if the currently selected item should stay in the middle of the
1997 list when the view is scrolled, set the \c preferredHighlightBegin and
1998 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1999 item would be. If the \c currentItem is changed programmatically, the list will
2000 automatically scroll so that the current item is in the middle of the view.
2001 Furthermore, the behavior of the current item index will occur whether or not a
2004 Valid values for \c highlightRangeMode are:
2007 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2008 However, the highlight can move outside of the range at the ends of the list or due
2009 to mouse interaction.
2010 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2011 The current item changes if a keyboard or mouse action would cause the highlight to move
2012 outside of the range.
2013 \o ListView.NoHighlightRange - this is the default value.
2016 qreal QDeclarativeListView::preferredHighlightBegin() const
2018 Q_D(const QDeclarativeListView);
2019 return d->highlightRangeStart;
2022 void QDeclarativeListView::setPreferredHighlightBegin(qreal start)
2024 Q_D(QDeclarativeListView);
2025 d->highlightRangeStartValid = true;
2026 if (d->highlightRangeStart == start)
2028 d->highlightRangeStart = start;
2029 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2030 emit preferredHighlightBeginChanged();
2033 void QDeclarativeListView::resetPreferredHighlightBegin()
2035 Q_D(QDeclarativeListView);
2036 d->highlightRangeStartValid = false;
2037 if (d->highlightRangeStart == 0)
2039 d->highlightRangeStart = 0;
2040 emit preferredHighlightBeginChanged();
2043 qreal QDeclarativeListView::preferredHighlightEnd() const
2045 Q_D(const QDeclarativeListView);
2046 return d->highlightRangeEnd;
2049 void QDeclarativeListView::setPreferredHighlightEnd(qreal end)
2051 Q_D(QDeclarativeListView);
2052 d->highlightRangeEndValid = true;
2053 if (d->highlightRangeEnd == end)
2055 d->highlightRangeEnd = end;
2056 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2057 emit preferredHighlightEndChanged();
2060 void QDeclarativeListView::resetPreferredHighlightEnd()
2062 Q_D(QDeclarativeListView);
2063 d->highlightRangeEndValid = false;
2064 if (d->highlightRangeEnd == 0)
2066 d->highlightRangeEnd = 0;
2067 emit preferredHighlightEndChanged();
2070 QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
2072 Q_D(const QDeclarativeListView);
2073 return d->highlightRange;
2076 void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode)
2078 Q_D(QDeclarativeListView);
2079 if (d->highlightRange == mode)
2081 d->highlightRange = mode;
2082 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2083 emit highlightRangeModeChanged();
2087 \qmlproperty real ListView::spacing
2089 This property holds the spacing between items.
2091 The default value is 0.
2093 qreal QDeclarativeListView::spacing() const
2095 Q_D(const QDeclarativeListView);
2099 void QDeclarativeListView::setSpacing(qreal spacing)
2101 Q_D(QDeclarativeListView);
2102 if (spacing != d->spacing) {
2103 d->spacing = spacing;
2105 emit spacingChanged();
2110 \qmlproperty enumeration ListView::orientation
2111 This property holds the orientation of the list.
2116 \o ListView.Horizontal - Items are laid out horizontally
2117 \o ListView.Vertical (default) - Items are laid out vertically
2122 \o Horizontal orientation:
2123 \image ListViewHorizontal.png
2126 \o Vertical orientation:
2127 \image listview-highlight.png
2130 QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
2132 Q_D(const QDeclarativeListView);
2136 void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation)
2138 Q_D(QDeclarativeListView);
2139 if (d->orient != orientation) {
2140 d->orient = orientation;
2141 if (d->orient == QDeclarativeListView::Vertical) {
2142 setContentWidth(-1);
2143 setFlickableDirection(VerticalFlick);
2146 setContentHeight(-1);
2147 setFlickableDirection(HorizontalFlick);
2151 emit orientationChanged();
2156 \qmlproperty enumeration ListView::layoutDirection
2157 This property holds the layout direction of the horizontal list.
2162 \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2163 \o Qt.RightToLeft - Items will be laid out from right to let.
2166 \sa ListView::effectiveLayoutDirection
2169 Qt::LayoutDirection QDeclarativeListView::layoutDirection() const
2171 Q_D(const QDeclarativeListView);
2172 return d->layoutDirection;
2175 void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2177 Q_D(QDeclarativeListView);
2178 if (d->layoutDirection != layoutDirection) {
2179 d->layoutDirection = layoutDirection;
2181 emit layoutDirectionChanged();
2182 emit effectiveLayoutDirectionChanged();
2187 \qmlproperty enumeration ListView::effectiveLayoutDirection
2188 This property holds the effective layout direction of the horizontal list.
2190 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2191 the visual layout direction of the horizontal list will be mirrored. However, the
2192 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2194 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2197 Qt::LayoutDirection QDeclarativeListView::effectiveLayoutDirection() const
2199 Q_D(const QDeclarativeListView);
2200 if (d->effectiveLayoutMirror)
2201 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2203 return d->layoutDirection;
2207 \qmlproperty bool ListView::keyNavigationWraps
2208 This property holds whether the list wraps key navigation.
2210 If this is true, key navigation that would move the current item selection
2211 past the end of the list instead wraps around and moves the selection to
2212 the start of the list, and vice-versa.
2214 By default, key navigation is not wrapped.
2216 bool QDeclarativeListView::isWrapEnabled() const
2218 Q_D(const QDeclarativeListView);
2222 void QDeclarativeListView::setWrapEnabled(bool wrap)
2224 Q_D(QDeclarativeListView);
2225 if (d->wrap == wrap)
2228 emit keyNavigationWrapsChanged();
2232 \qmlproperty int ListView::cacheBuffer
2233 This property determines whether delegates are retained outside the
2234 visible area of the view.
2236 If this value is non-zero, the view keeps as many delegates
2237 instantiated as it can fit within the buffer specified. For example,
2238 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2239 set to 40, then up to 2 delegates above and 2 delegates below the visible
2240 area may be retained.
2242 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2243 instantiated delegates.
2245 Setting this value can improve the smoothness of scrolling behavior at the expense
2246 of additional memory usage. It is not a substitute for creating efficient
2247 delegates; the fewer elements in a delegate, the faster a view can be
2250 int QDeclarativeListView::cacheBuffer() const
2252 Q_D(const QDeclarativeListView);
2256 void QDeclarativeListView::setCacheBuffer(int b)
2258 Q_D(QDeclarativeListView);
2259 if (d->buffer != b) {
2261 if (isComponentComplete()) {
2262 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
2265 emit cacheBufferChanged();
2270 \qmlproperty string ListView::section.property
2271 \qmlproperty enumeration ListView::section.criteria
2272 \qmlproperty Component ListView::section.delegate
2274 These properties hold the expression to be evaluated for the \l section attached property.
2276 The \l section attached property enables a ListView to be visually
2277 separated into different parts. These properties determine how sections
2280 \c section.property holds the name of the property that is the basis
2283 \c section.criteria holds the criteria for forming each section based on
2284 \c section.property. This value can be one of:
2287 \o ViewSection.FullString (default) - sections are created based on the
2288 \c section.property value.
2289 \o ViewSection.FirstCharacter - sections are created based on the first
2290 character of the \c section.property value (for example, 'A', 'B', 'C'
2291 sections, etc. for an address book)
2294 \c section.delegate holds the delegate component for each section.
2296 Each item in the list has attached properties named \c ListView.section,
2297 \c ListView.previousSection and \c ListView.nextSection. These may be
2298 used to place a section header for related items.
2300 For example, here is a ListView that displays a list of animals, separated
2301 into sections. Each item in the ListView is placed in a different section
2302 depending on the "size" property of the model item. The \c sectionHeading
2303 delegate component provides the light blue bar that marks the beginning of
2307 \snippet examples/declarative/modelviews/listview/sections.qml 0
2309 \image qml-listview-sections-example.png
2311 \note Adding sections to a ListView does not automatically re-order the
2312 list items by the section criteria.
2313 If the model is not ordered by section, then it is possible that
2314 the sections created will not be unique; each boundary between
2315 differing sections will result in a section header being created
2316 even if that section exists elsewhere.
2318 \sa {declarative/modelviews/listview}{ListView examples}
2320 QDeclarativeViewSection *QDeclarativeListView::sectionCriteria()
2322 Q_D(QDeclarativeListView);
2323 if (!d->sectionCriteria) {
2324 d->sectionCriteria = new QDeclarativeViewSection(this);
2325 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2327 return d->sectionCriteria;
2331 \qmlproperty string ListView::currentSection
2332 This property holds the section that is currently at the beginning of the view.
2334 QString QDeclarativeListView::currentSection() const
2336 Q_D(const QDeclarativeListView);
2337 return d->currentSection;
2341 \qmlproperty real ListView::highlightMoveSpeed
2342 \qmlproperty int ListView::highlightMoveDuration
2343 \qmlproperty real ListView::highlightResizeSpeed
2344 \qmlproperty int ListView::highlightResizeDuration
2346 These properties hold the move and resize animation speed of the highlight delegate.
2348 \l highlightFollowsCurrentItem must be true for these properties
2351 The default value for the speed properties is 400 pixels/second.
2352 The default value for the duration properties is -1, i.e. the
2353 highlight will take as much time as necessary to move at the set speed.
2355 These properties have the same characteristics as a SmoothedAnimation.
2357 \sa highlightFollowsCurrentItem
2359 qreal QDeclarativeListView::highlightMoveSpeed() const
2361 Q_D(const QDeclarativeListView);\
2362 return d->highlightMoveSpeed;
2365 void QDeclarativeListView::setHighlightMoveSpeed(qreal speed)
2367 Q_D(QDeclarativeListView);\
2368 if (d->highlightMoveSpeed != speed) {
2369 d->highlightMoveSpeed = speed;
2370 if (d->highlightPosAnimator)
2371 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2372 emit highlightMoveSpeedChanged();
2376 int QDeclarativeListView::highlightMoveDuration() const
2378 Q_D(const QDeclarativeListView);
2379 return d->highlightMoveDuration;
2382 void QDeclarativeListView::setHighlightMoveDuration(int duration)
2384 Q_D(QDeclarativeListView);\
2385 if (d->highlightMoveDuration != duration) {
2386 d->highlightMoveDuration = duration;
2387 if (d->highlightPosAnimator)
2388 d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2389 emit highlightMoveDurationChanged();
2393 qreal QDeclarativeListView::highlightResizeSpeed() const
2395 Q_D(const QDeclarativeListView);\
2396 return d->highlightResizeSpeed;
2399 void QDeclarativeListView::setHighlightResizeSpeed(qreal speed)
2401 Q_D(QDeclarativeListView);\
2402 if (d->highlightResizeSpeed != speed) {
2403 d->highlightResizeSpeed = speed;
2404 if (d->highlightSizeAnimator)
2405 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2406 emit highlightResizeSpeedChanged();
2410 int QDeclarativeListView::highlightResizeDuration() const
2412 Q_D(const QDeclarativeListView);
2413 return d->highlightResizeDuration;
2416 void QDeclarativeListView::setHighlightResizeDuration(int duration)
2418 Q_D(QDeclarativeListView);\
2419 if (d->highlightResizeDuration != duration) {
2420 d->highlightResizeDuration = duration;
2421 if (d->highlightSizeAnimator)
2422 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2423 emit highlightResizeDurationChanged();
2428 \qmlproperty enumeration ListView::snapMode
2430 This property determines how the view scrolling will settle following a drag or flick.
2431 The possible values are:
2434 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2435 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2437 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2438 visible item at the time the mouse button is released. This mode is particularly
2439 useful for moving one page at a time.
2442 \c snapMode does not affect the \l currentIndex. To update the
2443 \l currentIndex as the list is moved, set \l highlightRangeMode
2444 to \c ListView.StrictlyEnforceRange.
2446 \sa highlightRangeMode
2448 QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
2450 Q_D(const QDeclarativeListView);
2454 void QDeclarativeListView::setSnapMode(SnapMode mode)
2456 Q_D(QDeclarativeListView);
2457 if (d->snapMode != mode) {
2459 emit snapModeChanged();
2464 \qmlproperty Component ListView::footer
2465 This property holds the component to use as the footer.
2467 An instance of the footer component is created for each view. The
2468 footer is positioned at the end of the view, after any items.
2472 QDeclarativeComponent *QDeclarativeListView::footer() const
2474 Q_D(const QDeclarativeListView);
2475 return d->footerComponent;
2478 void QDeclarativeListView::setFooter(QDeclarativeComponent *footer)
2480 Q_D(QDeclarativeListView);
2481 if (d->footerComponent != footer) {
2484 scene()->removeItem(d->footer->item);
2485 d->footer->item->deleteLater();
2489 d->footerComponent = footer;
2490 d->minExtentDirty = true;
2491 d->maxExtentDirty = true;
2492 if (isComponentComplete()) {
2494 d->updateViewport();
2497 emit footerChanged();
2502 \qmlproperty Component ListView::header
2503 This property holds the component to use as the header.
2505 An instance of the header component is created for each view. The
2506 header is positioned at the beginning of the view, before any items.
2510 QDeclarativeComponent *QDeclarativeListView::header() const
2512 Q_D(const QDeclarativeListView);
2513 return d->headerComponent;
2516 void QDeclarativeListView::setHeader(QDeclarativeComponent *header)
2518 Q_D(QDeclarativeListView);
2519 if (d->headerComponent != header) {
2522 scene()->removeItem(d->header->item);
2523 d->header->item->deleteLater();
2527 d->headerComponent = header;
2528 d->minExtentDirty = true;
2529 d->maxExtentDirty = true;
2530 if (isComponentComplete()) {
2533 d->updateViewport();
2536 emit headerChanged();
2540 void QDeclarativeListView::setContentX(qreal pos)
2542 Q_D(QDeclarativeListView);
2543 // Positioning the view manually should override any current movement state
2544 d->moveReason = QDeclarativeListViewPrivate::Other;
2545 QDeclarativeFlickable::setContentX(pos);
2548 void QDeclarativeListView::setContentY(qreal pos)
2550 Q_D(QDeclarativeListView);
2551 // Positioning the view manually should override any current movement state
2552 d->moveReason = QDeclarativeListViewPrivate::Other;
2553 QDeclarativeFlickable::setContentY(pos);
2556 bool QDeclarativeListView::event(QEvent *event)
2558 Q_D(QDeclarativeListView);
2559 if (event->type() == QEvent::User) {
2564 return QDeclarativeFlickable::event(event);
2567 void QDeclarativeListView::viewportMoved()
2569 Q_D(QDeclarativeListView);
2570 QDeclarativeFlickable::viewportMoved();
2573 // Recursion can occur due to refill changing the content size.
2574 if (d->inViewportMoved)
2576 d->inViewportMoved = true;
2577 d->lazyRelease = true;
2579 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2580 d->moveReason = QDeclarativeListViewPrivate::Mouse;
2581 if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) {
2582 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2583 // reposition highlight
2584 qreal pos = d->highlight->position();
2586 qreal highlightStart;
2588 if (d->isRightToLeft()) {
2589 // Handle Right-To-Left exceptions
2590 viewPos = -d->position()-d->size();
2591 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2592 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2594 viewPos = d->position();
2595 highlightStart = d->highlightRangeStart;
2596 highlightEnd = d->highlightRangeEnd;
2598 if (pos > viewPos + highlightEnd - d->highlight->size())
2599 pos = viewPos + highlightEnd - d->highlight->size();
2600 if (pos < viewPos + highlightStart)
2601 pos = viewPos + highlightStart;
2602 d->highlightPosAnimator->stop();
2603 d->highlight->setPosition(qRound(pos));
2605 // update current index
2606 if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) {
2607 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2608 d->updateCurrent(snapItem->index);
2613 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2614 d->inFlickCorrection = true;
2615 // Near an end and it seems that the extent has changed?
2616 // Recalculate the flick so that we don't end up in an odd position.
2617 if (yflick() && !d->vData.inOvershoot) {
2618 if (d->vData.velocity > 0) {
2619 const qreal minY = minYExtent();
2620 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2621 && minY != d->vData.flickTarget)
2622 d->flickY(-d->vData.smoothVelocity.value());
2623 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2624 } else if (d->vData.velocity < 0) {
2625 const qreal maxY = maxYExtent();
2626 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2627 && maxY != d->vData.flickTarget)
2628 d->flickY(-d->vData.smoothVelocity.value());
2629 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2633 if (xflick() && !d->hData.inOvershoot) {
2634 if (d->hData.velocity > 0) {
2635 const qreal minX = minXExtent();
2636 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2637 && minX != d->hData.flickTarget)
2638 d->flickX(-d->hData.smoothVelocity.value());
2639 d->bufferMode = d->isRightToLeft()
2640 ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::BufferBefore;
2641 } else if (d->hData.velocity < 0) {
2642 const qreal maxX = maxXExtent();
2643 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2644 && maxX != d->hData.flickTarget)
2645 d->flickX(-d->hData.smoothVelocity.value());
2646 d->bufferMode = d->isRightToLeft()
2647 ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter;
2650 d->inFlickCorrection = false;
2652 d->inViewportMoved = false;
2655 qreal QDeclarativeListView::minYExtent() const
2657 Q_D(const QDeclarativeListView);
2658 if (d->orient == QDeclarativeListView::Horizontal)
2659 return QDeclarativeFlickable::minYExtent();
2660 if (d->minExtentDirty) {
2661 d->minExtent = -d->startPosition();
2662 if (d->header && d->visibleItems.count())
2663 d->minExtent += d->header->size();
2664 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2665 d->minExtent += d->highlightRangeStart;
2666 if (d->sectionCriteria) {
2667 if (d->visibleItem(0))
2668 d->minExtent -= d->visibleItem(0)->sectionSize();
2670 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2672 d->minExtentDirty = false;
2675 return d->minExtent;
2678 qreal QDeclarativeListView::maxYExtent() const
2680 Q_D(const QDeclarativeListView);
2681 if (d->orient == QDeclarativeListView::Horizontal)
2683 if (d->maxExtentDirty) {
2684 if (!d->model || !d->model->count()) {
2685 d->maxExtent = d->header ? -d->header->size() : 0;
2686 d->maxExtent += height();
2687 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2688 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2689 if (d->highlightRangeEnd != d->highlightRangeStart)
2690 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2692 d->maxExtent = -(d->endPosition() - height() + 1);
2695 d->maxExtent -= d->footer->size();
2696 qreal minY = minYExtent();
2697 if (d->maxExtent > minY)
2698 d->maxExtent = minY;
2699 d->maxExtentDirty = false;
2701 return d->maxExtent;
2704 qreal QDeclarativeListView::minXExtent() const
2706 Q_D(const QDeclarativeListView);
2707 if (d->orient == QDeclarativeListView::Vertical)
2708 return QDeclarativeFlickable::minXExtent();
2709 if (d->minExtentDirty) {
2710 d->minExtent = -d->startPosition();
2712 qreal highlightStart;
2714 qreal endPositionFirstItem = 0;
2715 if (d->isRightToLeft()) {
2716 if (d->model && d->model->count())
2717 endPositionFirstItem = d->positionAt(d->model->count()-1);
2719 d->minExtent += d->header->size();
2720 highlightStart = d->highlightRangeStartValid
2721 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2722 : d->size() - (d->lastPosition()-endPositionFirstItem);
2723 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2725 d->minExtent += d->footer->size();
2726 qreal maxX = maxXExtent();
2727 if (d->minExtent < maxX)
2728 d->minExtent = maxX;
2730 endPositionFirstItem = d->endPositionAt(0);
2731 highlightStart = d->highlightRangeStart;
2732 highlightEnd = d->highlightRangeEnd;
2733 if (d->header && d->visibleItems.count())
2734 d->minExtent += d->header->size();
2736 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2737 d->minExtent += highlightStart;
2738 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2740 d->minExtentDirty = false;
2743 return d->minExtent;
2746 qreal QDeclarativeListView::maxXExtent() const
2748 Q_D(const QDeclarativeListView);
2749 if (d->orient == QDeclarativeListView::Vertical)
2751 if (d->maxExtentDirty) {
2752 qreal highlightStart;
2754 qreal lastItemPosition = 0;
2756 if (d->isRightToLeft()) {
2757 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2758 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2759 lastItemPosition = d->endPosition();
2761 highlightStart = d->highlightRangeStart;
2762 highlightEnd = d->highlightRangeEnd;
2763 if (d->model && d->model->count())
2764 lastItemPosition = d->positionAt(d->model->count()-1);
2766 if (!d->model || !d->model->count()) {
2767 if (!d->isRightToLeft())
2768 d->maxExtent = d->header ? -d->header->size() : 0;
2769 d->maxExtent += width();
2770 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2771 d->maxExtent = -(lastItemPosition - highlightStart);
2772 if (highlightEnd != highlightStart) {
2773 d->maxExtent = d->isRightToLeft()
2774 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2775 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2778 d->maxExtent = -(d->endPosition() - width() + 1);
2780 if (d->isRightToLeft()) {
2781 if (d->header && d->visibleItems.count())
2782 d->maxExtent -= d->header->size();
2785 d->maxExtent -= d->footer->size();
2786 qreal minX = minXExtent();
2787 if (d->maxExtent > minX)
2788 d->maxExtent = minX;
2790 d->maxExtentDirty = false;
2792 return d->maxExtent;
2795 void QDeclarativeListView::keyPressEvent(QKeyEvent *event)
2797 Q_D(QDeclarativeListView);
2798 keyPressPreHandler(event);
2799 if (event->isAccepted())
2802 if (d->model && d->model->count() && d->interactive) {
2803 if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2804 || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2805 || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) {
2806 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2807 decrementCurrentIndex();
2810 } else if (d->wrap) {
2814 } else if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2815 || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2816 || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) {
2817 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2818 incrementCurrentIndex();
2821 } else if (d->wrap) {
2828 QDeclarativeFlickable::keyPressEvent(event);
2831 void QDeclarativeListView::geometryChanged(const QRectF &newGeometry,
2832 const QRectF &oldGeometry)
2834 Q_D(QDeclarativeListView);
2835 d->maxExtentDirty = true;
2836 d->minExtentDirty = true;
2837 if (d->isRightToLeft() && d->orient == QDeclarativeListView::Horizontal) {
2838 // maintain position relative to the right edge
2839 int dx = newGeometry.width() - oldGeometry.width();
2840 setContentX(contentX() - dx);
2842 QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry);
2847 \qmlmethod ListView::incrementCurrentIndex()
2849 Increments the current index. The current index will wrap
2850 if keyNavigationWraps is true and it is currently at the end.
2851 This method has no effect if the \l count is zero.
2853 \bold Note: methods should only be called after the Component has completed.
2855 void QDeclarativeListView::incrementCurrentIndex()
2857 Q_D(QDeclarativeListView);
2858 int count = d->model ? d->model->count() : 0;
2859 if (count && (currentIndex() < count - 1 || d->wrap)) {
2860 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2861 int index = currentIndex()+1;
2862 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2867 \qmlmethod ListView::decrementCurrentIndex()
2869 Decrements the current index. The current index will wrap
2870 if keyNavigationWraps is true and it is currently at the beginning.
2871 This method has no effect if the \l count is zero.
2873 \bold Note: methods should only be called after the Component has completed.
2875 void QDeclarativeListView::decrementCurrentIndex()
2877 Q_D(QDeclarativeListView);
2878 int count = d->model ? d->model->count() : 0;
2879 if (count && (currentIndex() > 0 || d->wrap)) {
2880 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2881 int index = currentIndex()-1;
2882 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2886 void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode)
2888 Q_Q(QDeclarativeListView);
2891 if (mode < QDeclarativeListView::Beginning || mode > QDeclarativeListView::Contain)
2893 int idx = qMax(qMin(index, model->count()-1), 0);
2895 if (layoutScheduled)
2897 qreal pos = isRightToLeft() ? -position() - size() : position();
2898 FxListItem *item = visibleItem(idx);
2900 if (orient == QDeclarativeListView::Vertical)
2901 maxExtent = -q->maxYExtent();
2903 maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2906 int itemPos = positionAt(idx);
2907 // save the currently visible items in case any of them end up visible again
2908 QList<FxListItem*> oldVisible = visibleItems;
2909 visibleItems.clear();
2910 visiblePos = itemPos;
2912 setPosition(qMin(qreal(itemPos), maxExtent));
2913 // now release the reference to all the old visible items.
2914 for (int i = 0; i < oldVisible.count(); ++i)
2915 releaseItem(oldVisible.at(i));
2916 item = visibleItem(idx);
2919 const qreal itemPos = item->position();
2921 case QDeclarativeListView::Beginning:
2923 if (index < 0 && header)
2924 pos -= header->size();
2926 case QDeclarativeListView::Center:
2927 pos = itemPos - (size() - item->size())/2;
2929 case QDeclarativeListView::End:
2930 pos = itemPos - size() + item->size();
2931 if (index >= model->count() && footer)
2932 pos += footer->size();
2934 case QDeclarativeListView::Visible:
2935 if (itemPos > pos + size())
2936 pos = itemPos - size() + item->size();
2937 else if (item->endPosition() < pos)
2940 case QDeclarativeListView::Contain:
2941 if (item->endPosition() > pos + size())
2942 pos = itemPos - size() + item->size();
2946 pos = qMin(pos, maxExtent);
2948 if (orient == QDeclarativeListView::Vertical) {
2949 minExtent = -q->minYExtent();
2951 minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2953 pos = qMax(pos, minExtent);
2954 moveReason = QDeclarativeListViewPrivate::Other;
2958 if (autoHighlight) {
2959 highlight->setPosition(currentItem->itemPosition());
2960 highlight->setSize(currentItem->itemSize());
2969 \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode)
2971 Positions the view such that the \a index is at the position specified by
2975 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2976 \o ListView.Center - position item in the center of the view.
2977 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2978 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2979 bring the item into view.
2980 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
2981 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2984 If positioning the view at \a index would cause empty space to be displayed at
2985 the beginning or end of the view, the view will be positioned at the boundary.
2987 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2988 at a particular index. This is unreliable since removing items from the start
2989 of the list does not cause all other items to be repositioned, and because
2990 the actual start of the view can vary based on the size of the delegates.
2991 The correct way to bring an item into view is with \c positionViewAtIndex.
2993 \bold Note: methods should only be called after the Component has completed. To position
2994 the view at startup, this method should be called by Component.onCompleted. For
2995 example, to position the view at the end:
2998 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3001 void QDeclarativeListView::positionViewAtIndex(int index, int mode)
3003 Q_D(QDeclarativeListView);
3004 if (!d->isValid() || index < 0 || index >= d->model->count())
3006 d->positionViewAtIndex(index, mode);
3010 \qmlmethod ListView::positionViewAtBeginning()
3011 \qmlmethod ListView::positionViewAtEnd()
3014 Positions the view at the beginning or end, taking into account any header or footer.
3016 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3017 at a particular index. This is unreliable since removing items from the start
3018 of the list does not cause all other items to be repositioned, and because
3019 the actual start of the view can vary based on the size of the delegates.
3021 \bold Note: methods should only be called after the Component has completed. To position
3022 the view at startup, this method should be called by Component.onCompleted. For
3023 example, to position the view at the end on startup:
3026 Component.onCompleted: positionViewAtEnd()
3029 void QDeclarativeListView::positionViewAtBeginning()
3031 Q_D(QDeclarativeListView);
3034 d->positionViewAtIndex(-1, Beginning);
3037 void QDeclarativeListView::positionViewAtEnd()
3039 Q_D(QDeclarativeListView);
3042 d->positionViewAtIndex(d->model->count(), End);
3046 \qmlmethod int ListView::indexAt(int x, int y)
3048 Returns the index of the visible item containing the point \a x, \a y in content
3049 coordinates. If there is no item at the point specified, or the item is
3050 not visible -1 is returned.
3052 If the item is outside the visible area, -1 is returned, regardless of
3053 whether an item will exist at that point when scrolled into view.
3055 \bold Note: methods should only be called after the Component has completed.
3057 int QDeclarativeListView::indexAt(qreal x, qreal y) const
3059 Q_D(const QDeclarativeListView);
3060 for (int i = 0; i < d->visibleItems.count(); ++i) {
3061 const FxListItem *listItem = d->visibleItems.at(i);
3062 if(listItem->contains(x, y))
3063 return listItem->index;
3069 void QDeclarativeListView::componentComplete()
3071 Q_D(QDeclarativeListView);
3072 QDeclarativeFlickable::componentComplete();
3078 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3079 if (d->currentIndex < 0 && !d->currentIndexCleared)
3080 d->updateCurrent(0);
3082 d->updateCurrent(d->currentIndex);
3083 if (d->highlight && d->currentItem) {
3084 if (d->autoHighlight)
3085 d->highlight->setPosition(d->currentItem->position());
3086 d->updateTrackedItem();
3088 d->moveReason = QDeclarativeListViewPrivate::Other;
3093 void QDeclarativeListView::updateSections()
3095 Q_D(QDeclarativeListView);
3096 if (isComponentComplete() && d->model) {
3097 QList<QByteArray> roles;
3098 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3099 roles << d->sectionCriteria->property().toUtf8();
3100 d->model->setWatchedRoles(roles);
3101 d->updateSections();
3107 void QDeclarativeListView::refill()
3109 Q_D(QDeclarativeListView);
3110 if (d->isRightToLeft())
3111 d->refill(-d->position()-d->size()+1, -d->position());
3113 d->refill(d->position(), d->position()+d->size()-1);
3116 void QDeclarativeListView::trackedPositionChanged()
3118 Q_D(QDeclarativeListView);
3119 if (!d->trackedItem || !d->currentItem)
3121 if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) {
3122 qreal trackedPos = qCeil(d->trackedItem->position());
3123 qreal trackedSize = d->trackedItem->size();
3124 if (d->trackedItem != d->currentItem) {
3125 trackedPos -= d->currentItem->sectionSize();
3126 trackedSize += d->currentItem->sectionSize();
3129 qreal highlightStart;
3131 if (d->isRightToLeft()) {
3132 viewPos = -d->position()-d->size();
3133 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3134 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3136 viewPos = d->position();
3137 highlightStart = d->highlightRangeStart;
3138 highlightEnd = d->highlightRangeEnd;
3140 qreal pos = viewPos;
3141 if (d->haveHighlightRange) {
3142 if (d->highlightRange == StrictlyEnforceRange) {
3143 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3144 pos = trackedPos - highlightEnd + d->trackedItem->size();
3145 if (trackedPos < pos + highlightStart)
3146 pos = trackedPos - highlightStart;
3148 if (trackedPos < d->startPosition() + highlightStart) {
3149 pos = d->startPosition();
3150 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3151 pos = d->endPosition() - d->size() + 1;
3152 if (pos < d->startPosition())
3153 pos = d->startPosition();
3155 if (trackedPos < viewPos + highlightStart) {
3156 pos = trackedPos - highlightStart;
3157 } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
3158 pos = trackedPos - highlightEnd + trackedSize;
3163 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
3164 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
3165 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
3166 && d->currentItem->endPosition() >= viewPos + d->size()) {
3167 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
3168 pos = d->trackedItem->endPosition() - d->size() + 1;
3169 if (trackedSize > d->size())
3172 pos = d->currentItem->endPosition() - d->size() + 1;
3173 if (d->currentItem->size() > d->size())
3174 pos = d->currentItem->position();
3178 if (viewPos != pos) {
3180 d->calcVelocity = true;
3181 d->setPosition(pos);
3182 d->calcVelocity = false;
3187 void QDeclarativeListView::itemsInserted(int modelIndex, int count)
3189 Q_D(QDeclarativeListView);
3190 if (!isComponentComplete())
3192 d->updateUnrequestedIndexes();
3193 d->moveReason = QDeclarativeListViewPrivate::Other;
3195 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
3196 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
3199 int i = d->visibleItems.count() - 1;
3200 while (i > 0 && d->visibleItems.at(i)->index == -1)
3202 if (i == 0 && d->visibleItems.first()->index == -1) {
3203 // there are no visible items except items marked for removal
3204 index = d->visibleItems.count();
3205 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
3206 && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
3207 // Special case of appending an item to the model.
3208 index = d->visibleItems.count();
3210 if (modelIndex < d->visibleIndex) {
3211 // Insert before visible items
3212 d->visibleIndex += count;
3213 for (int i = 0; i < d->visibleItems.count(); ++i) {
3214 FxListItem *listItem = d->visibleItems.at(i);
3215 if (listItem->index != -1 && listItem->index >= modelIndex)
3216 listItem->index += count;
3219 if (d->currentIndex >= modelIndex) {
3220 // adjust current item index
3221 d->currentIndex += count;
3223 d->currentItem->index = d->currentIndex;
3224 emit currentIndexChanged();
3226 d->scheduleLayout();
3227 d->itemCount += count;
3228 emit countChanged();
3233 // index can be the next item past the end of the visible items list (i.e. appended)
3235 if (d->visibleItems.count()) {
3236 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
3237 : d->visibleItems.last()->endPosition()+d->spacing+1;
3238 } else if (d->itemCount == 0 && d->header) {
3239 pos = d->header->size();
3242 int initialPos = pos;
3244 QList<FxListItem*> added;
3245 bool addedVisible = false;
3246 FxListItem *firstVisible = d->firstVisibleItem();
3247 if (firstVisible && pos < firstVisible->position()) {
3248 // Insert items before the visible item.
3249 int insertionIdx = index;
3251 int from = tempPos - d->buffer;
3252 for (i = count-1; i >= 0 && pos > from; --i) {
3253 if (!addedVisible) {
3254 d->scheduleLayout();
3255 addedVisible = true;
3257 FxListItem *item = d->createItem(modelIndex + i);
3258 d->visibleItems.insert(insertionIdx, item);
3259 pos -= item->size() + d->spacing;
3260 item->setPosition(pos);
3264 // If we didn't insert all our new items - anything
3265 // before the current index is not visible - remove it.
3266 while (insertionIdx--) {
3267 FxListItem *item = d->visibleItems.takeFirst();
3268 if (item->index != -1)
3270 d->releaseItem(item);
3273 // adjust pos of items before inserted items.
3274 for (int i = insertionIdx-1; i >= 0; i--) {
3275 FxListItem *listItem = d->visibleItems.at(i);
3276 listItem->setPosition(listItem->position() - (initialPos - pos));
3281 int to = d->buffer+tempPos+d->size()-1;
3282 for (i = 0; i < count && pos <= to; ++i) {
3283 if (!addedVisible) {
3284 d->scheduleLayout();
3285 addedVisible = true;
3287 FxListItem *item = d->createItem(modelIndex + i);
3288 d->visibleItems.insert(index, item);
3289 item->setPosition(pos);
3291 pos += item->size() + d->spacing;
3295 // We didn't insert all our new items, which means anything
3296 // beyond the current index is not visible - remove it.
3297 while (d->visibleItems.count() > index)
3298 d->releaseItem(d->visibleItems.takeLast());
3300 diff = pos - initialPos;
3302 if (d->itemCount && d->currentIndex >= modelIndex) {
3303 // adjust current item index
3304 d->currentIndex += count;
3305 if (d->currentItem) {
3306 d->currentItem->index = d->currentIndex;
3307 d->currentItem->setPosition(d->currentItem->position() + diff);
3309 emit currentIndexChanged();
3310 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
3311 d->updateCurrent(0);
3313 // Update the indexes of the following visible items.
3314 for (; index < d->visibleItems.count(); ++index) {
3315 FxListItem *listItem = d->visibleItems.at(index);
3316 if (d->currentItem && listItem->item != d->currentItem->item)
3317 listItem->setPosition(listItem->position() + diff);
3318 if (listItem->index != -1)
3319 listItem->index += count;
3321 // everything is in order now - emit add() signal
3322 for (int j = 0; j < added.count(); ++j)
3323 added.at(j)->attached->emitAdd();
3325 d->updateSections();
3326 d->itemCount += count;
3327 emit countChanged();
3330 void QDeclarativeListView::itemsRemoved(int modelIndex, int count)
3332 Q_D(QDeclarativeListView);
3333 if (!isComponentComplete())
3335 d->moveReason = QDeclarativeListViewPrivate::Other;
3336 d->updateUnrequestedIndexes();
3337 d->itemCount -= count;
3339 FxListItem *firstVisible = d->firstVisibleItem();
3340 int preRemovedSize = 0;
3341 bool removedVisible = false;
3342 // Remove the items from the visible list, skipping anything already marked for removal
3343 QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3344 while (it != d->visibleItems.end()) {
3345 FxListItem *item = *it;
3346 if (item->index == -1 || item->index < modelIndex) {
3347 // already removed, or before removed items
3349 } else if (item->index >= modelIndex + count) {
3350 // after removed items
3351 item->index -= count;
3355 if (!removedVisible) {
3356 d->scheduleLayout();
3357 removedVisible = true;
3359 item->attached->emitRemove();
3360 if (item->attached->delayRemove()) {
3362 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
3365 if (item == firstVisible)
3367 if (firstVisible && item->position() < firstVisible->position())
3368 preRemovedSize += item->size();
3369 it = d->visibleItems.erase(it);
3370 d->releaseItem(item);
3375 if (firstVisible && d->visibleItems.first() != firstVisible)
3376 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
3379 if (d->currentIndex >= modelIndex + count) {
3380 d->currentIndex -= count;
3382 d->currentItem->index -= count;
3383 emit currentIndexChanged();
3384 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
3385 // current item has been removed.
3386 d->currentItem->attached->setIsCurrentItem(false);
3387 d->releaseItem(d->currentItem);
3389 d->currentIndex = -1;
3391 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
3393 emit currentIndexChanged();
3396 // update visibleIndex
3397 bool haveVisibleIndex = false;
3398 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3399 if ((*it)->index != -1) {
3400 d->visibleIndex = (*it)->index;
3401 haveVisibleIndex = true;
3406 if (removedVisible && !haveVisibleIndex) {
3407 d->timeline.clear();
3408 if (d->itemCount == 0) {
3409 d->visibleIndex = 0;
3410 d->visiblePos = d->header ? d->header->size() : 0;
3416 if (modelIndex < d->visibleIndex)
3417 d->visibleIndex = modelIndex+1;
3418 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3422 d->updateSections();
3423 emit countChanged();
3426 void QDeclarativeListView::destroyRemoved()
3428 Q_D(QDeclarativeListView);
3429 for (QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3430 it != d->visibleItems.end();) {
3431 FxListItem *listItem = *it;
3432 if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3433 d->releaseItem(listItem);
3434 it = d->visibleItems.erase(it);
3440 // Correct the positioning of the items
3441 d->updateSections();
3445 void QDeclarativeListView::itemsMoved(int from, int to, int count)
3447 Q_D(QDeclarativeListView);
3448 if (!isComponentComplete())
3450 d->updateUnrequestedIndexes();
3452 if (d->visibleItems.isEmpty()) {
3457 d->moveReason = QDeclarativeListViewPrivate::Other;
3458 FxListItem *firstVisible = d->firstVisibleItem();
3459 qreal firstItemPos = firstVisible->position();
3460 QHash<int,FxListItem*> moved;
3463 QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3464 while (it != d->visibleItems.end()) {
3465 FxListItem *item = *it;
3466 if (item->index >= from && item->index < from + count) {
3467 // take the items that are moving
3468 item->index += (to-from);
3469 moved.insert(item->index, item);
3470 if (item->position() < firstItemPos)
3471 moveBy += item->size();
3472 it = d->visibleItems.erase(it);
3474 // move everything after the moved items.
3475 if (item->index > from && item->index != -1)
3476 item->index -= count;
3481 int remaining = count;
3482 int endIndex = d->visibleIndex;
3483 it = d->visibleItems.begin();
3484 while (it != d->visibleItems.end()) {
3485 FxListItem *item = *it;
3486 if (remaining && item->index >= to && item->index < to + count) {
3487 // place items in the target position, reusing any existing items
3488 FxListItem *movedItem = moved.take(item->index);
3490 movedItem = d->createItem(item->index);
3491 if (item->index <= firstVisible->index)
3492 moveBy -= movedItem->size();
3493 it = d->visibleItems.insert(it, movedItem);
3497 if (item->index != -1) {
3498 if (item->index >= to) {
3499 // update everything after the moved items.
3500 item->index += count;
3502 endIndex = item->index;
3508 // If we have moved items to the end of the visible items
3509 // then add any existing moved items that we have
3510 while (FxListItem *item = moved.take(endIndex+1)) {
3511 d->visibleItems.append(item);
3515 // update visibleIndex
3516 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3517 if ((*it)->index != -1) {
3518 d->visibleIndex = (*it)->index;
3523 // Fix current index
3524 if (d->currentIndex >= 0 && d->currentItem) {
3525 int oldCurrent = d->currentIndex;
3526 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3527 if (oldCurrent != d->currentIndex) {
3528 d->currentItem->index = d->currentIndex;
3529 emit currentIndexChanged();
3533 // Whatever moved items remain are no longer visible items.
3534 while (moved.count()) {
3535 int idx = moved.begin().key();
3536 FxListItem *item = moved.take(idx);
3537 if (d->currentItem && item->item == d->currentItem->item)
3538 item->setPosition(d->positionAt(idx));
3539 d->releaseItem(item);
3542 // Ensure we don't cause an ugly list scroll.
3543 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3545 d->updateSections();
3549 void QDeclarativeListView::itemsChanged(int, int)
3551 Q_D(QDeclarativeListView);
3552 d->updateSections();
3556 void QDeclarativeListView::modelReset()
3558 Q_D(QDeclarativeListView);
3559 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3561 if (d->highlight && d->currentItem) {
3562 if (d->autoHighlight)
3563 d->highlight->setPosition(d->currentItem->position());
3564 d->updateTrackedItem();
3566 d->moveReason = QDeclarativeListViewPrivate::Other;
3567 emit countChanged();
3570 void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item)
3572 Q_D(QDeclarativeListView);
3573 if (d->requestedIndex != index) {
3574 item->setParentItem(contentItem());
3575 d->unrequestedItems.insert(item, index);
3576 if (d->orient == QDeclarativeListView::Vertical) {
3577 item->setY(d->positionAt(index));
3579 if (d->isRightToLeft())
3580 item->setX(-d->positionAt(index)-item->width());
3582 item->setX(d->positionAt(index));
3587 void QDeclarativeListView::destroyingItem(QDeclarativeItem *item)
3589 Q_D(QDeclarativeListView);
3590 d->unrequestedItems.remove(item);
3593 void QDeclarativeListView::animStopped()
3595 Q_D(QDeclarativeListView);
3596 d->bufferMode = QDeclarativeListViewPrivate::NoBuffer;
3597 if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange)
3598 d->updateHighlight();
3601 QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj)
3603 return new QDeclarativeListViewAttached(obj);