Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquicklistview.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
8
9#include <private/qqmlobjectmodel_p.h>
10#include <QtQml/qqmlexpression.h>
11#include <QtQml/qqmlengine.h>
12#include <QtQml/qqmlinfo.h>
13#include <QtGui/qevent.h>
14#include <QtCore/qcoreapplication.h>
15#include <QtCore/qmath.h>
16
17#include <private/qquicksmoothedanimation_p_p.h>
18#include <private/qqmlcomponent_p.h>
19
21
22#ifndef QML_FLICK_SNAPONETHRESHOLD
23#define QML_FLICK_SNAPONETHRESHOLD 30
24#endif
25
26Q_STATIC_LOGGING_CATEGORY(lcEvents, "qt.quick.listview.events")
27
28class FxListItemSG;
29
31{
32public:
33 Q_DECLARE_PUBLIC(QQuickListView)
35
38 bool isRightToLeft() const;
39 bool isBottomToTop() const;
40
41 qreal positionAt(int index) const override;
42 qreal endPositionAt(int index) const override;
43 qreal originPosition() const override;
44 qreal lastPosition() const override;
45
46 FxViewItem *itemBefore(int modelIndex) const;
47 QString sectionAt(int modelIndex);
48 qreal snapPosAt(qreal pos);
50
51 void init() override;
52 void clear(bool onDestruction) override;
53
54 bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) override;
55 bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) override;
57
58 void removeItem(FxViewItem *item);
59
60 FxViewItem *newViewItem(int index, QQuickItem *item) override;
61 void initializeViewItem(FxViewItem *item) override;
62 bool releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag) override;
63 void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) override;
64 void repositionPackageItemAt(QQuickItem *item, int index) override;
65 void resetFirstItemPosition(qreal pos = 0.0) override;
66 void adjustFirstItem(qreal forwards, qreal backwards, int) override;
67 void updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult) override;
68
69 void createHighlight(bool onDestruction = false) override;
73
74 void setPosition(qreal pos) override;
75 void layoutVisibleItems(int fromModelIndex = 0) override;
76
77 bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView) override;
78#if QT_CONFIG(quick_viewtransitions)
80#endif
81
84 QQuickItem *getSectionItem(const QString &section);
85 void releaseSectionItem(QQuickItem *item);
90
91 qreal headerSize() const override;
92 qreal footerSize() const override;
93 bool showHeaderForIndex(int index) const override;
94 bool showFooterForIndex(int index) const override;
99
100 void initializeComponentItem(QQuickItem *item) const override;
101
102 void changedVisibleIndex(int newIndex) override;
104
106
107 void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeometry) override;
109 void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
110 bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
111 QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override;
112
113 QQuickItemViewAttached *getAttachedObject(const QObject *object) const override;
114
117
118 bool wantsPointerEvent(const QPointerEvent *event) override;
119
125
128
135
138 static const int sectionCacheSize = 5;
142 QQuickItem *nextSectionItem;
146
148
153
154 bool correctFlick : 1;
157
159
162 , visiblePos(0)
163 , averageSize(100.0), spacing(0.0)
169 , sectionCriteria(nullptr), currentSectionItem(nullptr), nextSectionItem(nullptr)
172 , correctFlick(false), inFlickCorrection(false), wantedMousePress(false)
173 {
174 highlightMoveDuration = -1; //override default value set in base class
175 }
176
177 friend class QQuickViewSection;
178
179 static void setSectionHelper(QQmlContext *context, QQuickItem *sectionItem, const QString &section);
180};
181
182//----------------------------------------------------------------------------
183
184QQuickViewSection::QQuickViewSection(QQuickListView *parent)
185 : QObject(parent), m_criteria(FullString), m_delegate(nullptr), m_labelPositioning(InlineLabels)
186 , m_view(parent ? QQuickListViewPrivate::get(parent) : nullptr)
187{
188}
189
190void QQuickViewSection::setProperty(const QString &property)
191{
192 if (property != m_property) {
193 m_property = property;
194 emit propertyChanged();
195 // notify view that the contents of the sections must be recalculated
196 m_view->updateSectionCriteria();
197 }
198}
199
200void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
201{
202 if (criteria != m_criteria) {
203 m_criteria = criteria;
204 emit criteriaChanged();
205 // notify view that the contents of the sections must be recalculated
206 m_view->updateSectionCriteria();
207 }
208}
209
210void QQuickViewSection::setDelegate(QQmlComponent *delegate)
211{
212 if (delegate != m_delegate) {
213 if (m_delegate)
214 m_view->releaseSectionItems();
215 m_delegate = delegate;
216 emit delegateChanged();
217 m_view->forceLayoutPolish();
218 }
219}
220
221QString QQuickViewSection::sectionString(const QString &value)
222{
223 if (m_criteria == FirstCharacter)
224 return value.isEmpty() ? QString() : value.at(0);
225 else
226 return value;
227}
228
229void QQuickViewSection::setLabelPositioning(int l)
230{
231 if (m_labelPositioning != l) {
232 m_labelPositioning = l;
233 emit labelPositioningChanged();
234 m_view->forceLayoutPolish();
235 }
236}
237
238//----------------------------------------------------------------------------
239
241{
242public:
243 FxListItemSG(QQuickItem *i, QQuickListView *v, bool own) : FxViewItem(i, v, own, static_cast<QQuickItemViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(i))), view(v)
244 {
245 }
246
247 inline QQuickItem *section() const {
248 return item && attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : nullptr;
249 }
250 void setSection(QQuickItem *s) {
251 static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
252 }
253
254 qreal position() const override {
255 if (section()) {
256 if (view->orientation() == QQuickListView::Vertical)
257 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
258 else
259 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
260 } else {
261 return itemPosition();
262 }
263 }
265 if (view->orientation() == QQuickListView::Vertical)
266 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -itemHeight()-itemY() : itemY());
267 else
268 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -itemWidth()-itemX() : itemX());
269 }
270 qreal size() const override {
271 if (section())
272 return (view->orientation() == QQuickListView::Vertical ? itemHeight()+section()->height() : itemWidth()+section()->width());
273 else
274 return (view->orientation() == QQuickListView::Vertical ? itemHeight() : itemWidth());
275 }
276 qreal itemSize() const {
277 return (view->orientation() == QQuickListView::Vertical ? itemHeight() : itemWidth());
278 }
279 qreal sectionSize() const override {
280 if (section())
281 return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
282 return 0.0;
283 }
284 qreal endPosition() const override {
285 if (view->orientation() == QQuickListView::Vertical) {
286 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
287 ? -itemY()
288 : itemY() + itemHeight());
289 } else {
290 return (view->effectiveLayoutDirection() == Qt::RightToLeft
291 ? -itemX()
292 : itemX() + itemWidth());
293 }
294 }
295
296 void setPosition(qreal pos, bool immediate = false, bool resetInactiveAxis = true) {
297 // position the section immediately even if there is a transition
298 if (section()) {
299 if (view->orientation() == QQuickListView::Vertical) {
300 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
301 section()->setY(-section()->height()-pos);
302 else
303 section()->setY(pos);
304 } else {
305 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
306 section()->setX(-section()->width()-pos);
307 else
308 section()->setX(pos);
309 }
310 }
311 moveTo(pointForPosition(pos, resetInactiveAxis), immediate);
312 }
313
314 void setSize(qreal size) {
315 if (view->orientation() == QQuickListView::Vertical)
316 item->setHeight(size);
317 else
318 item->setWidth(size);
319 }
320 bool contains(qreal x, qreal y) const override {
321 return (x >= itemX() && x < itemX() + itemWidth() &&
322 y >= itemY() && y < itemY() + itemHeight());
323 }
324
325 QQuickListView *view;
326
327private:
328 QPointF pointForPosition(qreal pos, bool resetInactiveAxis) const {
329 if (view->orientation() == QQuickListView::Vertical) {
330 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
331 if (section())
332 pos += section()->height();
333 return QPointF(resetInactiveAxis ? 0 : itemX(), -itemHeight() - pos);
334 } else {
335 if (section())
336 pos += section()->height();
337 return QPointF(resetInactiveAxis ? 0 : itemX(), pos);
338 }
339 } else {
340 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
341 if (section())
342 pos += section()->width();
343 return QPointF(-itemWidth() - pos, resetInactiveAxis ? 0 : itemY());
344 } else {
345 if (section())
346 pos += section()->width();
347 return QPointF(pos, resetInactiveAxis ? 0 : itemY());
348 }
349 }
350 }
351};
352
353/*! \internal
354 \brief A helper class for iterating over a model that might change
355
356 When populating the ListView from a model under normal
357 circumstances, we would iterate over the range of model indices
358 correspondning to the visual range, and basically call
359 createItem(index++) in order to create each item.
360
361 This will also emit Component.onCompleted() for each item, which
362 might do some weird things... For instance, it might remove itself
363 from the model, and this might change model count and the indices
364 of the other subsequent entries in the model.
365
366 This class takes such changes to the model into consideration while
367 iterating, and will adjust the iterator index and keep track of
368 whether the iterator has reached the end of the range.
369
370 It keeps track of changes to the model by connecting to
371 QQmlInstanceModel::modelUpdated() from its constructor.
372 When destroyed, it will automatically disconnect. You can
373 explicitly disconnect earlier by calling \fn disconnect().
374*/
376public:
377 MutableModelIterator(QQmlInstanceModel *model, int iBegin, int iEnd)
378 : removedAtIndex(false)
379 , backwards(iEnd < iBegin)
380 {
381 conn = QObject::connect(model, &QQmlInstanceModel::modelUpdated, model,
382 [&] (const QQmlChangeSet &changeSet, bool /*reset*/)
383 {
384 for (const QQmlChangeSet::Change &rem : changeSet.removes()) {
385 idxEnd -= rem.count;
386 if (rem.start() <= index) {
387 index -= rem.count;
388 if (index < rem.start() + rem.count)
389 removedAtIndex = true; // model index was removed
390 }
391 }
392 for (const QQmlChangeSet::Change &ins : changeSet.inserts()) {
393 idxEnd += ins.count;
394 if (ins.start() <= index)
395 index += ins.count;
396 }
397 }
398 );
399 index = iBegin;
400 idxEnd = iEnd;
401 }
402
403 bool hasNext() const {
404 return backwards ? index > idxEnd : index < idxEnd;
405 }
406
407 void next() { index += (backwards ? -1 : +1); }
408
413
415 {
416 if (conn) {
417 QObject::disconnect(conn);
418 conn = QMetaObject::Connection(); // set to nullptr
419 }
420 }
421 int index = 0;
423 unsigned removedAtIndex : 1;
424 unsigned backwards : 1;
425private:
426 QMetaObject::Connection conn;
427};
428
429
430//----------------------------------------------------------------------------
431
436
437Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
438{
439 return static_cast<Qt::Orientation>(orient);
440}
441
443{
444 Q_Q(const QQuickListView);
445 return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
446}
447
449{
450 return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
451}
452
453// Returns the item before modelIndex, if created.
454// May return an item marked for removal.
456{
457 if (modelIndex < visibleIndex)
458 return nullptr;
459 int idx = 1;
460 int lastIndex = -1;
461 while (idx < visibleItems.size()) {
462 FxViewItem *item = visibleItems.at(idx);
463 if (item->index != -1)
464 lastIndex = item->index;
465 if (item->index == modelIndex)
466 return visibleItems.at(idx-1);
467 ++idx;
468 }
469 if (lastIndex == modelIndex-1)
470 return visibleItems.constLast();
471 return nullptr;
472}
473
475{
476 Q_Q(QQuickListView);
477 if (orient == QQuickListView::Vertical) {
478 if (isBottomToTop())
479 q->QQuickFlickable::setContentY(-pos-size());
480 else
481 q->QQuickFlickable::setContentY(pos);
482 } else {
483 if (isRightToLeft())
484 q->QQuickFlickable::setContentX(-pos-size());
485 else
486 q->QQuickFlickable::setContentX(pos);
487 }
488}
489
491{
492 qreal pos = 0;
493 if (!visibleItems.isEmpty()) {
494 pos = (*visibleItems.constBegin())->position();
495 if (visibleIndex > 0)
496 pos -= visibleIndex * (averageSize + spacing);
497 }
498 return pos;
499}
500
502{
503 qreal pos = 0;
504 if (!visibleItems.isEmpty()) {
505 int invisibleCount = INT_MIN;
506 int delayRemovedCount = 0;
507 for (int i = visibleItems.size()-1; i >= 0; --i) {
508 FxViewItem *item = visibleItems.at(i);
509 if (item->index != -1) {
510 // Find the invisible count after the last visible item with known index
511 invisibleCount = model->count() - (item->index + 1 + delayRemovedCount);
512 break;
513 } else if (item->attached->delayRemove()) {
514 ++delayRemovedCount;
515 }
516 }
517 if (invisibleCount == INT_MIN) {
518 // All visible items are in delayRemove state
519 invisibleCount = model->count();
520 }
521 pos = (*(visibleItems.constEnd() - 1))->endPosition();
522 if (invisibleCount > 0)
523 pos += invisibleCount * (averageSize + spacing);
524 } else if (model && model->count()) {
525 pos = (model->count() * averageSize + (model->count()-1) * spacing);
526 }
527 return pos;
528}
529
531{
532 if (FxViewItem *item = visibleItem(modelIndex)) {
533 return item->position();
534 }
535 if (!visibleItems.isEmpty()) {
536 if (modelIndex < visibleIndex) {
537 int count = visibleIndex - modelIndex;
538 qreal cs = 0;
539 if (modelIndex == currentIndex && currentItem) {
540 cs = currentItem->size() + spacing;
541 --count;
542 }
543 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
544 } else {
545 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
546 return (*(visibleItems.constEnd() - 1))->endPosition() + spacing + count * (averageSize + spacing);
547 }
548 }
549 return 0;
550}
551
553{
554 if (FxViewItem *item = visibleItem(modelIndex))
555 return item->endPosition();
556 if (!visibleItems.isEmpty()) {
557 if (modelIndex < visibleIndex) {
558 int count = visibleIndex - modelIndex;
559 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
560 } else {
561 int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
562 return (*(visibleItems.constEnd() - 1))->endPosition() + count * (averageSize + spacing);
563 }
564 }
565 return 0;
566}
567
569{
570 if (FxViewItem *item = visibleItem(modelIndex))
571 return item->attached->section();
572
573 QString section;
574 if (sectionCriteria && modelIndex >= 0 && modelIndex < itemCount) {
575 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
576 section = sectionCriteria->sectionString(propValue);
577 }
578
579 return section;
580}
581
583{
584 if (FxListItemSG *snapItem = static_cast<FxListItemSG*>(snapItemAt(pos)))
585 return snapItem->itemPosition();
586 if (visibleItems.size()) {
587 qreal firstPos = (*visibleItems.constBegin())->position();
588 qreal endPos = (*(visibleItems.constEnd() - 1))->position();
589 if (pos < firstPos) {
590 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
591 } else if (pos > endPos)
592 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
593 }
594 return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
595}
596
598{
599 const qreal velocity = orient == QQuickListView::Vertical ? vData.velocity : hData.velocity;
600 FxViewItem *snapItem = nullptr;
601 FxViewItem *prevItem = nullptr;
602 qreal prevItemSize = 0;
603 for (FxViewItem *item : std::as_const(visibleItems)) {
604 if (item->index == -1)
605 continue;
606
607 const FxListItemSG *listItem = static_cast<FxListItemSG *>(item);
608 qreal itemTop = listItem->position();
609 qreal itemSize = listItem->size();
610 if (highlight && itemTop >= pos && listItem->endPosition() <= pos + highlight->size())
611 return item;
612
613 if (listItem->section() && velocity > 0) {
614 if (itemTop + listItem->sectionSize() / 2 >= pos && itemTop - prevItemSize / 2 < pos)
615 snapItem = prevItem;
616 itemTop = listItem->itemPosition();
617 itemSize = listItem->itemSize();
618 }
619
620 // Middle of item and spacing (i.e. the middle of the distance between this item and the next
621 qreal halfwayToNextItem = itemTop + (itemSize+spacing) / 2;
622 qreal halfwayToPrevItem = itemTop - (prevItemSize+spacing) / 2;
623 if (halfwayToNextItem >= pos && halfwayToPrevItem < pos)
624 snapItem = item;
625
626 prevItemSize = listItem->itemSize();
627 prevItem = item;
628 }
629 return snapItem;
630}
631
633{
634 visiblePos = positionAt(newIndex);
635 visibleIndex = newIndex;
636}
637
639{
640 QQuickItemViewPrivate::init();
641 ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
642}
643
644void QQuickListViewPrivate::clear(bool onDestruction)
645{
646 for (int i = 0; i < sectionCacheSize; ++i) {
647 delete sectionCache[i];
648 sectionCache[i] = nullptr;
649 }
650 visiblePos = 0;
652 currentSectionItem = nullptr;
654 nextSectionItem = nullptr;
655 lastVisibleSection = QString();
656 QQuickItemViewPrivate::clear(onDestruction);
657}
658
659FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
660{
661 Q_Q(QQuickListView);
662
663 FxListItemSG *listItem = new FxListItemSG(item, q, false);
664 listItem->index = modelIndex;
665
666 // initialise attached properties
667 if (sectionCriteria) {
668 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
669 QString section = sectionCriteria->sectionString(propValue);
670 QString prevSection;
671 QString nextSection;
672 if (modelIndex > 0) {
673 if (FxViewItem *item = itemBefore(modelIndex))
674 prevSection = item->attached->section();
675 else
676 prevSection = sectionAt(modelIndex-1);
677 }
678 if (modelIndex < model->count()-1) {
679 nextSection = sectionAt(modelIndex+1);
680 }
681 listItem->attached->setSections(prevSection, section, nextSection);
682 }
683
684 return listItem;
685}
686
687void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
688{
689 QQuickItemViewPrivate::initializeViewItem(item);
690
691 // need to track current items that are animating
692 item->trackGeometry(true);
693
694 if (sectionCriteria && sectionCriteria->delegate()) {
695 if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
696 updateInlineSection(static_cast<FxListItemSG*>(item));
697 }
698}
699
700bool QQuickListViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag)
701{
702 if (!item || !model)
703 return QQuickItemViewPrivate::releaseItem(item, reusableFlag);
704
705 QPointer<QQuickItem> it = item->item;
706 QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
707
708 bool released = QQuickItemViewPrivate::releaseItem(item, reusableFlag);
709 if (released && it && att && att->m_sectionItem) {
710 QQuickItemPrivate::get(att->m_sectionItem)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
711
712 // We hold no more references to this item
713 int i = 0;
714 do {
715 if (!sectionCache[i]) {
716 sectionCache[i] = att->m_sectionItem;
717 sectionCache[i]->setVisible(false);
718 att->m_sectionItem = nullptr;
719 break;
720 }
721 ++i;
722 } while (i < sectionCacheSize);
723 delete att->m_sectionItem;
724 att->m_sectionItem = nullptr;
725 }
726
727 return released;
728}
729
730bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
731{
732 qreal itemEnd = visiblePos;
733 if (visibleItems.size()) {
734 visiblePos = (*visibleItems.constBegin())->position();
735 itemEnd = (*(visibleItems.constEnd() - 1))->endPosition() + spacing;
736 }
737
738 int modelIndex = findLastVisibleIndex();
739 bool haveValidItems = modelIndex >= 0;
740 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
741
742 if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
743 || bufferTo < visiblePos - averageSize - spacing)) {
744 // We've jumped more than a page. Estimate which items are now
745 // visible and fill from there.
746 int count = (fillFrom - itemEnd) / (averageSize + spacing);
747 int newModelIdx = qBound(0, modelIndex + count, model->count());
748 count = newModelIdx - modelIndex;
749 if (count) {
750 releaseVisibleItems(reusableFlag);
751 modelIndex = newModelIdx;
752 visibleIndex = modelIndex;
753 visiblePos = itemEnd + count * (averageSize + spacing);
754 itemEnd = visiblePos;
755 }
756 }
757
758 QQmlIncubator::IncubationMode incubationMode = doBuffer ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested;
759
760 bool changed = false;
761 FxListItemSG *item = nullptr;
762 qreal pos = itemEnd;
763 while (modelIndex < model->count() && pos <= fillTo) {
764 if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, incubationMode))))
765 break;
766 qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer << "item" << (QObject *)(item->item);
767#if QT_CONFIG(quick_viewtransitions)
768 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
769#endif
770 item->setPosition(pos, true);
771 if (item->item)
772 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
773 pos += item->size() + spacing;
774 visibleItems.append(item);
775 ++modelIndex;
776 changed = true;
777 }
778
779 if (doBuffer && requestedIndex != -1) // already waiting for an item
780 return changed;
781
782 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
783 if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, incubationMode))))
784 break;
785 qCDebug(lcItemViewDelegateLifecycle) << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer << "item" << (QObject *)(item->item);
786 --visibleIndex;
787 visiblePos -= item->size() + spacing;
788#if QT_CONFIG(quick_viewtransitions)
789 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
790#endif
791 item->setPosition(visiblePos, true);
792 if (item->item)
793 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
794 visibleItems.prepend(item);
795 changed = true;
796 }
797
798 return changed;
799}
800
801void QQuickListViewPrivate::removeItem(FxViewItem *item)
802{
803#if QT_CONFIG(quick_viewtransitions)
804 if (item->transitionScheduledOrRunning()) {
805 qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << (QObject *)(item->item);
806 item->releaseAfterTransition = true;
807 releasePendingTransition.append(item);
808 } else
809#endif
810 {
811 qCDebug(lcItemViewDelegateLifecycle) << "\treleasing stationary item" << item->index << (QObject *)(item->item);
812 if (auto *att = static_cast<QQuickListViewAttached*>(item->attached)) {
813 releaseSectionItem(att->m_sectionItem);
814 att->m_sectionItem = nullptr;
815 }
816 releaseItem(item, reusableFlag);
817 }
818}
819
820bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
821{
822 FxViewItem *item = nullptr;
823 bool changed = false;
824
825 // Remove items from the start of the view.
826 // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
827 // removed, otherwise a zero-sized item is infinitely added and removed over and
828 // over by refill().
829 int index = 0;
830 while (visibleItems.size() > 1 && index < visibleItems.size()
831 && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
832 if (item->attached->delayRemove())
833 break;
834
835 if (item->size() > 0) {
836 qCDebug(lcItemViewDelegateLifecycle) << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
837 // remove this item and all zero-sized items before it
838 while (item) {
839 if (item->index != -1)
840 visibleIndex++;
841 visibleItems.removeAt(index);
842 removeItem(item);
843 if (index == 0)
844 break;
845 item = visibleItems.at(--index);
846 }
847 changed = true;
848 } else {
849 index++;
850 }
851 }
852
853 while (visibleItems.size() > 1 && (item = visibleItems.constLast()) && item->position() > bufferTo) {
854 if (item->attached->delayRemove())
855 break;
856 qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.size()-1 << item->position() << (QObject *)(item->item);
857 visibleItems.removeLast();
858 removeItem(item);
859 changed = true;
860 }
861
862 return changed;
863}
864
866{
867 if (visibleItems.size())
868 visiblePos = (*visibleItems.constBegin())->position();
870 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
871 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
873 }
874 if (sectionCriteria)
876 updateUnrequestedPositions();
877}
878
880{
881 if (!visibleItems.isEmpty()) {
882 const qreal from = isContentFlowReversed() ? -position()-displayMarginBeginning-size() : position()-displayMarginBeginning;
883 const qreal to = isContentFlowReversed() ? -position()+displayMarginEnd : position()+size()+displayMarginEnd;
884
885 FxListItemSG *firstItem = static_cast<FxListItemSG *>(visibleItems.constFirst());
886 bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
887
888#if QT_CONFIG(quick_viewtransitions)
889 /* Set position of first item in list view when populate transition is configured, as it doesn't set
890 while adding visible item (addVisibleItem()) to the view */
891 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true))
892 resetFirstItemPosition(isContentFlowReversed() ? -firstItem->position()-firstItem->size() : firstItem->position());
893#endif
894
895 firstVisibleItemPosition = firstItem->position();
896 qreal sum = firstItem->size();
897 qreal pos = firstItem->position() + firstItem->size() + spacing;
898 firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
899
900 // setPosition will affect the position of the item, and its section, if it has one.
901 // This will prevent them from potentially overlapping.
902 if (firstItem->section())
903 firstItem->setPosition(firstItem->position());
904
905 for (int i=1; i < visibleItems.size(); ++i) {
906 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
907 if (item->index >= fromModelIndex) {
908 item->setPosition(pos);
909 item->setVisible(item->endPosition() >= from && item->position() <= to);
910 }
911 pos += item->size() + spacing;
912 sum += item->size();
913 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
914 }
915 averageSize = qRound(sum / visibleItems.size());
916
917 // move current item if it is not a visible item.
918 if (currentIndex >= 0 && currentItem && !fixedCurrent)
919 static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
920
923 }
924}
925
926void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
927{
928 static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
929}
930
931void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
932{
933 Q_Q(QQuickListView);
934 qreal pos = position();
935 if (orient == QQuickListView::Vertical) {
936 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
937 if (isBottomToTop())
938 item->setY(-positionAt(index)-item->height());
939 else
940 item->setY(positionAt(index));
941 }
942 } else {
943 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
944 if (isRightToLeft())
945 item->setX(-positionAt(index)-item->width());
946 else
947 item->setX(positionAt(index));
948 }
949 }
950}
951
953{
954 FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.constFirst());
955 item->setPosition(pos);
956}
957
958void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
959{
960 if (!visibleItems.size())
961 return;
962 qreal diff = forwards - backwards;
963 static_cast<FxListItemSG*>(visibleItems.constFirst())->setPosition(visibleItems.constFirst()->position() + diff);
964}
965
966void QQuickListViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult)
967{
968 if (item != visibleItems.constFirst())
969 QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(item, removeResult);
970}
971
972void QQuickListViewPrivate::createHighlight(bool onDestruction)
973{
974 bool changed = false;
975 if (highlight) {
976 if (trackedItem == highlight.get())
977 trackedItem = nullptr;
978 highlight.reset();
979
980 highlightPosAnimator.reset();
981 highlightWidthAnimator.reset();
982 highlightHeightAnimator.reset();
983 highlightPosAnimator = nullptr;
984 highlightWidthAnimator = nullptr;
985 highlightHeightAnimator = nullptr;
986
987 changed = true;
988 }
989
990 if (onDestruction)
991 return;
992
993 Q_Q(QQuickListView);
994 if (currentItem) {
995 QQuickItem *item = createHighlightItem();
996 if (item) {
997 std::unique_ptr<FxListItemSG> newHighlight
998 = std::make_unique<FxListItemSG>(item, q, true);
999 newHighlight->trackGeometry(true);
1000
1001 if (autoHighlight) {
1002 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
1003 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
1004 }
1005 const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
1006 highlightPosAnimator = std::make_unique<QSmoothedAnimation>();
1007 highlightPosAnimator->target = QQmlProperty(item, posProp);
1008 highlightPosAnimator->velocity = highlightMoveVelocity;
1009 highlightPosAnimator->userDuration = highlightMoveDuration;
1010
1011 highlightWidthAnimator = std::make_unique<QSmoothedAnimation>();
1012 highlightWidthAnimator->velocity = highlightResizeVelocity;
1013 highlightWidthAnimator->userDuration = highlightResizeDuration;
1014 highlightWidthAnimator->target = QQmlProperty(item, QStringLiteral("width"));
1015
1016 highlightHeightAnimator = std::make_unique<QSmoothedAnimation>();
1017 highlightHeightAnimator->velocity = highlightResizeVelocity;
1018 highlightHeightAnimator->userDuration = highlightResizeDuration;
1019 highlightHeightAnimator->target = QQmlProperty(item, QStringLiteral("height"));
1020
1021 highlight = std::move(newHighlight);
1022 changed = true;
1023 }
1024 }
1025 if (changed)
1026 emit q->highlightItemChanged();
1027}
1028
1030{
1031 applyPendingChanges();
1032
1033 if ((!currentItem && highlight) || (currentItem && !highlight))
1035 bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1036 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
1037 // auto-update highlight
1038 FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
1039 highlightPosAnimator->to = isContentFlowReversed()
1040 ? -listItem->itemPosition()-listItem->itemSize()
1041 : listItem->itemPosition();
1042 highlightWidthAnimator->to = listItem->item->width();
1043 highlightHeightAnimator->to = listItem->item->height();
1044 if (orient == QQuickListView::Vertical) {
1045 if (highlight->item->width() == 0)
1046 highlight->item->setWidth(currentItem->item->width());
1047 } else {
1048 if (highlight->item->height() == 0)
1049 highlight->item->setHeight(currentItem->item->height());
1050 }
1051
1052 highlightPosAnimator->restart();
1053 highlightWidthAnimator->restart();
1054 highlightHeightAnimator->restart();
1055 }
1056 updateTrackedItem();
1057}
1058
1060{
1061 if (highlight && currentItem) {
1062 static_cast<FxListItemSG*>(highlight.get())->setPosition(
1063 static_cast<FxListItemSG*>(currentItem)->itemPosition());
1064 }
1065}
1066
1068{
1069 if (!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange)
1070 return false;
1071
1072 return (highlightPosAnimator && highlightPosAnimator->isRunning()) ||
1073 (highlightHeightAnimator && highlightHeightAnimator->isRunning()) ||
1074 (highlightWidthAnimator && highlightWidthAnimator->isRunning());
1075}
1076
1077
1078QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
1079{
1080 Q_Q(QQuickListView);
1081 QQuickItem *sectionItem = nullptr;
1082 int i = sectionCacheSize-1;
1083 while (i >= 0 && !sectionCache[i])
1084 --i;
1085 if (i >= 0) {
1086 sectionItem = sectionCache[i];
1087 sectionCache[i] = nullptr;
1088 sectionItem->setVisible(true);
1089 QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
1090 setSectionHelper(context, sectionItem, section);
1091 } else {
1092 QQmlComponent* delegate = sectionCriteria->delegate();
1093 const bool reuseExistingContext = delegate->isBound();
1094 auto delegatePriv = QQmlComponentPrivate::get(delegate);
1095 QQmlPropertyCache::ConstPtr rootPropertyCache;
1096
1097 QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
1098 auto baseContext = creationContext ? creationContext : qmlContext(q);
1099 // if we need to insert a context property, we need a separate context
1100 QQmlContext *context = reuseExistingContext ? baseContext : new QQmlContext(baseContext);
1101 QObject *nobj = delegate->beginCreate(context);
1102 if (nobj) {
1103 if (delegatePriv->hadTopLevelRequiredProperties()) {
1104 delegate->setInitialProperties(nobj, {{QLatin1String("section"), section}});
1105 } else if (!reuseExistingContext) {
1106 context->setContextProperty(QLatin1String("section"), section);
1107 }
1108 if (!reuseExistingContext)
1109 QQml_setParent_noEvent(context, nobj);
1110 sectionItem = qobject_cast<QQuickItem *>(nobj);
1111 if (!sectionItem) {
1112 delete nobj;
1113 } else {
1114 if (qFuzzyIsNull(sectionItem->z()))
1115 sectionItem->setZ(2);
1116 QQml_setParent_noEvent(sectionItem, contentItem);
1117 sectionItem->setParentItem(contentItem);
1118 }
1119 // sections are not controlled by FxListItemSG, so apply attached properties here
1120 auto *attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(sectionItem));
1121 attached->setView(q);
1122 } else if (!reuseExistingContext) {
1123 delete context;
1124 }
1125 sectionCriteria->delegate()->completeCreate();
1126 }
1127
1128 if (sectionItem)
1129 QQuickItemPrivate::get(sectionItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
1130
1131 return sectionItem;
1132}
1133
1135{
1136 if (!item)
1137 return;
1138 int i = 0;
1139
1140 QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
1141
1142 do {
1143 if (!sectionCache[i]) {
1144 sectionCache[i] = item;
1145 sectionCache[i]->setVisible(false);
1146 return;
1147 }
1148 ++i;
1149 } while (i < sectionCacheSize);
1150 delete item;
1151}
1152
1153
1155{
1156 for (FxViewItem *item : std::as_const(visibleItems)) {
1157 FxListItemSG *listItem = static_cast<FxListItemSG *>(item);
1158 if (listItem->section()) {
1159 qreal pos = listItem->position();
1161 listItem->setSection(nullptr);
1162 listItem->setPosition(pos);
1163 }
1164 }
1165 for (int i = 0; i < sectionCacheSize; ++i) {
1166 delete sectionCache[i];
1167 sectionCache[i] = nullptr;
1168 }
1169}
1170
1172{
1173 if (!sectionCriteria || !sectionCriteria->delegate())
1174 return;
1175 if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
1176 && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
1177 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
1178 if (!listItem->section()) {
1179 qreal pos = listItem->position();
1180 listItem->setSection(getSectionItem(listItem->attached->m_section));
1181 listItem->setPosition(pos);
1182 } else {
1183 QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
1184 setSectionHelper(context, listItem->section(), listItem->attached->m_section);
1185 }
1186 } else if (listItem->section()) {
1187 qreal pos = listItem->position();
1189 listItem->setSection(nullptr);
1190 listItem->setPosition(pos);
1191 }
1192}
1193
1195{
1196 if (!sectionCriteria || !sectionCriteria->delegate()
1197 || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1198 return;
1199
1200 bool isFlowReversed = isContentFlowReversed();
1201 qreal viewPos = isFlowReversed ? -position()-size() : position();
1202 qreal startPos = hasStickyHeader() ? header->endPosition() : viewPos;
1203 qreal endPos = hasStickyFooter() ? footer->position() : viewPos + size();
1204
1205 QQuickItem *sectionItem = nullptr;
1206 QQuickItem *lastSectionItem = nullptr;
1207 int index = 0;
1208 while (index < visibleItems.size()) {
1209 if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1210 // Find the current section header and last visible section header
1211 // and hide them if they will overlap a static section header.
1212 qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1213 qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1214 bool visTop = true;
1215 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1216 visTop = isFlowReversed ? -sectionPos-sectionSize >= startPos : sectionPos >= startPos;
1217 bool visBot = true;
1218 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1219 visBot = isFlowReversed ? -sectionPos <= endPos : sectionPos + sectionSize < endPos;
1220 section->setVisible(visBot && visTop);
1221 if (visTop && !sectionItem)
1222 sectionItem = section;
1223 if (isFlowReversed) {
1224 if (-sectionPos <= endPos)
1225 lastSectionItem = section;
1226 } else {
1227 if (sectionPos + sectionSize < endPos)
1228 lastSectionItem = section;
1229 }
1230 }
1231 ++index;
1232 }
1233
1234 // Current section header
1235 if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.size()) {
1236 if (!currentSectionItem) {
1237 currentSectionItem = getSectionItem(currentSection);
1238 } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1239 QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1240 setSectionHelper(context, currentSectionItem, currentSection);
1241 }
1242 currentStickySection = currentSection;
1243 if (!currentSectionItem)
1244 return;
1245
1246 qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1247 bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1248
1249 currentSectionItem->setVisible(!atBeginning && (!header || hasStickyHeader() || header->endPosition() < viewPos));
1250 qreal pos = isFlowReversed ? position() + size() - sectionSize : startPos;
1251 if (header)
1252 pos = isFlowReversed ? qMin(-header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1253 if (sectionItem) {
1254 qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1255 pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1256 }
1257 if (footer)
1258 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1259 if (orient == QQuickListView::Vertical)
1260 currentSectionItem->setY(pos);
1261 else
1262 currentSectionItem->setX(pos);
1263 } else if (currentSectionItem) {
1265 currentSectionItem = nullptr;
1266 }
1267
1268 // Next section footer
1269 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.size()) {
1270 if (!nextSectionItem) {
1271 nextSectionItem = getSectionItem(nextSection);
1272 } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1273 QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1274 setSectionHelper(context, nextSectionItem, nextSection);
1275 }
1276 nextStickySection = nextSection;
1277 if (!nextSectionItem)
1278 return;
1279
1280 qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1281 nextSectionItem->setVisible(!nextSection.isEmpty());
1282 qreal pos = isFlowReversed ? position() : endPos - sectionSize;
1283 if (footer)
1284 pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1285 if (lastSectionItem) {
1286 qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1287 pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1288 }
1289 if (header)
1290 pos = isFlowReversed ? qMin(-header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1291 if (orient == QQuickListView::Vertical)
1292 nextSectionItem->setY(pos);
1293 else
1294 nextSectionItem->setX(pos);
1295 } else if (nextSectionItem) {
1297 nextSectionItem = nullptr;
1298 }
1299}
1300
1302{
1303 Q_Q(QQuickListView);
1304 if (!q->isComponentComplete())
1305 return;
1306
1307 QQuickItemViewPrivate::updateSections();
1308
1309 if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1310 QString prevSection;
1311 if (visibleIndex > 0)
1312 prevSection = sectionAt(visibleIndex-1);
1313 QQuickListViewAttached *prevAtt = nullptr;
1314 int prevIdx = -1;
1315 int idx = -1;
1316 for (FxViewItem *item : std::as_const(visibleItems)) {
1317 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(item->attached);
1318 attached->setPrevSection(prevSection);
1319 if (item->index != -1) {
1320 QString propValue = model->stringValue(item->index, sectionCriteria->property());
1321 attached->setSection(sectionCriteria->sectionString(propValue));
1322 idx = item->index;
1323 }
1324 updateInlineSection(static_cast<FxListItemSG*>(item));
1325 if (prevAtt)
1326 prevAtt->setNextSection(sectionAt(prevIdx+1));
1327 prevSection = attached->section();
1328 prevAtt = attached;
1329 prevIdx = item->index;
1330 }
1331 if (prevAtt) {
1332 if (idx > 0 && idx < model->count()-1)
1333 prevAtt->setNextSection(sectionAt(idx+1));
1334 else
1335 prevAtt->setNextSection(QString());
1336 }
1337 }
1338
1339 lastVisibleSection = QString();
1340}
1341
1343{
1344 Q_Q(QQuickListView);
1345 if (!sectionCriteria || visibleItems.isEmpty()) {
1346 if (!currentSection.isEmpty()) {
1347 currentSection.clear();
1348 emit q->currentSectionChanged();
1349 }
1350 return;
1351 }
1352 bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1353 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1354 qreal startPos = hasStickyHeader() ? header->endPosition() : viewPos;
1355 int index = 0;
1356 int modelIndex = visibleIndex;
1357 while (index < visibleItems.size()) {
1358 FxViewItem *item = visibleItems.at(index);
1359 if (item->endPosition() > startPos)
1360 break;
1361 if (item->index != -1)
1362 modelIndex = item->index;
1363 ++index;
1364 }
1365
1366 QString newSection = currentSection;
1367 if (index < visibleItems.size())
1368 newSection = visibleItems.at(index)->attached->section();
1369 else
1370 newSection = (*visibleItems.constBegin())->attached->section();
1371 if (newSection != currentSection) {
1372 currentSection = newSection;
1374 emit q->currentSectionChanged();
1375 }
1376
1377 if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1378 // Don't want to scan for next section on every movement, so remember
1379 // the last section in the visible area and only scan for the next
1380 // section when that changes. Clearing lastVisibleSection will also
1381 // force searching.
1382 QString lastSection = currentSection;
1383 qreal endPos = hasStickyFooter() ? footer->position() : viewPos + size();
1384 if (nextSectionItem && !inlineSections)
1385 endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1386 while (index < visibleItems.size()) {
1387 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(index));
1388 if (listItem->itemPosition() >= endPos)
1389 break;
1390 if (listItem->index != -1)
1391 modelIndex = listItem->index;
1392 lastSection = listItem->attached->section();
1393 ++index;
1394 }
1395
1396 if (lastVisibleSection != lastSection) {
1397 nextSection = QString();
1398 lastVisibleSection = lastSection;
1399 for (int i = modelIndex; i < itemCount; ++i) {
1400 QString section = sectionAt(i);
1401 if (section != lastSection) {
1402 nextSection = section;
1404 break;
1405 }
1406 }
1407 }
1408 }
1409}
1410
1412{
1413 QQuickItemViewPrivate::initializeCurrentItem();
1414
1415 if (currentItem) {
1416 FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1417
1418 // don't reposition the item if it is already in the visibleItems list
1419 FxViewItem *actualItem = visibleItem(currentIndex);
1420 if (!actualItem) {
1421 if (currentIndex == visibleIndex - 1 && visibleItems.size()) {
1422 // We can calculate exact postion in this case
1423 listItem->setPosition(visibleItems.constFirst()->position() - currentItem->size() - spacing);
1424 } else {
1425 // Create current item now and position as best we can.
1426 // Its position will be corrected when it becomes visible.
1427 listItem->setPosition(positionAt(currentIndex));
1428 }
1429 }
1430
1431 if (visibleItems.isEmpty())
1432 averageSize = listItem->size();
1433 }
1434}
1435
1437{
1438 if (!visibleItems.size())
1439 return;
1440 qreal sum = 0.0;
1441 for (FxViewItem *item : std::as_const(visibleItems))
1442 sum += item->size();
1443 averageSize = qRound(sum / visibleItems.size());
1444}
1445
1447{
1448 return header ? header->size() : 0.0;
1449}
1450
1452{
1453 return footer ? footer->size() : 0.0;
1454}
1455
1457{
1458 return index == 0;
1459}
1460
1462{
1463 return model && index == model->count()-1;
1464}
1465
1467{
1468 Q_Q(QQuickListView);
1469 bool created = false;
1470 if (!footer) {
1471 QQuickItem *item = createComponentItem(footerComponent, 1.0);
1472 if (!item)
1473 return;
1474 footer = new FxListItemSG(item, q, true);
1475 footer->trackGeometry(true);
1476 created = true;
1477 }
1478
1479 FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1480 if (footerPositioning == QQuickListView::OverlayFooter) {
1481 listItem->setPosition(isContentFlowReversed() ? -position() - footerSize() : position() + size() - footerSize(), false, false);
1482 } else if (visibleItems.size()) {
1483 if (footerPositioning == QQuickListView::PullBackFooter) {
1484 qreal viewPos = isContentFlowReversed() ? -position() : position() + size();
1485 // using qBound() would throw an assert here, because max < min is a valid case
1486 // here, if the list's delegates do not fill the whole view
1487 qreal clampedPos = qMax(originPosition() - footerSize() + size(), qMin(listItem->position(), lastPosition()));
1488 listItem->setPosition(qBound(viewPos - footerSize(), clampedPos, viewPos), false, false);
1489 } else {
1490 qreal endPos = lastPosition();
1491 if (findLastVisibleIndex() == model->count()-1) {
1492 listItem->setPosition(endPos, false, false);
1493 } else {
1494 qreal visiblePos = position() + q->height();
1495 if (endPos <= visiblePos || listItem->position() < endPos)
1496 listItem->setPosition(endPos, false, false);
1497 }
1498 }
1499 } else {
1500 listItem->setPosition(visiblePos, false, false);
1501 }
1502
1503 if (created)
1504 emit q->footerItemChanged();
1505}
1506
1508{
1510 QObjectPrivate::disconnect(&timeline, &QQuickTimeLine::updated, this, &QQuickListViewPrivate::fixupHeader);
1511}
1512
1514{
1515 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1516 const bool fixingUp = (orient == QQuickListView::Vertical ? vData : hData).fixingUp;
1517 if (fixingUp && headerPositioning == QQuickListView::PullBackHeader && visibleItems.size()) {
1518 int fixupDura = timeline.duration();
1519 if (fixupDura < 0)
1520 fixupDura = fixupDuration/2;
1521 const int t = timeline.time();
1522
1523 const qreal progress = qreal(t)/fixupDura;
1524 const qreal ultimateHeaderPosition = desiredHeaderVisible ? desiredViewportPosition : desiredViewportPosition - headerSize();
1525 const qreal headerPosition = fixupHeaderPosition * (1 - progress) + ultimateHeaderPosition * progress;
1526 const qreal viewPos = isContentFlowReversed() ? -position() - size() : position();
1527 const qreal clampedPos = qBound(originPosition() - headerSize(), headerPosition, lastPosition() - size());
1528 listItem->setPosition(qBound(viewPos - headerSize(), clampedPos, viewPos));
1529 }
1530}
1531
1533{
1534 Q_Q(QQuickListView);
1535 bool created = false;
1536 if (!header) {
1537 QQuickItem *item = createComponentItem(headerComponent, 1.0);
1538 if (!item)
1539 return;
1540 header = new FxListItemSG(item, q, true);
1541 header->trackGeometry(true);
1542 created = true;
1543 }
1544
1545 FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1546 if (headerPositioning == QQuickListView::OverlayHeader) {
1547 listItem->setPosition(isContentFlowReversed() ? -position() - size() : position(), false, false);
1548 } else if (visibleItems.size()) {
1549 const bool fixingUp = (orient == QQuickListView::Vertical ? vData : hData).fixingUp;
1550 if (headerPositioning == QQuickListView::PullBackHeader) {
1551 qreal headerPosition = listItem->position();
1552 const qreal viewPos = isContentFlowReversed() ? -position() - size() : position();
1553 // Make sure the header is not shown if we absolutely do not have any plans to show it
1554 if (fixingUp && !headerNeedsSeparateFixup)
1555 headerPosition = viewPos - headerSize();
1556 // using qBound() would throw an assert here, because max < min is a valid case
1557 // here, if the list's delegates do not fill the whole view
1558 qreal clampedPos = qMax(originPosition() - headerSize(), qMin(headerPosition, lastPosition() - size()));
1559 listItem->setPosition(qBound(viewPos - headerSize(), clampedPos, viewPos), false, false);
1560 } else {
1561 qreal startPos = originPosition();
1562 if (visibleIndex == 0) {
1563 listItem->setPosition(startPos - headerSize(), false, false);
1564 } else {
1565 if (position() <= startPos || listItem->position() > startPos - headerSize())
1566 listItem->setPosition(startPos - headerSize(), false, false);
1567 }
1568 }
1569 } else {
1570 listItem->setPosition(-headerSize(), false, false);
1571 }
1572
1573 if (created)
1574 emit q->headerItemChanged();
1575}
1576
1578{
1579 return header && headerPositioning != QQuickListView::InlineHeader;
1580}
1581
1583{
1584 return footer && footerPositioning != QQuickListView::InlineFooter;
1585}
1586
1588{
1589 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
1590 qmlAttachedPropertiesObject<QQuickListView>(item));
1591 if (attached) // can be null for default components (see createComponentItem)
1592 attached->setView(const_cast<QQuickListView*>(q_func()));
1593}
1594
1596 const QRectF &oldGeometry)
1597{
1598 Q_Q(QQuickListView);
1599
1600 QQuickItemViewPrivate::itemGeometryChanged(item, change, oldGeometry);
1601 if (!q->isComponentComplete())
1602 return;
1603
1604 if (currentItem && currentItem->item == item) {
1605 const bool contentFlowReversed = isContentFlowReversed();
1606 const qreal pos = position();
1607 const qreal sz = size();
1608 const qreal from = contentFlowReversed ? -pos - displayMarginBeginning - sz : pos - displayMarginBeginning;
1609 const qreal to = contentFlowReversed ? -pos + displayMarginEnd : pos + sz + displayMarginEnd;
1610 QQuickItemPrivate::get(currentItem->item)->setCulled(currentItem->endPosition() < from || currentItem->position() > to);
1611 }
1612
1613 if (item != contentItem && (!highlight || item != highlight->item)) {
1614 if ((orient == QQuickListView::Vertical && change.heightChange())
1615 || (orient == QQuickListView::Horizontal && change.widthChange())) {
1616
1617 // if visibleItems.first() has resized, adjust its pos since it is used to
1618 // position all subsequent items
1619 if (visibleItems.size() && item == visibleItems.constFirst()->item) {
1620 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.constFirst());
1621#if QT_CONFIG(quick_viewtransitions)
1622 if (listItem->transitionScheduledOrRunning())
1623 return;
1624#endif
1625 if (orient == QQuickListView::Vertical) {
1626 const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height();
1627 const qreal heightDiff = item->height() - oldGeometry.height();
1628 if (verticalLayoutDirection == QQuickListView::TopToBottom && oldItemEndPosition < q->contentY())
1629 listItem->setPosition(listItem->position() - heightDiff, true);
1630 else if (verticalLayoutDirection == QQuickListView::BottomToTop && oldItemEndPosition > q->contentY())
1631 listItem->setPosition(listItem->position() + heightDiff, true);
1632 } else {
1633 const qreal oldItemEndPosition = q->effectiveLayoutDirection() == Qt::RightToLeft ? -oldGeometry.x() : oldGeometry.x() + oldGeometry.width();
1634 const qreal widthDiff = item->width() - oldGeometry.width();
1635 if (q->effectiveLayoutDirection() == Qt::LeftToRight && oldItemEndPosition < q->contentX())
1636 listItem->setPosition(listItem->position() - widthDiff, true);
1637 else if (q->effectiveLayoutDirection() == Qt::RightToLeft && oldItemEndPosition > q->contentX())
1638 listItem->setPosition(listItem->position() + widthDiff, true);
1639 }
1640 }
1641 forceLayoutPolish();
1642 }
1643 }
1644}
1645
1647{
1648 if (orient == QQuickListView::Vertical)
1649 fixupY();
1650 else
1651 fixupX();
1653}
1654
1655void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1656{
1657 if (orient == QQuickListView::Horizontal && &data == &vData) {
1658 if (flickableDirection != QQuickFlickable::HorizontalFlick)
1659 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1660 return;
1661 } else if (orient == QQuickListView::Vertical && &data == &hData) {
1662 if (flickableDirection != QQuickFlickable::VerticalFlick)
1663 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1664 return;
1665 }
1666
1667 // update footer if all visible items have been removed
1668 if (visibleItems.size() == 0)
1670
1671 correctFlick = false;
1672 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1673 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1674
1675 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1676
1677 if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1678 /*
1679 There are many ways items can "snap" (align) when a flick by mouse/touch is about to end.
1680 The following table describes how things are snapped for a TopToBottom ListView (the
1681 behavior of the other orientations can be derived from TopToBottom):
1682
1683 | header\\range | No highlight range | Has a highlight range |
1684 |------------------ | ----------------------------- | --------------------- |
1685 | No header | Snaps to ListView top | Snaps to preferredHighlightBegin position [1] |
1686 | InlineHeader | Snaps to ListView top | Snaps to preferredHighlightBegin position [1] |
1687 | OverlayHeader | Snaps to header | Snaps to neither [!] |
1688 | PullbackHeader | Snaps to header/ListView top | Snaps to preferredHighlightBegin when header is pulled back. Snaps to neither when header is pulled in. |
1689
1690 Notes:
1691 [1]: If there is no item below preferredHighlightBegin, it will snap to preferredHighlightEnd
1692 [!]: This is likely not intended behavior
1693 */
1694 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1695 if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1696 // if we've been dragged < averageSize/2 then bias towards the next item
1697 qreal dist = data.move.value() - data.pressPos;
1698 qreal bias = 0;
1699 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1700 bias = averageSize/2;
1701 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1702 bias = -averageSize/2;
1703 if (isContentFlowReversed())
1704 bias = -bias;
1705 tempPosition -= bias;
1706 }
1707
1708 qreal snapOffset = 0;
1709 qreal overlayHeaderOffset = 0;
1710 bool isHeaderWithinBounds = false;
1711 if (header) {
1712 qreal visiblePartOfHeader = header->position() + header->size() - tempPosition;
1713 isHeaderWithinBounds = visiblePartOfHeader > 0;
1714 switch (headerPositioning) {
1715 case QQuickListView::OverlayHeader:
1716 snapOffset = header->size();
1717 overlayHeaderOffset = header->size();
1718 break;
1719 case QQuickListView::InlineHeader:
1720 if (isHeaderWithinBounds && tempPosition < originPosition())
1721 // For the inline header, we want to snap to the first item
1722 // if we're more than halfway down the inline header.
1723 // So if we look for an item halfway down of the header
1724 snapOffset = header->size() / 2;
1725 break;
1726 case QQuickListView::PullBackHeader:
1727 desiredHeaderVisible = visiblePartOfHeader > header->size()/2;
1728 if (qFuzzyCompare(header->position(), tempPosition)) {
1729 // header was pulled down; make sure it remains visible and snap items to bottom of header
1730 snapOffset = header->size();
1731 } else if (desiredHeaderVisible) {
1732 // More than 50% of the header is shown. Show it fully.
1733 // Scroll the view so the next item snaps to the header.
1734 snapOffset = header->size();
1735 overlayHeaderOffset = header->size();
1736 }
1737 break;
1738 }
1739 }
1740
1741 // If there are pending changes, the item returned from snapItemAt might get deleted as
1742 // soon as applyPendingChanges() is called (from e.g. updateHighlight()).
1743 // Therefore, apply the pending changes before we call snapItemAt()
1744 if (strictHighlightRange)
1746
1747 FxViewItem *topItem = nullptr;
1748 FxViewItem *bottomItem = nullptr;
1749 if (snapResizeTargetIndex >= 0) {
1750 topItem = visibleItem(snapResizeTargetIndex);
1751 bottomItem = topItem;
1752 }
1753 if (!topItem)
1754 topItem = snapItemAt(tempPosition + snapOffset + highlightRangeStart);
1755 if (!bottomItem)
1756 bottomItem = snapItemAt(tempPosition + snapOffset + highlightRangeEnd);
1757 if (strictHighlightRange && currentItem) {
1758 // StrictlyEnforceRange always keeps an item in range
1759 if (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))
1760 topItem = currentItem;
1761 if (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))
1762 bottomItem = currentItem;
1763 }
1764
1765 qreal pos = 0;
1766 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1767
1768 if (header && !topItem && isInBounds) {
1769 // We are trying to pull back further than needed
1770 switch (headerPositioning) {
1771 case QQuickListView::OverlayHeader:
1772 pos = startPosition() - overlayHeaderOffset;
1773 break;
1774 case QQuickListView::InlineHeader:
1775 pos = isContentFlowReversed() ? header->size() - size() : header->position();
1776 break;
1777 case QQuickListView::PullBackHeader:
1778 pos = isContentFlowReversed() ? -size() : startPosition();
1779 break;
1780 }
1781 } else if (topItem && (isInBounds || strictHighlightRange)) {
1782 if (topItem->index == 0 && header && !hasStickyHeader() && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1783 pos = isContentFlowReversed() ? -header->position() + highlightRangeStart - size() : (header->position() - highlightRangeStart + header->size());
1784 } else {
1785 if (header && headerPositioning == QQuickListView::PullBackHeader) {
1786 // We pulled down the header. If it isn't pulled all way down, we need to snap
1787 // the header.
1788 if (qFuzzyCompare(tempPosition, header->position())) {
1789 // It is pulled all way down. Scroll-snap the content, but not the header.
1790 if (isContentFlowReversed())
1791 pos = -static_cast<FxListItemSG*>(topItem)->itemPosition() + highlightRangeStart - size() + snapOffset;
1792 else
1793 pos = static_cast<FxListItemSG*>(topItem)->itemPosition() - highlightRangeStart - snapOffset;
1794 } else {
1795 // Header is not pulled all way down, make it completely visible or hide it.
1796 // Depends on how much of the header is visible.
1798 // More than half of the header is visible - show it.
1799 // Scroll so that the topItem is aligned to a fully visible header
1800 if (isContentFlowReversed())
1801 pos = -static_cast<FxListItemSG*>(topItem)->itemPosition() + highlightRangeStart - size() + headerSize();
1802 else
1803 pos = static_cast<FxListItemSG*>(topItem)->itemPosition() - highlightRangeStart - headerSize();
1804 } else {
1805 // Less than half is visible - hide the header. Scroll so
1806 // that the topItem is aligned to the top of the view
1807 if (isContentFlowReversed())
1808 pos = -static_cast<FxListItemSG*>(topItem)->itemPosition() + highlightRangeStart - size();
1809 else
1810 pos = static_cast<FxListItemSG*>(topItem)->itemPosition() - highlightRangeStart;
1811 }
1812 }
1813
1814 headerNeedsSeparateFixup = isHeaderWithinBounds || desiredHeaderVisible;
1816 // We need to animate the header independently if it starts visible or should end as visible,
1817 // since the header should not necessarily follow the content.
1818 // Store the desired viewport position.
1819 // Also store the header position so we know where to animate the header from (fixupHeaderPosition).
1820 // We deduce the desired header position from the desiredViewportPosition variable.
1821 pos = qBound(-minExtent, pos, -maxExtent);
1822 desiredViewportPosition = isContentFlowReversed() ? -pos - size() : pos;
1823
1824 FxListItemSG *headerItem = static_cast<FxListItemSG*>(header);
1825 fixupHeaderPosition = headerItem->position();
1826
1827 // follow the same fixup timeline
1828 QObjectPrivate::connect(&timeline, &QQuickTimeLine::updated, this, &QQuickListViewPrivate::fixupHeader);
1829 QObjectPrivate::connect(&timeline, &QQuickTimeLine::completed, this, &QQuickListViewPrivate::fixupHeaderCompleted);
1830 }
1831 } else if (isContentFlowReversed()) {
1832 pos = -static_cast<FxListItemSG*>(topItem)->itemPosition() + highlightRangeStart - size() + overlayHeaderOffset;
1833 } else {
1834 pos = static_cast<FxListItemSG*>(topItem)->itemPosition() - highlightRangeStart - overlayHeaderOffset;
1835 }
1836 }
1837 } else if (bottomItem && isInBounds) {
1838 if (isContentFlowReversed())
1839 pos = -static_cast<FxListItemSG*>(bottomItem)->itemPosition() + highlightRangeEnd - size() + overlayHeaderOffset;
1840 else
1841 pos = static_cast<FxListItemSG*>(bottomItem)->itemPosition() - highlightRangeEnd - overlayHeaderOffset;
1842 } else {
1843 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1844 return;
1845 }
1846 // If we have the CurrentLabelAtStart flag set, then we need to consider
1847 // the section size while calculating the position
1848 if (sectionCriteria
1849 && (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1850 && currentSectionItem) {
1851 auto sectionSize = (orient == QQuickListView::Vertical) ? currentSectionItem->height()
1852 : currentSectionItem->width();
1853 if (isContentFlowReversed())
1854 pos += sectionSize;
1855 else
1856 pos -= sectionSize;
1857 }
1858
1859 pos = qBound(-minExtent, pos, -maxExtent);
1860
1861 qreal dist = qAbs(data.move + pos);
1862 if (dist >= 0) {
1863 // Even if dist == 0 we still start the timeline, because we use the same timeline for
1864 // moving the header. And we might need to move the header while the content does not
1865 // need moving
1866 timeline.reset(data.move);
1867 if (fixupMode != Immediate) {
1868 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1869 data.fixingUp = true;
1870 } else {
1871 timeline.set(data.move, -pos);
1872 }
1873 vTime = timeline.time();
1874 }
1875 } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1877 qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1878 if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1879 viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1880 if (viewPos > pos - highlightRangeStart)
1881 viewPos = pos - highlightRangeStart;
1882 if (isContentFlowReversed())
1883 viewPos = -viewPos-size();
1884
1885 timeline.reset(data.move);
1886 if (viewPos != position()) {
1887 if (fixupMode != Immediate) {
1888 if (fixupMode == ExtentChanged && data.fixingUp)
1889 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::OutQuad), fixupDuration/2);
1890 else
1891 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1892 data.fixingUp = true;
1893 } else {
1894 timeline.set(data.move, -viewPos);
1895 }
1896 }
1897 vTime = timeline.time();
1898 } else {
1899 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1900 }
1901 data.inOvershoot = false;
1902 fixupMode = Normal;
1903}
1904
1905bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1906 QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
1907{
1908 data.fixingUp = false;
1909 moveReason = Mouse;
1910 if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1911 correctFlick = true;
1912 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, eventType, velocity);
1913 }
1914 qreal maxDistance = 0;
1915 const qreal dataValue =
1916 isContentFlowReversed() ? -data.move.value() + size() : data.move.value();
1917
1918 // -ve velocity means list is moving up/left
1919 if (velocity > 0) {
1920 if (data.move.value() < minExtent) {
1921 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1922 // averageSize/2 + 1 - next item
1923 qreal bias = averageSize / 2 + 1 - (pressed ? data.pressPos : 0);
1924 if (isContentFlowReversed())
1925 bias = -bias;
1926 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1927 maxDistance = qAbs(data.flickTarget - data.move.value());
1928 velocity = maxVelocity;
1929 } else {
1930 maxDistance = qAbs(minExtent - data.move.value());
1931 }
1932 }
1933 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1934 data.flickTarget = minExtent;
1935 } else {
1936 if (data.move.value() > maxExtent) {
1937 if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1938 // averageSize/2 + 1 - next item
1939 qreal bias = averageSize / 2 + 1 - (pressed ? data.pressPos : 0);
1940 if (isContentFlowReversed())
1941 bias = -bias;
1942 data.flickTarget =
1943 -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1944 maxDistance = qAbs(data.flickTarget - data.move.value());
1945 velocity = -maxVelocity;
1946 } else {
1947 maxDistance = qAbs(maxExtent - data.move.value());
1948 }
1949 }
1950 if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1951 data.flickTarget = maxExtent;
1952 }
1953 bool overShoot = boundsBehavior & QQuickFlickable::OvershootBounds;
1954 if (maxDistance > 0 || overShoot) {
1955 // These modes require the list to stop exactly on an item boundary.
1956 // The initial flick will estimate the boundary to stop on.
1957 // Since list items can have variable sizes, the boundary will be
1958 // reevaluated and adjusted as we approach the boundary.
1959 qreal v = velocity;
1960 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1961 if (v < 0)
1962 v = -maxVelocity;
1963 else
1964 v = maxVelocity;
1965 }
1966 if (!hData.flicking && !vData.flicking) {
1967 // the initial flick - estimate boundary
1968 qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
1969 qreal v2 = v * v;
1970 overshootDist = 0.0;
1971 // + averageSize/4 to encourage moving at least one item in the flick direction
1972 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1973 if (maxDistance > 0)
1974 dist = qMin(dist, maxDistance);
1975 if (v > 0)
1976 dist = -dist;
1977 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1978 if (snapMode != QQuickListView::SnapOneItem) {
1979 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1980 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1981 }
1982 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1983 if (overShoot) {
1984 if (data.flickTarget > minExtent) {
1985 overshootDist = overShootDistance(vSize);
1986 data.flickTarget += overshootDist;
1987 } else if (data.flickTarget < maxExtent) {
1988 overshootDist = overShootDistance(vSize);
1989 data.flickTarget -= overshootDist;
1990 }
1991 }
1992 qreal adjDist = -data.flickTarget + data.move.value();
1993 if (qAbs(adjDist) > qAbs(dist)) {
1994 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1995 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1996 if (adjv2 > v2) {
1997 v2 = adjv2;
1998 v = qSqrt(v2);
1999 if (dist > 0)
2000 v = -v;
2001 }
2002 }
2003 dist = adjDist;
2004 accel = v2 / (2.0f * qAbs(dist));
2005 } else if (overShoot) {
2006 data.flickTarget = data.move.value() - dist;
2007 if (data.flickTarget > minExtent) {
2008 overshootDist = overShootDistance(vSize);
2009 data.flickTarget += overshootDist;
2010 } else if (data.flickTarget < maxExtent) {
2011 overshootDist = overShootDistance(vSize);
2012 data.flickTarget -= overshootDist;
2013 }
2014 }
2015 timeline.reset(data.move);
2016 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
2017 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
2018 correctFlick = true;
2019 return true;
2020 } else {
2021 // reevaluate the target boundary.
2022 qreal newtarget = data.flickTarget;
2023 if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
2024 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
2025 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
2026 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
2027 }
2028 if (velocity < 0 && newtarget <= maxExtent)
2029 newtarget = maxExtent - overshootDist;
2030 else if (velocity > 0 && newtarget >= minExtent)
2031 newtarget = minExtent + overshootDist;
2032 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
2033 if (qAbs(velocity) < _q_MinimumFlickVelocity)
2034 correctFlick = false;
2035 return false;
2036 }
2037 data.flickTarget = newtarget;
2038 qreal dist = -newtarget + data.move.value();
2039 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
2040 correctFlick = false;
2041 timeline.reset(data.move);
2042 fixup(data, minExtent, maxExtent);
2043 return false;
2044 }
2045 timeline.reset(data.move);
2046 timeline.accelDistance(data.move, v, -dist);
2047 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
2048 return false;
2049 }
2050 } else {
2051 correctFlick = false;
2052 timeline.reset(data.move);
2053 fixup(data, minExtent, maxExtent);
2054 return false;
2055 }
2056}
2057
2058void QQuickListViewPrivate::setSectionHelper(QQmlContext *context, QQuickItem *sectionItem, const QString &section)
2059{
2060 if (!QQmlContextData::get(context)->isInternal() && context->contextProperty(QLatin1String("section")).isValid())
2061 context->setContextProperty(QLatin1String("section"), section);
2062 else
2063 sectionItem->setProperty("section", section);
2064}
2065
2067{
2068 QObject *attachedObject = qmlAttachedPropertiesObject<QQuickListView>(object);
2069 return static_cast<QQuickItemViewAttached *>(attachedObject);
2070}
2071
2072//----------------------------------------------------------------------------
2073
2074/*!
2075 \qmltype ListView
2076 \nativetype QQuickListView
2077 \inqmlmodule QtQuick
2078 \ingroup qtquick-views
2079 \inherits Flickable
2080 \brief Provides a list view of items provided by a model.
2081
2082 A ListView displays data from models created from built-in QML types like ListModel
2083 and XmlListModel, or custom model classes defined in C++ that inherit from
2084 QAbstractItemModel or QAbstractListModel.
2085
2086 A ListView has a \l model, which defines the data to be displayed, and
2087 a \l delegate, which defines how the data should be displayed. Items in a
2088 ListView are laid out horizontally or vertically. List views are inherently
2089 flickable because ListView inherits from \l Flickable.
2090
2091 \note ListView will only load as many delegate items as needed to fill up the view.
2092 Items outside of the view will not be loaded unless a sufficient \l cacheBuffer has
2093 been set. Hence, a ListView with zero width or height might not load any delegate
2094 items at all.
2095
2096 \section1 Example Usage
2097
2098 The following example shows the definition of a simple list model defined
2099 in a file called \c ContactModel.qml:
2100
2101 \snippet qml/listview/ContactModel.qml 0
2102
2103 Another component can display this model data in a ListView, like this:
2104
2105 \snippet qml/listview/listview.qml import
2106 \codeline
2107 \snippet qml/listview/listview.qml classdocs simple
2108
2109 \image listview-simple.png
2110
2111 Here, the ListView creates a \c ContactModel component for its model, and a \l Text item
2112 for its delegate. The view will create a new \l Text component for each item in the model. Notice
2113 the delegate is able to access the model's \c name and \c number data directly.
2114
2115 An improved list view is shown below. The delegate is visually improved and is moved
2116 into a separate \c contactDelegate component.
2117
2118 \snippet qml/listview/listview.qml classdocs advanced
2119 \image listview-highlight.png
2120
2121 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
2122 and \c focus is set to \c true to enable keyboard navigation for the list view.
2123 The list view itself is a focus scope (see \l{Keyboard Focus in Qt Quick} for more details).
2124
2125 Delegates are instantiated as needed and may be destroyed at any time.
2126 As such, \l {Avoid Storing State in Delegates}{state should \e never be stored in a delegate}.
2127 Delegates are usually parented to ListView's \l {Flickable::contentItem}{contentItem}, but
2128 typically depending on whether it's visible in the view or not, the \e parent
2129 can change, and sometimes be \c null. Because of that, binding to
2130 the parent's properties from within the delegate is \e not recommended. If you
2131 want the delegate to fill out the width of the ListView, consider
2132 using one of the following approaches instead:
2133
2134 \code
2135 ListView {
2136 id: listView
2137 // ...
2138
2139 delegate: Item {
2140 // Incorrect.
2141 width: parent.width
2142
2143 // Correct.
2144 width: listView.width
2145 width: ListView.view.width
2146 // ...
2147 }
2148 }
2149 \endcode
2150
2151 ListView attaches a number of properties to the root item of the delegate, for example
2152 \c ListView.isCurrentItem. In the following example, the root delegate item can access
2153 this attached property directly as \c ListView.isCurrentItem, while the child
2154 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
2155
2156 \snippet qml/listview/listview.qml isCurrentItem
2157
2158 \note Views do not enable \e clip automatically. If the view
2159 is not clipped by another item or the screen, it will be necessary
2160 to set \e {clip: true} in order to have the out of view items clipped
2161 nicely.
2162
2163
2164 \section1 ListView Layouts
2165
2166 The layout of the items in a ListView can be controlled by these properties:
2167
2168 \list
2169 \li \l orientation - controls whether items flow horizontally or vertically.
2170 This value can be either Qt.Horizontal or Qt.Vertical.
2171 \li \l layoutDirection - controls the horizontal layout direction for a
2172 horizontally-oriented view: that is, whether items are laid out from the left side of
2173 the view to the right, or vice-versa. This value can be either Qt.LeftToRight or Qt.RightToLeft.
2174 \li \l verticalLayoutDirection - controls the vertical layout direction for a vertically-oriented
2175 view: that is, whether items are laid out from the top of the view down towards the bottom of
2176 the view, or vice-versa. This value can be either ListView.TopToBottom or ListView.BottomToTop.
2177 \endlist
2178
2179 By default, a ListView has a vertical orientation, and items are laid out from top to bottom. The
2180 table below shows the different layouts that a ListView can have, depending on the values of
2181 the properties listed above.
2182
2183 \table
2184 \header
2185 \li {2, 1}
2186 \b ListViews with Qt.Vertical orientation
2187 \row
2188 \li Top to bottom
2189 \image listview-layout-toptobottom.png
2190 \li Bottom to top
2191 \image listview-layout-bottomtotop.png
2192 \header
2193 \li {2, 1}
2194 \b ListViews with Qt.Horizontal orientation
2195 \row
2196 \li Left to right
2197 \image listview-layout-lefttoright.png
2198 \li Right to left
2199 \image listview-layout-righttoleft.png
2200 \endtable
2201
2202 \section1 Flickable Direction
2203
2204 By default, a vertical ListView sets \l {Flickable::}{flickableDirection} to \e Flickable.Vertical,
2205 and a horizontal ListView sets it to \e Flickable.Horizontal. Furthermore, a vertical ListView only
2206 calculates (estimates) the \l {Flickable::}{contentHeight}, and a horizontal ListView only calculates
2207 the \l {Flickable::}{contentWidth}. The other dimension is set to \e -1.
2208
2209 Since Qt 5.9 (Qt Quick 2.9), it is possible to make a ListView that can be flicked to both directions.
2210 In order to do this, the \l {Flickable::}{flickableDirection} can be set to \e Flickable.AutoFlickDirection
2211 or \e Flickable.AutoFlickIfNeeded, and the desired \e contentWidth or \e contentHeight must be provided.
2212
2213 \snippet qml/listview/listview.qml flickBothDirections
2214
2215 \section1 Stacking Order in ListView
2216
2217 The \l {QQuickItem::z}{Z value} of items determines whether they are
2218 rendered above or below other items. ListView uses several different
2219 default Z values, depending on what type of item is being created:
2220
2221 \table
2222 \header
2223 \li Property
2224 \li Default Z value
2225 \row
2226 \li \l delegate
2227 \li 1
2228 \row
2229 \li \l footer
2230 \li 1
2231 \row
2232 \li \l header
2233 \li 1
2234 \row
2235 \li \l highlight
2236 \li 0
2237 \row
2238 \li \l section.delegate
2239 \li 2
2240 \endtable
2241
2242 These default values are set if the Z value of the item is \c 0, so setting
2243 the Z value of these items to \c 0 has no effect. Note that the Z value is
2244 of type \l [QML] {real}, so it is possible to set fractional
2245 values like \c 0.1.
2246
2247 \section1 Reusing Items
2248
2249 Since 5.15, ListView can be configured to recycle items instead of instantiating
2250 from the \l delegate whenever new rows are flicked into view. This approach improves
2251 performance, depending on the complexity of the delegate. Reusing
2252 items is off by default (for backwards compatibility reasons), but can be switched
2253 on by setting the \l reuseItems property to \c true.
2254
2255 When an item is flicked out, it moves to the \e{reuse pool}, which is an
2256 internal cache of unused items. When this happens, the \l ListView::pooled
2257 signal is emitted to inform the item about it. Likewise, when the item is
2258 moved back from the pool, the \l ListView::reused signal is emitted.
2259
2260 Any item properties that come from the model are updated when the
2261 item is reused. This includes \c index and \c row, but also
2262 any model roles.
2263
2264 \note \l {Avoid Storing State in Delegates}{Avoid storing any state inside
2265 a delegate}. If you do, reset it manually on receiving the
2266 \l ListView::reused signal.
2267
2268 If an item has timers or animations, consider pausing them on receiving
2269 the \l ListView::pooled signal. That way you avoid using the CPU resources
2270 for items that are not visible. Likewise, if an item has resources that
2271 cannot be reused, they could be freed up.
2272
2273 \note While an item is in the pool, it might still be alive and respond
2274 to connected signals and bindings.
2275
2276 \note For an item to be pooled, it needs to be completely flicked out of the bounds
2277 of the view, \e including the extra margins set with \l {ListView::}{cacheBuffer}.
2278 Some items will also never be pooled or reused, such as \l currentItem.
2279
2280 The following example shows a delegate that animates a spinning rectangle. When
2281 it is pooled, the animation is temporarily paused:
2282
2283 \snippet qml/listview/ReusableDelegate.qml 0
2284
2285 \sa {QML Data Models}, GridView, PathView, {Qt Quick Examples - Views}
2286
2287 \section1 Variable Delegate Size and Section Labels
2288
2289 Variable delegate sizes might lead to resizing and skipping of any attached
2290 \l {ScrollBar}. This is because ListView estimates its content size from
2291 allocated items (usually only the visible items, the rest are assumed to be of
2292 similar size), and variable delegate sizes prevent an accurate estimation. To
2293 reduce this effect, \l {ListView::}{cacheBuffer} can be set to higher values,
2294 effectively creating more items and improving the size estimate of unallocated
2295 items, at the expense of additional memory usage. \l{ListView::section}{Sections}
2296 have the same effect because they attach and elongate the section label to the
2297 first item within the section.
2298
2299 \section1 Avoid Storing State in Delegates
2300
2301 ListView's delegates are instantiated as needed and may be destroyed when
2302 out of view. For an illustration of this, run the following example:
2303
2304 \snippet qml/listview/stateInDelegate.qml ListView
2305
2306 When an item is clicked, \c channelActivated is set to \c true. However,
2307 because delegates can be \l {Reusing Items}{reused} and destroyed, all
2308 state is lost when the view is moved far enough. When the delegate becomes
2309 visible again, it will have its default, unmodified state (or, in the case
2310 of an item that was reused, old state from a previous item).
2311
2312 To avoid this, state should be stored in the model:
2313
2314 \snippet qml/listview/stateInModel.qml ListView
2315
2316 \section1 Hiding Delegates
2317
2318 Setting a delegate's \l {Item::}{visible} property to \c false will hide
2319 that item, but the space it occupied in the view will remain. It is
2320 possible to set the item's \l {Item::}{height} to \c 0 (for a \l
2321 {ListView::orientation}{vertical} ListView):
2322
2323 \snippet qml/listview/hideDelegate.qml ListView
2324
2325 Note that the hidden state is stored in the model, following the advice of
2326 the \l {Avoid Storing State in Delegates} section.
2327
2328 However, if \l spacing is non-zero, there will be uneven gaps between
2329 delegates.
2330
2331 A better option is to filter your model so that items that should not be
2332 visible are not loaded by the view at all. This can be achieved with
2333 \l QSortFilterProxyModel.
2334
2335 Another option is to \l {Item::enabled}{disable} the delegate instead of
2336 hiding it.
2337*/
2338QQuickListView::QQuickListView(QQuickItem *parent)
2339 : QQuickItemView(*(new QQuickListViewPrivate), parent)
2340{
2341}
2342
2343QQuickListView::~QQuickListView()
2344{
2345}
2346
2347/*!
2348 \qmlattachedproperty bool QtQuick::ListView::isCurrentItem
2349 \readonly
2350
2351 This attached property is true if this delegate is the current item; otherwise false.
2352
2353 It is attached to each instance of the delegate.
2354
2355 This property may be used to adjust the appearance of the current item, for example:
2356
2357 \snippet qml/listview/listview.qml isCurrentItem
2358*/
2359
2360/*!
2361 \qmlattachedproperty ListView QtQuick::ListView::view
2362 \readonly
2363
2364 This attached property holds the view that manages this delegate instance.
2365
2366 It is attached to each instance of the delegate and also to the header, the footer,
2367 the section and the highlight delegates.
2368*/
2369
2370/*!
2371 \qmlattachedproperty string QtQuick::ListView::previousSection
2372 \readonly
2373
2374 This attached property holds the section of the previous element.
2375
2376 It is attached to each instance of the delegate.
2377
2378 The section is evaluated using the \l {ListView::section.property}{section} properties.
2379*/
2380
2381/*!
2382 \qmlattachedproperty string QtQuick::ListView::nextSection
2383 \readonly
2384
2385 This attached property holds the section of the next element.
2386
2387 It is attached to each instance of the delegate.
2388
2389 The section is evaluated using the \l {ListView::section.property}{section} properties.
2390*/
2391
2392/*!
2393 \qmlattachedproperty string QtQuick::ListView::section
2394 \readonly
2395
2396 This attached property holds the section of this element.
2397
2398 It is attached to each instance of the delegate.
2399
2400 The section is evaluated using the \l {ListView::section.property}{section} properties.
2401*/
2402
2403/*!
2404 \qmlattachedproperty bool QtQuick::ListView::delayRemove
2405
2406 This attached property holds whether the delegate may be destroyed. It
2407 is attached to each instance of the delegate. The default value is false.
2408
2409 It is sometimes necessary to delay the destruction of an item
2410 until an animation completes. The example delegate below ensures that the
2411 animation completes before the item is removed from the list.
2412
2413 \snippet qml/listview/listview.qml delayRemove
2414
2415 If a \l remove transition has been specified, it will not be applied until
2416 delayRemove is returned to \c false.
2417*/
2418
2419/*!
2420 \qmlattachedsignal QtQuick::ListView::add()
2421 This attached signal is emitted immediately after an item is added to the view.
2422
2423 If an \l add transition is specified, it is applied immediately after
2424 this signal is handled.
2425*/
2426
2427/*!
2428 \qmlattachedsignal QtQuick::ListView::remove()
2429 This attached signal is emitted immediately before an item is removed from the view.
2430
2431 If a \l remove transition has been specified, it is applied after
2432 this signal is handled, providing that \l delayRemove is false.
2433*/
2434
2435/*!
2436 \qmlproperty model QtQuick::ListView::model
2437 This property holds the model providing data for the list.
2438
2439 The model provides the set of data that is used to create the items
2440 in the view. Models can be created directly in QML using \l ListModel,
2441 \l ObjectModel, or provided by C++ model classes. If a C++ model class is
2442 used, it must be a subclass of \l QAbstractItemModel or a simple list.
2443
2444 \sa {qml-data-models}{Data Models}
2445*/
2446
2447/*!
2448 \qmlproperty Component QtQuick::ListView::delegate
2449
2450 The delegate provides a template defining each item instantiated by the view.
2451 The index is exposed as an accessible \c index property. Properties of the
2452 model are also available depending upon the type of \l {qml-data-models}{Data Model}.
2453
2454 The number of objects and bindings in the delegate has a direct effect on the
2455 flicking performance of the view. If at all possible, place functionality
2456 that is not needed for the normal display of the delegate in a \l Loader which
2457 can load additional components when needed.
2458
2459 The ListView will lay out the items based on the size of the root item
2460 in the delegate.
2461
2462 It is recommended that the delegate's size be a whole number to avoid sub-pixel
2463 alignment of items.
2464
2465 The default \l {QQuickItem::z}{stacking order} of delegate instances is \c 1.
2466
2467 \note Delegates are instantiated as needed and may be destroyed at any time.
2468 They are parented to ListView's \l {Flickable::contentItem}{contentItem}, not to the view itself.
2469 State should \e never be stored in a delegate.
2470
2471 \sa {Stacking Order in ListView}
2472*/
2473
2474/*!
2475 \qmlproperty enumeration QtQuick::ListView::delegateModelAccess
2476 \since 6.10
2477
2478 \include delegatemodelaccess.qdocinc
2479*/
2480
2481/*!
2482 \qmlproperty int QtQuick::ListView::currentIndex
2483 \qmlproperty Item QtQuick::ListView::currentItem
2484
2485 The \c currentIndex property holds the index of the current item, and
2486 \c currentItem holds the current item. Setting the currentIndex to -1
2487 will clear the highlight and set currentItem to null.
2488
2489 If highlightFollowsCurrentItem is \c true, setting either of these
2490 properties will smoothly scroll the ListView so that the current
2491 item becomes visible.
2492
2493 Note that the position of the current item
2494 may only be approximate until it becomes visible in the view.
2495
2496 As \c currentItem needs to work with any delegate, its type is \l{Item}.
2497 Often, a \c ListView is however used with exactly one type of delegate.
2498 In that case, casting \c currentItem to the delegate's type can help
2499 tooling and lead to more efficient code:
2500
2501 \qml
2502 component Message : Item {
2503 required property string sender
2504 required property string text
2505 }
2506 ListView {
2507 id: messageView
2508 delegate: Message {}
2509 model: messageModel
2510 }
2511 Button {
2512 text: "Reply to %1".arg((messageView.currentItem) as Message).sender
2513 }
2514 \endqml
2515*/
2516
2517/*!
2518 \qmlproperty Item QtQuick::ListView::highlightItem
2519
2520 This holds the highlight item created from the \l highlight component.
2521
2522 The \c highlightItem is managed by the view unless
2523 \l highlightFollowsCurrentItem is set to false.
2524 The default \l {QQuickItem::z}{stacking order}
2525 of the highlight item is \c 0.
2526
2527 \sa highlight, highlightFollowsCurrentItem, {Stacking Order in ListView}
2528*/
2529
2530/*!
2531 \qmlproperty int QtQuick::ListView::count
2532 The property reflects the number of items in the \l ListView's model,
2533 regardless of whether they are visible or instantiated as \c Item of a delegate component.
2534*/
2535
2536/*!
2537 \qmlproperty bool QtQuick::ListView::reuseItems
2538
2539 This property enables you to reuse items that are instantiated
2540 from the \l delegate. If set to \c false, any currently
2541 pooled items are destroyed.
2542
2543 This property is \c false by default.
2544
2545 \since 5.15
2546
2547 \sa {Reusing items}, pooled(), reused()
2548*/
2549
2550/*!
2551 \qmlattachedsignal QtQuick::ListView::pooled()
2552
2553 This signal is emitted after an item has been added to the reuse
2554 pool. You can use it to pause ongoing timers or animations inside
2555 the item, or free up resources that cannot be reused.
2556
2557 This signal is emitted only if the \l reuseItems property is \c true.
2558
2559 \sa {Reusing items}, reuseItems, reused()
2560*/
2561
2562/*!
2563 \qmlattachedsignal QtQuick::ListView::reused()
2564
2565 This signal is emitted after an item has been reused. At this point, the
2566 item has been taken out of the pool and placed inside the content view,
2567 and the model properties such as \c index and \c row have been updated.
2568
2569 Other properties that are not provided by the model does not change when an
2570 item is reused. You should avoid storing any state inside a delegate, but if
2571 you do, manually reset that state on receiving this signal.
2572
2573 This signal is emitted when the item is reused, and not the first time the
2574 item is created.
2575
2576 This signal is emitted only if the \l reuseItems property is \c true.
2577
2578 \sa {Reusing items}, reuseItems, pooled()
2579*/
2580
2581/*!
2582 \qmlproperty Component QtQuick::ListView::highlight
2583 This property holds the component to use as the highlight.
2584
2585 An instance of the highlight component is created for each list.
2586 The geometry of the resulting component instance is managed by the list
2587 so as to stay with the current item, unless the highlightFollowsCurrentItem
2588 property is false. The default \l {QQuickItem::z}{stacking order} of the
2589 highlight item is \c 0.
2590
2591 \sa highlightItem, highlightFollowsCurrentItem,
2592 {Qt Quick Examples - Views#Using Highlight}{ListView Highlight Example},
2593 {Stacking Order in ListView}
2594*/
2595
2596/*!
2597 \qmlproperty bool QtQuick::ListView::highlightFollowsCurrentItem
2598 This property holds whether the highlight is managed by the view.
2599
2600 If this property is true (the default value), the highlight is moved smoothly
2601 to follow the current item. Otherwise, the
2602 highlight is not moved by the view, and any movement must be implemented
2603 by the highlight.
2604
2605 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
2606
2607 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
2608
2609 Note that the highlight animation also affects the way that the view
2610 is scrolled. This is because the view moves to maintain the
2611 highlight within the preferred highlight range (or visible viewport).
2612
2613 \sa highlight, highlightMoveVelocity
2614*/
2615//###Possibly rename these properties, since they are very useful even without a highlight?
2616/*!
2617 \qmlproperty real QtQuick::ListView::preferredHighlightBegin
2618 \qmlproperty real QtQuick::ListView::preferredHighlightEnd
2619 \qmlproperty enumeration QtQuick::ListView::highlightRangeMode
2620
2621 These properties define the preferred range of the highlight (for the current item)
2622 within the view. The \c preferredHighlightBegin value must be less than the
2623 \c preferredHighlightEnd value.
2624
2625 These properties affect the position of the current item when the list is scrolled.
2626 For example, if the currently selected item should stay in the middle of the
2627 list when the view is scrolled, set the \c preferredHighlightBegin and
2628 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
2629 item would be. If the \c currentItem is changed programmatically, the list will
2630 automatically scroll so that the current item is in the middle of the view.
2631 Furthermore, the behavior of the current item index will occur whether or not a
2632 highlight exists.
2633
2634 Valid values for \c highlightRangeMode are:
2635
2636 \value ListView.ApplyRange the view attempts to maintain the highlight within the range.
2637 However, the highlight can move outside of the range at the
2638 ends of the list or due to mouse interaction.
2639 \value ListView.StrictlyEnforceRange the highlight never moves outside of the range.
2640 The current item changes if a keyboard or mouse action would
2641 cause the highlight to move outside of the range.
2642 \value ListView.NoHighlightRange this is the default value.
2643*/
2644void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
2645{
2646 Q_D(QQuickListView);
2647 if (d->autoHighlight != autoHighlight) {
2648 if (!autoHighlight) {
2649 if (d->highlightPosAnimator)
2650 d->highlightPosAnimator->stop();
2651 if (d->highlightWidthAnimator)
2652 d->highlightWidthAnimator->stop();
2653 if (d->highlightHeightAnimator)
2654 d->highlightHeightAnimator->stop();
2655 }
2656 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
2657 }
2658}
2659
2660/*!
2661 \qmlproperty real QtQuick::ListView::spacing
2662
2663 This property holds the spacing between items.
2664
2665 The default value is 0.
2666*/
2667qreal QQuickListView::spacing() const
2668{
2669 Q_D(const QQuickListView);
2670 return d->spacing;
2671}
2672
2673void QQuickListView::setSpacing(qreal spacing)
2674{
2675 Q_D(QQuickListView);
2676 if (spacing != d->spacing) {
2677 d->spacing = spacing;
2678 d->forceLayoutPolish();
2679 emit spacingChanged();
2680 }
2681}
2682
2683/*!
2684 \qmlproperty enumeration QtQuick::ListView::orientation
2685 This property holds the orientation of the list.
2686
2687 Possible values:
2688
2689 \value ListView.Horizontal Items are laid out horizontally
2690 \br
2691 \inlineimage ListViewHorizontal.png
2692 \value ListView.Vertical (default) Items are laid out vertically
2693 \br
2694 \inlineimage listview-highlight.png
2695
2696 \sa {Flickable Direction}
2697*/
2698QQuickListView::Orientation QQuickListView::orientation() const
2699{
2700 Q_D(const QQuickListView);
2701 return d->orient;
2702}
2703
2704void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2705{
2706 Q_D(QQuickListView);
2707 if (d->orient != orientation) {
2708 d->orient = orientation;
2709 if (d->orient == Vertical) {
2710 if (d->flickableDirection == HorizontalFlick) {
2711 setFlickableDirection(VerticalFlick);
2712 if (isComponentComplete())
2713 setContentWidth(-1);
2714 }
2715 setContentX(0);
2716 } else {
2717 if (d->flickableDirection == VerticalFlick) {
2718 setFlickableDirection(HorizontalFlick);
2719 if (isComponentComplete())
2720 setContentHeight(-1);
2721 }
2722 setContentY(0);
2723 }
2724 d->regenerate(true);
2725 emit orientationChanged();
2726 }
2727}
2728
2729/*!
2730 \qmlproperty enumeration QtQuick::ListView::layoutDirection
2731 This property holds the layout direction of a horizontally-oriented list.
2732
2733 Possible values:
2734
2735 \value Qt.LeftToRight (default) Items will be laid out from left to right.
2736 \value Qt.RightToLeft Items will be laid out from right to left.
2737
2738 Setting this property has no effect if the \l orientation is Qt.Vertical.
2739
2740 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2741*/
2742
2743
2744/*!
2745 \qmlproperty enumeration QtQuick::ListView::effectiveLayoutDirection
2746 This property holds the effective layout direction of a horizontally-oriented list.
2747
2748 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2749 the visual layout direction of the horizontal list will be mirrored. However, the
2750 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2751
2752 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2753*/
2754
2755
2756/*!
2757 \qmlproperty enumeration QtQuick::ListView::verticalLayoutDirection
2758 This property holds the layout direction of a vertically-oriented list.
2759
2760 Possible values:
2761
2762 \value ListView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
2763 \value ListView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
2764
2765 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2766
2767 \sa ListView::layoutDirection
2768*/
2769
2770
2771/*!
2772 \qmlproperty bool QtQuick::ListView::keyNavigationWraps
2773 This property holds whether the list wraps key navigation.
2774
2775 If this is true, key navigation that would move the current item selection
2776 past the end of the list instead wraps around and moves the selection to
2777 the start of the list, and vice-versa.
2778
2779 By default, key navigation is not wrapped.
2780*/
2781
2782/*!
2783 \qmlproperty bool QtQuick::ListView::keyNavigationEnabled
2784 \since 5.7
2785
2786 This property holds whether the key navigation of the list is enabled.
2787
2788 If this is \c true, the user can navigate the view with a keyboard.
2789 It is useful for applications that need to selectively enable or
2790 disable mouse and keyboard interaction.
2791
2792 By default, the value of this property is bound to
2793 \l {Flickable::}{interactive} to ensure behavior compatibility for
2794 existing applications. When explicitly set, it will cease to be bound to
2795 the interactive property.
2796
2797 \sa {Flickable::}{interactive}
2798*/
2799
2800
2801/*!
2802 \qmlproperty int QtQuick::ListView::cacheBuffer
2803 This property determines whether delegates are retained outside the
2804 visible area of the view.
2805
2806 If this value is greater than zero, the view may keep as many delegates
2807 instantiated as it can fit within the buffer specified. For example,
2808 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2809 set to 40, then up to 2 delegates above and 2 delegates below the visible
2810 area may be created/retained. The buffered delegates are created asynchronously,
2811 allowing creation to occur across multiple frames and reducing the
2812 likelihood of skipping frames. In order to improve painting performance
2813 delegates outside the visible area are not painted.
2814
2815 The default value of this property is platform dependent, but will usually
2816 be a value greater than zero. Negative values are ignored.
2817
2818 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2819 instantiated delegates.
2820
2821 \note Setting this property is not a replacement for creating efficient delegates.
2822 It can improve the smoothness of scrolling behavior at the expense of additional
2823 memory usage. The fewer objects and bindings in a delegate, the faster a
2824 view can be scrolled. It is important to realize that setting a cacheBuffer
2825 will only postpone issues caused by slow-loading delegates, it is not a
2826 solution for this scenario.
2827
2828 The cacheBuffer operates outside of any display margins specified by
2829 displayMarginBeginning or displayMarginEnd.
2830*/
2831
2832/*!
2833 \qmlproperty int QtQuick::ListView::displayMarginBeginning
2834 \qmlproperty int QtQuick::ListView::displayMarginEnd
2835 \since QtQuick 2.3
2836
2837 This property allows delegates to be displayed outside of the view geometry.
2838
2839 If this value is non-zero, the view will create extra delegates before the
2840 start of the view, or after the end. The view will create as many delegates
2841 as it can fit into the pixel size specified.
2842
2843 For example, if in a vertical view the delegate is 20 pixels high and
2844 \c displayMarginBeginning and \c displayMarginEnd are both set to 40,
2845 then 2 delegates above and 2 delegates below will be created and shown.
2846
2847 The default value is 0.
2848
2849 This property is meant for allowing certain UI configurations,
2850 and not as a performance optimization. If you wish to create delegates
2851 outside of the view geometry for performance reasons, you probably
2852 want to use the cacheBuffer property instead.
2853*/
2854
2855/*!
2856 \qmlpropertygroup QtQuick::ListView::section
2857 \qmlproperty string QtQuick::ListView::section.property
2858 \qmlproperty enumeration QtQuick::ListView::section.criteria
2859 \qmlproperty Component QtQuick::ListView::section.delegate
2860 \qmlproperty enumeration QtQuick::ListView::section.labelPositioning
2861
2862 These properties determine the expression to be evaluated and appearance
2863 of the section labels.
2864
2865 \c section.property holds the name of the property that is the basis
2866 of each section.
2867
2868 \c section.criteria holds the criteria for forming each section based on
2869 \c section.property. This value can be one of:
2870
2871 \value ViewSection.FullString (default) sections are created based on the
2872 \c section.property value.
2873 \value ViewSection.FirstCharacter sections are created based on the first character of
2874 the \c section.property value (for example,
2875 'A', 'B', 'C' ... sections for an address book.)
2876
2877 A case insensitive comparison is used when determining section
2878 boundaries.
2879
2880 \c section.delegate holds the delegate component for each section. The
2881 default \l {QQuickItem::z}{stacking order} of section delegate instances
2882 is \c 2. If you declare a \c required property named "section" in it,
2883 that property will contain the section's title.
2884
2885 \c section.labelPositioning determines whether the current and/or
2886 next section labels stick to the start/end of the view, and whether
2887 the labels are shown inline. This value can be a combination of:
2888
2889 \value ViewSection.InlineLabels
2890 (default) section labels are shown inline between the item delegates
2891 separating sections.
2892 \value ViewSection.CurrentLabelAtStart
2893 the current section label sticks to the start of the view as it is moved.
2894 \value ViewSection.NextLabelAtEnd
2895 the next section label (beyond all visible sections) sticks to the end
2896 of the view as it is moved.
2897 \note Enabling \c ViewSection.NextLabelAtEnd requires the view to scan
2898 ahead for the next section, which has performance implications,
2899 especially for slower models.
2900
2901 Each item in the list has attached properties named \c ListView.section,
2902 \c ListView.previousSection and \c ListView.nextSection.
2903
2904 For example, here is a ListView that displays a list of animals, separated
2905 into sections. Each item in the ListView is placed in a different section
2906 depending on the "size" property of the model item. The \c sectionHeading
2907 delegate component provides the light blue bar that marks the beginning of
2908 each section.
2909
2910
2911 \snippet views/listview/sections.qml 0
2912
2913 \image qml-listview-sections-example.png
2914
2915 \note Adding sections to a ListView does not automatically re-order the
2916 list items by the section criteria.
2917 If the model is not ordered by section, then it is possible that
2918 the sections created will not be unique; each boundary between
2919 differing sections will result in a section header being created
2920 even if that section exists elsewhere.
2921
2922 \sa {Qt Quick Examples - Views}{ListView examples},
2923 {Stacking Order in ListView}
2924*/
2925QQuickViewSection *QQuickListView::sectionCriteria()
2926{
2927 Q_D(QQuickListView);
2928 if (!d->sectionCriteria)
2929 d->sectionCriteria = new QQuickViewSection(this);
2930 return d->sectionCriteria;
2931}
2932
2933/*!
2934 \qmlproperty string QtQuick::ListView::currentSection
2935 This property holds the section that is currently at the beginning of the view.
2936*/
2937QString QQuickListView::currentSection() const
2938{
2939 Q_D(const QQuickListView);
2940 return d->currentSection;
2941}
2942
2943/*!
2944 \qmlproperty real QtQuick::ListView::highlightMoveVelocity
2945 \qmlproperty int QtQuick::ListView::highlightMoveDuration
2946 \qmlproperty real QtQuick::ListView::highlightResizeVelocity
2947 \qmlproperty int QtQuick::ListView::highlightResizeDuration
2948
2949 These properties control the speed of the move and resize animations for the
2950 highlight delegate.
2951
2952 \l highlightFollowsCurrentItem must be true for these properties
2953 to have effect.
2954
2955 The default value for the velocity properties is 400 pixels/second.
2956 The default value for the duration properties is -1, i.e. the
2957 highlight will take as much time as necessary to move at the set speed.
2958
2959 These properties have the same characteristics as a SmoothedAnimation:
2960 if both the velocity and duration are set, the animation will use
2961 whichever gives the shorter duration.
2962
2963 The move velocity and duration properties are used to control movement due
2964 to index changes; for example, when incrementCurrentIndex() is called. When
2965 the user flicks a ListView, the velocity from the flick is used to control
2966 the movement instead.
2967
2968 To set only one property, the other can be set to \c -1. For example,
2969 if you only want to animate the duration and not velocity, use the
2970 following code:
2971
2972 \code
2973 highlightMoveDuration: 1000
2974 highlightMoveVelocity: -1
2975 \endcode
2976
2977 \sa highlightFollowsCurrentItem
2978*/
2979qreal QQuickListView::highlightMoveVelocity() const
2980{
2981 Q_D(const QQuickListView);
2982 return d->highlightMoveVelocity;
2983}
2984
2985void QQuickListView::setHighlightMoveVelocity(qreal speed)
2986{
2987 Q_D(QQuickListView);
2988 if (d->highlightMoveVelocity != speed) {
2989 d->highlightMoveVelocity = speed;
2990 if (d->highlightPosAnimator)
2991 d->highlightPosAnimator->velocity = d->highlightMoveVelocity;
2992 emit highlightMoveVelocityChanged();
2993 }
2994}
2995
2996void QQuickListView::setHighlightMoveDuration(int duration)
2997{
2998 Q_D(QQuickListView);
2999 if (d->highlightMoveDuration != duration) {
3000 if (d->highlightPosAnimator)
3001 d->highlightPosAnimator->userDuration = duration;
3002 QQuickItemView::setHighlightMoveDuration(duration);
3003 }
3004}
3005
3006qreal QQuickListView::highlightResizeVelocity() const
3007{
3008 Q_D(const QQuickListView);
3009 return d->highlightResizeVelocity;
3010}
3011
3012void QQuickListView::setHighlightResizeVelocity(qreal speed)
3013{
3014 Q_D(QQuickListView);
3015 if (d->highlightResizeVelocity != speed) {
3016 d->highlightResizeVelocity = speed;
3017 if (d->highlightWidthAnimator)
3018 d->highlightWidthAnimator->velocity = d->highlightResizeVelocity;
3019 if (d->highlightHeightAnimator)
3020 d->highlightHeightAnimator->velocity = d->highlightResizeVelocity;
3021 emit highlightResizeVelocityChanged();
3022 }
3023}
3024
3025int QQuickListView::highlightResizeDuration() const
3026{
3027 Q_D(const QQuickListView);
3028 return d->highlightResizeDuration;
3029}
3030
3031void QQuickListView::setHighlightResizeDuration(int duration)
3032{
3033 Q_D(QQuickListView);
3034 if (d->highlightResizeDuration != duration) {
3035 d->highlightResizeDuration = duration;
3036 if (d->highlightWidthAnimator)
3037 d->highlightWidthAnimator->userDuration = d->highlightResizeDuration;
3038 if (d->highlightHeightAnimator)
3039 d->highlightHeightAnimator->userDuration = d->highlightResizeDuration;
3040 emit highlightResizeDurationChanged();
3041 }
3042}
3043
3044/*!
3045 \qmlproperty enumeration QtQuick::ListView::snapMode
3046
3047 This property determines how the view scrolling will settle following a drag or flick.
3048 The possible values are:
3049
3050 \value ListView.NoSnap (default) the view stops anywhere within the visible area.
3051 \value ListView.SnapToItem the view settles with an item aligned with the start of the view.
3052 \value ListView.SnapOneItem the view settles no more than one item away from the first
3053 visible item at the time the mouse button is released. This mode is particularly
3054 useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
3055 show a stronger affinity to neighboring items when movement occurs. For example, a
3056 short drag that snaps back to the current item with SnapToItem might snap to a
3057 neighboring item with SnapOneItem.
3058
3059 \c snapMode does not affect the \l currentIndex. To update the
3060 \l currentIndex as the list is moved, set \l highlightRangeMode
3061 to \c ListView.StrictlyEnforceRange.
3062
3063 \sa highlightRangeMode
3064*/
3065QQuickListView::SnapMode QQuickListView::snapMode() const
3066{
3067 Q_D(const QQuickListView);
3068 return d->snapMode;
3069}
3070
3071void QQuickListView::setSnapMode(SnapMode mode)
3072{
3073 Q_D(QQuickListView);
3074 if (d->snapMode != mode) {
3075 d->snapMode = mode;
3076 emit snapModeChanged();
3077 d->fixupPosition();
3078 }
3079}
3080
3081
3082/*!
3083 \qmlproperty Component QtQuick::ListView::footer
3084 This property holds the component to use as the footer.
3085
3086 An instance of the footer component is created for each view. The
3087 footer is positioned at the end of the view, after any items. The
3088 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
3089
3090 \sa header, footerItem, {Stacking Order in ListView}
3091*/
3092
3093
3094/*!
3095 \qmlproperty Component QtQuick::ListView::header
3096 This property holds the component to use as the header.
3097
3098 An instance of the header component is created for each view. The
3099 header is positioned at the beginning of the view, before any items.
3100 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
3101
3102 \sa footer, headerItem, {Stacking Order in ListView}
3103*/
3104
3105/*!
3106 \qmlproperty Item QtQuick::ListView::headerItem
3107 This holds the header item created from the \l header component.
3108
3109 An instance of the header component is created for each view. The
3110 header is positioned at the beginning of the view, before any items.
3111 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
3112
3113 \sa header, footerItem, {Stacking Order in ListView}
3114*/
3115
3116/*!
3117 \qmlproperty Item QtQuick::ListView::footerItem
3118 This holds the footer item created from the \l footer component.
3119
3120 An instance of the footer component is created for each view. The
3121 footer is positioned at the end of the view, after any items. The
3122 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
3123
3124 \sa footer, headerItem, {Stacking Order in ListView}
3125*/
3126
3127/*!
3128 \qmlproperty enumeration QtQuick::ListView::headerPositioning
3129 \since Qt 5.4
3130
3131 This property determines the positioning of the \l{headerItem}{header item}.
3132
3133 \value ListView.InlineHeader (default) The header is positioned at the beginning
3134 of the content and moves together with the content like an ordinary item.
3135
3136 \value ListView.OverlayHeader The header is positioned at the beginning of the view.
3137
3138 \value ListView.PullBackHeader The header is positioned at the beginning of the view.
3139 The header can be pushed away by moving the content forwards, and pulled back by
3140 moving the content backwards.
3141
3142 \note This property has no effect on the \l {QQuickItem::z}{stacking order}
3143 of the header. For example, if the header should be shown above the
3144 \l delegate items when using \c ListView.OverlayHeader, its Z value
3145 should be set to a value higher than that of the delegates. For more
3146 information, see \l {Stacking Order in ListView}.
3147
3148 \note If \c headerPositioning is not set to \c ListView.InlineHeader, the
3149 user cannot press and flick the list from the header. In any case, the
3150 \l{headerItem}{header item} may contain items or event handlers that
3151 provide custom handling of mouse or touch input.
3152*/
3153QQuickListView::HeaderPositioning QQuickListView::headerPositioning() const
3154{
3155 Q_D(const QQuickListView);
3156 return d->headerPositioning;
3157}
3158
3159void QQuickListView::setHeaderPositioning(QQuickListView::HeaderPositioning positioning)
3160{
3161 Q_D(QQuickListView);
3162 if (d->headerPositioning != positioning) {
3163 d->applyPendingChanges();
3164 d->headerPositioning = positioning;
3165 if (isComponentComplete()) {
3166 d->updateHeader();
3167 d->updateViewport();
3168 d->fixupPosition();
3169 }
3170 emit headerPositioningChanged();
3171 }
3172}
3173
3174/*!
3175 \qmlproperty enumeration QtQuick::ListView::footerPositioning
3176 \since Qt 5.4
3177
3178 This property determines the positioning of the \l{footerItem}{footer item}.
3179
3180 \value ListView.InlineFooter (default) The footer is positioned at the end
3181 of the content and moves together with the content like an ordinary item.
3182
3183 \value ListView.OverlayFooter The footer is positioned at the end of the view.
3184
3185 \value ListView.PullBackFooter The footer is positioned at the end of the view.
3186 The footer can be pushed away by moving the content backwards, and pulled back by
3187 moving the content forwards.
3188
3189 \note This property has no effect on the \l {QQuickItem::z}{stacking order}
3190 of the footer. For example, if the footer should be shown above the
3191 \l delegate items when using \c ListView.OverlayFooter, its Z value
3192 should be set to a value higher than that of the delegates. For more
3193 information, see \l {Stacking Order in ListView}.
3194
3195 \note If \c footerPositioning is not set to \c ListView.InlineFooter, the
3196 user cannot press and flick the list from the footer. In any case, the
3197 \l{footerItem}{footer item} may contain items or event handlers that
3198 provide custom handling of mouse or touch input.
3199*/
3200QQuickListView::FooterPositioning QQuickListView::footerPositioning() const
3201{
3202 Q_D(const QQuickListView);
3203 return d->footerPositioning;
3204}
3205
3206void QQuickListView::setFooterPositioning(QQuickListView::FooterPositioning positioning)
3207{
3208 Q_D(QQuickListView);
3209 if (d->footerPositioning != positioning) {
3210 d->applyPendingChanges();
3211 d->footerPositioning = positioning;
3212 if (isComponentComplete()) {
3213 d->updateFooter();
3214 d->updateViewport();
3215 d->fixupPosition();
3216 }
3217 emit footerPositioningChanged();
3218 }
3219}
3220
3221/*!
3222 \qmlproperty Transition QtQuick::ListView::populate
3223
3224 This property holds the transition to apply to the items that are initially created
3225 for a view.
3226
3227 It is applied to all items that are created when:
3228
3229 \list
3230 \li The view is first created
3231 \li The view's \l model changes in such a way that the visible delegates are completely replaced
3232 \li The view's \l model is \l {QAbstractItemModel::beginResetModel()}{reset}, if the model is a
3233 QAbstractItemModel subclass
3234 \endlist
3235
3236 For example, here is a view that specifies such a transition:
3237
3238 \code
3239 ListView {
3240 ...
3241 populate: Transition {
3242 NumberAnimation { properties: "x,y"; duration: 1000 }
3243 }
3244 }
3245 \endcode
3246
3247 When the view is initialized, the view will create all the necessary items for the view,
3248 then animate them to their correct positions within the view over one second.
3249
3250 However when scrolling the view later, the populate transition does not
3251 run, even though delegates are being instantiated as they become visible.
3252 When the model changes in a way that new delegates become visible, the
3253 \l add transition is the one that runs. So you should not depend on the
3254 \c populate transition to initialize properties in the delegate, because it
3255 does not apply to every delegate. If your animation sets the \c to value of
3256 a property, the property should initially have the \c to value, and the
3257 animation should set the \c from value in case it is animated:
3258
3259 \code
3260 ListView {
3261 ...
3262 delegate: Rectangle {
3263 opacity: 1 // not necessary because it's the default
3264 }
3265 populate: Transition {
3266 NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
3267 }
3268 }
3269 \endcode
3270
3271 For more details and examples on how to use view transitions, see the ViewTransition
3272 documentation.
3273
3274 \sa add, ViewTransition
3275*/
3276
3277/*!
3278 \qmlproperty Transition QtQuick::ListView::add
3279
3280 This property holds the transition to apply to items that are added to the view.
3281
3282 For example, here is a view that specifies such a transition:
3283
3284 \code
3285 ListView {
3286 ...
3287 add: Transition {
3288 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
3289 }
3290 }
3291 \endcode
3292
3293 Whenever an item is added to the above view, the item will be animated from the position (100,100)
3294 to its final x,y position within the view, over one second. The transition only applies to
3295 the new items that are added to the view; it does not apply to the items below that are
3296 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
3297 or \l addDisplaced properties.
3298
3299 For more details and examples on how to use view transitions, see the ViewTransition
3300 documentation.
3301
3302 \note This transition is not applied to the items that are created when the view is initially
3303 populated, or when the view's \l model changes. (In those cases, the \l populate transition is
3304 applied instead.) Additionally, this transition should \e not animate the height of the new item;
3305 doing so will cause any items beneath the new item to be laid out at the wrong position. Instead,
3306 the height can be animated within the \l {add}{onAdd} handler in the delegate.
3307
3308 \sa addDisplaced, populate, ViewTransition
3309*/
3310
3311/*!
3312 \qmlproperty Transition QtQuick::ListView::addDisplaced
3313
3314 This property holds the transition to apply to items within the view that are displaced by
3315 the addition of other items to the view.
3316
3317 For example, here is a view that specifies such a transition:
3318
3319 \code
3320 ListView {
3321 ...
3322 addDisplaced: Transition {
3323 NumberAnimation { properties: "x,y"; duration: 1000 }
3324 }
3325 }
3326 \endcode
3327
3328 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
3329 them to move down (or sideways, if horizontally orientated) within the view. As this
3330 displacement occurs, the items' movement to their new x,y positions within the view will be
3331 animated by a NumberAnimation over one second, as specified. This transition is not applied to
3332 the new item that has been added to the view; to animate the added items, set the \l add
3333 property.
3334
3335 If an item is displaced by multiple types of operations at the same time, it is not defined as to
3336 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
3337 if it is not necessary to specify different transitions depending on whether an item is displaced
3338 by an add, move or remove operation, consider setting the \l displaced property instead.
3339
3340 For more details and examples on how to use view transitions, see the ViewTransition
3341 documentation.
3342
3343 \note This transition is not applied to the items that are created when the view is initially
3344 populated, or when the view's \l model changes. In those cases, the \l populate transition is
3345 applied instead.
3346
3347 \sa displaced, add, populate, ViewTransition
3348*/
3349
3350/*!
3351 \qmlproperty Transition QtQuick::ListView::move
3352
3353 This property holds the transition to apply to items in the view that are being moved due
3354 to a move operation in the view's \l model.
3355
3356 For example, here is a view that specifies such a transition:
3357
3358 \code
3359 ListView {
3360 ...
3361 move: Transition {
3362 NumberAnimation { properties: "x,y"; duration: 1000 }
3363 }
3364 }
3365 \endcode
3366
3367 Whenever the \l model performs a move operation to move a particular set of indexes, the
3368 respective items in the view will be animated to their new positions in the view over one
3369 second. The transition only applies to the items that are the subject of the move operation
3370 in the model; it does not apply to items below them that are displaced by the move operation.
3371 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
3372
3373 For more details and examples on how to use view transitions, see the ViewTransition
3374 documentation.
3375
3376 \sa moveDisplaced, ViewTransition
3377*/
3378
3379/*!
3380 \qmlproperty Transition QtQuick::ListView::moveDisplaced
3381
3382 This property holds the transition to apply to items that are displaced by a move operation in
3383 the view's \l model.
3384
3385 For example, here is a view that specifies such a transition:
3386
3387 \code
3388 ListView {
3389 ...
3390 moveDisplaced: Transition {
3391 NumberAnimation { properties: "x,y"; duration: 1000 }
3392 }
3393 }
3394 \endcode
3395
3396 Whenever the \l model performs a move operation to move a particular set of indexes, the items
3397 between the source and destination indexes of the move operation are displaced, causing them
3398 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
3399 displacement occurs, the items' movement to their new x,y positions within the view will be
3400 animated by a NumberAnimation over one second, as specified. This transition is not applied to
3401 the items that are the actual subjects of the move operation; to animate the moved items, set
3402 the \l move property.
3403
3404 If an item is displaced by multiple types of operations at the same time, it is not defined as to
3405 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
3406 if it is not necessary to specify different transitions depending on whether an item is displaced
3407 by an add, move or remove operation, consider setting the \l displaced property instead.
3408
3409 For more details and examples on how to use view transitions, see the ViewTransition
3410 documentation.
3411
3412 \sa displaced, move, ViewTransition
3413*/
3414
3415/*!
3416 \qmlproperty Transition QtQuick::ListView::remove
3417
3418 This property holds the transition to apply to items that are removed from the view.
3419
3420 For example, here is a view that specifies such a transition:
3421
3422 \code
3423 ListView {
3424 ...
3425 remove: Transition {
3426 ParallelAnimation {
3427 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
3428 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
3429 }
3430 }
3431 }
3432 \endcode
3433
3434 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
3435 over one second, and in parallel will also change its opacity to 0. The transition
3436 only applies to the items that are removed from the view; it does not apply to the items below
3437 them that are displaced by the removal of the items. To animate the displaced items, set the
3438 \l displaced or \l removeDisplaced properties.
3439
3440 Note that by the time the transition is applied, the item has already been removed from the
3441 model; any references to the model data for the removed index will not be valid.
3442
3443 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
3444 remove transition will not be applied until \l delayRemove becomes false again.
3445
3446 For more details and examples on how to use view transitions, see the ViewTransition
3447 documentation.
3448
3449 \sa removeDisplaced, ViewTransition
3450*/
3451
3452/*!
3453 \qmlproperty Transition QtQuick::ListView::removeDisplaced
3454
3455 This property holds the transition to apply to items in the view that are displaced by the
3456 removal of other items in the view.
3457
3458 For example, here is a view that specifies such a transition:
3459
3460 \code
3461 ListView {
3462 ...
3463 removeDisplaced: Transition {
3464 NumberAnimation { properties: "x,y"; duration: 1000 }
3465 }
3466 }
3467 \endcode
3468
3469 Whenever an item is removed from the above view, all items beneath it are displaced, causing
3470 them to move upwards (or sideways, if horizontally orientated) within the view. As this
3471 displacement occurs, the items' movement to their new x,y positions within the view will be
3472 animated by a NumberAnimation over one second, as specified. This transition is not applied to
3473 the item that has actually been removed from the view; to animate the removed items, set the
3474 \l remove property.
3475
3476 If an item is displaced by multiple types of operations at the same time, it is not defined as to
3477 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
3478 if it is not necessary to specify different transitions depending on whether an item is displaced
3479 by an add, move or remove operation, consider setting the \l displaced property instead.
3480
3481 For more details and examples on how to use view transitions, see the ViewTransition
3482 documentation.
3483
3484 \sa displaced, remove, ViewTransition
3485*/
3486
3487/*!
3488 \qmlproperty Transition QtQuick::ListView::displaced
3489 This property holds the generic transition to apply to items that have been displaced by
3490 any model operation that affects the view.
3491
3492 This is a convenience for specifying the generic transition to be applied to any items
3493 that are displaced by an add, move or remove operation, without having to specify the
3494 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
3495 is a view that specifies a displaced transition:
3496
3497 \code
3498 ListView {
3499 ...
3500 displaced: Transition {
3501 NumberAnimation { properties: "x,y"; duration: 1000 }
3502 }
3503 }
3504 \endcode
3505
3506 When any item is added, moved or removed within the above view, the items below it are
3507 displaced, causing them to move down (or sideways, if horizontally orientated) within the
3508 view. As this displacement occurs, the items' movement to their new x,y positions within
3509 the view will be animated by a NumberAnimation over one second, as specified.
3510
3511 If a view specifies this generic displaced transition as well as a specific addDisplaced,
3512 moveDisplaced or removeDisplaced transition, the more specific transition will be used
3513 instead of the generic displaced transition when the relevant operation occurs, providing that
3514 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
3515 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
3516
3517 For more details and examples on how to use view transitions, see the ViewTransition
3518 documentation.
3519
3520 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
3521*/
3522
3523void QQuickListView::viewportMoved(Qt::Orientations orient)
3524{
3525 Q_D(QQuickListView);
3526 QQuickItemView::viewportMoved(orient);
3527
3528 if (!d->itemCount) {
3529 if (d->hasStickyHeader())
3530 d->updateHeader();
3531 if (d->hasStickyFooter())
3532 d->updateFooter();
3533 return;
3534 }
3535
3536 // Recursion can occur due to refill changing the content size.
3537 if (d->inViewportMoved)
3538 return;
3539 d->inViewportMoved = true;
3540
3541 if (yflick()) {
3542 if (d->isBottomToTop())
3543 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
3544 else
3545 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
3546 } else {
3547 if (d->isRightToLeft())
3548 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
3549 else
3550 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
3551 }
3552
3553 d->refillOrLayout();
3554
3555 // Set visibility of items to eliminate cost of items outside the visible area.
3556 qreal from = d->isContentFlowReversed() ? -d->position()-d->displayMarginBeginning-d->size() : d->position()-d->displayMarginBeginning;
3557 qreal to = d->isContentFlowReversed() ? -d->position()+d->displayMarginEnd : d->position()+d->size()+d->displayMarginEnd;
3558 for (FxViewItem *item : std::as_const(d->visibleItems)) {
3559 if (item->item)
3560 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
3561 }
3562 if (d->currentItem)
3563 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
3564
3565 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
3566 d->moveReason = QQuickListViewPrivate::Mouse;
3567 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
3568 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
3569 // reposition highlight
3570 qreal pos = d->highlight->position();
3571 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
3572 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
3573 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
3574 if (pos < viewPos + d->highlightRangeStart)
3575 pos = viewPos + d->highlightRangeStart;
3576 if (pos != d->highlight->position()) {
3577 d->highlightPosAnimator->stop();
3578 static_cast<FxListItemSG*>(d->highlight.get())->setPosition(pos);
3579 } else {
3580 d->updateHighlight();
3581 }
3582
3583 // update current index
3584 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
3585 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
3586 d->updateCurrent(snapItem->index);
3587 }
3588 }
3589 }
3590
3591 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
3592 d->inFlickCorrection = true;
3593 // Near an end and it seems that the extent has changed?
3594 // Recalculate the flick so that we don't end up in an odd position.
3595 if (yflick() && !d->vData.inOvershoot) {
3596 if (d->vData.velocity > 0) {
3597 const qreal minY = minYExtent();
3598 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
3599 && minY != d->vData.flickTarget)
3600 d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
3601 } else if (d->vData.velocity < 0) {
3602 const qreal maxY = maxYExtent();
3603 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
3604 && maxY != d->vData.flickTarget)
3605 d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
3606 }
3607 }
3608
3609 if (xflick() && !d->hData.inOvershoot) {
3610 if (d->hData.velocity > 0) {
3611 const qreal minX = minXExtent();
3612 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
3613 && minX != d->hData.flickTarget)
3614 d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
3615 } else if (d->hData.velocity < 0) {
3616 const qreal maxX = maxXExtent();
3617 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
3618 && maxX != d->hData.flickTarget)
3619 d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
3620 }
3621 }
3622 d->inFlickCorrection = false;
3623 }
3624 if (d->hasStickyHeader())
3625 d->updateHeader();
3626 if (d->hasStickyFooter())
3627 d->updateFooter();
3628 if (d->sectionCriteria) {
3629 d->updateCurrentSection();
3630 d->updateStickySections();
3631 }
3632 d->inViewportMoved = false;
3633}
3634
3635void QQuickListView::keyPressEvent(QKeyEvent *event)
3636{
3637 Q_D(QQuickListView);
3638 if (d->model && d->model->count() && ((d->interactive && !d->explicitKeyNavigationEnabled)
3639 || (d->explicitKeyNavigationEnabled && d->keyNavigationEnabled))) {
3640 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
3641 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
3642 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
3643 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
3644 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
3645 decrementCurrentIndex();
3646 event->accept();
3647 return;
3648 } else if (d->wrap) {
3649 event->accept();
3650 return;
3651 }
3652 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
3653 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
3654 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
3655 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
3656 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
3657 incrementCurrentIndex();
3658 event->accept();
3659 return;
3660 } else if (d->wrap) {
3661 event->accept();
3662 return;
3663 }
3664 }
3665 }
3666 event->ignore();
3667 QQuickItemView::keyPressEvent(event);
3668}
3669
3670void QQuickListView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
3671{
3672 Q_D(QQuickListView);
3673
3674 if (d->model) {
3675 // When the view changes size, we force the pool to
3676 // shrink by releasing all pooled items.
3677 d->model->drainReusableItemsPool(0);
3678 }
3679
3680 if (d->isRightToLeft()) {
3681 // maintain position relative to the right edge
3682 qreal dx = newGeometry.width() - oldGeometry.width();
3683 setContentX(contentX() - dx);
3684 } else if (d->isBottomToTop()) {
3685 // maintain position relative to the bottom edge
3686 qreal dy = newGeometry.height() - oldGeometry.height();
3687 setContentY(contentY() - dy);
3688 }
3689
3690 // When view-relative delegates resize, the content position becomes
3691 // stale and fixup() snaps to the wrong item. Record the current snap
3692 // target here; fixup() will use it instead of snapItemAt().
3693 // StrictlyEnforceRange is excluded — its fixup() path already forces
3694 // currentItem as the snap target.
3695 const bool vertical = (d->orient == QQuickListView::Vertical);
3696 const qreal oldSize = vertical ? oldGeometry.height() : oldGeometry.width();
3697 const qreal newSize = vertical ? newGeometry.height() : newGeometry.width();
3698
3699 if (d->snapMode == QQuickListView::SnapOneItem
3700 && !(d->haveHighlightRange && d->highlightRange == QQuickListView::StrictlyEnforceRange)
3701 && !d->visibleItems.isEmpty() && !qFuzzyCompare(oldSize, newSize) && oldSize > 0) {
3702 qreal viewPos = d->isContentFlowReversed() ? -d->position() - d->size() : d->position();
3703 if (FxViewItem *snapped = d->snapItemAt(viewPos)) {
3704 if (snapped->index >= 0)
3705 d->snapResizeTargetIndex = snapped->index;
3706 }
3707 }
3708
3709 QQuickItemView::geometryChange(newGeometry, oldGeometry);
3710}
3711
3712void QQuickListView::initItem(int index, QObject *object)
3713{
3714 QQuickItemView::initItem(index, object);
3715
3716 // setting the view from the FxViewItem wrapper is too late if the delegate
3717 // needs access to the view in Component.onCompleted
3718 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
3719 if (item) {
3720 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
3721 qmlAttachedPropertiesObject<QQuickListView>(item));
3722 if (attached)
3723 attached->setView(this);
3724 }
3725}
3726
3727qreal QQuickListView::maxYExtent() const
3728{
3729 Q_D(const QQuickListView);
3730 if (d->layoutOrientation() == Qt::Horizontal && d->flickableDirection != HorizontalFlick)
3731 return QQuickFlickable::maxYExtent();
3732 return QQuickItemView::maxYExtent();
3733}
3734
3735qreal QQuickListView::maxXExtent() const
3736{
3737 Q_D(const QQuickListView);
3738 if (d->layoutOrientation() == Qt::Vertical && d->flickableDirection != VerticalFlick)
3739 return QQuickFlickable::maxXExtent();
3740 return QQuickItemView::maxXExtent();
3741}
3742
3743/*!
3744 \qmlmethod void QtQuick::ListView::incrementCurrentIndex()
3745
3746 Increments the current index. The current index will wrap
3747 if keyNavigationWraps is true and it is currently at the end.
3748 This method has no effect if the \l count is zero.
3749
3750 \b Note: methods should only be called after the Component has completed.
3751*/
3752void QQuickListView::incrementCurrentIndex()
3753{
3754 Q_D(QQuickListView);
3755 int count = d->model ? d->model->count() : 0;
3756 if (count && (currentIndex() < count - 1 || d->wrap)) {
3757 d->moveReason = QQuickListViewPrivate::SetIndex;
3758 int index = currentIndex()+1;
3759 setCurrentIndex((index >= 0 && index < count) ? index : 0);
3760 }
3761}
3762
3763/*!
3764 \qmlmethod void QtQuick::ListView::decrementCurrentIndex()
3765
3766 Decrements the current index. The current index will wrap
3767 if keyNavigationWraps is true and it is currently at the beginning.
3768 This method has no effect if the \l count is zero.
3769
3770 \b Note: methods should only be called after the Component has completed.
3771*/
3772void QQuickListView::decrementCurrentIndex()
3773{
3774 Q_D(QQuickListView);
3775 int count = d->model ? d->model->count() : 0;
3776 if (count && (currentIndex() > 0 || d->wrap)) {
3777 d->moveReason = QQuickListViewPrivate::SetIndex;
3778 int index = currentIndex()-1;
3779 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
3780 }
3781}
3782
3784{
3785 Q_Q(QQuickListView);
3786 if (q->isComponentComplete() && model) {
3787 QList<QByteArray> roles;
3788 if (sectionCriteria && !sectionCriteria->property().isEmpty())
3789 roles << sectionCriteria->property().toUtf8();
3790 model->setWatchedRoles(roles);
3792 if (itemCount)
3793 forceLayoutPolish();
3794 }
3795}
3796
3797bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
3798{
3799 Q_Q(QQuickListView);
3800#if !QT_CONFIG(quick_viewtransitions)
3801 Q_UNUSED(movingIntoView)
3802#endif
3803 int modelIndex = change.index;
3804 int count = change.count;
3805
3806 if (q->size().isNull() && visibleItems.isEmpty())
3807 return false;
3808
3809 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
3810 int index = visibleItems.size() ? mapFromModel(modelIndex) : 0;
3811 qreal lastVisiblePos = buffer + displayMarginEnd + tempPos + size();
3812
3813 if (index < 0) {
3814 int i = visibleItems.size() - 1;
3815 while (i > 0 && visibleItems.at(i)->index == -1)
3816 --i;
3817 if (i == 0 && visibleItems.constFirst()->index == -1) {
3818 // there are no visible items except items marked for removal
3819 index = visibleItems.size();
3820 } else if (visibleItems.at(i)->index + 1 == modelIndex
3821 && visibleItems.at(i)->endPosition() <= lastVisiblePos) {
3822 // Special case of appending an item to the model.
3823 index = visibleItems.size();
3824 } else {
3825 if (modelIndex < visibleIndex) {
3826 // Insert before visible items
3827 visibleIndex += count;
3828 for (FxViewItem *item : std::as_const(visibleItems)) {
3829 if (item->index != -1 && item->index >= modelIndex)
3830 item->index += count;
3831 }
3832 }
3833 return true;
3834 }
3835 }
3836
3837 // index can be the next item past the end of the visible items list (i.e. appended)
3838 qreal pos = 0;
3839 if (visibleItems.size()) {
3840 pos = index < visibleItems.size() ? visibleItems.at(index)->position()
3841 : visibleItems.constLast()->endPosition() + spacing;
3842 }
3843
3844 // Update the indexes of the following visible items.
3845 for (FxViewItem *item : std::as_const(visibleItems)) {
3846 if (item->index != -1 && item->index >= modelIndex) {
3847 item->index += count;
3848#if QT_CONFIG(quick_viewtransitions)
3849 if (change.isMove())
3850 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
3851 else
3852 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
3853#endif
3854 }
3855 }
3856
3857 bool visibleAffected = false;
3858 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
3859 // Insert items before the visible item.
3860 int insertionIdx = index;
3861 qreal from = tempPos - displayMarginBeginning - buffer;
3862
3863 if (insertionIdx < visibleIndex) {
3864 if (pos >= from) {
3865 // items won't be visible, just note the size for repositioning
3866 insertResult->sizeChangesBeforeVisiblePos += count * (averageSize + spacing);
3867 }
3868 } else {
3869 MutableModelIterator it(model, modelIndex + count - 1, modelIndex -1);
3870 for (; it.hasNext() && pos >= from; it.next()) {
3871 // item is before first visible e.g. in cache buffer
3872 FxViewItem *item = nullptr;
3873 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
3874 item->index = it.index;
3875 if (!item)
3876 item = createItem(it.index, QQmlIncubator::Synchronous);
3877 if (!item)
3878 return false;
3879 if (it.removedAtIndex)
3880 continue;
3881
3882 visibleAffected = true;
3883 visibleItems.insert(insertionIdx, item);
3884 if (insertionIdx == 0)
3885 insertResult->changedFirstItem = true;
3886 if (!change.isMove()) {
3887 addedItems->append(item);
3888#if QT_CONFIG(quick_viewtransitions)
3889 if (transitioner)
3890 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
3891 else
3892#endif
3893 static_cast<FxListItemSG *>(item)->setPosition(pos, true);
3894 }
3895 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
3896 pos -= item->size() + spacing;
3897 index++;
3898 }
3899 }
3900
3901 int firstOkIdx = -1;
3902 for (int i = 0; i <= insertionIdx && i < visibleItems.size() - 1; i++) {
3903 if (visibleItems.at(i)->index + 1 != visibleItems.at(i + 1)->index) {
3904 firstOkIdx = i + 1;
3905 break;
3906 }
3907 }
3908 for (int i = 0; i < firstOkIdx; i++) {
3909 FxViewItem *nvItem = visibleItems.takeFirst();
3910 addedItems->removeOne(nvItem);
3911 removeItem(nvItem);
3912 }
3913
3914 } else {
3915 MutableModelIterator it(model, modelIndex, modelIndex + count);
3916 for (; it.hasNext() && pos <= lastVisiblePos; it.next()) {
3917 visibleAffected = true;
3918 FxViewItem *item = nullptr;
3919 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
3920 item->index = it.index;
3921#if QT_CONFIG(quick_viewtransitions)
3922 bool newItem = !item;
3923#endif
3924 it.removedAtIndex = false;
3925 if (!item)
3926 item = createItem(it.index, QQmlIncubator::Synchronous);
3927 if (!item)
3928 return false;
3929 if (it.removedAtIndex) {
3930 releaseItem(item, reusableFlag);
3931 continue;
3932 }
3933
3934 if (index < visibleItems.size())
3935 visibleItems.insert(index, item);
3936 else // special case of appending an item to the model - as above
3937 visibleItems.append(item);
3938 if (index == 0)
3939 insertResult->changedFirstItem = true;
3940 if (change.isMove()) {
3941 // we know this is a move target, since move displaced items that are
3942 // shuffled into view due to a move would be added in refill()
3943#if QT_CONFIG(quick_viewtransitions)
3944 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
3945 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
3946#endif
3947 } else {
3948 addedItems->append(item);
3949#if QT_CONFIG(quick_viewtransitions)
3950 if (transitioner)
3951 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
3952 else
3953#endif
3954 static_cast<FxListItemSG *>(item)->setPosition(pos, true);
3955 }
3956 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
3957 pos += item->size() + spacing;
3958 ++index;
3959 }
3960 it.disconnect();
3961
3962 if (0 < index && index < visibleItems.size()) {
3963 FxViewItem *prevItem = visibleItems.at(index - 1);
3964 FxViewItem *item = visibleItems.at(index);
3965 if (prevItem->index != item->index - 1) {
3966 int i = index;
3967#if QT_CONFIG(quick_viewtransitions)
3968 qreal prevPos = prevItem->position();
3969#endif
3970 while (i < visibleItems.size()) {
3971 FxListItemSG *nvItem = static_cast<FxListItemSG *>(visibleItems.takeLast());
3972 insertResult->sizeChangesAfterVisiblePos -= nvItem->size() + spacing;
3973 addedItems->removeOne(nvItem);
3974#if QT_CONFIG(quick_viewtransitions)
3975 if (nvItem->transitionScheduledOrRunning())
3976 nvItem->setPosition(prevPos + (nvItem->index - prevItem->index) * averageSize);
3977#endif
3978 removeItem(nvItem);
3979 }
3980 }
3981 }
3982 }
3983
3984 updateVisibleIndex();
3985
3986 return visibleAffected;
3987}
3988
3989#if QT_CONFIG(quick_viewtransitions)
3990void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
3991{
3992 Q_UNUSED(insertionResult);
3993
3994 if (!transitioner)
3995 return;
3996
3997 int markerItemIndex = -1;
3998 for (int i=0; i<visibleItems.size(); i++) {
3999 if (visibleItems.at(i)->index == afterModelIndex) {
4000 markerItemIndex = i;
4001 break;
4002 }
4003 }
4004 if (markerItemIndex < 0)
4005 return;
4006
4007 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
4008 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
4009 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
4010
4011 for (int i=markerItemIndex+1; i<visibleItems.size(); i++) {
4012 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
4013 if (listItem->position() >= viewEndPos)
4014 break;
4015 if (!listItem->transitionScheduledOrRunning()) {
4016 qreal pos = listItem->position();
4017 listItem->setPosition(pos - sizeRemoved);
4018 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
4019 listItem->setPosition(pos);
4020 }
4021 }
4022}
4023#endif
4024
4025/*!
4026 \qmlmethod void QtQuick::ListView::positionViewAtIndex(int index, PositionMode mode)
4027
4028 Positions the view such that the \a index is at the position specified by \a mode:
4029
4030 \value ListView.Beginning position item at the top (or left for horizontal orientation) of the view.
4031 \value ListView.Center position item in the center of the view.
4032 \value ListView.End position item at bottom (or right for horizontal orientation) of the view.
4033 \value ListView.Visible if any part of the item is visible then take no action, otherwise
4034 bring the item into view.
4035 \value ListView.Contain ensure the entire item is visible. If the item is larger than the view,
4036 the item is positioned at the top (or left for horizontal orientation) of the view.
4037 \value ListView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid
4038 if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled via \l snapMode.
4039
4040 If positioning the view at \a index would cause empty space to be displayed at
4041 the beginning or end of the view, the view will be positioned at the boundary.
4042
4043 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
4044 at a particular index. This is unreliable since removing items from the start
4045 of the list does not cause all other items to be repositioned, and because
4046 the actual start of the view can vary based on the size of the delegates.
4047 The correct way to bring an item into view is with \c positionViewAtIndex.
4048
4049 \b Note: methods should only be called after the Component has completed. To position
4050 the view at startup, this method should be called by Component.onCompleted. For
4051 example, to position the view at the end:
4052
4053 \code
4054 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
4055 \endcode
4056*/
4057
4058/*!
4059 \qmlmethod void QtQuick::ListView::positionViewAtBeginning()
4060 \qmlmethod void QtQuick::ListView::positionViewAtEnd()
4061
4062 Positions the view at the beginning or end, taking into account any header or footer.
4063
4064 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
4065 at a particular index. This is unreliable since removing items from the start
4066 of the list does not cause all other items to be repositioned, and because
4067 the actual start of the view can vary based on the size of the delegates.
4068
4069 \b Note: methods should only be called after the Component has completed. To position
4070 the view at startup, this method should be called by Component.onCompleted. For
4071 example, to position the view at the end on startup:
4072
4073 \code
4074 Component.onCompleted: positionViewAtEnd()
4075 \endcode
4076*/
4077
4078/*!
4079 \qmlmethod int QtQuick::ListView::indexAt(real x, real y)
4080
4081 Returns the index of the visible item containing the point \a x, \a y in content
4082 coordinates. If there is no item at the point specified, or the item is
4083 not visible -1 is returned.
4084
4085 If the item is outside the visible area, -1 is returned, regardless of
4086 whether an item will exist at that point when scrolled into view.
4087
4088 \b Note: methods should only be called after the Component has completed.
4089*/
4090
4091/*!
4092 \qmlmethod Item QtQuick::ListView::itemAt(real x, real y)
4093
4094 Returns the visible item containing the point \a x, \a y in content
4095 coordinates. If there is no item at the point specified, or the item is
4096 not visible null is returned.
4097
4098 If the item is outside the visible area, null is returned, regardless of
4099 whether an item will exist at that point when scrolled into view.
4100
4101 \b Note: methods should only be called after the Component has completed.
4102*/
4103
4104/*!
4105 \qmlmethod Item QtQuick::ListView::itemAtIndex(int index)
4106
4107 Returns the item for \a index. If there is no item for that index, for example
4108 because it has not been created yet, or because it has been panned out of
4109 the visible area and removed from the cache, null is returned.
4110
4111 \b Note: this method should only be called after the Component has completed.
4112 The returned value should also not be stored since it can turn to null
4113 as soon as control goes out of the calling scope, if the view releases that item.
4114
4115 \since 5.13
4116*/
4117
4118/*!
4119 \qmlmethod void QtQuick::ListView::forceLayout()
4120
4121 Responding to changes in the model is usually batched to happen only once
4122 per frame. This means that inside script blocks it is possible for the
4123 underlying model to have changed, but the ListView has not caught up yet.
4124
4125 This method forces the ListView to immediately respond to any outstanding
4126 changes in the model.
4127
4128 \since 5.1
4129
4130 \b Note: methods should only be called after the Component has completed.
4131*/
4132
4133QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
4134{
4135 return new QQuickListViewAttached(obj);
4136}
4137
4138/*! \internal
4139 Prevents clicking or dragging through floating headers (QTBUG-74046).
4140*/
4141bool QQuickListViewPrivate::wantsPointerEvent(const QPointerEvent *event)
4142{
4143 Q_Q(const QQuickListView);
4144 bool ret = true;
4145
4146 QPointF pos = event->points().first().position();
4147 if (!pos.isNull()) {
4148 if (auto header = q->headerItem()) {
4149 if (q->headerPositioning() != QQuickListView::InlineHeader &&
4150 header->contains(q->mapToItem(header, pos)))
4151 ret = false;
4152 }
4153 if (auto footer = q->footerItem()) {
4154 if (q->footerPositioning() != QQuickListView::InlineFooter &&
4155 footer->contains(q->mapToItem(footer, pos)))
4156 ret = false;
4157 }
4158 }
4159
4160 switch (event->type()) {
4161 case QEvent::MouseButtonPress:
4162 wantedMousePress = ret;
4163 break;
4164 case QEvent::MouseMove:
4165 ret = wantedMousePress;
4166 break;
4167 default:
4168 break;
4169 }
4170
4171 qCDebug(lcEvents) << q << (ret ? "WANTS" : "DOESN'T want") << event;
4172 return ret;
4173}
4174
4175QT_END_NAMESPACE
4176
4177#include "moc_qquicklistview_p.cpp"
bool contains(qreal x, qreal y) const override
FxListItemSG(QQuickItem *i, QQuickListView *v, bool own)
qreal itemPosition() const
QQuickItem * section() const
qreal position() const override
qreal endPosition() const override
void setSize(qreal size)
qreal size() const override
qreal sectionSize() const override
void setSection(QQuickItem *s)
void setPosition(qreal pos, bool immediate=false, bool resetInactiveAxis=true)
qreal itemSize() const
QQuickListView * view
MutableModelIterator(QQmlInstanceModel *model, int iBegin, int iEnd)
void releaseSectionItem(QQuickItem *item)
QQuickItem * sectionCache[sectionCacheSize]
void createHighlight(bool onDestruction=false) override
void visibleItemsChanged() override
bool hasStickyHeader() const override
std::unique_ptr< QSmoothedAnimation > highlightWidthAnimator
static void setSectionHelper(QQmlContext *context, QQuickItem *sectionItem, const QString &section)
FxViewItem * snapItemAt(qreal pos)
QQuickListView::Orientation orient
void updateInlineSection(FxListItemSG *)
void updateSections() override
qreal originPosition() const override
QQuickListView::FooterPositioning footerPositioning
void fixupPosition() override
QString sectionAt(int modelIndex)
void updateSectionCriteria() override
qreal snapPosAt(qreal pos)
void initializeComponentItem(QQuickItem *item) const override
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeometry) override
void updateHeader() override
void layoutVisibleItems(int fromModelIndex=0) override
QQuickItem * currentSectionItem
qreal endPositionAt(int index) const override
void updateHighlight() override
void setPosition(qreal pos) override
qreal lastPosition() const override
std::unique_ptr< QSmoothedAnimation > highlightHeightAnimator
bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) override
void adjustFirstItem(qreal forwards, qreal backwards, int) override
void repositionPackageItemAt(QQuickItem *item, int index) override
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override
bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList< FxViewItem * > *addedItems, QList< MovedItem > *movingIntoView) override
void changedVisibleIndex(int newIndex) override
bool releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag) override
QQuickViewSection * sectionCriteria
bool showFooterForIndex(int index) const override
FxViewItem * newViewItem(int index, QQuickItem *item) override
void resetHighlightPosition() override
qreal footerSize() const override
bool wantsPointerEvent(const QPointerEvent *event) override
void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) override
bool showHeaderForIndex(int index) const override
void initializeCurrentItem() override
QQuickListView::HeaderPositioning headerPositioning
void updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult) override
void initializeViewItem(FxViewItem *item) override
bool hasStickyFooter() const override
qreal positionAt(int index) const override
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override
void updateFooter() override
FxViewItem * itemBefore(int modelIndex) const
static const int sectionCacheSize
QQuickItemViewAttached * getAttachedObject(const QObject *object) const override
bool movingFromHighlight() override
void resetFirstItemPosition(qreal pos=0.0) override
bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) override
std::unique_ptr< QSmoothedAnimation > highlightPosAnimator
QQuickItem * getSectionItem(const QString &section)
void removeItem(FxViewItem *item)
bool isContentFlowReversed() const override
QQuickListView::SnapMode snapMode
qreal headerSize() const override
void clear(bool onDestruction) override
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
#define QML_FLICK_SNAPONETHRESHOLD