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

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