1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsglistview_p.h"
43 #include "qsgitemview_p_p.h"
44 #include "qsgvisualitemmodel_p.h"
46 #include <QtDeclarative/qdeclarativeexpression.h>
47 #include <QtDeclarative/qdeclarativeengine.h>
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
53 #include <private/qdeclarativesmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
58 void QSGViewSection::setProperty(const QString &property)
60 if (property != m_property) {
61 m_property = property;
62 emit propertyChanged();
66 void QSGViewSection::setCriteria(QSGViewSection::SectionCriteria criteria)
68 if (criteria != m_criteria) {
69 m_criteria = criteria;
70 emit criteriaChanged();
74 void QSGViewSection::setDelegate(QDeclarativeComponent *delegate)
76 if (delegate != m_delegate) {
77 m_delegate = delegate;
78 emit delegateChanged();
82 QString QSGViewSection::sectionString(const QString &value)
84 if (m_criteria == FirstCharacter)
85 return value.isEmpty() ? QString() : value.at(0);
90 //----------------------------------------------------------------------------
92 class FxListItemSG : public FxViewItem
95 FxListItemSG(QSGItem *i, QSGListView *v, bool own) : FxViewItem(i, own), section(0), view(v) {
96 attached = static_cast<QSGListViewAttached*>(qmlAttachedPropertiesObject<QSGListView>(item));
98 static_cast<QSGListViewAttached*>(attached)->setView(view);
103 qreal position() const {
105 if (view->orientation() == QSGListView::Vertical)
108 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
110 return itemPosition();
113 qreal itemPosition() const {
114 if (view->orientation() == QSGListView::Vertical)
117 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
121 return (view->orientation() == QSGListView::Vertical ? item->height()+section->height() : item->width()+section->width());
123 return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
125 qreal itemSize() const {
126 return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
128 qreal sectionSize() const {
130 return (view->orientation() == QSGListView::Vertical ? section->height() : section->width());
133 qreal endPosition() const {
134 if (view->orientation() == QSGListView::Vertical) {
135 return item->y() + item->height();
137 return (view->effectiveLayoutDirection() == Qt::RightToLeft
139 : item->x() + item->width());
142 void setPosition(qreal pos) {
143 if (view->orientation() == QSGListView::Vertical) {
146 pos += section->height();
150 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
152 section->setX(-section->width()-pos);
153 pos += section->width();
155 item->setX(-item->width()-pos);
159 pos += section->width();
165 void setSize(qreal size) {
166 if (view->orientation() == QSGListView::Vertical)
167 item->setHeight(size);
169 item->setWidth(size);
171 bool contains(qreal x, qreal y) const {
172 return (x >= item->x() && x < item->x() + item->width() &&
173 y >= item->y() && y < item->y() + item->height());
180 //----------------------------------------------------------------------------
182 class QSGListViewPrivate : public QSGItemViewPrivate
184 Q_DECLARE_PUBLIC(QSGListView)
186 virtual Qt::Orientation layoutOrientation() const;
187 virtual bool isContentFlowReversed() const;
188 bool isRightToLeft() const;
190 virtual qreal positionAt(int index) const;
191 virtual qreal endPositionAt(int index) const;
192 virtual qreal originPosition() const;
193 virtual qreal lastPosition() const;
195 FxViewItem *nextVisibleItem() const;
196 FxViewItem *itemBefore(int modelIndex) const;
197 QString sectionAt(int modelIndex);
198 qreal snapPosAt(qreal pos);
199 FxViewItem *snapItemAt(qreal pos);
202 virtual void clear();
204 virtual bool addVisibleItems(int fillFrom, int fillTo, bool doBuffer);
205 virtual bool removeNonVisibleItems(int bufferFrom, int bufferTo);
206 virtual void visibleItemsChanged();
208 virtual FxViewItem *newViewItem(int index, QSGItem *item);
209 virtual void initializeViewItem(FxViewItem *item);
210 virtual void releaseItem(FxViewItem *item);
211 virtual void repositionPackageItemAt(QSGItem *item, int index);
213 virtual void createHighlight();
214 virtual void updateHighlight();
215 virtual void resetHighlightPosition();
217 virtual void setPosition(qreal pos);
218 virtual void layoutVisibleItems();
220 virtual void updateSections();
221 void createSection(FxListItemSG *);
222 void updateCurrentSection();
224 virtual qreal headerSize() const;
225 virtual qreal footerSize() const;
226 virtual bool showHeaderForIndex(int index) const;
227 virtual bool showFooterForIndex(int index) const;
228 virtual void updateHeader();
229 virtual void updateFooter();
231 virtual void changedVisibleIndex(int newIndex);
232 virtual void initializeCurrentItem();
234 void updateAverage();
236 void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
237 virtual void fixupPosition();
238 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
239 virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
240 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
242 QSGListView::Orientation orient;
246 QSGListView::SnapMode snapMode;
248 QSmoothedAnimation *highlightPosAnimator;
249 QSmoothedAnimation *highlightSizeAnimator;
250 qreal highlightMoveSpeed;
251 qreal highlightResizeSpeed;
252 int highlightResizeDuration;
254 QSGViewSection *sectionCriteria;
255 QString currentSection;
256 static const int sectionCacheSize = 4;
257 QSGItem *sectionCache[sectionCacheSize];
260 bool correctFlick : 1;
261 bool inFlickCorrection : 1;
264 : orient(QSGListView::Vertical)
266 , averageSize(100.0), spacing(0.0)
267 , snapMode(QSGListView::NoSnap)
268 , highlightPosAnimator(0), highlightSizeAnimator(0)
269 , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
271 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
275 bool QSGListViewPrivate::isContentFlowReversed() const
277 return isRightToLeft();
280 Qt::Orientation QSGListViewPrivate::layoutOrientation() const
282 return static_cast<Qt::Orientation>(orient);
285 bool QSGListViewPrivate::isRightToLeft() const
287 Q_Q(const QSGListView);
288 return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
291 FxViewItem *QSGListViewPrivate::nextVisibleItem() const
293 const qreal pos = isRightToLeft() ? -position()-size() : position();
294 bool foundFirst = false;
295 for (int i = 0; i < visibleItems.count(); ++i) {
296 FxViewItem *item = visibleItems.at(i);
297 if (item->index != -1) {
300 else if (item->position() < pos && item->endPosition() >= pos)
307 // Returns the item before modelIndex, if created.
308 // May return an item marked for removal.
309 FxViewItem *QSGListViewPrivate::itemBefore(int modelIndex) const
311 if (modelIndex < visibleIndex)
315 while (idx < visibleItems.count()) {
316 FxViewItem *item = visibleItems.at(idx);
317 if (item->index != -1)
318 lastIndex = item->index;
319 if (item->index == modelIndex)
320 return visibleItems.at(idx-1);
323 if (lastIndex == modelIndex-1)
324 return visibleItems.last();
328 void QSGListViewPrivate::setPosition(qreal pos)
331 if (orient == QSGListView::Vertical) {
332 q->QSGFlickable::setContentY(pos);
335 q->QSGFlickable::setContentX(-pos-size());
337 q->QSGFlickable::setContentX(pos);
341 qreal QSGListViewPrivate::originPosition() const
344 if (!visibleItems.isEmpty()) {
345 pos = (*visibleItems.constBegin())->position();
346 if (visibleIndex > 0)
347 pos -= visibleIndex * (averageSize + spacing);
352 qreal QSGListViewPrivate::lastPosition() const
355 if (!visibleItems.isEmpty()) {
356 int invisibleCount = visibleItems.count() - visibleIndex;
357 for (int i = visibleItems.count()-1; i >= 0; --i) {
358 if (visibleItems.at(i)->index != -1) {
359 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
363 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
364 } else if (model && model->count()) {
365 pos = (model->count() * averageSize + (model->count()-1) * spacing);
370 qreal QSGListViewPrivate::positionAt(int modelIndex) const
372 if (FxViewItem *item = visibleItem(modelIndex))
373 return item->position();
374 if (!visibleItems.isEmpty()) {
375 if (modelIndex < visibleIndex) {
376 int count = visibleIndex - modelIndex;
378 if (modelIndex == currentIndex && currentItem) {
379 cs = currentItem->size() + spacing;
382 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
384 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
385 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
391 qreal QSGListViewPrivate::endPositionAt(int modelIndex) const
393 if (FxViewItem *item = visibleItem(modelIndex))
394 return item->endPosition();
395 if (!visibleItems.isEmpty()) {
396 if (modelIndex < visibleIndex) {
397 int count = visibleIndex - modelIndex;
398 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
400 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
401 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
407 QString QSGListViewPrivate::sectionAt(int modelIndex)
409 if (FxViewItem *item = visibleItem(modelIndex))
410 return item->attached->section();
413 if (sectionCriteria) {
414 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
415 section = sectionCriteria->sectionString(propValue);
421 qreal QSGListViewPrivate::snapPosAt(qreal pos)
423 if (FxViewItem *snapItem = snapItemAt(pos))
424 return snapItem->position();
425 if (visibleItems.count()) {
426 qreal firstPos = (*visibleItems.constBegin())->position();
427 qreal endPos = (*(--visibleItems.constEnd()))->position();
428 if (pos < firstPos) {
429 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
430 } else if (pos > endPos)
431 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
433 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
436 FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos)
438 FxViewItem *snapItem = 0;
439 qreal prevItemSize = 0;
440 for (int i = 0; i < visibleItems.count(); ++i) {
441 FxViewItem *item = visibleItems.at(i);
442 if (item->index == -1)
444 qreal itemTop = item->position();
445 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
447 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
449 prevItemSize = item->size();
454 void QSGListViewPrivate::changedVisibleIndex(int newIndex)
456 visiblePos = positionAt(newIndex);
457 visibleIndex = newIndex;
460 void QSGListViewPrivate::init()
462 QSGItemViewPrivate::init();
463 ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
466 void QSGListViewPrivate::clear()
468 for (int i = 0; i < sectionCacheSize; ++i) {
469 delete sectionCache[i];
473 QSGItemViewPrivate::clear();
476 FxViewItem *QSGListViewPrivate::newViewItem(int modelIndex, QSGItem *item)
480 FxListItemSG *listItem = new FxListItemSG(item, q, false);
481 listItem->index = modelIndex;
483 // initialise attached properties
484 if (sectionCriteria) {
485 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
486 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
487 if (modelIndex > 0) {
488 if (FxViewItem *item = itemBefore(modelIndex))
489 listItem->attached->m_prevSection = item->attached->section();
491 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
493 if (modelIndex < model->count()-1) {
494 if (FxViewItem *item = visibleItem(modelIndex+1))
495 listItem->attached->m_nextSection = static_cast<QSGListViewAttached*>(item->attached)->section();
497 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
504 void QSGListViewPrivate::initializeViewItem(FxViewItem *item)
506 QSGItemViewPrivate::initializeViewItem(item);
508 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
509 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
511 if (sectionCriteria && sectionCriteria->delegate()) {
512 if (item->attached->m_prevSection != item->attached->m_section)
513 createSection(static_cast<FxListItemSG*>(item));
517 void QSGListViewPrivate::releaseItem(FxViewItem *item)
520 FxListItemSG* listItem = static_cast<FxListItemSG*>(item);
521 if (listItem->section) {
524 if (!sectionCache[i]) {
525 sectionCache[i] = listItem->section;
526 sectionCache[i]->setVisible(false);
527 listItem->section = 0;
531 } while (i < sectionCacheSize);
532 delete listItem->section;
535 QSGItemViewPrivate::releaseItem(item);
538 bool QSGListViewPrivate::addVisibleItems(int fillFrom, int fillTo, bool doBuffer)
540 qreal itemEnd = visiblePos;
541 if (visibleItems.count()) {
542 visiblePos = (*visibleItems.constBegin())->position();
543 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
546 int modelIndex = findLastVisibleIndex();
547 bool haveValidItems = modelIndex >= 0;
548 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
550 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
551 || fillTo < visiblePos - averageSize - spacing)) {
552 // We've jumped more than a page. Estimate which items are now
553 // visible and fill from there.
554 int count = (fillFrom - itemEnd) / (averageSize + spacing);
555 for (int i = 0; i < visibleItems.count(); ++i)
556 releaseItem(visibleItems.at(i));
557 visibleItems.clear();
559 if (modelIndex >= model->count()) {
560 count -= modelIndex - model->count() + 1;
561 modelIndex = model->count() - 1;
562 } else if (modelIndex < 0) {
566 visibleIndex = modelIndex;
567 visiblePos = itemEnd + count * (averageSize + spacing);
568 itemEnd = visiblePos;
571 bool changed = false;
572 FxListItemSG *item = 0;
574 while (modelIndex < model->count() && pos <= fillTo) {
575 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
576 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex))))
578 item->setPosition(pos);
579 pos += item->size() + spacing;
580 visibleItems.append(item);
583 if (doBuffer) // never buffer more than one item per frame
586 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos >= fillFrom) {
587 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
588 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1))))
591 visiblePos -= item->size() + spacing;
592 item->setPosition(visiblePos);
593 visibleItems.prepend(item);
595 if (doBuffer) // never buffer more than one item per frame
602 bool QSGListViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo)
604 FxViewItem *item = 0;
605 bool changed = false;
607 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() <= bufferFrom) {
608 if (item->attached->delayRemove())
610 if (item->size() == 0)
612 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
613 if (item->index != -1)
615 visibleItems.removeFirst();
619 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
620 if (item->attached->delayRemove())
622 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
623 visibleItems.removeLast();
631 void QSGListViewPrivate::visibleItemsChanged()
633 if (visibleItems.count())
634 visiblePos = (*visibleItems.constBegin())->position();
636 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
637 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
641 updateCurrentSection();
645 updateUnrequestedPositions();
648 void QSGListViewPrivate::layoutVisibleItems()
650 if (!visibleItems.isEmpty()) {
651 bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item;
652 qreal sum = (*visibleItems.constBegin())->size();
653 qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing;
654 for (int i=1; i < visibleItems.count(); ++i) {
655 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
656 item->setPosition(pos);
657 pos += item->size() + spacing;
659 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
661 averageSize = qRound(sum / visibleItems.count());
663 // move current item if it is not a visible item.
664 if (currentIndex >= 0 && currentItem && !fixedCurrent) {
665 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
670 void QSGListViewPrivate::repositionPackageItemAt(QSGItem *item, int index)
673 qreal pos = position();
674 if (orient == QSGListView::Vertical) {
675 if (item->y() + item->height() > pos && item->y() < pos + q->height())
676 item->setY(positionAt(index));
678 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
680 item->setX(-positionAt(index)-item->width());
682 item->setX(positionAt(index));
687 void QSGListViewPrivate::createHighlight()
690 bool changed = false;
692 if (trackedItem == highlight)
697 delete highlightPosAnimator;
698 delete highlightSizeAnimator;
699 highlightPosAnimator = 0;
700 highlightSizeAnimator = 0;
706 QSGItem *item = createHighlightItem();
708 FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
711 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
712 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
714 const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x");
715 highlightPosAnimator = new QSmoothedAnimation(q);
716 highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
717 highlightPosAnimator->velocity = highlightMoveSpeed;
718 highlightPosAnimator->userDuration = highlightMoveDuration;
720 const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width");
721 highlightSizeAnimator = new QSmoothedAnimation(q);
722 highlightSizeAnimator->velocity = highlightResizeSpeed;
723 highlightSizeAnimator->userDuration = highlightResizeDuration;
724 highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
726 highlight = newHighlight;
731 emit q->highlightItemChanged();
734 void QSGListViewPrivate::updateHighlight()
736 if ((!currentItem && highlight) || (currentItem && !highlight))
738 bool strictHighlight = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange;
739 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
740 // auto-update highlight
741 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
742 highlightPosAnimator->to = isRightToLeft()
743 ? -listItem->itemPosition()-listItem->itemSize()
744 : listItem->itemPosition();
745 highlightSizeAnimator->to = listItem->itemSize();
746 if (orient == QSGListView::Vertical) {
747 if (highlight->item->width() == 0)
748 highlight->item->setWidth(currentItem->item->width());
750 if (highlight->item->height() == 0)
751 highlight->item->setHeight(currentItem->item->height());
754 highlightPosAnimator->restart();
755 highlightSizeAnimator->restart();
760 void QSGListViewPrivate::resetHighlightPosition()
762 if (highlight && currentItem)
763 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
766 void QSGListViewPrivate::createSection(FxListItemSG *listItem)
769 if (!sectionCriteria || !sectionCriteria->delegate())
771 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
772 if (!listItem->section) {
773 qreal pos = listItem->position();
774 int i = sectionCacheSize-1;
775 while (i >= 0 && !sectionCache[i])
778 listItem->section = sectionCache[i];
780 listItem->section->setVisible(true);
781 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
782 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
784 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
785 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
786 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
788 QDeclarative_setParent_noEvent(context, nobj);
789 listItem->section = qobject_cast<QSGItem *>(nobj);
790 if (!listItem->section) {
793 listItem->section->setZ(1);
794 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
795 listItem->section->setParentItem(q->contentItem());
800 sectionCriteria->delegate()->completeCreate();
802 listItem->setPosition(pos);
804 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
805 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
807 } else if (listItem->section) {
808 qreal pos = listItem->position();
811 if (!sectionCache[i]) {
812 sectionCache[i] = listItem->section;
813 sectionCache[i]->setVisible(false);
814 listItem->section = 0;
818 } while (i < sectionCacheSize);
819 delete listItem->section;
820 listItem->section = 0;
821 listItem->setPosition(pos);
825 void QSGListViewPrivate::updateSections()
827 QSGItemViewPrivate::updateSections();
829 if (sectionCriteria && !visibleItems.isEmpty()) {
831 if (visibleIndex > 0)
832 prevSection = sectionAt(visibleIndex-1);
833 QSGListViewAttached *prevAtt = 0;
835 for (int i = 0; i < visibleItems.count(); ++i) {
836 QSGListViewAttached *attached = static_cast<QSGListViewAttached*>(visibleItems.at(i)->attached);
837 attached->setPrevSection(prevSection);
838 if (visibleItems.at(i)->index != -1) {
839 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
840 attached->setSection(sectionCriteria->sectionString(propValue));
841 idx = visibleItems.at(i)->index;
843 createSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
845 prevAtt->setNextSection(attached->section());
846 prevSection = attached->section();
850 if (idx > 0 && idx < model->count()-1)
851 prevAtt->setNextSection(sectionAt(idx+1));
853 prevAtt->setNextSection(QString());
858 void QSGListViewPrivate::updateCurrentSection()
861 if (!sectionCriteria || visibleItems.isEmpty()) {
862 if (!currentSection.isEmpty()) {
863 currentSection.clear();
864 emit q->currentSectionChanged();
869 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= position())
872 QString newSection = currentSection;
873 if (index < visibleItems.count())
874 newSection = visibleItems.at(index)->attached->section();
876 newSection = (*visibleItems.constBegin())->attached->section();
877 if (newSection != currentSection) {
878 currentSection = newSection;
879 emit q->currentSectionChanged();
883 void QSGListViewPrivate::initializeCurrentItem()
885 QSGItemViewPrivate::initializeCurrentItem();
888 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
890 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
891 // We can calculate exact postion in this case
892 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
894 // Create current item now and position as best we can.
895 // Its position will be corrected when it becomes visible.
896 listItem->setPosition(positionAt(currentIndex));
899 // Avoid showing section delegate twice. We still need the section heading so that
900 // currentItem positioning works correctly.
901 // This is slightly sub-optimal, but section heading caching minimizes the impact.
902 if (listItem->section)
903 listItem->section->setVisible(false);
905 if (visibleItems.isEmpty())
906 averageSize = listItem->size();
910 void QSGListViewPrivate::updateAverage()
912 if (!visibleItems.count())
915 for (int i = 0; i < visibleItems.count(); ++i)
916 sum += visibleItems.at(i)->size();
917 averageSize = qRound(sum / visibleItems.count());
920 qreal QSGListViewPrivate::headerSize() const
922 return header ? header->size() : 0.0;
925 qreal QSGListViewPrivate::footerSize() const
927 return footer ? footer->size() : 0.0;
930 bool QSGListViewPrivate::showHeaderForIndex(int index) const
935 bool QSGListViewPrivate::showFooterForIndex(int index) const
937 return index == model->count()-1;
940 void QSGListViewPrivate::updateFooter()
943 bool created = false;
945 QSGItem *item = createComponentItem(footerComponent, true);
949 footer = new FxListItemSG(item, q, true);
953 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
954 if (visibleItems.count()) {
955 qreal endPos = lastPosition();
956 if (findLastVisibleIndex() == model->count()-1) {
957 listItem->setPosition(endPos);
959 qreal visiblePos = position() + q->height();
960 if (endPos <= visiblePos || listItem->position() < endPos)
961 listItem->setPosition(endPos);
964 listItem->setPosition(visiblePos);
968 emit q->footerItemChanged();
971 void QSGListViewPrivate::updateHeader()
974 bool created = false;
976 QSGItem *item = createComponentItem(headerComponent, true);
980 header = new FxListItemSG(item, q, true);
984 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
986 if (visibleItems.count()) {
987 qreal startPos = originPosition();
988 if (visibleIndex == 0) {
989 listItem->setPosition(startPos - headerSize());
991 if (position() <= startPos || listItem->position() > startPos - headerSize())
992 listItem->setPosition(startPos - headerSize());
995 listItem->setPosition(-headerSize());
1000 emit q->headerItemChanged();
1003 void QSGListViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1006 QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1007 if (!q->isComponentComplete())
1009 if (item != contentItem && (!highlight || item != highlight->item)) {
1010 if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height())
1011 || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1017 void QSGListViewPrivate::fixupPosition()
1019 if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange)
1020 || snapMode != QSGListView::NoSnap)
1022 if (orient == QSGListView::Vertical)
1028 void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1030 if ((orient == QSGListView::Horizontal && &data == &vData)
1031 || (orient == QSGListView::Vertical && &data == &hData))
1034 correctFlick = false;
1035 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1037 qreal highlightStart;
1040 if (isRightToLeft()) {
1041 // Handle Right-To-Left exceptions
1042 viewPos = -position()-size();
1043 highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1044 highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1046 viewPos = position();
1047 highlightStart = highlightRangeStart;
1048 highlightEnd = highlightRangeEnd;
1051 if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange
1052 && moveReason != QSGListViewPrivate::SetIndex) {
1054 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1055 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightEnd)
1056 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightEnd;
1057 if (viewPos > pos - highlightStart)
1058 viewPos = pos - highlightStart;
1059 if (isRightToLeft())
1060 viewPos = -viewPos-size();
1062 timeline.reset(data.move);
1063 if (viewPos != position()) {
1064 if (fixupMode != Immediate) {
1065 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1066 data.fixingUp = true;
1068 timeline.set(data.move, -viewPos);
1071 vTime = timeline.time();
1072 } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) {
1073 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1074 FxViewItem *topItem = snapItemAt(tempPosition+highlightStart);
1075 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1077 bool isInBounds = -position() > maxExtent && -position() < minExtent;
1078 if (topItem && isInBounds) {
1079 if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+headerSize()/2) {
1080 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1082 if (isRightToLeft())
1083 pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1085 pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1087 } else if (bottomItem && isInBounds) {
1088 if (isRightToLeft())
1089 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1091 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1093 QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
1097 qreal dist = qAbs(data.move + pos);
1099 timeline.reset(data.move);
1100 if (fixupMode != Immediate) {
1101 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1102 data.fixingUp = true;
1104 timeline.set(data.move, -pos);
1106 vTime = timeline.time();
1109 QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
1111 data.inOvershoot = false;
1115 void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1116 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1120 data.fixingUp = false;
1122 if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) {
1123 correctFlick = true;
1124 QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1127 qreal maxDistance = 0;
1128 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1129 // -ve velocity means list is moving up/left
1131 if (data.move.value() < minExtent) {
1132 if (snapMode == QSGListView::SnapOneItem) {
1133 if (FxViewItem *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1134 maxDistance = qAbs(item->position() + dataValue);
1136 maxDistance = qAbs(minExtent - data.move.value());
1139 if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1140 data.flickTarget = minExtent;
1142 if (data.move.value() > maxExtent) {
1143 if (snapMode == QSGListView::SnapOneItem) {
1144 if (FxViewItem *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1145 maxDistance = qAbs(item->position() + dataValue);
1147 maxDistance = qAbs(maxExtent - data.move.value());
1150 if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1151 data.flickTarget = maxExtent;
1153 bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
1154 qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1155 if (maxDistance > 0 || overShoot) {
1156 // These modes require the list to stop exactly on an item boundary.
1157 // The initial flick will estimate the boundary to stop on.
1158 // Since list items can have variable sizes, the boundary will be
1159 // reevaluated and adjusted as we approach the boundary.
1161 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1167 if (!flickingHorizontally && !flickingVertically) {
1168 // the initial flick - estimate boundary
1169 qreal accel = deceleration;
1171 overshootDist = 0.0;
1172 // + averageSize/4 to encourage moving at least one item in the flick direction
1173 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1174 if (maxDistance > 0)
1175 dist = qMin(dist, maxDistance);
1178 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) {
1179 qreal distTemp = isRightToLeft() ? -dist : dist;
1180 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1181 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1183 if (data.flickTarget >= minExtent) {
1184 overshootDist = overShootDistance(vSize);
1185 data.flickTarget += overshootDist;
1186 } else if (data.flickTarget <= maxExtent) {
1187 overshootDist = overShootDistance(vSize);
1188 data.flickTarget -= overshootDist;
1191 qreal adjDist = -data.flickTarget + data.move.value();
1192 if (qAbs(adjDist) > qAbs(dist)) {
1193 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1194 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1203 accel = v2 / (2.0f * qAbs(dist));
1204 } else if (overShoot) {
1205 data.flickTarget = data.move.value() - dist;
1206 if (data.flickTarget >= minExtent) {
1207 overshootDist = overShootDistance(vSize);
1208 data.flickTarget += overshootDist;
1209 } else if (data.flickTarget <= maxExtent) {
1210 overshootDist = overShootDistance(vSize);
1211 data.flickTarget -= overshootDist;
1214 timeline.reset(data.move);
1215 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1216 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1217 if (!flickingHorizontally && q->xflick()) {
1218 flickingHorizontally = true;
1219 emit q->flickingChanged();
1220 emit q->flickingHorizontallyChanged();
1221 emit q->flickStarted();
1223 if (!flickingVertically && q->yflick()) {
1224 flickingVertically = true;
1225 emit q->flickingChanged();
1226 emit q->flickingVerticallyChanged();
1227 emit q->flickStarted();
1229 correctFlick = true;
1231 // reevaluate the target boundary.
1232 qreal newtarget = data.flickTarget;
1233 if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) {
1234 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1235 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1236 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1238 if (velocity < 0 && newtarget <= maxExtent)
1239 newtarget = maxExtent - overshootDist;
1240 else if (velocity > 0 && newtarget >= minExtent)
1241 newtarget = minExtent + overshootDist;
1242 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1243 if (qAbs(velocity) < MinimumFlickVelocity)
1244 correctFlick = false;
1247 data.flickTarget = newtarget;
1248 qreal dist = -newtarget + data.move.value();
1249 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1250 correctFlick = false;
1251 timeline.reset(data.move);
1252 fixup(data, minExtent, maxExtent);
1255 timeline.reset(data.move);
1256 timeline.accelDistance(data.move, v, -dist);
1257 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1260 correctFlick = false;
1261 timeline.reset(data.move);
1262 fixup(data, minExtent, maxExtent);
1266 //----------------------------------------------------------------------------
1268 QSGListView::QSGListView(QSGItem *parent)
1269 : QSGItemView(*(new QSGListViewPrivate), parent)
1273 QSGListView::~QSGListView()
1277 void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1280 if (d->autoHighlight != autoHighlight) {
1281 if (!autoHighlight) {
1282 if (d->highlightPosAnimator)
1283 d->highlightPosAnimator->stop();
1284 if (d->highlightSizeAnimator)
1285 d->highlightSizeAnimator->stop();
1287 QSGItemView::setHighlightFollowsCurrentItem(autoHighlight);
1291 qreal QSGListView::spacing() const
1293 Q_D(const QSGListView);
1297 void QSGListView::setSpacing(qreal spacing)
1300 if (spacing != d->spacing) {
1301 d->spacing = spacing;
1303 emit spacingChanged();
1307 QSGListView::Orientation QSGListView::orientation() const
1309 Q_D(const QSGListView);
1313 void QSGListView::setOrientation(QSGListView::Orientation orientation)
1316 if (d->orient != orientation) {
1317 d->orient = orientation;
1318 if (d->orient == Vertical) {
1319 setContentWidth(-1);
1320 setFlickableDirection(VerticalFlick);
1323 setContentHeight(-1);
1324 setFlickableDirection(HorizontalFlick);
1328 emit orientationChanged();
1332 QSGViewSection *QSGListView::sectionCriteria()
1335 if (!d->sectionCriteria) {
1336 d->sectionCriteria = new QSGViewSection(this);
1337 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
1339 return d->sectionCriteria;
1342 QString QSGListView::currentSection() const
1344 Q_D(const QSGListView);
1345 return d->currentSection;
1348 qreal QSGListView::highlightMoveSpeed() const
1350 Q_D(const QSGListView);
1351 return d->highlightMoveSpeed;
1354 void QSGListView::setHighlightMoveSpeed(qreal speed)
1357 if (d->highlightMoveSpeed != speed) {
1358 d->highlightMoveSpeed = speed;
1359 if (d->highlightPosAnimator)
1360 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
1361 emit highlightMoveSpeedChanged();
1365 void QSGListView::setHighlightMoveDuration(int duration)
1368 if (d->highlightMoveDuration != duration) {
1369 if (d->highlightPosAnimator)
1370 d->highlightPosAnimator->userDuration = duration;
1371 QSGItemView::setHighlightMoveDuration(duration);
1375 qreal QSGListView::highlightResizeSpeed() const
1377 Q_D(const QSGListView);
1378 return d->highlightResizeSpeed;
1381 void QSGListView::setHighlightResizeSpeed(qreal speed)
1384 if (d->highlightResizeSpeed != speed) {
1385 d->highlightResizeSpeed = speed;
1386 if (d->highlightSizeAnimator)
1387 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
1388 emit highlightResizeSpeedChanged();
1392 int QSGListView::highlightResizeDuration() const
1394 Q_D(const QSGListView);
1395 return d->highlightResizeDuration;
1398 void QSGListView::setHighlightResizeDuration(int duration)
1401 if (d->highlightResizeDuration != duration) {
1402 d->highlightResizeDuration = duration;
1403 if (d->highlightSizeAnimator)
1404 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
1405 emit highlightResizeDurationChanged();
1409 QSGListView::SnapMode QSGListView::snapMode() const
1411 Q_D(const QSGListView);
1415 void QSGListView::setSnapMode(SnapMode mode)
1418 if (d->snapMode != mode) {
1420 emit snapModeChanged();
1424 void QSGListView::viewportMoved()
1427 QSGItemView::viewportMoved();
1430 // Recursion can occur due to refill changing the content size.
1431 if (d->inViewportMoved)
1433 d->inViewportMoved = true;
1434 d->lazyRelease = true;
1436 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
1437 d->moveReason = QSGListViewPrivate::Mouse;
1438 if (d->moveReason != QSGListViewPrivate::SetIndex) {
1439 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
1440 // reposition highlight
1441 qreal pos = d->highlight->position();
1443 qreal highlightStart;
1445 if (d->isRightToLeft()) {
1446 // Handle Right-To-Left exceptions
1447 viewPos = -d->position()-d->size();
1448 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
1449 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
1451 viewPos = d->position();
1452 highlightStart = d->highlightRangeStart;
1453 highlightEnd = d->highlightRangeEnd;
1455 if (pos > viewPos + highlightEnd - d->highlight->size())
1456 pos = viewPos + highlightEnd - d->highlight->size();
1457 if (pos < viewPos + highlightStart)
1458 pos = viewPos + highlightStart;
1459 if (pos != d->highlight->position()) {
1460 d->highlightPosAnimator->stop();
1461 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
1463 d->updateHighlight();
1466 // update current index
1467 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
1468 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
1469 d->updateCurrent(snapItem->index);
1474 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
1475 d->inFlickCorrection = true;
1476 // Near an end and it seems that the extent has changed?
1477 // Recalculate the flick so that we don't end up in an odd position.
1478 if (yflick() && !d->vData.inOvershoot) {
1479 if (d->vData.velocity > 0) {
1480 const qreal minY = minYExtent();
1481 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
1482 && minY != d->vData.flickTarget)
1483 d->flickY(-d->vData.smoothVelocity.value());
1484 d->bufferMode = QSGListViewPrivate::BufferBefore;
1485 } else if (d->vData.velocity < 0) {
1486 const qreal maxY = maxYExtent();
1487 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
1488 && maxY != d->vData.flickTarget)
1489 d->flickY(-d->vData.smoothVelocity.value());
1490 d->bufferMode = QSGListViewPrivate::BufferAfter;
1494 if (xflick() && !d->hData.inOvershoot) {
1495 if (d->hData.velocity > 0) {
1496 const qreal minX = minXExtent();
1497 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
1498 && minX != d->hData.flickTarget)
1499 d->flickX(-d->hData.smoothVelocity.value());
1500 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore;
1501 } else if (d->hData.velocity < 0) {
1502 const qreal maxX = maxXExtent();
1503 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
1504 && maxX != d->hData.flickTarget)
1505 d->flickX(-d->hData.smoothVelocity.value());
1506 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter;
1509 d->inFlickCorrection = false;
1511 d->inViewportMoved = false;
1514 void QSGListView::keyPressEvent(QKeyEvent *event)
1517 if (d->model && d->model->count() && d->interactive) {
1518 if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
1519 || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
1520 || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) {
1521 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
1522 decrementCurrentIndex();
1525 } else if (d->wrap) {
1529 } else if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
1530 || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
1531 || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) {
1532 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
1533 incrementCurrentIndex();
1536 } else if (d->wrap) {
1543 QSGItemView::keyPressEvent(event);
1546 void QSGListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1549 if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) {
1550 // maintain position relative to the right edge
1551 int dx = newGeometry.width() - oldGeometry.width();
1552 setContentX(contentX() - dx);
1554 QSGItemView::geometryChanged(newGeometry, oldGeometry);
1558 void QSGListView::incrementCurrentIndex()
1561 int count = d->model ? d->model->count() : 0;
1562 if (count && (currentIndex() < count - 1 || d->wrap)) {
1563 d->moveReason = QSGListViewPrivate::SetIndex;
1564 int index = currentIndex()+1;
1565 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1569 void QSGListView::decrementCurrentIndex()
1572 int count = d->model ? d->model->count() : 0;
1573 if (count && (currentIndex() > 0 || d->wrap)) {
1574 d->moveReason = QSGListViewPrivate::SetIndex;
1575 int index = currentIndex()-1;
1576 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1580 void QSGListView::updateSections()
1583 if (isComponentComplete() && d->model) {
1584 QList<QByteArray> roles;
1585 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
1586 roles << d->sectionCriteria->property().toUtf8();
1587 d->model->setWatchedRoles(roles);
1588 d->updateSections();
1594 void QSGListView::itemsInserted(int modelIndex, int count)
1597 if (!isComponentComplete() || !d->model || !d->model->isValid())
1599 d->updateUnrequestedIndexes();
1600 d->moveReason = QSGListViewPrivate::Other;
1602 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
1603 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
1605 int i = d->visibleItems.count() - 1;
1606 while (i > 0 && d->visibleItems.at(i)->index == -1)
1608 if (i == 0 && d->visibleItems.first()->index == -1) {
1609 // there are no visible items except items marked for removal
1610 index = d->visibleItems.count();
1611 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
1612 && d->visibleItems.at(i)->endPosition() <= d->buffer+tempPos+d->size()) {
1613 // Special case of appending an item to the model.
1614 index = d->visibleItems.count();
1616 if (modelIndex < d->visibleIndex) {
1617 // Insert before visible items
1618 d->visibleIndex += count;
1619 for (int i = 0; i < d->visibleItems.count(); ++i) {
1620 FxViewItem *item = d->visibleItems.at(i);
1621 if (item->index != -1 && item->index >= modelIndex)
1622 item->index += count;
1625 if (d->currentIndex >= modelIndex) {
1626 // adjust current item index
1627 d->currentIndex += count;
1629 d->currentItem->index = d->currentIndex;
1630 emit currentIndexChanged();
1632 d->scheduleLayout();
1633 d->itemCount += count;
1634 emit countChanged();
1639 // index can be the next item past the end of the visible items list (i.e. appended)
1641 if (d->visibleItems.count()) {
1642 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
1643 : d->visibleItems.last()->endPosition()+d->spacing;
1646 int initialPos = pos;
1648 QList<FxListItemSG*> added;
1649 bool addedVisible = false;
1650 FxViewItem *firstVisible = d->firstVisibleItem();
1651 if (firstVisible && pos < firstVisible->position()) {
1652 // Insert items before the visible item.
1653 int insertionIdx = index;
1655 int from = tempPos - d->buffer;
1656 for (i = count-1; i >= 0 && pos > from; --i) {
1657 if (!addedVisible) {
1658 d->scheduleLayout();
1659 addedVisible = true;
1661 FxListItemSG *item = static_cast<FxListItemSG*>(d->createItem(modelIndex + i));
1662 d->visibleItems.insert(insertionIdx, item);
1663 pos -= item->size() + d->spacing;
1664 item->setPosition(pos);
1668 // If we didn't insert all our new items - anything
1669 // before the current index is not visible - remove it.
1670 while (insertionIdx--) {
1671 FxListItemSG *item = static_cast<FxListItemSG*>(d->visibleItems.takeFirst());
1672 if (item->index != -1)
1674 d->releaseItem(item);
1677 // adjust pos of items before inserted items.
1678 for (int i = insertionIdx-1; i >= 0; i--) {
1679 FxListItemSG *listItem = static_cast<FxListItemSG*>(d->visibleItems.at(i));
1680 listItem->setPosition(listItem->position() - (initialPos - pos));
1685 int to = d->buffer+tempPos+d->size();
1686 for (i = 0; i < count && pos <= to; ++i) {
1687 if (!addedVisible) {
1688 d->scheduleLayout();
1689 addedVisible = true;
1691 FxListItemSG *item = static_cast<FxListItemSG*>(d->createItem(modelIndex + i));
1692 d->visibleItems.insert(index, item);
1693 item->setPosition(pos);
1695 pos += item->size() + d->spacing;
1699 // We didn't insert all our new items, which means anything
1700 // beyond the current index is not visible - remove it.
1701 while (d->visibleItems.count() > index)
1702 d->releaseItem(d->visibleItems.takeLast());
1704 diff = pos - initialPos;
1706 if (d->itemCount && d->currentIndex >= modelIndex) {
1707 // adjust current item index
1708 d->currentIndex += count;
1709 if (d->currentItem) {
1710 d->currentItem->index = d->currentIndex;
1711 static_cast<FxListItemSG *>(d->currentItem)->setPosition(static_cast<FxListItemSG *>(d->currentItem)->position() + diff);
1713 emit currentIndexChanged();
1714 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
1715 d->updateCurrent(0);
1717 // Update the indexes of the following visible items.
1718 for (; index < d->visibleItems.count(); ++index) {
1719 FxViewItem *item = d->visibleItems.at(index);
1720 if (d->currentItem && item->item != d->currentItem->item)
1721 static_cast<FxListItemSG*>(item)->setPosition(item->position() + diff);
1722 if (item->index != -1)
1723 item->index += count;
1725 // everything is in order now - emit add() signal
1726 for (int j = 0; j < added.count(); ++j)
1727 added.at(j)->attached->emitAdd();
1729 d->updateSections();
1730 d->itemCount += count;
1731 emit countChanged();
1734 void QSGListView::itemsRemoved(int modelIndex, int count)
1737 if (!isComponentComplete() || !d->model || !d->model->isValid())
1739 d->moveReason = QSGListViewPrivate::Other;
1740 d->updateUnrequestedIndexes();
1741 d->itemCount -= count;
1743 FxViewItem *firstVisible = d->firstVisibleItem();
1744 int preRemovedSize = 0;
1745 bool removedVisible = false;
1746 // Remove the items from the visible list, skipping anything already marked for removal
1747 QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1748 while (it != d->visibleItems.end()) {
1749 FxViewItem *item = *it;
1750 if (item->index == -1 || item->index < modelIndex) {
1751 // already removed, or before removed items
1753 } else if (item->index >= modelIndex + count) {
1754 // after removed items
1755 item->index -= count;
1759 if (!removedVisible) {
1760 d->scheduleLayout();
1761 removedVisible = true;
1763 item->attached->emitRemove();
1764 if (item->attached->delayRemove()) {
1766 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
1769 if (item == firstVisible)
1771 if (firstVisible && item->position() < firstVisible->position())
1772 preRemovedSize += item->size();
1773 it = d->visibleItems.erase(it);
1774 d->releaseItem(item);
1779 if (firstVisible && d->visibleItems.first() != firstVisible)
1780 static_cast<FxListItemSG*>(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + preRemovedSize);
1782 // update visibleIndex
1783 bool haveVisibleIndex = false;
1784 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1785 if ((*it)->index != -1) {
1786 d->visibleIndex = (*it)->index;
1787 haveVisibleIndex = true;
1793 if (d->currentIndex >= modelIndex + count) {
1794 d->currentIndex -= count;
1796 d->currentItem->index -= count;
1797 emit currentIndexChanged();
1798 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1799 // current item has been removed.
1800 if (d->currentItem) {
1801 d->currentItem->attached->setIsCurrentItem(false);
1802 d->releaseItem(d->currentItem);
1805 d->currentIndex = -1;
1807 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
1809 emit currentIndexChanged();
1812 if (!haveVisibleIndex) {
1813 d->timeline.clear();
1814 if (removedVisible && d->itemCount == 0) {
1815 d->visibleIndex = 0;
1817 d->setPosition(d->contentStartPosition());
1821 if (modelIndex < d->visibleIndex)
1822 d->visibleIndex = modelIndex+1;
1823 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
1827 d->updateSections();
1828 emit countChanged();
1831 void QSGListView::itemsMoved(int from, int to, int count)
1834 if (!isComponentComplete() || !d->isValid())
1836 d->updateUnrequestedIndexes();
1838 if (d->visibleItems.isEmpty()) {
1843 d->moveReason = QSGListViewPrivate::Other;
1845 bool movingBackwards = from > to;
1846 d->adjustMoveParameters(&from, &to, &count);
1848 QHash<int,FxViewItem*> moved;
1850 FxViewItem *firstVisible = d->firstVisibleItem();
1851 int firstItemIndex = firstVisible ? firstVisible->index : -1;
1853 // if visibleItems.first() is above the content start pos, and the items
1854 // beneath it are moved, ensure this first item is later repositioned correctly
1855 // (to above the next visible item) so that subsequent layout() is correct
1856 bool repositionFirstItem = firstVisible
1857 && d->visibleItems.first()->position() < firstVisible->position()
1858 && from > d->visibleItems.first()->index;
1860 QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1861 while (it != d->visibleItems.end()) {
1862 FxViewItem *item = *it;
1863 if (item->index >= from && item->index < from + count) {
1864 // take the items that are moving
1865 item->index += (to-from);
1866 moved.insert(item->index, item);
1867 if (repositionFirstItem)
1868 moveBy += item->size();
1869 it = d->visibleItems.erase(it);
1871 // move everything after the moved items.
1872 if (item->index > from && item->index != -1)
1873 item->index -= count;
1879 int endIndex = d->visibleIndex;
1880 it = d->visibleItems.begin();
1881 while (it != d->visibleItems.end()) {
1882 FxViewItem *item = *it;
1883 if (movedCount < count && item->index >= to && item->index < to + count) {
1884 // place items in the target position, reusing any existing items
1885 int targetIndex = item->index + movedCount;
1886 FxViewItem *movedItem = moved.take(targetIndex);
1888 movedItem = d->createItem(targetIndex);
1889 it = d->visibleItems.insert(it, movedItem);
1893 if (item->index != -1) {
1894 if (item->index >= to) {
1895 // update everything after the moved items.
1896 item->index += count;
1898 endIndex = item->index;
1904 // If we have moved items to the end of the visible items
1905 // then add any existing moved items that we have
1906 while (FxViewItem *item = moved.take(endIndex+1)) {
1907 d->visibleItems.append(item);
1911 // update visibleIndex
1912 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1913 if ((*it)->index != -1) {
1914 d->visibleIndex = (*it)->index;
1919 // if first visible item is moving but another item is moving up to replace it,
1920 // do this positioning now to avoid shifting all content forwards
1921 if (movingBackwards && firstItemIndex >= 0) {
1922 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1923 if ((*it)->index == firstItemIndex) {
1924 static_cast<FxListItemSG*>(*it)->setPosition(firstVisible->position());
1930 // Fix current index
1931 if (d->currentIndex >= 0 && d->currentItem) {
1932 int oldCurrent = d->currentIndex;
1933 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
1934 if (oldCurrent != d->currentIndex) {
1935 d->currentItem->index = d->currentIndex;
1936 emit currentIndexChanged();
1940 // Whatever moved items remain are no longer visible items.
1941 while (moved.count()) {
1942 int idx = moved.begin().key();
1943 FxViewItem *item = moved.take(idx);
1944 if (d->currentItem && item->item == d->currentItem->item)
1945 static_cast<FxListItemSG*>(item)->setPosition(d->positionAt(idx));
1946 d->releaseItem(item);
1949 // Ensure we don't cause an ugly list scroll.
1950 if (d->visibleItems.count())
1951 static_cast<FxListItemSG*>(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + moveBy);
1953 d->updateSections();
1958 QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
1960 return new QSGListViewAttached(obj);