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