1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativelistview_p.h"
44 #include "QtQuick1/private/qdeclarativeflickable_p_p.h"
45 #include "QtQuick1/private/qdeclarativevisualitemmodel_p.h"
47 #include "QtQuick1/private/qdeclarativesmoothedanimation_p_p.h"
48 #include <QtDeclarative/qdeclarativeexpression.h>
49 #include <QtDeclarative/qdeclarativeengine.h>
50 #include <QtDeclarative/private/qdeclarativeguard_p.h>
51 #include <QtDeclarative/qdeclarativeinfo.h>
60 void QDeclarative1ViewSection::setProperty(const QString &property)
62 if (property != m_property) {
63 m_property = property;
64 emit propertyChanged();
68 void QDeclarative1ViewSection::setCriteria(QDeclarative1ViewSection::SectionCriteria criteria)
70 if (criteria != m_criteria) {
71 m_criteria = criteria;
72 emit criteriaChanged();
76 void QDeclarative1ViewSection::setDelegate(QDeclarativeComponent *delegate)
78 if (delegate != m_delegate) {
79 m_delegate = delegate;
80 emit delegateChanged();
84 QString QDeclarative1ViewSection::sectionString(const QString &value)
86 if (m_criteria == FirstCharacter)
87 return value.isEmpty() ? QString() : value.at(0);
92 //----------------------------------------------------------------------------
97 FxListItem1(QDeclarativeItem *i, QDeclarative1ListView *v) : item(i), section(0), view(v) {
98 attached = static_cast<QDeclarative1ListViewAttached*>(qmlAttachedPropertiesObject<QDeclarative1ListView>(item));
100 attached->setView(view);
103 qreal position() const {
105 if (view->orientation() == QDeclarative1ListView::Vertical)
108 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
110 return itemPosition();
114 qreal itemPosition() const {
115 if (view->orientation() == QDeclarative1ListView::Vertical)
118 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
122 return (view->orientation() == QDeclarative1ListView::Vertical ? item->height()+section->height() : item->width()+section->width());
124 return (view->orientation() == QDeclarative1ListView::Vertical ? item->height() : item->width());
126 qreal itemSize() const {
127 return (view->orientation() == QDeclarative1ListView::Vertical ? item->height() : item->width());
129 qreal sectionSize() const {
131 return (view->orientation() == QDeclarative1ListView::Vertical ? section->height() : section->width());
134 qreal endPosition() const {
135 if (view->orientation() == QDeclarative1ListView::Vertical) {
136 return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
138 return (view->effectiveLayoutDirection() == Qt::RightToLeft
139 ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
140 : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
143 void setPosition(qreal pos) {
144 if (view->orientation() == QDeclarative1ListView::Vertical) {
147 pos += section->height();
151 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
153 section->setX(-section->width()-pos);
154 pos += section->width();
156 item->setX(-item->width()-pos);
160 pos += section->width();
166 void setSize(qreal size) {
167 if (view->orientation() == QDeclarative1ListView::Vertical)
168 item->setHeight(size);
170 item->setWidth(size);
172 bool contains(qreal x, qreal y) const {
173 return (x >= item->x() && x < item->x() + item->width() &&
174 y >= item->y() && y < item->y() + item->height());
177 QDeclarativeItem *item;
178 QDeclarativeItem *section;
179 QDeclarative1ListView *view;
180 QDeclarative1ListViewAttached *attached;
184 //----------------------------------------------------------------------------
186 class QDeclarative1ListViewPrivate : public QDeclarative1FlickablePrivate
188 Q_DECLARE_PUBLIC(QDeclarative1ListView)
191 QDeclarative1ListViewPrivate()
192 : currentItem(0), orient(QDeclarative1ListView::Vertical), layoutDirection(Qt::LeftToRight)
193 , visiblePos(0), visibleIndex(0)
194 , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
195 , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
196 , highlightRangeStartValid(false), highlightRangeEndValid(false)
197 , highlightComponent(0), highlight(0), trackedItem(0)
198 , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
199 , sectionCriteria(0), spacing(0.0)
200 , highlightMoveSpeed(400), highlightMoveDuration(-1)
201 , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarative1ListView::NoHighlightRange)
202 , snapMode(QDeclarative1ListView::NoSnap), overshootDist(0.0)
203 , footerComponent(0), footer(0), headerComponent(0), header(0)
204 , bufferMode(BufferBefore | BufferAfter)
205 , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
206 , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
207 , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
208 , inViewportMoved(false)
209 , minExtentDirty(true), maxExtentDirty(true)
214 FxListItem1 *createItem(int modelIndex);
215 void releaseItem(FxListItem1 *item);
217 FxListItem1 *visibleItem(int modelIndex) const {
218 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
219 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
220 FxListItem1 *item = visibleItems.at(i);
221 if (item->index == modelIndex)
228 FxListItem1 *firstVisibleItem() const {
229 const qreal pos = isRightToLeft() ? -position()-size() : position();
230 for (int i = 0; i < visibleItems.count(); ++i) {
231 FxListItem1 *item = visibleItems.at(i);
232 if (item->index != -1 && item->endPosition() > pos)
235 return visibleItems.count() ? visibleItems.first() : 0;
238 FxListItem1 *nextVisibleItem() const {
239 const qreal pos = isRightToLeft() ? -position()-size() : position();
240 bool foundFirst = false;
241 for (int i = 0; i < visibleItems.count(); ++i) {
242 FxListItem1 *item = visibleItems.at(i);
243 if (item->index != -1) {
246 else if (item->position() < pos && item->endPosition() > pos)
253 // Returns the item before modelIndex, if created.
254 // May return an item marked for removal.
255 FxListItem1 *itemBefore(int modelIndex) const {
256 if (modelIndex < visibleIndex)
260 while (idx < visibleItems.count()) {
261 FxListItem1 *item = visibleItems.at(idx);
262 if (item->index != -1)
263 lastIndex = item->index;
264 if (item->index == modelIndex)
265 return visibleItems.at(idx-1);
268 if (lastIndex == modelIndex-1)
269 return visibleItems.last();
274 Q_Q(QDeclarative1ListView);
275 if (q->isComponentComplete()) {
278 q->scene()->removeItem(header->item);
279 header->item->deleteLater();
285 q->scene()->removeItem(footer->item);
286 footer->item->deleteLater();
295 updateCurrent(currentIndex);
299 void mirrorChange() {
300 Q_Q(QDeclarative1ListView);
302 emit q->effectiveLayoutDirectionChanged();
305 bool isRightToLeft() const {
306 Q_Q(const QDeclarative1ListView);
307 return orient == QDeclarative1ListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
310 qreal position() const {
311 Q_Q(const QDeclarative1ListView);
312 return orient == QDeclarative1ListView::Vertical ? q->contentY() : q->contentX();
315 void setPosition(qreal pos) {
316 Q_Q(QDeclarative1ListView);
317 if (orient == QDeclarative1ListView::Vertical) {
318 q->QDeclarative1Flickable::setContentY(pos);
321 q->QDeclarative1Flickable::setContentX(-pos-size());
323 q->QDeclarative1Flickable::setContentX(pos);
327 Q_Q(const QDeclarative1ListView);
328 return orient == QDeclarative1ListView::Vertical ? q->height() : q->width();
331 qreal originPosition() const {
333 if (!visibleItems.isEmpty()) {
334 pos = (*visibleItems.constBegin())->position();
335 if (visibleIndex > 0)
336 pos -= visibleIndex * (averageSize + spacing);
341 qreal lastPosition() const {
343 if (!visibleItems.isEmpty()) {
344 int invisibleCount = visibleItems.count() - visibleIndex;
345 for (int i = visibleItems.count()-1; i >= 0; --i) {
346 if (visibleItems.at(i)->index != -1) {
347 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
351 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
352 } else if (model && model->count()) {
353 pos = model->count() * averageSize + (model->count()-1) * spacing;
358 qreal startPosition() const {
359 return isRightToLeft() ? -lastPosition()-1 : originPosition();
362 qreal endPosition() const {
363 return isRightToLeft() ? -originPosition()-1 : lastPosition();
366 qreal positionAt(int modelIndex) const {
367 if (FxListItem1 *item = visibleItem(modelIndex))
368 return item->position();
369 if (!visibleItems.isEmpty()) {
370 if (modelIndex < visibleIndex) {
371 int count = visibleIndex - modelIndex;
373 if (modelIndex == currentIndex && currentItem) {
374 cs = currentItem->size() + spacing;
377 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
379 int idx = visibleItems.count() - 1;
380 while (idx >= 0 && visibleItems.at(idx)->index == -1)
385 idx = visibleItems.at(idx)->index;
386 int count = modelIndex - idx - 1;
388 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
394 qreal endPositionAt(int modelIndex) const {
395 if (FxListItem1 *item = visibleItem(modelIndex))
396 return item->endPosition();
397 if (!visibleItems.isEmpty()) {
398 if (modelIndex < visibleIndex) {
399 int count = visibleIndex - modelIndex;
400 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
402 int idx = visibleItems.count() - 1;
403 while (idx >= 0 && visibleItems.at(idx)->index == -1)
408 idx = visibleItems.at(idx)->index;
409 int count = modelIndex - idx - 1;
410 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
416 QString sectionAt(int modelIndex) {
417 if (FxListItem1 *item = visibleItem(modelIndex))
418 return item->attached->section();
421 if (sectionCriteria) {
422 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
423 section = sectionCriteria->sectionString(propValue);
429 bool isValid() const {
430 return model && model->count() && model->isValid();
433 qreal snapPosAt(qreal pos) {
434 if (FxListItem1 *snapItem = snapItemAt(pos))
435 return snapItem->position();
436 if (visibleItems.count()) {
437 qreal firstPos = visibleItems.first()->position();
438 qreal endPos = visibleItems.last()->position();
439 if (pos < firstPos) {
440 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
441 } else if (pos > endPos)
442 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
444 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
447 FxListItem1 *snapItemAt(qreal pos) {
448 FxListItem1 *snapItem = 0;
449 for (int i = 0; i < visibleItems.count(); ++i) {
450 FxListItem1 *item = visibleItems[i];
451 if (item->index == -1)
453 qreal itemTop = item->position();
454 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
456 if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
462 int lastVisibleIndex() const {
464 for (int i = visibleItems.count()-1; i >= 0; --i) {
465 FxListItem1 *listItem = visibleItems.at(i);
466 if (listItem->index != -1) {
467 lastIndex = listItem->index;
474 // map a model index to visibleItems index.
475 int mapFromModel(int modelIndex) const {
476 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
478 for (int i = 0; i < visibleItems.count(); ++i) {
479 FxListItem1 *listItem = visibleItems.at(i);
480 if (listItem->index == modelIndex)
482 if (listItem->index > modelIndex)
485 return -1; // Not in visibleList
488 void updateViewport() {
489 Q_Q(QDeclarative1ListView);
490 if (orient == QDeclarative1ListView::Vertical) {
491 q->setContentHeight(endPosition() - startPosition() + 1);
493 q->setContentWidth(endPosition() - startPosition() + 1);
497 void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
498 Q_Q(QDeclarative1ListView);
499 QDeclarative1FlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
500 if (!q->isComponentComplete())
502 if (item != contentItem && (!highlight || item != highlight->item)) {
503 if ((orient == QDeclarative1ListView::Vertical && newGeometry.height() != oldGeometry.height())
504 || (orient == QDeclarative1ListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
508 if ((header && header->item == item) || (footer && footer->item == item)) {
514 if (currentItem && currentItem->item == item)
516 if (trackedItem && trackedItem->item == item)
517 q->trackedPositionChanged();
520 // for debugging only
521 void checkVisible() const {
523 for (int i = 0; i < visibleItems.count(); ++i) {
524 FxListItem1 *listItem = visibleItems.at(i);
525 if (listItem->index == -1) {
527 } else if (listItem->index != visibleIndex + i - skip) {
528 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
533 void refill(qreal from, qreal to, bool doBuffer = false);
534 void scheduleLayout();
536 void updateUnrequestedIndexes();
537 void updateUnrequestedPositions();
538 void updateTrackedItem();
539 void createHighlight();
540 void updateHighlight();
541 void createSection(FxListItem1 *);
542 void updateSections();
543 void updateCurrentSection();
544 void updateCurrent(int);
545 void updateAverage();
548 void fixupPosition();
549 void positionViewAtIndex(int index, int mode);
550 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
551 virtual void flick(QDeclarative1FlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
552 QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity);
554 QDeclarativeGuard<QDeclarative1VisualModel> model;
555 QVariant modelVariant;
556 QList<FxListItem1*> visibleItems;
557 QHash<QDeclarativeItem*,int> unrequestedItems;
558 FxListItem1 *currentItem;
559 QDeclarative1ListView::Orientation orient;
560 Qt::LayoutDirection layoutDirection;
567 qreal highlightRangeStart;
568 qreal highlightRangeEnd;
569 bool highlightRangeStartValid;
570 bool highlightRangeEndValid;
571 QDeclarativeComponent *highlightComponent;
572 FxListItem1 *highlight;
573 FxListItem1 *trackedItem;
574 enum MovementReason { Other, SetIndex, Mouse };
575 MovementReason moveReason;
577 QSmoothedAnimation_1 *highlightPosAnimator;
578 QSmoothedAnimation_1 *highlightSizeAnimator;
579 QDeclarative1ViewSection *sectionCriteria;
580 QString currentSection;
581 static const int sectionCacheSize = 4;
582 QDeclarativeItem *sectionCache[sectionCacheSize];
584 qreal highlightMoveSpeed;
585 int highlightMoveDuration;
586 qreal highlightResizeSpeed;
587 int highlightResizeDuration;
588 QDeclarative1ListView::HighlightRangeMode highlightRange;
589 QDeclarative1ListView::SnapMode snapMode;
591 QDeclarativeComponent *footerComponent;
593 QDeclarativeComponent *headerComponent;
595 enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
597 mutable qreal minExtent;
598 mutable qreal maxExtent;
602 bool autoHighlight : 1;
603 bool haveHighlightRange : 1;
604 bool correctFlick : 1;
605 bool inFlickCorrection : 1;
606 bool lazyRelease : 1;
607 bool deferredRelease : 1;
608 bool layoutScheduled : 1;
609 bool currentIndexCleared : 1;
610 bool inViewportMoved : 1;
611 mutable bool minExtentDirty : 1;
612 mutable bool maxExtentDirty : 1;
615 void QDeclarative1ListViewPrivate::init()
617 Q_Q(QDeclarative1ListView);
618 q->setFlag(QGraphicsItem::ItemIsFocusScope);
619 addItemChangeListener(this, Geometry);
620 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
621 q->setFlickableDirection(QDeclarative1Flickable::VerticalFlick);
622 ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize);
625 void QDeclarative1ListViewPrivate::clear()
628 for (int i = 0; i < visibleItems.count(); ++i)
629 releaseItem(visibleItems.at(i));
630 visibleItems.clear();
631 for (int i = 0; i < sectionCacheSize; ++i) {
632 delete sectionCache[i];
635 visiblePos = header ? header->size() : 0;
637 releaseItem(currentItem);
641 minExtentDirty = true;
642 maxExtentDirty = true;
646 FxListItem1 *QDeclarative1ListViewPrivate::createItem(int modelIndex)
648 Q_Q(QDeclarative1ListView);
650 requestedIndex = modelIndex;
651 FxListItem1 *listItem = 0;
652 if (QDeclarativeItem *item = model->item(modelIndex, false)) {
653 listItem = new FxListItem1(item, q);
654 listItem->index = modelIndex;
655 // initialise attached properties
656 if (sectionCriteria) {
657 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
658 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
659 if (modelIndex > 0) {
660 if (FxListItem1 *item = itemBefore(modelIndex))
661 listItem->attached->m_prevSection = item->attached->section();
663 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
665 if (modelIndex < model->count()-1) {
666 if (FxListItem1 *item = visibleItem(modelIndex+1))
667 listItem->attached->m_nextSection = item->attached->section();
669 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
672 if (model->completePending()) {
674 listItem->item->setZValue(1);
675 listItem->item->setParentItem(q->contentItem());
676 model->completeItem();
678 listItem->item->setParentItem(q->contentItem());
680 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
681 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
682 if (sectionCriteria && sectionCriteria->delegate()) {
683 if (listItem->attached->m_prevSection != listItem->attached->m_section)
684 createSection(listItem);
686 unrequestedItems.remove(listItem->item);
693 void QDeclarative1ListViewPrivate::releaseItem(FxListItem1 *item)
695 Q_Q(QDeclarative1ListView);
698 if (trackedItem == item)
700 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item->item));
701 itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
702 if (model->release(item->item) == 0) {
703 // item was not destroyed, and we no longer reference it.
704 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
709 if (!sectionCache[i]) {
710 sectionCache[i] = item->section;
711 sectionCache[i]->setVisible(false);
716 } while (i < sectionCacheSize);
717 delete item->section;
722 void QDeclarative1ListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
724 Q_Q(QDeclarative1ListView);
725 if (!isValid() || !q->isComponentComplete())
727 itemCount = model->count();
728 qreal bufferFrom = from - buffer;
729 qreal bufferTo = to + buffer;
730 qreal fillFrom = from;
732 if (doBuffer && (bufferMode & BufferAfter))
734 if (doBuffer && (bufferMode & BufferBefore))
735 fillFrom = bufferFrom;
737 bool haveValidItems = false;
738 int modelIndex = visibleIndex;
739 qreal itemEnd = visiblePos-1;
740 if (!visibleItems.isEmpty()) {
741 visiblePos = (*visibleItems.constBegin())->position();
742 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
743 int i = visibleItems.count() - 1;
744 while (i > 0 && visibleItems.at(i)->index == -1)
746 if (visibleItems.at(i)->index != -1) {
747 haveValidItems = true;
748 modelIndex = visibleItems.at(i)->index + 1;
752 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
753 || fillTo < visiblePos - averageSize - spacing)) {
754 // We've jumped more than a page. Estimate which items are now
755 // visible and fill from there.
756 int count = (fillFrom - itemEnd) / (averageSize + spacing);
757 for (int i = 0; i < visibleItems.count(); ++i)
758 releaseItem(visibleItems.at(i));
759 visibleItems.clear();
761 if (modelIndex >= model->count()) {
762 count -= modelIndex - model->count() + 1;
763 modelIndex = model->count() - 1;
764 } else if (modelIndex < 0) {
768 visibleIndex = modelIndex;
769 visiblePos = itemEnd + count * (averageSize + spacing) + 1;
770 itemEnd = visiblePos-1;
773 bool changed = false;
774 FxListItem1 *item = 0;
775 qreal pos = itemEnd + 1;
776 while (modelIndex < model->count() && pos <= fillTo) {
777 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
778 if (!(item = createItem(modelIndex)))
780 item->setPosition(pos);
781 pos += item->size() + spacing;
782 visibleItems.append(item);
785 if (doBuffer) // never buffer more than one item per frame
788 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
789 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
790 if (!(item = createItem(visibleIndex-1)))
793 visiblePos -= item->size() + spacing;
794 item->setPosition(visiblePos);
795 visibleItems.prepend(item);
797 if (doBuffer) // never buffer more than one item per frame
801 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
802 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
803 if (item->attached->delayRemove())
805 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
806 if (item->index != -1)
808 visibleItems.removeFirst();
812 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
813 if (item->attached->delayRemove())
815 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
816 visibleItems.removeLast();
820 deferredRelease = false;
822 deferredRelease = true;
825 minExtentDirty = true;
826 maxExtentDirty = true;
827 if (visibleItems.count())
828 visiblePos = (*visibleItems.constBegin())->position();
830 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
831 currentItem->setPosition(positionAt(currentIndex));
836 updateCurrentSection();
842 updateUnrequestedPositions();
843 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
844 refill(from, to, true);
849 void QDeclarative1ListViewPrivate::scheduleLayout()
851 Q_Q(QDeclarative1ListView);
852 if (!layoutScheduled) {
853 layoutScheduled = true;
854 QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority);
858 void QDeclarative1ListViewPrivate::layout()
860 Q_Q(QDeclarative1ListView);
861 layoutScheduled = false;
862 if (!isValid() && !visibleItems.count()) {
867 if (!visibleItems.isEmpty()) {
868 bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
869 qreal sum = visibleItems.first()->size();
870 qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
871 for (int i=1; i < visibleItems.count(); ++i) {
872 FxListItem1 *item = visibleItems.at(i);
873 item->setPosition(pos);
874 pos += item->size() + spacing;
876 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
878 averageSize = qRound(sum / visibleItems.count());
879 // move current item if it is not a visible item.
880 if (currentIndex >= 0 && currentItem && !fixedCurrent)
881 currentItem->setPosition(positionAt(currentIndex));
884 minExtentDirty = true;
885 maxExtentDirty = true;
887 if (!q->isMoving() && !q->isFlicking()) {
898 void QDeclarative1ListViewPrivate::updateUnrequestedIndexes()
900 Q_Q(QDeclarative1ListView);
901 QHash<QDeclarativeItem*,int>::iterator it;
902 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
903 *it = model->indexOf(it.key(), q);
906 void QDeclarative1ListViewPrivate::updateUnrequestedPositions()
908 Q_Q(QDeclarative1ListView);
909 if (unrequestedItems.count()) {
910 qreal pos = position();
911 QHash<QDeclarativeItem*,int>::const_iterator it;
912 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
913 QDeclarativeItem *item = it.key();
914 if (orient == QDeclarative1ListView::Vertical) {
915 if (item->y() + item->height() > pos && item->y() < pos + q->height())
916 item->setY(positionAt(*it));
918 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
920 item->setX(-positionAt(*it)-item->width());
922 item->setX(positionAt(*it));
929 void QDeclarative1ListViewPrivate::updateTrackedItem()
931 Q_Q(QDeclarative1ListView);
932 FxListItem1 *item = currentItem;
937 q->trackedPositionChanged();
940 void QDeclarative1ListViewPrivate::createHighlight()
942 Q_Q(QDeclarative1ListView);
943 bool changed = false;
945 if (trackedItem == highlight)
947 if (highlight->item->scene())
948 highlight->item->scene()->removeItem(highlight->item);
949 highlight->item->deleteLater();
952 delete highlightPosAnimator;
953 delete highlightSizeAnimator;
954 highlightPosAnimator = 0;
955 highlightSizeAnimator = 0;
960 QDeclarativeItem *item = 0;
961 if (highlightComponent) {
962 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
963 QObject *nobj = highlightComponent->create(highlightContext);
965 QDeclarative_setParent_noEvent(highlightContext, nobj);
966 item = qobject_cast<QDeclarativeItem *>(nobj);
970 delete highlightContext;
973 item = new QDeclarativeItem;
976 QDeclarative_setParent_noEvent(item, q->contentItem());
977 item->setParentItem(q->contentItem());
978 highlight = new FxListItem1(item, q);
979 if (currentItem && autoHighlight) {
980 if (orient == QDeclarative1ListView::Vertical) {
981 highlight->item->setHeight(currentItem->item->height());
983 highlight->item->setWidth(currentItem->item->width());
985 highlight->setPosition(currentItem->itemPosition());
987 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
988 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
989 const QLatin1String posProp(orient == QDeclarative1ListView::Vertical ? "y" : "x");
990 highlightPosAnimator = new QSmoothedAnimation_1(q);
991 highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
992 highlightPosAnimator->velocity = highlightMoveSpeed;
993 highlightPosAnimator->userDuration = highlightMoveDuration;
994 const QLatin1String sizeProp(orient == QDeclarative1ListView::Vertical ? "height" : "width");
995 highlightSizeAnimator = new QSmoothedAnimation_1(q);
996 highlightSizeAnimator->velocity = highlightResizeSpeed;
997 highlightSizeAnimator->userDuration = highlightResizeDuration;
998 highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
1000 highlightPosAnimator->restart();
1001 highlightSizeAnimator->restart();
1007 emit q->highlightItemChanged();
1010 void QDeclarative1ListViewPrivate::updateHighlight()
1012 if ((!currentItem && highlight) || (currentItem && !highlight))
1014 if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
1015 // auto-update highlight
1016 highlightPosAnimator->to = isRightToLeft()
1017 ? -currentItem->itemPosition()-currentItem->itemSize()
1018 : currentItem->itemPosition();
1019 highlightSizeAnimator->to = currentItem->itemSize();
1020 if (orient == QDeclarative1ListView::Vertical) {
1021 if (highlight->item->width() == 0)
1022 highlight->item->setWidth(currentItem->item->width());
1024 if (highlight->item->height() == 0)
1025 highlight->item->setHeight(currentItem->item->height());
1027 highlightPosAnimator->restart();
1028 highlightSizeAnimator->restart();
1030 updateTrackedItem();
1033 void QDeclarative1ListViewPrivate::createSection(FxListItem1 *listItem)
1035 Q_Q(QDeclarative1ListView);
1036 if (!sectionCriteria || !sectionCriteria->delegate())
1038 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
1039 if (!listItem->section) {
1040 qreal pos = listItem->position();
1041 int i = sectionCacheSize-1;
1042 while (i >= 0 && !sectionCache[i])
1045 listItem->section = sectionCache[i];
1046 sectionCache[i] = 0;
1047 listItem->section->setVisible(true);
1048 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1049 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1051 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1052 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1053 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
1055 QDeclarative_setParent_noEvent(context, nobj);
1056 listItem->section = qobject_cast<QDeclarativeItem *>(nobj);
1057 if (!listItem->section) {
1060 listItem->section->setZValue(1);
1061 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
1062 listItem->section->setParentItem(q->contentItem());
1067 sectionCriteria->delegate()->completeCreate();
1069 listItem->setPosition(pos);
1071 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1072 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1074 } else if (listItem->section) {
1075 qreal pos = listItem->position();
1078 if (!sectionCache[i]) {
1079 sectionCache[i] = listItem->section;
1080 sectionCache[i]->setVisible(false);
1081 listItem->section = 0;
1085 } while (i < sectionCacheSize);
1086 delete listItem->section;
1087 listItem->section = 0;
1088 listItem->setPosition(pos);
1092 void QDeclarative1ListViewPrivate::updateSections()
1094 if (sectionCriteria && !visibleItems.isEmpty()) {
1095 QString prevSection;
1096 if (visibleIndex > 0)
1097 prevSection = sectionAt(visibleIndex-1);
1098 QDeclarative1ListViewAttached *prevAtt = 0;
1100 for (int i = 0; i < visibleItems.count(); ++i) {
1101 QDeclarative1ListViewAttached *attached = visibleItems.at(i)->attached;
1102 attached->setPrevSection(prevSection);
1103 if (visibleItems.at(i)->index != -1) {
1104 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1105 attached->setSection(sectionCriteria->sectionString(propValue));
1106 idx = visibleItems.at(i)->index;
1108 createSection(visibleItems.at(i));
1110 prevAtt->setNextSection(attached->section());
1111 prevSection = attached->section();
1115 if (idx > 0 && idx < model->count()-1)
1116 prevAtt->setNextSection(sectionAt(idx+1));
1118 prevAtt->setNextSection(QString());
1123 void QDeclarative1ListViewPrivate::updateCurrentSection()
1125 Q_Q(QDeclarative1ListView);
1126 if (!sectionCriteria || visibleItems.isEmpty()) {
1127 if (!currentSection.isEmpty()) {
1128 currentSection.clear();
1129 emit q->currentSectionChanged();
1134 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1137 QString newSection = currentSection;
1138 if (index < visibleItems.count())
1139 newSection = visibleItems.at(index)->attached->section();
1141 newSection = visibleItems.first()->attached->section();
1142 if (newSection != currentSection) {
1143 currentSection = newSection;
1144 emit q->currentSectionChanged();
1148 void QDeclarative1ListViewPrivate::updateCurrent(int modelIndex)
1150 Q_Q(QDeclarative1ListView);
1151 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1153 currentItem->attached->setIsCurrentItem(false);
1154 releaseItem(currentItem);
1156 currentIndex = modelIndex;
1157 emit q->currentIndexChanged();
1159 } else if (currentIndex != modelIndex) {
1160 currentIndex = modelIndex;
1161 emit q->currentIndexChanged();
1166 if (currentItem && currentIndex == modelIndex) {
1170 FxListItem1 *oldCurrentItem = currentItem;
1171 currentIndex = modelIndex;
1172 currentItem = createItem(modelIndex);
1173 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1174 oldCurrentItem->attached->setIsCurrentItem(false);
1176 if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1177 // We can calculate exact postion in this case
1178 currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1180 // Create current item now and position as best we can.
1181 // Its position will be corrected when it becomes visible.
1182 currentItem->setPosition(positionAt(modelIndex));
1184 currentItem->item->setFocus(true);
1185 currentItem->attached->setIsCurrentItem(true);
1186 // Avoid showing section delegate twice. We still need the section heading so that
1187 // currentItem positioning works correctly.
1188 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1189 if (currentItem->section)
1190 currentItem->section->setVisible(false);
1191 if (visibleItems.isEmpty())
1192 averageSize = currentItem->size();
1195 emit q->currentIndexChanged();
1196 // Release the old current item
1197 releaseItem(oldCurrentItem);
1200 void QDeclarative1ListViewPrivate::updateAverage()
1202 if (!visibleItems.count())
1205 for (int i = 0; i < visibleItems.count(); ++i)
1206 sum += visibleItems.at(i)->size();
1207 averageSize = qRound(sum / visibleItems.count());
1210 void QDeclarative1ListViewPrivate::updateFooter()
1212 Q_Q(QDeclarative1ListView);
1213 if (!footer && footerComponent) {
1214 QDeclarativeItem *item = 0;
1215 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1216 QObject *nobj = footerComponent->create(context);
1218 QDeclarative_setParent_noEvent(context, nobj);
1219 item = qobject_cast<QDeclarativeItem *>(nobj);
1226 QDeclarative_setParent_noEvent(item, q->contentItem());
1227 item->setParentItem(q->contentItem());
1229 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1230 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1231 footer = new FxListItem1(item, q);
1235 if (visibleItems.count()) {
1236 qreal endPos = lastPosition() + 1;
1237 if (lastVisibleIndex() == model->count()-1) {
1238 footer->setPosition(endPos);
1240 qreal visiblePos = position() + q->height();
1241 if (endPos <= visiblePos || footer->position() < endPos)
1242 footer->setPosition(endPos);
1245 footer->setPosition(visiblePos);
1250 void QDeclarative1ListViewPrivate::updateHeader()
1252 Q_Q(QDeclarative1ListView);
1253 if (!header && headerComponent) {
1254 QDeclarativeItem *item = 0;
1255 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1256 QObject *nobj = headerComponent->create(context);
1258 QDeclarative_setParent_noEvent(context, nobj);
1259 item = qobject_cast<QDeclarativeItem *>(nobj);
1266 QDeclarative_setParent_noEvent(item, q->contentItem());
1267 item->setParentItem(q->contentItem());
1269 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1270 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1271 header = new FxListItem1(item, q);
1275 if (visibleItems.count()) {
1276 qreal startPos = originPosition();
1277 if (visibleIndex == 0) {
1278 header->setPosition(startPos - header->size());
1280 if (position() <= startPos || header->position() > startPos - header->size())
1281 header->setPosition(startPos - header->size());
1284 visiblePos = header->size();
1285 header->setPosition(0);
1290 void QDeclarative1ListViewPrivate::fixupPosition()
1292 if ((haveHighlightRange && highlightRange == QDeclarative1ListView::StrictlyEnforceRange)
1293 || snapMode != QDeclarative1ListView::NoSnap)
1295 if (orient == QDeclarative1ListView::Vertical)
1301 void QDeclarative1ListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1303 if ((orient == QDeclarative1ListView::Horizontal && &data == &vData)
1304 || (orient == QDeclarative1ListView::Vertical && &data == &hData))
1307 correctFlick = false;
1308 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1310 qreal highlightStart;
1313 if (isRightToLeft()) {
1314 // Handle Right-To-Left exceptions
1315 viewPos = -position()-size();
1316 highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1317 highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1319 viewPos = position();
1320 highlightStart = highlightRangeStart;
1321 highlightEnd = highlightRangeEnd;
1324 if (currentItem && haveHighlightRange && highlightRange == QDeclarative1ListView::StrictlyEnforceRange
1325 && moveReason != QDeclarative1ListViewPrivate::SetIndex) {
1327 qreal pos = currentItem->itemPosition();
1328 if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1329 viewPos = pos + currentItem->itemSize() - highlightEnd;
1330 if (viewPos > pos - highlightStart)
1331 viewPos = pos - highlightStart;
1332 if (isRightToLeft())
1333 viewPos = -viewPos-size();
1335 timeline.reset(data.move);
1336 if (viewPos != position()) {
1337 if (fixupMode != Immediate) {
1338 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1339 data.fixingUp = true;
1341 timeline.set(data.move, -viewPos);
1344 vTime = timeline.time();
1345 } else if (snapMode != QDeclarative1ListView::NoSnap && moveReason != QDeclarative1ListViewPrivate::SetIndex) {
1346 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1347 FxListItem1 *topItem = snapItemAt(tempPosition+highlightStart);
1348 FxListItem1 *bottomItem = snapItemAt(tempPosition+highlightEnd);
1350 bool isInBounds = -position() > maxExtent && -position() < minExtent;
1351 if (topItem && isInBounds) {
1352 if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2) {
1353 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1355 if (isRightToLeft())
1356 pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1358 pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1360 } else if (bottomItem && isInBounds) {
1361 if (isRightToLeft())
1362 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1364 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1366 QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent);
1370 qreal dist = qAbs(data.move + pos);
1372 timeline.reset(data.move);
1373 if (fixupMode != Immediate) {
1374 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1375 data.fixingUp = true;
1377 timeline.set(data.move, -pos);
1379 vTime = timeline.time();
1382 QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent);
1384 data.inOvershoot = false;
1388 void QDeclarative1ListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1389 QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity)
1391 Q_Q(QDeclarative1ListView);
1393 data.fixingUp = false;
1395 if ((!haveHighlightRange || highlightRange != QDeclarative1ListView::StrictlyEnforceRange) && snapMode == QDeclarative1ListView::NoSnap) {
1396 correctFlick = true;
1397 QDeclarative1FlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1400 qreal maxDistance = 0;
1401 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1402 // -ve velocity means list is moving up/left
1404 if (data.move.value() < minExtent) {
1405 if (snapMode == QDeclarative1ListView::SnapOneItem) {
1406 if (FxListItem1 *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1407 maxDistance = qAbs(item->position() + dataValue);
1409 maxDistance = qAbs(minExtent - data.move.value());
1412 if (snapMode == QDeclarative1ListView::NoSnap && highlightRange != QDeclarative1ListView::StrictlyEnforceRange)
1413 data.flickTarget = minExtent;
1415 if (data.move.value() > maxExtent) {
1416 if (snapMode == QDeclarative1ListView::SnapOneItem) {
1417 if (FxListItem1 *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1418 maxDistance = qAbs(item->position() + dataValue);
1420 maxDistance = qAbs(maxExtent - data.move.value());
1423 if (snapMode == QDeclarative1ListView::NoSnap && highlightRange != QDeclarative1ListView::StrictlyEnforceRange)
1424 data.flickTarget = maxExtent;
1427 bool overShoot = boundsBehavior == QDeclarative1Flickable::DragAndOvershootBounds;
1428 qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1430 if (maxDistance > 0 || overShoot) {
1431 // These modes require the list to stop exactly on an item boundary.
1432 // The initial flick will estimate the boundary to stop on.
1433 // Since list items can have variable sizes, the boundary will be
1434 // reevaluated and adjusted as we approach the boundary.
1436 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1442 if (!flickingHorizontally && !flickingVertically) {
1443 // the initial flick - estimate boundary
1444 qreal accel = deceleration;
1446 overshootDist = 0.0;
1447 // + averageSize/4 to encourage moving at least one item in the flick direction
1448 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1449 if (maxDistance > 0)
1450 dist = qMin(dist, maxDistance);
1453 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarative1ListView::SnapOneItem) {
1454 qreal distTemp = isRightToLeft() ? -dist : dist;
1455 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1456 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1458 if (data.flickTarget >= minExtent) {
1459 overshootDist = overShootDistance(vSize);
1460 data.flickTarget += overshootDist;
1461 } else if (data.flickTarget <= maxExtent) {
1462 overshootDist = overShootDistance(vSize);
1463 data.flickTarget -= overshootDist;
1466 qreal adjDist = -data.flickTarget + data.move.value();
1467 if (qAbs(adjDist) > qAbs(dist)) {
1468 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1469 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1478 accel = v2 / (2.0f * qAbs(dist));
1479 } else if (overShoot) {
1480 data.flickTarget = data.move.value() - dist;
1481 if (data.flickTarget >= minExtent) {
1482 overshootDist = overShootDistance(vSize);
1483 data.flickTarget += overshootDist;
1484 } else if (data.flickTarget <= maxExtent) {
1485 overshootDist = overShootDistance(vSize);
1486 data.flickTarget -= overshootDist;
1490 timeline.reset(data.move);
1491 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1492 timeline.callback(QDeclarative1TimeLineCallback(&data.move, fixupCallback, this));
1493 if (!flickingHorizontally && q->xflick()) {
1494 flickingHorizontally = true;
1495 emit q->flickingChanged();
1496 emit q->flickingHorizontallyChanged();
1497 emit q->flickStarted();
1499 if (!flickingVertically && q->yflick()) {
1500 flickingVertically = true;
1501 emit q->flickingChanged();
1502 emit q->flickingVerticallyChanged();
1503 emit q->flickStarted();
1505 correctFlick = true;
1507 // reevaluate the target boundary.
1508 qreal newtarget = data.flickTarget;
1509 if (snapMode != QDeclarative1ListView::NoSnap || highlightRange == QDeclarative1ListView::StrictlyEnforceRange) {
1510 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1511 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1512 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1514 if (velocity < 0 && newtarget <= maxExtent)
1515 newtarget = maxExtent - overshootDist;
1516 else if (velocity > 0 && newtarget >= minExtent)
1517 newtarget = minExtent + overshootDist;
1518 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1519 if (qAbs(velocity) < MinimumFlickVelocity)
1520 correctFlick = false;
1523 data.flickTarget = newtarget;
1524 qreal dist = -newtarget + data.move.value();
1525 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1526 correctFlick = false;
1527 timeline.reset(data.move);
1528 fixup(data, minExtent, maxExtent);
1532 timeline.reset(data.move);
1533 timeline.accelDistance(data.move, v, -dist);
1534 timeline.callback(QDeclarative1TimeLineCallback(&data.move, fixupCallback, this));
1537 correctFlick = false;
1538 timeline.reset(data.move);
1539 fixup(data, minExtent, maxExtent);
1543 //----------------------------------------------------------------------------
1546 \qmlclass ListView QDeclarative1ListView
1547 \inqmlmodule QtQuick 1
1548 \ingroup qml-view-elements
1551 \brief The ListView item provides a list view of items provided by a model.
1553 A ListView displays data from models created from built-in QML elements like ListModel
1554 and XmlListModel, or custom model classes defined in C++ that inherit from
1557 A ListView has a \l model, which defines the data to be displayed, and
1558 a \l delegate, which defines how the data should be displayed. Items in a
1559 ListView are laid out horizontally or vertically. List views are inherently
1560 flickable because ListView inherits from \l Flickable.
1562 \section1 Example Usage
1564 The following example shows the definition of a simple list model defined
1565 in a file called \c ContactModel.qml:
1567 \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1569 Another component can display this model data in a ListView, like this:
1571 \snippet doc/src/snippets/declarative/listview/listview.qml import
1573 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1575 \image listview-simple.png
1577 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1578 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1579 the delegate is able to access the model's \c name and \c number data directly.
1581 An improved list view is shown below. The delegate is visually improved and is moved
1582 into a separate \c contactDelegate component.
1584 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1585 \image listview-highlight.png
1587 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1588 and \c focus is set to \c true to enable keyboard navigation for the list view.
1589 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1591 Delegates are instantiated as needed and may be destroyed at any time.
1592 State should \e never be stored in a delegate.
1594 ListView attaches a number of properties to the root item of the delegate, for example
1595 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1596 this attached property directly as \c ListView.isCurrentItem, while the child
1597 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1599 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1601 \note Views do not enable \e clip automatically. If the view
1602 is not clipped by another item or the screen, it will be necessary
1603 to set \e {clip: true} in order to have the out of view items clipped
1606 \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1609 QDeclarative1ListView::QDeclarative1ListView(QDeclarativeItem *parent)
1610 : QDeclarative1Flickable(*(new QDeclarative1ListViewPrivate), parent)
1612 Q_D(QDeclarative1ListView);
1616 QDeclarative1ListView::~QDeclarative1ListView()
1618 Q_D(QDeclarative1ListView);
1627 \qmlattachedproperty bool ListView::isCurrentItem
1628 This attached property is true if this delegate is the current item; otherwise false.
1630 It is attached to each instance of the delegate.
1632 This property may be used to adjust the appearance of the current item, for example:
1634 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1638 \qmlattachedproperty ListView ListView::view
1639 This attached property holds the view that manages this delegate instance.
1641 It is attached to each instance of the delegate.
1645 \qmlattachedproperty string ListView::previousSection
1646 This attached property holds the section of the previous element.
1648 It is attached to each instance of the delegate.
1650 The section is evaluated using the \l {ListView::section.property}{section} properties.
1654 \qmlattachedproperty string ListView::nextSection
1655 This attached property holds the section of the next element.
1657 It is attached to each instance of the delegate.
1659 The section is evaluated using the \l {ListView::section.property}{section} properties.
1663 \qmlattachedproperty string ListView::section
1664 This attached property holds the section of this element.
1666 It is attached to each instance of the delegate.
1668 The section is evaluated using the \l {ListView::section.property}{section} properties.
1672 \qmlattachedproperty bool ListView::delayRemove
1673 This attached property holds whether the delegate may be destroyed.
1675 It is attached to each instance of the delegate.
1677 It is sometimes necessary to delay the destruction of an item
1678 until an animation completes.
1680 The example delegate below ensures that the animation completes before
1681 the item is removed from the list.
1683 \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1687 \qmlattachedsignal QtQuick1::ListView::onAdd()
1688 This attached handler is called immediately after an item is added to the view.
1692 \qmlattachedsignal QtQuick1::ListView::onRemove()
1693 This attached handler is called immediately before an item is removed from the view.
1697 \qmlproperty model QtQuick1::ListView::model
1698 This property holds the model providing data for the list.
1700 The model provides the set of data that is used to create the items
1701 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1702 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1703 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1705 \sa {qmlmodels}{Data Models}
1707 QVariant QDeclarative1ListView::model() const
1709 Q_D(const QDeclarative1ListView);
1710 return d->modelVariant;
1713 void QDeclarative1ListView::setModel(const QVariant &model)
1715 Q_D(QDeclarative1ListView);
1716 if (d->modelVariant == model)
1719 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1720 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1721 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1722 disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1723 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1724 disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1725 disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1728 QDeclarative1VisualModel *oldModel = d->model;
1731 d->modelVariant = model;
1732 QObject *object = qvariant_cast<QObject*>(model);
1733 QDeclarative1VisualModel *vim = 0;
1734 if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) {
1737 d->ownModel = false;
1742 d->model = new QDeclarative1VisualDataModel(qmlContext(this), this);
1745 d->model = oldModel;
1747 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model))
1748 dataModel->setModel(model);
1751 d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore | QDeclarative1ListViewPrivate::BufferAfter;
1752 if (isComponentComplete()) {
1755 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1758 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
1759 d->updateCurrent(d->currentIndex);
1760 if (d->highlight && d->currentItem) {
1761 if (d->autoHighlight)
1762 d->highlight->setPosition(d->currentItem->position());
1763 d->updateTrackedItem();
1766 d->updateViewport();
1768 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1769 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1770 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1771 connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1772 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1773 connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1774 connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1775 emit countChanged();
1777 emit modelChanged();
1781 \qmlproperty Component QtQuick1::ListView::delegate
1783 The delegate provides a template defining each item instantiated by the view.
1784 The index is exposed as an accessible \c index property. Properties of the
1785 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1787 The number of elements in the delegate has a direct effect on the
1788 flicking performance of the view. If at all possible, place functionality
1789 that is not needed for the normal display of the delegate in a \l Loader which
1790 can load additional elements when needed.
1792 The ListView will lay out the items based on the size of the root item
1795 It is recommended that the delagate's size be a whole number to avoid sub-pixel
1798 \note Delegates are instantiated as needed and may be destroyed at any time.
1799 State should \e never be stored in a delegate.
1801 QDeclarativeComponent *QDeclarative1ListView::delegate() const
1803 Q_D(const QDeclarative1ListView);
1805 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model))
1806 return dataModel->delegate();
1812 void QDeclarative1ListView::setDelegate(QDeclarativeComponent *delegate)
1814 Q_D(QDeclarative1ListView);
1815 if (delegate == this->delegate())
1818 d->model = new QDeclarative1VisualDataModel(qmlContext(this));
1821 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) {
1822 int oldCount = dataModel->count();
1823 dataModel->setDelegate(delegate);
1824 if (isComponentComplete()) {
1825 for (int i = 0; i < d->visibleItems.count(); ++i)
1826 d->releaseItem(d->visibleItems.at(i));
1827 d->visibleItems.clear();
1828 d->releaseItem(d->currentItem);
1832 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
1833 d->updateCurrent(d->currentIndex);
1834 if (d->highlight && d->currentItem) {
1835 if (d->autoHighlight)
1836 d->highlight->setPosition(d->currentItem->position());
1837 d->updateTrackedItem();
1839 d->updateViewport();
1841 if (oldCount != dataModel->count())
1842 emit countChanged();
1844 emit delegateChanged();
1848 \qmlproperty int QtQuick1::ListView::currentIndex
1849 \qmlproperty Item QtQuick1::ListView::currentItem
1851 The \c currentIndex property holds the index of the current item, and
1852 \c currentItem holds the current item. Setting the currentIndex to -1
1853 will clear the highlight and set currentItem to null.
1855 If highlightFollowsCurrentItem is \c true, setting either of these
1856 properties will smoothly scroll the ListView so that the current
1857 item becomes visible.
1859 Note that the position of the current item
1860 may only be approximate until it becomes visible in the view.
1862 int QDeclarative1ListView::currentIndex() const
1864 Q_D(const QDeclarative1ListView);
1865 return d->currentIndex;
1868 void QDeclarative1ListView::setCurrentIndex(int index)
1870 Q_D(QDeclarative1ListView);
1871 if (d->requestedIndex >= 0) // currently creating item
1873 d->currentIndexCleared = (index == -1);
1874 if (index == d->currentIndex)
1876 if (isComponentComplete() && d->isValid()) {
1877 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
1878 d->updateCurrent(index);
1879 } else if (d->currentIndex != index) {
1880 d->currentIndex = index;
1881 emit currentIndexChanged();
1885 QDeclarativeItem *QDeclarative1ListView::currentItem()
1887 Q_D(QDeclarative1ListView);
1888 if (!d->currentItem)
1890 return d->currentItem->item;
1894 \qmlproperty Item QtQuick1::ListView::highlightItem
1896 This holds the highlight item created from the \l highlight component.
1898 The \c highlightItem is managed by the view unless
1899 \l highlightFollowsCurrentItem is set to false.
1901 \sa highlight, highlightFollowsCurrentItem
1903 QDeclarativeItem *QDeclarative1ListView::highlightItem()
1905 Q_D(QDeclarative1ListView);
1908 return d->highlight->item;
1912 \qmlproperty int QtQuick1::ListView::count
1913 This property holds the number of items in the view.
1915 int QDeclarative1ListView::count() const
1917 Q_D(const QDeclarative1ListView);
1919 return d->model->count();
1924 \qmlproperty Component QtQuick1::ListView::highlight
1925 This property holds the component to use as the highlight.
1927 An instance of the highlight component is created for each list.
1928 The geometry of the resulting component instance is managed by the list
1929 so as to stay with the current item, unless the highlightFollowsCurrentItem
1932 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1934 QDeclarativeComponent *QDeclarative1ListView::highlight() const
1936 Q_D(const QDeclarative1ListView);
1937 return d->highlightComponent;
1940 void QDeclarative1ListView::setHighlight(QDeclarativeComponent *highlight)
1942 Q_D(QDeclarative1ListView);
1943 if (highlight != d->highlightComponent) {
1944 d->highlightComponent = highlight;
1945 d->createHighlight();
1947 d->updateHighlight();
1948 emit highlightChanged();
1953 \qmlproperty bool QtQuick1::ListView::highlightFollowsCurrentItem
1954 This property holds whether the highlight is managed by the view.
1956 If this property is true (the default value), the highlight is moved smoothly
1957 to follow the current item. Otherwise, the
1958 highlight is not moved by the view, and any movement must be implemented
1961 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1963 \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1965 Note that the highlight animation also affects the way that the view
1966 is scrolled. This is because the view moves to maintain the
1967 highlight within the preferred highlight range (or visible viewport).
1969 \sa highlight, highlightMoveSpeed
1971 bool QDeclarative1ListView::highlightFollowsCurrentItem() const
1973 Q_D(const QDeclarative1ListView);
1974 return d->autoHighlight;
1977 void QDeclarative1ListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1979 Q_D(QDeclarative1ListView);
1980 if (d->autoHighlight != autoHighlight) {
1981 d->autoHighlight = autoHighlight;
1982 if (autoHighlight) {
1983 d->updateHighlight();
1985 if (d->highlightPosAnimator)
1986 d->highlightPosAnimator->stop();
1987 if (d->highlightSizeAnimator)
1988 d->highlightSizeAnimator->stop();
1990 emit highlightFollowsCurrentItemChanged();
1994 //###Possibly rename these properties, since they are very useful even without a highlight?
1996 \qmlproperty real QtQuick1::ListView::preferredHighlightBegin
1997 \qmlproperty real QtQuick1::ListView::preferredHighlightEnd
1998 \qmlproperty enumeration QtQuick1::ListView::highlightRangeMode
2000 These properties define the preferred range of the highlight (for the current item)
2001 within the view. The \c preferredHighlightBegin value must be less than the
2002 \c preferredHighlightEnd value.
2004 These properties affect the position of the current item when the list is scrolled.
2005 For example, if the currently selected item should stay in the middle of the
2006 list when the view is scrolled, set the \c preferredHighlightBegin and
2007 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
2008 item would be. If the \c currentItem is changed programmatically, the list will
2009 automatically scroll so that the current item is in the middle of the view.
2010 Furthermore, the behavior of the current item index will occur whether or not a
2013 Valid values for \c highlightRangeMode are:
2016 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2017 However, the highlight can move outside of the range at the ends of the list or due
2018 to mouse interaction.
2019 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2020 The current item changes if a keyboard or mouse action would cause the highlight to move
2021 outside of the range.
2022 \o ListView.NoHighlightRange - this is the default value.
2025 qreal QDeclarative1ListView::preferredHighlightBegin() const
2027 Q_D(const QDeclarative1ListView);
2028 return d->highlightRangeStart;
2031 void QDeclarative1ListView::setPreferredHighlightBegin(qreal start)
2033 Q_D(QDeclarative1ListView);
2034 d->highlightRangeStartValid = true;
2035 if (d->highlightRangeStart == start)
2037 d->highlightRangeStart = start;
2038 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2039 emit preferredHighlightBeginChanged();
2042 void QDeclarative1ListView::resetPreferredHighlightBegin()
2044 Q_D(QDeclarative1ListView);
2045 d->highlightRangeStartValid = false;
2046 if (d->highlightRangeStart == 0)
2048 d->highlightRangeStart = 0;
2049 emit preferredHighlightBeginChanged();
2052 qreal QDeclarative1ListView::preferredHighlightEnd() const
2054 Q_D(const QDeclarative1ListView);
2055 return d->highlightRangeEnd;
2058 void QDeclarative1ListView::setPreferredHighlightEnd(qreal end)
2060 Q_D(QDeclarative1ListView);
2061 d->highlightRangeEndValid = true;
2062 if (d->highlightRangeEnd == end)
2064 d->highlightRangeEnd = end;
2065 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2066 emit preferredHighlightEndChanged();
2069 void QDeclarative1ListView::resetPreferredHighlightEnd()
2071 Q_D(QDeclarative1ListView);
2072 d->highlightRangeEndValid = false;
2073 if (d->highlightRangeEnd == 0)
2075 d->highlightRangeEnd = 0;
2076 emit preferredHighlightEndChanged();
2079 QDeclarative1ListView::HighlightRangeMode QDeclarative1ListView::highlightRangeMode() const
2081 Q_D(const QDeclarative1ListView);
2082 return d->highlightRange;
2085 void QDeclarative1ListView::setHighlightRangeMode(HighlightRangeMode mode)
2087 Q_D(QDeclarative1ListView);
2088 if (d->highlightRange == mode)
2090 d->highlightRange = mode;
2091 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2092 emit highlightRangeModeChanged();
2096 \qmlproperty real QtQuick1::ListView::spacing
2098 This property holds the spacing between items.
2100 The default value is 0.
2102 qreal QDeclarative1ListView::spacing() const
2104 Q_D(const QDeclarative1ListView);
2108 void QDeclarative1ListView::setSpacing(qreal spacing)
2110 Q_D(QDeclarative1ListView);
2111 if (spacing != d->spacing) {
2112 d->spacing = spacing;
2114 emit spacingChanged();
2119 \qmlproperty enumeration QtQuick1::ListView::orientation
2120 This property holds the orientation of the list.
2125 \o ListView.Horizontal - Items are laid out horizontally
2126 \o ListView.Vertical (default) - Items are laid out vertically
2131 \o Horizontal orientation:
2132 \image ListViewHorizontal.png
2135 \o Vertical orientation:
2136 \image listview-highlight.png
2139 QDeclarative1ListView::Orientation QDeclarative1ListView::orientation() const
2141 Q_D(const QDeclarative1ListView);
2145 void QDeclarative1ListView::setOrientation(QDeclarative1ListView::Orientation orientation)
2147 Q_D(QDeclarative1ListView);
2148 if (d->orient != orientation) {
2149 d->orient = orientation;
2150 if (d->orient == QDeclarative1ListView::Vertical) {
2151 setContentWidth(-1);
2152 setFlickableDirection(VerticalFlick);
2155 setContentHeight(-1);
2156 setFlickableDirection(HorizontalFlick);
2160 emit orientationChanged();
2165 \qmlproperty enumeration QtQuick1::ListView::layoutDirection
2166 This property holds the layout direction of the horizontal list.
2171 \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2172 \o Qt.RightToLeft - Items will be laid out from right to let.
2175 \sa ListView::effectiveLayoutDirection
2178 Qt::LayoutDirection QDeclarative1ListView::layoutDirection() const
2180 Q_D(const QDeclarative1ListView);
2181 return d->layoutDirection;
2184 void QDeclarative1ListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2186 Q_D(QDeclarative1ListView);
2187 if (d->layoutDirection != layoutDirection) {
2188 d->layoutDirection = layoutDirection;
2190 emit layoutDirectionChanged();
2191 emit effectiveLayoutDirectionChanged();
2196 \qmlproperty enumeration QtQuick1::ListView::effectiveLayoutDirection
2197 This property holds the effective layout direction of the horizontal list.
2199 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2200 the visual layout direction of the horizontal list will be mirrored. However, the
2201 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2203 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2206 Qt::LayoutDirection QDeclarative1ListView::effectiveLayoutDirection() const
2208 Q_D(const QDeclarative1ListView);
2209 if (d->effectiveLayoutMirror)
2210 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2212 return d->layoutDirection;
2216 \qmlproperty bool QtQuick1::ListView::keyNavigationWraps
2217 This property holds whether the list wraps key navigation.
2219 If this is true, key navigation that would move the current item selection
2220 past the end of the list instead wraps around and moves the selection to
2221 the start of the list, and vice-versa.
2223 By default, key navigation is not wrapped.
2225 bool QDeclarative1ListView::isWrapEnabled() const
2227 Q_D(const QDeclarative1ListView);
2231 void QDeclarative1ListView::setWrapEnabled(bool wrap)
2233 Q_D(QDeclarative1ListView);
2234 if (d->wrap == wrap)
2237 emit keyNavigationWrapsChanged();
2241 \qmlproperty int QtQuick1::ListView::cacheBuffer
2242 This property determines whether delegates are retained outside the
2243 visible area of the view.
2245 If this value is non-zero, the view keeps as many delegates
2246 instantiated as it can fit within the buffer specified. For example,
2247 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2248 set to 40, then up to 2 delegates above and 2 delegates below the visible
2249 area may be retained.
2251 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2252 instantiated delegates.
2254 Setting this value can improve the smoothness of scrolling behavior at the expense
2255 of additional memory usage. It is not a substitute for creating efficient
2256 delegates; the fewer elements in a delegate, the faster a view can be
2259 int QDeclarative1ListView::cacheBuffer() const
2261 Q_D(const QDeclarative1ListView);
2265 void QDeclarative1ListView::setCacheBuffer(int b)
2267 Q_D(QDeclarative1ListView);
2268 if (d->buffer != b) {
2270 if (isComponentComplete()) {
2271 d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore | QDeclarative1ListViewPrivate::BufferAfter;
2274 emit cacheBufferChanged();
2279 \qmlproperty string QtQuick1::ListView::section.property
2280 \qmlproperty enumeration QtQuick1::ListView::section.criteria
2281 \qmlproperty Component QtQuick1::ListView::section.delegate
2283 These properties hold the expression to be evaluated for the \l section attached property.
2285 The \l section attached property enables a ListView to be visually
2286 separated into different parts. These properties determine how sections
2289 \c section.property holds the name of the property that is the basis
2292 \c section.criteria holds the criteria for forming each section based on
2293 \c section.property. This value can be one of:
2296 \o ViewSection.FullString (default) - sections are created based on the
2297 \c section.property value.
2298 \o ViewSection.FirstCharacter - sections are created based on the first
2299 character of the \c section.property value (for example, 'A', 'B', 'C'
2300 sections, etc. for an address book)
2303 \c section.delegate holds the delegate component for each section.
2305 Each item in the list has attached properties named \c ListView.section,
2306 \c ListView.previousSection and \c ListView.nextSection. These may be
2307 used to place a section header for related items.
2309 For example, here is a ListView that displays a list of animals, separated
2310 into sections. Each item in the ListView is placed in a different section
2311 depending on the "size" property of the model item. The \c sectionHeading
2312 delegate component provides the light blue bar that marks the beginning of
2316 \snippet examples/declarative/modelviews/listview/sections.qml 0
2318 \image qml-listview-sections-example.png
2320 \note Adding sections to a ListView does not automatically re-order the
2321 list items by the section criteria.
2322 If the model is not ordered by section, then it is possible that
2323 the sections created will not be unique; each boundary between
2324 differing sections will result in a section header being created
2325 even if that section exists elsewhere.
2327 \sa {declarative/modelviews/listview}{ListView examples}
2329 QDeclarative1ViewSection *QDeclarative1ListView::sectionCriteria()
2331 Q_D(QDeclarative1ListView);
2332 if (!d->sectionCriteria) {
2333 d->sectionCriteria = new QDeclarative1ViewSection(this);
2334 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2336 return d->sectionCriteria;
2340 \qmlproperty string QtQuick1::ListView::currentSection
2341 This property holds the section that is currently at the beginning of the view.
2343 QString QDeclarative1ListView::currentSection() const
2345 Q_D(const QDeclarative1ListView);
2346 return d->currentSection;
2350 \qmlproperty real QtQuick1::ListView::highlightMoveSpeed
2351 \qmlproperty int QtQuick1::ListView::highlightMoveDuration
2352 \qmlproperty real QtQuick1::ListView::highlightResizeSpeed
2353 \qmlproperty int QtQuick1::ListView::highlightResizeDuration
2355 These properties hold the move and resize animation speed of the highlight delegate.
2357 \l highlightFollowsCurrentItem must be true for these properties
2360 The default value for the speed properties is 400 pixels/second.
2361 The default value for the duration properties is -1, i.e. the
2362 highlight will take as much time as necessary to move at the set speed.
2364 These properties have the same characteristics as a SmoothedAnimation.
2366 \sa highlightFollowsCurrentItem
2368 qreal QDeclarative1ListView::highlightMoveSpeed() const
2370 Q_D(const QDeclarative1ListView);\
2371 return d->highlightMoveSpeed;
2374 void QDeclarative1ListView::setHighlightMoveSpeed(qreal speed)
2376 Q_D(QDeclarative1ListView);\
2377 if (d->highlightMoveSpeed != speed) {
2378 d->highlightMoveSpeed = speed;
2379 if (d->highlightPosAnimator)
2380 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2381 emit highlightMoveSpeedChanged();
2385 int QDeclarative1ListView::highlightMoveDuration() const
2387 Q_D(const QDeclarative1ListView);
2388 return d->highlightMoveDuration;
2391 void QDeclarative1ListView::setHighlightMoveDuration(int duration)
2393 Q_D(QDeclarative1ListView);\
2394 if (d->highlightMoveDuration != duration) {
2395 d->highlightMoveDuration = duration;
2396 if (d->highlightPosAnimator)
2397 d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2398 emit highlightMoveDurationChanged();
2402 qreal QDeclarative1ListView::highlightResizeSpeed() const
2404 Q_D(const QDeclarative1ListView);\
2405 return d->highlightResizeSpeed;
2408 void QDeclarative1ListView::setHighlightResizeSpeed(qreal speed)
2410 Q_D(QDeclarative1ListView);\
2411 if (d->highlightResizeSpeed != speed) {
2412 d->highlightResizeSpeed = speed;
2413 if (d->highlightSizeAnimator)
2414 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2415 emit highlightResizeSpeedChanged();
2419 int QDeclarative1ListView::highlightResizeDuration() const
2421 Q_D(const QDeclarative1ListView);
2422 return d->highlightResizeDuration;
2425 void QDeclarative1ListView::setHighlightResizeDuration(int duration)
2427 Q_D(QDeclarative1ListView);\
2428 if (d->highlightResizeDuration != duration) {
2429 d->highlightResizeDuration = duration;
2430 if (d->highlightSizeAnimator)
2431 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2432 emit highlightResizeDurationChanged();
2437 \qmlproperty enumeration QtQuick1::ListView::snapMode
2439 This property determines how the view scrolling will settle following a drag or flick.
2440 The possible values are:
2443 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2444 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2446 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2447 visible item at the time the mouse button is released. This mode is particularly
2448 useful for moving one page at a time.
2451 \c snapMode does not affect the \l currentIndex. To update the
2452 \l currentIndex as the list is moved, set \l highlightRangeMode
2453 to \c ListView.StrictlyEnforceRange.
2455 \sa highlightRangeMode
2457 QDeclarative1ListView::SnapMode QDeclarative1ListView::snapMode() const
2459 Q_D(const QDeclarative1ListView);
2463 void QDeclarative1ListView::setSnapMode(SnapMode mode)
2465 Q_D(QDeclarative1ListView);
2466 if (d->snapMode != mode) {
2468 emit snapModeChanged();
2473 \qmlproperty Component QtQuick1::ListView::footer
2474 This property holds the component to use as the footer.
2476 An instance of the footer component is created for each view. The
2477 footer is positioned at the end of the view, after any items.
2481 QDeclarativeComponent *QDeclarative1ListView::footer() const
2483 Q_D(const QDeclarative1ListView);
2484 return d->footerComponent;
2487 void QDeclarative1ListView::setFooter(QDeclarativeComponent *footer)
2489 Q_D(QDeclarative1ListView);
2490 if (d->footerComponent != footer) {
2493 scene()->removeItem(d->footer->item);
2494 d->footer->item->deleteLater();
2498 d->footerComponent = footer;
2499 d->minExtentDirty = true;
2500 d->maxExtentDirty = true;
2501 if (isComponentComplete()) {
2503 d->updateViewport();
2506 emit footerChanged();
2511 \qmlproperty Component QtQuick1::ListView::header
2512 This property holds the component to use as the header.
2514 An instance of the header component is created for each view. The
2515 header is positioned at the beginning of the view, before any items.
2519 QDeclarativeComponent *QDeclarative1ListView::header() const
2521 Q_D(const QDeclarative1ListView);
2522 return d->headerComponent;
2525 void QDeclarative1ListView::setHeader(QDeclarativeComponent *header)
2527 Q_D(QDeclarative1ListView);
2528 if (d->headerComponent != header) {
2531 scene()->removeItem(d->header->item);
2532 d->header->item->deleteLater();
2536 d->headerComponent = header;
2537 d->minExtentDirty = true;
2538 d->maxExtentDirty = true;
2539 if (isComponentComplete()) {
2542 d->updateViewport();
2545 emit headerChanged();
2549 void QDeclarative1ListView::setContentX(qreal pos)
2551 Q_D(QDeclarative1ListView);
2552 // Positioning the view manually should override any current movement state
2553 d->moveReason = QDeclarative1ListViewPrivate::Other;
2554 QDeclarative1Flickable::setContentX(pos);
2557 void QDeclarative1ListView::setContentY(qreal pos)
2559 Q_D(QDeclarative1ListView);
2560 // Positioning the view manually should override any current movement state
2561 d->moveReason = QDeclarative1ListViewPrivate::Other;
2562 QDeclarative1Flickable::setContentY(pos);
2565 bool QDeclarative1ListView::event(QEvent *event)
2567 Q_D(QDeclarative1ListView);
2568 if (event->type() == QEvent::User) {
2573 return QDeclarative1Flickable::event(event);
2576 void QDeclarative1ListView::viewportMoved()
2578 Q_D(QDeclarative1ListView);
2579 QDeclarative1Flickable::viewportMoved();
2582 // Recursion can occur due to refill changing the content size.
2583 if (d->inViewportMoved)
2585 d->inViewportMoved = true;
2586 d->lazyRelease = true;
2588 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2589 d->moveReason = QDeclarative1ListViewPrivate::Mouse;
2590 if (d->moveReason != QDeclarative1ListViewPrivate::SetIndex) {
2591 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2592 // reposition highlight
2593 qreal pos = d->highlight->position();
2595 qreal highlightStart;
2597 if (d->isRightToLeft()) {
2598 // Handle Right-To-Left exceptions
2599 viewPos = -d->position()-d->size();
2600 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2601 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2603 viewPos = d->position();
2604 highlightStart = d->highlightRangeStart;
2605 highlightEnd = d->highlightRangeEnd;
2607 if (pos > viewPos + highlightEnd - d->highlight->size())
2608 pos = viewPos + highlightEnd - d->highlight->size();
2609 if (pos < viewPos + highlightStart)
2610 pos = viewPos + highlightStart;
2611 d->highlightPosAnimator->stop();
2612 d->highlight->setPosition(qRound(pos));
2614 // update current index
2615 if (FxListItem1 *snapItem = d->snapItemAt(d->highlight->position())) {
2616 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2617 d->updateCurrent(snapItem->index);
2622 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2623 d->inFlickCorrection = true;
2624 // Near an end and it seems that the extent has changed?
2625 // Recalculate the flick so that we don't end up in an odd position.
2626 if (yflick() && !d->vData.inOvershoot) {
2627 if (d->vData.velocity > 0) {
2628 const qreal minY = minYExtent();
2629 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2630 && minY != d->vData.flickTarget)
2631 d->flickY(-d->vData.smoothVelocity.value());
2632 d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore;
2633 } else if (d->vData.velocity < 0) {
2634 const qreal maxY = maxYExtent();
2635 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2636 && maxY != d->vData.flickTarget)
2637 d->flickY(-d->vData.smoothVelocity.value());
2638 d->bufferMode = QDeclarative1ListViewPrivate::BufferAfter;
2642 if (xflick() && !d->hData.inOvershoot) {
2643 if (d->hData.velocity > 0) {
2644 const qreal minX = minXExtent();
2645 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2646 && minX != d->hData.flickTarget)
2647 d->flickX(-d->hData.smoothVelocity.value());
2648 d->bufferMode = d->isRightToLeft()
2649 ? QDeclarative1ListViewPrivate::BufferAfter : QDeclarative1ListViewPrivate::BufferBefore;
2650 } else if (d->hData.velocity < 0) {
2651 const qreal maxX = maxXExtent();
2652 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2653 && maxX != d->hData.flickTarget)
2654 d->flickX(-d->hData.smoothVelocity.value());
2655 d->bufferMode = d->isRightToLeft()
2656 ? QDeclarative1ListViewPrivate::BufferBefore : QDeclarative1ListViewPrivate::BufferAfter;
2659 d->inFlickCorrection = false;
2661 d->inViewportMoved = false;
2664 qreal QDeclarative1ListView::minYExtent() const
2666 Q_D(const QDeclarative1ListView);
2667 if (d->orient == QDeclarative1ListView::Horizontal)
2668 return QDeclarative1Flickable::minYExtent();
2669 if (d->minExtentDirty) {
2670 d->minExtent = -d->startPosition();
2671 if (d->header && d->visibleItems.count())
2672 d->minExtent += d->header->size();
2673 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2674 d->minExtent += d->highlightRangeStart;
2675 if (d->sectionCriteria) {
2676 if (d->visibleItem(0))
2677 d->minExtent -= d->visibleItem(0)->sectionSize();
2679 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2681 d->minExtentDirty = false;
2684 return d->minExtent;
2687 qreal QDeclarative1ListView::maxYExtent() const
2689 Q_D(const QDeclarative1ListView);
2690 if (d->orient == QDeclarative1ListView::Horizontal)
2692 if (d->maxExtentDirty) {
2693 if (!d->model || !d->model->count()) {
2694 d->maxExtent = d->header ? -d->header->size() : 0;
2695 d->maxExtent += height();
2696 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2697 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2698 if (d->highlightRangeEnd != d->highlightRangeStart)
2699 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2701 d->maxExtent = -(d->endPosition() - height() + 1);
2704 d->maxExtent -= d->footer->size();
2705 qreal minY = minYExtent();
2706 if (d->maxExtent > minY)
2707 d->maxExtent = minY;
2708 d->maxExtentDirty = false;
2710 return d->maxExtent;
2713 qreal QDeclarative1ListView::minXExtent() const
2715 Q_D(const QDeclarative1ListView);
2716 if (d->orient == QDeclarative1ListView::Vertical)
2717 return QDeclarative1Flickable::minXExtent();
2718 if (d->minExtentDirty) {
2719 d->minExtent = -d->startPosition();
2721 qreal highlightStart;
2723 qreal endPositionFirstItem = 0;
2724 if (d->isRightToLeft()) {
2725 if (d->model && d->model->count())
2726 endPositionFirstItem = d->positionAt(d->model->count()-1);
2728 d->minExtent += d->header->size();
2729 highlightStart = d->highlightRangeStartValid
2730 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2731 : d->size() - (d->lastPosition()-endPositionFirstItem);
2732 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2734 d->minExtent += d->footer->size();
2735 qreal maxX = maxXExtent();
2736 if (d->minExtent < maxX)
2737 d->minExtent = maxX;
2739 endPositionFirstItem = d->endPositionAt(0);
2740 highlightStart = d->highlightRangeStart;
2741 highlightEnd = d->highlightRangeEnd;
2742 if (d->header && d->visibleItems.count())
2743 d->minExtent += d->header->size();
2745 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2746 d->minExtent += highlightStart;
2747 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2749 d->minExtentDirty = false;
2752 return d->minExtent;
2755 qreal QDeclarative1ListView::maxXExtent() const
2757 Q_D(const QDeclarative1ListView);
2758 if (d->orient == QDeclarative1ListView::Vertical)
2760 if (d->maxExtentDirty) {
2761 qreal highlightStart;
2763 qreal lastItemPosition = 0;
2765 if (d->isRightToLeft()) {
2766 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2767 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2768 lastItemPosition = d->endPosition();
2770 highlightStart = d->highlightRangeStart;
2771 highlightEnd = d->highlightRangeEnd;
2772 if (d->model && d->model->count())
2773 lastItemPosition = d->positionAt(d->model->count()-1);
2775 if (!d->model || !d->model->count()) {
2776 if (!d->isRightToLeft())
2777 d->maxExtent = d->header ? -d->header->size() : 0;
2778 d->maxExtent += width();
2779 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2780 d->maxExtent = -(lastItemPosition - highlightStart);
2781 if (highlightEnd != highlightStart) {
2782 d->maxExtent = d->isRightToLeft()
2783 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2784 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2787 d->maxExtent = -(d->endPosition() - width() + 1);
2789 if (d->isRightToLeft()) {
2790 if (d->header && d->visibleItems.count())
2791 d->maxExtent -= d->header->size();
2794 d->maxExtent -= d->footer->size();
2795 qreal minX = minXExtent();
2796 if (d->maxExtent > minX)
2797 d->maxExtent = minX;
2799 d->maxExtentDirty = false;
2801 return d->maxExtent;
2804 void QDeclarative1ListView::keyPressEvent(QKeyEvent *event)
2806 Q_D(QDeclarative1ListView);
2807 keyPressPreHandler(event);
2808 if (event->isAccepted())
2811 if (d->model && d->model->count() && d->interactive) {
2812 if ((d->orient == QDeclarative1ListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2813 || (d->orient == QDeclarative1ListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2814 || (d->orient == QDeclarative1ListView::Vertical && event->key() == Qt::Key_Up)) {
2815 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2816 decrementCurrentIndex();
2819 } else if (d->wrap) {
2823 } else if ((d->orient == QDeclarative1ListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2824 || (d->orient == QDeclarative1ListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2825 || (d->orient == QDeclarative1ListView::Vertical && event->key() == Qt::Key_Down)) {
2826 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2827 incrementCurrentIndex();
2830 } else if (d->wrap) {
2837 QDeclarative1Flickable::keyPressEvent(event);
2840 void QDeclarative1ListView::geometryChanged(const QRectF &newGeometry,
2841 const QRectF &oldGeometry)
2843 Q_D(QDeclarative1ListView);
2844 d->maxExtentDirty = true;
2845 d->minExtentDirty = true;
2846 if (d->isRightToLeft() && d->orient == QDeclarative1ListView::Horizontal) {
2847 // maintain position relative to the right edge
2848 int dx = newGeometry.width() - oldGeometry.width();
2849 setContentX(contentX() - dx);
2851 QDeclarative1Flickable::geometryChanged(newGeometry, oldGeometry);
2856 \qmlmethod QtQuick1::ListView::incrementCurrentIndex()
2858 Increments the current index. The current index will wrap
2859 if keyNavigationWraps is true and it is currently at the end.
2860 This method has no effect if the \l count is zero.
2862 \bold Note: methods should only be called after the Component has completed.
2864 void QDeclarative1ListView::incrementCurrentIndex()
2866 Q_D(QDeclarative1ListView);
2867 int count = d->model ? d->model->count() : 0;
2868 if (count && (currentIndex() < count - 1 || d->wrap)) {
2869 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
2870 int index = currentIndex()+1;
2871 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2876 \qmlmethod QtQuick1::ListView::decrementCurrentIndex()
2878 Decrements the current index. The current index will wrap
2879 if keyNavigationWraps is true and it is currently at the beginning.
2880 This method has no effect if the \l count is zero.
2882 \bold Note: methods should only be called after the Component has completed.
2884 void QDeclarative1ListView::decrementCurrentIndex()
2886 Q_D(QDeclarative1ListView);
2887 int count = d->model ? d->model->count() : 0;
2888 if (count && (currentIndex() > 0 || d->wrap)) {
2889 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
2890 int index = currentIndex()-1;
2891 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2895 void QDeclarative1ListViewPrivate::positionViewAtIndex(int index, int mode)
2897 Q_Q(QDeclarative1ListView);
2900 if (mode < QDeclarative1ListView::Beginning || mode > QDeclarative1ListView::Contain)
2902 int idx = qMax(qMin(index, model->count()-1), 0);
2904 if (layoutScheduled)
2906 qreal pos = isRightToLeft() ? -position() - size() : position();
2907 FxListItem1 *item = visibleItem(idx);
2909 if (orient == QDeclarative1ListView::Vertical)
2910 maxExtent = -q->maxYExtent();
2912 maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2915 int itemPos = positionAt(idx);
2916 // save the currently visible items in case any of them end up visible again
2917 QList<FxListItem1*> oldVisible = visibleItems;
2918 visibleItems.clear();
2919 visiblePos = itemPos;
2921 setPosition(qMin(qreal(itemPos), maxExtent));
2922 // now release the reference to all the old visible items.
2923 for (int i = 0; i < oldVisible.count(); ++i)
2924 releaseItem(oldVisible.at(i));
2925 item = visibleItem(idx);
2928 const qreal itemPos = item->position();
2930 case QDeclarative1ListView::Beginning:
2932 if (index < 0 && header)
2933 pos -= header->size();
2935 case QDeclarative1ListView::Center:
2936 pos = itemPos - (size() - item->size())/2;
2938 case QDeclarative1ListView::End:
2939 pos = itemPos - size() + item->size();
2940 if (index >= model->count() && footer)
2941 pos += footer->size();
2943 case QDeclarative1ListView::Visible:
2944 if (itemPos > pos + size())
2945 pos = itemPos - size() + item->size();
2946 else if (item->endPosition() < pos)
2949 case QDeclarative1ListView::Contain:
2950 if (item->endPosition() > pos + size())
2951 pos = itemPos - size() + item->size();
2955 pos = qMin(pos, maxExtent);
2957 if (orient == QDeclarative1ListView::Vertical) {
2958 minExtent = -q->minYExtent();
2960 minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2962 pos = qMax(pos, minExtent);
2963 moveReason = QDeclarative1ListViewPrivate::Other;
2967 if (autoHighlight) {
2968 highlight->setPosition(currentItem->itemPosition());
2969 highlight->setSize(currentItem->itemSize());
2978 \qmlmethod QtQuick1::ListView::positionViewAtIndex(int index, PositionMode mode)
2980 Positions the view such that the \a index is at the position specified by
2984 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2985 \o ListView.Center - position item in the center of the view.
2986 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2987 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2988 bring the item into view.
2989 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
2990 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2993 If positioning the view at \a index would cause empty space to be displayed at
2994 the beginning or end of the view, the view will be positioned at the boundary.
2996 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2997 at a particular index. This is unreliable since removing items from the start
2998 of the list does not cause all other items to be repositioned, and because
2999 the actual start of the view can vary based on the size of the delegates.
3000 The correct way to bring an item into view is with \c positionViewAtIndex.
3002 \bold Note: methods should only be called after the Component has completed. To position
3003 the view at startup, this method should be called by Component.onCompleted. For
3004 example, to position the view at the end:
3007 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3010 void QDeclarative1ListView::positionViewAtIndex(int index, int mode)
3012 Q_D(QDeclarative1ListView);
3013 if (!d->isValid() || index < 0 || index >= d->model->count())
3015 d->positionViewAtIndex(index, mode);
3019 \qmlmethod QtQuick1::ListView::positionViewAtBeginning()
3020 \qmlmethod QtQuick1::ListView::positionViewAtEnd()
3023 Positions the view at the beginning or end, taking into account any header or footer.
3025 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3026 at a particular index. This is unreliable since removing items from the start
3027 of the list does not cause all other items to be repositioned, and because
3028 the actual start of the view can vary based on the size of the delegates.
3030 \bold Note: methods should only be called after the Component has completed. To position
3031 the view at startup, this method should be called by Component.onCompleted. For
3032 example, to position the view at the end on startup:
3035 Component.onCompleted: positionViewAtEnd()
3038 void QDeclarative1ListView::positionViewAtBeginning()
3040 Q_D(QDeclarative1ListView);
3043 d->positionViewAtIndex(-1, Beginning);
3046 void QDeclarative1ListView::positionViewAtEnd()
3048 Q_D(QDeclarative1ListView);
3051 d->positionViewAtIndex(d->model->count(), End);
3055 \qmlmethod int QtQuick1::ListView::indexAt(int x, int y)
3057 Returns the index of the visible item containing the point \a x, \a y in content
3058 coordinates. If there is no item at the point specified, or the item is
3059 not visible -1 is returned.
3061 If the item is outside the visible area, -1 is returned, regardless of
3062 whether an item will exist at that point when scrolled into view.
3064 \bold Note: methods should only be called after the Component has completed.
3066 int QDeclarative1ListView::indexAt(qreal x, qreal y) const
3068 Q_D(const QDeclarative1ListView);
3069 for (int i = 0; i < d->visibleItems.count(); ++i) {
3070 const FxListItem1 *listItem = d->visibleItems.at(i);
3071 if(listItem->contains(x, y))
3072 return listItem->index;
3078 void QDeclarative1ListView::componentComplete()
3080 Q_D(QDeclarative1ListView);
3081 QDeclarative1Flickable::componentComplete();
3087 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
3088 if (d->currentIndex < 0 && !d->currentIndexCleared)
3089 d->updateCurrent(0);
3091 d->updateCurrent(d->currentIndex);
3092 if (d->highlight && d->currentItem) {
3093 if (d->autoHighlight)
3094 d->highlight->setPosition(d->currentItem->position());
3095 d->updateTrackedItem();
3097 d->moveReason = QDeclarative1ListViewPrivate::Other;
3102 void QDeclarative1ListView::updateSections()
3104 Q_D(QDeclarative1ListView);
3105 if (isComponentComplete() && d->model) {
3106 QList<QByteArray> roles;
3107 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3108 roles << d->sectionCriteria->property().toUtf8();
3109 d->model->setWatchedRoles(roles);
3110 d->updateSections();
3116 void QDeclarative1ListView::refill()
3118 Q_D(QDeclarative1ListView);
3119 if (d->isRightToLeft())
3120 d->refill(-d->position()-d->size()+1, -d->position());
3122 d->refill(d->position(), d->position()+d->size()-1);
3125 void QDeclarative1ListView::trackedPositionChanged()
3127 Q_D(QDeclarative1ListView);
3128 if (!d->trackedItem || !d->currentItem)
3130 if (d->moveReason == QDeclarative1ListViewPrivate::SetIndex) {
3131 qreal trackedPos = qCeil(d->trackedItem->position());
3132 qreal trackedSize = d->trackedItem->size();
3133 if (d->trackedItem != d->currentItem) {
3134 trackedPos -= d->currentItem->sectionSize();
3135 trackedSize += d->currentItem->sectionSize();
3138 qreal highlightStart;
3140 if (d->isRightToLeft()) {
3141 viewPos = -d->position()-d->size();
3142 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3143 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3145 viewPos = d->position();
3146 highlightStart = d->highlightRangeStart;
3147 highlightEnd = d->highlightRangeEnd;
3149 qreal pos = viewPos;
3150 if (d->haveHighlightRange) {
3151 if (d->highlightRange == StrictlyEnforceRange) {
3152 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3153 pos = trackedPos - highlightEnd + d->trackedItem->size();
3154 if (trackedPos < pos + highlightStart)
3155 pos = trackedPos - highlightStart;
3157 if (trackedPos < d->startPosition() + highlightStart) {
3158 pos = d->startPosition();
3159 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3160 pos = d->endPosition() - d->size() + 1;
3161 if (pos < d->startPosition())
3162 pos = d->startPosition();
3164 if (trackedPos > pos + highlightEnd - trackedSize)
3165 pos = trackedPos - highlightEnd + trackedSize;
3166 if (trackedPos < pos + highlightStart)
3167 pos = trackedPos - highlightStart;
3171 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
3172 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
3173 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
3174 && d->currentItem->endPosition() >= viewPos + d->size()) {
3175 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
3176 pos = d->trackedItem->endPosition() - d->size() + 1;
3177 if (trackedSize > d->size())
3180 pos = d->currentItem->endPosition() - d->size() + 1;
3181 if (d->currentItem->size() > d->size())
3182 pos = d->currentItem->position();
3186 if (viewPos != pos) {
3188 d->calcVelocity = true;
3189 d->setPosition(pos);
3190 d->calcVelocity = false;
3195 void QDeclarative1ListView::itemsInserted(int modelIndex, int count)
3197 Q_D(QDeclarative1ListView);
3198 if (!isComponentComplete())
3200 d->updateUnrequestedIndexes();
3201 d->moveReason = QDeclarative1ListViewPrivate::Other;
3203 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
3204 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
3207 int i = d->visibleItems.count() - 1;
3208 while (i > 0 && d->visibleItems.at(i)->index == -1)
3210 if (i == 0 && d->visibleItems.first()->index == -1) {
3211 // there are no visible items except items marked for removal
3212 index = d->visibleItems.count();
3213 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
3214 && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
3215 // Special case of appending an item to the model.
3216 index = d->visibleItems.count();
3218 if (modelIndex < d->visibleIndex) {
3219 // Insert before visible items
3220 d->visibleIndex += count;
3221 for (int i = 0; i < d->visibleItems.count(); ++i) {
3222 FxListItem1 *listItem = d->visibleItems.at(i);
3223 if (listItem->index != -1 && listItem->index >= modelIndex)
3224 listItem->index += count;
3227 if (d->currentIndex >= modelIndex) {
3228 // adjust current item index
3229 d->currentIndex += count;
3231 d->currentItem->index = d->currentIndex;
3232 emit currentIndexChanged();
3234 d->scheduleLayout();
3235 d->itemCount += count;
3236 emit countChanged();
3241 // index can be the next item past the end of the visible items list (i.e. appended)
3243 if (d->visibleItems.count()) {
3244 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
3245 : d->visibleItems.last()->endPosition()+d->spacing+1;
3246 } else if (d->itemCount == 0 && d->header) {
3247 pos = d->header->size();
3250 int initialPos = pos;
3252 QList<FxListItem1*> added;
3253 bool addedVisible = false;
3254 FxListItem1 *firstVisible = d->firstVisibleItem();
3255 if (firstVisible && pos < firstVisible->position()) {
3256 // Insert items before the visible item.
3257 int insertionIdx = index;
3259 int from = tempPos - d->buffer;
3260 for (i = count-1; i >= 0 && pos > from; --i) {
3261 if (!addedVisible) {
3262 d->scheduleLayout();
3263 addedVisible = true;
3265 FxListItem1 *item = d->createItem(modelIndex + i);
3266 d->visibleItems.insert(insertionIdx, item);
3267 pos -= item->size() + d->spacing;
3268 item->setPosition(pos);
3272 // If we didn't insert all our new items - anything
3273 // before the current index is not visible - remove it.
3274 while (insertionIdx--) {
3275 FxListItem1 *item = d->visibleItems.takeFirst();
3276 if (item->index != -1)
3278 d->releaseItem(item);
3281 // adjust pos of items before inserted items.
3282 for (int i = insertionIdx-1; i >= 0; i--) {
3283 FxListItem1 *listItem = d->visibleItems.at(i);
3284 listItem->setPosition(listItem->position() - (initialPos - pos));
3289 int to = d->buffer+tempPos+d->size();
3290 for (i = 0; i < count && pos <= to; ++i) {
3291 if (!addedVisible) {
3292 d->scheduleLayout();
3293 addedVisible = true;
3295 FxListItem1 *item = d->createItem(modelIndex + i);
3296 d->visibleItems.insert(index, item);
3297 item->setPosition(pos);
3299 pos += item->size() + d->spacing;
3303 // We didn't insert all our new items, which means anything
3304 // beyond the current index is not visible - remove it.
3305 while (d->visibleItems.count() > index)
3306 d->releaseItem(d->visibleItems.takeLast());
3308 diff = pos - initialPos;
3310 if (d->itemCount && d->currentIndex >= modelIndex) {
3311 // adjust current item index
3312 d->currentIndex += count;
3313 if (d->currentItem) {
3314 d->currentItem->index = d->currentIndex;
3315 d->currentItem->setPosition(d->currentItem->position() + diff);
3317 emit currentIndexChanged();
3318 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
3319 d->updateCurrent(0);
3321 // Update the indexes of the following visible items.
3322 for (; index < d->visibleItems.count(); ++index) {
3323 FxListItem1 *listItem = d->visibleItems.at(index);
3324 if (d->currentItem && listItem->item != d->currentItem->item)
3325 listItem->setPosition(listItem->position() + diff);
3326 if (listItem->index != -1)
3327 listItem->index += count;
3329 // everything is in order now - emit add() signal
3330 for (int j = 0; j < added.count(); ++j)
3331 added.at(j)->attached->emitAdd();
3333 d->updateSections();
3334 d->itemCount += count;
3335 emit countChanged();
3338 void QDeclarative1ListView::itemsRemoved(int modelIndex, int count)
3340 Q_D(QDeclarative1ListView);
3341 if (!isComponentComplete())
3343 d->moveReason = QDeclarative1ListViewPrivate::Other;
3344 d->updateUnrequestedIndexes();
3345 d->itemCount -= count;
3347 FxListItem1 *firstVisible = d->firstVisibleItem();
3348 int preRemovedSize = 0;
3349 bool removedVisible = false;
3350 // Remove the items from the visible list, skipping anything already marked for removal
3351 QList<FxListItem1*>::Iterator it = d->visibleItems.begin();
3352 while (it != d->visibleItems.end()) {
3353 FxListItem1 *item = *it;
3354 if (item->index == -1 || item->index < modelIndex) {
3355 // already removed, or before removed items
3357 } else if (item->index >= modelIndex + count) {
3358 // after removed items
3359 item->index -= count;
3363 if (!removedVisible) {
3364 d->scheduleLayout();
3365 removedVisible = true;
3367 item->attached->emitRemove();
3368 if (item->attached->delayRemove()) {
3370 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
3373 if (item == firstVisible)
3375 if (firstVisible && item->position() < firstVisible->position())
3376 preRemovedSize += item->size();
3377 it = d->visibleItems.erase(it);
3378 d->releaseItem(item);
3383 if (firstVisible && d->visibleItems.first() != firstVisible)
3384 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
3386 // update visibleIndex
3387 bool haveVisibleIndex = false;
3388 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3389 if ((*it)->index != -1) {
3390 d->visibleIndex = (*it)->index;
3391 haveVisibleIndex = true;
3397 if (d->currentIndex >= modelIndex + count) {
3398 d->currentIndex -= count;
3400 d->currentItem->index -= count;
3401 emit currentIndexChanged();
3402 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
3403 // current item has been removed.
3404 d->currentItem->attached->setIsCurrentItem(false);
3405 d->releaseItem(d->currentItem);
3407 d->currentIndex = -1;
3409 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
3411 emit currentIndexChanged();
3414 if (!haveVisibleIndex) {
3415 d->timeline.clear();
3416 if (removedVisible && d->itemCount == 0) {
3417 d->visibleIndex = 0;
3418 d->visiblePos = d->header ? d->header->size() : 0;
3424 if (modelIndex < d->visibleIndex)
3425 d->visibleIndex = modelIndex+1;
3426 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3430 d->updateSections();
3431 emit countChanged();
3434 void QDeclarative1ListView::destroyRemoved()
3436 Q_D(QDeclarative1ListView);
3437 for (QList<FxListItem1*>::Iterator it = d->visibleItems.begin();
3438 it != d->visibleItems.end();) {
3439 FxListItem1 *listItem = *it;
3440 if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3441 d->releaseItem(listItem);
3442 it = d->visibleItems.erase(it);
3448 // Correct the positioning of the items
3449 d->updateSections();
3453 void QDeclarative1ListView::itemsMoved(int from, int to, int count)
3455 Q_D(QDeclarative1ListView);
3456 if (!isComponentComplete())
3458 d->updateUnrequestedIndexes();
3460 if (d->visibleItems.isEmpty()) {
3465 d->moveReason = QDeclarative1ListViewPrivate::Other;
3466 FxListItem1 *firstVisible = d->firstVisibleItem();
3467 qreal firstItemPos = firstVisible->position();
3468 QHash<int,FxListItem1*> moved;
3471 QList<FxListItem1*>::Iterator it = d->visibleItems.begin();
3472 while (it != d->visibleItems.end()) {
3473 FxListItem1 *item = *it;
3474 if (item->index >= from && item->index < from + count) {
3475 // take the items that are moving
3476 item->index += (to-from);
3477 moved.insert(item->index, item);
3478 if (item->position() < firstItemPos)
3479 moveBy += item->size();
3480 it = d->visibleItems.erase(it);
3482 // move everything after the moved items.
3483 if (item->index > from && item->index != -1)
3484 item->index -= count;
3489 int remaining = count;
3490 int endIndex = d->visibleIndex;
3491 it = d->visibleItems.begin();
3492 while (it != d->visibleItems.end()) {
3493 FxListItem1 *item = *it;
3494 if (remaining && item->index >= to && item->index < to + count) {
3495 // place items in the target position, reusing any existing items
3496 FxListItem1 *movedItem = moved.take(item->index);
3498 movedItem = d->createItem(item->index);
3499 if (item->index <= firstVisible->index)
3500 moveBy -= movedItem->size();
3501 it = d->visibleItems.insert(it, movedItem);
3505 if (item->index != -1) {
3506 if (item->index >= to) {
3507 // update everything after the moved items.
3508 item->index += count;
3510 endIndex = item->index;
3516 // If we have moved items to the end of the visible items
3517 // then add any existing moved items that we have
3518 while (FxListItem1 *item = moved.take(endIndex+1)) {
3519 d->visibleItems.append(item);
3523 // update visibleIndex
3524 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3525 if ((*it)->index != -1) {
3526 d->visibleIndex = (*it)->index;
3531 // Fix current index
3532 if (d->currentIndex >= 0 && d->currentItem) {
3533 int oldCurrent = d->currentIndex;
3534 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3535 if (oldCurrent != d->currentIndex) {
3536 d->currentItem->index = d->currentIndex;
3537 emit currentIndexChanged();
3541 // Whatever moved items remain are no longer visible items.
3542 while (moved.count()) {
3543 int idx = moved.begin().key();
3544 FxListItem1 *item = moved.take(idx);
3545 if (d->currentItem && item->item == d->currentItem->item)
3546 item->setPosition(d->positionAt(idx));
3547 d->releaseItem(item);
3550 // Ensure we don't cause an ugly list scroll.
3551 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3553 d->updateSections();
3557 void QDeclarative1ListView::itemsChanged(int, int)
3559 Q_D(QDeclarative1ListView);
3560 d->updateSections();
3564 void QDeclarative1ListView::modelReset()
3566 Q_D(QDeclarative1ListView);
3567 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
3569 if (d->highlight && d->currentItem) {
3570 if (d->autoHighlight)
3571 d->highlight->setPosition(d->currentItem->position());
3572 d->updateTrackedItem();
3574 d->moveReason = QDeclarative1ListViewPrivate::Other;
3575 emit countChanged();
3578 void QDeclarative1ListView::createdItem(int index, QDeclarativeItem *item)
3580 Q_D(QDeclarative1ListView);
3581 if (d->requestedIndex != index) {
3582 item->setParentItem(contentItem());
3583 d->unrequestedItems.insert(item, index);
3584 if (d->orient == QDeclarative1ListView::Vertical) {
3585 item->setY(d->positionAt(index));
3587 if (d->isRightToLeft())
3588 item->setX(-d->positionAt(index)-item->width());
3590 item->setX(d->positionAt(index));
3595 void QDeclarative1ListView::destroyingItem(QDeclarativeItem *item)
3597 Q_D(QDeclarative1ListView);
3598 d->unrequestedItems.remove(item);
3601 void QDeclarative1ListView::animStopped()
3603 Q_D(QDeclarative1ListView);
3604 d->bufferMode = QDeclarative1ListViewPrivate::NoBuffer;
3605 if (d->haveHighlightRange && d->highlightRange == QDeclarative1ListView::StrictlyEnforceRange)
3606 d->updateHighlight();
3609 QDeclarative1ListViewAttached *QDeclarative1ListView::qmlAttachedProperties(QObject *obj)
3611 return new QDeclarative1ListViewAttached(obj);