Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickgridview.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
8
9#include <private/qqmlobjectmodel_p.h>
10#include <private/qquicksmoothedanimation_p_p.h>
11
12#include <QtGui/qevent.h>
13#include <QtCore/qmath.h>
14#include <QtCore/qcoreapplication.h>
15#include "qplatformdefs.h"
16
17#include <cmath>
18
19QT_BEGIN_NAMESPACE
20
21#ifndef QML_FLICK_SNAPONETHRESHOLD
22#define QML_FLICK_SNAPONETHRESHOLD 30
23#endif
24
25//----------------------------------------------------------------------------
26
27class FxGridItemSG : public FxViewItem
28{
29public:
30 FxGridItemSG(QQuickItem *i, QQuickGridView *v, bool own) : FxViewItem(i, v, own, static_cast<QQuickItemViewAttached*>(qmlAttachedPropertiesObject<QQuickGridView>(i))), view(v)
31 {
32 }
33
34 qreal position() const override {
35 return rowPos();
36 }
37
38 qreal endPosition() const override {
39 return endRowPos();
40 }
41
42 qreal size() const override {
43 return view->flow() == QQuickGridView::FlowLeftToRight ? view->cellHeight() : view->cellWidth();
44 }
45
46 qreal sectionSize() const override {
47 return 0.0;
48 }
49
50 qreal rowPos() const {
51 if (view->flow() == QQuickGridView::FlowLeftToRight)
52 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -view->cellHeight()-itemY() : itemY());
53 else
54 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-itemX() : itemX());
55 }
56
57 qreal colPos() const {
58 if (view->flow() == QQuickGridView::FlowLeftToRight) {
59 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
60 qreal colSize = view->cellWidth();
61 int columns = view->width()/colSize;
62 return colSize * (columns-1) - itemX();
63 } else {
64 return itemX();
65 }
66 } else {
67 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
68 return -view->cellHeight() - itemY();
69 } else {
70 return itemY();
71 }
72 }
73 }
74 qreal endRowPos() const {
75 if (view->flow() == QQuickGridView::FlowLeftToRight) {
76 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
77 return -itemY();
78 else
79 return itemY() + view->cellHeight();
80 } else {
81 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
82 return -itemX();
83 else
84 return itemX() + view->cellWidth();
85 }
86 }
87 void setPosition(qreal col, qreal row, bool immediate = false) {
88 moveTo(pointForPosition(col, row), immediate);
89 }
90 bool contains(qreal x, qreal y) const override {
91 return (x >= itemX() && x < itemX() + view->cellWidth() &&
92 y >= itemY() && y < itemY() + view->cellHeight());
93 }
94
95 QQuickGridView *view;
96
97private:
98 QPointF pointForPosition(qreal col, qreal row) const {
99 qreal x;
100 qreal y;
101 if (view->flow() == QQuickGridView::FlowLeftToRight) {
102 x = col;
103 y = row;
104 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
105 int columns = view->width()/view->cellWidth();
106 x = view->cellWidth() * (columns-1) - col;
107 }
108 } else {
109 x = row;
110 y = col;
111 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
112 x = -view->cellWidth() - row;
113 }
114 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
115 y = -view->cellHeight() - y;
116 return QPointF(x, y);
117 }
118};
119
120//----------------------------------------------------------------------------
121
123{
124 Q_DECLARE_PUBLIC(QQuickGridView)
125
126public:
129
130 qreal positionAt(int index) const override;
131 qreal endPositionAt(int index) const override;
132 qreal originPosition() const override;
133 qreal lastPosition() const override;
134
135 qreal rowSize() const;
136 qreal colSize() const;
137 qreal colPosAt(int modelIndex) const;
138 qreal rowPosAt(int modelIndex) const;
139 qreal snapPosAt(qreal pos) const;
140 FxViewItem *snapItemAt(qreal pos) const;
141 int snapIndex() const;
142 qreal contentXForPosition(qreal pos) const;
143 qreal contentYForPosition(qreal pos) const;
144
146
147 bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) override;
148 bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) override;
149
150 void removeItem(FxViewItem *item);
151
152 FxViewItem *newViewItem(int index, QQuickItem *item) override;
153 void initializeViewItem(FxViewItem *item) override;
154 void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) override;
155 void repositionPackageItemAt(QQuickItem *item, int index) override;
156 void resetFirstItemPosition(qreal pos = 0.0) override;
157 void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible) override;
158
159 void createHighlight(bool onDestruction = false) override;
162
163 void setPosition(qreal pos) override;
164 void layoutVisibleItems(int fromModelIndex = 0) override;
165 bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView) override;
166#if QT_CONFIG(quick_viewtransitions)
168#endif
170
171 qreal headerSize() const override;
172 qreal footerSize() const override;
173 bool showHeaderForIndex(int index) const override;
174 bool showFooterForIndex(int index) const override;
177
178 void initializeComponentItem(QQuickItem *item) const override;
179
180 void changedVisibleIndex(int newIndex) override;
182
185 void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
186 bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
187 QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override;
188
189 QQuickItemViewAttached *getAttachedObject(const QObject *object) const override;
190
196
199
207 {
208 delete highlightXAnimator;
209 delete highlightYAnimator;
210 }
211};
212
213Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const
214{
215 return flow == QQuickGridView::FlowLeftToRight ? Qt::Vertical : Qt::Horizontal;
216}
217
219{
220 Q_Q(const QQuickGridView);
221
222 return (flow == QQuickGridView::FlowLeftToRight && verticalLayoutDirection == QQuickItemView::BottomToTop)
223 || (flow == QQuickGridView::FlowTopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft);
224}
225
227{
228 visibleIndex = newIndex / columns * columns;
229}
230
232{
233 Q_Q(QQuickGridView);
234 q->QQuickFlickable::setContentX(contentXForPosition(pos));
235 q->QQuickFlickable::setContentY(contentYForPosition(pos));
236}
237
239{
240 qreal pos = 0;
241 if (!visibleItems.isEmpty())
242 pos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
243 return pos;
244}
245
247{
248 qreal pos = 0;
249 if (model && (model->count() || !visibleItems.isEmpty())) {
250 qreal lastRowPos = model->count() ? rowPosAt(model->count() - 1) : 0;
251 if (!visibleItems.isEmpty()) {
252 // If there are items in delayRemove state, they may be after any items linked to the model
253 lastRowPos = qMax(lastRowPos, static_cast<FxGridItemSG*>(visibleItems.last())->rowPos());
254 }
255 pos = lastRowPos + rowSize();
256 }
257 return pos;
258}
259
261{
262 return rowPosAt(index);
263}
264
266{
267 return rowPosAt(index) + rowSize();
268}
269
271 return flow == QQuickGridView::FlowLeftToRight ? cellHeight : cellWidth;
272}
274 return flow == QQuickGridView::FlowLeftToRight ? cellWidth : cellHeight;
275}
276
278{
279 if (FxViewItem *item = visibleItem(modelIndex))
280 return static_cast<FxGridItemSG*>(item)->colPos();
281 if (!visibleItems.isEmpty()) {
282 if (modelIndex == visibleIndex) {
283 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
284 return firstItem->colPos();
285 } else if (modelIndex < visibleIndex) {
286 int count = (visibleIndex - modelIndex) % columns;
287 int col = static_cast<FxGridItemSG*>(visibleItems.first())->colPos() / colSize();
288 col = (columns - count + col) % columns;
289 return col * colSize();
290 } else {
291 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
292 int count = modelIndex - lastItem->index;
293 int col = lastItem->colPos() / colSize();
294 col = (col + count) % columns;
295 return col * colSize();
296 }
297 }
298 return (modelIndex % columns) * colSize();
299}
300
302{
303 if (FxViewItem *item = visibleItem(modelIndex))
304 return static_cast<FxGridItemSG*>(item)->rowPos();
305 if (!visibleItems.isEmpty()) {
306 if (modelIndex == visibleIndex) {
307 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
308 return firstItem->rowPos();
309 } else if (modelIndex < visibleIndex) {
310 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
311 int firstCol = firstItem->colPos() / colSize();
312 int col = visibleIndex - modelIndex + (columns - firstCol - 1);
313 int rows = col / columns;
314 return firstItem->rowPos() - rows * rowSize();
315 } else {
316 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
317 int count = modelIndex - lastItem->index;
318 int col = lastItem->colPos() + count * colSize();
319 int rows = col / (columns * colSize());
320 return lastItem->rowPos() + rows * rowSize();
321 }
322 }
323
324 qreal rowPos = ((modelIndex / columns) * rowSize());
325
326 if (flow == QQuickGridView::FlowLeftToRight && verticalLayoutDirection == QQuickItemView::TopToBottom) {
327 // Add the effective startpos of row 0. Start by subtracting minExtent, which will contain the
328 // height of the rows outside the beginning of the content item. (Rows can end up outside if
329 // e.g flicking the viewport a long way down, changing cellSize, and then flick back).
330 // NOTE: It's not clearly understood why the flow == QQuickGridView::FlowLeftToRight guard is
331 // needed, since the flow shouldn't normally affect the y postition of an index. But without
332 // it, several auto tests start failing, so we keep it until this part is better understood.
333 rowPos -= minExtent;
334 // minExtent will also contain the size of the topMargin (vData.startMargin), the header, and
335 // the highlightRangeStart. Those should be added before the start of row 0. So we need to subtract
336 // them from the rowPos. But only the largest of topMargin and highlightRangeStart will need
337 // to be taken into account, since having a topMargin will also ensure that currentItem ends
338 // up within the requested highlight range when view is positioned at the beginning.
339 rowPos += qMax(vData.startMargin, highlightRangeStart) + headerSize();
340 }
341
342 return rowPos;
343}
344
346{
347 Q_Q(const QQuickGridView);
348 qreal snapPos = 0;
349 if (!visibleItems.isEmpty()) {
350 qreal highlightStart = highlightRangeStart;
351 pos += highlightStart;
352 pos += rowSize()/2;
353 snapPos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
354 snapPos = pos - std::fmod(pos - snapPos, qreal(rowSize()));
355 snapPos -= highlightStart;
356 qreal maxExtent;
357 qreal minExtent;
359 maxExtent = q->minXExtent()-size();
360 minExtent = q->maxXExtent()-size();
361 } else {
362 maxExtent = flow == QQuickGridView::FlowLeftToRight ? -q->maxYExtent() : -q->maxXExtent();
363 minExtent = flow == QQuickGridView::FlowLeftToRight ? -q->minYExtent() : -q->minXExtent();
364 }
365 if (snapPos > maxExtent)
366 snapPos = maxExtent;
367 if (snapPos < minExtent)
368 snapPos = minExtent;
369 }
370 return snapPos;
371}
372
374{
375 for (FxViewItem *item : visibleItems) {
376 if (item->index == -1)
377 continue;
378 qreal itemTop = item->position();
379 if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
380 return item;
381 }
382 return nullptr;
383}
384
386{
387 int index = currentIndex;
388 for (FxViewItem *item : visibleItems) {
389 if (item->index == -1)
390 continue;
391 qreal itemTop = item->position();
392 FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight.get());
393 if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) {
394 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(item);
395 index = gridItem->index;
396 if (gridItem->colPos() >= hItem->colPos()-colSize()/2 && gridItem->colPos() < hItem->colPos()+colSize()/2)
397 return gridItem->index;
398 }
399 }
400 return index;
401}
402
404{
405 Q_Q(const QQuickGridView);
406 if (flow == QQuickGridView::FlowLeftToRight) {
407 // vertical scroll
408 if (q->effectiveLayoutDirection() == Qt::LeftToRight) {
409 return -q->leftMargin();
410 } else {
411 qreal colSize = cellWidth;
412 int columns = (q->width() - q->leftMargin() - q->rightMargin()) / colSize;
413 return -q->width() + q->rightMargin() + (cellWidth * columns);
414 }
415 } else {
416 // horizontal scroll
417 if (q->effectiveLayoutDirection() == Qt::LeftToRight)
418 return pos;
419 else
420 return -pos - q->width();
421 }
422}
423
425{
426 Q_Q(const QQuickGridView);
427 if (flow == QQuickGridView::FlowLeftToRight) {
428 // vertical scroll
429 if (verticalLayoutDirection == QQuickItemView::TopToBottom)
430 return pos;
431 else
432 return -pos - q->height();
433 } else {
434 // horizontal scroll
435 if (verticalLayoutDirection == QQuickItemView::TopToBottom)
436 return -q->topMargin();
437 else
438 return -q->height() + q->bottomMargin();
439 }
440}
441
443{
444 Q_Q(QQuickGridView);
445 qreal length = flow == QQuickGridView::FlowLeftToRight
446 ? q->width() - q->leftMargin() - q->rightMargin()
447 : q->height() - q->topMargin() - q->bottomMargin();
448 columns = qMax(1, qFloor(length / colSize()));
449}
450
451FxViewItem *QQuickGridViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
452{
453 Q_Q(QQuickGridView);
454 Q_UNUSED(modelIndex);
455 return new FxGridItemSG(item, q, false);
456}
457
458void QQuickGridViewPrivate::initializeViewItem(FxViewItem *item)
459{
460 QQuickItemViewPrivate::initializeViewItem(item);
461
462 // need to track current items that are animating
463 item->trackGeometry(true);
464}
465
466bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
467{
468 qreal colPos = colPosAt(visibleIndex);
469 qreal rowPos = rowPosAt(visibleIndex);
470 if (visibleItems.size()) {
471 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.constLast());
472 rowPos = lastItem->rowPos();
473 int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize());
474 if (++colNum >= columns) {
475 colNum = 0;
476 rowPos += rowSize();
477 }
478 colPos = colNum * colSize();
479 }
480
481 int modelIndex = findLastVisibleIndex();
482 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
483
484 if (visibleItems.size() && (bufferFrom > rowPos + rowSize()*2
485 || bufferTo < rowPosAt(visibleIndex) - rowSize())) {
486 // We've jumped more than a page. Estimate which items are now
487 // visible and fill from there.
488 int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
489 releaseVisibleItems(reusableFlag);
490 modelIndex += count;
491 if (modelIndex >= model->count())
492 modelIndex = model->count() - 1;
493 else if (modelIndex < 0)
494 modelIndex = 0;
495 modelIndex = modelIndex / columns * columns;
496 visibleIndex = modelIndex;
497 colPos = colPosAt(visibleIndex);
498 rowPos = rowPosAt(visibleIndex);
499 }
500
501 int colNum = qFloor((colPos+colSize()/2) / colSize());
502 FxGridItemSG *item = nullptr;
503 bool changed = false;
504
505 QQmlIncubator::IncubationMode incubationMode = doBuffer ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested;
506
507 while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
508 qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << colPos << rowPos;
509 if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex, incubationMode))))
510 break;
511#if QT_CONFIG(quick_viewtransitions)
512 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
513 item->setPosition(colPos, rowPos, true);
514#endif
515 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
516 visibleItems.append(item);
517 if (++colNum >= columns) {
518 colNum = 0;
519 rowPos += rowSize();
520 }
521 colPos = colNum * colSize();
522 ++modelIndex;
523 changed = true;
524 }
525
526 if (doBuffer && requestedIndex != -1) // already waiting for an item
527 return changed;
528
529 // Find first column
530 if (visibleItems.size()) {
531 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
532 rowPos = firstItem->rowPos();
533 colPos = firstItem->colPos();
534 }
535 colNum = qFloor((colPos+colSize()/2) / colSize());
536 if (--colNum < 0) {
537 colNum = columns - 1;
538 rowPos -= rowSize();
539 }
540
541 // Prepend
542 colPos = colNum * colSize();
543 while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
544 qCDebug(lcItemViewDelegateLifecycle) << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
545 if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1, incubationMode))))
546 break;
547 --visibleIndex;
548#if QT_CONFIG(quick_viewtransitions)
549 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
550 item->setPosition(colPos, rowPos, true);
551#endif
552 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
553 visibleItems.prepend(item);
554 if (--colNum < 0) {
555 colNum = columns-1;
556 rowPos -= rowSize();
557 }
558 colPos = colNum * colSize();
559 changed = true;
560 }
561
562 return changed;
563}
564
565void QQuickGridViewPrivate::removeItem(FxViewItem *item)
566{
567#if QT_CONFIG(quick_viewtransitions)
568 if (item->transitionScheduledOrRunning()) {
569 qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName();
570 item->releaseAfterTransition = true;
571 releasePendingTransition.append(item);
572 } else
573#endif
574 {
575 releaseItem(item, reusableFlag);
576 }
577}
578
579bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
580{
581 FxGridItemSG *item = nullptr;
582 bool changed = false;
583
584 while (visibleItems.size() > 1
585 && (item = static_cast<FxGridItemSG*>(visibleItems.constFirst()))
586 && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
587 if (item->attached->delayRemove())
588 break;
589 qCDebug(lcItemViewDelegateLifecycle) << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
590 if (item->index != -1)
591 visibleIndex++;
592 visibleItems.removeFirst();
593 removeItem(item);
594 changed = true;
595 }
596 while (visibleItems.size() > 1
597 && (item = static_cast<FxGridItemSG*>(visibleItems.constLast()))
598 && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
599 if (item->attached->delayRemove())
600 break;
601 qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.size()-1;
602 visibleItems.removeLast();
603 removeItem(item);
604 changed = true;
605 }
606
607 return changed;
608}
609
611{
613 QQuickItemViewPrivate::updateViewport();
614}
615
617{
618 if (visibleItems.size()) {
619 const qreal from = isContentFlowReversed() ? -position()-displayMarginBeginning-size() : position()-displayMarginBeginning;
620 const qreal to = isContentFlowReversed() ? -position()+displayMarginEnd : position()+size()+displayMarginEnd;
621
622 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
623 qreal rowPos = firstItem->rowPos();
624 qreal colPos = firstItem->colPos();
625 int col = visibleIndex % columns;
626 if (colPos != col * colSize()) {
627 colPos = col * colSize();
628 firstItem->setPosition(colPos, rowPos);
629 }
630 firstItem->setVisible(firstItem->rowPos() + rowSize() >= from && firstItem->rowPos() <= to);
631 for (int i = 1; i < visibleItems.size(); ++i) {
632 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
633 if (++col >= columns) {
634 col = 0;
635 rowPos += rowSize();
636 }
637 colPos = col * colSize();
638 if (item->index >= fromModelIndex) {
639 item->setPosition(colPos, rowPos);
640 item->setVisible(item->rowPos() + rowSize() >= from && item->rowPos() <= to);
641 }
642 }
643 }
644}
645
646void QQuickGridViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
647{
648 int count = sizeBuffer / rowSize();
649 static_cast<FxGridItemSG *>(item)->setPosition(colPosAt(index + count), rowPosAt(index + count));
650}
651
652void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
653{
654 Q_Q(QQuickGridView);
655 qreal pos = position();
656 if (flow == QQuickGridView::FlowLeftToRight) {
657 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
658 qreal y = (verticalLayoutDirection == QQuickItemView::TopToBottom)
659 ? rowPosAt(index)
660 : -rowPosAt(index) - item->height();
661 item->setPosition(QPointF(colPosAt(index), y));
662 }
663 } else {
664 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
665 qreal y = (verticalLayoutDirection == QQuickItemView::TopToBottom)
666 ? colPosAt(index)
667 : -colPosAt(index) - item->height();
668 if (flow == QQuickGridView::FlowTopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft)
669 item->setPosition(QPointF(-rowPosAt(index)-item->width(), y));
670 else
671 item->setPosition(QPointF(rowPosAt(index), y));
672 }
673 }
674}
675
677{
678 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.constFirst());
679 item->setPosition(0, pos);
680}
681
682void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible)
683{
684 if (!visibleItems.size())
685 return;
686
687 int moveCount = (forwards - backwards) / rowSize();
688 if (moveCount == 0 && changeBeforeVisible != 0)
689 moveCount += (changeBeforeVisible % columns) - (columns - 1);
690
691 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
692 gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
693}
694
695void QQuickGridViewPrivate::createHighlight(bool onDestruction)
696{
697 bool changed = false;
698 if (highlight) {
699 if (trackedItem == highlight.get())
700 trackedItem = nullptr;
701 highlight.reset();
702
703 delete highlightXAnimator;
704 delete highlightYAnimator;
705 highlightXAnimator = nullptr;
706 highlightYAnimator = nullptr;
707
708 changed = true;
709 }
710
711 if (onDestruction)
712 return;
713
714 Q_Q(QQuickGridView);
715 if (currentItem) {
716 QQuickItem *item = createHighlightItem();
717 if (item) {
718 std::unique_ptr<FxGridItemSG> newHighlight
719 = std::make_unique<FxGridItemSG>(item, q, true);
720 newHighlight->trackGeometry(true);
721 if (autoHighlight)
723 highlightXAnimator = new QSmoothedAnimation;
724 highlightXAnimator->target = QQmlProperty(item, QLatin1String("x"));
725 highlightXAnimator->userDuration = highlightMoveDuration;
726 highlightYAnimator = new QSmoothedAnimation;
727 highlightYAnimator->target = QQmlProperty(item, QLatin1String("y"));
728 highlightYAnimator->userDuration = highlightMoveDuration;
729
730 highlight = std::move(newHighlight);
731 changed = true;
732 }
733 }
734 if (changed)
735 emit q->highlightItemChanged();
736}
737
739{
740 applyPendingChanges();
741
742 if ((!currentItem && highlight) || (currentItem && !highlight))
744 bool strictHighlight = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
745 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
746 // auto-update highlight
747 highlightXAnimator->to = currentItem->itemX();
748 highlightYAnimator->to = currentItem->itemY();
749 highlight->item->setSize(currentItem->item->size());
750
751 highlightXAnimator->restart();
752 highlightYAnimator->restart();
753 }
754 updateTrackedItem();
755}
756
758{
759 if (highlight && currentItem) {
760 FxGridItemSG *cItem = static_cast<FxGridItemSG*>(currentItem);
761 static_cast<FxGridItemSG *>(highlight.get())->setPosition(cItem->colPos(), cItem->rowPos());
762 }
763}
764
766{
767 if (!header)
768 return 0.0;
769 return flow == QQuickGridView::FlowLeftToRight ? header->item->height() : header->item->width();
770}
771
773{
774 if (!footer)
775 return 0.0;
776 return flow == QQuickGridView::FlowLeftToRight? footer->item->height() : footer->item->width();
777}
778
780{
781 return index / columns == 0;
782}
783
785{
786 return index / columns == (model->count()-1) / columns;
787}
788
790{
791 Q_Q(QQuickGridView);
792 bool created = false;
793 if (!footer) {
794 QQuickItem *item = createComponentItem(footerComponent, 1.0);
795 if (!item)
796 return;
797 footer = new FxGridItemSG(item, q, true);
798 footer->trackGeometry(true);
799 created = true;
800 }
801
802 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(footer);
803 qreal colOffset = 0;
804 qreal rowOffset = 0;
805 if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
806 if (flow == QQuickGridView::FlowTopToBottom)
807 rowOffset += gridItem->item->width() - cellWidth;
808 else
809 colOffset += gridItem->item->width() - cellWidth;
810 }
811 if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
812 if (flow == QQuickGridView::FlowTopToBottom)
813 colOffset += gridItem->item->height() - cellHeight;
814 else
815 rowOffset += gridItem->item->height() - cellHeight;
816 }
817 if (visibleItems.size()) {
818 qreal endPos = lastPosition();
819 if (findLastVisibleIndex() == model->count()-1) {
820 gridItem->setPosition(colOffset, endPos + rowOffset);
821 } else {
822 qreal visiblePos = isContentFlowReversed() ? -position() : position() + size();
823 if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset)
824 gridItem->setPosition(colOffset, endPos + rowOffset);
825 }
826 } else {
827 gridItem->setPosition(colOffset, rowOffset);
828 }
829
830 if (created)
831 emit q->footerItemChanged();
832}
833
835{
836 QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
837 qmlAttachedPropertiesObject<QQuickGridView>(item));
838 if (attached)
839 attached->setView(const_cast<QQuickGridView*>(q_func()));
840}
841
843{
844 Q_Q(QQuickGridView);
845 bool created = false;
846 if (!header) {
847 QQuickItem *item = createComponentItem(headerComponent, 1.0);
848 if (!item)
849 return;
850 header = new FxGridItemSG(item, q, true);
851 header->trackGeometry(true);
852 created = true;
853 }
854
855 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(header);
856 qreal colOffset = 0;
857 qreal rowOffset = -headerSize();
858 if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
859 if (flow == QQuickGridView::FlowTopToBottom)
860 rowOffset += gridItem->item->width() - cellWidth;
861 else
862 colOffset += gridItem->item->width() - cellWidth;
863 }
864 if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
865 if (flow == QQuickGridView::FlowTopToBottom)
866 colOffset += gridItem->item->height() - cellHeight;
867 else
868 rowOffset += gridItem->item->height() - cellHeight;
869 }
870 if (visibleItems.size()) {
871 qreal startPos = originPosition();
872 if (visibleIndex == 0) {
873 gridItem->setPosition(colOffset, startPos + rowOffset);
874 } else {
875 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
876 qreal headerPos = isContentFlowReversed() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
877 if (tempPos <= startPos || headerPos > startPos + rowOffset)
878 gridItem->setPosition(colOffset, startPos + rowOffset);
879 }
880 } else {
882 gridItem->setPosition(colOffset, rowOffset);
883 else
884 gridItem->setPosition(colOffset, -headerSize());
885 }
886
887 if (created)
888 emit q->headerItemChanged();
889}
890
892{
893 if (currentItem && currentIndex >= 0) {
894 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(currentItem);
895 FxViewItem *actualItem = visibleItem(currentIndex);
896
897 // don't reposition the item if it's about to be transitioned to another position
898 if ((!actualItem
899#if QT_CONFIG(quick_viewtransitions)
900 || !actualItem->transitionScheduledOrRunning()
901#endif
902 ))
903 gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
904 }
905}
906
908{
909 if (flow == QQuickGridView::FlowLeftToRight)
910 fixupY();
911 else
912 fixupX();
913}
914
915void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
916{
917 if ((flow == QQuickGridView::FlowTopToBottom && &data == &vData)
918 || (flow == QQuickGridView::FlowLeftToRight && &data == &hData))
919 return;
920
921 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
922
923 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
924
925 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
926 if (snapMode != QQuickGridView::NoSnap) {
927 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
928 if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
929 // if we've been dragged < rowSize()/2 then bias towards the next row
930 qreal dist = data.move.value() - data.pressPos;
931 qreal bias = 0;
932 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
933 bias = rowSize()/2;
934 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2)
935 bias = -rowSize()/2;
937 bias = -bias;
938 tempPosition -= bias;
939 }
940 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
941 if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
942 // StrictlyEnforceRange always keeps an item in range
944 topItem = currentItem;
945 }
946 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
947 if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
948 // StrictlyEnforceRange always keeps an item in range
950 bottomItem = currentItem;
951 }
952 qreal pos;
953 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
954 if (topItem && (isInBounds || strictHighlightRange)) {
955 qreal headerPos = header ? static_cast<FxGridItemSG*>(header)->rowPos() : 0;
956 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) {
957 pos = isContentFlowReversed() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart;
958 } else {
959 if (isContentFlowReversed())
960 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
961 else
962 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
963 }
964 } else if (bottomItem && isInBounds) {
965 if (isContentFlowReversed())
966 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
967 else
968 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
969 } else {
970 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
971 return;
972 }
973
974 qreal dist = qAbs(data.move + pos);
975 if (dist > 0) {
976 timeline.reset(data.move);
977 if (fixupMode != Immediate) {
978 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
979 data.fixingUp = true;
980 } else {
981 timeline.set(data.move, -pos);
982 }
983 vTime = timeline.time();
984 }
985 } else if (haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange) {
986 if (currentItem) {
988 qreal pos = static_cast<FxGridItemSG*>(currentItem)->rowPos();
989 if (viewPos < pos + rowSize() - highlightRangeEnd)
990 viewPos = pos + rowSize() - highlightRangeEnd;
991 if (viewPos > pos - highlightRangeStart)
992 viewPos = pos - highlightRangeStart;
994 viewPos = -viewPos-size();
995 timeline.reset(data.move);
996 if (viewPos != position()) {
997 if (fixupMode != Immediate) {
998 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
999 data.fixingUp = true;
1000 } else {
1001 timeline.set(data.move, -viewPos);
1002 }
1003 }
1004 vTime = timeline.time();
1005 }
1006 } else {
1007 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1008 }
1009 data.inOvershoot = false;
1010 fixupMode = Normal;
1011}
1012
1013bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1014 QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
1015{
1016 data.fixingUp = false;
1017 moveReason = Mouse;
1018 if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange)
1019 && snapMode == QQuickGridView::NoSnap) {
1020 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, eventType, velocity);
1021 }
1022 qreal maxDistance = 0;
1023 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1024 // -ve velocity means list is moving up/left
1025 if (velocity > 0) {
1026 if (data.move.value() < minExtent) {
1027 if (snapMode == QQuickGridView::SnapOneRow) {
1028 // if we've been dragged < averageSize/2 then bias towards the next item
1029 qreal dist = data.move.value() - data.pressPos;
1030 qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
1032 bias = -bias;
1033 data.flickTarget = -snapPosAt(-dataValue - bias);
1034 maxDistance = qAbs(data.flickTarget - data.move.value());
1035 velocity = maxVelocity;
1036 } else {
1037 maxDistance = qAbs(minExtent - data.move.value());
1038 }
1039 }
1040 if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
1041 data.flickTarget = minExtent;
1042 } else {
1043 if (data.move.value() > maxExtent) {
1044 if (snapMode == QQuickGridView::SnapOneRow) {
1045 // if we've been dragged < averageSize/2 then bias towards the next item
1046 qreal dist = data.move.value() - data.pressPos;
1047 qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
1049 bias = -bias;
1050 data.flickTarget = -snapPosAt(-dataValue + bias);
1051 maxDistance = qAbs(data.flickTarget - data.move.value());
1052 velocity = -maxVelocity;
1053 } else {
1054 maxDistance = qAbs(maxExtent - data.move.value());
1055 }
1056 }
1057 if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
1058 data.flickTarget = maxExtent;
1059 }
1060 bool overShoot = boundsBehavior & QQuickFlickable::OvershootBounds;
1061 if (maxDistance > 0 || overShoot) {
1062 // This mode requires the grid to stop exactly on a row boundary.
1063 qreal v = velocity;
1064 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1065 if (v < 0)
1066 v = -maxVelocity;
1067 else
1068 v = maxVelocity;
1069 }
1070 qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
1071 qreal v2 = v * v;
1072 qreal overshootDist = 0.0;
1073 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) {
1074 // + rowSize()/4 to encourage moving at least one item in the flick direction
1075 qreal dist = v2 / (accel * 2.0) + rowSize()/4;
1076 dist = qMin(dist, maxDistance);
1077 if (v > 0)
1078 dist = -dist;
1079 if (snapMode != QQuickGridView::SnapOneRow) {
1080 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1081 data.flickTarget = -snapPosAt(-dataValue + distTemp);
1082 }
1083 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1084 if (overShoot) {
1085 if (data.flickTarget >= minExtent) {
1086 overshootDist = overShootDistance(vSize);
1087 data.flickTarget += overshootDist;
1088 } else if (data.flickTarget <= maxExtent) {
1089 overshootDist = overShootDistance(vSize);
1090 data.flickTarget -= overshootDist;
1091 }
1092 }
1093 qreal adjDist = -data.flickTarget + data.move.value();
1094 if (qAbs(adjDist) > qAbs(dist)) {
1095 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1096 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1097 if (adjv2 > v2) {
1098 v2 = adjv2;
1099 v = qSqrt(v2);
1100 if (dist > 0)
1101 v = -v;
1102 }
1103 }
1104 dist = adjDist;
1105 accel = v2 / (2.0f * qAbs(dist));
1106 } else {
1107 data.flickTarget = velocity > 0 ? minExtent : maxExtent;
1108 overshootDist = overShoot ? overShootDistance(vSize) : 0;
1109 }
1110 timeline.reset(data.move);
1111 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1112 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1113 return true;
1114 } else {
1115 timeline.reset(data.move);
1116 fixup(data, minExtent, maxExtent);
1117 return false;
1118 }
1119}
1120
1122{
1123 QObject *attachedObject = qmlAttachedPropertiesObject<QQuickGridView>(object);
1124 return static_cast<QQuickItemViewAttached *>(attachedObject);
1125}
1126
1127
1128//----------------------------------------------------------------------------
1129/*!
1130 \qmltype GridView
1131 \nativetype QQuickGridView
1132 \inqmlmodule QtQuick
1133 \ingroup qtquick-views
1134
1135 \inherits Flickable
1136 \brief For specifying a grid view of items provided by a model.
1137
1138 A GridView displays data from models created from built-in QML types like ListModel
1139 and XmlListModel, or custom model classes defined in C++ that inherit from
1140 QAbstractListModel.
1141
1142 A GridView has a \l model, which defines the data to be displayed, and
1143 a \l delegate, which defines how the data should be displayed. Items in a
1144 GridView are laid out horizontally or vertically. Grid views are inherently flickable
1145 as GridView inherits from \l Flickable.
1146
1147 \section1 Example Usage
1148
1149 The following example shows the definition of a simple list model defined
1150 in a file called \c ContactModel.qml:
1151
1152 \snippet qml/gridview/ContactModel.qml 0
1153
1154 \div {class="float-right"}
1155 \inlineimage gridview-simple.png
1156 \enddiv
1157
1158 This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
1159 for more information about creating reusable components like this.
1160
1161 Another component can display this model data in a GridView, as in the following
1162 example, which creates a \c ContactModel component for its model, and a \l Column
1163 (containing \l Image and \l Text items) for its delegate.
1164
1165 \clearfloat
1166 \snippet qml/gridview/gridview.qml import
1167 \codeline
1168 \snippet qml/gridview/gridview.qml classdocs simple
1169
1170 \div {class="float-right"}
1171 \inlineimage gridview-highlight.png
1172 \enddiv
1173
1174 The view will create a new delegate for each item in the model. Note that the delegate
1175 is able to access the model's \c name and \c portrait data directly.
1176
1177 An improved grid view is shown below. The delegate is visually improved and is moved
1178 into a separate \c contactDelegate component.
1179
1180 \clearfloat
1181 \snippet qml/gridview/gridview.qml classdocs advanced
1182
1183 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1184 and \c focus is set to \c true to enable keyboard navigation for the grid view.
1185 The grid view itself is a focus scope (see \l{Keyboard Focus in Qt Quick} for more details).
1186
1187 Delegates are instantiated as needed and may be destroyed at any time.
1188 State should \e never be stored in a delegate.
1189
1190 GridView attaches a number of properties to the root item of the delegate, for example
1191 \c {GridView.isCurrentItem}. In the following example, the root delegate item can access
1192 this attached property directly as \c GridView.isCurrentItem, while the child
1193 \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
1194
1195 \snippet qml/gridview/gridview.qml isCurrentItem
1196
1197 \note Views do not set the \l{Item::}{clip} property automatically.
1198 If the view is not clipped by another item or the screen, it will be necessary
1199 to set this property to true in order to clip the items that are partially or
1200 fully outside the view.
1201
1202
1203 \section1 GridView Layouts
1204
1205 The layout of the items in a GridView can be controlled by these properties:
1206
1207 \list
1208 \li \l flow - controls whether items flow from left to right (as a series of rows)
1209 or from top to bottom (as a series of columns). This value can be either
1210 GridView.FlowLeftToRight or GridView.FlowTopToBottom.
1211 \li \l layoutDirection - controls the horizontal layout direction: that is, whether items
1212 are laid out from the left side of the view to the right, or vice-versa. This value can
1213 be either Qt.LeftToRight or Qt.RightToLeft.
1214 \li \l verticalLayoutDirection - controls the vertical layout direction: that is, whether items
1215 are laid out from the top of the view down towards the bottom of the view, or vice-versa.
1216 This value can be either GridView.TopToBottom or GridView.BottomToTop.
1217 \endlist
1218
1219 By default, a GridView flows from left to right, and items are laid out from left to right
1220 horizontally, and from top to bottom vertically.
1221
1222 These properties can be combined to produce a variety of layouts, as shown in the table below.
1223 The GridViews in the first row all have a \l flow value of GridView.FlowLeftToRight, but use
1224 different combinations of horizontal and vertical layout directions (specified by \l layoutDirection
1225 and \l verticalLayoutDirection respectively). Similarly, the GridViews in the second row below
1226 all have a \l flow value of GridView.FlowTopToBottom, but use different combinations of horizontal and
1227 vertical layout directions to lay out their items in different ways.
1228
1229 \table
1230 \header
1231 \li {4, 1}
1232 \b GridViews with GridView.FlowLeftToRight flow
1233 \row
1234 \li \b (H) Left to right \b (V) Top to bottom
1235 \image gridview-layout-lefttoright-ltr-ttb.png
1236 \li \b (H) Right to left \b (V) Top to bottom
1237 \image gridview-layout-lefttoright-rtl-ttb.png
1238 \li \b (H) Left to right \b (V) Bottom to top
1239 \image gridview-layout-lefttoright-ltr-btt.png
1240 \li \b (H) Right to left \b (V) Bottom to top
1241 \image gridview-layout-lefttoright-rtl-btt.png
1242 \header
1243 \li {4, 1}
1244 \b GridViews with GridView.FlowTopToBottom flow
1245 \row
1246 \li \b (H) Left to right \b (V) Top to bottom
1247 \image gridview-layout-toptobottom-ltr-ttb.png
1248 \li \b (H) Right to left \b (V) Top to bottom
1249 \image gridview-layout-toptobottom-rtl-ttb.png
1250 \li \b (H) Left to right \b (V) Bottom to top
1251 \image gridview-layout-toptobottom-ltr-btt.png
1252 \li \b (H) Right to left \b (V) Bottom to top
1253 \image gridview-layout-toptobottom-rtl-btt.png
1254 \endtable
1255
1256 \sa {QML Data Models}, ListView, PathView, {Qt Quick Examples - Views}
1257*/
1258
1259QQuickGridView::QQuickGridView(QQuickItem *parent)
1260 : QQuickItemView(*(new QQuickGridViewPrivate), parent)
1261{
1262}
1263
1264void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1265{
1266 Q_D(QQuickGridView);
1267 if (d->autoHighlight != autoHighlight) {
1268 if (!autoHighlight && d->highlightXAnimator) {
1269 d->highlightXAnimator->stop();
1270 d->highlightYAnimator->stop();
1271 }
1272 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1273 }
1274}
1275
1276/*!
1277 \qmlattachedproperty bool QtQuick::GridView::isCurrentItem
1278 \readonly
1279
1280 This attached property is true if this delegate is the current item; otherwise false.
1281
1282 It is attached to each instance of the delegate.
1283
1284 \snippet qml/gridview/gridview.qml isCurrentItem
1285*/
1286
1287/*!
1288 \qmlattachedproperty GridView QtQuick::GridView::view
1289 \readonly
1290
1291 This attached property holds the view that manages this delegate instance.
1292
1293 It is attached to each instance of the delegate and also to the header, the footer
1294 and the highlight delegates.
1295*/
1296
1297/*!
1298 \qmlattachedproperty bool QtQuick::GridView::delayRemove
1299
1300 This attached property holds whether the delegate may be destroyed. It
1301 is attached to each instance of the delegate. The default value is false.
1302
1303 It is sometimes necessary to delay the destruction of an item
1304 until an animation completes. The example delegate below ensures that the
1305 animation completes before the item is removed from the list.
1306
1307 \snippet qml/gridview/gridview.qml delayRemove
1308
1309 If a \l remove transition has been specified, it will not be applied until
1310 delayRemove is returned to \c false.
1311*/
1312
1313/*!
1314 \qmlattachedsignal QtQuick::GridView::add()
1315 This attached signal is emitted immediately after an item is added to the view.
1316*/
1317
1318/*!
1319 \qmlattachedsignal QtQuick::GridView::remove()
1320 This attached signal is emitted immediately before an item is removed from the view.
1321
1322 If a \l remove transition has been specified, it is applied after
1323 this signal is handled, providing that \l delayRemove is false.
1324*/
1325
1326
1327/*!
1328 \qmlproperty model QtQuick::GridView::model
1329 This property holds the model providing data for the grid.
1330
1331 The model provides the set of data that is used to create the items
1332 in the view. Models can be created directly in QML using \l ListModel,
1333 \l DelegateModel, \l ObjectModel, or provided by C++ model classes.
1334 If a C++ model class is used, it must be a subclass of
1335 \l QAbstractItemModel or a simple list.
1336
1337 \sa {qml-data-models}{Data Models}
1338*/
1339
1340/*!
1341 \qmlproperty Component QtQuick::GridView::delegate
1342
1343 The delegate provides a template defining each item instantiated by the view.
1344 The index is exposed as an accessible \c index property. Properties of the
1345 model are also available depending upon the type of \l {qml-data-models}{Data Model}.
1346
1347 The number of objects and bindings in the delegate has a direct effect on the
1348 flicking performance of the view. If at all possible, place functionality
1349 that is not needed for the normal display of the delegate in a \l Loader which
1350 can load additional components when needed.
1351
1352 The item size of the GridView is determined by cellHeight and cellWidth. It will not resize the items
1353 based on the size of the root item in the delegate.
1354
1355 The default \l {QQuickItem::z}{stacking order} of delegate instances is \c 1.
1356
1357 \note Delegates are instantiated as needed and may be destroyed at any time.
1358 State should \e never be stored in a delegate.
1359*/
1360
1361/*!
1362 \qmlproperty enumeration QtQuick::GridView::delegateModelAccess
1363 \since 6.10
1364
1365 \include delegatemodelaccess.qdocinc
1366*/
1367
1368/*!
1369 \qmlproperty int QtQuick::GridView::currentIndex
1370 \qmlproperty Item QtQuick::GridView::currentItem
1371
1372 The \c currentIndex property holds the index of the current item, and
1373 \c currentItem holds the current item. Setting the currentIndex to -1
1374 will clear the highlight and set currentItem to null.
1375
1376 If highlightFollowsCurrentItem is \c true, setting either of these
1377 properties will smoothly scroll the GridView so that the current
1378 item becomes visible.
1379
1380 Note that the position of the current item
1381 may only be approximate until it becomes visible in the view.
1382*/
1383
1384
1385/*!
1386 \qmlproperty Item QtQuick::GridView::highlightItem
1387
1388 This holds the highlight item created from the \l highlight component.
1389
1390 The highlightItem is managed by the view unless
1391 \l highlightFollowsCurrentItem is set to false.
1392 The default \l {QQuickItem::z}{stacking order}
1393 of the highlight item is \c 0.
1394
1395 \sa highlight, highlightFollowsCurrentItem
1396*/
1397
1398
1399/*!
1400 \qmlproperty int QtQuick::GridView::count
1401 This property holds the number of items in the model.
1402*/
1403
1404/*!
1405 \qmlproperty bool QtQuick::GridView::reuseItems
1406
1407 This property enables you to reuse items that are instantiated
1408 from the \l delegate. If set to \c false, any currently
1409 pooled items are destroyed.
1410
1411 This property is \c false by default.
1412
1413 \since 5.15
1414
1415 \sa {Reusing items}, pooled(), reused()
1416*/
1417
1418/*!
1419 \qmlattachedsignal QtQuick::GridView::pooled()
1420
1421 This signal is emitted after an item has been added to the reuse
1422 pool. You can use it to pause ongoing timers or animations inside
1423 the item, or free up resources that cannot be reused.
1424
1425 This signal is emitted only if the \l reuseItems property is \c true.
1426
1427 \sa {Reusing items}, reuseItems, reused()
1428*/
1429
1430/*!
1431 \qmlattachedsignal QtQuick::GridView::reused()
1432
1433 This signal is emitted after an item has been reused. At this point, the
1434 item has been taken out of the pool and placed inside the content view,
1435 and the model properties such as \c index and \c row have been updated.
1436
1437 Other properties that are not provided by the model does not change when an
1438 item is reused. You should avoid storing any state inside a delegate, but if
1439 you do, manually reset that state on receiving this signal.
1440
1441 This signal is emitted when the item is reused, and not the first time the
1442 item is created.
1443
1444 This signal is emitted only if the \l reuseItems property is \c true.
1445
1446 \sa {Reusing items}, reuseItems, pooled()
1447*/
1448
1449/*!
1450 \qmlproperty Component QtQuick::GridView::highlight
1451 This property holds the component to use as the highlight.
1452
1453 An instance of the highlight component is created for each view.
1454 The geometry of the resulting component instance will be managed by the view
1455 so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1456 The default \l {QQuickItem::z}{stacking order} of the highlight item is \c 0.
1457
1458 \sa highlightItem, highlightFollowsCurrentItem
1459*/
1460
1461/*!
1462 \qmlproperty bool QtQuick::GridView::highlightFollowsCurrentItem
1463 This property sets whether the highlight is managed by the view.
1464
1465 If this property is true (the default value), the highlight is moved smoothly
1466 to follow the current item. Otherwise, the
1467 highlight is not moved by the view, and any movement must be implemented
1468 by the highlight.
1469
1470 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1471
1472 \snippet qml/gridview/gridview.qml highlightFollowsCurrentItem
1473*/
1474
1475
1476/*!
1477 \qmlproperty int QtQuick::GridView::highlightMoveDuration
1478 This property holds the move animation duration of the highlight delegate.
1479
1480 highlightFollowsCurrentItem must be true for this property
1481 to have effect.
1482
1483 The default value for the duration is 150ms.
1484
1485 \sa highlightFollowsCurrentItem
1486*/
1487
1488/*!
1489 \qmlproperty real QtQuick::GridView::preferredHighlightBegin
1490 \qmlproperty real QtQuick::GridView::preferredHighlightEnd
1491 \qmlproperty enumeration QtQuick::GridView::highlightRangeMode
1492
1493 These properties define the preferred range of the highlight (for the current item)
1494 within the view. The \c preferredHighlightBegin value must be less than the
1495 \c preferredHighlightEnd value.
1496
1497 These properties affect the position of the current item when the view is scrolled.
1498 For example, if the currently selected item should stay in the middle of the
1499 view when it is scrolled, set the \c preferredHighlightBegin and
1500 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1501 item would be. If the \c currentItem is changed programmatically, the view will
1502 automatically scroll so that the current item is in the middle of the view.
1503 Furthermore, the behavior of the current item index will occur whether or not a
1504 highlight exists.
1505
1506 Valid values for \c highlightRangeMode are:
1507
1508 \value GridView.ApplyRange the view attempts to maintain the highlight within the range.
1509 However, the highlight can move outside of the range at the ends of the view or due
1510 to mouse interaction.
1511 \value GridView.StrictlyEnforceRange the highlight never moves outside of the range.
1512 The current item changes if a keyboard or mouse action would cause the highlight to move
1513 outside of the range.
1514 \value GridView.NoHighlightRange the default value
1515*/
1516
1517
1518/*!
1519 \qmlproperty enumeration QtQuick::GridView::layoutDirection
1520 This property holds the layout direction of the grid.
1521
1522 Possible values:
1523
1524 \value Qt.LeftToRight (default) Items will be laid out starting in the top, left corner. The flow is
1525 dependent on the \l GridView::flow property.
1526 \value Qt.RightToLeft Items will be laid out starting in the top, right corner. The flow is dependent
1527 on the \l GridView::flow property.
1528
1529 \b Note: If GridView::flow is set to GridView.FlowLeftToRight, this is not to be confused if
1530 GridView::layoutDirection is set to Qt.RightToLeft. The GridView.FlowLeftToRight flow value simply
1531 indicates that the flow is horizontal.
1532
1533 \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
1534*/
1535
1536
1537/*!
1538 \qmlproperty enumeration QtQuick::GridView::effectiveLayoutDirection
1539 This property holds the effective layout direction of the grid.
1540
1541 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1542 the visual layout direction of the grid will be mirrored. However, the
1543 property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1544
1545 \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1546*/
1547
1548/*!
1549 \qmlproperty enumeration QtQuick::GridView::verticalLayoutDirection
1550 This property holds the vertical layout direction of the grid.
1551
1552 Possible values:
1553
1554 \value GridView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
1555 \value GridView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
1556
1557 \sa GridView::layoutDirection
1558*/
1559
1560/*!
1561 \qmlproperty bool QtQuick::GridView::keyNavigationWraps
1562 This property holds whether the grid wraps key navigation
1563
1564 If this is true, key navigation that would move the current item selection
1565 past one end of the view instead wraps around and moves the selection to
1566 the other end of the view.
1567
1568 By default, key navigation is not wrapped.
1569*/
1570
1571/*!
1572 \qmlproperty bool QtQuick::GridView::keyNavigationEnabled
1573 \since 5.7
1574
1575 This property holds whether the key navigation of the grid is enabled.
1576
1577 If this is \c true, the user can navigate the view with a keyboard.
1578 It is useful for applications that need to selectively enable or
1579 disable mouse and keyboard interaction.
1580
1581 By default, the value of this property is bound to
1582 \l {Flickable::}{interactive} to ensure behavior compatibility for
1583 existing applications. When explicitly set, it will cease to be bound to
1584 the interactive property.
1585
1586 \sa {Flickable::}{interactive}
1587*/
1588
1589/*!
1590 \qmlproperty int QtQuick::GridView::cacheBuffer
1591 This property determines whether delegates are retained outside the
1592 visible area of the view.
1593
1594 If this value is greater than zero, the view may keep as many delegates
1595 instantiated as will fit within the buffer specified. For example,
1596 if in a vertical view the delegate is 20 pixels high, there are 3
1597 columns and \c cacheBuffer is
1598 set to 40, then up to 6 delegates above and 6 delegates below the visible
1599 area may be created/retained. The buffered delegates are created asynchronously,
1600 allowing creation to occur across multiple frames and reducing the
1601 likelihood of skipping frames. In order to improve painting performance
1602 delegates outside the visible area are not painted.
1603
1604 The default value of this property is platform dependent, but will usually
1605 be a value greater than zero. Negative values are ignored.
1606
1607 Note that cacheBuffer is not a pixel buffer - it only maintains additional
1608 instantiated delegates.
1609
1610 \note Setting this property is not a replacement for creating efficient delegates.
1611 It can improve the smoothness of scrolling behavior at the expense of additional
1612 memory usage. The fewer objects and bindings in a delegate, the faster a
1613 view can be scrolled. It is important to realize that setting a cacheBuffer
1614 will only postpone issues caused by slow-loading delegates, it is not a
1615 solution for this scenario.
1616
1617 The cacheBuffer operates outside of any display margins specified by
1618 displayMarginBeginning or displayMarginEnd.
1619*/
1620
1621/*!
1622 \qmlproperty int QtQuick::GridView::displayMarginBeginning
1623 \qmlproperty int QtQuick::GridView::displayMarginEnd
1624 \since QtQuick 2.3
1625
1626 This property allows delegates to be displayed outside of the view geometry.
1627
1628 If this value is non-zero, the view will create extra delegates before the
1629 start of the view, or after the end. The view will create as many delegates
1630 as it can fit into the pixel size specified.
1631
1632 For example, if in a vertical view the delegate is 20 pixels high,
1633 there are 3 columns, and
1634 \c displayMarginBeginning and \c displayMarginEnd are both set to 40,
1635 then 6 delegates above and 6 delegates below will be created and shown.
1636
1637 The default value is 0.
1638
1639 This property is meant for allowing certain UI configurations,
1640 and not as a performance optimization. If you wish to create delegates
1641 outside of the view geometry for performance reasons, you probably
1642 want to use the cacheBuffer property instead.
1643*/
1644
1645void QQuickGridView::setHighlightMoveDuration(int duration)
1646{
1647 Q_D(QQuickGridView);
1648 if (d->highlightMoveDuration != duration) {
1649 if (d->highlightYAnimator) {
1650 d->highlightXAnimator->userDuration = duration;
1651 d->highlightYAnimator->userDuration = duration;
1652 }
1653 QQuickItemView::setHighlightMoveDuration(duration);
1654 }
1655}
1656
1657/*!
1658 \qmlproperty enumeration QtQuick::GridView::flow
1659 This property holds the flow of the grid.
1660
1661 Possible values:
1662
1663 \value GridView.FlowLeftToRight (default) Items are laid out from left to right, and the view scrolls vertically
1664 \value GridView.FlowTopToBottom Items are laid out from top to bottom, and the view scrolls horizontally
1665*/
1666QQuickGridView::Flow QQuickGridView::flow() const
1667{
1668 Q_D(const QQuickGridView);
1669 return d->flow;
1670}
1671
1672void QQuickGridView::setFlow(Flow flow)
1673{
1674 Q_D(QQuickGridView);
1675 if (d->flow != flow) {
1676 d->flow = flow;
1677 if (d->flow == FlowLeftToRight) {
1678 setContentWidth(-1);
1679 setFlickableDirection(VerticalFlick);
1680 } else {
1681 setContentHeight(-1);
1682 setFlickableDirection(HorizontalFlick);
1683 }
1684 setContentX(0);
1685 setContentY(0);
1686 d->regenerate(true);
1687 emit flowChanged();
1688 }
1689}
1690
1691
1692/*!
1693 \qmlproperty real QtQuick::GridView::cellWidth
1694 \qmlproperty real QtQuick::GridView::cellHeight
1695
1696 These properties holds the width and height of each cell in the grid.
1697
1698 The default cell size is 100x100.
1699*/
1700qreal QQuickGridView::cellWidth() const
1701{
1702 Q_D(const QQuickGridView);
1703 return d->cellWidth;
1704}
1705
1706void QQuickGridView::setCellWidth(qreal cellWidth)
1707{
1708 Q_D(QQuickGridView);
1709 if (cellWidth != d->cellWidth && cellWidth > 0) {
1710 d->cellWidth = qMax(qreal(1), cellWidth);
1711 d->updateViewport();
1712 emit cellWidthChanged();
1713 d->forceLayoutPolish();
1714 QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
1715 }
1716}
1717
1718qreal QQuickGridView::cellHeight() const
1719{
1720 Q_D(const QQuickGridView);
1721 return d->cellHeight;
1722}
1723
1724void QQuickGridView::setCellHeight(qreal cellHeight)
1725{
1726 Q_D(QQuickGridView);
1727 if (cellHeight != d->cellHeight && cellHeight > 0) {
1728 d->cellHeight = qMax(qreal(1), cellHeight);
1729 d->updateViewport();
1730 emit cellHeightChanged();
1731 d->forceLayoutPolish();
1732 QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
1733 }
1734}
1735/*!
1736 \qmlproperty enumeration QtQuick::GridView::snapMode
1737
1738 This property determines how the view scrolling will settle following a drag or flick.
1739 The possible values are:
1740
1741 \value GridView.NoSnap (default) the view stops anywhere within the visible area.
1742 \value GridView.SnapToRow the view settles with a row (or column for \c GridView.FlowTopToBottom flow)
1743 aligned with the start of the view.
1744 \value GridView.SnapOneRow the view will settle no more than one row (or column for \c GridView.FlowTopToBottom flow)
1745 away from the first visible row at the time the mouse button is released.
1746 This mode is particularly useful for moving one page at a time.
1747*/
1748QQuickGridView::SnapMode QQuickGridView::snapMode() const
1749{
1750 Q_D(const QQuickGridView);
1751 return d->snapMode;
1752}
1753
1754void QQuickGridView::setSnapMode(SnapMode mode)
1755{
1756 Q_D(QQuickGridView);
1757 if (d->snapMode != mode) {
1758 d->snapMode = mode;
1759 emit snapModeChanged();
1760 }
1761}
1762
1763
1764/*!
1765 \qmlproperty Component QtQuick::GridView::footer
1766 This property holds the component to use as the footer.
1767
1768 An instance of the footer component is created for each view. The
1769 footer is positioned at the end of the view, after any items. The
1770 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
1771
1772 \sa header, footerItem
1773*/
1774/*!
1775 \qmlproperty Component QtQuick::GridView::header
1776 This property holds the component to use as the header.
1777
1778 An instance of the header component is created for each view. The
1779 header is positioned at the beginning of the view, before any items.
1780 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
1781
1782 \sa footer, headerItem
1783*/
1784
1785/*!
1786 \qmlproperty Item QtQuick::GridView::headerItem
1787 This holds the header item created from the \l header component.
1788
1789 An instance of the header component is created for each view. The
1790 header is positioned at the beginning of the view, before any items.
1791 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
1792
1793 \sa header, footerItem
1794*/
1795
1796/*!
1797 \qmlproperty Item QtQuick::GridView::footerItem
1798 This holds the footer item created from the \l footer component.
1799
1800 An instance of the footer component is created for each view. The
1801 footer is positioned at the end of the view, after any items. The
1802 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
1803
1804 \sa footer, headerItem
1805*/
1806
1807/*!
1808 \qmlproperty Transition QtQuick::GridView::populate
1809
1810 This property holds the transition to apply to the items that are initially created
1811 for a view.
1812
1813 It is applied to all items that are created when:
1814
1815 \list
1816 \li The view is first created
1817 \li The view's \l model changes in such a way that the visible delegates are completely replaced
1818 \li The view's \l model is \l {QAbstractItemModel::beginResetModel()}{reset},
1819 if the model is a QAbstractItemModel subclass
1820 \endlist
1821
1822 For example, here is a view that specifies such a transition:
1823
1824 \code
1825 GridView {
1826 ...
1827 populate: Transition {
1828 NumberAnimation { properties: "x,y"; duration: 1000 }
1829 }
1830 }
1831 \endcode
1832
1833 When the view is initialized, the view will create all the necessary items for the view,
1834 then animate them to their correct positions within the view over one second.
1835
1836 However when scrolling the view later, the populate transition does not
1837 run, even though delegates are being instantiated as they become visible.
1838 When the model changes in a way that new delegates become visible, the
1839 \l add transition is the one that runs. So you should not depend on the
1840 \c populate transition to initialize properties in the delegate, because it
1841 does not apply to every delegate. If your animation sets the \c to value of
1842 a property, the property should initially have the \c to value, and the
1843 animation should set the \c from value in case it is animated:
1844
1845 \code
1846 GridView {
1847 ...
1848 delegate: Rectangle {
1849 opacity: 1 // not necessary because it's the default; but don't set 0
1850 ...
1851 }
1852 populate: Transition {
1853 NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
1854 }
1855 }
1856 \endcode
1857
1858 For more details and examples on how to use view transitions, see the ViewTransition
1859 documentation.
1860
1861 \sa add, ViewTransition
1862*/
1863
1864/*!
1865 \qmlproperty Transition QtQuick::GridView::add
1866
1867 This property holds the transition to apply to items that are added to the view.
1868
1869 For example, here is a view that specifies such a transition:
1870
1871 \code
1872 GridView {
1873 ...
1874 add: Transition {
1875 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
1876 }
1877 }
1878 \endcode
1879
1880 Whenever an item is added to the above view, the item will be animated from the position (100,100)
1881 to its final x,y position within the view, over one second. The transition only applies to
1882 the new items that are added to the view; it does not apply to the items below that are
1883 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
1884 or \l addDisplaced properties.
1885
1886 For more details and examples on how to use view transitions, see the ViewTransition
1887 documentation.
1888
1889 \note This transition is not applied to the items that are created when the view is initially
1890 populated, or when the view's \l model changes. (In those cases, the \l populate transition is
1891 applied instead.) Additionally, this transition should \e not animate the height of the new item;
1892 doing so will cause any items beneath the new item to be laid out at the wrong position. Instead,
1893 the height can be animated within the \l {add}{onAdd} handler in the delegate.
1894
1895 \sa addDisplaced, populate, ViewTransition
1896*/
1897
1898/*!
1899 \qmlproperty Transition QtQuick::GridView::addDisplaced
1900
1901 This property holds the transition to apply to items within the view that are displaced by
1902 the addition of other items to the view.
1903
1904 For example, here is a view that specifies such a transition:
1905
1906 \code
1907 GridView {
1908 ...
1909 addDisplaced: Transition {
1910 NumberAnimation { properties: "x,y"; duration: 1000 }
1911 }
1912 }
1913 \endcode
1914
1915 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
1916 them to move down (or sideways, if horizontally orientated) within the view. As this
1917 displacement occurs, the items' movement to their new x,y positions within the view will be
1918 animated by a NumberAnimation over one second, as specified. This transition is not applied to
1919 the new item that has been added to the view; to animate the added items, set the \l add
1920 property.
1921
1922 If an item is displaced by multiple types of operations at the same time, it is not defined as to
1923 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1924 if it is not necessary to specify different transitions depending on whether an item is displaced
1925 by an add, move or remove operation, consider setting the \l displaced property instead.
1926
1927 For more details and examples on how to use view transitions, see the ViewTransition
1928 documentation.
1929
1930 \note This transition is not applied to the items that are created when the view is initially
1931 populated, or when the view's \l model changes. In those cases, the \l populate transition is
1932 applied instead.
1933
1934 \sa displaced, add, populate, ViewTransition
1935*/
1936/*!
1937 \qmlproperty Transition QtQuick::GridView::move
1938
1939 This property holds the transition to apply to items in the view that are being moved due
1940 to a move operation in the view's \l model.
1941
1942 For example, here is a view that specifies such a transition:
1943
1944 \code
1945 GridView {
1946 ...
1947 move: Transition {
1948 NumberAnimation { properties: "x,y"; duration: 1000 }
1949 }
1950 }
1951 \endcode
1952
1953 Whenever the \l model performs a move operation to move a particular set of indexes, the
1954 respective items in the view will be animated to their new positions in the view over one
1955 second. The transition only applies to the items that are the subject of the move operation
1956 in the model; it does not apply to items below them that are displaced by the move operation.
1957 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
1958
1959 For more details and examples on how to use view transitions, see the ViewTransition
1960 documentation.
1961
1962 \sa moveDisplaced, ViewTransition
1963*/
1964
1965/*!
1966 \qmlproperty Transition QtQuick::GridView::moveDisplaced
1967
1968 This property holds the transition to apply to items that are displaced by a move operation in
1969 the view's \l model.
1970
1971 For example, here is a view that specifies such a transition:
1972
1973 \code
1974 GridView {
1975 ...
1976 moveDisplaced: Transition {
1977 NumberAnimation { properties: "x,y"; duration: 1000 }
1978 }
1979 }
1980 \endcode
1981
1982 Whenever the \l model performs a move operation to move a particular set of indexes, the items
1983 between the source and destination indexes of the move operation are displaced, causing them
1984 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
1985 displacement occurs, the items' movement to their new x,y positions within the view will be
1986 animated by a NumberAnimation over one second, as specified. This transition is not applied to
1987 the items that are the actual subjects of the move operation; to animate the moved items, set
1988 the \l move property.
1989
1990 If an item is displaced by multiple types of operations at the same time, it is not defined as to
1991 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1992 if it is not necessary to specify different transitions depending on whether an item is displaced
1993 by an add, move or remove operation, consider setting the \l displaced property instead.
1994
1995 For more details and examples on how to use view transitions, see the ViewTransition
1996 documentation.
1997
1998 \sa displaced, move, ViewTransition
1999*/
2000
2001/*!
2002 \qmlproperty Transition QtQuick::GridView::remove
2003
2004 This property holds the transition to apply to items that are removed from the view.
2005
2006 For example, here is a view that specifies such a transition:
2007
2008 \code
2009 GridView {
2010 ...
2011 remove: Transition {
2012 ParallelAnimation {
2013 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2014 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2015 }
2016 }
2017 }
2018 \endcode
2019
2020 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2021 over one second, and in parallel will also change its opacity to 0. The transition
2022 only applies to the items that are removed from the view; it does not apply to the items below
2023 them that are displaced by the removal of the items. To animate the displaced items, set the
2024 \l displaced or \l removeDisplaced properties.
2025
2026 Note that by the time the transition is applied, the item has already been removed from the
2027 model; any references to the model data for the removed index will not be valid.
2028
2029 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2030 remove transition will not be applied until \l delayRemove becomes false again.
2031
2032 For more details and examples on how to use view transitions, see the ViewTransition
2033 documentation.
2034
2035 \sa removeDisplaced, ViewTransition
2036*/
2037
2038/*!
2039 \qmlproperty Transition QtQuick::GridView::removeDisplaced
2040
2041 This property holds the transition to apply to items in the view that are displaced by the
2042 removal of other items in the view.
2043
2044 For example, here is a view that specifies such a transition:
2045
2046 \code
2047 GridView {
2048 ...
2049 removeDisplaced: Transition {
2050 NumberAnimation { properties: "x,y"; duration: 1000 }
2051 }
2052 }
2053 \endcode
2054
2055 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2056 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2057 displacement occurs, the items' movement to their new x,y positions within the view will be
2058 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2059 the item that has actually been removed from the view; to animate the removed items, set the
2060 \l remove property.
2061
2062 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2063 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2064 if it is not necessary to specify different transitions depending on whether an item is displaced
2065 by an add, move or remove operation, consider setting the \l displaced property instead.
2066
2067 For more details and examples on how to use view transitions, see the ViewTransition
2068 documentation.
2069
2070 \sa displaced, remove, ViewTransition
2071*/
2072
2073/*!
2074 \qmlproperty Transition QtQuick::GridView::displaced
2075 This property holds the generic transition to apply to items that have been displaced by
2076 any model operation that affects the view.
2077
2078 This is a convenience for specifying a generic transition for items that are displaced
2079 by add, move or remove operations, without having to specify the individual addDisplaced,
2080 moveDisplaced and removeDisplaced properties. For example, here is a view that specifies
2081 a displaced transition:
2082
2083 \code
2084 GridView {
2085 ...
2086 displaced: Transition {
2087 NumberAnimation { properties: "x,y"; duration: 1000 }
2088 }
2089 }
2090 \endcode
2091
2092 When any item is added, moved or removed within the above view, the items below it are
2093 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2094 view. As this displacement occurs, the items' movement to their new x,y positions within
2095 the view will be animated by a NumberAnimation over one second, as specified.
2096
2097 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2098 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2099 instead of the generic displaced transition when the relevant operation occurs, providing that
2100 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2101 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2102
2103 For more details and examples on how to use view transitions, see the ViewTransition
2104 documentation.
2105
2106 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2107*/
2108
2109void QQuickGridView::viewportMoved(Qt::Orientations orient)
2110{
2111 Q_D(QQuickGridView);
2112 QQuickItemView::viewportMoved(orient);
2113 if (!d->itemCount)
2114 return;
2115 if (d->inViewportMoved)
2116 return;
2117 d->inViewportMoved = true;
2118
2119 if (yflick()) {
2120 if (d->isContentFlowReversed())
2121 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
2122 else
2123 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2124 } else {
2125 if (d->isContentFlowReversed())
2126 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
2127 else
2128 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2129 }
2130
2131 d->refillOrLayout();
2132
2133 // Set visibility of items to eliminate cost of items outside the visible area.
2134 qreal from = d->isContentFlowReversed() ? -d->position()-d->displayMarginBeginning-d->size() : d->position()-d->displayMarginBeginning;
2135 qreal to = d->isContentFlowReversed() ? -d->position()+d->displayMarginEnd : d->position()+d->size()+d->displayMarginEnd;
2136 for (FxViewItem *item : std::as_const(d->visibleItems)) {
2137 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(item);
2138 QQuickItemPrivate::get(gridItem->item)->setCulled(gridItem->rowPos() + d->rowSize() < from || gridItem->rowPos() > to);
2139 }
2140 if (d->currentItem) {
2141 FxGridItemSG *item = static_cast<FxGridItemSG*>(d->currentItem);
2142 QQuickItemPrivate::get(item->item)->setCulled(item->rowPos() + d->rowSize() < from || item->rowPos() > to);
2143 }
2144
2145 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2146 d->moveReason = QQuickGridViewPrivate::Mouse;
2147 if (d->moveReason != QQuickGridViewPrivate::SetIndex) {
2148 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2149 // reposition highlight
2150 qreal pos = d->highlight->position();
2151 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2152 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2153 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2154 if (pos < viewPos + d->highlightRangeStart)
2155 pos = viewPos + d->highlightRangeStart;
2156
2157 if (pos != d->highlight->position()) {
2158 d->highlightXAnimator->stop();
2159 d->highlightYAnimator->stop();
2160 FxGridItemSG *sgHighlight = static_cast<FxGridItemSG *>(d->highlight.get());
2161 sgHighlight->setPosition(sgHighlight->colPos(), pos);
2162 } else {
2163 d->updateHighlight();
2164 }
2165
2166 // update current index
2167 int idx = d->snapIndex();
2168 if (idx >= 0 && idx != d->currentIndex) {
2169 d->updateCurrent(idx);
2170 if (d->currentItem
2171 && static_cast<FxGridItemSG*>(d->currentItem)->colPos()
2172 != static_cast<FxGridItemSG*>(d->highlight.get())->colPos()
2173 && d->autoHighlight) {
2174 if (d->flow == FlowLeftToRight)
2175 d->highlightXAnimator->to = d->currentItem->itemX();
2176 else
2177 d->highlightYAnimator->to = d->currentItem->itemY();
2178 }
2179 }
2180 }
2181 }
2182
2183 d->inViewportMoved = false;
2184}
2185
2186void QQuickGridView::keyPressEvent(QKeyEvent *event)
2187{
2188 Q_D(QQuickGridView);
2189 if (d->model && d->model->count() && ((d->interactive && !d->explicitKeyNavigationEnabled)
2190 || (d->explicitKeyNavigationEnabled && d->keyNavigationEnabled))) {
2191 d->moveReason = QQuickGridViewPrivate::SetIndex;
2192 int oldCurrent = currentIndex();
2193 switch (event->key()) {
2194 case Qt::Key_Up:
2195 moveCurrentIndexUp();
2196 break;
2197 case Qt::Key_Down:
2198 moveCurrentIndexDown();
2199 break;
2200 case Qt::Key_Left:
2201 moveCurrentIndexLeft();
2202 break;
2203 case Qt::Key_Right:
2204 moveCurrentIndexRight();
2205 break;
2206 default:
2207 break;
2208 }
2209 if (oldCurrent != currentIndex() || d->wrap) {
2210 event->accept();
2211 return;
2212 }
2213 }
2214 event->ignore();
2215 QQuickItemView::keyPressEvent(event);
2216}
2217
2218void QQuickGridView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
2219{
2220 Q_D(QQuickGridView);
2221 d->resetColumns();
2222
2223 if (newGeometry.width() != oldGeometry.width()
2224 && newGeometry.height() != oldGeometry.height()) {
2225 d->setPosition(d->position());
2226 } else if (newGeometry.width() != oldGeometry.width()) {
2227 QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
2228 } else if (newGeometry.height() != oldGeometry.height()) {
2229 QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
2230 }
2231
2232 QQuickItemView::geometryChange(newGeometry, oldGeometry);
2233}
2234
2235void QQuickGridView::initItem(int index, QObject *obj)
2236{
2237 QQuickItemView::initItem(index, obj);
2238
2239 // setting the view from the FxViewItem wrapper is too late if the delegate
2240 // needs access to the view in Component.onCompleted
2241 QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
2242 if (item) {
2243 QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
2244 qmlAttachedPropertiesObject<QQuickGridView>(item));
2245 if (attached)
2246 attached->setView(this);
2247 }
2248}
2249
2250/*!
2251 \qmlmethod QtQuick::GridView::moveCurrentIndexUp()
2252
2253 Move the currentIndex up one item in the view.
2254 The current index will wrap if keyNavigationWraps is true and it
2255 is currently at the end. This method has no effect if the \l count is zero.
2256
2257 \b Note: methods should only be called after the Component has completed.
2258*/
2259
2260
2261void QQuickGridView::moveCurrentIndexUp()
2262{
2263 Q_D(QQuickGridView);
2264 const int count = d->model ? d->model->count() : 0;
2265 if (!count)
2266 return;
2267 if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2268 if (d->flow == QQuickGridView::FlowLeftToRight) {
2269 if (currentIndex() >= d->columns || d->wrap) {
2270 int index = currentIndex() - d->columns;
2271 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2272 }
2273 } else {
2274 if (currentIndex() > 0 || d->wrap) {
2275 int index = currentIndex() - 1;
2276 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2277 }
2278 }
2279 } else {
2280 if (d->flow == QQuickGridView::FlowLeftToRight) {
2281 if (currentIndex() < count - d->columns || d->wrap) {
2282 int index = currentIndex()+d->columns;
2283 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2284 }
2285 } else {
2286 if (currentIndex() < count - 1 || d->wrap) {
2287 int index = currentIndex() + 1;
2288 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2289 }
2290 }
2291 }
2292}
2293
2294/*!
2295 \qmlmethod QtQuick::GridView::moveCurrentIndexDown()
2296
2297 Move the currentIndex down one item in the view.
2298 The current index will wrap if keyNavigationWraps is true and it
2299 is currently at the end. This method has no effect if the \l count is zero.
2300
2301 \b Note: methods should only be called after the Component has completed.
2302*/
2303void QQuickGridView::moveCurrentIndexDown()
2304{
2305 Q_D(QQuickGridView);
2306 const int count = d->model ? d->model->count() : 0;
2307 if (!count)
2308 return;
2309
2310 if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2311 if (d->flow == QQuickGridView::FlowLeftToRight) {
2312 if (currentIndex() < count - d->columns || d->wrap) {
2313 int index = currentIndex()+d->columns;
2314 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2315 }
2316 } else {
2317 if (currentIndex() < count - 1 || d->wrap) {
2318 int index = currentIndex() + 1;
2319 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2320 }
2321 }
2322 } else {
2323 if (d->flow == QQuickGridView::FlowLeftToRight) {
2324 if (currentIndex() >= d->columns || d->wrap) {
2325 int index = currentIndex() - d->columns;
2326 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2327 }
2328 } else {
2329 if (currentIndex() > 0 || d->wrap) {
2330 int index = currentIndex() - 1;
2331 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2332 }
2333 }
2334 }
2335}
2336
2337/*!
2338 \qmlmethod QtQuick::GridView::moveCurrentIndexLeft()
2339
2340 Move the currentIndex left one item in the view.
2341 The current index will wrap if keyNavigationWraps is true and it
2342 is currently at the end. This method has no effect if the \l count is zero.
2343
2344 \b Note: methods should only be called after the Component has completed.
2345*/
2346void QQuickGridView::moveCurrentIndexLeft()
2347{
2348 Q_D(QQuickGridView);
2349 const int count = d->model ? d->model->count() : 0;
2350 if (!count)
2351 return;
2352 if (effectiveLayoutDirection() == Qt::LeftToRight) {
2353 if (d->flow == QQuickGridView::FlowLeftToRight) {
2354 if (currentIndex() > 0 || d->wrap) {
2355 int index = currentIndex() - 1;
2356 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2357 }
2358 } else {
2359 if (currentIndex() >= d->columns || d->wrap) {
2360 int index = currentIndex() - d->columns;
2361 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2362 }
2363 }
2364 } else {
2365 if (d->flow == QQuickGridView::FlowLeftToRight) {
2366 if (currentIndex() < count - 1 || d->wrap) {
2367 int index = currentIndex() + 1;
2368 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2369 }
2370 } else {
2371 if (currentIndex() < count - d->columns || d->wrap) {
2372 int index = currentIndex() + d->columns;
2373 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2374 }
2375 }
2376 }
2377}
2378
2379
2380/*!
2381 \qmlmethod QtQuick::GridView::moveCurrentIndexRight()
2382
2383 Move the currentIndex right one item in the view.
2384 The current index will wrap if keyNavigationWraps is true and it
2385 is currently at the end. This method has no effect if the \l count is zero.
2386
2387 \b Note: methods should only be called after the Component has completed.
2388*/
2389void QQuickGridView::moveCurrentIndexRight()
2390{
2391 Q_D(QQuickGridView);
2392 const int count = d->model ? d->model->count() : 0;
2393 if (!count)
2394 return;
2395 if (effectiveLayoutDirection() == Qt::LeftToRight) {
2396 if (d->flow == QQuickGridView::FlowLeftToRight) {
2397 if (currentIndex() < count - 1 || d->wrap) {
2398 int index = currentIndex() + 1;
2399 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2400 }
2401 } else {
2402 if (currentIndex() < count - d->columns || d->wrap) {
2403 int index = currentIndex()+d->columns;
2404 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2405 }
2406 }
2407 } else {
2408 if (d->flow == QQuickGridView::FlowLeftToRight) {
2409 if (currentIndex() > 0 || d->wrap) {
2410 int index = currentIndex() - 1;
2411 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2412 }
2413 } else {
2414 if (currentIndex() >= d->columns || d->wrap) {
2415 int index = currentIndex() - d->columns;
2416 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2417 }
2418 }
2419 }
2420}
2421
2422bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2423{
2424 Q_Q(QQuickGridView);
2425
2426 if (q->size().isNull())
2427 return false;
2428
2429 int modelIndex = change.index;
2430 int count = change.count;
2431
2432 int index = visibleItems.size() ? mapFromModel(modelIndex) : 0;
2433
2434 if (index < 0) {
2435 int i = visibleItems.size() - 1;
2436 while (i > 0 && visibleItems.at(i)->index == -1)
2437 --i;
2438 if (visibleItems.at(i)->index + 1 == modelIndex) {
2439 // Special case of appending an item to the model.
2440 index = visibleItems.size();
2441 } else {
2442 if (modelIndex <= visibleIndex) {
2443 // Insert before visible items
2444 visibleIndex += count;
2445 for (FxViewItem *item : std::as_const(visibleItems)) {
2446 if (item->index != -1 && item->index >= modelIndex)
2447 item->index += count;
2448 }
2449 }
2450 return true;
2451 }
2452 }
2453
2454 qreal tempPos = isContentFlowReversed() ? -position()-size()+q->width()+1 : position();
2455 qreal colPos = 0;
2456 qreal rowPos = 0;
2457 int colNum = 0;
2458 if (visibleItems.size()) {
2459 if (index < visibleItems.size()) {
2460 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index));
2461 colPos = gridItem->colPos();
2462 rowPos = gridItem->rowPos();
2463 colNum = qFloor((colPos+colSize()/2) / colSize());
2464 } else {
2465 // appending items to visible list
2466 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index-1));
2467 rowPos = gridItem->rowPos();
2468 colNum = qFloor((gridItem->colPos()+colSize()/2) / colSize());
2469 if (++colNum >= columns) {
2470 colNum = 0;
2471 rowPos += rowSize();
2472 }
2473 colPos = colNum * colSize();
2474 }
2475 }
2476
2477#if QT_CONFIG(quick_viewtransitions)
2478 // Update the indexes of the following visible items.
2479 for (FxViewItem *item : std::as_const(visibleItems)) {
2480 if (item->index != -1 && item->index >= modelIndex) {
2481 item->index += count;
2482 if (change.isMove())
2483 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2484 else
2485 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2486 }
2487 }
2488#endif
2489
2490 int prevVisibleCount = visibleItems.size();
2491 if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
2492 // Insert items before the visible item.
2493 int insertionIdx = index;
2494 int i = count - 1;
2495 int from = tempPos - buffer - displayMarginBeginning;
2496
2497 if (rowPos > from && insertionIdx < visibleIndex) {
2498 // items won't be visible, just note the size for repositioning
2499 insertResult->countChangeBeforeVisible += count;
2500 insertResult->sizeChangesBeforeVisiblePos += ((count + columns - 1) / columns) * rowSize();
2501 } else {
2502 while (i >= 0) {
2503 // item is before first visible e.g. in cache buffer
2504 FxViewItem *item = nullptr;
2505 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2506 item->index = modelIndex + i;
2507 if (!item)
2508 item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
2509 if (!item)
2510 return false;
2511
2512 QQuickItemPrivate::get(item->item)->setCulled(false);
2513 visibleItems.insert(insertionIdx, item);
2514 if (insertionIdx == 0)
2515 insertResult->changedFirstItem = true;
2516 if (!change.isMove()) {
2517 addedItems->append(item);
2518#if QT_CONFIG(quick_viewtransitions)
2519 if (transitioner)
2520 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2521 else
2522#endif
2523 item->moveTo(QPointF(colPos, rowPos), true);
2524 }
2525 insertResult->sizeChangesBeforeVisiblePos += rowSize();
2526
2527 if (--colNum < 0 ) {
2528 colNum = columns - 1;
2529 rowPos -= rowSize();
2530 }
2531 colPos = colNum * colSize();
2532 index++;
2533 i--;
2534 }
2535 }
2536
2537 // There may be gaps in the index sequence of visibleItems because
2538 // of the index shift/update done before the insertion just above.
2539 // Find if there is any...
2540 int firstOkIdx = -1;
2541 for (int i = 0; i <= insertionIdx && i < visibleItems.size() - 1; i++) {
2542 if (visibleItems.at(i)->index + 1 != visibleItems.at(i + 1)->index) {
2543 firstOkIdx = i + 1;
2544 break;
2545 }
2546 }
2547 // ... and remove all the items before that one
2548 for (int i = 0; i < firstOkIdx; i++) {
2549 FxViewItem *nvItem = visibleItems.takeFirst();
2550 addedItems->removeOne(nvItem);
2551 removeItem(nvItem);
2552 }
2553
2554 } else {
2555 int i = 0;
2556 int to = buffer+displayMarginEnd+tempPos+size()-1;
2557 while (i < count && rowPos <= to + rowSize()*(columns - colNum)/qreal(columns+1)) {
2558 FxViewItem *item = nullptr;
2559 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2560 item->index = modelIndex + i;
2561 bool newItem = !item;
2562 if (!item)
2563 item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
2564 if (!item)
2565 return false;
2566
2567 QQuickItemPrivate::get(item->item)->setCulled(false);
2568 visibleItems.insert(index, item);
2569 if (index == 0)
2570 insertResult->changedFirstItem = true;
2571 if (change.isMove()) {
2572 // we know this is a move target, since move displaced items that are
2573 // shuffled into view due to a move would be added in refill()
2574 if (newItem
2575#if QT_CONFIG(quick_viewtransitions)
2576 && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)
2577#endif
2578 )
2579 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2580 } else {
2581 addedItems->append(item);
2582#if QT_CONFIG(quick_viewtransitions)
2583 if (transitioner)
2584 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2585 else
2586#endif
2587 item->moveTo(QPointF(colPos, rowPos), true);
2588 }
2589 insertResult->sizeChangesAfterVisiblePos += rowSize();
2590
2591 if (++colNum >= columns) {
2592 colNum = 0;
2593 rowPos += rowSize();
2594 }
2595 colPos = colNum * colSize();
2596 ++index;
2597 ++i;
2598 }
2599 }
2600
2601 updateVisibleIndex();
2602
2603 return visibleItems.size() > prevVisibleCount;
2604}
2605
2606#if QT_CONFIG(quick_viewtransitions)
2607void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2608{
2609 if (!transitioner)
2610 return;
2611
2612 int markerItemIndex = -1;
2613 for (int i=0; i<visibleItems.size(); i++) {
2614 if (visibleItems.at(i)->index == afterModelIndex) {
2615 markerItemIndex = i;
2616 break;
2617 }
2618 }
2619 if (markerItemIndex < 0)
2620 return;
2621
2622 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2623 int countItemsRemoved = -(removalResult.sizeChangesAfterVisiblePos / rowSize());
2624
2625 // account for whether first item has changed if < 1 row was removed before visible
2626 int changeBeforeVisible = insertionResult.countChangeBeforeVisible - removalResult.countChangeBeforeVisible;
2627 if (changeBeforeVisible != 0)
2628 countItemsRemoved += (changeBeforeVisible % columns) - (columns - 1);
2629
2630 countItemsRemoved -= removalResult.countChangeAfterVisibleItems;
2631
2632 for (int i=markerItemIndex+1; i<visibleItems.size(); i++) {
2633 FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems.at(i));
2634 if (gridItem->position() >= viewEndPos)
2635 break;
2636 if (!gridItem->transitionScheduledOrRunning()) {
2637 qreal origRowPos = gridItem->colPos();
2638 qreal origColPos = gridItem->rowPos();
2639 int indexDiff = gridItem->index - countItemsRemoved;
2640 gridItem->setPosition((indexDiff % columns) * colSize(), (indexDiff / columns) * rowSize());
2641 gridItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2642 gridItem->setPosition(origRowPos, origColPos);
2643 }
2644 }
2645}
2646#endif
2647
2649{
2650 // If we add or remove items before visible items, a layout may be
2651 // required to ensure item 0 is in the first column.
2652 return modelIndex < visibleIndex;
2653}
2654
2655/*!
2656 \qmlmethod QtQuick::GridView::positionViewAtIndex(int index, PositionMode mode)
2657
2658 Positions the view such that the \a index is at the position specified by
2659 \a mode:
2660
2661 \value GridView.Beginning position item at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
2662 \value GridView.Center position item in the center of the view.
2663 \value GridView.End position item at bottom (or right for horizontal orientation) of the view.
2664 \value GridView.Visible if any part of the item is visible then take no action, otherwise
2665 bring the item into view.
2666 \value GridView.Contain ensure the entire item is visible. If the item is larger than the view, the item
2667 is positioned at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
2668 \value GridView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid if
2669 \l highlightRangeMode is \c StrictlyEnforceRange or snapping is enabled via \l snapMode.
2670
2671 If positioning the view at the index would cause empty space to be displayed at
2672 the beginning or end of the view, the view will be positioned at the boundary.
2673
2674 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2675 at a particular index. This is unreliable since removing items from the start
2676 of the view does not cause all other items to be repositioned.
2677 The correct way to bring an item into view is with \c positionViewAtIndex.
2678
2679 \b Note: methods should only be called after the Component has completed. To position
2680 the view at startup, this method should be called by Component.onCompleted. For
2681 example, to position the view at the end:
2682
2683 \code
2684 Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
2685 \endcode
2686*/
2687
2688/*!
2689 \qmlmethod QtQuick::GridView::positionViewAtBeginning()
2690 \qmlmethod QtQuick::GridView::positionViewAtEnd()
2691
2692 Positions the view at the beginning or end, taking into account any header or footer.
2693
2694 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2695 at a particular index. This is unreliable since removing items from the start
2696 of the list does not cause all other items to be repositioned, and because
2697 the actual start of the view can vary based on the size of the delegates.
2698
2699 \b Note: methods should only be called after the Component has completed. To position
2700 the view at startup, this method should be called by Component.onCompleted. For
2701 example, to position the view at the end on startup:
2702
2703 \code
2704 Component.onCompleted: positionViewAtEnd()
2705 \endcode
2706*/
2707
2708/*!
2709 \qmlmethod int QtQuick::GridView::indexAt(real x, real y)
2710
2711 Returns the index of the visible item containing the point \a x, \a y in
2712 \l {QQuickFlickable::contentItem}{content item} coordinates. If there is
2713 no item at the point specified, or the item is not visible -1 is returned.
2714
2715 If the item is outside the visible area, -1 is returned, regardless of
2716 whether an item will exist at that point when scrolled into view.
2717
2718 \note if you add a MouseArea as a child of the GridView, it will return
2719 positions in GridView coordinates rather than content item coordinates.
2720 To use those positions in a call to this function, you need to map them
2721 first:
2722
2723 \code
2724 GridView {
2725 id: view
2726 MouseArea {
2727 anchors.fill: parent
2728 onClicked: (mouse) => {
2729 let posInGridView = Qt.point(mouse.x, mouse.y)
2730 let posInContentItem = mapToItem(view.contentItem, posInGridView)
2731 let index = view.indexAt(posInContentItem.x, posInContentItem.y)
2732 }
2733 }
2734 }
2735 \endcode
2736
2737 \b Note: methods should only be called after the Component has completed.
2738
2739 \sa itemAt
2740*/
2741
2742/*!
2743 \qmlmethod Item QtQuick::GridView::itemAt(real x, real y)
2744
2745 Returns the visible item containing the point \a x, \a y in
2746 \l {QQuickFlickable::contentItem}{content item} coordinates. If there
2747 is no item at the point specified, or the item is not visible null is returned.
2748
2749 If the item is outside the visible area, null is returned, regardless of
2750 whether an item will exist at that point when scrolled into view.
2751
2752 \b Note: methods should only be called after the Component has completed.
2753
2754 \sa indexAt
2755*/
2756
2757/*!
2758 \qmlmethod Item QtQuick::GridView::itemAtIndex(int index)
2759
2760 Returns the item for \a index. If there is no item for that index, for example
2761 because it has not been created yet, or because it has been panned out of
2762 the visible area and removed from the cache, null is returned.
2763
2764 \b Note: this method should only be called after the Component has completed.
2765 The returned value should also not be stored since it can turn to null
2766 as soon as control goes out of the calling scope, if the view releases that item.
2767
2768 \since 5.13
2769*/
2770
2771/*!
2772 \qmlmethod QtQuick::GridView::forceLayout()
2773
2774 Responding to changes in the model is usually batched to happen only once
2775 per frame. This means that inside script blocks it is possible for the
2776 underlying model to have changed, but the GridView has not caught up yet.
2777
2778 This method forces the GridView to immediately respond to any outstanding
2779 changes in the model.
2780
2781 \since 5.1
2782
2783 \b Note: methods should only be called after the Component has completed.
2784*/
2785
2786QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj)
2787{
2788 return new QQuickGridViewAttached(obj);
2789}
2790
2791QT_END_NAMESPACE
2792
2793#include "moc_qquickgridview_p.cpp"
qreal footerSize() const override
bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) override
qreal snapPosAt(qreal pos) const
void fixupPosition() override
void updateViewport() override
void updateHighlight() override
bool showFooterForIndex(int index) const override
bool isContentFlowReversed() const override
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override
void repositionPackageItemAt(QQuickItem *item, int index) override
bool showHeaderForIndex(int index) const override
bool needsRefillForAddedOrRemovedIndex(int index) const override
void createHighlight(bool onDestruction=false) override
QSmoothedAnimation * highlightYAnimator
void initializeViewItem(FxViewItem *item) override
void removeItem(FxViewItem *item)
qreal positionAt(int index) const override
void updateFooter() override
qreal endPositionAt(int index) const override
void setPosition(qreal pos) override
QQuickItemViewAttached * getAttachedObject(const QObject *object) const override
void resetFirstItemPosition(qreal pos=0.0) override
bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList< FxViewItem * > *addedItems, QList< MovedItem > *movingIntoView) override
void initializeComponentItem(QQuickItem *item) const override
void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible) override
qreal rowPosAt(int modelIndex) const
void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) override
FxViewItem * snapItemAt(qreal pos) const
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override
void resetHighlightPosition() override
void changedVisibleIndex(int newIndex) override
void layoutVisibleItems(int fromModelIndex=0) override
qreal contentYForPosition(qreal pos) const
qreal lastPosition() const override
FxViewItem * newViewItem(int index, QQuickItem *item) override
qreal contentXForPosition(qreal pos) const
qreal headerSize() const override
qreal originPosition() const override
void updateHeader() override
bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) override
qreal colPosAt(int modelIndex) const
void initializeCurrentItem() override
QSmoothedAnimation * highlightXAnimator
#define QML_FLICK_SNAPONETHRESHOLD