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