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