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

source code of qtdeclarative/src/quick/items/qquicklistview.cpp