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

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