1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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/qdeclarativesmoothedanimation_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) : FxViewItem(i, own), view(v) {
70 attached = static_cast<QQuickGridViewAttached*>(qmlAttachedPropertiesObject<QQuickGridView>(item));
72 static_cast<QQuickGridViewAttached*>(attached)->setView(view);
77 qreal position() const {
81 qreal endPosition() const {
86 return view->flow() == QQuickGridView::LeftToRight ? view->cellHeight() : view->cellWidth();
89 qreal sectionSize() const {
93 qreal rowPos() const {
94 if (view->flow() == QQuickGridView::LeftToRight)
97 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-item->x() : item->x());
100 qreal colPos() const {
101 if (view->flow() == QQuickGridView::LeftToRight) {
102 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
103 qreal colSize = view->cellWidth();
104 int columns = view->width()/colSize;
105 return colSize * (columns-1) - item->x();
113 qreal endRowPos() const {
114 if (view->flow() == QQuickGridView::LeftToRight) {
115 return item->y() + view->cellHeight();
117 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
120 return item->x() + view->cellWidth();
123 void setPosition(qreal col, qreal row) {
124 if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
125 if (view->flow() == QQuickGridView::LeftToRight) {
126 int columns = view->width()/view->cellWidth();
127 item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row));
129 item->setPos(QPointF(-view->cellWidth()-row, col));
132 if (view->flow() == QQuickGridView::LeftToRight)
133 item->setPos(QPointF(col, row));
135 item->setPos(QPointF(row, col));
138 bool contains(qreal x, qreal y) const {
139 return (x >= item->x() && x < item->x() + view->cellWidth() &&
140 y >= item->y() && y < item->y() + view->cellHeight());
143 QQuickGridView *view;
146 //----------------------------------------------------------------------------
148 class QQuickGridViewPrivate : public QQuickItemViewPrivate
150 Q_DECLARE_PUBLIC(QQuickGridView)
153 virtual Qt::Orientation layoutOrientation() const;
154 virtual bool isContentFlowReversed() const;
155 bool isRightToLeftTopToBottom() const;
157 virtual qreal positionAt(int index) const;
158 virtual qreal endPositionAt(int index) const;
159 virtual qreal originPosition() const;
160 virtual qreal lastPosition() const;
162 qreal rowSize() const;
163 qreal colSize() const;
164 qreal colPosAt(int modelIndex) const;
165 qreal rowPosAt(int modelIndex) const;
166 qreal snapPosAt(qreal pos) const;
167 FxViewItem *snapItemAt(qreal pos) const;
168 int snapIndex() const;
170 virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
171 virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
172 virtual void visibleItemsChanged();
174 virtual FxViewItem *newViewItem(int index, QQuickItem *item);
175 virtual void repositionPackageItemAt(QQuickItem *item, int index);
176 virtual void resetFirstItemPosition(qreal pos = 0.0);
177 virtual void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible);
179 virtual void createHighlight();
180 virtual void updateHighlight();
181 virtual void resetHighlightPosition();
183 virtual void setPosition(qreal pos);
184 virtual void layoutVisibleItems(int fromModelIndex = 0);
185 virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems);
186 virtual bool needsRefillForAddedOrRemovedIndex(int index) const;
188 virtual qreal headerSize() const;
189 virtual qreal footerSize() const;
190 virtual bool showHeaderForIndex(int index) const;
191 virtual bool showFooterForIndex(int index) const;
192 virtual void updateHeader();
193 virtual void updateFooter();
195 virtual void changedVisibleIndex(int newIndex);
196 virtual void initializeCurrentItem();
198 virtual void updateViewport();
199 virtual void fixupPosition();
200 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
201 virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
202 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
204 QQuickGridView::Flow flow;
208 QQuickGridView::SnapMode snapMode;
210 QSmoothedAnimation *highlightXAnimator;
211 QSmoothedAnimation *highlightYAnimator;
213 QQuickGridViewPrivate()
214 : flow(QQuickGridView::LeftToRight)
215 , cellWidth(100), cellHeight(100), columns(1)
216 , snapMode(QQuickGridView::NoSnap)
217 , highlightXAnimator(0), highlightYAnimator(0)
221 Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const
223 return flow == QQuickGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal;
226 bool QQuickGridViewPrivate::isContentFlowReversed() const
228 return isRightToLeftTopToBottom();
231 bool QQuickGridViewPrivate::isRightToLeftTopToBottom() const
233 Q_Q(const QQuickGridView);
234 return flow == QQuickGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft;
237 void QQuickGridViewPrivate::changedVisibleIndex(int newIndex)
239 visibleIndex = newIndex / columns * columns;
242 void QQuickGridViewPrivate::setPosition(qreal pos)
245 if (flow == QQuickGridView::LeftToRight) {
246 q->QQuickFlickable::setContentY(pos);
247 q->QQuickFlickable::setContentX(0);
249 if (q->effectiveLayoutDirection() == Qt::LeftToRight)
250 q->QQuickFlickable::setContentX(pos);
252 q->QQuickFlickable::setContentX(-pos-size());
253 q->QQuickFlickable::setContentY(0);
257 qreal QQuickGridViewPrivate::originPosition() const
260 if (!visibleItems.isEmpty())
261 pos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
265 qreal QQuickGridViewPrivate::lastPosition() const
268 if (model && model->count()) {
269 // get end position of last item
270 pos = (rowPosAt(model->count() - 1) + rowSize());
275 qreal QQuickGridViewPrivate::positionAt(int index) const
277 return rowPosAt(index);
280 qreal QQuickGridViewPrivate::endPositionAt(int index) const
282 return rowPosAt(index) + rowSize();
285 qreal QQuickGridViewPrivate::rowSize() const {
286 return flow == QQuickGridView::LeftToRight ? cellHeight : cellWidth;
288 qreal QQuickGridViewPrivate::colSize() const {
289 return flow == QQuickGridView::LeftToRight ? cellWidth : cellHeight;
292 qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const
294 if (FxViewItem *item = visibleItem(modelIndex))
295 return static_cast<FxGridItemSG*>(item)->colPos();
296 if (!visibleItems.isEmpty()) {
297 if (modelIndex == visibleIndex) {
298 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
299 return firstItem->colPos();
300 } else if (modelIndex < visibleIndex) {
301 int count = (visibleIndex - modelIndex) % columns;
302 int col = static_cast<FxGridItemSG*>(visibleItems.first())->colPos() / colSize();
303 col = (columns - count + col) % columns;
304 return col * colSize();
306 int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns;
307 return static_cast<FxGridItemSG*>(visibleItems.last())->colPos() - count * colSize();
310 return (modelIndex % columns) * colSize();
313 qreal QQuickGridViewPrivate::rowPosAt(int modelIndex) const
315 if (FxViewItem *item = visibleItem(modelIndex))
316 return static_cast<FxGridItemSG*>(item)->rowPos();
317 if (!visibleItems.isEmpty()) {
318 if (modelIndex == visibleIndex) {
319 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
320 return firstItem->rowPos();
321 } else if (modelIndex < visibleIndex) {
322 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
323 int firstCol = firstItem->colPos() / colSize();
324 int col = visibleIndex - modelIndex + (columns - firstCol - 1);
325 int rows = col / columns;
326 return firstItem->rowPos() - rows * rowSize();
328 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
329 int count = modelIndex - lastItem->index;
330 int col = lastItem->colPos() + count * colSize();
331 int rows = col / (columns * colSize());
332 return lastItem->rowPos() + rows * rowSize();
335 return (modelIndex / columns) * rowSize();
339 qreal QQuickGridViewPrivate::snapPosAt(qreal pos) const
341 Q_Q(const QQuickGridView);
343 if (!visibleItems.isEmpty()) {
344 qreal highlightStart = highlightRangeStart;
345 pos += highlightStart;
347 snapPos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
348 snapPos = pos - fmodf(pos - snapPos, qreal(rowSize()));
349 snapPos -= highlightStart;
352 if (isRightToLeftTopToBottom()) {
353 maxExtent = q->minXExtent()-size();
354 minExtent = q->maxXExtent()-size();
356 maxExtent = flow == QQuickGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent();
357 minExtent = flow == QQuickGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent();
359 if (snapPos > maxExtent)
361 if (snapPos < minExtent)
367 FxViewItem *QQuickGridViewPrivate::snapItemAt(qreal pos) const
369 for (int i = 0; i < visibleItems.count(); ++i) {
370 FxViewItem *item = visibleItems.at(i);
371 if (item->index == -1)
373 qreal itemTop = item->position();
374 if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
380 int QQuickGridViewPrivate::snapIndex() const
382 int index = currentIndex;
383 for (int i = 0; i < visibleItems.count(); ++i) {
384 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
385 if (item->index == -1)
387 qreal itemTop = item->position();
388 FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight);
389 if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) {
391 if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2)
398 FxViewItem *QQuickGridViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
401 Q_UNUSED(modelIndex);
402 return new FxGridItemSG(item, q, false);
405 bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
407 qreal colPos = colPosAt(visibleIndex);
408 qreal rowPos = rowPosAt(visibleIndex);
409 if (visibleItems.count()) {
410 FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
411 rowPos = lastItem->rowPos();
412 int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize());
413 if (++colNum >= columns) {
417 colPos = colNum * colSize();
420 int modelIndex = findLastVisibleIndex();
421 modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
423 if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2
424 || fillTo < rowPosAt(visibleIndex) - rowSize())) {
425 // We've jumped more than a page. Estimate which items are now
426 // visible and fill from there.
427 int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
428 for (int i = 0; i < visibleItems.count(); ++i)
429 releaseItem(visibleItems.at(i));
430 visibleItems.clear();
432 if (modelIndex >= model->count())
433 modelIndex = model->count() - 1;
434 else if (modelIndex < 0)
436 modelIndex = modelIndex / columns * columns;
437 visibleIndex = modelIndex;
438 colPos = colPosAt(visibleIndex);
439 rowPos = rowPosAt(visibleIndex);
442 int colNum = qFloor((colPos+colSize()/2) / colSize());
443 FxGridItemSG *item = 0;
444 bool changed = false;
446 while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
447 #ifdef DEBUG_DELEGATE_LIFECYCLE
448 qDebug() << "refill: append item" << modelIndex << colPos << rowPos;
450 if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex, doBuffer))))
452 item->setPosition(colPos, rowPos);
453 item->item->setVisible(!doBuffer);
454 visibleItems.append(item);
455 if (++colNum >= columns) {
459 colPos = colNum * colSize();
464 if (doBuffer && requestedIndex != -1) // already waiting for an item
468 if (visibleItems.count()) {
469 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
470 rowPos = firstItem->rowPos();
471 colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize());
473 colNum = columns - 1;
477 colNum = qFloor((colPos+colSize()/2) / colSize());
481 colPos = colNum * colSize();
482 while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
483 #ifdef DEBUG_DELEGATE_LIFECYCLE
484 qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
486 if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1, doBuffer))))
489 item->setPosition(colPos, rowPos);
490 item->item->setVisible(!doBuffer);
491 visibleItems.prepend(item);
496 colPos = colNum * colSize();
503 bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
505 FxGridItemSG *item = 0;
506 bool changed = false;
508 while (visibleItems.count() > 1
509 && (item = static_cast<FxGridItemSG*>(visibleItems.first()))
510 && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
511 if (item->attached->delayRemove())
513 #ifdef DEBUG_DELEGATE_LIFECYCLE
514 qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
516 if (item->index != -1)
518 visibleItems.removeFirst();
522 while (visibleItems.count() > 1
523 && (item = static_cast<FxGridItemSG*>(visibleItems.last()))
524 && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
525 if (item->attached->delayRemove())
527 #ifdef DEBUG_DELEGATE_LIFECYCLE
528 qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1;
530 visibleItems.removeLast();
538 void QQuickGridViewPrivate::visibleItemsChanged()
545 void QQuickGridViewPrivate::updateViewport()
548 qreal length = flow == QQuickGridView::LeftToRight ? q->width() : q->height();
549 columns = (int)qMax((length + colSize()/2) / colSize(), qreal(1.));
550 QQuickItemViewPrivate::updateViewport();
553 void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex)
555 if (visibleItems.count()) {
556 const qreal from = isContentFlowReversed() ? -position() - size() : position();
557 const qreal to = isContentFlowReversed() ? -position() : position() + size();
559 FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
560 qreal rowPos = firstItem->rowPos();
561 qreal colPos = firstItem->colPos();
562 int col = visibleIndex % columns;
563 if (colPos != col * colSize()) {
564 colPos = col * colSize();
565 firstItem->setPosition(colPos, rowPos);
566 firstItem->item->setVisible(rowPos + rowSize() >= from && rowPos <= to);
568 for (int i = 1; i < visibleItems.count(); ++i) {
569 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
570 if (++col >= columns) {
574 colPos = col * colSize();
575 if (item->index >= fromModelIndex) {
576 item->setPosition(colPos, rowPos);
577 item->item->setVisible(rowPos + rowSize() >= from && rowPos <= to);
583 void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
586 qreal pos = position();
587 if (flow == QQuickGridView::LeftToRight) {
588 if (item->y() + item->height() > pos && item->y() < pos + q->height())
589 item->setPos(QPointF(colPosAt(index), rowPosAt(index)));
591 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
592 if (isRightToLeftTopToBottom())
593 item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index)));
595 item->setPos(QPointF(rowPosAt(index), colPosAt(index)));
600 void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos)
602 FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.first());
603 item->setPosition(0, pos);
606 void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible)
608 if (!visibleItems.count())
611 int moveCount = (forwards - backwards) / rowSize();
613 if (changeBeforeVisible)
614 moveCount += (changeBeforeVisible%columns) - (columns - 1);
616 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first());
617 gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
620 void QQuickGridViewPrivate::createHighlight()
623 bool changed = false;
625 if (trackedItem == highlight)
630 delete highlightXAnimator;
631 delete highlightYAnimator;
632 highlightXAnimator = 0;
633 highlightYAnimator = 0;
639 QQuickItem *item = createHighlightItem();
641 FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true);
643 resetHighlightPosition();
644 highlightXAnimator = new QSmoothedAnimation(q);
645 highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x"));
646 highlightXAnimator->userDuration = highlightMoveDuration;
647 highlightYAnimator = new QSmoothedAnimation(q);
648 highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y"));
649 highlightYAnimator->userDuration = highlightMoveDuration;
651 highlight = newHighlight;
656 emit q->highlightItemChanged();
659 void QQuickGridViewPrivate::updateHighlight()
661 applyPendingChanges();
663 if ((!currentItem && highlight) || (currentItem && !highlight))
665 bool strictHighlight = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
666 if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
667 // auto-update highlight
668 highlightXAnimator->to = currentItem->item->x();
669 highlightYAnimator->to = currentItem->item->y();
670 highlight->item->setWidth(currentItem->item->width());
671 highlight->item->setHeight(currentItem->item->height());
673 highlightXAnimator->restart();
674 highlightYAnimator->restart();
679 void QQuickGridViewPrivate::resetHighlightPosition()
681 if (highlight && currentItem) {
682 FxGridItemSG *cItem = static_cast<FxGridItemSG*>(currentItem);
683 static_cast<FxGridItemSG*>(highlight)->setPosition(cItem->colPos(), cItem->rowPos());
687 qreal QQuickGridViewPrivate::headerSize() const
691 return flow == QQuickGridView::LeftToRight ? header->item->height() : header->item->width();
694 qreal QQuickGridViewPrivate::footerSize() const
698 return flow == QQuickGridView::LeftToRight? footer->item->height() : footer->item->width();
701 bool QQuickGridViewPrivate::showHeaderForIndex(int index) const
703 return index / columns == 0;
706 bool QQuickGridViewPrivate::showFooterForIndex(int index) const
708 return index / columns == (model->count()-1) / columns;
711 void QQuickGridViewPrivate::updateFooter()
714 bool created = false;
716 QQuickItem *item = createComponentItem(footerComponent, true);
720 footer = new FxGridItemSG(item, q, true);
724 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(footer);
727 if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
728 if (flow == QQuickGridView::TopToBottom)
729 rowOffset = gridItem->item->width() - cellWidth;
731 colOffset = gridItem->item->width() - cellWidth;
733 if (visibleItems.count()) {
734 qreal endPos = lastPosition();
735 if (findLastVisibleIndex() == model->count()-1) {
736 gridItem->setPosition(colOffset, endPos + rowOffset);
738 qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size();
739 if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset)
740 gridItem->setPosition(colOffset, endPos + rowOffset);
743 gridItem->setPosition(colOffset, rowOffset);
747 emit q->footerItemChanged();
750 void QQuickGridViewPrivate::updateHeader()
753 bool created = false;
755 QQuickItem *item = createComponentItem(headerComponent, true);
759 header = new FxGridItemSG(item, q, true);
763 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(header);
765 qreal rowOffset = -headerSize();
766 if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
767 if (flow == QQuickGridView::TopToBottom)
768 rowOffset += gridItem->item->width()-cellWidth;
770 colOffset = gridItem->item->width()-cellWidth;
772 if (visibleItems.count()) {
773 qreal startPos = originPosition();
774 if (visibleIndex == 0) {
775 gridItem->setPosition(colOffset, startPos + rowOffset);
777 qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position();
778 qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
779 if (tempPos <= startPos || headerPos > startPos + rowOffset)
780 gridItem->setPosition(colOffset, startPos + rowOffset);
783 if (isRightToLeftTopToBottom())
784 gridItem->setPosition(colOffset, rowOffset);
786 gridItem->setPosition(colOffset, -headerSize());
790 emit q->headerItemChanged();
793 void QQuickGridViewPrivate::initializeCurrentItem()
795 if (currentItem && currentIndex >= 0) {
796 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(currentItem);
797 gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
801 void QQuickGridViewPrivate::fixupPosition()
804 if (flow == QQuickGridView::LeftToRight)
810 void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
812 if ((flow == QQuickGridView::TopToBottom && &data == &vData)
813 || (flow == QQuickGridView::LeftToRight && &data == &hData))
816 fixupMode = moveReason == Mouse ? fixupMode : Immediate;
818 qreal viewPos = isRightToLeftTopToBottom() ? -position()-size() : position();
820 bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
821 if (snapMode != QQuickGridView::NoSnap) {
822 qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position();
823 if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
824 // if we've been dragged < rowSize()/2 then bias towards the next row
825 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
827 if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
829 else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2)
831 if (isRightToLeftTopToBottom())
833 tempPosition -= bias;
835 FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
836 if (!topItem && strictHighlightRange && currentItem) {
837 // StrictlyEnforceRange always keeps an item in range
839 topItem = currentItem;
841 FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
842 if (!bottomItem && strictHighlightRange && currentItem) {
843 // StrictlyEnforceRange always keeps an item in range
845 bottomItem = currentItem;
848 bool isInBounds = -position() > maxExtent && -position() <= minExtent;
849 if (topItem && (isInBounds || strictHighlightRange)) {
850 qreal headerPos = header ? static_cast<FxGridItemSG*>(header)->rowPos() : 0;
851 if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) {
852 pos = isRightToLeftTopToBottom() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart;
854 if (isRightToLeftTopToBottom())
855 pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
857 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
859 } else if (bottomItem && isInBounds) {
860 if (isRightToLeftTopToBottom())
861 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
863 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
865 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
869 qreal dist = qAbs(data.move + pos);
871 timeline.reset(data.move);
872 if (fixupMode != Immediate) {
873 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
874 data.fixingUp = true;
876 timeline.set(data.move, -pos);
878 vTime = timeline.time();
880 } else if (haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange) {
883 qreal pos = static_cast<FxGridItemSG*>(currentItem)->rowPos();
884 if (viewPos < pos + rowSize() - highlightRangeEnd)
885 viewPos = pos + rowSize() - highlightRangeEnd;
886 if (viewPos > pos - highlightRangeStart)
887 viewPos = pos - highlightRangeStart;
888 if (isRightToLeftTopToBottom())
889 viewPos = -viewPos-size();
890 timeline.reset(data.move);
891 if (viewPos != position()) {
892 if (fixupMode != Immediate) {
893 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
894 data.fixingUp = true;
896 timeline.set(data.move, -viewPos);
899 vTime = timeline.time();
902 QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
904 data.inOvershoot = false;
908 void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
909 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
912 data.fixingUp = false;
914 if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange)
915 && snapMode == QQuickGridView::NoSnap) {
916 QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
919 qreal maxDistance = 0;
920 qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value();
921 // -ve velocity means list is moving up/left
923 if (data.move.value() < minExtent) {
924 if (snapMode == QQuickGridView::SnapOneRow) {
925 // if we've been dragged < averageSize/2 then bias towards the next item
926 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
927 qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
928 if (isRightToLeftTopToBottom())
930 data.flickTarget = -snapPosAt(-dataValue - bias);
931 maxDistance = qAbs(data.flickTarget - data.move.value());
932 velocity = maxVelocity;
934 maxDistance = qAbs(minExtent - data.move.value());
937 if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
938 data.flickTarget = minExtent;
940 if (data.move.value() > maxExtent) {
941 if (snapMode == QQuickGridView::SnapOneRow) {
942 // if we've been dragged < averageSize/2 then bias towards the next item
943 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
944 qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
945 if (isRightToLeftTopToBottom())
947 data.flickTarget = -snapPosAt(-dataValue + bias);
948 maxDistance = qAbs(data.flickTarget - data.move.value());
949 velocity = -maxVelocity;
951 maxDistance = qAbs(maxExtent - data.move.value());
954 if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
955 data.flickTarget = maxExtent;
957 bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
958 if (maxDistance > 0 || overShoot) {
959 // This mode requires the grid to stop exactly on a row boundary.
961 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
967 qreal accel = deceleration;
969 qreal overshootDist = 0.0;
970 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) {
971 // + rowSize()/4 to encourage moving at least one item in the flick direction
972 qreal dist = v2 / (accel * 2.0) + rowSize()/4;
973 dist = qMin(dist, maxDistance);
976 if (snapMode != QQuickGridView::SnapOneRow) {
977 qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist;
978 data.flickTarget = -snapPosAt(-dataValue + distTemp);
980 data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget;
982 if (data.flickTarget >= minExtent) {
983 overshootDist = overShootDistance(vSize);
984 data.flickTarget += overshootDist;
985 } else if (data.flickTarget <= maxExtent) {
986 overshootDist = overShootDistance(vSize);
987 data.flickTarget -= overshootDist;
990 qreal adjDist = -data.flickTarget + data.move.value();
991 if (qAbs(adjDist) > qAbs(dist)) {
992 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
993 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1002 accel = v2 / (2.0f * qAbs(dist));
1004 data.flickTarget = velocity > 0 ? minExtent : maxExtent;
1005 overshootDist = overShoot ? overShootDistance(vSize) : 0;
1007 timeline.reset(data.move);
1008 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1009 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1010 if (!hData.flicking && q->xflick()) {
1011 hData.flicking = true;
1012 emit q->flickingChanged();
1013 emit q->flickingHorizontallyChanged();
1014 emit q->flickStarted();
1016 if (!vData.flicking && q->yflick()) {
1017 vData.flicking = true;
1018 emit q->flickingChanged();
1019 emit q->flickingVerticallyChanged();
1020 emit q->flickStarted();
1023 timeline.reset(data.move);
1024 fixup(data, minExtent, maxExtent);
1029 //----------------------------------------------------------------------------
1031 \qmlclass GridView QQuickGridView
1032 \inqmlmodule QtQuick 2
1033 \ingroup qml-view-elements
1036 \brief The GridView item provides a grid view of items provided by a model.
1038 A GridView displays data from models created from built-in QML elements like ListModel
1039 and XmlListModel, or custom model classes defined in C++ that inherit from
1042 A GridView has a \l model, which defines the data to be displayed, and
1043 a \l delegate, which defines how the data should be displayed. Items in a
1044 GridView are laid out horizontally or vertically. Grid views are inherently flickable
1045 as GridView inherits from \l Flickable.
1047 \section1 Example Usage
1049 The following example shows the definition of a simple list model defined
1050 in a file called \c ContactModel.qml:
1052 \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0
1054 \div {class="float-right"}
1055 \inlineimage gridview-simple.png
1058 This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
1059 for more information about creating reusable components like this.
1061 Another component can display this model data in a GridView, as in the following
1062 example, which creates a \c ContactModel component for its model, and a \l Column element
1063 (containing \l Image and \l Text elements) for its delegate.
1066 \snippet doc/src/snippets/declarative/gridview/gridview.qml import
1068 \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple
1070 \div {class="float-right"}
1071 \inlineimage gridview-highlight.png
1074 The view will create a new delegate for each item in the model. Note that the delegate
1075 is able to access the model's \c name and \c portrait data directly.
1077 An improved grid view is shown below. The delegate is visually improved and is moved
1078 into a separate \c contactDelegate component.
1081 \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced
1083 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1084 and \c focus is set to \c true to enable keyboard navigation for the grid view.
1085 The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1087 Delegates are instantiated as needed and may be destroyed at any time.
1088 State should \e never be stored in a delegate.
1090 GridView attaches a number of properties to the root item of the delegate, for example
1091 \c {GridView.isCurrentItem}. In the following example, the root delegate item can access
1092 this attached property directly as \c GridView.isCurrentItem, while the child
1093 \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
1095 \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
1097 \note Views do not set the \l{Item::}{clip} property automatically.
1098 If the view is not clipped by another item or the screen, it will be necessary
1099 to set this property to true in order to clip the items that are partially or
1100 fully outside the view.
1102 \sa {declarative/modelviews/gridview}{GridView example}
1105 QQuickGridView::QQuickGridView(QQuickItem *parent)
1106 : QQuickItemView(*(new QQuickGridViewPrivate), parent)
1110 QQuickGridView::~QQuickGridView()
1114 void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1116 Q_D(QQuickGridView);
1117 if (d->autoHighlight != autoHighlight) {
1118 if (!autoHighlight && d->highlightXAnimator) {
1119 d->highlightXAnimator->stop();
1120 d->highlightYAnimator->stop();
1122 QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1127 \qmlattachedproperty bool QtQuick2::GridView::isCurrentItem
1128 This attached property is true if this delegate is the current item; otherwise false.
1130 It is attached to each instance of the delegate.
1134 \qmlattachedproperty GridView QtQuick2::GridView::view
1135 This attached property holds the view that manages this delegate instance.
1137 It is attached to each instance of the delegate.
1139 \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
1143 \qmlattachedproperty bool QtQuick2::GridView::delayRemove
1144 This attached property holds whether the delegate may be destroyed.
1146 It is attached to each instance of the delegate.
1148 It is sometimes necessary to delay the destruction of an item
1149 until an animation completes.
1151 The example below ensures that the animation completes before
1152 the item is removed from the grid.
1154 \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove
1158 \qmlattachedsignal QtQuick2::GridView::onAdd()
1159 This attached handler is called immediately after an item is added to the view.
1163 \qmlattachedsignal QtQuick2::GridView::onRemove()
1164 This attached handler is called immediately before an item is removed from the view.
1169 \qmlproperty model QtQuick2::GridView::model
1170 This property holds the model providing data for the grid.
1172 The model provides the set of data that is used to create the items
1173 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1174 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1175 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1177 \sa {qmlmodels}{Data Models}
1181 \qmlproperty Component QtQuick2::GridView::delegate
1183 The delegate provides a template defining each item instantiated by the view.
1184 The index is exposed as an accessible \c index property. Properties of the
1185 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1187 The number of elements in the delegate has a direct effect on the
1188 flicking performance of the view. If at all possible, place functionality
1189 that is not needed for the normal display of the delegate in a \l Loader which
1190 can load additional elements when needed.
1192 The GridView will layout the items based on the size of the root item
1195 \note Delegates are instantiated as needed and may be destroyed at any time.
1196 State should \e never be stored in a delegate.
1200 \qmlproperty int QtQuick2::GridView::currentIndex
1201 \qmlproperty Item QtQuick2::GridView::currentItem
1203 The \c currentIndex property holds the index of the current item, and
1204 \c currentItem holds the current item. Setting the currentIndex to -1
1205 will clear the highlight and set currentItem to null.
1207 If highlightFollowsCurrentItem is \c true, setting either of these
1208 properties will smoothly scroll the GridView so that the current
1209 item becomes visible.
1211 Note that the position of the current item
1212 may only be approximate until it becomes visible in the view.
1217 \qmlproperty Item QtQuick2::GridView::highlightItem
1219 This holds the highlight item created from the \l highlight component.
1221 The highlightItem is managed by the view unless
1222 \l highlightFollowsCurrentItem is set to false.
1224 \sa highlight, highlightFollowsCurrentItem
1229 \qmlproperty int QtQuick2::GridView::count
1230 This property holds the number of items in the view.
1235 \qmlproperty Component QtQuick2::GridView::highlight
1236 This property holds the component to use as the highlight.
1238 An instance of the highlight component is created for each view.
1239 The geometry of the resulting component instance will be managed by the view
1240 so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1242 \sa highlightItem, highlightFollowsCurrentItem
1246 \qmlproperty bool QtQuick2::GridView::highlightFollowsCurrentItem
1247 This property sets whether the highlight is managed by the view.
1249 If this property is true (the default value), the highlight is moved smoothly
1250 to follow the current item. Otherwise, the
1251 highlight is not moved by the view, and any movement must be implemented
1254 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1256 \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem
1261 \qmlproperty int QtQuick2::GridView::highlightMoveDuration
1262 This property holds the move animation duration of the highlight delegate.
1264 highlightFollowsCurrentItem must be true for this property
1267 The default value for the duration is 150ms.
1269 \sa highlightFollowsCurrentItem
1273 \qmlproperty real QtQuick2::GridView::preferredHighlightBegin
1274 \qmlproperty real QtQuick2::GridView::preferredHighlightEnd
1275 \qmlproperty enumeration QtQuick2::GridView::highlightRangeMode
1277 These properties define the preferred range of the highlight (for the current item)
1278 within the view. The \c preferredHighlightBegin value must be less than the
1279 \c preferredHighlightEnd value.
1281 These properties affect the position of the current item when the view is scrolled.
1282 For example, if the currently selected item should stay in the middle of the
1283 view when it is scrolled, set the \c preferredHighlightBegin and
1284 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1285 item would be. If the \c currentItem is changed programmatically, the view will
1286 automatically scroll so that the current item is in the middle of the view.
1287 Furthermore, the behavior of the current item index will occur whether or not a
1290 Valid values for \c highlightRangeMode are:
1293 \o GridView.ApplyRange - the view attempts to maintain the highlight within the range.
1294 However, the highlight can move outside of the range at the ends of the view or due
1295 to mouse interaction.
1296 \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
1297 The current item changes if a keyboard or mouse action would cause the highlight to move
1298 outside of the range.
1299 \o GridView.NoHighlightRange - this is the default value.
1305 \qmlproperty enumeration QtQuick2::GridView::layoutDirection
1306 This property holds the layout direction of the grid.
1311 \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
1312 dependent on the \l GridView::flow property.
1313 \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
1314 on the \l GridView::flow property.
1317 \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
1318 GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
1319 indicates that the flow is horizontal.
1324 \qmlproperty enumeration QtQuick2::GridView::effectiveLayoutDirection
1325 This property holds the effective layout direction of the grid.
1327 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1328 the visual layout direction of the grid will be mirrored. However, the
1329 property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1331 \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1334 \qmlproperty bool QtQuick2::GridView::keyNavigationWraps
1335 This property holds whether the grid wraps key navigation
1337 If this is true, key navigation that would move the current item selection
1338 past one end of the view instead wraps around and moves the selection to
1339 the other end of the view.
1341 By default, key navigation is not wrapped.
1344 \qmlproperty int QtQuick2::GridView::cacheBuffer
1345 This property determines whether delegates are retained outside the
1346 visible area of the view.
1348 If non-zero the view may keep as many delegates
1349 instantiated as will fit within the buffer specified. For example,
1350 if in a vertical view the delegate is 20 pixels high, there are 3
1351 columns and \c cacheBuffer is
1352 set to 40, then up to 6 delegates above and 6 delegates below the visible
1353 area may be created/retained. The buffered delegates are created asynchronously,
1354 allowing creation to occur across multiple frames and reducing the
1355 likelihood of skipping frames. In order to improve painting performance
1356 delegates outside the visible area have their \l visible property set to
1357 false until they are moved into the visible area.
1359 Note that cacheBuffer is not a pixel buffer - it only maintains additional
1360 instantiated delegates.
1362 Setting this value can make scrolling the list smoother at the expense
1363 of additional memory usage. It is not a substitute for creating efficient
1364 delegates; the fewer elements in a delegate, the faster a view may be
1367 void QQuickGridView::setHighlightMoveDuration(int duration)
1369 Q_D(QQuickGridView);
1370 if (d->highlightMoveDuration != duration) {
1371 if (d->highlightYAnimator) {
1372 d->highlightXAnimator->userDuration = duration;
1373 d->highlightYAnimator->userDuration = duration;
1375 QQuickItemView::setHighlightMoveDuration(duration);
1380 \qmlproperty enumeration QtQuick2::GridView::flow
1381 This property holds the flow of the grid.
1386 \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
1387 \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
1390 QQuickGridView::Flow QQuickGridView::flow() const
1392 Q_D(const QQuickGridView);
1396 void QQuickGridView::setFlow(Flow flow)
1398 Q_D(QQuickGridView);
1399 if (d->flow != flow) {
1401 if (d->flow == LeftToRight) {
1402 setContentWidth(-1);
1403 setFlickableDirection(VerticalFlick);
1405 setContentHeight(-1);
1406 setFlickableDirection(HorizontalFlick);
1417 \qmlproperty real QtQuick2::GridView::cellWidth
1418 \qmlproperty real QtQuick2::GridView::cellHeight
1420 These properties holds the width and height of each cell in the grid.
1422 The default cell size is 100x100.
1424 qreal QQuickGridView::cellWidth() const
1426 Q_D(const QQuickGridView);
1427 return d->cellWidth;
1430 void QQuickGridView::setCellWidth(qreal cellWidth)
1432 Q_D(QQuickGridView);
1433 if (cellWidth != d->cellWidth && cellWidth > 0) {
1434 d->cellWidth = qMax(qreal(1), cellWidth);
1435 d->updateViewport();
1436 emit cellWidthChanged();
1437 d->forceLayout = true;
1442 qreal QQuickGridView::cellHeight() const
1444 Q_D(const QQuickGridView);
1445 return d->cellHeight;
1448 void QQuickGridView::setCellHeight(qreal cellHeight)
1450 Q_D(QQuickGridView);
1451 if (cellHeight != d->cellHeight && cellHeight > 0) {
1452 d->cellHeight = qMax(qreal(1), cellHeight);
1453 d->updateViewport();
1454 emit cellHeightChanged();
1455 d->forceLayout = true;
1460 \qmlproperty enumeration QtQuick2::GridView::snapMode
1462 This property determines how the view scrolling will settle following a drag or flick.
1463 The possible values are:
1466 \o GridView.NoSnap (default) - the view stops anywhere within the visible area.
1467 \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
1468 aligned with the start of the view.
1469 \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
1470 away from the first visible row at the time the mouse button is released.
1471 This mode is particularly useful for moving one page at a time.
1475 QQuickGridView::SnapMode QQuickGridView::snapMode() const
1477 Q_D(const QQuickGridView);
1481 void QQuickGridView::setSnapMode(SnapMode mode)
1483 Q_D(QQuickGridView);
1484 if (d->snapMode != mode) {
1486 emit snapModeChanged();
1492 \qmlproperty Component QtQuick2::GridView::footer
1493 This property holds the component to use as the footer.
1495 An instance of the footer component is created for each view. The
1496 footer is positioned at the end of the view, after any items.
1498 \sa header, footerItem
1501 \qmlproperty Component QtQuick2::GridView::header
1502 This property holds the component to use as the header.
1504 An instance of the header component is created for each view. The
1505 header is positioned at the beginning of the view, before any items.
1507 \sa footer, headerItem
1511 \qmlproperty Item QtQuick2::GridView::headerItem
1512 This holds the header item created from the \l header component.
1514 An instance of the header component is created for each view. The
1515 header is positioned at the beginning of the view, before any items.
1517 \sa header, footerItem
1521 \qmlproperty Item QtQuick2::GridView::footerItem
1522 This holds the footer item created from the \l footer component.
1524 An instance of the footer component is created for each view. The
1525 footer is positioned at the end of the view, after any items.
1527 \sa footer, headerItem
1530 void QQuickGridView::viewportMoved()
1532 Q_D(QQuickGridView);
1533 QQuickItemView::viewportMoved();
1536 if (d->inViewportMoved)
1538 d->inViewportMoved = true;
1541 d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
1542 else if (d->isRightToLeftTopToBottom())
1543 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
1545 d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
1549 // Set visibility of items to eliminate cost of items outside the visible area.
1550 qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1551 qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
1552 for (int i = 0; i < d->visibleItems.count(); ++i) {
1553 FxGridItemSG *item = static_cast<FxGridItemSG*>(d->visibleItems.at(i));
1554 item->item->setVisible(item->rowPos() + d->rowSize() >= from && item->rowPos() <= to);
1557 if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
1558 d->moveReason = QQuickGridViewPrivate::Mouse;
1559 if (d->moveReason != QQuickGridViewPrivate::SetIndex) {
1560 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
1561 // reposition highlight
1562 qreal pos = d->highlight->position();
1563 qreal viewPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size() : d->position();
1564 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
1565 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
1566 if (pos < viewPos + d->highlightRangeStart)
1567 pos = viewPos + d->highlightRangeStart;
1569 if (pos != d->highlight->position()) {
1570 d->highlightXAnimator->stop();
1571 d->highlightYAnimator->stop();
1572 static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
1574 d->updateHighlight();
1577 // update current index
1578 int idx = d->snapIndex();
1579 if (idx >= 0 && idx != d->currentIndex) {
1580 d->updateCurrent(idx);
1581 if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
1582 if (d->flow == LeftToRight)
1583 d->highlightXAnimator->to = d->currentItem->item->x();
1585 d->highlightYAnimator->to = d->currentItem->item->y();
1591 d->inViewportMoved = false;
1594 void QQuickGridView::keyPressEvent(QKeyEvent *event)
1596 Q_D(QQuickGridView);
1597 if (d->model && d->model->count() && d->interactive) {
1598 d->moveReason = QQuickGridViewPrivate::SetIndex;
1599 int oldCurrent = currentIndex();
1600 switch (event->key()) {
1602 moveCurrentIndexUp();
1605 moveCurrentIndexDown();
1608 moveCurrentIndexLeft();
1611 moveCurrentIndexRight();
1616 if (oldCurrent != currentIndex()) {
1622 QQuickItemView::keyPressEvent(event);
1625 \qmlmethod QtQuick2::GridView::moveCurrentIndexUp()
1627 Move the currentIndex up one item in the view.
1628 The current index will wrap if keyNavigationWraps is true and it
1629 is currently at the end. This method has no effect if the \l count is zero.
1631 \bold Note: methods should only be called after the Component has completed.
1635 void QQuickGridView::moveCurrentIndexUp()
1637 Q_D(QQuickGridView);
1638 const int count = d->model ? d->model->count() : 0;
1641 if (d->flow == QQuickGridView::LeftToRight) {
1642 if (currentIndex() >= d->columns || d->wrap) {
1643 int index = currentIndex() - d->columns;
1644 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1647 if (currentIndex() > 0 || d->wrap) {
1648 int index = currentIndex() - 1;
1649 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1655 \qmlmethod QtQuick2::GridView::moveCurrentIndexDown()
1657 Move the currentIndex down one item in the view.
1658 The current index will wrap if keyNavigationWraps is true and it
1659 is currently at the end. This method has no effect if the \l count is zero.
1661 \bold Note: methods should only be called after the Component has completed.
1663 void QQuickGridView::moveCurrentIndexDown()
1665 Q_D(QQuickGridView);
1666 const int count = d->model ? d->model->count() : 0;
1669 if (d->flow == QQuickGridView::LeftToRight) {
1670 if (currentIndex() < count - d->columns || d->wrap) {
1671 int index = currentIndex()+d->columns;
1672 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1675 if (currentIndex() < count - 1 || d->wrap) {
1676 int index = currentIndex() + 1;
1677 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1683 \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft()
1685 Move the currentIndex left one item in the view.
1686 The current index will wrap if keyNavigationWraps is true and it
1687 is currently at the end. This method has no effect if the \l count is zero.
1689 \bold Note: methods should only be called after the Component has completed.
1691 void QQuickGridView::moveCurrentIndexLeft()
1693 Q_D(QQuickGridView);
1694 const int count = d->model ? d->model->count() : 0;
1697 if (effectiveLayoutDirection() == Qt::LeftToRight) {
1698 if (d->flow == QQuickGridView::LeftToRight) {
1699 if (currentIndex() > 0 || d->wrap) {
1700 int index = currentIndex() - 1;
1701 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1704 if (currentIndex() >= d->columns || d->wrap) {
1705 int index = currentIndex() - d->columns;
1706 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1710 if (d->flow == QQuickGridView::LeftToRight) {
1711 if (currentIndex() < count - 1 || d->wrap) {
1712 int index = currentIndex() + 1;
1713 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1716 if (currentIndex() < count - d->columns || d->wrap) {
1717 int index = currentIndex() + d->columns;
1718 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1726 \qmlmethod QtQuick2::GridView::moveCurrentIndexRight()
1728 Move the currentIndex right one item in the view.
1729 The current index will wrap if keyNavigationWraps is true and it
1730 is currently at the end. This method has no effect if the \l count is zero.
1732 \bold Note: methods should only be called after the Component has completed.
1734 void QQuickGridView::moveCurrentIndexRight()
1736 Q_D(QQuickGridView);
1737 const int count = d->model ? d->model->count() : 0;
1740 if (effectiveLayoutDirection() == Qt::LeftToRight) {
1741 if (d->flow == QQuickGridView::LeftToRight) {
1742 if (currentIndex() < count - 1 || d->wrap) {
1743 int index = currentIndex() + 1;
1744 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1747 if (currentIndex() < count - d->columns || d->wrap) {
1748 int index = currentIndex()+d->columns;
1749 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1753 if (d->flow == QQuickGridView::LeftToRight) {
1754 if (currentIndex() > 0 || d->wrap) {
1755 int index = currentIndex() - 1;
1756 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1759 if (currentIndex() >= d->columns || d->wrap) {
1760 int index = currentIndex() - d->columns;
1761 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1767 bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems)
1769 Q_Q(QQuickGridView);
1771 int modelIndex = change.index;
1772 int count = change.count;
1774 int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
1777 int i = visibleItems.count() - 1;
1778 while (i > 0 && visibleItems.at(i)->index == -1)
1780 if (visibleItems.at(i)->index + 1 == modelIndex) {
1781 // Special case of appending an item to the model.
1782 index = visibleItems.count();
1784 if (modelIndex <= visibleIndex) {
1785 // Insert before visible items
1786 visibleIndex += count;
1787 for (int i = 0; i < visibleItems.count(); ++i) {
1788 FxViewItem *item = visibleItems.at(i);
1789 if (item->index != -1 && item->index >= modelIndex)
1790 item->index += count;
1797 qreal tempPos = isRightToLeftTopToBottom() ? -position()-size()+q->width()+1 : position();
1801 if (visibleItems.count()) {
1802 if (index < visibleItems.count()) {
1803 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index));
1804 colPos = gridItem->colPos();
1805 rowPos = gridItem->rowPos();
1806 colNum = qFloor((colPos+colSize()/2) / colSize());
1808 // appending items to visible list
1809 FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index-1));
1810 rowPos = gridItem->rowPos();
1811 colNum = qFloor((gridItem->colPos()+colSize()/2) / colSize());
1812 if (++colNum >= columns) {
1814 rowPos += rowSize();
1816 colPos = colNum * colSize();
1820 // Update the indexes of the following visible items.
1821 for (int i = 0; i < visibleItems.count(); ++i) {
1822 FxViewItem *item = visibleItems.at(i);
1823 if (item->index != -1 && item->index >= modelIndex)
1824 item->index += count;
1827 int prevVisibleCount = visibleItems.count();
1828 if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
1829 // Insert items before the visible item.
1830 int insertionIdx = index;
1832 int from = tempPos - buffer;
1835 if (rowPos > from && insertionIdx < visibleIndex) {
1836 // item won't be visible, just note the size for repositioning
1837 insertResult->changeBeforeVisible++;
1839 // item is before first visible e.g. in cache buffer
1840 FxViewItem *item = 0;
1841 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
1842 item->index = modelIndex + i;
1844 item = createItem(modelIndex + i);
1848 item->item->setVisible(true);
1849 visibleItems.insert(insertionIdx, item);
1850 if (!change.isMove())
1851 addedItems->append(item);
1852 insertResult->sizeChangesBeforeVisiblePos += rowSize();
1855 if (--colNum < 0 ) {
1856 colNum = columns - 1;
1857 rowPos -= rowSize();
1859 colPos = colNum * colSize();
1865 int to = buffer+tempPos+size()-1;
1866 while (i < count && rowPos <= to + rowSize()*(columns - colNum)/qreal(columns+1)) {
1867 FxViewItem *item = 0;
1868 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
1869 item->index = modelIndex + i;
1871 item = createItem(modelIndex + i);
1875 item->item->setVisible(true);
1876 visibleItems.insert(index, item);
1878 insertResult->changedFirstItem = true;
1879 if (!change.isMove())
1880 addedItems->append(item);
1881 insertResult->sizeChangesAfterVisiblePos += rowSize();
1883 if (++colNum >= columns) {
1885 rowPos += rowSize();
1887 colPos = colNum * colSize();
1893 updateVisibleIndex();
1895 return visibleItems.count() > prevVisibleCount;
1898 bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
1900 // If we add or remove items before visible items, a layout may be
1901 // required to ensure item 0 is in the first column.
1902 return modelIndex < visibleIndex;
1906 \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode)
1908 Positions the view such that the \a index is at the position specified by
1912 \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
1913 \o GridView.Center - position item in the center of the view.
1914 \o GridView.End - position item at bottom (or right for horizontal orientation) of the view.
1915 \o GridView.Visible - if any part of the item is visible then take no action, otherwise
1916 bring the item into view.
1917 \o GridView.Contain - ensure the entire item is visible. If the item is larger than
1918 the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
1921 If positioning the view at the index would cause empty space to be displayed at
1922 the beginning or end of the view, the view will be positioned at the boundary.
1924 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
1925 at a particular index. This is unreliable since removing items from the start
1926 of the view does not cause all other items to be repositioned.
1927 The correct way to bring an item into view is with \c positionViewAtIndex.
1929 \bold Note: methods should only be called after the Component has completed. To position
1930 the view at startup, this method should be called by Component.onCompleted. For
1931 example, to position the view at the end:
1934 Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
1939 \qmlmethod QtQuick2::GridView::positionViewAtBeginning()
1940 \qmlmethod QtQuick2::GridView::positionViewAtEnd()
1942 Positions the view at the beginning or end, taking into account any header or footer.
1944 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
1945 at a particular index. This is unreliable since removing items from the start
1946 of the list does not cause all other items to be repositioned, and because
1947 the actual start of the view can vary based on the size of the delegates.
1949 \bold Note: methods should only be called after the Component has completed. To position
1950 the view at startup, this method should be called by Component.onCompleted. For
1951 example, to position the view at the end on startup:
1954 Component.onCompleted: positionViewAtEnd()
1959 \qmlmethod int QtQuick2::GridView::indexAt(int x, int y)
1961 Returns the index of the visible item containing the point \a x, \a y in content
1962 coordinates. If there is no item at the point specified, or the item is
1963 not visible -1 is returned.
1965 If the item is outside the visible area, -1 is returned, regardless of
1966 whether an item will exist at that point when scrolled into view.
1968 \bold Note: methods should only be called after the Component has completed.
1972 \qmlmethod Item QtQuick2::GridView::itemAt(int x, int y)
1974 Returns the visible item containing the point \a x, \a y in content
1975 coordinates. If there is no item at the point specified, or the item is
1976 not visible null is returned.
1978 If the item is outside the visible area, null is returned, regardless of
1979 whether an item will exist at that point when scrolled into view.
1981 \bold Note: methods should only be called after the Component has completed.
1984 QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj)
1986 return new QQuickGridViewAttached(obj);