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 QList<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 , inRefill(false)
1658{
1659 bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
1660 bufferPause.setLoopCount(1);
1661 bufferPause.setDuration(16);
1662}
1663
1664static const QQuickItemPrivate::ChangeTypes itemChangeListenerTypes = QQuickItemPrivate::Destroyed;
1665
1666QQuickItemViewPrivate::~QQuickItemViewPrivate()
1667{
1668#if QT_CONFIG(quick_viewtransitions)
1669 if (transitioner)
1670 transitioner->setChangeListener(nullptr);
1671 delete transitioner;
1672#endif
1673}
1674
1675bool QQuickItemViewPrivate::isValid() const
1676{
1677 return model && model->count() && model->isValid();
1678}
1679
1680qreal QQuickItemViewPrivate::position() const
1681{
1682 Q_Q(const QQuickItemView);
1683 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1684}
1685
1686qreal QQuickItemViewPrivate::size() const
1687{
1688 Q_Q(const QQuickItemView);
1689 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1690}
1691
1692qreal QQuickItemViewPrivate::startPosition() const
1693{
1694 return isContentFlowReversed() ? -lastPosition() : originPosition();
1695}
1696
1697qreal QQuickItemViewPrivate::endPosition() const
1698{
1699 return isContentFlowReversed() ? -originPosition() : lastPosition();
1700}
1701
1702qreal QQuickItemViewPrivate::contentStartOffset() const
1703{
1704 qreal pos = -headerSize();
1705 if (layoutOrientation() == Qt::Vertical) {
1706 if (isContentFlowReversed())
1707 pos -= vData.endMargin;
1708 else
1709 pos -= vData.startMargin;
1710 } else {
1711 if (isContentFlowReversed())
1712 pos -= hData.endMargin;
1713 else
1714 pos -= hData.startMargin;
1715 }
1716 return pos;
1717}
1718
1719int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1720{
1721 for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
1722 auto item = *it;
1723 if (item->index != -1)
1724 return item->index;
1725 }
1726 return defaultValue;
1727}
1728
1729FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1730 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.size()) {
1731 for (int i = modelIndex - visibleIndex; i < visibleItems.size(); ++i) {
1732 FxViewItem *item = visibleItems.at(i);
1733 if (item->index == modelIndex)
1734 return item;
1735 }
1736 }
1737 return nullptr;
1738}
1739
1740FxViewItem *QQuickItemViewPrivate::firstItemInView() const {
1741 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1742 for (FxViewItem *item : visibleItems) {
1743 if (item->index != -1 && item->endPosition() > pos)
1744 return item;
1745 }
1746 return visibleItems.size() ? visibleItems.first() : 0;
1747}
1748
1749int QQuickItemViewPrivate::findLastIndexInView() const
1750{
1751 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1752 for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
1753 auto item = *it;
1754 if (item->index != -1 && item->position() <= viewEndPos)
1755 return item->index;
1756 }
1757 return -1;
1758}
1759
1760// Map a model index to visibleItems list index.
1761// These may differ if removed items are still present in the visible list,
1762// e.g. doing a removal animation
1763int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1764{
1765 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.size())
1766 return -1;
1767 for (int i = 0; i < visibleItems.size(); ++i) {
1768 FxViewItem *item = visibleItems.at(i);
1769 if (item->index == modelIndex)
1770 return i;
1771 if (item->index > modelIndex)
1772 return -1;
1773 }
1774 return -1; // Not in visibleList
1775}
1776
1777void QQuickItemViewPrivate::init()
1778{
1779 Q_Q(QQuickItemView);
1780 q->setFlag(QQuickItem::ItemIsFocusScope);
1781 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1782 QObject::connect(q, &QQuickFlickable::interactiveChanged, q, &QQuickItemView::keyNavigationEnabledChanged);
1783 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1784}
1785
1786void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1787{
1788 Q_Q(QQuickItemView);
1789 applyPendingChanges();
1790 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1791 if (currentItem) {
1792 if (currentItem->attached)
1793 currentItem->attached->setIsCurrentItem(false);
1794 releaseCurrentItem(reusableFlag);
1795 currentIndex = modelIndex;
1796 emit q->currentIndexChanged();
1797 emit q->currentItemChanged();
1798 updateHighlight();
1799 } else if (currentIndex != modelIndex) {
1800 currentIndex = modelIndex;
1801 emit q->currentIndexChanged();
1802 }
1803 return;
1804 }
1805
1806 if (currentItem && currentIndex == modelIndex) {
1807 updateHighlight();
1808 return;
1809 }
1810
1811 FxViewItem *oldCurrentItem = currentItem;
1812 int oldCurrentIndex = currentIndex;
1813 currentIndex = modelIndex;
1814 currentItem = createItem(modelIndex, QQmlIncubator::AsynchronousIfNested);
1815 if (oldCurrentItem && oldCurrentItem->attached && (!currentItem || oldCurrentItem->item != currentItem->item))
1816 oldCurrentItem->attached->setIsCurrentItem(false);
1817 if (currentItem) {
1818 currentItem->item->setFocus(true);
1819 if (currentItem->attached)
1820 currentItem->attached->setIsCurrentItem(true);
1821 initializeCurrentItem();
1822 }
1823
1824 updateHighlight();
1825 if (oldCurrentIndex != currentIndex)
1826 emit q->currentIndexChanged();
1827 if (oldCurrentItem != currentItem
1828 && (!oldCurrentItem || !currentItem || oldCurrentItem->item != currentItem->item))
1829 emit q->currentItemChanged();
1830 releaseItem(oldCurrentItem, reusableFlag);
1831}
1832
1833void QQuickItemViewPrivate::clear(bool onDestruction)
1834{
1835 Q_Q(QQuickItemView);
1836
1837 isClearing = true;
1838 auto cleanup = qScopeGuard([this] { isClearing = false; });
1839
1840 currentChanges.reset();
1841 bufferedChanges.reset();
1842 timeline.clear();
1843
1844 releaseVisibleItems(QQmlInstanceModel::NotReusable);
1845 visibleIndex = 0;
1846
1847#if QT_CONFIG(quick_viewtransitions)
1848 for (FxViewItem *item : std::as_const(releasePendingTransition)) {
1849 item->releaseAfterTransition = false;
1850 releaseItem(item, QQmlInstanceModel::NotReusable);
1851 }
1852 releasePendingTransition.clear();
1853#endif
1854
1855 const bool hadCurrentItem = currentItem != nullptr;
1856 releaseCurrentItem(QQmlDelegateModel::NotReusable);
1857 if (hadCurrentItem)
1858 emit q->currentItemChanged();
1859 createHighlight(onDestruction);
1860 trackedItem = nullptr;
1861
1862 if (requestedIndex >= 0) {
1863 if (model)
1864 model->cancel(requestedIndex);
1865 requestedIndex = -1;
1866 }
1867
1868 markExtentsDirty();
1869 itemCount = 0;
1870}
1871
1872
1873void QQuickItemViewPrivate::mirrorChange()
1874{
1875 Q_Q(QQuickItemView);
1876 regenerate();
1877 emit q->effectiveLayoutDirectionChanged();
1878}
1879
1880void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1881{
1882 Q_Q(QQuickItemView);
1883 fillCacheBuffer = true;
1884 q->polish();
1885}
1886
1887void QQuickItemViewPrivate::refill()
1888{
1889 qreal s = qMax(size(), qreal(0.));
1890 const auto pos = position();
1891 if (isContentFlowReversed())
1892 refill(-pos - displayMarginBeginning-s, -pos + displayMarginEnd);
1893 else
1894 refill(pos - displayMarginBeginning, pos + displayMarginEnd+s);
1895}
1896
1897void QQuickItemViewPrivate::refill(qreal from, qreal to)
1898{
1899 Q_Q(QQuickItemView);
1900 if (!model || !model->isValid() || !q->isComponentComplete())
1901 return;
1902 if (q->size().isNull() && visibleItems.isEmpty())
1903 return;
1904 if (!model->count()) {
1905 updateHeader();
1906 updateFooter();
1907 updateViewport();
1908 return;
1909 }
1910
1911 if (inRefill)
1912 return;
1913 inRefill = true;
1914
1915 do {
1916 bufferPause.stop();
1917 if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) {
1918 currentChanges.reset();
1919 bufferedChanges.reset();
1920 releaseVisibleItems(reusableFlag);
1921 }
1922
1923 int prevCount = itemCount;
1924 itemCount = model->count();
1925 qreal bufferFrom = from - buffer;
1926 qreal bufferTo = to + buffer;
1927 qreal fillFrom = from;
1928 qreal fillTo = to;
1929
1930 bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
1931
1932 if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1933 if (added) {
1934 // We've already created a new delegate this frame.
1935 // Just schedule a buffer refill.
1936 bufferPause.start();
1937 } else {
1938 if (bufferMode & BufferAfter)
1939 fillTo = bufferTo;
1940 if (bufferMode & BufferBefore)
1941 fillFrom = bufferFrom;
1942 added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, true);
1943 }
1944 }
1945
1946 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1947
1948 if (added || removed) {
1949 markExtentsDirty();
1950 updateBeginningEnd();
1951 visibleItemsChanged();
1952 updateHeader();
1953 updateFooter();
1954 updateViewport();
1955 }
1956
1957 if (prevCount != itemCount)
1958 emitCountChanged();
1959 } while (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges());
1960 storeFirstVisibleItemPosition();
1961 inRefill = false;
1962}
1963
1964void QQuickItemViewPrivate::regenerate(bool orientationChanged)
1965{
1966 Q_Q(QQuickItemView);
1967 if (q->isComponentComplete()) {
1968 if (orientationChanged) {
1969 delete header;
1970 header = nullptr;
1971 delete footer;
1972 footer = nullptr;
1973 }
1974 clear();
1975 updateHeader();
1976 updateFooter();
1977 updateViewport();
1978 setPosition(contentStartOffset());
1979 refill();
1980 updateCurrent(currentIndex);
1981 }
1982}
1983
1984void QQuickItemViewPrivate::updateViewport()
1985{
1986 Q_Q(QQuickItemView);
1987 qreal extra = headerSize() + footerSize();
1988 qreal contentSize = isValid() || !visibleItems.isEmpty() ? (endPosition() - startPosition()) : 0.0;
1989 if (layoutOrientation() == Qt::Vertical)
1990 q->setContentHeight(contentSize + extra);
1991 else
1992 q->setContentWidth(contentSize + extra);
1993}
1994
1995void QQuickItemViewPrivate::layout()
1996{
1997 Q_Q(QQuickItemView);
1998 if (inLayout)
1999 return;
2000
2001 inLayout = true;
2002
2003 // viewBounds contains bounds before any add/remove/move operation to the view
2004 QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
2005
2006 // We use isNull for the size check, because isEmpty returns true
2007 // if either dimension is negative, but apparently we support negative-sized
2008 // views (see tst_QQuickListView::resizeView).
2009 if ((!isValid() && !visibleItems.size()) || q->size().isNull()) {
2010 if (q->size().isNull() && hasPendingChanges()) {
2011 // count() refers to the number of items in the model, not in the view
2012 // (which is why we don't emit for the !visibleItems.size() case).
2013 // If there are pending model changes, emit countChanged in order to
2014 // support the use case of QTBUG-129165, where visible is bound to count > 0
2015 // and the ListView is in a layout with Layout.preferredHeight bound to
2016 // contentHeight. This ensures that a hidden ListView will become visible.
2017 emitCountChanged();
2018 }
2019
2020 clear();
2021 setPosition(contentStartOffset());
2022 updateViewport();
2023#if QT_CONFIG(quick_viewtransitions)
2024 if (transitioner)
2025 transitioner->setPopulateTransitionEnabled(false);
2026#endif
2027 inLayout = false;
2028 return;
2029 }
2030
2031#if QT_CONFIG(quick_viewtransitions)
2032 if (runDelayedRemoveTransition && transitioner
2033 && transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2034 // assume that any items moving now are moving due to the remove - if they schedule
2035 // a different transition, that will override this one anyway
2036 for (int i=0; i<visibleItems.size(); i++)
2037 visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2038 }
2039#endif
2040
2041 ChangeResult insertionPosChanges;
2042 ChangeResult removalPosChanges;
2043 if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
2044 if (fillCacheBuffer) {
2045 fillCacheBuffer = false;
2046 refill();
2047 }
2048 inLayout = false;
2049 return;
2050 }
2051 forceLayout = false;
2052
2053#if QT_CONFIG(quick_viewtransitions)
2054 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
2055 // Give the view one more chance to refill itself,
2056 // in case its size is changed such that more delegates become visible after component completed
2057 refill();
2058 for (FxViewItem *item : std::as_const(visibleItems)) {
2059 if (!item->transitionScheduledOrRunning())
2060 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
2061 }
2062 }
2063#endif
2064
2065 updateSections();
2066 layoutVisibleItems();
2067 storeFirstVisibleItemPosition();
2068
2069#if QT_CONFIG(quick_viewtransitions)
2070 int lastIndexInView = findLastIndexInView();
2071#endif
2072 refill();
2073 markExtentsDirty();
2074 updateHighlight();
2075
2076 if (!q->isMoving() && !q->isFlicking() && !movingFromHighlight()) {
2077 fixupPosition();
2078 refill();
2079 }
2080
2081 updateHeader();
2082 updateFooter();
2083 updateViewport();
2084 updateUnrequestedPositions();
2085
2086#if QT_CONFIG(quick_viewtransitions)
2087 if (transitioner) {
2088 // items added in the last refill() may need to be transitioned in - e.g. a remove
2089 // causes items to slide up into view
2090 if (lastIndexInView != -1 &&
2091 (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false)
2092 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false))) {
2093 translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
2094 }
2095
2096 prepareVisibleItemTransitions();
2097
2098 // We cannot use iterators here as erasing from a container invalidates them.
2099 for (int i = 0, count = releasePendingTransition.size(); i < count;) {
2100 auto success = prepareNonVisibleItemTransition(releasePendingTransition[i], viewBounds);
2101 // prepareNonVisibleItemTransition() may remove items while in fast flicking.
2102 // Invisible animating items are kicked in or out the viewPort.
2103 // Recheck count to test if the item got removed. In that case the same index points
2104 // to a different item now.
2105 const int old_count = count;
2106 count = releasePendingTransition.size();
2107 if (old_count > count)
2108 continue;
2109
2110 if (!success) {
2111 releaseItem(releasePendingTransition[i], reusableFlag);
2112 releasePendingTransition.remove(i);
2113 --count;
2114 } else {
2115 ++i;
2116 }
2117 }
2118
2119 for (int i=0; i<visibleItems.size(); i++)
2120 visibleItems[i]->startTransition(transitioner);
2121 for (int i=0; i<releasePendingTransition.size(); i++)
2122 releasePendingTransition[i]->startTransition(transitioner);
2123
2124 transitioner->setPopulateTransitionEnabled(false);
2125 transitioner->resetTargetLists();
2126 }
2127#endif
2128
2129 if (!currentItem)
2130 updateCurrent(currentIndex);
2131
2132#if QT_CONFIG(quick_viewtransitions)
2133 runDelayedRemoveTransition = false;
2134#endif
2135 inLayout = false;
2136}
2137
2138bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
2139{
2140 Q_Q(QQuickItemView);
2141 if (!q->isComponentComplete() || !hasPendingChanges())
2142 return false;
2143
2144 if (bufferedChanges.hasPendingChanges()) {
2145 currentChanges.applyBufferedChanges(bufferedChanges);
2146 bufferedChanges.reset();
2147 }
2148
2149 updateUnrequestedIndexes();
2150
2151 FxViewItem *prevVisibleItemsFirst = visibleItems.size() ? *visibleItems.constBegin() : nullptr;
2152 int prevItemCount = itemCount;
2153 int prevVisibleItemsCount = visibleItems.size();
2154 bool visibleAffected = false;
2155 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
2156 || !currentChanges.pendingChanges.inserts().isEmpty();
2157
2158 FxViewItem *prevFirstItemInView = firstItemInView();
2159 QQmlNullableValue<qreal> prevFirstItemInViewPos;
2160 int prevFirstItemInViewIndex = -1;
2161 if (prevFirstItemInView) {
2162 prevFirstItemInViewPos = prevFirstItemInView->position();
2163 prevFirstItemInViewIndex = prevFirstItemInView->index;
2164 }
2165 qreal prevVisibleItemsFirstPos = visibleItems.size() ? firstVisibleItemPosition : 0.0;
2166
2167 totalInsertionResult->visiblePos = prevFirstItemInViewPos;
2168 totalRemovalResult->visiblePos = prevFirstItemInViewPos;
2169
2170 const QList<QQmlChangeSet::Change> &removals = currentChanges.pendingChanges.removes();
2171 const QList<QQmlChangeSet::Change> &insertions = currentChanges.pendingChanges.inserts();
2172 ChangeResult insertionResult(prevFirstItemInViewPos);
2173 ChangeResult removalResult(prevFirstItemInViewPos);
2174
2175 int removedCount = 0;
2176 for (const QQmlChangeSet::Change &r : removals) {
2177 itemCount -= r.count;
2178 if (applyRemovalChange(r, &removalResult, &removedCount))
2179 visibleAffected = true;
2180 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(r.index))
2181 visibleAffected = true;
2182 const int correctedFirstVisibleIndex = prevFirstItemInViewIndex - removalResult.countChangeBeforeVisible;
2183 if (correctedFirstVisibleIndex >= 0 && r.index < correctedFirstVisibleIndex) {
2184 if (r.index + r.count < correctedFirstVisibleIndex)
2185 removalResult.countChangeBeforeVisible += r.count;
2186 else
2187 removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - r.index);
2188 }
2189 }
2190#if QT_CONFIG(quick_viewtransitions)
2191 if (runDelayedRemoveTransition) {
2192 QQmlChangeSet::Change removal;
2193 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
2194 FxViewItem *item = *it;
2195 if (item->index == -1 && (!item->attached || !item->attached->delayRemove())) {
2196 removeItem(item, removal, &removalResult);
2197 removedCount++;
2198 it = visibleItems.erase(it);
2199 } else {
2200 ++it;
2201 }
2202 }
2203 }
2204#endif
2205 *totalRemovalResult += removalResult;
2206 if (!removals.isEmpty()) {
2207 updateVisibleIndex();
2208
2209 // set positions correctly for the next insertion
2210 if (!insertions.isEmpty()) {
2211 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
2212 layoutVisibleItems(removals.first().index);
2213 storeFirstVisibleItemPosition();
2214 }
2215 }
2216
2217 QList<FxViewItem *> newItems;
2218 QList<MovedItem> movingIntoView;
2219
2220 for (int i=0; i<insertions.size(); i++) {
2221 bool wasEmpty = visibleItems.isEmpty();
2222 if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
2223 visibleAffected = true;
2224 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
2225 visibleAffected = true;
2226 if (wasEmpty && !visibleItems.isEmpty())
2227 resetFirstItemPosition();
2228 *totalInsertionResult += insertionResult;
2229
2230 // set positions correctly for the next insertion
2231 if (i < insertions.size() - 1) {
2232 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
2233 layoutVisibleItems(insertions[i].index);
2234 storeFirstVisibleItemPosition();
2235 }
2236 itemCount += insertions[i].count;
2237 }
2238 for (FxViewItem *item : std::as_const(newItems)) {
2239 if (item->attached)
2240 item->attached->emitAdd();
2241 }
2242
2243#if QT_CONFIG(quick_viewtransitions)
2244 // for each item that was moved directly into the view as a result of a move(),
2245 // find the index it was moved from in order to set its initial position, so that we
2246 // can transition it from this "original" position to its new position in the view
2247 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
2248 for (const MovedItem &m : std::as_const(movingIntoView)) {
2249 int fromIndex = findMoveKeyIndex(m.moveKey, removals);
2250 if (fromIndex >= 0) {
2251 if (prevFirstItemInViewIndex >= 0 && fromIndex < prevFirstItemInViewIndex)
2252 repositionItemAt(m.item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
2253 else
2254 repositionItemAt(m.item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
2255 m.item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2256 }
2257 }
2258 }
2259#endif
2260
2261 // reposition visibleItems.first() correctly so that the content y doesn't jump
2262 if (removedCount != prevVisibleItemsCount)
2263 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
2264
2265 // Whatever removed/moved items remain are no longer visible items.
2266#if QT_CONFIG(quick_viewtransitions)
2267 prepareRemoveTransitions(&currentChanges.removedItems);
2268#endif
2269 for (auto it = currentChanges.removedItems.begin();
2270 it != currentChanges.removedItems.end(); ++it) {
2271 releaseItem(it.value(), reusableFlag);
2272 }
2273 currentChanges.removedItems.clear();
2274
2275 if (currentChanges.currentChanged) {
2276 if (currentChanges.currentRemoved && currentItem) {
2277 if (currentItem->item && currentItem->attached)
2278 currentItem->attached->setIsCurrentItem(false);
2279 const bool hadCurrentItem = currentItem != nullptr;
2280 releaseCurrentItem(reusableFlag);
2281 if (hadCurrentItem)
2282 emit q->currentItemChanged();
2283 }
2284 if (!currentIndexCleared)
2285 updateCurrent(currentChanges.newCurrentIndex);
2286 }
2287
2288 if (!visibleAffected)
2289 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
2290 currentChanges.reset();
2291
2292 updateSections();
2293 if (prevItemCount != itemCount)
2294 emitCountChanged();
2295 if (!visibleAffected && viewportChanged)
2296 updateViewport();
2297
2298 return visibleAffected;
2299}
2300
2301bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &removal, ChangeResult *removeResult, int *removedCount)
2302{
2303 Q_Q(QQuickItemView);
2304 bool visibleAffected = false;
2305
2306 if (visibleItems.size() && removal.index + removal.count > visibleItems.constLast()->index) {
2307 if (removal.index > visibleItems.constLast()->index)
2308 removeResult->countChangeAfterVisibleItems += removal.count;
2309 else
2310 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index);
2311 }
2312
2313 QList<FxViewItem*>::Iterator it = visibleItems.begin();
2314 while (it != visibleItems.end()) {
2315 FxViewItem *item = *it;
2316 if (item->index == -1 || item->index < removal.index) {
2317 // already removed, or before removed items
2318 if (!visibleAffected && item->index < removal.index)
2319 visibleAffected = true;
2320 ++it;
2321 } else if (item->index >= removal.index + removal.count) {
2322 // after removed items
2323 item->index -= removal.count;
2324#if QT_CONFIG(quick_viewtransitions)
2325 if (removal.isMove())
2326 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2327 else
2328 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2329#endif
2330 ++it;
2331 } else {
2332 // removed item
2333 visibleAffected = true;
2334 if (!removal.isMove() && item->item && item->attached)
2335 item->attached->emitRemove();
2336
2337 if (item->item && item->attached && item->attached->delayRemove() && !removal.isMove()) {
2338 item->index = -1;
2339 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2340 ++it;
2341 } else {
2342 removeItem(item, removal, removeResult);
2343 if (!removal.isMove())
2344 (*removedCount)++;
2345 it = visibleItems.erase(it);
2346 }
2347 }
2348 }
2349
2350 return visibleAffected;
2351}
2352
2353void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Change &removal, ChangeResult *removeResult)
2354{
2355 if (removeResult->visiblePos.isValid()) {
2356 if (item->position() < removeResult->visiblePos)
2357 updateSizeChangesBeforeVisiblePos(item, removeResult);
2358 else
2359 removeResult->sizeChangesAfterVisiblePos += item->size();
2360 }
2361 if (removal.isMove()) {
2362 currentChanges.removedItems.replace(removal.moveKey(item->index), item);
2363#if QT_CONFIG(quick_viewtransitions)
2364 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2365#endif
2366 } else {
2367 // track item so it is released later
2368 currentChanges.removedItems.insert(QQmlChangeSet::MoveKey(), item);
2369 }
2370 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2371 removeResult->changedFirstItem = true;
2372}
2373
2374void QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult)
2375{
2376 removeResult->sizeChangesBeforeVisiblePos += item->size();
2377}
2378
2379void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2380 qreal prevVisibleItemsFirstPos,
2381 FxViewItem *prevFirstVisible,
2382 ChangeResult *insertionResult,
2383 ChangeResult *removalResult)
2384{
2385 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2386
2387 // reposition visibleItems.first() correctly so that the content y doesn't jump
2388 if (visibleItems.size()) {
2389 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2390 resetFirstItemPosition(prevVisibleItemsFirstPos);
2391
2392 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2393 && prevFirstVisible != *visibleItems.constBegin()) {
2394 // the previous visibleItems.first() was also the first visible item, and it has been
2395 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2396 if (!insertionResult->changedFirstItem)
2397 resetFirstItemPosition(prevVisibleItemsFirstPos);
2398
2399 } else if (prevViewPos.isValid()) {
2400 qreal moveForwardsBy = 0;
2401 qreal moveBackwardsBy = 0;
2402
2403 // shift visibleItems.first() relative to the number of added/removed items
2404 const auto pos = visibleItems.constFirst()->position();
2405 if (pos > prevViewPos) {
2406 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2407 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2408 } else if (pos < prevViewPos) {
2409 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2410 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2411 }
2412 adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2413 }
2414 insertionResult->reset();
2415 removalResult->reset();
2416 }
2417}
2418
2419#if QT_CONFIG(quick_viewtransitions)
2420void QQuickItemViewPrivate::createTransitioner()
2421{
2422 if (!transitioner) {
2423 transitioner = new QQuickItemViewTransitioner;
2424 transitioner->setChangeListener(this);
2425 }
2426}
2427
2428void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2429{
2430 Q_Q(QQuickItemView);
2431 if (!transitioner)
2432 return;
2433
2434 // must call for every visible item to init or discard transitions
2435 QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
2436 for (int i=0; i<visibleItems.size(); i++)
2437 visibleItems[i]->prepareTransition(transitioner, viewBounds);
2438}
2439
2440void QQuickItemViewPrivate::prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems)
2441{
2442 if (!transitioner)
2443 return;
2444
2445 if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)
2446 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2447 for (auto it = removedItems->begin(); it != removedItems->end(); ) {
2448 bool isRemove = it.key().moveId < 0;
2449 if (isRemove) {
2450 FxViewItem *item = *it;
2451 item->trackGeometry(false);
2452 item->releaseAfterTransition = true;
2453 releasePendingTransition.append(item);
2454 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
2455 it = removedItems->erase(it);
2456 } else {
2457 ++it;
2458 }
2459 }
2460 }
2461}
2462
2463bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2464{
2465 // Called for items that have been removed from visibleItems and may now be
2466 // transitioned out of the view. This applies to items that are being directly
2467 // removed, or moved to outside of the view, as well as those that are
2468 // displaced to a position outside of the view due to an insert or move.
2469
2470 if (!transitioner)
2471 return false;
2472
2473 if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2474 repositionItemAt(item, item->index, 0);
2475
2476 bool success = false;
2477 ACTION_IF_DELETED(item, success = item->prepareTransition(transitioner, viewBounds), return success);
2478
2479 if (success) {
2480 item->releaseAfterTransition = true;
2481 return true;
2482 }
2483 return false;
2484}
2485
2486void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2487{
2488 for (int i=0; i<releasePendingTransition.size(); i++) {
2489 if (releasePendingTransition.at(i)->transitionableItem == item) {
2490 releaseItem(releasePendingTransition.takeAt(i), reusableFlag);
2491 return;
2492 }
2493 }
2494}
2495#endif
2496
2497/*
2498 This may return 0 if the item is being created asynchronously.
2499 When the item becomes available, refill() will be called and the item
2500 will be returned on the next call to createItem().
2501*/
2502FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::IncubationMode incubationMode)
2503{
2504 Q_Q(QQuickItemView);
2505
2506 if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous)
2507 return nullptr;
2508
2509#if QT_CONFIG(quick_viewtransitions)
2510 for (int i=0; i<releasePendingTransition.size(); i++) {
2511 if (releasePendingTransition.at(i)->index == modelIndex
2512 && !releasePendingTransition.at(i)->isPendingRemoval()) {
2513 releasePendingTransition[i]->releaseAfterTransition = false;
2514 return releasePendingTransition.takeAt(i);
2515 }
2516 }
2517#endif
2518
2519 inRequest = true;
2520
2521 // The model will run this same range check internally but produce a warning and return nullptr.
2522 // Since we handle this result graciously in our code, we preempt this warning by checking the range ourselves.
2523 QObject* object = modelIndex < model->count() ? model->object(modelIndex, incubationMode) : nullptr;
2524 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
2525
2526 if (!item) {
2527 if (!object) {
2528 if (requestedIndex == -1 && model->incubationStatus(modelIndex) == QQmlIncubator::Loading) {
2529 // The reason we didn't receive an item is because it's incubating async. We keep track
2530 // of this by assigning the index we're waiting for to 'requestedIndex'. This will e.g. let
2531 // the view avoid unnecessary layout calls until the item has been loaded.
2532 requestedIndex = modelIndex;
2533 }
2534 } else {
2535 model->release(object);
2536 if (!delegateValidated) {
2537 delegateValidated = true;
2538 QObject* delegate = q->delegate();
2539 qmlWarning(delegate ? delegate : q) << QQuickItemView::tr("Delegate must be of Item type");
2540 }
2541 }
2542 inRequest = false;
2543 return nullptr;
2544 } else {
2545 // Container removes and instantly deletes items created within ObjectModels.
2546 // We need to account for this to avoid having references to deleted items.
2547 // itemDestroyed is called as a result of adding this listener.
2548 if (qobject_cast<QQmlObjectModel *>(model))
2549 QQuickItemPrivate::get(item)->updateOrAddItemChangeListener(this, itemChangeListenerTypes);
2550
2551 item->setParentItem(q->contentItem());
2552 if (requestedIndex == modelIndex)
2553 requestedIndex = -1;
2554 FxViewItem *viewItem = newViewItem(modelIndex, item);
2555 if (viewItem) {
2556 viewItem->index = modelIndex;
2557 // do other set up for the new item that should not happen
2558 // until after bindings are evaluated
2559 initializeViewItem(viewItem);
2560 unrequestedItems.remove(item);
2561 }
2562 inRequest = false;
2563 return viewItem;
2564 }
2565}
2566
2567void QQuickItemView::createdItem(int index, QObject* object)
2568{
2569 Q_D(QQuickItemView);
2570
2571 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2572 if (!d->inRequest) {
2573 d->unrequestedItems.insert(item, index);
2574 d->requestedIndex = -1;
2575 if (d->hasPendingChanges())
2576 d->layout();
2577 else
2578 d->refill();
2579 if (d->unrequestedItems.contains(item))
2580 d->repositionPackageItemAt(item, index);
2581 else if (index == d->currentIndex)
2582 d->updateCurrent(index);
2583 }
2584}
2585
2586void QQuickItemView::initItem(int, QObject *object)
2587{
2588 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2589 if (item) {
2590 if (qFuzzyIsNull(item->z()))
2591 item->setZ(1);
2592 item->setParentItem(contentItem());
2593 QQuickItemPrivate::get(item)->setCulled(true);
2594 }
2595}
2596
2597// This is called when the model (if it's a QQmlInstanceModel) emits destroyingItem.
2598void QQuickItemView::destroyingItem(QObject *object)
2599{
2600 Q_D(QQuickItemView);
2601 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2602 if (item) {
2603 item->setParentItem(nullptr);
2604 d->unrequestedItems.remove(item);
2605 QQuickItemPrivate::get(item)->removeItemChangeListener(d, itemChangeListenerTypes);
2606 }
2607}
2608
2609void QQuickItemView::onItemPooled(int modelIndex, QObject *object)
2610{
2611 Q_UNUSED(modelIndex);
2612
2613 if (auto *attached = d_func()->getAttachedObject(object))
2614 emit attached->pooled();
2615}
2616
2617void QQuickItemView::onItemReused(int modelIndex, QObject *object)
2618{
2619 Q_UNUSED(modelIndex);
2620
2621 if (auto *attached = d_func()->getAttachedObject(object))
2622 emit attached->reused();
2623}
2624
2625bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag)
2626{
2627 Q_Q(QQuickItemView);
2628 if (!item)
2629 return true;
2630 if (trackedItem == item)
2631 trackedItem = nullptr;
2632 item->trackGeometry(false);
2633
2634 QQmlInstanceModel::ReleaseFlags flags = {};
2635 if (QPointer<QQuickItem> quickItem = item->item) {
2636 if (model) {
2637 flags = model->release(quickItem, reusableFlag);
2638 if (!flags) {
2639 // item was not destroyed, and we no longer reference it.
2640 if (quickItem->parentItem() == contentItem) {
2641 // Only cull the item if its parent item is still our contentItem.
2642 // One case where this can happen is moving an item out of one ObjectModel and into another.
2643 QQuickItemPrivate::get(quickItem)->setCulled(true);
2644 }
2645 // If deleteLater was called, the item isn't long for this world and so we shouldn't store references to it.
2646 // This can happen when a Repeater is used to populate items in SwipeView's ListView contentItem.
2647 if (!isClearing && !QObjectPrivate::get(quickItem)->deleteLaterCalled)
2648 unrequestedItems.insert(quickItem, model->indexOf(quickItem, q));
2649 } else if (flags & QQmlInstanceModel::Destroyed) {
2650 quickItem->setParentItem(nullptr);
2651 } else if (flags & QQmlInstanceModel::Pooled) {
2652 item->setVisible(false);
2653 }
2654 }
2655
2656 QQuickItemPrivate::get(quickItem)->removeItemChangeListener(this, itemChangeListenerTypes);
2657 delete item->transitionableItem;
2658 item->transitionableItem = nullptr;
2659 }
2660
2661 delete item;
2662 return flags != QQmlInstanceModel::Referenced;
2663}
2664
2665/*!
2666 \internal
2667
2668 Called when an item created in an ObjectModel is deleted rather than
2669 removing it via the model.
2670
2671 Similar in what it does to destroyRemoved except that it intentionally
2672 doesn't account for delayRemove.
2673*/
2674void QQuickItemViewPrivate::itemDestroyed(QQuickItem *item)
2675{
2676 // We can't check model->indexOf(item, q_func()) here, because the item
2677 // may not exist there, so we instead check visibleItems.
2678 FxViewItem *visibleFxItem = nullptr;
2679 const int indexOfItem = -1;
2680 for (auto *fxItem : std::as_const(visibleItems)) {
2681 if (fxItem->item == item) {
2682 visibleFxItem = fxItem;
2683 break;
2684 }
2685 }
2686
2687 // Make sure that we don't try to clean up the same FxViewItem twice,
2688 // as apparently there can be two FxViewItems for the same QQuickItem.
2689 if (currentItem && visibleFxItem)
2690 Q_ASSERT(currentItem != visibleFxItem);
2691
2692 if (visibleFxItem) {
2693 qCDebug(lcItemViewDelegateLifecycle) << "removing deleted item"
2694 << item << visibleFxItem << "at index" << indexOfItem << "without running transitions";
2695 // We need to remove it from visibleItems manually, as we don't want to call
2696 // removeNonVisibleItems since it won't remove items with transitions.
2697 const bool removedVisibleFxItem = visibleItems.removeOne(visibleFxItem);
2698 Q_ASSERT(removedVisibleFxItem);
2699 releaseItem(visibleFxItem, QQmlDelegateModel::NotReusable);
2700 }
2701
2702 if (currentItem && currentItem->item == item) {
2703 releaseItem(currentItem, QQmlDelegateModel::NotReusable);
2704 currentItem = nullptr;
2705 }
2706
2707 // Update the positioning of the items.
2708 forceLayoutPolish();
2709}
2710
2711QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2712{
2713 QQuickItem *item = nullptr;
2714 if (!inRequest) {
2715 inRequest = true;
2716 item = createComponentItem(highlightComponent, 0.0, true);
2717 inRequest = false;
2718 }
2719 return item;
2720}
2721
2722QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault) const
2723{
2724 Q_Q(const QQuickItemView);
2725
2726 QQuickItem *item = nullptr;
2727 if (component) {
2728 QQmlContext *context = component->creationContext();
2729 if (!context)
2730 context = qmlContext(q);
2731
2732 if (QObject *nobj = component->beginCreate(context)) {
2733 item = qobject_cast<QQuickItem *>(nobj);
2734 if (!item)
2735 delete nobj;
2736 }
2737 } else if (createDefault) {
2738 item = new QQuickItem;
2739 }
2740 if (item) {
2741 if (qFuzzyIsNull(item->z()))
2742 item->setZ(zValue);
2743 QQml_setParent_noEvent(item, q->contentItem());
2744 item->setParentItem(q->contentItem());
2745
2746 initializeComponentItem(item);
2747 }
2748 if (component)
2749 component->completeCreate();
2750 return item;
2751}
2752
2753/*!
2754 \internal
2755
2756 Allows derived classes to do any initialization required for \a item
2757 before completeCreate() is called on it. For example, any attached
2758 properties required by the item can be set.
2759
2760 This is similar to initItem(), but as that has logic specific to
2761 delegate items, we use a separate function for non-delegates.
2762*/
2763void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const
2764{
2765 Q_UNUSED(item);
2766}
2767
2768void QQuickItemViewPrivate::updateTrackedItem()
2769{
2770 Q_Q(QQuickItemView);
2771 FxViewItem *item = currentItem;
2772 if (highlight)
2773 item = highlight.get();
2774 trackedItem = item;
2775
2776 if (trackedItem)
2777 q->trackedPositionChanged();
2778}
2779
2780void QQuickItemViewPrivate::updateUnrequestedIndexes()
2781{
2782 Q_Q(QQuickItemView);
2783 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(), end = unrequestedItems.end(); it != end; ++it)
2784 *it = model->indexOf(it.key(), q);
2785}
2786
2787void QQuickItemViewPrivate::updateUnrequestedPositions()
2788{
2789 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) {
2790 if (it.value() >= 0)
2791 repositionPackageItemAt(it.key(), it.value());
2792 }
2793}
2794
2795void QQuickItemViewPrivate::updateVisibleIndex()
2796{
2797 typedef QList<FxViewItem*>::const_iterator FxViewItemListConstIt;
2798
2799 visibleIndex = 0;
2800 for (FxViewItemListConstIt it = visibleItems.constBegin(), cend = visibleItems.constEnd(); it != cend; ++it) {
2801 if ((*it)->index != -1) {
2802 visibleIndex = (*it)->index;
2803 break;
2804 }
2805 }
2806}
2807
2808QQmlDelegateModel::DelegateModelAccess QQuickItemView::delegateModelAccess() const
2809{
2810 Q_D(const QQuickItemView);
2811 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->model))
2812 return dataModel->delegateModelAccess();
2813 return QQmlDelegateModel::Qt5ReadWrite;
2814}
2815
2816void QQuickItemView::setDelegateModelAccess(
2817 QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
2818{
2819 Q_D(QQuickItemView);
2820 const auto setExplicitDelegateModelAccess = [&](QQmlDelegateModel *delegateModel) {
2821 delegateModel->setDelegateModelAccess(delegateModelAccess);
2822 d->explicitDelegateModelAccess = true;
2823 };
2824
2825 if (!d->model) {
2826 if (delegateModelAccess == QQmlDelegateModel::Qt5ReadWrite) {
2827 // Explicitly set delegateModelAccess to Legacy. We can do this without model.
2828 d->explicitDelegateModelAccess = true;
2829 return;
2830 }
2831
2832 setExplicitDelegateModelAccess(QQmlDelegateModel::createForView(this, d));
2833
2834 // The new model is not connected to applyDelegateModelAccessChange, yet. We only do this
2835 // once there is actual data, via an explicit setModel(). So we have to manually emit the
2836 // delegateModelAccessChanged() here.
2837 emit delegateModelAccessChanged();
2838 return;
2839 }
2840
2841 if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(d->model)) {
2842 // Disable the warning in applyDelegateModelAccessChange since the new delegate model
2843 // access is also explicit.
2844 d->explicitDelegateModelAccess = false;
2845 setExplicitDelegateModelAccess(delegateModel);
2846 return;
2847 }
2848
2849 if (delegateModelAccess == QQmlDelegateModel::Qt5ReadWrite) {
2850 d->explicitDelegateModelAccess = true; // Explicitly set null delegate always works
2851 } else {
2852 qmlWarning(this) << "Cannot set a delegateModelAccess on an explicitly provided "
2853 "non-DelegateModel";
2854 }
2855}
2856
2857QT_END_NAMESPACE
2858
2859#include "moc_qquickitemview_p.cpp"
Combined button and popup list for selecting options.
static const QQuickItemPrivate::ChangeTypes itemChangeListenerTypes
static FxViewItem * fxViewItemAtPosition(const QList< FxViewItem * > &items, qreal x, qreal y)
#define QML_VIEW_DEFAULTCACHEBUFFER