1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** 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>
55 #include "qplatformdefs.h"
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
63 void QDeclarative1ViewSection::setProperty(const QString &property)
65 if (property != m_property) {
66 m_property = property;
67 emit propertyChanged();
71 void QDeclarative1ViewSection::setCriteria(QDeclarative1ViewSection::SectionCriteria criteria)
73 if (criteria != m_criteria) {
74 m_criteria = criteria;
75 emit criteriaChanged();
79 void QDeclarative1ViewSection::setDelegate(QDeclarativeComponent *delegate)
81 if (delegate != m_delegate) {
82 m_delegate = delegate;
83 emit delegateChanged();
87 QString QDeclarative1ViewSection::sectionString(const QString &value)
89 if (m_criteria == FirstCharacter)
90 return value.isEmpty() ? QString() : value.at(0);
95 //----------------------------------------------------------------------------
100 FxListItem1(QDeclarativeItem *i, QDeclarative1ListView *v) : item(i), section(0), view(v) {
101 attached = static_cast<QDeclarative1ListViewAttached*>(qmlAttachedPropertiesObject<QDeclarative1ListView>(item));
103 attached->setView(view);
106 qreal position() const {
108 if (view->orientation() == QDeclarative1ListView::Vertical)
111 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
113 return itemPosition();
117 qreal itemPosition() const {
118 if (view->orientation() == QDeclarative1ListView::Vertical)
121 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
125 return (view->orientation() == QDeclarative1ListView::Vertical ? item->height()+section->height() : item->width()+section->width());
127 return (view->orientation() == QDeclarative1ListView::Vertical ? item->height() : item->width());
129 qreal itemSize() const {
130 return (view->orientation() == QDeclarative1ListView::Vertical ? item->height() : item->width());
132 qreal sectionSize() const {
134 return (view->orientation() == QDeclarative1ListView::Vertical ? section->height() : section->width());
137 qreal endPosition() const {
138 if (view->orientation() == QDeclarative1ListView::Vertical) {
139 return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
141 return (view->effectiveLayoutDirection() == Qt::RightToLeft
142 ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
143 : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
146 void setPosition(qreal pos) {
147 if (view->orientation() == QDeclarative1ListView::Vertical) {
150 pos += section->height();
154 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
156 section->setX(-section->width()-pos);
157 pos += section->width();
159 item->setX(-item->width()-pos);
163 pos += section->width();
169 void setSize(qreal size) {
170 if (view->orientation() == QDeclarative1ListView::Vertical)
171 item->setHeight(size);
173 item->setWidth(size);
175 bool contains(qreal x, qreal y) const {
176 return (x >= item->x() && x < item->x() + item->width() &&
177 y >= item->y() && y < item->y() + item->height());
180 QDeclarativeItem *item;
181 QDeclarativeItem *section;
182 QDeclarative1ListView *view;
183 QDeclarative1ListViewAttached *attached;
187 //----------------------------------------------------------------------------
189 class QDeclarative1ListViewPrivate : public QDeclarative1FlickablePrivate
191 Q_DECLARE_PUBLIC(QDeclarative1ListView)
194 QDeclarative1ListViewPrivate()
195 : currentItem(0), orient(QDeclarative1ListView::Vertical), layoutDirection(Qt::LeftToRight)
196 , visiblePos(0), visibleIndex(0)
197 , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
198 , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
199 , highlightRangeStartValid(false), highlightRangeEndValid(false)
200 , highlightComponent(0), highlight(0), trackedItem(0)
201 , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
202 , sectionCriteria(0), spacing(0.0)
203 , highlightMoveSpeed(400), highlightMoveDuration(-1)
204 , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarative1ListView::NoHighlightRange)
205 , snapMode(QDeclarative1ListView::NoSnap), overshootDist(0.0)
206 , footerComponent(0), footer(0), headerComponent(0), header(0)
207 , bufferMode(BufferBefore | BufferAfter)
208 , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
209 , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
210 , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
211 , inViewportMoved(false)
212 , minExtentDirty(true), maxExtentDirty(true)
217 FxListItem1 *createItem(int modelIndex);
218 void releaseItem(FxListItem1 *item);
220 FxListItem1 *visibleItem(int modelIndex) const {
221 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
222 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
223 FxListItem1 *item = visibleItems.at(i);
224 if (item->index == modelIndex)
231 FxListItem1 *firstVisibleItem() const {
232 const qreal pos = isRightToLeft() ? -position()-size() : position();
233 for (int i = 0; i < visibleItems.count(); ++i) {
234 FxListItem1 *item = visibleItems.at(i);
235 if (item->index != -1 && item->endPosition() > pos)
238 return visibleItems.count() ? visibleItems.first() : 0;
241 // Returns the item before modelIndex, if created.
242 // May return an item marked for removal.
243 FxListItem1 *itemBefore(int modelIndex) const {
244 if (modelIndex < visibleIndex)
248 while (idx < visibleItems.count()) {
249 FxListItem1 *item = visibleItems.at(idx);
250 if (item->index != -1)
251 lastIndex = item->index;
252 if (item->index == modelIndex)
253 return visibleItems.at(idx-1);
256 if (lastIndex == modelIndex-1)
257 return visibleItems.last();
262 Q_Q(QDeclarative1ListView);
263 if (q->isComponentComplete()) {
266 q->scene()->removeItem(header->item);
267 header->item->deleteLater();
273 q->scene()->removeItem(footer->item);
274 footer->item->deleteLater();
283 updateCurrent(currentIndex);
287 void mirrorChange() {
288 Q_Q(QDeclarative1ListView);
290 emit q->effectiveLayoutDirectionChanged();
293 bool isRightToLeft() const {
294 Q_Q(const QDeclarative1ListView);
295 return orient == QDeclarative1ListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
298 qreal position() const {
299 Q_Q(const QDeclarative1ListView);
300 return orient == QDeclarative1ListView::Vertical ? q->contentY() : q->contentX();
303 void setPosition(qreal pos) {
304 Q_Q(QDeclarative1ListView);
305 if (orient == QDeclarative1ListView::Vertical) {
306 q->QDeclarative1Flickable::setContentY(pos);
309 q->QDeclarative1Flickable::setContentX(-pos-size());
311 q->QDeclarative1Flickable::setContentX(pos);
315 Q_Q(const QDeclarative1ListView);
316 return orient == QDeclarative1ListView::Vertical ? q->height() : q->width();
319 qreal originPosition() const {
321 if (!visibleItems.isEmpty()) {
322 pos = (*visibleItems.constBegin())->position();
323 if (visibleIndex > 0)
324 pos -= visibleIndex * (averageSize + spacing);
329 qreal lastPosition() const {
331 if (!visibleItems.isEmpty()) {
332 int invisibleCount = visibleItems.count() - visibleIndex;
333 for (int i = visibleItems.count()-1; i >= 0; --i) {
334 if (visibleItems.at(i)->index != -1) {
335 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
339 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
340 } else if (model && model->count()) {
341 pos = model->count() * averageSize + (model->count()-1) * spacing;
346 qreal startPosition() const {
347 return isRightToLeft() ? -lastPosition()-1 : originPosition();
350 qreal endPosition() const {
351 return isRightToLeft() ? -originPosition()-1 : lastPosition();
354 qreal positionAt(int modelIndex) const {
355 if (FxListItem1 *item = visibleItem(modelIndex))
356 return item->position();
357 if (!visibleItems.isEmpty()) {
358 if (modelIndex < visibleIndex) {
359 int count = visibleIndex - modelIndex;
361 if (modelIndex == currentIndex && currentItem) {
362 cs = currentItem->size() + spacing;
365 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
367 int idx = visibleItems.count() - 1;
368 while (idx >= 0 && visibleItems.at(idx)->index == -1)
373 idx = visibleItems.at(idx)->index;
374 int count = modelIndex - idx - 1;
376 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
382 qreal endPositionAt(int modelIndex) const {
383 if (FxListItem1 *item = visibleItem(modelIndex))
384 return item->endPosition();
385 if (!visibleItems.isEmpty()) {
386 if (modelIndex < visibleIndex) {
387 int count = visibleIndex - modelIndex;
388 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
390 int idx = visibleItems.count() - 1;
391 while (idx >= 0 && visibleItems.at(idx)->index == -1)
396 idx = visibleItems.at(idx)->index;
397 int count = modelIndex - idx - 1;
398 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
404 QString sectionAt(int modelIndex) {
405 if (FxListItem1 *item = visibleItem(modelIndex))
406 return item->attached->section();
409 if (sectionCriteria) {
410 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
411 section = sectionCriteria->sectionString(propValue);
417 bool isValid() const {
418 return model && model->count() && model->isValid();
421 qreal snapPosAt(qreal pos) {
422 if (FxListItem1 *snapItem = snapItemAt(pos))
423 return snapItem->position();
424 if (visibleItems.count()) {
425 qreal firstPos = visibleItems.first()->position();
426 qreal endPos = visibleItems.last()->position();
427 if (pos < firstPos) {
428 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
429 } else if (pos > endPos)
430 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
432 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
435 FxListItem1 *snapItemAt(qreal pos) {
436 FxListItem1 *snapItem = 0;
437 qreal prevItemSize = 0;
438 for (int i = 0; i < visibleItems.count(); ++i) {
439 FxListItem1 *item = visibleItems[i];
440 if (item->index == -1)
442 qreal itemTop = item->position();
443 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
445 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
447 prevItemSize = item->size();
452 int lastVisibleIndex() const {
454 for (int i = visibleItems.count()-1; i >= 0; --i) {
455 FxListItem1 *listItem = visibleItems.at(i);
456 if (listItem->index != -1) {
457 lastIndex = listItem->index;
464 // map a model index to visibleItems index.
465 int mapFromModel(int modelIndex) const {
466 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
468 for (int i = 0; i < visibleItems.count(); ++i) {
469 FxListItem1 *listItem = visibleItems.at(i);
470 if (listItem->index == modelIndex)
472 if (listItem->index > modelIndex)
475 return -1; // Not in visibleList
478 void updateViewport() {
479 Q_Q(QDeclarative1ListView);
480 if (orient == QDeclarative1ListView::Vertical) {
481 q->setContentHeight(endPosition() - startPosition() + 1);
483 q->setContentWidth(endPosition() - startPosition() + 1);
487 void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
488 Q_Q(QDeclarative1ListView);
489 QDeclarative1FlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
490 if (!q->isComponentComplete())
492 if (item != contentItem && (!highlight || item != highlight->item)) {
493 if ((orient == QDeclarative1ListView::Vertical && newGeometry.height() != oldGeometry.height())
494 || (orient == QDeclarative1ListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
498 if ((header && header->item == item) || (footer && footer->item == item)) {
504 if (currentItem && currentItem->item == item)
506 if (trackedItem && trackedItem->item == item)
507 q->trackedPositionChanged();
510 // for debugging only
511 void checkVisible() const {
513 for (int i = 0; i < visibleItems.count(); ++i) {
514 FxListItem1 *listItem = visibleItems.at(i);
515 if (listItem->index == -1) {
517 } else if (listItem->index != visibleIndex + i - skip) {
518 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
523 void refill(qreal from, qreal to, bool doBuffer = false);
524 void scheduleLayout();
526 void updateUnrequestedIndexes();
527 void updateUnrequestedPositions();
528 void updateTrackedItem();
529 void createHighlight();
530 void updateHighlight();
531 void createSection(FxListItem1 *);
532 void updateSections();
533 void updateCurrentSection();
534 void updateCurrent(int);
535 void updateAverage();
538 void fixupPosition();
539 void positionViewAtIndex(int index, int mode);
540 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
541 virtual void flick(QDeclarative1FlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
542 QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity);
544 QDeclarativeGuard<QDeclarative1VisualModel> model;
545 QVariant modelVariant;
546 QList<FxListItem1*> visibleItems;
547 QHash<QDeclarativeItem*,int> unrequestedItems;
548 FxListItem1 *currentItem;
549 QDeclarative1ListView::Orientation orient;
550 Qt::LayoutDirection layoutDirection;
557 qreal highlightRangeStart;
558 qreal highlightRangeEnd;
559 bool highlightRangeStartValid;
560 bool highlightRangeEndValid;
561 QDeclarativeComponent *highlightComponent;
562 FxListItem1 *highlight;
563 FxListItem1 *trackedItem;
564 enum MovementReason { Other, SetIndex, Mouse };
565 MovementReason moveReason;
567 QSmoothedAnimation_1 *highlightPosAnimator;
568 QSmoothedAnimation_1 *highlightSizeAnimator;
569 QDeclarative1ViewSection *sectionCriteria;
570 QString currentSection;
571 static const int sectionCacheSize = 4;
572 QDeclarativeItem *sectionCache[sectionCacheSize];
574 qreal highlightMoveSpeed;
575 int highlightMoveDuration;
576 qreal highlightResizeSpeed;
577 int highlightResizeDuration;
578 QDeclarative1ListView::HighlightRangeMode highlightRange;
579 QDeclarative1ListView::SnapMode snapMode;
581 QDeclarativeComponent *footerComponent;
583 QDeclarativeComponent *headerComponent;
585 enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
587 mutable qreal minExtent;
588 mutable qreal maxExtent;
592 bool autoHighlight : 1;
593 bool haveHighlightRange : 1;
594 bool correctFlick : 1;
595 bool inFlickCorrection : 1;
596 bool lazyRelease : 1;
597 bool deferredRelease : 1;
598 bool layoutScheduled : 1;
599 bool currentIndexCleared : 1;
600 bool inViewportMoved : 1;
601 mutable bool minExtentDirty : 1;
602 mutable bool maxExtentDirty : 1;
605 void QDeclarative1ListViewPrivate::init()
607 Q_Q(QDeclarative1ListView);
608 q->setFlag(QGraphicsItem::ItemIsFocusScope);
609 addItemChangeListener(this, Geometry);
610 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
611 q->setFlickableDirection(QDeclarative1Flickable::VerticalFlick);
612 ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize);
615 void QDeclarative1ListViewPrivate::clear()
618 for (int i = 0; i < visibleItems.count(); ++i)
619 releaseItem(visibleItems.at(i));
620 visibleItems.clear();
621 for (int i = 0; i < sectionCacheSize; ++i) {
622 delete sectionCache[i];
625 visiblePos = header ? header->size() : 0;
627 releaseItem(currentItem);
631 minExtentDirty = true;
632 maxExtentDirty = true;
636 FxListItem1 *QDeclarative1ListViewPrivate::createItem(int modelIndex)
638 Q_Q(QDeclarative1ListView);
640 requestedIndex = modelIndex;
641 FxListItem1 *listItem = 0;
642 if (QDeclarativeItem *item = model->item(modelIndex, false)) {
643 listItem = new FxListItem1(item, q);
644 listItem->index = modelIndex;
645 // initialise attached properties
646 if (sectionCriteria) {
647 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
648 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
649 if (modelIndex > 0) {
650 if (FxListItem1 *item = itemBefore(modelIndex))
651 listItem->attached->m_prevSection = item->attached->section();
653 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
655 if (modelIndex < model->count()-1) {
656 if (FxListItem1 *item = visibleItem(modelIndex+1))
657 listItem->attached->m_nextSection = item->attached->section();
659 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
662 if (model->completePending()) {
664 listItem->item->setZValue(1);
665 listItem->item->setParentItem(q->contentItem());
666 model->completeItem();
668 listItem->item->setParentItem(q->contentItem());
670 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
671 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
672 if (sectionCriteria && sectionCriteria->delegate()) {
673 if (listItem->attached->m_prevSection != listItem->attached->m_section)
674 createSection(listItem);
676 unrequestedItems.remove(listItem->item);
683 void QDeclarative1ListViewPrivate::releaseItem(FxListItem1 *item)
685 Q_Q(QDeclarative1ListView);
688 if (trackedItem == item)
690 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item->item));
691 itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
692 if (model->release(item->item) == 0) {
693 // item was not destroyed, and we no longer reference it.
694 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
699 if (!sectionCache[i]) {
700 sectionCache[i] = item->section;
701 sectionCache[i]->setVisible(false);
706 } while (i < sectionCacheSize);
707 delete item->section;
712 void QDeclarative1ListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
714 Q_Q(QDeclarative1ListView);
715 if (!isValid() || !q->isComponentComplete())
717 itemCount = model->count();
718 qreal bufferFrom = from - buffer;
719 qreal bufferTo = to + buffer;
720 qreal fillFrom = from;
722 if (doBuffer && (bufferMode & BufferAfter))
724 if (doBuffer && (bufferMode & BufferBefore))
725 fillFrom = bufferFrom;
727 bool haveValidItems = false;
728 int modelIndex = visibleIndex;
729 qreal itemEnd = visiblePos-1;
730 if (!visibleItems.isEmpty()) {
731 visiblePos = (*visibleItems.constBegin())->position();
732 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
733 int i = visibleItems.count() - 1;
734 while (i > 0 && visibleItems.at(i)->index == -1)
736 if (visibleItems.at(i)->index != -1) {
737 haveValidItems = true;
738 modelIndex = visibleItems.at(i)->index + 1;
742 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
743 || fillTo < visiblePos - averageSize - spacing)) {
744 // We've jumped more than a page. Estimate which items are now
745 // visible and fill from there.
746 int count = (fillFrom - itemEnd) / (averageSize + spacing);
747 for (int i = 0; i < visibleItems.count(); ++i)
748 releaseItem(visibleItems.at(i));
749 visibleItems.clear();
751 if (modelIndex >= model->count()) {
752 count -= modelIndex - model->count() + 1;
753 modelIndex = model->count() - 1;
754 } else if (modelIndex < 0) {
758 visibleIndex = modelIndex;
759 visiblePos = itemEnd + count * (averageSize + spacing) + 1;
760 itemEnd = visiblePos-1;
763 bool changed = false;
764 FxListItem1 *item = 0;
765 qreal pos = itemEnd + 1;
766 while (modelIndex < model->count() && pos <= fillTo) {
767 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
768 if (!(item = createItem(modelIndex)))
770 item->setPosition(pos);
771 pos += item->size() + spacing;
772 visibleItems.append(item);
775 if (doBuffer) // never buffer more than one item per frame
778 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
779 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
780 if (!(item = createItem(visibleIndex-1)))
783 visiblePos -= item->size() + spacing;
784 item->setPosition(visiblePos);
785 visibleItems.prepend(item);
787 if (doBuffer) // never buffer more than one item per frame
791 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
792 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
793 if (item->attached->delayRemove())
795 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
796 if (item->index != -1)
798 visibleItems.removeFirst();
802 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
803 if (item->attached->delayRemove())
805 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
806 visibleItems.removeLast();
810 deferredRelease = false;
812 deferredRelease = true;
815 minExtentDirty = true;
816 maxExtentDirty = true;
817 if (visibleItems.count())
818 visiblePos = (*visibleItems.constBegin())->position();
820 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
821 currentItem->setPosition(positionAt(currentIndex));
826 updateCurrentSection();
832 updateUnrequestedPositions();
833 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
834 refill(from, to, true);
839 void QDeclarative1ListViewPrivate::scheduleLayout()
841 Q_Q(QDeclarative1ListView);
842 if (!layoutScheduled) {
843 layoutScheduled = true;
844 QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority);
848 void QDeclarative1ListViewPrivate::layout()
850 Q_Q(QDeclarative1ListView);
851 layoutScheduled = false;
852 if (!isValid() && !visibleItems.count()) {
857 if (!visibleItems.isEmpty()) {
858 bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
859 qreal sum = visibleItems.first()->size();
860 qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
861 for (int i=1; i < visibleItems.count(); ++i) {
862 FxListItem1 *item = visibleItems.at(i);
863 item->setPosition(pos);
864 pos += item->size() + spacing;
866 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
868 averageSize = qRound(sum / visibleItems.count());
869 // move current item if it is not a visible item.
870 if (currentIndex >= 0 && currentItem && !fixedCurrent)
871 currentItem->setPosition(positionAt(currentIndex));
874 minExtentDirty = true;
875 maxExtentDirty = true;
877 if (!q->isMoving() && !q->isFlicking()) {
888 void QDeclarative1ListViewPrivate::updateUnrequestedIndexes()
890 Q_Q(QDeclarative1ListView);
891 QHash<QDeclarativeItem*,int>::iterator it;
892 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
893 *it = model->indexOf(it.key(), q);
896 void QDeclarative1ListViewPrivate::updateUnrequestedPositions()
898 Q_Q(QDeclarative1ListView);
899 if (unrequestedItems.count()) {
900 qreal pos = position();
901 QHash<QDeclarativeItem*,int>::const_iterator it;
902 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
903 QDeclarativeItem *item = it.key();
904 if (orient == QDeclarative1ListView::Vertical) {
905 if (item->y() + item->height() > pos && item->y() < pos + q->height())
906 item->setY(positionAt(*it));
908 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
910 item->setX(-positionAt(*it)-item->width());
912 item->setX(positionAt(*it));
919 void QDeclarative1ListViewPrivate::updateTrackedItem()
921 Q_Q(QDeclarative1ListView);
922 FxListItem1 *item = currentItem;
927 q->trackedPositionChanged();
930 void QDeclarative1ListViewPrivate::createHighlight()
932 Q_Q(QDeclarative1ListView);
933 bool changed = false;
935 if (trackedItem == highlight)
937 if (highlight->item->scene())
938 highlight->item->scene()->removeItem(highlight->item);
939 highlight->item->deleteLater();
942 delete highlightPosAnimator;
943 delete highlightSizeAnimator;
944 highlightPosAnimator = 0;
945 highlightSizeAnimator = 0;
950 QDeclarativeItem *item = 0;
951 if (highlightComponent) {
952 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
953 QObject *nobj = highlightComponent->create(highlightContext);
955 QDeclarative_setParent_noEvent(highlightContext, nobj);
956 item = qobject_cast<QDeclarativeItem *>(nobj);
960 delete highlightContext;
963 item = new QDeclarativeItem;
966 QDeclarative_setParent_noEvent(item, q->contentItem());
967 item->setParentItem(q->contentItem());
968 highlight = new FxListItem1(item, q);
969 if (currentItem && autoHighlight) {
970 if (orient == QDeclarative1ListView::Vertical) {
971 highlight->item->setHeight(currentItem->item->height());
973 highlight->item->setWidth(currentItem->item->width());
975 highlight->setPosition(currentItem->itemPosition());
977 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
978 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
979 const QLatin1String posProp(orient == QDeclarative1ListView::Vertical ? "y" : "x");
980 highlightPosAnimator = new QSmoothedAnimation_1(q);
981 highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
982 highlightPosAnimator->velocity = highlightMoveSpeed;
983 highlightPosAnimator->userDuration = highlightMoveDuration;
984 const QLatin1String sizeProp(orient == QDeclarative1ListView::Vertical ? "height" : "width");
985 highlightSizeAnimator = new QSmoothedAnimation_1(q);
986 highlightSizeAnimator->velocity = highlightResizeSpeed;
987 highlightSizeAnimator->userDuration = highlightResizeDuration;
988 highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
990 highlightPosAnimator->restart();
991 highlightSizeAnimator->restart();
997 emit q->highlightItemChanged();
1000 void QDeclarative1ListViewPrivate::updateHighlight()
1002 if ((!currentItem && highlight) || (currentItem && !highlight))
1004 if (currentItem && autoHighlight && highlight && !hData.moving && !vData.moving) {
1005 // auto-update highlight
1006 highlightPosAnimator->to = isRightToLeft()
1007 ? -currentItem->itemPosition()-currentItem->itemSize()
1008 : currentItem->itemPosition();
1009 highlightSizeAnimator->to = currentItem->itemSize();
1010 if (orient == QDeclarative1ListView::Vertical) {
1011 if (highlight->item->width() == 0)
1012 highlight->item->setWidth(currentItem->item->width());
1014 if (highlight->item->height() == 0)
1015 highlight->item->setHeight(currentItem->item->height());
1017 highlightPosAnimator->restart();
1018 highlightSizeAnimator->restart();
1020 updateTrackedItem();
1023 void QDeclarative1ListViewPrivate::createSection(FxListItem1 *listItem)
1025 Q_Q(QDeclarative1ListView);
1026 if (!sectionCriteria || !sectionCriteria->delegate())
1028 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
1029 if (!listItem->section) {
1030 qreal pos = listItem->position();
1031 int i = sectionCacheSize-1;
1032 while (i >= 0 && !sectionCache[i])
1035 listItem->section = sectionCache[i];
1036 sectionCache[i] = 0;
1037 listItem->section->setVisible(true);
1038 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1039 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1041 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1042 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1043 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
1045 QDeclarative_setParent_noEvent(context, nobj);
1046 listItem->section = qobject_cast<QDeclarativeItem *>(nobj);
1047 if (!listItem->section) {
1050 listItem->section->setZValue(1);
1051 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
1052 listItem->section->setParentItem(q->contentItem());
1057 sectionCriteria->delegate()->completeCreate();
1059 listItem->setPosition(pos);
1061 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1062 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1064 } else if (listItem->section) {
1065 qreal pos = listItem->position();
1068 if (!sectionCache[i]) {
1069 sectionCache[i] = listItem->section;
1070 sectionCache[i]->setVisible(false);
1071 listItem->section = 0;
1075 } while (i < sectionCacheSize);
1076 delete listItem->section;
1077 listItem->section = 0;
1078 listItem->setPosition(pos);
1082 void QDeclarative1ListViewPrivate::updateSections()
1084 if (sectionCriteria && !visibleItems.isEmpty()) {
1085 QString prevSection;
1086 if (visibleIndex > 0)
1087 prevSection = sectionAt(visibleIndex-1);
1088 QDeclarative1ListViewAttached *prevAtt = 0;
1090 for (int i = 0; i < visibleItems.count(); ++i) {
1091 QDeclarative1ListViewAttached *attached = visibleItems.at(i)->attached;
1092 attached->setPrevSection(prevSection);
1093 if (visibleItems.at(i)->index != -1) {
1094 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1095 attached->setSection(sectionCriteria->sectionString(propValue));
1096 idx = visibleItems.at(i)->index;
1098 createSection(visibleItems.at(i));
1100 prevAtt->setNextSection(attached->section());
1101 prevSection = attached->section();
1105 if (idx > 0 && idx < model->count()-1)
1106 prevAtt->setNextSection(sectionAt(idx+1));
1108 prevAtt->setNextSection(QString());
1113 void QDeclarative1ListViewPrivate::updateCurrentSection()
1115 Q_Q(QDeclarative1ListView);
1116 if (!sectionCriteria || visibleItems.isEmpty()) {
1117 if (!currentSection.isEmpty()) {
1118 currentSection.clear();
1119 emit q->currentSectionChanged();
1124 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1127 QString newSection = currentSection;
1128 if (index < visibleItems.count())
1129 newSection = visibleItems.at(index)->attached->section();
1131 newSection = visibleItems.first()->attached->section();
1132 if (newSection != currentSection) {
1133 currentSection = newSection;
1134 emit q->currentSectionChanged();
1138 void QDeclarative1ListViewPrivate::updateCurrent(int modelIndex)
1140 Q_Q(QDeclarative1ListView);
1141 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1143 currentItem->attached->setIsCurrentItem(false);
1144 releaseItem(currentItem);
1146 currentIndex = modelIndex;
1147 emit q->currentIndexChanged();
1149 } else if (currentIndex != modelIndex) {
1150 currentIndex = modelIndex;
1151 emit q->currentIndexChanged();
1156 if (currentItem && currentIndex == modelIndex) {
1160 FxListItem1 *oldCurrentItem = currentItem;
1161 currentIndex = modelIndex;
1162 currentItem = createItem(modelIndex);
1163 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1164 oldCurrentItem->attached->setIsCurrentItem(false);
1166 if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1167 // We can calculate exact postion in this case
1168 currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1170 // Create current item now and position as best we can.
1171 // Its position will be corrected when it becomes visible.
1172 currentItem->setPosition(positionAt(modelIndex));
1174 currentItem->item->setFocus(true);
1175 currentItem->attached->setIsCurrentItem(true);
1176 // Avoid showing section delegate twice. We still need the section heading so that
1177 // currentItem positioning works correctly.
1178 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1179 if (currentItem->section)
1180 currentItem->section->setVisible(false);
1181 if (visibleItems.isEmpty())
1182 averageSize = currentItem->size();
1185 emit q->currentIndexChanged();
1186 // Release the old current item
1187 releaseItem(oldCurrentItem);
1190 void QDeclarative1ListViewPrivate::updateAverage()
1192 if (!visibleItems.count())
1195 for (int i = 0; i < visibleItems.count(); ++i)
1196 sum += visibleItems.at(i)->size();
1197 averageSize = qRound(sum / visibleItems.count());
1200 void QDeclarative1ListViewPrivate::updateFooter()
1202 Q_Q(QDeclarative1ListView);
1203 if (!footer && footerComponent) {
1204 QDeclarativeItem *item = 0;
1205 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1206 QObject *nobj = footerComponent->create(context);
1208 QDeclarative_setParent_noEvent(context, nobj);
1209 item = qobject_cast<QDeclarativeItem *>(nobj);
1216 QDeclarative_setParent_noEvent(item, q->contentItem());
1217 item->setParentItem(q->contentItem());
1219 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1220 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1221 footer = new FxListItem1(item, q);
1225 if (visibleItems.count()) {
1226 qreal endPos = lastPosition() + 1;
1227 if (lastVisibleIndex() == model->count()-1) {
1228 footer->setPosition(endPos);
1230 qreal visiblePos = position() + q->height();
1231 if (endPos <= visiblePos || footer->position() < endPos)
1232 footer->setPosition(endPos);
1235 footer->setPosition(visiblePos);
1240 void QDeclarative1ListViewPrivate::updateHeader()
1242 Q_Q(QDeclarative1ListView);
1243 if (!header && headerComponent) {
1244 QDeclarativeItem *item = 0;
1245 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1246 QObject *nobj = headerComponent->create(context);
1248 QDeclarative_setParent_noEvent(context, nobj);
1249 item = qobject_cast<QDeclarativeItem *>(nobj);
1256 QDeclarative_setParent_noEvent(item, q->contentItem());
1257 item->setParentItem(q->contentItem());
1259 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1260 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1261 header = new FxListItem1(item, q);
1265 if (visibleItems.count()) {
1266 qreal startPos = originPosition();
1267 if (visibleIndex == 0) {
1268 header->setPosition(startPos - header->size());
1270 if (position() <= startPos || header->position() > startPos - header->size())
1271 header->setPosition(startPos - header->size());
1274 visiblePos = header->size();
1275 header->setPosition(0);
1280 void QDeclarative1ListViewPrivate::fixupPosition()
1282 if ((haveHighlightRange && highlightRange == QDeclarative1ListView::StrictlyEnforceRange)
1283 || snapMode != QDeclarative1ListView::NoSnap)
1285 if (orient == QDeclarative1ListView::Vertical)
1291 void QDeclarative1ListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1293 if ((orient == QDeclarative1ListView::Horizontal && &data == &vData)
1294 || (orient == QDeclarative1ListView::Vertical && &data == &hData))
1297 correctFlick = false;
1298 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1299 bool strictHighlightRange = haveHighlightRange && highlightRange == QDeclarative1ListView::StrictlyEnforceRange;
1301 qreal highlightStart;
1304 if (isRightToLeft()) {
1305 // Handle Right-To-Left exceptions
1306 viewPos = -position()-size();
1307 highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1308 highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1310 viewPos = position();
1311 highlightStart = highlightRangeStart;
1312 highlightEnd = highlightRangeEnd;
1315 if (snapMode != QDeclarative1ListView::NoSnap && moveReason != QDeclarative1ListViewPrivate::SetIndex) {
1316 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1317 if (snapMode == QDeclarative1ListView::SnapOneItem && moveReason == Mouse) {
1318 // if we've been dragged < averageSize/2 then bias towards the next item
1319 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1321 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1322 bias = averageSize/2;
1323 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1324 bias = -averageSize/2;
1325 if (isRightToLeft())
1327 tempPosition -= bias;
1329 FxListItem1 *topItem = snapItemAt(tempPosition+highlightStart);
1330 if (!topItem && strictHighlightRange && currentItem) {
1331 // StrictlyEnforceRange always keeps an item in range
1333 topItem = currentItem;
1335 FxListItem1 *bottomItem = snapItemAt(tempPosition+highlightEnd);
1336 if (!bottomItem && strictHighlightRange && currentItem) {
1337 // StrictlyEnforceRange always keeps an item in range
1339 bottomItem = currentItem;
1342 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1343 if (topItem && (isInBounds || strictHighlightRange)) {
1344 if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2 && !strictHighlightRange) {
1345 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1347 if (isRightToLeft())
1348 pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1350 pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1352 } else if (bottomItem && isInBounds) {
1353 if (isRightToLeft())
1354 pos = qMax(qMin(-bottomItem->position() + highlightEnd - size(), -maxExtent), -minExtent);
1356 pos = qMax(qMin(bottomItem->position() - highlightEnd, -maxExtent), -minExtent);
1358 QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent);
1362 qreal dist = qAbs(data.move + pos);
1364 timeline.reset(data.move);
1365 if (fixupMode != Immediate) {
1366 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1367 data.fixingUp = true;
1369 timeline.set(data.move, -pos);
1371 vTime = timeline.time();
1373 } else if (currentItem && strictHighlightRange
1374 && moveReason != QDeclarative1ListViewPrivate::SetIndex) {
1376 qreal pos = currentItem->itemPosition();
1377 if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1378 viewPos = pos + currentItem->itemSize() - highlightEnd;
1379 if (viewPos > pos - highlightStart)
1380 viewPos = pos - highlightStart;
1381 if (isRightToLeft())
1382 viewPos = -viewPos-size();
1384 timeline.reset(data.move);
1385 if (viewPos != position()) {
1386 if (fixupMode != Immediate) {
1387 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1388 data.fixingUp = true;
1390 timeline.set(data.move, -viewPos);
1393 vTime = timeline.time();
1395 QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent);
1397 data.inOvershoot = false;
1401 void QDeclarative1ListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1402 QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity)
1404 Q_Q(QDeclarative1ListView);
1406 data.fixingUp = false;
1408 if ((!haveHighlightRange || highlightRange != QDeclarative1ListView::StrictlyEnforceRange) && snapMode == QDeclarative1ListView::NoSnap) {
1409 correctFlick = true;
1410 QDeclarative1FlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1413 qreal maxDistance = 0;
1414 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1415 qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1416 // -ve velocity means list is moving up/left
1418 if (data.move.value() < minExtent) {
1419 if (snapMode == QDeclarative1ListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1420 // if we've been dragged < averageSize/2 then bias towards the next item
1421 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1422 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1423 if (isRightToLeft())
1425 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) - bias) + highlightStart;
1426 maxDistance = qAbs(data.flickTarget - data.move.value());
1427 velocity = maxVelocity;
1429 maxDistance = qAbs(minExtent - data.move.value());
1432 if (snapMode == QDeclarative1ListView::NoSnap && highlightRange != QDeclarative1ListView::StrictlyEnforceRange)
1433 data.flickTarget = minExtent;
1435 if (data.move.value() > maxExtent) {
1436 if (snapMode == QDeclarative1ListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1437 // if we've been dragged < averageSize/2 then bias towards the next item
1438 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1439 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1440 if (isRightToLeft())
1442 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + bias) + highlightStart;
1443 maxDistance = qAbs(data.flickTarget - data.move.value());
1444 velocity = -maxVelocity;
1446 maxDistance = qAbs(maxExtent - data.move.value());
1449 if (snapMode == QDeclarative1ListView::NoSnap && highlightRange != QDeclarative1ListView::StrictlyEnforceRange)
1450 data.flickTarget = maxExtent;
1453 bool overShoot = boundsBehavior == QDeclarative1Flickable::DragAndOvershootBounds;
1455 if (maxDistance > 0 || overShoot) {
1456 // These modes require the list to stop exactly on an item boundary.
1457 // The initial flick will estimate the boundary to stop on.
1458 // Since list items can have variable sizes, the boundary will be
1459 // reevaluated and adjusted as we approach the boundary.
1461 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1467 if (!hData.flicking && !vData.flicking) {
1468 // the initial flick - estimate boundary
1469 qreal accel = deceleration;
1471 overshootDist = 0.0;
1472 // + averageSize/4 to encourage moving at least one item in the flick direction
1473 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1474 if (maxDistance > 0)
1475 dist = qMin(dist, maxDistance);
1478 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarative1ListView::SnapOneItem) {
1479 if (snapMode != QDeclarative1ListView::SnapOneItem) {
1480 qreal distTemp = isRightToLeft() ? -dist : dist;
1481 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1483 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1485 if (data.flickTarget >= minExtent) {
1486 overshootDist = overShootDistance(vSize);
1487 data.flickTarget += overshootDist;
1488 } else if (data.flickTarget <= maxExtent) {
1489 overshootDist = overShootDistance(vSize);
1490 data.flickTarget -= overshootDist;
1493 qreal adjDist = -data.flickTarget + data.move.value();
1494 if (qAbs(adjDist) > qAbs(dist)) {
1495 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1496 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1505 accel = v2 / (2.0f * qAbs(dist));
1506 } else if (overShoot) {
1507 data.flickTarget = data.move.value() - dist;
1508 if (data.flickTarget >= minExtent) {
1509 overshootDist = overShootDistance(vSize);
1510 data.flickTarget += overshootDist;
1511 } else if (data.flickTarget <= maxExtent) {
1512 overshootDist = overShootDistance(vSize);
1513 data.flickTarget -= overshootDist;
1517 timeline.reset(data.move);
1518 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1519 timeline.callback(QDeclarative1TimeLineCallback(&data.move, fixupCallback, this));
1520 if (!hData.flicking && q->xflick()) {
1521 hData.flicking = true;
1522 emit q->flickingChanged();
1523 emit q->flickingHorizontallyChanged();
1524 emit q->flickStarted();
1526 if (!vData.flicking && q->yflick()) {
1527 vData.flicking = true;
1528 emit q->flickingChanged();
1529 emit q->flickingVerticallyChanged();
1530 emit q->flickStarted();
1532 correctFlick = true;
1534 // reevaluate the target boundary.
1535 qreal newtarget = data.flickTarget;
1536 if (snapMode != QDeclarative1ListView::NoSnap || highlightRange == QDeclarative1ListView::StrictlyEnforceRange) {
1537 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1538 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1539 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1541 if (velocity < 0 && newtarget <= maxExtent)
1542 newtarget = maxExtent - overshootDist;
1543 else if (velocity > 0 && newtarget >= minExtent)
1544 newtarget = minExtent + overshootDist;
1545 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1546 if (qAbs(velocity) < MinimumFlickVelocity)
1547 correctFlick = false;
1550 data.flickTarget = newtarget;
1551 qreal dist = -newtarget + data.move.value();
1552 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1553 correctFlick = false;
1554 timeline.reset(data.move);
1555 fixup(data, minExtent, maxExtent);
1559 timeline.reset(data.move);
1560 timeline.accelDistance(data.move, v, -dist);
1561 timeline.callback(QDeclarative1TimeLineCallback(&data.move, fixupCallback, this));
1564 correctFlick = false;
1565 timeline.reset(data.move);
1566 fixup(data, minExtent, maxExtent);
1570 //----------------------------------------------------------------------------
1573 \qmlclass ListView QDeclarative1ListView
1574 \inqmlmodule QtQuick 1
1575 \ingroup qml-view-elements
1578 \brief The ListView item provides a list view of items provided by a model.
1580 A ListView displays data from models created from built-in QML elements like ListModel
1581 and XmlListModel, or custom model classes defined in C++ that inherit from
1584 A ListView has a \l model, which defines the data to be displayed, and
1585 a \l delegate, which defines how the data should be displayed. Items in a
1586 ListView are laid out horizontally or vertically. List views are inherently
1587 flickable because ListView inherits from \l Flickable.
1589 \section1 Example Usage
1591 The following example shows the definition of a simple list model defined
1592 in a file called \c ContactModel.qml:
1594 \snippet doc/src/snippets/qtquick1/listview/ContactModel.qml 0
1596 Another component can display this model data in a ListView, like this:
1598 \snippet doc/src/snippets/qtquick1/listview/listview.qml import
1600 \snippet doc/src/snippets/qtquick1/listview/listview.qml classdocs simple
1602 \image listview-simple.png
1604 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1605 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1606 the delegate is able to access the model's \c name and \c number data directly.
1608 An improved list view is shown below. The delegate is visually improved and is moved
1609 into a separate \c contactDelegate component.
1611 \snippet doc/src/snippets/qtquick1/listview/listview.qml classdocs advanced
1612 \image listview-highlight.png
1614 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1615 and \c focus is set to \c true to enable keyboard navigation for the list view.
1616 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1618 Delegates are instantiated as needed and may be destroyed at any time.
1619 State should \e never be stored in a delegate.
1621 ListView attaches a number of properties to the root item of the delegate, for example
1622 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1623 this attached property directly as \c ListView.isCurrentItem, while the child
1624 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1626 \snippet doc/src/snippets/qtquick1/listview/listview.qml isCurrentItem
1628 \note Views do not enable \e clip automatically. If the view
1629 is not clipped by another item or the screen, it will be necessary
1630 to set \e {clip: true} in order to have the out of view items clipped
1633 \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1636 QDeclarative1ListView::QDeclarative1ListView(QDeclarativeItem *parent)
1637 : QDeclarative1Flickable(*(new QDeclarative1ListViewPrivate), parent)
1639 Q_D(QDeclarative1ListView);
1643 QDeclarative1ListView::~QDeclarative1ListView()
1645 Q_D(QDeclarative1ListView);
1654 \qmlattachedproperty bool ListView::isCurrentItem
1655 This attached property is true if this delegate is the current item; otherwise false.
1657 It is attached to each instance of the delegate.
1659 This property may be used to adjust the appearance of the current item, for example:
1661 \snippet doc/src/snippets/qtquick1/listview/listview.qml isCurrentItem
1665 \qmlattachedproperty ListView ListView::view
1666 This attached property holds the view that manages this delegate instance.
1668 It is attached to each instance of the delegate.
1672 \qmlattachedproperty string ListView::previousSection
1673 This attached property holds the section of the previous element.
1675 It is attached to each instance of the delegate.
1677 The section is evaluated using the \l {ListView::section.property}{section} properties.
1681 \qmlattachedproperty string ListView::nextSection
1682 This attached property holds the section of the next element.
1684 It is attached to each instance of the delegate.
1686 The section is evaluated using the \l {ListView::section.property}{section} properties.
1690 \qmlattachedproperty string ListView::section
1691 This attached property holds the section of this element.
1693 It is attached to each instance of the delegate.
1695 The section is evaluated using the \l {ListView::section.property}{section} properties.
1699 \qmlattachedproperty bool ListView::delayRemove
1700 This attached property holds whether the delegate may be destroyed.
1702 It is attached to each instance of the delegate.
1704 It is sometimes necessary to delay the destruction of an item
1705 until an animation completes.
1707 The example delegate below ensures that the animation completes before
1708 the item is removed from the list.
1710 \snippet doc/src/snippets/qtquick1/listview/listview.qml delayRemove
1714 \qmlattachedsignal QtQuick1::ListView::onAdd()
1715 This attached handler is called immediately after an item is added to the view.
1719 \qmlattachedsignal QtQuick1::ListView::onRemove()
1720 This attached handler is called immediately before an item is removed from the view.
1724 \qmlproperty model QtQuick1::ListView::model
1725 This property holds the model providing data for the list.
1727 The model provides the set of data that is used to create the items
1728 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1729 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1730 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1732 \sa {qmlmodels}{Data Models}
1734 QVariant QDeclarative1ListView::model() const
1736 Q_D(const QDeclarative1ListView);
1737 return d->modelVariant;
1740 void QDeclarative1ListView::setModel(const QVariant &model)
1742 Q_D(QDeclarative1ListView);
1743 if (d->modelVariant == model)
1746 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1747 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1748 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1749 disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1750 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1751 disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1752 disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1755 QDeclarative1VisualModel *oldModel = d->model;
1758 d->modelVariant = model;
1759 QObject *object = qvariant_cast<QObject*>(model);
1760 QDeclarative1VisualModel *vim = 0;
1761 if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) {
1764 d->ownModel = false;
1769 d->model = new QDeclarative1VisualDataModel(qmlContext(this), this);
1772 d->model = oldModel;
1774 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model))
1775 dataModel->setModel(model);
1778 d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore | QDeclarative1ListViewPrivate::BufferAfter;
1779 if (isComponentComplete()) {
1782 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1785 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
1786 d->updateCurrent(d->currentIndex);
1787 if (d->highlight && d->currentItem) {
1788 if (d->autoHighlight)
1789 d->highlight->setPosition(d->currentItem->position());
1790 d->updateTrackedItem();
1793 d->updateViewport();
1795 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1796 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1797 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1798 connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1799 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1800 connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1801 connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1802 emit countChanged();
1804 emit modelChanged();
1808 \qmlproperty Component QtQuick1::ListView::delegate
1810 The delegate provides a template defining each item instantiated by the view.
1811 The index is exposed as an accessible \c index property. Properties of the
1812 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1814 The number of elements in the delegate has a direct effect on the
1815 flicking performance of the view. If at all possible, place functionality
1816 that is not needed for the normal display of the delegate in a \l Loader which
1817 can load additional elements when needed.
1819 The ListView will lay out the items based on the size of the root item
1822 It is recommended that the delagate's size be a whole number to avoid sub-pixel
1825 \note Delegates are instantiated as needed and may be destroyed at any time.
1826 State should \e never be stored in a delegate.
1828 QDeclarativeComponent *QDeclarative1ListView::delegate() const
1830 Q_D(const QDeclarative1ListView);
1832 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model))
1833 return dataModel->delegate();
1839 void QDeclarative1ListView::setDelegate(QDeclarativeComponent *delegate)
1841 Q_D(QDeclarative1ListView);
1842 if (delegate == this->delegate())
1845 d->model = new QDeclarative1VisualDataModel(qmlContext(this));
1848 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) {
1849 int oldCount = dataModel->count();
1850 dataModel->setDelegate(delegate);
1851 if (isComponentComplete()) {
1852 for (int i = 0; i < d->visibleItems.count(); ++i)
1853 d->releaseItem(d->visibleItems.at(i));
1854 d->visibleItems.clear();
1855 d->releaseItem(d->currentItem);
1859 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
1860 d->updateCurrent(d->currentIndex);
1861 if (d->highlight && d->currentItem) {
1862 if (d->autoHighlight)
1863 d->highlight->setPosition(d->currentItem->position());
1864 d->updateTrackedItem();
1866 d->updateViewport();
1868 if (oldCount != dataModel->count())
1869 emit countChanged();
1871 emit delegateChanged();
1875 \qmlproperty int QtQuick1::ListView::currentIndex
1876 \qmlproperty Item QtQuick1::ListView::currentItem
1878 The \c currentIndex property holds the index of the current item, and
1879 \c currentItem holds the current item. Setting the currentIndex to -1
1880 will clear the highlight and set currentItem to null.
1882 If highlightFollowsCurrentItem is \c true, setting either of these
1883 properties will smoothly scroll the ListView so that the current
1884 item becomes visible.
1886 Note that the position of the current item
1887 may only be approximate until it becomes visible in the view.
1889 int QDeclarative1ListView::currentIndex() const
1891 Q_D(const QDeclarative1ListView);
1892 return d->currentIndex;
1895 void QDeclarative1ListView::setCurrentIndex(int index)
1897 Q_D(QDeclarative1ListView);
1898 if (d->requestedIndex >= 0) // currently creating item
1900 d->currentIndexCleared = (index == -1);
1901 if (index == d->currentIndex)
1903 if (isComponentComplete() && d->isValid()) {
1904 if (d->layoutScheduled)
1906 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
1907 d->updateCurrent(index);
1908 } else if (d->currentIndex != index) {
1909 d->currentIndex = index;
1910 emit currentIndexChanged();
1914 QDeclarativeItem *QDeclarative1ListView::currentItem()
1916 Q_D(QDeclarative1ListView);
1917 if (!d->currentItem)
1919 return d->currentItem->item;
1923 \qmlproperty Item QtQuick1::ListView::highlightItem
1925 This holds the highlight item created from the \l highlight component.
1927 The \c highlightItem is managed by the view unless
1928 \l highlightFollowsCurrentItem is set to false.
1930 \sa highlight, highlightFollowsCurrentItem
1932 QDeclarativeItem *QDeclarative1ListView::highlightItem()
1934 Q_D(QDeclarative1ListView);
1937 return d->highlight->item;
1941 \qmlproperty int QtQuick1::ListView::count
1942 This property holds the number of items in the view.
1944 int QDeclarative1ListView::count() const
1946 Q_D(const QDeclarative1ListView);
1948 return d->model->count();
1953 \qmlproperty Component QtQuick1::ListView::highlight
1954 This property holds the component to use as the highlight.
1956 An instance of the highlight component is created for each list.
1957 The geometry of the resulting component instance is managed by the list
1958 so as to stay with the current item, unless the highlightFollowsCurrentItem
1961 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1963 QDeclarativeComponent *QDeclarative1ListView::highlight() const
1965 Q_D(const QDeclarative1ListView);
1966 return d->highlightComponent;
1969 void QDeclarative1ListView::setHighlight(QDeclarativeComponent *highlight)
1971 Q_D(QDeclarative1ListView);
1972 if (highlight != d->highlightComponent) {
1973 d->highlightComponent = highlight;
1974 d->createHighlight();
1976 d->updateHighlight();
1977 emit highlightChanged();
1982 \qmlproperty bool QtQuick1::ListView::highlightFollowsCurrentItem
1983 This property holds whether the highlight is managed by the view.
1985 If this property is true (the default value), the highlight is moved smoothly
1986 to follow the current item. Otherwise, the
1987 highlight is not moved by the view, and any movement must be implemented
1990 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1992 \snippet doc/src/snippets/qtquick1/listview/listview.qml highlightFollowsCurrentItem
1994 Note that the highlight animation also affects the way that the view
1995 is scrolled. This is because the view moves to maintain the
1996 highlight within the preferred highlight range (or visible viewport).
1998 \sa highlight, highlightMoveSpeed
2000 bool QDeclarative1ListView::highlightFollowsCurrentItem() const
2002 Q_D(const QDeclarative1ListView);
2003 return d->autoHighlight;
2006 void QDeclarative1ListView::setHighlightFollowsCurrentItem(bool autoHighlight)
2008 Q_D(QDeclarative1ListView);
2009 if (d->autoHighlight != autoHighlight) {
2010 d->autoHighlight = autoHighlight;
2011 if (autoHighlight) {
2012 d->updateHighlight();
2014 if (d->highlightPosAnimator)
2015 d->highlightPosAnimator->stop();
2016 if (d->highlightSizeAnimator)
2017 d->highlightSizeAnimator->stop();
2019 emit highlightFollowsCurrentItemChanged();
2023 //###Possibly rename these properties, since they are very useful even without a highlight?
2025 \qmlproperty real QtQuick1::ListView::preferredHighlightBegin
2026 \qmlproperty real QtQuick1::ListView::preferredHighlightEnd
2027 \qmlproperty enumeration QtQuick1::ListView::highlightRangeMode
2029 These properties define the preferred range of the highlight (for the current item)
2030 within the view. The \c preferredHighlightBegin value must be less than the
2031 \c preferredHighlightEnd value.
2033 These properties affect the position of the current item when the list is scrolled.
2034 For example, if the currently selected item should stay in the middle of the
2035 list when the view is scrolled, set the \c preferredHighlightBegin and
2036 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
2037 item would be. If the \c currentItem is changed programmatically, the list will
2038 automatically scroll so that the current item is in the middle of the view.
2039 Furthermore, the behavior of the current item index will occur whether or not a
2042 Valid values for \c highlightRangeMode are:
2045 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2046 However, the highlight can move outside of the range at the ends of the list or due
2047 to mouse interaction.
2048 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2049 The current item changes if a keyboard or mouse action would cause the highlight to move
2050 outside of the range.
2051 \o ListView.NoHighlightRange - this is the default value.
2054 qreal QDeclarative1ListView::preferredHighlightBegin() const
2056 Q_D(const QDeclarative1ListView);
2057 return d->highlightRangeStart;
2060 void QDeclarative1ListView::setPreferredHighlightBegin(qreal start)
2062 Q_D(QDeclarative1ListView);
2063 d->highlightRangeStartValid = true;
2064 if (d->highlightRangeStart == start)
2066 d->highlightRangeStart = start;
2067 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2068 emit preferredHighlightBeginChanged();
2071 void QDeclarative1ListView::resetPreferredHighlightBegin()
2073 Q_D(QDeclarative1ListView);
2074 d->highlightRangeStartValid = false;
2075 if (d->highlightRangeStart == 0)
2077 d->highlightRangeStart = 0;
2078 emit preferredHighlightBeginChanged();
2081 qreal QDeclarative1ListView::preferredHighlightEnd() const
2083 Q_D(const QDeclarative1ListView);
2084 return d->highlightRangeEnd;
2087 void QDeclarative1ListView::setPreferredHighlightEnd(qreal end)
2089 Q_D(QDeclarative1ListView);
2090 d->highlightRangeEndValid = true;
2091 if (d->highlightRangeEnd == end)
2093 d->highlightRangeEnd = end;
2094 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2095 emit preferredHighlightEndChanged();
2098 void QDeclarative1ListView::resetPreferredHighlightEnd()
2100 Q_D(QDeclarative1ListView);
2101 d->highlightRangeEndValid = false;
2102 if (d->highlightRangeEnd == 0)
2104 d->highlightRangeEnd = 0;
2105 emit preferredHighlightEndChanged();
2108 QDeclarative1ListView::HighlightRangeMode QDeclarative1ListView::highlightRangeMode() const
2110 Q_D(const QDeclarative1ListView);
2111 return d->highlightRange;
2114 void QDeclarative1ListView::setHighlightRangeMode(HighlightRangeMode mode)
2116 Q_D(QDeclarative1ListView);
2117 if (d->highlightRange == mode)
2119 d->highlightRange = mode;
2120 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2121 emit highlightRangeModeChanged();
2125 \qmlproperty real QtQuick1::ListView::spacing
2127 This property holds the spacing between items.
2129 The default value is 0.
2131 qreal QDeclarative1ListView::spacing() const
2133 Q_D(const QDeclarative1ListView);
2137 void QDeclarative1ListView::setSpacing(qreal spacing)
2139 Q_D(QDeclarative1ListView);
2140 if (spacing != d->spacing) {
2141 d->spacing = spacing;
2143 emit spacingChanged();
2148 \qmlproperty enumeration QtQuick1::ListView::orientation
2149 This property holds the orientation of the list.
2154 \o ListView.Horizontal - Items are laid out horizontally
2155 \o ListView.Vertical (default) - Items are laid out vertically
2160 \o Horizontal orientation:
2161 \image ListViewHorizontal.png
2164 \o Vertical orientation:
2165 \image listview-highlight.png
2168 QDeclarative1ListView::Orientation QDeclarative1ListView::orientation() const
2170 Q_D(const QDeclarative1ListView);
2174 void QDeclarative1ListView::setOrientation(QDeclarative1ListView::Orientation orientation)
2176 Q_D(QDeclarative1ListView);
2177 if (d->orient != orientation) {
2178 d->orient = orientation;
2179 if (d->orient == QDeclarative1ListView::Vertical) {
2180 setContentWidth(-1);
2181 setFlickableDirection(VerticalFlick);
2184 setContentHeight(-1);
2185 setFlickableDirection(HorizontalFlick);
2189 emit orientationChanged();
2194 \qmlproperty enumeration QtQuick1::ListView::layoutDirection
2195 This property holds the layout direction of the horizontal list.
2200 \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2201 \o Qt.RightToLeft - Items will be laid out from right to let.
2204 \sa ListView::effectiveLayoutDirection
2207 Qt::LayoutDirection QDeclarative1ListView::layoutDirection() const
2209 Q_D(const QDeclarative1ListView);
2210 return d->layoutDirection;
2213 void QDeclarative1ListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2215 Q_D(QDeclarative1ListView);
2216 if (d->layoutDirection != layoutDirection) {
2217 d->layoutDirection = layoutDirection;
2219 emit layoutDirectionChanged();
2220 emit effectiveLayoutDirectionChanged();
2225 \qmlproperty enumeration QtQuick1::ListView::effectiveLayoutDirection
2226 This property holds the effective layout direction of the horizontal list.
2228 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2229 the visual layout direction of the horizontal list will be mirrored. However, the
2230 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2232 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2235 Qt::LayoutDirection QDeclarative1ListView::effectiveLayoutDirection() const
2237 Q_D(const QDeclarative1ListView);
2238 if (d->effectiveLayoutMirror)
2239 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2241 return d->layoutDirection;
2245 \qmlproperty bool QtQuick1::ListView::keyNavigationWraps
2246 This property holds whether the list wraps key navigation.
2248 If this is true, key navigation that would move the current item selection
2249 past the end of the list instead wraps around and moves the selection to
2250 the start of the list, and vice-versa.
2252 By default, key navigation is not wrapped.
2254 bool QDeclarative1ListView::isWrapEnabled() const
2256 Q_D(const QDeclarative1ListView);
2260 void QDeclarative1ListView::setWrapEnabled(bool wrap)
2262 Q_D(QDeclarative1ListView);
2263 if (d->wrap == wrap)
2266 emit keyNavigationWrapsChanged();
2270 \qmlproperty int QtQuick1::ListView::cacheBuffer
2271 This property determines whether delegates are retained outside the
2272 visible area of the view.
2274 If this value is non-zero, the view keeps as many delegates
2275 instantiated as it can fit within the buffer specified. For example,
2276 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2277 set to 40, then up to 2 delegates above and 2 delegates below the visible
2278 area may be retained.
2280 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2281 instantiated delegates.
2283 Setting this value can improve the smoothness of scrolling behavior at the expense
2284 of additional memory usage. It is not a substitute for creating efficient
2285 delegates; the fewer elements in a delegate, the faster a view can be
2288 int QDeclarative1ListView::cacheBuffer() const
2290 Q_D(const QDeclarative1ListView);
2294 void QDeclarative1ListView::setCacheBuffer(int b)
2296 Q_D(QDeclarative1ListView);
2297 if (d->buffer != b) {
2299 if (isComponentComplete()) {
2300 d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore | QDeclarative1ListViewPrivate::BufferAfter;
2303 emit cacheBufferChanged();
2308 \qmlproperty string QtQuick1::ListView::section.property
2309 \qmlproperty enumeration QtQuick1::ListView::section.criteria
2310 \qmlproperty Component QtQuick1::ListView::section.delegate
2312 These properties hold the expression to be evaluated for the \l section attached property.
2314 The \l section attached property enables a ListView to be visually
2315 separated into different parts. These properties determine how sections
2318 \c section.property holds the name of the property that is the basis
2321 \c section.criteria holds the criteria for forming each section based on
2322 \c section.property. This value can be one of:
2325 \o ViewSection.FullString (default) - sections are created based on the
2326 \c section.property value.
2327 \o ViewSection.FirstCharacter - sections are created based on the first
2328 character of the \c section.property value (for example, 'A', 'B', 'C'
2329 sections, etc. for an address book)
2332 \c section.delegate holds the delegate component for each section.
2334 Each item in the list has attached properties named \c ListView.section,
2335 \c ListView.previousSection and \c ListView.nextSection. These may be
2336 used to place a section header for related items.
2338 For example, here is a ListView that displays a list of animals, separated
2339 into sections. Each item in the ListView is placed in a different section
2340 depending on the "size" property of the model item. The \c sectionHeading
2341 delegate component provides the light blue bar that marks the beginning of
2345 \snippet examples/declarative/modelviews/listview/sections.qml 0
2347 \image qml-listview-sections-example.png
2349 \note Adding sections to a ListView does not automatically re-order the
2350 list items by the section criteria.
2351 If the model is not ordered by section, then it is possible that
2352 the sections created will not be unique; each boundary between
2353 differing sections will result in a section header being created
2354 even if that section exists elsewhere.
2356 \sa {declarative/modelviews/listview}{ListView examples}
2358 QDeclarative1ViewSection *QDeclarative1ListView::sectionCriteria()
2360 Q_D(QDeclarative1ListView);
2361 if (!d->sectionCriteria) {
2362 d->sectionCriteria = new QDeclarative1ViewSection(this);
2363 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2365 return d->sectionCriteria;
2369 \qmlproperty string QtQuick1::ListView::currentSection
2370 This property holds the section that is currently at the beginning of the view.
2372 QString QDeclarative1ListView::currentSection() const
2374 Q_D(const QDeclarative1ListView);
2375 return d->currentSection;
2379 \qmlproperty real QtQuick1::ListView::highlightMoveSpeed
2380 \qmlproperty int QtQuick1::ListView::highlightMoveDuration
2381 \qmlproperty real QtQuick1::ListView::highlightResizeSpeed
2382 \qmlproperty int QtQuick1::ListView::highlightResizeDuration
2384 These properties hold the move and resize animation speed of the highlight delegate.
2386 \l highlightFollowsCurrentItem must be true for these properties
2389 The default value for the speed properties is 400 pixels/second.
2390 The default value for the duration properties is -1, i.e. the
2391 highlight will take as much time as necessary to move at the set speed.
2393 These properties have the same characteristics as a SmoothedAnimation.
2395 \sa highlightFollowsCurrentItem
2397 qreal QDeclarative1ListView::highlightMoveSpeed() const
2399 Q_D(const QDeclarative1ListView);\
2400 return d->highlightMoveSpeed;
2403 void QDeclarative1ListView::setHighlightMoveSpeed(qreal speed)
2405 Q_D(QDeclarative1ListView);\
2406 if (d->highlightMoveSpeed != speed) {
2407 d->highlightMoveSpeed = speed;
2408 if (d->highlightPosAnimator)
2409 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2410 emit highlightMoveSpeedChanged();
2414 int QDeclarative1ListView::highlightMoveDuration() const
2416 Q_D(const QDeclarative1ListView);
2417 return d->highlightMoveDuration;
2420 void QDeclarative1ListView::setHighlightMoveDuration(int duration)
2422 Q_D(QDeclarative1ListView);\
2423 if (d->highlightMoveDuration != duration) {
2424 d->highlightMoveDuration = duration;
2425 if (d->highlightPosAnimator)
2426 d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2427 emit highlightMoveDurationChanged();
2431 qreal QDeclarative1ListView::highlightResizeSpeed() const
2433 Q_D(const QDeclarative1ListView);\
2434 return d->highlightResizeSpeed;
2437 void QDeclarative1ListView::setHighlightResizeSpeed(qreal speed)
2439 Q_D(QDeclarative1ListView);\
2440 if (d->highlightResizeSpeed != speed) {
2441 d->highlightResizeSpeed = speed;
2442 if (d->highlightSizeAnimator)
2443 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2444 emit highlightResizeSpeedChanged();
2448 int QDeclarative1ListView::highlightResizeDuration() const
2450 Q_D(const QDeclarative1ListView);
2451 return d->highlightResizeDuration;
2454 void QDeclarative1ListView::setHighlightResizeDuration(int duration)
2456 Q_D(QDeclarative1ListView);\
2457 if (d->highlightResizeDuration != duration) {
2458 d->highlightResizeDuration = duration;
2459 if (d->highlightSizeAnimator)
2460 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2461 emit highlightResizeDurationChanged();
2466 \qmlproperty enumeration QtQuick1::ListView::snapMode
2468 This property determines how the view scrolling will settle following a drag or flick.
2469 The possible values are:
2472 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2473 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2475 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2476 visible item at the time the mouse button is released. This mode is particularly
2477 useful for moving one page at a time.
2480 \c snapMode does not affect the \l currentIndex. To update the
2481 \l currentIndex as the list is moved, set \l highlightRangeMode
2482 to \c ListView.StrictlyEnforceRange.
2484 \sa highlightRangeMode
2486 QDeclarative1ListView::SnapMode QDeclarative1ListView::snapMode() const
2488 Q_D(const QDeclarative1ListView);
2492 void QDeclarative1ListView::setSnapMode(SnapMode mode)
2494 Q_D(QDeclarative1ListView);
2495 if (d->snapMode != mode) {
2497 emit snapModeChanged();
2502 \qmlproperty Component QtQuick1::ListView::footer
2503 This property holds the component to use as the footer.
2505 An instance of the footer component is created for each view. The
2506 footer is positioned at the end of the view, after any items.
2510 QDeclarativeComponent *QDeclarative1ListView::footer() const
2512 Q_D(const QDeclarative1ListView);
2513 return d->footerComponent;
2516 void QDeclarative1ListView::setFooter(QDeclarativeComponent *footer)
2518 Q_D(QDeclarative1ListView);
2519 if (d->footerComponent != footer) {
2522 scene()->removeItem(d->footer->item);
2523 d->footer->item->deleteLater();
2527 d->footerComponent = footer;
2528 d->minExtentDirty = true;
2529 d->maxExtentDirty = true;
2530 if (isComponentComplete()) {
2532 d->updateViewport();
2535 emit footerChanged();
2540 \qmlproperty Component QtQuick1::ListView::header
2541 This property holds the component to use as the header.
2543 An instance of the header component is created for each view. The
2544 header is positioned at the beginning of the view, before any items.
2548 QDeclarativeComponent *QDeclarative1ListView::header() const
2550 Q_D(const QDeclarative1ListView);
2551 return d->headerComponent;
2554 void QDeclarative1ListView::setHeader(QDeclarativeComponent *header)
2556 Q_D(QDeclarative1ListView);
2557 if (d->headerComponent != header) {
2560 scene()->removeItem(d->header->item);
2561 d->header->item->deleteLater();
2565 d->headerComponent = header;
2566 d->minExtentDirty = true;
2567 d->maxExtentDirty = true;
2568 if (isComponentComplete()) {
2571 d->updateViewport();
2574 emit headerChanged();
2578 void QDeclarative1ListView::setContentX(qreal pos)
2580 Q_D(QDeclarative1ListView);
2581 // Positioning the view manually should override any current movement state
2582 d->moveReason = QDeclarative1ListViewPrivate::Other;
2583 QDeclarative1Flickable::setContentX(pos);
2586 void QDeclarative1ListView::setContentY(qreal pos)
2588 Q_D(QDeclarative1ListView);
2589 // Positioning the view manually should override any current movement state
2590 d->moveReason = QDeclarative1ListViewPrivate::Other;
2591 QDeclarative1Flickable::setContentY(pos);
2594 bool QDeclarative1ListView::event(QEvent *event)
2596 Q_D(QDeclarative1ListView);
2597 if (event->type() == QEvent::User) {
2598 if (d->layoutScheduled)
2603 return QDeclarative1Flickable::event(event);
2606 void QDeclarative1ListView::viewportMoved()
2608 Q_D(QDeclarative1ListView);
2609 QDeclarative1Flickable::viewportMoved();
2612 // Recursion can occur due to refill changing the content size.
2613 if (d->inViewportMoved)
2615 d->inViewportMoved = true;
2616 d->lazyRelease = true;
2618 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2619 d->moveReason = QDeclarative1ListViewPrivate::Mouse;
2620 if (d->moveReason != QDeclarative1ListViewPrivate::SetIndex) {
2621 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2622 // reposition highlight
2623 qreal pos = d->highlight->position();
2625 qreal highlightStart;
2627 if (d->isRightToLeft()) {
2628 // Handle Right-To-Left exceptions
2629 viewPos = -d->position()-d->size();
2630 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2631 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2633 viewPos = d->position();
2634 highlightStart = d->highlightRangeStart;
2635 highlightEnd = d->highlightRangeEnd;
2637 if (pos > viewPos + highlightEnd - d->highlight->size())
2638 pos = viewPos + highlightEnd - d->highlight->size();
2639 if (pos < viewPos + highlightStart)
2640 pos = viewPos + highlightStart;
2641 d->highlightPosAnimator->stop();
2642 d->highlight->setPosition(qRound(pos));
2644 // update current index
2645 if (FxListItem1 *snapItem = d->snapItemAt(d->highlight->position())) {
2646 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2647 d->updateCurrent(snapItem->index);
2652 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2653 d->inFlickCorrection = true;
2654 // Near an end and it seems that the extent has changed?
2655 // Recalculate the flick so that we don't end up in an odd position.
2656 if (yflick() && !d->vData.inOvershoot) {
2657 if (d->vData.velocity > 0) {
2658 const qreal minY = minYExtent();
2659 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2660 && minY != d->vData.flickTarget)
2661 d->flickY(-d->vData.smoothVelocity.value());
2662 d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore;
2663 } else if (d->vData.velocity < 0) {
2664 const qreal maxY = maxYExtent();
2665 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2666 && maxY != d->vData.flickTarget)
2667 d->flickY(-d->vData.smoothVelocity.value());
2668 d->bufferMode = QDeclarative1ListViewPrivate::BufferAfter;
2672 if (xflick() && !d->hData.inOvershoot) {
2673 if (d->hData.velocity > 0) {
2674 const qreal minX = minXExtent();
2675 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2676 && minX != d->hData.flickTarget)
2677 d->flickX(-d->hData.smoothVelocity.value());
2678 d->bufferMode = d->isRightToLeft()
2679 ? QDeclarative1ListViewPrivate::BufferAfter : QDeclarative1ListViewPrivate::BufferBefore;
2680 } else if (d->hData.velocity < 0) {
2681 const qreal maxX = maxXExtent();
2682 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2683 && maxX != d->hData.flickTarget)
2684 d->flickX(-d->hData.smoothVelocity.value());
2685 d->bufferMode = d->isRightToLeft()
2686 ? QDeclarative1ListViewPrivate::BufferBefore : QDeclarative1ListViewPrivate::BufferAfter;
2689 d->inFlickCorrection = false;
2691 d->inViewportMoved = false;
2694 qreal QDeclarative1ListView::minYExtent() const
2696 Q_D(const QDeclarative1ListView);
2697 if (d->orient == QDeclarative1ListView::Horizontal)
2698 return QDeclarative1Flickable::minYExtent();
2699 if (d->minExtentDirty) {
2700 d->minExtent = -d->startPosition();
2701 if (d->header && d->visibleItems.count())
2702 d->minExtent += d->header->size();
2703 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2704 d->minExtent += d->highlightRangeStart;
2705 if (d->sectionCriteria) {
2706 if (d->visibleItem(0))
2707 d->minExtent -= d->visibleItem(0)->sectionSize();
2709 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2711 d->minExtentDirty = false;
2714 return d->minExtent;
2717 qreal QDeclarative1ListView::maxYExtent() const
2719 Q_D(const QDeclarative1ListView);
2720 if (d->orient == QDeclarative1ListView::Horizontal)
2722 if (d->maxExtentDirty) {
2723 if (!d->model || !d->model->count()) {
2724 d->maxExtent = d->header ? -d->header->size() : 0;
2725 d->maxExtent += height();
2726 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2727 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2728 if (d->highlightRangeEnd != d->highlightRangeStart)
2729 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2731 d->maxExtent = -(d->endPosition() - height() + 1);
2734 d->maxExtent -= d->footer->size();
2735 qreal minY = minYExtent();
2736 if (d->maxExtent > minY)
2737 d->maxExtent = minY;
2738 d->maxExtentDirty = false;
2740 return d->maxExtent;
2743 qreal QDeclarative1ListView::minXExtent() const
2745 Q_D(const QDeclarative1ListView);
2746 if (d->orient == QDeclarative1ListView::Vertical)
2747 return QDeclarative1Flickable::minXExtent();
2748 if (d->minExtentDirty) {
2749 d->minExtent = -d->startPosition();
2751 qreal highlightStart;
2753 qreal endPositionFirstItem = 0;
2754 if (d->isRightToLeft()) {
2755 if (d->model && d->model->count())
2756 endPositionFirstItem = d->positionAt(d->model->count()-1);
2758 d->minExtent += d->header->size();
2759 highlightStart = d->highlightRangeStartValid
2760 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2761 : d->size() - (d->lastPosition()-endPositionFirstItem);
2762 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2764 d->minExtent += d->footer->size();
2765 qreal maxX = maxXExtent();
2766 if (d->minExtent < maxX)
2767 d->minExtent = maxX;
2769 endPositionFirstItem = d->endPositionAt(0);
2770 highlightStart = d->highlightRangeStart;
2771 highlightEnd = d->highlightRangeEnd;
2772 if (d->header && d->visibleItems.count())
2773 d->minExtent += d->header->size();
2775 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2776 d->minExtent += d->isRightToLeft() ? -highlightStart : highlightStart;
2777 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2779 d->minExtentDirty = false;
2782 return d->minExtent;
2785 qreal QDeclarative1ListView::maxXExtent() const
2787 Q_D(const QDeclarative1ListView);
2788 if (d->orient == QDeclarative1ListView::Vertical)
2790 if (d->maxExtentDirty) {
2791 qreal highlightStart;
2793 qreal lastItemPosition = 0;
2795 if (d->isRightToLeft()) {
2796 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2797 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2798 lastItemPosition = d->endPosition();
2800 highlightStart = d->highlightRangeStart;
2801 highlightEnd = d->highlightRangeEnd;
2802 if (d->model && d->model->count())
2803 lastItemPosition = d->positionAt(d->model->count()-1);
2805 if (!d->model || !d->model->count()) {
2806 if (!d->isRightToLeft())
2807 d->maxExtent = d->header ? -d->header->size() : 0;
2808 d->maxExtent += width();
2809 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2810 d->maxExtent = -(lastItemPosition - highlightStart);
2811 if (highlightEnd != highlightStart) {
2812 d->maxExtent = d->isRightToLeft()
2813 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2814 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2817 d->maxExtent = -(d->endPosition() - width() + 1);
2819 if (d->isRightToLeft()) {
2820 if (d->header && d->visibleItems.count())
2821 d->maxExtent -= d->header->size();
2824 d->maxExtent -= d->footer->size();
2825 qreal minX = minXExtent();
2826 if (d->maxExtent > minX)
2827 d->maxExtent = minX;
2829 d->maxExtentDirty = false;
2831 return d->maxExtent;
2834 void QDeclarative1ListView::keyPressEvent(QKeyEvent *event)
2836 Q_D(QDeclarative1ListView);
2837 keyPressPreHandler(event);
2838 if (event->isAccepted())
2841 if (d->model && d->model->count() && d->interactive) {
2842 if ((d->orient == QDeclarative1ListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2843 || (d->orient == QDeclarative1ListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2844 || (d->orient == QDeclarative1ListView::Vertical && event->key() == Qt::Key_Up)) {
2845 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2846 decrementCurrentIndex();
2849 } else if (d->wrap) {
2853 } else if ((d->orient == QDeclarative1ListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2854 || (d->orient == QDeclarative1ListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2855 || (d->orient == QDeclarative1ListView::Vertical && event->key() == Qt::Key_Down)) {
2856 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2857 incrementCurrentIndex();
2860 } else if (d->wrap) {
2867 QDeclarative1Flickable::keyPressEvent(event);
2870 void QDeclarative1ListView::geometryChanged(const QRectF &newGeometry,
2871 const QRectF &oldGeometry)
2873 Q_D(QDeclarative1ListView);
2874 d->maxExtentDirty = true;
2875 d->minExtentDirty = true;
2876 if (d->isRightToLeft() && d->orient == QDeclarative1ListView::Horizontal) {
2877 // maintain position relative to the right edge
2878 int dx = newGeometry.width() - oldGeometry.width();
2879 setContentX(contentX() - dx);
2881 QDeclarative1Flickable::geometryChanged(newGeometry, oldGeometry);
2886 \qmlmethod QtQuick1::ListView::incrementCurrentIndex()
2888 Increments the current index. The current index will wrap
2889 if keyNavigationWraps is true and it is currently at the end.
2890 This method has no effect if the \l count is zero.
2892 \bold Note: methods should only be called after the Component has completed.
2894 void QDeclarative1ListView::incrementCurrentIndex()
2896 Q_D(QDeclarative1ListView);
2897 int count = d->model ? d->model->count() : 0;
2898 if (count && (currentIndex() < count - 1 || d->wrap)) {
2899 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
2900 int index = currentIndex()+1;
2901 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2906 \qmlmethod QtQuick1::ListView::decrementCurrentIndex()
2908 Decrements the current index. The current index will wrap
2909 if keyNavigationWraps is true and it is currently at the beginning.
2910 This method has no effect if the \l count is zero.
2912 \bold Note: methods should only be called after the Component has completed.
2914 void QDeclarative1ListView::decrementCurrentIndex()
2916 Q_D(QDeclarative1ListView);
2917 int count = d->model ? d->model->count() : 0;
2918 if (count && (currentIndex() > 0 || d->wrap)) {
2919 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
2920 int index = currentIndex()-1;
2921 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2925 void QDeclarative1ListViewPrivate::positionViewAtIndex(int index, int mode)
2927 Q_Q(QDeclarative1ListView);
2930 if (mode < QDeclarative1ListView::Beginning || mode > QDeclarative1ListView::Contain)
2932 int idx = qMax(qMin(index, model->count()-1), 0);
2934 if (layoutScheduled)
2936 qreal pos = isRightToLeft() ? -position() - size() : position();
2937 FxListItem1 *item = visibleItem(idx);
2939 if (orient == QDeclarative1ListView::Vertical)
2940 maxExtent = -q->maxYExtent();
2942 maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2945 int itemPos = positionAt(idx);
2946 // save the currently visible items in case any of them end up visible again
2947 QList<FxListItem1*> oldVisible = visibleItems;
2948 visibleItems.clear();
2949 visiblePos = itemPos;
2951 setPosition(qMin(qreal(itemPos), maxExtent));
2952 // now release the reference to all the old visible items.
2953 for (int i = 0; i < oldVisible.count(); ++i)
2954 releaseItem(oldVisible.at(i));
2955 item = visibleItem(idx);
2958 const qreal itemPos = item->position();
2960 case QDeclarative1ListView::Beginning:
2962 if (index < 0 && header)
2963 pos -= header->size();
2965 case QDeclarative1ListView::Center:
2966 pos = itemPos - (size() - item->size())/2;
2968 case QDeclarative1ListView::End:
2969 pos = itemPos - size() + item->size();
2970 if (index >= model->count() && footer)
2971 pos += footer->size();
2973 case QDeclarative1ListView::Visible:
2974 if (itemPos > pos + size())
2975 pos = itemPos - size() + item->size();
2976 else if (item->endPosition() < pos)
2979 case QDeclarative1ListView::Contain:
2980 if (item->endPosition() > pos + size())
2981 pos = itemPos - size() + item->size();
2985 pos = qMin(pos, maxExtent);
2987 if (orient == QDeclarative1ListView::Vertical) {
2988 minExtent = -q->minYExtent();
2990 minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2992 pos = qMax(pos, minExtent);
2993 moveReason = QDeclarative1ListViewPrivate::Other;
2997 if (autoHighlight) {
2998 highlight->setPosition(currentItem->itemPosition());
2999 highlight->setSize(currentItem->itemSize());
3008 \qmlmethod QtQuick1::ListView::positionViewAtIndex(int index, PositionMode mode)
3010 Positions the view such that the \a index is at the position specified by
3014 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
3015 \o ListView.Center - position item in the center of the view.
3016 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3017 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
3018 bring the item into view.
3019 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
3020 the view the item is positioned at the top (or left for horizontal orientation) of the view.
3023 If positioning the view at \a index would cause empty space to be displayed at
3024 the beginning or end of the view, the view will be positioned at the boundary.
3026 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3027 at a particular index. This is unreliable since removing items from the start
3028 of the list does not cause all other items to be repositioned, and because
3029 the actual start of the view can vary based on the size of the delegates.
3030 The correct way to bring an item into view is with \c positionViewAtIndex.
3032 \bold Note: methods should only be called after the Component has completed. To position
3033 the view at startup, this method should be called by Component.onCompleted. For
3034 example, to position the view at the end:
3037 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3040 void QDeclarative1ListView::positionViewAtIndex(int index, int mode)
3042 Q_D(QDeclarative1ListView);
3043 if (!d->isValid() || index < 0 || index >= d->model->count())
3045 d->positionViewAtIndex(index, mode);
3049 \qmlmethod QtQuick1::ListView::positionViewAtBeginning()
3050 \qmlmethod QtQuick1::ListView::positionViewAtEnd()
3053 Positions the view at the beginning or end, taking into account any header or footer.
3055 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3056 at a particular index. This is unreliable since removing items from the start
3057 of the list does not cause all other items to be repositioned, and because
3058 the actual start of the view can vary based on the size of the delegates.
3060 \bold Note: methods should only be called after the Component has completed. To position
3061 the view at startup, this method should be called by Component.onCompleted. For
3062 example, to position the view at the end on startup:
3065 Component.onCompleted: positionViewAtEnd()
3068 void QDeclarative1ListView::positionViewAtBeginning()
3070 Q_D(QDeclarative1ListView);
3073 d->positionViewAtIndex(-1, Beginning);
3076 void QDeclarative1ListView::positionViewAtEnd()
3078 Q_D(QDeclarative1ListView);
3081 d->positionViewAtIndex(d->model->count(), End);
3085 \qmlmethod int QtQuick1::ListView::indexAt(int x, int y)
3087 Returns the index of the visible item containing the point \a x, \a y in content
3088 coordinates. If there is no item at the point specified, or the item is
3089 not visible -1 is returned.
3091 If the item is outside the visible area, -1 is returned, regardless of
3092 whether an item will exist at that point when scrolled into view.
3094 \bold Note: methods should only be called after the Component has completed.
3096 int QDeclarative1ListView::indexAt(qreal x, qreal y) const
3098 Q_D(const QDeclarative1ListView);
3099 for (int i = 0; i < d->visibleItems.count(); ++i) {
3100 const FxListItem1 *listItem = d->visibleItems.at(i);
3101 if(listItem->contains(x, y))
3102 return listItem->index;
3108 void QDeclarative1ListView::componentComplete()
3110 Q_D(QDeclarative1ListView);
3111 QDeclarative1Flickable::componentComplete();
3117 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
3118 if (d->currentIndex < 0 && !d->currentIndexCleared)
3119 d->updateCurrent(0);
3121 d->updateCurrent(d->currentIndex);
3122 if (d->highlight && d->currentItem) {
3123 if (d->autoHighlight)
3124 d->highlight->setPosition(d->currentItem->position());
3125 d->updateTrackedItem();
3127 d->moveReason = QDeclarative1ListViewPrivate::Other;
3132 void QDeclarative1ListView::updateSections()
3134 Q_D(QDeclarative1ListView);
3135 if (isComponentComplete() && d->model) {
3136 QList<QByteArray> roles;
3137 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3138 roles << d->sectionCriteria->property().toUtf8();
3139 d->model->setWatchedRoles(roles);
3140 d->updateSections();
3146 void QDeclarative1ListView::refill()
3148 Q_D(QDeclarative1ListView);
3149 if (d->isRightToLeft())
3150 d->refill(-d->position()-d->size()+1, -d->position());
3152 d->refill(d->position(), d->position()+d->size()-1);
3155 void QDeclarative1ListView::trackedPositionChanged()
3157 Q_D(QDeclarative1ListView);
3158 if (!d->trackedItem || !d->currentItem)
3160 if (d->moveReason == QDeclarative1ListViewPrivate::SetIndex) {
3161 qreal trackedPos = qCeil(d->trackedItem->position());
3162 qreal trackedSize = d->trackedItem->size();
3163 if (d->trackedItem != d->currentItem) {
3164 trackedSize += d->currentItem->sectionSize();
3167 qreal highlightStart;
3169 if (d->isRightToLeft()) {
3170 viewPos = -d->position()-d->size();
3171 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3172 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3174 viewPos = d->position();
3175 highlightStart = d->highlightRangeStart;
3176 highlightEnd = d->highlightRangeEnd;
3178 qreal pos = viewPos;
3179 if (d->haveHighlightRange) {
3180 if (d->highlightRange == StrictlyEnforceRange) {
3181 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3182 pos = trackedPos - highlightEnd + d->trackedItem->size();
3183 if (trackedPos < pos + highlightStart)
3184 pos = trackedPos - highlightStart;
3186 if (trackedPos < d->startPosition() + highlightStart) {
3187 pos = d->startPosition();
3188 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3189 pos = d->endPosition() - d->size() + 1;
3190 if (pos < d->startPosition())
3191 pos = d->startPosition();
3193 if (trackedPos > pos + highlightEnd - trackedSize)
3194 pos = trackedPos - highlightEnd + trackedSize;
3195 if (trackedPos < pos + highlightStart)
3196 pos = trackedPos - highlightStart;
3200 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
3201 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
3202 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
3203 && d->currentItem->endPosition() >= viewPos + d->size()) {
3204 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
3205 pos = d->trackedItem->endPosition() - d->size() + 1;
3206 if (trackedSize > d->size())
3209 pos = d->currentItem->endPosition() - d->size() + 1;
3210 if (d->currentItem->size() > d->size())
3211 pos = d->currentItem->position();
3215 if (viewPos != pos) {
3217 d->calcVelocity = true;
3218 d->setPosition(pos);
3219 d->calcVelocity = false;
3224 void QDeclarative1ListView::itemsInserted(int modelIndex, int count)
3226 Q_D(QDeclarative1ListView);
3227 if (!isComponentComplete() || !d->model || !d->model->isValid())
3229 d->updateUnrequestedIndexes();
3230 d->moveReason = QDeclarative1ListViewPrivate::Other;
3232 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
3233 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
3236 int i = d->visibleItems.count() - 1;
3237 while (i > 0 && d->visibleItems.at(i)->index == -1)
3239 if (i == 0 && d->visibleItems.first()->index == -1) {
3240 // there are no visible items except items marked for removal
3241 index = d->visibleItems.count();
3242 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
3243 && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
3244 // Special case of appending an item to the model.
3245 index = d->visibleItems.count();
3247 if (modelIndex < d->visibleIndex) {
3248 // Insert before visible items
3249 d->visibleIndex += count;
3250 for (int i = 0; i < d->visibleItems.count(); ++i) {
3251 FxListItem1 *listItem = d->visibleItems.at(i);
3252 if (listItem->index != -1 && listItem->index >= modelIndex)
3253 listItem->index += count;
3256 if (d->currentIndex >= modelIndex) {
3257 // adjust current item index
3258 d->currentIndex += count;
3260 d->currentItem->index = d->currentIndex;
3261 emit currentIndexChanged();
3263 d->scheduleLayout();
3264 d->itemCount += count;
3265 emit countChanged();
3270 // index can be the next item past the end of the visible items list (i.e. appended)
3272 if (d->visibleItems.count()) {
3273 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
3274 : d->visibleItems.last()->endPosition()+d->spacing+1;
3275 } else if (d->itemCount == 0 && d->header) {
3276 pos = d->header->size();
3279 int initialPos = pos;
3281 QList<FxListItem1*> added;
3282 bool addedVisible = false;
3283 FxListItem1 *firstVisible = d->firstVisibleItem();
3284 if (firstVisible && pos < firstVisible->position()) {
3285 // Insert items before the visible item.
3286 int insertionIdx = index;
3288 int from = tempPos - d->buffer;
3289 for (i = count-1; i >= 0 && pos > from; --i) {
3290 if (!addedVisible) {
3291 d->scheduleLayout();
3292 addedVisible = true;
3294 FxListItem1 *item = d->createItem(modelIndex + i);
3295 d->visibleItems.insert(insertionIdx, item);
3296 pos -= item->size() + d->spacing;
3297 item->setPosition(pos);
3301 // If we didn't insert all our new items - anything
3302 // before the current index is not visible - remove it.
3303 while (insertionIdx--) {
3304 FxListItem1 *item = d->visibleItems.takeFirst();
3305 if (item->index != -1)
3307 d->releaseItem(item);
3310 // adjust pos of items before inserted items.
3311 for (int i = insertionIdx-1; i >= 0; i--) {
3312 FxListItem1 *listItem = d->visibleItems.at(i);
3313 listItem->setPosition(listItem->position() - (initialPos - pos));
3318 int to = d->buffer+tempPos+d->size();
3319 for (i = 0; i < count && pos <= to; ++i) {
3320 if (!addedVisible) {
3321 d->scheduleLayout();
3322 addedVisible = true;
3324 FxListItem1 *item = d->createItem(modelIndex + i);
3325 d->visibleItems.insert(index, item);
3326 item->setPosition(pos);
3328 pos += item->size() + d->spacing;
3332 // We didn't insert all our new items, which means anything
3333 // beyond the current index is not visible - remove it.
3334 while (d->visibleItems.count() > index)
3335 d->releaseItem(d->visibleItems.takeLast());
3337 diff = pos - initialPos;
3339 if (d->itemCount && d->currentIndex >= modelIndex) {
3340 // adjust current item index
3341 d->currentIndex += count;
3342 if (d->currentItem) {
3343 d->currentItem->index = d->currentIndex;
3344 d->currentItem->setPosition(d->currentItem->position() + diff);
3346 emit currentIndexChanged();
3347 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
3348 d->updateCurrent(0);
3350 // Update the indexes of the following visible items.
3351 for (; index < d->visibleItems.count(); ++index) {
3352 FxListItem1 *listItem = d->visibleItems.at(index);
3353 if (d->currentItem && listItem->item != d->currentItem->item)
3354 listItem->setPosition(listItem->position() + diff);
3355 if (listItem->index != -1)
3356 listItem->index += count;
3358 // everything is in order now - emit add() signal
3359 for (int j = 0; j < added.count(); ++j)
3360 added.at(j)->attached->emitAdd();
3362 d->updateSections();
3363 d->itemCount += count;
3364 emit countChanged();
3367 void QDeclarative1ListView::itemsRemoved(int modelIndex, int count)
3369 Q_D(QDeclarative1ListView);
3370 if (!isComponentComplete() || !d->model || !d->model->isValid())
3372 d->moveReason = QDeclarative1ListViewPrivate::Other;
3373 d->updateUnrequestedIndexes();
3374 d->itemCount -= count;
3376 FxListItem1 *firstVisible = d->firstVisibleItem();
3377 int preRemovedSize = 0;
3378 bool removedVisible = false;
3379 // Remove the items from the visible list, skipping anything already marked for removal
3380 QList<FxListItem1*>::Iterator it = d->visibleItems.begin();
3381 while (it != d->visibleItems.end()) {
3382 FxListItem1 *item = *it;
3383 if (item->index == -1 || item->index < modelIndex) {
3384 // already removed, or before removed items
3386 } else if (item->index >= modelIndex + count) {
3387 // after removed items
3388 item->index -= count;
3392 if (!removedVisible) {
3393 d->scheduleLayout();
3394 removedVisible = true;
3396 item->attached->emitRemove();
3397 if (item->attached->delayRemove()) {
3399 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
3402 if (item == firstVisible)
3404 if (firstVisible && item->position() < firstVisible->position())
3405 preRemovedSize += item->size();
3406 it = d->visibleItems.erase(it);
3407 d->releaseItem(item);
3412 if (firstVisible && d->visibleItems.first() != firstVisible)
3413 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
3415 // update visibleIndex
3416 bool haveVisibleIndex = false;
3417 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3418 if ((*it)->index != -1) {
3419 d->visibleIndex = (*it)->index;
3420 haveVisibleIndex = true;
3426 if (d->currentIndex >= modelIndex + count) {
3427 d->currentIndex -= count;
3429 d->currentItem->index -= count;
3430 emit currentIndexChanged();
3431 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
3432 // current item has been removed.
3433 d->currentItem->attached->setIsCurrentItem(false);
3434 d->releaseItem(d->currentItem);
3436 d->currentIndex = -1;
3438 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
3440 emit currentIndexChanged();
3443 if (!haveVisibleIndex) {
3444 d->timeline.clear();
3445 if (removedVisible && d->itemCount == 0) {
3446 d->visibleIndex = 0;
3447 d->visiblePos = d->header ? d->header->size() : 0;
3453 if (modelIndex < d->visibleIndex)
3454 d->visibleIndex = modelIndex+1;
3455 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3459 d->updateSections();
3460 emit countChanged();
3463 void QDeclarative1ListView::destroyRemoved()
3465 Q_D(QDeclarative1ListView);
3466 for (QList<FxListItem1*>::Iterator it = d->visibleItems.begin();
3467 it != d->visibleItems.end();) {
3468 FxListItem1 *listItem = *it;
3469 if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3470 d->releaseItem(listItem);
3471 it = d->visibleItems.erase(it);
3477 // Correct the positioning of the items
3478 d->updateSections();
3482 void QDeclarative1ListView::itemsMoved(int from, int to, int count)
3484 Q_D(QDeclarative1ListView);
3485 if (!isComponentComplete() || !d->isValid())
3487 d->updateUnrequestedIndexes();
3489 if (d->visibleItems.isEmpty()) {
3494 d->moveReason = QDeclarative1ListViewPrivate::Other;
3495 FxListItem1 *firstVisible = d->firstVisibleItem();
3496 qreal firstItemPos = firstVisible->position();
3497 QHash<int,FxListItem1*> moved;
3500 QList<FxListItem1*>::Iterator it = d->visibleItems.begin();
3501 while (it != d->visibleItems.end()) {
3502 FxListItem1 *item = *it;
3503 if (item->index >= from && item->index < from + count) {
3504 // take the items that are moving
3505 item->index += (to-from);
3506 moved.insert(item->index, item);
3507 if (item->position() < firstItemPos)
3508 moveBy += item->size();
3509 it = d->visibleItems.erase(it);
3511 // move everything after the moved items.
3512 if (item->index > from && item->index != -1)
3513 item->index -= count;
3518 int remaining = count;
3519 int endIndex = d->visibleIndex;
3520 it = d->visibleItems.begin();
3521 while (it != d->visibleItems.end()) {
3522 FxListItem1 *item = *it;
3523 if (remaining && item->index >= to && item->index < to + count) {
3524 // place items in the target position, reusing any existing items
3525 FxListItem1 *movedItem = moved.take(item->index);
3527 movedItem = d->createItem(item->index);
3528 if (item->index <= firstVisible->index)
3529 moveBy -= movedItem->size();
3530 it = d->visibleItems.insert(it, movedItem);
3534 if (item->index != -1) {
3535 if (item->index >= to) {
3536 // update everything after the moved items.
3537 item->index += count;
3539 endIndex = item->index;
3545 // If we have moved items to the end of the visible items
3546 // then add any existing moved items that we have
3547 while (FxListItem1 *item = moved.take(endIndex+1)) {
3548 d->visibleItems.append(item);
3552 // update visibleIndex
3553 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3554 if ((*it)->index != -1) {
3555 d->visibleIndex = (*it)->index;
3560 // Fix current index
3561 if (d->currentIndex >= 0 && d->currentItem) {
3562 int oldCurrent = d->currentIndex;
3563 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3564 if (oldCurrent != d->currentIndex) {
3565 d->currentItem->index = d->currentIndex;
3566 emit currentIndexChanged();
3570 // Whatever moved items remain are no longer visible items.
3571 while (moved.count()) {
3572 int idx = moved.begin().key();
3573 FxListItem1 *item = moved.take(idx);
3574 if (d->currentItem && item->item == d->currentItem->item)
3575 item->setPosition(d->positionAt(idx));
3576 d->releaseItem(item);
3579 // Ensure we don't cause an ugly list scroll.
3580 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3582 d->updateSections();
3586 void QDeclarative1ListView::itemsChanged(int, int)
3588 Q_D(QDeclarative1ListView);
3589 d->updateSections();
3593 void QDeclarative1ListView::modelReset()
3595 Q_D(QDeclarative1ListView);
3596 d->moveReason = QDeclarative1ListViewPrivate::SetIndex;
3598 if (d->highlight && d->currentItem) {
3599 if (d->autoHighlight)
3600 d->highlight->setPosition(d->currentItem->position());
3601 d->updateTrackedItem();
3603 d->moveReason = QDeclarative1ListViewPrivate::Other;
3604 emit countChanged();
3607 void QDeclarative1ListView::createdItem(int index, QDeclarativeItem *item)
3609 Q_D(QDeclarative1ListView);
3610 if (d->requestedIndex != index) {
3611 item->setParentItem(contentItem());
3612 d->unrequestedItems.insert(item, index);
3613 if (d->orient == QDeclarative1ListView::Vertical) {
3614 item->setY(d->positionAt(index));
3616 if (d->isRightToLeft())
3617 item->setX(-d->positionAt(index)-item->width());
3619 item->setX(d->positionAt(index));
3624 void QDeclarative1ListView::destroyingItem(QDeclarativeItem *item)
3626 Q_D(QDeclarative1ListView);
3627 d->unrequestedItems.remove(item);
3630 void QDeclarative1ListView::animStopped()
3632 Q_D(QDeclarative1ListView);
3633 d->bufferMode = QDeclarative1ListViewPrivate::NoBuffer;
3634 if (d->haveHighlightRange && d->highlightRange == QDeclarative1ListView::StrictlyEnforceRange)
3635 d->updateHighlight();
3638 QDeclarative1ListViewAttached *QDeclarative1ListView::qmlAttachedProperties(QObject *obj)
3640 return new QDeclarative1ListViewAttached(obj);