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