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