1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickitemview_p_p.h"
5#include <QtQml/qqmlcomponent.h>
6#include "qquickitemviewfxitem_p_p.h"
7#include <QtQuick/private/qquicktransition_p.h>
8#include <QtQml/QQmlInfo>
9#include "qplatformdefs.h"
10
11QT_BEGIN_NAMESPACE
12
13Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
14Q_STATIC_LOGGING_CATEGORY(lcCount, "qt.quick.itemview.count")
15
16// Default cacheBuffer for all views.
17#ifndef QML_VIEW_DEFAULTCACHEBUFFER
18#define QML_VIEW_DEFAULTCACHEBUFFER 320
19#endif
20
21FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemViewAttached *attached)
22 : QQuickItemViewFxItem(i, own, QQuickItemViewPrivate::get(o: v))
23 , view(v)
24 , attached(attached)
25{
26}
27
28QQuickItemViewChangeSet::QQuickItemViewChangeSet()
29 : active(false)
30{
31 reset();
32}
33
34bool QQuickItemViewChangeSet::hasPendingChanges() const
35{
36 return !pendingChanges.isEmpty();
37}
38
39void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet)
40{
41 pendingChanges.apply(changeSet);
42
43 int moveId = -1;
44 int moveOffset = 0;
45
46 for (const QQmlChangeSet::Change &r : changeSet.removes()) {
47 itemCount -= r.count;
48 if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
49 newCurrentIndex -= r.count;
50 currentChanged = true;
51 } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) {
52 // current item has been removed.
53 if (r.isMove()) {
54 moveId = r.moveId;
55 moveOffset = newCurrentIndex - r.index;
56 } else {
57 currentRemoved = true;
58 newCurrentIndex = -1;
59 if (itemCount)
60 newCurrentIndex = qMin(a: r.index, b: itemCount - 1);
61 }
62 currentChanged = true;
63 }
64 }
65 for (const QQmlChangeSet::Change &i : changeSet.inserts()) {
66 if (moveId == -1) {
67 if (itemCount && newCurrentIndex >= i.index) {
68 newCurrentIndex += i.count;
69 currentChanged = true;
70 } else if (newCurrentIndex < 0) {
71 newCurrentIndex = 0;
72 currentChanged = true;
73 } else if (newCurrentIndex == 0 && !itemCount) {
74 // this is the first item, set the initial current index
75 currentChanged = true;
76 }
77 } else if (moveId == i.moveId) {
78 newCurrentIndex = i.index + moveOffset;
79 }
80 itemCount += i.count;
81 }
82}
83
84void QQuickItemViewChangeSet::applyBufferedChanges(const QQuickItemViewChangeSet &other)
85{
86 if (!other.hasPendingChanges())
87 return;
88
89 pendingChanges.apply(changeSet: other.pendingChanges);
90 itemCount = other.itemCount;
91 newCurrentIndex = other.newCurrentIndex;
92 currentChanged = other.currentChanged;
93 currentRemoved = other.currentRemoved;
94}
95
96void QQuickItemViewChangeSet::prepare(int currentIndex, int count)
97{
98 if (active)
99 return;
100 reset();
101 active = true;
102 itemCount = count;
103 newCurrentIndex = currentIndex;
104}
105
106void QQuickItemViewChangeSet::reset()
107{
108 itemCount = 0;
109 newCurrentIndex = -1;
110 pendingChanges.clear();
111 removedItems.clear();
112 active = false;
113 currentChanged = false;
114 currentRemoved = false;
115}
116
117//-----------------------------------
118
119QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
120 : QQuickFlickable(dd, parent)
121{
122 Q_D(QQuickItemView);
123 d->init();
124}
125
126QQuickItemView::~QQuickItemView()
127{
128 Q_D(QQuickItemView);
129 d->clear(onDestruction: true);
130 if (d->ownModel) {
131 delete d->model;
132 } else {
133 QQmlDelegateModelPointer model(d->model);
134 d->disconnectModel(q: this, model: &model);
135 }
136 delete d->header;
137 delete d->footer;
138}
139
140
141QQuickItem *QQuickItemView::currentItem() const
142{
143 Q_D(const QQuickItemView);
144 return d->currentItem ? d->currentItem->item : nullptr;
145}
146
147QVariant QQuickItemView::model() const
148{
149 Q_D(const QQuickItemView);
150 return d->modelVariant;
151}
152
153void QQuickItemView::setModel(const QVariant &m)
154{
155 Q_D(QQuickItemView);
156 QVariant model = m;
157 if (model.userType() == qMetaTypeId<QJSValue>())
158 model = model.value<QJSValue>().toVariant();
159
160 if (d->modelVariant == model)
161 return;
162
163 QQmlDelegateModelPointer oldModel(d->model);
164 d->disconnectModel(q: this, model: &oldModel);
165
166 d->clear();
167 d->model = nullptr;
168 d->setPosition(d->contentStartOffset());
169 d->modelVariant = model;
170
171 QObject *object = qvariant_cast<QObject *>(v: model);
172
173 QQmlDelegateModelPointer newModel(qobject_cast<QQmlInstanceModel *>(object));
174 if (newModel) {
175 if (d->explicitDelegate) {
176 QQmlComponent *delegate = nullptr;
177 if (QQmlDelegateModel *old = oldModel.delegateModel())
178 delegate = old->delegate();
179
180 if (QQmlDelegateModel *delegateModel = newModel.delegateModel()) {
181 delegateModel->setDelegate(delegate);
182 } else if (delegate) {
183 qmlWarning(me: this) << "Cannot retain explicitly set delegate on non-DelegateModel";
184 d->explicitDelegate = false;
185 }
186 }
187
188 if (d->explicitDelegateModelAccess) {
189 QQmlDelegateModel::DelegateModelAccess access = QQmlDelegateModel::Qt5ReadWrite;
190 if (QQmlDelegateModel *old = oldModel.delegateModel())
191 access = old->delegateModelAccess();
192
193 if (QQmlDelegateModel *delegateModel = newModel.delegateModel()) {
194 delegateModel->setDelegateModelAccess(access);
195 } else if (access != QQmlDelegateModel::Qt5ReadWrite) {
196 qmlWarning(me: this) << "Cannot retain explicitly set delegate model access "
197 "on non-DelegateModel";
198 d->explicitDelegateModelAccess = false;
199 }
200 }
201
202 if (d->ownModel) {
203 delete oldModel.instanceModel();
204 d->ownModel = false;
205 }
206 d->model = newModel.instanceModel();
207 } else if (d->ownModel) {
208 newModel = oldModel;
209 d->model = newModel.instanceModel();
210 if (QQmlDelegateModel *delegateModel = newModel.delegateModel())
211 delegateModel->setModel(model);
212 } else {
213 newModel = QQmlDelegateModel::createForView(q: this, d);
214 if (d->explicitDelegate) {
215 QQmlComponent *delegate = nullptr;
216 if (QQmlDelegateModel *old = oldModel.delegateModel())
217 delegate = old->delegate();
218 newModel.delegateModel()->setDelegate(delegate);
219 }
220
221 if (d->explicitDelegateModelAccess) {
222 QQmlDelegateModel::DelegateModelAccess access = QQmlDelegateModel::Qt5ReadWrite;
223 if (QQmlDelegateModel *old = oldModel.delegateModel())
224 access = old->delegateModelAccess();
225 newModel.delegateModel()->setDelegateModelAccess(access);
226 }
227
228 newModel.delegateModel()->setModel(model);
229 }
230
231 d->connectModel(q: this, model: &newModel);
232 emit modelChanged();
233 d->moveReason = QQuickItemViewPrivate::Other;
234}
235
236QQmlComponent *QQuickItemView::delegate() const
237{
238 Q_D(const QQuickItemView);
239 if (d->model) {
240 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(object: d->model))
241 return dataModel->delegate();
242 }
243
244 return nullptr;
245}
246
247void QQuickItemView::setDelegate(QQmlComponent *delegate)
248{
249 Q_D(QQuickItemView);
250 const auto setExplicitDelegate = [&](QQmlDelegateModel *delegateModel) {
251 int oldCount = delegateModel->count();
252 delegateModel->setDelegate(delegate);
253 if (oldCount != delegateModel->count())
254 d->emitCountChanged();
255 d->explicitDelegate = true;
256 d->delegateValidated = false;
257 };
258
259 if (!d->model) {
260 if (!delegate) {
261 // Explicitly set a null delegate. We can do this without model.
262 d->explicitDelegate = true;
263 return;
264 }
265
266 setExplicitDelegate(QQmlDelegateModel::createForView(q: this, d));
267 // The new model is not connected to applyDelegateChange, yet. We only do this once
268 // there is actual data, via an explicit setModel(). So we have to manually emit the
269 // delegateChanged() here.
270 emit delegateChanged();
271 return;
272 }
273
274 if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(object: d->model)) {
275 // Disable the warning in applyDelegateChange since the new delegate is also explicit.
276 d->explicitDelegate = false;
277 setExplicitDelegate(delegateModel);
278 return;
279 }
280
281 if (delegate)
282 qmlWarning(me: this) << "Cannot set a delegate on an explicitly provided non-DelegateModel";
283 else
284 d->explicitDelegate = true; // Explicitly set null delegate always works
285}
286
287
288int QQuickItemView::count() const
289{
290 Q_D(const QQuickItemView);
291 if (!d->model)
292 return 0;
293 return d->model->count();
294}
295
296int QQuickItemView::currentIndex() const
297{
298 Q_D(const QQuickItemView);
299 return d->currentIndex;
300}
301
302void QQuickItemView::setCurrentIndex(int index)
303{
304 Q_D(QQuickItemView);
305 if (d->inRequest) // currently creating item
306 return;
307 d->currentIndexCleared = (index == -1);
308
309 d->applyPendingChanges();
310 if (index == d->currentIndex)
311 return;
312 if (isComponentComplete() && d->isValid()) {
313 d->moveReason = QQuickItemViewPrivate::SetIndex;
314 d->updateCurrent(modelIndex: index);
315 } else if (d->currentIndex != index) {
316 d->currentIndex = index;
317 emit currentIndexChanged();
318 }
319}
320
321
322bool QQuickItemView::isWrapEnabled() const
323{
324 Q_D(const QQuickItemView);
325 return d->wrap;
326}
327
328void QQuickItemView::setWrapEnabled(bool wrap)
329{
330 Q_D(QQuickItemView);
331 if (d->wrap == wrap)
332 return;
333 d->wrap = wrap;
334 emit keyNavigationWrapsChanged();
335}
336
337bool QQuickItemView::isKeyNavigationEnabled() const
338{
339 Q_D(const QQuickItemView);
340 return d->explicitKeyNavigationEnabled ? d->keyNavigationEnabled : d->interactive;
341}
342
343void QQuickItemView::setKeyNavigationEnabled(bool keyNavigationEnabled)
344{
345 // TODO: default binding to "interactive" can be removed in Qt 6; it only exists for compatibility reasons.
346 Q_D(QQuickItemView);
347 const bool wasImplicit = !d->explicitKeyNavigationEnabled;
348 if (wasImplicit)
349 QObject::disconnect(sender: this, signal: &QQuickFlickable::interactiveChanged, receiver: this, slot: &QQuickItemView::keyNavigationEnabledChanged);
350
351 d->explicitKeyNavigationEnabled = true;
352
353 // Ensure that we emit the change signal in case there is no different in value.
354 if (d->keyNavigationEnabled != keyNavigationEnabled || wasImplicit) {
355 d->keyNavigationEnabled = keyNavigationEnabled;
356 emit keyNavigationEnabledChanged();
357 }
358}
359
360int QQuickItemView::cacheBuffer() const
361{
362 Q_D(const QQuickItemView);
363 return d->buffer;
364}
365
366void QQuickItemView::setCacheBuffer(int b)
367{
368 Q_D(QQuickItemView);
369 if (b < 0) {
370 qmlWarning(me: this) << "Cannot set a negative cache buffer";
371 return;
372 }
373
374 if (d->buffer != b) {
375 d->buffer = b;
376 if (isComponentComplete()) {
377 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
378 d->refillOrLayout();
379 }
380 emit cacheBufferChanged();
381 }
382}
383
384int QQuickItemView::displayMarginBeginning() const
385{
386 Q_D(const QQuickItemView);
387 return d->displayMarginBeginning;
388}
389
390void QQuickItemView::setDisplayMarginBeginning(int margin)
391{
392 Q_D(QQuickItemView);
393 if (d->displayMarginBeginning != margin) {
394 d->displayMarginBeginning = margin;
395 if (isComponentComplete()) {
396 d->forceLayoutPolish();
397 }
398 emit displayMarginBeginningChanged();
399 }
400}
401
402int QQuickItemView::displayMarginEnd() const
403{
404 Q_D(const QQuickItemView);
405 return d->displayMarginEnd;
406}
407
408void QQuickItemView::setDisplayMarginEnd(int margin)
409{
410 Q_D(QQuickItemView);
411 if (d->displayMarginEnd != margin) {
412 d->displayMarginEnd = margin;
413 if (isComponentComplete()) {
414 d->forceLayoutPolish();
415 }
416 emit displayMarginEndChanged();
417 }
418}
419
420Qt::LayoutDirection QQuickItemView::layoutDirection() const
421{
422 Q_D(const QQuickItemView);
423 return d->layoutDirection;
424}
425
426void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
427{
428 Q_D(QQuickItemView);
429 if (d->layoutDirection != layoutDirection) {
430 d->layoutDirection = layoutDirection;
431 d->regenerate();
432 emit layoutDirectionChanged();
433 emit effectiveLayoutDirectionChanged();
434 }
435}
436
437Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
438{
439 Q_D(const QQuickItemView);
440 if (d->effectiveLayoutMirror)
441 return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
442 else
443 return d->layoutDirection;
444}
445
446QQuickItemView::VerticalLayoutDirection QQuickItemView::verticalLayoutDirection() const
447{
448 Q_D(const QQuickItemView);
449 return d->verticalLayoutDirection;
450}
451
452void QQuickItemView::setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection)
453{
454 Q_D(QQuickItemView);
455 if (d->verticalLayoutDirection != layoutDirection) {
456 d->verticalLayoutDirection = layoutDirection;
457 d->regenerate();
458 emit verticalLayoutDirectionChanged();
459 }
460}
461
462QQmlComponent *QQuickItemView::header() const
463{
464 Q_D(const QQuickItemView);
465 return d->headerComponent;
466}
467
468QQuickItem *QQuickItemView::headerItem() const
469{
470 Q_D(const QQuickItemView);
471 return d->header ? d->header->item : nullptr;
472}
473
474void QQuickItemView::setHeader(QQmlComponent *headerComponent)
475{
476 Q_D(QQuickItemView);
477 if (d->headerComponent != headerComponent) {
478 d->applyPendingChanges();
479 delete d->header;
480 d->header = nullptr;
481 d->headerComponent = headerComponent;
482
483 d->markExtentsDirty();
484
485 if (isComponentComplete()) {
486 d->updateHeader();
487 d->updateFooter();
488 d->updateViewport();
489 d->fixupPosition();
490 } else {
491 emit headerItemChanged();
492 }
493 emit headerChanged();
494 }
495}
496
497QQmlComponent *QQuickItemView::footer() const
498{
499 Q_D(const QQuickItemView);
500 return d->footerComponent;
501}
502
503QQuickItem *QQuickItemView::footerItem() const
504{
505 Q_D(const QQuickItemView);
506 return d->footer ? d->footer->item : nullptr;
507}
508
509void QQuickItemView::setFooter(QQmlComponent *footerComponent)
510{
511 Q_D(QQuickItemView);
512 if (d->footerComponent != footerComponent) {
513 d->applyPendingChanges();
514 delete d->footer;
515 d->footer = nullptr;
516 d->footerComponent = footerComponent;
517
518 if (isComponentComplete()) {
519 d->updateFooter();
520 d->updateViewport();
521 d->fixupPosition();
522 } else {
523 emit footerItemChanged();
524 }
525 emit footerChanged();
526 }
527}
528
529QQmlComponent *QQuickItemView::highlight() const
530{
531 Q_D(const QQuickItemView);
532 return d->highlightComponent;
533}
534
535void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
536{
537 Q_D(QQuickItemView);
538 if (highlightComponent != d->highlightComponent) {
539 d->applyPendingChanges();
540 d->highlightComponent = highlightComponent;
541 d->createHighlight();
542 if (d->currentItem)
543 d->updateHighlight();
544 emit highlightChanged();
545 }
546}
547
548QQuickItem *QQuickItemView::highlightItem() const
549{
550 Q_D(const QQuickItemView);
551 return d->highlight ? d->highlight->item : nullptr;
552}
553
554bool QQuickItemView::highlightFollowsCurrentItem() const
555{
556 Q_D(const QQuickItemView);
557 return d->autoHighlight;
558}
559
560void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
561{
562 Q_D(QQuickItemView);
563 if (d->autoHighlight != autoHighlight) {
564 d->autoHighlight = autoHighlight;
565 if (autoHighlight)
566 d->updateHighlight();
567 emit highlightFollowsCurrentItemChanged();
568 }
569}
570
571QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
572{
573 Q_D(const QQuickItemView);
574 return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
575}
576
577void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
578{
579 Q_D(QQuickItemView);
580 if (d->highlightRange == mode)
581 return;
582 d->highlightRange = mode;
583 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
584 if (isComponentComplete()) {
585 d->updateViewport();
586 d->moveReason = QQuickItemViewPrivate::Other;
587 d->fixupPosition();
588 }
589 emit highlightRangeModeChanged();
590}
591
592//###Possibly rename these properties, since they are very useful even without a highlight?
593qreal QQuickItemView::preferredHighlightBegin() const
594{
595 Q_D(const QQuickItemView);
596 return d->highlightRangeStart;
597}
598
599void QQuickItemView::setPreferredHighlightBegin(qreal start)
600{
601 Q_D(QQuickItemView);
602 d->highlightRangeStartValid = true;
603 if (d->highlightRangeStart == start)
604 return;
605 d->highlightRangeStart = start;
606 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
607 if (isComponentComplete()) {
608 d->updateViewport();
609 if (!isMoving() && !isFlicking()) {
610 d->moveReason = QQuickItemViewPrivate::Other;
611 d->fixupPosition();
612 }
613 }
614 emit preferredHighlightBeginChanged();
615}
616
617void QQuickItemView::resetPreferredHighlightBegin()
618{
619 Q_D(QQuickItemView);
620 d->highlightRangeStartValid = false;
621 if (d->highlightRangeStart == 0)
622 return;
623 d->highlightRangeStart = 0;
624 if (isComponentComplete()) {
625 d->updateViewport();
626 if (!isMoving() && !isFlicking()) {
627 d->moveReason = QQuickItemViewPrivate::Other;
628 d->fixupPosition();
629 }
630 }
631 emit preferredHighlightBeginChanged();
632}
633
634qreal QQuickItemView::preferredHighlightEnd() const
635{
636 Q_D(const QQuickItemView);
637 return d->highlightRangeEnd;
638}
639
640void QQuickItemView::setPreferredHighlightEnd(qreal end)
641{
642 Q_D(QQuickItemView);
643 d->highlightRangeEndValid = true;
644 if (d->highlightRangeEnd == end)
645 return;
646 d->highlightRangeEnd = end;
647 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
648 if (isComponentComplete()) {
649 d->updateViewport();
650 if (!isMoving() && !isFlicking()) {
651 d->moveReason = QQuickItemViewPrivate::Other;
652 d->fixupPosition();
653 }
654 }
655 emit preferredHighlightEndChanged();
656}
657
658void QQuickItemView::resetPreferredHighlightEnd()
659{
660 Q_D(QQuickItemView);
661 d->highlightRangeEndValid = false;
662 if (d->highlightRangeEnd == 0)
663 return;
664 d->highlightRangeEnd = 0;
665 if (isComponentComplete()) {
666 d->updateViewport();
667 if (!isMoving() && !isFlicking()) {
668 d->moveReason = QQuickItemViewPrivate::Other;
669 d->fixupPosition();
670 }
671 }
672 emit preferredHighlightEndChanged();
673}
674
675int QQuickItemView::highlightMoveDuration() const
676{
677 Q_D(const QQuickItemView);
678 return d->highlightMoveDuration;
679}
680
681void QQuickItemView::setHighlightMoveDuration(int duration)
682{
683 Q_D(QQuickItemView);
684 if (d->highlightMoveDuration != duration) {
685 d->highlightMoveDuration = duration;
686 emit highlightMoveDurationChanged();
687 }
688}
689
690bool QQuickItemView::reuseItems() const
691{
692 return bool(d_func()->reusableFlag == QQmlDelegateModel::Reusable);
693}
694
695void QQuickItemView::setReuseItems(bool reuse)
696{
697 Q_D(QQuickItemView);
698 if (reuseItems() == reuse)
699 return;
700
701 d->reusableFlag = reuse ? QQmlDelegateModel::Reusable : QQmlDelegateModel::NotReusable;
702
703 if (!reuse && d->model) {
704 // When we're told to not reuse items, we
705 // immediately, as documented, drain the pool.
706 d->model->drainReusableItemsPool(maxPoolTime: 0);
707 }
708
709 emit reuseItemsChanged();
710}
711
712#if QT_CONFIG(quick_viewtransitions)
713QQuickTransition *QQuickItemView::populateTransition() const
714{
715 Q_D(const QQuickItemView);
716 return d->transitioner ? d->transitioner->populateTransition : nullptr;
717}
718
719void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
720{
721 Q_D(QQuickItemView);
722 d->createTransitioner();
723 if (d->transitioner->populateTransition != transition) {
724 d->transitioner->populateTransition = transition;
725 emit populateTransitionChanged();
726 }
727}
728
729QQuickTransition *QQuickItemView::addTransition() const
730{
731 Q_D(const QQuickItemView);
732 return d->transitioner ? d->transitioner->addTransition : nullptr;
733}
734
735void QQuickItemView::setAddTransition(QQuickTransition *transition)
736{
737 Q_D(QQuickItemView);
738 d->createTransitioner();
739 if (d->transitioner->addTransition != transition) {
740 d->transitioner->addTransition = transition;
741 emit addTransitionChanged();
742 }
743}
744
745QQuickTransition *QQuickItemView::addDisplacedTransition() const
746{
747 Q_D(const QQuickItemView);
748 return d->transitioner ? d->transitioner->addDisplacedTransition : nullptr;
749}
750
751void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
752{
753 Q_D(QQuickItemView);
754 d->createTransitioner();
755 if (d->transitioner->addDisplacedTransition != transition) {
756 d->transitioner->addDisplacedTransition = transition;
757 emit addDisplacedTransitionChanged();
758 }
759}
760
761QQuickTransition *QQuickItemView::moveTransition() const
762{
763 Q_D(const QQuickItemView);
764 return d->transitioner ? d->transitioner->moveTransition : nullptr;
765}
766
767void QQuickItemView::setMoveTransition(QQuickTransition *transition)
768{
769 Q_D(QQuickItemView);
770 d->createTransitioner();
771 if (d->transitioner->moveTransition != transition) {
772 d->transitioner->moveTransition = transition;
773 emit moveTransitionChanged();
774 }
775}
776
777QQuickTransition *QQuickItemView::moveDisplacedTransition() const
778{
779 Q_D(const QQuickItemView);
780 return d->transitioner ? d->transitioner->moveDisplacedTransition : nullptr;
781}
782
783void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
784{
785 Q_D(QQuickItemView);
786 d->createTransitioner();
787 if (d->transitioner->moveDisplacedTransition != transition) {
788 d->transitioner->moveDisplacedTransition = transition;
789 emit moveDisplacedTransitionChanged();
790 }
791}
792
793QQuickTransition *QQuickItemView::removeTransition() const
794{
795 Q_D(const QQuickItemView);
796 return d->transitioner ? d->transitioner->removeTransition : nullptr;
797}
798
799void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
800{
801 Q_D(QQuickItemView);
802 d->createTransitioner();
803 if (d->transitioner->removeTransition != transition) {
804 d->transitioner->removeTransition = transition;
805 emit removeTransitionChanged();
806 }
807}
808
809QQuickTransition *QQuickItemView::removeDisplacedTransition() const
810{
811 Q_D(const QQuickItemView);
812 return d->transitioner ? d->transitioner->removeDisplacedTransition : nullptr;
813}
814
815void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
816{
817 Q_D(QQuickItemView);
818 d->createTransitioner();
819 if (d->transitioner->removeDisplacedTransition != transition) {
820 d->transitioner->removeDisplacedTransition = transition;
821 emit removeDisplacedTransitionChanged();
822 }
823}
824
825QQuickTransition *QQuickItemView::displacedTransition() const
826{
827 Q_D(const QQuickItemView);
828 return d->transitioner ? d->transitioner->displacedTransition : nullptr;
829}
830
831void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
832{
833 Q_D(QQuickItemView);
834 d->createTransitioner();
835 if (d->transitioner->displacedTransition != transition) {
836 d->transitioner->displacedTransition = transition;
837 emit displacedTransitionChanged();
838 }
839}
840#endif
841
842void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
843{
844 if (!isValid())
845 return;
846 if (mode < QQuickItemView::Beginning || mode > QQuickItemView::SnapPosition)
847 return;
848
849 Q_Q(QQuickItemView);
850 q->cancelFlick();
851 applyPendingChanges();
852 const int modelCount = model->count();
853 int idx = qMax(a: qMin(a: index, b: modelCount - 1), b: 0);
854
855 const auto viewSize = size();
856 qreal pos = isContentFlowReversed() ? -position() - viewSize : position();
857 FxViewItem *item = visibleItem(modelIndex: idx);
858 qreal maxExtent = calculatedMaxExtent();
859 if (!item) {
860 qreal itemPos = positionAt(index: idx);
861 changedVisibleIndex(newIndex: idx);
862 // save the currently visible items in case any of them end up visible again
863 const QList<FxViewItem *> oldVisible = visibleItems;
864 visibleItems.clear();
865 setPosition(qMin(a: itemPos, b: maxExtent));
866 // now release the reference to all the old visible items.
867 for (FxViewItem *item : oldVisible)
868 releaseItem(item, reusableFlag);
869 item = visibleItem(modelIndex: idx);
870 }
871 if (item) {
872 const bool stickyHeader = hasStickyHeader();
873 const bool stickyFooter = hasStickyFooter();
874 const qreal stickyHeaderSize = stickyHeader ? headerSize() : 0;
875 const qreal stickyFooterSize = stickyFooter ? footerSize() : 0;
876
877 const qreal itemPos = item->position();
878 switch (mode) {
879 case QQuickItemView::Beginning:
880 pos = itemPos;
881 if (header && (index < 0 || stickyHeader))
882 pos -= headerSize();
883 break;
884 case QQuickItemView::Center:
885 pos = itemPos - (viewSize - item->size())/2;
886 break;
887 case QQuickItemView::End:
888 pos = itemPos - viewSize + item->size();
889 if (footer && (index >= modelCount || stickyFooter))
890 pos += footerSize();
891 break;
892 case QQuickItemView::Visible:
893 if (itemPos > pos + viewSize - stickyFooterSize)
894 pos = item->endPosition() - viewSize + stickyFooterSize;
895 else if (item->endPosition() <= pos - stickyHeaderSize)
896 pos = itemPos - stickyHeaderSize;
897 break;
898 case QQuickItemView::Contain:
899 if (item->endPosition() >= pos + viewSize + stickyFooterSize)
900 pos = itemPos - viewSize + item->size() + stickyFooterSize;
901 if (itemPos - stickyHeaderSize < pos)
902 pos = itemPos - stickyHeaderSize;
903 break;
904 case QQuickItemView::SnapPosition:
905 pos = itemPos - highlightRangeStart - stickyHeaderSize;
906 break;
907 }
908 pos = qMin(a: pos, b: maxExtent);
909 qreal minExtent = calculatedMinExtent();
910 pos = qMax(a: pos, b: minExtent);
911 moveReason = QQuickItemViewPrivate::Other;
912 setPosition(pos);
913
914 if (highlight) {
915 if (autoHighlight)
916 resetHighlightPosition();
917 updateHighlight();
918 }
919 }
920 fixupPosition();
921}
922
923void QQuickItemView::positionViewAtIndex(int index, int mode)
924{
925 Q_D(QQuickItemView);
926 if (!d->isValid() || index < 0 || index >= d->model->count())
927 return;
928 d->positionViewAtIndex(index, mode);
929}
930
931
932void QQuickItemView::positionViewAtBeginning()
933{
934 Q_D(QQuickItemView);
935 if (!d->isValid())
936 return;
937 d->positionViewAtIndex(index: -1, mode: Beginning);
938}
939
940void QQuickItemView::positionViewAtEnd()
941{
942 Q_D(QQuickItemView);
943 if (!d->isValid())
944 return;
945 d->positionViewAtIndex(index: d->model->count(), mode: End);
946}
947
948static FxViewItem * fxViewItemAtPosition(const QList<FxViewItem *> &items, qreal x, qreal y)
949{
950 for (FxViewItem *item : items) {
951 if (item->contains(x, y))
952 return item;
953 }
954 return nullptr;
955}
956
957int QQuickItemView::indexAt(qreal x, qreal y) const
958{
959 Q_D(const QQuickItemView);
960 const FxViewItem *item = fxViewItemAtPosition(items: d->visibleItems, x, y);
961 return item ? item->index : -1;
962}
963
964QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
965{
966 Q_D(const QQuickItemView);
967 const FxViewItem *item = fxViewItemAtPosition(items: d->visibleItems, x, y);
968 return item ? item->item : nullptr;
969}
970
971QQuickItem *QQuickItemView::itemAtIndex(int index) const
972{
973 Q_D(const QQuickItemView);
974 const FxViewItem *item = d->visibleItem(modelIndex: index);
975 return item ? item->item : nullptr;
976}
977
978void QQuickItemView::forceLayout()
979{
980 Q_D(QQuickItemView);
981 if (isComponentComplete() && (d->currentChanges.hasPendingChanges() || d->forceLayout))
982 d->layout();
983}
984
985void QQuickItemViewPrivate::applyPendingChanges()
986{
987 Q_Q(QQuickItemView);
988 if (q->isComponentComplete() && currentChanges.hasPendingChanges())
989 layout();
990}
991
992int QQuickItemViewPrivate::findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector<QQmlChangeSet::Change> &changes) const
993{
994 for (int i=0; i<changes.size(); i++) {
995 for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
996 if (changes[i].moveKey(index: j) == key)
997 return j;
998 }
999 }
1000 return -1;
1001}
1002
1003qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
1004{
1005 Q_Q(const QQuickItemView);
1006
1007 qreal highlightStart;
1008 qreal highlightEnd;
1009 qreal endPositionFirstItem = 0;
1010 qreal extent = -startPosition() + axisData.startMargin;
1011 if (isContentFlowReversed()) {
1012 if (model && model->count())
1013 endPositionFirstItem = positionAt(index: model->count()-1);
1014 else
1015 extent += headerSize();
1016 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1017 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1018 extent += footerSize();
1019 qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
1020 if (extent < maxExtentAlongAxis)
1021 extent = maxExtentAlongAxis;
1022 } else {
1023 endPositionFirstItem = endPositionAt(index: 0);
1024 highlightStart = highlightRangeStart;
1025 highlightEnd = highlightRangeEnd;
1026 extent += headerSize();
1027 }
1028 if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1029 extent += highlightStart;
1030 FxViewItem *firstItem = visibleItem(modelIndex: 0);
1031 if (firstItem)
1032 extent -= firstItem->sectionSize();
1033 extent = isContentFlowReversed()
1034 ? qMin(a: extent, b: endPositionFirstItem + highlightEnd)
1035 : qMax(a: extent, b: -(endPositionFirstItem - highlightEnd));
1036 }
1037 return extent;
1038}
1039
1040qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
1041{
1042 Q_Q(const QQuickItemView);
1043
1044 qreal highlightStart;
1045 qreal highlightEnd;
1046 qreal lastItemPosition = 0;
1047 qreal extent = 0;
1048 if (isContentFlowReversed()) {
1049 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1050 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1051 lastItemPosition = endPosition();
1052 } else {
1053 highlightStart = highlightRangeStart;
1054 highlightEnd = highlightRangeEnd;
1055 if (model && model->count())
1056 lastItemPosition = positionAt(index: model->count()-1);
1057 }
1058 if (!model || !model->count()) {
1059 if (!isContentFlowReversed())
1060 maxExtent = header ? -headerSize() : 0;
1061 extent += forXAxis ? q->width() : q->height();
1062 } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1063 extent = -(lastItemPosition - highlightStart);
1064 if (highlightEnd != highlightStart) {
1065 extent = isContentFlowReversed()
1066 ? qMax(a: extent, b: -(endPosition() - highlightEnd))
1067 : qMin(a: extent, b: -(endPosition() - highlightEnd));
1068 }
1069 } else {
1070 extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
1071 }
1072 if (isContentFlowReversed()) {
1073 extent -= headerSize();
1074 extent -= axisData.endMargin;
1075 } else {
1076 extent -= footerSize();
1077 extent -= axisData.endMargin;
1078 qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
1079 if (extent > minExtentAlongAxis)
1080 extent = minExtentAlongAxis;
1081 }
1082
1083 return extent;
1084}
1085
1086qreal QQuickItemViewPrivate::calculatedMinExtent() const
1087{
1088 Q_Q(const QQuickItemView);
1089 qreal minExtent;
1090 if (layoutOrientation() == Qt::Vertical)
1091 minExtent = isContentFlowReversed() ? q->maxYExtent() - size(): -q->minYExtent();
1092 else
1093 minExtent = isContentFlowReversed() ? q->maxXExtent() - size(): -q->minXExtent();
1094 return minExtent;
1095
1096}
1097
1098qreal QQuickItemViewPrivate::calculatedMaxExtent() const
1099{
1100 Q_Q(const QQuickItemView);
1101 qreal maxExtent;
1102 if (layoutOrientation() == Qt::Vertical)
1103 maxExtent = isContentFlowReversed() ? q->minYExtent() - size(): -q->maxYExtent();
1104 else
1105 maxExtent = isContentFlowReversed() ? q->minXExtent() - size(): -q->maxXExtent();
1106 return maxExtent;
1107}
1108
1109void QQuickItemViewPrivate::connectModel(QQuickItemView *q, QQmlDelegateModelPointer *model)
1110{
1111 QQmlInstanceModel *instanceModel = model->instanceModel();
1112 if (!instanceModel)
1113 return;
1114
1115 bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1116 QObject::connect(sender: instanceModel, signal: &QQmlInstanceModel::createdItem,
1117 context: q, slot: &QQuickItemView::createdItem);
1118 QObject::connect(sender: instanceModel, signal: &QQmlInstanceModel::initItem,
1119 context: q, slot: &QQuickItemView::initItem);
1120 QObject::connect(sender: instanceModel, signal: &QQmlInstanceModel::destroyingItem,
1121 context: q, slot: &QQuickItemView::destroyingItem);
1122 if (QQmlDelegateModel *delegateModel = model->delegateModel()) {
1123 QObject::connect(sender: delegateModel, signal: &QQmlInstanceModel::itemPooled,
1124 context: q, slot: &QQuickItemView::onItemPooled);
1125 QObject::connect(sender: delegateModel, signal: &QQmlInstanceModel::itemReused,
1126 context: q, slot: &QQuickItemView::onItemReused);
1127 }
1128
1129 if (q->isComponentComplete()) {
1130 updateSectionCriteria();
1131 refill();
1132 /* Setting currentIndex to -2 ensures that we always enter the "currentIndex changed"
1133 code path in setCurrentIndex, updating bindings depending on currentIndex.*/
1134 currentIndex = -2;
1135 q->setCurrentIndex(instanceModel->count() > 0 ? 0 : -1);
1136 updateViewport();
1137
1138#if QT_CONFIG(quick_viewtransitions)
1139 if (transitioner && transitioner->populateTransition) {
1140 transitioner->setPopulateTransitionEnabled(true);
1141 forceLayoutPolish();
1142 }
1143#endif
1144 }
1145
1146 QObject::connect(sender: instanceModel, signal: &QQmlInstanceModel::modelUpdated,
1147 context: q, slot: &QQuickItemView::modelUpdated);
1148 if (QQmlDelegateModel *dataModel = model->delegateModel()) {
1149 QObjectPrivate::connect(
1150 sender: dataModel, signal: &QQmlDelegateModel::delegateChanged,
1151 receiverPrivate: this, slot: &QQuickItemViewPrivate::applyDelegateChange);
1152 QObjectPrivate::connect(
1153 sender: dataModel, signal: &QQmlDelegateModel::delegateModelAccessChanged,
1154 receiverPrivate: this, slot: &QQuickItemViewPrivate::applyDelegateModelAccessChange);
1155 }
1156
1157 emitCountChanged();
1158}
1159
1160void QQuickItemViewPrivate::disconnectModel(QQuickItemView *q, QQmlDelegateModelPointer *model)
1161{
1162 QQmlInstanceModel *instanceModel = model->instanceModel();
1163 if (!instanceModel)
1164 return;
1165
1166
1167 QObject::disconnect(sender: instanceModel, signal: &QQmlInstanceModel::modelUpdated,
1168 receiver: q, slot: &QQuickItemView::modelUpdated);
1169 QObject::disconnect(sender: instanceModel, signal: &QQmlInstanceModel::initItem,
1170 receiver: q, slot: &QQuickItemView::initItem);
1171 QObject::disconnect(sender: instanceModel, signal: &QQmlInstanceModel::createdItem,
1172 receiver: q, slot: &QQuickItemView::createdItem);
1173 QObject::disconnect(sender: instanceModel, signal: &QQmlInstanceModel::destroyingItem,
1174 receiver: q, slot: &QQuickItemView::destroyingItem);
1175 if (QQmlDelegateModel *delegateModel = model->delegateModel()) {
1176 QObject::disconnect(sender: delegateModel, signal: &QQmlInstanceModel::itemPooled,
1177 receiver: q, slot: &QQuickItemView::onItemPooled);
1178 QObject::disconnect(sender: delegateModel, signal: &QQmlInstanceModel::itemReused,
1179 receiver: q, slot: &QQuickItemView::onItemReused);
1180 QObjectPrivate::disconnect(
1181 sender: delegateModel, signal: &QQmlDelegateModel::delegateChanged,
1182 receiverPrivate: this, slot: &QQuickItemViewPrivate::applyDelegateChange);
1183 QObjectPrivate::disconnect(
1184 sender: delegateModel, signal: &QQmlDelegateModel::delegateModelAccessChanged,
1185 receiverPrivate: this, slot: &QQuickItemViewPrivate::applyDelegateModelAccessChange);
1186 }
1187}
1188
1189void QQuickItemViewPrivate::applyDelegateChange()
1190{
1191 Q_Q(QQuickItemView);
1192
1193 QQmlDelegateModel::applyDelegateChangeOnView(q, d: this);
1194
1195 releaseVisibleItems(reusableFlag: QQmlDelegateModel::NotReusable);
1196 releaseCurrentItem(reusableFlag: QQmlDelegateModel::NotReusable);
1197 updateSectionCriteria();
1198 refill();
1199 moveReason = QQuickItemViewPrivate::SetIndex;
1200 updateCurrent(modelIndex: currentIndex);
1201 if (highlight && currentItem) {
1202 if (autoHighlight)
1203 resetHighlightPosition();
1204 updateTrackedItem();
1205 }
1206 moveReason = QQuickItemViewPrivate::Other;
1207 updateViewport();
1208}
1209
1210// for debugging only
1211void QQuickItemViewPrivate::checkVisible() const
1212{
1213 int skip = 0;
1214 for (int i = 0; i < visibleItems.size(); ++i) {
1215 FxViewItem *item = visibleItems.at(i);
1216 if (item->index == -1) {
1217 ++skip;
1218 } else if (item->index != visibleIndex + i - skip) {
1219 qFatal(msg: "index %d %d %d", visibleIndex, i, item->index);
1220 }
1221 }
1222}
1223
1224// for debugging only
1225void QQuickItemViewPrivate::showVisibleItems() const
1226{
1227 qDebug() << "Visible items:";
1228 for (FxViewItem *item : visibleItems) {
1229 qDebug() << "\t" << item->index
1230 << item->item->objectName()
1231 << item->position();
1232 }
1233}
1234
1235// Simplifies debugging of count.
1236void QQuickItemViewPrivate::emitCountChanged()
1237{
1238 Q_Q(QQuickItemView);
1239 qCDebug(lcCount).nospace() << "about to emit countChanged for " << q << "; count changed to " << q->count();
1240 emit q->countChanged();
1241}
1242
1243void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
1244 const QRectF &oldGeometry)
1245{
1246 Q_Q(QQuickItemView);
1247 QQuickFlickablePrivate::itemGeometryChanged(item, change, oldGeometry);
1248 if (!q->isComponentComplete())
1249 return;
1250
1251 if (header && header->item == item) {
1252 updateHeader();
1253 markExtentsDirty();
1254 updateViewport();
1255 if (!q->isMoving() && !q->isFlicking())
1256 fixupPosition();
1257 } else if (footer && footer->item == item) {
1258 updateFooter();
1259 markExtentsDirty();
1260 updateViewport();
1261 if (!q->isMoving() && !q->isFlicking())
1262 fixupPosition();
1263 }
1264
1265 if (currentItem && currentItem->item == item) {
1266 // don't allow item movement transitions to trigger a re-layout and
1267 // start new transitions
1268 bool prevInLayout = inLayout;
1269#if QT_CONFIG(quick_viewtransitions)
1270 if (!inLayout) {
1271 FxViewItem *actualItem = transitioner ? visibleItem(modelIndex: currentIndex) : nullptr;
1272 if (actualItem && actualItem->transitionRunning())
1273 inLayout = true;
1274 }
1275#endif
1276 updateHighlight();
1277 inLayout = prevInLayout;
1278 }
1279
1280 if (trackedItem && trackedItem->item == item)
1281 q->trackedPositionChanged();
1282}
1283
1284void QQuickItemView::destroyRemoved()
1285{
1286 Q_D(QQuickItemView);
1287
1288#if QT_CONFIG(quick_viewtransitions)
1289 bool hasRemoveTransition = false;
1290 bool hasRemoveTransitionAsTarget = false;
1291 if (d->transitioner) {
1292 hasRemoveTransition = d->transitioner->canTransition(type: QQuickItemViewTransitioner::RemoveTransition, asTarget: false);
1293 hasRemoveTransitionAsTarget = d->transitioner->canTransition(type: QQuickItemViewTransitioner::RemoveTransition, asTarget: true);
1294 }
1295#endif
1296
1297 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1298 it != d->visibleItems.end();) {
1299 FxViewItem *item = *it;
1300 if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) {
1301#if QT_CONFIG(quick_viewtransitions)
1302 if (hasRemoveTransitionAsTarget) {
1303 // don't remove from visibleItems until next layout()
1304 d->runDelayedRemoveTransition = true;
1305 QObject::disconnect(sender: item->attached, SIGNAL(delayRemoveChanged()), receiver: this, SLOT(destroyRemoved()));
1306 ++it;
1307 } else {
1308 if (hasRemoveTransition)
1309 d->runDelayedRemoveTransition = true;
1310#endif
1311 d->releaseItem(item, reusableFlag: d->reusableFlag);
1312 it = d->visibleItems.erase(pos: it);
1313#if QT_CONFIG(quick_viewtransitions)
1314 }
1315#endif
1316 } else {
1317 ++it;
1318 }
1319 }
1320
1321 // Correct the positioning of the items
1322 d->forceLayoutPolish();
1323}
1324
1325void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
1326{
1327 Q_D(QQuickItemView);
1328 if (reset) {
1329 cancelFlick();
1330#if QT_CONFIG(quick_viewtransitions)
1331 if (d->transitioner)
1332 d->transitioner->setPopulateTransitionEnabled(true);
1333#endif
1334 d->moveReason = QQuickItemViewPrivate::SetIndex;
1335 d->regenerate();
1336 if (d->highlight && d->currentItem) {
1337 if (d->autoHighlight)
1338 d->resetHighlightPosition();
1339 d->updateTrackedItem();
1340 }
1341 d->moveReason = QQuickItemViewPrivate::Other;
1342 d->emitCountChanged();
1343#if QT_CONFIG(quick_viewtransitions)
1344 if (d->transitioner && d->transitioner->populateTransition)
1345 d->forceLayoutPolish();
1346#endif
1347 } else {
1348 if (d->inLayout) {
1349 d->bufferedChanges.prepare(currentIndex: d->currentIndex, count: d->itemCount);
1350 d->bufferedChanges.applyChanges(changeSet);
1351 } else {
1352 if (d->bufferedChanges.hasPendingChanges()) {
1353 d->currentChanges.applyBufferedChanges(other: d->bufferedChanges);
1354 d->bufferedChanges.reset();
1355 }
1356 d->currentChanges.prepare(currentIndex: d->currentIndex, count: d->itemCount);
1357 d->currentChanges.applyChanges(changeSet);
1358 }
1359 polish();
1360 }
1361}
1362
1363void QQuickItemView::animStopped()
1364{
1365 Q_D(QQuickItemView);
1366 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1367 d->refillOrLayout();
1368 if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1369 d->updateHighlight();
1370}
1371
1372
1373void QQuickItemView::trackedPositionChanged()
1374{
1375 Q_D(QQuickItemView);
1376 if (!d->trackedItem || !d->currentItem)
1377 return;
1378
1379 if (d->inLayout) {
1380 polish();
1381 return;
1382 }
1383
1384 const bool needMoveToTrackHighlight = d->autoHighlight || d->highlightRange != NoHighlightRange;
1385
1386 if (d->moveReason == QQuickItemViewPrivate::SetIndex && needMoveToTrackHighlight) {
1387 qreal trackedPos = d->trackedItem->position();
1388 qreal trackedSize = d->trackedItem->size();
1389 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1390 qreal pos = viewPos;
1391 if (d->haveHighlightRange) {
1392 if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1393 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1394 if (trackedPos < pos + d->highlightRangeStart)
1395 pos = trackedPos - d->highlightRangeStart;
1396 if (d->highlightRange != StrictlyEnforceRange) {
1397 qreal maxExtent = d->calculatedMaxExtent();
1398 if (pos > maxExtent)
1399 pos = maxExtent;
1400 qreal minExtent = d->calculatedMinExtent();
1401 if (pos < minExtent)
1402 pos = minExtent;
1403 }
1404 } else {
1405 if (d->trackedItem != d->currentItem) {
1406 // also make section header visible
1407 trackedPos -= d->currentItem->sectionSize();
1408 trackedSize += d->currentItem->sectionSize();
1409 }
1410 qreal trackedEndPos = d->trackedItem->endPosition();
1411 qreal toItemPos = d->currentItem->position();
1412 qreal toItemEndPos = d->currentItem->endPosition();
1413 if (d->showHeaderForIndex(index: d->currentIndex)) {
1414 qreal startOffset = -d->contentStartOffset();
1415 trackedPos -= startOffset;
1416 trackedEndPos -= startOffset;
1417 toItemPos -= startOffset;
1418 toItemEndPos -= startOffset;
1419 } else if (d->showFooterForIndex(index: d->currentIndex)) {
1420 qreal endOffset = d->footerSize();
1421 if (d->layoutOrientation() == Qt::Vertical) {
1422 if (d->isContentFlowReversed())
1423 endOffset += d->vData.startMargin;
1424 else
1425 endOffset += d->vData.endMargin;
1426 } else {
1427 if (d->isContentFlowReversed())
1428 endOffset += d->hData.startMargin;
1429 else
1430 endOffset += d->hData.endMargin;
1431 }
1432 trackedPos += endOffset;
1433 trackedEndPos += endOffset;
1434 toItemPos += endOffset;
1435 toItemEndPos += endOffset;
1436 }
1437
1438 if (trackedEndPos >= viewPos + d->size()
1439 && toItemEndPos >= viewPos + d->size()) {
1440 if (trackedEndPos <= toItemEndPos) {
1441 pos = trackedEndPos - d->size();
1442 if (trackedSize > d->size())
1443 pos = trackedPos;
1444 } else {
1445 pos = toItemEndPos - d->size();
1446 if (d->currentItem->size() > d->size())
1447 pos = d->currentItem->position();
1448 }
1449 }
1450 if (trackedPos < pos && toItemPos < pos)
1451 pos = qMax(a: trackedPos, b: toItemPos);
1452 }
1453 if (viewPos != pos) {
1454 d->calcVelocity = true;
1455 d->setPosition(pos);
1456 d->calcVelocity = false;
1457 }
1458 }
1459}
1460
1461void QQuickItemView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1462{
1463 Q_D(QQuickItemView);
1464 d->markExtentsDirty();
1465 if (isComponentComplete() && (d->isValid() || !d->visibleItems.isEmpty()))
1466 d->forceLayoutPolish();
1467 QQuickFlickable::geometryChange(newGeometry, oldGeometry);
1468}
1469
1470qreal QQuickItemView::minYExtent() const
1471{
1472 Q_D(const QQuickItemView);
1473 if (d->layoutOrientation() == Qt::Horizontal)
1474 return QQuickFlickable::minYExtent();
1475
1476 if (d->vData.minExtentDirty) {
1477 d->minExtent = d->minExtentForAxis(axisData: d->vData, forXAxis: false);
1478 d->vData.minExtentDirty = false;
1479 }
1480
1481 return d->minExtent;
1482}
1483
1484qreal QQuickItemView::maxYExtent() const
1485{
1486 Q_D(const QQuickItemView);
1487 if (d->layoutOrientation() == Qt::Horizontal)
1488 return QQuickFlickable::maxYExtent();
1489
1490 if (d->vData.maxExtentDirty) {
1491 d->maxExtent = d->maxExtentForAxis(axisData: d->vData, forXAxis: false);
1492 d->vData.maxExtentDirty = false;
1493 }
1494
1495 return d->maxExtent;
1496}
1497
1498qreal QQuickItemView::minXExtent() const
1499{
1500 Q_D(const QQuickItemView);
1501 if (d->layoutOrientation() == Qt::Vertical)
1502 return QQuickFlickable::minXExtent();
1503
1504 if (d->hData.minExtentDirty) {
1505 d->minExtent = d->minExtentForAxis(axisData: d->hData, forXAxis: true);
1506 d->hData.minExtentDirty = false;
1507 }
1508
1509 return d->minExtent;
1510}
1511
1512qreal QQuickItemView::maxXExtent() const
1513{
1514 Q_D(const QQuickItemView);
1515 if (d->layoutOrientation() == Qt::Vertical)
1516 return QQuickFlickable::maxXExtent();
1517
1518 if (d->hData.maxExtentDirty) {
1519 d->maxExtent = d->maxExtentForAxis(axisData: d->hData, forXAxis: true);
1520 d->hData.maxExtentDirty = false;
1521 }
1522
1523 return d->maxExtent;
1524}
1525
1526void QQuickItemView::setContentX(qreal pos)
1527{
1528 Q_D(QQuickItemView);
1529 // Positioning the view manually should override any current movement state
1530 d->moveReason = QQuickItemViewPrivate::Other;
1531 QQuickFlickable::setContentX(pos);
1532}
1533
1534void QQuickItemView::setContentY(qreal pos)
1535{
1536 Q_D(QQuickItemView);
1537 // Positioning the view manually should override any current movement state
1538 d->moveReason = QQuickItemViewPrivate::Other;
1539 QQuickFlickable::setContentY(pos);
1540}
1541
1542qreal QQuickItemView::originX() const
1543{
1544 Q_D(const QQuickItemView);
1545 if (d->layoutOrientation() == Qt::Horizontal
1546 && effectiveLayoutDirection() == Qt::RightToLeft
1547 && contentWidth() < width()) {
1548 return -d->lastPosition() - d->footerSize();
1549 }
1550 return QQuickFlickable::originX();
1551}
1552
1553qreal QQuickItemView::originY() const
1554{
1555 Q_D(const QQuickItemView);
1556 if (d->layoutOrientation() == Qt::Vertical
1557 && d->verticalLayoutDirection == QQuickItemView::BottomToTop
1558 && contentHeight() < height()) {
1559 return -d->lastPosition() - d->footerSize();
1560 }
1561 return QQuickFlickable::originY();
1562}
1563
1564void QQuickItemView::updatePolish()
1565{
1566 Q_D(QQuickItemView);
1567 QQuickFlickable::updatePolish();
1568 d->layout();
1569}
1570
1571void QQuickItemView::componentComplete()
1572{
1573 Q_D(QQuickItemView);
1574 if (d->model && d->ownModel)
1575 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
1576
1577 QQuickFlickable::componentComplete();
1578
1579 d->updateSectionCriteria();
1580 d->updateHeader();
1581 d->updateFooter();
1582 d->updateViewport();
1583 d->setPosition(d->contentStartOffset());
1584#if QT_CONFIG(quick_viewtransitions)
1585 if (d->transitioner)
1586 d->transitioner->setPopulateTransitionEnabled(true);
1587#endif
1588
1589 if (d->isValid()) {
1590 d->refill();
1591 d->moveReason = QQuickItemViewPrivate::SetIndex;
1592 if (d->currentIndex < 0 && !d->currentIndexCleared)
1593 d->updateCurrent(modelIndex: 0);
1594 else
1595 d->updateCurrent(modelIndex: d->currentIndex);
1596 if (d->highlight && d->currentItem) {
1597 if (d->autoHighlight)
1598 d->resetHighlightPosition();
1599 d->updateTrackedItem();
1600 }
1601 d->moveReason = QQuickItemViewPrivate::Other;
1602 d->fixupPosition();
1603 }
1604 if (d->model && d->model->count())
1605 d->emitCountChanged();
1606}
1607
1608
1609
1610QQuickItemViewPrivate::QQuickItemViewPrivate()
1611 : itemCount(0)
1612 , buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
1613 , displayMarginBeginning(0), displayMarginEnd(0)
1614 , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
1615 , visibleIndex(0)
1616 , currentIndex(-1), currentItem(nullptr)
1617 , trackedItem(nullptr), requestedIndex(-1)
1618 , highlightComponent(nullptr), highlight(nullptr)
1619 , highlightRange(QQuickItemView::NoHighlightRange)
1620 , highlightRangeStart(0), highlightRangeEnd(0)
1621 , highlightMoveDuration(150)
1622 , headerComponent(nullptr), header(nullptr), footerComponent(nullptr), footer(nullptr)
1623#if QT_CONFIG(quick_viewtransitions)
1624 , transitioner(nullptr)
1625#endif
1626 , minExtent(0), maxExtent(0)
1627 , ownModel(false), wrap(false)
1628 , keyNavigationEnabled(true)
1629 , explicitKeyNavigationEnabled(false)
1630 , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1631 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1632 , fillCacheBuffer(false), inRequest(false)
1633#if QT_CONFIG(quick_viewtransitions)
1634 , runDelayedRemoveTransition(false)
1635#endif
1636 , delegateValidated(false)
1637 , isClearing(false)
1638 , explicitDelegate(false)
1639 , explicitDelegateModelAccess(false)
1640{
1641 bufferPause.addAnimationChangeListener(listener: this, QAbstractAnimationJob::Completion);
1642 bufferPause.setLoopCount(1);
1643 bufferPause.setDuration(16);
1644}
1645
1646static const QQuickItemPrivate::ChangeTypes itemChangeListenerTypes = QQuickItemPrivate::Destroyed;
1647
1648QQuickItemViewPrivate::~QQuickItemViewPrivate()
1649{
1650#if QT_CONFIG(quick_viewtransitions)
1651 if (transitioner)
1652 transitioner->setChangeListener(nullptr);
1653 delete transitioner;
1654#endif
1655}
1656
1657bool QQuickItemViewPrivate::isValid() const
1658{
1659 return model && model->count() && model->isValid();
1660}
1661
1662qreal QQuickItemViewPrivate::position() const
1663{
1664 Q_Q(const QQuickItemView);
1665 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1666}
1667
1668qreal QQuickItemViewPrivate::size() const
1669{
1670 Q_Q(const QQuickItemView);
1671 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1672}
1673
1674qreal QQuickItemViewPrivate::startPosition() const
1675{
1676 return isContentFlowReversed() ? -lastPosition() : originPosition();
1677}
1678
1679qreal QQuickItemViewPrivate::endPosition() const
1680{
1681 return isContentFlowReversed() ? -originPosition() : lastPosition();
1682}
1683
1684qreal QQuickItemViewPrivate::contentStartOffset() const
1685{
1686 qreal pos = -headerSize();
1687 if (layoutOrientation() == Qt::Vertical) {
1688 if (isContentFlowReversed())
1689 pos -= vData.endMargin;
1690 else
1691 pos -= vData.startMargin;
1692 } else {
1693 if (isContentFlowReversed())
1694 pos -= hData.endMargin;
1695 else
1696 pos -= hData.startMargin;
1697 }
1698 return pos;
1699}
1700
1701int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1702{
1703 for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
1704 auto item = *it;
1705 if (item->index != -1)
1706 return item->index;
1707 }
1708 return defaultValue;
1709}
1710
1711FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1712 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.size()) {
1713 for (int i = modelIndex - visibleIndex; i < visibleItems.size(); ++i) {
1714 FxViewItem *item = visibleItems.at(i);
1715 if (item->index == modelIndex)
1716 return item;
1717 }
1718 }
1719 return nullptr;
1720}
1721
1722FxViewItem *QQuickItemViewPrivate::firstItemInView() const {
1723 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1724 for (FxViewItem *item : visibleItems) {
1725 if (item->index != -1 && item->endPosition() > pos)
1726 return item;
1727 }
1728 return visibleItems.size() ? visibleItems.first() : 0;
1729}
1730
1731int QQuickItemViewPrivate::findLastIndexInView() const
1732{
1733 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1734 for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
1735 auto item = *it;
1736 if (item->index != -1 && item->position() <= viewEndPos)
1737 return item->index;
1738 }
1739 return -1;
1740}
1741
1742// Map a model index to visibleItems list index.
1743// These may differ if removed items are still present in the visible list,
1744// e.g. doing a removal animation
1745int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1746{
1747 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.size())
1748 return -1;
1749 for (int i = 0; i < visibleItems.size(); ++i) {
1750 FxViewItem *item = visibleItems.at(i);
1751 if (item->index == modelIndex)
1752 return i;
1753 if (item->index > modelIndex)
1754 return -1;
1755 }
1756 return -1; // Not in visibleList
1757}
1758
1759void QQuickItemViewPrivate::init()
1760{
1761 Q_Q(QQuickItemView);
1762 q->setFlag(flag: QQuickItem::ItemIsFocusScope);
1763 QObject::connect(sender: q, SIGNAL(movementEnded()), receiver: q, SLOT(animStopped()));
1764 QObject::connect(sender: q, signal: &QQuickFlickable::interactiveChanged, context: q, slot: &QQuickItemView::keyNavigationEnabledChanged);
1765 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1766}
1767
1768void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1769{
1770 Q_Q(QQuickItemView);
1771 applyPendingChanges();
1772 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1773 if (currentItem) {
1774 if (currentItem->attached)
1775 currentItem->attached->setIsCurrentItem(false);
1776 releaseCurrentItem(reusableFlag);
1777 currentIndex = modelIndex;
1778 emit q->currentIndexChanged();
1779 emit q->currentItemChanged();
1780 updateHighlight();
1781 } else if (currentIndex != modelIndex) {
1782 currentIndex = modelIndex;
1783 emit q->currentIndexChanged();
1784 }
1785 return;
1786 }
1787
1788 if (currentItem && currentIndex == modelIndex) {
1789 updateHighlight();
1790 return;
1791 }
1792
1793 FxViewItem *oldCurrentItem = currentItem;
1794 int oldCurrentIndex = currentIndex;
1795 currentIndex = modelIndex;
1796 currentItem = createItem(modelIndex, incubationMode: QQmlIncubator::AsynchronousIfNested);
1797 if (oldCurrentItem && oldCurrentItem->attached && (!currentItem || oldCurrentItem->item != currentItem->item))
1798 oldCurrentItem->attached->setIsCurrentItem(false);
1799 if (currentItem) {
1800 currentItem->item->setFocus(true);
1801 if (currentItem->attached)
1802 currentItem->attached->setIsCurrentItem(true);
1803 initializeCurrentItem();
1804 }
1805
1806 updateHighlight();
1807 if (oldCurrentIndex != currentIndex)
1808 emit q->currentIndexChanged();
1809 if (oldCurrentItem != currentItem
1810 && (!oldCurrentItem || !currentItem || oldCurrentItem->item != currentItem->item))
1811 emit q->currentItemChanged();
1812 releaseItem(item: oldCurrentItem, reusableFlag);
1813}
1814
1815void QQuickItemViewPrivate::clear(bool onDestruction)
1816{
1817 Q_Q(QQuickItemView);
1818
1819 isClearing = true;
1820 auto cleanup = qScopeGuard(f: [this] { isClearing = false; });
1821
1822 currentChanges.reset();
1823 bufferedChanges.reset();
1824 timeline.clear();
1825
1826 releaseVisibleItems(reusableFlag: QQmlInstanceModel::NotReusable);
1827 visibleIndex = 0;
1828
1829#if QT_CONFIG(quick_viewtransitions)
1830 for (FxViewItem *item : std::as_const(t&: releasePendingTransition)) {
1831 item->releaseAfterTransition = false;
1832 releaseItem(item, reusableFlag: QQmlInstanceModel::NotReusable);
1833 }
1834 releasePendingTransition.clear();
1835#endif
1836
1837 const bool hadCurrentItem = currentItem != nullptr;
1838 releaseCurrentItem(reusableFlag: QQmlDelegateModel::NotReusable);
1839 if (hadCurrentItem)
1840 emit q->currentItemChanged();
1841 createHighlight(onDestruction);
1842 trackedItem = nullptr;
1843
1844 if (requestedIndex >= 0) {
1845 if (model)
1846 model->cancel(requestedIndex);
1847 requestedIndex = -1;
1848 }
1849
1850 markExtentsDirty();
1851 itemCount = 0;
1852}
1853
1854
1855void QQuickItemViewPrivate::mirrorChange()
1856{
1857 Q_Q(QQuickItemView);
1858 regenerate();
1859 emit q->effectiveLayoutDirectionChanged();
1860}
1861
1862void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1863{
1864 Q_Q(QQuickItemView);
1865 fillCacheBuffer = true;
1866 q->polish();
1867}
1868
1869void QQuickItemViewPrivate::refill()
1870{
1871 qreal s = qMax(a: size(), b: qreal(0.));
1872 const auto pos = position();
1873 if (isContentFlowReversed())
1874 refill(from: -pos - displayMarginBeginning-s, to: -pos + displayMarginEnd);
1875 else
1876 refill(from: pos - displayMarginBeginning, to: pos + displayMarginEnd+s);
1877}
1878
1879void QQuickItemViewPrivate::refill(qreal from, qreal to)
1880{
1881 Q_Q(QQuickItemView);
1882 if (!model || !model->isValid() || !q->isComponentComplete())
1883 return;
1884 if (q->size().isNull() && visibleItems.isEmpty())
1885 return;
1886 if (!model->count()) {
1887 updateHeader();
1888 updateFooter();
1889 updateViewport();
1890 return;
1891 }
1892
1893 do {
1894 bufferPause.stop();
1895 if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) {
1896 currentChanges.reset();
1897 bufferedChanges.reset();
1898 releaseVisibleItems(reusableFlag);
1899 }
1900
1901 int prevCount = itemCount;
1902 itemCount = model->count();
1903 qreal bufferFrom = from - buffer;
1904 qreal bufferTo = to + buffer;
1905 qreal fillFrom = from;
1906 qreal fillTo = to;
1907
1908 bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, doBuffer: false);
1909
1910 if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1911 if (added) {
1912 // We've already created a new delegate this frame.
1913 // Just schedule a buffer refill.
1914 bufferPause.start();
1915 } else {
1916 if (bufferMode & BufferAfter)
1917 fillTo = bufferTo;
1918 if (bufferMode & BufferBefore)
1919 fillFrom = bufferFrom;
1920 added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, doBuffer: true);
1921 }
1922 }
1923
1924 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1925
1926 if (added || removed) {
1927 markExtentsDirty();
1928 updateBeginningEnd();
1929 visibleItemsChanged();
1930 updateHeader();
1931 updateFooter();
1932 updateViewport();
1933 }
1934
1935 if (prevCount != itemCount)
1936 emitCountChanged();
1937 } while (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges());
1938 storeFirstVisibleItemPosition();
1939}
1940
1941void QQuickItemViewPrivate::regenerate(bool orientationChanged)
1942{
1943 Q_Q(QQuickItemView);
1944 if (q->isComponentComplete()) {
1945 if (orientationChanged) {
1946 delete header;
1947 header = nullptr;
1948 delete footer;
1949 footer = nullptr;
1950 }
1951 clear();
1952 updateHeader();
1953 updateFooter();
1954 updateViewport();
1955 setPosition(contentStartOffset());
1956 refill();
1957 updateCurrent(modelIndex: currentIndex);
1958 }
1959}
1960
1961void QQuickItemViewPrivate::updateViewport()
1962{
1963 Q_Q(QQuickItemView);
1964 qreal extra = headerSize() + footerSize();
1965 qreal contentSize = isValid() || !visibleItems.isEmpty() ? (endPosition() - startPosition()) : 0.0;
1966 if (layoutOrientation() == Qt::Vertical)
1967 q->setContentHeight(contentSize + extra);
1968 else
1969 q->setContentWidth(contentSize + extra);
1970}
1971
1972void QQuickItemViewPrivate::layout()
1973{
1974 Q_Q(QQuickItemView);
1975 if (inLayout)
1976 return;
1977
1978 inLayout = true;
1979
1980 // viewBounds contains bounds before any add/remove/move operation to the view
1981 QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
1982
1983 // We use isNull for the size check, because isEmpty returns true
1984 // if either dimension is negative, but apparently we support negative-sized
1985 // views (see tst_QQuickListView::resizeView).
1986 if ((!isValid() && !visibleItems.size()) || q->size().isNull()) {
1987 if (q->size().isNull() && hasPendingChanges()) {
1988 // count() refers to the number of items in the model, not in the view
1989 // (which is why we don't emit for the !visibleItems.size() case).
1990 // If there are pending model changes, emit countChanged in order to
1991 // support the use case of QTBUG-129165, where visible is bound to count > 0
1992 // and the ListView is in a layout with Layout.preferredHeight bound to
1993 // contentHeight. This ensures that a hidden ListView will become visible.
1994 emitCountChanged();
1995 }
1996
1997 clear();
1998 setPosition(contentStartOffset());
1999 updateViewport();
2000#if QT_CONFIG(quick_viewtransitions)
2001 if (transitioner)
2002 transitioner->setPopulateTransitionEnabled(false);
2003#endif
2004 inLayout = false;
2005 return;
2006 }
2007
2008#if QT_CONFIG(quick_viewtransitions)
2009 if (runDelayedRemoveTransition && transitioner
2010 && transitioner->canTransition(type: QQuickItemViewTransitioner::RemoveTransition, asTarget: false)) {
2011 // assume that any items moving now are moving due to the remove - if they schedule
2012 // a different transition, that will override this one anyway
2013 for (int i=0; i<visibleItems.size(); i++)
2014 visibleItems[i]->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::RemoveTransition, asTarget: false);
2015 }
2016#endif
2017
2018 ChangeResult insertionPosChanges;
2019 ChangeResult removalPosChanges;
2020 if (!applyModelChanges(insertionResult: &insertionPosChanges, removalResult: &removalPosChanges) && !forceLayout) {
2021 if (fillCacheBuffer) {
2022 fillCacheBuffer = false;
2023 refill();
2024 }
2025 inLayout = false;
2026 return;
2027 }
2028 forceLayout = false;
2029
2030#if QT_CONFIG(quick_viewtransitions)
2031 if (transitioner && transitioner->canTransition(type: QQuickItemViewTransitioner::PopulateTransition, asTarget: true)) {
2032 // Give the view one more chance to refill itself,
2033 // in case its size is changed such that more delegates become visible after component completed
2034 refill();
2035 for (FxViewItem *item : std::as_const(t&: visibleItems)) {
2036 if (!item->transitionScheduledOrRunning())
2037 item->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::PopulateTransition, asTarget: true);
2038 }
2039 }
2040#endif
2041
2042 updateSections();
2043 layoutVisibleItems();
2044 storeFirstVisibleItemPosition();
2045
2046#if QT_CONFIG(quick_viewtransitions)
2047 int lastIndexInView = findLastIndexInView();
2048#endif
2049 refill();
2050 markExtentsDirty();
2051 updateHighlight();
2052
2053 if (!q->isMoving() && !q->isFlicking() && !movingFromHighlight()) {
2054 fixupPosition();
2055 refill();
2056 }
2057
2058 updateHeader();
2059 updateFooter();
2060 updateViewport();
2061 updateUnrequestedPositions();
2062
2063#if QT_CONFIG(quick_viewtransitions)
2064 if (transitioner) {
2065 // items added in the last refill() may need to be transitioned in - e.g. a remove
2066 // causes items to slide up into view
2067 if (lastIndexInView != -1 &&
2068 (transitioner->canTransition(type: QQuickItemViewTransitioner::MoveTransition, asTarget: false)
2069 || transitioner->canTransition(type: QQuickItemViewTransitioner::RemoveTransition, asTarget: false))) {
2070 translateAndTransitionItemsAfter(afterIndex: lastIndexInView, insertionResult: insertionPosChanges, removalResult: removalPosChanges);
2071 }
2072
2073 prepareVisibleItemTransitions();
2074
2075 // We cannot use iterators here as erasing from a container invalidates them.
2076 for (int i = 0, count = releasePendingTransition.size(); i < count;) {
2077 auto success = prepareNonVisibleItemTransition(item: releasePendingTransition[i], viewBounds);
2078 // prepareNonVisibleItemTransition() may remove items while in fast flicking.
2079 // Invisible animating items are kicked in or out the viewPort.
2080 // Recheck count to test if the item got removed. In that case the same index points
2081 // to a different item now.
2082 const int old_count = count;
2083 count = releasePendingTransition.size();
2084 if (old_count > count)
2085 continue;
2086
2087 if (!success) {
2088 releaseItem(item: releasePendingTransition[i], reusableFlag);
2089 releasePendingTransition.remove(i);
2090 --count;
2091 } else {
2092 ++i;
2093 }
2094 }
2095
2096 for (int i=0; i<visibleItems.size(); i++)
2097 visibleItems[i]->startTransition(transitioner);
2098 for (int i=0; i<releasePendingTransition.size(); i++)
2099 releasePendingTransition[i]->startTransition(transitioner);
2100
2101 transitioner->setPopulateTransitionEnabled(false);
2102 transitioner->resetTargetLists();
2103 }
2104#endif
2105
2106 if (!currentItem)
2107 updateCurrent(modelIndex: currentIndex);
2108
2109#if QT_CONFIG(quick_viewtransitions)
2110 runDelayedRemoveTransition = false;
2111#endif
2112 inLayout = false;
2113}
2114
2115bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
2116{
2117 Q_Q(QQuickItemView);
2118 if (!q->isComponentComplete() || !hasPendingChanges())
2119 return false;
2120
2121 if (bufferedChanges.hasPendingChanges()) {
2122 currentChanges.applyBufferedChanges(other: bufferedChanges);
2123 bufferedChanges.reset();
2124 }
2125
2126 updateUnrequestedIndexes();
2127
2128 FxViewItem *prevVisibleItemsFirst = visibleItems.size() ? *visibleItems.constBegin() : nullptr;
2129 int prevItemCount = itemCount;
2130 int prevVisibleItemsCount = visibleItems.size();
2131 bool visibleAffected = false;
2132 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
2133 || !currentChanges.pendingChanges.inserts().isEmpty();
2134
2135 FxViewItem *prevFirstItemInView = firstItemInView();
2136 QQmlNullableValue<qreal> prevFirstItemInViewPos;
2137 int prevFirstItemInViewIndex = -1;
2138 if (prevFirstItemInView) {
2139 prevFirstItemInViewPos = prevFirstItemInView->position();
2140 prevFirstItemInViewIndex = prevFirstItemInView->index;
2141 }
2142 qreal prevVisibleItemsFirstPos = visibleItems.size() ? firstVisibleItemPosition : 0.0;
2143
2144 totalInsertionResult->visiblePos = prevFirstItemInViewPos;
2145 totalRemovalResult->visiblePos = prevFirstItemInViewPos;
2146
2147 const QVector<QQmlChangeSet::Change> &removals = currentChanges.pendingChanges.removes();
2148 const QVector<QQmlChangeSet::Change> &insertions = currentChanges.pendingChanges.inserts();
2149 ChangeResult insertionResult(prevFirstItemInViewPos);
2150 ChangeResult removalResult(prevFirstItemInViewPos);
2151
2152 int removedCount = 0;
2153 for (const QQmlChangeSet::Change &r : removals) {
2154 itemCount -= r.count;
2155 if (applyRemovalChange(removal: r, changeResult: &removalResult, removedCount: &removedCount))
2156 visibleAffected = true;
2157 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(r.index))
2158 visibleAffected = true;
2159 const int correctedFirstVisibleIndex = prevFirstItemInViewIndex - removalResult.countChangeBeforeVisible;
2160 if (correctedFirstVisibleIndex >= 0 && r.index < correctedFirstVisibleIndex) {
2161 if (r.index + r.count < correctedFirstVisibleIndex)
2162 removalResult.countChangeBeforeVisible += r.count;
2163 else
2164 removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - r.index);
2165 }
2166 }
2167#if QT_CONFIG(quick_viewtransitions)
2168 if (runDelayedRemoveTransition) {
2169 QQmlChangeSet::Change removal;
2170 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
2171 FxViewItem *item = *it;
2172 if (item->index == -1 && (!item->attached || !item->attached->delayRemove())) {
2173 removeItem(item, removal, removeResult: &removalResult);
2174 removedCount++;
2175 it = visibleItems.erase(pos: it);
2176 } else {
2177 ++it;
2178 }
2179 }
2180 }
2181#endif
2182 *totalRemovalResult += removalResult;
2183 if (!removals.isEmpty()) {
2184 updateVisibleIndex();
2185
2186 // set positions correctly for the next insertion
2187 if (!insertions.isEmpty()) {
2188 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible: prevFirstItemInView, insertionResult: &insertionResult, removalResult: &removalResult);
2189 layoutVisibleItems(fromModelIndex: removals.first().index);
2190 storeFirstVisibleItemPosition();
2191 }
2192 }
2193
2194 QList<FxViewItem *> newItems;
2195 QList<MovedItem> movingIntoView;
2196
2197 for (int i=0; i<insertions.size(); i++) {
2198 bool wasEmpty = visibleItems.isEmpty();
2199 if (applyInsertionChange(insert: insertions[i], changeResult: &insertionResult, newItems: &newItems, movingIntoView: &movingIntoView))
2200 visibleAffected = true;
2201 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
2202 visibleAffected = true;
2203 if (wasEmpty && !visibleItems.isEmpty())
2204 resetFirstItemPosition();
2205 *totalInsertionResult += insertionResult;
2206
2207 // set positions correctly for the next insertion
2208 if (i < insertions.size() - 1) {
2209 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible: prevFirstItemInView, insertionResult: &insertionResult, removalResult: &removalResult);
2210 layoutVisibleItems(fromModelIndex: insertions[i].index);
2211 storeFirstVisibleItemPosition();
2212 }
2213 itemCount += insertions[i].count;
2214 }
2215 for (FxViewItem *item : std::as_const(t&: newItems)) {
2216 if (item->attached)
2217 item->attached->emitAdd();
2218 }
2219
2220#if QT_CONFIG(quick_viewtransitions)
2221 // for each item that was moved directly into the view as a result of a move(),
2222 // find the index it was moved from in order to set its initial position, so that we
2223 // can transition it from this "original" position to its new position in the view
2224 if (transitioner && transitioner->canTransition(type: QQuickItemViewTransitioner::MoveTransition, asTarget: true)) {
2225 for (const MovedItem &m : std::as_const(t&: movingIntoView)) {
2226 int fromIndex = findMoveKeyIndex(key: m.moveKey, changes: removals);
2227 if (fromIndex >= 0) {
2228 if (prevFirstItemInViewIndex >= 0 && fromIndex < prevFirstItemInViewIndex)
2229 repositionItemAt(item: m.item, index: fromIndex, sizeBuffer: -totalInsertionResult->sizeChangesAfterVisiblePos);
2230 else
2231 repositionItemAt(item: m.item, index: fromIndex, sizeBuffer: totalInsertionResult->sizeChangesAfterVisiblePos);
2232 m.item->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::MoveTransition, asTarget: true);
2233 }
2234 }
2235 }
2236#endif
2237
2238 // reposition visibleItems.first() correctly so that the content y doesn't jump
2239 if (removedCount != prevVisibleItemsCount)
2240 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible: prevFirstItemInView, insertionResult: &insertionResult, removalResult: &removalResult);
2241
2242 // Whatever removed/moved items remain are no longer visible items.
2243#if QT_CONFIG(quick_viewtransitions)
2244 prepareRemoveTransitions(removedItems: &currentChanges.removedItems);
2245#endif
2246 for (auto it = currentChanges.removedItems.begin();
2247 it != currentChanges.removedItems.end(); ++it) {
2248 releaseItem(item: it.value(), reusableFlag);
2249 }
2250 currentChanges.removedItems.clear();
2251
2252 if (currentChanges.currentChanged) {
2253 if (currentChanges.currentRemoved && currentItem) {
2254 if (currentItem->item && currentItem->attached)
2255 currentItem->attached->setIsCurrentItem(false);
2256 const bool hadCurrentItem = currentItem != nullptr;
2257 releaseCurrentItem(reusableFlag);
2258 if (hadCurrentItem)
2259 emit q->currentItemChanged();
2260 }
2261 if (!currentIndexCleared)
2262 updateCurrent(modelIndex: currentChanges.newCurrentIndex);
2263 }
2264
2265 if (!visibleAffected)
2266 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
2267 currentChanges.reset();
2268
2269 updateSections();
2270 if (prevItemCount != itemCount)
2271 emitCountChanged();
2272 if (!visibleAffected && viewportChanged)
2273 updateViewport();
2274
2275 return visibleAffected;
2276}
2277
2278bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &removal, ChangeResult *removeResult, int *removedCount)
2279{
2280 Q_Q(QQuickItemView);
2281 bool visibleAffected = false;
2282
2283 if (visibleItems.size() && removal.index + removal.count > visibleItems.constLast()->index) {
2284 if (removal.index > visibleItems.constLast()->index)
2285 removeResult->countChangeAfterVisibleItems += removal.count;
2286 else
2287 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index);
2288 }
2289
2290 QList<FxViewItem*>::Iterator it = visibleItems.begin();
2291 while (it != visibleItems.end()) {
2292 FxViewItem *item = *it;
2293 if (item->index == -1 || item->index < removal.index) {
2294 // already removed, or before removed items
2295 if (!visibleAffected && item->index < removal.index)
2296 visibleAffected = true;
2297 ++it;
2298 } else if (item->index >= removal.index + removal.count) {
2299 // after removed items
2300 item->index -= removal.count;
2301#if QT_CONFIG(quick_viewtransitions)
2302 if (removal.isMove())
2303 item->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::MoveTransition, asTarget: false);
2304 else
2305 item->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::RemoveTransition, asTarget: false);
2306#endif
2307 ++it;
2308 } else {
2309 // removed item
2310 visibleAffected = true;
2311 if (!removal.isMove() && item->item && item->attached)
2312 item->attached->emitRemove();
2313
2314 if (item->item && item->attached && item->attached->delayRemove() && !removal.isMove()) {
2315 item->index = -1;
2316 QObject::connect(sender: item->attached, SIGNAL(delayRemoveChanged()), receiver: q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2317 ++it;
2318 } else {
2319 removeItem(item, removal, removeResult);
2320 if (!removal.isMove())
2321 (*removedCount)++;
2322 it = visibleItems.erase(pos: it);
2323 }
2324 }
2325 }
2326
2327 return visibleAffected;
2328}
2329
2330void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Change &removal, ChangeResult *removeResult)
2331{
2332 if (removeResult->visiblePos.isValid()) {
2333 if (item->position() < removeResult->visiblePos)
2334 updateSizeChangesBeforeVisiblePos(item, removeResult);
2335 else
2336 removeResult->sizeChangesAfterVisiblePos += item->size();
2337 }
2338 if (removal.isMove()) {
2339 currentChanges.removedItems.replace(key: removal.moveKey(index: item->index), value: item);
2340#if QT_CONFIG(quick_viewtransitions)
2341 item->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::MoveTransition, asTarget: true);
2342#endif
2343 } else {
2344 // track item so it is released later
2345 currentChanges.removedItems.insert(key: QQmlChangeSet::MoveKey(), value: item);
2346 }
2347 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2348 removeResult->changedFirstItem = true;
2349}
2350
2351void QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult)
2352{
2353 removeResult->sizeChangesBeforeVisiblePos += item->size();
2354}
2355
2356void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2357 qreal prevVisibleItemsFirstPos,
2358 FxViewItem *prevFirstVisible,
2359 ChangeResult *insertionResult,
2360 ChangeResult *removalResult)
2361{
2362 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2363
2364 // reposition visibleItems.first() correctly so that the content y doesn't jump
2365 if (visibleItems.size()) {
2366 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2367 resetFirstItemPosition(pos: prevVisibleItemsFirstPos);
2368
2369 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2370 && prevFirstVisible != *visibleItems.constBegin()) {
2371 // the previous visibleItems.first() was also the first visible item, and it has been
2372 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2373 if (!insertionResult->changedFirstItem)
2374 resetFirstItemPosition(pos: prevVisibleItemsFirstPos);
2375
2376 } else if (prevViewPos.isValid()) {
2377 qreal moveForwardsBy = 0;
2378 qreal moveBackwardsBy = 0;
2379
2380 // shift visibleItems.first() relative to the number of added/removed items
2381 const auto pos = visibleItems.constFirst()->position();
2382 if (pos > prevViewPos) {
2383 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2384 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2385 } else if (pos < prevViewPos) {
2386 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2387 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2388 }
2389 adjustFirstItem(forwards: moveForwardsBy, backwards: moveBackwardsBy, changeBeforeVisible: insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2390 }
2391 insertionResult->reset();
2392 removalResult->reset();
2393 }
2394}
2395
2396#if QT_CONFIG(quick_viewtransitions)
2397void QQuickItemViewPrivate::createTransitioner()
2398{
2399 if (!transitioner) {
2400 transitioner = new QQuickItemViewTransitioner;
2401 transitioner->setChangeListener(this);
2402 }
2403}
2404
2405void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2406{
2407 Q_Q(QQuickItemView);
2408 if (!transitioner)
2409 return;
2410
2411 // must call for every visible item to init or discard transitions
2412 QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
2413 for (int i=0; i<visibleItems.size(); i++)
2414 visibleItems[i]->prepareTransition(transitioner, viewBounds);
2415}
2416
2417void QQuickItemViewPrivate::prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems)
2418{
2419 if (!transitioner)
2420 return;
2421
2422 if (transitioner->canTransition(type: QQuickItemViewTransitioner::RemoveTransition, asTarget: true)
2423 || transitioner->canTransition(type: QQuickItemViewTransitioner::RemoveTransition, asTarget: false)) {
2424 for (auto it = removedItems->begin(); it != removedItems->end(); ) {
2425 bool isRemove = it.key().moveId < 0;
2426 if (isRemove) {
2427 FxViewItem *item = *it;
2428 item->trackGeometry(track: false);
2429 item->releaseAfterTransition = true;
2430 releasePendingTransition.append(t: item);
2431 item->transitionNextReposition(transitioner, type: QQuickItemViewTransitioner::RemoveTransition, asTarget: true);
2432 it = removedItems->erase(it);
2433 } else {
2434 ++it;
2435 }
2436 }
2437 }
2438}
2439
2440bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2441{
2442 // Called for items that have been removed from visibleItems and may now be
2443 // transitioned out of the view. This applies to items that are being directly
2444 // removed, or moved to outside of the view, as well as those that are
2445 // displaced to a position outside of the view due to an insert or move.
2446
2447 if (!transitioner)
2448 return false;
2449
2450 if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2451 repositionItemAt(item, index: item->index, sizeBuffer: 0);
2452
2453 bool success = false;
2454 ACTION_IF_DELETED(item, success = item->prepareTransition(transitioner, viewBounds), return success);
2455
2456 if (success) {
2457 item->releaseAfterTransition = true;
2458 return true;
2459 }
2460 return false;
2461}
2462
2463void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2464{
2465 for (int i=0; i<releasePendingTransition.size(); i++) {
2466 if (releasePendingTransition.at(i)->transitionableItem == item) {
2467 releaseItem(item: releasePendingTransition.takeAt(i), reusableFlag);
2468 return;
2469 }
2470 }
2471}
2472#endif
2473
2474/*
2475 This may return 0 if the item is being created asynchronously.
2476 When the item becomes available, refill() will be called and the item
2477 will be returned on the next call to createItem().
2478*/
2479FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::IncubationMode incubationMode)
2480{
2481 Q_Q(QQuickItemView);
2482
2483 if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous)
2484 return nullptr;
2485
2486#if QT_CONFIG(quick_viewtransitions)
2487 for (int i=0; i<releasePendingTransition.size(); i++) {
2488 if (releasePendingTransition.at(i)->index == modelIndex
2489 && !releasePendingTransition.at(i)->isPendingRemoval()) {
2490 releasePendingTransition[i]->releaseAfterTransition = false;
2491 return releasePendingTransition.takeAt(i);
2492 }
2493 }
2494#endif
2495
2496 inRequest = true;
2497
2498 // The model will run this same range check internally but produce a warning and return nullptr.
2499 // Since we handle this result graciously in our code, we preempt this warning by checking the range ourselves.
2500 QObject* object = modelIndex < model->count() ? model->object(index: modelIndex, incubationMode) : nullptr;
2501 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
2502
2503 if (!item) {
2504 if (!object) {
2505 if (requestedIndex == -1 && model->incubationStatus(index: modelIndex) == QQmlIncubator::Loading) {
2506 // The reason we didn't receive an item is because it's incubating async. We keep track
2507 // of this by assigning the index we're waiting for to 'requestedIndex'. This will e.g. let
2508 // the view avoid unnecessary layout calls until the item has been loaded.
2509 requestedIndex = modelIndex;
2510 }
2511 } else {
2512 model->release(object);
2513 if (!delegateValidated) {
2514 delegateValidated = true;
2515 QObject* delegate = q->delegate();
2516 qmlWarning(me: delegate ? delegate : q) << QQuickItemView::tr(s: "Delegate must be of Item type");
2517 }
2518 }
2519 inRequest = false;
2520 return nullptr;
2521 } else {
2522 // Container removes and instantly deletes items created within ObjectModels.
2523 // We need to account for this to avoid having references to deleted items.
2524 // itemDestroyed is called as a result of adding this listener.
2525 if (qobject_cast<QQmlObjectModel *>(object: model))
2526 QQuickItemPrivate::get(item)->updateOrAddItemChangeListener(listener: this, types: itemChangeListenerTypes);
2527
2528 item->setParentItem(q->contentItem());
2529 if (requestedIndex == modelIndex)
2530 requestedIndex = -1;
2531 FxViewItem *viewItem = newViewItem(index: modelIndex, item);
2532 if (viewItem) {
2533 viewItem->index = modelIndex;
2534 // do other set up for the new item that should not happen
2535 // until after bindings are evaluated
2536 initializeViewItem(viewItem);
2537 unrequestedItems.remove(key: item);
2538 }
2539 inRequest = false;
2540 return viewItem;
2541 }
2542}
2543
2544void QQuickItemView::createdItem(int index, QObject* object)
2545{
2546 Q_D(QQuickItemView);
2547
2548 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2549 if (!d->inRequest) {
2550 d->unrequestedItems.insert(key: item, value: index);
2551 d->requestedIndex = -1;
2552 if (d->hasPendingChanges())
2553 d->layout();
2554 else
2555 d->refill();
2556 if (d->unrequestedItems.contains(key: item))
2557 d->repositionPackageItemAt(item, index);
2558 else if (index == d->currentIndex)
2559 d->updateCurrent(modelIndex: index);
2560 }
2561}
2562
2563void QQuickItemView::initItem(int, QObject *object)
2564{
2565 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2566 if (item) {
2567 if (qFuzzyIsNull(d: item->z()))
2568 item->setZ(1);
2569 item->setParentItem(contentItem());
2570 QQuickItemPrivate::get(item)->setCulled(true);
2571 }
2572}
2573
2574// This is called when the model (if it's a QQmlInstanceModel) emits destroyingItem.
2575void QQuickItemView::destroyingItem(QObject *object)
2576{
2577 Q_D(QQuickItemView);
2578 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2579 if (item) {
2580 item->setParentItem(nullptr);
2581 d->unrequestedItems.remove(key: item);
2582 QQuickItemPrivate::get(item)->removeItemChangeListener(d, types: itemChangeListenerTypes);
2583 }
2584}
2585
2586void QQuickItemView::onItemPooled(int modelIndex, QObject *object)
2587{
2588 Q_UNUSED(modelIndex);
2589
2590 if (auto *attached = d_func()->getAttachedObject(object))
2591 emit attached->pooled();
2592}
2593
2594void QQuickItemView::onItemReused(int modelIndex, QObject *object)
2595{
2596 Q_UNUSED(modelIndex);
2597
2598 if (auto *attached = d_func()->getAttachedObject(object))
2599 emit attached->reused();
2600}
2601
2602bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag)
2603{
2604 Q_Q(QQuickItemView);
2605 if (!item)
2606 return true;
2607 if (trackedItem == item)
2608 trackedItem = nullptr;
2609 item->trackGeometry(track: false);
2610
2611 QQmlInstanceModel::ReleaseFlags flags = {};
2612 if (QPointer<QQuickItem> quickItem = item->item) {
2613 if (model) {
2614 flags = model->release(object: quickItem, reusableFlag);
2615 if (!flags) {
2616 // item was not destroyed, and we no longer reference it.
2617 if (quickItem->parentItem() == contentItem) {
2618 // Only cull the item if its parent item is still our contentItem.
2619 // One case where this can happen is moving an item out of one ObjectModel and into another.
2620 QQuickItemPrivate::get(item: quickItem)->setCulled(true);
2621 }
2622 // If deleteLater was called, the item isn't long for this world and so we shouldn't store references to it.
2623 // This can happen when a Repeater is used to populate items in SwipeView's ListView contentItem.
2624 if (!isClearing && !QObjectPrivate::get(o: quickItem)->deleteLaterCalled)
2625 unrequestedItems.insert(key: quickItem, value: model->indexOf(object: quickItem, objectContext: q));
2626 } else if (flags & QQmlInstanceModel::Destroyed) {
2627 quickItem->setParentItem(nullptr);
2628 } else if (flags & QQmlInstanceModel::Pooled) {
2629 item->setVisible(false);
2630 }
2631 }
2632
2633 QQuickItemPrivate::get(item: quickItem)->removeItemChangeListener(this, types: itemChangeListenerTypes);
2634 delete item->transitionableItem;
2635 item->transitionableItem = nullptr;
2636 }
2637
2638 delete item;
2639 return flags != QQmlInstanceModel::Referenced;
2640}
2641
2642/*!
2643 \internal
2644
2645 Called when an item created in an ObjectModel is deleted rather than
2646 removing it via the model.
2647
2648 Similar in what it does to destroyRemoved except that it intentionally
2649 doesn't account for delayRemove.
2650*/
2651void QQuickItemViewPrivate::itemDestroyed(QQuickItem *item)
2652{
2653 // We can't check model->indexOf(item, q_func()) here, because the item
2654 // may not exist there, so we instead check visibleItems.
2655 FxViewItem *visibleFxItem = nullptr;
2656 const int indexOfItem = -1;
2657 for (auto *fxItem : std::as_const(t&: visibleItems)) {
2658 if (fxItem->item == item) {
2659 visibleFxItem = fxItem;
2660 break;
2661 }
2662 }
2663
2664 // Make sure that we don't try to clean up the same FxViewItem twice,
2665 // as apparently there can be two FxViewItems for the same QQuickItem.
2666 if (currentItem && visibleFxItem)
2667 Q_ASSERT(currentItem != visibleFxItem);
2668
2669 if (visibleFxItem) {
2670 qCDebug(lcItemViewDelegateLifecycle) << "removing deleted item"
2671 << item << visibleFxItem << "at index" << indexOfItem << "without running transitions";
2672 // We need to remove it from visibleItems manually, as we don't want to call
2673 // removeNonVisibleItems since it won't remove items with transitions.
2674 const bool removedVisibleFxItem = visibleItems.removeOne(t: visibleFxItem);
2675 Q_ASSERT(removedVisibleFxItem);
2676 releaseItem(item: visibleFxItem, reusableFlag: QQmlDelegateModel::NotReusable);
2677 }
2678
2679 if (currentItem && currentItem->item == item) {
2680 releaseItem(item: currentItem, reusableFlag: QQmlDelegateModel::NotReusable);
2681 currentItem = nullptr;
2682 }
2683
2684 // Update the positioning of the items.
2685 forceLayoutPolish();
2686}
2687
2688QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2689{
2690 QQuickItem *item = nullptr;
2691 if (!inRequest) {
2692 inRequest = true;
2693 item = createComponentItem(component: highlightComponent, zValue: 0.0, createDefault: true);
2694 inRequest = false;
2695 }
2696 return item;
2697}
2698
2699QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault) const
2700{
2701 Q_Q(const QQuickItemView);
2702
2703 QQuickItem *item = nullptr;
2704 if (component) {
2705 QQmlContext *context = component->creationContext();
2706 if (!context)
2707 context = qmlContext(q);
2708
2709 if (QObject *nobj = component->beginCreate(context)) {
2710 item = qobject_cast<QQuickItem *>(o: nobj);
2711 if (!item)
2712 delete nobj;
2713 }
2714 } else if (createDefault) {
2715 item = new QQuickItem;
2716 }
2717 if (item) {
2718 if (qFuzzyIsNull(d: item->z()))
2719 item->setZ(zValue);
2720 QQml_setParent_noEvent(object: item, parent: q->contentItem());
2721 item->setParentItem(q->contentItem());
2722
2723 initializeComponentItem(item);
2724 }
2725 if (component)
2726 component->completeCreate();
2727 return item;
2728}
2729
2730/*!
2731 \internal
2732
2733 Allows derived classes to do any initialization required for \a item
2734 before completeCreate() is called on it. For example, any attached
2735 properties required by the item can be set.
2736
2737 This is similar to initItem(), but as that has logic specific to
2738 delegate items, we use a separate function for non-delegates.
2739*/
2740void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const
2741{
2742 Q_UNUSED(item);
2743}
2744
2745void QQuickItemViewPrivate::updateTrackedItem()
2746{
2747 Q_Q(QQuickItemView);
2748 FxViewItem *item = currentItem;
2749 if (highlight)
2750 item = highlight.get();
2751 trackedItem = item;
2752
2753 if (trackedItem)
2754 q->trackedPositionChanged();
2755}
2756
2757void QQuickItemViewPrivate::updateUnrequestedIndexes()
2758{
2759 Q_Q(QQuickItemView);
2760 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(), end = unrequestedItems.end(); it != end; ++it)
2761 *it = model->indexOf(object: it.key(), objectContext: q);
2762}
2763
2764void QQuickItemViewPrivate::updateUnrequestedPositions()
2765{
2766 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) {
2767 if (it.value() >= 0)
2768 repositionPackageItemAt(item: it.key(), index: it.value());
2769 }
2770}
2771
2772void QQuickItemViewPrivate::updateVisibleIndex()
2773{
2774 typedef QList<FxViewItem*>::const_iterator FxViewItemListConstIt;
2775
2776 visibleIndex = 0;
2777 for (FxViewItemListConstIt it = visibleItems.constBegin(), cend = visibleItems.constEnd(); it != cend; ++it) {
2778 if ((*it)->index != -1) {
2779 visibleIndex = (*it)->index;
2780 break;
2781 }
2782 }
2783}
2784
2785QQmlDelegateModel::DelegateModelAccess QQuickItemView::delegateModelAccess() const
2786{
2787 Q_D(const QQuickItemView);
2788 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(object: d->model))
2789 return dataModel->delegateModelAccess();
2790 return QQmlDelegateModel::Qt5ReadWrite;
2791}
2792
2793void QQuickItemView::setDelegateModelAccess(
2794 QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
2795{
2796 Q_D(QQuickItemView);
2797 const auto setExplicitDelegateModelAccess = [&](QQmlDelegateModel *delegateModel) {
2798 delegateModel->setDelegateModelAccess(delegateModelAccess);
2799 d->explicitDelegateModelAccess = true;
2800 };
2801
2802 if (!d->model) {
2803 if (delegateModelAccess == QQmlDelegateModel::Qt5ReadWrite) {
2804 // Explicitly set delegateModelAccess to Legacy. We can do this without model.
2805 d->explicitDelegateModelAccess = true;
2806 return;
2807 }
2808
2809 setExplicitDelegateModelAccess(QQmlDelegateModel::createForView(q: this, d));
2810
2811 // The new model is not connected to applyDelegateModelAccessChange, yet. We only do this
2812 // once there is actual data, via an explicit setModel(). So we have to manually emit the
2813 // delegateModelAccessChanged() here.
2814 emit delegateModelAccessChanged();
2815 return;
2816 }
2817
2818 if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(object: d->model)) {
2819 // Disable the warning in applyDelegateModelAccessChange since the new delegate model
2820 // access is also explicit.
2821 d->explicitDelegateModelAccess = false;
2822 setExplicitDelegateModelAccess(delegateModel);
2823 return;
2824 }
2825
2826 if (delegateModelAccess == QQmlDelegateModel::Qt5ReadWrite) {
2827 d->explicitDelegateModelAccess = true; // Explicitly set null delegate always works
2828 } else {
2829 qmlWarning(me: this) << "Cannot set a delegateModelAccess on an explicitly provided "
2830 "non-DelegateModel";
2831 }
2832}
2833
2834QT_END_NAMESPACE
2835
2836#include "moc_qquickitemview_p.cpp"
2837

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