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

source code of qtdeclarative/src/quick/items/qquickgridview.cpp