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;
936 if (isContentFlowReversed())
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;
993 if (isContentFlowReversed())
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;
1031 if (isContentFlowReversed())
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;
1048 if (isContentFlowReversed())
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 {Grid of contacts with icons: Jim Williams, John Brown, Bill Smyth, Sam Wise}
1157 \enddiv
1158
1159 This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
1160 for more information about creating reusable components like this.
1161
1162 Another component can display this model data in a GridView, as in the following
1163 example, which creates a \c ContactModel component for its model, and a \l Column
1164 (containing \l Image and \l Text items) for its delegate.
1165
1166 \clearfloat
1167 \snippet qml/gridview/gridview.qml import
1168 \codeline
1169 \snippet qml/gridview/gridview.qml classdocs simple
1170
1171 \div {class="float-right"}
1172 \inlineimage gridview-highlight.png
1173 {Contact grid with John Brown highlighted in blue}
1174 \enddiv
1175
1176 The view will create a new delegate for each item in the model. Note that the delegate
1177 is able to access the model's \c name and \c portrait data directly.
1178
1179 An improved grid view is shown below. The delegate is visually improved and is moved
1180 into a separate \c contactDelegate component.
1181
1182 \clearfloat
1183 \snippet qml/gridview/gridview.qml classdocs advanced
1184
1185 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1186 and \c focus is set to \c true to enable keyboard navigation for the grid view.
1187 The grid view itself is a focus scope (see \l{Keyboard Focus in Qt Quick} for more details).
1188
1189 Delegates are instantiated as needed and may be destroyed at any time.
1190 State should \e never be stored in a delegate.
1191
1192 GridView attaches a number of properties to the root item of the delegate, for example
1193 \c {GridView.isCurrentItem}. In the following example, the root delegate item can access
1194 this attached property directly as \c GridView.isCurrentItem, while the child
1195 \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
1196
1197 \snippet qml/gridview/gridview.qml isCurrentItem
1198
1199 \note Views do not set the \l{Item::}{clip} property automatically.
1200 If the view is not clipped by another item or the screen, it will be necessary
1201 to set this property to true in order to clip the items that are partially or
1202 fully outside the view.
1203
1204
1205 \section1 GridView Layouts
1206
1207 The layout of the items in a GridView can be controlled by these properties:
1208
1209 \list
1210 \li \l flow - controls whether items flow from left to right (as a series of rows)
1211 or from top to bottom (as a series of columns). This value can be either
1212 GridView.FlowLeftToRight or GridView.FlowTopToBottom.
1213 \li \l layoutDirection - controls the horizontal layout direction: that is, whether items
1214 are laid out from the left side of the view to the right, or vice-versa. This value can
1215 be either Qt.LeftToRight or Qt.RightToLeft.
1216 \li \l verticalLayoutDirection - controls the vertical layout direction: that is, whether items
1217 are laid out from the top of the view down towards the bottom of the view, or vice-versa.
1218 This value can be either GridView.TopToBottom or GridView.BottomToTop.
1219 \endlist
1220
1221 By default, a GridView flows from left to right, and items are laid out from left to right
1222 horizontally, and from top to bottom vertically.
1223
1224 These properties can be combined to produce a variety of layouts, as shown in the table below.
1225 The GridViews in the first row all have a \l flow value of GridView.FlowLeftToRight, but use
1226 different combinations of horizontal and vertical layout directions (specified by \l layoutDirection
1227 and \l verticalLayoutDirection respectively). Similarly, the GridViews in the second row below
1228 all have a \l flow value of GridView.FlowTopToBottom, but use different combinations of horizontal and
1229 vertical layout directions to lay out their items in different ways.
1230
1231 \table
1232 \header
1233 \li {4, 1}
1234 \b GridViews with GridView.FlowLeftToRight flow
1235 \row
1236 \li \b (H) Left to right \b (V) Top to bottom
1237 \image gridview-layout-lefttoright-ltr-ttb.png {Grid with items
1238 0-11 flowing left to right, rows arranged top to bottom}
1239 \li \b (H) Right to left \b (V) Top to bottom
1240 \image gridview-layout-lefttoright-rtl-ttb.png {Grid with items
1241 0-11 flowing right to left, rows arranged top to bottom}
1242 \li \b (H) Left to right \b (V) Bottom to top
1243 \image gridview-layout-lefttoright-ltr-btt.png {Grid with items
1244 0-11 flowing left to right, rows arranged bottom to top}
1245 \li \b (H) Right to left \b (V) Bottom to top
1246 \image gridview-layout-lefttoright-rtl-btt.png {Grid with items
1247 0-11 flowing right to left, rows arranged bottom to top}
1248 \header
1249 \li {4, 1}
1250 \b GridViews with GridView.FlowTopToBottom flow
1251 \row
1252 \li \b (H) Left to right \b (V) Top to bottom
1253 \image gridview-layout-toptobottom-ltr-ttb.png {Grid with items
1254 0-11 flowing top to bottom, columns arranged left to right}
1255 \li \b (H) Right to left \b (V) Top to bottom
1256 \image gridview-layout-toptobottom-rtl-ttb.png {Grid with items
1257 0-11 flowing top to bottom, columns arranged right to left}
1258 \li \b (H) Left to right \b (V) Bottom to top
1259 \image gridview-layout-toptobottom-ltr-btt.png {Grid with items
1260 0-11 flowing bottom to top, columns arranged left to right}
1261 \li \b (H) Right to left \b (V) Bottom to top
1262 \image gridview-layout-toptobottom-rtl-btt.png {Grid with items
1263 0-11 flowing bottom to top, columns arranged right to left}
1264 \endtable
1265
1266 \sa {QML Data Models}, ListView, PathView, {Qt Quick Examples - Views}
1267*/
1268
1269QQuickGridView::QQuickGridView(QQuickItem *parent)
1270 : QQuickItemView(*(new QQuickGridViewPrivate), parent)
1271{
1272}
1273
1274void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1275{
1276 Q_D(QQuickGridView);
1277 if (d->autoHighlight != autoHighlight) {
1278 if (!autoHighlight && d->highlightXAnimator) {
1279 d->highlightXAnimator->stop();
1280 d->highlightYAnimator->stop();
1281 }
1282 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1283 }
1284}
1285
1286/*!
1287 \qmlattachedproperty bool QtQuick::GridView::isCurrentItem
1288 \readonly
1289
1290 This attached property is true if this delegate is the current item; otherwise false.
1291
1292 It is attached to each instance of the delegate.
1293
1294 \snippet qml/gridview/gridview.qml isCurrentItem
1295*/
1296
1297/*!
1298 \qmlattachedproperty GridView QtQuick::GridView::view
1299 \readonly
1300
1301 This attached property holds the view that manages this delegate instance.
1302
1303 It is attached to each instance of the delegate and also to the header, the footer
1304 and the highlight delegates.
1305*/
1306
1307/*!
1308 \qmlattachedproperty bool QtQuick::GridView::delayRemove
1309
1310 This attached property holds whether the delegate may be destroyed. It
1311 is attached to each instance of the delegate. The default value is false.
1312
1313 It is sometimes necessary to delay the destruction of an item
1314 until an animation completes. The example delegate below ensures that the
1315 animation completes before the item is removed from the list.
1316
1317 \snippet qml/gridview/gridview.qml delayRemove
1318
1319 If a \l remove transition has been specified, it will not be applied until
1320 delayRemove is returned to \c false.
1321*/
1322
1323/*!
1324 \qmlattachedsignal QtQuick::GridView::add()
1325 This attached signal is emitted immediately after an item is added to the view.
1326*/
1327
1328/*!
1329 \qmlattachedsignal QtQuick::GridView::remove()
1330 This attached signal is emitted immediately before an item is removed from the view.
1331
1332 If a \l remove transition has been specified, it is applied after
1333 this signal is handled, providing that \l delayRemove is false.
1334*/
1335
1336
1337/*!
1338 \qmlproperty model QtQuick::GridView::model
1339 This property holds the model providing data for the grid.
1340
1341 The model provides the set of data that is used to create the items
1342 in the view. Models can be created directly in QML using \l ListModel,
1343 \l DelegateModel, \l ObjectModel, or provided by C++ model classes.
1344 If a C++ model class is used, it must be a subclass of
1345 \l QAbstractItemModel or a simple list.
1346
1347 \sa {qml-data-models}{Data Models}
1348*/
1349
1350/*!
1351 \qmlproperty Component QtQuick::GridView::delegate
1352
1353 The delegate provides a template defining each item instantiated by the view.
1354 The index is exposed as an accessible \c index property. Properties of the
1355 model are also available depending upon the type of \l {qml-data-models}{Data Model}.
1356
1357 The number of objects and bindings in the delegate has a direct effect on the
1358 flicking performance of the view. If at all possible, place functionality
1359 that is not needed for the normal display of the delegate in a \l Loader which
1360 can load additional components when needed.
1361
1362 The item size of the GridView is determined by cellHeight and cellWidth. It will not resize the items
1363 based on the size of the root item in the delegate.
1364
1365 The default \l {QQuickItem::z}{stacking order} of delegate instances is \c 1.
1366
1367 \note Delegates are instantiated as needed and may be destroyed at any time.
1368 State should \e never be stored in a delegate.
1369*/
1370
1371/*!
1372 \qmlproperty enumeration QtQuick::GridView::delegateModelAccess
1373 \since 6.10
1374
1375 \include delegatemodelaccess.qdocinc
1376*/
1377
1378/*!
1379 \qmlproperty int QtQuick::GridView::currentIndex
1380 \qmlproperty Item QtQuick::GridView::currentItem
1381
1382 The \c currentIndex property holds the index of the current item, and
1383 \c currentItem holds the current item. Setting the currentIndex to -1
1384 will clear the highlight and set currentItem to null.
1385
1386 If highlightFollowsCurrentItem is \c true, setting either of these
1387 properties will smoothly scroll the GridView so that the current
1388 item becomes visible.
1389
1390 Note that the position of the current item
1391 may only be approximate until it becomes visible in the view.
1392*/
1393
1394
1395/*!
1396 \qmlproperty Item QtQuick::GridView::highlightItem
1397
1398 This holds the highlight item created from the \l highlight component.
1399
1400 The highlightItem is managed by the view unless
1401 \l highlightFollowsCurrentItem is set to false.
1402 The default \l {QQuickItem::z}{stacking order}
1403 of the highlight item is \c 0.
1404
1405 \sa highlight, highlightFollowsCurrentItem
1406*/
1407
1408
1409/*!
1410 \qmlproperty int QtQuick::GridView::count
1411 This property holds the number of items in the model.
1412*/
1413
1414/*!
1415 \qmlproperty bool QtQuick::GridView::reuseItems
1416
1417 This property enables you to reuse items that are instantiated
1418 from the \l delegate. If set to \c false, any currently
1419 pooled items are destroyed.
1420
1421 This property is \c false by default.
1422
1423 \since 5.15
1424
1425 \sa {Reusing items}, pooled(), reused()
1426*/
1427
1428/*!
1429 \qmlattachedsignal QtQuick::GridView::pooled()
1430
1431 This signal is emitted after an item has been added to the reuse
1432 pool. You can use it to pause ongoing timers or animations inside
1433 the item, or free up resources that cannot be reused.
1434
1435 This signal is emitted only if the \l reuseItems property is \c true.
1436
1437 \sa {Reusing items}, reuseItems, reused()
1438*/
1439
1440/*!
1441 \qmlattachedsignal QtQuick::GridView::reused()
1442
1443 This signal is emitted after an item has been reused. At this point, the
1444 item has been taken out of the pool and placed inside the content view,
1445 and the model properties such as \c index and \c row have been updated.
1446
1447 Other properties that are not provided by the model does not change when an
1448 item is reused. You should avoid storing any state inside a delegate, but if
1449 you do, manually reset that state on receiving this signal.
1450
1451 This signal is emitted when the item is reused, and not the first time the
1452 item is created.
1453
1454 This signal is emitted only if the \l reuseItems property is \c true.
1455
1456 \sa {Reusing items}, reuseItems, pooled()
1457*/
1458
1459/*!
1460 \qmlproperty Component QtQuick::GridView::highlight
1461 This property holds the component to use as the highlight.
1462
1463 An instance of the highlight component is created for each view.
1464 The geometry of the resulting component instance will be managed by the view
1465 so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1466 The default \l {QQuickItem::z}{stacking order} of the highlight item is \c 0.
1467
1468 \sa highlightItem, highlightFollowsCurrentItem
1469*/
1470
1471/*!
1472 \qmlproperty bool QtQuick::GridView::highlightFollowsCurrentItem
1473 This property sets whether the highlight is managed by the view.
1474
1475 If this property is true (the default value), the highlight is moved smoothly
1476 to follow the current item. Otherwise, the
1477 highlight is not moved by the view, and any movement must be implemented
1478 by the highlight.
1479
1480 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1481
1482 \snippet qml/gridview/gridview.qml highlightFollowsCurrentItem
1483*/
1484
1485
1486/*!
1487 \qmlproperty int QtQuick::GridView::highlightMoveDuration
1488 This property holds the move animation duration of the highlight delegate.
1489
1490 highlightFollowsCurrentItem must be true for this property
1491 to have effect.
1492
1493 The default value for the duration is 150ms.
1494
1495 \sa highlightFollowsCurrentItem
1496*/
1497
1498/*!
1499 \qmlproperty real QtQuick::GridView::preferredHighlightBegin
1500 \qmlproperty real QtQuick::GridView::preferredHighlightEnd
1501 \qmlproperty enumeration QtQuick::GridView::highlightRangeMode
1502
1503 These properties define the preferred range of the highlight (for the current item)
1504 within the view. The \c preferredHighlightBegin value must be less than the
1505 \c preferredHighlightEnd value.
1506
1507 These properties affect the position of the current item when the view is scrolled.
1508 For example, if the currently selected item should stay in the middle of the
1509 view when it is scrolled, set the \c preferredHighlightBegin and
1510 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1511 item would be. If the \c currentItem is changed programmatically, the view will
1512 automatically scroll so that the current item is in the middle of the view.
1513 Furthermore, the behavior of the current item index will occur whether or not a
1514 highlight exists.
1515
1516 Valid values for \c highlightRangeMode are:
1517
1518 \value GridView.ApplyRange the view attempts to maintain the highlight within the range.
1519 However, the highlight can move outside of the range at the ends of the view or due
1520 to mouse interaction.
1521 \value GridView.StrictlyEnforceRange the highlight never moves outside of the range.
1522 The current item changes if a keyboard or mouse action would cause the highlight to move
1523 outside of the range.
1524 \value GridView.NoHighlightRange the default value
1525*/
1526
1527
1528/*!
1529 \qmlproperty enumeration QtQuick::GridView::layoutDirection
1530 This property holds the layout direction of the grid.
1531
1532 Possible values:
1533
1534 \value Qt.LeftToRight (default) Items will be laid out starting in the top, left corner. The flow is
1535 dependent on the \l GridView::flow property.
1536 \value Qt.RightToLeft Items will be laid out starting in the top, right corner. The flow is dependent
1537 on the \l GridView::flow property.
1538
1539 \b Note: If GridView::flow is set to GridView.FlowLeftToRight, this is not to be confused if
1540 GridView::layoutDirection is set to Qt.RightToLeft. The GridView.FlowLeftToRight flow value simply
1541 indicates that the flow is horizontal.
1542
1543 \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
1544*/
1545
1546
1547/*!
1548 \qmlproperty enumeration QtQuick::GridView::effectiveLayoutDirection
1549 This property holds the effective layout direction of the grid.
1550
1551 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1552 the visual layout direction of the grid will be mirrored. However, the
1553 property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1554
1555 \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1556*/
1557
1558/*!
1559 \qmlproperty enumeration QtQuick::GridView::verticalLayoutDirection
1560 This property holds the vertical layout direction of the grid.
1561
1562 Possible values:
1563
1564 \value GridView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
1565 \value GridView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
1566
1567 \sa GridView::layoutDirection
1568*/
1569
1570/*!
1571 \qmlproperty bool QtQuick::GridView::keyNavigationWraps
1572 This property holds whether the grid wraps key navigation
1573
1574 If this is true, key navigation that would move the current item selection
1575 past one end of the view instead wraps around and moves the selection to
1576 the other end of the view.
1577
1578 By default, key navigation is not wrapped.
1579*/
1580
1581/*!
1582 \qmlproperty bool QtQuick::GridView::keyNavigationEnabled
1583 \since 5.7
1584
1585 This property holds whether the key navigation of the grid is enabled.
1586
1587 If this is \c true, the user can navigate the view with a keyboard.
1588 It is useful for applications that need to selectively enable or
1589 disable mouse and keyboard interaction.
1590
1591 By default, the value of this property is bound to
1592 \l {Flickable::}{interactive} to ensure behavior compatibility for
1593 existing applications. When explicitly set, it will cease to be bound to
1594 the interactive property.
1595
1596 \sa {Flickable::}{interactive}
1597*/
1598
1599/*!
1600 \qmlproperty int QtQuick::GridView::cacheBuffer
1601 This property determines whether delegates are retained outside the
1602 visible area of the view.
1603
1604 If this value is greater than zero, the view may keep as many delegates
1605 instantiated as will fit within the buffer specified. For example,
1606 if in a vertical view the delegate is 20 pixels high, there are 3
1607 columns and \c cacheBuffer is
1608 set to 40, then up to 6 delegates above and 6 delegates below the visible
1609 area may be created/retained. The buffered delegates are created asynchronously,
1610 allowing creation to occur across multiple frames and reducing the
1611 likelihood of skipping frames. In order to improve painting performance
1612 delegates outside the visible area are not painted.
1613
1614 The default value of this property is platform dependent, but will usually
1615 be a value greater than zero. Negative values are ignored.
1616
1617 Note that cacheBuffer is not a pixel buffer - it only maintains additional
1618 instantiated delegates.
1619
1620 \note Setting this property is not a replacement for creating efficient delegates.
1621 It can improve the smoothness of scrolling behavior at the expense of additional
1622 memory usage. The fewer objects and bindings in a delegate, the faster a
1623 view can be scrolled. It is important to realize that setting a cacheBuffer
1624 will only postpone issues caused by slow-loading delegates, it is not a
1625 solution for this scenario.
1626
1627 The cacheBuffer operates outside of any display margins specified by
1628 displayMarginBeginning or displayMarginEnd.
1629*/
1630
1631/*!
1632 \qmlproperty int QtQuick::GridView::displayMarginBeginning
1633 \qmlproperty int QtQuick::GridView::displayMarginEnd
1634 \since QtQuick 2.3
1635
1636 This property allows delegates to be displayed outside of the view geometry.
1637
1638 If this value is non-zero, the view will create extra delegates before the
1639 start of the view, or after the end. The view will create as many delegates
1640 as it can fit into the pixel size specified.
1641
1642 For example, if in a vertical view the delegate is 20 pixels high,
1643 there are 3 columns, and
1644 \c displayMarginBeginning and \c displayMarginEnd are both set to 40,
1645 then 6 delegates above and 6 delegates below will be created and shown.
1646
1647 The default value is 0.
1648
1649 This property is meant for allowing certain UI configurations,
1650 and not as a performance optimization. If you wish to create delegates
1651 outside of the view geometry for performance reasons, you probably
1652 want to use the cacheBuffer property instead.
1653*/
1654
1655void QQuickGridView::setHighlightMoveDuration(int duration)
1656{
1657 Q_D(QQuickGridView);
1658 if (d->highlightMoveDuration != duration) {
1659 if (d->highlightYAnimator) {
1660 d->highlightXAnimator->userDuration = duration;
1661 d->highlightYAnimator->userDuration = duration;
1662 }
1663 QQuickItemView::setHighlightMoveDuration(duration);
1664 }
1665}
1666
1667/*!
1668 \qmlproperty enumeration QtQuick::GridView::flow
1669 This property holds the flow of the grid.
1670
1671 Possible values:
1672
1673 \value GridView.FlowLeftToRight (default) Items are laid out from left to right, and the view scrolls vertically
1674 \value GridView.FlowTopToBottom Items are laid out from top to bottom, and the view scrolls horizontally
1675*/
1676QQuickGridView::Flow QQuickGridView::flow() const
1677{
1678 Q_D(const QQuickGridView);
1679 return d->flow;
1680}
1681
1682void QQuickGridView::setFlow(Flow flow)
1683{
1684 Q_D(QQuickGridView);
1685 if (d->flow != flow) {
1686 d->flow = flow;
1687 if (d->flow == FlowLeftToRight) {
1688 setContentWidth(-1);
1689 setFlickableDirection(VerticalFlick);
1690 } else {
1691 setContentHeight(-1);
1692 setFlickableDirection(HorizontalFlick);
1693 }
1694 setContentX(0);
1695 setContentY(0);
1696 d->regenerate(true);
1697 emit flowChanged();
1698 }
1699}
1700
1701
1702/*!
1703 \qmlproperty real QtQuick::GridView::cellWidth
1704 \qmlproperty real QtQuick::GridView::cellHeight
1705
1706 These properties holds the width and height of each cell in the grid.
1707
1708 The default cell size is 100x100.
1709*/
1710qreal QQuickGridView::cellWidth() const
1711{
1712 Q_D(const QQuickGridView);
1713 return d->cellWidth;
1714}
1715
1716void QQuickGridView::setCellWidth(qreal cellWidth)
1717{
1718 Q_D(QQuickGridView);
1719 if (cellWidth != d->cellWidth && cellWidth > 0) {
1720 d->cellWidth = qMax(qreal(1), cellWidth);
1721 d->updateViewport();
1722 emit cellWidthChanged();
1723 d->forceLayoutPolish();
1724 QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
1725 }
1726}
1727
1728qreal QQuickGridView::cellHeight() const
1729{
1730 Q_D(const QQuickGridView);
1731 return d->cellHeight;
1732}
1733
1734void QQuickGridView::setCellHeight(qreal cellHeight)
1735{
1736 Q_D(QQuickGridView);
1737 if (cellHeight != d->cellHeight && cellHeight > 0) {
1738 d->cellHeight = qMax(qreal(1), cellHeight);
1739 d->updateViewport();
1740 emit cellHeightChanged();
1741 d->forceLayoutPolish();
1742 QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
1743 }
1744}
1745/*!
1746 \qmlproperty enumeration QtQuick::GridView::snapMode
1747
1748 This property determines how the view scrolling will settle following a drag or flick.
1749 The possible values are:
1750
1751 \value GridView.NoSnap (default) the view stops anywhere within the visible area.
1752 \value GridView.SnapToRow the view settles with a row (or column for \c GridView.FlowTopToBottom flow)
1753 aligned with the start of the view.
1754 \value GridView.SnapOneRow the view will settle no more than one row (or column for \c GridView.FlowTopToBottom flow)
1755 away from the first visible row at the time the mouse button is released.
1756 This mode is particularly useful for moving one page at a time.
1757*/
1758QQuickGridView::SnapMode QQuickGridView::snapMode() const
1759{
1760 Q_D(const QQuickGridView);
1761 return d->snapMode;
1762}
1763
1764void QQuickGridView::setSnapMode(SnapMode mode)
1765{
1766 Q_D(QQuickGridView);
1767 if (d->snapMode != mode) {
1768 d->snapMode = mode;
1769 emit snapModeChanged();
1770 }
1771}
1772
1773
1774/*!
1775 \qmlproperty Component QtQuick::GridView::footer
1776 This property holds the component to use as the footer.
1777
1778 An instance of the footer component is created for each view. The
1779 footer is positioned at the end of the view, after any items. The
1780 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
1781
1782 \sa header, footerItem
1783*/
1784/*!
1785 \qmlproperty Component QtQuick::GridView::header
1786 This property holds the component to use as the header.
1787
1788 An instance of the header component is created for each view. The
1789 header is positioned at the beginning of the view, before any items.
1790 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
1791
1792 \sa footer, headerItem
1793*/
1794
1795/*!
1796 \qmlproperty Item QtQuick::GridView::headerItem
1797 This holds the header item created from the \l header component.
1798
1799 An instance of the header component is created for each view. The
1800 header is positioned at the beginning of the view, before any items.
1801 The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
1802
1803 \sa header, footerItem
1804*/
1805
1806/*!
1807 \qmlproperty Item QtQuick::GridView::footerItem
1808 This holds the footer item created from the \l footer component.
1809
1810 An instance of the footer component is created for each view. The
1811 footer is positioned at the end of the view, after any items. The
1812 default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
1813
1814 \sa footer, headerItem
1815*/
1816
1817/*!
1818 \qmlproperty Transition QtQuick::GridView::populate
1819
1820 This property holds the transition to apply to the items that are initially created
1821 for a view.
1822
1823 It is applied to all items that are created when:
1824
1825 \list
1826 \li The view is first created
1827 \li The view's \l model changes in such a way that the visible delegates are completely replaced
1828 \li The view's \l model is \l {QAbstractItemModel::beginResetModel()}{reset},
1829 if the model is a QAbstractItemModel subclass
1830 \endlist
1831
1832 For example, here is a view that specifies such a transition:
1833
1834 \code
1835 GridView {
1836 ...
1837 populate: Transition {
1838 NumberAnimation { properties: "x,y"; duration: 1000 }
1839 }
1840 }
1841 \endcode
1842
1843 When the view is initialized, the view will create all the necessary items for the view,
1844 then animate them to their correct positions within the view over one second.
1845
1846 However when scrolling the view later, the populate transition does not
1847 run, even though delegates are being instantiated as they become visible.
1848 When the model changes in a way that new delegates become visible, the
1849 \l add transition is the one that runs. So you should not depend on the
1850 \c populate transition to initialize properties in the delegate, because it
1851 does not apply to every delegate. If your animation sets the \c to value of
1852 a property, the property should initially have the \c to value, and the
1853 animation should set the \c from value in case it is animated:
1854
1855 \code
1856 GridView {
1857 ...
1858 delegate: Rectangle {
1859 opacity: 1 // not necessary because it's the default; but don't set 0
1860 ...
1861 }
1862 populate: Transition {
1863 NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
1864 }
1865 }
1866 \endcode
1867
1868 For more details and examples on how to use view transitions, see the ViewTransition
1869 documentation.
1870
1871 \sa add, ViewTransition
1872*/
1873
1874/*!
1875 \qmlproperty Transition QtQuick::GridView::add
1876
1877 This property holds the transition to apply to items that are added to the view.
1878
1879 For example, here is a view that specifies such a transition:
1880
1881 \code
1882 GridView {
1883 ...
1884 add: Transition {
1885 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
1886 }
1887 }
1888 \endcode
1889
1890 Whenever an item is added to the above view, the item will be animated from the position (100,100)
1891 to its final x,y position within the view, over one second. The transition only applies to
1892 the new items that are added to the view; it does not apply to the items below that are
1893 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
1894 or \l addDisplaced properties.
1895
1896 For more details and examples on how to use view transitions, see the ViewTransition
1897 documentation.
1898
1899 \note This transition is not applied to the items that are created when the view is initially
1900 populated, or when the view's \l model changes. (In those cases, the \l populate transition is
1901 applied instead.) Additionally, this transition should \e not animate the height of the new item;
1902 doing so will cause any items beneath the new item to be laid out at the wrong position. Instead,
1903 the height can be animated within the \l {add}{onAdd} handler in the delegate.
1904
1905 \sa addDisplaced, populate, ViewTransition
1906*/
1907
1908/*!
1909 \qmlproperty Transition QtQuick::GridView::addDisplaced
1910
1911 This property holds the transition to apply to items within the view that are displaced by
1912 the addition of other items to the view.
1913
1914 For example, here is a view that specifies such a transition:
1915
1916 \code
1917 GridView {
1918 ...
1919 addDisplaced: Transition {
1920 NumberAnimation { properties: "x,y"; duration: 1000 }
1921 }
1922 }
1923 \endcode
1924
1925 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
1926 them to move down (or sideways, if horizontally orientated) within the view. As this
1927 displacement occurs, the items' movement to their new x,y positions within the view will be
1928 animated by a NumberAnimation over one second, as specified. This transition is not applied to
1929 the new item that has been added to the view; to animate the added items, set the \l add
1930 property.
1931
1932 If an item is displaced by multiple types of operations at the same time, it is not defined as to
1933 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1934 if it is not necessary to specify different transitions depending on whether an item is displaced
1935 by an add, move or remove operation, consider setting the \l displaced property instead.
1936
1937 For more details and examples on how to use view transitions, see the ViewTransition
1938 documentation.
1939
1940 \note This transition is not applied to the items that are created when the view is initially
1941 populated, or when the view's \l model changes. In those cases, the \l populate transition is
1942 applied instead.
1943
1944 \sa displaced, add, populate, ViewTransition
1945*/
1946/*!
1947 \qmlproperty Transition QtQuick::GridView::move
1948
1949 This property holds the transition to apply to items in the view that are being moved due
1950 to a move operation in the view's \l model.
1951
1952 For example, here is a view that specifies such a transition:
1953
1954 \code
1955 GridView {
1956 ...
1957 move: Transition {
1958 NumberAnimation { properties: "x,y"; duration: 1000 }
1959 }
1960 }
1961 \endcode
1962
1963 Whenever the \l model performs a move operation to move a particular set of indexes, the
1964 respective items in the view will be animated to their new positions in the view over one
1965 second. The transition only applies to the items that are the subject of the move operation
1966 in the model; it does not apply to items below them that are displaced by the move operation.
1967 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
1968
1969 For more details and examples on how to use view transitions, see the ViewTransition
1970 documentation.
1971
1972 \sa moveDisplaced, ViewTransition
1973*/
1974
1975/*!
1976 \qmlproperty Transition QtQuick::GridView::moveDisplaced
1977
1978 This property holds the transition to apply to items that are displaced by a move operation in
1979 the view's \l model.
1980
1981 For example, here is a view that specifies such a transition:
1982
1983 \code
1984 GridView {
1985 ...
1986 moveDisplaced: Transition {
1987 NumberAnimation { properties: "x,y"; duration: 1000 }
1988 }
1989 }
1990 \endcode
1991
1992 Whenever the \l model performs a move operation to move a particular set of indexes, the items
1993 between the source and destination indexes of the move operation are displaced, causing them
1994 to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
1995 displacement occurs, the items' movement to their new x,y positions within the view will be
1996 animated by a NumberAnimation over one second, as specified. This transition is not applied to
1997 the items that are the actual subjects of the move operation; to animate the moved items, set
1998 the \l move property.
1999
2000 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2001 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2002 if it is not necessary to specify different transitions depending on whether an item is displaced
2003 by an add, move or remove operation, consider setting the \l displaced property instead.
2004
2005 For more details and examples on how to use view transitions, see the ViewTransition
2006 documentation.
2007
2008 \sa displaced, move, ViewTransition
2009*/
2010
2011/*!
2012 \qmlproperty Transition QtQuick::GridView::remove
2013
2014 This property holds the transition to apply to items that are removed from the view.
2015
2016 For example, here is a view that specifies such a transition:
2017
2018 \code
2019 GridView {
2020 ...
2021 remove: Transition {
2022 ParallelAnimation {
2023 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2024 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2025 }
2026 }
2027 }
2028 \endcode
2029
2030 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2031 over one second, and in parallel will also change its opacity to 0. The transition
2032 only applies to the items that are removed from the view; it does not apply to the items below
2033 them that are displaced by the removal of the items. To animate the displaced items, set the
2034 \l displaced or \l removeDisplaced properties.
2035
2036 Note that by the time the transition is applied, the item has already been removed from the
2037 model; any references to the model data for the removed index will not be valid.
2038
2039 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2040 remove transition will not be applied until \l delayRemove becomes false again.
2041
2042 For more details and examples on how to use view transitions, see the ViewTransition
2043 documentation.
2044
2045 \sa removeDisplaced, ViewTransition
2046*/
2047
2048/*!
2049 \qmlproperty Transition QtQuick::GridView::removeDisplaced
2050
2051 This property holds the transition to apply to items in the view that are displaced by the
2052 removal of other items in the view.
2053
2054 For example, here is a view that specifies such a transition:
2055
2056 \code
2057 GridView {
2058 ...
2059 removeDisplaced: Transition {
2060 NumberAnimation { properties: "x,y"; duration: 1000 }
2061 }
2062 }
2063 \endcode
2064
2065 Whenever an item is removed from the above view, all items beneath it are displaced, causing
2066 them to move upwards (or sideways, if horizontally orientated) within the view. As this
2067 displacement occurs, the items' movement to their new x,y positions within the view will be
2068 animated by a NumberAnimation over one second, as specified. This transition is not applied to
2069 the item that has actually been removed from the view; to animate the removed items, set the
2070 \l remove property.
2071
2072 If an item is displaced by multiple types of operations at the same time, it is not defined as to
2073 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2074 if it is not necessary to specify different transitions depending on whether an item is displaced
2075 by an add, move or remove operation, consider setting the \l displaced property instead.
2076
2077 For more details and examples on how to use view transitions, see the ViewTransition
2078 documentation.
2079
2080 \sa displaced, remove, ViewTransition
2081*/
2082
2083/*!
2084 \qmlproperty Transition QtQuick::GridView::displaced
2085 This property holds the generic transition to apply to items that have been displaced by
2086 any model operation that affects the view.
2087
2088 This is a convenience for specifying a generic transition for items that are displaced
2089 by add, move or remove operations, without having to specify the individual addDisplaced,
2090 moveDisplaced and removeDisplaced properties. For example, here is a view that specifies
2091 a displaced transition:
2092
2093 \code
2094 GridView {
2095 ...
2096 displaced: Transition {
2097 NumberAnimation { properties: "x,y"; duration: 1000 }
2098 }
2099 }
2100 \endcode
2101
2102 When any item is added, moved or removed within the above view, the items below it are
2103 displaced, causing them to move down (or sideways, if horizontally orientated) within the
2104 view. As this displacement occurs, the items' movement to their new x,y positions within
2105 the view will be animated by a NumberAnimation over one second, as specified.
2106
2107 If a view specifies this generic displaced transition as well as a specific addDisplaced,
2108 moveDisplaced or removeDisplaced transition, the more specific transition will be used
2109 instead of the generic displaced transition when the relevant operation occurs, providing that
2110 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2111 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2112
2113 For more details and examples on how to use view transitions, see the ViewTransition
2114 documentation.
2115
2116 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2117*/
2118
2119void QQuickGridView::viewportMoved(Qt::Orientations orient)
2120{
2121 Q_D(QQuickGridView);
2122 QQuickItemView::viewportMoved(orient);
2123 if (!d->itemCount)
2124 return;
2125 if (d->inViewportMoved)
2126 return;
2127 d->inViewportMoved = true;
2128
2129 if (yflick()) {
2130 if (d->isContentFlowReversed())
2131 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
2132 else
2133 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2134 } else {
2135 if (d->isContentFlowReversed())
2136 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
2137 else
2138 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2139 }
2140
2141 d->refillOrLayout();
2142
2143 // Set visibility of items to eliminate cost of items outside the visible area.
2144 qreal from = d->isContentFlowReversed() ? -d->position()-d->displayMarginBeginning-d->size() : d->position()-d->displayMarginBeginning;
2145 qreal to = d->isContentFlowReversed() ? -d->position()+d->displayMarginEnd : d->position()+d->size()+d->displayMarginEnd;
2146 for (FxViewItem *item : std::as_const(d->visibleItems)) {
2147 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(item);
2148 QQuickItemPrivate::get(gridItem->item)->setCulled(gridItem->rowPos() + d->rowSize() < from || gridItem->rowPos() > to);
2149 }
2150 if (d->currentItem) {
2151 FxGridItemSG *item = static_cast<FxGridItemSG*>(d->currentItem);
2152 QQuickItemPrivate::get(item->item)->setCulled(item->rowPos() + d->rowSize() < from || item->rowPos() > to);
2153 }
2154
2155 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2156 d->moveReason = QQuickGridViewPrivate::Mouse;
2157 if (d->moveReason != QQuickGridViewPrivate::SetIndex) {
2158 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2159 // reposition highlight
2160 qreal pos = d->highlight->position();
2161 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2162 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2163 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2164 if (pos < viewPos + d->highlightRangeStart)
2165 pos = viewPos + d->highlightRangeStart;
2166
2167 if (pos != d->highlight->position()) {
2168 d->highlightXAnimator->stop();
2169 d->highlightYAnimator->stop();
2170 FxGridItemSG *sgHighlight = static_cast<FxGridItemSG *>(d->highlight.get());
2171 sgHighlight->setPosition(sgHighlight->colPos(), pos);
2172 } else {
2173 d->updateHighlight();
2174 }
2175
2176 // update current index
2177 int idx = d->snapIndex();
2178 if (idx >= 0 && idx != d->currentIndex) {
2179 d->updateCurrent(idx);
2180 if (d->currentItem
2181 && static_cast<FxGridItemSG*>(d->currentItem)->colPos()
2182 != static_cast<FxGridItemSG*>(d->highlight.get())->colPos()
2183 && d->autoHighlight) {
2184 if (d->flow == FlowLeftToRight)
2185 d->highlightXAnimator->to = d->currentItem->itemX();
2186 else
2187 d->highlightYAnimator->to = d->currentItem->itemY();
2188 }
2189 }
2190 }
2191 }
2192
2193 d->inViewportMoved = false;
2194}
2195
2196void QQuickGridView::keyPressEvent(QKeyEvent *event)
2197{
2198 Q_D(QQuickGridView);
2199 if (d->model && d->model->count() && ((d->interactive && !d->explicitKeyNavigationEnabled)
2200 || (d->explicitKeyNavigationEnabled && d->keyNavigationEnabled))) {
2201 d->moveReason = QQuickGridViewPrivate::SetIndex;
2202 int oldCurrent = currentIndex();
2203 switch (event->key()) {
2204 case Qt::Key_Up:
2205 moveCurrentIndexUp();
2206 break;
2207 case Qt::Key_Down:
2208 moveCurrentIndexDown();
2209 break;
2210 case Qt::Key_Left:
2211 moveCurrentIndexLeft();
2212 break;
2213 case Qt::Key_Right:
2214 moveCurrentIndexRight();
2215 break;
2216 default:
2217 break;
2218 }
2219 if (oldCurrent != currentIndex() || d->wrap) {
2220 event->accept();
2221 return;
2222 }
2223 }
2224 event->ignore();
2225 QQuickItemView::keyPressEvent(event);
2226}
2227
2228void QQuickGridView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
2229{
2230 Q_D(QQuickGridView);
2231 d->resetColumns();
2232
2233 if (newGeometry.width() != oldGeometry.width()
2234 && newGeometry.height() != oldGeometry.height()) {
2235 d->setPosition(d->position());
2236 } else if (newGeometry.width() != oldGeometry.width()) {
2237 QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
2238 } else if (newGeometry.height() != oldGeometry.height()) {
2239 QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
2240 }
2241
2242 QQuickItemView::geometryChange(newGeometry, oldGeometry);
2243}
2244
2245void QQuickGridView::initItem(int index, QObject *obj)
2246{
2247 QQuickItemView::initItem(index, obj);
2248
2249 // setting the view from the FxViewItem wrapper is too late if the delegate
2250 // needs access to the view in Component.onCompleted
2251 QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
2252 if (item) {
2253 QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
2254 qmlAttachedPropertiesObject<QQuickGridView>(item));
2255 if (attached)
2256 attached->setView(this);
2257 }
2258}
2259
2260/*!
2261 \qmlmethod void QtQuick::GridView::moveCurrentIndexUp()
2262
2263 Move the currentIndex up one item in the view.
2264 The current index will wrap if keyNavigationWraps is true and it
2265 is currently at the end. This method has no effect if the \l count is zero.
2266
2267 \b Note: methods should only be called after the Component has completed.
2268*/
2269
2270
2271void QQuickGridView::moveCurrentIndexUp()
2272{
2273 Q_D(QQuickGridView);
2274 const int count = d->model ? d->model->count() : 0;
2275 if (!count)
2276 return;
2277 if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2278 if (d->flow == QQuickGridView::FlowLeftToRight) {
2279 if (currentIndex() >= d->columns || d->wrap) {
2280 int index = currentIndex() - d->columns;
2281 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2282 }
2283 } else {
2284 if (currentIndex() > 0 || d->wrap) {
2285 int index = currentIndex() - 1;
2286 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2287 }
2288 }
2289 } else {
2290 if (d->flow == QQuickGridView::FlowLeftToRight) {
2291 if (currentIndex() < count - d->columns || d->wrap) {
2292 int index = currentIndex()+d->columns;
2293 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2294 }
2295 } else {
2296 if (currentIndex() < count - 1 || d->wrap) {
2297 int index = currentIndex() + 1;
2298 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2299 }
2300 }
2301 }
2302}
2303
2304/*!
2305 \qmlmethod void QtQuick::GridView::moveCurrentIndexDown()
2306
2307 Move the currentIndex down one item in the view.
2308 The current index will wrap if keyNavigationWraps is true and it
2309 is currently at the end. This method has no effect if the \l count is zero.
2310
2311 \b Note: methods should only be called after the Component has completed.
2312*/
2313void QQuickGridView::moveCurrentIndexDown()
2314{
2315 Q_D(QQuickGridView);
2316 const int count = d->model ? d->model->count() : 0;
2317 if (!count)
2318 return;
2319
2320 if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2321 if (d->flow == QQuickGridView::FlowLeftToRight) {
2322 if (currentIndex() < count - d->columns || d->wrap) {
2323 int index = currentIndex()+d->columns;
2324 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2325 }
2326 } else {
2327 if (currentIndex() < count - 1 || d->wrap) {
2328 int index = currentIndex() + 1;
2329 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2330 }
2331 }
2332 } else {
2333 if (d->flow == QQuickGridView::FlowLeftToRight) {
2334 if (currentIndex() >= d->columns || d->wrap) {
2335 int index = currentIndex() - d->columns;
2336 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2337 }
2338 } else {
2339 if (currentIndex() > 0 || d->wrap) {
2340 int index = currentIndex() - 1;
2341 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2342 }
2343 }
2344 }
2345}
2346
2347/*!
2348 \qmlmethod void QtQuick::GridView::moveCurrentIndexLeft()
2349
2350 Move the currentIndex left one item in the view.
2351 The current index will wrap if keyNavigationWraps is true and it
2352 is currently at the end. This method has no effect if the \l count is zero.
2353
2354 \b Note: methods should only be called after the Component has completed.
2355*/
2356void QQuickGridView::moveCurrentIndexLeft()
2357{
2358 Q_D(QQuickGridView);
2359 const int count = d->model ? d->model->count() : 0;
2360 if (!count)
2361 return;
2362 if (effectiveLayoutDirection() == Qt::LeftToRight) {
2363 if (d->flow == QQuickGridView::FlowLeftToRight) {
2364 if (currentIndex() > 0 || d->wrap) {
2365 int index = currentIndex() - 1;
2366 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2367 }
2368 } else {
2369 if (currentIndex() >= d->columns || d->wrap) {
2370 int index = currentIndex() - d->columns;
2371 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2372 }
2373 }
2374 } else {
2375 if (d->flow == QQuickGridView::FlowLeftToRight) {
2376 if (currentIndex() < count - 1 || d->wrap) {
2377 int index = currentIndex() + 1;
2378 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2379 }
2380 } else {
2381 if (currentIndex() < count - d->columns || d->wrap) {
2382 int index = currentIndex() + d->columns;
2383 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2384 }
2385 }
2386 }
2387}
2388
2389
2390/*!
2391 \qmlmethod void QtQuick::GridView::moveCurrentIndexRight()
2392
2393 Move the currentIndex right one item in the view.
2394 The current index will wrap if keyNavigationWraps is true and it
2395 is currently at the end. This method has no effect if the \l count is zero.
2396
2397 \b Note: methods should only be called after the Component has completed.
2398*/
2399void QQuickGridView::moveCurrentIndexRight()
2400{
2401 Q_D(QQuickGridView);
2402 const int count = d->model ? d->model->count() : 0;
2403 if (!count)
2404 return;
2405 if (effectiveLayoutDirection() == Qt::LeftToRight) {
2406 if (d->flow == QQuickGridView::FlowLeftToRight) {
2407 if (currentIndex() < count - 1 || d->wrap) {
2408 int index = currentIndex() + 1;
2409 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2410 }
2411 } else {
2412 if (currentIndex() < count - d->columns || d->wrap) {
2413 int index = currentIndex()+d->columns;
2414 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2415 }
2416 }
2417 } else {
2418 if (d->flow == QQuickGridView::FlowLeftToRight) {
2419 if (currentIndex() > 0 || d->wrap) {
2420 int index = currentIndex() - 1;
2421 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2422 }
2423 } else {
2424 if (currentIndex() >= d->columns || d->wrap) {
2425 int index = currentIndex() - d->columns;
2426 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2427 }
2428 }
2429 }
2430}
2431
2432bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2433{
2434 Q_Q(QQuickGridView);
2435
2436 if (q->size().isNull())
2437 return false;
2438
2439 int modelIndex = change.index;
2440 int count = change.count;
2441
2442 int index = visibleItems.size() ? mapFromModel(modelIndex) : 0;
2443
2444 if (index < 0) {
2445 int i = visibleItems.size() - 1;
2446 while (i > 0 && visibleItems.at(i)->index == -1)
2447 --i;
2448 if (visibleItems.at(i)->index + 1 == modelIndex) {
2449 // Special case of appending an item to the model.
2450 index = visibleItems.size();
2451 } else {
2452 if (modelIndex <= visibleIndex) {
2453 // Insert before visible items
2454 visibleIndex += count;
2455 for (FxViewItem *item : std::as_const(visibleItems)) {
2456 if (item->index != -1 && item->index >= modelIndex)
2457 item->index += count;
2458 }
2459 }
2460 return true;
2461 }
2462 }
2463
2464 qreal tempPos = isContentFlowReversed() ? -position()-size()+q->width()+1 : position();
2465 qreal colPos = 0;
2466 qreal rowPos = 0;
2467 int colNum = 0;
2468 if (visibleItems.size()) {
2469 if (index < visibleItems.size()) {
2470 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index));
2471 colPos = gridItem->colPos();
2472 rowPos = gridItem->rowPos();
2473 colNum = qFloor((colPos+colSize()/2) / colSize());
2474 } else {
2475 // appending items to visible list
2476 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index-1));
2477 rowPos = gridItem->rowPos();
2478 colNum = qFloor((gridItem->colPos()+colSize()/2) / colSize());
2479 if (++colNum >= columns) {
2480 colNum = 0;
2481 rowPos += rowSize();
2482 }
2483 colPos = colNum * colSize();
2484 }
2485 }
2486
2487#if QT_CONFIG(quick_viewtransitions)
2488 // Update the indexes of the following visible items.
2489 for (FxViewItem *item : std::as_const(visibleItems)) {
2490 if (item->index != -1 && item->index >= modelIndex) {
2491 item->index += count;
2492 if (change.isMove())
2493 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2494 else
2495 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2496 }
2497 }
2498#endif
2499
2500 int prevVisibleCount = visibleItems.size();
2501 if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
2502 // Insert items before the visible item.
2503 int insertionIdx = index;
2504 int i = count - 1;
2505 int from = tempPos - buffer - displayMarginBeginning;
2506
2507 if (rowPos > from && insertionIdx < visibleIndex) {
2508 // items won't be visible, just note the size for repositioning
2509 insertResult->countChangeBeforeVisible += count;
2510 insertResult->sizeChangesBeforeVisiblePos += ((count + columns - 1) / columns) * rowSize();
2511 } else {
2512 while (i >= 0) {
2513 // item is before first visible e.g. in cache buffer
2514 FxViewItem *item = nullptr;
2515 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2516 item->index = modelIndex + i;
2517 if (!item)
2518 item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
2519 if (!item)
2520 return false;
2521
2522 QQuickItemPrivate::get(item->item)->setCulled(false);
2523 visibleItems.insert(insertionIdx, item);
2524 if (insertionIdx == 0)
2525 insertResult->changedFirstItem = true;
2526 if (!change.isMove()) {
2527 addedItems->append(item);
2528#if QT_CONFIG(quick_viewtransitions)
2529 if (transitioner)
2530 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2531 else
2532#endif
2533 item->moveTo(QPointF(colPos, rowPos), true);
2534 }
2535 insertResult->sizeChangesBeforeVisiblePos += rowSize();
2536
2537 if (--colNum < 0 ) {
2538 colNum = columns - 1;
2539 rowPos -= rowSize();
2540 }
2541 colPos = colNum * colSize();
2542 index++;
2543 i--;
2544 }
2545 }
2546
2547 // There may be gaps in the index sequence of visibleItems because
2548 // of the index shift/update done before the insertion just above.
2549 // Find if there is any...
2550 int firstOkIdx = -1;
2551 for (int i = 0; i <= insertionIdx && i < visibleItems.size() - 1; i++) {
2552 if (visibleItems.at(i)->index + 1 != visibleItems.at(i + 1)->index) {
2553 firstOkIdx = i + 1;
2554 break;
2555 }
2556 }
2557 // ... and remove all the items before that one
2558 for (int i = 0; i < firstOkIdx; i++) {
2559 FxViewItem *nvItem = visibleItems.takeFirst();
2560 addedItems->removeOne(nvItem);
2561 removeItem(nvItem);
2562 }
2563
2564 } else {
2565 int i = 0;
2566 int to = buffer+displayMarginEnd+tempPos+size()-1;
2567 while (i < count && rowPos <= to + rowSize()*(columns - colNum)/qreal(columns+1)) {
2568 FxViewItem *item = nullptr;
2569 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2570 item->index = modelIndex + i;
2571 bool newItem = !item;
2572 if (!item)
2573 item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
2574 if (!item)
2575 return false;
2576
2577 QQuickItemPrivate::get(item->item)->setCulled(false);
2578 visibleItems.insert(index, item);
2579 if (index == 0)
2580 insertResult->changedFirstItem = true;
2581 if (change.isMove()) {
2582 // we know this is a move target, since move displaced items that are
2583 // shuffled into view due to a move would be added in refill()
2584 if (newItem
2585#if QT_CONFIG(quick_viewtransitions)
2586 && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)
2587#endif
2588 )
2589 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2590 } else {
2591 addedItems->append(item);
2592#if QT_CONFIG(quick_viewtransitions)
2593 if (transitioner)
2594 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2595 else
2596#endif
2597 item->moveTo(QPointF(colPos, rowPos), true);
2598 }
2599 insertResult->sizeChangesAfterVisiblePos += rowSize();
2600
2601 if (++colNum >= columns) {
2602 colNum = 0;
2603 rowPos += rowSize();
2604 }
2605 colPos = colNum * colSize();
2606 ++index;
2607 ++i;
2608 }
2609 }
2610
2611 updateVisibleIndex();
2612
2613 return visibleItems.size() > prevVisibleCount;
2614}
2615
2616#if QT_CONFIG(quick_viewtransitions)
2617void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2618{
2619 if (!transitioner)
2620 return;
2621
2622 int markerItemIndex = -1;
2623 for (int i=0; i<visibleItems.size(); i++) {
2624 if (visibleItems.at(i)->index == afterModelIndex) {
2625 markerItemIndex = i;
2626 break;
2627 }
2628 }
2629 if (markerItemIndex < 0)
2630 return;
2631
2632 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2633 int countItemsRemoved = -(removalResult.sizeChangesAfterVisiblePos / rowSize());
2634
2635 // account for whether first item has changed if < 1 row was removed before visible
2636 int changeBeforeVisible = insertionResult.countChangeBeforeVisible - removalResult.countChangeBeforeVisible;
2637 if (changeBeforeVisible != 0)
2638 countItemsRemoved += (changeBeforeVisible % columns) - (columns - 1);
2639
2640 countItemsRemoved -= removalResult.countChangeAfterVisibleItems;
2641
2642 for (int i=markerItemIndex+1; i<visibleItems.size(); i++) {
2643 FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems.at(i));
2644 if (gridItem->position() >= viewEndPos)
2645 break;
2646 if (!gridItem->transitionScheduledOrRunning()) {
2647 qreal origRowPos = gridItem->colPos();
2648 qreal origColPos = gridItem->rowPos();
2649 int indexDiff = gridItem->index - countItemsRemoved;
2650 gridItem->setPosition((indexDiff % columns) * colSize(), (indexDiff / columns) * rowSize());
2651 gridItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2652 gridItem->setPosition(origRowPos, origColPos);
2653 }
2654 }
2655}
2656#endif
2657
2659{
2660 // If we add or remove items before visible items, a layout may be
2661 // required to ensure item 0 is in the first column.
2662 return modelIndex < visibleIndex;
2663}
2664
2665/*!
2666 \qmlmethod void QtQuick::GridView::positionViewAtIndex(int index, PositionMode mode)
2667
2668 Positions the view such that the \a index is at the position specified by
2669 \a mode:
2670
2671 \value GridView.Beginning position item at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
2672 \value GridView.Center position item in the center of the view.
2673 \value GridView.End position item at bottom (or right for horizontal orientation) of the view.
2674 \value GridView.Visible if any part of the item is visible then take no action, otherwise
2675 bring the item into view.
2676 \value GridView.Contain ensure the entire item is visible. If the item is larger than the view, the item
2677 is positioned at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
2678 \value GridView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid if
2679 \l highlightRangeMode is \c StrictlyEnforceRange or snapping is enabled via \l snapMode.
2680
2681 If positioning the view at the index would cause empty space to be displayed at
2682 the beginning or end of the view, the view will be positioned at the boundary.
2683
2684 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2685 at a particular index. This is unreliable since removing items from the start
2686 of the view does not cause all other items to be repositioned.
2687 The correct way to bring an item into view is with \c positionViewAtIndex.
2688
2689 \b Note: methods should only be called after the Component has completed. To position
2690 the view at startup, this method should be called by Component.onCompleted. For
2691 example, to position the view at the end:
2692
2693 \code
2694 Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
2695 \endcode
2696*/
2697
2698/*!
2699 \qmlmethod void QtQuick::GridView::positionViewAtBeginning()
2700 \qmlmethod void QtQuick::GridView::positionViewAtEnd()
2701
2702 Positions the view at the beginning or end, taking into account any header or footer.
2703
2704 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2705 at a particular index. This is unreliable since removing items from the start
2706 of the list does not cause all other items to be repositioned, and because
2707 the actual start of the view can vary based on the size of the delegates.
2708
2709 \b Note: methods should only be called after the Component has completed. To position
2710 the view at startup, this method should be called by Component.onCompleted. For
2711 example, to position the view at the end on startup:
2712
2713 \code
2714 Component.onCompleted: positionViewAtEnd()
2715 \endcode
2716*/
2717
2718/*!
2719 \qmlmethod int QtQuick::GridView::indexAt(real x, real y)
2720
2721 Returns the index of the visible item containing the point \a x, \a y in
2722 \l {QtQuick::Flickable::contentItem}{content item} coordinates. If there is
2723 no item at the point specified, or the item is not visible -1 is returned.
2724
2725 If the item is outside the visible area, -1 is returned, regardless of
2726 whether an item will exist at that point when scrolled into view.
2727
2728 \note if you add a MouseArea as a child of the GridView, it will return
2729 positions in GridView coordinates rather than content item coordinates.
2730 To use those positions in a call to this function, you need to map them
2731 first:
2732
2733 \code
2734 GridView {
2735 id: view
2736 MouseArea {
2737 anchors.fill: parent
2738 onClicked: (mouse) => {
2739 let posInGridView = Qt.point(mouse.x, mouse.y)
2740 let posInContentItem = mapToItem(view.contentItem, posInGridView)
2741 let index = view.indexAt(posInContentItem.x, posInContentItem.y)
2742 }
2743 }
2744 }
2745 \endcode
2746
2747 \b Note: methods should only be called after the Component has completed.
2748
2749 \sa itemAt
2750*/
2751
2752/*!
2753 \qmlmethod Item QtQuick::GridView::itemAt(real x, real y)
2754
2755 Returns the visible item containing the point \a x, \a y in
2756 \l {QtQuick::Flickable::contentItem}{content item} coordinates. If there
2757 is no item at the point specified, or the item is not visible null is returned.
2758
2759 If the item is outside the visible area, null is returned, regardless of
2760 whether an item will exist at that point when scrolled into view.
2761
2762 \b Note: methods should only be called after the Component has completed.
2763
2764 \sa indexAt
2765*/
2766
2767/*!
2768 \qmlmethod Item QtQuick::GridView::itemAtIndex(int index)
2769
2770 Returns the item for \a index. If there is no item for that index, for example
2771 because it has not been created yet, or because it has been panned out of
2772 the visible area and removed from the cache, null is returned.
2773
2774 \b Note: this method should only be called after the Component has completed.
2775 The returned value should also not be stored since it can turn to null
2776 as soon as control goes out of the calling scope, if the view releases that item.
2777
2778 \since 5.13
2779*/
2780
2781/*!
2782 \qmlmethod void QtQuick::GridView::forceLayout()
2783
2784 Responding to changes in the model is usually batched to happen only once
2785 per frame. This means that inside script blocks it is possible for the
2786 underlying model to have changed, but the GridView has not caught up yet.
2787
2788 This method forces the GridView to immediately respond to any outstanding
2789 changes in the model.
2790
2791 \since 5.1
2792
2793 \b Note: methods should only be called after the Component has completed.
2794*/
2795
2796QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj)
2797{
2798 return new QQuickGridViewAttached(obj);
2799}
2800
2801QT_END_NAMESPACE
2802
2803#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