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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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