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