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