1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickgridview_p.h"
43 #include "qquickvisualitemmodel_p.h"
44 #include "qquickflickable_p_p.h"
45 #include "qquickitemview_p_p.h"
47 #include <private/qquicksmoothedanimation_p_p.h>
48 #include <private/qlistmodelinterface_p.h>
50 #include <QtGui/qevent.h>
51 #include <QtCore/qmath.h>
52 #include <QtCore/qcoreapplication.h>
54 #include "qplatformdefs.h"
58 #ifndef QML_FLICK_SNAPONETHRESHOLD
59 #define QML_FLICK_SNAPONETHRESHOLD 30
62 //#define DEBUG_DELEGATE_LIFECYCLE
64 //----------------------------------------------------------------------------
66 class FxGridItemSG : public FxViewItem
69 FxGridItemSG(QQuickItem *i, QQuickGridView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
70 attached = static_cast<QQuickGridViewAttached*>(qmlAttachedPropertiesObject<QQuickGridView>(item));
72 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
73 itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
79 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
80 itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
84 qreal position() const {
88 qreal endPosition() const {
93 return view->flow() == QQuickGridView::FlowLeftToRight ? view->cellHeight() : view->cellWidth();
96 qreal sectionSize() const {
100 qreal rowPos() const {
101 if (view->flow() == QQuickGridView::FlowLeftToRight)
102 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -view->cellHeight()-itemY() : itemY());
104 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-itemX() : itemX());
107 qreal colPos() const {
108 if (view->flow() == QQuickGridView::FlowLeftToRight) {
109 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
110 qreal colSize = view->cellWidth();
111 int columns = view->width()/colSize;
112 return colSize * (columns-1) - itemX();
117 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
118 return -view->cellHeight() - itemY();
124 qreal endRowPos() const {
125 if (view->flow() == QQuickGridView::FlowLeftToRight) {
126 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
129 return itemY() + view->cellHeight();
131 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
134 return itemX() + view->cellWidth();
137 void setPosition(qreal col, qreal row, bool immediate = false) {
138 moveTo(pointForPosition(col, row), immediate);
140 bool contains(qreal x, qreal y) const {
141 return (x >= itemX() && x < itemX() + view->cellWidth() &&
142 y >= itemY() && y < itemY() + view->cellHeight());
145 QQuickGridView *view;
148 QPointF pointForPosition(qreal col, qreal row) const {
151 if (view->flow() == QQuickGridView::FlowLeftToRight) {
154 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
155 int columns = view->width()/view->cellWidth();
156 x = view->cellWidth() * (columns-1) - col;
161 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
162 x = -view->cellWidth() - row;
164 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
165 y = -view->cellHeight() - y;
166 return QPointF(x, y);
170 //----------------------------------------------------------------------------
172 class QQuickGridViewPrivate : public QQuickItemViewPrivate
174 Q_DECLARE_PUBLIC(QQuickGridView)
177 virtual Qt::Orientation layoutOrientation() const;
178 virtual bool isContentFlowReversed() const;
180 virtual qreal positionAt(int index) const;
181 virtual qreal endPositionAt(int index) const;
182 virtual qreal originPosition() const;
183 virtual qreal lastPosition() const;
185 qreal rowSize() const;
186 qreal colSize() const;
187 qreal colPosAt(int modelIndex) const;
188 qreal rowPosAt(int modelIndex) const;
189 qreal snapPosAt(qreal pos) const;
190 FxViewItem *snapItemAt(qreal pos) const;
191 int snapIndex() const;
192 qreal contentXForPosition(qreal pos) const;
193 qreal contentYForPosition(qreal pos) const;
197 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer);
198 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
200 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
201 virtual void initializeViewItem(FxViewItem *item);
202 virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
203 virtual void repositionPackageItemAt(QQuickItem *item, int index);
204 virtual void resetFirstItemPosition(qreal pos = 0.0);
205 virtual void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible);
207 virtual void createHighlight();
208 virtual void updateHighlight();
209 virtual void resetHighlightPosition();
211 virtual void setPosition(qreal pos);
212 virtual void layoutVisibleItems(int fromModelIndex = 0);
213 virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
214 virtual void translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
215 virtual bool needsRefillForAddedOrRemovedIndex(int index) const;
217 virtual qreal headerSize() const;
218 virtual qreal footerSize() const;
219 virtual bool showHeaderForIndex(int index) const;
220 virtual bool showFooterForIndex(int index) const;
221 virtual void updateHeader();
222 virtual void updateFooter();
224 virtual void changedVisibleIndex(int newIndex);
225 virtual void initializeCurrentItem();
227 virtual void updateViewport();
228 virtual void fixupPosition();
229 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
230 virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
231 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
233 QQuickGridView::Flow flow;
237 QQuickGridView::SnapMode snapMode;
239 QSmoothedAnimation *highlightXAnimator;
240 QSmoothedAnimation *highlightYAnimator;
242 QQuickGridViewPrivate()
243 : flow(QQuickGridView::FlowLeftToRight)
244 , cellWidth(100), cellHeight(100), columns(1)
245 , snapMode(QQuickGridView::NoSnap)
246 , highlightXAnimator(0), highlightYAnimator(0)
248 ~QQuickGridViewPrivate()
250 delete highlightXAnimator;
251 delete highlightYAnimator;
255 Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const
257 return flow == QQuickGridView::FlowLeftToRight ? Qt::Vertical : Qt::Horizontal;
260 bool QQuickGridViewPrivate::isContentFlowReversed() const
262 Q_Q(const QQuickGridView);
264 return (flow == QQuickGridView::FlowLeftToRight && verticalLayoutDirection == QQuickItemView::BottomToTop)
265 || (flow == QQuickGridView::FlowTopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft);
268 void QQuickGridViewPrivate::changedVisibleIndex(int newIndex)
270 visibleIndex = newIndex / columns * columns;
273 void QQuickGridViewPrivate::setPosition(qreal pos)
276 q->QQuickFlickable::setContentX(contentXForPosition(pos));
277 q->QQuickFlickable::setContentY(contentYForPosition(pos));
280 qreal QQuickGridViewPrivate::originPosition() const
283 if (!visibleItems.isEmpty())
284 pos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
288 qreal QQuickGridViewPrivate::lastPosition() const
291 if (model && model->count()) {
292 // get end position of last item
293 pos = (rowPosAt(model->count() - 1) + rowSize());
298 qreal QQuickGridViewPrivate::positionAt(int index) const
300 return rowPosAt(index);
303 qreal QQuickGridViewPrivate::endPositionAt(int index) const
305 return rowPosAt(index) + rowSize();
308 qreal QQuickGridViewPrivate::rowSize() const {
309 return flow == QQuickGridView::FlowLeftToRight ? cellHeight : cellWidth;
311 qreal QQuickGridViewPrivate::colSize() const {
312 return flow == QQuickGridView::FlowLeftToRight ? cellWidth : cellHeight;
315 qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const
317 if (FxViewItem *item = visibleItem(modelIndex))
318 return static_cast<FxGridItemSG*>(item)->colPos();
319 if (!visibleItems.isEmpty()) {
320 if (modelIndex == visibleIndex) {
321 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
322 return firstItem->colPos();
323 } else if (modelIndex < visibleIndex) {
324 int count = (visibleIndex - modelIndex) % columns;
325 int col = static_cast<FxGridItemSG*>(visibleItems.first())->colPos() / colSize();
326 col = (columns - count + col) % columns;
327 return col * colSize();
329 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
330 int count = modelIndex - lastItem->index;
331 int col = lastItem->colPos() / colSize();
332 col = (col + count) % columns;
333 return col * colSize();
336 return (modelIndex % columns) * colSize();
339 qreal QQuickGridViewPrivate::rowPosAt(int modelIndex) const
341 if (FxViewItem *item = visibleItem(modelIndex))
342 return static_cast<FxGridItemSG*>(item)->rowPos();
343 if (!visibleItems.isEmpty()) {
344 if (modelIndex == visibleIndex) {
345 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
346 return firstItem->rowPos();
347 } else if (modelIndex < visibleIndex) {
348 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
349 int firstCol = firstItem->colPos() / colSize();
350 int col = visibleIndex - modelIndex + (columns - firstCol - 1);
351 int rows = col / columns;
352 return firstItem->rowPos() - rows * rowSize();
354 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
355 int count = modelIndex - lastItem->index;
356 int col = lastItem->colPos() + count * colSize();
357 int rows = col / (columns * colSize());
358 return lastItem->rowPos() + rows * rowSize();
361 return (modelIndex / columns) * rowSize();
365 qreal QQuickGridViewPrivate::snapPosAt(qreal pos) const
367 Q_Q(const QQuickGridView);
369 if (!visibleItems.isEmpty()) {
370 qreal highlightStart = highlightRangeStart;
371 pos += highlightStart;
373 snapPos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
374 snapPos = pos - fmodf(pos - snapPos, qreal(rowSize()));
375 snapPos -= highlightStart;
378 if (isContentFlowReversed()) {
379 maxExtent = q->minXExtent()-size();
380 minExtent = q->maxXExtent()-size();
382 maxExtent = flow == QQuickGridView::FlowLeftToRight ? -q->maxYExtent() : -q->maxXExtent();
383 minExtent = flow == QQuickGridView::FlowLeftToRight ? -q->minYExtent() : -q->minXExtent();
385 if (snapPos > maxExtent)
387 if (snapPos < minExtent)
393 FxViewItem *QQuickGridViewPrivate::snapItemAt(qreal pos) const
395 for (int i = 0; i < visibleItems.count(); ++i) {
396 FxViewItem *item = visibleItems.at(i);
397 if (item->index == -1)
399 qreal itemTop = item->position();
400 if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
406 int QQuickGridViewPrivate::snapIndex() const
408 int index = currentIndex;
409 for (int i = 0; i < visibleItems.count(); ++i) {
410 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
411 if (item->index == -1)
413 qreal itemTop = item->position();
414 FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight);
415 if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) {
417 if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2)
424 qreal QQuickGridViewPrivate::contentXForPosition(qreal pos) const
426 Q_Q(const QQuickGridView);
427 if (flow == QQuickGridView::FlowLeftToRight) {
429 if (q->effectiveLayoutDirection() == Qt::LeftToRight) {
432 qreal colSize = cellWidth;
433 int columns = q->width()/colSize;
434 return -q->width() + (cellWidth * columns);
438 if (q->effectiveLayoutDirection() == Qt::LeftToRight)
441 return -pos - q->width();
445 qreal QQuickGridViewPrivate::contentYForPosition(qreal pos) const
447 Q_Q(const QQuickGridView);
448 if (flow == QQuickGridView::FlowLeftToRight) {
450 if (verticalLayoutDirection == QQuickItemView::TopToBottom)
453 return -pos - q->height();
456 if (verticalLayoutDirection == QQuickItemView::TopToBottom)
463 void QQuickGridViewPrivate::resetColumns()
466 qreal length = flow == QQuickGridView::FlowLeftToRight ? q->width() : q->height();
467 columns = (int)qMax((length + colSize()/2) / colSize(), qreal(1.));
470 FxViewItem *QQuickGridViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
473 Q_UNUSED(modelIndex);
474 return new FxGridItemSG(item, q, false, false);
477 void QQuickGridViewPrivate::initializeViewItem(FxViewItem *item)
479 QQuickItemViewPrivate::initializeViewItem(item);
481 // need to track current items that are animating
482 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
483 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
486 bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
488 qreal colPos = colPosAt(visibleIndex);
489 qreal rowPos = rowPosAt(visibleIndex);
490 if (visibleItems.count()) {
491 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
492 rowPos = lastItem->rowPos();
493 int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize());
494 if (++colNum >= columns) {
498 colPos = colNum * colSize();
501 int modelIndex = findLastVisibleIndex();
502 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
504 if (visibleItems.count() && (bufferFrom > rowPos + rowSize()*2
505 || bufferTo < rowPosAt(visibleIndex) - rowSize())) {
506 // We've jumped more than a page. Estimate which items are now
507 // visible and fill from there.
508 int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
509 for (int i = 0; i < visibleItems.count(); ++i)
510 releaseItem(visibleItems.at(i));
511 visibleItems.clear();
513 if (modelIndex >= model->count())
514 modelIndex = model->count() - 1;
515 else if (modelIndex < 0)
517 modelIndex = modelIndex / columns * columns;
518 visibleIndex = modelIndex;
519 colPos = colPosAt(visibleIndex);
520 rowPos = rowPosAt(visibleIndex);
523 int colNum = qFloor((colPos+colSize()/2) / colSize());
524 FxGridItemSG *item = 0;
525 bool changed = false;
527 while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
528 #ifdef DEBUG_DELEGATE_LIFECYCLE
529 qDebug() << "refill: append item" << modelIndex << colPos << rowPos;
531 if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex, doBuffer))))
533 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
534 item->setPosition(colPos, rowPos, true);
535 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
536 visibleItems.append(item);
537 if (++colNum >= columns) {
541 colPos = colNum * colSize();
546 if (doBuffer && requestedIndex != -1) // already waiting for an item
550 if (visibleItems.count()) {
551 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
552 rowPos = firstItem->rowPos();
553 colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize());
555 colNum = columns - 1;
559 colNum = qFloor((colPos+colSize()/2) / colSize());
563 colPos = colNum * colSize();
564 while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
565 #ifdef DEBUG_DELEGATE_LIFECYCLE
566 qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
568 if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1, doBuffer))))
571 if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
572 item->setPosition(colPos, rowPos, true);
573 QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
574 visibleItems.prepend(item);
579 colPos = colNum * colSize();
586 bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
588 FxGridItemSG *item = 0;
589 bool changed = false;
591 while (visibleItems.count() > 1
592 && (item = static_cast<FxGridItemSG*>(visibleItems.first()))
593 && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
594 if (item->attached->delayRemove())
596 #ifdef DEBUG_DELEGATE_LIFECYCLE
597 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
599 if (item->index != -1)
601 visibleItems.removeFirst();
602 if (item->transitionScheduledOrRunning()) {
603 #ifdef DEBUG_DELEGATE_LIFECYCLE
604 qDebug() << "\tnot releasing animating item:" << item->index << item->item->objectName();
606 item->releaseAfterTransition = true;
607 releasePendingTransition.append(item);
613 while (visibleItems.count() > 1
614 && (item = static_cast<FxGridItemSG*>(visibleItems.last()))
615 && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
616 if (item->attached->delayRemove())
618 #ifdef DEBUG_DELEGATE_LIFECYCLE
619 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1;
621 visibleItems.removeLast();
622 if (item->transitionScheduledOrRunning()) {
623 #ifdef DEBUG_DELEGATE_LIFECYCLE
624 qDebug() << "\tnot releasing animating item:" << item->index << item->item->objectName();
626 item->releaseAfterTransition = true;
627 releasePendingTransition.append(item);
637 void QQuickGridViewPrivate::updateViewport()
640 QQuickItemViewPrivate::updateViewport();
643 void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex)
645 if (visibleItems.count()) {
646 const qreal from = isContentFlowReversed() ? -position() - size() : position();
647 const qreal to = isContentFlowReversed() ? -position() : position() + size();
649 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
650 qreal rowPos = firstItem->rowPos();
651 qreal colPos = firstItem->colPos();
652 int col = visibleIndex % columns;
653 if (colPos != col * colSize()) {
654 colPos = col * colSize();
655 firstItem->setPosition(colPos, rowPos);
656 firstItem->setVisible(firstItem->rowPos() + rowSize() >= from && firstItem->rowPos() <= to);
658 for (int i = 1; i < visibleItems.count(); ++i) {
659 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
660 if (++col >= columns) {
664 colPos = col * colSize();
665 if (item->index >= fromModelIndex) {
666 item->setPosition(colPos, rowPos);
667 item->setVisible(item->rowPos() + rowSize() >= from && item->rowPos() <= to);
673 void QQuickGridViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
675 int count = sizeBuffer / rowSize();
676 static_cast<FxGridItemSG *>(item)->setPosition(colPosAt(index + count), rowPosAt(index + count));
679 void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
682 qreal pos = position();
683 if (flow == QQuickGridView::FlowLeftToRight) {
684 if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
685 qreal y = (verticalLayoutDirection == QQuickItemView::TopToBottom)
687 : -rowPosAt(index) - item->height();
688 item->setPos(QPointF(colPosAt(index), y));
691 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
692 qreal y = (verticalLayoutDirection == QQuickItemView::TopToBottom)
694 : -colPosAt(index) - item->height();
695 if (flow == QQuickGridView::FlowTopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft)
696 item->setPos(QPointF(-rowPosAt(index)-item->width(), y));
698 item->setPos(QPointF(rowPosAt(index), y));
703 void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos)
705 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.first());
706 item->setPosition(0, pos);
709 void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible)
711 if (!visibleItems.count())
714 int moveCount = (forwards - backwards) / rowSize();
715 if (moveCount == 0 && changeBeforeVisible != 0)
716 moveCount += (changeBeforeVisible % columns) - (columns - 1);
718 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first());
719 gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
722 void QQuickGridViewPrivate::createHighlight()
725 bool changed = false;
727 if (trackedItem == highlight)
732 delete highlightXAnimator;
733 delete highlightYAnimator;
734 highlightXAnimator = 0;
735 highlightYAnimator = 0;
741 QQuickItem *item = createHighlightItem();
743 FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true, true);
745 resetHighlightPosition();
746 highlightXAnimator = new QSmoothedAnimation;
747 highlightXAnimator->target = QQmlProperty(item, QLatin1String("x"));
748 highlightXAnimator->userDuration = highlightMoveDuration;
749 highlightYAnimator = new QSmoothedAnimation;
750 highlightYAnimator->target = QQmlProperty(item, QLatin1String("y"));
751 highlightYAnimator->userDuration = highlightMoveDuration;
753 highlight = newHighlight;
758 emit q->highlightItemChanged();
761 void QQuickGridViewPrivate::updateHighlight()
763 applyPendingChanges();
765 if ((!currentItem && highlight) || (currentItem && !highlight))
767 bool strictHighlight = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
768 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
769 // auto-update highlight
770 highlightXAnimator->to = currentItem->itemX();
771 highlightYAnimator->to = currentItem->itemY();
772 highlight->item->setWidth(currentItem->item->width());
773 highlight->item->setHeight(currentItem->item->height());
775 highlightXAnimator->restart();
776 highlightYAnimator->restart();
781 void QQuickGridViewPrivate::resetHighlightPosition()
783 if (highlight && currentItem) {
784 FxGridItemSG *cItem = static_cast<FxGridItemSG*>(currentItem);
785 static_cast<FxGridItemSG*>(highlight)->setPosition(cItem->colPos(), cItem->rowPos());
789 qreal QQuickGridViewPrivate::headerSize() const
793 return flow == QQuickGridView::FlowLeftToRight ? header->item->height() : header->item->width();
796 qreal QQuickGridViewPrivate::footerSize() const
800 return flow == QQuickGridView::FlowLeftToRight? footer->item->height() : footer->item->width();
803 bool QQuickGridViewPrivate::showHeaderForIndex(int index) const
805 return index / columns == 0;
808 bool QQuickGridViewPrivate::showFooterForIndex(int index) const
810 return index / columns == (model->count()-1) / columns;
813 void QQuickGridViewPrivate::updateFooter()
816 bool created = false;
818 QQuickItem *item = createComponentItem(footerComponent, 1.0);
821 footer = new FxGridItemSG(item, q, true, true);
825 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(footer);
828 if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
829 if (flow == QQuickGridView::FlowTopToBottom)
830 rowOffset += gridItem->item->width() - cellWidth;
832 colOffset += gridItem->item->width() - cellWidth;
834 if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
835 if (flow == QQuickGridView::FlowTopToBottom)
836 colOffset += gridItem->item->height() - cellHeight;
838 rowOffset += gridItem->item->height() - cellHeight;
840 if (visibleItems.count()) {
841 qreal endPos = lastPosition();
842 if (findLastVisibleIndex() == model->count()-1) {
843 gridItem->setPosition(colOffset, endPos + rowOffset);
845 qreal visiblePos = isContentFlowReversed() ? -position() : position() + size();
846 if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset)
847 gridItem->setPosition(colOffset, endPos + rowOffset);
850 gridItem->setPosition(colOffset, rowOffset);
854 emit q->footerItemChanged();
857 void QQuickGridViewPrivate::updateHeader()
860 bool created = false;
862 QQuickItem *item = createComponentItem(headerComponent, 1.0);
865 header = new FxGridItemSG(item, q, true, true);
869 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(header);
871 qreal rowOffset = -headerSize();
872 if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
873 if (flow == QQuickGridView::FlowTopToBottom)
874 rowOffset += gridItem->item->width() - cellWidth;
876 colOffset += gridItem->item->width() - cellWidth;
878 if (verticalLayoutDirection == QQuickItemView::BottomToTop) {
879 if (flow == QQuickGridView::FlowTopToBottom)
880 colOffset += gridItem->item->height() - cellHeight;
882 rowOffset += gridItem->item->height() - cellHeight;
884 if (visibleItems.count()) {
885 qreal startPos = originPosition();
886 if (visibleIndex == 0) {
887 gridItem->setPosition(colOffset, startPos + rowOffset);
889 qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
890 qreal headerPos = isContentFlowReversed() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
891 if (tempPos <= startPos || headerPos > startPos + rowOffset)
892 gridItem->setPosition(colOffset, startPos + rowOffset);
895 if (isContentFlowReversed())
896 gridItem->setPosition(colOffset, rowOffset);
898 gridItem->setPosition(colOffset, -headerSize());
902 emit q->headerItemChanged();
905 void QQuickGridViewPrivate::initializeCurrentItem()
907 if (currentItem && currentIndex >= 0) {
908 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(currentItem);
909 FxViewItem *actualItem = visibleItem(currentIndex);
911 // don't reposition the item if it's about to be transitioned to another position
912 if ((!actualItem || !actualItem->transitionScheduledOrRunning()))
913 gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
917 void QQuickGridViewPrivate::fixupPosition()
920 if (flow == QQuickGridView::FlowLeftToRight)
926 void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
928 if ((flow == QQuickGridView::FlowTopToBottom && &data == &vData)
929 || (flow == QQuickGridView::FlowLeftToRight && &data == &hData))
932 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
934 qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
936 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
937 if (snapMode != QQuickGridView::NoSnap) {
938 qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
939 if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
940 // if we've been dragged < rowSize()/2 then bias towards the next row
941 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
943 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
945 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2)
947 if (isContentFlowReversed())
949 tempPosition -= bias;
951 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
952 if (!topItem && strictHighlightRange && currentItem) {
953 // StrictlyEnforceRange always keeps an item in range
955 topItem = currentItem;
957 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
958 if (!bottomItem && strictHighlightRange && currentItem) {
959 // StrictlyEnforceRange always keeps an item in range
961 bottomItem = currentItem;
964 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
965 if (topItem && (isInBounds || strictHighlightRange)) {
966 qreal headerPos = header ? static_cast<FxGridItemSG*>(header)->rowPos() : 0;
967 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) {
968 pos = isContentFlowReversed() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart;
970 if (isContentFlowReversed())
971 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
973 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
975 } else if (bottomItem && isInBounds) {
976 if (isContentFlowReversed())
977 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
979 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
981 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
985 qreal dist = qAbs(data.move + pos);
987 timeline.reset(data.move);
988 if (fixupMode != Immediate) {
989 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
990 data.fixingUp = true;
992 timeline.set(data.move, -pos);
994 vTime = timeline.time();
996 } else if (haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange) {
999 qreal pos = static_cast<FxGridItemSG*>(currentItem)->rowPos();
1000 if (viewPos < pos + rowSize() - highlightRangeEnd)
1001 viewPos = pos + rowSize() - highlightRangeEnd;
1002 if (viewPos > pos - highlightRangeStart)
1003 viewPos = pos - highlightRangeStart;
1004 if (isContentFlowReversed())
1005 viewPos = -viewPos-size();
1006 timeline.reset(data.move);
1007 if (viewPos != position()) {
1008 if (fixupMode != Immediate) {
1009 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1010 data.fixingUp = true;
1012 timeline.set(data.move, -viewPos);
1015 vTime = timeline.time();
1018 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1020 data.inOvershoot = false;
1024 bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1025 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1027 data.fixingUp = false;
1029 if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange)
1030 && snapMode == QQuickGridView::NoSnap) {
1031 return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1033 qreal maxDistance = 0;
1034 qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1035 // -ve velocity means list is moving up/left
1037 if (data.move.value() < minExtent) {
1038 if (snapMode == QQuickGridView::SnapOneRow) {
1039 // if we've been dragged < averageSize/2 then bias towards the next item
1040 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1041 qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
1042 if (isContentFlowReversed())
1044 data.flickTarget = -snapPosAt(-dataValue - bias);
1045 maxDistance = qAbs(data.flickTarget - data.move.value());
1046 velocity = maxVelocity;
1048 maxDistance = qAbs(minExtent - data.move.value());
1051 if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
1052 data.flickTarget = minExtent;
1054 if (data.move.value() > maxExtent) {
1055 if (snapMode == QQuickGridView::SnapOneRow) {
1056 // if we've been dragged < averageSize/2 then bias towards the next item
1057 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1058 qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
1059 if (isContentFlowReversed())
1061 data.flickTarget = -snapPosAt(-dataValue + bias);
1062 maxDistance = qAbs(data.flickTarget - data.move.value());
1063 velocity = -maxVelocity;
1065 maxDistance = qAbs(maxExtent - data.move.value());
1068 if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
1069 data.flickTarget = maxExtent;
1071 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1072 if (maxDistance > 0 || overShoot) {
1073 // This mode requires the grid to stop exactly on a row boundary.
1075 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1081 qreal accel = deceleration;
1083 qreal overshootDist = 0.0;
1084 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) {
1085 // + rowSize()/4 to encourage moving at least one item in the flick direction
1086 qreal dist = v2 / (accel * 2.0) + rowSize()/4;
1087 dist = qMin(dist, maxDistance);
1090 if (snapMode != QQuickGridView::SnapOneRow) {
1091 qreal distTemp = isContentFlowReversed() ? -dist : dist;
1092 data.flickTarget = -snapPosAt(-dataValue + distTemp);
1094 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1096 if (data.flickTarget >= minExtent) {
1097 overshootDist = overShootDistance(vSize);
1098 data.flickTarget += overshootDist;
1099 } else if (data.flickTarget <= maxExtent) {
1100 overshootDist = overShootDistance(vSize);
1101 data.flickTarget -= overshootDist;
1104 qreal adjDist = -data.flickTarget + data.move.value();
1105 if (qAbs(adjDist) > qAbs(dist)) {
1106 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1107 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1116 accel = v2 / (2.0f * qAbs(dist));
1118 data.flickTarget = velocity > 0 ? minExtent : maxExtent;
1119 overshootDist = overShoot ? overShootDistance(vSize) : 0;
1121 timeline.reset(data.move);
1122 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1123 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1126 timeline.reset(data.move);
1127 fixup(data, minExtent, maxExtent);
1133 //----------------------------------------------------------------------------
1135 \qmlclass GridView QQuickGridView
1136 \inqmlmodule QtQuick 2
1137 \ingroup qtquick-views
1140 \brief For specifying a grid view of items provided by a model
1142 A GridView displays data from models created from built-in QML elements like ListModel
1143 and XmlListModel, or custom model classes defined in C++ that inherit from
1146 A GridView has a \l model, which defines the data to be displayed, and
1147 a \l delegate, which defines how the data should be displayed. Items in a
1148 GridView are laid out horizontally or vertically. Grid views are inherently flickable
1149 as GridView inherits from \l Flickable.
1151 \section1 Example Usage
1153 The following example shows the definition of a simple list model defined
1154 in a file called \c ContactModel.qml:
1156 \snippet qml/gridview/ContactModel.qml 0
1158 \div {class="float-right"}
1159 \inlineimage gridview-simple.png
1162 This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
1163 for more information about creating reusable components like this.
1165 Another component can display this model data in a GridView, as in the following
1166 example, which creates a \c ContactModel component for its model, and a \l Column element
1167 (containing \l Image and \l Text elements) for its delegate.
1170 \snippet qml/gridview/gridview.qml import
1172 \snippet qml/gridview/gridview.qml classdocs simple
1174 \div {class="float-right"}
1175 \inlineimage gridview-highlight.png
1178 The view will create a new delegate for each item in the model. Note that the delegate
1179 is able to access the model's \c name and \c portrait data directly.
1181 An improved grid view is shown below. The delegate is visually improved and is moved
1182 into a separate \c contactDelegate component.
1185 \snippet qml/gridview/gridview.qml classdocs advanced
1187 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1188 and \c focus is set to \c true to enable keyboard navigation for the grid view.
1189 The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1191 Delegates are instantiated as needed and may be destroyed at any time.
1192 State should \e never be stored in a delegate.
1194 GridView attaches a number of properties to the root item of the delegate, for example
1195 \c {GridView.isCurrentItem}. In the following example, the root delegate item can access
1196 this attached property directly as \c GridView.isCurrentItem, while the child
1197 \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
1199 \snippet qml/gridview/gridview.qml isCurrentItem
1201 \note Views do not set the \l{Item::}{clip} property automatically.
1202 If the view is not clipped by another item or the screen, it will be necessary
1203 to set this property to true in order to clip the items that are partially or
1204 fully outside the view.
1207 \section1 GridView layouts
1209 The layout of the items in a GridView can be controlled by these properties:
1212 \li \l flow - controls whether items flow from left to right (as a series of rows)
1213 or from top to bottom (as a series of columns). This value can be either
1214 GridView.LeftToRight or GridView.TopToBottom.
1215 \li \l layoutDirection - controls the horizontal layout direction: that is, whether items
1216 are laid out from the left side of the view to the right, or vice-versa. This value can
1217 be either Qt.LeftToRight or Qt.RightToLeft.
1218 \li \l verticalLayoutDirection - controls the vertical layout direction: that is, whether items
1219 are laid out from the top of the view down towards the bottom of the view, or vice-versa.
1220 This value can be either GridView.TopToBottom or GridView.BottomToTop.
1223 By default, a GridView flows from left to right, and items are laid out from left to right
1224 horizontally, and from top to bottom vertically.
1226 These properties can be combined to produce a variety of layouts, as shown in the table below.
1227 The GridViews in the first row all have a \l flow value of GridView.LeftToRight, but use
1228 different combinations of horizontal and vertical layout directions (specified by \l layoutDirection
1229 and \l verticalLayoutDirection respectively). Similarly, the GridViews in the second row below
1230 all have a \l flow value of GridView.TopToBottom, but use different combinations of horizontal and
1231 vertical layout directions to lay out their items in different ways.
1236 \bold GridViews with GridView.LeftToRight flow
1238 \li \bold (H) Left to right \bold (V) Top to bottom
1239 \image gridview-layout-lefttoright-ltr-ttb.png
1240 \li \bold (H) Right to left \bold (V) Top to bottom
1241 \image gridview-layout-lefttoright-rtl-ttb.png
1242 \li \bold (H) Left to right \bold (V) Bottom to top
1243 \image gridview-layout-lefttoright-ltr-btt.png
1244 \li \bold (H) Right to left \bold (V) Bottom to top
1245 \image gridview-layout-lefttoright-rtl-btt.png
1248 \bold GridViews with GridView.TopToBottom flow
1250 \li \bold (H) Left to right \bold (V) Top to bottom
1251 \image gridview-layout-toptobottom-ltr-ttb.png
1252 \li \bold (H) Right to left \bold (V) Top to bottom
1253 \image gridview-layout-toptobottom-rtl-ttb.png
1254 \li \bold (H) Left to right \bold (V) Bottom to top
1255 \image gridview-layout-toptobottom-ltr-btt.png
1256 \li \bold (H) Right to left \bold (V) Bottom to top
1257 \image gridview-layout-toptobottom-rtl-btt.png
1260 \sa {quick/modelviews/gridview}{GridView example}
1263 QQuickGridView::QQuickGridView(QQuickItem *parent)
1264 : QQuickItemView(*(new QQuickGridViewPrivate), parent)
1268 QQuickGridView::~QQuickGridView()
1272 void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1274 Q_D(QQuickGridView);
1275 if (d->autoHighlight != autoHighlight) {
1276 if (!autoHighlight && d->highlightXAnimator) {
1277 d->highlightXAnimator->stop();
1278 d->highlightYAnimator->stop();
1280 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1285 \qmlattachedproperty bool QtQuick2::GridView::isCurrentItem
1286 This attached property is true if this delegate is the current item; otherwise false.
1288 It is attached to each instance of the delegate.
1292 \qmlattachedproperty GridView QtQuick2::GridView::view
1293 This attached property holds the view that manages this delegate instance.
1295 It is attached to each instance of the delegate.
1297 \snippet qml/gridview/gridview.qml isCurrentItem
1301 \qmlattachedproperty bool QtQuick2::GridView::delayRemove
1302 This attached property holds whether the delegate may be destroyed. It
1303 is attached to each instance of the delegate. The default value is false.
1305 It is sometimes necessary to delay the destruction of an item
1306 until an animation completes. The example delegate below ensures that the
1307 animation completes before the item is removed from the list.
1309 \snippet qml/gridview/gridview.qml delayRemove
1311 If a \l remove transition has been specified, it will not be applied until
1312 delayRemove is returned to \c false.
1316 \qmlattachedsignal QtQuick2::GridView::onAdd()
1317 This attached handler is called immediately after an item is added to the view.
1321 \qmlattachedsignal QtQuick2::GridView::onRemove()
1322 This attached handler is called immediately before an item is removed from the view.
1324 If a \l remove transition has been specified, it is applied after
1325 this signal handler is called, providing that delayRemove is false.
1330 \qmlproperty model QtQuick2::GridView::model
1331 This property holds the model providing data for the grid.
1333 The model provides the set of data that is used to create the items
1334 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1335 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1336 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1338 \sa {qmlmodels}{Data Models}
1342 \qmlproperty Component QtQuick2::GridView::delegate
1344 The delegate provides a template defining each item instantiated by the view.
1345 The index is exposed as an accessible \c index property. Properties of the
1346 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1348 The number of elements in the delegate has a direct effect on the
1349 flicking performance of the view. If at all possible, place functionality
1350 that is not needed for the normal display of the delegate in a \l Loader which
1351 can load additional elements when needed.
1353 The GridView will layout the items based on the size of the root item
1356 \note Delegates are instantiated as needed and may be destroyed at any time.
1357 State should \e never be stored in a delegate.
1361 \qmlproperty int QtQuick2::GridView::currentIndex
1362 \qmlproperty Item QtQuick2::GridView::currentItem
1364 The \c currentIndex property holds the index of the current item, and
1365 \c currentItem holds the current item. Setting the currentIndex to -1
1366 will clear the highlight and set currentItem to null.
1368 If highlightFollowsCurrentItem is \c true, setting either of these
1369 properties will smoothly scroll the GridView so that the current
1370 item becomes visible.
1372 Note that the position of the current item
1373 may only be approximate until it becomes visible in the view.
1378 \qmlproperty Item QtQuick2::GridView::highlightItem
1380 This holds the highlight item created from the \l highlight component.
1382 The highlightItem is managed by the view unless
1383 \l highlightFollowsCurrentItem is set to false.
1385 \sa highlight, highlightFollowsCurrentItem
1390 \qmlproperty int QtQuick2::GridView::count
1391 This property holds the number of items in the view.
1396 \qmlproperty Component QtQuick2::GridView::highlight
1397 This property holds the component to use as the highlight.
1399 An instance of the highlight component is created for each view.
1400 The geometry of the resulting component instance will be managed by the view
1401 so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1403 \sa highlightItem, highlightFollowsCurrentItem
1407 \qmlproperty bool QtQuick2::GridView::highlightFollowsCurrentItem
1408 This property sets whether the highlight is managed by the view.
1410 If this property is true (the default value), the highlight is moved smoothly
1411 to follow the current item. Otherwise, the
1412 highlight is not moved by the view, and any movement must be implemented
1415 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1417 \snippet qml/gridview/gridview.qml highlightFollowsCurrentItem
1422 \qmlproperty int QtQuick2::GridView::highlightMoveDuration
1423 This property holds the move animation duration of the highlight delegate.
1425 highlightFollowsCurrentItem must be true for this property
1428 The default value for the duration is 150ms.
1430 \sa highlightFollowsCurrentItem
1434 \qmlproperty real QtQuick2::GridView::preferredHighlightBegin
1435 \qmlproperty real QtQuick2::GridView::preferredHighlightEnd
1436 \qmlproperty enumeration QtQuick2::GridView::highlightRangeMode
1438 These properties define the preferred range of the highlight (for the current item)
1439 within the view. The \c preferredHighlightBegin value must be less than the
1440 \c preferredHighlightEnd value.
1442 These properties affect the position of the current item when the view is scrolled.
1443 For example, if the currently selected item should stay in the middle of the
1444 view when it is scrolled, set the \c preferredHighlightBegin and
1445 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1446 item would be. If the \c currentItem is changed programmatically, the view will
1447 automatically scroll so that the current item is in the middle of the view.
1448 Furthermore, the behavior of the current item index will occur whether or not a
1451 Valid values for \c highlightRangeMode are:
1454 \li GridView.ApplyRange - the view attempts to maintain the highlight within the range.
1455 However, the highlight can move outside of the range at the ends of the view or due
1456 to mouse interaction.
1457 \li GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
1458 The current item changes if a keyboard or mouse action would cause the highlight to move
1459 outside of the range.
1460 \li GridView.NoHighlightRange - this is the default value.
1466 \qmlproperty enumeration QtQuick2::GridView::layoutDirection
1467 This property holds the layout direction of the grid.
1472 \li Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
1473 dependent on the \l GridView::flow property.
1474 \li Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
1475 on the \l GridView::flow property.
1478 \b Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
1479 GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
1480 indicates that the flow is horizontal.
1482 \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
1487 \qmlproperty enumeration QtQuick2::GridView::effectiveLayoutDirection
1488 This property holds the effective layout direction of the grid.
1490 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1491 the visual layout direction of the grid will be mirrored. However, the
1492 property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1494 \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1498 \qmlproperty enumeration QtQuick2::GridView::verticalLayoutDirection
1499 This property holds the vertical layout direction of the grid.
1504 \li GridView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
1505 \li GridView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
1508 \sa GridView::layoutDirection
1512 \qmlproperty bool QtQuick2::GridView::keyNavigationWraps
1513 This property holds whether the grid wraps key navigation
1515 If this is true, key navigation that would move the current item selection
1516 past one end of the view instead wraps around and moves the selection to
1517 the other end of the view.
1519 By default, key navigation is not wrapped.
1522 \qmlproperty int QtQuick2::GridView::cacheBuffer
1523 This property determines whether delegates are retained outside the
1524 visible area of the view.
1526 If non-zero the view may keep as many delegates
1527 instantiated as will fit within the buffer specified. For example,
1528 if in a vertical view the delegate is 20 pixels high, there are 3
1529 columns and \c cacheBuffer is
1530 set to 40, then up to 6 delegates above and 6 delegates below the visible
1531 area may be created/retained. The buffered delegates are created asynchronously,
1532 allowing creation to occur across multiple frames and reducing the
1533 likelihood of skipping frames. In order to improve painting performance
1534 delegates outside the visible area are not painted.
1536 The default value of this property is platform dependent, but will usually
1537 be a non-zero value.
1539 Note that cacheBuffer is not a pixel buffer - it only maintains additional
1540 instantiated delegates.
1542 Setting this value can make scrolling the list smoother at the expense
1543 of additional memory usage. It is not a substitute for creating efficient
1544 delegates; the fewer elements in a delegate, the faster a view may be
1547 void QQuickGridView::setHighlightMoveDuration(int duration)
1549 Q_D(QQuickGridView);
1550 if (d->highlightMoveDuration != duration) {
1551 if (d->highlightYAnimator) {
1552 d->highlightXAnimator->userDuration = duration;
1553 d->highlightYAnimator->userDuration = duration;
1555 QQuickItemView::setHighlightMoveDuration(duration);
1560 \qmlproperty enumeration QtQuick2::GridView::flow
1561 This property holds the flow of the grid.
1566 \li GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
1567 \li GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
1570 QQuickGridView::Flow QQuickGridView::flow() const
1572 Q_D(const QQuickGridView);
1576 void QQuickGridView::setFlow(Flow flow)
1578 Q_D(QQuickGridView);
1579 if (d->flow != flow) {
1581 if (d->flow == FlowLeftToRight) {
1582 setContentWidth(-1);
1583 setFlickableDirection(VerticalFlick);
1585 setContentHeight(-1);
1586 setFlickableDirection(HorizontalFlick);
1597 \qmlproperty real QtQuick2::GridView::cellWidth
1598 \qmlproperty real QtQuick2::GridView::cellHeight
1600 These properties holds the width and height of each cell in the grid.
1602 The default cell size is 100x100.
1604 qreal QQuickGridView::cellWidth() const
1606 Q_D(const QQuickGridView);
1607 return d->cellWidth;
1610 void QQuickGridView::setCellWidth(qreal cellWidth)
1612 Q_D(QQuickGridView);
1613 if (cellWidth != d->cellWidth && cellWidth > 0) {
1614 d->cellWidth = qMax(qreal(1), cellWidth);
1615 d->updateViewport();
1616 emit cellWidthChanged();
1617 d->forceLayoutPolish();
1621 qreal QQuickGridView::cellHeight() const
1623 Q_D(const QQuickGridView);
1624 return d->cellHeight;
1627 void QQuickGridView::setCellHeight(qreal cellHeight)
1629 Q_D(QQuickGridView);
1630 if (cellHeight != d->cellHeight && cellHeight > 0) {
1631 d->cellHeight = qMax(qreal(1), cellHeight);
1632 d->updateViewport();
1633 emit cellHeightChanged();
1634 d->forceLayoutPolish();
1638 \qmlproperty enumeration QtQuick2::GridView::snapMode
1640 This property determines how the view scrolling will settle following a drag or flick.
1641 The possible values are:
1644 \li GridView.NoSnap (default) - the view stops anywhere within the visible area.
1645 \li GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
1646 aligned with the start of the view.
1647 \li GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
1648 away from the first visible row at the time the mouse button is released.
1649 This mode is particularly useful for moving one page at a time.
1653 QQuickGridView::SnapMode QQuickGridView::snapMode() const
1655 Q_D(const QQuickGridView);
1659 void QQuickGridView::setSnapMode(SnapMode mode)
1661 Q_D(QQuickGridView);
1662 if (d->snapMode != mode) {
1664 emit snapModeChanged();
1670 \qmlproperty Component QtQuick2::GridView::footer
1671 This property holds the component to use as the footer.
1673 An instance of the footer component is created for each view. The
1674 footer is positioned at the end of the view, after any items.
1676 \sa header, footerItem
1679 \qmlproperty Component QtQuick2::GridView::header
1680 This property holds the component to use as the header.
1682 An instance of the header component is created for each view. The
1683 header is positioned at the beginning of the view, before any items.
1685 \sa footer, headerItem
1689 \qmlproperty Item QtQuick2::GridView::headerItem
1690 This holds the header item created from the \l header component.
1692 An instance of the header component is created for each view. The
1693 header is positioned at the beginning of the view, before any items.
1695 \sa header, footerItem
1699 \qmlproperty Item QtQuick2::GridView::footerItem
1700 This holds the footer item created from the \l footer component.
1702 An instance of the footer component is created for each view. The
1703 footer is positioned at the end of the view, after any items.
1705 \sa footer, headerItem
1709 \qmlproperty Transition QtQuick2::GridView::populate
1711 This property holds the transition to apply to the items that are initially created
1714 It is applied to all items that are created when:
1717 \li The view is first created
1718 \li The view's \l model changes
1719 \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
1722 For example, here is a view that specifies such a transition:
1727 populate: Transition {
1728 NumberAnimation { properties: "x,y"; duration: 1000 }
1733 When the view is initialized, the view will create all the necessary items for the view,
1734 then animate them to their correct positions within the view over one second.
1736 For more details and examples on how to use view transitions, see the ViewTransition
1739 \sa add, ViewTransition
1743 \qmlproperty Transition QtQuick2::GridView::add
1745 This property holds the transition to apply to items that are added to the view.
1747 For example, here is a view that specifies such a transition:
1753 NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
1758 Whenever an item is added to the above view, the item will be animated from the position (100,100)
1759 to its final x,y position within the view, over one second. The transition only applies to
1760 the new items that are added to the view; it does not apply to the items below that are
1761 displaced by the addition of the new items. To animate the displaced items, set the \l displaced
1762 or \l addDisplaced properties.
1764 For more details and examples on how to use view transitions, see the ViewTransition
1767 \note This transition is not applied to the items that are created when the view is initially
1768 populated, or when the view's \l model changes. In those cases, the \l populate transition is
1771 \sa addDisplaced, populate, ViewTransition
1775 \qmlproperty Transition QtQuick2::GridView::addDisplaced
1777 This property holds the transition to apply to items within the view that are displaced by
1778 the addition of other items to the view.
1780 For example, here is a view that specifies such a transition:
1785 addDisplaced: Transition {
1786 NumberAnimation { properties: "x,y"; duration: 1000 }
1791 Whenever an item is added to the above view, all items beneath the new item are displaced, causing
1792 them to move down (or sideways, if horizontally orientated) within the view. As this
1793 displacement occurs, the items' movement to their new x,y positions within the view will be
1794 animated by a NumberAnimation over one second, as specified. This transition is not applied to
1795 the new item that has been added to the view; to animate the added items, set the \l add
1798 If an item is displaced by multiple types of operations at the same time, it is not defined as to
1799 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1800 if it is not necessary to specify different transitions depending on whether an item is displaced
1801 by an add, move or remove operation, consider setting the \l displaced property instead.
1803 For more details and examples on how to use view transitions, see the ViewTransition
1806 \note This transition is not applied to the items that are created when the view is initially
1807 populated, or when the view's \l model changes. In those cases, the \l populate transition is
1810 \sa displaced, add, populate, ViewTransition
1813 \qmlproperty Transition QtQuick2::GridView::move
1815 This property holds the transition to apply to items in the view that are being moved due
1816 to a move operation in the view's \l model.
1818 For example, here is a view that specifies such a transition:
1824 NumberAnimation { properties: "x,y"; duration: 1000 }
1829 Whenever the \l model performs a move operation to move a particular set of indexes, the
1830 respective items in the view will be animated to their new positions in the view over one
1831 second. The transition only applies to the items that are the subject of the move operation
1832 in the model; it does not apply to items below them that are displaced by the move operation.
1833 To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
1835 For more details and examples on how to use view transitions, see the ViewTransition
1838 \sa moveDisplaced, ViewTransition
1842 \qmlproperty Transition QtQuick2::GridView::moveDisplaced
1844 This property holds the transition to apply to items that are displaced by a move operation in
1845 the view's \l model.
1847 For example, here is a view that specifies such a transition:
1852 moveDisplaced: Transition {
1853 NumberAnimation { properties: "x,y"; duration: 1000 }
1858 Whenever the \l model performs a move operation to move a particular set of indexes, the items
1859 between the source and destination indexes of the move operation are displaced, causing them
1860 to move upwards or downwards (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 items that are the actual subjects of the move operation; to animate the moved items, set
1864 the \l move property.
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.
1871 For more details and examples on how to use view transitions, see the ViewTransition
1874 \sa displaced, move, ViewTransition
1878 \qmlproperty Transition QtQuick2::GridView::remove
1880 This property holds the transition to apply to items that are removed from the view.
1882 For example, here is a view that specifies such a transition:
1887 remove: Transition {
1889 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
1890 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
1896 Whenever an item is removed from the above view, the item will be animated to the position (100,100)
1897 over one second, and in parallel will also change its opacity to 0. The transition
1898 only applies to the items that are removed from the view; it does not apply to the items below
1899 them that are displaced by the removal of the items. To animate the displaced items, set the
1900 \l displaced or \l removeDisplaced properties.
1902 Note that by the time the transition is applied, the item has already been removed from the
1903 model; any references to the model data for the removed index will not be valid.
1905 Additionally, if the \l delayRemove attached property has been set for a delegate item, the
1906 remove transition will not be applied until \l delayRemove becomes false again.
1908 For more details and examples on how to use view transitions, see the ViewTransition
1911 \sa removeDisplaced, ViewTransition
1915 \qmlproperty Transition QtQuick2::GridView::removeDisplaced
1917 This property holds the transition to apply to items in the view that are displaced by the
1918 removal of other items in the view.
1920 For example, here is a view that specifies such a transition:
1925 removeDisplaced: Transition {
1926 NumberAnimation { properties: "x,y"; duration: 1000 }
1931 Whenever an item is removed from the above view, all items beneath it are displaced, causing
1932 them to move upwards (or sideways, if horizontally orientated) within the view. As this
1933 displacement occurs, the items' movement to their new x,y positions within the view will be
1934 animated by a NumberAnimation over one second, as specified. This transition is not applied to
1935 the item that has actually been removed from the view; to animate the removed items, set the
1938 If an item is displaced by multiple types of operations at the same time, it is not defined as to
1939 whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1940 if it is not necessary to specify different transitions depending on whether an item is displaced
1941 by an add, move or remove operation, consider setting the \l displaced property instead.
1943 For more details and examples on how to use view transitions, see the ViewTransition
1946 \sa displaced, remove, ViewTransition
1950 \qmlproperty Transition QtQuick2::GridView::displaced
1951 This property holds the generic transition to apply to items that have been displaced by
1952 any model operation that affects the view.
1954 This is a convenience for specifying a generic transition for items that are displaced
1955 by add, move or remove operations, without having to specify the individual addDisplaced,
1956 moveDisplaced and removeDisplaced properties. For example, here is a view that specifies
1957 a displaced transition:
1962 displaced: Transition {
1963 NumberAnimation { properties: "x,y"; duration: 1000 }
1968 When any item is added, moved or removed within the above view, the items below it are
1969 displaced, causing them to move down (or sideways, if horizontally orientated) within the
1970 view. As this displacement occurs, the items' movement to their new x,y positions within
1971 the view will be animated by a NumberAnimation over one second, as specified.
1973 If a view specifies this generic displaced transition as well as a specific addDisplaced,
1974 moveDisplaced or removeDisplaced transition, the more specific transition will be used
1975 instead of the generic displaced transition when the relevant operation occurs, providing that
1976 the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
1977 to false). If it has indeed been disabled, the generic displaced transition is applied instead.
1979 For more details and examples on how to use view transitions, see the ViewTransition
1982 \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
1985 void QQuickGridView::viewportMoved(Qt::Orientations orient)
1987 Q_D(QQuickGridView);
1988 QQuickItemView::viewportMoved(orient);
1991 if (d->inViewportMoved)
1993 d->inViewportMoved = true;
1996 if (d->isContentFlowReversed())
1997 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
1999 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2001 if (d->isContentFlowReversed())
2002 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
2004 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2007 d->refillOrLayout();
2009 // Set visibility of items to eliminate cost of items outside the visible area.
2010 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2011 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2012 for (int i = 0; i < d->visibleItems.count(); ++i) {
2013 FxGridItemSG *item = static_cast<FxGridItemSG*>(d->visibleItems.at(i));
2014 QQuickItemPrivate::get(item->item)->setCulled(item->rowPos() + d->rowSize() < from || item->rowPos() > to);
2016 if (d->currentItem) {
2017 FxGridItemSG *item = static_cast<FxGridItemSG*>(d->currentItem);
2018 QQuickItemPrivate::get(item->item)->setCulled(item->rowPos() + d->rowSize() < from || item->rowPos() > to);
2021 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2022 d->moveReason = QQuickGridViewPrivate::Mouse;
2023 if (d->moveReason != QQuickGridViewPrivate::SetIndex) {
2024 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2025 // reposition highlight
2026 qreal pos = d->highlight->position();
2027 qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2028 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2029 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2030 if (pos < viewPos + d->highlightRangeStart)
2031 pos = viewPos + d->highlightRangeStart;
2033 if (pos != d->highlight->position()) {
2034 d->highlightXAnimator->stop();
2035 d->highlightYAnimator->stop();
2036 static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
2038 d->updateHighlight();
2041 // update current index
2042 int idx = d->snapIndex();
2043 if (idx >= 0 && idx != d->currentIndex) {
2044 d->updateCurrent(idx);
2045 if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
2046 if (d->flow == FlowLeftToRight)
2047 d->highlightXAnimator->to = d->currentItem->itemX();
2049 d->highlightYAnimator->to = d->currentItem->itemY();
2055 d->inViewportMoved = false;
2058 void QQuickGridView::keyPressEvent(QKeyEvent *event)
2060 Q_D(QQuickGridView);
2061 if (d->model && d->model->count() && d->interactive) {
2062 d->moveReason = QQuickGridViewPrivate::SetIndex;
2063 int oldCurrent = currentIndex();
2064 switch (event->key()) {
2066 moveCurrentIndexUp();
2069 moveCurrentIndexDown();
2072 moveCurrentIndexLeft();
2075 moveCurrentIndexRight();
2080 if (oldCurrent != currentIndex() || d->wrap) {
2086 QQuickItemView::keyPressEvent(event);
2089 void QQuickGridView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2091 Q_D(QQuickGridView);
2094 if (newGeometry.width() != oldGeometry.width()
2095 && newGeometry.height() != oldGeometry.height()) {
2096 d->setPosition(d->position());
2097 } else if (newGeometry.width() != oldGeometry.width()) {
2098 QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
2099 } else if (newGeometry.height() != oldGeometry.height()) {
2100 QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
2103 QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2106 void QQuickGridView::initItem(int index, QQuickItem *item)
2108 QQuickItemView::initItem(index, item);
2109 QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
2110 qmlAttachedPropertiesObject<QQuickGridView>(item));
2112 attached->setView(this);
2116 \qmlmethod QtQuick2::GridView::moveCurrentIndexUp()
2118 Move the currentIndex up one item in the view.
2119 The current index will wrap if keyNavigationWraps is true and it
2120 is currently at the end. This method has no effect if the \l count is zero.
2122 \b Note: methods should only be called after the Component has completed.
2126 void QQuickGridView::moveCurrentIndexUp()
2128 Q_D(QQuickGridView);
2129 const int count = d->model ? d->model->count() : 0;
2132 if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2133 if (d->flow == QQuickGridView::FlowLeftToRight) {
2134 if (currentIndex() >= d->columns || d->wrap) {
2135 int index = currentIndex() - d->columns;
2136 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2139 if (currentIndex() > 0 || d->wrap) {
2140 int index = currentIndex() - 1;
2141 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2145 if (d->flow == QQuickGridView::FlowLeftToRight) {
2146 if (currentIndex() < count - d->columns || d->wrap) {
2147 int index = currentIndex()+d->columns;
2148 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2151 if (currentIndex() < count - 1 || d->wrap) {
2152 int index = currentIndex() + 1;
2153 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2160 \qmlmethod QtQuick2::GridView::moveCurrentIndexDown()
2162 Move the currentIndex down one item in the view.
2163 The current index will wrap if keyNavigationWraps is true and it
2164 is currently at the end. This method has no effect if the \l count is zero.
2166 \b Note: methods should only be called after the Component has completed.
2168 void QQuickGridView::moveCurrentIndexDown()
2170 Q_D(QQuickGridView);
2171 const int count = d->model ? d->model->count() : 0;
2175 if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2176 if (d->flow == QQuickGridView::FlowLeftToRight) {
2177 if (currentIndex() < count - d->columns || d->wrap) {
2178 int index = currentIndex()+d->columns;
2179 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2182 if (currentIndex() < count - 1 || d->wrap) {
2183 int index = currentIndex() + 1;
2184 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2188 if (d->flow == QQuickGridView::FlowLeftToRight) {
2189 if (currentIndex() >= d->columns || d->wrap) {
2190 int index = currentIndex() - d->columns;
2191 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2194 if (currentIndex() > 0 || d->wrap) {
2195 int index = currentIndex() - 1;
2196 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2203 \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft()
2205 Move the currentIndex left one item in the view.
2206 The current index will wrap if keyNavigationWraps is true and it
2207 is currently at the end. This method has no effect if the \l count is zero.
2209 \b Note: methods should only be called after the Component has completed.
2211 void QQuickGridView::moveCurrentIndexLeft()
2213 Q_D(QQuickGridView);
2214 const int count = d->model ? d->model->count() : 0;
2217 if (effectiveLayoutDirection() == Qt::LeftToRight) {
2218 if (d->flow == QQuickGridView::FlowLeftToRight) {
2219 if (currentIndex() > 0 || d->wrap) {
2220 int index = currentIndex() - 1;
2221 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2224 if (currentIndex() >= d->columns || d->wrap) {
2225 int index = currentIndex() - d->columns;
2226 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2230 if (d->flow == QQuickGridView::FlowLeftToRight) {
2231 if (currentIndex() < count - 1 || d->wrap) {
2232 int index = currentIndex() + 1;
2233 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2236 if (currentIndex() < count - d->columns || d->wrap) {
2237 int index = currentIndex() + d->columns;
2238 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2246 \qmlmethod QtQuick2::GridView::moveCurrentIndexRight()
2248 Move the currentIndex right one item in the view.
2249 The current index will wrap if keyNavigationWraps is true and it
2250 is currently at the end. This method has no effect if the \l count is zero.
2252 \b Note: methods should only be called after the Component has completed.
2254 void QQuickGridView::moveCurrentIndexRight()
2256 Q_D(QQuickGridView);
2257 const int count = d->model ? d->model->count() : 0;
2260 if (effectiveLayoutDirection() == Qt::LeftToRight) {
2261 if (d->flow == QQuickGridView::FlowLeftToRight) {
2262 if (currentIndex() < count - 1 || d->wrap) {
2263 int index = currentIndex() + 1;
2264 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2267 if (currentIndex() < count - d->columns || d->wrap) {
2268 int index = currentIndex()+d->columns;
2269 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2273 if (d->flow == QQuickGridView::FlowLeftToRight) {
2274 if (currentIndex() > 0 || d->wrap) {
2275 int index = currentIndex() - 1;
2276 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2279 if (currentIndex() >= d->columns || d->wrap) {
2280 int index = currentIndex() - d->columns;
2281 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2287 bool QQuickGridViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2289 Q_Q(QQuickGridView);
2291 int modelIndex = change.index;
2292 int count = change.count;
2294 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2297 int i = visibleItems.count() - 1;
2298 while (i > 0 && visibleItems.at(i)->index == -1)
2300 if (visibleItems.at(i)->index + 1 == modelIndex) {
2301 // Special case of appending an item to the model.
2302 index = visibleItems.count();
2304 if (modelIndex <= visibleIndex) {
2305 // Insert before visible items
2306 visibleIndex += count;
2307 for (int i = 0; i < visibleItems.count(); ++i) {
2308 FxViewItem *item = visibleItems.at(i);
2309 if (item->index != -1 && item->index >= modelIndex)
2310 item->index += count;
2317 qreal tempPos = isContentFlowReversed() ? -position()-size()+q->width()+1 : position();
2321 if (visibleItems.count()) {
2322 if (index < visibleItems.count()) {
2323 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index));
2324 colPos = gridItem->colPos();
2325 rowPos = gridItem->rowPos();
2326 colNum = qFloor((colPos+colSize()/2) / colSize());
2328 // appending items to visible list
2329 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index-1));
2330 rowPos = gridItem->rowPos();
2331 colNum = qFloor((gridItem->colPos()+colSize()/2) / colSize());
2332 if (++colNum >= columns) {
2334 rowPos += rowSize();
2336 colPos = colNum * colSize();
2340 // Update the indexes of the following visible items.
2341 for (int i = 0; i < visibleItems.count(); ++i) {
2342 FxViewItem *item = visibleItems.at(i);
2343 if (item->index != -1 && item->index >= modelIndex) {
2344 item->index += count;
2345 if (change.isMove())
2346 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2348 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2352 int prevVisibleCount = visibleItems.count();
2353 if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
2354 // Insert items before the visible item.
2355 int insertionIdx = index;
2357 int from = tempPos - buffer;
2360 if (rowPos > from && insertionIdx < visibleIndex) {
2361 // item won't be visible, just note the size for repositioning
2362 insertResult->countChangeBeforeVisible++;
2364 // item is before first visible e.g. in cache buffer
2365 FxViewItem *item = 0;
2366 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2367 item->index = modelIndex + i;
2369 item = createItem(modelIndex + i);
2373 QQuickItemPrivate::get(item->item)->setCulled(false);
2374 visibleItems.insert(insertionIdx, item);
2375 if (insertionIdx == 0)
2376 insertResult->changedFirstItem = true;
2377 if (!change.isMove()) {
2378 addedItems->append(item);
2379 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2381 insertResult->sizeChangesBeforeVisiblePos += rowSize();
2384 if (--colNum < 0 ) {
2385 colNum = columns - 1;
2386 rowPos -= rowSize();
2388 colPos = colNum * colSize();
2394 int to = buffer+tempPos+size()-1;
2395 while (i < count && rowPos <= to + rowSize()*(columns - colNum)/qreal(columns+1)) {
2396 FxViewItem *item = 0;
2397 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2398 item->index = modelIndex + i;
2399 bool newItem = !item;
2401 item = createItem(modelIndex + i);
2405 QQuickItemPrivate::get(item->item)->setCulled(false);
2406 visibleItems.insert(index, item);
2408 insertResult->changedFirstItem = true;
2409 if (change.isMove()) {
2410 // we know this is a move target, since move displaced items that are
2411 // shuffled into view due to a move would be added in refill()
2412 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2413 movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2415 addedItems->append(item);
2416 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2418 insertResult->sizeChangesAfterVisiblePos += rowSize();
2420 if (++colNum >= columns) {
2422 rowPos += rowSize();
2424 colPos = colNum * colSize();
2430 updateVisibleIndex();
2432 return visibleItems.count() > prevVisibleCount;
2435 void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2440 int markerItemIndex = -1;
2441 for (int i=0; i<visibleItems.count(); i++) {
2442 if (visibleItems[i]->index == afterModelIndex) {
2443 markerItemIndex = i;
2447 if (markerItemIndex < 0)
2450 const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2451 int countItemsRemoved = -(removalResult.sizeChangesAfterVisiblePos / rowSize());
2453 // account for whether first item has changed if < 1 row was removed before visible
2454 int changeBeforeVisible = insertionResult.countChangeBeforeVisible - removalResult.countChangeBeforeVisible;
2455 if (changeBeforeVisible != 0)
2456 countItemsRemoved += (changeBeforeVisible % columns) - (columns - 1);
2458 countItemsRemoved -= removalResult.countChangeAfterVisibleItems;
2460 for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2461 FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems[i]);
2462 if (!gridItem->transitionScheduledOrRunning()) {
2463 qreal origRowPos = gridItem->colPos();
2464 qreal origColPos = gridItem->rowPos();
2465 int indexDiff = gridItem->index - countItemsRemoved;
2466 gridItem->setPosition((indexDiff % columns) * colSize(), (indexDiff / columns) * rowSize());
2467 gridItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2468 gridItem->setPosition(origRowPos, origColPos);
2473 bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
2475 // If we add or remove items before visible items, a layout may be
2476 // required to ensure item 0 is in the first column.
2477 return modelIndex < visibleIndex;
2481 \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode)
2483 Positions the view such that the \a index is at the position specified by
2487 \li GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
2488 \li GridView.Center - position item in the center of the view.
2489 \li GridView.End - position item at bottom (or right for horizontal orientation) of the view.
2490 \li GridView.Visible - if any part of the item is visible then take no action, otherwise
2491 bring the item into view.
2492 \li GridView.Contain - ensure the entire item is visible. If the item is larger than
2493 the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
2496 If positioning the view at the index would cause empty space to be displayed at
2497 the beginning or end of the view, the view will be positioned at the boundary.
2499 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2500 at a particular index. This is unreliable since removing items from the start
2501 of the view does not cause all other items to be repositioned.
2502 The correct way to bring an item into view is with \c positionViewAtIndex.
2504 \b Note: methods should only be called after the Component has completed. To position
2505 the view at startup, this method should be called by Component.onCompleted. For
2506 example, to position the view at the end:
2509 Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
2514 \qmlmethod QtQuick2::GridView::positionViewAtBeginning()
2515 \qmlmethod QtQuick2::GridView::positionViewAtEnd()
2517 Positions the view at the beginning or end, taking into account any header or footer.
2519 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2520 at a particular index. This is unreliable since removing items from the start
2521 of the list does not cause all other items to be repositioned, and because
2522 the actual start of the view can vary based on the size of the delegates.
2524 \b Note: methods should only be called after the Component has completed. To position
2525 the view at startup, this method should be called by Component.onCompleted. For
2526 example, to position the view at the end on startup:
2529 Component.onCompleted: positionViewAtEnd()
2534 \qmlmethod int QtQuick2::GridView::indexAt(int x, int y)
2536 Returns the index of the visible item containing the point \a x, \a y in content
2537 coordinates. If there is no item at the point specified, or the item is
2538 not visible -1 is returned.
2540 If the item is outside the visible area, -1 is returned, regardless of
2541 whether an item will exist at that point when scrolled into view.
2543 \b Note: methods should only be called after the Component has completed.
2547 \qmlmethod Item QtQuick2::GridView::itemAt(int x, int y)
2549 Returns the visible item containing the point \a x, \a y in content
2550 coordinates. If there is no item at the point specified, or the item is
2551 not visible null is returned.
2553 If the item is outside the visible area, null is returned, regardless of
2554 whether an item will exist at that point when scrolled into view.
2556 \b Note: methods should only be called after the Component has completed.
2559 QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj)
2561 return new QQuickGridViewAttached(obj);