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 void updateHeader();
227 virtual void updateFooter();
229 virtual void changedVisibleIndex(int newIndex);
230 virtual void initializeCurrentItem();
232 void updateAverage();
234 void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
235 virtual void fixupPosition();
236 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
237 virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
238 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
240 QSGListView::Orientation orient;
244 QSGListView::SnapMode snapMode;
246 QSmoothedAnimation *highlightPosAnimator;
247 QSmoothedAnimation *highlightSizeAnimator;
248 qreal highlightMoveSpeed;
249 qreal highlightResizeSpeed;
250 int highlightResizeDuration;
252 QSGViewSection *sectionCriteria;
253 QString currentSection;
254 static const int sectionCacheSize = 4;
255 QSGItem *sectionCache[sectionCacheSize];
258 bool correctFlick : 1;
259 bool inFlickCorrection : 1;
262 : orient(QSGListView::Vertical)
264 , averageSize(100.0), spacing(0.0)
265 , snapMode(QSGListView::NoSnap)
266 , highlightPosAnimator(0), highlightSizeAnimator(0)
267 , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
269 , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
273 bool QSGListViewPrivate::isContentFlowReversed() const
275 return isRightToLeft();
278 Qt::Orientation QSGListViewPrivate::layoutOrientation() const
280 return static_cast<Qt::Orientation>(orient);
283 bool QSGListViewPrivate::isRightToLeft() const
285 Q_Q(const QSGListView);
286 return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
289 FxViewItem *QSGListViewPrivate::nextVisibleItem() const
291 const qreal pos = isRightToLeft() ? -position()-size() : position();
292 bool foundFirst = false;
293 for (int i = 0; i < visibleItems.count(); ++i) {
294 FxViewItem *item = visibleItems.at(i);
295 if (item->index != -1) {
298 else if (item->position() < pos && item->endPosition() >= pos)
305 // Returns the item before modelIndex, if created.
306 // May return an item marked for removal.
307 FxViewItem *QSGListViewPrivate::itemBefore(int modelIndex) const
309 if (modelIndex < visibleIndex)
313 while (idx < visibleItems.count()) {
314 FxViewItem *item = visibleItems.at(idx);
315 if (item->index != -1)
316 lastIndex = item->index;
317 if (item->index == modelIndex)
318 return visibleItems.at(idx-1);
321 if (lastIndex == modelIndex-1)
322 return visibleItems.last();
326 void QSGListViewPrivate::setPosition(qreal pos)
329 if (orient == QSGListView::Vertical) {
330 q->QSGFlickable::setContentY(pos);
333 q->QSGFlickable::setContentX(-pos-size());
335 q->QSGFlickable::setContentX(pos);
339 qreal QSGListViewPrivate::originPosition() const
342 if (!visibleItems.isEmpty()) {
343 pos = (*visibleItems.constBegin())->position();
344 if (visibleIndex > 0)
345 pos -= visibleIndex * (averageSize + spacing);
350 qreal QSGListViewPrivate::lastPosition() const
353 if (!visibleItems.isEmpty()) {
354 int invisibleCount = visibleItems.count() - visibleIndex;
355 for (int i = visibleItems.count()-1; i >= 0; --i) {
356 if (visibleItems.at(i)->index != -1) {
357 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
361 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
362 } else if (model && model->count()) {
363 pos = (model->count() * averageSize + (model->count()-1) * spacing);
368 qreal QSGListViewPrivate::positionAt(int modelIndex) const
370 if (FxViewItem *item = visibleItem(modelIndex))
371 return item->position();
372 if (!visibleItems.isEmpty()) {
373 if (modelIndex < visibleIndex) {
374 int count = visibleIndex - modelIndex;
376 if (modelIndex == currentIndex && currentItem) {
377 cs = currentItem->size() + spacing;
380 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
382 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
383 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
389 qreal QSGListViewPrivate::endPositionAt(int modelIndex) const
391 if (FxViewItem *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;
398 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
399 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
405 QString QSGListViewPrivate::sectionAt(int modelIndex)
407 if (FxViewItem *item = visibleItem(modelIndex))
408 return item->attached->section();
411 if (sectionCriteria) {
412 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
413 section = sectionCriteria->sectionString(propValue);
419 qreal QSGListViewPrivate::snapPosAt(qreal pos)
421 if (FxViewItem *snapItem = snapItemAt(pos))
422 return snapItem->position();
423 if (visibleItems.count()) {
424 qreal firstPos = (*visibleItems.constBegin())->position();
425 qreal endPos = (*(--visibleItems.constEnd()))->position();
426 if (pos < firstPos) {
427 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
428 } else if (pos > endPos)
429 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
431 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
434 FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos)
436 FxViewItem *snapItem = 0;
437 qreal prevItemSize = 0;
438 for (int i = 0; i < visibleItems.count(); ++i) {
439 FxViewItem *item = visibleItems.at(i);
440 if (item->index == -1)
442 qreal itemTop = item->position();
443 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
445 if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
447 prevItemSize = item->size();
452 void QSGListViewPrivate::changedVisibleIndex(int newIndex)
454 visiblePos = positionAt(newIndex);
455 visibleIndex = newIndex;
458 void QSGListViewPrivate::init()
460 QSGItemViewPrivate::init();
461 ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
464 void QSGListViewPrivate::clear()
466 for (int i = 0; i < sectionCacheSize; ++i) {
467 delete sectionCache[i];
471 QSGItemViewPrivate::clear();
474 FxViewItem *QSGListViewPrivate::newViewItem(int modelIndex, QSGItem *item)
478 FxListItemSG *listItem = new FxListItemSG(item, q, false);
479 listItem->index = modelIndex;
481 // initialise attached properties
482 if (sectionCriteria) {
483 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
484 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
485 if (modelIndex > 0) {
486 if (FxViewItem *item = itemBefore(modelIndex))
487 listItem->attached->m_prevSection = item->attached->section();
489 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
491 if (modelIndex < model->count()-1) {
492 if (FxViewItem *item = visibleItem(modelIndex+1))
493 listItem->attached->m_nextSection = static_cast<QSGListViewAttached*>(item->attached)->section();
495 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
502 void QSGListViewPrivate::initializeViewItem(FxViewItem *item)
504 QSGItemViewPrivate::initializeViewItem(item);
506 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
507 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
509 if (sectionCriteria && sectionCriteria->delegate()) {
510 if (item->attached->m_prevSection != item->attached->m_section)
511 createSection(static_cast<FxListItemSG*>(item));
515 void QSGListViewPrivate::releaseItem(FxViewItem *item)
518 FxListItemSG* listItem = static_cast<FxListItemSG*>(item);
519 if (listItem->section) {
522 if (!sectionCache[i]) {
523 sectionCache[i] = listItem->section;
524 sectionCache[i]->setVisible(false);
525 listItem->section = 0;
529 } while (i < sectionCacheSize);
530 delete listItem->section;
533 QSGItemViewPrivate::releaseItem(item);
536 bool QSGListViewPrivate::addVisibleItems(int fillFrom, int fillTo, bool doBuffer)
538 qreal itemEnd = visiblePos;
539 if (visibleItems.count()) {
540 visiblePos = (*visibleItems.constBegin())->position();
541 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
544 int modelIndex = findLastVisibleIndex();
545 bool haveValidItems = modelIndex >= 0;
546 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
548 if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
549 || fillTo < visiblePos - averageSize - spacing)) {
550 // We've jumped more than a page. Estimate which items are now
551 // visible and fill from there.
552 int count = (fillFrom - itemEnd) / (averageSize + spacing);
553 for (int i = 0; i < visibleItems.count(); ++i)
554 releaseItem(visibleItems.at(i));
555 visibleItems.clear();
557 if (modelIndex >= model->count()) {
558 count -= modelIndex - model->count() + 1;
559 modelIndex = model->count() - 1;
560 } else if (modelIndex < 0) {
564 visibleIndex = modelIndex;
565 visiblePos = itemEnd + count * (averageSize + spacing);
566 itemEnd = visiblePos;
569 bool changed = false;
570 FxListItemSG *item = 0;
572 while (modelIndex < model->count() && pos <= fillTo) {
573 // qDebug() << "refill: append item" << modelIndex << "pos" << pos;
574 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex))))
576 item->setPosition(pos);
577 pos += item->size() + spacing;
578 visibleItems.append(item);
581 if (doBuffer) // never buffer more than one item per frame
584 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos >= fillFrom) {
585 // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
586 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1))))
589 visiblePos -= item->size() + spacing;
590 item->setPosition(visiblePos);
591 visibleItems.prepend(item);
593 if (doBuffer) // never buffer more than one item per frame
600 bool QSGListViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo)
602 FxViewItem *item = 0;
603 bool changed = false;
605 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() <= bufferFrom) {
606 if (item->attached->delayRemove())
608 if (item->size() == 0)
610 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
611 if (item->index != -1)
613 visibleItems.removeFirst();
617 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
618 if (item->attached->delayRemove())
620 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
621 visibleItems.removeLast();
629 void QSGListViewPrivate::visibleItemsChanged()
631 if (visibleItems.count())
632 visiblePos = (*visibleItems.constBegin())->position();
634 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
635 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
639 updateCurrentSection();
643 updateUnrequestedPositions();
646 void QSGListViewPrivate::layoutVisibleItems()
648 if (!visibleItems.isEmpty()) {
649 bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item;
650 qreal sum = (*visibleItems.constBegin())->size();
651 qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing;
652 for (int i=1; i < visibleItems.count(); ++i) {
653 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
654 item->setPosition(pos);
655 pos += item->size() + spacing;
657 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
659 averageSize = qRound(sum / visibleItems.count());
661 // move current item if it is not a visible item.
662 if (currentIndex >= 0 && currentItem && !fixedCurrent) {
663 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
668 void QSGListViewPrivate::repositionPackageItemAt(QSGItem *item, int index)
671 qreal pos = position();
672 if (orient == QSGListView::Vertical) {
673 if (item->y() + item->height() > pos && item->y() < pos + q->height())
674 item->setY(positionAt(index));
676 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
678 item->setX(-positionAt(index)-item->width());
680 item->setX(positionAt(index));
685 void QSGListViewPrivate::createHighlight()
688 bool changed = false;
690 if (trackedItem == highlight)
695 delete highlightPosAnimator;
696 delete highlightSizeAnimator;
697 highlightPosAnimator = 0;
698 highlightSizeAnimator = 0;
704 QSGItem *item = createHighlightItem();
706 FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
709 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
710 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
712 const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x");
713 highlightPosAnimator = new QSmoothedAnimation(q);
714 highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
715 highlightPosAnimator->velocity = highlightMoveSpeed;
716 highlightPosAnimator->userDuration = highlightMoveDuration;
718 const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width");
719 highlightSizeAnimator = new QSmoothedAnimation(q);
720 highlightSizeAnimator->velocity = highlightResizeSpeed;
721 highlightSizeAnimator->userDuration = highlightResizeDuration;
722 highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
724 highlight = newHighlight;
729 emit q->highlightItemChanged();
732 void QSGListViewPrivate::updateHighlight()
734 if ((!currentItem && highlight) || (currentItem && !highlight))
736 bool strictHighlight = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange;
737 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
738 // auto-update highlight
739 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
740 highlightPosAnimator->to = isRightToLeft()
741 ? -listItem->itemPosition()-listItem->itemSize()
742 : listItem->itemPosition();
743 highlightSizeAnimator->to = listItem->itemSize();
744 if (orient == QSGListView::Vertical) {
745 if (highlight->item->width() == 0)
746 highlight->item->setWidth(currentItem->item->width());
748 if (highlight->item->height() == 0)
749 highlight->item->setHeight(currentItem->item->height());
752 highlightPosAnimator->restart();
753 highlightSizeAnimator->restart();
758 void QSGListViewPrivate::resetHighlightPosition()
760 if (highlight && currentItem)
761 static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
764 void QSGListViewPrivate::createSection(FxListItemSG *listItem)
767 if (!sectionCriteria || !sectionCriteria->delegate())
769 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
770 if (!listItem->section) {
771 qreal pos = listItem->position();
772 int i = sectionCacheSize-1;
773 while (i >= 0 && !sectionCache[i])
776 listItem->section = sectionCache[i];
778 listItem->section->setVisible(true);
779 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
780 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
782 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
783 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
784 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
786 QDeclarative_setParent_noEvent(context, nobj);
787 listItem->section = qobject_cast<QSGItem *>(nobj);
788 if (!listItem->section) {
791 listItem->section->setZ(1);
792 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
793 listItem->section->setParentItem(q->contentItem());
798 sectionCriteria->delegate()->completeCreate();
800 listItem->setPosition(pos);
802 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
803 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
805 } else if (listItem->section) {
806 qreal pos = listItem->position();
809 if (!sectionCache[i]) {
810 sectionCache[i] = listItem->section;
811 sectionCache[i]->setVisible(false);
812 listItem->section = 0;
816 } while (i < sectionCacheSize);
817 delete listItem->section;
818 listItem->section = 0;
819 listItem->setPosition(pos);
823 void QSGListViewPrivate::updateSections()
825 QSGItemViewPrivate::updateSections();
827 if (sectionCriteria && !visibleItems.isEmpty()) {
829 if (visibleIndex > 0)
830 prevSection = sectionAt(visibleIndex-1);
831 QSGListViewAttached *prevAtt = 0;
833 for (int i = 0; i < visibleItems.count(); ++i) {
834 QSGListViewAttached *attached = static_cast<QSGListViewAttached*>(visibleItems.at(i)->attached);
835 attached->setPrevSection(prevSection);
836 if (visibleItems.at(i)->index != -1) {
837 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
838 attached->setSection(sectionCriteria->sectionString(propValue));
839 idx = visibleItems.at(i)->index;
841 createSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
843 prevAtt->setNextSection(attached->section());
844 prevSection = attached->section();
848 if (idx > 0 && idx < model->count()-1)
849 prevAtt->setNextSection(sectionAt(idx+1));
851 prevAtt->setNextSection(QString());
856 void QSGListViewPrivate::updateCurrentSection()
859 if (!sectionCriteria || visibleItems.isEmpty()) {
860 if (!currentSection.isEmpty()) {
861 currentSection.clear();
862 emit q->currentSectionChanged();
867 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= position())
870 QString newSection = currentSection;
871 if (index < visibleItems.count())
872 newSection = visibleItems.at(index)->attached->section();
874 newSection = (*visibleItems.constBegin())->attached->section();
875 if (newSection != currentSection) {
876 currentSection = newSection;
877 emit q->currentSectionChanged();
881 void QSGListViewPrivate::initializeCurrentItem()
883 QSGItemViewPrivate::initializeCurrentItem();
886 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
888 if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
889 // We can calculate exact postion in this case
890 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
892 // Create current item now and position as best we can.
893 // Its position will be corrected when it becomes visible.
894 listItem->setPosition(positionAt(currentIndex));
897 // Avoid showing section delegate twice. We still need the section heading so that
898 // currentItem positioning works correctly.
899 // This is slightly sub-optimal, but section heading caching minimizes the impact.
900 if (listItem->section)
901 listItem->section->setVisible(false);
903 if (visibleItems.isEmpty())
904 averageSize = listItem->size();
908 void QSGListViewPrivate::updateAverage()
910 if (!visibleItems.count())
913 for (int i = 0; i < visibleItems.count(); ++i)
914 sum += visibleItems.at(i)->size();
915 averageSize = qRound(sum / visibleItems.count());
918 qreal QSGListViewPrivate::headerSize() const
920 return header ? header->size() : 0.0;
923 qreal QSGListViewPrivate::footerSize() const
925 return footer ? footer->size() : 0.0;
928 void QSGListViewPrivate::updateFooter()
931 bool created = false;
933 QSGItem *item = createComponentItem(footerComponent, true);
937 footer = new FxListItemSG(item, q, true);
941 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
942 if (visibleItems.count()) {
943 qreal endPos = lastPosition();
944 if (findLastVisibleIndex() == model->count()-1) {
945 listItem->setPosition(endPos);
947 qreal visiblePos = position() + q->height();
948 if (endPos <= visiblePos || listItem->position() < endPos)
949 listItem->setPosition(endPos);
952 listItem->setPosition(visiblePos);
956 emit q->footerItemChanged();
959 void QSGListViewPrivate::updateHeader()
962 bool created = false;
964 QSGItem *item = createComponentItem(headerComponent, true);
968 header = new FxListItemSG(item, q, true);
972 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
974 if (visibleItems.count()) {
975 qreal startPos = originPosition();
976 if (visibleIndex == 0) {
977 listItem->setPosition(startPos - headerSize());
979 if (position() <= startPos || listItem->position() > startPos - headerSize())
980 listItem->setPosition(startPos - headerSize());
983 listItem->setPosition(-headerSize());
988 emit q->headerItemChanged();
991 void QSGListViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
994 QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
995 if (!q->isComponentComplete())
997 if (item != contentItem && (!highlight || item != highlight->item)) {
998 if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height())
999 || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1005 void QSGListViewPrivate::fixupPosition()
1007 if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange)
1008 || snapMode != QSGListView::NoSnap)
1010 if (orient == QSGListView::Vertical)
1016 void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1018 if ((orient == QSGListView::Horizontal && &data == &vData)
1019 || (orient == QSGListView::Vertical && &data == &hData))
1022 correctFlick = false;
1023 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1025 qreal highlightStart;
1028 if (isRightToLeft()) {
1029 // Handle Right-To-Left exceptions
1030 viewPos = -position()-size();
1031 highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1032 highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1034 viewPos = position();
1035 highlightStart = highlightRangeStart;
1036 highlightEnd = highlightRangeEnd;
1039 if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange
1040 && moveReason != QSGListViewPrivate::SetIndex) {
1042 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1043 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightEnd)
1044 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightEnd;
1045 if (viewPos > pos - highlightStart)
1046 viewPos = pos - highlightStart;
1047 if (isRightToLeft())
1048 viewPos = -viewPos-size();
1050 timeline.reset(data.move);
1051 if (viewPos != position()) {
1052 if (fixupMode != Immediate) {
1053 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1054 data.fixingUp = true;
1056 timeline.set(data.move, -viewPos);
1059 vTime = timeline.time();
1060 } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) {
1061 qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1062 FxViewItem *topItem = snapItemAt(tempPosition+highlightStart);
1063 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1065 bool isInBounds = -position() > maxExtent && -position() < minExtent;
1066 if (topItem && isInBounds) {
1067 if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+headerSize()/2) {
1068 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1070 if (isRightToLeft())
1071 pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1073 pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1075 } else if (bottomItem && isInBounds) {
1076 if (isRightToLeft())
1077 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1079 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1081 QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
1085 qreal dist = qAbs(data.move + pos);
1087 timeline.reset(data.move);
1088 if (fixupMode != Immediate) {
1089 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1090 data.fixingUp = true;
1092 timeline.set(data.move, -pos);
1094 vTime = timeline.time();
1097 QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
1099 data.inOvershoot = false;
1103 void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1104 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1108 data.fixingUp = false;
1110 if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) {
1111 correctFlick = true;
1112 QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1115 qreal maxDistance = 0;
1116 qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1117 // -ve velocity means list is moving up/left
1119 if (data.move.value() < minExtent) {
1120 if (snapMode == QSGListView::SnapOneItem) {
1121 if (FxViewItem *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1122 maxDistance = qAbs(item->position() + dataValue);
1124 maxDistance = qAbs(minExtent - data.move.value());
1127 if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1128 data.flickTarget = minExtent;
1130 if (data.move.value() > maxExtent) {
1131 if (snapMode == QSGListView::SnapOneItem) {
1132 if (FxViewItem *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1133 maxDistance = qAbs(item->position() + dataValue);
1135 maxDistance = qAbs(maxExtent - data.move.value());
1138 if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1139 data.flickTarget = maxExtent;
1141 bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
1142 qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1143 if (maxDistance > 0 || overShoot) {
1144 // These modes require the list to stop exactly on an item boundary.
1145 // The initial flick will estimate the boundary to stop on.
1146 // Since list items can have variable sizes, the boundary will be
1147 // reevaluated and adjusted as we approach the boundary.
1149 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1155 if (!flickingHorizontally && !flickingVertically) {
1156 // the initial flick - estimate boundary
1157 qreal accel = deceleration;
1159 overshootDist = 0.0;
1160 // + averageSize/4 to encourage moving at least one item in the flick direction
1161 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1162 if (maxDistance > 0)
1163 dist = qMin(dist, maxDistance);
1166 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) {
1167 qreal distTemp = isRightToLeft() ? -dist : dist;
1168 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1169 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1171 if (data.flickTarget >= minExtent) {
1172 overshootDist = overShootDistance(vSize);
1173 data.flickTarget += overshootDist;
1174 } else if (data.flickTarget <= maxExtent) {
1175 overshootDist = overShootDistance(vSize);
1176 data.flickTarget -= overshootDist;
1179 qreal adjDist = -data.flickTarget + data.move.value();
1180 if (qAbs(adjDist) > qAbs(dist)) {
1181 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1182 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1191 accel = v2 / (2.0f * qAbs(dist));
1192 } else if (overShoot) {
1193 data.flickTarget = data.move.value() - dist;
1194 if (data.flickTarget >= minExtent) {
1195 overshootDist = overShootDistance(vSize);
1196 data.flickTarget += overshootDist;
1197 } else if (data.flickTarget <= maxExtent) {
1198 overshootDist = overShootDistance(vSize);
1199 data.flickTarget -= overshootDist;
1202 timeline.reset(data.move);
1203 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1204 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1205 if (!flickingHorizontally && q->xflick()) {
1206 flickingHorizontally = true;
1207 emit q->flickingChanged();
1208 emit q->flickingHorizontallyChanged();
1209 emit q->flickStarted();
1211 if (!flickingVertically && q->yflick()) {
1212 flickingVertically = true;
1213 emit q->flickingChanged();
1214 emit q->flickingVerticallyChanged();
1215 emit q->flickStarted();
1217 correctFlick = true;
1219 // reevaluate the target boundary.
1220 qreal newtarget = data.flickTarget;
1221 if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) {
1222 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1223 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1224 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1226 if (velocity < 0 && newtarget <= maxExtent)
1227 newtarget = maxExtent - overshootDist;
1228 else if (velocity > 0 && newtarget >= minExtent)
1229 newtarget = minExtent + overshootDist;
1230 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1231 if (qAbs(velocity) < MinimumFlickVelocity)
1232 correctFlick = false;
1235 data.flickTarget = newtarget;
1236 qreal dist = -newtarget + data.move.value();
1237 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1238 correctFlick = false;
1239 timeline.reset(data.move);
1240 fixup(data, minExtent, maxExtent);
1243 timeline.reset(data.move);
1244 timeline.accelDistance(data.move, v, -dist);
1245 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1248 correctFlick = false;
1249 timeline.reset(data.move);
1250 fixup(data, minExtent, maxExtent);
1254 //----------------------------------------------------------------------------
1256 QSGListView::QSGListView(QSGItem *parent)
1257 : QSGItemView(*(new QSGListViewPrivate), parent)
1261 QSGListView::~QSGListView()
1265 void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1268 if (d->autoHighlight != autoHighlight) {
1269 if (!autoHighlight) {
1270 if (d->highlightPosAnimator)
1271 d->highlightPosAnimator->stop();
1272 if (d->highlightSizeAnimator)
1273 d->highlightSizeAnimator->stop();
1275 QSGItemView::setHighlightFollowsCurrentItem(autoHighlight);
1279 qreal QSGListView::spacing() const
1281 Q_D(const QSGListView);
1285 void QSGListView::setSpacing(qreal spacing)
1288 if (spacing != d->spacing) {
1289 d->spacing = spacing;
1291 emit spacingChanged();
1295 QSGListView::Orientation QSGListView::orientation() const
1297 Q_D(const QSGListView);
1301 void QSGListView::setOrientation(QSGListView::Orientation orientation)
1304 if (d->orient != orientation) {
1305 d->orient = orientation;
1306 if (d->orient == Vertical) {
1307 setContentWidth(-1);
1308 setFlickableDirection(VerticalFlick);
1311 setContentHeight(-1);
1312 setFlickableDirection(HorizontalFlick);
1316 emit orientationChanged();
1320 QSGViewSection *QSGListView::sectionCriteria()
1323 if (!d->sectionCriteria) {
1324 d->sectionCriteria = new QSGViewSection(this);
1325 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
1327 return d->sectionCriteria;
1330 QString QSGListView::currentSection() const
1332 Q_D(const QSGListView);
1333 return d->currentSection;
1336 qreal QSGListView::highlightMoveSpeed() const
1338 Q_D(const QSGListView);
1339 return d->highlightMoveSpeed;
1342 void QSGListView::setHighlightMoveSpeed(qreal speed)
1345 if (d->highlightMoveSpeed != speed) {
1346 d->highlightMoveSpeed = speed;
1347 if (d->highlightPosAnimator)
1348 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
1349 emit highlightMoveSpeedChanged();
1353 void QSGListView::setHighlightMoveDuration(int duration)
1356 if (d->highlightMoveDuration != duration) {
1357 if (d->highlightPosAnimator)
1358 d->highlightPosAnimator->userDuration = duration;
1359 QSGItemView::setHighlightMoveDuration(duration);
1363 qreal QSGListView::highlightResizeSpeed() const
1365 Q_D(const QSGListView);
1366 return d->highlightResizeSpeed;
1369 void QSGListView::setHighlightResizeSpeed(qreal speed)
1372 if (d->highlightResizeSpeed != speed) {
1373 d->highlightResizeSpeed = speed;
1374 if (d->highlightSizeAnimator)
1375 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
1376 emit highlightResizeSpeedChanged();
1380 int QSGListView::highlightResizeDuration() const
1382 Q_D(const QSGListView);
1383 return d->highlightResizeDuration;
1386 void QSGListView::setHighlightResizeDuration(int duration)
1389 if (d->highlightResizeDuration != duration) {
1390 d->highlightResizeDuration = duration;
1391 if (d->highlightSizeAnimator)
1392 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
1393 emit highlightResizeDurationChanged();
1397 QSGListView::SnapMode QSGListView::snapMode() const
1399 Q_D(const QSGListView);
1403 void QSGListView::setSnapMode(SnapMode mode)
1406 if (d->snapMode != mode) {
1408 emit snapModeChanged();
1412 void QSGListView::viewportMoved()
1415 QSGItemView::viewportMoved();
1418 // Recursion can occur due to refill changing the content size.
1419 if (d->inViewportMoved)
1421 d->inViewportMoved = true;
1422 d->lazyRelease = true;
1424 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
1425 d->moveReason = QSGListViewPrivate::Mouse;
1426 if (d->moveReason != QSGListViewPrivate::SetIndex) {
1427 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
1428 // reposition highlight
1429 qreal pos = d->highlight->position();
1431 qreal highlightStart;
1433 if (d->isRightToLeft()) {
1434 // Handle Right-To-Left exceptions
1435 viewPos = -d->position()-d->size();
1436 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
1437 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
1439 viewPos = d->position();
1440 highlightStart = d->highlightRangeStart;
1441 highlightEnd = d->highlightRangeEnd;
1443 if (pos > viewPos + highlightEnd - d->highlight->size())
1444 pos = viewPos + highlightEnd - d->highlight->size();
1445 if (pos < viewPos + highlightStart)
1446 pos = viewPos + highlightStart;
1447 if (pos != d->highlight->position()) {
1448 d->highlightPosAnimator->stop();
1449 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
1451 d->updateHighlight();
1454 // update current index
1455 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
1456 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
1457 d->updateCurrent(snapItem->index);
1462 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
1463 d->inFlickCorrection = true;
1464 // Near an end and it seems that the extent has changed?
1465 // Recalculate the flick so that we don't end up in an odd position.
1466 if (yflick() && !d->vData.inOvershoot) {
1467 if (d->vData.velocity > 0) {
1468 const qreal minY = minYExtent();
1469 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
1470 && minY != d->vData.flickTarget)
1471 d->flickY(-d->vData.smoothVelocity.value());
1472 d->bufferMode = QSGListViewPrivate::BufferBefore;
1473 } else if (d->vData.velocity < 0) {
1474 const qreal maxY = maxYExtent();
1475 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
1476 && maxY != d->vData.flickTarget)
1477 d->flickY(-d->vData.smoothVelocity.value());
1478 d->bufferMode = QSGListViewPrivate::BufferAfter;
1482 if (xflick() && !d->hData.inOvershoot) {
1483 if (d->hData.velocity > 0) {
1484 const qreal minX = minXExtent();
1485 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
1486 && minX != d->hData.flickTarget)
1487 d->flickX(-d->hData.smoothVelocity.value());
1488 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore;
1489 } else if (d->hData.velocity < 0) {
1490 const qreal maxX = maxXExtent();
1491 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
1492 && maxX != d->hData.flickTarget)
1493 d->flickX(-d->hData.smoothVelocity.value());
1494 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter;
1497 d->inFlickCorrection = false;
1499 d->inViewportMoved = false;
1502 void QSGListView::keyPressEvent(QKeyEvent *event)
1505 if (d->model && d->model->count() && d->interactive) {
1506 if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
1507 || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
1508 || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) {
1509 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
1510 decrementCurrentIndex();
1513 } else if (d->wrap) {
1517 } else if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
1518 || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
1519 || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) {
1520 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
1521 incrementCurrentIndex();
1524 } else if (d->wrap) {
1531 QSGItemView::keyPressEvent(event);
1534 void QSGListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1537 if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) {
1538 // maintain position relative to the right edge
1539 int dx = newGeometry.width() - oldGeometry.width();
1540 setContentX(contentX() - dx);
1542 QSGItemView::geometryChanged(newGeometry, oldGeometry);
1546 void QSGListView::incrementCurrentIndex()
1549 int count = d->model ? d->model->count() : 0;
1550 if (count && (currentIndex() < count - 1 || d->wrap)) {
1551 d->moveReason = QSGListViewPrivate::SetIndex;
1552 int index = currentIndex()+1;
1553 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1557 void QSGListView::decrementCurrentIndex()
1560 int count = d->model ? d->model->count() : 0;
1561 if (count && (currentIndex() > 0 || d->wrap)) {
1562 d->moveReason = QSGListViewPrivate::SetIndex;
1563 int index = currentIndex()-1;
1564 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1568 void QSGListView::updateSections()
1571 if (isComponentComplete() && d->model) {
1572 QList<QByteArray> roles;
1573 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
1574 roles << d->sectionCriteria->property().toUtf8();
1575 d->model->setWatchedRoles(roles);
1576 d->updateSections();
1582 void QSGListView::itemsInserted(int modelIndex, int count)
1585 if (!isComponentComplete() || !d->model || !d->model->isValid())
1587 d->updateUnrequestedIndexes();
1588 d->moveReason = QSGListViewPrivate::Other;
1590 qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
1591 int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
1593 int i = d->visibleItems.count() - 1;
1594 while (i > 0 && d->visibleItems.at(i)->index == -1)
1596 if (i == 0 && d->visibleItems.first()->index == -1) {
1597 // there are no visible items except items marked for removal
1598 index = d->visibleItems.count();
1599 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
1600 && d->visibleItems.at(i)->endPosition() <= d->buffer+tempPos+d->size()) {
1601 // Special case of appending an item to the model.
1602 index = d->visibleItems.count();
1604 if (modelIndex < d->visibleIndex) {
1605 // Insert before visible items
1606 d->visibleIndex += count;
1607 for (int i = 0; i < d->visibleItems.count(); ++i) {
1608 FxViewItem *item = d->visibleItems.at(i);
1609 if (item->index != -1 && item->index >= modelIndex)
1610 item->index += count;
1613 if (d->currentIndex >= modelIndex) {
1614 // adjust current item index
1615 d->currentIndex += count;
1617 d->currentItem->index = d->currentIndex;
1618 emit currentIndexChanged();
1620 d->scheduleLayout();
1621 d->itemCount += count;
1622 emit countChanged();
1627 // index can be the next item past the end of the visible items list (i.e. appended)
1629 if (d->visibleItems.count()) {
1630 pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
1631 : d->visibleItems.last()->endPosition()+d->spacing;
1634 int initialPos = pos;
1636 QList<FxListItemSG*> added;
1637 bool addedVisible = false;
1638 FxViewItem *firstVisible = d->firstVisibleItem();
1639 if (firstVisible && pos < firstVisible->position()) {
1640 // Insert items before the visible item.
1641 int insertionIdx = index;
1643 int from = tempPos - d->buffer;
1644 for (i = count-1; i >= 0 && pos > from; --i) {
1645 if (!addedVisible) {
1646 d->scheduleLayout();
1647 addedVisible = true;
1649 FxListItemSG *item = static_cast<FxListItemSG*>(d->createItem(modelIndex + i));
1650 d->visibleItems.insert(insertionIdx, item);
1651 pos -= item->size() + d->spacing;
1652 item->setPosition(pos);
1656 // If we didn't insert all our new items - anything
1657 // before the current index is not visible - remove it.
1658 while (insertionIdx--) {
1659 FxListItemSG *item = static_cast<FxListItemSG*>(d->visibleItems.takeFirst());
1660 if (item->index != -1)
1662 d->releaseItem(item);
1665 // adjust pos of items before inserted items.
1666 for (int i = insertionIdx-1; i >= 0; i--) {
1667 FxListItemSG *listItem = static_cast<FxListItemSG*>(d->visibleItems.at(i));
1668 listItem->setPosition(listItem->position() - (initialPos - pos));
1673 int to = d->buffer+tempPos+d->size();
1674 for (i = 0; i < count && pos <= to; ++i) {
1675 if (!addedVisible) {
1676 d->scheduleLayout();
1677 addedVisible = true;
1679 FxListItemSG *item = static_cast<FxListItemSG*>(d->createItem(modelIndex + i));
1680 d->visibleItems.insert(index, item);
1681 item->setPosition(pos);
1683 pos += item->size() + d->spacing;
1687 // We didn't insert all our new items, which means anything
1688 // beyond the current index is not visible - remove it.
1689 while (d->visibleItems.count() > index)
1690 d->releaseItem(d->visibleItems.takeLast());
1692 diff = pos - initialPos;
1694 if (d->itemCount && d->currentIndex >= modelIndex) {
1695 // adjust current item index
1696 d->currentIndex += count;
1697 if (d->currentItem) {
1698 d->currentItem->index = d->currentIndex;
1699 static_cast<FxListItemSG *>(d->currentItem)->setPosition(static_cast<FxListItemSG *>(d->currentItem)->position() + diff);
1701 emit currentIndexChanged();
1702 } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
1703 d->updateCurrent(0);
1705 // Update the indexes of the following visible items.
1706 for (; index < d->visibleItems.count(); ++index) {
1707 FxViewItem *item = d->visibleItems.at(index);
1708 if (d->currentItem && item->item != d->currentItem->item)
1709 static_cast<FxListItemSG*>(item)->setPosition(item->position() + diff);
1710 if (item->index != -1)
1711 item->index += count;
1713 // everything is in order now - emit add() signal
1714 for (int j = 0; j < added.count(); ++j)
1715 added.at(j)->attached->emitAdd();
1717 d->updateSections();
1718 d->itemCount += count;
1719 emit countChanged();
1722 void QSGListView::itemsRemoved(int modelIndex, int count)
1725 if (!isComponentComplete() || !d->model || !d->model->isValid())
1727 d->moveReason = QSGListViewPrivate::Other;
1728 d->updateUnrequestedIndexes();
1729 d->itemCount -= count;
1731 FxViewItem *firstVisible = d->firstVisibleItem();
1732 int preRemovedSize = 0;
1733 bool removedVisible = false;
1734 // Remove the items from the visible list, skipping anything already marked for removal
1735 QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1736 while (it != d->visibleItems.end()) {
1737 FxViewItem *item = *it;
1738 if (item->index == -1 || item->index < modelIndex) {
1739 // already removed, or before removed items
1741 } else if (item->index >= modelIndex + count) {
1742 // after removed items
1743 item->index -= count;
1747 if (!removedVisible) {
1748 d->scheduleLayout();
1749 removedVisible = true;
1751 item->attached->emitRemove();
1752 if (item->attached->delayRemove()) {
1754 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
1757 if (item == firstVisible)
1759 if (firstVisible && item->position() < firstVisible->position())
1760 preRemovedSize += item->size();
1761 it = d->visibleItems.erase(it);
1762 d->releaseItem(item);
1767 if (firstVisible && d->visibleItems.first() != firstVisible)
1768 static_cast<FxListItemSG*>(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + preRemovedSize);
1770 // update visibleIndex
1771 bool haveVisibleIndex = false;
1772 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1773 if ((*it)->index != -1) {
1774 d->visibleIndex = (*it)->index;
1775 haveVisibleIndex = true;
1781 if (d->currentIndex >= modelIndex + count) {
1782 d->currentIndex -= count;
1784 d->currentItem->index -= count;
1785 emit currentIndexChanged();
1786 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1787 // current item has been removed.
1788 if (d->currentItem) {
1789 d->currentItem->attached->setIsCurrentItem(false);
1790 d->releaseItem(d->currentItem);
1793 d->currentIndex = -1;
1795 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
1797 emit currentIndexChanged();
1800 if (!haveVisibleIndex) {
1801 d->timeline.clear();
1802 if (removedVisible && d->itemCount == 0) {
1803 d->visibleIndex = 0;
1805 d->setPosition(d->contentStartPosition());
1809 if (modelIndex < d->visibleIndex)
1810 d->visibleIndex = modelIndex+1;
1811 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
1815 d->updateSections();
1816 emit countChanged();
1819 void QSGListView::itemsMoved(int from, int to, int count)
1822 if (!isComponentComplete() || !d->isValid())
1824 d->updateUnrequestedIndexes();
1826 if (d->visibleItems.isEmpty()) {
1831 d->moveReason = QSGListViewPrivate::Other;
1833 bool movingBackwards = from > to;
1834 d->adjustMoveParameters(&from, &to, &count);
1836 QHash<int,FxViewItem*> moved;
1838 FxViewItem *firstVisible = d->firstVisibleItem();
1839 int firstItemIndex = firstVisible ? firstVisible->index : -1;
1841 // if visibleItems.first() is above the content start pos, and the items
1842 // beneath it are moved, ensure this first item is later repositioned correctly
1843 // (to above the next visible item) so that subsequent layout() is correct
1844 bool repositionFirstItem = firstVisible
1845 && d->visibleItems.first()->position() < firstVisible->position()
1846 && from > d->visibleItems.first()->index;
1848 QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1849 while (it != d->visibleItems.end()) {
1850 FxViewItem *item = *it;
1851 if (item->index >= from && item->index < from + count) {
1852 // take the items that are moving
1853 item->index += (to-from);
1854 moved.insert(item->index, item);
1855 if (repositionFirstItem)
1856 moveBy += item->size();
1857 it = d->visibleItems.erase(it);
1859 // move everything after the moved items.
1860 if (item->index > from && item->index != -1)
1861 item->index -= count;
1867 int endIndex = d->visibleIndex;
1868 it = d->visibleItems.begin();
1869 while (it != d->visibleItems.end()) {
1870 FxViewItem *item = *it;
1871 if (movedCount < count && item->index >= to && item->index < to + count) {
1872 // place items in the target position, reusing any existing items
1873 int targetIndex = item->index + movedCount;
1874 FxViewItem *movedItem = moved.take(targetIndex);
1876 movedItem = d->createItem(targetIndex);
1877 it = d->visibleItems.insert(it, movedItem);
1881 if (item->index != -1) {
1882 if (item->index >= to) {
1883 // update everything after the moved items.
1884 item->index += count;
1886 endIndex = item->index;
1892 // If we have moved items to the end of the visible items
1893 // then add any existing moved items that we have
1894 while (FxViewItem *item = moved.take(endIndex+1)) {
1895 d->visibleItems.append(item);
1899 // update visibleIndex
1900 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1901 if ((*it)->index != -1) {
1902 d->visibleIndex = (*it)->index;
1907 // if first visible item is moving but another item is moving up to replace it,
1908 // do this positioning now to avoid shifting all content forwards
1909 if (movingBackwards && firstItemIndex >= 0) {
1910 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1911 if ((*it)->index == firstItemIndex) {
1912 static_cast<FxListItemSG*>(*it)->setPosition(firstVisible->position());
1918 // Fix current index
1919 if (d->currentIndex >= 0 && d->currentItem) {
1920 int oldCurrent = d->currentIndex;
1921 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
1922 if (oldCurrent != d->currentIndex) {
1923 d->currentItem->index = d->currentIndex;
1924 emit currentIndexChanged();
1928 // Whatever moved items remain are no longer visible items.
1929 while (moved.count()) {
1930 int idx = moved.begin().key();
1931 FxViewItem *item = moved.take(idx);
1932 if (d->currentItem && item->item == d->currentItem->item)
1933 static_cast<FxListItemSG*>(item)->setPosition(d->positionAt(idx));
1934 d->releaseItem(item);
1937 // Ensure we don't cause an ugly list scroll.
1938 if (d->visibleItems.count())
1939 static_cast<FxListItemSG*>(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + moveBy);
1941 d->updateSections();
1946 QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
1948 return new QSGListViewAttached(obj);