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