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,
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
2486/*!
2487 \qmlproperty Item QtQuick::ListView::highlightItem
2488
2489 This holds the highlight item created from the \l highlight component.
2490
2491 The \c highlightItem is managed by the view unless
2492 \l highlightFollowsCurrentItem is set to false.
2493 The default \l {QQuickItem::z}{stacking order}
2494 of the highlight item is \c 0.
2495
2496 \sa highlight, highlightFollowsCurrentItem, {Stacking Order in ListView}
2497*/
2498
2499/*!
2500 \qmlproperty int QtQuick::ListView::count
2501 The property reflects the number of items in the \l ListView's model,
2502 regardless of whether they are visible or instantiated as \c Item of a delegate component.
2503*/
2504
2505/*!
2506 \qmlproperty bool QtQuick::ListView::reuseItems
2507
2508 This property enables you to reuse items that are instantiated
2509 from the \l delegate. If set to \c false, any currently
2510 pooled items are destroyed.
2511
2512 This property is \c false by default.
2513
2514 \since 5.15
2515
2516 \sa {Reusing items}, pooled(), reused()
2517*/
2518
2519/*!
2520 \qmlattachedsignal QtQuick::ListView::pooled()
2521
2522 This signal is emitted after an item has been added to the reuse
2523 pool. You can use it to pause ongoing timers or animations inside
2524 the item, or free up resources that cannot be reused.
2525
2526 This signal is emitted only if the \l reuseItems property is \c true.
2527
2528 \sa {Reusing items}, reuseItems, reused()
2529*/
2530
2531/*!
2532 \qmlattachedsignal QtQuick::ListView::reused()
2533
2534 This signal is emitted after an item has been reused. At this point, the
2535 item has been taken out of the pool and placed inside the content view,
2536 and the model properties such as \c index and \c row have been updated.
2537
2538 Other properties that are not provided by the model does not change when an
2539 item is reused. You should avoid storing any state inside a delegate, but if
2540 you do, manually reset that state on receiving this signal.
2541
2542 This signal is emitted when the item is reused, and not the first time the
2543 item is created.
2544
2545 This signal is emitted only if the \l reuseItems property is \c true.
2546
2547 \sa {Reusing items}, reuseItems, pooled()
2548*/
2549
2550/*!
2551 \qmlproperty Component QtQuick::ListView::highlight
2552 This property holds the component to use as the highlight.
2553
2554 An instance of the highlight component is created for each list.
2555 The geometry of the resulting component instance is managed by the list
2556 so as to stay with the current item, unless the highlightFollowsCurrentItem
2557 property is false. The default \l {QQuickItem::z}{stacking order} of the
2558 highlight item is \c 0.
2559
2560 \sa highlightItem, highlightFollowsCurrentItem,
2561 {Qt Quick Examples - Views#Using Highlight}{ListView Highlight Example},
2562 {Stacking Order in ListView}
2563*/
2564
2565/*!
2566 \qmlproperty bool QtQuick::ListView::highlightFollowsCurrentItem
2567 This property holds whether the highlight is managed by the view.
2568
2569 If this property is true (the default value), the highlight is moved smoothly
2570 to follow the current item. Otherwise, the
2571 highlight is not moved by the view, and any movement must be implemented
2572 by the highlight.
2573
2574 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
2575
2576 \snippet qml/listview/listview.qml highlightFollowsCurrentItem
2577
2578 Note that the highlight animation also affects the way that the view
2579 is scrolled. This is because the view moves to maintain the
2580 highlight within the preferred highlight range (or visible viewport).
2581
2582 \sa highlight, highlightMoveVelocity
2583*/
2584//###Possibly rename these properties, since they are very useful even without a highlight?
2585/*!
2586 \qmlproperty real QtQuick::ListView::preferredHighlightBegin
2587 \qmlproperty real QtQuick::ListView::preferredHighlightEnd
2588 \qmlproperty enumeration QtQuick::ListView::highlightRangeMode
2589
2590 These properties define the preferred range of the highlight (for the current item)
2591 within the view. The \c preferredHighlightBegin value must be less than the
2592 \c preferredHighlightEnd value.
2593
2594 These properties affect the position of the current item when the list is scrolled.
2595 For example, if the currently selected item should stay in the middle of the
2596 list when the view is scrolled, set the \c preferredHighlightBegin and
2597 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
2598 item would be. If the \c currentItem is changed programmatically, the list will
2599 automatically scroll so that the current item is in the middle of the view.
2600 Furthermore, the behavior of the current item index will occur whether or not a
2601 highlight exists.
2602
2603 Valid values for \c highlightRangeMode are:
2604
2605 \value ListView.ApplyRange the view attempts to maintain the highlight within the range.
2606 However, the highlight can move outside of the range at the
2607 ends of the list or due to mouse interaction.
2608 \value ListView.StrictlyEnforceRange the highlight never moves outside of the range.
2609 The current item changes if a keyboard or mouse action would
2610 cause the highlight to move outside of the range.
2611 \value ListView.NoHighlightRange this is the default value.
2612*/
2613void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
2614{
2615 Q_D(QQuickListView);
2616 if (d->autoHighlight != autoHighlight) {
2617 if (!autoHighlight) {
2618 if (d->highlightPosAnimator)
2619 d->highlightPosAnimator->stop();
2620 if (d->highlightWidthAnimator)
2621 d->highlightWidthAnimator->stop();
2622 if (d->highlightHeightAnimator)
2623 d->highlightHeightAnimator->stop();
2624 }
2625 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
2626 }
2627}
2628
2629/*!
2630 \qmlproperty real QtQuick::ListView::spacing
2631
2632 This property holds the spacing between items.
2633
2634 The default value is 0.
2635*/
2636qreal QQuickListView::spacing() const
2637{
2638 Q_D(const QQuickListView);
2639 return d->spacing;
2640}
2641
2642void QQuickListView::setSpacing(qreal spacing)
2643{
2644 Q_D(QQuickListView);
2645 if (spacing != d->spacing) {
2646 d->spacing = spacing;
2647 d->forceLayoutPolish();
2648 emit spacingChanged();
2649 }
2650}
2651
2652/*!
2653 \qmlproperty enumeration QtQuick::ListView::orientation
2654 This property holds the orientation of the list.
2655
2656 Possible values:
2657
2658 \value ListView.Horizontal Items are laid out horizontally
2659 \br
2660 \inlineimage ListViewHorizontal.png
2661 \value ListView.Vertical (default) Items are laid out vertically
2662 \br
2663 \inlineimage listview-highlight.png
2664
2665 \sa {Flickable Direction}
2666*/
2667QQuickListView::Orientation QQuickListView::orientation() const
2668{
2669 Q_D(const QQuickListView);
2670 return d->orient;
2671}
2672
2673void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
2674{
2675 Q_D(QQuickListView);
2676 if (d->orient != orientation) {
2677 d->orient = orientation;
2678 if (d->orient == Vertical) {
2679 if (d->flickableDirection == HorizontalFlick) {
2680 setFlickableDirection(VerticalFlick);
2681 if (isComponentComplete())
2682 setContentWidth(-1);
2683 }
2684 setContentX(0);
2685 } else {
2686 if (d->flickableDirection == VerticalFlick) {
2687 setFlickableDirection(HorizontalFlick);
2688 if (isComponentComplete())
2689 setContentHeight(-1);
2690 }
2691 setContentY(0);
2692 }
2693 d->regenerate(true);
2694 emit orientationChanged();
2695 }
2696}
2697
2698/*!
2699 \qmlproperty enumeration QtQuick::ListView::layoutDirection
2700 This property holds the layout direction of a horizontally-oriented list.
2701
2702 Possible values:
2703
2704 \value Qt.LeftToRight (default) Items will be laid out from left to right.
2705 \value Qt.RightToLeft Items will be laid out from right to left.
2706
2707 Setting this property has no effect if the \l orientation is Qt.Vertical.
2708
2709 \sa ListView::effectiveLayoutDirection, ListView::verticalLayoutDirection
2710*/
2711
2712
2713/*!
2714 \qmlproperty enumeration QtQuick::ListView::effectiveLayoutDirection
2715 This property holds the effective layout direction of a horizontally-oriented list.
2716
2717 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2718 the visual layout direction of the horizontal list will be mirrored. However, the
2719 property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2720
2721 \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2722*/
2723
2724
2725/*!
2726 \qmlproperty enumeration QtQuick::ListView::verticalLayoutDirection
2727 This property holds the layout direction of a vertically-oriented list.
2728
2729 Possible values:
2730
2731 \value ListView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
2732 \value ListView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
2733
2734 Setting this property has no effect if the \l orientation is Qt.Horizontal.
2735
2736 \sa ListView::layoutDirection
2737*/
2738
2739
2740/*!
2741 \qmlproperty bool QtQuick::ListView::keyNavigationWraps
2742 This property holds whether the list wraps key navigation.
2743
2744 If this is true, key navigation that would move the current item selection
2745 past the end of the list instead wraps around and moves the selection to
2746 the start of the list, and vice-versa.
2747
2748 By default, key navigation is not wrapped.
2749*/
2750
2751/*!
2752 \qmlproperty bool QtQuick::ListView::keyNavigationEnabled
2753 \since 5.7
2754
2755 This property holds whether the key navigation of the list is enabled.
2756
2757 If this is \c true, the user can navigate the view with a keyboard.
2758 It is useful for applications that need to selectively enable or
2759 disable mouse and keyboard interaction.
2760
2761 By default, the value of this property is bound to
2762 \l {Flickable::}{interactive} to ensure behavior compatibility for
2763 existing applications. When explicitly set, it will cease to be bound to
2764 the interactive property.
2765
2766 \sa {Flickable::}{interactive}
2767*/
2768
2769
2770/*!
2771 \qmlproperty int QtQuick::ListView::cacheBuffer
2772 This property determines whether delegates are retained outside the
2773 visible area of the view.
2774
2775 If this value is greater than zero, the view may keep as many delegates
2776 instantiated as it can fit within the buffer specified. For example,
2777 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2778 set to 40, then up to 2 delegates above and 2 delegates below the visible
2779 area may be created/retained. The buffered delegates are created asynchronously,
2780 allowing creation to occur across multiple frames and reducing the
2781 likelihood of skipping frames. In order to improve painting performance
2782 delegates outside the visible area are not painted.
2783
2784 The default value of this property is platform dependent, but will usually
2785 be a value greater than zero. Negative values are ignored.
2786
2787 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2788 instantiated delegates.
2789
2790 \note Setting this property is not a replacement for creating efficient delegates.
2791 It can improve the smoothness of scrolling behavior at the expense of additional
2792 memory usage. The fewer objects and bindings in a delegate, the faster a
2793 view can be scrolled. It is important to realize that setting a cacheBuffer
2794 will only postpone issues caused by slow-loading delegates, it is not a
2795 solution for this scenario.
2796
2797 The cacheBuffer operates outside of any display margins specified by
2798 displayMarginBeginning or displayMarginEnd.
2799*/
2800
2801/*!
2802 \qmlproperty int QtQuick::ListView::displayMarginBeginning
2803 \qmlproperty int QtQuick::ListView::displayMarginEnd
2804 \since QtQuick 2.3
2805
2806 This property allows delegates to be displayed outside of the view geometry.
2807
2808 If this value is non-zero, the view will create extra delegates before the
2809 start of the view, or after the end. The view will create as many delegates
2810 as it can fit into the pixel size specified.
2811
2812 For example, if in a vertical view the delegate is 20 pixels high and
2813 \c displayMarginBeginning and \c displayMarginEnd are both set to 40,
2814 then 2 delegates above and 2 delegates below will be created and shown.
2815
2816 The default value is 0.
2817
2818 This property is meant for allowing certain UI configurations,
2819 and not as a performance optimization. If you wish to create delegates
2820 outside of the view geometry for performance reasons, you probably
2821 want to use the cacheBuffer property instead.
2822*/
2823
2824/*!
2825 \qmlpropertygroup QtQuick::ListView::section
2826 \qmlproperty string QtQuick::ListView::section.property
2827 \qmlproperty enumeration QtQuick::ListView::section.criteria
2828 \qmlproperty Component QtQuick::ListView::section.delegate
2829 \qmlproperty enumeration QtQuick::ListView::section.labelPositioning
2830
2831 These properties determine the expression to be evaluated and appearance
2832 of the section labels.
2833
2834 \c section.property holds the name of the property that is the basis
2835 of each section.
2836
2837 \c section.criteria holds the criteria for forming each section based on
2838 \c section.property. This value can be one of:
2839
2840 \value ViewSection.FullString (default) sections are created based on the
2841 \c section.property value.
2842 \value ViewSection.FirstCharacter sections are created based on the first character of
2843 the \c section.property value (for example,
2844 'A', 'B', 'C' ... sections for an address book.)
2845
2846 A case insensitive comparison is used when determining section
2847 boundaries.
2848
2849 \c section.delegate holds the delegate component for each section. The
2850 default \l {QQuickItem::z}{stacking order} of section delegate instances
2851 is \c 2. If you declare a \c required property named "section" in it,
2852 that property will contain the section's title.
2853
2854 \c section.labelPositioning determines whether the current and/or
2855 next section labels stick to the start/end of the view, and whether
2856 the labels are shown inline. This value can be a combination of:
2857
2858 \value ViewSection.InlineLabels
2859 (default) section labels are shown inline between the item delegates
2860 separating sections.
2861 \value ViewSection.CurrentLabelAtStart
2862 the current section label sticks to the start of the view as it is moved.
2863 \value ViewSection.NextLabelAtEnd
2864 the next section label (beyond all visible sections) sticks to the end
2865 of the view as it is moved.
2866 \note Enabling \c ViewSection.NextLabelAtEnd requires the view to scan
2867 ahead for the next section, which has performance implications,
2868 especially for slower models.
2869
2870 Each item in the list has attached properties named \c ListView.section,
2871 \c ListView.previousSection and \c ListView.nextSection.
2872
2873 For example, here is a ListView that displays a list of animals, separated
2874 into sections. Each item in the ListView is placed in a different section
2875 depending on the "size" property of the model item. The \c sectionHeading
2876 delegate component provides the light blue bar that marks the beginning of
2877 each section.
2878
2879
2880 \snippet views/listview/sections.qml 0
2881
2882 \image qml-listview-sections-example.png
2883
2884 \note Adding sections to a ListView does not automatically re-order the
2885 list items by the section criteria.
2886 If the model is not ordered by section, then it is possible that
2887 the sections created will not be unique; each boundary between
2888 differing sections will result in a section header being created
2889 even if that section exists elsewhere.
2890
2891 \sa {Qt Quick Examples - Views}{ListView examples},
2892 {Stacking Order in ListView}
2893*/
2894QQuickViewSection *QQuickListView::sectionCriteria()
2895{
2896 Q_D(QQuickListView);
2897 if (!d->sectionCriteria)
2898 d->sectionCriteria = new QQuickViewSection(this);
2899 return d->sectionCriteria;
2900}
2901
2902/*!
2903 \qmlproperty string QtQuick::ListView::currentSection
2904 This property holds the section that is currently at the beginning of the view.
2905*/
2906QString QQuickListView::currentSection() const
2907{
2908 Q_D(const QQuickListView);
2909 return d->currentSection;
2910}
2911
2912/*!
2913 \qmlproperty real QtQuick::ListView::highlightMoveVelocity
2914 \qmlproperty int QtQuick::ListView::highlightMoveDuration
2915 \qmlproperty real QtQuick::ListView::highlightResizeVelocity
2916 \qmlproperty int QtQuick::ListView::highlightResizeDuration
2917
2918 These properties control the speed of the move and resize animations for the
2919 highlight delegate.
2920
2921 \l highlightFollowsCurrentItem must be true for these properties
2922 to have effect.
2923
2924 The default value for the velocity properties is 400 pixels/second.
2925 The default value for the duration properties is -1, i.e. the
2926 highlight will take as much time as necessary to move at the set speed.
2927
2928 These properties have the same characteristics as a SmoothedAnimation:
2929 if both the velocity and duration are set, the animation will use
2930 whichever gives the shorter duration.
2931
2932 The move velocity and duration properties are used to control movement due
2933 to index changes; for example, when incrementCurrentIndex() is called. When
2934 the user flicks a ListView, the velocity from the flick is used to control
2935 the movement instead.
2936
2937 To set only one property, the other can be set to \c -1. For example,
2938 if you only want to animate the duration and not velocity, use the
2939 following code:
2940
2941 \code
2942 highlightMoveDuration: 1000
2943 highlightMoveVelocity: -1
2944 \endcode
2945
2946 \sa highlightFollowsCurrentItem
2947*/
2948qreal QQuickListView::highlightMoveVelocity() const
2949{
2950 Q_D(const QQuickListView);
2951 return d->highlightMoveVelocity;
2952}
2953
2954void QQuickListView::setHighlightMoveVelocity(qreal speed)
2955{
2956 Q_D(QQuickListView);
2957 if (d->highlightMoveVelocity != speed) {
2958 d->highlightMoveVelocity = speed;
2959 if (d->highlightPosAnimator)
2960 d->highlightPosAnimator->velocity = d->highlightMoveVelocity;
2961 emit highlightMoveVelocityChanged();
2962 }
2963}
2964
2965void QQuickListView::setHighlightMoveDuration(int duration)
2966{
2967 Q_D(QQuickListView);
2968 if (d->highlightMoveDuration != duration) {
2969 if (d->highlightPosAnimator)
2970 d->highlightPosAnimator->userDuration = duration;
2971 QQuickItemView::setHighlightMoveDuration(duration);
2972 }
2973}
2974
2975qreal QQuickListView::highlightResizeVelocity() const
2976{
2977 Q_D(const QQuickListView);
2978 return d->highlightResizeVelocity;
2979}
2980
2981void QQuickListView::setHighlightResizeVelocity(qreal speed)
2982{
2983 Q_D(QQuickListView);
2984 if (d->highlightResizeVelocity != speed) {
2985 d->highlightResizeVelocity = speed;
2986 if (d->highlightWidthAnimator)
2987 d->highlightWidthAnimator->velocity = d->highlightResizeVelocity;
2988 if (d->highlightHeightAnimator)
2989 d->highlightHeightAnimator->velocity = d->highlightResizeVelocity;
2990 emit highlightResizeVelocityChanged();
2991 }
2992}
2993
2994int QQuickListView::highlightResizeDuration() const
2995{
2996 Q_D(const QQuickListView);
2997 return d->highlightResizeDuration;
2998}
2999
3000void QQuickListView::setHighlightResizeDuration(int duration)
3001{
3002 Q_D(QQuickListView);
3003 if (d->highlightResizeDuration != duration) {
3004 d->highlightResizeDuration = duration;
3005 if (d->highlightWidthAnimator)
3006 d->highlightWidthAnimator->userDuration = d->highlightResizeDuration;
3007 if (d->highlightHeightAnimator)
3008 d->highlightHeightAnimator->userDuration = d->highlightResizeDuration;
3009 emit highlightResizeDurationChanged();
3010 }
3011}
3012
3013/*!
3014 \qmlproperty enumeration QtQuick::ListView::snapMode
3015
3016 This property determines how the view scrolling will settle following a drag or flick.
3017 The possible values are:
3018
3019 \value ListView.NoSnap (default) the view stops anywhere within the visible area.
3020 \value ListView.SnapToItem the view settles with an item aligned with the start of the view.
3021 \value ListView.SnapOneItem the view settles no more than one item away from the first
3022 visible item at the time the mouse button is released. This mode is particularly
3023 useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
3024 show a stronger affinity to neighboring items when movement occurs. For example, a
3025 short drag that snaps back to the current item with SnapToItem might snap to a
3026 neighboring item with SnapOneItem.
3027
3028 \c snapMode does not affect the \l currentIndex. To update the
3029 \l currentIndex as the list is moved, set \l highlightRangeMode
3030 to \c ListView.StrictlyEnforceRange.
3031
3032 \sa highlightRangeMode
3033*/
3034QQuickListView::SnapMode QQuickListView::snapMode() const
3035{
3036 Q_D(const QQuickListView);
3037 return d->snapMode;
3038}
3039
3040void QQuickListView::setSnapMode(SnapMode mode)
3041{
3042 Q_D(QQuickListView);
3043 if (d->snapMode != mode) {
3044 d->snapMode = mode;
3045 emit snapModeChanged();
3046 d->fixupPosition();
3047 }
3048}
3049
3050
3051/*!
3052 \qmlproperty Component QtQuick::ListView::footer
3053 This property holds the component to use as the footer.
3054
3055 An instance of the footer component is created for each view. The
3056 footer is positioned at the end of the view, after any items. The
3057 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
3058
3059 \sa header, footerItem, {Stacking Order in ListView}
3060*/
3061
3062
3063/*!
3064 \qmlproperty Component QtQuick::ListView::header
3065 This property holds the component to use as the header.
3066
3067 An instance of the header component is created for each view. The
3068 header is positioned at the beginning of the view, before any items.
3069 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
3070
3071 \sa footer, headerItem, {Stacking Order in ListView}
3072*/
3073
3074/*!
3075 \qmlproperty Item QtQuick::ListView::headerItem
3076 This holds the header item created from the \l header component.
3077
3078 An instance of the header component is created for each view. The
3079 header is positioned at the beginning of the view, before any items.
3080 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
3081
3082 \sa header, footerItem, {Stacking Order in ListView}
3083*/
3084
3085/*!
3086 \qmlproperty Item QtQuick::ListView::footerItem
3087 This holds the footer item created from the \l footer component.
3088
3089 An instance of the footer component is created for each view. The
3090 footer is positioned at the end of the view, after any items. The
3091 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
3092
3093 \sa footer, headerItem, {Stacking Order in ListView}
3094*/
3095
3096/*!
3097 \qmlproperty enumeration QtQuick::ListView::headerPositioning
3098 \since Qt 5.4
3099
3100 This property determines the positioning of the \l{headerItem}{header item}.
3101
3102 \value ListView.InlineHeader (default) The header is positioned at the beginning
3103 of the content and moves together with the content like an ordinary item.
3104
3105 \value ListView.OverlayHeader The header is positioned at the beginning of the view.
3106
3107 \value ListView.PullBackHeader The header is positioned at the beginning of the view.
3108 The header can be pushed away by moving the content forwards, and pulled back by
3109 moving the content backwards.
3110
3111 \note This property has no effect on the \l {QQuickItem::z}{stacking order}
3112 of the header. For example, if the header should be shown above the
3113 \l delegate items when using \c ListView.OverlayHeader, its Z value
3114 should be set to a value higher than that of the delegates. For more
3115 information, see \l {Stacking Order in ListView}.
3116
3117 \note If \c headerPositioning is not set to \c ListView.InlineHeader, the
3118 user cannot press and flick the list from the header. In any case, the
3119 \l{headerItem}{header item} may contain items or event handlers that
3120 provide custom handling of mouse or touch input.
3121*/
3122QQuickListView::HeaderPositioning QQuickListView::headerPositioning() const
3123{
3124 Q_D(const QQuickListView);
3125 return d->headerPositioning;
3126}
3127
3128void QQuickListView::setHeaderPositioning(QQuickListView::HeaderPositioning positioning)
3129{
3130 Q_D(QQuickListView);
3131 if (d->headerPositioning != positioning) {
3132 d->applyPendingChanges();
3133 d->headerPositioning = positioning;
3134 if (isComponentComplete()) {
3135 d->updateHeader();
3136 d->updateViewport();
3137 d->fixupPosition();
3138 }
3139 emit headerPositioningChanged();
3140 }
3141}
3142
3143/*!
3144 \qmlproperty enumeration QtQuick::ListView::footerPositioning
3145 \since Qt 5.4
3146
3147 This property determines the positioning of the \l{footerItem}{footer item}.
3148
3149 \value ListView.InlineFooter (default) The footer is positioned at the end
3150 of the content and moves together with the content like an ordinary item.
3151
3152 \value ListView.OverlayFooter The footer is positioned at the end of the view.
3153
3154 \value ListView.PullBackFooter The footer is positioned at the end of the view.
3155 The footer can be pushed away by moving the content backwards, and pulled back by
3156 moving the content forwards.
3157
3158 \note This property has no effect on the \l {QQuickItem::z}{stacking order}
3159 of the footer. For example, if the footer should be shown above the
3160 \l delegate items when using \c ListView.OverlayFooter, its Z value
3161 should be set to a value higher than that of the delegates. For more
3162 information, see \l {Stacking Order in ListView}.
3163
3164 \note If \c footerPositioning is not set to \c ListView.InlineFooter, the
3165 user cannot press and flick the list from the footer. In any case, the
3166 \l{footerItem}{footer item} may contain items or event handlers that
3167 provide custom handling of mouse or touch input.
3168*/
3169QQuickListView::FooterPositioning QQuickListView::footerPositioning() const
3170{
3171 Q_D(const QQuickListView);
3172 return d->footerPositioning;
3173}
3174
3175void QQuickListView::setFooterPositioning(QQuickListView::FooterPositioning positioning)
3176{
3177 Q_D(QQuickListView);
3178 if (d->footerPositioning != positioning) {
3179 d->applyPendingChanges();
3180 d->footerPositioning = positioning;
3181 if (isComponentComplete()) {
3182 d->updateFooter();
3183 d->updateViewport();
3184 d->fixupPosition();
3185 }
3186 emit footerPositioningChanged();
3187 }
3188}
3189
3190/*!
3191 \qmlproperty Transition QtQuick::ListView::populate
3192
3193 This property holds the transition to apply to the items that are initially created
3194 for a view.
3195
3196 It is applied to all items that are created when:
3197
3198 \list
3199 \li The view is first created
3200 \li The view's \l model changes in such a way that the visible delegates are completely replaced
3201 \li The view's \l model is \l {QAbstractItemModel::beginResetModel()}{reset}, if the model is a
3202 QAbstractItemModel subclass
3203 \endlist
3204
3205 For example, here is a view that specifies such a transition:
3206
3207 \code
3208 ListView {
3209 ...
3210 populate: Transition {
3211 NumberAnimation { properties: "x,y"; duration: 1000 }
3212 }
3213 }
3214 \endcode
3215
3216 When the view is initialized, the view will create all the necessary items for the view,
3217 then animate them to their correct positions within the view over one second.
3218
3219 However when scrolling the view later, the populate transition does not
3220 run, even though delegates are being instantiated as they become visible.
3221 When the model changes in a way that new delegates become visible, the
3222 \l add transition is the one that runs. So you should not depend on the
3223 \c populate transition to initialize properties in the delegate, because it
3224 does not apply to every delegate. If your animation sets the \c to value of
3225 a property, the property should initially have the \c to value, and the
3226 animation should set the \c from value in case it is animated:
3227
3228 \code
3229 ListView {
3230 ...
3231 delegate: Rectangle {
3232 opacity: 1 // not necessary because it's the default
3233 }
3234 populate: Transition {
3235 NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
3236 }
3237 }
3238 \endcode
3239
3240 For more details and examples on how to use view transitions, see the ViewTransition
3241 documentation.
3242
3243 \sa add, ViewTransition
3244*/
3245
3246/*!
3247 \qmlproperty Transition QtQuick::ListView::add
3248
3249 This property holds the transition to apply to items that are added to the view.
3250
3251 For example, here is a view that specifies such a transition:
3252
3253 \code
3254 ListView {
3255 ...
3256 add: Transition {
3257 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
3258 }
3259 }
3260 \endcode
3261
3262 Whenever an item is added to the above view, the item will be animated from the position (100,100)
3263 to its final x,y position within the view, over one second. The transition only applies to
3264 the new items that are added to the view; it does not apply to the items below that are
3265 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
3266 or \l addDisplaced properties.
3267
3268 For more details and examples on how to use view transitions, see the ViewTransition
3269 documentation.
3270
3271 \note This transition is not applied to the items that are created when the view is initially
3272 populated, or when the view's \l model changes. (In those cases, the \l populate transition is
3273 applied instead.) Additionally, this transition should \e not animate the height of the new item;
3274 doing so will cause any items beneath the new item to be laid out at the wrong position. Instead,
3275 the height can be animated within the \l {add}{onAdd} handler in the delegate.
3276
3277 \sa addDisplaced, populate, ViewTransition
3278*/
3279
3280/*!
3281 \qmlproperty Transition QtQuick::ListView::addDisplaced
3282
3283 This property holds the transition to apply to items within the view that are displaced by
3284 the addition of other items to the view.
3285
3286 For example, here is a view that specifies such a transition:
3287
3288 \code
3289 ListView {
3290 ...
3291 addDisplaced: Transition {
3292 NumberAnimation { properties: "x,y"; duration: 1000 }
3293 }
3294 }
3295 \endcode
3296
3297 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
3298 them to move down (or sideways, if horizontally orientated) within the view. As this
3299 displacement occurs, the items' movement to their new x,y positions within the view will be
3300 animated by a NumberAnimation over one second, as specified. This transition is not applied to
3301 the new item that has been added to the view; to animate the added items, set the \l add
3302 property.
3303
3304 If an item is displaced by multiple types of operations at the same time, it is not defined as to
3305 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
3306 if it is not necessary to specify different transitions depending on whether an item is displaced
3307 by an add, move or remove operation, consider setting the \l displaced property instead.
3308
3309 For more details and examples on how to use view transitions, see the ViewTransition
3310 documentation.
3311
3312 \note This transition is not applied to the items that are created when the view is initially
3313 populated, or when the view's \l model changes. In those cases, the \l populate transition is
3314 applied instead.
3315
3316 \sa displaced, add, populate, ViewTransition
3317*/
3318
3319/*!
3320 \qmlproperty Transition QtQuick::ListView::move
3321
3322 This property holds the transition to apply to items in the view that are being moved due
3323 to a move operation in the view's \l model.
3324
3325 For example, here is a view that specifies such a transition:
3326
3327 \code
3328 ListView {
3329 ...
3330 move: Transition {
3331 NumberAnimation { properties: "x,y"; duration: 1000 }
3332 }
3333 }
3334 \endcode
3335
3336 Whenever the \l model performs a move operation to move a particular set of indexes, the
3337 respective items in the view will be animated to their new positions in the view over one
3338 second. The transition only applies to the items that are the subject of the move operation
3339 in the model; it does not apply to items below them that are displaced by the move operation.
3340 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
3341
3342 For more details and examples on how to use view transitions, see the ViewTransition
3343 documentation.
3344
3345 \sa moveDisplaced, ViewTransition
3346*/
3347
3348/*!
3349 \qmlproperty Transition QtQuick::ListView::moveDisplaced
3350
3351 This property holds the transition to apply to items that are displaced by a move operation in
3352 the view's \l model.
3353
3354 For example, here is a view that specifies such a transition:
3355
3356 \code
3357 ListView {
3358 ...
3359 moveDisplaced: Transition {
3360 NumberAnimation { properties: "x,y"; duration: 1000 }
3361 }
3362 }
3363 \endcode
3364
3365 Whenever the \l model performs a move operation to move a particular set of indexes, the items
3366 between the source and destination indexes of the move operation are displaced, causing them
3367 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
3368 displacement occurs, the items' movement to their new x,y positions within the view will be
3369 animated by a NumberAnimation over one second, as specified. This transition is not applied to
3370 the items that are the actual subjects of the move operation; to animate the moved items, set
3371 the \l move property.
3372
3373 If an item is displaced by multiple types of operations at the same time, it is not defined as to
3374 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
3375 if it is not necessary to specify different transitions depending on whether an item is displaced
3376 by an add, move or remove operation, consider setting the \l displaced property instead.
3377
3378 For more details and examples on how to use view transitions, see the ViewTransition
3379 documentation.
3380
3381 \sa displaced, move, ViewTransition
3382*/
3383
3384/*!
3385 \qmlproperty Transition QtQuick::ListView::remove
3386
3387 This property holds the transition to apply to items that are removed from the view.
3388
3389 For example, here is a view that specifies such a transition:
3390
3391 \code
3392 ListView {
3393 ...
3394 remove: Transition {
3395 ParallelAnimation {
3396 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
3397 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
3398 }
3399 }
3400 }
3401 \endcode
3402
3403 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
3404 over one second, and in parallel will also change its opacity to 0. The transition
3405 only applies to the items that are removed from the view; it does not apply to the items below
3406 them that are displaced by the removal of the items. To animate the displaced items, set the
3407 \l displaced or \l removeDisplaced properties.
3408
3409 Note that by the time the transition is applied, the item has already been removed from the
3410 model; any references to the model data for the removed index will not be valid.
3411
3412 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
3413 remove transition will not be applied until \l delayRemove becomes false again.
3414
3415 For more details and examples on how to use view transitions, see the ViewTransition
3416 documentation.
3417
3418 \sa removeDisplaced, ViewTransition
3419*/
3420
3421/*!
3422 \qmlproperty Transition QtQuick::ListView::removeDisplaced
3423
3424 This property holds the transition to apply to items in the view that are displaced by the
3425 removal of other items in the view.
3426
3427 For example, here is a view that specifies such a transition:
3428
3429 \code
3430 ListView {
3431 ...
3432 removeDisplaced: Transition {
3433 NumberAnimation { properties: "x,y"; duration: 1000 }
3434 }
3435 }
3436 \endcode
3437
3438 Whenever an item is removed from the above view, all items beneath it are displaced, causing
3439 them to move upwards (or sideways, if horizontally orientated) within the view. As this
3440 displacement occurs, the items' movement to their new x,y positions within the view will be
3441 animated by a NumberAnimation over one second, as specified. This transition is not applied to
3442 the item that has actually been removed from the view; to animate the removed items, set the
3443 \l remove property.
3444
3445 If an item is displaced by multiple types of operations at the same time, it is not defined as to
3446 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
3447 if it is not necessary to specify different transitions depending on whether an item is displaced
3448 by an add, move or remove operation, consider setting the \l displaced property instead.
3449
3450 For more details and examples on how to use view transitions, see the ViewTransition
3451 documentation.
3452
3453 \sa displaced, remove, ViewTransition
3454*/
3455
3456/*!
3457 \qmlproperty Transition QtQuick::ListView::displaced
3458 This property holds the generic transition to apply to items that have been displaced by
3459 any model operation that affects the view.
3460
3461 This is a convenience for specifying the generic transition to be applied to any items
3462 that are displaced by an add, move or remove operation, without having to specify the
3463 individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
3464 is a view that specifies a displaced transition:
3465
3466 \code
3467 ListView {
3468 ...
3469 displaced: Transition {
3470 NumberAnimation { properties: "x,y"; duration: 1000 }
3471 }
3472 }
3473 \endcode
3474
3475 When any item is added, moved or removed within the above view, the items below it are
3476 displaced, causing them to move down (or sideways, if horizontally orientated) within the
3477 view. As this displacement occurs, the items' movement to their new x,y positions within
3478 the view will be animated by a NumberAnimation over one second, as specified.
3479
3480 If a view specifies this generic displaced transition as well as a specific addDisplaced,
3481 moveDisplaced or removeDisplaced transition, the more specific transition will be used
3482 instead of the generic displaced transition when the relevant operation occurs, providing that
3483 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
3484 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
3485
3486 For more details and examples on how to use view transitions, see the ViewTransition
3487 documentation.
3488
3489 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
3490*/
3491
3492void QQuickListView::viewportMoved(Qt::Orientations orient)
3493{
3494 Q_D(QQuickListView);
3495 QQuickItemView::viewportMoved(orient);
3496
3497 if (!d->itemCount) {
3498 if (d->hasStickyHeader())
3499 d->updateHeader();
3500 if (d->hasStickyFooter())
3501 d->updateFooter();
3502 return;
3503 }
3504
3505 // Recursion can occur due to refill changing the content size.
3506 if (d->inViewportMoved)
3507 return;
3508 d->inViewportMoved = true;
3509
3510 if (yflick()) {
3511 if (d->isBottomToTop())
3512 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
3513 else
3514 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
3515 } else {
3516 if (d->isRightToLeft())
3517 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
3518 else
3519 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
3520 }
3521
3522 d->refillOrLayout();
3523
3524 // Set visibility of items to eliminate cost of items outside the visible area.
3525 qreal from = d->isContentFlowReversed() ? -d->position()-d->displayMarginBeginning-d->size() : d->position()-d->displayMarginBeginning;
3526 qreal to = d->isContentFlowReversed() ? -d->position()+d->displayMarginEnd : d->position()+d->size()+d->displayMarginEnd;
3527 for (FxViewItem *item : std::as_const(d->visibleItems)) {
3528 if (item->item)
3529 QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to);
3530 }
3531 if (d->currentItem)
3532 QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to);
3533
3534 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
3535 d->moveReason = QQuickListViewPrivate::Mouse;
3536 if (d->moveReason != QQuickListViewPrivate::SetIndex) {
3537 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
3538 // reposition highlight
3539 qreal pos = d->highlight->position();
3540 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
3541 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
3542 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
3543 if (pos < viewPos + d->highlightRangeStart)
3544 pos = viewPos + d->highlightRangeStart;
3545 if (pos != d->highlight->position()) {
3546 d->highlightPosAnimator->stop();
3547 static_cast<FxListItemSG*>(d->highlight.get())->setPosition(pos);
3548 } else {
3549 d->updateHighlight();
3550 }
3551
3552 // update current index
3553 if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
3554 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
3555 d->updateCurrent(snapItem->index);
3556 }
3557 }
3558 }
3559
3560 if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
3561 d->inFlickCorrection = true;
3562 // Near an end and it seems that the extent has changed?
3563 // Recalculate the flick so that we don't end up in an odd position.
3564 if (yflick() && !d->vData.inOvershoot) {
3565 if (d->vData.velocity > 0) {
3566 const qreal minY = minYExtent();
3567 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
3568 && minY != d->vData.flickTarget)
3569 d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
3570 } else if (d->vData.velocity < 0) {
3571 const qreal maxY = maxYExtent();
3572 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
3573 && maxY != d->vData.flickTarget)
3574 d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
3575 }
3576 }
3577
3578 if (xflick() && !d->hData.inOvershoot) {
3579 if (d->hData.velocity > 0) {
3580 const qreal minX = minXExtent();
3581 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
3582 && minX != d->hData.flickTarget)
3583 d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
3584 } else if (d->hData.velocity < 0) {
3585 const qreal maxX = maxXExtent();
3586 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
3587 && maxX != d->hData.flickTarget)
3588 d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
3589 }
3590 }
3591 d->inFlickCorrection = false;
3592 }
3593 if (d->hasStickyHeader())
3594 d->updateHeader();
3595 if (d->hasStickyFooter())
3596 d->updateFooter();
3597 if (d->sectionCriteria) {
3598 d->updateCurrentSection();
3599 d->updateStickySections();
3600 }
3601 d->inViewportMoved = false;
3602}
3603
3604void QQuickListView::keyPressEvent(QKeyEvent *event)
3605{
3606 Q_D(QQuickListView);
3607 if (d->model && d->model->count() && ((d->interactive && !d->explicitKeyNavigationEnabled)
3608 || (d->explicitKeyNavigationEnabled && d->keyNavigationEnabled))) {
3609 if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
3610 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
3611 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Up)
3612 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Down)) {
3613 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
3614 decrementCurrentIndex();
3615 event->accept();
3616 return;
3617 } else if (d->wrap) {
3618 event->accept();
3619 return;
3620 }
3621 } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
3622 || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
3623 || (d->orient == QQuickListView::Vertical && !d->isBottomToTop() && event->key() == Qt::Key_Down)
3624 || (d->orient == QQuickListView::Vertical && d->isBottomToTop() && event->key() == Qt::Key_Up)) {
3625 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
3626 incrementCurrentIndex();
3627 event->accept();
3628 return;
3629 } else if (d->wrap) {
3630 event->accept();
3631 return;
3632 }
3633 }
3634 }
3635 event->ignore();
3636 QQuickItemView::keyPressEvent(event);
3637}
3638
3639void QQuickListView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
3640{
3641 Q_D(QQuickListView);
3642
3643 if (d->model) {
3644 // When the view changes size, we force the pool to
3645 // shrink by releasing all pooled items.
3646 d->model->drainReusableItemsPool(0);
3647 }
3648
3649 if (d->isRightToLeft()) {
3650 // maintain position relative to the right edge
3651 qreal dx = newGeometry.width() - oldGeometry.width();
3652 setContentX(contentX() - dx);
3653 } else if (d->isBottomToTop()) {
3654 // maintain position relative to the bottom edge
3655 qreal dy = newGeometry.height() - oldGeometry.height();
3656 setContentY(contentY() - dy);
3657 }
3658 QQuickItemView::geometryChange(newGeometry, oldGeometry);
3659}
3660
3661void QQuickListView::initItem(int index, QObject *object)
3662{
3663 QQuickItemView::initItem(index, object);
3664
3665 // setting the view from the FxViewItem wrapper is too late if the delegate
3666 // needs access to the view in Component.onCompleted
3667 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
3668 if (item) {
3669 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
3670 qmlAttachedPropertiesObject<QQuickListView>(item));
3671 if (attached)
3672 attached->setView(this);
3673 }
3674}
3675
3676qreal QQuickListView::maxYExtent() const
3677{
3678 Q_D(const QQuickListView);
3679 if (d->layoutOrientation() == Qt::Horizontal && d->flickableDirection != HorizontalFlick)
3680 return QQuickFlickable::maxYExtent();
3681 return QQuickItemView::maxYExtent();
3682}
3683
3684qreal QQuickListView::maxXExtent() const
3685{
3686 Q_D(const QQuickListView);
3687 if (d->layoutOrientation() == Qt::Vertical && d->flickableDirection != VerticalFlick)
3688 return QQuickFlickable::maxXExtent();
3689 return QQuickItemView::maxXExtent();
3690}
3691
3692/*!
3693 \qmlmethod QtQuick::ListView::incrementCurrentIndex()
3694
3695 Increments the current index. The current index will wrap
3696 if keyNavigationWraps is true and it is currently at the end.
3697 This method has no effect if the \l count is zero.
3698
3699 \b Note: methods should only be called after the Component has completed.
3700*/
3701void QQuickListView::incrementCurrentIndex()
3702{
3703 Q_D(QQuickListView);
3704 int count = d->model ? d->model->count() : 0;
3705 if (count && (currentIndex() < count - 1 || d->wrap)) {
3706 d->moveReason = QQuickListViewPrivate::SetIndex;
3707 int index = currentIndex()+1;
3708 setCurrentIndex((index >= 0 && index < count) ? index : 0);
3709 }
3710}
3711
3712/*!
3713 \qmlmethod QtQuick::ListView::decrementCurrentIndex()
3714
3715 Decrements the current index. The current index will wrap
3716 if keyNavigationWraps is true and it is currently at the beginning.
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::decrementCurrentIndex()
3722{
3723 Q_D(QQuickListView);
3724 int count = d->model ? d->model->count() : 0;
3725 if (count && (currentIndex() > 0 || d->wrap)) {
3726 d->moveReason = QQuickListViewPrivate::SetIndex;
3727 int index = currentIndex()-1;
3728 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
3729 }
3730}
3731
3733{
3734 Q_Q(QQuickListView);
3735 if (q->isComponentComplete() && model) {
3736 QList<QByteArray> roles;
3737 if (sectionCriteria && !sectionCriteria->property().isEmpty())
3738 roles << sectionCriteria->property().toUtf8();
3739 model->setWatchedRoles(roles);
3741 if (itemCount)
3742 forceLayoutPolish();
3743 }
3744}
3745
3746bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
3747{
3748 Q_Q(QQuickListView);
3749#if QT_CONFIG(quick_viewtransitions)
3750 Q_UNUSED(movingIntoView)
3751#endif
3752 int modelIndex = change.index;
3753 int count = change.count;
3754
3755 if (q->size().isNull() && visibleItems.isEmpty())
3756 return false;
3757
3758 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
3759 int index = visibleItems.size() ? mapFromModel(modelIndex) : 0;
3760 qreal lastVisiblePos = buffer + displayMarginEnd + tempPos + size();
3761
3762 if (index < 0) {
3763 int i = visibleItems.size() - 1;
3764 while (i > 0 && visibleItems.at(i)->index == -1)
3765 --i;
3766 if (i == 0 && visibleItems.constFirst()->index == -1) {
3767 // there are no visible items except items marked for removal
3768 index = visibleItems.size();
3769 } else if (visibleItems.at(i)->index + 1 == modelIndex
3770 && visibleItems.at(i)->endPosition() <= lastVisiblePos) {
3771 // Special case of appending an item to the model.
3772 index = visibleItems.size();
3773 } else {
3774 if (modelIndex < visibleIndex) {
3775 // Insert before visible items
3776 visibleIndex += count;
3777 for (FxViewItem *item : std::as_const(visibleItems)) {
3778 if (item->index != -1 && item->index >= modelIndex)
3779 item->index += count;
3780 }
3781 }
3782 return true;
3783 }
3784 }
3785
3786 // index can be the next item past the end of the visible items list (i.e. appended)
3787 qreal pos = 0;
3788 if (visibleItems.size()) {
3789 pos = index < visibleItems.size() ? visibleItems.at(index)->position()
3790 : visibleItems.constLast()->endPosition() + spacing;
3791 }
3792
3793 // Update the indexes of the following visible items.
3794 for (FxViewItem *item : std::as_const(visibleItems)) {
3795 if (item->index != -1 && item->index >= modelIndex) {
3796 item->index += count;
3797#if QT_CONFIG(quick_viewtransitions)
3798 if (change.isMove())
3799 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
3800 else
3801 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
3802#endif
3803 }
3804 }
3805
3806 bool visibleAffected = false;
3807 if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
3808 // Insert items before the visible item.
3809 int insertionIdx = index;
3810 qreal from = tempPos - displayMarginBeginning - buffer;
3811
3812 if (insertionIdx < visibleIndex) {
3813 if (pos >= from) {
3814 // items won't be visible, just note the size for repositioning
3815 insertResult->sizeChangesBeforeVisiblePos += count * (averageSize + spacing);
3816 }
3817 } else {
3818 MutableModelIterator it(model, modelIndex + count - 1, modelIndex -1);
3819 for (; it.hasNext() && pos >= from; it.next()) {
3820 // item is before first visible e.g. in cache buffer
3821 FxViewItem *item = nullptr;
3822 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
3823 item->index = it.index;
3824 if (!item)
3825 item = createItem(it.index, QQmlIncubator::Synchronous);
3826 if (!item)
3827 return false;
3828 if (it.removedAtIndex)
3829 continue;
3830
3831 visibleAffected = true;
3832 visibleItems.insert(insertionIdx, item);
3833 if (insertionIdx == 0)
3834 insertResult->changedFirstItem = true;
3835 if (!change.isMove()) {
3836 addedItems->append(item);
3837#if QT_CONFIG(quick_viewtransitions)
3838 if (transitioner)
3839 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
3840 else
3841#endif
3842 static_cast<FxListItemSG *>(item)->setPosition(pos, true);
3843 }
3844 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
3845 pos -= item->size() + spacing;
3846 index++;
3847 }
3848 }
3849
3850 int firstOkIdx = -1;
3851 for (int i = 0; i <= insertionIdx && i < visibleItems.size() - 1; i++) {
3852 if (visibleItems.at(i)->index + 1 != visibleItems.at(i + 1)->index) {
3853 firstOkIdx = i + 1;
3854 break;
3855 }
3856 }
3857 for (int i = 0; i < firstOkIdx; i++) {
3858 FxViewItem *nvItem = visibleItems.takeFirst();
3859 addedItems->removeOne(nvItem);
3860 removeItem(nvItem);
3861 }
3862
3863 } else {
3864 MutableModelIterator it(model, modelIndex, modelIndex + count);
3865 for (; it.hasNext() && pos <= lastVisiblePos; it.next()) {
3866 visibleAffected = true;
3867 FxViewItem *item = nullptr;
3868 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
3869 item->index = it.index;
3870#if QT_CONFIG(quick_viewtransitions)
3871 bool newItem = !item;
3872#endif
3873 it.removedAtIndex = false;
3874 if (!item)
3875 item = createItem(it.index, QQmlIncubator::Synchronous);
3876 if (!item)
3877 return false;
3878 if (it.removedAtIndex) {
3879 releaseItem(item, reusableFlag);
3880 continue;
3881 }
3882
3883 if (index < visibleItems.size())
3884 visibleItems.insert(index, item);
3885 else // special case of appending an item to the model - as above
3886 visibleItems.append(item);
3887 if (index == 0)
3888 insertResult->changedFirstItem = true;
3889 if (change.isMove()) {
3890 // we know this is a move target, since move displaced items that are
3891 // shuffled into view due to a move would be added in refill()
3892#if QT_CONFIG(quick_viewtransitions)
3893 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
3894 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
3895#endif
3896 } else {
3897 addedItems->append(item);
3898#if QT_CONFIG(quick_viewtransitions)
3899 if (transitioner)
3900 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
3901 else
3902#endif
3903 static_cast<FxListItemSG *>(item)->setPosition(pos, true);
3904 }
3905 insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
3906 pos += item->size() + spacing;
3907 ++index;
3908 }
3909 it.disconnect();
3910
3911 if (0 < index && index < visibleItems.size()) {
3912 FxViewItem *prevItem = visibleItems.at(index - 1);
3913 FxViewItem *item = visibleItems.at(index);
3914 if (prevItem->index != item->index - 1) {
3915 int i = index;
3916#if QT_CONFIG(quick_viewtransitions)
3917 qreal prevPos = prevItem->position();
3918#endif
3919 while (i < visibleItems.size()) {
3920 FxListItemSG *nvItem = static_cast<FxListItemSG *>(visibleItems.takeLast());
3921 insertResult->sizeChangesAfterVisiblePos -= nvItem->size() + spacing;
3922 addedItems->removeOne(nvItem);
3923#if QT_CONFIG(quick_viewtransitions)
3924 if (nvItem->transitionScheduledOrRunning())
3925 nvItem->setPosition(prevPos + (nvItem->index - prevItem->index) * averageSize);
3926#endif
3927 removeItem(nvItem);
3928 }
3929 }
3930 }
3931 }
3932
3933 updateVisibleIndex();
3934
3935 return visibleAffected;
3936}
3937
3938#if QT_CONFIG(quick_viewtransitions)
3939void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
3940{
3941 Q_UNUSED(insertionResult);
3942
3943 if (!transitioner)
3944 return;
3945
3946 int markerItemIndex = -1;
3947 for (int i=0; i<visibleItems.size(); i++) {
3948 if (visibleItems.at(i)->index == afterModelIndex) {
3949 markerItemIndex = i;
3950 break;
3951 }
3952 }
3953 if (markerItemIndex < 0)
3954 return;
3955
3956 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
3957 qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
3958 - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
3959
3960 for (int i=markerItemIndex+1; i<visibleItems.size(); i++) {
3961 FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
3962 if (listItem->position() >= viewEndPos)
3963 break;
3964 if (!listItem->transitionScheduledOrRunning()) {
3965 qreal pos = listItem->position();
3966 listItem->setPosition(pos - sizeRemoved);
3967 listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
3968 listItem->setPosition(pos);
3969 }
3970 }
3971}
3972#endif
3973
3974/*!
3975 \qmlmethod QtQuick::ListView::positionViewAtIndex(int index, PositionMode mode)
3976
3977 Positions the view such that the \a index is at the position specified by \a mode:
3978
3979 \value ListView.Beginning position item at the top (or left for horizontal orientation) of the view.
3980 \value ListView.Center position item in the center of the view.
3981 \value ListView.End position item at bottom (or right for horizontal orientation) of the view.
3982 \value ListView.Visible if any part of the item is visible then take no action, otherwise
3983 bring the item into view.
3984 \value ListView.Contain ensure the entire item is visible. If the item is larger than the view,
3985 the item is positioned at the top (or left for horizontal orientation) of the view.
3986 \value ListView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid
3987 if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled via \l snapMode.
3988
3989 If positioning the view at \a index would cause empty space to be displayed at
3990 the beginning or end of the view, the view will be positioned at the boundary.
3991
3992 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3993 at a particular index. This is unreliable since removing items from the start
3994 of the list does not cause all other items to be repositioned, and because
3995 the actual start of the view can vary based on the size of the delegates.
3996 The correct way to bring an item into view is with \c positionViewAtIndex.
3997
3998 \b Note: methods should only be called after the Component has completed. To position
3999 the view at startup, this method should be called by Component.onCompleted. For
4000 example, to position the view at the end:
4001
4002 \code
4003 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
4004 \endcode
4005*/
4006
4007/*!
4008 \qmlmethod QtQuick::ListView::positionViewAtBeginning()
4009 \qmlmethod QtQuick::ListView::positionViewAtEnd()
4010
4011 Positions the view at the beginning or end, taking into account any header or footer.
4012
4013 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
4014 at a particular index. This is unreliable since removing items from the start
4015 of the list does not cause all other items to be repositioned, and because
4016 the actual start of the view can vary based on the size of the delegates.
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 on startup:
4021
4022 \code
4023 Component.onCompleted: positionViewAtEnd()
4024 \endcode
4025*/
4026
4027/*!
4028 \qmlmethod int QtQuick::ListView::indexAt(real x, real y)
4029
4030 Returns the index of the visible item containing the point \a x, \a y in content
4031 coordinates. If there is no item at the point specified, or the item is
4032 not visible -1 is returned.
4033
4034 If the item is outside the visible area, -1 is returned, regardless of
4035 whether an item will exist at that point when scrolled into view.
4036
4037 \b Note: methods should only be called after the Component has completed.
4038*/
4039
4040/*!
4041 \qmlmethod Item QtQuick::ListView::itemAt(real x, real y)
4042
4043 Returns the visible item containing the point \a x, \a y in content
4044 coordinates. If there is no item at the point specified, or the item is
4045 not visible null is returned.
4046
4047 If the item is outside the visible area, null is returned, regardless of
4048 whether an item will exist at that point when scrolled into view.
4049
4050 \b Note: methods should only be called after the Component has completed.
4051*/
4052
4053/*!
4054 \qmlmethod Item QtQuick::ListView::itemAtIndex(int index)
4055
4056 Returns the item for \a index. If there is no item for that index, for example
4057 because it has not been created yet, or because it has been panned out of
4058 the visible area and removed from the cache, null is returned.
4059
4060 \b Note: this method should only be called after the Component has completed.
4061 The returned value should also not be stored since it can turn to null
4062 as soon as control goes out of the calling scope, if the view releases that item.
4063
4064 \since 5.13
4065*/
4066
4067/*!
4068 \qmlmethod QtQuick::ListView::forceLayout()
4069
4070 Responding to changes in the model is usually batched to happen only once
4071 per frame. This means that inside script blocks it is possible for the
4072 underlying model to have changed, but the ListView has not caught up yet.
4073
4074 This method forces the ListView to immediately respond to any outstanding
4075 changes in the model.
4076
4077 \since 5.1
4078
4079 \b Note: methods should only be called after the Component has completed.
4080*/
4081
4082QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
4083{
4084 return new QQuickListViewAttached(obj);
4085}
4086
4087/*! \internal
4088 Prevents clicking or dragging through floating headers (QTBUG-74046).
4089*/
4090bool QQuickListViewPrivate::wantsPointerEvent(const QPointerEvent *event)
4091{
4092 Q_Q(const QQuickListView);
4093 bool ret = true;
4094
4095 QPointF pos = event->points().first().position();
4096 if (!pos.isNull()) {
4097 if (auto header = q->headerItem()) {
4098 if (q->headerPositioning() != QQuickListView::InlineHeader &&
4099 header->contains(q->mapToItem(header, pos)))
4100 ret = false;
4101 }
4102 if (auto footer = q->footerItem()) {
4103 if (q->footerPositioning() != QQuickListView::InlineFooter &&
4104 footer->contains(q->mapToItem(footer, pos)))
4105 ret = false;
4106 }
4107 }
4108
4109 switch (event->type()) {
4110 case QEvent::MouseButtonPress:
4111 wantedMousePress = ret;
4112 break;
4113 case QEvent::MouseMove:
4114 ret = wantedMousePress;
4115 break;
4116 default:
4117 break;
4118 }
4119
4120 qCDebug(lcEvents) << q << (ret ? "WANTS" : "DOESN'T want") << event;
4121 return ret;
4122}
4123
4124QT_END_NAMESPACE
4125
4126#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
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
#define QML_FLICK_SNAPONETHRESHOLD