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 // Determine the position. Use contentStartOffset() when positioning item 0 at
870 // the beginning to avoid positionAt() estimation errors when the list includes
871 // section headers and item 0 is outside the cache buffer.
872 qreal itemPos = (mode == QQuickItemView::Beginning && idx == 0)
873 ? contentStartOffset()
874 : positionAt(idx);
875 changedVisibleIndex(idx);
876
877 // save the currently visible items in case any of them end up visible again
878 const QList<FxViewItem *> oldVisible = visibleItems;
879 visibleItems.clear();
880
881 if (mode == QQuickItemView::Beginning && idx == 0) {
882 // Re-sync visiblePos after visibleItems has been cleared.
883 changedVisibleIndex(idx);
884 // Invalidate the extent cache. With visibleItems cleared,
885 // originPosition() returns 0, so a fresh minYExtent() is
886 // correct.
887 // Without this, a stale minYExtent() causes setViewportY()
888 // to clamp the position when boundsMovement == StopAtBounds,
889 // and causes fixupPosition() to shift contentY away from 0.
890 markExtentsDirty();
891 }
892
893 setPosition(qMin(itemPos, maxExtent));
894
895 // now release the reference to all the old visible items.
896 for (FxViewItem *item : oldVisible)
897 releaseItem(item, reusableFlag);
898 item = visibleItem(idx);
899 }
900 if (item) {
901 const bool stickyHeader = hasStickyHeader();
902 const bool stickyFooter = hasStickyFooter();
903 const qreal stickyHeaderSize = stickyHeader ? headerSize() : 0;
904 const qreal stickyFooterSize = stickyFooter ? footerSize() : 0;
905
906 const qreal itemPos = item->position();
907 switch (mode) {
908 case QQuickItemView::Beginning:
909 pos = itemPos;
910 if (header && (index < 0 || stickyHeader))
911 pos -= headerSize();
912 break;
913 case QQuickItemView::Center:
914 pos = itemPos - (viewSize - item->size())/2;
915 break;
916 case QQuickItemView::End:
917 pos = itemPos - viewSize + item->size();
918 if (footer && (index >= modelCount || stickyFooter))
919 pos += footerSize();
920 break;
921 case QQuickItemView::Visible:
922 if (itemPos > pos + viewSize - stickyFooterSize)
923 pos = item->endPosition() - viewSize + stickyFooterSize;
924 else if (item->endPosition() <= pos - stickyHeaderSize)
925 pos = itemPos - stickyHeaderSize;
926 break;
927 case QQuickItemView::Contain:
928 if (item->endPosition() >= pos + viewSize + stickyFooterSize)
929 pos = itemPos - viewSize + item->size() + stickyFooterSize;
930 if (itemPos - stickyHeaderSize < pos)
931 pos = itemPos - stickyHeaderSize;
932 break;
933 case QQuickItemView::SnapPosition:
934 pos = itemPos - highlightRangeStart - stickyHeaderSize;
935 break;
936 }
937 pos = qMin(pos, maxExtent);
938 qreal minExtent = calculatedMinExtent();
939 pos = qMax(pos, minExtent);
940 moveReason = QQuickItemViewPrivate::Other;
941 setPosition(pos);
942
943 if (highlight) {
944 if (autoHighlight)
945 resetHighlightPosition();
946 updateHighlight();
947 }
948 }
949 fixupPosition();
950}
951
952void QQuickItemView::positionViewAtIndex(int index, int mode)
953{
954 Q_D(QQuickItemView);
955 if (!d->isValid() || index < 0 || index >= d->model->count())
956 return;
957 d->positionViewAtIndex(index, mode);
958}
959
960
961void QQuickItemView::positionViewAtBeginning()
962{
963 Q_D(QQuickItemView);
964 if (!d->isValid())
965 return;
966 d->positionViewAtIndex(-1, Beginning);
967}
968
969void QQuickItemView::positionViewAtEnd()
970{
971 Q_D(QQuickItemView);
972 if (!d->isValid())
973 return;
974 d->positionViewAtIndex(d->model->count(), End);
975}
976
977static FxViewItem * fxViewItemAtPosition(const QList<FxViewItem *> &items, qreal x, qreal y)
978{
979 for (FxViewItem *item : items) {
980 if (item->contains(x, y))
981 return item;
982 }
983 return nullptr;
984}
985
986int QQuickItemView::indexAt(qreal x, qreal y) const
987{
988 Q_D(const QQuickItemView);
989 const FxViewItem *item = fxViewItemAtPosition(d->visibleItems, x, y);
990 return item ? item->index : -1;
991}
992
993QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
994{
995 Q_D(const QQuickItemView);
996 const FxViewItem *item = fxViewItemAtPosition(d->visibleItems, x, y);
997 return item ? item->item : nullptr;
998}
999
1000QQuickItem *QQuickItemView::itemAtIndex(int index) const
1001{
1002 Q_D(const QQuickItemView);
1003 const FxViewItem *item = d->visibleItem(index);
1004 return item ? item->item : nullptr;
1005}
1006
1007void QQuickItemView::forceLayout()
1008{
1009 Q_D(QQuickItemView);
1010 if (isComponentComplete() && (d->currentChanges.hasPendingChanges() || d->forceLayout))
1011 d->layout();
1012}
1013
1014void QQuickItemViewPrivate::applyPendingChanges()
1015{
1016 Q_Q(QQuickItemView);
1017 if (q->isComponentComplete() && currentChanges.hasPendingChanges())
1018 layout();
1019}
1020
1021int QQuickItemViewPrivate::findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QList<QQmlChangeSet::Change> &changes) const
1022{
1023 for (int i=0; i<changes.size(); i++) {
1024 for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
1025 if (changes[i].moveKey(j) == key)
1026 return j;
1027 }
1028 }
1029 return -1;
1030}
1031
1032qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
1033{
1034 Q_Q(const QQuickItemView);
1035
1036 qreal highlightStart;
1037 qreal highlightEnd;
1038 qreal endPositionFirstItem = 0;
1039 qreal extent = -startPosition() + axisData.startMargin;
1040 if (isContentFlowReversed()) {
1041 if (model && model->count())
1042 endPositionFirstItem = positionAt(model->count()-1);
1043 else
1044 extent += headerSize();
1045 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1046 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1047 extent += footerSize();
1048 qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
1049 if (extent < maxExtentAlongAxis)
1050 extent = maxExtentAlongAxis;
1051 } else {
1052 endPositionFirstItem = endPositionAt(0);
1053 highlightStart = highlightRangeStart;
1054 highlightEnd = highlightRangeEnd;
1055 extent += headerSize();
1056 }
1057 if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1058 extent += highlightStart;
1059 FxViewItem *firstItem = visibleItem(0);
1060 if (firstItem)
1061 extent -= firstItem->sectionSize();
1062 extent = isContentFlowReversed()
1063 ? qMin(extent, endPositionFirstItem + highlightEnd)
1064 : qMax(extent, -(endPositionFirstItem - highlightEnd));
1065 }
1066 return extent;
1067}
1068
1069qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
1070{
1071 Q_Q(const QQuickItemView);
1072
1073 qreal highlightStart;
1074 qreal highlightEnd;
1075 qreal lastItemPosition = 0;
1076 qreal extent = 0;
1077 if (isContentFlowReversed()) {
1078 highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1079 highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1080 lastItemPosition = endPosition();
1081 } else {
1082 highlightStart = highlightRangeStart;
1083 highlightEnd = highlightRangeEnd;
1084 if (model && model->count())
1085 lastItemPosition = positionAt(model->count()-1);
1086 }
1087 if (!model || !model->count()) {
1088 if (!isContentFlowReversed())
1089 maxExtent = header ? -headerSize() : 0;
1090 extent += forXAxis ? q->width() : q->height();
1091 } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1092 extent = -(lastItemPosition - highlightStart);
1093 if (highlightEnd != highlightStart) {
1094 extent = isContentFlowReversed()
1095 ? qMax(extent, -(endPosition() - highlightEnd))
1096 : qMin(extent, -(endPosition() - highlightEnd));
1097 }
1098 } else {
1099 extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
1100 }
1101 if (isContentFlowReversed()) {
1102 extent -= headerSize();
1103 extent -= axisData.endMargin;
1104 } else {
1105 extent -= footerSize();
1106 extent -= axisData.endMargin;
1107 qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
1108 if (extent > minExtentAlongAxis)
1109 extent = minExtentAlongAxis;
1110 }
1111
1112 return extent;
1113}
1114
1115qreal QQuickItemViewPrivate::calculatedMinExtent() const
1116{
1117 Q_Q(const QQuickItemView);
1118 qreal minExtent;
1119 if (layoutOrientation() == Qt::Vertical)
1120 minExtent = isContentFlowReversed() ? q->maxYExtent() - size(): -q->minYExtent();
1121 else
1122 minExtent = isContentFlowReversed() ? q->maxXExtent() - size(): -q->minXExtent();
1123 return minExtent;
1124
1125}
1126
1127qreal QQuickItemViewPrivate::calculatedMaxExtent() const
1128{
1129 Q_Q(const QQuickItemView);
1130 qreal maxExtent;
1131 if (layoutOrientation() == Qt::Vertical)
1132 maxExtent = isContentFlowReversed() ? q->minYExtent() - size(): -q->maxYExtent();
1133 else
1134 maxExtent = isContentFlowReversed() ? q->minXExtent() - size(): -q->maxXExtent();
1135 return maxExtent;
1136}
1137
1138void QQuickItemViewPrivate::connectModel(QQuickItemView *q, QQmlDelegateModelPointer *model)
1139{
1140 QQmlInstanceModel *instanceModel = model->instanceModel();
1141 if (!instanceModel)
1142 return;
1143
1144 bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1145 QObject::connect(instanceModel, &QQmlInstanceModel::createdItem,
1146 q, &QQuickItemView::createdItem);
1147 QObject::connect(instanceModel, &QQmlInstanceModel::initItem,
1148 q, &QQuickItemView::initItem);
1149 QObject::connect(instanceModel, &QQmlInstanceModel::destroyingItem,
1150 q, &QQuickItemView::destroyingItem);
1151 if (QQmlDelegateModel *delegateModel = model->delegateModel()) {
1152 QObject::connect(delegateModel, &QQmlInstanceModel::itemPooled,
1153 q, &QQuickItemView::onItemPooled);
1154 QObject::connect(delegateModel, &QQmlInstanceModel::itemReused,
1155 q, &QQuickItemView::onItemReused);
1156 }
1157
1158 if (q->isComponentComplete()) {
1159 updateSectionCriteria();
1160 refill();
1161 /* Setting currentIndex to -2 ensures that we always enter the "currentIndex changed"
1162 code path in setCurrentIndex, updating bindings depending on currentIndex.*/
1163 currentIndex = -2;
1164 q->setCurrentIndex(instanceModel->count() > 0 ? 0 : -1);
1165 updateViewport();
1166
1167#if QT_CONFIG(quick_viewtransitions)
1168 if (transitioner && transitioner->populateTransition) {
1169 transitioner->setPopulateTransitionEnabled(true);
1170 forceLayoutPolish();
1171 }
1172#endif
1173 }
1174
1175 QObject::connect(instanceModel, &QQmlInstanceModel::modelUpdated,
1176 q, &QQuickItemView::modelUpdated);
1177 if (QQmlDelegateModel *dataModel = model->delegateModel()) {
1178 QObjectPrivate::connect(
1179 dataModel, &QQmlDelegateModel::delegateChanged,
1180 this, &QQuickItemViewPrivate::applyDelegateChange);
1181 QObjectPrivate::connect(
1182 dataModel, &QQmlDelegateModel::delegateModelAccessChanged,
1183 this, &QQuickItemViewPrivate::applyDelegateModelAccessChange);
1184 if (ownModel) {
1185 QObject::connect(dataModel, &QQmlDelegateModel::modelChanged,
1186 q, &QQuickItemView::modelChanged);
1187 }
1188 }
1189
1190 emitCountChanged();
1191}
1192
1193void QQuickItemViewPrivate::disconnectModel(QQuickItemView *q, QQmlDelegateModelPointer *model)
1194{
1195 QQmlInstanceModel *instanceModel = model->instanceModel();
1196 if (!instanceModel)
1197 return;
1198
1199
1200 QObject::disconnect(instanceModel, &QQmlInstanceModel::modelUpdated,
1201 q, &QQuickItemView::modelUpdated);
1202 QObject::disconnect(instanceModel, &QQmlInstanceModel::initItem,
1203 q, &QQuickItemView::initItem);
1204 QObject::disconnect(instanceModel, &QQmlInstanceModel::createdItem,
1205 q, &QQuickItemView::createdItem);
1206 QObject::disconnect(instanceModel, &QQmlInstanceModel::destroyingItem,
1207 q, &QQuickItemView::destroyingItem);
1208 if (QQmlDelegateModel *delegateModel = model->delegateModel()) {
1209 QObject::disconnect(delegateModel, &QQmlInstanceModel::itemPooled,
1210 q, &QQuickItemView::onItemPooled);
1211 QObject::disconnect(delegateModel, &QQmlInstanceModel::itemReused,
1212 q, &QQuickItemView::onItemReused);
1213 QObjectPrivate::disconnect(
1214 delegateModel, &QQmlDelegateModel::delegateChanged,
1215 this, &QQuickItemViewPrivate::applyDelegateChange);
1216 QObjectPrivate::disconnect(
1217 delegateModel, &QQmlDelegateModel::delegateModelAccessChanged,
1218 this, &QQuickItemViewPrivate::applyDelegateModelAccessChange);
1219 if (ownModel) {
1220 QObject::disconnect(delegateModel, &QQmlDelegateModel::modelChanged,
1221 q, &QQuickItemView::modelChanged);
1222 }
1223 }
1224}
1225
1226void QQuickItemViewPrivate::applyDelegateChange()
1227{
1228 Q_Q(QQuickItemView);
1229
1230 QQmlDelegateModel::applyDelegateChangeOnView(q, this);
1231
1232 releaseVisibleItems(QQmlDelegateModel::NotReusable);
1233 releaseCurrentItem(QQmlDelegateModel::NotReusable);
1234 updateSectionCriteria();
1235 refill();
1236 moveReason = QQuickItemViewPrivate::SetIndex;
1237 updateCurrent(currentIndex);
1238 if (highlight && currentItem) {
1239 if (autoHighlight)
1240 resetHighlightPosition();
1241 updateTrackedItem();
1242 }
1243 moveReason = QQuickItemViewPrivate::Other;
1244 updateViewport();
1245}
1246
1247// for debugging only
1248void QQuickItemViewPrivate::checkVisible() const
1249{
1250 int skip = 0;
1251 for (int i = 0; i < visibleItems.size(); ++i) {
1252 FxViewItem *item = visibleItems.at(i);
1253 if (item->index == -1) {
1254 ++skip;
1255 } else if (item->index != visibleIndex + i - skip) {
1256 qFatal("index %d %d %d", visibleIndex, i, item->index);
1257 }
1258 }
1259}
1260
1261// for debugging only
1262void QQuickItemViewPrivate::showVisibleItems() const
1263{
1264 qDebug() << "Visible items:";
1265 for (FxViewItem *item : visibleItems) {
1266 qDebug() << "\t" << item->index
1267 << item->item->objectName()
1268 << item->position();
1269 }
1270}
1271
1272// Simplifies debugging of count.
1273void QQuickItemViewPrivate::emitCountChanged()
1274{
1275 Q_Q(QQuickItemView);
1276 qCDebug(lcCount).nospace() << "about to emit countChanged for " << q << "; count changed to " << q->count();
1277 emit q->countChanged();
1278}
1279
1280void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
1281 const QRectF &oldGeometry)
1282{
1283 Q_Q(QQuickItemView);
1284 QQuickFlickablePrivate::itemGeometryChanged(item, change, oldGeometry);
1285 if (!q->isComponentComplete())
1286 return;
1287
1288 if (header && header->item == item) {
1289 updateHeader();
1290 markExtentsDirty();
1291 updateViewport();
1292 if (!q->isMoving() && !q->isFlicking())
1293 fixupPosition();
1294 } else if (footer && footer->item == item) {
1295 updateFooter();
1296 markExtentsDirty();
1297 updateViewport();
1298 if (!q->isMoving() && !q->isFlicking())
1299 fixupPosition();
1300 }
1301
1302 if (currentItem && currentItem->item == item) {
1303 // don't allow item movement transitions to trigger a re-layout and
1304 // start new transitions
1305 bool prevInLayout = inLayout;
1306#if QT_CONFIG(quick_viewtransitions)
1307 if (!inLayout) {
1308 FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : nullptr;
1309 if (actualItem && actualItem->transitionRunning())
1310 inLayout = true;
1311 }
1312#endif
1313 updateHighlight();
1314 inLayout = prevInLayout;
1315 }
1316
1317 if (trackedItem && trackedItem->item == item)
1318 q->trackedPositionChanged();
1319}
1320
1321void QQuickItemView::destroyRemoved()
1322{
1323 Q_D(QQuickItemView);
1324
1325#if QT_CONFIG(quick_viewtransitions)
1326 bool hasRemoveTransition = false;
1327 bool hasRemoveTransitionAsTarget = false;
1328 if (d->transitioner) {
1329 hasRemoveTransition = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false);
1330 hasRemoveTransitionAsTarget = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true);
1331 }
1332#endif
1333
1334 for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1335 it != d->visibleItems.end();) {
1336 FxViewItem *item = *it;
1337 if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) {
1338#if QT_CONFIG(quick_viewtransitions)
1339 if (hasRemoveTransitionAsTarget) {
1340 // don't remove from visibleItems until next layout()
1341 d->runDelayedRemoveTransition = true;
1342 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1343 ++it;
1344 } else {
1345 if (hasRemoveTransition)
1346 d->runDelayedRemoveTransition = true;
1347#endif
1348 d->releaseItem(item, d->reusableFlag);
1349 it = d->visibleItems.erase(it);
1350#if QT_CONFIG(quick_viewtransitions)
1351 }
1352#endif
1353 } else {
1354 ++it;
1355 }
1356 }
1357
1358 // Correct the positioning of the items
1359 d->forceLayoutPolish();
1360}
1361
1362void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
1363{
1364 Q_D(QQuickItemView);
1365 if (reset) {
1366 cancelFlick();
1367#if QT_CONFIG(quick_viewtransitions)
1368 if (d->transitioner)
1369 d->transitioner->setPopulateTransitionEnabled(true);
1370#endif
1371 d->moveReason = QQuickItemViewPrivate::SetIndex;
1372 d->regenerate();
1373 if (d->highlight && d->currentItem) {
1374 if (d->autoHighlight)
1375 d->resetHighlightPosition();
1376 d->updateTrackedItem();
1377 }
1378 d->moveReason = QQuickItemViewPrivate::Other;
1379 d->emitCountChanged();
1380#if QT_CONFIG(quick_viewtransitions)
1381 if (d->transitioner && d->transitioner->populateTransition)
1382 d->forceLayoutPolish();
1383#endif
1384 } else {
1385 if (d->inLayout) {
1386 d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
1387 d->bufferedChanges.applyChanges(changeSet);
1388 } else {
1389 if (d->bufferedChanges.hasPendingChanges()) {
1390 d->currentChanges.applyBufferedChanges(d->bufferedChanges);
1391 d->bufferedChanges.reset();
1392 }
1393 d->currentChanges.prepare(d->currentIndex, d->itemCount);
1394 d->currentChanges.applyChanges(changeSet);
1395 }
1396 polish();
1397 }
1398}
1399
1400void QQuickItemView::animStopped()
1401{
1402 Q_D(QQuickItemView);
1403 d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1404 d->refillOrLayout();
1405 if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1406 d->updateHighlight();
1407}
1408
1409
1410void QQuickItemView::trackedPositionChanged()
1411{
1412 Q_D(QQuickItemView);
1413 if (!d->trackedItem || !d->currentItem)
1414 return;
1415
1416 if (d->inLayout) {
1417 polish();
1418 return;
1419 }
1420
1421 const bool needMoveToTrackHighlight = d->autoHighlight || d->highlightRange != NoHighlightRange;
1422
1423 if (d->moveReason == QQuickItemViewPrivate::SetIndex && needMoveToTrackHighlight) {
1424 qreal trackedPos = d->trackedItem->position();
1425 qreal trackedSize = d->trackedItem->size();
1426 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1427 qreal pos = viewPos;
1428 if (d->haveHighlightRange) {
1429 if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1430 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1431 if (trackedPos < pos + d->highlightRangeStart)
1432 pos = trackedPos - d->highlightRangeStart;
1433 if (d->highlightRange != StrictlyEnforceRange) {
1434 qreal maxExtent = d->calculatedMaxExtent();
1435 if (pos > maxExtent)
1436 pos = maxExtent;
1437 qreal minExtent = d->calculatedMinExtent();
1438 if (pos < minExtent)
1439 pos = minExtent;
1440 }
1441 } else {
1442 if (d->trackedItem != d->currentItem) {
1443 // also make section header visible
1444 trackedPos -= d->currentItem->sectionSize();
1445 trackedSize += d->currentItem->sectionSize();
1446 }
1447 qreal trackedEndPos = d->trackedItem->endPosition();
1448 qreal toItemPos = d->currentItem->position();
1449 qreal toItemEndPos = d->currentItem->endPosition();
1450 if (d->showHeaderForIndex(d->currentIndex)) {
1451 qreal startOffset = -d->contentStartOffset();
1452 trackedPos -= startOffset;
1453 trackedEndPos -= startOffset;
1454 toItemPos -= startOffset;
1455 toItemEndPos -= startOffset;
1456 } else if (d->showFooterForIndex(d->currentIndex)) {
1457 qreal endOffset = d->footerSize();
1458 if (d->layoutOrientation() == Qt::Vertical) {
1459 if (d->isContentFlowReversed())
1460 endOffset += d->vData.startMargin;
1461 else
1462 endOffset += d->vData.endMargin;
1463 } else {
1464 if (d->isContentFlowReversed())
1465 endOffset += d->hData.startMargin;
1466 else
1467 endOffset += d->hData.endMargin;
1468 }
1469 trackedPos += endOffset;
1470 trackedEndPos += endOffset;
1471 toItemPos += endOffset;
1472 toItemEndPos += endOffset;
1473 }
1474
1475 if (trackedEndPos >= viewPos + d->size()
1476 && toItemEndPos >= viewPos + d->size()) {
1477 if (trackedEndPos <= toItemEndPos) {
1478 pos = trackedEndPos - d->size();
1479 if (trackedSize > d->size())
1480 pos = trackedPos;
1481 } else {
1482 pos = toItemEndPos - d->size();
1483 if (d->currentItem->size() > d->size())
1484 pos = d->currentItem->position();
1485 }
1486 }
1487 if (trackedPos < pos && toItemPos < pos)
1488 pos = qMax(trackedPos, toItemPos);
1489 }
1490 if (viewPos != pos) {
1491 d->calcVelocity = true;
1492 d->setPosition(pos);
1493 d->calcVelocity = false;
1494 }
1495 }
1496}
1497
1498void QQuickItemView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1499{
1500 Q_D(QQuickItemView);
1501 d->markExtentsDirty();
1502 if (isComponentComplete() && (d->isValid() || !d->visibleItems.isEmpty()))
1503 d->forceLayoutPolish();
1504 QQuickFlickable::geometryChange(newGeometry, oldGeometry);
1505}
1506
1507qreal QQuickItemView::minYExtent() const
1508{
1509 Q_D(const QQuickItemView);
1510 if (d->layoutOrientation() == Qt::Horizontal)
1511 return QQuickFlickable::minYExtent();
1512
1513 if (d->vData.minExtentDirty) {
1514 d->minExtent = d->minExtentForAxis(d->vData, false);
1515 d->vData.minExtentDirty = false;
1516 }
1517
1518 return d->minExtent;
1519}
1520
1521qreal QQuickItemView::maxYExtent() const
1522{
1523 Q_D(const QQuickItemView);
1524 if (d->layoutOrientation() == Qt::Horizontal)
1525 return QQuickFlickable::maxYExtent();
1526
1527 if (d->vData.maxExtentDirty) {
1528 d->maxExtent = d->maxExtentForAxis(d->vData, false);
1529 d->vData.maxExtentDirty = false;
1530 }
1531
1532 return d->maxExtent;
1533}
1534
1535qreal QQuickItemView::minXExtent() const
1536{
1537 Q_D(const QQuickItemView);
1538 if (d->layoutOrientation() == Qt::Vertical)
1539 return QQuickFlickable::minXExtent();
1540
1541 if (d->hData.minExtentDirty) {
1542 d->minExtent = d->minExtentForAxis(d->hData, true);
1543 d->hData.minExtentDirty = false;
1544 }
1545
1546 return d->minExtent;
1547}
1548
1549qreal QQuickItemView::maxXExtent() const
1550{
1551 Q_D(const QQuickItemView);
1552 if (d->layoutOrientation() == Qt::Vertical)
1553 return QQuickFlickable::maxXExtent();
1554
1555 if (d->hData.maxExtentDirty) {
1556 d->maxExtent = d->maxExtentForAxis(d->hData, true);
1557 d->hData.maxExtentDirty = false;
1558 }
1559
1560 return d->maxExtent;
1561}
1562
1563void QQuickItemView::setContentX(qreal pos)
1564{
1565 Q_D(QQuickItemView);
1566 // Positioning the view manually should override any current movement state
1567 d->moveReason = QQuickItemViewPrivate::Other;
1568 QQuickFlickable::setContentX(pos);
1569}
1570
1571void QQuickItemView::setContentY(qreal pos)
1572{
1573 Q_D(QQuickItemView);
1574 // Positioning the view manually should override any current movement state
1575 d->moveReason = QQuickItemViewPrivate::Other;
1576 QQuickFlickable::setContentY(pos);
1577}
1578
1579qreal QQuickItemView::originX() const
1580{
1581 Q_D(const QQuickItemView);
1582 if (d->layoutOrientation() == Qt::Horizontal
1583 && effectiveLayoutDirection() == Qt::RightToLeft
1584 && contentWidth() < width()) {
1585 return -d->lastPosition() - d->footerSize();
1586 }
1587 return QQuickFlickable::originX();
1588}
1589
1590qreal QQuickItemView::originY() const
1591{
1592 Q_D(const QQuickItemView);
1593 if (d->layoutOrientation() == Qt::Vertical
1594 && d->verticalLayoutDirection == QQuickItemView::BottomToTop
1595 && contentHeight() < height()) {
1596 return -d->lastPosition() - d->footerSize();
1597 }
1598 return QQuickFlickable::originY();
1599}
1600
1601void QQuickItemView::updatePolish()
1602{
1603 Q_D(QQuickItemView);
1604 QQuickFlickable::updatePolish();
1605 d->layout();
1606}
1607
1608void QQuickItemView::componentComplete()
1609{
1610 Q_D(QQuickItemView);
1611 if (d->model && d->ownModel)
1612 static_cast<QQmlDelegateModel *>(d->model.data())->componentComplete();
1613
1614 QQuickFlickable::componentComplete();
1615
1616 d->updateSectionCriteria();
1617 d->updateHeader();
1618 d->updateFooter();
1619 d->updateViewport();
1620 d->setPosition(d->contentStartOffset());
1621#if QT_CONFIG(quick_viewtransitions)
1622 if (d->transitioner)
1623 d->transitioner->setPopulateTransitionEnabled(true);
1624#endif
1625
1626 if (d->isValid()) {
1627 d->refill();
1628 d->moveReason = QQuickItemViewPrivate::SetIndex;
1629 if (d->currentIndex < 0 && !d->currentIndexCleared)
1630 d->updateCurrent(0);
1631 else
1632 d->updateCurrent(d->currentIndex);
1633 if (d->highlight && d->currentItem) {
1634 if (d->autoHighlight)
1635 d->resetHighlightPosition();
1636 d->updateTrackedItem();
1637 }
1638 d->moveReason = QQuickItemViewPrivate::Other;
1639 d->fixupPosition();
1640 }
1641 if (d->model && d->model->count())
1642 d->emitCountChanged();
1643}
1644
1645
1646
1647QQuickItemViewPrivate::QQuickItemViewPrivate()
1648 : itemCount(0)
1649 , buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
1650 , displayMarginBeginning(0), displayMarginEnd(0)
1651 , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
1652 , visibleIndex(0)
1653 , currentIndex(-1), currentItem(nullptr)
1654 , trackedItem(nullptr), requestedIndex(-1)
1655 , highlightComponent(nullptr), highlight(nullptr)
1656 , highlightRange(QQuickItemView::NoHighlightRange)
1657 , highlightRangeStart(0), highlightRangeEnd(0)
1658 , highlightMoveDuration(150)
1659 , headerComponent(nullptr), header(nullptr), footerComponent(nullptr), footer(nullptr)
1660#if QT_CONFIG(quick_viewtransitions)
1661 , transitioner(nullptr)
1662#endif
1663 , minExtent(0), maxExtent(0)
1664 , ownModel(false), wrap(false)
1665 , keyNavigationEnabled(true)
1666 , explicitKeyNavigationEnabled(false)
1667 , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1668 , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1669 , fillCacheBuffer(false), inRequest(false)
1670#if QT_CONFIG(quick_viewtransitions)
1671 , runDelayedRemoveTransition(false)
1672#endif
1673 , delegateValidated(false)
1674 , isClearing(false)
1675 , explicitDelegate(false)
1676 , explicitDelegateModelAccess(false)
1677 , inRefill(false)
1678{
1679 bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
1680 bufferPause.setLoopCount(1);
1681 bufferPause.setDuration(16);
1682}
1683
1684static const QQuickItemPrivate::ChangeTypes itemChangeListenerTypes = QQuickItemPrivate::Destroyed;
1685
1686QQuickItemViewPrivate::~QQuickItemViewPrivate()
1687{
1688#if QT_CONFIG(quick_viewtransitions)
1689 if (transitioner)
1690 transitioner->setChangeListener(nullptr);
1691 delete transitioner;
1692#endif
1693}
1694
1695bool QQuickItemViewPrivate::isValid() const
1696{
1697 return model && model->count() && model->isValid();
1698}
1699
1700qreal QQuickItemViewPrivate::position() const
1701{
1702 Q_Q(const QQuickItemView);
1703 return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1704}
1705
1706qreal QQuickItemViewPrivate::size() const
1707{
1708 Q_Q(const QQuickItemView);
1709 return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1710}
1711
1712qreal QQuickItemViewPrivate::startPosition() const
1713{
1714 return isContentFlowReversed() ? -lastPosition() : originPosition();
1715}
1716
1717qreal QQuickItemViewPrivate::endPosition() const
1718{
1719 return isContentFlowReversed() ? -originPosition() : lastPosition();
1720}
1721
1722qreal QQuickItemViewPrivate::contentStartOffset() const
1723{
1724 qreal pos = -headerSize();
1725 if (layoutOrientation() == Qt::Vertical) {
1726 if (isContentFlowReversed())
1727 pos -= vData.endMargin;
1728 else
1729 pos -= vData.startMargin;
1730 } else {
1731 if (isContentFlowReversed())
1732 pos -= hData.endMargin;
1733 else
1734 pos -= hData.startMargin;
1735 }
1736 return pos;
1737}
1738
1739int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1740{
1741 for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
1742 auto item = *it;
1743 if (item->index != -1)
1744 return item->index;
1745 }
1746 return defaultValue;
1747}
1748
1749FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1750 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.size()) {
1751 for (int i = modelIndex - visibleIndex; i < visibleItems.size(); ++i) {
1752 FxViewItem *item = visibleItems.at(i);
1753 if (item->index == modelIndex)
1754 return item;
1755 }
1756 }
1757 return nullptr;
1758}
1759
1760FxViewItem *QQuickItemViewPrivate::firstItemInView() const {
1761 const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1762 for (FxViewItem *item : visibleItems) {
1763 if (item->index != -1 && item->endPosition() > pos)
1764 return item;
1765 }
1766 return visibleItems.size() ? visibleItems.first() : 0;
1767}
1768
1769int QQuickItemViewPrivate::findLastIndexInView() const
1770{
1771 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1772 for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
1773 auto item = *it;
1774 if (item->index != -1 && item->position() <= viewEndPos)
1775 return item->index;
1776 }
1777 return -1;
1778}
1779
1780// Map a model index to visibleItems list index.
1781// These may differ if removed items are still present in the visible list,
1782// e.g. doing a removal animation
1783int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1784{
1785 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.size())
1786 return -1;
1787 for (int i = 0; i < visibleItems.size(); ++i) {
1788 FxViewItem *item = visibleItems.at(i);
1789 if (item->index == modelIndex)
1790 return i;
1791 if (item->index > modelIndex)
1792 return -1;
1793 }
1794 return -1; // Not in visibleList
1795}
1796
1797void QQuickItemViewPrivate::init()
1798{
1799 Q_Q(QQuickItemView);
1800 q->setFlag(QQuickItem::ItemIsFocusScope);
1801 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1802 QObject::connect(q, &QQuickFlickable::interactiveChanged, q, &QQuickItemView::keyNavigationEnabledChanged);
1803 q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1804}
1805
1806void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1807{
1808 Q_Q(QQuickItemView);
1809 applyPendingChanges();
1810 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1811 if (currentItem) {
1812 if (currentItem->attached)
1813 currentItem->attached->setIsCurrentItem(false);
1814 releaseCurrentItem(reusableFlag);
1815 currentIndex = modelIndex;
1816 emit q->currentIndexChanged();
1817 emit q->currentItemChanged();
1818 updateHighlight();
1819 } else if (currentIndex != modelIndex) {
1820 currentIndex = modelIndex;
1821 emit q->currentIndexChanged();
1822 }
1823 return;
1824 }
1825
1826 if (currentItem && currentIndex == modelIndex) {
1827 updateHighlight();
1828 return;
1829 }
1830
1831 FxViewItem *oldCurrentItem = currentItem;
1832 int oldCurrentIndex = currentIndex;
1833 currentIndex = modelIndex;
1834 currentItem = createItem(modelIndex, QQmlIncubator::AsynchronousIfNested);
1835 if (oldCurrentItem && oldCurrentItem->attached && (!currentItem || oldCurrentItem->item != currentItem->item))
1836 oldCurrentItem->attached->setIsCurrentItem(false);
1837 if (currentItem) {
1838 currentItem->item->setFocus(true);
1839 if (currentItem->attached)
1840 currentItem->attached->setIsCurrentItem(true);
1841 initializeCurrentItem();
1842 }
1843
1844 updateHighlight();
1845 if (oldCurrentIndex != currentIndex)
1846 emit q->currentIndexChanged();
1847 if (oldCurrentItem != currentItem
1848 && (!oldCurrentItem || !currentItem || oldCurrentItem->item != currentItem->item))
1849 emit q->currentItemChanged();
1850 releaseItem(oldCurrentItem, reusableFlag);
1851}
1852
1853void QQuickItemViewPrivate::clear(bool onDestruction)
1854{
1855 Q_Q(QQuickItemView);
1856
1857 isClearing = true;
1858 auto cleanup = qScopeGuard([this] { isClearing = false; });
1859
1860 currentChanges.reset();
1861 bufferedChanges.reset();
1862 timeline.clear();
1863
1864 releaseVisibleItems(QQmlInstanceModel::NotReusable);
1865 visibleIndex = 0;
1866
1867#if QT_CONFIG(quick_viewtransitions)
1868 for (FxViewItem *item : std::as_const(releasePendingTransition)) {
1869 item->releaseAfterTransition = false;
1870 releaseItem(item, QQmlInstanceModel::NotReusable);
1871 }
1872 releasePendingTransition.clear();
1873#endif
1874
1875 const bool hadCurrentItem = currentItem != nullptr;
1876 releaseCurrentItem(QQmlDelegateModel::NotReusable);
1877 if (hadCurrentItem)
1878 emit q->currentItemChanged();
1879 createHighlight(onDestruction);
1880 trackedItem = nullptr;
1881
1882 if (requestedIndex >= 0) {
1883 if (model)
1884 model->cancel(requestedIndex);
1885 requestedIndex = -1;
1886 }
1887
1888 markExtentsDirty();
1889 itemCount = 0;
1890}
1891
1892
1893void QQuickItemViewPrivate::mirrorChange()
1894{
1895 Q_Q(QQuickItemView);
1896 regenerate();
1897 emit q->effectiveLayoutDirectionChanged();
1898}
1899
1900void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1901{
1902 Q_Q(QQuickItemView);
1903 fillCacheBuffer = true;
1904 q->polish();
1905}
1906
1907void QQuickItemViewPrivate::refill()
1908{
1909 qreal s = qMax(size(), qreal(0.));
1910 const auto pos = position();
1911 if (isContentFlowReversed())
1912 refill(-pos - displayMarginBeginning-s, -pos + displayMarginEnd);
1913 else
1914 refill(pos - displayMarginBeginning, pos + displayMarginEnd+s);
1915}
1916
1917void QQuickItemViewPrivate::refill(qreal from, qreal to)
1918{
1919 Q_Q(QQuickItemView);
1920 if (!model || !model->isValid() || !q->isComponentComplete())
1921 return;
1922 if (q->size().isNull() && visibleItems.isEmpty())
1923 return;
1924 if (!model->count()) {
1925 updateHeader();
1926 updateFooter();
1927 updateViewport();
1928 return;
1929 }
1930
1931 if (inRefill)
1932 return;
1933 inRefill = true;
1934
1935 do {
1936 bufferPause.stop();
1937 if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) {
1938 currentChanges.reset();
1939 bufferedChanges.reset();
1940 releaseVisibleItems(reusableFlag);
1941 }
1942
1943 int prevCount = itemCount;
1944 itemCount = model->count();
1945 qreal bufferFrom = from - buffer;
1946 qreal bufferTo = to + buffer;
1947 qreal fillFrom = from;
1948 qreal fillTo = to;
1949
1950 bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
1951
1952 if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1953 if (added) {
1954 // We've already created a new delegate this frame.
1955 // Just schedule a buffer refill.
1956 bufferPause.start();
1957 } else {
1958 if (bufferMode & BufferAfter)
1959 fillTo = bufferTo;
1960 if (bufferMode & BufferBefore)
1961 fillFrom = bufferFrom;
1962 added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, true);
1963 }
1964 }
1965
1966 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1967
1968 if (added || removed) {
1969 markExtentsDirty();
1970 updateBeginningEnd();
1971 visibleItemsChanged();
1972 updateHeader();
1973 updateFooter();
1974 updateViewport();
1975 }
1976
1977 if (prevCount != itemCount)
1978 emitCountChanged();
1979 } while (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges());
1980 storeFirstVisibleItemPosition();
1981 inRefill = false;
1982}
1983
1984void QQuickItemViewPrivate::regenerate(bool orientationChanged)
1985{
1986 Q_Q(QQuickItemView);
1987 if (q->isComponentComplete()) {
1988 if (orientationChanged) {
1989 delete header;
1990 header = nullptr;
1991 delete footer;
1992 footer = nullptr;
1993 }
1994 clear();
1995 updateHeader();
1996 updateFooter();
1997 updateViewport();
1998 setPosition(contentStartOffset());
1999 refill();
2000 updateCurrent(currentIndex);
2001 }
2002}
2003
2004void QQuickItemViewPrivate::updateViewport()
2005{
2006 Q_Q(QQuickItemView);
2007 qreal extra = headerSize() + footerSize();
2008 qreal contentSize = isValid() || !visibleItems.isEmpty() ? (endPosition() - startPosition()) : 0.0;
2009 if (layoutOrientation() == Qt::Vertical)
2010 q->setContentHeight(contentSize + extra);
2011 else
2012 q->setContentWidth(contentSize + extra);
2013}
2014
2015void QQuickItemViewPrivate::layout()
2016{
2017 Q_Q(QQuickItemView);
2018 if (inLayout)
2019 return;
2020
2021 inLayout = true;
2022
2023 // viewBounds contains bounds before any add/remove/move operation to the view
2024 QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
2025
2026 // We use isNull for the size check, because isEmpty returns true
2027 // if either dimension is negative, but apparently we support negative-sized
2028 // views (see tst_QQuickListView::resizeView).
2029 if ((!isValid() && !visibleItems.size()) || q->size().isNull()) {
2030 if (q->size().isNull() && hasPendingChanges()) {
2031 // count() refers to the number of items in the model, not in the view
2032 // (which is why we don't emit for the !visibleItems.size() case).
2033 // If there are pending model changes, emit countChanged in order to
2034 // support the use case of QTBUG-129165, where visible is bound to count > 0
2035 // and the ListView is in a layout with Layout.preferredHeight bound to
2036 // contentHeight. This ensures that a hidden ListView will become visible.
2037 emitCountChanged();
2038 }
2039
2040 clear();
2041 setPosition(contentStartOffset());
2042 updateViewport();
2043#if QT_CONFIG(quick_viewtransitions)
2044 if (transitioner)
2045 transitioner->setPopulateTransitionEnabled(false);
2046#endif
2047 inLayout = false;
2048 return;
2049 }
2050
2051#if QT_CONFIG(quick_viewtransitions)
2052 if (runDelayedRemoveTransition && transitioner
2053 && transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2054 // assume that any items moving now are moving due to the remove - if they schedule
2055 // a different transition, that will override this one anyway
2056 for (int i=0; i<visibleItems.size(); i++)
2057 visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2058 }
2059#endif
2060
2061 ChangeResult insertionPosChanges;
2062 ChangeResult removalPosChanges;
2063 if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
2064 if (fillCacheBuffer) {
2065 fillCacheBuffer = false;
2066 refill();
2067 }
2068 inLayout = false;
2069 return;
2070 }
2071 forceLayout = false;
2072
2073#if QT_CONFIG(quick_viewtransitions)
2074 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
2075 // Give the view one more chance to refill itself,
2076 // in case its size is changed such that more delegates become visible after component completed
2077 refill();
2078 for (FxViewItem *item : std::as_const(visibleItems)) {
2079 if (!item->transitionScheduledOrRunning())
2080 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
2081 }
2082 }
2083#endif
2084
2085 updateSections();
2086 layoutVisibleItems();
2087 storeFirstVisibleItemPosition();
2088
2089#if QT_CONFIG(quick_viewtransitions)
2090 int lastIndexInView = findLastIndexInView();
2091#endif
2092 refill();
2093 markExtentsDirty();
2094 updateHighlight();
2095
2096 if (!q->isMoving() && !q->isFlicking() && !movingFromHighlight()) {
2097 fixupPosition();
2098 refill();
2099 }
2100
2101 updateHeader();
2102 updateFooter();
2103 updateViewport();
2104 updateUnrequestedPositions();
2105
2106#if QT_CONFIG(quick_viewtransitions)
2107 if (transitioner) {
2108 // items added in the last refill() may need to be transitioned in - e.g. a remove
2109 // causes items to slide up into view
2110 if (lastIndexInView != -1 &&
2111 (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false)
2112 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false))) {
2113 translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
2114 }
2115
2116 prepareVisibleItemTransitions();
2117
2118 // We cannot use iterators here as erasing from a container invalidates them.
2119 for (int i = 0, count = releasePendingTransition.size(); i < count;) {
2120 auto success = prepareNonVisibleItemTransition(releasePendingTransition[i], viewBounds);
2121 // prepareNonVisibleItemTransition() may remove items while in fast flicking.
2122 // Invisible animating items are kicked in or out the viewPort.
2123 // Recheck count to test if the item got removed. In that case the same index points
2124 // to a different item now.
2125 const int old_count = count;
2126 count = releasePendingTransition.size();
2127 if (old_count > count)
2128 continue;
2129
2130 if (!success) {
2131 releaseItem(releasePendingTransition[i], reusableFlag);
2132 releasePendingTransition.remove(i);
2133 --count;
2134 } else {
2135 ++i;
2136 }
2137 }
2138
2139 for (int i=0; i<visibleItems.size(); i++)
2140 visibleItems[i]->startTransition(transitioner);
2141 for (int i=0; i<releasePendingTransition.size(); i++)
2142 releasePendingTransition[i]->startTransition(transitioner);
2143
2144 transitioner->setPopulateTransitionEnabled(false);
2145 transitioner->resetTargetLists();
2146 }
2147#endif
2148
2149 if (!currentItem)
2150 updateCurrent(currentIndex);
2151
2152#if QT_CONFIG(quick_viewtransitions)
2153 runDelayedRemoveTransition = false;
2154#endif
2155 inLayout = false;
2156}
2157
2158bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
2159{
2160 Q_Q(QQuickItemView);
2161 if (!q->isComponentComplete() || !hasPendingChanges())
2162 return false;
2163
2164 if (bufferedChanges.hasPendingChanges()) {
2165 currentChanges.applyBufferedChanges(bufferedChanges);
2166 bufferedChanges.reset();
2167 }
2168
2169 updateUnrequestedIndexes();
2170
2171 FxViewItem *prevVisibleItemsFirst = visibleItems.size() ? *visibleItems.constBegin() : nullptr;
2172 int prevItemCount = itemCount;
2173 int prevVisibleItemsCount = visibleItems.size();
2174 bool visibleAffected = false;
2175 bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
2176 || !currentChanges.pendingChanges.inserts().isEmpty();
2177
2178 FxViewItem *prevFirstItemInView = firstItemInView();
2179 QQmlNullableValue<qreal> prevFirstItemInViewPos;
2180 int prevFirstItemInViewIndex = -1;
2181 if (prevFirstItemInView) {
2182 prevFirstItemInViewPos = prevFirstItemInView->position();
2183 prevFirstItemInViewIndex = prevFirstItemInView->index;
2184 }
2185 qreal prevVisibleItemsFirstPos = visibleItems.size() ? firstVisibleItemPosition : 0.0;
2186
2187 totalInsertionResult->visiblePos = prevFirstItemInViewPos;
2188 totalRemovalResult->visiblePos = prevFirstItemInViewPos;
2189
2190 const QList<QQmlChangeSet::Change> &removals = currentChanges.pendingChanges.removes();
2191 const QList<QQmlChangeSet::Change> &insertions = currentChanges.pendingChanges.inserts();
2192 ChangeResult insertionResult(prevFirstItemInViewPos);
2193 ChangeResult removalResult(prevFirstItemInViewPos);
2194
2195 int removedCount = 0;
2196 for (const QQmlChangeSet::Change &r : removals) {
2197 itemCount -= r.count;
2198 if (applyRemovalChange(r, &removalResult, &removedCount))
2199 visibleAffected = true;
2200 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(r.index))
2201 visibleAffected = true;
2202 const int correctedFirstVisibleIndex = prevFirstItemInViewIndex - removalResult.countChangeBeforeVisible;
2203 if (correctedFirstVisibleIndex >= 0 && r.index < correctedFirstVisibleIndex) {
2204 if (r.index + r.count < correctedFirstVisibleIndex)
2205 removalResult.countChangeBeforeVisible += r.count;
2206 else
2207 removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - r.index);
2208 }
2209 }
2210#if QT_CONFIG(quick_viewtransitions)
2211 if (runDelayedRemoveTransition) {
2212 QQmlChangeSet::Change removal;
2213 for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
2214 FxViewItem *item = *it;
2215 if (item->index == -1 && (!item->attached || !item->attached->delayRemove())) {
2216 removeItem(item, removal, &removalResult);
2217 removedCount++;
2218 it = visibleItems.erase(it);
2219 } else {
2220 ++it;
2221 }
2222 }
2223 }
2224#endif
2225 *totalRemovalResult += removalResult;
2226 if (!removals.isEmpty()) {
2227 updateVisibleIndex();
2228
2229 // set positions correctly for the next insertion
2230 if (!insertions.isEmpty()) {
2231 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
2232 layoutVisibleItems(removals.first().index);
2233 storeFirstVisibleItemPosition();
2234 }
2235 }
2236
2237 QList<FxViewItem *> newItems;
2238 QList<MovedItem> movingIntoView;
2239
2240 for (int i=0; i<insertions.size(); i++) {
2241 bool wasEmpty = visibleItems.isEmpty();
2242 if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
2243 visibleAffected = true;
2244 if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
2245 visibleAffected = true;
2246 if (wasEmpty && !visibleItems.isEmpty())
2247 resetFirstItemPosition();
2248 *totalInsertionResult += insertionResult;
2249
2250 // set positions correctly for the next insertion
2251 if (i < insertions.size() - 1) {
2252 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
2253 layoutVisibleItems(insertions[i].index);
2254 storeFirstVisibleItemPosition();
2255 }
2256 itemCount += insertions[i].count;
2257 }
2258 for (FxViewItem *item : std::as_const(newItems)) {
2259 if (item->attached)
2260 item->attached->emitAdd();
2261 }
2262
2263#if QT_CONFIG(quick_viewtransitions)
2264 // for each item that was moved directly into the view as a result of a move(),
2265 // find the index it was moved from in order to set its initial position, so that we
2266 // can transition it from this "original" position to its new position in the view
2267 if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
2268 for (const MovedItem &m : std::as_const(movingIntoView)) {
2269 int fromIndex = findMoveKeyIndex(m.moveKey, removals);
2270 if (fromIndex >= 0) {
2271 if (prevFirstItemInViewIndex >= 0 && fromIndex < prevFirstItemInViewIndex)
2272 repositionItemAt(m.item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
2273 else
2274 repositionItemAt(m.item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
2275 m.item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2276 }
2277 }
2278 }
2279#endif
2280
2281 // reposition visibleItems.first() correctly so that the content y doesn't jump
2282 if (removedCount != prevVisibleItemsCount)
2283 repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
2284
2285 // Whatever removed/moved items remain are no longer visible items.
2286#if QT_CONFIG(quick_viewtransitions)
2287 prepareRemoveTransitions(&currentChanges.removedItems);
2288#endif
2289 for (auto it = currentChanges.removedItems.begin();
2290 it != currentChanges.removedItems.end(); ++it) {
2291 releaseItem(it.value(), reusableFlag);
2292 }
2293 currentChanges.removedItems.clear();
2294
2295 if (currentChanges.currentChanged) {
2296 if (currentChanges.currentRemoved && currentItem) {
2297 if (currentItem->item && currentItem->attached)
2298 currentItem->attached->setIsCurrentItem(false);
2299 const bool hadCurrentItem = currentItem != nullptr;
2300 releaseCurrentItem(reusableFlag);
2301 if (hadCurrentItem)
2302 emit q->currentItemChanged();
2303 }
2304 if (!currentIndexCleared)
2305 updateCurrent(currentChanges.newCurrentIndex);
2306 }
2307
2308 if (!visibleAffected)
2309 visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
2310 currentChanges.reset();
2311
2312 updateSections();
2313 if (prevItemCount != itemCount)
2314 emitCountChanged();
2315 if (!visibleAffected && viewportChanged)
2316 updateViewport();
2317
2318 return visibleAffected;
2319}
2320
2321bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &removal, ChangeResult *removeResult, int *removedCount)
2322{
2323 Q_Q(QQuickItemView);
2324 bool visibleAffected = false;
2325
2326 if (visibleItems.size() && removal.index + removal.count > visibleItems.constLast()->index) {
2327 if (removal.index > visibleItems.constLast()->index)
2328 removeResult->countChangeAfterVisibleItems += removal.count;
2329 else
2330 removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index);
2331 }
2332
2333 QList<FxViewItem*>::Iterator it = visibleItems.begin();
2334 while (it != visibleItems.end()) {
2335 FxViewItem *item = *it;
2336 if (item->index == -1 || item->index < removal.index) {
2337 // already removed, or before removed items
2338 if (!visibleAffected && item->index < removal.index)
2339 visibleAffected = true;
2340 ++it;
2341 } else if (item->index >= removal.index + removal.count) {
2342 // after removed items
2343 item->index -= removal.count;
2344#if QT_CONFIG(quick_viewtransitions)
2345 if (removal.isMove())
2346 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2347 else
2348 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2349#endif
2350 ++it;
2351 } else {
2352 // removed item
2353 visibleAffected = true;
2354 if (!removal.isMove() && item->item && item->attached)
2355 item->attached->emitRemove();
2356
2357 if (item->item && item->attached && item->attached->delayRemove() && !removal.isMove()) {
2358 item->index = -1;
2359 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2360 ++it;
2361 } else {
2362 removeItem(item, removal, removeResult);
2363 if (!removal.isMove())
2364 (*removedCount)++;
2365 it = visibleItems.erase(it);
2366 }
2367 }
2368 }
2369
2370 return visibleAffected;
2371}
2372
2373void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Change &removal, ChangeResult *removeResult)
2374{
2375 if (removeResult->visiblePos.isValid()) {
2376 if (item->position() < removeResult->visiblePos)
2377 updateSizeChangesBeforeVisiblePos(item, removeResult);
2378 else
2379 removeResult->sizeChangesAfterVisiblePos += item->size();
2380 }
2381 if (removal.isMove()) {
2382 currentChanges.removedItems.replace(removal.moveKey(item->index), item);
2383#if QT_CONFIG(quick_viewtransitions)
2384 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2385#endif
2386 } else {
2387 // track item so it is released later
2388 currentChanges.removedItems.insert(QQmlChangeSet::MoveKey(), item);
2389 }
2390 if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2391 removeResult->changedFirstItem = true;
2392}
2393
2394void QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult)
2395{
2396 removeResult->sizeChangesBeforeVisiblePos += item->size();
2397}
2398
2399void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2400 qreal prevVisibleItemsFirstPos,
2401 FxViewItem *prevFirstVisible,
2402 ChangeResult *insertionResult,
2403 ChangeResult *removalResult)
2404{
2405 const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2406
2407 // reposition visibleItems.first() correctly so that the content y doesn't jump
2408 if (visibleItems.size()) {
2409 if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2410 resetFirstItemPosition(prevVisibleItemsFirstPos);
2411
2412 if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2413 && prevFirstVisible != *visibleItems.constBegin()) {
2414 // the previous visibleItems.first() was also the first visible item, and it has been
2415 // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2416 if (!insertionResult->changedFirstItem)
2417 resetFirstItemPosition(prevVisibleItemsFirstPos);
2418
2419 } else if (prevViewPos.isValid()) {
2420 qreal moveForwardsBy = 0;
2421 qreal moveBackwardsBy = 0;
2422
2423 // shift visibleItems.first() relative to the number of added/removed items
2424 const auto pos = visibleItems.constFirst()->position();
2425 if (pos > prevViewPos) {
2426 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2427 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2428 } else if (pos < prevViewPos) {
2429 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2430 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2431 }
2432 adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2433 }
2434 insertionResult->reset();
2435 removalResult->reset();
2436 }
2437}
2438
2439#if QT_CONFIG(quick_viewtransitions)
2440void QQuickItemViewPrivate::createTransitioner()
2441{
2442 if (!transitioner) {
2443 transitioner = new QQuickItemViewTransitioner;
2444 transitioner->setChangeListener(this);
2445 }
2446}
2447
2448void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2449{
2450 Q_Q(QQuickItemView);
2451 if (!transitioner)
2452 return;
2453
2454 // must call for every visible item to init or discard transitions
2455 QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height());
2456 for (int i=0; i<visibleItems.size(); i++)
2457 visibleItems[i]->prepareTransition(transitioner, viewBounds);
2458}
2459
2460void QQuickItemViewPrivate::prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems)
2461{
2462 if (!transitioner)
2463 return;
2464
2465 if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)
2466 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2467 for (auto it = removedItems->begin(); it != removedItems->end(); ) {
2468 bool isRemove = it.key().moveId < 0;
2469 if (isRemove) {
2470 FxViewItem *item = *it;
2471 item->trackGeometry(false);
2472 item->releaseAfterTransition = true;
2473 releasePendingTransition.append(item);
2474 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
2475 it = removedItems->erase(it);
2476 } else {
2477 ++it;
2478 }
2479 }
2480 }
2481}
2482
2483bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2484{
2485 // Called for items that have been removed from visibleItems and may now be
2486 // transitioned out of the view. This applies to items that are being directly
2487 // removed, or moved to outside of the view, as well as those that are
2488 // displaced to a position outside of the view due to an insert or move.
2489
2490 if (!transitioner)
2491 return false;
2492
2493 if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2494 repositionItemAt(item, item->index, 0);
2495
2496 bool success = false;
2497 ACTION_IF_DELETED(item, success = item->prepareTransition(transitioner, viewBounds), return success);
2498
2499 if (success) {
2500 item->releaseAfterTransition = true;
2501 return true;
2502 }
2503 return false;
2504}
2505
2506void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2507{
2508 for (int i=0; i<releasePendingTransition.size(); i++) {
2509 if (releasePendingTransition.at(i)->transitionableItem == item) {
2510 releaseItem(releasePendingTransition.takeAt(i), reusableFlag);
2511 return;
2512 }
2513 }
2514}
2515#endif
2516
2517/*
2518 This may return 0 if the item is being created asynchronously.
2519 When the item becomes available, refill() will be called and the item
2520 will be returned on the next call to createItem().
2521*/
2522FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::IncubationMode incubationMode)
2523{
2524 Q_Q(QQuickItemView);
2525
2526 if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous)
2527 return nullptr;
2528
2529#if QT_CONFIG(quick_viewtransitions)
2530 for (int i=0; i<releasePendingTransition.size(); i++) {
2531 if (releasePendingTransition.at(i)->index == modelIndex
2532 && !releasePendingTransition.at(i)->isPendingRemoval()) {
2533 releasePendingTransition[i]->releaseAfterTransition = false;
2534 return releasePendingTransition.takeAt(i);
2535 }
2536 }
2537#endif
2538
2539 inRequest = true;
2540
2541 // The model will run this same range check internally but produce a warning and return nullptr.
2542 // Since we handle this result graciously in our code, we preempt this warning by checking the range ourselves.
2543 QObject* object = modelIndex < model->count() ? model->object(modelIndex, incubationMode) : nullptr;
2544 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
2545
2546 if (!item) {
2547 if (!object) {
2548 if (requestedIndex == -1 && model->incubationStatus(modelIndex) == QQmlIncubator::Loading) {
2549 // The reason we didn't receive an item is because it's incubating async. We keep track
2550 // of this by assigning the index we're waiting for to 'requestedIndex'. This will e.g. let
2551 // the view avoid unnecessary layout calls until the item has been loaded.
2552 requestedIndex = modelIndex;
2553 }
2554 } else {
2555 model->release(object);
2556 if (!delegateValidated) {
2557 delegateValidated = true;
2558 QObject* delegate = q->delegate();
2559 qmlWarning(delegate ? delegate : q) << QQuickItemView::tr("Delegate must be of Item type");
2560 }
2561 }
2562 inRequest = false;
2563 return nullptr;
2564 } else {
2565 // Container removes and instantly deletes items created within ObjectModels.
2566 // We need to account for this to avoid having references to deleted items.
2567 // itemDestroyed is called as a result of adding this listener.
2568 if (qobject_cast<QQmlObjectModel *>(model))
2569 QQuickItemPrivate::get(item)->updateOrAddItemChangeListener(this, itemChangeListenerTypes);
2570
2571 item->setParentItem(q->contentItem());
2572 if (requestedIndex == modelIndex)
2573 requestedIndex = -1;
2574 FxViewItem *viewItem = newViewItem(modelIndex, item);
2575 if (viewItem) {
2576 viewItem->index = modelIndex;
2577 // do other set up for the new item that should not happen
2578 // until after bindings are evaluated
2579 initializeViewItem(viewItem);
2580 unrequestedItems.remove(item);
2581 }
2582 inRequest = false;
2583 return viewItem;
2584 }
2585}
2586
2587void QQuickItemView::createdItem(int index, QObject* object)
2588{
2589 Q_D(QQuickItemView);
2590
2591 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2592 if (!d->inRequest) {
2593 d->unrequestedItems.insert(item, index);
2594 d->requestedIndex = -1;
2595 if (d->hasPendingChanges())
2596 d->layout();
2597 else
2598 d->refill();
2599 if (d->unrequestedItems.contains(item))
2600 d->repositionPackageItemAt(item, index);
2601 else if (index == d->currentIndex)
2602 d->updateCurrent(index);
2603 }
2604}
2605
2606void QQuickItemView::initItem(int, QObject *object)
2607{
2608 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2609 if (item) {
2610 if (qFuzzyIsNull(item->z()))
2611 item->setZ(1);
2612 item->setParentItem(contentItem());
2613 QQuickItemPrivate::get(item)->setCulled(true);
2614 }
2615}
2616
2617// This is called when the model (if it's a QQmlInstanceModel) emits destroyingItem.
2618void QQuickItemView::destroyingItem(QObject *object)
2619{
2620 Q_D(QQuickItemView);
2621 QQuickItem* item = qmlobject_cast<QQuickItem*>(object);
2622 if (item) {
2623 item->setParentItem(nullptr);
2624 d->unrequestedItems.remove(item);
2625 QQuickItemPrivate::get(item)->removeItemChangeListener(d, itemChangeListenerTypes);
2626 }
2627}
2628
2629void QQuickItemView::onItemPooled(int modelIndex, QObject *object)
2630{
2631 Q_UNUSED(modelIndex);
2632
2633 if (auto *attached = d_func()->getAttachedObject(object))
2634 emit attached->pooled();
2635}
2636
2637void QQuickItemView::onItemReused(int modelIndex, QObject *object)
2638{
2639 Q_UNUSED(modelIndex);
2640
2641 if (auto *attached = d_func()->getAttachedObject(object))
2642 emit attached->reused();
2643}
2644
2645bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag)
2646{
2647 Q_Q(QQuickItemView);
2648 if (!item)
2649 return true;
2650 if (trackedItem == item)
2651 trackedItem = nullptr;
2652 item->trackGeometry(false);
2653
2654 QQmlInstanceModel::ReleaseFlags flags = {};
2655 if (QPointer<QQuickItem> quickItem = item->item) {
2656 if (model) {
2657 flags = model->release(quickItem, reusableFlag);
2658 if (!flags) {
2659 // item was not destroyed, and we no longer reference it.
2660 if (quickItem->parentItem() == contentItem) {
2661 // Only cull the item if its parent item is still our contentItem.
2662 // One case where this can happen is moving an item out of one ObjectModel and into another.
2663 QQuickItemPrivate::get(quickItem)->setCulled(true);
2664 }
2665 // If deleteLater was called, the item isn't long for this world and so we shouldn't store references to it.
2666 // This can happen when a Repeater is used to populate items in SwipeView's ListView contentItem.
2667 if (!isClearing && !QObjectPrivate::get(quickItem)->deleteLaterCalled)
2668 unrequestedItems.insert(quickItem, model->indexOf(quickItem, q));
2669 } else if (flags & QQmlInstanceModel::Destroyed) {
2670 quickItem->setParentItem(nullptr);
2671 } else if (flags & QQmlInstanceModel::Pooled) {
2672 item->setVisible(false);
2673 }
2674 }
2675
2676 QQuickItemPrivate::get(quickItem)->removeItemChangeListener(this, itemChangeListenerTypes);
2677#if QT_CONFIG(quick_viewtransitions)
2678 delete item->transitionableItem;
2679 item->transitionableItem = nullptr;
2680#endif
2681 }
2682
2683 delete item;
2684 return flags != QQmlInstanceModel::Referenced;
2685}
2686
2687/*!
2688 \internal
2689
2690 Called when an item created in an ObjectModel is deleted rather than
2691 removing it via the model.
2692
2693 Similar in what it does to destroyRemoved except that it intentionally
2694 doesn't account for delayRemove.
2695*/
2696void QQuickItemViewPrivate::itemDestroyed(QQuickItem *item)
2697{
2698 // We can't check model->indexOf(item, q_func()) here, because the item
2699 // may not exist there, so we instead check visibleItems.
2700 FxViewItem *visibleFxItem = nullptr;
2701 const int indexOfItem = -1;
2702 for (auto *fxItem : std::as_const(visibleItems)) {
2703 if (fxItem->item == item) {
2704 visibleFxItem = fxItem;
2705 break;
2706 }
2707 }
2708
2709 // Make sure that we don't try to clean up the same FxViewItem twice,
2710 // as apparently there can be two FxViewItems for the same QQuickItem.
2711 if (currentItem && visibleFxItem)
2712 Q_ASSERT(currentItem != visibleFxItem);
2713
2714 if (visibleFxItem) {
2715 qCDebug(lcItemViewDelegateLifecycle) << "removing deleted item"
2716 << item << visibleFxItem << "at index" << indexOfItem << "without running transitions";
2717 // We need to remove it from visibleItems manually, as we don't want to call
2718 // removeNonVisibleItems since it won't remove items with transitions.
2719 const bool removedVisibleFxItem = visibleItems.removeOne(visibleFxItem);
2720 Q_ASSERT(removedVisibleFxItem);
2721 releaseItem(visibleFxItem, QQmlDelegateModel::NotReusable);
2722 }
2723
2724 if (currentItem && currentItem->item == item) {
2725 releaseItem(currentItem, QQmlDelegateModel::NotReusable);
2726 currentItem = nullptr;
2727 }
2728
2729 // Update the positioning of the items.
2730 forceLayoutPolish();
2731}
2732
2733QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2734{
2735 QQuickItem *item = nullptr;
2736 if (!inRequest) {
2737 inRequest = true;
2738 item = createComponentItem(highlightComponent, 0.0, true);
2739 inRequest = false;
2740 }
2741 return item;
2742}
2743
2744QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault) const
2745{
2746 Q_Q(const QQuickItemView);
2747
2748 QQuickItem *item = nullptr;
2749 if (component) {
2750 QQmlContext *context = component->creationContext();
2751 if (!context)
2752 context = qmlContext(q);
2753
2754 if (QObject *nobj = component->beginCreate(context)) {
2755 item = qobject_cast<QQuickItem *>(nobj);
2756 if (!item)
2757 delete nobj;
2758 }
2759 } else if (createDefault) {
2760 item = new QQuickItem;
2761 }
2762 if (item) {
2763 if (qFuzzyIsNull(item->z()))
2764 item->setZ(zValue);
2765 QQml_setParent_noEvent(item, q->contentItem());
2766 item->setParentItem(q->contentItem());
2767
2768 initializeComponentItem(item);
2769 }
2770 if (component)
2771 component->completeCreate();
2772 return item;
2773}
2774
2775/*!
2776 \internal
2777
2778 Allows derived classes to do any initialization required for \a item
2779 before completeCreate() is called on it. For example, any attached
2780 properties required by the item can be set.
2781
2782 This is similar to initItem(), but as that has logic specific to
2783 delegate items, we use a separate function for non-delegates.
2784*/
2785void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const
2786{
2787 Q_UNUSED(item);
2788}
2789
2790void QQuickItemViewPrivate::updateTrackedItem()
2791{
2792 Q_Q(QQuickItemView);
2793 FxViewItem *item = currentItem;
2794 if (highlight)
2795 item = highlight.get();
2796 trackedItem = item;
2797
2798 if (trackedItem)
2799 q->trackedPositionChanged();
2800}
2801
2802void QQuickItemViewPrivate::updateUnrequestedIndexes()
2803{
2804 Q_Q(QQuickItemView);
2805 for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(), end = unrequestedItems.end(); it != end; ++it)
2806 *it = model->indexOf(it.key(), q);
2807}
2808
2809void QQuickItemViewPrivate::updateUnrequestedPositions()
2810{
2811 for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) {
2812 if (it.value() >= 0)
2813 repositionPackageItemAt(it.key(), it.value());
2814 }
2815}
2816
2817void QQuickItemViewPrivate::updateVisibleIndex()
2818{
2819 typedef QList<FxViewItem*>::const_iterator FxViewItemListConstIt;
2820
2821 visibleIndex = 0;
2822 for (FxViewItemListConstIt it = visibleItems.constBegin(), cend = visibleItems.constEnd(); it != cend; ++it) {
2823 if ((*it)->index != -1) {
2824 visibleIndex = (*it)->index;
2825 break;
2826 }
2827 }
2828}
2829
2830QQmlDelegateModel::DelegateModelAccess QQuickItemView::delegateModelAccess() const
2831{
2832 Q_D(const QQuickItemView);
2833 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->model))
2834 return dataModel->delegateModelAccess();
2835 return QQmlDelegateModel::Qt5ReadWrite;
2836}
2837
2838void QQuickItemView::setDelegateModelAccess(
2839 QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
2840{
2841 Q_D(QQuickItemView);
2842 const auto setExplicitDelegateModelAccess = [&](QQmlDelegateModel *delegateModel) {
2843 delegateModel->setDelegateModelAccess(delegateModelAccess);
2844 d->explicitDelegateModelAccess = true;
2845 };
2846
2847 if (!d->model) {
2848 if (delegateModelAccess == QQmlDelegateModel::Qt5ReadWrite) {
2849 // Explicitly set delegateModelAccess to Legacy. We can do this without model.
2850 d->explicitDelegateModelAccess = true;
2851 return;
2852 }
2853
2854 setExplicitDelegateModelAccess(QQmlDelegateModel::createForView(this, d));
2855
2856 // The new model is not connected to applyDelegateModelAccessChange, yet. We only do this
2857 // once there is actual data, via an explicit setModel(). So we have to manually emit the
2858 // delegateModelAccessChanged() here.
2859 emit delegateModelAccessChanged();
2860 return;
2861 }
2862
2863 if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(d->model)) {
2864 // Disable the warning in applyDelegateModelAccessChange since the new delegate model
2865 // access is also explicit.
2866 d->explicitDelegateModelAccess = false;
2867 setExplicitDelegateModelAccess(delegateModel);
2868 return;
2869 }
2870
2871 if (delegateModelAccess == QQmlDelegateModel::Qt5ReadWrite) {
2872 d->explicitDelegateModelAccess = true; // Explicitly set null delegate always works
2873 } else {
2874 qmlWarning(this) << "Cannot set a delegateModelAccess on an explicitly provided "
2875 "non-DelegateModel";
2876 }
2877}
2878
2879QT_END_NAMESPACE
2880
2881#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