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