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