1 // Commit: ce38c6e3a9b7eb336cbd9cd1e9520a5000c8f8ac
2 /****************************************************************************
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
41 ****************************************************************************/
43 #include "qsglistview_p.h"
44 #include "qsgflickable_p_p.h"
45 #include "qsgvisualitemmodel_p.h"
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/qdeclarativeengine.h>
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtGui/qevent.h>
51 #include <QtCore/qmath.h>
52 #include <QtCore/qcoreapplication.h>
54 #include <private/qdeclarativesmoothedanimation_p_p.h>
55 #include <private/qlistmodelinterface_p.h>
59 void QSGViewSection::setProperty(const QString &property)
61 if (property != m_property) {
62 m_property = property;
63 emit propertyChanged();
67 void QSGViewSection::setCriteria(QSGViewSection::SectionCriteria criteria)
69 if (criteria != m_criteria) {
70 m_criteria = criteria;
71 emit criteriaChanged();
75 void QSGViewSection::setDelegate(QDeclarativeComponent *delegate)
77 if (delegate != m_delegate) {
78 m_delegate = delegate;
79 emit delegateChanged();
83 QString QSGViewSection::sectionString(const QString &value)
85 if (m_criteria == FirstCharacter)
86 return value.isEmpty() ? QString() : value.at(0);
91 //----------------------------------------------------------------------------
96 FxListItemSG(QSGItem *i, QSGListView *v) : item(i), section(0), view(v) {
97 attached = static_cast<QSGListViewAttached*>(qmlAttachedPropertiesObject<QSGListView>(item));
99 attached->setView(view);
102 qreal position() const {
104 if (view->orientation() == QSGListView::Vertical)
107 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
109 return itemPosition();
112 qreal itemPosition() const {
113 if (view->orientation() == QSGListView::Vertical)
116 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
120 return (view->orientation() == QSGListView::Vertical ? item->height()+section->height() : item->width()+section->width());
122 return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
124 qreal itemSize() const {
125 return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
127 qreal sectionSize() const {
129 return (view->orientation() == QSGListView::Vertical ? section->height() : section->width());
132 qreal endPosition() const {
133 if (view->orientation() == QSGListView::Vertical) {
134 return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
136 return (view->effectiveLayoutDirection() == Qt::RightToLeft
137 ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
138 : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
141 void setPosition(qreal pos) {
142 if (view->orientation() == QSGListView::Vertical) {
145 pos += section->height();
149 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
151 section->setX(-section->width()-pos);
152 pos += section->width();
154 item->setX(-item->width()-pos);
158 pos += section->width();
164 void setSize(qreal size) {
165 if (view->orientation() == QSGListView::Vertical)
166 item->setHeight(size);
168 item->setWidth(size);
170 bool contains(qreal x, qreal y) const {
171 return (x >= item->x() && x < item->x() + item->width() &&
172 y >= item->y() && y < item->y() + item->height());
178 QSGListViewAttached *attached;
182 //----------------------------------------------------------------------------
184 class QSGListViewPrivate : public QSGFlickablePrivate
186 Q_DECLARE_PUBLIC(QSGListView)
190 : currentItem(0), orient(QSGListView::Vertical), layoutDirection(Qt::LeftToRight)
191 , visiblePos(0), visibleIndex(0)
192 , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
193 , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
194 , highlightComponent(0), highlight(0), trackedItem(0)
195 , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
196 , sectionCriteria(0), spacing(0.0)
197 , highlightMoveSpeed(400), highlightMoveDuration(-1)
198 , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QSGListView::NoHighlightRange)
199 , snapMode(QSGListView::NoSnap), overshootDist(0.0)
200 , footerComponent(0), footer(0), headerComponent(0), header(0)
201 , bufferMode(BufferBefore | BufferAfter)
202 , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
203 , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
204 , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
205 , inViewportMoved(false)
206 , highlightRangeStartValid(false), highlightRangeEndValid(false)
207 , minExtentDirty(true), maxExtentDirty(true)
212 FxListItemSG *createItem(int modelIndex);
213 void releaseItem(FxListItemSG *item);
215 FxListItemSG *visibleItem(int modelIndex) const {
216 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
217 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
218 FxListItemSG *item = visibleItems.at(i);
219 if (item->index == modelIndex)
226 FxListItemSG *firstVisibleItem() const {
227 const qreal pos = isRightToLeft() ? -position()-size() : position();
228 for (int i = 0; i < visibleItems.count(); ++i) {
229 FxListItemSG *item = visibleItems.at(i);
230 if (item->index != -1 && item->endPosition() > pos)
233 return visibleItems.count() ? visibleItems.first() : 0;
236 FxListItemSG *nextVisibleItem() const {
237 const qreal pos = isRightToLeft() ? -position()-size() : position();
238 bool foundFirst = false;
239 for (int i = 0; i < visibleItems.count(); ++i) {
240 FxListItemSG *item = visibleItems.at(i);
241 if (item->index != -1) {
244 else if (item->position() < pos && item->endPosition() > pos)
251 // Returns the item before modelIndex, if created.
252 // May return an item marked for removal.
253 FxListItemSG *itemBefore(int modelIndex) const {
254 if (modelIndex < visibleIndex)
258 while (idx < visibleItems.count()) {
259 FxListItemSG *item = visibleItems.at(idx);
260 if (item->index != -1)
261 lastIndex = item->index;
262 if (item->index == modelIndex)
263 return visibleItems.at(idx-1);
266 if (lastIndex == modelIndex-1)
267 return visibleItems.last();
273 if (q->isComponentComplete()) {
275 // XXX todo - the original did scene()->removeItem(). Why?
276 header->item->setParentItem(0);
277 header->item->deleteLater();
282 // XXX todo - the original did scene()->removeItem(). Why?
283 footer->item->setParentItem(0);
284 footer->item->deleteLater();
293 updateCurrent(currentIndex);
297 void mirrorChange() {
300 emit q->effectiveLayoutDirectionChanged();
303 bool isRightToLeft() const {
304 Q_Q(const QSGListView);
305 return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
308 qreal position() const {
309 Q_Q(const QSGListView);
310 return orient == QSGListView::Vertical ? q->contentY() : q->contentX();
312 void setPosition(qreal pos) {
314 if (orient == QSGListView::Vertical) {
315 q->QSGFlickable::setContentY(pos);
318 q->QSGFlickable::setContentX(-pos-size());
320 q->QSGFlickable::setContentX(pos);
324 Q_Q(const QSGListView);
325 return orient == QSGListView::Vertical ? q->height() : q->width();
328 qreal originPosition() const {
330 if (!visibleItems.isEmpty()) {
331 pos = (*visibleItems.constBegin())->position();
332 if (visibleIndex > 0)
333 pos -= visibleIndex * (averageSize + spacing);
338 qreal lastPosition() const {
340 if (!visibleItems.isEmpty()) {
341 int invisibleCount = visibleItems.count() - visibleIndex;
342 for (int i = visibleItems.count()-1; i >= 0; --i) {
343 if (visibleItems.at(i)->index != -1) {
344 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
348 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
349 } else if (model && model->count()) {
350 pos = model->count() * averageSize + (model->count()-1) * spacing;
355 qreal startPosition() const {
356 return isRightToLeft() ? -lastPosition()-1 : originPosition();
359 qreal endPosition() const {
360 return isRightToLeft() ? -originPosition()-1 : lastPosition();
363 qreal positionAt(int modelIndex) const {
364 if (FxListItemSG *item = visibleItem(modelIndex))
365 return item->position();
366 if (!visibleItems.isEmpty()) {
367 if (modelIndex < visibleIndex) {
368 int count = visibleIndex - modelIndex;
370 if (modelIndex == currentIndex && currentItem) {
371 cs = currentItem->size() + spacing;
374 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
376 int idx = visibleItems.count() - 1;
377 while (idx >= 0 && visibleItems.at(idx)->index == -1)
382 idx = visibleItems.at(idx)->index;
383 int count = modelIndex - idx - 1;
384 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
390 qreal endPositionAt(int modelIndex) const {
391 if (FxListItemSG *item = visibleItem(modelIndex))
392 return item->endPosition();
393 if (!visibleItems.isEmpty()) {
394 if (modelIndex < visibleIndex) {
395 int count = visibleIndex - modelIndex;
396 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
398 int idx = visibleItems.count() - 1;
399 while (idx >= 0 && visibleItems.at(idx)->index == -1)
404 idx = visibleItems.at(idx)->index;
405 int count = modelIndex - idx - 1;
406 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
412 QString sectionAt(int modelIndex) {
413 if (FxListItemSG *item = visibleItem(modelIndex))
414 return item->attached->section();
417 if (sectionCriteria) {
418 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
419 section = sectionCriteria->sectionString(propValue);
425 bool isValid() const {
426 return model && model->count() && model->isValid();
429 qreal snapPosAt(qreal pos) {
430 if (FxListItemSG *snapItem = snapItemAt(pos))
431 return snapItem->position();
432 if (visibleItems.count()) {
433 qreal firstPos = visibleItems.first()->position();
434 qreal endPos = visibleItems.last()->position();
435 if (pos < firstPos) {
436 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
437 } else if (pos > endPos)
438 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
440 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
443 FxListItemSG *snapItemAt(qreal pos) {
444 FxListItemSG *snapItem = 0;
445 for (int i = 0; i < visibleItems.count(); ++i) {
446 FxListItemSG *item = visibleItems[i];
447 if (item->index == -1)
449 qreal itemTop = item->position();
450 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
452 if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
458 int lastVisibleIndex() const {
460 for (int i = visibleItems.count()-1; i >= 0; --i) {
461 FxListItemSG *listItem = visibleItems.at(i);
462 if (listItem->index != -1) {
463 lastIndex = listItem->index;
470 // map a model index to visibleItems index.
471 int mapFromModel(int modelIndex) const {
472 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
474 for (int i = 0; i < visibleItems.count(); ++i) {
475 FxListItemSG *listItem = visibleItems.at(i);
476 if (listItem->index == modelIndex)
478 if (listItem->index > modelIndex)
481 return -1; // Not in visibleList
484 void updateViewport() {
486 if (orient == QSGListView::Vertical) {
487 q->setContentHeight(endPosition() - startPosition() + 1);
489 q->setContentWidth(endPosition() - startPosition() + 1);
493 void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
495 QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
496 if (!q->isComponentComplete())
498 if (item != contentItem && (!highlight || item != highlight->item)) {
499 if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height())
500 || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
504 if ((header && header->item == item) || (footer && footer->item == item)) {
510 if (currentItem && currentItem->item == item)
512 if (trackedItem && trackedItem->item == item)
513 q->trackedPositionChanged();
516 // for debugging only
517 void checkVisible() const {
519 for (int i = 0; i < visibleItems.count(); ++i) {
520 FxListItemSG *listItem = visibleItems.at(i);
521 if (listItem->index == -1) {
523 } else if (listItem->index != visibleIndex + i - skip) {
524 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
529 void refill(qreal from, qreal to, bool doBuffer = false);
530 void scheduleLayout();
532 void updateUnrequestedIndexes();
533 void updateUnrequestedPositions();
534 void updateTrackedItem();
535 void createHighlight();
536 void updateHighlight();
537 void createSection(FxListItemSG *);
538 void updateSections();
539 void updateCurrentSection();
540 void updateCurrent(int);
541 void updateAverage();
544 void fixupPosition();
545 void positionViewAtIndex(int index, int mode);
546 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
547 virtual void flick(QSGFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
548 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
550 QDeclarativeGuard<QSGVisualModel> model;
551 QVariant modelVariant;
552 QList<FxListItemSG*> visibleItems;
553 QHash<QSGItem*,int> unrequestedItems;
554 FxListItemSG *currentItem;
555 QSGListView::Orientation orient;
556 Qt::LayoutDirection layoutDirection;
563 qreal highlightRangeStart;
564 qreal highlightRangeEnd;
565 QDeclarativeComponent *highlightComponent;
566 FxListItemSG *highlight;
567 FxListItemSG *trackedItem;
568 enum MovementReason { Other, SetIndex, Mouse };
569 MovementReason moveReason;
571 QSmoothedAnimation *highlightPosAnimator;
572 QSmoothedAnimation *highlightSizeAnimator;
573 QSGViewSection *sectionCriteria;
574 QString currentSection;
575 static const int sectionCacheSize = 4;
576 QSGItem *sectionCache[sectionCacheSize];
578 qreal highlightMoveSpeed;
579 int highlightMoveDuration;
580 qreal highlightResizeSpeed;
581 int highlightResizeDuration;
582 QSGListView::HighlightRangeMode highlightRange;
583 QSGListView::SnapMode snapMode;
585 QDeclarativeComponent *footerComponent;
586 FxListItemSG *footer;
587 QDeclarativeComponent *headerComponent;
588 FxListItemSG *header;
589 enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
591 mutable qreal minExtent;
592 mutable qreal maxExtent;
596 bool autoHighlight : 1;
597 bool haveHighlightRange : 1;
598 bool correctFlick : 1;
599 bool inFlickCorrection : 1;
600 bool lazyRelease : 1;
601 bool deferredRelease : 1;
602 bool layoutScheduled : 1;
603 bool currentIndexCleared : 1;
604 bool inViewportMoved : 1;
605 bool highlightRangeStartValid : 1;
606 bool highlightRangeEndValid : 1;
607 mutable bool minExtentDirty : 1;
608 mutable bool maxExtentDirty : 1;
611 void QSGListViewPrivate::init()
614 q->setFlag(QSGItem::ItemIsFocusScope);
615 addItemChangeListener(this, Geometry);
616 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
617 q->setFlickableDirection(QSGFlickable::VerticalFlick);
618 ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
621 void QSGListViewPrivate::clear()
624 for (int i = 0; i < visibleItems.count(); ++i)
625 releaseItem(visibleItems.at(i));
626 visibleItems.clear();
627 for (int i = 0; i < sectionCacheSize; ++i) {
628 delete sectionCache[i];
631 visiblePos = header ? header->size() : 0;
633 releaseItem(currentItem);
637 minExtentDirty = true;
638 maxExtentDirty = true;
642 FxListItemSG *QSGListViewPrivate::createItem(int modelIndex)
646 requestedIndex = modelIndex;
647 FxListItemSG *listItem = 0;
648 if (QSGItem *item = model->item(modelIndex, false)) {
649 listItem = new FxListItemSG(item, q);
650 listItem->index = modelIndex;
651 // initialise attached properties
652 if (sectionCriteria) {
653 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
654 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
655 if (modelIndex > 0) {
656 if (FxListItemSG *item = itemBefore(modelIndex))
657 listItem->attached->m_prevSection = item->attached->section();
659 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
661 if (modelIndex < model->count()-1) {
662 if (FxListItemSG *item = visibleItem(modelIndex+1))
663 listItem->attached->m_nextSection = item->attached->section();
665 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
668 if (model->completePending()) {
670 listItem->item->setZ(1);
671 listItem->item->setParentItem(q->contentItem());
672 model->completeItem();
674 listItem->item->setParentItem(q->contentItem());
676 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
677 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
678 if (sectionCriteria && sectionCriteria->delegate()) {
679 if (listItem->attached->m_prevSection != listItem->attached->m_section)
680 createSection(listItem);
682 unrequestedItems.remove(listItem->item);
689 void QSGListViewPrivate::releaseItem(FxListItemSG *item)
694 if (trackedItem == item)
696 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
697 itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
698 if (model->release(item->item) == 0) {
699 // item was not destroyed, and we no longer reference it.
700 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
705 if (!sectionCache[i]) {
706 sectionCache[i] = item->section;
707 sectionCache[i]->setVisible(false);
712 } while (i < sectionCacheSize);
713 delete item->section;
718 void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
721 if (!isValid() || !q->isComponentComplete())
723 itemCount = model->count();
724 qreal bufferFrom = from - buffer;
725 qreal bufferTo = to + buffer;
726 qreal fillFrom = from;
728 if (doBuffer && (bufferMode & BufferAfter))
730 if (doBuffer && (bufferMode & BufferBefore))
731 fillFrom = bufferFrom;
733 int modelIndex = visibleIndex;
734 qreal itemEnd = visiblePos-1;
735 if (!visibleItems.isEmpty()) {
736 visiblePos = (*visibleItems.constBegin())->position();
737 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
738 int i = visibleItems.count() - 1;
739 while (i > 0 && visibleItems.at(i)->index == -1)
741 if (visibleItems.at(i)->index != -1)
742 modelIndex = visibleItems.at(i)->index + 1;
745 bool changed = false;
746 FxListItemSG *item = 0;
747 qreal pos = itemEnd + 1;
748 while (modelIndex < model->count() && pos <= fillTo) {
749 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
750 if (!(item = createItem(modelIndex)))
752 item->setPosition(pos);
753 pos += item->size() + spacing;
754 visibleItems.append(item);
757 if (doBuffer) // never buffer more than one item per frame
760 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
761 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
762 if (!(item = createItem(visibleIndex-1)))
765 visiblePos -= item->size() + spacing;
766 item->setPosition(visiblePos);
767 visibleItems.prepend(item);
769 if (doBuffer) // never buffer more than one item per frame
773 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
774 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
775 if (item->attached->delayRemove())
777 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
778 if (item->index != -1)
780 visibleItems.removeFirst();
784 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
785 if (item->attached->delayRemove())
787 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
788 visibleItems.removeLast();
792 deferredRelease = false;
794 deferredRelease = true;
797 minExtentDirty = true;
798 maxExtentDirty = true;
799 if (visibleItems.count())
800 visiblePos = (*visibleItems.constBegin())->position();
802 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
803 currentItem->setPosition(positionAt(currentIndex));
808 updateCurrentSection();
814 updateUnrequestedPositions();
815 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
816 refill(from, to, true);
821 void QSGListViewPrivate::scheduleLayout()
824 if (!layoutScheduled) {
825 layoutScheduled = true;
830 void QSGListViewPrivate::layout()
833 layoutScheduled = false;
834 if (!isValid() && !visibleItems.count()) {
839 if (!visibleItems.isEmpty()) {
840 bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
841 qreal sum = visibleItems.first()->size();
842 qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
843 for (int i=1; i < visibleItems.count(); ++i) {
844 FxListItemSG *item = visibleItems.at(i);
845 item->setPosition(pos);
846 pos += item->size() + spacing;
848 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
850 averageSize = qRound(sum / visibleItems.count());
851 // move current item if it is not a visible item.
852 if (currentIndex >= 0 && currentItem && !fixedCurrent)
853 currentItem->setPosition(positionAt(currentIndex));
856 minExtentDirty = true;
857 maxExtentDirty = true;
859 if (!q->isMoving() && !q->isFlicking()) {
870 void QSGListViewPrivate::updateUnrequestedIndexes()
873 QHash<QSGItem*,int>::iterator it;
874 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
875 *it = model->indexOf(it.key(), q);
878 void QSGListViewPrivate::updateUnrequestedPositions()
881 if (unrequestedItems.count()) {
882 qreal pos = position();
883 QHash<QSGItem*,int>::const_iterator it;
884 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
885 QSGItem *item = it.key();
886 if (orient == QSGListView::Vertical) {
887 if (item->y() + item->height() > pos && item->y() < pos + q->height())
888 item->setY(positionAt(*it));
890 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
892 item->setX(-positionAt(*it)-item->width());
894 item->setX(positionAt(*it));
901 void QSGListViewPrivate::updateTrackedItem()
904 FxListItemSG *item = currentItem;
909 q->trackedPositionChanged();
912 void QSGListViewPrivate::createHighlight()
915 bool changed = false;
917 if (trackedItem == highlight)
919 delete highlight->item;
922 delete highlightPosAnimator;
923 delete highlightSizeAnimator;
924 highlightPosAnimator = 0;
925 highlightSizeAnimator = 0;
931 if (highlightComponent) {
932 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
933 QObject *nobj = highlightComponent->create(highlightContext);
935 QDeclarative_setParent_noEvent(highlightContext, nobj);
936 item = qobject_cast<QSGItem *>(nobj);
940 delete highlightContext;
946 QDeclarative_setParent_noEvent(item, q->contentItem());
947 item->setParentItem(q->contentItem());
948 highlight = new FxListItemSG(item, q);
949 if (currentItem && autoHighlight) {
950 if (orient == QSGListView::Vertical) {
951 highlight->item->setHeight(currentItem->item->height());
953 highlight->item->setWidth(currentItem->item->width());
955 highlight->setPosition(currentItem->itemPosition());
957 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
958 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
959 const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x");
960 highlightPosAnimator = new QSmoothedAnimation(q);
961 highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
962 highlightPosAnimator->velocity = highlightMoveSpeed;
963 highlightPosAnimator->userDuration = highlightMoveDuration;
964 const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width");
965 highlightSizeAnimator = new QSmoothedAnimation(q);
966 highlightSizeAnimator->velocity = highlightResizeSpeed;
967 highlightSizeAnimator->userDuration = highlightResizeDuration;
968 highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
970 highlightPosAnimator->restart();
971 highlightSizeAnimator->restart();
977 emit q->highlightItemChanged();
980 void QSGListViewPrivate::updateHighlight()
982 if ((!currentItem && highlight) || (currentItem && !highlight))
984 if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
985 // auto-update highlight
986 highlightPosAnimator->to = isRightToLeft()
987 ? -currentItem->itemPosition()-currentItem->itemSize()
988 : currentItem->itemPosition();
989 highlightSizeAnimator->to = currentItem->itemSize();
990 if (orient == QSGListView::Vertical) {
991 if (highlight->item->width() == 0)
992 highlight->item->setWidth(currentItem->item->width());
994 if (highlight->item->height() == 0)
995 highlight->item->setHeight(currentItem->item->height());
997 highlightPosAnimator->restart();
998 highlightSizeAnimator->restart();
1000 updateTrackedItem();
1003 void QSGListViewPrivate::createSection(FxListItemSG *listItem)
1006 if (!sectionCriteria || !sectionCriteria->delegate())
1008 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
1009 if (!listItem->section) {
1010 qreal pos = listItem->position();
1011 int i = sectionCacheSize-1;
1012 while (i >= 0 && !sectionCache[i])
1015 listItem->section = sectionCache[i];
1016 sectionCache[i] = 0;
1017 listItem->section->setVisible(true);
1018 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1019 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1021 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1022 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1023 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
1025 QDeclarative_setParent_noEvent(context, nobj);
1026 listItem->section = qobject_cast<QSGItem *>(nobj);
1027 if (!listItem->section) {
1030 listItem->section->setZ(1);
1031 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
1032 listItem->section->setParentItem(q->contentItem());
1037 sectionCriteria->delegate()->completeCreate();
1039 listItem->setPosition(pos);
1041 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1042 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1044 } else if (listItem->section) {
1045 qreal pos = listItem->position();
1048 if (!sectionCache[i]) {
1049 sectionCache[i] = listItem->section;
1050 sectionCache[i]->setVisible(false);
1051 listItem->section = 0;
1055 } while (i < sectionCacheSize);
1056 delete listItem->section;
1057 listItem->section = 0;
1058 listItem->setPosition(pos);
1062 void QSGListViewPrivate::updateSections()
1064 if (sectionCriteria && !visibleItems.isEmpty()) {
1065 QString prevSection;
1066 if (visibleIndex > 0)
1067 prevSection = sectionAt(visibleIndex-1);
1068 QSGListViewAttached *prevAtt = 0;
1070 for (int i = 0; i < visibleItems.count(); ++i) {
1071 QSGListViewAttached *attached = visibleItems.at(i)->attached;
1072 attached->setPrevSection(prevSection);
1073 if (visibleItems.at(i)->index != -1) {
1074 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1075 attached->setSection(sectionCriteria->sectionString(propValue));
1076 idx = visibleItems.at(i)->index;
1078 createSection(visibleItems.at(i));
1080 prevAtt->setNextSection(attached->section());
1081 prevSection = attached->section();
1085 if (idx > 0 && idx < model->count()-1)
1086 prevAtt->setNextSection(sectionAt(idx+1));
1088 prevAtt->setNextSection(QString());
1093 void QSGListViewPrivate::updateCurrentSection()
1096 if (!sectionCriteria || visibleItems.isEmpty()) {
1097 if (!currentSection.isEmpty()) {
1098 currentSection.clear();
1099 emit q->currentSectionChanged();
1104 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1107 QString newSection = currentSection;
1108 if (index < visibleItems.count())
1109 newSection = visibleItems.at(index)->attached->section();
1111 newSection = visibleItems.first()->attached->section();
1112 if (newSection != currentSection) {
1113 currentSection = newSection;
1114 emit q->currentSectionChanged();
1118 void QSGListViewPrivate::updateCurrent(int modelIndex)
1121 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1123 currentItem->attached->setIsCurrentItem(false);
1124 releaseItem(currentItem);
1126 currentIndex = modelIndex;
1127 emit q->currentIndexChanged();
1129 } else if (currentIndex != modelIndex) {
1130 currentIndex = modelIndex;
1131 emit q->currentIndexChanged();
1136 if (currentItem && currentIndex == modelIndex) {
1140 FxListItemSG *oldCurrentItem = currentItem;
1141 currentIndex = modelIndex;
1142 currentItem = createItem(modelIndex);
1143 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1144 oldCurrentItem->attached->setIsCurrentItem(false);
1146 if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1147 // We can calculate exact postion in this case
1148 currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1150 // Create current item now and position as best we can.
1151 // Its position will be corrected when it becomes visible.
1152 currentItem->setPosition(positionAt(modelIndex));
1154 currentItem->item->setFocus(true);
1155 currentItem->attached->setIsCurrentItem(true);
1156 // Avoid showing section delegate twice. We still need the section heading so that
1157 // currentItem positioning works correctly.
1158 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1159 if (currentItem->section)
1160 currentItem->section->setVisible(false);
1161 if (visibleItems.isEmpty())
1162 averageSize = currentItem->size();
1165 emit q->currentIndexChanged();
1166 // Release the old current item
1167 releaseItem(oldCurrentItem);
1170 void QSGListViewPrivate::updateAverage()
1172 if (!visibleItems.count())
1175 for (int i = 0; i < visibleItems.count(); ++i)
1176 sum += visibleItems.at(i)->size();
1177 averageSize = qRound(sum / visibleItems.count());
1180 void QSGListViewPrivate::updateFooter()
1183 if (!footer && footerComponent) {
1185 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1186 QObject *nobj = footerComponent->create(context);
1188 QDeclarative_setParent_noEvent(context, nobj);
1189 item = qobject_cast<QSGItem *>(nobj);
1196 QDeclarative_setParent_noEvent(item, q->contentItem());
1197 item->setParentItem(q->contentItem());
1199 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1200 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1201 footer = new FxListItemSG(item, q);
1205 if (visibleItems.count()) {
1206 qreal endPos = lastPosition() + 1;
1207 if (lastVisibleIndex() == model->count()-1) {
1208 footer->setPosition(endPos);
1210 qreal visiblePos = position() + q->height();
1211 if (endPos <= visiblePos || footer->position() < endPos)
1212 footer->setPosition(endPos);
1215 footer->setPosition(visiblePos);
1220 void QSGListViewPrivate::updateHeader()
1223 if (!header && headerComponent) {
1225 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1226 QObject *nobj = headerComponent->create(context);
1228 QDeclarative_setParent_noEvent(context, nobj);
1229 item = qobject_cast<QSGItem *>(nobj);
1236 QDeclarative_setParent_noEvent(item, q->contentItem());
1237 item->setParentItem(q->contentItem());
1239 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1240 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1241 header = new FxListItemSG(item, q);
1245 if (visibleItems.count()) {
1246 qreal startPos = originPosition();
1247 if (visibleIndex == 0) {
1248 header->setPosition(startPos - header->size());
1250 if (position() <= startPos || header->position() > startPos - header->size())
1251 header->setPosition(startPos - header->size());
1255 visiblePos = header->size();
1256 header->setPosition(0);
1261 void QSGListViewPrivate::fixupPosition()
1263 if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange)
1264 || snapMode != QSGListView::NoSnap)
1266 if (orient == QSGListView::Vertical)
1272 void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1274 if ((orient == QSGListView::Horizontal && &data == &vData)
1275 || (orient == QSGListView::Vertical && &data == &hData))
1278 correctFlick = false;
1279 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1281 qreal highlightStart;
1284 if (isRightToLeft()) {
1285 // Handle Right-To-Left exceptions
1286 viewPos = -position()-size();
1287 highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1288 highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1290 viewPos = position();
1291 highlightStart = highlightRangeStart;
1292 highlightEnd = highlightRangeEnd;
1295 if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange
1296 && moveReason != QSGListViewPrivate::SetIndex) {
1298 qreal pos = currentItem->itemPosition();
1299 if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1300 viewPos = pos + currentItem->itemSize() - highlightEnd;
1301 if (viewPos > pos - highlightStart)
1302 viewPos = pos - highlightStart;
1303 if (isRightToLeft())
1304 viewPos = -viewPos-size();
1306 timeline.reset(data.move);
1307 if (viewPos != position()) {
1308 if (fixupMode != Immediate) {
1309 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1310 data.fixingUp = true;
1312 timeline.set(data.move, -viewPos);
1315 vTime = timeline.time();
1316 } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) {
1317 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1318 FxListItemSG *topItem = snapItemAt(tempPosition+highlightStart);
1319 FxListItemSG *bottomItem = snapItemAt(tempPosition+highlightEnd);
1321 bool isInBounds = -position() > maxExtent && -position() < minExtent;
1322 if (topItem && isInBounds) {
1323 if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2) {
1324 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1326 if (isRightToLeft())
1327 pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1329 pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1331 } else if (bottomItem && isInBounds) {
1332 if (isRightToLeft())
1333 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1335 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1337 QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
1341 qreal dist = qAbs(data.move + pos);
1343 timeline.reset(data.move);
1344 if (fixupMode != Immediate) {
1345 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1346 data.fixingUp = true;
1348 timeline.set(data.move, -pos);
1350 vTime = timeline.time();
1353 QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
1358 void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1359 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1363 data.fixingUp = false;
1365 if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) {
1366 correctFlick = true;
1367 QSGFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1370 qreal maxDistance = 0;
1371 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1372 // -ve velocity means list is moving up/left
1374 if (data.move.value() < minExtent) {
1375 if (snapMode == QSGListView::SnapOneItem) {
1376 if (FxListItemSG *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1377 maxDistance = qAbs(item->position() + dataValue);
1379 maxDistance = qAbs(minExtent - data.move.value());
1382 if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1383 data.flickTarget = minExtent;
1385 if (data.move.value() > maxExtent) {
1386 if (snapMode == QSGListView::SnapOneItem) {
1387 if (FxListItemSG *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1388 maxDistance = qAbs(item->position() + dataValue);
1390 maxDistance = qAbs(maxExtent - data.move.value());
1393 if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1394 data.flickTarget = maxExtent;
1396 bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
1397 qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1398 if (maxDistance > 0 || overShoot) {
1399 // These modes require the list to stop exactly on an item boundary.
1400 // The initial flick will estimate the boundary to stop on.
1401 // Since list items can have variable sizes, the boundary will be
1402 // reevaluated and adjusted as we approach the boundary.
1404 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1410 if (!flickingHorizontally && !flickingVertically) {
1411 // the initial flick - estimate boundary
1412 qreal accel = deceleration;
1414 overshootDist = 0.0;
1415 // + averageSize/4 to encourage moving at least one item in the flick direction
1416 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1417 if (maxDistance > 0)
1418 dist = qMin(dist, maxDistance);
1421 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) {
1422 qreal distTemp = isRightToLeft() ? -dist : dist;
1423 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1424 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1426 if (data.flickTarget >= minExtent) {
1427 overshootDist = overShootDistance(v, vSize);
1428 data.flickTarget += overshootDist;
1429 } else if (data.flickTarget <= maxExtent) {
1430 overshootDist = overShootDistance(v, vSize);
1431 data.flickTarget -= overshootDist;
1434 qreal adjDist = -data.flickTarget + data.move.value();
1435 if (qAbs(adjDist) > qAbs(dist)) {
1436 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1437 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1446 accel = v2 / (2.0f * qAbs(dist));
1447 } else if (overShoot) {
1448 data.flickTarget = data.move.value() - dist;
1449 if (data.flickTarget >= minExtent) {
1450 overshootDist = overShootDistance(v, vSize);
1451 data.flickTarget += overshootDist;
1452 } else if (data.flickTarget <= maxExtent) {
1453 overshootDist = overShootDistance(v, vSize);
1454 data.flickTarget -= overshootDist;
1457 timeline.reset(data.move);
1458 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1459 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1460 if (!flickingHorizontally && q->xflick()) {
1461 flickingHorizontally = true;
1462 emit q->flickingChanged();
1463 emit q->flickingHorizontallyChanged();
1464 emit q->flickStarted();
1466 if (!flickingVertically && q->yflick()) {
1467 flickingVertically = true;
1468 emit q->flickingChanged();
1469 emit q->flickingVerticallyChanged();
1470 emit q->flickStarted();
1472 correctFlick = true;
1474 // reevaluate the target boundary.
1475 qreal newtarget = data.flickTarget;
1476 if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) {
1477 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1478 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1479 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1481 if (velocity < 0 && newtarget <= maxExtent)
1482 newtarget = maxExtent - overshootDist;
1483 else if (velocity > 0 && newtarget >= minExtent)
1484 newtarget = minExtent + overshootDist;
1485 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1486 if (qAbs(velocity) < MinimumFlickVelocity)
1487 correctFlick = false;
1490 data.flickTarget = newtarget;
1491 qreal dist = -newtarget + data.move.value();
1492 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1493 correctFlick = false;
1494 timeline.reset(data.move);
1495 fixup(data, minExtent, maxExtent);
1498 timeline.reset(data.move);
1499 timeline.accelDistance(data.move, v, -dist);
1500 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1503 correctFlick = false;
1504 timeline.reset(data.move);
1505 fixup(data, minExtent, maxExtent);
1509 //----------------------------------------------------------------------------
1511 QSGListView::QSGListView(QSGItem *parent)
1512 : QSGFlickable(*(new QSGListViewPrivate), parent)
1518 QSGListView::~QSGListView()
1528 QVariant QSGListView::model() const
1530 Q_D(const QSGListView);
1531 return d->modelVariant;
1534 void QSGListView::setModel(const QVariant &model)
1537 if (d->modelVariant == model)
1540 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1541 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1542 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1543 disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1544 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1545 disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
1546 disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
1549 QSGVisualModel *oldModel = d->model;
1552 d->modelVariant = model;
1553 QObject *object = qvariant_cast<QObject*>(model);
1554 QSGVisualModel *vim = 0;
1555 if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
1558 d->ownModel = false;
1563 d->model = new QSGVisualDataModel(qmlContext(this), this);
1566 d->model = oldModel;
1568 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
1569 dataModel->setModel(model);
1572 d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter;
1573 if (isComponentComplete()) {
1576 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1579 d->moveReason = QSGListViewPrivate::SetIndex;
1580 d->updateCurrent(d->currentIndex);
1581 if (d->highlight && d->currentItem) {
1582 if (d->autoHighlight)
1583 d->highlight->setPosition(d->currentItem->position());
1584 d->updateTrackedItem();
1587 d->updateViewport();
1589 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1590 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1591 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1592 connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1593 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1594 connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
1595 connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
1596 emit countChanged();
1598 emit modelChanged();
1601 QDeclarativeComponent *QSGListView::delegate() const
1603 Q_D(const QSGListView);
1605 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
1606 return dataModel->delegate();
1612 void QSGListView::setDelegate(QDeclarativeComponent *delegate)
1615 if (delegate == this->delegate())
1618 d->model = new QSGVisualDataModel(qmlContext(this));
1621 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
1622 dataModel->setDelegate(delegate);
1623 if (isComponentComplete()) {
1624 for (int i = 0; i < d->visibleItems.count(); ++i)
1625 d->releaseItem(d->visibleItems.at(i));
1626 d->visibleItems.clear();
1627 d->releaseItem(d->currentItem);
1631 d->moveReason = QSGListViewPrivate::SetIndex;
1632 d->updateCurrent(d->currentIndex);
1633 if (d->highlight && d->currentItem) {
1634 if (d->autoHighlight)
1635 d->highlight->setPosition(d->currentItem->position());
1636 d->updateTrackedItem();
1638 d->updateViewport();
1641 emit delegateChanged();
1644 int QSGListView::currentIndex() const
1646 Q_D(const QSGListView);
1647 return d->currentIndex;
1650 void QSGListView::setCurrentIndex(int index)
1653 if (d->requestedIndex >= 0) // currently creating item
1655 d->currentIndexCleared = (index == -1);
1656 if (index == d->currentIndex)
1658 if (isComponentComplete() && d->isValid()) {
1659 d->moveReason = QSGListViewPrivate::SetIndex;
1660 d->updateCurrent(index);
1661 } else if (d->currentIndex != index) {
1662 d->currentIndex = index;
1663 emit currentIndexChanged();
1667 QSGItem *QSGListView::currentItem()
1670 if (!d->currentItem)
1672 return d->currentItem->item;
1675 QSGItem *QSGListView::highlightItem()
1680 return d->highlight->item;
1683 int QSGListView::count() const
1685 Q_D(const QSGListView);
1687 return d->model->count();
1691 QDeclarativeComponent *QSGListView::highlight() const
1693 Q_D(const QSGListView);
1694 return d->highlightComponent;
1697 void QSGListView::setHighlight(QDeclarativeComponent *highlight)
1700 if (highlight != d->highlightComponent) {
1701 d->highlightComponent = highlight;
1702 d->createHighlight();
1704 d->updateHighlight();
1705 emit highlightChanged();
1709 bool QSGListView::highlightFollowsCurrentItem() const
1711 Q_D(const QSGListView);
1712 return d->autoHighlight;
1715 void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1718 if (d->autoHighlight != autoHighlight) {
1719 d->autoHighlight = autoHighlight;
1720 if (autoHighlight) {
1721 d->updateHighlight();
1723 if (d->highlightPosAnimator)
1724 d->highlightPosAnimator->stop();
1725 if (d->highlightSizeAnimator)
1726 d->highlightSizeAnimator->stop();
1728 emit highlightFollowsCurrentItemChanged();
1732 //###Possibly rename these properties, since they are very useful even without a highlight?
1733 qreal QSGListView::preferredHighlightBegin() const
1735 Q_D(const QSGListView);
1736 return d->highlightRangeStart;
1739 void QSGListView::setPreferredHighlightBegin(qreal start)
1742 d->highlightRangeStartValid = true;
1743 if (d->highlightRangeStart == start)
1745 d->highlightRangeStart = start;
1746 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1747 emit preferredHighlightBeginChanged();
1750 void QSGListView::resetPreferredHighlightBegin()
1753 d->highlightRangeStartValid = false;
1754 if (d->highlightRangeStart == 0)
1756 d->highlightRangeStart = 0;
1757 emit preferredHighlightBeginChanged();
1760 qreal QSGListView::preferredHighlightEnd() const
1762 Q_D(const QSGListView);
1763 return d->highlightRangeEnd;
1766 void QSGListView::setPreferredHighlightEnd(qreal end)
1769 d->highlightRangeEndValid = true;
1770 if (d->highlightRangeEnd == end)
1772 d->highlightRangeEnd = end;
1773 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1774 emit preferredHighlightEndChanged();
1777 void QSGListView::resetPreferredHighlightEnd()
1780 d->highlightRangeEndValid = false;
1781 if (d->highlightRangeEnd == 0)
1783 d->highlightRangeEnd = 0;
1784 emit preferredHighlightEndChanged();
1787 QSGListView::HighlightRangeMode QSGListView::highlightRangeMode() const
1789 Q_D(const QSGListView);
1790 return d->highlightRange;
1793 void QSGListView::setHighlightRangeMode(HighlightRangeMode mode)
1796 if (d->highlightRange == mode)
1798 d->highlightRange = mode;
1799 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1800 emit highlightRangeModeChanged();
1803 qreal QSGListView::spacing() const
1805 Q_D(const QSGListView);
1809 void QSGListView::setSpacing(qreal spacing)
1812 if (spacing != d->spacing) {
1813 d->spacing = spacing;
1815 emit spacingChanged();
1819 QSGListView::Orientation QSGListView::orientation() const
1821 Q_D(const QSGListView);
1825 void QSGListView::setOrientation(QSGListView::Orientation orientation)
1828 if (d->orient != orientation) {
1829 d->orient = orientation;
1830 if (d->orient == QSGListView::Vertical) {
1831 setContentWidth(-1);
1832 setFlickableDirection(VerticalFlick);
1834 setContentHeight(-1);
1835 setFlickableDirection(HorizontalFlick);
1838 emit orientationChanged();
1842 Qt::LayoutDirection QSGListView::layoutDirection() const
1844 Q_D(const QSGListView);
1845 return d->layoutDirection;
1848 void QSGListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1851 if (d->layoutDirection != layoutDirection) {
1852 d->layoutDirection = layoutDirection;
1854 emit layoutDirectionChanged();
1855 emit effectiveLayoutDirectionChanged();
1859 Qt::LayoutDirection QSGListView::effectiveLayoutDirection() const
1861 Q_D(const QSGListView);
1862 if (d->effectiveLayoutMirror)
1863 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
1865 return d->layoutDirection;
1868 bool QSGListView::isWrapEnabled() const
1870 Q_D(const QSGListView);
1874 void QSGListView::setWrapEnabled(bool wrap)
1877 if (d->wrap == wrap)
1880 emit keyNavigationWrapsChanged();
1883 int QSGListView::cacheBuffer() const
1885 Q_D(const QSGListView);
1889 void QSGListView::setCacheBuffer(int b)
1892 if (d->buffer != b) {
1894 if (isComponentComplete()) {
1895 d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter;
1898 emit cacheBufferChanged();
1902 QSGViewSection *QSGListView::sectionCriteria()
1905 if (!d->sectionCriteria) {
1906 d->sectionCriteria = new QSGViewSection(this);
1907 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
1909 return d->sectionCriteria;
1912 QString QSGListView::currentSection() const
1914 Q_D(const QSGListView);
1915 return d->currentSection;
1918 qreal QSGListView::highlightMoveSpeed() const
1920 Q_D(const QSGListView);\
1921 return d->highlightMoveSpeed;
1924 void QSGListView::setHighlightMoveSpeed(qreal speed)
1927 if (d->highlightMoveSpeed != speed) {
1928 d->highlightMoveSpeed = speed;
1929 if (d->highlightPosAnimator)
1930 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
1931 emit highlightMoveSpeedChanged();
1935 int QSGListView::highlightMoveDuration() const
1937 Q_D(const QSGListView);
1938 return d->highlightMoveDuration;
1941 void QSGListView::setHighlightMoveDuration(int duration)
1944 if (d->highlightMoveDuration != duration) {
1945 d->highlightMoveDuration = duration;
1946 if (d->highlightPosAnimator)
1947 d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
1948 emit highlightMoveDurationChanged();
1952 qreal QSGListView::highlightResizeSpeed() const
1954 Q_D(const QSGListView);\
1955 return d->highlightResizeSpeed;
1958 void QSGListView::setHighlightResizeSpeed(qreal speed)
1961 if (d->highlightResizeSpeed != speed) {
1962 d->highlightResizeSpeed = speed;
1963 if (d->highlightSizeAnimator)
1964 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
1965 emit highlightResizeSpeedChanged();
1969 int QSGListView::highlightResizeDuration() const
1971 Q_D(const QSGListView);
1972 return d->highlightResizeDuration;
1975 void QSGListView::setHighlightResizeDuration(int duration)
1978 if (d->highlightResizeDuration != duration) {
1979 d->highlightResizeDuration = duration;
1980 if (d->highlightSizeAnimator)
1981 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
1982 emit highlightResizeDurationChanged();
1986 QSGListView::SnapMode QSGListView::snapMode() const
1988 Q_D(const QSGListView);
1992 void QSGListView::setSnapMode(SnapMode mode)
1995 if (d->snapMode != mode) {
1997 emit snapModeChanged();
2001 QDeclarativeComponent *QSGListView::footer() const
2003 Q_D(const QSGListView);
2004 return d->footerComponent;
2007 void QSGListView::setFooter(QDeclarativeComponent *footer)
2010 if (d->footerComponent != footer) {
2012 // XXX todo - the original did scene()->removeItem(). Why?
2013 d->footer->item->setParentItem(0);
2014 d->footer->item->deleteLater();
2018 d->footerComponent = footer;
2019 d->minExtentDirty = true;
2020 d->maxExtentDirty = true;
2021 if (isComponentComplete()) {
2023 d->updateViewport();
2026 emit footerChanged();
2030 QDeclarativeComponent *QSGListView::header() const
2032 Q_D(const QSGListView);
2033 return d->headerComponent;
2036 void QSGListView::setHeader(QDeclarativeComponent *header)
2039 if (d->headerComponent != header) {
2041 // XXX todo - the original did scene()->removeItem(). Why?
2042 d->header->item->setParentItem(0);
2043 d->header->item->deleteLater();
2047 d->headerComponent = header;
2048 d->minExtentDirty = true;
2049 d->maxExtentDirty = true;
2050 if (isComponentComplete()) {
2053 d->updateViewport();
2056 emit headerChanged();
2060 void QSGListView::setContentX(qreal pos)
2063 // Positioning the view manually should override any current movement state
2064 d->moveReason = QSGListViewPrivate::Other;
2065 QSGFlickable::setContentX(pos);
2068 void QSGListView::setContentY(qreal pos)
2071 // Positioning the view manually should override any current movement state
2072 d->moveReason = QSGListViewPrivate::Other;
2073 QSGFlickable::setContentY(pos);
2076 void QSGListView::updatePolish()
2079 QSGFlickable::updatePolish();
2083 void QSGListView::viewportMoved()
2086 QSGFlickable::viewportMoved();
2089 // Recursion can occur due to refill changing the content size.
2090 if (d->inViewportMoved)
2092 d->inViewportMoved = true;
2093 d->lazyRelease = true;
2095 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2096 d->moveReason = QSGListViewPrivate::Mouse;
2097 if (d->moveReason != QSGListViewPrivate::SetIndex) {
2098 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2099 // reposition highlight
2100 qreal pos = d->highlight->position();
2102 qreal highlightStart;
2104 if (d->isRightToLeft()) {
2105 // Handle Right-To-Left exceptions
2106 viewPos = -d->position()-d->size();
2107 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2108 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2110 viewPos = d->position();
2111 highlightStart = d->highlightRangeStart;
2112 highlightEnd = d->highlightRangeEnd;
2114 if (pos > viewPos + highlightEnd - d->highlight->size())
2115 pos = viewPos + highlightEnd - d->highlight->size();
2116 if (pos < viewPos + highlightStart)
2117 pos = viewPos + highlightStart;
2118 d->highlightPosAnimator->stop();
2119 d->highlight->setPosition(qRound(pos));
2121 // update current index
2122 if (FxListItemSG *snapItem = d->snapItemAt(d->highlight->position())) {
2123 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2124 d->updateCurrent(snapItem->index);
2129 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2130 d->inFlickCorrection = true;
2131 // Near an end and it seems that the extent has changed?
2132 // Recalculate the flick so that we don't end up in an odd position.
2134 if (d->vData.velocity > 0) {
2135 const qreal minY = minYExtent();
2136 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2137 && minY != d->vData.flickTarget)
2138 d->flickY(-d->vData.smoothVelocity.value());
2139 d->bufferMode = QSGListViewPrivate::BufferBefore;
2140 } else if (d->vData.velocity < 0) {
2141 const qreal maxY = maxYExtent();
2142 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2143 && maxY != d->vData.flickTarget)
2144 d->flickY(-d->vData.smoothVelocity.value());
2145 d->bufferMode = QSGListViewPrivate::BufferAfter;
2150 if (d->hData.velocity > 0) {
2151 const qreal minX = minXExtent();
2152 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2153 && minX != d->hData.flickTarget)
2154 d->flickX(-d->hData.smoothVelocity.value());
2155 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore;
2156 } else if (d->hData.velocity < 0) {
2157 const qreal maxX = maxXExtent();
2158 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2159 && maxX != d->hData.flickTarget)
2160 d->flickX(-d->hData.smoothVelocity.value());
2161 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter;
2164 d->inFlickCorrection = false;
2166 d->inViewportMoved = false;
2169 qreal QSGListView::minYExtent() const
2171 Q_D(const QSGListView);
2172 if (d->orient == QSGListView::Horizontal)
2173 return QSGFlickable::minYExtent();
2174 if (d->minExtentDirty) {
2175 d->minExtent = -d->startPosition();
2176 if (d->header && d->visibleItems.count())
2177 d->minExtent += d->header->size();
2178 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2179 d->minExtent += d->highlightRangeStart;
2180 if (d->sectionCriteria) {
2181 if (d->visibleItem(0))
2182 d->minExtent -= d->visibleItem(0)->sectionSize();
2184 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2186 d->minExtentDirty = false;
2189 return d->minExtent;
2192 qreal QSGListView::maxYExtent() const
2194 Q_D(const QSGListView);
2195 if (d->orient == QSGListView::Horizontal)
2197 if (d->maxExtentDirty) {
2198 if (!d->model || !d->model->count()) {
2199 d->maxExtent = d->header ? -d->header->size() : 0;
2200 d->maxExtent += height();
2201 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2202 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2203 if (d->highlightRangeEnd != d->highlightRangeStart)
2204 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2206 d->maxExtent = -(d->endPosition() - height() + 1);
2209 d->maxExtent -= d->footer->size();
2210 qreal minY = minYExtent();
2211 if (d->maxExtent > minY)
2212 d->maxExtent = minY;
2213 d->maxExtentDirty = false;
2215 return d->maxExtent;
2218 qreal QSGListView::minXExtent() const
2220 Q_D(const QSGListView);
2221 if (d->orient == QSGListView::Vertical)
2222 return QSGFlickable::minXExtent();
2223 if (d->minExtentDirty) {
2224 d->minExtent = -d->startPosition();
2225 qreal highlightStart;
2227 qreal endPositionFirstItem = 0;
2228 if (d->isRightToLeft()) {
2229 if (d->model && d->model->count())
2230 endPositionFirstItem = d->positionAt(d->model->count()-1);
2232 d->minExtent += d->header->size();
2233 highlightStart = d->highlightRangeStartValid
2234 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2235 : d->size() - (d->lastPosition()-endPositionFirstItem);
2236 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2238 d->minExtent += d->footer->size();
2239 qreal maxX = maxXExtent();
2240 if (d->minExtent < maxX)
2241 d->minExtent = maxX;
2243 endPositionFirstItem = d->endPositionAt(0);
2244 highlightStart = d->highlightRangeStart;
2245 highlightEnd = d->highlightRangeEnd;
2246 if (d->header && d->visibleItems.count())
2247 d->minExtent += d->header->size();
2249 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2250 d->minExtent += highlightStart;
2251 d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2253 d->minExtentDirty = false;
2256 return d->minExtent;
2259 qreal QSGListView::maxXExtent() const
2261 Q_D(const QSGListView);
2262 if (d->orient == QSGListView::Vertical)
2264 if (d->maxExtentDirty) {
2265 qreal highlightStart;
2267 qreal lastItemPosition = 0;
2269 if (d->isRightToLeft()) {
2270 highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2271 highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2272 lastItemPosition = d->endPosition();
2274 highlightStart = d->highlightRangeStart;
2275 highlightEnd = d->highlightRangeEnd;
2276 if (d->model && d->model->count())
2277 lastItemPosition = d->positionAt(d->model->count()-1);
2279 if (!d->model || !d->model->count()) {
2280 if (!d->isRightToLeft())
2281 d->maxExtent = d->header ? -d->header->size() : 0;
2282 d->maxExtent += width();
2283 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2284 d->maxExtent = -(lastItemPosition - highlightStart);
2285 if (highlightEnd != highlightStart) {
2286 d->maxExtent = d->isRightToLeft()
2287 ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2288 : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2291 d->maxExtent = -(d->endPosition() - width() + 1);
2293 if (d->isRightToLeft()) {
2294 if (d->header && d->visibleItems.count())
2295 d->maxExtent -= d->header->size();
2298 d->maxExtent -= d->footer->size();
2299 qreal minX = minXExtent();
2300 if (d->maxExtent > minX)
2301 d->maxExtent = minX;
2303 d->maxExtentDirty = false;
2306 return d->maxExtent;
2309 void QSGListView::keyPressEvent(QKeyEvent *event)
2312 if (d->model && d->model->count() && d->interactive) {
2313 if ((!d->isRightToLeft() && event->key() == Qt::Key_Left)
2314 || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2315 || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) {
2316 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2317 decrementCurrentIndex();
2320 } else if (d->wrap) {
2324 } else if ((!d->isRightToLeft() && event->key() == Qt::Key_Right)
2325 || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2326 || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) {
2327 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2328 incrementCurrentIndex();
2331 } else if (d->wrap) {
2338 QSGFlickable::keyPressEvent(event);
2341 void QSGListView::geometryChanged(const QRectF &newGeometry,
2342 const QRectF &oldGeometry)
2345 d->maxExtentDirty = true;
2346 d->minExtentDirty = true;
2347 if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) {
2348 // maintain position relative to the right edge
2349 int dx = newGeometry.width() - oldGeometry.width();
2350 setContentX(contentX() - dx);
2352 QSGFlickable::geometryChanged(newGeometry, oldGeometry);
2356 void QSGListView::incrementCurrentIndex()
2359 int count = d->model ? d->model->count() : 0;
2360 if (count && (currentIndex() < count - 1 || d->wrap)) {
2361 d->moveReason = QSGListViewPrivate::SetIndex;
2362 int index = currentIndex()+1;
2363 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2367 void QSGListView::decrementCurrentIndex()
2370 int count = d->model ? d->model->count() : 0;
2371 if (count && (currentIndex() > 0 || d->wrap)) {
2372 d->moveReason = QSGListViewPrivate::SetIndex;
2373 int index = currentIndex()-1;
2374 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2378 void QSGListViewPrivate::positionViewAtIndex(int index, int mode)
2383 if (mode < QSGListView::Beginning || mode > QSGListView::Contain)
2385 int idx = qMax(qMin(index, model->count()-1), 0);
2387 if (layoutScheduled)
2389 qreal pos = isRightToLeft() ? -position() - size() : position();
2390 FxListItemSG *item = visibleItem(idx);
2392 if (orient == QSGListView::Vertical)
2393 maxExtent = -q->maxYExtent();
2395 maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2397 int itemPos = positionAt(idx);
2398 // save the currently visible items in case any of them end up visible again
2399 QList<FxListItemSG*> oldVisible = visibleItems;
2400 visibleItems.clear();
2401 visiblePos = itemPos;
2403 setPosition(qMin(qreal(itemPos), maxExtent));
2404 // now release the reference to all the old visible items.
2405 for (int i = 0; i < oldVisible.count(); ++i)
2406 releaseItem(oldVisible.at(i));
2407 item = visibleItem(idx);
2410 const qreal itemPos = item->position();
2412 case QSGListView::Beginning:
2414 if (index < 0 && header)
2415 pos -= header->size();
2417 case QSGListView::Center:
2418 pos = itemPos - (size() - item->size())/2;
2420 case QSGListView::End:
2421 pos = itemPos - size() + item->size();
2422 if (index >= model->count() && footer)
2423 pos += footer->size();
2425 case QSGListView::Visible:
2426 if (itemPos > pos + size())
2427 pos = itemPos - size() + item->size();
2428 else if (item->endPosition() < pos)
2431 case QSGListView::Contain:
2432 if (item->endPosition() > pos + size())
2433 pos = itemPos - size() + item->size();
2437 pos = qMin(pos, maxExtent);
2439 if (orient == QSGListView::Vertical) {
2440 minExtent = -q->minYExtent();
2442 minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2444 pos = qMax(pos, minExtent);
2445 moveReason = QSGListViewPrivate::Other;
2449 if (autoHighlight) {
2450 highlight->setPosition(currentItem->itemPosition());
2451 highlight->setSize(currentItem->itemSize());
2459 void QSGListView::positionViewAtIndex(int index, int mode)
2462 if (!d->isValid() || index < 0 || index >= d->model->count())
2464 d->positionViewAtIndex(index, mode);
2467 void QSGListView::positionViewAtBeginning()
2472 d->positionViewAtIndex(-1, Beginning);
2475 void QSGListView::positionViewAtEnd()
2480 d->positionViewAtIndex(d->model->count(), End);
2483 int QSGListView::indexAt(qreal x, qreal y) const
2485 Q_D(const QSGListView);
2486 for (int i = 0; i < d->visibleItems.count(); ++i) {
2487 const FxListItemSG *listItem = d->visibleItems.at(i);
2488 if(listItem->contains(x, y))
2489 return listItem->index;
2495 void QSGListView::componentComplete()
2498 QSGFlickable::componentComplete();
2504 d->moveReason = QSGListViewPrivate::SetIndex;
2505 if (d->currentIndex < 0 && !d->currentIndexCleared)
2506 d->updateCurrent(0);
2508 d->updateCurrent(d->currentIndex);
2509 if (d->highlight && d->currentItem) {
2510 if (d->autoHighlight)
2511 d->highlight->setPosition(d->currentItem->position());
2512 d->updateTrackedItem();
2514 d->moveReason = QSGListViewPrivate::Other;
2519 void QSGListView::updateSections()
2522 if (isComponentComplete() && d->model) {
2523 QList<QByteArray> roles;
2524 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2525 roles << d->sectionCriteria->property().toUtf8();
2526 d->model->setWatchedRoles(roles);
2527 d->updateSections();
2533 void QSGListView::refill()
2536 if (d->isRightToLeft())
2537 d->refill(-d->position()-d->size()+1, -d->position());
2539 d->refill(d->position(), d->position()+d->size()-1);
2542 void QSGListView::trackedPositionChanged()
2545 if (!d->trackedItem || !d->currentItem)
2547 if (d->moveReason == QSGListViewPrivate::SetIndex) {
2548 qreal trackedPos = qCeil(d->trackedItem->position());
2549 qreal trackedSize = d->trackedItem->size();
2550 if (d->trackedItem != d->currentItem) {
2551 trackedPos -= d->currentItem->sectionSize();
2552 trackedSize += d->currentItem->sectionSize();
2555 qreal highlightStart;
2557 if (d->isRightToLeft()) {
2558 viewPos = -d->position()-d->size();
2559 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2560 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2562 viewPos = d->position();
2563 highlightStart = d->highlightRangeStart;
2564 highlightEnd = d->highlightRangeEnd;
2566 qreal pos = viewPos;
2567 if (d->haveHighlightRange) {
2568 if (d->highlightRange == StrictlyEnforceRange) {
2569 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
2570 pos = trackedPos - highlightEnd + d->trackedItem->size();
2571 if (trackedPos < pos + highlightStart)
2572 pos = trackedPos - highlightStart;
2574 if (trackedPos < d->startPosition() + highlightStart) {
2575 pos = d->startPosition();
2576 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
2577 pos = d->endPosition() - d->size() + 1;
2578 if (pos < d->startPosition())
2579 pos = d->startPosition();
2581 if (trackedPos < viewPos + highlightStart) {
2582 pos = trackedPos - highlightStart;
2583 } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
2584 pos = trackedPos - highlightEnd + trackedSize;
2589 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
2590 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
2591 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
2592 && d->currentItem->endPosition() >= viewPos + d->size()) {
2593 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
2594 pos = d->trackedItem->endPosition() - d->size() + 1;
2595 if (trackedSize > d->size())
2598 pos = d->currentItem->endPosition() - d->size() + 1;
2599 if (d->currentItem->size() > d->size())
2600 pos = d->currentItem->position();
2604 if (viewPos != pos) {
2606 d->calcVelocity = true;
2607 d->setPosition(pos);
2608 d->calcVelocity = false;
2613 void QSGListView::itemsInserted(int modelIndex, int count)
2616 if (!isComponentComplete())
2618 d->updateUnrequestedIndexes();
2619 d->moveReason = QSGListViewPrivate::Other;
2621 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
2622 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
2624 int i = d->visibleItems.count() - 1;
2625 while (i > 0 && d->visibleItems.at(i)->index == -1)
2627 if (i == 0 && d->visibleItems.first()->index == -1) {
2628 // there are no visible items except items marked for removal
2629 index = d->visibleItems.count();
2630 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
2631 && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
2632 // Special case of appending an item to the model.
2633 index = d->visibleItems.count();
2635 if (modelIndex < d->visibleIndex) {
2636 // Insert before visible items
2637 d->visibleIndex += count;
2638 for (int i = 0; i < d->visibleItems.count(); ++i) {
2639 FxListItemSG *listItem = d->visibleItems.at(i);
2640 if (listItem->index != -1 && listItem->index >= modelIndex)
2641 listItem->index += count;
2644 if (d->currentIndex >= modelIndex) {
2645 // adjust current item index
2646 d->currentIndex += count;
2648 d->currentItem->index = d->currentIndex;
2649 emit currentIndexChanged();
2651 d->scheduleLayout();
2652 d->itemCount += count;
2653 emit countChanged();
2658 // index can be the next item past the end of the visible items list (i.e. appended)
2660 if (d->visibleItems.count()) {
2661 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
2662 : d->visibleItems.last()->endPosition()+d->spacing+1;
2663 } else if (d->itemCount == 0 && d->header) {
2664 pos = d->header->size();
2667 int initialPos = pos;
2669 QList<FxListItemSG*> added;
2670 bool addedVisible = false;
2671 FxListItemSG *firstVisible = d->firstVisibleItem();
2672 if (firstVisible && pos < firstVisible->position()) {
2673 // Insert items before the visible item.
2674 int insertionIdx = index;
2676 int from = tempPos - d->buffer;
2677 for (i = count-1; i >= 0 && pos > from; --i) {
2678 if (!addedVisible) {
2679 d->scheduleLayout();
2680 addedVisible = true;
2682 FxListItemSG *item = d->createItem(modelIndex + i);
2683 d->visibleItems.insert(insertionIdx, item);
2684 pos -= item->size() + d->spacing;
2685 item->setPosition(pos);
2689 // If we didn't insert all our new items - anything
2690 // before the current index is not visible - remove it.
2691 while (insertionIdx--) {
2692 FxListItemSG *item = d->visibleItems.takeFirst();
2693 if (item->index != -1)
2695 d->releaseItem(item);
2698 // adjust pos of items before inserted items.
2699 for (int i = insertionIdx-1; i >= 0; i--) {
2700 FxListItemSG *listItem = d->visibleItems.at(i);
2701 listItem->setPosition(listItem->position() - (initialPos - pos));
2706 int to = d->buffer+tempPos+d->size()-1;
2707 for (i = 0; i < count && pos <= to; ++i) {
2708 if (!addedVisible) {
2709 d->scheduleLayout();
2710 addedVisible = true;
2712 FxListItemSG *item = d->createItem(modelIndex + i);
2713 d->visibleItems.insert(index, item);
2714 item->setPosition(pos);
2716 pos += item->size() + d->spacing;
2720 // We didn't insert all our new items, which means anything
2721 // beyond the current index is not visible - remove it.
2722 while (d->visibleItems.count() > index)
2723 d->releaseItem(d->visibleItems.takeLast());
2725 diff = pos - initialPos;
2727 if (d->itemCount && d->currentIndex >= modelIndex) {
2728 // adjust current item index
2729 d->currentIndex += count;
2730 if (d->currentItem) {
2731 d->currentItem->index = d->currentIndex;
2732 d->currentItem->setPosition(d->currentItem->position() + diff);
2734 emit currentIndexChanged();
2735 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
2736 d->updateCurrent(0);
2738 // Update the indexes of the following visible items.
2739 for (; index < d->visibleItems.count(); ++index) {
2740 FxListItemSG *listItem = d->visibleItems.at(index);
2741 if (d->currentItem && listItem->item != d->currentItem->item)
2742 listItem->setPosition(listItem->position() + diff);
2743 if (listItem->index != -1)
2744 listItem->index += count;
2746 // everything is in order now - emit add() signal
2747 for (int j = 0; j < added.count(); ++j)
2748 added.at(j)->attached->emitAdd();
2750 d->updateSections();
2751 d->itemCount += count;
2752 emit countChanged();
2755 void QSGListView::itemsRemoved(int modelIndex, int count)
2758 if (!isComponentComplete())
2760 d->moveReason = QSGListViewPrivate::Other;
2761 d->updateUnrequestedIndexes();
2762 d->itemCount -= count;
2764 FxListItemSG *firstVisible = d->firstVisibleItem();
2765 int preRemovedSize = 0;
2766 bool removedVisible = false;
2767 // Remove the items from the visible list, skipping anything already marked for removal
2768 QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
2769 while (it != d->visibleItems.end()) {
2770 FxListItemSG *item = *it;
2771 if (item->index == -1 || item->index < modelIndex) {
2772 // already removed, or before removed items
2774 } else if (item->index >= modelIndex + count) {
2775 // after removed items
2776 item->index -= count;
2780 if (!removedVisible) {
2781 d->scheduleLayout();
2782 removedVisible = true;
2784 item->attached->emitRemove();
2785 if (item->attached->delayRemove()) {
2787 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
2790 if (item == firstVisible)
2792 if (firstVisible && item->position() < firstVisible->position())
2793 preRemovedSize += item->size();
2794 it = d->visibleItems.erase(it);
2795 d->releaseItem(item);
2800 if (firstVisible && d->visibleItems.first() != firstVisible)
2801 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
2804 if (d->currentIndex >= modelIndex + count) {
2805 d->currentIndex -= count;
2807 d->currentItem->index -= count;
2808 emit currentIndexChanged();
2809 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
2810 // current item has been removed.
2811 if (d->currentItem) {
2812 d->currentItem->attached->setIsCurrentItem(false);
2813 d->releaseItem(d->currentItem);
2816 d->currentIndex = -1;
2818 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
2820 emit currentIndexChanged();
2823 // update visibleIndex
2824 bool haveVisibleIndex = false;
2825 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2826 if ((*it)->index != -1) {
2827 d->visibleIndex = (*it)->index;
2828 haveVisibleIndex = true;
2833 if (removedVisible && !haveVisibleIndex) {
2834 d->timeline.clear();
2835 if (d->itemCount == 0) {
2836 d->visibleIndex = 0;
2837 d->visiblePos = d->header ? d->header->size() : 0;
2842 if (modelIndex < d->visibleIndex)
2843 d->visibleIndex = modelIndex+1;
2844 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
2848 d->updateSections();
2849 emit countChanged();
2852 void QSGListView::destroyRemoved()
2855 for (QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
2856 it != d->visibleItems.end();) {
2857 FxListItemSG *listItem = *it;
2858 if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
2859 d->releaseItem(listItem);
2860 it = d->visibleItems.erase(it);
2866 // Correct the positioning of the items
2867 d->updateSections();
2871 void QSGListView::itemsMoved(int from, int to, int count)
2874 if (!isComponentComplete())
2876 d->updateUnrequestedIndexes();
2878 if (d->visibleItems.isEmpty()) {
2883 d->moveReason = QSGListViewPrivate::Other;
2884 FxListItemSG *firstVisible = d->firstVisibleItem();
2885 qreal firstItemPos = firstVisible->position();
2886 QHash<int,FxListItemSG*> moved;
2889 QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
2890 while (it != d->visibleItems.end()) {
2891 FxListItemSG *item = *it;
2892 if (item->index >= from && item->index < from + count) {
2893 // take the items that are moving
2894 item->index += (to-from);
2895 moved.insert(item->index, item);
2896 if (item->position() < firstItemPos)
2897 moveBy += item->size();
2898 it = d->visibleItems.erase(it);
2900 // move everything after the moved items.
2901 if (item->index > from && item->index != -1)
2902 item->index -= count;
2907 int remaining = count;
2908 int endIndex = d->visibleIndex;
2909 it = d->visibleItems.begin();
2910 while (it != d->visibleItems.end()) {
2911 FxListItemSG *item = *it;
2912 if (remaining && item->index >= to && item->index < to + count) {
2913 // place items in the target position, reusing any existing items
2914 FxListItemSG *movedItem = moved.take(item->index);
2916 movedItem = d->createItem(item->index);
2917 if (item->index <= firstVisible->index)
2918 moveBy -= movedItem->size();
2919 it = d->visibleItems.insert(it, movedItem);
2923 if (item->index != -1) {
2924 if (item->index >= to) {
2925 // update everything after the moved items.
2926 item->index += count;
2928 endIndex = item->index;
2934 // If we have moved items to the end of the visible items
2935 // then add any existing moved items that we have
2936 while (FxListItemSG *item = moved.take(endIndex+1)) {
2937 d->visibleItems.append(item);
2941 // update visibleIndex
2942 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2943 if ((*it)->index != -1) {
2944 d->visibleIndex = (*it)->index;
2949 // Fix current index
2950 if (d->currentIndex >= 0 && d->currentItem) {
2951 int oldCurrent = d->currentIndex;
2952 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
2953 if (oldCurrent != d->currentIndex) {
2954 d->currentItem->index = d->currentIndex;
2955 emit currentIndexChanged();
2959 // Whatever moved items remain are no longer visible items.
2960 while (moved.count()) {
2961 int idx = moved.begin().key();
2962 FxListItemSG *item = moved.take(idx);
2963 if (d->currentItem && item->item == d->currentItem->item)
2964 item->setPosition(d->positionAt(idx));
2965 d->releaseItem(item);
2968 // Ensure we don't cause an ugly list scroll.
2969 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
2971 d->updateSections();
2975 void QSGListView::itemsChanged(int, int)
2978 d->updateSections();
2982 void QSGListView::modelReset()
2985 d->moveReason = QSGListViewPrivate::SetIndex;
2987 if (d->highlight && d->currentItem) {
2988 if (d->autoHighlight)
2989 d->highlight->setPosition(d->currentItem->position());
2990 d->updateTrackedItem();
2992 d->moveReason = QSGListViewPrivate::Other;
2993 emit countChanged();
2996 void QSGListView::createdItem(int index, QSGItem *item)
2999 if (d->requestedIndex != index) {
3000 item->setParentItem(contentItem());
3001 d->unrequestedItems.insert(item, index);
3002 if (d->orient == QSGListView::Vertical) {
3003 item->setY(d->positionAt(index));
3005 if (d->isRightToLeft())
3006 item->setX(-d->positionAt(index)-item->width());
3008 item->setX(d->positionAt(index));
3013 void QSGListView::destroyingItem(QSGItem *item)
3016 d->unrequestedItems.remove(item);
3019 void QSGListView::animStopped()
3022 d->bufferMode = QSGListViewPrivate::NoBuffer;
3023 if (d->haveHighlightRange && d->highlightRange == QSGListView::StrictlyEnforceRange)
3024 d->updateHighlight();
3027 QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
3029 return new QSGListViewAttached(obj);