|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtDeclarative module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "private/qdeclarativegridview_p.h" |
|
43 |
|
44 #include "private/qdeclarativevisualitemmodel_p.h" |
|
45 #include "private/qdeclarativeflickable_p_p.h" |
|
46 |
|
47 #include "private/qdeclarativesmoothedanimation_p_p.h" |
|
48 #include <qdeclarativeguard_p.h> |
|
49 |
|
50 #include <qlistmodelinterface_p.h> |
|
51 #include <QKeyEvent> |
|
52 |
|
53 #include <qmath.h> |
|
54 #include <math.h> |
|
55 |
|
56 QT_BEGIN_NAMESPACE |
|
57 |
|
58 |
|
59 //---------------------------------------------------------------------------- |
|
60 |
|
61 class FxGridItem |
|
62 { |
|
63 public: |
|
64 FxGridItem(QDeclarativeItem *i, QDeclarativeGridView *v) : item(i), view(v) { |
|
65 attached = static_cast<QDeclarativeGridViewAttached*>(qmlAttachedPropertiesObject<QDeclarativeGridView>(item)); |
|
66 if (attached) |
|
67 attached->m_view = view; |
|
68 } |
|
69 ~FxGridItem() {} |
|
70 |
|
71 qreal rowPos() const { return (view->flow() == QDeclarativeGridView::LeftToRight ? item->y() : item->x()); } |
|
72 qreal colPos() const { return (view->flow() == QDeclarativeGridView::LeftToRight ? item->x() : item->y()); } |
|
73 qreal endRowPos() const { |
|
74 return view->flow() == QDeclarativeGridView::LeftToRight |
|
75 ? item->y() + view->cellHeight() - 1 |
|
76 : item->x() + view->cellWidth() - 1; |
|
77 } |
|
78 void setPosition(qreal col, qreal row) { |
|
79 if (view->flow() == QDeclarativeGridView::LeftToRight) { |
|
80 item->setPos(QPointF(col, row)); |
|
81 } else { |
|
82 item->setPos(QPointF(row, col)); |
|
83 } |
|
84 } |
|
85 bool contains(int x, int y) const { |
|
86 return (x >= item->x() && x < item->x() + view->cellWidth() && |
|
87 y >= item->y() && y < item->y() + view->cellHeight()); |
|
88 } |
|
89 |
|
90 QDeclarativeItem *item; |
|
91 QDeclarativeGridView *view; |
|
92 QDeclarativeGridViewAttached *attached; |
|
93 int index; |
|
94 }; |
|
95 |
|
96 //---------------------------------------------------------------------------- |
|
97 |
|
98 class QDeclarativeGridViewPrivate : public QDeclarativeFlickablePrivate |
|
99 { |
|
100 Q_DECLARE_PUBLIC(QDeclarativeGridView) |
|
101 |
|
102 public: |
|
103 QDeclarativeGridViewPrivate() |
|
104 : currentItem(0), flow(QDeclarativeGridView::LeftToRight) |
|
105 , visibleIndex(0) , currentIndex(-1) |
|
106 , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) |
|
107 , highlightRangeStart(0), highlightRangeEnd(0), highlightRange(QDeclarativeGridView::NoHighlightRange) |
|
108 , highlightComponent(0), highlight(0), trackedItem(0) |
|
109 , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0) |
|
110 , highlightMoveDuration(150) |
|
111 , bufferMode(BufferBefore | BufferAfter), snapMode(QDeclarativeGridView::NoSnap) |
|
112 , ownModel(false), wrap(false), autoHighlight(true) |
|
113 , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) |
|
114 , deferredRelease(false), haveHighlightRange(false) {} |
|
115 |
|
116 void init(); |
|
117 void clear(); |
|
118 FxGridItem *createItem(int modelIndex); |
|
119 void releaseItem(FxGridItem *item); |
|
120 void refill(qreal from, qreal to, bool doBuffer=false); |
|
121 |
|
122 void updateGrid(); |
|
123 void scheduleLayout(); |
|
124 void layout(); |
|
125 void updateUnrequestedIndexes(); |
|
126 void updateUnrequestedPositions(); |
|
127 void updateTrackedItem(); |
|
128 void createHighlight(); |
|
129 void updateHighlight(); |
|
130 void updateCurrent(int modelIndex); |
|
131 void fixupPosition(); |
|
132 |
|
133 FxGridItem *visibleItem(int modelIndex) const { |
|
134 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { |
|
135 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { |
|
136 FxGridItem *item = visibleItems.at(i); |
|
137 if (item->index == modelIndex) |
|
138 return item; |
|
139 } |
|
140 } |
|
141 return 0; |
|
142 } |
|
143 |
|
144 qreal position() const { |
|
145 Q_Q(const QDeclarativeGridView); |
|
146 return flow == QDeclarativeGridView::LeftToRight ? q->contentY() : q->contentX(); |
|
147 } |
|
148 void setPosition(qreal pos) { |
|
149 Q_Q(QDeclarativeGridView); |
|
150 if (flow == QDeclarativeGridView::LeftToRight) |
|
151 q->setContentY(pos); |
|
152 else |
|
153 q->setContentX(pos); |
|
154 } |
|
155 int size() const { |
|
156 Q_Q(const QDeclarativeGridView); |
|
157 return flow == QDeclarativeGridView::LeftToRight ? q->height() : q->width(); |
|
158 } |
|
159 qreal startPosition() const { |
|
160 qreal pos = 0; |
|
161 if (!visibleItems.isEmpty()) |
|
162 pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); |
|
163 return pos; |
|
164 } |
|
165 |
|
166 qreal endPosition() const { |
|
167 qreal pos = 0; |
|
168 if (model && model->count()) |
|
169 pos = rowPosAt(model->count() - 1) + rowSize(); |
|
170 return pos; |
|
171 } |
|
172 |
|
173 bool isValid() const { |
|
174 return model && model->count() && model->isValid(); |
|
175 } |
|
176 |
|
177 int rowSize() const { |
|
178 return flow == QDeclarativeGridView::LeftToRight ? cellHeight : cellWidth; |
|
179 } |
|
180 int colSize() const { |
|
181 return flow == QDeclarativeGridView::LeftToRight ? cellWidth : cellHeight; |
|
182 } |
|
183 |
|
184 qreal colPosAt(int modelIndex) const { |
|
185 if (FxGridItem *item = visibleItem(modelIndex)) |
|
186 return item->colPos(); |
|
187 if (!visibleItems.isEmpty()) { |
|
188 if (modelIndex < visibleIndex) { |
|
189 int count = (visibleIndex - modelIndex) % columns; |
|
190 int col = visibleItems.first()->colPos() / colSize(); |
|
191 col = (columns - count + col) % columns; |
|
192 return col * colSize(); |
|
193 } else { |
|
194 int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; |
|
195 return visibleItems.last()->colPos() - count * colSize(); |
|
196 } |
|
197 } else { |
|
198 return (modelIndex % columns) * colSize(); |
|
199 } |
|
200 return 0; |
|
201 } |
|
202 qreal rowPosAt(int modelIndex) const { |
|
203 if (FxGridItem *item = visibleItem(modelIndex)) |
|
204 return item->rowPos(); |
|
205 if (!visibleItems.isEmpty()) { |
|
206 if (modelIndex < visibleIndex) { |
|
207 int firstCol = visibleItems.first()->colPos() / colSize(); |
|
208 int col = visibleIndex - modelIndex + (columns - firstCol - 1); |
|
209 int rows = col / columns; |
|
210 return visibleItems.first()->rowPos() - rows * rowSize(); |
|
211 } else { |
|
212 int count = modelIndex - visibleItems.last()->index; |
|
213 int col = visibleItems.last()->colPos() + count * colSize(); |
|
214 int rows = col / (columns * colSize()); |
|
215 return visibleItems.last()->rowPos() + rows * rowSize(); |
|
216 } |
|
217 } else { |
|
218 return (modelIndex / columns) * rowSize(); |
|
219 } |
|
220 return 0; |
|
221 } |
|
222 |
|
223 FxGridItem *firstVisibleItem() const { |
|
224 const qreal pos = position(); |
|
225 for (int i = 0; i < visibleItems.count(); ++i) { |
|
226 FxGridItem *item = visibleItems.at(i); |
|
227 if (item->index != -1 && item->endRowPos() > pos) |
|
228 return item; |
|
229 } |
|
230 return visibleItems.count() ? visibleItems.first() : 0; |
|
231 } |
|
232 |
|
233 // Map a model index to visibleItems list index. |
|
234 // These may differ if removed items are still present in the visible list, |
|
235 // e.g. doing a removal animation |
|
236 int mapFromModel(int modelIndex) const { |
|
237 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) |
|
238 return -1; |
|
239 for (int i = 0; i < visibleItems.count(); ++i) { |
|
240 FxGridItem *listItem = visibleItems.at(i); |
|
241 if (listItem->index == modelIndex) |
|
242 return i + visibleIndex; |
|
243 if (listItem->index > modelIndex) |
|
244 return -1; |
|
245 } |
|
246 return -1; // Not in visibleList |
|
247 } |
|
248 |
|
249 qreal snapPosAt(qreal pos) { |
|
250 qreal snapPos = 0; |
|
251 if (!visibleItems.isEmpty()) { |
|
252 pos += rowSize()/2; |
|
253 snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); |
|
254 snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); |
|
255 } |
|
256 return snapPos; |
|
257 } |
|
258 |
|
259 int snapIndex() { |
|
260 int index = currentIndex; |
|
261 for (int i = 0; i < visibleItems.count(); ++i) { |
|
262 FxGridItem *item = visibleItems[i]; |
|
263 if (item->index == -1) |
|
264 continue; |
|
265 qreal itemTop = item->rowPos(); |
|
266 if (itemTop >= highlight->rowPos()-rowSize()/2 && itemTop < highlight->rowPos()+rowSize()/2) { |
|
267 index = item->index; |
|
268 if (item->colPos() >= highlight->colPos()-colSize()/2 && item->colPos() < highlight->colPos()+colSize()/2) |
|
269 return item->index; |
|
270 } |
|
271 } |
|
272 return index; |
|
273 } |
|
274 |
|
275 virtual void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { |
|
276 Q_Q(const QDeclarativeGridView); |
|
277 QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); |
|
278 if (item == q) { |
|
279 if (newGeometry.height() != oldGeometry.height() |
|
280 || newGeometry.width() != oldGeometry.width()) { |
|
281 if (q->isComponentComplete()) { |
|
282 updateGrid(); |
|
283 scheduleLayout(); |
|
284 } |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); |
|
290 virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, |
|
291 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); |
|
292 |
|
293 // for debugging only |
|
294 void checkVisible() const { |
|
295 int skip = 0; |
|
296 for (int i = 0; i < visibleItems.count(); ++i) { |
|
297 FxGridItem *listItem = visibleItems.at(i); |
|
298 if (listItem->index == -1) { |
|
299 ++skip; |
|
300 } else if (listItem->index != visibleIndex + i - skip) { |
|
301 for (int j = 0; j < visibleItems.count(); j++) |
|
302 qDebug() << " index" << j << "item index" << visibleItems.at(j)->index; |
|
303 qFatal("index %d %d %d", visibleIndex, i, listItem->index); |
|
304 } |
|
305 } |
|
306 } |
|
307 |
|
308 QDeclarativeGuard<QDeclarativeVisualModel> model; |
|
309 QVariant modelVariant; |
|
310 QList<FxGridItem*> visibleItems; |
|
311 QHash<QDeclarativeItem*,int> unrequestedItems; |
|
312 FxGridItem *currentItem; |
|
313 QDeclarativeGridView::Flow flow; |
|
314 int visibleIndex; |
|
315 int currentIndex; |
|
316 int cellWidth; |
|
317 int cellHeight; |
|
318 int columns; |
|
319 int requestedIndex; |
|
320 int itemCount; |
|
321 qreal highlightRangeStart; |
|
322 qreal highlightRangeEnd; |
|
323 QDeclarativeGridView::HighlightRangeMode highlightRange; |
|
324 QDeclarativeComponent *highlightComponent; |
|
325 FxGridItem *highlight; |
|
326 FxGridItem *trackedItem; |
|
327 enum MovementReason { Other, SetIndex, Mouse }; |
|
328 MovementReason moveReason; |
|
329 int buffer; |
|
330 QSmoothedAnimation *highlightXAnimator; |
|
331 QSmoothedAnimation *highlightYAnimator; |
|
332 int highlightMoveDuration; |
|
333 enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; |
|
334 int bufferMode; |
|
335 QDeclarativeGridView::SnapMode snapMode; |
|
336 |
|
337 bool ownModel : 1; |
|
338 bool wrap : 1; |
|
339 bool autoHighlight : 1; |
|
340 bool fixCurrentVisibility : 1; |
|
341 bool lazyRelease : 1; |
|
342 bool layoutScheduled : 1; |
|
343 bool deferredRelease : 1; |
|
344 bool haveHighlightRange : 1; |
|
345 }; |
|
346 |
|
347 void QDeclarativeGridViewPrivate::init() |
|
348 { |
|
349 Q_Q(QDeclarativeGridView); |
|
350 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); |
|
351 q->setFlag(QGraphicsItem::ItemIsFocusScope); |
|
352 q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); |
|
353 addItemChangeListener(this, Geometry); |
|
354 } |
|
355 |
|
356 void QDeclarativeGridViewPrivate::clear() |
|
357 { |
|
358 for (int i = 0; i < visibleItems.count(); ++i) |
|
359 releaseItem(visibleItems.at(i)); |
|
360 visibleItems.clear(); |
|
361 visibleIndex = 0; |
|
362 releaseItem(currentItem); |
|
363 currentItem = 0; |
|
364 createHighlight(); |
|
365 trackedItem = 0; |
|
366 itemCount = 0; |
|
367 } |
|
368 |
|
369 FxGridItem *QDeclarativeGridViewPrivate::createItem(int modelIndex) |
|
370 { |
|
371 Q_Q(QDeclarativeGridView); |
|
372 // create object |
|
373 requestedIndex = modelIndex; |
|
374 FxGridItem *listItem = 0; |
|
375 if (QDeclarativeItem *item = model->item(modelIndex, false)) { |
|
376 listItem = new FxGridItem(item, q); |
|
377 listItem->index = modelIndex; |
|
378 if (model->completePending()) { |
|
379 // complete |
|
380 listItem->item->setZValue(1); |
|
381 listItem->item->setParentItem(q->viewport()); |
|
382 model->completeItem(); |
|
383 } else { |
|
384 listItem->item->setParentItem(q->viewport()); |
|
385 } |
|
386 unrequestedItems.remove(listItem->item); |
|
387 } |
|
388 requestedIndex = -1; |
|
389 return listItem; |
|
390 } |
|
391 |
|
392 |
|
393 void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item) |
|
394 { |
|
395 Q_Q(QDeclarativeGridView); |
|
396 if (!item || !model) |
|
397 return; |
|
398 if (trackedItem == item) { |
|
399 QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); |
|
400 QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); |
|
401 trackedItem = 0; |
|
402 } |
|
403 if (model->release(item->item) == 0) { |
|
404 // item was not destroyed, and we no longer reference it. |
|
405 unrequestedItems.insert(item->item, model->indexOf(item->item, q)); |
|
406 } |
|
407 delete item; |
|
408 } |
|
409 |
|
410 void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) |
|
411 { |
|
412 Q_Q(QDeclarativeGridView); |
|
413 if (!isValid() || !q->isComponentComplete()) |
|
414 return; |
|
415 |
|
416 itemCount = model->count(); |
|
417 qreal bufferFrom = from - buffer; |
|
418 qreal bufferTo = to + buffer; |
|
419 qreal fillFrom = from; |
|
420 qreal fillTo = to; |
|
421 if (doBuffer && (bufferMode & BufferAfter)) |
|
422 fillTo = bufferTo; |
|
423 if (doBuffer && (bufferMode & BufferBefore)) |
|
424 fillFrom = bufferFrom; |
|
425 |
|
426 bool changed = false; |
|
427 |
|
428 int colPos = colPosAt(visibleIndex); |
|
429 int rowPos = rowPosAt(visibleIndex); |
|
430 int modelIndex = visibleIndex; |
|
431 if (visibleItems.count()) { |
|
432 rowPos = visibleItems.last()->rowPos(); |
|
433 colPos = visibleItems.last()->colPos() + colSize(); |
|
434 if (colPos > colSize() * (columns-1)) { |
|
435 colPos = 0; |
|
436 rowPos += rowSize(); |
|
437 } |
|
438 int i = visibleItems.count() - 1; |
|
439 while (i > 0 && visibleItems.at(i)->index == -1) |
|
440 --i; |
|
441 modelIndex = visibleItems.at(i)->index + 1; |
|
442 } |
|
443 int colNum = colPos / colSize(); |
|
444 |
|
445 FxGridItem *item = 0; |
|
446 |
|
447 // Item creation and release is staggered in order to avoid |
|
448 // creating/releasing multiple items in one frame |
|
449 // while flicking (as much as possible). |
|
450 while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { |
|
451 // qDebug() << "refill: append item" << modelIndex; |
|
452 if (!(item = createItem(modelIndex))) |
|
453 break; |
|
454 item->setPosition(colPos, rowPos); |
|
455 visibleItems.append(item); |
|
456 colPos += colSize(); |
|
457 colNum++; |
|
458 if (colPos > colSize() * (columns-1)) { |
|
459 colPos = 0; |
|
460 colNum = 0; |
|
461 rowPos += rowSize(); |
|
462 } |
|
463 ++modelIndex; |
|
464 changed = true; |
|
465 if (doBuffer) // never buffer more than one item per frame |
|
466 break; |
|
467 } |
|
468 |
|
469 if (visibleItems.count()) { |
|
470 rowPos = visibleItems.first()->rowPos(); |
|
471 colPos = visibleItems.first()->colPos() - colSize(); |
|
472 if (colPos < 0) { |
|
473 colPos = colSize() * (columns - 1); |
|
474 rowPos -= rowSize(); |
|
475 } |
|
476 } |
|
477 colNum = colPos / colSize(); |
|
478 while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){ |
|
479 // qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; |
|
480 if (!(item = createItem(visibleIndex-1))) |
|
481 break; |
|
482 --visibleIndex; |
|
483 item->setPosition(colPos, rowPos); |
|
484 visibleItems.prepend(item); |
|
485 colPos -= colSize(); |
|
486 colNum--; |
|
487 if (colPos < 0) { |
|
488 colPos = colSize() * (columns - 1); |
|
489 colNum = columns-1; |
|
490 rowPos -= rowSize(); |
|
491 } |
|
492 changed = true; |
|
493 if (doBuffer) // never buffer more than one item per frame |
|
494 break; |
|
495 } |
|
496 |
|
497 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create |
|
498 while (visibleItems.count() > 1 |
|
499 && (item = visibleItems.first()) |
|
500 && item->endRowPos() < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { |
|
501 if (item->attached->delayRemove()) |
|
502 break; |
|
503 // qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); |
|
504 if (item->index != -1) |
|
505 visibleIndex++; |
|
506 visibleItems.removeFirst(); |
|
507 releaseItem(item); |
|
508 changed = true; |
|
509 } |
|
510 while (visibleItems.count() > 1 |
|
511 && (item = visibleItems.last()) |
|
512 && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { |
|
513 if (item->attached->delayRemove()) |
|
514 break; |
|
515 // qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; |
|
516 visibleItems.removeLast(); |
|
517 releaseItem(item); |
|
518 changed = true; |
|
519 } |
|
520 deferredRelease = false; |
|
521 } else { |
|
522 deferredRelease = true; |
|
523 } |
|
524 if (changed) { |
|
525 if (flow == QDeclarativeGridView::LeftToRight) |
|
526 q->setContentHeight(endPosition() - startPosition()); |
|
527 else |
|
528 q->setContentWidth(endPosition() - startPosition()); |
|
529 } else if (!doBuffer && buffer && bufferMode != NoBuffer) { |
|
530 refill(from, to, true); |
|
531 } |
|
532 lazyRelease = false; |
|
533 } |
|
534 |
|
535 void QDeclarativeGridViewPrivate::updateGrid() |
|
536 { |
|
537 Q_Q(QDeclarativeGridView); |
|
538 columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); |
|
539 if (isValid()) { |
|
540 if (flow == QDeclarativeGridView::LeftToRight) |
|
541 q->setContentHeight(endPosition() - startPosition()); |
|
542 else |
|
543 q->setContentWidth(endPosition() - startPosition()); |
|
544 } |
|
545 } |
|
546 |
|
547 void QDeclarativeGridViewPrivate::scheduleLayout() |
|
548 { |
|
549 Q_Q(QDeclarativeGridView); |
|
550 if (!layoutScheduled) { |
|
551 layoutScheduled = true; |
|
552 QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); |
|
553 } |
|
554 } |
|
555 |
|
556 void QDeclarativeGridViewPrivate::layout() |
|
557 { |
|
558 Q_Q(QDeclarativeGridView); |
|
559 layoutScheduled = false; |
|
560 if (!isValid()) { |
|
561 clear(); |
|
562 return; |
|
563 } |
|
564 if (visibleItems.count()) { |
|
565 qreal rowPos = visibleItems.first()->rowPos(); |
|
566 qreal colPos = visibleItems.first()->colPos(); |
|
567 int col = visibleIndex % columns; |
|
568 if (colPos != col * colSize()) { |
|
569 colPos = col * colSize(); |
|
570 visibleItems.first()->setPosition(colPos, rowPos); |
|
571 } |
|
572 for (int i = 1; i < visibleItems.count(); ++i) { |
|
573 FxGridItem *item = visibleItems.at(i); |
|
574 colPos += colSize(); |
|
575 if (colPos > colSize() * (columns-1)) { |
|
576 colPos = 0; |
|
577 rowPos += rowSize(); |
|
578 } |
|
579 item->setPosition(colPos, rowPos); |
|
580 } |
|
581 } |
|
582 q->refill(); |
|
583 updateHighlight(); |
|
584 moveReason = Other; |
|
585 if (flow == QDeclarativeGridView::LeftToRight) { |
|
586 q->setContentHeight(endPosition() - startPosition()); |
|
587 fixupY(); |
|
588 } else { |
|
589 q->setContentWidth(endPosition() - startPosition()); |
|
590 fixupX(); |
|
591 } |
|
592 updateUnrequestedPositions(); |
|
593 } |
|
594 |
|
595 void QDeclarativeGridViewPrivate::updateUnrequestedIndexes() |
|
596 { |
|
597 Q_Q(QDeclarativeGridView); |
|
598 QHash<QDeclarativeItem*,int>::iterator it; |
|
599 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) |
|
600 *it = model->indexOf(it.key(), q); |
|
601 } |
|
602 |
|
603 void QDeclarativeGridViewPrivate::updateUnrequestedPositions() |
|
604 { |
|
605 QHash<QDeclarativeItem*,int>::const_iterator it; |
|
606 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { |
|
607 if (flow == QDeclarativeGridView::LeftToRight) { |
|
608 it.key()->setPos(QPointF(colPosAt(*it), rowPosAt(*it))); |
|
609 } else { |
|
610 it.key()->setPos(QPointF(rowPosAt(*it), colPosAt(*it))); |
|
611 } |
|
612 } |
|
613 } |
|
614 |
|
615 void QDeclarativeGridViewPrivate::updateTrackedItem() |
|
616 { |
|
617 Q_Q(QDeclarativeGridView); |
|
618 FxGridItem *item = currentItem; |
|
619 if (highlight) |
|
620 item = highlight; |
|
621 |
|
622 if (trackedItem && item != trackedItem) { |
|
623 QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); |
|
624 QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); |
|
625 trackedItem = 0; |
|
626 } |
|
627 |
|
628 if (!trackedItem && item) { |
|
629 trackedItem = item; |
|
630 QObject::connect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); |
|
631 QObject::connect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); |
|
632 } |
|
633 if (trackedItem) |
|
634 q->trackedPositionChanged(); |
|
635 } |
|
636 |
|
637 void QDeclarativeGridViewPrivate::createHighlight() |
|
638 { |
|
639 Q_Q(QDeclarativeGridView); |
|
640 bool changed = false; |
|
641 if (highlight) { |
|
642 if (trackedItem == highlight) |
|
643 trackedItem = 0; |
|
644 delete highlight->item; |
|
645 delete highlight; |
|
646 highlight = 0; |
|
647 delete highlightXAnimator; |
|
648 delete highlightYAnimator; |
|
649 highlightXAnimator = 0; |
|
650 highlightYAnimator = 0; |
|
651 changed = true; |
|
652 } |
|
653 |
|
654 if (currentItem) { |
|
655 QDeclarativeItem *item = 0; |
|
656 if (highlightComponent) { |
|
657 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); |
|
658 QObject *nobj = highlightComponent->create(highlightContext); |
|
659 if (nobj) { |
|
660 QDeclarative_setParent_noEvent(highlightContext, nobj); |
|
661 item = qobject_cast<QDeclarativeItem *>(nobj); |
|
662 if (!item) |
|
663 delete nobj; |
|
664 } else { |
|
665 delete highlightContext; |
|
666 } |
|
667 } else { |
|
668 item = new QDeclarativeItem; |
|
669 QDeclarative_setParent_noEvent(item, q->viewport()); |
|
670 item->setParentItem(q->viewport()); |
|
671 } |
|
672 if (item) { |
|
673 QDeclarative_setParent_noEvent(item, q->viewport()); |
|
674 item->setParentItem(q->viewport()); |
|
675 highlight = new FxGridItem(item, q); |
|
676 highlightXAnimator = new QSmoothedAnimation(q); |
|
677 highlightXAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("x")); |
|
678 highlightXAnimator->userDuration = highlightMoveDuration; |
|
679 highlightYAnimator = new QSmoothedAnimation(q); |
|
680 highlightYAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("y")); |
|
681 highlightYAnimator->userDuration = highlightMoveDuration; |
|
682 if (autoHighlight) { |
|
683 highlightXAnimator->restart(); |
|
684 highlightYAnimator->restart(); |
|
685 } |
|
686 changed = true; |
|
687 } |
|
688 } |
|
689 if (changed) |
|
690 emit q->highlightItemChanged(); |
|
691 } |
|
692 |
|
693 void QDeclarativeGridViewPrivate::updateHighlight() |
|
694 { |
|
695 if ((!currentItem && highlight) || (currentItem && !highlight)) |
|
696 createHighlight(); |
|
697 if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { |
|
698 // auto-update highlight |
|
699 highlightXAnimator->to = currentItem->item->x(); |
|
700 highlightYAnimator->to = currentItem->item->y(); |
|
701 highlight->item->setWidth(currentItem->item->width()); |
|
702 highlight->item->setHeight(currentItem->item->height()); |
|
703 highlightXAnimator->restart(); |
|
704 highlightYAnimator->restart(); |
|
705 } |
|
706 updateTrackedItem(); |
|
707 } |
|
708 |
|
709 void QDeclarativeGridViewPrivate::updateCurrent(int modelIndex) |
|
710 { |
|
711 Q_Q(QDeclarativeGridView); |
|
712 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { |
|
713 if (currentItem) { |
|
714 currentItem->attached->setIsCurrentItem(false); |
|
715 releaseItem(currentItem); |
|
716 currentItem = 0; |
|
717 currentIndex = -1; |
|
718 updateHighlight(); |
|
719 emit q->currentIndexChanged(); |
|
720 } |
|
721 return; |
|
722 } |
|
723 |
|
724 if (currentItem && currentIndex == modelIndex) { |
|
725 updateHighlight(); |
|
726 return; |
|
727 } |
|
728 |
|
729 FxGridItem *oldCurrentItem = currentItem; |
|
730 currentIndex = modelIndex; |
|
731 currentItem = createItem(modelIndex); |
|
732 fixCurrentVisibility = true; |
|
733 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) |
|
734 oldCurrentItem->attached->setIsCurrentItem(false); |
|
735 if (currentItem) { |
|
736 currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex)); |
|
737 currentItem->item->setFocus(true); |
|
738 currentItem->attached->setIsCurrentItem(true); |
|
739 } |
|
740 updateHighlight(); |
|
741 emit q->currentIndexChanged(); |
|
742 releaseItem(oldCurrentItem); |
|
743 } |
|
744 |
|
745 void QDeclarativeGridViewPrivate::fixupPosition() |
|
746 { |
|
747 moveReason = Other; |
|
748 if (flow == QDeclarativeGridView::LeftToRight) |
|
749 fixupY(); |
|
750 else |
|
751 fixupX(); |
|
752 } |
|
753 |
|
754 void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) |
|
755 { |
|
756 Q_Q(QDeclarativeGridView); |
|
757 if ((flow == QDeclarativeGridView::TopToBottom && &data == &vData) |
|
758 || (flow == QDeclarativeGridView::LeftToRight && &data == &hData)) |
|
759 return; |
|
760 |
|
761 int oldDuration = fixupDuration; |
|
762 fixupDuration = moveReason == Mouse ? fixupDuration : 0; |
|
763 |
|
764 if (haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { |
|
765 if (currentItem) { |
|
766 updateHighlight(); |
|
767 qreal pos = currentItem->rowPos(); |
|
768 qreal viewPos = position(); |
|
769 if (viewPos < pos + rowSize() - highlightRangeEnd) |
|
770 viewPos = pos + rowSize() - highlightRangeEnd; |
|
771 if (viewPos > pos - highlightRangeStart) |
|
772 viewPos = pos - highlightRangeStart; |
|
773 |
|
774 timeline.reset(data.move); |
|
775 if (viewPos != position()) { |
|
776 if (fixupDuration) { |
|
777 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); |
|
778 } else { |
|
779 data.move.setValue(-viewPos); |
|
780 q->viewportMoved(); |
|
781 } |
|
782 } |
|
783 vTime = timeline.time(); |
|
784 } |
|
785 } else if (snapMode != QDeclarativeGridView::NoSnap) { |
|
786 qreal pos = -snapPosAt(-(data.move.value() - highlightRangeStart)) + highlightRangeStart; |
|
787 pos = qMin(qMax(pos, maxExtent), minExtent); |
|
788 qreal dist = qAbs(data.move.value() - pos); |
|
789 if (dist > 0) { |
|
790 timeline.reset(data.move); |
|
791 if (fixupDuration) { |
|
792 timeline.move(data.move, pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); |
|
793 } else { |
|
794 data.move.setValue(pos); |
|
795 q->viewportMoved(); |
|
796 } |
|
797 vTime = timeline.time(); |
|
798 } |
|
799 } else { |
|
800 QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); |
|
801 } |
|
802 fixupDuration = oldDuration; |
|
803 } |
|
804 |
|
805 void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, |
|
806 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) |
|
807 { |
|
808 Q_Q(QDeclarativeGridView); |
|
809 |
|
810 moveReason = Mouse; |
|
811 if ((!haveHighlightRange || highlightRange != QDeclarativeGridView::StrictlyEnforceRange) |
|
812 && snapMode == QDeclarativeGridView::NoSnap) { |
|
813 QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); |
|
814 return; |
|
815 } |
|
816 qreal maxDistance = 0; |
|
817 // -ve velocity means list is moving up |
|
818 if (velocity > 0) { |
|
819 if (data.move.value() < minExtent) { |
|
820 if (snapMode == QDeclarativeGridView::SnapOneRow) { |
|
821 if (FxGridItem *item = firstVisibleItem()) |
|
822 maxDistance = qAbs(item->rowPos() + data.move.value()); |
|
823 } else { |
|
824 maxDistance = qAbs(minExtent - data.move.value()); |
|
825 } |
|
826 } |
|
827 if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) |
|
828 data.flickTarget = minExtent; |
|
829 } else { |
|
830 if (data.move.value() > maxExtent) { |
|
831 if (snapMode == QDeclarativeGridView::SnapOneRow) { |
|
832 qreal pos = snapPosAt(-data.move.value()) + rowSize(); |
|
833 maxDistance = qAbs(pos + data.move.value()); |
|
834 } else { |
|
835 maxDistance = qAbs(maxExtent - data.move.value()); |
|
836 } |
|
837 } |
|
838 if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) |
|
839 data.flickTarget = maxExtent; |
|
840 } |
|
841 bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; |
|
842 if (maxDistance > 0 || overShoot) { |
|
843 // This mode requires the grid to stop exactly on a row boundary. |
|
844 qreal v = velocity; |
|
845 if (maxVelocity != -1 && maxVelocity < qAbs(v)) { |
|
846 if (v < 0) |
|
847 v = -maxVelocity; |
|
848 else |
|
849 v = maxVelocity; |
|
850 } |
|
851 qreal accel = deceleration; |
|
852 qreal v2 = v * v; |
|
853 qreal overshootDist = 0.0; |
|
854 if (maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) { |
|
855 // + rowSize()/4 to encourage moving at least one item in the flick direction |
|
856 qreal dist = v2 / (accel * 2.0) + rowSize()/4; |
|
857 if (v > 0) |
|
858 dist = -dist; |
|
859 data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; |
|
860 qreal adjDist = -data.flickTarget + data.move.value(); |
|
861 if (qAbs(adjDist) > qAbs(dist)) { |
|
862 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration |
|
863 qreal adjv2 = accel * 2.0f * qAbs(adjDist); |
|
864 if (adjv2 > v2) { |
|
865 v2 = adjv2; |
|
866 v = qSqrt(v2); |
|
867 if (dist > 0) |
|
868 v = -v; |
|
869 } |
|
870 } |
|
871 dist = adjDist; |
|
872 accel = v2 / (2.0f * qAbs(dist)); |
|
873 } else { |
|
874 data.flickTarget = velocity > 0 ? minExtent : maxExtent; |
|
875 overshootDist = overShoot ? overShootDistance(v, vSize) : 0; |
|
876 } |
|
877 timeline.reset(data.move); |
|
878 timeline.accel(data.move, v, accel, maxDistance + overshootDist); |
|
879 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); |
|
880 if (!flickingHorizontally && q->xflick()) { |
|
881 flickingHorizontally = true; |
|
882 emit q->flickingChanged(); |
|
883 emit q->flickingHorizontallyChanged(); |
|
884 emit q->flickStarted(); |
|
885 } |
|
886 if (!flickingVertically && q->yflick()) { |
|
887 flickingVertically = true; |
|
888 emit q->flickingChanged(); |
|
889 emit q->flickingVerticallyChanged(); |
|
890 emit q->flickStarted(); |
|
891 } |
|
892 } else { |
|
893 timeline.reset(data.move); |
|
894 fixup(data, minExtent, maxExtent); |
|
895 } |
|
896 } |
|
897 |
|
898 |
|
899 //---------------------------------------------------------------------------- |
|
900 |
|
901 /*! |
|
902 \qmlclass GridView QDeclarativeGridView |
|
903 \since 4.7 |
|
904 \inherits Flickable |
|
905 \brief The GridView item provides a grid view of items provided by a model. |
|
906 |
|
907 The model is typically provided by a QAbstractListModel "C++ model object", |
|
908 but can also be created directly in QML. |
|
909 |
|
910 The items are laid out top to bottom (vertically) or left to right (horizontally) |
|
911 and may be flicked to scroll. |
|
912 |
|
913 The below example creates a very simple grid, using a QML model. |
|
914 |
|
915 \image gridview.png |
|
916 |
|
917 \snippet doc/src/snippets/declarative/gridview/gridview.qml 3 |
|
918 |
|
919 The model is defined as a ListModel using QML: |
|
920 \quotefile doc/src/snippets/declarative/gridview/dummydata/ContactModel.qml |
|
921 |
|
922 In this case ListModel is a handy way for us to test our UI. In practice |
|
923 the model would be implemented in C++, or perhaps via a SQL data source. |
|
924 |
|
925 Delegates are instantiated as needed and may be destroyed at any time. |
|
926 State should \e never be stored in a delegate. |
|
927 |
|
928 \bold Note that views do not enable \e clip automatically. If the view |
|
929 is not clipped by another item or the screen, it will be necessary |
|
930 to set \e {clip: true} in order to have the out of view items clipped |
|
931 nicely. |
|
932 */ |
|
933 QDeclarativeGridView::QDeclarativeGridView(QDeclarativeItem *parent) |
|
934 : QDeclarativeFlickable(*(new QDeclarativeGridViewPrivate), parent) |
|
935 { |
|
936 Q_D(QDeclarativeGridView); |
|
937 d->init(); |
|
938 } |
|
939 |
|
940 QDeclarativeGridView::~QDeclarativeGridView() |
|
941 { |
|
942 Q_D(QDeclarativeGridView); |
|
943 d->clear(); |
|
944 if (d->ownModel) |
|
945 delete d->model; |
|
946 } |
|
947 |
|
948 /*! |
|
949 \qmlattachedproperty bool GridView::isCurrentItem |
|
950 This attched property is true if this delegate is the current item; otherwise false. |
|
951 |
|
952 It is attached to each instance of the delegate. |
|
953 */ |
|
954 |
|
955 /*! |
|
956 \qmlattachedproperty GridView GridView::view |
|
957 This attached property holds the view that manages this delegate instance. |
|
958 |
|
959 It is attached to each instance of the delegate. |
|
960 */ |
|
961 |
|
962 /*! |
|
963 \qmlattachedproperty bool GridView::delayRemove |
|
964 This attached property holds whether the delegate may be destroyed. |
|
965 |
|
966 It is attached to each instance of the delegate. |
|
967 |
|
968 It is sometimes necessary to delay the destruction of an item |
|
969 until an animation completes. |
|
970 |
|
971 The example below ensures that the animation completes before |
|
972 the item is removed from the grid. |
|
973 |
|
974 \code |
|
975 Component { |
|
976 id: myDelegate |
|
977 Item { |
|
978 id: wrapper |
|
979 GridView.onRemove: SequentialAnimation { |
|
980 PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: true } |
|
981 NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 250; easing.type: Easing.InOutQuad } |
|
982 PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false } |
|
983 } |
|
984 } |
|
985 } |
|
986 \endcode |
|
987 */ |
|
988 |
|
989 /*! |
|
990 \qmlattachedsignal GridView::onAdd() |
|
991 This attached handler is called immediately after an item is added to the view. |
|
992 */ |
|
993 |
|
994 /*! |
|
995 \qmlattachedsignal GridView::onRemove() |
|
996 This attached handler is called immediately before an item is removed from the view. |
|
997 */ |
|
998 |
|
999 |
|
1000 /*! |
|
1001 \qmlproperty model GridView::model |
|
1002 This property holds the model providing data for the grid. |
|
1003 |
|
1004 The model provides a set of data that is used to create the items |
|
1005 for the view. For large or dynamic datasets the model is usually |
|
1006 provided by a C++ model object. The C++ model object must be a \l |
|
1007 {QAbstractItemModel} subclass, a VisualModel, or a simple list. |
|
1008 |
|
1009 \sa {qmlmodels}{Data Models} |
|
1010 */ |
|
1011 QVariant QDeclarativeGridView::model() const |
|
1012 { |
|
1013 Q_D(const QDeclarativeGridView); |
|
1014 return d->modelVariant; |
|
1015 } |
|
1016 |
|
1017 void QDeclarativeGridView::setModel(const QVariant &model) |
|
1018 { |
|
1019 Q_D(QDeclarativeGridView); |
|
1020 if (d->modelVariant == model) |
|
1021 return; |
|
1022 if (d->model) { |
|
1023 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); |
|
1024 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); |
|
1025 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); |
|
1026 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); |
|
1027 disconnect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); |
|
1028 disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); |
|
1029 } |
|
1030 d->clear(); |
|
1031 d->modelVariant = model; |
|
1032 QObject *object = qvariant_cast<QObject*>(model); |
|
1033 QDeclarativeVisualModel *vim = 0; |
|
1034 if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) { |
|
1035 if (d->ownModel) { |
|
1036 delete d->model; |
|
1037 d->ownModel = false; |
|
1038 } |
|
1039 d->model = vim; |
|
1040 } else { |
|
1041 if (!d->ownModel) { |
|
1042 d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); |
|
1043 d->ownModel = true; |
|
1044 } |
|
1045 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
|
1046 dataModel->setModel(model); |
|
1047 } |
|
1048 if (d->model) { |
|
1049 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore | QDeclarativeGridViewPrivate::BufferAfter; |
|
1050 if (isComponentComplete()) { |
|
1051 refill(); |
|
1052 if (d->currentIndex >= d->model->count() || d->currentIndex < 0) { |
|
1053 setCurrentIndex(0); |
|
1054 } else { |
|
1055 d->moveReason = QDeclarativeGridViewPrivate::SetIndex; |
|
1056 d->updateCurrent(d->currentIndex); |
|
1057 } |
|
1058 } |
|
1059 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); |
|
1060 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); |
|
1061 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); |
|
1062 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); |
|
1063 connect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); |
|
1064 connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); |
|
1065 emit countChanged(); |
|
1066 } |
|
1067 emit modelChanged(); |
|
1068 } |
|
1069 |
|
1070 /*! |
|
1071 \qmlproperty Component GridView::delegate |
|
1072 |
|
1073 The delegate provides a template defining each item instantiated by the view. |
|
1074 The index is exposed as an accessible \c index property. Properties of the |
|
1075 model are also available depending upon the type of \l {qmlmodels}{Data Model}. |
|
1076 |
|
1077 The number of elements in the delegate has a direct effect on the |
|
1078 flicking performance of the view. If at all possible, place functionality |
|
1079 that is not needed for the normal display of the delegate in a \l Loader which |
|
1080 can load additional elements when needed. |
|
1081 |
|
1082 Note that the GridView will layout the items based on the size of the root item |
|
1083 in the delegate. |
|
1084 |
|
1085 Here is an example delegate: |
|
1086 \snippet doc/src/snippets/declarative/gridview/gridview.qml 0 |
|
1087 */ |
|
1088 QDeclarativeComponent *QDeclarativeGridView::delegate() const |
|
1089 { |
|
1090 Q_D(const QDeclarativeGridView); |
|
1091 if (d->model) { |
|
1092 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
|
1093 return dataModel->delegate(); |
|
1094 } |
|
1095 |
|
1096 return 0; |
|
1097 } |
|
1098 |
|
1099 void QDeclarativeGridView::setDelegate(QDeclarativeComponent *delegate) |
|
1100 { |
|
1101 Q_D(QDeclarativeGridView); |
|
1102 if (delegate == this->delegate()) |
|
1103 return; |
|
1104 |
|
1105 if (!d->ownModel) { |
|
1106 d->model = new QDeclarativeVisualDataModel(qmlContext(this)); |
|
1107 d->ownModel = true; |
|
1108 } |
|
1109 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) { |
|
1110 dataModel->setDelegate(delegate); |
|
1111 if (isComponentComplete()) { |
|
1112 refill(); |
|
1113 d->moveReason = QDeclarativeGridViewPrivate::SetIndex; |
|
1114 d->updateCurrent(d->currentIndex); |
|
1115 } |
|
1116 emit delegateChanged(); |
|
1117 } |
|
1118 } |
|
1119 |
|
1120 /*! |
|
1121 \qmlproperty int GridView::currentIndex |
|
1122 \qmlproperty Item GridView::currentItem |
|
1123 |
|
1124 \c currentIndex holds the index of the current item. |
|
1125 \c currentItem is the current item. Note that the position of the current item |
|
1126 may only be approximate until it becomes visible in the view. |
|
1127 */ |
|
1128 int QDeclarativeGridView::currentIndex() const |
|
1129 { |
|
1130 Q_D(const QDeclarativeGridView); |
|
1131 return d->currentIndex; |
|
1132 } |
|
1133 |
|
1134 void QDeclarativeGridView::setCurrentIndex(int index) |
|
1135 { |
|
1136 Q_D(QDeclarativeGridView); |
|
1137 if (d->requestedIndex >= 0) // currently creating item |
|
1138 return; |
|
1139 if (isComponentComplete() && d->isValid() && index != d->currentIndex && index < d->model->count() && index >= 0) { |
|
1140 d->moveReason = QDeclarativeGridViewPrivate::SetIndex; |
|
1141 cancelFlick(); |
|
1142 d->updateCurrent(index); |
|
1143 } else if (index != d->currentIndex) { |
|
1144 d->currentIndex = index; |
|
1145 emit currentIndexChanged(); |
|
1146 } |
|
1147 } |
|
1148 |
|
1149 QDeclarativeItem *QDeclarativeGridView::currentItem() |
|
1150 { |
|
1151 Q_D(QDeclarativeGridView); |
|
1152 if (!d->currentItem) |
|
1153 return 0; |
|
1154 return d->currentItem->item; |
|
1155 } |
|
1156 |
|
1157 /*! |
|
1158 \qmlproperty Item GridView::highlightItem |
|
1159 |
|
1160 \c highlightItem holds the highlight item, which was created |
|
1161 from the \l highlight component. |
|
1162 |
|
1163 The highlightItem is managed by the view unless |
|
1164 \l highlightFollowsCurrentItem is set to false. |
|
1165 |
|
1166 \sa highlight, highlightFollowsCurrentItem |
|
1167 */ |
|
1168 QDeclarativeItem *QDeclarativeGridView::highlightItem() |
|
1169 { |
|
1170 Q_D(QDeclarativeGridView); |
|
1171 if (!d->highlight) |
|
1172 return 0; |
|
1173 return d->highlight->item; |
|
1174 } |
|
1175 |
|
1176 /*! |
|
1177 \qmlproperty int GridView::count |
|
1178 This property holds the number of items in the view. |
|
1179 */ |
|
1180 int QDeclarativeGridView::count() const |
|
1181 { |
|
1182 Q_D(const QDeclarativeGridView); |
|
1183 if (d->model) |
|
1184 return d->model->count(); |
|
1185 return 0; |
|
1186 } |
|
1187 |
|
1188 /*! |
|
1189 \qmlproperty Component GridView::highlight |
|
1190 This property holds the component to use as the highlight. |
|
1191 |
|
1192 An instance of the highlight component will be created for each view. |
|
1193 The geometry of the resultant component instance will be managed by the view |
|
1194 so as to stay with the current item, unless the highlightFollowsCurrentItem property is false. |
|
1195 |
|
1196 The below example demonstrates how to make a simple highlight: |
|
1197 \snippet doc/src/snippets/declarative/gridview/gridview.qml 1 |
|
1198 |
|
1199 \sa highlightItem, highlightFollowsCurrentItem |
|
1200 */ |
|
1201 QDeclarativeComponent *QDeclarativeGridView::highlight() const |
|
1202 { |
|
1203 Q_D(const QDeclarativeGridView); |
|
1204 return d->highlightComponent; |
|
1205 } |
|
1206 |
|
1207 void QDeclarativeGridView::setHighlight(QDeclarativeComponent *highlight) |
|
1208 { |
|
1209 Q_D(QDeclarativeGridView); |
|
1210 if (highlight != d->highlightComponent) { |
|
1211 d->highlightComponent = highlight; |
|
1212 d->updateCurrent(d->currentIndex); |
|
1213 emit highlightChanged(); |
|
1214 } |
|
1215 } |
|
1216 |
|
1217 /*! |
|
1218 \qmlproperty bool GridView::highlightFollowsCurrentItem |
|
1219 This property sets whether the highlight is managed by the view. |
|
1220 |
|
1221 If highlightFollowsCurrentItem is true, the highlight will be moved smoothly |
|
1222 to follow the current item. If highlightFollowsCurrentItem is false, the |
|
1223 highlight will not be moved by the view, and must be implemented |
|
1224 by the highlight component, for example: |
|
1225 |
|
1226 \code |
|
1227 Component { |
|
1228 id: myHighlight |
|
1229 Rectangle { |
|
1230 id: wrapper; color: "lightsteelblue"; radius: 4; width: 320; height: 60 |
|
1231 SpringFollow on y { source: wrapper.GridView.view.currentItem.y; spring: 3; damping: 0.2 } |
|
1232 SpringFollow on x { source: wrapper.GridView.view.currentItem.x; spring: 3; damping: 0.2 } |
|
1233 } |
|
1234 } |
|
1235 \endcode |
|
1236 */ |
|
1237 bool QDeclarativeGridView::highlightFollowsCurrentItem() const |
|
1238 { |
|
1239 Q_D(const QDeclarativeGridView); |
|
1240 return d->autoHighlight; |
|
1241 } |
|
1242 |
|
1243 void QDeclarativeGridView::setHighlightFollowsCurrentItem(bool autoHighlight) |
|
1244 { |
|
1245 Q_D(QDeclarativeGridView); |
|
1246 if (d->autoHighlight != autoHighlight) { |
|
1247 d->autoHighlight = autoHighlight; |
|
1248 if (autoHighlight) { |
|
1249 d->updateHighlight(); |
|
1250 } else if (d->highlightXAnimator) { |
|
1251 d->highlightXAnimator->stop(); |
|
1252 d->highlightYAnimator->stop(); |
|
1253 } |
|
1254 } |
|
1255 } |
|
1256 |
|
1257 /*! |
|
1258 \qmlproperty int GridView::highlightMoveDuration |
|
1259 This property holds the move animation duration of the highlight delegate. |
|
1260 |
|
1261 highlightFollowsCurrentItem must be true for this property |
|
1262 to have effect. |
|
1263 |
|
1264 The default value for the duration is 150ms. |
|
1265 |
|
1266 \sa highlightFollowsCurrentItem |
|
1267 */ |
|
1268 int QDeclarativeGridView::highlightMoveDuration() const |
|
1269 { |
|
1270 Q_D(const QDeclarativeGridView); |
|
1271 return d->highlightMoveDuration; |
|
1272 } |
|
1273 |
|
1274 void QDeclarativeGridView::setHighlightMoveDuration(int duration) |
|
1275 { |
|
1276 Q_D(QDeclarativeGridView); |
|
1277 if (d->highlightMoveDuration != duration) { |
|
1278 d->highlightMoveDuration = duration; |
|
1279 if (d->highlightYAnimator) { |
|
1280 d->highlightXAnimator->userDuration = d->highlightMoveDuration; |
|
1281 d->highlightYAnimator->userDuration = d->highlightMoveDuration; |
|
1282 } |
|
1283 emit highlightMoveDurationChanged(); |
|
1284 } |
|
1285 } |
|
1286 |
|
1287 |
|
1288 /*! |
|
1289 \qmlproperty real GridView::preferredHighlightBegin |
|
1290 \qmlproperty real GridView::preferredHighlightEnd |
|
1291 \qmlproperty enumeration GridView::highlightRangeMode |
|
1292 |
|
1293 These properties set the preferred range of the highlight (current item) |
|
1294 within the view. |
|
1295 |
|
1296 Note that this is the correct way to influence where the |
|
1297 current item ends up when the view scrolls. For example, if you want the |
|
1298 currently selected item to be in the middle of the list, then set the |
|
1299 highlight range to be where the middle item would go. Then, when the view scrolls, |
|
1300 the currently selected item will be the item at that spot. This also applies to |
|
1301 when the currently selected item changes - it will scroll to within the preferred |
|
1302 highlight range. Furthermore, the behaviour of the current item index will occur |
|
1303 whether or not a highlight exists. |
|
1304 |
|
1305 If highlightRangeMode is set to \e GridView.ApplyRange the view will |
|
1306 attempt to maintain the highlight within the range, however |
|
1307 the highlight can move outside of the range at the ends of the list |
|
1308 or due to a mouse interaction. |
|
1309 |
|
1310 If highlightRangeMode is set to \e GridView.StrictlyEnforceRange the highlight will never |
|
1311 move outside of the range. This means that the current item will change |
|
1312 if a keyboard or mouse action would cause the highlight to move |
|
1313 outside of the range. |
|
1314 |
|
1315 The default value is \e GridView.NoHighlightRange. |
|
1316 |
|
1317 Note that a valid range requires preferredHighlightEnd to be greater |
|
1318 than or equal to preferredHighlightBegin. |
|
1319 */ |
|
1320 qreal QDeclarativeGridView::preferredHighlightBegin() const |
|
1321 { |
|
1322 Q_D(const QDeclarativeGridView); |
|
1323 return d->highlightRangeStart; |
|
1324 } |
|
1325 |
|
1326 void QDeclarativeGridView::setPreferredHighlightBegin(qreal start) |
|
1327 { |
|
1328 Q_D(QDeclarativeGridView); |
|
1329 if (d->highlightRangeStart == start) |
|
1330 return; |
|
1331 d->highlightRangeStart = start; |
|
1332 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; |
|
1333 emit preferredHighlightBeginChanged(); |
|
1334 } |
|
1335 |
|
1336 qreal QDeclarativeGridView::preferredHighlightEnd() const |
|
1337 { |
|
1338 Q_D(const QDeclarativeGridView); |
|
1339 return d->highlightRangeEnd; |
|
1340 } |
|
1341 |
|
1342 void QDeclarativeGridView::setPreferredHighlightEnd(qreal end) |
|
1343 { |
|
1344 Q_D(QDeclarativeGridView); |
|
1345 if (d->highlightRangeEnd == end) |
|
1346 return; |
|
1347 d->highlightRangeEnd = end; |
|
1348 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; |
|
1349 emit preferredHighlightEndChanged(); |
|
1350 } |
|
1351 |
|
1352 QDeclarativeGridView::HighlightRangeMode QDeclarativeGridView::highlightRangeMode() const |
|
1353 { |
|
1354 Q_D(const QDeclarativeGridView); |
|
1355 return d->highlightRange; |
|
1356 } |
|
1357 |
|
1358 void QDeclarativeGridView::setHighlightRangeMode(HighlightRangeMode mode) |
|
1359 { |
|
1360 Q_D(QDeclarativeGridView); |
|
1361 if (d->highlightRange == mode) |
|
1362 return; |
|
1363 d->highlightRange = mode; |
|
1364 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; |
|
1365 emit highlightRangeModeChanged(); |
|
1366 } |
|
1367 |
|
1368 |
|
1369 /*! |
|
1370 \qmlproperty enumeration GridView::flow |
|
1371 This property holds the flow of the grid. |
|
1372 |
|
1373 Possible values are \c GridView.LeftToRight (default) and \c GridView.TopToBottom. |
|
1374 |
|
1375 If \a flow is \c GridView.LeftToRight, the view will scroll vertically. |
|
1376 If \a flow is \c GridView.TopToBottom, the view will scroll horizontally. |
|
1377 */ |
|
1378 QDeclarativeGridView::Flow QDeclarativeGridView::flow() const |
|
1379 { |
|
1380 Q_D(const QDeclarativeGridView); |
|
1381 return d->flow; |
|
1382 } |
|
1383 |
|
1384 void QDeclarativeGridView::setFlow(Flow flow) |
|
1385 { |
|
1386 Q_D(QDeclarativeGridView); |
|
1387 if (d->flow != flow) { |
|
1388 d->flow = flow; |
|
1389 if (d->flow == LeftToRight) { |
|
1390 setContentWidth(-1); |
|
1391 setFlickableDirection(QDeclarativeFlickable::VerticalFlick); |
|
1392 } else { |
|
1393 setContentHeight(-1); |
|
1394 setFlickableDirection(QDeclarativeFlickable::HorizontalFlick); |
|
1395 } |
|
1396 d->clear(); |
|
1397 d->updateGrid(); |
|
1398 refill(); |
|
1399 d->updateCurrent(d->currentIndex); |
|
1400 emit flowChanged(); |
|
1401 } |
|
1402 } |
|
1403 |
|
1404 /*! |
|
1405 \qmlproperty bool GridView::keyNavigationWraps |
|
1406 This property holds whether the grid wraps key navigation |
|
1407 |
|
1408 If this property is true then key presses to move off of one end of the grid will cause the |
|
1409 selection to jump to the other side. |
|
1410 */ |
|
1411 bool QDeclarativeGridView::isWrapEnabled() const |
|
1412 { |
|
1413 Q_D(const QDeclarativeGridView); |
|
1414 return d->wrap; |
|
1415 } |
|
1416 |
|
1417 void QDeclarativeGridView::setWrapEnabled(bool wrap) |
|
1418 { |
|
1419 Q_D(QDeclarativeGridView); |
|
1420 if (d->wrap == wrap) |
|
1421 return; |
|
1422 d->wrap = wrap; |
|
1423 emit keyNavigationWrapsChanged(); |
|
1424 } |
|
1425 |
|
1426 /*! |
|
1427 \qmlproperty int GridView::cacheBuffer |
|
1428 This property determines whether delegates are retained outside the |
|
1429 visible area of the view. |
|
1430 |
|
1431 If non-zero the view will keep as many delegates |
|
1432 instantiated as will fit within the buffer specified. For example, |
|
1433 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is |
|
1434 set to 40, then up to 2 delegates above and 2 delegates below the visible |
|
1435 area may be retained. |
|
1436 |
|
1437 Note that cacheBuffer is not a pixel buffer - it only maintains additional |
|
1438 instantiated delegates. |
|
1439 |
|
1440 Setting this value can make scrolling the list smoother at the expense |
|
1441 of additional memory usage. It is not a substitute for creating efficient |
|
1442 delegates; the fewer elements in a delegate, the faster a view may be |
|
1443 scrolled. |
|
1444 */ |
|
1445 int QDeclarativeGridView::cacheBuffer() const |
|
1446 { |
|
1447 Q_D(const QDeclarativeGridView); |
|
1448 return d->buffer; |
|
1449 } |
|
1450 |
|
1451 void QDeclarativeGridView::setCacheBuffer(int buffer) |
|
1452 { |
|
1453 Q_D(QDeclarativeGridView); |
|
1454 if (d->buffer != buffer) { |
|
1455 d->buffer = buffer; |
|
1456 if (isComponentComplete()) |
|
1457 refill(); |
|
1458 emit cacheBufferChanged(); |
|
1459 } |
|
1460 } |
|
1461 |
|
1462 /*! |
|
1463 \qmlproperty int GridView::cellWidth |
|
1464 \qmlproperty int GridView::cellHeight |
|
1465 |
|
1466 These properties holds the width and height of each cell in the grid |
|
1467 |
|
1468 The default cell size is 100x100. |
|
1469 */ |
|
1470 int QDeclarativeGridView::cellWidth() const |
|
1471 { |
|
1472 Q_D(const QDeclarativeGridView); |
|
1473 return d->cellWidth; |
|
1474 } |
|
1475 |
|
1476 void QDeclarativeGridView::setCellWidth(int cellWidth) |
|
1477 { |
|
1478 Q_D(QDeclarativeGridView); |
|
1479 if (cellWidth != d->cellWidth && cellWidth > 0) { |
|
1480 d->cellWidth = qMax(1, cellWidth); |
|
1481 d->updateGrid(); |
|
1482 emit cellWidthChanged(); |
|
1483 d->layout(); |
|
1484 } |
|
1485 } |
|
1486 |
|
1487 int QDeclarativeGridView::cellHeight() const |
|
1488 { |
|
1489 Q_D(const QDeclarativeGridView); |
|
1490 return d->cellHeight; |
|
1491 } |
|
1492 |
|
1493 void QDeclarativeGridView::setCellHeight(int cellHeight) |
|
1494 { |
|
1495 Q_D(QDeclarativeGridView); |
|
1496 if (cellHeight != d->cellHeight && cellHeight > 0) { |
|
1497 d->cellHeight = qMax(1, cellHeight); |
|
1498 d->updateGrid(); |
|
1499 emit cellHeightChanged(); |
|
1500 d->layout(); |
|
1501 } |
|
1502 } |
|
1503 /*! |
|
1504 \qmlproperty enumeration GridView::snapMode |
|
1505 |
|
1506 This property determines where the view will settle following a drag or flick. |
|
1507 The allowed values are: |
|
1508 |
|
1509 \list |
|
1510 \o GridView.NoSnap (default) - the view will stop anywhere within the visible area. |
|
1511 \o GridView.SnapToRow - the view will settle with a row (or column for TopToBottom flow) |
|
1512 aligned with the start of the view. |
|
1513 \o GridView.SnapOneRow - the view will settle no more than one row (or column for TopToBottom flow) |
|
1514 away from the first visible row at the time the mouse button is released. |
|
1515 This mode is particularly useful for moving one page at a time. |
|
1516 \endlist |
|
1517 |
|
1518 */ |
|
1519 QDeclarativeGridView::SnapMode QDeclarativeGridView::snapMode() const |
|
1520 { |
|
1521 Q_D(const QDeclarativeGridView); |
|
1522 return d->snapMode; |
|
1523 } |
|
1524 |
|
1525 void QDeclarativeGridView::setSnapMode(SnapMode mode) |
|
1526 { |
|
1527 Q_D(QDeclarativeGridView); |
|
1528 if (d->snapMode != mode) { |
|
1529 d->snapMode = mode; |
|
1530 emit snapModeChanged(); |
|
1531 } |
|
1532 } |
|
1533 |
|
1534 bool QDeclarativeGridView::event(QEvent *event) |
|
1535 { |
|
1536 Q_D(QDeclarativeGridView); |
|
1537 if (event->type() == QEvent::User) { |
|
1538 d->layout(); |
|
1539 return true; |
|
1540 } |
|
1541 |
|
1542 return QDeclarativeFlickable::event(event); |
|
1543 } |
|
1544 |
|
1545 void QDeclarativeGridView::viewportMoved() |
|
1546 { |
|
1547 Q_D(QDeclarativeGridView); |
|
1548 QDeclarativeFlickable::viewportMoved(); |
|
1549 d->lazyRelease = true; |
|
1550 if (d->flickingHorizontally || d->flickingVertically) { |
|
1551 if (yflick()) { |
|
1552 if (d->vData.velocity > 0) |
|
1553 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; |
|
1554 else if (d->vData.velocity < 0) |
|
1555 d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; |
|
1556 } |
|
1557 |
|
1558 if (xflick()) { |
|
1559 if (d->hData.velocity > 0) |
|
1560 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; |
|
1561 else if (d->hData.velocity < 0) |
|
1562 d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; |
|
1563 } |
|
1564 } |
|
1565 refill(); |
|
1566 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) |
|
1567 d->moveReason = QDeclarativeGridViewPrivate::Mouse; |
|
1568 if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) { |
|
1569 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { |
|
1570 // reposition highlight |
|
1571 qreal pos = d->highlight->rowPos(); |
|
1572 qreal viewPos = d->position(); |
|
1573 if (pos > viewPos + d->highlightRangeEnd - d->rowSize()) |
|
1574 pos = viewPos + d->highlightRangeEnd - d->rowSize(); |
|
1575 if (pos < viewPos + d->highlightRangeStart) |
|
1576 pos = viewPos + d->highlightRangeStart; |
|
1577 d->highlight->setPosition(d->highlight->colPos(), qRound(pos)); |
|
1578 |
|
1579 // update current index |
|
1580 int idx = d->snapIndex(); |
|
1581 if (idx >= 0 && idx != d->currentIndex) { |
|
1582 d->updateCurrent(idx); |
|
1583 if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) { |
|
1584 if (d->flow == LeftToRight) |
|
1585 d->highlightXAnimator->to = d->currentItem->item->x(); |
|
1586 else |
|
1587 d->highlightYAnimator->to = d->currentItem->item->y(); |
|
1588 } |
|
1589 } |
|
1590 } |
|
1591 } |
|
1592 } |
|
1593 |
|
1594 qreal QDeclarativeGridView::minYExtent() const |
|
1595 { |
|
1596 Q_D(const QDeclarativeGridView); |
|
1597 if (d->flow == QDeclarativeGridView::TopToBottom) |
|
1598 return QDeclarativeFlickable::minYExtent(); |
|
1599 qreal extent = -d->startPosition(); |
|
1600 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { |
|
1601 extent += d->highlightRangeStart; |
|
1602 extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); |
|
1603 } |
|
1604 return extent; |
|
1605 } |
|
1606 |
|
1607 qreal QDeclarativeGridView::maxYExtent() const |
|
1608 { |
|
1609 Q_D(const QDeclarativeGridView); |
|
1610 if (d->flow == QDeclarativeGridView::TopToBottom) |
|
1611 return QDeclarativeFlickable::maxYExtent(); |
|
1612 qreal extent; |
|
1613 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { |
|
1614 extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); |
|
1615 if (d->highlightRangeEnd != d->highlightRangeStart) |
|
1616 extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); |
|
1617 } else { |
|
1618 extent = -(d->endPosition() - height()); |
|
1619 } |
|
1620 const qreal minY = minYExtent(); |
|
1621 if (extent > minY) |
|
1622 extent = minY; |
|
1623 return extent; |
|
1624 } |
|
1625 |
|
1626 qreal QDeclarativeGridView::minXExtent() const |
|
1627 { |
|
1628 Q_D(const QDeclarativeGridView); |
|
1629 if (d->flow == QDeclarativeGridView::LeftToRight) |
|
1630 return QDeclarativeFlickable::minXExtent(); |
|
1631 qreal extent = -d->startPosition(); |
|
1632 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { |
|
1633 extent += d->highlightRangeStart; |
|
1634 extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); |
|
1635 } |
|
1636 return extent; |
|
1637 } |
|
1638 |
|
1639 qreal QDeclarativeGridView::maxXExtent() const |
|
1640 { |
|
1641 Q_D(const QDeclarativeGridView); |
|
1642 if (d->flow == QDeclarativeGridView::LeftToRight) |
|
1643 return QDeclarativeFlickable::maxXExtent(); |
|
1644 qreal extent; |
|
1645 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { |
|
1646 extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); |
|
1647 if (d->highlightRangeEnd != d->highlightRangeStart) |
|
1648 extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); |
|
1649 } else { |
|
1650 extent = -(d->endPosition() - height()); |
|
1651 } |
|
1652 const qreal minX = minXExtent(); |
|
1653 if (extent > minX) |
|
1654 extent = minX; |
|
1655 return extent; |
|
1656 } |
|
1657 |
|
1658 void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) |
|
1659 { |
|
1660 Q_D(QDeclarativeGridView); |
|
1661 keyPressPreHandler(event); |
|
1662 if (event->isAccepted()) |
|
1663 return; |
|
1664 if (d->model && d->model->count() && d->interactive) { |
|
1665 d->moveReason = QDeclarativeGridViewPrivate::SetIndex; |
|
1666 int oldCurrent = currentIndex(); |
|
1667 switch (event->key()) { |
|
1668 case Qt::Key_Up: |
|
1669 moveCurrentIndexUp(); |
|
1670 break; |
|
1671 case Qt::Key_Down: |
|
1672 moveCurrentIndexDown(); |
|
1673 break; |
|
1674 case Qt::Key_Left: |
|
1675 moveCurrentIndexLeft(); |
|
1676 break; |
|
1677 case Qt::Key_Right: |
|
1678 moveCurrentIndexRight(); |
|
1679 break; |
|
1680 default: |
|
1681 break; |
|
1682 } |
|
1683 if (oldCurrent != currentIndex()) { |
|
1684 event->accept(); |
|
1685 return; |
|
1686 } |
|
1687 } |
|
1688 d->moveReason = QDeclarativeGridViewPrivate::Other; |
|
1689 event->ignore(); |
|
1690 QDeclarativeFlickable::keyPressEvent(event); |
|
1691 } |
|
1692 |
|
1693 /*! |
|
1694 \qmlmethod GridView::moveCurrentIndexUp() |
|
1695 |
|
1696 Move the currentIndex up one item in the view. |
|
1697 The current index will wrap if keyNavigationWraps is true and it |
|
1698 is currently at the end. |
|
1699 */ |
|
1700 void QDeclarativeGridView::moveCurrentIndexUp() |
|
1701 { |
|
1702 Q_D(QDeclarativeGridView); |
|
1703 if (d->flow == QDeclarativeGridView::LeftToRight) { |
|
1704 if (currentIndex() >= d->columns || d->wrap) { |
|
1705 int index = currentIndex() - d->columns; |
|
1706 setCurrentIndex(index >= 0 ? index : d->model->count()-1); |
|
1707 } |
|
1708 } else { |
|
1709 if (currentIndex() > 0 || d->wrap) { |
|
1710 int index = currentIndex() - 1; |
|
1711 setCurrentIndex(index >= 0 ? index : d->model->count()-1); |
|
1712 } |
|
1713 } |
|
1714 } |
|
1715 |
|
1716 /*! |
|
1717 \qmlmethod GridView::moveCurrentIndexDown() |
|
1718 |
|
1719 Move the currentIndex down one item in the view. |
|
1720 The current index will wrap if keyNavigationWraps is true and it |
|
1721 is currently at the end. |
|
1722 */ |
|
1723 void QDeclarativeGridView::moveCurrentIndexDown() |
|
1724 { |
|
1725 Q_D(QDeclarativeGridView); |
|
1726 if (d->flow == QDeclarativeGridView::LeftToRight) { |
|
1727 if (currentIndex() < d->model->count() - d->columns || d->wrap) { |
|
1728 int index = currentIndex()+d->columns; |
|
1729 setCurrentIndex(index < d->model->count() ? index : 0); |
|
1730 } |
|
1731 } else { |
|
1732 if (currentIndex() < d->model->count() - 1 || d->wrap) { |
|
1733 int index = currentIndex() + 1; |
|
1734 setCurrentIndex(index < d->model->count() ? index : 0); |
|
1735 } |
|
1736 } |
|
1737 } |
|
1738 |
|
1739 /*! |
|
1740 \qmlmethod GridView::moveCurrentIndexLeft() |
|
1741 |
|
1742 Move the currentIndex left one item in the view. |
|
1743 The current index will wrap if keyNavigationWraps is true and it |
|
1744 is currently at the end. |
|
1745 */ |
|
1746 void QDeclarativeGridView::moveCurrentIndexLeft() |
|
1747 { |
|
1748 Q_D(QDeclarativeGridView); |
|
1749 if (d->flow == QDeclarativeGridView::LeftToRight) { |
|
1750 if (currentIndex() > 0 || d->wrap) { |
|
1751 int index = currentIndex() - 1; |
|
1752 setCurrentIndex(index >= 0 ? index : d->model->count()-1); |
|
1753 } |
|
1754 } else { |
|
1755 if (currentIndex() >= d->columns || d->wrap) { |
|
1756 int index = currentIndex() - d->columns; |
|
1757 setCurrentIndex(index >= 0 ? index : d->model->count()-1); |
|
1758 } |
|
1759 } |
|
1760 } |
|
1761 |
|
1762 /*! |
|
1763 \qmlmethod GridView::moveCurrentIndexRight() |
|
1764 |
|
1765 Move the currentIndex right one item in the view. |
|
1766 The current index will wrap if keyNavigationWraps is true and it |
|
1767 is currently at the end. |
|
1768 */ |
|
1769 void QDeclarativeGridView::moveCurrentIndexRight() |
|
1770 { |
|
1771 Q_D(QDeclarativeGridView); |
|
1772 if (d->flow == QDeclarativeGridView::LeftToRight) { |
|
1773 if (currentIndex() < d->model->count() - 1 || d->wrap) { |
|
1774 int index = currentIndex() + 1; |
|
1775 setCurrentIndex(index < d->model->count() ? index : 0); |
|
1776 } |
|
1777 } else { |
|
1778 if (currentIndex() < d->model->count() - d->columns || d->wrap) { |
|
1779 int index = currentIndex()+d->columns; |
|
1780 setCurrentIndex(index < d->model->count() ? index : 0); |
|
1781 } |
|
1782 } |
|
1783 } |
|
1784 |
|
1785 /*! |
|
1786 \qmlmethod GridView::positionViewAtIndex(int index, PositionMode mode) |
|
1787 |
|
1788 Positions the view such that the \a index is at the position specified by |
|
1789 \a mode: |
|
1790 |
|
1791 \list |
|
1792 \o Beginning - position item at the top (or left for TopToBottom flow) of the view. |
|
1793 \o Center- position item in the center of the view. |
|
1794 \o End - position item at bottom (or right for horizontal orientation) of the view. |
|
1795 \o Visible - if any part of the item is visible then take no action, otherwise |
|
1796 bring the item into view. |
|
1797 \o Contain - ensure the entire item is visible. If the item is larger than |
|
1798 the view the item is positioned at the top (or left for TopToBottom flow) of the view. |
|
1799 \endlist |
|
1800 |
|
1801 If positioning the view at the index would cause empty space to be displayed at |
|
1802 the beginning or end of the view, the view will be positioned at the boundary. |
|
1803 |
|
1804 It is not recommended to use contentX or contentY to position the view |
|
1805 at a particular index. This is unreliable since removing items from the start |
|
1806 of the view does not cause all other items to be repositioned. |
|
1807 The correct way to bring an item into view is with positionViewAtIndex. |
|
1808 */ |
|
1809 void QDeclarativeGridView::positionViewAtIndex(int index, int mode) |
|
1810 { |
|
1811 Q_D(QDeclarativeGridView); |
|
1812 if (!d->isValid() || index < 0 || index >= d->model->count()) |
|
1813 return; |
|
1814 if (mode < Beginning || mode > Contain) |
|
1815 return; |
|
1816 |
|
1817 qreal pos = d->position(); |
|
1818 FxGridItem *item = d->visibleItem(index); |
|
1819 if (!item) { |
|
1820 int itemPos = d->rowPosAt(index); |
|
1821 // save the currently visible items in case any of them end up visible again |
|
1822 QList<FxGridItem*> oldVisible = d->visibleItems; |
|
1823 d->visibleItems.clear(); |
|
1824 d->visibleIndex = index - index % d->columns; |
|
1825 d->setPosition(itemPos); |
|
1826 // now release the reference to all the old visible items. |
|
1827 for (int i = 0; i < oldVisible.count(); ++i) |
|
1828 d->releaseItem(oldVisible.at(i)); |
|
1829 item = d->visibleItem(index); |
|
1830 } |
|
1831 if (item) { |
|
1832 qreal itemPos = item->rowPos(); |
|
1833 switch (mode) { |
|
1834 case Beginning: |
|
1835 pos = itemPos; |
|
1836 break; |
|
1837 case Center: |
|
1838 pos = itemPos - (d->size() - d->rowSize())/2; |
|
1839 break; |
|
1840 case End: |
|
1841 pos = itemPos - d->size() + d->rowSize(); |
|
1842 break; |
|
1843 case Visible: |
|
1844 if (itemPos > pos + d->size()) |
|
1845 pos = itemPos - d->size() + d->rowSize(); |
|
1846 else if (item->endRowPos() < pos) |
|
1847 pos = itemPos; |
|
1848 break; |
|
1849 case Contain: |
|
1850 if (item->endRowPos() > pos + d->size()) |
|
1851 pos = itemPos - d->size() + d->rowSize(); |
|
1852 if (itemPos < pos) |
|
1853 pos = itemPos; |
|
1854 } |
|
1855 qreal maxExtent = d->flow == QDeclarativeGridView::LeftToRight ? -maxYExtent() : -maxXExtent(); |
|
1856 pos = qMin(pos, maxExtent); |
|
1857 qreal minExtent = d->flow == QDeclarativeGridView::LeftToRight ? -minYExtent() : -minXExtent(); |
|
1858 pos = qMax(pos, minExtent); |
|
1859 d->setPosition(pos); |
|
1860 } |
|
1861 d->fixupPosition(); |
|
1862 } |
|
1863 |
|
1864 /*! |
|
1865 \qmlmethod int GridView::indexAt(int x, int y) |
|
1866 |
|
1867 Returns the index of the visible item containing the point \a x, \a y in content |
|
1868 coordinates. If there is no item at the point specified, or the item is |
|
1869 not visible -1 is returned. |
|
1870 |
|
1871 If the item is outside the visible area, -1 is returned, regardless of |
|
1872 whether an item will exist at that point when scrolled into view. |
|
1873 */ |
|
1874 int QDeclarativeGridView::indexAt(int x, int y) const |
|
1875 { |
|
1876 Q_D(const QDeclarativeGridView); |
|
1877 for (int i = 0; i < d->visibleItems.count(); ++i) { |
|
1878 const FxGridItem *listItem = d->visibleItems.at(i); |
|
1879 if(listItem->contains(x, y)) |
|
1880 return listItem->index; |
|
1881 } |
|
1882 |
|
1883 return -1; |
|
1884 } |
|
1885 |
|
1886 void QDeclarativeGridView::componentComplete() |
|
1887 { |
|
1888 Q_D(QDeclarativeGridView); |
|
1889 QDeclarativeFlickable::componentComplete(); |
|
1890 d->updateGrid(); |
|
1891 if (d->isValid()) { |
|
1892 refill(); |
|
1893 if (d->currentIndex < 0) |
|
1894 d->updateCurrent(0); |
|
1895 else |
|
1896 d->updateCurrent(d->currentIndex); |
|
1897 d->fixupPosition(); |
|
1898 } |
|
1899 } |
|
1900 |
|
1901 void QDeclarativeGridView::trackedPositionChanged() |
|
1902 { |
|
1903 Q_D(QDeclarativeGridView); |
|
1904 if (!d->trackedItem || !d->currentItem) |
|
1905 return; |
|
1906 if (!d->flickingHorizontally && !d->flickingVertically && !d->movingHorizontally && !d->movingVertically |
|
1907 && d->moveReason == QDeclarativeGridViewPrivate::SetIndex) { |
|
1908 const qreal trackedPos = d->trackedItem->rowPos(); |
|
1909 const qreal viewPos = d->position(); |
|
1910 if (d->haveHighlightRange) { |
|
1911 if (d->highlightRange == StrictlyEnforceRange) { |
|
1912 qreal pos = viewPos; |
|
1913 if (trackedPos > pos + d->highlightRangeEnd - d->rowSize()) |
|
1914 pos = trackedPos - d->highlightRangeEnd + d->rowSize(); |
|
1915 if (trackedPos < pos + d->highlightRangeStart) |
|
1916 pos = trackedPos - d->highlightRangeStart; |
|
1917 d->setPosition(pos); |
|
1918 } else { |
|
1919 qreal pos = viewPos; |
|
1920 if (trackedPos < d->startPosition() + d->highlightRangeStart) { |
|
1921 pos = d->startPosition(); |
|
1922 } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + d->highlightRangeEnd) { |
|
1923 pos = d->endPosition() - d->size(); |
|
1924 if (pos < d->startPosition()) |
|
1925 pos = d->startPosition(); |
|
1926 } else { |
|
1927 if (trackedPos < viewPos + d->highlightRangeStart) { |
|
1928 pos = trackedPos - d->highlightRangeStart; |
|
1929 } else if (trackedPos > viewPos + d->highlightRangeEnd - d->rowSize()) { |
|
1930 pos = trackedPos - d->highlightRangeEnd + d->rowSize(); |
|
1931 } |
|
1932 } |
|
1933 d->setPosition(pos); |
|
1934 } |
|
1935 } else { |
|
1936 if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) { |
|
1937 d->setPosition(d->currentItem->rowPos() < trackedPos ? trackedPos : d->currentItem->rowPos()); |
|
1938 } else if (d->trackedItem->endRowPos() > viewPos + d->size() |
|
1939 && d->currentItem->endRowPos() > viewPos + d->size()) { |
|
1940 qreal pos; |
|
1941 if (d->trackedItem->endRowPos() < d->currentItem->endRowPos()) { |
|
1942 pos = d->trackedItem->endRowPos() - d->size(); |
|
1943 if (d->rowSize() > d->size()) |
|
1944 pos = trackedPos; |
|
1945 } else { |
|
1946 pos = d->currentItem->endRowPos() - d->size(); |
|
1947 if (d->rowSize() > d->size()) |
|
1948 pos = d->currentItem->rowPos(); |
|
1949 } |
|
1950 d->setPosition(pos); |
|
1951 } |
|
1952 } |
|
1953 } |
|
1954 } |
|
1955 |
|
1956 void QDeclarativeGridView::itemsInserted(int modelIndex, int count) |
|
1957 { |
|
1958 Q_D(QDeclarativeGridView); |
|
1959 if (!isComponentComplete()) |
|
1960 return; |
|
1961 if (!d->visibleItems.count() || d->model->count() <= 1) { |
|
1962 d->scheduleLayout(); |
|
1963 if (d->currentIndex >= modelIndex) { |
|
1964 // adjust current item index |
|
1965 d->currentIndex += count; |
|
1966 if (d->currentItem) |
|
1967 d->currentItem->index = d->currentIndex; |
|
1968 emit currentIndexChanged(); |
|
1969 } else if (d->currentIndex < 0) { |
|
1970 d->updateCurrent(0); |
|
1971 } |
|
1972 d->itemCount += count; |
|
1973 emit countChanged(); |
|
1974 return; |
|
1975 } |
|
1976 |
|
1977 int index = d->mapFromModel(modelIndex); |
|
1978 if (index == -1) { |
|
1979 int i = d->visibleItems.count() - 1; |
|
1980 while (i > 0 && d->visibleItems.at(i)->index == -1) |
|
1981 --i; |
|
1982 if (d->visibleItems.at(i)->index + 1 == modelIndex) { |
|
1983 // Special case of appending an item to the model. |
|
1984 index = d->visibleIndex + d->visibleItems.count(); |
|
1985 } else { |
|
1986 if (modelIndex <= d->visibleIndex) { |
|
1987 // Insert before visible items |
|
1988 d->visibleIndex += count; |
|
1989 for (int i = 0; i < d->visibleItems.count(); ++i) { |
|
1990 FxGridItem *listItem = d->visibleItems.at(i); |
|
1991 if (listItem->index != -1 && listItem->index >= modelIndex) |
|
1992 listItem->index += count; |
|
1993 } |
|
1994 } |
|
1995 if (d->currentIndex >= modelIndex) { |
|
1996 // adjust current item index |
|
1997 d->currentIndex += count; |
|
1998 if (d->currentItem) |
|
1999 d->currentItem->index = d->currentIndex; |
|
2000 emit currentIndexChanged(); |
|
2001 } |
|
2002 d->scheduleLayout(); |
|
2003 d->itemCount += count; |
|
2004 emit countChanged(); |
|
2005 return; |
|
2006 } |
|
2007 } |
|
2008 |
|
2009 // At least some of the added items will be visible |
|
2010 int insertCount = count; |
|
2011 if (index < d->visibleIndex) { |
|
2012 insertCount -= d->visibleIndex - index; |
|
2013 index = d->visibleIndex; |
|
2014 modelIndex = d->visibleIndex; |
|
2015 } |
|
2016 |
|
2017 index -= d->visibleIndex; |
|
2018 int to = d->buffer+d->position()+d->size()-1; |
|
2019 int colPos, rowPos; |
|
2020 if (index < d->visibleItems.count()) { |
|
2021 colPos = d->visibleItems.at(index)->colPos(); |
|
2022 rowPos = d->visibleItems.at(index)->rowPos(); |
|
2023 } else { |
|
2024 // appending items to visible list |
|
2025 colPos = d->visibleItems.at(index-1)->colPos() + d->colSize(); |
|
2026 rowPos = d->visibleItems.at(index-1)->rowPos(); |
|
2027 if (colPos > d->colSize() * (d->columns-1)) { |
|
2028 colPos = 0; |
|
2029 rowPos += d->rowSize(); |
|
2030 } |
|
2031 } |
|
2032 |
|
2033 // Update the indexes of the following visible items. |
|
2034 for (int i = 0; i < d->visibleItems.count(); ++i) { |
|
2035 FxGridItem *listItem = d->visibleItems.at(i); |
|
2036 if (listItem->index != -1 && listItem->index >= modelIndex) |
|
2037 listItem->index += count; |
|
2038 } |
|
2039 |
|
2040 bool addedVisible = false; |
|
2041 QList<FxGridItem*> added; |
|
2042 int i = 0; |
|
2043 while (i < insertCount && rowPos <= to + d->rowSize()*(d->columns - (colPos/d->colSize()))/qreal(d->columns)) { |
|
2044 if (!addedVisible) { |
|
2045 d->scheduleLayout(); |
|
2046 addedVisible = true; |
|
2047 } |
|
2048 FxGridItem *item = d->createItem(modelIndex + i); |
|
2049 d->visibleItems.insert(index, item); |
|
2050 item->setPosition(colPos, rowPos); |
|
2051 added.append(item); |
|
2052 colPos += d->colSize(); |
|
2053 if (colPos > d->colSize() * (d->columns-1)) { |
|
2054 colPos = 0; |
|
2055 rowPos += d->rowSize(); |
|
2056 } |
|
2057 ++index; |
|
2058 ++i; |
|
2059 } |
|
2060 if (i < insertCount) { |
|
2061 // We didn't insert all our new items, which means anything |
|
2062 // beyond the current index is not visible - remove it. |
|
2063 while (d->visibleItems.count() > index) { |
|
2064 d->releaseItem(d->visibleItems.takeLast()); |
|
2065 } |
|
2066 } |
|
2067 |
|
2068 // update visibleIndex |
|
2069 d->visibleIndex = 0; |
|
2070 for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { |
|
2071 if ((*it)->index != -1) { |
|
2072 d->visibleIndex = (*it)->index; |
|
2073 break; |
|
2074 } |
|
2075 } |
|
2076 |
|
2077 if (d->currentIndex >= modelIndex) { |
|
2078 // adjust current item index |
|
2079 d->currentIndex += count; |
|
2080 if (d->currentItem) { |
|
2081 d->currentItem->index = d->currentIndex; |
|
2082 d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); |
|
2083 } |
|
2084 emit currentIndexChanged(); |
|
2085 } |
|
2086 |
|
2087 // everything is in order now - emit add() signal |
|
2088 for (int j = 0; j < added.count(); ++j) |
|
2089 added.at(j)->attached->emitAdd(); |
|
2090 |
|
2091 d->itemCount += count; |
|
2092 emit countChanged(); |
|
2093 } |
|
2094 |
|
2095 void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) |
|
2096 { |
|
2097 Q_D(QDeclarativeGridView); |
|
2098 if (!isComponentComplete()) |
|
2099 return; |
|
2100 |
|
2101 d->itemCount -= count; |
|
2102 bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count; |
|
2103 bool removedVisible = false; |
|
2104 |
|
2105 // Remove the items from the visible list, skipping anything already marked for removal |
|
2106 QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); |
|
2107 while (it != d->visibleItems.end()) { |
|
2108 FxGridItem *item = *it; |
|
2109 if (item->index == -1 || item->index < modelIndex) { |
|
2110 // already removed, or before removed items |
|
2111 if (item->index < modelIndex && !removedVisible) { |
|
2112 d->scheduleLayout(); |
|
2113 removedVisible = true; |
|
2114 } |
|
2115 ++it; |
|
2116 } else if (item->index >= modelIndex + count) { |
|
2117 // after removed items |
|
2118 item->index -= count; |
|
2119 ++it; |
|
2120 } else { |
|
2121 // removed item |
|
2122 if (!removedVisible) { |
|
2123 d->scheduleLayout(); |
|
2124 removedVisible = true; |
|
2125 } |
|
2126 item->attached->emitRemove(); |
|
2127 if (item->attached->delayRemove()) { |
|
2128 item->index = -1; |
|
2129 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); |
|
2130 ++it; |
|
2131 } else { |
|
2132 it = d->visibleItems.erase(it); |
|
2133 d->releaseItem(item); |
|
2134 } |
|
2135 } |
|
2136 } |
|
2137 |
|
2138 // fix current |
|
2139 if (d->currentIndex >= modelIndex + count) { |
|
2140 d->currentIndex -= count; |
|
2141 if (d->currentItem) |
|
2142 d->currentItem->index -= count; |
|
2143 emit currentIndexChanged(); |
|
2144 } else if (currentRemoved) { |
|
2145 // current item has been removed. |
|
2146 d->releaseItem(d->currentItem); |
|
2147 d->currentItem = 0; |
|
2148 d->currentIndex = -1; |
|
2149 if (d->itemCount) |
|
2150 d->updateCurrent(qMin(modelIndex, d->itemCount-1)); |
|
2151 } |
|
2152 |
|
2153 // update visibleIndex |
|
2154 d->visibleIndex = 0; |
|
2155 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { |
|
2156 if ((*it)->index != -1) { |
|
2157 d->visibleIndex = (*it)->index; |
|
2158 break; |
|
2159 } |
|
2160 } |
|
2161 |
|
2162 if (removedVisible && d->visibleItems.isEmpty()) { |
|
2163 d->timeline.clear(); |
|
2164 d->setPosition(0); |
|
2165 if (d->itemCount == 0) |
|
2166 update(); |
|
2167 } |
|
2168 |
|
2169 emit countChanged(); |
|
2170 } |
|
2171 |
|
2172 void QDeclarativeGridView::destroyRemoved() |
|
2173 { |
|
2174 Q_D(QDeclarativeGridView); |
|
2175 for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); |
|
2176 it != d->visibleItems.end();) { |
|
2177 FxGridItem *listItem = *it; |
|
2178 if (listItem->index == -1 && listItem->attached->delayRemove() == false) { |
|
2179 d->releaseItem(listItem); |
|
2180 it = d->visibleItems.erase(it); |
|
2181 } else { |
|
2182 ++it; |
|
2183 } |
|
2184 } |
|
2185 |
|
2186 // Correct the positioning of the items |
|
2187 d->layout(); |
|
2188 } |
|
2189 |
|
2190 void QDeclarativeGridView::itemsMoved(int from, int to, int count) |
|
2191 { |
|
2192 Q_D(QDeclarativeGridView); |
|
2193 if (!isComponentComplete()) |
|
2194 return; |
|
2195 QHash<int,FxGridItem*> moved; |
|
2196 |
|
2197 bool removedBeforeVisible = false; |
|
2198 FxGridItem *firstItem = d->firstVisibleItem(); |
|
2199 |
|
2200 if (from < to && from < d->visibleIndex && to > d->visibleIndex) |
|
2201 removedBeforeVisible = true; |
|
2202 |
|
2203 QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); |
|
2204 while (it != d->visibleItems.end()) { |
|
2205 FxGridItem *item = *it; |
|
2206 if (item->index >= from && item->index < from + count) { |
|
2207 // take the items that are moving |
|
2208 item->index += (to-from); |
|
2209 moved.insert(item->index, item); |
|
2210 it = d->visibleItems.erase(it); |
|
2211 if (item->rowPos() < firstItem->rowPos()) |
|
2212 removedBeforeVisible = true; |
|
2213 } else { |
|
2214 if (item->index > from && item->index != -1) { |
|
2215 // move everything after the moved items. |
|
2216 item->index -= count; |
|
2217 if (item->index < d->visibleIndex) |
|
2218 d->visibleIndex = item->index; |
|
2219 } else if (item->index != -1) { |
|
2220 removedBeforeVisible = true; |
|
2221 } |
|
2222 ++it; |
|
2223 } |
|
2224 } |
|
2225 |
|
2226 int remaining = count; |
|
2227 int endIndex = d->visibleIndex; |
|
2228 it = d->visibleItems.begin(); |
|
2229 while (it != d->visibleItems.end()) { |
|
2230 FxGridItem *item = *it; |
|
2231 if (remaining && item->index >= to && item->index < to + count) { |
|
2232 // place items in the target position, reusing any existing items |
|
2233 FxGridItem *movedItem = moved.take(item->index); |
|
2234 if (!movedItem) |
|
2235 movedItem = d->createItem(item->index); |
|
2236 it = d->visibleItems.insert(it, movedItem); |
|
2237 if (it == d->visibleItems.begin() && firstItem) |
|
2238 movedItem->setPosition(firstItem->colPos(), firstItem->rowPos()); |
|
2239 ++it; |
|
2240 --remaining; |
|
2241 } else { |
|
2242 if (item->index != -1) { |
|
2243 if (item->index >= to) { |
|
2244 // update everything after the moved items. |
|
2245 item->index += count; |
|
2246 } |
|
2247 endIndex = item->index; |
|
2248 } |
|
2249 ++it; |
|
2250 } |
|
2251 } |
|
2252 |
|
2253 // If we have moved items to the end of the visible items |
|
2254 // then add any existing moved items that we have |
|
2255 while (FxGridItem *item = moved.take(endIndex+1)) { |
|
2256 d->visibleItems.append(item); |
|
2257 ++endIndex; |
|
2258 } |
|
2259 |
|
2260 // update visibleIndex |
|
2261 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { |
|
2262 if ((*it)->index != -1) { |
|
2263 d->visibleIndex = (*it)->index; |
|
2264 break; |
|
2265 } |
|
2266 } |
|
2267 |
|
2268 // Fix current index |
|
2269 if (d->currentIndex >= 0 && d->currentItem) { |
|
2270 int oldCurrent = d->currentIndex; |
|
2271 d->currentIndex = d->model->indexOf(d->currentItem->item, this); |
|
2272 if (oldCurrent != d->currentIndex) { |
|
2273 d->currentItem->index = d->currentIndex; |
|
2274 emit currentIndexChanged(); |
|
2275 } |
|
2276 } |
|
2277 |
|
2278 // Whatever moved items remain are no longer visible items. |
|
2279 while (moved.count()) { |
|
2280 int idx = moved.begin().key(); |
|
2281 FxGridItem *item = moved.take(idx); |
|
2282 if (item->item == d->currentItem->item) |
|
2283 item->setPosition(d->colPosAt(idx), d->rowPosAt(idx)); |
|
2284 d->releaseItem(item); |
|
2285 } |
|
2286 |
|
2287 d->layout(); |
|
2288 } |
|
2289 |
|
2290 void QDeclarativeGridView::modelReset() |
|
2291 { |
|
2292 Q_D(QDeclarativeGridView); |
|
2293 d->clear(); |
|
2294 refill(); |
|
2295 d->moveReason = QDeclarativeGridViewPrivate::SetIndex; |
|
2296 d->updateCurrent(d->currentIndex); |
|
2297 emit countChanged(); |
|
2298 } |
|
2299 |
|
2300 void QDeclarativeGridView::createdItem(int index, QDeclarativeItem *item) |
|
2301 { |
|
2302 Q_D(QDeclarativeGridView); |
|
2303 if (d->requestedIndex != index) { |
|
2304 item->setParentItem(this); |
|
2305 d->unrequestedItems.insert(item, index); |
|
2306 if (d->flow == QDeclarativeGridView::LeftToRight) { |
|
2307 item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index))); |
|
2308 } else { |
|
2309 item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index))); |
|
2310 } |
|
2311 } |
|
2312 } |
|
2313 |
|
2314 void QDeclarativeGridView::destroyingItem(QDeclarativeItem *item) |
|
2315 { |
|
2316 Q_D(QDeclarativeGridView); |
|
2317 d->unrequestedItems.remove(item); |
|
2318 } |
|
2319 |
|
2320 void QDeclarativeGridView::animStopped() |
|
2321 { |
|
2322 Q_D(QDeclarativeGridView); |
|
2323 d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer; |
|
2324 if (d->haveHighlightRange && d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange) |
|
2325 d->updateHighlight(); |
|
2326 } |
|
2327 |
|
2328 void QDeclarativeGridView::refill() |
|
2329 { |
|
2330 Q_D(QDeclarativeGridView); |
|
2331 d->refill(d->position(), d->position()+d->size()-1); |
|
2332 } |
|
2333 |
|
2334 |
|
2335 QDeclarativeGridViewAttached *QDeclarativeGridView::qmlAttachedProperties(QObject *obj) |
|
2336 { |
|
2337 return new QDeclarativeGridViewAttached(obj); |
|
2338 } |
|
2339 |
|
2340 QT_END_NAMESPACE |