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