1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/qstringlistmodel.h>
31#include <QtQuick/qquickview.h>
32#include <QtQuickTest/QtQuickTest>
33#include <QtQml/qqmlengine.h>
34#include <QtQml/qqmlcomponent.h>
35#include <QtQml/qqmlcontext.h>
36#include <QtQml/qqmlexpression.h>
37#include <QtQml/qqmlincubator.h>
38#include <QtQml/qqmlcontext.h>
39#include <QtQuick/private/qquickitem_p.h>
40#include <QtQuick/private/qquickitemview_p_p.h>
41#include <QtQuick/private/qquickgridview_p.h>
42#include <QtQuick/private/qquicktext_p.h>
43#include <QtQmlModels/private/qqmllistmodel_p.h>
44#include "../../shared/util.h"
45#include "../shared/viewtestutil.h"
46#include "../shared/visualtestutil.h"
47#include <QtGui/qguiapplication.h>
48#include "qplatformdefs.h"
49
50#include <math.h>
51
52Q_DECLARE_METATYPE(QQuickGridView::Flow)
53Q_DECLARE_METATYPE(Qt::LayoutDirection)
54Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection)
55Q_DECLARE_METATYPE(QQuickItemView::PositionMode)
56Q_DECLARE_METATYPE(Qt::Key)
57
58using namespace QQuickViewTestUtil;
59using namespace QQuickVisualTestUtil;
60
61#define SHARE_VIEWS
62
63class tst_QQuickGridView : public QQmlDataTest
64{
65 Q_OBJECT
66public:
67 tst_QQuickGridView();
68
69private slots:
70 void init();
71 void cleanupTestCase();
72 void items();
73 void changed();
74 void inserted_basic();
75 void inserted_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
76 void inserted_defaultLayout_data();
77 void insertBeforeVisible();
78 void insertBeforeVisible_data();
79 void removed_basic();
80 void removed_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
81 void removed_defaultLayout_data();
82 void addOrRemoveBeforeVisible();
83 void addOrRemoveBeforeVisible_data();
84 void clear();
85 void moved_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
86 void moved_defaultLayout_data();
87 void multipleChanges_condensed() { multipleChanges(condensed: true); }
88 void multipleChanges_condensed_data() { multipleChanges_data(); }
89 void multipleChanges_uncondensed() { multipleChanges(condensed: false); }
90 void multipleChanges_uncondensed_data() { multipleChanges_data(); }
91 void swapWithFirstItem();
92 void changeFlow();
93 void currentIndex();
94 void noCurrentIndex();
95 void keyNavigation();
96 void keyNavigation_data();
97 void defaultValues();
98 void properties();
99 void propertyChanges();
100 void componentChanges();
101 void modelChanges();
102 void positionViewAtBeginningEnd();
103 void positionViewAtIndex();
104 void positionViewAtIndex_data();
105 void mirroring();
106 void snapping();
107 void resetModel();
108 void enforceRange();
109 void enforceRange_rightToLeft();
110 void QTBUG_8456();
111 void manualHighlight();
112 void footer();
113 void footer_data();
114 void initialZValues();
115 void initialZValues_data();
116 void header();
117 void header_data();
118 void extents();
119 void extents_data();
120 void resetModel_headerFooter();
121 void resizeViewAndRepaint();
122 void resizeGrid();
123 void resizeGrid_data();
124 void changeColumnCount();
125 void indexAt_itemAt_data();
126 void indexAt_itemAt();
127 void onAdd();
128 void onAdd_data();
129 void onRemove();
130 void onRemove_data();
131 void attachedProperties_QTBUG_32836();
132 void columnCount();
133 void margins();
134 void creationContext();
135 void snapToRow_data();
136 void snapToRow();
137 void snapOneRow_data();
138 void snapOneRow();
139 void unaligned();
140 void cacheBuffer();
141 void asynchronous();
142 void unrequestedVisibility();
143
144 void populateTransitions();
145 void populateTransitions_data();
146 void addTransitions();
147 void addTransitions_data();
148 void moveTransitions();
149 void moveTransitions_data();
150 void removeTransitions();
151 void removeTransitions_data();
152 void displacedTransitions();
153 void displacedTransitions_data();
154 void multipleTransitions();
155 void multipleTransitions_data();
156 void multipleDisplaced();
157 void regression_QTBUG_57225();
158 void regression_QTBUG_57225_data();
159
160 void inserted_leftToRight_RtL_TtB();
161 void inserted_leftToRight_RtL_TtB_data();
162 void inserted_leftToRight_LtR_BtT();
163 void inserted_leftToRight_LtR_BtT_data();
164 void inserted_leftToRight_RtL_BtT();
165 void inserted_leftToRight_RtL_BtT_data();
166 void inserted_topToBottom_LtR_TtB();
167 void inserted_topToBottom_LtR_TtB_data();
168 void inserted_topToBottom_RtL_TtB();
169 void inserted_topToBottom_RtL_TtB_data();
170 void inserted_topToBottom_LtR_BtT();
171 void inserted_topToBottom_LtR_BtT_data();
172 void inserted_topToBottom_RtL_BtT();
173 void inserted_topToBottom_RtL_BtT_data();
174
175 void removed_leftToRight_RtL_TtB();
176 void removed_leftToRight_RtL_TtB_data();
177 void removed_leftToRight_LtR_BtT();
178 void removed_leftToRight_LtR_BtT_data();
179 void removed_leftToRight_RtL_BtT();
180 void removed_leftToRight_RtL_BtT_data();
181 void removed_topToBottom_LtR_TtB();
182 void removed_topToBottom_LtR_TtB_data();
183 void removed_topToBottom_RtL_TtB();
184 void removed_topToBottom_RtL_TtB_data();
185 void removed_topToBottom_LtR_BtT();
186 void removed_topToBottom_LtR_BtT_data();
187 void removed_topToBottom_RtL_BtT();
188 void removed_topToBottom_RtL_BtT_data();
189
190 void moved_leftToRight_RtL_TtB();
191 void moved_leftToRight_RtL_TtB_data();
192 void moved_leftToRight_LtR_BtT();
193 void moved_leftToRight_LtR_BtT_data();
194 void moved_leftToRight_RtL_BtT();
195 void moved_leftToRight_RtL_BtT_data();
196 void moved_topToBottom_LtR_TtB();
197 void moved_topToBottom_LtR_TtB_data();
198 void moved_topToBottom_RtL_TtB();
199 void moved_topToBottom_RtL_TtB_data();
200 void moved_topToBottom_LtR_BtT();
201 void moved_topToBottom_LtR_BtT_data();
202 void moved_topToBottom_RtL_BtT();
203 void moved_topToBottom_RtL_BtT_data();
204
205 void displayMargin();
206 void negativeDisplayMargin();
207
208 void jsArrayChange();
209
210 void contentHeightWithDelayRemove_data();
211 void contentHeightWithDelayRemove();
212
213 void QTBUG_45640();
214 void QTBUG_49218();
215 void QTBUG_48870_fastModelUpdates();
216
217 void keyNavigationEnabled();
218 void resizeDynamicCellWidthRtL();
219 void releaseItems();
220
221private:
222 QList<int> toIntList(const QVariantList &list);
223 void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
224 void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes);
225 void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems);
226
227 void multipleChanges(bool condensed);
228 void multipleChanges_data();
229
230 QPointF expectedItemPos(QQuickGridView *grid, int index, qreal rowOffset = 0) {
231 qreal x;
232 qreal y;
233 if (grid->flow() == QQuickGridView::FlowLeftToRight) {
234 int columns = grid->width() / grid->cellWidth();
235 x = (index % columns) * grid->cellWidth();
236 y = (index / columns) * grid->cellHeight();
237 if (grid->effectiveLayoutDirection() == Qt::RightToLeft) {
238 int col = (index % columns) * grid->cellWidth();
239 x = grid->cellWidth() * (columns - 1) - col;
240 }
241
242 qreal offset = grid->cellHeight() * rowOffset;
243 if (grid->verticalLayoutDirection() == QQuickItemView::TopToBottom)
244 y += offset;
245 else
246 y = -grid->cellHeight() - y - offset;
247 } else {
248 int rows = grid->height() / grid->cellHeight();
249 x = (index / rows) * grid->cellWidth();
250 y = (index % rows) * grid->cellHeight();
251 if (grid->effectiveLayoutDirection() == Qt::RightToLeft)
252 x = -x - grid->cellWidth();
253
254 qreal offset = grid->cellWidth() * rowOffset;
255 if (grid->effectiveLayoutDirection() == Qt::RightToLeft)
256 x -= offset;
257 else
258 x += offset;
259 if (grid->verticalLayoutDirection() == QQuickItemView::BottomToTop)
260 y = -grid->cellHeight() - y;
261 }
262 return QPointF(x, y);
263 }
264
265 // Sets contentY (or contentX in TopToBottom flow) according to given row offset
266 // (in LeftToRight flow) or col offset (in TopToBottom).
267 bool setContentPos(QQuickGridView *gridview, qreal rowOrColOffset) {
268 bool contentPosChanged = (rowOrColOffset != 0);
269 qreal contentOffset = gridview->flow() == QQuickGridView::FlowLeftToRight
270 ? rowOrColOffset * gridview->cellHeight()
271 : rowOrColOffset * gridview->cellWidth();
272
273 if (gridview->flow() == QQuickGridView::FlowLeftToRight) {
274 if (gridview->verticalLayoutDirection() == QQuickItemView::BottomToTop)
275 contentOffset = -gridview->height() - contentOffset;
276 } else {
277 if (gridview->effectiveLayoutDirection() == Qt::RightToLeft)
278 contentOffset = -gridview->width() - contentOffset;
279 }
280 if (gridview->flow() == QQuickGridView::FlowLeftToRight)
281 gridview->setContentY(contentOffset);
282 else
283 gridview->setContentX(contentOffset);
284 return contentPosChanged;
285 }
286
287#ifdef SHARE_VIEWS
288 QQuickView *getView() {
289 if (m_view) {
290 if (QString(QTest::currentTestFunction()) != testForView) {
291 delete m_view;
292 m_view = nullptr;
293 } else {
294 m_view->setSource(QUrl());
295 return m_view;
296 }
297 }
298
299 testForView = QTest::currentTestFunction();
300 m_view = createView();
301 return m_view;
302 }
303 void releaseView(QQuickView *view) {
304 Q_ASSERT(view == m_view);
305 Q_UNUSED(view)
306 m_view->setSource(QUrl());
307 }
308#else
309 QQuickView *getView() {
310 return createView();
311 }
312 void releaseView(QQuickView *view) {
313 delete view;
314 }
315#endif
316
317 QQuickView *m_view;
318 QString testForView;
319};
320
321tst_QQuickGridView::tst_QQuickGridView() : m_view(nullptr)
322{
323}
324
325void tst_QQuickGridView::init()
326{
327#ifdef SHARE_VIEWS
328 if (m_view && QString(QTest::currentTestFunction()) != testForView) {
329 testForView = QString();
330 delete m_view;
331 m_view = nullptr;
332 }
333#endif
334}
335
336void tst_QQuickGridView::cleanupTestCase()
337{
338#ifdef SHARE_VIEWS
339 testForView = QString();
340 delete m_view;
341 m_view = nullptr;
342#endif
343}
344
345void tst_QQuickGridView::items()
346{
347 QQuickView *window = createView();
348
349 QaimModel model;
350 model.addItem(name: "Fred", number: "12345");
351 model.addItem(name: "John", number: "2345");
352 model.addItem(name: "Bob", number: "54321");
353 model.addItem(name: "Billy", number: "22345");
354 model.addItem(name: "Sam", number: "2945");
355 model.addItem(name: "Ben", number: "04321");
356 model.addItem(name: "Jim", number: "0780");
357
358 QQmlContext *ctxt = window->rootContext();
359 ctxt->setContextProperty("testModel", &model);
360
361 window->setSource(testFileUrl(fileName: "gridview1.qml"));
362 qApp->processEvents();
363
364 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
365 QTRY_VERIFY(gridview != nullptr);
366
367 QQuickItem *contentItem = gridview->contentItem();
368 QTRY_VERIFY(contentItem != nullptr);
369
370 QTRY_COMPARE(gridview->count(), model.count());
371 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
372 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
373
374 for (int i = 0; i < model.count(); ++i) {
375 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
376 QTRY_VERIFY(name != nullptr);
377 QTRY_COMPARE(name->text(), model.name(i));
378 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: i);
379 QTRY_VERIFY(number != nullptr);
380 QTRY_COMPARE(number->text(), model.number(i));
381 }
382
383 // set an empty model and confirm that items are destroyed
384 QaimModel model2;
385 ctxt->setContextProperty("testModel", &model2);
386
387 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
388 QTRY_COMPARE(itemCount, 0);
389
390 delete window;
391}
392
393void tst_QQuickGridView::changed()
394{
395 QQuickView *window = createView();
396
397 QaimModel model;
398 model.addItem(name: "Fred", number: "12345");
399 model.addItem(name: "John", number: "2345");
400 model.addItem(name: "Bob", number: "54321");
401 model.addItem(name: "Billy", number: "22345");
402 model.addItem(name: "Sam", number: "2945");
403 model.addItem(name: "Ben", number: "04321");
404 model.addItem(name: "Jim", number: "0780");
405
406 QQmlContext *ctxt = window->rootContext();
407 ctxt->setContextProperty("testModel", &model);
408
409 window->setSource(testFileUrl(fileName: "gridview1.qml"));
410 qApp->processEvents();
411
412 QQuickFlickable *gridview = findItem<QQuickFlickable>(parent: window->rootObject(), objectName: "grid");
413 QTRY_VERIFY(gridview != nullptr);
414
415 QQuickItem *contentItem = gridview->contentItem();
416 QTRY_VERIFY(contentItem != nullptr);
417
418 model.modifyItem(idx: 1, name: "Will", number: "9876");
419 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 1);
420 QTRY_VERIFY(name != nullptr);
421 QTRY_COMPARE(name->text(), model.name(1));
422 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 1);
423 QTRY_VERIFY(number != nullptr);
424 QTRY_COMPARE(number->text(), model.number(1));
425
426 delete window;
427}
428
429void tst_QQuickGridView::inserted_basic()
430{
431 QQuickView *window = createView();
432 window->show();
433
434 QaimModel model;
435 model.addItem(name: "Fred", number: "12345");
436 model.addItem(name: "John", number: "2345");
437 model.addItem(name: "Bob", number: "54321");
438
439 window->rootContext()->setContextProperty("testModel", &model);
440 window->setSource(testFileUrl(fileName: "gridview1.qml"));
441 qApp->processEvents();
442
443 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
444 QTRY_VERIFY(gridview != nullptr);
445
446 QQuickItem *contentItem = gridview->contentItem();
447 QTRY_VERIFY(contentItem != nullptr);
448
449 model.insertItem(index: 1, name: "Will", number: "9876");
450
451 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
452 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
453
454 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 1);
455 QTRY_VERIFY(name != nullptr);
456 QTRY_COMPARE(name->text(), model.name(1));
457 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 1);
458 QTRY_VERIFY(number != nullptr);
459 QTRY_COMPARE(number->text(), model.number(1));
460
461 // Checks that onAdd is called
462 int added = window->rootObject()->property(name: "added").toInt();
463 QTRY_COMPARE(added, 1);
464
465 // Confirm items positioned correctly
466 for (int i = 0; i < model.count(); ++i) {
467 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
468 QTRY_COMPARE(item->x(), (i%3)*80.0);
469 QTRY_COMPARE(item->y(), (i/3)*60.0);
470 }
471
472 model.insertItem(index: 0, name: "Foo", number: "1111"); // zero index, and current item
473
474 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
475
476 name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 0);
477 QTRY_VERIFY(name != nullptr);
478 QTRY_COMPARE(name->text(), model.name(0));
479 number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 0);
480 QTRY_VERIFY(number != nullptr);
481 QTRY_COMPARE(number->text(), model.number(0));
482
483 QTRY_COMPARE(gridview->currentIndex(), 1);
484
485 // Confirm items positioned correctly
486 for (int i = 0; i < model.count(); ++i) {
487 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
488 QTRY_COMPARE(item->x(), qreal((i%3)*80));
489 QTRY_COMPARE(item->y(), qreal((i/3)*60));
490 }
491
492 for (int i = model.count(); i < 30; ++i)
493 model.insertItem(index: i, name: "Hello", number: QString::number(i));
494
495 gridview->setContentY(120);
496
497 // Insert item outside visible area
498 model.insertItem(index: 1, name: "Hello", number: "1324");
499
500 QTRY_COMPARE(gridview->contentY(), qreal(120));
501
502 delete window;
503}
504
505void tst_QQuickGridView::inserted_defaultLayout(QQuickGridView::Flow flow,
506 Qt::LayoutDirection horizLayout,
507 QQuickItemView::VerticalLayoutDirection verticalLayout)
508{
509 QFETCH(qreal, contentYRowOffset);
510 QFETCH(int, insertIndex);
511 QFETCH(int, insertCount);
512 QFETCH(int, insertIndex_ttb);
513 QFETCH(int, insertCount_ttb);
514 QFETCH(qreal, rowOffsetAfterMove);
515
516 QaimModel model;
517 for (int i = 0; i < 30; i++)
518 model.addItem(name: "Item" + QString::number(i), number: "");
519
520 QQuickView *window = getView();
521 QQmlContext *ctxt = window->rootContext();
522 ctxt->setContextProperty("testModel", &model);
523 ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
524 ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
525 ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
526 window->setSource(testFileUrl(fileName: "layouts.qml"));
527 window->show();
528 qApp->processEvents();
529
530 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
531 QTRY_VERIFY(gridview != nullptr);
532 QQuickItem *contentItem = gridview->contentItem();
533 QTRY_VERIFY(contentItem != nullptr);
534
535 if (flow == QQuickGridView::FlowTopToBottom) {
536 insertIndex = insertIndex_ttb;
537 insertCount = insertCount_ttb;
538 }
539 if (setContentPos(gridview, rowOrColOffset: contentYRowOffset))
540 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
541
542 QList<QPair<QString, QString> > newData;
543 for (int i=0; i<insertCount; i++)
544 newData << qMakePair(x: QString("value %1").arg(a: i), y: QString::number(i));
545 model.insertItems(index: insertIndex, items: newData);
546 gridview->forceLayout();
547 QTRY_COMPARE(gridview->property("count").toInt(), model.count());
548
549 // check visibleItems.first() is in correct position
550 QQuickItem *item0 = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
551 QVERIFY(item0);
552 QPointF firstPos(0, 0);
553 if (horizLayout == Qt::RightToLeft)
554 firstPos.rx() = flow == QQuickGridView::FlowLeftToRight ? gridview->width() - gridview->cellWidth() : -gridview->cellWidth();
555 if (verticalLayout == QQuickItemView::BottomToTop)
556 firstPos.ry() -= gridview->cellHeight();
557 QCOMPARE(item0->position(), firstPos);
558
559 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
560 int firstVisibleIndex = -1;
561 for (int i=0; i<items.count(); i++) {
562 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
563 if (item && delegateVisible(item)) {
564 firstVisibleIndex = i;
565 break;
566 }
567 }
568 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
569
570 // Confirm items positioned correctly and indexes correct
571 for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
572 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
573 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
574 QCOMPARE(item->position(), expectedItemPos(gridview, i, rowOffsetAfterMove));
575 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
576 QVERIFY(name != nullptr);
577 QCOMPARE(name->text(), model.name(i));
578 }
579
580 releaseView(view: window);
581}
582
583void tst_QQuickGridView::inserted_defaultLayout_data()
584{
585 QTest::addColumn<qreal>(name: "contentYRowOffset");
586 QTest::addColumn<int>(name: "insertIndex");
587 QTest::addColumn<int>(name: "insertCount");
588 QTest::addColumn<int>(name: "insertIndex_ttb");
589 QTest::addColumn<int>(name: "insertCount_ttb");
590 QTest::addColumn<qreal>(name: "rowOffsetAfterMove");
591
592 QTest::newRow(dataTag: "add 1, before visible items")
593 << 2.0 // show 6-23
594 << 5 << 1
595 << 9 << 1
596 << 0.0; // insert 1 above first visible, grid is rearranged; first visible moves forward within its row
597 // new 1st visible item is at 0
598
599 QTest::newRow(dataTag: "add 2, before visible items")
600 << 2.0 // show 6-23
601 << 5 << 2
602 << 9 << 2
603 << 0.0; // insert 2 above first visible, grid is rearranged; first visible moves forward within its row
604
605 QTest::newRow(dataTag: "add 3, before visible items")
606 << 2.0 // show 6-23
607 << 5 << 3
608 << 9 << 5
609 << -1.0; // insert 3 (1 row) above first visible in negative pos, first visible does not move
610
611 QTest::newRow(dataTag: "add 5, before visible items")
612 << 2.0 // show 6-23
613 << 5 << 5
614 << 9 << 7
615 << -1.0; // insert 1 row + 2 items above first visible, 1 row added at negative pos,
616 // grid is rearranged and first visible moves forward within its row
617
618 QTest::newRow(dataTag: "add 6, before visible items")
619 << 2.0 // show 6-23
620 << 5 << 6
621 << 9 << 10
622 << -1.0 * 2; // insert 2 rows above first visible in negative pos, first visible does not move
623
624
625
626 QTest::newRow(dataTag: "add 1, at start of visible, content at start")
627 << 0.0
628 << 0 << 1
629 << 0 << 1
630 << 0.0;
631
632 QTest::newRow(dataTag: "add multiple, at start of visible, content at start")
633 << 0.0
634 << 0 << 3
635 << 0 << 5
636 << 0.0;
637
638 QTest::newRow(dataTag: "add 1, at start of visible, content not at start")
639 << 2.0 // show 6-23
640 << 6 << 1
641 << 10 << 1
642 << 0.0;
643
644 QTest::newRow(dataTag: "add multiple, at start of visible, content not at start")
645 << 2.0 // show 6-23
646 << 6 << 3
647 << 10 << 5
648 << 0.0;
649
650
651 QTest::newRow(dataTag: "add 1, at end of visible, content at start")
652 << 0.0
653 << 17 << 1
654 << 14 << 1
655 << 0.0;
656
657 QTest::newRow(dataTag: "add row, at end of visible, content at start")
658 << 0.0
659 << 17 << 3
660 << 14 << 5
661 << 0.0;
662
663 QTest::newRow(dataTag: "add 1, at end of visible, content not at start")
664 << 2.0 // show 6-23
665 << 17+6 << 1
666 << 14+10 << 1
667 << 0.0;
668
669 QTest::newRow(dataTag: "add multiple, at end of visible, content not at start")
670 << 2.0 // show 6-23
671 << 17+6 << 3
672 << 14+10 << 5
673 << 0.0;
674
675
676 QTest::newRow(dataTag: "add 1, after visible, content at start")
677 << 0.0
678 << 20 << 1
679 << 18 << 1
680 << 0.0;
681
682 QTest::newRow(dataTag: "add row, after visible, content at start")
683 << 0.0
684 << 20 << 3
685 << 18 << 5
686 << 0.0;
687
688 QTest::newRow(dataTag: "add 1, after visible, content not at start")
689 << 2.0 // show 6-23
690 << 20+6 << 1
691 << 18+10 << 1
692 << 0.0;
693
694 QTest::newRow(dataTag: "add multiple, after visible, content not at start")
695 << 2.0 // show 6-23
696 << 20+6 << 3
697 << 18+10 << 3
698 << 0.0;
699}
700
701void tst_QQuickGridView::insertBeforeVisible()
702{
703 QFETCH(int, insertIndex);
704 QFETCH(int, insertCount);
705 QFETCH(int, cacheBuffer);
706
707 QQuickText *name;
708 QQuickView *window = getView();
709
710 QaimModel model;
711 for (int i = 0; i < 30; i++)
712 model.addItem(name: "Item" + QString::number(i), number: "");
713
714 QQmlContext *ctxt = window->rootContext();
715 ctxt->setContextProperty("testModel", &model);
716 window->setSource(testFileUrl(fileName: "gridview1.qml"));
717 window->show();
718 qApp->processEvents();
719
720 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
721 QTRY_VERIFY(gridview != nullptr);
722 QQuickItem *contentItem = gridview->contentItem();
723 QTRY_VERIFY(contentItem != nullptr);
724
725 gridview->setCacheBuffer(cacheBuffer);
726 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
727
728 // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
729 int firstVisibleIndex = 12; // move to an index where the top item is not visible
730 gridview->setContentY(firstVisibleIndex/3 * 60.0);
731 gridview->setCurrentIndex(firstVisibleIndex);
732 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
733
734 QTRY_COMPARE(gridview->currentIndex(), firstVisibleIndex);
735 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: firstVisibleIndex);
736 QVERIFY(item);
737 QCOMPARE(item->y(), gridview->contentY());
738
739 QList<QPair<QString, QString> > newData;
740 for (int i=0; i<insertCount; i++)
741 newData << qMakePair(x: QString("value %1").arg(a: i), y: QString::number(i));
742 model.insertItems(index: insertIndex, items: newData);
743 gridview->forceLayout();
744 QTRY_COMPARE(gridview->property("count").toInt(), model.count());
745
746 // now, moving to the top of the view should position the inserted items correctly
747 int itemsOffsetAfterMove = (insertCount / 3) * -60.0;
748 gridview->setCurrentIndex(0);
749 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
750 QTRY_COMPARE(gridview->currentIndex(), 0);
751 QTRY_COMPARE(gridview->contentY(), 0.0 + itemsOffsetAfterMove);
752
753 // Confirm items positioned correctly and indexes correct
754 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
755 for (int i = 0; i < model.count() && i < itemCount; ++i) {
756 item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
757 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
758 QCOMPARE(item->x(), (i%3)*80.0);
759 QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
760 name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
761 QVERIFY(name != nullptr);
762 QTRY_COMPARE(name->text(), model.name(i));
763 }
764
765 releaseView(view: window);
766}
767
768void tst_QQuickGridView::insertBeforeVisible_data()
769{
770 QTest::addColumn<int>(name: "insertIndex");
771 QTest::addColumn<int>(name: "insertCount");
772 QTest::addColumn<int>(name: "cacheBuffer");
773
774 QTest::newRow(dataTag: "insert 1 at 0, 0 buffer") << 0 << 1 << 0;
775 QTest::newRow(dataTag: "insert 1 at 0, 100 buffer") << 0 << 1 << 100;
776 QTest::newRow(dataTag: "insert 1 at 0, 500 buffer") << 0 << 1 << 500;
777
778 QTest::newRow(dataTag: "insert 1 at 1, 0 buffer") << 1 << 1 << 0;
779 QTest::newRow(dataTag: "insert 1 at 1, 100 buffer") << 1 << 1 << 100;
780 QTest::newRow(dataTag: "insert 1 at 1, 500 buffer") << 1 << 1 << 500;
781
782 QTest::newRow(dataTag: "insert multiple at 0, 0 buffer") << 0 << 6 << 0;
783 QTest::newRow(dataTag: "insert multiple at 0, 100 buffer") << 0 << 6 << 100;
784 QTest::newRow(dataTag: "insert multiple at 0, 500 buffer") << 0 << 6 << 500;
785
786 QTest::newRow(dataTag: "insert multiple at 1, 0 buffer") << 1 << 6 << 0;
787 QTest::newRow(dataTag: "insert multiple at 1, 100 buffer") << 1 << 6 << 100;
788 QTest::newRow(dataTag: "insert multiple at 1, 500 buffer") << 1 << 6 << 500;
789}
790
791void tst_QQuickGridView::removed_basic()
792{
793 QQuickView *window = createView();
794 window->show();
795
796 QaimModel model;
797 for (int i = 0; i < 40; i++)
798 model.addItem(name: "Item" + QString::number(i), number: "");
799
800 window->rootContext()->setContextProperty("testModel", &model);
801 window->setSource(testFileUrl(fileName: "gridview1.qml"));
802 qApp->processEvents();
803
804 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
805 QTRY_VERIFY(gridview != nullptr);
806 QQuickItem *contentItem = gridview->contentItem();
807 QTRY_VERIFY(contentItem != nullptr);
808 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
809
810 model.removeItem(index: 1);
811 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
812
813 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 1);
814 QTRY_VERIFY(name != nullptr);
815 QTRY_COMPARE(name->text(), model.name(1));
816 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 1);
817 QTRY_VERIFY(number != nullptr);
818 QTRY_COMPARE(number->text(), model.number(1));
819
820
821 // Checks that onRemove is called
822 QString removed = window->rootObject()->property(name: "removed").toString();
823 QTRY_COMPARE(removed, QString("Item1"));
824
825 // Confirm items positioned correctly
826 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
827 for (int i = 0; i < model.count() && i < itemCount; ++i) {
828 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
829 if (!item) qWarning() << "Item" << i << "not found";
830 QTRY_COMPARE(item->x(), qreal((i%3)*80));
831 QTRY_COMPARE(item->y(), qreal((i/3)*60));
832 }
833
834 // Remove first item (which is the current item);
835 model.removeItem(index: 0);
836 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
837
838 name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 0);
839 QTRY_VERIFY(name != nullptr);
840 QTRY_COMPARE(name->text(), model.name(0));
841 number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 0);
842 QTRY_VERIFY(number != nullptr);
843 QTRY_COMPARE(number->text(), model.number(0));
844
845
846 // Confirm items positioned correctly
847 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
848 for (int i = 0; i < model.count() && i < itemCount; ++i) {
849 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
850 if (!item) qWarning() << "Item" << i << "not found";
851 QTRY_COMPARE(item->x(), qreal((i%3)*80));
852 QTRY_COMPARE(item->y(), qreal((i/3)*60));
853 }
854
855 // Remove items not visible
856 model.removeItem(index: 25);
857 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
858
859 // Confirm items positioned correctly
860 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
861 for (int i = 0; i < model.count() && i < itemCount; ++i) {
862 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
863 if (!item) qWarning() << "Item" << i << "not found";
864 QTRY_COMPARE(item->x(), qreal((i%3)*80));
865 QTRY_COMPARE(item->y(), qreal((i/3)*60));
866 }
867
868 // Remove items before visible
869 gridview->setContentY(120);
870 gridview->setCurrentIndex(10);
871
872 // Setting currentIndex above shouldn't cause view to scroll
873 QTRY_COMPARE(gridview->contentY(), 120.0);
874
875 model.removeItem(index: 1);
876 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
877
878 // Confirm items positioned correctly
879 for (int i = 6; i < 18; ++i) {
880 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
881 if (!item) qWarning() << "Item" << i << "not found";
882 QTRY_COMPARE(item->x(), qreal((i%3)*80));
883 QTRY_COMPARE(item->y(), qreal((i/3)*60));
884 }
885
886 // Remove currentIndex
887 QQuickItem *oldCurrent = gridview->currentItem();
888 model.removeItem(index: 9);
889 QTRY_COMPARE(window->rootObject()->property("count").toInt(), model.count());
890
891 QTRY_COMPARE(gridview->currentIndex(), 9);
892 QTRY_VERIFY(gridview->currentItem() != oldCurrent);
893
894 gridview->setContentY(0);
895 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
896
897 // Confirm items positioned correctly
898 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
899 for (int i = 0; i < model.count() && i < itemCount; ++i) {
900 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
901 QTRY_COMPARE(item->x(), qreal((i%3)*80));
902 QTRY_COMPARE(item->y(), qreal((i/3)*60));
903 }
904
905 // remove item outside current view.
906 gridview->setCurrentIndex(32);
907 gridview->setContentY(240);
908
909 model.removeItem(index: 30);
910 QTRY_COMPARE(gridview->currentIndex(), 31);
911
912 // remove current item beyond visible items.
913 gridview->setCurrentIndex(20);
914 gridview->setContentY(0);
915 model.removeItem(index: 20);
916
917 QTRY_COMPARE(gridview->currentIndex(), 20);
918 QTRY_VERIFY(gridview->currentItem() != nullptr);
919
920 // remove item before current, but visible
921 gridview->setCurrentIndex(8);
922 gridview->setContentY(240);
923 oldCurrent = gridview->currentItem();
924 model.removeItem(index: 6);
925
926 QTRY_COMPARE(gridview->currentIndex(), 7);
927 QTRY_COMPARE(gridview->currentItem(), oldCurrent);
928
929 delete window;
930}
931
932void tst_QQuickGridView::removed_defaultLayout(QQuickGridView::Flow flow,
933 Qt::LayoutDirection horizLayout,
934 QQuickItemView::VerticalLayoutDirection verticalLayout)
935{
936 QFETCH(qreal, contentYRowOffset);
937 QFETCH(int, removeIndex);
938 QFETCH(int, removeCount);
939 QFETCH(int, removeIndex_ttb);
940 QFETCH(int, removeCount_ttb);
941 QFETCH(qreal, rowOffsetAfterMove);
942 QFETCH(QString, firstVisible);
943 QFETCH(QString, firstVisible_ttb);
944
945 QQuickView *window = getView();
946
947 QaimModel model;
948 for (int i = 0; i < 30; i++)
949 model.addItem(name: "Item" + QString::number(i), number: "");
950
951 QQmlContext *ctxt = window->rootContext();
952 ctxt->setContextProperty("testModel", &model);
953 ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
954 ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
955 ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
956 window->setSource(testFileUrl(fileName: "layouts.qml"));
957 window->show();
958 qApp->processEvents();
959
960 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
961 QTRY_VERIFY(gridview != nullptr);
962 QQuickItem *contentItem = gridview->contentItem();
963 QTRY_VERIFY(contentItem != nullptr);
964
965 if (flow == QQuickGridView::FlowTopToBottom) {
966 removeIndex = removeIndex_ttb;
967 removeCount = removeCount_ttb;
968 firstVisible = firstVisible_ttb;
969 }
970 if (setContentPos(gridview, rowOrColOffset: contentYRowOffset))
971 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
972
973 model.removeItems(index: removeIndex, count: removeCount);
974 gridview->forceLayout();
975 QTRY_COMPARE(gridview->property("count").toInt(), model.count());
976
977 QString firstName;
978 int firstVisibleIndex = -1;
979 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
980 QRectF viewRect(gridview->contentX(), gridview->contentY(), gridview->width(), gridview->height());
981 for (int i=0; i<items.count(); i++) {
982 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
983 if (item) {
984 QRectF itemRect(item->x(), item->y(), item->width(), item->height());
985 if (delegateVisible(item) && viewRect.intersects(r: itemRect)) {
986 firstVisibleIndex = i;
987 QQmlExpression en(qmlContext(item), item, "name");
988 firstName = en.evaluate().toString();
989 break;
990 }
991 }
992 }
993 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
994 QCOMPARE(firstName, firstVisible);
995
996 // Confirm items positioned correctly and indexes correct
997 for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
998 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
999 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1000 QCOMPARE(item->position(), expectedItemPos(gridview, i, rowOffsetAfterMove));
1001 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
1002 QVERIFY(name != nullptr);
1003 QTRY_COMPARE(name->text(), model.name(i));
1004 }
1005
1006 releaseView(view: window);
1007}
1008
1009void tst_QQuickGridView::removed_defaultLayout_data()
1010{
1011 QTest::addColumn<qreal>(name: "contentYRowOffset");
1012 QTest::addColumn<int>(name: "removeIndex");
1013 QTest::addColumn<int>(name: "removeCount");
1014 QTest::addColumn<int>(name: "removeIndex_ttb");
1015 QTest::addColumn<int>(name: "removeCount_ttb");
1016 QTest::addColumn<qreal>(name: "rowOffsetAfterMove");
1017 QTest::addColumn<QString>(name: "firstVisible");
1018 QTest::addColumn<QString>(name: "firstVisible_ttb");
1019
1020 QTest::newRow(dataTag: "remove 1, before visible items")
1021 << 2.0 // show 6-23
1022 << 2 << 1
1023 << 4 << 1
1024 << 0.0 << "Item7" << "Item11";
1025
1026 QTest::newRow(dataTag: "remove 1, before visible position")
1027 << 2.0 // show 6-23
1028 << 3 << 1
1029 << 5 << 1
1030 << 0.0 << "Item7" << "Item11";
1031
1032 QTest::newRow(dataTag: "remove multiple (1 row), all before visible items")
1033 << 2.0
1034 << 1 << 3
1035 << 1 << 5
1036 << 1.0 << "Item6" << "Item10"; // removed top row, slide down by 1 row
1037
1038 QTest::newRow(dataTag: "remove multiple, all before visible items, remove item 0")
1039 << 2.0
1040 << 0 << 4
1041 << 0 << 6
1042 << 1.0 << "Item7" << "Item11"; // removed top row, slide down by 1 row
1043
1044 QTest::newRow(dataTag: "remove multiple rows, all before visible items")
1045 << 4.0 // show 12-29
1046 << 1 << 7
1047 << 1 << 12
1048 << 2.0 << "Item13" << "Item17";
1049
1050 QTest::newRow(dataTag: "remove one row before visible, content y not on item border")
1051 << 1.5
1052 << 0 << 3
1053 << 0 << 5
1054 << 1.0 << "Item3" << "Item5"; // 1 row removed
1055
1056 QTest::newRow(dataTag: "remove mix of visible/non-visible")
1057 << 2.0 // show 6-23
1058 << 2 << 3
1059 << 4 << 3
1060 << 1.0 << "Item6" << "Item8"; // 1 row removed
1061
1062
1063 // remove 3,4,5 before the visible pos, first row moves down to just before the visible pos,
1064 // items 6,7 are removed from view, item 8 slides up to original pos of item 6 (120px)
1065 QTest::newRow(dataTag: "remove multiple, mix of items from before and within visible items")
1066 << 2.0
1067 << 3 << 5
1068 << 5 << 7
1069 << 1.0 << "Item8" << "Item12"; // adjust for the 1 row removed before the visible
1070
1071 QTest::newRow(dataTag: "remove multiple, mix of items from before and within visible items, remove item 0")
1072 << 2.0
1073 << 0 << 8
1074 << 0 << 12
1075 << 1.0 * 2 << "Item8" << "Item12"; // adjust for the 2 rows removed before the visible
1076
1077
1078 QTest::newRow(dataTag: "remove 1, from start of visible, content at start")
1079 << 0.0
1080 << 0 << 1
1081 << 0 << 1
1082 << 0.0 << "Item1" << "Item1";
1083
1084 QTest::newRow(dataTag: "remove multiple, from start of visible, content at start")
1085 << 0.0
1086 << 0 << 3
1087 << 0 << 5
1088 << 0.0 << "Item3" << "Item5";
1089
1090 QTest::newRow(dataTag: "remove 1, from start of visible, content not at start")
1091 << 2.0 // show 6-23
1092 << 4 << 1
1093 << 7 << 1
1094 << 0.0 << "Item7" << "Item11";
1095
1096 QTest::newRow(dataTag: "remove multiple, from start of visible, content not at start")
1097 << 2.0 // show 6-23
1098 << 4 << 3
1099 << 7 << 5
1100 << 0.0 << "Item9" << "Item15";
1101
1102
1103 QTest::newRow(dataTag: "remove 1, from middle of visible, content at start")
1104 << 0.0
1105 << 10 << 1
1106 << 12 << 1
1107 << 0.0 << "Item0" << "Item0";
1108
1109 QTest::newRow(dataTag: "remove multiple, from middle of visible, content at start")
1110 << 0.0
1111 << 10 << 5
1112 << 12 << 5
1113 << 0.0 << "Item0" << "Item0";
1114
1115 QTest::newRow(dataTag: "remove 1, from middle of visible, content not at start")
1116 << 2.0 // show 6-23
1117 << 10 << 1
1118 << 12 << 1
1119 << 0.0 << "Item6" << "Item10";
1120
1121 QTest::newRow(dataTag: "remove multiple, from middle of visible, content not at start")
1122 << 2.0 // show 6-23
1123 << 10 << 5
1124 << 12 << 7
1125 << 0.0 << "Item6" << "Item10";
1126
1127
1128 QTest::newRow(dataTag: "remove 1, after visible, content at start")
1129 << 0.0
1130 << 16 << 1
1131 << 15 << 1
1132 << 0.0 << "Item0" << "Item0";
1133
1134 QTest::newRow(dataTag: "remove multiple, after visible, content at start")
1135 << 0.0
1136 << 16 << 5
1137 << 15 << 7
1138 << 0.0 << "Item0" << "Item0";
1139
1140 QTest::newRow(dataTag: "remove 1, after visible, content not at start")
1141 << 2.0 // show 6-23
1142 << 16+4 << 1
1143 << 15+10 << 1
1144 << 0.0 << "Item6" << "Item10";
1145
1146 QTest::newRow(dataTag: "remove multiple, after visible, content not at start")
1147 << 2.0 // show 6-23
1148 << 16+4 << 5
1149 << 15 << 7
1150 << 0.0 << "Item6" << "Item10";
1151
1152 QTest::newRow(dataTag: "remove multiple, mix of items from within and after visible items")
1153 << 2.0 // show 6-23
1154 << 20 << 5
1155 << 22 << 7
1156 << 0.0 << "Item6" << "Item10";
1157}
1158
1159void tst_QQuickGridView::addOrRemoveBeforeVisible()
1160{
1161 // QTBUG-21588: ensure re-layout is done on grid after adding or removing
1162 // items from before the visible area
1163
1164 QFETCH(bool, doAdd);
1165 QFETCH(qreal, newTopContentY);
1166
1167 QQuickView *window = getView();
1168 window->show();
1169
1170 QaimModel model;
1171 for (int i = 0; i < 30; i++)
1172 model.addItem(name: "Item" + QString::number(i), number: "");
1173
1174 window->rootContext()->setContextProperty("testModel", &model);
1175 window->setSource(testFileUrl(fileName: "gridview1.qml"));
1176
1177 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1178 QTRY_VERIFY(gridview != nullptr);
1179 QQuickItem *contentItem = gridview->contentItem();
1180 QTRY_VERIFY(contentItem != nullptr);
1181
1182 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 0);
1183 QTRY_COMPARE(name->text(), QString("Item0"));
1184
1185 gridview->setCurrentIndex(0);
1186 qApp->processEvents();
1187 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1188
1189 // scroll down until item 0 is no longer drawn
1190 // (bug not triggered if we just move using content y, since that doesn't
1191 // refill and change the visible items)
1192 gridview->setCurrentIndex(24);
1193 qApp->processEvents();
1194
1195 QTRY_COMPARE(gridview->currentIndex(), 24);
1196 QTRY_COMPARE(gridview->contentY(), 220.0);
1197
1198 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1199 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 0)); // 0 shouldn't be visible
1200
1201 if (doAdd) {
1202 model.insertItem(index: 0, name: "New Item", number: "New Item number");
1203 QTRY_COMPARE(gridview->count(), 31);
1204 } else {
1205 model.removeItem(index: 0);
1206 QTRY_COMPARE(gridview->count(), 29);
1207 }
1208
1209 // scroll back up and item 0 should be gone
1210 gridview->setCurrentIndex(0);
1211 qApp->processEvents();
1212 QTRY_COMPARE(gridview->currentIndex(), 0);
1213 QTRY_COMPARE(gridview->contentY(), newTopContentY);
1214
1215 name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 0);
1216 if (doAdd)
1217 QCOMPARE(name->text(), QString("New Item"));
1218 else
1219 QCOMPARE(name->text(), QString("Item1"));
1220
1221 // Confirm items positioned correctly
1222 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
1223 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1224 QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", i));
1225 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
1226 QTRY_COMPARE(item->x(), qreal((i%3)*80));
1227 QTRY_VERIFY(item->y() == (i/3)*60 + newTopContentY);
1228 }
1229
1230 releaseView(view: window);
1231}
1232
1233void tst_QQuickGridView::addOrRemoveBeforeVisible_data()
1234{
1235 QTest::addColumn<bool>(name: "doAdd");
1236 QTest::addColumn<qreal>(name: "newTopContentY");
1237
1238 QTest::newRow(dataTag: "add") << true << -60.0;
1239 QTest::newRow(dataTag: "remove") << false << -60.0;
1240}
1241
1242void tst_QQuickGridView::clear()
1243{
1244 QQuickView *window = createView();
1245
1246 QaimModel model;
1247 for (int i = 0; i < 30; i++)
1248 model.addItem(name: "Item" + QString::number(i), number: "");
1249
1250 window->rootContext()->setContextProperty("testModel", &model);
1251 window->setSource(testFileUrl(fileName: "gridview1.qml"));
1252 window->show();
1253 qApp->processEvents();
1254
1255 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1256 QVERIFY(gridview != nullptr);
1257 QQuickItem *contentItem = gridview->contentItem();
1258 QVERIFY(contentItem != nullptr);
1259 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1260
1261 model.clear();
1262 gridview->forceLayout();
1263
1264 QCOMPARE(gridview->count(), 0);
1265 QVERIFY(!gridview->currentItem());
1266 QCOMPARE(gridview->contentY(), qreal(0));
1267 QCOMPARE(gridview->currentIndex(), -1);
1268 QCOMPARE(gridview->contentHeight(), 0.0);
1269
1270 // confirm sanity when adding an item to cleared list
1271 model.addItem(name: "New", number: "1");
1272 gridview->forceLayout();
1273 QTRY_COMPARE(gridview->count(), 1);
1274 QVERIFY(gridview->currentItem() != nullptr);
1275 QCOMPARE(gridview->currentIndex(), 0);
1276
1277 delete window;
1278}
1279
1280void tst_QQuickGridView::moved_defaultLayout(QQuickGridView::Flow flow,
1281 Qt::LayoutDirection horizLayout,
1282 QQuickItemView::VerticalLayoutDirection verticalLayout)
1283{
1284 QFETCH(qreal, contentYRowOffset);
1285 QFETCH(int, from);
1286 QFETCH(int, to);
1287 QFETCH(int, count);
1288 QFETCH(int, from_ttb);
1289 QFETCH(int, to_ttb);
1290 QFETCH(int, count_ttb);
1291 QFETCH(qreal, rowOffsetAfterMove);
1292
1293 QQuickView *window = getView();
1294
1295 QaimModel model;
1296 for (int i = 0; i < 30; i++)
1297 model.addItem(name: "Item" + QString::number(i), number: "");
1298
1299 QQmlContext *ctxt = window->rootContext();
1300 ctxt->setContextProperty("testModel", &model);
1301 ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
1302 ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
1303 ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
1304 window->setSource(testFileUrl(fileName: "layouts.qml"));
1305 window->show();
1306 qApp->processEvents();
1307
1308 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1309 QTRY_VERIFY(gridview != nullptr);
1310 QQuickItem *contentItem = gridview->contentItem();
1311 QTRY_VERIFY(contentItem != nullptr);
1312 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1313
1314 QQuickItem *currentItem = gridview->currentItem();
1315 QTRY_VERIFY(currentItem != nullptr);
1316
1317 if (flow == QQuickGridView::FlowTopToBottom) {
1318 from = from_ttb;
1319 to = to_ttb;
1320 count = count_ttb;
1321 }
1322 if (setContentPos(gridview, rowOrColOffset: contentYRowOffset))
1323 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1324
1325 model.moveItems(from, to, count);
1326 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1327
1328 // Confirm items positioned correctly and indexes correct
1329 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
1330 int firstVisibleIndex = -1;
1331 for (int i=0; i<items.count(); i++) {
1332 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
1333 if (item && delegateVisible(item)) {
1334 firstVisibleIndex = i;
1335 break;
1336 }
1337 }
1338 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1339
1340 for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
1341 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
1342 if (!item &&
1343 ( (flow == QQuickGridView::FlowLeftToRight && i >= firstVisibleIndex + (3*6))
1344 || (flow == QQuickGridView::FlowTopToBottom && i >= firstVisibleIndex + (5*3)) ) ) {
1345 continue; // index has moved out of view
1346 }
1347 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1348 QCOMPARE(item->position(), expectedItemPos(gridview, i, rowOffsetAfterMove));
1349 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
1350 QVERIFY(name != nullptr);
1351 QTRY_COMPARE(name->text(), model.name(i));
1352
1353 // current index should have been updated
1354 if (item == currentItem)
1355 QTRY_COMPARE(gridview->currentIndex(), i);
1356 }
1357
1358 releaseView(view: window);
1359}
1360
1361void tst_QQuickGridView::moved_defaultLayout_data()
1362{
1363 QTest::addColumn<qreal>(name: "contentYRowOffset");
1364 QTest::addColumn<int>(name: "from");
1365 QTest::addColumn<int>(name: "to");
1366 QTest::addColumn<int>(name: "count");
1367 QTest::addColumn<int>(name: "from_ttb");
1368 QTest::addColumn<int>(name: "to_ttb");
1369 QTest::addColumn<int>(name: "count_ttb");
1370 QTest::addColumn<qreal>(name: "rowOffsetAfterMove");
1371
1372 // model starts with 30 items, each 80x60, in area 240x320
1373 // 18 items should be visible at a time
1374
1375 // The first visible item should not move upwards and out of the view
1376 // if items are moved/removed before it.
1377
1378
1379 QTest::newRow(dataTag: "move 1 forwards, within visible items")
1380 << 0.0
1381 << 1 << 8 << 1
1382 << 2 << 12 << 1
1383 << 0.0;
1384
1385 QTest::newRow(dataTag: "move 1 forwards, from non-visible -> visible")
1386 << 2.0 // show 6-23
1387 << 1 << 8+6 << 1
1388 << 2 << 12+10 << 1
1389 << 0.0;
1390
1391 QTest::newRow(dataTag: "move 1 forwards, from non-visible -> visible (move first item)")
1392 << 2.0 // // show 6-23
1393 << 0 << 6 << 1
1394 << 0 << 10 << 1
1395 << 0.0;
1396
1397 QTest::newRow(dataTag: "move 1 forwards, from visible -> non-visible")
1398 << 0.0
1399 << 1 << 20 << 1
1400 << 1 << 20 << 1
1401 << 0.0;
1402
1403 QTest::newRow(dataTag: "move 1 forwards, from visible -> non-visible (move first item)")
1404 << 0.0
1405 << 0 << 20 << 1
1406 << 0 << 20 << 1
1407 << 0.0;
1408
1409
1410 QTest::newRow(dataTag: "move 1 backwards, within visible items")
1411 << 0.0
1412 << 10 << 5 << 1
1413 << 10 << 5 << 1
1414 << 0.0;
1415
1416 QTest::newRow(dataTag: "move 1 backwards, within visible items (to first index)")
1417 << 0.0
1418 << 10 << 0 << 1
1419 << 10 << 0 << 1
1420 << 0.0;
1421
1422 QTest::newRow(dataTag: "move 1 backwards, from non-visible -> visible")
1423 << 0.0
1424 << 28 << 8 << 1
1425 << 28 << 8 << 1
1426 << 0.0;
1427
1428 QTest::newRow(dataTag: "move 1 backwards, from non-visible -> visible (move last item)")
1429 << 0.0
1430 << 29 << 14 << 1
1431 << 29 << 14 << 1
1432 << 0.0;
1433
1434 QTest::newRow(dataTag: "move 1 backwards, from visible -> non-visible")
1435 << 2.0 // show 6-23
1436 << 7 << 1 << 1
1437 << 10 << 1 << 1
1438 << 0.0; // only 1 item moved back, so items shift accordingly and first row doesn't move
1439
1440 QTest::newRow(dataTag: "move 1 backwards, from visible -> non-visible (move first item)")
1441 << 2.0 // show 6-23
1442 << 7 << 0 << 1
1443 << 10 << 0 << 1
1444 << 0.0; // only 1 item moved back, so items shift accordingly and first row doesn't move
1445
1446
1447 QTest::newRow(dataTag: "move multiple forwards, within visible items")
1448 << 0.0
1449 << 0 << 5 << 3
1450 << 0 << 7 << 5
1451 << 0.0;
1452
1453 QTest::newRow(dataTag: "move multiple backwards, within visible items (move first item)")
1454 << 0.0
1455 << 10 << 0 << 3
1456 << 12 << 0 << 5
1457 << 0.0;
1458
1459 QTest::newRow(dataTag: "move multiple forwards, before visible items")
1460 << 2.0 // show 6-23
1461 << 3 << 4 << 3 // 3, 4, 5 move to after 6
1462 << 5 << 6 << 5
1463 << 1.0; // row of 3,4,5 has moved down
1464
1465 QTest::newRow(dataTag: "move multiple forwards, from non-visible -> visible")
1466 << 2.0 // show 6-23
1467 << 1 << 6 << 3
1468 << 1 << 10 << 5
1469 << 1.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is
1470
1471 QTest::newRow(dataTag: "move multiple forwards, from non-visible -> visible (move first item)")
1472 << 2.0 // show 6-23
1473 << 0 << 6 << 3
1474 << 0 << 10 << 5
1475 << 1.0; // top row moved and shifted to below 3rd row, all items should shift down by 1 row
1476
1477 QTest::newRow(dataTag: "move multiple forwards, mix of non-visible/visible")
1478 << 2.0
1479 << 3 << 16 << 6
1480 << 5 << 18 << 10
1481 << 1.0; // top two rows removed, third row is now the first visible
1482
1483 QTest::newRow(dataTag: "move multiple forwards, to bottom of view")
1484 << 0.0
1485 << 5 << 13 << 5
1486 << 1 << 8 << 6
1487 << 0.0;
1488
1489 QTest::newRow(dataTag: "move multiple forwards, to bottom of view, first row -> last")
1490 << 0.0
1491 << 0 << 15 << 3
1492 << 0 << 10 << 5
1493 << 0.0;
1494
1495 QTest::newRow(dataTag: "move multiple forwards, to bottom of view, content y not 0")
1496 << 2.0
1497 << 5+4 << 13+4 << 5
1498 << 11 << 19 << 6
1499 << 0.0;
1500
1501 QTest::newRow(dataTag: "move multiple forwards, from visible -> non-visible")
1502 << 0.0
1503 << 1 << 16 << 3
1504 << 1 << 18 << 5
1505 << 0.0;
1506
1507 QTest::newRow(dataTag: "move multiple forwards, from visible -> non-visible (move first item)")
1508 << 0.0
1509 << 0 << 16 << 3
1510 << 1 << 18 << 5
1511 << 0.0;
1512
1513
1514 QTest::newRow(dataTag: "move multiple backwards, within visible items")
1515 << 0.0
1516 << 4 << 1 << 3
1517 << 7 << 1 << 5
1518 << 0.0;
1519
1520 QTest::newRow(dataTag: "move multiple backwards, from non-visible -> visible")
1521 << 0.0
1522 << 20 << 4 << 3
1523 << 20 << 4 << 5
1524 << 0.0;
1525
1526 QTest::newRow(dataTag: "move multiple backwards, from non-visible -> visible (move last item)")
1527 << 0.0
1528 << 27 << 10 << 3
1529 << 25 << 8 << 5
1530 << 0.0;
1531
1532 QTest::newRow(dataTag: "move multiple backwards, from visible -> non-visible")
1533 << 2.0 // show 6-23
1534 << 16 << 1 << 3
1535 << 17 << 1 << 5
1536 << -1.0; // to minimize movement, items are added above visible area, all items move up by 1 row
1537
1538 QTest::newRow(dataTag: "move multiple backwards, from visible -> non-visible (move first item)")
1539 << 2.0 // show 6-23
1540 << 16 << 0 << 3
1541 << 17 << 0 << 5
1542 << -1.0; // 16,17,18 move to above item 0, all items move up by 1 row
1543}
1544
1545void tst_QQuickGridView::multipleChanges(bool condensed)
1546{
1547 QFETCH(int, startCount);
1548 QFETCH(QList<ListChange>, changes);
1549 QFETCH(int, newCount);
1550 QFETCH(int, newCurrentIndex);
1551
1552 QQuickView *window = getView();
1553
1554 QaimModel model;
1555 for (int i = 0; i < startCount; i++)
1556 model.addItem(name: "Item" + QString::number(i), number: "");
1557
1558 window->rootContext()->setContextProperty("testModel", &model);
1559 window->setSource(testFileUrl(fileName: "gridview1.qml"));
1560 window->show();
1561 qApp->processEvents();
1562
1563 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1564 QTRY_VERIFY(gridview != nullptr);
1565 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1566
1567 for (int i=0; i<changes.count(); i++) {
1568 switch (changes[i].type) {
1569 case ListChange::Inserted:
1570 {
1571 QList<QPair<QString, QString> > items;
1572 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1573 items << qMakePair(x: QString("new item %1").arg(a: j), y: QString::number(j));
1574 model.insertItems(index: changes[i].index, items);
1575 break;
1576 }
1577 case ListChange::Removed:
1578 model.removeItems(index: changes[i].index, count: changes[i].count);
1579 break;
1580 case ListChange::Moved:
1581 model.moveItems(from: changes[i].index, to: changes[i].to, count: changes[i].count);
1582 break;
1583 case ListChange::SetCurrent:
1584 gridview->setCurrentIndex(changes[i].index);
1585 break;
1586 case ListChange::SetContentY:
1587 gridview->setContentY(changes[i].pos);
1588 break;
1589 case ListChange::Polish:
1590 break;
1591 }
1592 if (condensed) {
1593 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1594 }
1595 }
1596 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1597
1598 QCOMPARE(gridview->count(), newCount);
1599 QCOMPARE(gridview->count(), model.count());
1600 QCOMPARE(gridview->currentIndex(), newCurrentIndex);
1601
1602 QQuickText *name;
1603 QQuickText *number;
1604 QQuickItem *contentItem = gridview->contentItem();
1605 QTRY_VERIFY(contentItem != nullptr);
1606 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
1607 for (int i=0; i < model.count() && i < itemCount; ++i) {
1608 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
1609 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1610 name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
1611 QVERIFY(name != nullptr);
1612 QTRY_COMPARE(name->text(), model.name(i));
1613 number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: i);
1614 QVERIFY(number != nullptr);
1615 QTRY_COMPARE(number->text(), model.number(i));
1616 }
1617
1618 releaseView(view: window);
1619}
1620
1621void tst_QQuickGridView::multipleChanges_data()
1622{
1623 QTest::addColumn<int>(name: "startCount");
1624 QTest::addColumn<QList<ListChange> >(name: "changes");
1625 QTest::addColumn<int>(name: "newCount");
1626 QTest::addColumn<int>(name: "newCurrentIndex");
1627
1628 QList<ListChange> changes;
1629
1630 for (int i=1; i<30; i++)
1631 changes << ListChange::remove(index: 0);
1632 QTest::newRow(dataTag: "remove all but 1, first->last") << 30 << changes << 1 << 0;
1633
1634 changes << ListChange::remove(index: 0);
1635 QTest::newRow(dataTag: "remove all") << 30 << changes << 0 << -1;
1636
1637 changes.clear();
1638 changes << ListChange::setCurrent(29);
1639 for (int i=29; i>0; i--)
1640 changes << ListChange::remove(index: i);
1641 QTest::newRow(dataTag: "remove last (current) -> first") << 30 << changes << 1 << 0;
1642
1643 QTest::newRow(dataTag: "remove then insert at 0") << 10 << (QList<ListChange>()
1644 << ListChange::remove(index: 0, count: 1)
1645 << ListChange::insert(index: 0, count: 1)
1646 ) << 10 << 1;
1647
1648 QTest::newRow(dataTag: "remove then insert at non-zero index") << 10 << (QList<ListChange>()
1649 << ListChange::setCurrent(2)
1650 << ListChange::remove(index: 2, count: 1)
1651 << ListChange::insert(index: 2, count: 1)
1652 ) << 10 << 3;
1653
1654 QTest::newRow(dataTag: "remove current then insert below it") << 10 << (QList<ListChange>()
1655 << ListChange::setCurrent(1)
1656 << ListChange::remove(index: 1, count: 3)
1657 << ListChange::insert(index: 2, count: 2)
1658 ) << 9 << 1;
1659
1660 QTest::newRow(dataTag: "remove current index then move it down") << 10 << (QList<ListChange>()
1661 << ListChange::setCurrent(2)
1662 << ListChange::remove(index: 1, count: 3)
1663 << ListChange::move(index: 1, to: 5, count: 1)
1664 ) << 7 << 5;
1665
1666 QTest::newRow(dataTag: "remove current index then move it up") << 10 << (QList<ListChange>()
1667 << ListChange::setCurrent(5)
1668 << ListChange::remove(index: 4, count: 3)
1669 << ListChange::move(index: 4, to: 1, count: 1)
1670 ) << 7 << 1;
1671
1672
1673 QTest::newRow(dataTag: "insert multiple times") << 0 << (QList<ListChange>()
1674 << ListChange::insert(index: 0, count: 2)
1675 << ListChange::insert(index: 0, count: 4)
1676 << ListChange::insert(index: 0, count: 6)
1677 ) << 12 << 10;
1678
1679 QTest::newRow(dataTag: "insert multiple times with current index changes") << 0 << (QList<ListChange>()
1680 << ListChange::insert(index: 0, count: 2)
1681 << ListChange::insert(index: 0, count: 4)
1682 << ListChange::insert(index: 0, count: 6)
1683 << ListChange::setCurrent(3)
1684 << ListChange::insert(index: 3, count: 2)
1685 ) << 14 << 5;
1686
1687 QTest::newRow(dataTag: "insert and remove all") << 0 << (QList<ListChange>()
1688 << ListChange::insert(index: 0, count: 30)
1689 << ListChange::remove(index: 0, count: 30)
1690 ) << 0 << -1;
1691
1692 QTest::newRow(dataTag: "insert and remove current") << 30 << (QList<ListChange>()
1693 << ListChange::insert(index: 1)
1694 << ListChange::setCurrent(1)
1695 << ListChange::remove(index: 1)
1696 ) << 30 << 1;
1697
1698 QTest::newRow(dataTag: "insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1699 << ListChange::insert(index: 0, count: 10)
1700 << ListChange::remove(index: 5, count: 10)
1701 ) << 10 << 5;
1702
1703 QTest::newRow(dataTag: "insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1704 << ListChange::insert(index: 0, count: 3)
1705 << ListChange::move(index: 0, to: 10, count: 3)
1706 ) << 13 << 0;
1707
1708 QTest::newRow(dataTag: "insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1709 << ListChange::insert(index: 0, count: 3)
1710 << ListChange::move(index: 0, to: 8, count: 5)
1711 ) << 13 << 11;
1712
1713 QTest::newRow(dataTag: "insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1714 << ListChange::setCurrent(9)
1715 << ListChange::insert(index: 10, count: 3)
1716 << ListChange::move(index: 8, to: 0, count: 5)
1717 ) << 13 << 1;
1718
1719
1720 QTest::newRow(dataTag: "move back and forth to same index") << 10 << (QList<ListChange>()
1721 << ListChange::setCurrent(1)
1722 << ListChange::move(index: 1, to: 2, count: 2)
1723 << ListChange::move(index: 2, to: 1, count: 2)
1724 ) << 10 << 1;
1725
1726 QTest::newRow(dataTag: "move forwards then back") << 10 << (QList<ListChange>()
1727 << ListChange::setCurrent(2)
1728 << ListChange::move(index: 1, to: 2, count: 3)
1729 << ListChange::move(index: 3, to: 0, count: 5)
1730 ) << 10 << 0;
1731
1732 QTest::newRow(dataTag: "move current, then remove it") << 10 << (QList<ListChange>()
1733 << ListChange::setCurrent(5)
1734 << ListChange::move(index: 5, to: 0, count: 1)
1735 << ListChange::remove(index: 0)
1736 ) << 9 << 0;
1737
1738 QTest::newRow(dataTag: "move current, then insert before it") << 10 << (QList<ListChange>()
1739 << ListChange::setCurrent(5)
1740 << ListChange::move(index: 5, to: 0, count: 1)
1741 << ListChange::insert(index: 0)
1742 ) << 11 << 1;
1743
1744 QTest::newRow(dataTag: "move multiple, then remove them") << 10 << (QList<ListChange>()
1745 << ListChange::setCurrent(1)
1746 << ListChange::move(index: 5, to: 1, count: 3)
1747 << ListChange::remove(index: 1, count: 3)
1748 ) << 7 << 1;
1749
1750 QTest::newRow(dataTag: "move multiple, then insert before them") << 10 << (QList<ListChange>()
1751 << ListChange::setCurrent(5)
1752 << ListChange::move(index: 5, to: 1, count: 3)
1753 << ListChange::insert(index: 1, count: 5)
1754 ) << 15 << 6;
1755
1756 QTest::newRow(dataTag: "move multiple, then insert after them") << 10 << (QList<ListChange>()
1757 << ListChange::setCurrent(3)
1758 << ListChange::move(index: 0, to: 1, count: 2)
1759 << ListChange::insert(index: 3, count: 5)
1760 ) << 15 << 8;
1761
1762
1763 QTest::newRow(dataTag: "clear current") << 0 << (QList<ListChange>()
1764 << ListChange::insert(index: 0, count: 5)
1765 << ListChange::setCurrent(-1)
1766 << ListChange::remove(index: 0, count: 5)
1767 << ListChange::insert(index: 0, count: 5)
1768 ) << 5 << -1;
1769
1770 QTest::newRow(dataTag: "remove, scroll") << 30 << (QList<ListChange>()
1771 << ListChange::remove(index: 20, count: 5)
1772 << ListChange::setContentY(20)
1773 ) << 25 << 0;
1774
1775 QTest::newRow(dataTag: "insert, scroll") << 10 << (QList<ListChange>()
1776 << ListChange::insert(index: 9, count: 5)
1777 << ListChange::setContentY(20)
1778 ) << 15 << 0;
1779
1780 QTest::newRow(dataTag: "move, scroll") << 20 << (QList<ListChange>()
1781 << ListChange::move(index: 15, to: 8, count: 3)
1782 << ListChange::setContentY(0)
1783 ) << 20 << 0;
1784
1785 QTest::newRow(dataTag: "clear, insert, scroll") << 30 << (QList<ListChange>()
1786 << ListChange::setContentY(20)
1787 << ListChange::remove(index: 0, count: 30)
1788 << ListChange::insert(index: 0, count: 2)
1789 << ListChange::setContentY(0)
1790 ) << 2 << 0;
1791}
1792
1793
1794void tst_QQuickGridView::swapWithFirstItem()
1795{
1796 // QTBUG_9697
1797 QQuickView *window = createView();
1798
1799 QaimModel model;
1800 for (int i = 0; i < 30; i++)
1801 model.addItem(name: "Item" + QString::number(i), number: "");
1802
1803 window->rootContext()->setContextProperty("testModel", &model);
1804 window->setSource(testFileUrl(fileName: "gridview1.qml"));
1805 window->show();
1806 qApp->processEvents();
1807
1808 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1809 QTRY_VERIFY(gridview != nullptr);
1810
1811 // ensure content position is stable
1812 gridview->setContentY(0);
1813 model.moveItem(from: 10, to: 0);
1814 QTRY_COMPARE(gridview->contentY(), qreal(0));
1815
1816 delete window;
1817}
1818
1819void tst_QQuickGridView::currentIndex()
1820{
1821 QaimModel initModel;
1822 for (int i = 0; i < 60; i++)
1823 initModel.addItem(name: "Item" + QString::number(i), number: QString::number(i));
1824
1825 QQuickView *window = new QQuickView(nullptr);
1826 window->setGeometry(posx: 0,posy: 0,w: 240,h: 320);
1827 window->show();
1828
1829 QQmlContext *ctxt = window->rootContext();
1830 ctxt->setContextProperty("testModel", &initModel);
1831
1832 QString filename(testFile(fileName: "gridview-initCurrent.qml"));
1833 window->setSource(QUrl::fromLocalFile(localfile: filename));
1834
1835 qApp->processEvents();
1836
1837 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1838 QVERIFY(gridview != nullptr);
1839 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1840
1841 QQuickItem *contentItem = gridview->contentItem();
1842 QVERIFY(contentItem != nullptr);
1843
1844 // currentIndex is initialized to 35
1845 // currentItem should be in view
1846 QCOMPARE(gridview->currentIndex(), 35);
1847 QCOMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 35));
1848 // Wait for possible animation to finish
1849 QTRY_COMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
1850 QCOMPARE(gridview->contentY(), 400.0);
1851
1852 // changing model should reset currentIndex to 0
1853 QaimModel model;
1854 for (int i = 0; i < 60; i++)
1855 model.addItem(name: "Item" + QString::number(i), number: QString::number(i));
1856 ctxt->setContextProperty("testModel", &model);
1857
1858 QCOMPARE(gridview->currentIndex(), 0);
1859 QCOMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
1860
1861 // confirm that the velocity is updated
1862 gridview->setCurrentIndex(35);
1863 QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
1864 gridview->setCurrentIndex(0);
1865 QTRY_COMPARE(gridview->verticalVelocity(), 0.0);
1866
1867 // footer should become visible if it is out of view, and then current index moves to the first row
1868 window->rootObject()->setProperty(name: "showFooter", value: true);
1869 QTRY_VERIFY(gridview->footerItem());
1870 gridview->setCurrentIndex(model.count()-3);
1871 QTRY_VERIFY(gridview->footerItem()->y() > gridview->contentY() + gridview->height());
1872 gridview->setCurrentIndex(model.count()-2);
1873 QTRY_COMPARE(gridview->contentY() + gridview->height(), (60.0 * model.count()/3) + gridview->footerItem()->height());
1874 window->rootObject()->setProperty(name: "showFooter", value: false);
1875
1876 // header should become visible if it is out of view, and then current index moves to the last row
1877 window->rootObject()->setProperty(name: "showHeader", value: true);
1878 QTRY_VERIFY(gridview->headerItem());
1879 gridview->setCurrentIndex(3);
1880 QTRY_VERIFY(gridview->headerItem()->y() + gridview->headerItem()->height() < gridview->contentY());
1881 gridview->setCurrentIndex(1);
1882 QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
1883 window->rootObject()->setProperty(name: "showHeader", value: false);
1884
1885 // turn off auto highlight
1886 gridview->setHighlightFollowsCurrentItem(false);
1887 QVERIFY(!gridview->highlightFollowsCurrentItem());
1888 QVERIFY(gridview->highlightItem());
1889 qreal hlPosX = gridview->highlightItem()->x();
1890 qreal hlPosY = gridview->highlightItem()->y();
1891
1892 gridview->setCurrentIndex(5);
1893 QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1894 QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1895
1896 // insert item before currentIndex
1897 window->rootObject()->setProperty(name: "currentItemChangedCount", value: QVariant(0));
1898 gridview->setCurrentIndex(28);
1899 QTRY_COMPARE(window->rootObject()->property("currentItemChangedCount").toInt(), 1);
1900 model.insertItem(index: 0, name: "Foo", number: "1111");
1901 QTRY_COMPARE(window->rootObject()->property("current").toInt(), 29);
1902 QCOMPARE(window->rootObject()->property("currentItemChangedCount").toInt(), 1);
1903
1904 // check removing highlight by setting currentIndex to -1;
1905 gridview->setCurrentIndex(-1);
1906
1907 QCOMPARE(gridview->currentIndex(), -1);
1908 QVERIFY(!gridview->highlightItem());
1909 QVERIFY(!gridview->currentItem());
1910
1911 // moving currentItem out of view should make it invisible
1912 gridview->setCurrentIndex(0);
1913 QTRY_VERIFY(delegateVisible(gridview->currentItem()));
1914 gridview->setContentY(200);
1915 QTRY_VERIFY(!delegateVisible(gridview->currentItem()));
1916
1917 delete window;
1918}
1919
1920void tst_QQuickGridView::noCurrentIndex()
1921{
1922 QaimModel model;
1923 for (int i = 0; i < 60; i++)
1924 model.addItem(name: "Item" + QString::number(i), number: QString::number(i));
1925
1926 QQuickView *window = new QQuickView(nullptr);
1927 window->setGeometry(posx: 0,posy: 0,w: 240,h: 320);
1928
1929 QQmlContext *ctxt = window->rootContext();
1930 ctxt->setContextProperty("testModel", &model);
1931
1932 QString filename(testFile(fileName: "gridview-noCurrent.qml"));
1933 window->setSource(QUrl::fromLocalFile(localfile: filename));
1934 window->show();
1935 qApp->processEvents();
1936
1937 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1938 QVERIFY(gridview != nullptr);
1939 QQuickItem *contentItem = gridview->contentItem();
1940 QVERIFY(contentItem != nullptr);
1941 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1942
1943 // current index should be -1
1944 QCOMPARE(gridview->currentIndex(), -1);
1945 QVERIFY(!gridview->currentItem());
1946 QVERIFY(!gridview->highlightItem());
1947 QCOMPARE(gridview->contentY(), 0.0);
1948
1949 gridview->setCurrentIndex(5);
1950 QCOMPARE(gridview->currentIndex(), 5);
1951 QVERIFY(gridview->currentItem());
1952 QVERIFY(gridview->highlightItem());
1953
1954 delete window;
1955}
1956
1957void tst_QQuickGridView::keyNavigation()
1958{
1959 QFETCH(QQuickGridView::Flow, flow);
1960 QFETCH(Qt::LayoutDirection, layoutDirection);
1961 QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
1962 QFETCH(Qt::Key, forwardsKey);
1963 QFETCH(Qt::Key, backwardsKey);
1964 QFETCH(QPointF, contentPosAtFirstItem);
1965 QFETCH(QPointF, contentPosAtLastItem);
1966 QFETCH(int, indexRightOf7);
1967 QFETCH(int, indexLeftOf7);
1968 QFETCH(int, indexUpFrom7);
1969 QFETCH(int, indexDownFrom7);
1970
1971 QaimModel model;
1972 for (int i = 0; i < 18; i++)
1973 model.addItem(name: "Item" + QString::number(i), number: "");
1974
1975 QQuickView *window = getView();
1976 window->rootContext()->setContextProperty("testModel", &model);
1977 window->setSource(testFileUrl(fileName: "gridview1.qml"));
1978 window->show();
1979 QVERIFY(QTest::qWaitForWindowActive(window));
1980
1981 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
1982 QTRY_VERIFY(gridview != nullptr);
1983 gridview->setFlow(flow);
1984 gridview->setLayoutDirection(layoutDirection);
1985 gridview->setVerticalLayoutDirection(verticalLayoutDirection);
1986 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
1987
1988 window->requestActivate();
1989 QVERIFY(QTest::qWaitForWindowActive(window));
1990 QTRY_COMPARE(qGuiApp->focusWindow(), window);
1991 QCOMPARE(gridview->currentIndex(), 0);
1992
1993 QTest::keyClick(window, key: forwardsKey);
1994 QCOMPARE(gridview->currentIndex(), 1);
1995
1996 QTest::keyClick(window, key: backwardsKey);
1997 QCOMPARE(gridview->currentIndex(), 0);
1998
1999 gridview->setCurrentIndex(7);
2000 gridview->moveCurrentIndexRight();
2001 QCOMPARE(gridview->currentIndex(), indexRightOf7);
2002 gridview->moveCurrentIndexLeft();
2003 QCOMPARE(gridview->currentIndex(), 7);
2004 gridview->moveCurrentIndexLeft();
2005 QCOMPARE(gridview->currentIndex(), indexLeftOf7);
2006 gridview->moveCurrentIndexRight();
2007 QCOMPARE(gridview->currentIndex(), 7);
2008 gridview->moveCurrentIndexUp();
2009 QCOMPARE(gridview->currentIndex(), indexUpFrom7);
2010 gridview->moveCurrentIndexDown();
2011 QCOMPARE(gridview->currentIndex(), 7);
2012 gridview->moveCurrentIndexDown();
2013 QCOMPARE(gridview->currentIndex(), indexDownFrom7);
2014
2015 gridview->setCurrentIndex(7);
2016 QTest::keyClick(window, key: Qt::Key_Right);
2017 QCOMPARE(gridview->currentIndex(), indexRightOf7);
2018 QTest::keyClick(window, key: Qt::Key_Left);
2019 QCOMPARE(gridview->currentIndex(), 7);
2020 QTest::keyClick(window, key: Qt::Key_Left);
2021 QCOMPARE(gridview->currentIndex(), indexLeftOf7);
2022 QTest::keyClick(window, key: Qt::Key_Right);
2023 QCOMPARE(gridview->currentIndex(), 7);
2024 QTest::keyClick(window, key: Qt::Key_Up);
2025 QCOMPARE(gridview->currentIndex(), indexUpFrom7);
2026 QTest::keyClick(window, key: Qt::Key_Down);
2027 QCOMPARE(gridview->currentIndex(), 7);
2028 QTest::keyClick(window, key: Qt::Key_Down);
2029 QCOMPARE(gridview->currentIndex(), indexDownFrom7);
2030
2031 // hold down a key to go forwards
2032 gridview->setCurrentIndex(0);
2033 for (int i=0; i<model.count()-1; i++) {
2034// QTest::qWait(500);
2035 QTest::simulateEvent(window, press: true, code: forwardsKey, modifier: Qt::NoModifier, text: "", repeat: true);
2036 QTRY_COMPARE(gridview->currentIndex(), i+1);
2037 }
2038 QTest::keyRelease(window, key: forwardsKey);
2039 QTRY_COMPARE(gridview->currentIndex(), model.count()-1);
2040 QTRY_COMPARE(gridview->contentX(), contentPosAtLastItem.x());
2041 QTRY_COMPARE(gridview->contentY(), contentPosAtLastItem.y());
2042
2043 // hold down a key to go backwards
2044 for (int i=model.count()-1; i > 0; i--) {
2045 QTest::simulateEvent(window, press: true, code: backwardsKey, modifier: Qt::NoModifier, text: "", repeat: true);
2046 QTRY_COMPARE(gridview->currentIndex(), i-1);
2047 }
2048 QTest::keyRelease(window, key: backwardsKey);
2049 QTRY_COMPARE(gridview->currentIndex(), 0);
2050 QTRY_COMPARE(gridview->contentX(), contentPosAtFirstItem.x());
2051 QTRY_COMPARE(gridview->contentY(), contentPosAtFirstItem.y());
2052
2053 // no wrap
2054 QVERIFY(!gridview->isWrapEnabled());
2055 QTest::keyClick(window, key: forwardsKey);
2056 QCOMPARE(gridview->currentIndex(), 1);
2057 QTest::keyClick(window, key: backwardsKey);
2058 QCOMPARE(gridview->currentIndex(), 0);
2059
2060 QTest::keyClick(window, key: backwardsKey);
2061 QCOMPARE(gridview->currentIndex(), 0);
2062
2063 // with wrap
2064 gridview->setWrapEnabled(true);
2065 QVERIFY(gridview->isWrapEnabled());
2066
2067 QTest::keyClick(window, key: backwardsKey);
2068 QCOMPARE(gridview->currentIndex(), model.count()-1);
2069 QTRY_COMPARE(gridview->contentX(), contentPosAtLastItem.x());
2070 QTRY_COMPARE(gridview->contentY(), contentPosAtLastItem.y());
2071
2072 QTest::keyClick(window, key: forwardsKey);
2073 QCOMPARE(gridview->currentIndex(), 0);
2074 QTRY_COMPARE(gridview->contentX(), contentPosAtFirstItem.x());
2075 QTRY_COMPARE(gridview->contentY(), contentPosAtFirstItem.y());
2076
2077 // Test key press still accepted when position wraps to same index.
2078 window->rootObject()->setProperty(name: "lastKey", value: 0);
2079 model.removeItems(index: 1, count: model.count() - 1);
2080
2081 QTest::keyClick(window, key: backwardsKey);
2082 QCOMPARE(window->rootObject()->property("lastKey").toInt(), 0);
2083
2084 QTest::keyClick(window, key: forwardsKey);
2085 QCOMPARE(window->rootObject()->property("lastKey").toInt(), 0);
2086
2087 // Test key press not accepted at limits when wrap is disabled.
2088 gridview->setWrapEnabled(false);
2089
2090 QTest::keyClick(window, key: backwardsKey);
2091 QCOMPARE(window->rootObject()->property("lastKey").toInt(), int(backwardsKey));
2092
2093 QTest::keyClick(window, key: forwardsKey);
2094 QCOMPARE(window->rootObject()->property("lastKey").toInt(), int(forwardsKey));
2095
2096 releaseView(view: window);
2097}
2098
2099void tst_QQuickGridView::keyNavigation_data()
2100{
2101 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
2102 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
2103 QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection");
2104 QTest::addColumn<Qt::Key>(name: "forwardsKey");
2105 QTest::addColumn<Qt::Key>(name: "backwardsKey");
2106 QTest::addColumn<QPointF>(name: "contentPosAtFirstItem");
2107 QTest::addColumn<QPointF>(name: "contentPosAtLastItem");
2108 QTest::addColumn<int>(name: "indexRightOf7");
2109 QTest::addColumn<int>(name: "indexLeftOf7");
2110 QTest::addColumn<int>(name: "indexUpFrom7");
2111 QTest::addColumn<int>(name: "indexDownFrom7");
2112
2113 QTest::newRow(dataTag: "LeftToRight, LtR, TtB")
2114 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
2115 << Qt::Key_Right << Qt::Key_Left
2116 << QPointF(0, 0)
2117 << QPointF(0, 40)
2118 << 8 << 6 << 4 << 10;
2119
2120 QTest::newRow(dataTag: "LeftToRight, RtL, TtB")
2121 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
2122 << Qt::Key_Left << Qt::Key_Right
2123 << QPointF(0, 0)
2124 << QPointF(0, 40)
2125 << 6 << 8 << 4 << 10;
2126
2127 QTest::newRow(dataTag: "LeftToRight, LtR, BtT")
2128 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
2129 << Qt::Key_Right << Qt::Key_Left
2130 << QPointF(0, -320)
2131 << QPointF(0, -360)
2132 << 8 << 6 << 10 << 4;
2133
2134 QTest::newRow(dataTag: "LeftToRight, RtL, BtT")
2135 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
2136 << Qt::Key_Left << Qt::Key_Right
2137 << QPointF(0, -320)
2138 << QPointF(0, -360)
2139 << 6 << 8 << 10 << 4;
2140
2141 QTest::newRow(dataTag: "TopToBottom, LtR, TtB")
2142 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
2143 << Qt::Key_Down << Qt::Key_Up
2144 << QPointF(0, 0)
2145 << QPointF(80, 0)
2146 << 12 << 2 << 6 << 8;
2147
2148 QTest::newRow(dataTag: "TopToBottom, RtL, TtB")
2149 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
2150 << Qt::Key_Down << Qt::Key_Up
2151 << QPointF(-240, 0)
2152 << QPointF(-320, 0)
2153 << 2 << 12 << 6 << 8;
2154
2155 QTest::newRow(dataTag: "TopToBottom, LtR, BtT")
2156 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
2157 << Qt::Key_Up << Qt::Key_Down
2158 << QPointF(0, -320)
2159 << QPointF(80, -320)
2160 << 12 << 2 << 8 << 6;
2161
2162 QTest::newRow(dataTag: "TopToBottom, RtL, BtT")
2163 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
2164 << Qt::Key_Up << Qt::Key_Down
2165 << QPointF(-240, -320)
2166 << QPointF(-320, -320)
2167 << 2 << 12 << 8 << 6;
2168}
2169
2170void tst_QQuickGridView::changeFlow()
2171{
2172 QQuickView *window = createView();
2173
2174 QaimModel model;
2175 for (int i = 0; i < 30; i++)
2176 model.addItem(name: "Item" + QString::number(i), number: QString::number(i));
2177
2178 QQmlContext *ctxt = window->rootContext();
2179 ctxt->setContextProperty("testModel", &model);
2180 ctxt->setContextProperty("testRightToLeft", QVariant(false));
2181 ctxt->setContextProperty("testTopToBottom", QVariant(false));
2182 ctxt->setContextProperty("testBottomToTop", QVariant(false));
2183
2184 window->setSource(testFileUrl(fileName: "layouts.qml"));
2185 qApp->processEvents();
2186
2187 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2188 QTRY_VERIFY(gridview != nullptr);
2189
2190 QQuickItem *contentItem = gridview->contentItem();
2191 QTRY_VERIFY(contentItem != nullptr);
2192
2193 // Confirm items positioned correctly and indexes correct
2194 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
2195 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2196 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
2197 if (!item) qWarning() << "Item" << i << "not found";
2198 QTRY_VERIFY(item);
2199 QTRY_COMPARE(item->x(), qreal((i%3)*80));
2200 QTRY_COMPARE(item->y(), qreal((i/3)*60));
2201 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
2202 QTRY_VERIFY(name != nullptr);
2203 QTRY_COMPARE(name->text(), model.name(i));
2204 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: i);
2205 QTRY_VERIFY(number != nullptr);
2206 QTRY_COMPARE(number->text(), model.number(i));
2207 }
2208
2209 ctxt->setContextProperty("testTopToBottom", QVariant(true));
2210
2211 // Confirm items positioned correctly and indexes correct
2212 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
2213 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2214 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
2215 if (!item) qWarning() << "Item" << i << "not found";
2216 QTRY_VERIFY(item);
2217 QTRY_COMPARE(item->x(), qreal((i/5)*80));
2218 QTRY_COMPARE(item->y(), qreal((i%5)*60));
2219 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
2220 QTRY_VERIFY(name != nullptr);
2221 QTRY_COMPARE(name->text(), model.name(i));
2222 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: i);
2223 QTRY_VERIFY(number != nullptr);
2224 QTRY_COMPARE(number->text(), model.number(i));
2225 }
2226
2227 ctxt->setContextProperty("testRightToLeft", QVariant(true));
2228
2229 // Confirm items positioned correctly and indexes correct
2230 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
2231 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2232 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
2233 if (!item) qWarning() << "Item" << i << "not found";
2234 QTRY_VERIFY(item);
2235 QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width()));
2236 QTRY_COMPARE(item->y(), qreal((i%5)*60));
2237 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
2238 QTRY_VERIFY(name != nullptr);
2239 QTRY_COMPARE(name->text(), model.name(i));
2240 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: i);
2241 QTRY_VERIFY(number != nullptr);
2242 QTRY_COMPARE(number->text(), model.number(i));
2243 }
2244 gridview->setContentX(100);
2245 QTRY_COMPARE(gridview->contentX(), 100.);
2246 ctxt->setContextProperty("testTopToBottom", QVariant(false));
2247 QTRY_COMPARE(gridview->contentX(), 0.);
2248
2249 // Confirm items positioned correctly and indexes correct
2250 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
2251 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2252 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
2253 if (!item) qWarning() << "Item" << i << "not found";
2254 QTRY_VERIFY(item);
2255 QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80));
2256 QTRY_COMPARE(item->y(), qreal((i/3)*60));
2257 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
2258 QTRY_VERIFY(name != nullptr);
2259 QTRY_COMPARE(name->text(), model.name(i));
2260 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: i);
2261 QTRY_VERIFY(number != nullptr);
2262 QTRY_COMPARE(number->text(), model.number(i));
2263 }
2264
2265 delete window;
2266}
2267
2268void tst_QQuickGridView::defaultValues()
2269{
2270 QQmlEngine engine;
2271 QQmlComponent c(&engine, testFileUrl(fileName: "gridview3.qml"));
2272 QQuickGridView *obj = qobject_cast<QQuickGridView*>(object: c.create());
2273
2274 QTRY_VERIFY(obj != nullptr);
2275 QTRY_COMPARE(obj->model(), QVariant());
2276 QTRY_VERIFY(!obj->delegate());
2277 QTRY_COMPARE(obj->currentIndex(), -1);
2278 QTRY_VERIFY(!obj->currentItem());
2279 QTRY_COMPARE(obj->count(), 0);
2280 QTRY_VERIFY(!obj->highlight());
2281 QTRY_VERIFY(!obj->highlightItem());
2282 QTRY_COMPARE(obj->highlightFollowsCurrentItem(), true);
2283 QTRY_COMPARE(obj->flow(), QQuickGridView::FlowLeftToRight);
2284 QTRY_COMPARE(obj->isWrapEnabled(), false);
2285#ifdef QML_VIEW_DEFAULTCACHEBUFFER
2286 QTRY_COMPARE(obj->cacheBuffer(), QML_VIEW_DEFAULTCACHEBUFFER);
2287#else
2288 QTRY_COMPARE(obj->cacheBuffer(), 320);
2289#endif
2290 QTRY_COMPARE(obj->cellWidth(), qreal(100)); //### Should 100 be the default?
2291 QTRY_COMPARE(obj->cellHeight(), qreal(100));
2292 delete obj;
2293}
2294
2295void tst_QQuickGridView::properties()
2296{
2297 QQmlEngine engine;
2298 QQmlComponent c(&engine, testFileUrl(fileName: "gridview2.qml"));
2299 QQuickGridView *obj = qobject_cast<QQuickGridView*>(object: c.create());
2300
2301 QTRY_VERIFY(obj != nullptr);
2302 QTRY_VERIFY(obj->model() != QVariant());
2303 QTRY_VERIFY(obj->delegate() != nullptr);
2304 QTRY_COMPARE(obj->currentIndex(), 0);
2305 QTRY_VERIFY(obj->currentItem() != nullptr);
2306 QTRY_COMPARE(obj->count(), 4);
2307 QTRY_VERIFY(obj->highlight() != nullptr);
2308 QTRY_VERIFY(obj->highlightItem() != nullptr);
2309 QTRY_COMPARE(obj->highlightFollowsCurrentItem(), false);
2310 QTRY_COMPARE(obj->flow(), QQuickGridView::FlowLeftToRight);
2311 QTRY_COMPARE(obj->isWrapEnabled(), true);
2312 QTRY_COMPARE(obj->cacheBuffer(), 200);
2313 QTRY_COMPARE(obj->cellWidth(), qreal(100));
2314 QTRY_COMPARE(obj->cellHeight(), qreal(100));
2315 delete obj;
2316}
2317
2318void tst_QQuickGridView::propertyChanges()
2319{
2320 QQuickView *window = createView();
2321 QTRY_VERIFY(window);
2322 window->setSource(testFileUrl(fileName: "propertychangestest.qml"));
2323
2324 QQuickGridView *gridView = window->rootObject()->findChild<QQuickGridView*>(aName: "gridView");
2325 QTRY_VERIFY(gridView);
2326
2327 QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged()));
2328 QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged()));
2329 QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged()));
2330 QSignalSpy flowSpy(gridView, SIGNAL(flowChanged()));
2331
2332 QTRY_COMPARE(gridView->isWrapEnabled(), true);
2333 QTRY_COMPARE(gridView->cacheBuffer(), 10);
2334 QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowLeftToRight);
2335
2336 gridView->setWrapEnabled(false);
2337 gridView->setCacheBuffer(3);
2338 gridView->setFlow(QQuickGridView::FlowTopToBottom);
2339
2340 QTRY_COMPARE(gridView->isWrapEnabled(), false);
2341 QTRY_COMPARE(gridView->cacheBuffer(), 3);
2342 QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowTopToBottom);
2343
2344 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2345 QTRY_COMPARE(cacheBufferSpy.count(),1);
2346 QTRY_COMPARE(flowSpy.count(),1);
2347
2348 gridView->setWrapEnabled(false);
2349 gridView->setCacheBuffer(3);
2350 gridView->setFlow(QQuickGridView::FlowTopToBottom);
2351
2352 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2353 QTRY_COMPARE(cacheBufferSpy.count(),1);
2354 QTRY_COMPARE(flowSpy.count(),1);
2355
2356 gridView->setFlow(QQuickGridView::FlowLeftToRight);
2357 QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowLeftToRight);
2358
2359 gridView->setWrapEnabled(true);
2360 gridView->setCacheBuffer(5);
2361 gridView->setLayoutDirection(Qt::RightToLeft);
2362
2363 QTRY_COMPARE(gridView->isWrapEnabled(), true);
2364 QTRY_COMPARE(gridView->cacheBuffer(), 5);
2365 QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft);
2366
2367 QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
2368 QTRY_COMPARE(cacheBufferSpy.count(),2);
2369 QTRY_COMPARE(layoutSpy.count(),1);
2370 QTRY_COMPARE(flowSpy.count(),2);
2371
2372 gridView->setWrapEnabled(true);
2373 gridView->setCacheBuffer(5);
2374 gridView->setLayoutDirection(Qt::RightToLeft);
2375
2376 QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
2377 QTRY_COMPARE(cacheBufferSpy.count(),2);
2378 QTRY_COMPARE(layoutSpy.count(),1);
2379 QTRY_COMPARE(flowSpy.count(),2);
2380
2381 gridView->setFlow(QQuickGridView::FlowTopToBottom);
2382 QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowTopToBottom);
2383 QTRY_COMPARE(flowSpy.count(),3);
2384
2385 gridView->setFlow(QQuickGridView::FlowTopToBottom);
2386 QTRY_COMPARE(flowSpy.count(),3);
2387
2388 delete window;
2389}
2390
2391void tst_QQuickGridView::componentChanges()
2392{
2393 QQuickView *window = createView();
2394 QTRY_VERIFY(window);
2395 window->setSource(testFileUrl(fileName: "propertychangestest.qml"));
2396
2397 QQuickGridView *gridView = window->rootObject()->findChild<QQuickGridView*>(aName: "gridView");
2398 QTRY_VERIFY(gridView);
2399
2400 QQmlComponent component(window->engine());
2401 component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", baseUrl: QUrl::fromLocalFile(localfile: ""));
2402
2403 QQmlComponent delegateComponent(window->engine());
2404 delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", baseUrl: QUrl::fromLocalFile(localfile: ""));
2405
2406 QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
2407 QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
2408 QSignalSpy headerSpy(gridView, SIGNAL(headerChanged()));
2409 QSignalSpy footerSpy(gridView, SIGNAL(footerChanged()));
2410 QSignalSpy headerItemSpy(gridView, SIGNAL(headerItemChanged()));
2411 QSignalSpy footerItemSpy(gridView, SIGNAL(footerItemChanged()));
2412
2413 gridView->setHighlight(&component);
2414 gridView->setDelegate(&delegateComponent);
2415 gridView->setHeader(&component);
2416 gridView->setFooter(&component);
2417
2418 QTRY_COMPARE(gridView->highlight(), &component);
2419 QTRY_COMPARE(gridView->delegate(), &delegateComponent);
2420 QTRY_COMPARE(gridView->header(), &component);
2421 QTRY_COMPARE(gridView->footer(), &component);
2422
2423 QVERIFY(gridView->headerItem());
2424 QVERIFY(gridView->footerItem());
2425
2426 QTRY_COMPARE(highlightSpy.count(),1);
2427 QTRY_COMPARE(delegateSpy.count(),1);
2428 QTRY_COMPARE(headerSpy.count(),1);
2429 QTRY_COMPARE(footerSpy.count(),1);
2430 QTRY_COMPARE(headerItemSpy.count(),1);
2431 QTRY_COMPARE(footerItemSpy.count(),1);
2432
2433 gridView->setHighlight(&component);
2434 gridView->setDelegate(&delegateComponent);
2435 gridView->setHeader(&component);
2436 gridView->setFooter(&component);
2437
2438 QTRY_COMPARE(highlightSpy.count(),1);
2439 QTRY_COMPARE(delegateSpy.count(),1);
2440 QTRY_COMPARE(headerSpy.count(),1);
2441 QTRY_COMPARE(footerSpy.count(),1);
2442 QTRY_COMPARE(headerItemSpy.count(),1);
2443 QTRY_COMPARE(footerItemSpy.count(),1);
2444
2445 delete window;
2446}
2447
2448void tst_QQuickGridView::modelChanges()
2449{
2450 QQuickView *window = createView();
2451 QTRY_VERIFY(window);
2452 window->setSource(testFileUrl(fileName: "propertychangestest.qml"));
2453
2454 QQuickGridView *gridView = window->rootObject()->findChild<QQuickGridView*>(aName: "gridView");
2455 QTRY_VERIFY(gridView);
2456
2457 QQmlListModel *alternateModel = window->rootObject()->findChild<QQmlListModel*>(aName: "alternateModel");
2458 QTRY_VERIFY(alternateModel);
2459 QVariant modelVariant = QVariant::fromValue<QObject *>(value: alternateModel);
2460 QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
2461
2462 gridView->setModel(modelVariant);
2463 QTRY_COMPARE(gridView->model(), modelVariant);
2464 QTRY_COMPARE(modelSpy.count(),1);
2465
2466 gridView->setModel(modelVariant);
2467 QTRY_COMPARE(modelSpy.count(),1);
2468
2469 gridView->setModel(QVariant());
2470 QTRY_COMPARE(modelSpy.count(),2);
2471 delete window;
2472}
2473
2474void tst_QQuickGridView::positionViewAtBeginningEnd()
2475{
2476 QQuickView *window = createView();
2477
2478 QaimModel model;
2479 for (int i = 0; i < 40; i++)
2480 model.addItem(name: "Item" + QString::number(i), number: "");
2481
2482 QQmlContext *ctxt = window->rootContext();
2483 ctxt->setContextProperty("testModel", &model);
2484 ctxt->setContextProperty("testRightToLeft", QVariant(false));
2485 ctxt->setContextProperty("testTopToBottom", QVariant(false));
2486 ctxt->setContextProperty("testBottomToTop", QVariant(false));
2487
2488 window->setSource(testFileUrl(fileName: "layouts.qml"));
2489 window->show();
2490 qApp->processEvents();
2491
2492 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2493 QTRY_VERIFY(gridview != nullptr);
2494 QQuickItem *contentItem = gridview->contentItem();
2495 QTRY_VERIFY(contentItem != nullptr);
2496 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
2497
2498 // positionViewAtBeginning
2499 gridview->setContentY(150);
2500 gridview->positionViewAtBeginning();
2501 QTRY_COMPARE(gridview->contentY(), 0.);
2502
2503 gridview->setContentY(80);
2504 window->rootObject()->setProperty(name: "showHeader", value: true);
2505 gridview->positionViewAtBeginning();
2506 QTRY_COMPARE(gridview->contentY(), -30.);
2507
2508 // positionViewAtEnd
2509 gridview->setContentY(150);
2510 gridview->positionViewAtEnd();
2511 QTRY_COMPARE(gridview->contentY(), 520.); // 14*60 - 320 (14 rows)
2512
2513 gridview->setContentY(80);
2514 window->rootObject()->setProperty(name: "showFooter", value: true);
2515 gridview->positionViewAtEnd();
2516 QTRY_COMPARE(gridview->contentY(), 550.);
2517
2518 // Test for Top To Bottom layout
2519 ctxt->setContextProperty("testTopToBottom", QVariant(true));
2520 window->rootObject()->setProperty(name: "showHeader", value: false);
2521 window->rootObject()->setProperty(name: "showFooter", value: false);
2522
2523 // positionViewAtBeginning
2524 gridview->setContentX(150);
2525 gridview->positionViewAtBeginning();
2526 QTRY_COMPARE(gridview->contentX(), 0.);
2527
2528 gridview->setContentX(80);
2529 window->rootObject()->setProperty(name: "showHeader", value: true);
2530 gridview->positionViewAtBeginning();
2531 QTRY_COMPARE(gridview->contentX(), -30.);
2532
2533 // positionViewAtEnd
2534 gridview->positionViewAtEnd();
2535 QTRY_COMPARE(gridview->contentX(), 400.); // 8*80 - 240 (8 columns)
2536
2537 gridview->setContentX(80);
2538 window->rootObject()->setProperty(name: "showFooter", value: true);
2539 gridview->positionViewAtEnd();
2540 QTRY_COMPARE(gridview->contentX(), 430.);
2541
2542 // set current item to outside visible view, position at beginning
2543 // and ensure highlight moves to current item
2544 gridview->setCurrentIndex(6);
2545 gridview->positionViewAtBeginning();
2546 QTRY_COMPARE(gridview->contentX(), -30.);
2547 QVERIFY(gridview->highlightItem());
2548 QCOMPARE(gridview->highlightItem()->x(), 80.);
2549
2550 delete window;
2551}
2552
2553void tst_QQuickGridView::positionViewAtIndex()
2554{
2555 QFETCH(bool, enforceRange);
2556 QFETCH(bool, topToBottom);
2557 QFETCH(bool, rightToLeft);
2558 QFETCH(qreal, initContentPos);
2559 QFETCH(int, index);
2560 QFETCH(QQuickGridView::PositionMode, mode);
2561 QFETCH(qreal, contentPos);
2562
2563 QQuickView *window = getView();
2564
2565 QaimModel model;
2566 for (int i = 0; i < 40; i++)
2567 model.addItem(name: "Item" + QString::number(i), number: "");
2568
2569 QQmlContext *ctxt = window->rootContext();
2570 ctxt->setContextProperty("testModel", &model);
2571 ctxt->setContextProperty("testRightToLeft", QVariant(rightToLeft));
2572 ctxt->setContextProperty("testTopToBottom", QVariant(topToBottom));
2573 ctxt->setContextProperty("testBottomToTop", QVariant(false));
2574
2575 window->setSource(testFileUrl(fileName: "layouts.qml"));
2576 window->show();
2577 qApp->processEvents();
2578
2579 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2580 QTRY_VERIFY(gridview != nullptr);
2581 QQuickItem *contentItem = gridview->contentItem();
2582 QTRY_VERIFY(contentItem != nullptr);
2583 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
2584
2585 window->rootObject()->setProperty(name: "enforceRange", value: enforceRange);
2586 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
2587
2588 if (topToBottom)
2589 gridview->setContentX(initContentPos);
2590 else
2591 gridview->setContentY(initContentPos);
2592
2593 gridview->positionViewAtIndex(index, mode);
2594 if (topToBottom)
2595 QTRY_COMPARE(gridview->contentX(), contentPos);
2596 else
2597 QTRY_COMPARE(gridview->contentY(), contentPos);
2598
2599 // Confirm items positioned correctly
2600 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
2601 for (int i = index; i < model.count() && i < itemCount-index-1; ++i) {
2602 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
2603 if (!item) qWarning() << "Item" << i << "not found";
2604 QTRY_VERIFY(item);
2605 if (topToBottom) {
2606 if (rightToLeft) {
2607 QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2608 QTRY_COMPARE(item->y(), qreal((i%5)*60));
2609 } else {
2610 QTRY_COMPARE(item->x(), (i/5)*80.);
2611 QTRY_COMPARE(item->y(), (i%5)*60.);
2612 }
2613 } else {
2614 QTRY_COMPARE(item->x(), (i%3)*80.);
2615 QTRY_COMPARE(item->y(), (i/3)*60.);
2616 }
2617 }
2618
2619 releaseView(view: window);
2620}
2621
2622void tst_QQuickGridView::positionViewAtIndex_data()
2623{
2624 QTest::addColumn<bool>(name: "enforceRange");
2625 QTest::addColumn<bool>(name: "topToBottom");
2626 QTest::addColumn<bool>(name: "rightToLeft");
2627 QTest::addColumn<qreal>(name: "initContentPos");
2628 QTest::addColumn<int>(name: "index");
2629 QTest::addColumn<QQuickGridView::PositionMode>(name: "mode");
2630 QTest::addColumn<qreal>(name: "contentPos");
2631
2632 QTest::newRow(dataTag: "no range, 4 at Beginning") << false << false << false << 0. << 4 << QQuickGridView::Beginning << 60.;
2633 QTest::newRow(dataTag: "no range, 4 at End") << false << false << false << 0. << 4 << QQuickGridView::End << 0.;
2634 QTest::newRow(dataTag: "no range, 21 at Beginning") << false << false << false << 0. << 21 << QQuickGridView::Beginning << 420.;
2635 // Position on an item that would leave empty space if positioned at the top
2636 QTest::newRow(dataTag: "no range, 31 at Beginning") << false << false << false << 0. << 31 << QQuickGridView::Beginning << 520.;
2637 QTest::newRow(dataTag: "no range, 30 at End") << false << false << false << 0. << 30 << QQuickGridView::End << 340.;
2638 QTest::newRow(dataTag: "no range, 15 at Center") << false << false << false << 0. << 15 << QQuickGridView::Center << 170.;
2639 // Ensure at least partially visible
2640 QTest::newRow(dataTag: "no range, 15 visible => Visible") << false << false << false << 302. << 15 << QQuickGridView::Visible << 302.;
2641 QTest::newRow(dataTag: "no range, 15 after visible => Visible") << false << false << false << 360. << 15 << QQuickGridView::Visible << 300.;
2642 QTest::newRow(dataTag: "no range, 20 visible => Visible") << false << false << false << 60. << 20 << QQuickGridView::Visible << 60.;
2643 QTest::newRow(dataTag: "no range, 20 before visible => Visible") << false << false << false << 20. << 20 << QQuickGridView::Visible << 100.;
2644 // Ensure completely visible
2645 QTest::newRow(dataTag: "no range, 20 visible => Contain") << false << false << false << 120. << 20 << QQuickGridView::Contain << 120.;
2646 QTest::newRow(dataTag: "no range, 15 partially visible => Contain") << false << false << false << 302. << 15 << QQuickGridView::Contain << 300.;
2647 QTest::newRow(dataTag: "no range, 20 partially visible => Contain") << false << false << false << 60. << 20 << QQuickGridView::Contain << 100.;
2648
2649 QTest::newRow(dataTag: "strict range, 4 at End") << true << false << false << 0. << 4 << QQuickGridView::End << -120.;
2650 QTest::newRow(dataTag: "strict range, 38 at Beginning") << true << false << false << 0. << 38 << QQuickGridView::Beginning << 660.;
2651 QTest::newRow(dataTag: "strict range, 15 at Center") << true << false << false << 0. << 15 << QQuickGridView::Center << 180.;
2652 QTest::newRow(dataTag: "strict range, 4 at SnapPosition") << true << false << false << 0. << 4 << QQuickGridView::SnapPosition << -60.;
2653 QTest::newRow(dataTag: "strict range, 10 at SnapPosition") << true << false << false << 0. << 10 << QQuickGridView::SnapPosition << 60.;
2654 QTest::newRow(dataTag: "strict range, 38 at SnapPosition") << true << false << false << 0. << 38 << QQuickGridView::SnapPosition << 600.;
2655
2656 // TopToBottom
2657 QTest::newRow(dataTag: "no range, ttb, 30 at End") << false << true << false << 0. << 30 << QQuickGridView::End << 320.;
2658 QTest::newRow(dataTag: "no range, ttb, 15 at Center") << false << true << false << 0. << 15 << QQuickGridView::Center << 160.;
2659 QTest::newRow(dataTag: "no range, ttb, 15 visible => Visible") << false << true << false << 160. << 15 << QQuickGridView::Visible << 160.;
2660 QTest::newRow(dataTag: "no range, ttb, 25 partially visible => Visible") << false << true << false << 170. << 25 << QQuickGridView::Visible << 170.;
2661 QTest::newRow(dataTag: "no range, ttb, 30 before visible => Visible") << false << true << false << 170. << 30 << QQuickGridView::Visible << 320.;
2662 QTest::newRow(dataTag: "no range, ttb, 25 partially visible => Contain") << false << true << false << 170. << 25 << QQuickGridView::Contain << 240.;
2663
2664 // RightToLeft
2665 QTest::newRow(dataTag: "no range, rtl, ttb, 6 at Beginning") << false << true << true << 0. << 6 << QQuickGridView::Beginning << -320.;
2666 QTest::newRow(dataTag: "no range, rtl, ttb, 21 at Beginning") << false << true << true << 0. << 21 << QQuickGridView::Beginning << -560.;
2667 // Position on an item that would leave empty space if positioned at the top
2668 QTest::newRow(dataTag: "no range, rtl, ttb, 31 at Beginning") << false << true << true << 0. << 31 << QQuickGridView::Beginning << -640.;
2669 QTest::newRow(dataTag: "no range, rtl, ttb, 0 at Beginning") << false << true << true << -400. << 0 << QQuickGridView::Beginning << -240.;
2670 QTest::newRow(dataTag: "no range, rtl, ttb, 30 at End") << false << true << true << 0. << 30 << QQuickGridView::End << -560.;
2671 QTest::newRow(dataTag: "no range, rtl, ttb, 15 at Center") << false << true << true << 0. << 15 << QQuickGridView::Center << -400.;
2672 QTest::newRow(dataTag: "no range, rtl, ttb, 15 visible => Visible") << false << true << true << -555. << 15 << QQuickGridView::Visible << -555.;
2673 QTest::newRow(dataTag: "no range, rtl, ttb, 15 not visible => Visible") << false << true << true << -239. << 15 << QQuickGridView::Visible << -320.;
2674 QTest::newRow(dataTag: "no range, rtl, ttb, 15 partially visible => Visible") << false << true << true << -300. << 15 << QQuickGridView::Visible << -300.;
2675 QTest::newRow(dataTag: "no range, rtl, ttb, 20 visible => Contain") << false << true << true << -400. << 20 << QQuickGridView::Contain << -400.;
2676 QTest::newRow(dataTag: "no range, rtl, ttb, 15 partially visible => Contain") << false << true << true << -315. << 15 << QQuickGridView::Contain << -320.;
2677}
2678
2679void tst_QQuickGridView::snapping()
2680{
2681 QQuickView *window = createView();
2682
2683 QaimModel model;
2684 for (int i = 0; i < 40; i++)
2685 model.addItem(name: "Item" + QString::number(i), number: "");
2686
2687 window->rootContext()->setContextProperty("testModel", &model);
2688 window->setSource(testFileUrl(fileName: "gridview1.qml"));
2689 qApp->processEvents();
2690
2691 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2692 QTRY_VERIFY(gridview != nullptr);
2693
2694 gridview->setHeight(220);
2695 QCOMPARE(gridview->height(), 220.);
2696
2697 gridview->positionViewAtIndex(index: 12, mode: QQuickGridView::Visible);
2698 QCOMPARE(gridview->contentY(), 80.);
2699
2700 gridview->setContentY(0);
2701 QCOMPARE(gridview->contentY(), 0.);
2702
2703 gridview->setSnapMode(QQuickGridView::SnapToRow);
2704 QCOMPARE(gridview->snapMode(), QQuickGridView::SnapToRow);
2705
2706 gridview->positionViewAtIndex(index: 12, mode: QQuickGridView::Visible);
2707 QCOMPARE(gridview->contentY(), 60.);
2708
2709 gridview->positionViewAtIndex(index: 15, mode: QQuickGridView::End);
2710 QCOMPARE(gridview->contentY(), 120.);
2711
2712 delete window;
2713
2714}
2715
2716void tst_QQuickGridView::mirroring()
2717{
2718 QQuickView *windowA = createView();
2719 windowA->setSource(testFileUrl(fileName: "mirroring.qml"));
2720 QQuickGridView *gridviewA = findItem<QQuickGridView>(parent: windowA->rootObject(), objectName: "view");
2721 QTRY_VERIFY(gridviewA != nullptr);
2722
2723 QQuickView *windowB = createView();
2724 windowB->setSource(testFileUrl(fileName: "mirroring.qml"));
2725 QQuickGridView *gridviewB = findItem<QQuickGridView>(parent: windowB->rootObject(), objectName: "view");
2726 QTRY_VERIFY(gridviewA != nullptr);
2727 qApp->processEvents();
2728
2729 QList<QString> objectNames;
2730 objectNames << "item1" << "item2"; // << "item3"
2731
2732 gridviewA->setProperty(name: "layoutDirection", value: Qt::LeftToRight);
2733 gridviewB->setProperty(name: "layoutDirection", value: Qt::RightToLeft);
2734 QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
2735
2736 // LTR != RTL
2737 foreach (const QString objectName, objectNames)
2738 QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2739
2740 gridviewA->setProperty(name: "layoutDirection", value: Qt::LeftToRight);
2741 gridviewB->setProperty(name: "layoutDirection", value: Qt::LeftToRight);
2742
2743 // LTR == LTR
2744 foreach (const QString objectName, objectNames)
2745 QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2746
2747 QCOMPARE(gridviewB->layoutDirection(), gridviewB->effectiveLayoutDirection());
2748 QQuickItemPrivate::get(item: gridviewB)->setLayoutMirror(true);
2749 QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
2750
2751 // LTR != LTR+mirror
2752 foreach (const QString objectName, objectNames)
2753 QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2754
2755 gridviewA->setProperty(name: "layoutDirection", value: Qt::RightToLeft);
2756
2757 // RTL == LTR+mirror
2758 foreach (const QString objectName, objectNames)
2759 QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2760
2761 gridviewB->setProperty(name: "layoutDirection", value: Qt::RightToLeft);
2762
2763 // RTL != RTL+mirror
2764 foreach (const QString objectName, objectNames)
2765 QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2766
2767 gridviewA->setProperty(name: "layoutDirection", value: Qt::LeftToRight);
2768
2769 // LTR == RTL+mirror
2770 foreach (const QString objectName, objectNames)
2771 QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2772
2773 delete windowA;
2774 delete windowB;
2775}
2776
2777void tst_QQuickGridView::resetModel()
2778{
2779 QQuickView *window = createView();
2780
2781 QStringList strings;
2782 strings << "one" << "two" << "three";
2783 QStringListModel model(strings);
2784
2785 QQmlContext *ctxt = window->rootContext();
2786 ctxt->setContextProperty("testModel", &model);
2787
2788 window->setSource(testFileUrl(fileName: "displaygrid.qml"));
2789 window->show();
2790 qApp->processEvents();
2791
2792 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2793 QTRY_VERIFY(gridview != nullptr);
2794 QQuickItem *contentItem = gridview->contentItem();
2795 QTRY_VERIFY(contentItem != nullptr);
2796 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
2797
2798 QTRY_COMPARE(gridview->count(), model.rowCount());
2799
2800 for (int i = 0; i < model.rowCount(); ++i) {
2801 QQuickText *display = findItem<QQuickText>(parent: contentItem, objectName: "displayText", index: i);
2802 QTRY_VERIFY(display != nullptr);
2803 QTRY_COMPARE(display->text(), strings.at(i));
2804 }
2805
2806 strings.clear();
2807 strings << "four" << "five" << "six" << "seven";
2808 model.setStringList(strings);
2809
2810 QTRY_COMPARE(gridview->count(), model.rowCount());
2811
2812 for (int i = 0; i < model.rowCount(); ++i) {
2813 QQuickText *display = findItem<QQuickText>(parent: contentItem, objectName: "displayText", index: i);
2814 QTRY_VERIFY(display != nullptr);
2815 QTRY_COMPARE(display->text(), strings.at(i));
2816 }
2817
2818 delete window;
2819}
2820
2821void tst_QQuickGridView::enforceRange()
2822{
2823 QQuickView *window = createView();
2824
2825 QaimModel model;
2826 for (int i = 0; i < 30; i++)
2827 model.addItem(name: "Item" + QString::number(i), number: "");
2828
2829 QQmlContext *ctxt = window->rootContext();
2830 ctxt->setContextProperty("testModel", &model);
2831 ctxt->setContextProperty("testRightToLeft", QVariant(false));
2832 ctxt->setContextProperty("testTopToBottom", QVariant(false));
2833
2834 window->setSource(testFileUrl(fileName: "gridview-enforcerange.qml"));
2835 window->show();
2836 qApp->processEvents();
2837 QVERIFY(window->rootObject() != nullptr);
2838
2839 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2840 QTRY_VERIFY(gridview != nullptr);
2841
2842 QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
2843 QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
2844 QTRY_COMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2845 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
2846
2847 QQuickItem *contentItem = gridview->contentItem();
2848 QTRY_VERIFY(contentItem != nullptr);
2849
2850 // view should be positioned at the top of the range.
2851 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
2852 QTRY_VERIFY(item);
2853 QTRY_COMPARE(gridview->contentY(), -100.0);
2854
2855 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 0);
2856 QTRY_VERIFY(name != nullptr);
2857 QTRY_COMPARE(name->text(), model.name(0));
2858 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 0);
2859 QTRY_VERIFY(number != nullptr);
2860 QTRY_COMPARE(number->text(), model.number(0));
2861
2862 // Check currentIndex is updated when contentItem moves
2863 gridview->setContentY(0);
2864 QTRY_COMPARE(gridview->currentIndex(), 2);
2865
2866 gridview->setCurrentIndex(5);
2867 QTRY_COMPARE(gridview->contentY(), 100.);
2868
2869 QaimModel model2;
2870 for (int i = 0; i < 5; i++)
2871 model2.addItem(name: "Item" + QString::number(i), number: "");
2872
2873 ctxt->setContextProperty("testModel", &model2);
2874 QCOMPARE(gridview->count(), 5);
2875
2876 delete window;
2877}
2878
2879void tst_QQuickGridView::enforceRange_rightToLeft()
2880{
2881 QQuickView *window = createView();
2882
2883 QaimModel model;
2884 for (int i = 0; i < 30; i++)
2885 model.addItem(name: "Item" + QString::number(i), number: "");
2886
2887 QQmlContext *ctxt = window->rootContext();
2888 ctxt->setContextProperty("testModel", &model);
2889 ctxt->setContextProperty("testRightToLeft", QVariant(true));
2890 ctxt->setContextProperty("testTopToBottom", QVariant(true));
2891
2892 window->setSource(testFileUrl(fileName: "gridview-enforcerange.qml"));
2893 window->show();
2894 QTRY_VERIFY(window->isExposed());
2895 QVERIFY(window->rootObject() != nullptr);
2896
2897 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2898 QTRY_VERIFY(gridview != nullptr);
2899
2900 QCOMPARE(gridview->preferredHighlightBegin(), 100.0);
2901 QCOMPARE(gridview->preferredHighlightEnd(), 100.0);
2902 QCOMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2903
2904 QQuickItem *contentItem = gridview->contentItem();
2905 QVERIFY(contentItem != nullptr);
2906
2907 // view should be positioned at the top of the range.
2908 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
2909 QVERIFY(item);
2910 QTRY_COMPARE(gridview->contentX(), -140.);
2911 QTRY_COMPARE(gridview->contentY(), 0.0);
2912
2913 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: 0);
2914 QTRY_VERIFY(name != nullptr);
2915 QTRY_COMPARE(name->text(), model.name(0));
2916 QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber", index: 0);
2917 QTRY_VERIFY(number != nullptr);
2918 QTRY_COMPARE(number->text(), model.number(0));
2919
2920 // Check currentIndex is updated when contentItem moves
2921 gridview->setContentX(-240);
2922 QTRY_COMPARE(gridview->currentIndex(), 3);
2923
2924 gridview->setCurrentIndex(7);
2925 QTRY_COMPARE(gridview->contentX(), -340.);
2926 QTRY_COMPARE(gridview->contentY(), 0.0);
2927
2928 QaimModel model2;
2929 for (int i = 0; i < 5; i++)
2930 model2.addItem(name: "Item" + QString::number(i), number: "");
2931
2932 ctxt->setContextProperty("testModel", &model2);
2933 QCOMPARE(gridview->count(), 5);
2934
2935 delete window;
2936}
2937
2938void tst_QQuickGridView::QTBUG_8456()
2939{
2940 QQuickView *window = createView();
2941
2942 window->setSource(testFileUrl(fileName: "setindex.qml"));
2943 qApp->processEvents();
2944
2945 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2946 QTRY_VERIFY(gridview != nullptr);
2947
2948 QTRY_COMPARE(gridview->currentIndex(), 0);
2949
2950 delete window;
2951}
2952
2953void tst_QQuickGridView::manualHighlight()
2954{
2955 QQuickView *window = createView();
2956
2957 QString filename(testFile(fileName: "manual-highlight.qml"));
2958 window->setSource(QUrl::fromLocalFile(localfile: filename));
2959
2960 qApp->processEvents();
2961
2962 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
2963 QTRY_VERIFY(gridview != nullptr);
2964
2965 QQuickItem *contentItem = gridview->contentItem();
2966 QTRY_VERIFY(contentItem != nullptr);
2967
2968 QTRY_COMPARE(gridview->currentIndex(), 0);
2969 QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2970 QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2971 QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2972
2973 gridview->setCurrentIndex(2);
2974
2975 QTRY_COMPARE(gridview->currentIndex(), 2);
2976 QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2977 QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2978 QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2979
2980 gridview->positionViewAtIndex(index: 8, mode: QQuickGridView::Contain);
2981
2982 QTRY_COMPARE(gridview->currentIndex(), 2);
2983 QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2984 QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2985 QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2986
2987 gridview->setFlow(QQuickGridView::FlowTopToBottom);
2988 QTRY_COMPARE(gridview->flow(), QQuickGridView::FlowTopToBottom);
2989
2990 gridview->setCurrentIndex(0);
2991 QTRY_COMPARE(gridview->currentIndex(), 0);
2992 QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2993 QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2994 QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2995
2996 delete window;
2997}
2998
2999
3000void tst_QQuickGridView::footer()
3001{
3002 QFETCH(QQuickGridView::Flow, flow);
3003 QFETCH(Qt::LayoutDirection, layoutDirection);
3004 QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3005 QFETCH(QPointF, initialFooterPos);
3006 QFETCH(QPointF, changedFooterPos);
3007 QFETCH(QPointF, initialContentPos);
3008 QFETCH(QPointF, firstDelegatePos);
3009 QFETCH(QPointF, resizeContentPos);
3010
3011 QQuickView *window = getView();
3012 window->show();
3013
3014 QaimModel model;
3015 for (int i = 0; i < 7; i++)
3016 model.addItem(name: "Item" + QString::number(i), number: "");
3017
3018 QQmlContext *ctxt = window->rootContext();
3019 ctxt->setContextProperty("testModel", &model);
3020 window->setSource(testFileUrl(fileName: "footer.qml"));
3021 qApp->processEvents();
3022
3023 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3024 QTRY_VERIFY(gridview != nullptr);
3025 gridview->setFlow(flow);
3026 gridview->setLayoutDirection(layoutDirection);
3027 gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3028 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3029
3030 QQuickItem *contentItem = gridview->contentItem();
3031 QTRY_VERIFY(contentItem != nullptr);
3032
3033 QQuickText *footer = findItem<QQuickText>(parent: contentItem, objectName: "footer");
3034 QVERIFY(footer);
3035 QCOMPARE(footer, gridview->footerItem());
3036
3037 QCOMPARE(footer->position(), initialFooterPos);
3038 QCOMPARE(footer->width(), 100.);
3039 QCOMPARE(footer->height(), 30.);
3040 QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3041
3042 if (flow == QQuickGridView::FlowLeftToRight)
3043 QCOMPARE(gridview->contentHeight(), (model.count()+2) / 3 * 60. + footer->height());
3044 else
3045 QCOMPARE(gridview->contentWidth(), (model.count()+3) / 5 * 80. + footer->width());
3046
3047 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
3048 QVERIFY(item);
3049 QCOMPARE(item->position(), firstDelegatePos);
3050
3051 if (flow == QQuickGridView::FlowLeftToRight) {
3052 // shrink by one row
3053 model.removeItem(index: 2);
3054 if (verticalLayoutDirection == QQuickItemView::TopToBottom)
3055 QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
3056 else
3057 QTRY_COMPARE(footer->y(), initialFooterPos.y() + gridview->cellHeight());
3058 } else {
3059 // shrink by one column
3060 model.removeItem(index: 2);
3061 model.removeItem(index: 3);
3062 if (layoutDirection == Qt::LeftToRight)
3063 QTRY_COMPARE(footer->x(), initialFooterPos.x() - gridview->cellWidth());
3064 else
3065 QTRY_COMPARE(footer->x(), initialFooterPos.x() + gridview->cellWidth());
3066 }
3067
3068 // remove all items
3069 model.clear();
3070 if (flow == QQuickGridView::FlowLeftToRight)
3071 QTRY_COMPARE(gridview->contentHeight(), footer->height());
3072 else
3073 QTRY_COMPARE(gridview->contentWidth(), footer->width());
3074
3075 QPointF posWhenNoItems(0, 0);
3076 if (layoutDirection == Qt::RightToLeft)
3077 posWhenNoItems.setX(flow == QQuickGridView::FlowLeftToRight ? gridview->width() - footer->width() : -footer->width());
3078 if (verticalLayoutDirection == QQuickItemView::BottomToTop)
3079 posWhenNoItems.setY(-footer->height());
3080 QTRY_COMPARE(footer->position(), posWhenNoItems);
3081
3082 // if header is toggled, it shouldn't affect the footer position
3083 window->rootObject()->setProperty(name: "showHeader", value: true);
3084 QVERIFY(findItem<QQuickItem>(contentItem, "header") != nullptr);
3085 QTRY_COMPARE(footer->position(), posWhenNoItems);
3086 window->rootObject()->setProperty(name: "showHeader", value: false);
3087
3088 // add 30 items
3089 for (int i = 0; i < 30; i++)
3090 model.addItem(name: "Item" + QString::number(i), number: "");
3091
3092 QSignalSpy footerItemSpy(gridview, SIGNAL(footerItemChanged()));
3093 QMetaObject::invokeMethod(obj: window->rootObject(), member: "changeFooter");
3094
3095 QCOMPARE(footerItemSpy.count(), 1);
3096
3097 footer = findItem<QQuickText>(parent: contentItem, objectName: "footer");
3098 QVERIFY(!footer);
3099 footer = findItem<QQuickText>(parent: contentItem, objectName: "footer2");
3100 QVERIFY(footer);
3101 QCOMPARE(footer, gridview->footerItem());
3102
3103 QCOMPARE(footer->position(), changedFooterPos);
3104 QCOMPARE(footer->width(), 50.);
3105 QCOMPARE(footer->height(), 20.);
3106
3107 // changing the footer shouldn't change the content pos
3108 QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3109
3110 item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
3111 QVERIFY(item);
3112 QCOMPARE(item->position(), firstDelegatePos);
3113
3114 gridview->positionViewAtEnd();
3115 footer->setHeight(10);
3116 footer->setWidth(40);
3117 QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
3118
3119 releaseView(view: window);
3120}
3121
3122void tst_QQuickGridView::footer_data()
3123{
3124 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
3125 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
3126 QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection");
3127 QTest::addColumn<QPointF>(name: "initialFooterPos");
3128 QTest::addColumn<QPointF>(name: "changedFooterPos");
3129 QTest::addColumn<QPointF>(name: "initialContentPos");
3130 QTest::addColumn<QPointF>(name: "firstDelegatePos");
3131 QTest::addColumn<QPointF>(name: "resizeContentPos");
3132
3133 // footer1 = 100 x 30
3134 // footer2 = 50 x 20
3135 // cells = 80 * 60
3136 // view width = 240
3137 // view height = 320
3138
3139 // footer below items, bottom left
3140 QTest::newRow(dataTag: "LeftToRight, LtR, TtB")
3141 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3142 << QPointF(0, 3 * 60) // 180 = height of 3 rows (cell height is 60)
3143 << QPointF(0, 10 * 60) // 30 items = 10 rows
3144 << QPointF(0, 0)
3145 << QPointF(0, 0)
3146 << QPointF(0, (10 * 60) - 320 + 10);
3147
3148 // footer below items, bottom right
3149 QTest::newRow(dataTag: "LeftToRight, RtL, TtB")
3150 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3151 << QPointF(240 - 100, 3 * 60)
3152 << QPointF((240 - 100) + 50, 10 * 60) // 50 = width diff between old and new footers
3153 << QPointF(0, 0)
3154 << QPointF(240 - 80, 0)
3155 << QPointF(0, (10 * 60) - 320 + 10);
3156
3157 // footer above items, top left
3158 QTest::newRow(dataTag: "LeftToRight, LtR, BtT")
3159 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3160 << QPointF(0, -(3 * 60) - 30)
3161 << QPointF(0, -(10 * 60) - 20)
3162 << QPointF(0, -320)
3163 << QPointF(0, -60)
3164 << QPointF(0, -(10 * 60) - 10);
3165
3166 // footer above items, top right
3167 QTest::newRow(dataTag: "LeftToRight, RtL, BtT")
3168 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3169 << QPointF(240 - 100, -(3 * 60) - 30)
3170 << QPointF((240 - 100) + 50, -(10 * 60) - 20)
3171 << QPointF(0, -320)
3172 << QPointF(240 - 80, -60)
3173 << QPointF(0, -(10 * 60) - 10);
3174
3175
3176 // footer to right of items, bottom right
3177 QTest::newRow(dataTag: "TopToBottom, LtR, TtB")
3178 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3179 << QPointF(2 * 80, 0) // 2 columns, cell width 80
3180 << QPointF(6 * 80, 0) // 30 items = 6 columns
3181 << QPointF(0, 0)
3182 << QPointF(0, 0)
3183 << QPointF((6 * 80) - 240 + 40, 0);
3184
3185 // footer to left of items, bottom right
3186 QTest::newRow(dataTag: "TopToBottom, RtL, TtB")
3187 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3188 << QPointF(-(2 * 80) - 100, 0)
3189 << QPointF(-(6 * 80) - 50, 0) // 50 = new footer width
3190 << QPointF(-240, 0)
3191 << QPointF(-80, 0)
3192 << QPointF(-(6 * 80) - 40, 0);
3193
3194 // footer to right of items, top right
3195 QTest::newRow(dataTag: "TopToBottom, LtR, BtT")
3196 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3197 << QPointF(2 * 80, -30)
3198 << QPointF(6 * 80, -20)
3199 << QPointF(0, -320)
3200 << QPointF(0, -60)
3201 << QPointF((6 * 80) - 240 + 40, -320);
3202
3203 // footer to left of items, top left
3204 QTest::newRow(dataTag: "TopToBottom, RtL, BtT")
3205 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3206 << QPointF(-(2 * 80) - 100, -30)
3207 << QPointF(-(6 * 80) - 50, -20)
3208 << QPointF(-240, -320)
3209 << QPointF(-80, -60)
3210 << QPointF(-(6 * 80) - 40, -320);
3211}
3212
3213void tst_QQuickGridView::initialZValues()
3214{
3215 QFETCH(QString, fileName);
3216 QQuickView *window = createView();
3217 window->setSource(testFileUrl(fileName));
3218 qApp->processEvents();
3219
3220 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3221 QTRY_VERIFY(gridview != nullptr);
3222 QQuickItem *contentItem = gridview->contentItem();
3223 QTRY_VERIFY(contentItem != nullptr);
3224
3225 QVERIFY(gridview->currentItem());
3226 QTRY_COMPARE(gridview->currentItem()->z(), gridview->property("itemZ").toReal());
3227
3228 QVERIFY(gridview->headerItem());
3229 QTRY_COMPARE(gridview->headerItem()->z(), gridview->property("headerZ").toReal());
3230
3231 QVERIFY(gridview->footerItem());
3232 QTRY_COMPARE(gridview->footerItem()->z(), gridview->property("footerZ").toReal());
3233
3234 QVERIFY(gridview->highlightItem());
3235 QTRY_COMPARE(gridview->highlightItem()->z(), gridview->property("highlightZ").toReal());
3236
3237 delete window;
3238}
3239
3240void tst_QQuickGridView::initialZValues_data()
3241{
3242 QTest::addColumn<QString>(name: "fileName");
3243 QTest::newRow(dataTag: "defaults") << "defaultZValues.qml";
3244 QTest::newRow(dataTag: "constants") << "constantZValues.qml";
3245 QTest::newRow(dataTag: "bindings") << "boundZValues.qml";
3246}
3247
3248void tst_QQuickGridView::header()
3249{
3250 QFETCH(QQuickGridView::Flow, flow);
3251 QFETCH(Qt::LayoutDirection, layoutDirection);
3252 QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3253 QFETCH(QPointF, initialHeaderPos);
3254 QFETCH(QPointF, changedHeaderPos);
3255 QFETCH(QPointF, initialContentPos);
3256 QFETCH(QPointF, changedContentPos);
3257 QFETCH(QPointF, firstDelegatePos);
3258 QFETCH(QPointF, resizeContentPos);
3259
3260 QaimModel model;
3261 for (int i = 0; i < 30; i++)
3262 model.addItem(name: "Item" + QString::number(i), number: "");
3263
3264 QQuickView *window = getView();
3265 window->rootContext()->setContextProperty("testModel", &model);
3266 window->rootContext()->setContextProperty("initialViewWidth", 240);
3267 window->rootContext()->setContextProperty("initialViewHeight", 320);
3268 window->setSource(testFileUrl(fileName: "header.qml"));
3269 window->show();
3270 qApp->processEvents();
3271
3272 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3273 QTRY_VERIFY(gridview != nullptr);
3274 gridview->setFlow(flow);
3275 gridview->setLayoutDirection(layoutDirection);
3276 gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3277 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3278
3279 QQuickItem *contentItem = gridview->contentItem();
3280 QTRY_VERIFY(contentItem != nullptr);
3281
3282 QQuickText *header = findItem<QQuickText>(parent: contentItem, objectName: "header");
3283 QVERIFY(header);
3284 QCOMPARE(header, gridview->headerItem());
3285
3286 QCOMPARE(header->position(), initialHeaderPos);
3287 QCOMPARE(header->width(), 100.);
3288 QCOMPARE(header->height(), 30.);
3289 QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3290
3291 if (flow == QQuickGridView::FlowLeftToRight)
3292 QCOMPARE(gridview->contentHeight(), (model.count()+2) / 3 * 60. + header->height());
3293 else
3294 QCOMPARE(gridview->contentWidth(), (model.count()+3) / 5 * 80. + header->width());
3295
3296 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
3297 QVERIFY(item);
3298 QCOMPARE(item->position(), firstDelegatePos);
3299
3300 model.clear();
3301 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3302 QCOMPARE(header->position(), initialHeaderPos); // header should stay where it is
3303 if (flow == QQuickGridView::FlowLeftToRight)
3304 QCOMPARE(gridview->contentHeight(), header->height());
3305 else
3306 QCOMPARE(gridview->contentWidth(), header->width());
3307
3308 for (int i = 0; i < 30; i++)
3309 model.addItem(name: "Item" + QString::number(i), number: "");
3310
3311 QSignalSpy headerItemSpy(gridview, SIGNAL(headerItemChanged()));
3312 QMetaObject::invokeMethod(obj: window->rootObject(), member: "changeHeader");
3313
3314 QCOMPARE(headerItemSpy.count(), 1);
3315
3316 header = findItem<QQuickText>(parent: contentItem, objectName: "header");
3317 QVERIFY(!header);
3318 header = findItem<QQuickText>(parent: contentItem, objectName: "header2");
3319 QVERIFY(header);
3320
3321 QCOMPARE(header, gridview->headerItem());
3322
3323 QCOMPARE(header->position(), changedHeaderPos);
3324 QCOMPARE(header->width(), 50.);
3325 QCOMPARE(header->height(), 20.);
3326 QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
3327
3328 item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
3329 QVERIFY(item);
3330 QCOMPARE(item->position(), firstDelegatePos);
3331
3332 header->setHeight(10);
3333 header->setWidth(40);
3334 QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
3335
3336 releaseView(view: window);
3337
3338
3339 // QTBUG-21207 header should become visible if view resizes from initial empty size
3340
3341 window = getView();
3342 window->rootContext()->setContextProperty("testModel", &model);
3343 window->rootContext()->setContextProperty("initialViewWidth", 240);
3344 window->rootContext()->setContextProperty("initialViewHeight", 320);
3345 window->setSource(testFileUrl(fileName: "header.qml"));
3346 window->show();
3347 qApp->processEvents();
3348
3349 gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3350 QTRY_VERIFY(gridview != nullptr);
3351 gridview->setFlow(flow);
3352 gridview->setLayoutDirection(layoutDirection);
3353 gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3354 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3355
3356 gridview->setWidth(240);
3357 gridview->setHeight(320);
3358 QTRY_COMPARE(gridview->headerItem()->position(), initialHeaderPos);
3359 QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3360
3361 releaseView(view: window);
3362}
3363
3364void tst_QQuickGridView::header_data()
3365{
3366 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
3367 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
3368 QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection");
3369 QTest::addColumn<QPointF>(name: "initialHeaderPos");
3370 QTest::addColumn<QPointF>(name: "changedHeaderPos");
3371 QTest::addColumn<QPointF>(name: "initialContentPos");
3372 QTest::addColumn<QPointF>(name: "changedContentPos");
3373 QTest::addColumn<QPointF>(name: "firstDelegatePos");
3374 QTest::addColumn<QPointF>(name: "resizeContentPos");
3375
3376 // header1 = 100 x 30
3377 // header2 = 50 x 20
3378 // cells = 80 x 60
3379 // view width = 240
3380
3381 // header above items, top left
3382 QTest::newRow(dataTag: "LeftToRight, LtR, TtB")
3383 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3384 << QPointF(0, -30)
3385 << QPointF(0, -20)
3386 << QPointF(0, -30)
3387 << QPointF(0, -20)
3388 << QPointF(0, 0)
3389 << QPointF(0, -10);
3390
3391 // header above items, top right
3392 QTest::newRow(dataTag: "LeftToRight, RtL, TtB")
3393 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3394 << QPointF(240 - 100, -30)
3395 << QPointF((240 - 100) + 50, -20) // 50 = width diff between old and new headers
3396 << QPointF(0, -30)
3397 << QPointF(0, -20)
3398 << QPointF(160, 0)
3399 << QPointF(0, -10);
3400
3401 // header below items, bottom left
3402 QTest::newRow(dataTag: "LeftToRight, LtR, BtT")
3403 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3404 << QPointF(0, 0)
3405 << QPointF(0, 0)
3406 << QPointF(0, -320 + 30)
3407 << QPointF(0, -320 + 20)
3408 << QPointF(0, -60)
3409 << QPointF(0, -320 + 10);
3410
3411 // header above items, top right
3412 QTest::newRow(dataTag: "LeftToRight, RtL, BtT")
3413 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3414 << QPointF(240 - 100, 0)
3415 << QPointF((240 - 100) + 50, 0)
3416 << QPointF(0, -320 + 30)
3417 << QPointF(0, -320 + 20)
3418 << QPointF(160, -60)
3419 << QPointF(0, -320 + 10);
3420
3421
3422 // header to left of items, bottom left
3423 QTest::newRow(dataTag: "TopToBottom, LtR, TtB")
3424 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3425 << QPointF(-100, 0)
3426 << QPointF(-50, 0)
3427 << QPointF(-100, 0)
3428 << QPointF(-50, 0)
3429 << QPointF(0, 0)
3430 << QPointF(-40, 0);
3431
3432 // header to right of items, bottom right
3433 QTest::newRow(dataTag: "TopToBottom, RtL, TtB")
3434 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3435 << QPointF(0, 0)
3436 << QPointF(0, 0)
3437 << QPointF(-(240 - 100), 0)
3438 << QPointF(-(240 - 50), 0)
3439 << QPointF(-80, 0)
3440 << QPointF(-(240 - 40), 0);
3441
3442 // header to left of items, top left
3443 QTest::newRow(dataTag: "TopToBottom, LtR, BtT")
3444 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3445 << QPointF(-100, -30)
3446 << QPointF(-50, -20)
3447 << QPointF(-100, -320)
3448 << QPointF(-50, -320)
3449 << QPointF(0, -60)
3450 << QPointF(-40, -320);
3451
3452 // header to right of items, top right
3453 QTest::newRow(dataTag: "TopToBottom, RtL, BtT")
3454 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3455 << QPointF(0, -30)
3456 << QPointF(0, -20)
3457 << QPointF(-(240 - 100), -320)
3458 << QPointF(-(240 - 50), -320)
3459 << QPointF(-80, -60)
3460 << QPointF(-(240 - 40), -320);
3461}
3462
3463class GVAccessor : public QQuickGridView
3464{
3465public:
3466 qreal minY() const { return minYExtent(); }
3467 qreal maxY() const { return maxYExtent(); }
3468 qreal minX() const { return minXExtent(); }
3469 qreal maxX() const { return maxXExtent(); }
3470};
3471
3472void tst_QQuickGridView::extents()
3473{
3474 QFETCH(QQuickGridView::Flow, flow);
3475 QFETCH(Qt::LayoutDirection, layoutDirection);
3476 QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3477 QFETCH(QPointF, headerPos);
3478 QFETCH(QPointF, footerPos);
3479 QFETCH(QPointF, minPos);
3480 QFETCH(QPointF, maxPos);
3481 QFETCH(QPointF, origin_empty);
3482 QFETCH(QPointF, origin_nonEmpty);
3483
3484 QQuickView *window = getView();
3485
3486 QaimModel model;
3487 QQmlContext *ctxt = window->rootContext();
3488
3489 ctxt->setContextProperty("testModel", &model);
3490 window->setSource(testFileUrl(fileName: "headerfooter.qml"));
3491 window->show();
3492 qApp->processEvents();
3493
3494 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
3495 QTRY_VERIFY(gridview != nullptr);
3496 gridview->setFlow(flow);
3497 gridview->setLayoutDirection(layoutDirection);
3498 gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3499 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3500
3501 QQuickItem *contentItem = gridview->contentItem();
3502 QTRY_VERIFY(contentItem != nullptr);
3503
3504 QQuickItem *header = findItem<QQuickItem>(parent: contentItem, objectName: "header");
3505 QVERIFY(header);
3506 QCOMPARE(header->position(), headerPos);
3507
3508 QQuickItem *footer = findItem<QQuickItem>(parent: contentItem, objectName: "footer");
3509 QVERIFY(footer);
3510 QCOMPARE(footer->position(), footerPos);
3511
3512 QCOMPARE(static_cast<GVAccessor*>(gridview)->minX(), minPos.x());
3513 QCOMPARE(static_cast<GVAccessor*>(gridview)->minY(), minPos.y());
3514 QCOMPARE(static_cast<GVAccessor*>(gridview)->maxX(), maxPos.x());
3515 QCOMPARE(static_cast<GVAccessor*>(gridview)->maxY(), maxPos.y());
3516
3517 QCOMPARE(gridview->originX(), origin_empty.x());
3518 QCOMPARE(gridview->originY(), origin_empty.y());
3519 for (int i=0; i<30; i++)
3520 model.addItem(name: "Item" + QString::number(i), number: "");
3521 gridview->forceLayout();
3522 QTRY_COMPARE(gridview->count(), model.count());
3523 QCOMPARE(gridview->originX(), origin_nonEmpty.x());
3524 QCOMPARE(gridview->originY(), origin_nonEmpty.y());
3525
3526 releaseView(view: window);
3527}
3528
3529void tst_QQuickGridView::extents_data()
3530{
3531 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
3532 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
3533 QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection");
3534 QTest::addColumn<QPointF>(name: "headerPos");
3535 QTest::addColumn<QPointF>(name: "footerPos");
3536 QTest::addColumn<QPointF>(name: "minPos");
3537 QTest::addColumn<QPointF>(name: "maxPos");
3538 QTest::addColumn<QPointF>(name: "origin_empty");
3539 QTest::addColumn<QPointF>(name: "origin_nonEmpty");
3540
3541 // header is 240x20 (or 20x320 in TopToBottom)
3542 // footer is 240x30 (or 30x320 in TopToBottom)
3543 // grid has 10 rows in LeftToRight mode and 6 columns in TopToBottom
3544
3545 QTest::newRow(dataTag: "LeftToRight, LtR, TtB")
3546 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3547 << QPointF(0, -20) << QPointF(0, 0)
3548 << QPointF(0, 20) << QPointF(240, 20)
3549 << QPointF(0, -20) << QPointF(0, -20);
3550
3551 QTest::newRow(dataTag: "LeftToRight, RtL, TtB")
3552 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3553 << QPointF(0, -20) << QPointF(0, 0)
3554 << QPointF(0, 20) << QPointF(240, 20)
3555 << QPointF(0, -20) << QPointF(0, -20);
3556
3557 QTest::newRow(dataTag: "LeftToRight, LtR, BtT")
3558 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3559 << QPointF(0, 0) << QPointF(0, -30)
3560 << QPointF(0, 320 - 20) << QPointF(240, 320 - 20) // content flow is reversed
3561 << QPointF(0, -30) << QPointF(0, (-60.0 * 10) - 30);
3562
3563 QTest::newRow(dataTag: "LeftToRight, RtL, BtT")
3564 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3565 << QPointF(0, 0) << QPointF(0, -30)
3566 << QPointF(0, 320 - 20) << QPointF(240, 320 - 20) // content flow is reversed
3567 << QPointF(0, -30) << QPointF(0, (-60.0 * 10) - 30);
3568
3569
3570 QTest::newRow(dataTag: "TopToBottom, LtR, TtB")
3571 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3572 << QPointF(-20, 0) << QPointF(0, 0)
3573 << QPointF(20, 0) << QPointF(20, 320)
3574 << QPointF(-20, 0) << QPointF(-20, 0);
3575
3576 QTest::newRow(dataTag: "TopToBottom, RtL, TtB")
3577 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3578 << QPointF(0, 0) << QPointF(-30, 0)
3579 << QPointF(240 - 20, 0) << QPointF(240 - 20, 320) // content flow is reversed
3580 << QPointF(-30, 0) << QPointF((-80.0 * 6) - 30, 0);
3581
3582 QTest::newRow(dataTag: "TopToBottom, LtR, BtT")
3583 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3584 << QPointF(-20, -320) << QPointF(0, -320)
3585 << QPointF(20, 0) << QPointF(20, 320)
3586 << QPointF(-20, 0) << QPointF(-20, 0);
3587
3588 QTest::newRow(dataTag: "TopToBottom, RtL, BtT")
3589 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3590 << QPointF(0, -320) << QPointF(-30, -320)
3591 << QPointF(240 - 20, 0) << QPointF(240 - 20, 320) // content flow is reversed
3592 << QPointF(-30, 0) << QPointF((-80.0 * 6) - 30, 0);
3593}
3594
3595void tst_QQuickGridView::resetModel_headerFooter()
3596{
3597 // Resetting a model shouldn't crash in views with header/footer
3598
3599 QQuickView *window = createView();
3600
3601 QaimModel model;
3602 for (int i = 0; i < 6; i++)
3603 model.addItem(name: "Item" + QString::number(i), number: "");
3604 QQmlContext *ctxt = window->rootContext();
3605 ctxt->setContextProperty("testModel", &model);
3606
3607 window->setSource(testFileUrl(fileName: "headerfooter.qml"));
3608 qApp->processEvents();
3609
3610 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
3611 QTRY_VERIFY(gridview != nullptr);
3612
3613 QQuickItem *contentItem = gridview->contentItem();
3614 QTRY_VERIFY(contentItem != nullptr);
3615
3616 QQuickItem *header = findItem<QQuickItem>(parent: contentItem, objectName: "header");
3617 QVERIFY(header);
3618 QCOMPARE(header->y(), -header->height());
3619
3620 QQuickItem *footer = findItem<QQuickItem>(parent: contentItem, objectName: "footer");
3621 QVERIFY(footer);
3622 QCOMPARE(footer->y(), 60.*2);
3623
3624 model.reset();
3625
3626 // A reset should not force a new header or footer to be created.
3627 QQuickItem *newHeader = findItem<QQuickItem>(parent: contentItem, objectName: "header");
3628 QCOMPARE(newHeader, header);
3629 QCOMPARE(header->y(), -header->height());
3630
3631 QQuickItem *newFooter = findItem<QQuickItem>(parent: contentItem, objectName: "footer");
3632 QCOMPARE(newFooter, footer);
3633 QCOMPARE(footer->y(), 60.*2);
3634
3635 delete window;
3636}
3637
3638void tst_QQuickGridView::resizeViewAndRepaint()
3639{
3640 QQuickView *window = createView();
3641 window->show();
3642
3643 QaimModel model;
3644 for (int i = 0; i < 40; i++)
3645 model.addItem(name: "Item" + QString::number(i), number: "");
3646
3647 QQmlContext *ctxt = window->rootContext();
3648 ctxt->setContextProperty("testModel", &model);
3649 ctxt->setContextProperty("initialWidth", 240);
3650 ctxt->setContextProperty("initialHeight", 100);
3651
3652 window->setSource(testFileUrl(fileName: "resizeview.qml"));
3653 window->show();
3654 qApp->processEvents();
3655
3656 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3657 QTRY_VERIFY(gridview != nullptr);
3658 QQuickItem *contentItem = gridview->contentItem();
3659 QTRY_VERIFY(contentItem != nullptr);
3660 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3661
3662 // item at index 10 should not be currently visible
3663 QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3664
3665 gridview->setHeight(320);
3666 QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3667
3668 gridview->setHeight(100);
3669 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3670
3671 // Ensure we handle -ve sizes
3672 gridview->setHeight(-100);
3673 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 3);
3674
3675 gridview->setCacheBuffer(120);
3676 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 9);
3677
3678 // ensure items in cache become visible
3679 gridview->setHeight(120);
3680 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 15);
3681
3682 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
3683 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3684 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3685 if (!item) qWarning() << "Item" << i << "not found";
3686 QTRY_VERIFY(item);
3687 QTRY_COMPARE(item->x(), qreal((i%3)*80));
3688 QTRY_COMPARE(item->y(), qreal((i/3)*60));
3689 QCOMPARE(delegateVisible(item), i < 9); // inside view visible, outside not visible
3690 }
3691
3692 // ensure items outside view become invisible
3693 gridview->setHeight(60);
3694 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 12);
3695
3696 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper", visibleOnly: false).count();
3697 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3698 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3699 if (!item) qWarning() << "Item" << i << "not found";
3700 QTRY_VERIFY(item);
3701 QTRY_COMPARE(item->x(), qreal((i%3)*80));
3702 QTRY_COMPARE(item->y(), qreal((i/3)*60));
3703 QCOMPARE(delegateVisible(item), i < 6); // inside view visible, outside not visible
3704 }
3705
3706 delete window;
3707}
3708
3709void tst_QQuickGridView::resizeGrid()
3710{
3711 QFETCH(QQuickGridView::Flow, flow);
3712 QFETCH(Qt::LayoutDirection, layoutDirection);
3713 QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3714 QFETCH(QPointF, initialContentPos);
3715 QFETCH(QPointF, firstItemPos);
3716
3717 QaimModel model;
3718 for (int i = 0; i < 30; i++)
3719 model.addItem(name: "Item" + QString::number(i), number: "");
3720
3721 QQuickView *window = getView();
3722 QQmlContext *ctxt = window->rootContext();
3723 ctxt->setContextProperty("testModel", &model);
3724 ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
3725 ctxt->setContextProperty("testRightToLeft", layoutDirection == Qt::RightToLeft);
3726 ctxt->setContextProperty("testBottomToTop", verticalLayoutDirection == QQuickGridView::BottomToTop);
3727 window->setSource(testFileUrl(fileName: "resizegrid.qml"));
3728 QQuickViewTestUtil::centerOnScreen(window, size: window->size());
3729 window->show();
3730 qApp->processEvents();
3731
3732 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3733 QTRY_VERIFY(gridview != nullptr);
3734 QQuickItem *contentItem = gridview->contentItem();
3735 QTRY_VERIFY(contentItem != nullptr);
3736
3737 // set the width to slightly larger than 3 items across, to test
3738 // items are aligned correctly in right-to-left
3739 window->rootObject()->setWidth(260);
3740 window->rootObject()->setHeight(320);
3741 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3742
3743 QCOMPARE(gridview->contentX(), initialContentPos.x());
3744 QCOMPARE(gridview->contentY(), initialContentPos.y());
3745
3746 QQuickItem *item0 = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: 0);
3747 QVERIFY(item0);
3748 QCOMPARE(item0->position(), firstItemPos);
3749
3750 // Confirm items positioned correctly and indexes correct
3751 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
3752 QVERIFY(items.count() >= 18 && items.count() <= 21);
3753 for (int i = 0; i < model.count() && i < items.count(); ++i) {
3754 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3755 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3756 QCOMPARE(item->position(), expectedItemPos(gridview, i, 0));
3757 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
3758 QVERIFY(name != nullptr);
3759 QCOMPARE(name->text(), model.name(i));
3760 }
3761
3762 // change from 3x5 grid to 4x7
3763 window->rootObject()->setWidth(window->rootObject()->width() + 80);
3764 window->rootObject()->setHeight(window->rootObject()->height() + 60*2);
3765 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3766
3767 // other than in LeftToRight+RightToLeft layout, the first item should not move
3768 // if view is resized
3769 QCOMPARE(findItem<QQuickItem>(contentItem, "wrapper", 0), item0);
3770 if (flow == QQuickGridView::FlowLeftToRight && layoutDirection == Qt::RightToLeft)
3771 firstItemPos.rx() += 80;
3772 QCOMPARE(item0->position(), firstItemPos);
3773
3774 QPointF newContentPos = initialContentPos;
3775 if (flow == QQuickGridView::FlowTopToBottom && layoutDirection == Qt::RightToLeft)
3776 newContentPos.rx() -= 80.0;
3777 if (verticalLayoutDirection == QQuickItemView::BottomToTop)
3778 newContentPos.ry() -= 60.0 * 2;
3779 QCOMPARE(gridview->contentX(), newContentPos.x());
3780 QCOMPARE(gridview->contentY(), newContentPos.y());
3781
3782 // Confirm items positioned correctly and indexes correct
3783 items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
3784 QVERIFY(items.count() >= 28);
3785 for (int i = 0; i < model.count() && i < items.count(); ++i) {
3786 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3787 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3788 QCOMPARE(item->position(), expectedItemPos(gridview, i, 0));
3789 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
3790 QVERIFY(name != nullptr);
3791 QCOMPARE(name->text(), model.name(i));
3792 }
3793
3794 releaseView(view: window);
3795}
3796
3797void tst_QQuickGridView::resizeGrid_data()
3798{
3799 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
3800 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
3801 QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection");
3802 QTest::addColumn<QPointF>(name: "initialContentPos");
3803 QTest::addColumn<QPointF>(name: "firstItemPos");
3804
3805 // Initial view width is 260, so in LeftToRight + right-to-left mode the
3806 // content x should be -20
3807
3808 QTest::newRow(dataTag: "LeftToRight, LtR, TtB")
3809 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3810 << QPointF(0, 0)
3811 << QPointF(0, 0);
3812
3813 QTest::newRow(dataTag: "LeftToRight, RtL, TtB")
3814 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3815 << QPointF(-20.0, 0)
3816 << QPointF(80.0 * 2, 0);
3817
3818 QTest::newRow(dataTag: "LeftToRight, LtR, BtT")
3819 << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3820 << QPointF(0, -320)
3821 << QPointF(0, -60.0);
3822
3823 QTest::newRow(dataTag: "LeftToRight, RtL, BtT")
3824 << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3825 << QPointF(-20.0, -320)
3826 << QPointF(80.0 * 2, -60.0);
3827
3828
3829 QTest::newRow(dataTag: "TopToBottom, LtR, TtB")
3830 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3831 << QPointF(0, 0)
3832 << QPointF(0, 0);
3833
3834 QTest::newRow(dataTag: "TopToBottom, RtL, TtB")
3835 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3836 << QPointF(-260, 0)
3837 << QPointF(-80.0, 0);
3838
3839 QTest::newRow(dataTag: "TopToBottom, LtR, BtT")
3840 << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3841 << QPointF(0, -320)
3842 << QPointF(0, -60.0);
3843
3844 QTest::newRow(dataTag: "TopToBottom, RtL, BtT")
3845 << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3846 << QPointF(-260, -320)
3847 << QPointF(-80.0, -60.0);
3848}
3849
3850
3851void tst_QQuickGridView::changeColumnCount()
3852{
3853 QaimModel model;
3854 for (int i = 0; i < 40; i++)
3855 model.addItem(name: "Item" + QString::number(i), number: "");
3856
3857 QQuickView *window = createView();
3858 QQmlContext *ctxt = window->rootContext();
3859 ctxt->setContextProperty("testModel", &model);
3860 ctxt->setContextProperty("initialWidth", 100);
3861 ctxt->setContextProperty("initialHeight", 320);
3862 window->setSource(testFileUrl(fileName: "resizeview.qml"));
3863 window->show();
3864 qApp->processEvents();
3865
3866 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3867 QTRY_VERIFY(gridview != nullptr);
3868 QQuickItem *contentItem = gridview->contentItem();
3869 QTRY_VERIFY(contentItem != nullptr);
3870 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3871
3872 // a single column of 6 items are visible
3873 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
3874 QCOMPARE(itemCount, 6);
3875 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3876 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3877 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3878 QCOMPARE(item->x(), 0.0);
3879 QCOMPARE(item->y(), qreal(i*60));
3880 }
3881
3882 // now 6x3 grid is visible, plus 1 extra below for refill
3883 gridview->setWidth(240);
3884 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3885 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
3886 QCOMPARE(itemCount, 6*3 + 1);
3887 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3888 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3889 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3890 QCOMPARE(item->x(), qreal((i%3)*80));
3891 QCOMPARE(item->y(), qreal((i/3)*60));
3892 }
3893
3894 // back to single column
3895 gridview->setWidth(100);
3896 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
3897 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
3898 QCOMPARE(itemCount, 6);
3899 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3900 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
3901 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3902 QCOMPARE(item->x(), 0.0);
3903 QCOMPARE(item->y(), qreal(i*60));
3904 }
3905
3906 delete window;
3907}
3908
3909void tst_QQuickGridView::indexAt_itemAt_data()
3910{
3911 QTest::addColumn<qreal>(name: "x");
3912 QTest::addColumn<qreal>(name: "y");
3913 QTest::addColumn<int>(name: "index");
3914
3915 QTest::newRow(dataTag: "Item 0 - 0, 0") << 0. << 0. << 0;
3916 QTest::newRow(dataTag: "Item 0 - 79, 59") << 79. << 59. << 0;
3917 QTest::newRow(dataTag: "Item 1 - 80, 0") << 80. << 0. << 1;
3918 QTest::newRow(dataTag: "Item 3 - 0, 60") << 0. << 60. << 3;
3919 QTest::newRow(dataTag: "No Item - 240, 0") << 240. << 0. << -1;
3920}
3921
3922void tst_QQuickGridView::indexAt_itemAt()
3923{
3924 QFETCH(qreal, x);
3925 QFETCH(qreal, y);
3926 QFETCH(int, index);
3927
3928 QQuickView *window = getView();
3929
3930 QaimModel model;
3931 model.addItem(name: "Fred", number: "12345");
3932 model.addItem(name: "John", number: "2345");
3933 model.addItem(name: "Bob", number: "54321");
3934 model.addItem(name: "Billy", number: "22345");
3935 model.addItem(name: "Sam", number: "2945");
3936 model.addItem(name: "Ben", number: "04321");
3937 model.addItem(name: "Jim", number: "0780");
3938
3939 window->rootContext()->setContextProperty("testModel", &model);
3940 window->setSource(testFileUrl(fileName: "gridview1.qml"));
3941 qApp->processEvents();
3942
3943 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
3944 QTRY_VERIFY(gridview != nullptr);
3945
3946 QQuickItem *contentItem = gridview->contentItem();
3947 QTRY_VERIFY(contentItem != nullptr);
3948
3949 QTRY_COMPARE(gridview->count(), model.count());
3950
3951 QQuickItem *item = nullptr;
3952 if (index >= 0) {
3953 item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index);
3954 QVERIFY(item);
3955 }
3956 QCOMPARE(gridview->indexAt(x, y), index);
3957 QVERIFY(gridview->itemAt(x, y) == item);
3958
3959 releaseView(view: window);
3960}
3961
3962void tst_QQuickGridView::onAdd()
3963{
3964 QFETCH(int, initialItemCount);
3965 QFETCH(int, itemsToAdd);
3966
3967 const int delegateWidth = 50;
3968 const int delegateHeight = 100;
3969 QaimModel model;
3970 QQuickView *window = getView();
3971 window->setGeometry(posx: 0,posy: 0,w: 5 * delegateWidth, h: 5 * delegateHeight); // just ensure all items fit
3972
3973 // these initial items should not trigger GridView.onAdd
3974 for (int i=0; i<initialItemCount; i++)
3975 model.addItem(name: "dummy value", number: "dummy value");
3976
3977 QQmlContext *ctxt = window->rootContext();
3978 ctxt->setContextProperty("testModel", &model);
3979 ctxt->setContextProperty("delegateWidth", delegateWidth);
3980 ctxt->setContextProperty("delegateHeight", delegateHeight);
3981 window->setSource(testFileUrl(fileName: "attachedSignals.qml"));
3982
3983 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
3984 gridview->setProperty(name: "width", value: window->width());
3985 gridview->setProperty(name: "height", value: window->height());
3986 qApp->processEvents();
3987
3988 QList<QPair<QString, QString> > items;
3989 for (int i=0; i<itemsToAdd; i++)
3990 items << qMakePair(x: QString("value %1").arg(a: i), y: QString::number(i));
3991 model.addItems(items);
3992
3993 gridview->forceLayout();
3994 QTRY_COMPARE(model.count(), gridview->count());
3995 qApp->processEvents();
3996
3997 QVariantList result = gridview->property(name: "addedDelegates").toList();
3998 QTRY_COMPARE(result.count(), items.count());
3999 for (int i=0; i<items.count(); i++)
4000 QCOMPARE(result[i].toString(), items[i].first);
4001
4002 releaseView(view: window);
4003}
4004
4005void tst_QQuickGridView::onAdd_data()
4006{
4007 QTest::addColumn<int>(name: "initialItemCount");
4008 QTest::addColumn<int>(name: "itemsToAdd");
4009
4010 QTest::newRow(dataTag: "0, add 1") << 0 << 1;
4011 QTest::newRow(dataTag: "0, add 2") << 0 << 2;
4012 QTest::newRow(dataTag: "0, add 10") << 0 << 10;
4013
4014 QTest::newRow(dataTag: "1, add 1") << 1 << 1;
4015 QTest::newRow(dataTag: "1, add 2") << 1 << 2;
4016 QTest::newRow(dataTag: "1, add 10") << 1 << 10;
4017
4018 QTest::newRow(dataTag: "5, add 1") << 5 << 1;
4019 QTest::newRow(dataTag: "5, add 2") << 5 << 2;
4020 QTest::newRow(dataTag: "5, add 10") << 5 << 10;
4021}
4022
4023void tst_QQuickGridView::onRemove()
4024{
4025 QFETCH(int, initialItemCount);
4026 QFETCH(int, indexToRemove);
4027 QFETCH(int, removeCount);
4028
4029 const int delegateWidth = 50;
4030 const int delegateHeight = 100;
4031 QaimModel model;
4032 for (int i=0; i<initialItemCount; i++)
4033 model.addItem(name: QString("value %1").arg(a: i), number: "dummy value");
4034
4035 QQuickView *window = getView();
4036 QQmlContext *ctxt = window->rootContext();
4037 ctxt->setContextProperty("testModel", &model);
4038 ctxt->setContextProperty("delegateWidth", delegateWidth);
4039 ctxt->setContextProperty("delegateHeight", delegateHeight);
4040 window->setSource(testFileUrl(fileName: "attachedSignals.qml"));
4041 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
4042
4043 model.removeItems(index: indexToRemove, count: removeCount);
4044 gridview->forceLayout();
4045 QTRY_COMPARE(model.count(), gridview->count());
4046 QCOMPARE(gridview->property("removedDelegateCount"), QVariant(removeCount));
4047
4048 releaseView(view: window);
4049}
4050
4051void tst_QQuickGridView::onRemove_data()
4052{
4053 QTest::addColumn<int>(name: "initialItemCount");
4054 QTest::addColumn<int>(name: "indexToRemove");
4055 QTest::addColumn<int>(name: "removeCount");
4056
4057 QTest::newRow(dataTag: "remove first") << 1 << 0 << 1;
4058 QTest::newRow(dataTag: "two items, remove first") << 2 << 0 << 1;
4059 QTest::newRow(dataTag: "two items, remove last") << 2 << 1 << 1;
4060 QTest::newRow(dataTag: "two items, remove all") << 2 << 0 << 2;
4061
4062 QTest::newRow(dataTag: "four items, remove first") << 4 << 0 << 1;
4063 QTest::newRow(dataTag: "four items, remove 0-2") << 4 << 0 << 2;
4064 QTest::newRow(dataTag: "four items, remove 1-3") << 4 << 1 << 2;
4065 QTest::newRow(dataTag: "four items, remove 2-4") << 4 << 2 << 2;
4066 QTest::newRow(dataTag: "four items, remove last") << 4 << 3 << 1;
4067 QTest::newRow(dataTag: "four items, remove all") << 4 << 0 << 4;
4068
4069 QTest::newRow(dataTag: "ten items, remove 1-8") << 10 << 0 << 8;
4070 QTest::newRow(dataTag: "ten items, remove 2-7") << 10 << 2 << 5;
4071 QTest::newRow(dataTag: "ten items, remove 4-10") << 10 << 4 << 6;
4072}
4073
4074void tst_QQuickGridView::attachedProperties_QTBUG_32836()
4075{
4076 QQuickView *window = createView();
4077
4078 window->setSource(testFileUrl(fileName: "attachedProperties.qml"));
4079 window->show();
4080 qApp->processEvents();
4081
4082 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
4083 QVERIFY(gridview != nullptr);
4084
4085 QQuickItem *header = gridview->headerItem();
4086 QVERIFY(header);
4087 QCOMPARE(header->width(), gridview->width());
4088
4089 QQuickItem *footer = gridview->footerItem();
4090 QVERIFY(footer);
4091 QCOMPARE(footer->width(), gridview->width());
4092
4093 QQuickItem *highlight = gridview->highlightItem();
4094 QVERIFY(highlight);
4095 QCOMPARE(highlight->width(), gridview->width());
4096
4097 QQuickItem *currentItem = gridview->currentItem();
4098 QVERIFY(currentItem);
4099 QCOMPARE(currentItem->width(), gridview->width());
4100
4101 delete window;
4102}
4103
4104void tst_QQuickGridView::columnCount()
4105{
4106 QQuickView window;
4107 window.setSource(testFileUrl(fileName: "gridview4.qml"));
4108 window.show();
4109 window.requestActivate();
4110 QVERIFY(QTest::qWaitForWindowActive(&window));
4111
4112 QQuickGridView *view = qobject_cast<QQuickGridView*>(object: window.rootObject());
4113
4114 QCOMPARE(view->cellWidth(), qreal(405)/qreal(9));
4115 QCOMPARE(view->cellHeight(), qreal(100));
4116
4117 QList<QQuickItem*> items = findItems<QQuickItem>(parent: view, objectName: "delegate");
4118 QCOMPARE(items.size(), 18);
4119 QCOMPARE(items.at(8)->y(), qreal(0));
4120 QCOMPARE(items.at(9)->y(), qreal(100));
4121}
4122
4123void tst_QQuickGridView::margins()
4124{
4125 {
4126 QQuickView *window = createView();
4127
4128 QaimModel model;
4129 for (int i = 0; i < 40; i++)
4130 model.addItem(name: "Item" + QString::number(i), number: "");
4131
4132 QQmlContext *ctxt = window->rootContext();
4133 ctxt->setContextProperty("testModel", &model);
4134 ctxt->setContextProperty("testRightToLeft", QVariant(false));
4135
4136 window->setSource(testFileUrl(fileName: "margins.qml"));
4137 window->show();
4138 qApp->processEvents();
4139
4140 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4141 QTRY_VERIFY(gridview != nullptr);
4142 QQuickItem *contentItem = gridview->contentItem();
4143 QTRY_VERIFY(contentItem != nullptr);
4144 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4145
4146 QCOMPARE(gridview->contentX(), -30.);
4147 QCOMPARE(gridview->originX(), 0.);
4148
4149 // check end bound
4150 gridview->positionViewAtEnd();
4151 qreal pos = gridview->contentX();
4152 gridview->setContentX(pos + 80);
4153 gridview->returnToBounds();
4154 QTRY_COMPARE(gridview->contentX(), pos + 50);
4155
4156 // remove item before visible and check that left margin is maintained
4157 // and originX is updated
4158 gridview->setContentX(200);
4159 model.removeItems(index: 0, count: 4);
4160 QTest::qWait(ms: 100);
4161 gridview->setContentX(-50);
4162 gridview->returnToBounds();
4163 QCOMPARE(gridview->originX(), 100.);
4164 QTRY_COMPARE(gridview->contentX(), 70.);
4165
4166 // reduce left margin
4167 gridview->setLeftMargin(20);
4168 QCOMPARE(gridview->originX(), 100.);
4169 QTRY_COMPARE(gridview->contentX(), 80.);
4170
4171 // check end bound
4172 gridview->positionViewAtEnd();
4173 QCOMPARE(gridview->originX(), 0.); // positionViewAtEnd() resets origin
4174 pos = gridview->contentX();
4175 gridview->setContentX(pos + 80);
4176 gridview->returnToBounds();
4177 QTRY_COMPARE(gridview->contentX(), pos + 50);
4178
4179 // reduce right margin
4180 pos = gridview->contentX();
4181 gridview->setRightMargin(40);
4182 QCOMPARE(gridview->originX(), 0.);
4183 QTRY_COMPARE(gridview->contentX(), pos-10);
4184
4185 delete window;
4186 }
4187 {
4188 //RTL
4189 QQuickView *window = createView();
4190 window->show();
4191
4192 QaimModel model;
4193 for (int i = 0; i < 40; i++)
4194 model.addItem(name: "Item" + QString::number(i), number: "");
4195
4196 QQmlContext *ctxt = window->rootContext();
4197 ctxt->setContextProperty("testModel", &model);
4198 ctxt->setContextProperty("testRightToLeft", QVariant(true));
4199
4200 window->setSource(testFileUrl(fileName: "margins.qml"));
4201 qApp->processEvents();
4202
4203 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4204 QTRY_VERIFY(gridview != nullptr);
4205
4206 QQuickItem *contentItem = gridview->contentItem();
4207 QTRY_VERIFY(contentItem != nullptr);
4208
4209 QTRY_COMPARE(gridview->contentX(), -240+50.);
4210 QTRY_COMPARE(gridview->originX(), -100. * 10);
4211
4212 // check end bound
4213 gridview->positionViewAtEnd();
4214 qreal pos = gridview->contentX();
4215 gridview->setContentX(pos - 80);
4216 gridview->returnToBounds();
4217 QTRY_COMPARE(gridview->contentX(), pos - 30);
4218
4219 // remove item before visible and check that left margin is maintained
4220 // and originX is updated
4221 gridview->setContentX(-400);
4222 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4223 model.removeItems(index: 0, count: 4);
4224 gridview->forceLayout();
4225 QTRY_COMPARE(model.count(), gridview->count());
4226 gridview->setContentX(-240+50);
4227 gridview->returnToBounds();
4228 QCOMPARE(gridview->originX(), -1000.);
4229 QTRY_COMPARE(gridview->contentX(), -240-50.);
4230
4231 // reduce right margin
4232 pos = gridview->contentX();
4233 gridview->setRightMargin(40);
4234 QCOMPARE(gridview->originX(), -1000.);
4235 QTRY_COMPARE(gridview->contentX(), -240-100 + 40.);
4236
4237 // check end bound
4238 gridview->positionViewAtEnd();
4239 QCOMPARE(gridview->originX(), -900.); // positionViewAtEnd() resets origin
4240 pos = gridview->contentX();
4241 gridview->setContentX(pos - 80);
4242 gridview->returnToBounds();
4243 QTRY_COMPARE(gridview->contentX(), pos - 30);
4244
4245 // reduce left margin
4246 pos = gridview->contentX();
4247 gridview->setLeftMargin(20);
4248 QCOMPARE(gridview->originX(), -900.);
4249 QTRY_COMPARE(gridview->contentX(), pos+10);
4250
4251 delete window;
4252 }
4253 {
4254 QQuickView *window = createView();
4255
4256 window->setSource(testFileUrl(fileName: "margins2.qml"));
4257 window->show();
4258 qApp->processEvents();
4259
4260 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4261 QVERIFY(gridview != nullptr);
4262 QQuickItem *contentItem = gridview->contentItem();
4263 QVERIFY(contentItem != nullptr);
4264 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4265 QCOMPARE(contentItem->x(), 200);
4266 QCOMPARE(contentItem->y(), 20);
4267
4268 delete window;
4269 }
4270}
4271
4272void tst_QQuickGridView::creationContext()
4273{
4274 QQuickView window;
4275 window.setGeometry(posx: 0,posy: 0,w: 240,h: 320);
4276 window.setSource(testFileUrl(fileName: "creationContext.qml"));
4277 qApp->processEvents();
4278
4279 QQuickItem *rootItem = qobject_cast<QQuickItem *>(object: window.rootObject());
4280 QVERIFY(rootItem);
4281 QVERIFY(rootItem->property("count").toInt() > 0);
4282
4283 QQuickItem *item;
4284 QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem"));
4285 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4286 QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
4287 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4288 QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
4289 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4290}
4291
4292void tst_QQuickGridView::snapToRow_data()
4293{
4294 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
4295 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
4296 QTest::addColumn<int>(name: "highlightRangeMode");
4297 QTest::addColumn<QPoint>(name: "flickStart");
4298 QTest::addColumn<QPoint>(name: "flickEnd");
4299 QTest::addColumn<qreal>(name: "snapAlignment");
4300 QTest::addColumn<qreal>(name: "endExtent");
4301 QTest::addColumn<qreal>(name: "startExtent");
4302
4303 QTest::newRow(dataTag: "vertical, left to right") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4304 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 800.0 << 0.0;
4305
4306 QTest::newRow(dataTag: "horizontal, left to right") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4307 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 800.0 << 0.0;
4308
4309 QTest::newRow(dataTag: "horizontal, right to left") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4310 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -800.0 - 240.0 << -240.0;
4311
4312 QTest::newRow(dataTag: "vertical, left to right, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4313 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 940.0 << -20.0;
4314
4315 QTest::newRow(dataTag: "horizontal, left to right, enforce range") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4316 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 940.0 << -20.0;
4317
4318 QTest::newRow(dataTag: "horizontal, right to left, enforce range") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4319 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -800.0 - 240.0 - 140.0 << -220.0;
4320}
4321
4322void tst_QQuickGridView::snapToRow()
4323{
4324 QFETCH(QQuickGridView::Flow, flow);
4325 QFETCH(Qt::LayoutDirection, layoutDirection);
4326 QFETCH(int, highlightRangeMode);
4327 QFETCH(QPoint, flickStart);
4328 QFETCH(QPoint, flickEnd);
4329 QFETCH(qreal, snapAlignment);
4330 QFETCH(qreal, endExtent);
4331 QFETCH(qreal, startExtent);
4332
4333 QQuickView *window = getView();
4334
4335 QQuickViewTestUtil::moveMouseAway(window);
4336 window->setSource(testFileUrl(fileName: "snapToRow.qml"));
4337 window->show();
4338 qApp->processEvents();
4339
4340 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4341 QTRY_VERIFY(gridview != nullptr);
4342
4343 gridview->setFlow(flow);
4344 gridview->setLayoutDirection(layoutDirection);
4345 gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4346 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4347
4348 QQuickItem *contentItem = gridview->contentItem();
4349 QTRY_VERIFY(contentItem != nullptr);
4350
4351 qreal origContentY = gridview->contentY();
4352 qreal origContentX = gridview->contentX();
4353 // confirm that a flick hits an item boundary
4354 flick(window, from: flickStart, to: flickEnd, duration: 180);
4355
4356 // wait until it's at least one cell further
4357 QTRY_VERIFY(qAbs(gridview->contentX() - origContentX) > 80 ||
4358 qAbs(gridview->contentY() - origContentY) > 80);
4359
4360 // click to stop it. Otherwise we wouldn't know how much further it will go. We don't want to it
4361 // to hit the endExtent, yet.
4362 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: flickEnd);
4363
4364 QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4365 if (flow == QQuickGridView::FlowLeftToRight)
4366 QCOMPARE(qreal(fmod(gridview->contentY(),80.0)), snapAlignment);
4367 else
4368 QCOMPARE(qreal(fmod(gridview->contentX(),80.0)), snapAlignment);
4369
4370 // flick to end
4371 do {
4372 flick(window, from: flickStart, to: flickEnd, duration: 180);
4373 QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4374 } while (flow == QQuickGridView::FlowLeftToRight
4375 ? !gridview->isAtYEnd()
4376 : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
4377
4378 if (flow == QQuickGridView::FlowLeftToRight)
4379 QCOMPARE(gridview->contentY(), endExtent);
4380 else
4381 QCOMPARE(gridview->contentX(), endExtent);
4382
4383 // flick to start
4384 do {
4385 flick(window, from: flickEnd, to: flickStart, duration: 180);
4386 QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4387 } while (flow == QQuickGridView::FlowLeftToRight
4388 ? !gridview->isAtYBeginning()
4389 : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
4390
4391 if (flow == QQuickGridView::FlowLeftToRight)
4392 QCOMPARE(gridview->contentY(), startExtent);
4393 else
4394 QCOMPARE(gridview->contentX(), startExtent);
4395
4396 releaseView(view: window);
4397}
4398
4399void tst_QQuickGridView::snapOneRow_data()
4400{
4401 QTest::addColumn<QQuickGridView::Flow>(name: "flow");
4402 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
4403 QTest::addColumn<int>(name: "highlightRangeMode");
4404 QTest::addColumn<QPoint>(name: "flickStart");
4405 QTest::addColumn<QPoint>(name: "flickEnd");
4406 QTest::addColumn<qreal>(name: "snapAlignment");
4407 QTest::addColumn<qreal>(name: "endExtent");
4408 QTest::addColumn<qreal>(name: "startExtent");
4409 QTest::addColumn<qreal>(name: "flickSlowdown");
4410
4411 QTest::newRow(dataTag: "vertical, left to right") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4412 << QPoint(20, 160) << QPoint(20, 20) << 100.0 << 240.0 << 0.0 << 1.0;
4413
4414 QTest::newRow(dataTag: "horizontal, left to right") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4415 << QPoint(160, 20) << QPoint(20, 20) << 100.0 << 240.0 << 0.0 << 1.0;
4416
4417 QTest::newRow(dataTag: "horizontal, right to left") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4418 << QPoint(20, 20) << QPoint(160, 20) << -340.0 << -240.0 - 240.0 << -240.0 << 1.0;
4419
4420 QTest::newRow(dataTag: "vertical, left to right, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4421 << QPoint(20, 160) << QPoint(20, 20) << 100.0 << 340.0 << -20.0 << 1.0;
4422
4423 QTest::newRow(dataTag: "horizontal, left to right, enforce range") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4424 << QPoint(160, 20) << QPoint(20, 20) << 100.0 << 340.0 << -20.0 << 1.0;
4425
4426 QTest::newRow(dataTag: "horizontal, right to left, enforce range") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4427 << QPoint(20, 20) << QPoint(160, 20) << -340.0 << -240.0 - 240.0 - 100.0 << -220.0 << 1.0;
4428
4429 // Using e.g. 120 rather than 95 always went to the next row.
4430 // Ensure this further movement has the same behavior
4431 QTest::newRow(dataTag: "vertical, left to right, no more blindspot") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4432 << QPoint(20, 160) << QPoint(20, 95) << 100.0 << 240.0 << 0.0 << 4.0;
4433
4434 // StrictlyEnforceRange should not override valid SnapOneItem decisions
4435 QTest::newRow(dataTag: "vertical, left to right, no more blindspot, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4436 << QPoint(20, 160) << QPoint(20, 95) << 100.0 << 340.0 << -20.0 << 4.0;
4437}
4438
4439void tst_QQuickGridView::snapOneRow()
4440{
4441 QFETCH(QQuickGridView::Flow, flow);
4442 QFETCH(Qt::LayoutDirection, layoutDirection);
4443 QFETCH(int, highlightRangeMode);
4444 QFETCH(QPoint, flickStart);
4445 QFETCH(QPoint, flickEnd);
4446 QFETCH(qreal, snapAlignment);
4447 QFETCH(qreal, endExtent);
4448 QFETCH(qreal, startExtent);
4449 QFETCH(qreal, flickSlowdown);
4450
4451 qreal flickDuration = 180 * flickSlowdown;
4452
4453 QQuickView *window = getView();
4454 QQuickViewTestUtil::moveMouseAway(window);
4455
4456 window->setSource(testFileUrl(fileName: "snapOneRow.qml"));
4457 window->show();
4458 qApp->processEvents();
4459
4460 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4461 QTRY_VERIFY(gridview != nullptr);
4462
4463 gridview->setFlow(flow);
4464 gridview->setLayoutDirection(layoutDirection);
4465 gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4466 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4467
4468 QQuickItem *contentItem = gridview->contentItem();
4469 QTRY_VERIFY(contentItem != nullptr);
4470
4471 QSignalSpy currentIndexSpy(gridview, SIGNAL(currentIndexChanged()));
4472
4473 // confirm that a flick hits next row boundary
4474 flick(window, from: flickStart, to: flickEnd, duration: flickDuration);
4475 QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4476 if (flow == QQuickGridView::FlowLeftToRight)
4477 QCOMPARE(gridview->contentY(), snapAlignment);
4478 else
4479 QCOMPARE(gridview->contentX(), snapAlignment);
4480
4481 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4482 QCOMPARE(gridview->currentIndex(), 2);
4483 QCOMPARE(currentIndexSpy.count(), 1);
4484 }
4485
4486 // flick to end
4487 do {
4488 flick(window, from: flickStart, to: flickEnd, duration: flickDuration);
4489 QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4490 } while (flow == QQuickGridView::FlowLeftToRight
4491 ? !gridview->isAtYEnd()
4492 : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
4493
4494 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4495 QCOMPARE(gridview->currentIndex(), 6);
4496 QCOMPARE(currentIndexSpy.count(), 3);
4497 }
4498
4499 if (flow == QQuickGridView::FlowLeftToRight)
4500 QCOMPARE(gridview->contentY(), endExtent);
4501 else
4502 QCOMPARE(gridview->contentX(), endExtent);
4503
4504 // flick to start
4505 do {
4506 flick(window, from: flickEnd, to: flickStart, duration: flickDuration);
4507 QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4508 } while (flow == QQuickGridView::FlowLeftToRight
4509 ? !gridview->isAtYBeginning()
4510 : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
4511
4512 if (flow == QQuickGridView::FlowLeftToRight)
4513 QCOMPARE(gridview->contentY(), startExtent);
4514 else
4515 QCOMPARE(gridview->contentX(), startExtent);
4516
4517 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4518 QCOMPARE(gridview->currentIndex(), 0);
4519 QCOMPARE(currentIndexSpy.count(), 6);
4520 }
4521
4522 releaseView(view: window);
4523}
4524
4525
4526void tst_QQuickGridView::unaligned()
4527{
4528 QQuickView *window = createView();
4529 window->show();
4530
4531 QaimModel model;
4532 for (int i = 0; i < 10; i++)
4533 model.addItem(name: "Item" + QString::number(i), number: "");
4534
4535 QQmlContext *ctxt = window->rootContext();
4536 ctxt->setContextProperty("testModel", &model);
4537
4538 window->setSource(testFileUrl(fileName: "unaligned.qml"));
4539 qApp->processEvents();
4540
4541 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
4542 QVERIFY(gridview != nullptr);
4543
4544 QQuickItem *contentItem = gridview->contentItem();
4545 QVERIFY(contentItem != nullptr);
4546
4547 for (int i = 0; i < 10; ++i) {
4548 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
4549 if (!item) qWarning() << "Item" << i << "not found";
4550 QVERIFY(item);
4551 QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
4552 QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
4553 }
4554
4555 // appending
4556 for (int i = 10; i < 18; ++i) {
4557 model.addItem(name: "Item" + QString::number(i), number: "");
4558 QQuickItem *item = nullptr;
4559 QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
4560 QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
4561 QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
4562 }
4563
4564 // inserting
4565 for (int i = 0; i < 10; ++i) {
4566 model.insertItem(index: i, name: "Item" + QString::number(i), number: "");
4567 QQuickItem *item = nullptr;
4568 QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
4569 QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
4570 QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
4571 }
4572
4573 // removing
4574 model.removeItems(index: 7, count: 10);
4575 gridview->forceLayout();
4576 QTRY_COMPARE(model.count(), gridview->count());
4577 for (int i = 0; i < 18; ++i) {
4578 QQuickItem *item = nullptr;
4579 QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
4580 QCOMPARE(item->x(), qreal(i%9)*gridview->cellWidth());
4581 QCOMPARE(item->y(), qreal(i/9)*gridview->cellHeight());
4582 }
4583
4584 delete window;
4585}
4586
4587void tst_QQuickGridView::populateTransitions()
4588{
4589 QFETCH(bool, staticallyPopulate);
4590 QFETCH(bool, dynamicallyPopulate);
4591 QFETCH(bool, usePopulateTransition);
4592
4593 QPointF transitionFrom(-50, -50);
4594 QPointF transitionVia(100, 100);
4595 QaimModel model_transitionFrom;
4596 QaimModel model_transitionVia;
4597
4598 QaimModel model;
4599 if (staticallyPopulate) {
4600 for (int i = 0; i < 30; i++)
4601 model.addItem(name: "item" + QString::number(i), number: "");
4602 }
4603
4604 QQuickView *window = getView();
4605 window->rootContext()->setContextProperty("testModel", &model);
4606 window->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
4607 window->rootContext()->setContextProperty("dynamicallyPopulate", dynamicallyPopulate);
4608 window->rootContext()->setContextProperty("transitionFrom", transitionFrom);
4609 window->rootContext()->setContextProperty("transitionVia", transitionVia);
4610 window->rootContext()->setContextProperty("model_transitionFrom", &model_transitionFrom);
4611 window->rootContext()->setContextProperty("model_transitionVia", &model_transitionVia);
4612 window->setSource(testFileUrl(fileName: "populateTransitions.qml"));
4613 window->show();
4614
4615 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4616 QVERIFY(gridview);
4617 QQuickItem *contentItem = gridview->contentItem();
4618 QVERIFY(contentItem);
4619
4620 // check the populate transition is run
4621 if (staticallyPopulate && usePopulateTransition) {
4622 QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 18);
4623 QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
4624 } else if (dynamicallyPopulate) {
4625 QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
4626 QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 18);
4627 } else {
4628 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4629 QCOMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
4630 QCOMPARE(gridview->property("countAddTransitions").toInt(), 0);
4631 }
4632
4633 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
4634 for (int i=0; i < model.count() && i < itemCount; ++i) {
4635 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
4636 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4637 QTRY_COMPARE(item->x(), (i%3)*80.0);
4638 QTRY_COMPARE(item->y(), (i/3)*60.0);
4639 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
4640 QVERIFY(name != nullptr);
4641 QTRY_COMPARE(name->text(), model.name(i));
4642 }
4643
4644 gridview->setProperty(name: "countPopulateTransitions", value: 0);
4645 gridview->setProperty(name: "countAddTransitions", value: 0);
4646
4647 // add an item and check this is done with add transition, not populate
4648 model.insertItem(index: 0, name: "another item", number: "");
4649 QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 1);
4650 QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
4651
4652 // clear the model
4653 window->rootContext()->setContextProperty("testModel", QVariant());
4654 QTRY_COMPARE(gridview->count(), 0);
4655 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 0);
4656 gridview->setProperty(name: "countPopulateTransitions", value: 0);
4657 gridview->setProperty(name: "countAddTransitions", value: 0);
4658
4659 // set to a valid model and check populate transition is run a second time
4660 model.clear();
4661 for (int i = 0; i < 30; i++)
4662 model.addItem(name: "item" + QString::number(i), number: "");
4663 window->rootContext()->setContextProperty("testModel", &model);
4664 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4665
4666 QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 18 : 0);
4667 QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
4668
4669 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
4670 for (int i=0; i < model.count() && i < itemCount; ++i) {
4671 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
4672 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4673 QTRY_COMPARE(item->x(), (i%3)*80.0);
4674 QTRY_COMPARE(item->y(), (i/3)*60.0);
4675 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
4676 QVERIFY(name != nullptr);
4677 QTRY_COMPARE(name->text(), model.name(i));
4678 }
4679
4680 // reset model and check populate transition is run again
4681 gridview->setProperty(name: "countPopulateTransitions", value: 0);
4682 gridview->setProperty(name: "countAddTransitions", value: 0);
4683 model.reset();
4684 QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 18 : 0);
4685 QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
4686
4687 itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
4688 for (int i=0; i < model.count() && i < itemCount; ++i) {
4689 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
4690 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4691 QTRY_COMPARE(item->x(), (i%3)*80.0);
4692 QTRY_COMPARE(item->y(), (i/3)*60.0);
4693 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
4694 QVERIFY(name != nullptr);
4695 QTRY_COMPARE(name->text(), model.name(i));
4696 }
4697
4698 releaseView(view: window);
4699}
4700
4701void tst_QQuickGridView::populateTransitions_data()
4702{
4703 QTest::addColumn<bool>(name: "staticallyPopulate");
4704 QTest::addColumn<bool>(name: "dynamicallyPopulate");
4705 QTest::addColumn<bool>(name: "usePopulateTransition");
4706
4707 QTest::newRow(dataTag: "static") << true << false << true;
4708 QTest::newRow(dataTag: "static, no populate") << true << false << false;
4709
4710 QTest::newRow(dataTag: "dynamic") << false << true << true;
4711 QTest::newRow(dataTag: "dynamic, no populate") << false << true << false;
4712
4713 QTest::newRow(dataTag: "empty to start with") << false << false << true;
4714 QTest::newRow(dataTag: "empty to start with, no populate") << false << false << false;
4715}
4716
4717void tst_QQuickGridView::addTransitions()
4718{
4719 QFETCH(int, initialItemCount);
4720 QFETCH(bool, shouldAnimateTargets);
4721 QFETCH(qreal, contentYRowOffset);
4722 QFETCH(int, insertionIndex);
4723 QFETCH(int, insertionCount);
4724 QFETCH(ListRange, expectedDisplacedIndexes);
4725
4726 // added items should start here
4727 QPointF targetItems_transitionFrom(-50, -50);
4728
4729 // displaced items should pass through this point
4730 QPointF displacedItems_transitionVia(100, 100);
4731
4732 QaimModel model;
4733 for (int i = 0; i < initialItemCount; i++)
4734 model.addItem(name: "Original item" + QString::number(i), number: "");
4735 QaimModel model_targetItems_transitionFrom;
4736 QaimModel model_displacedItems_transitionVia;
4737
4738 QQuickView *window = getView();
4739 QQmlContext *ctxt = window->rootContext();
4740 ctxt->setContextProperty("testModel", &model);
4741 ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
4742 ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
4743 ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
4744 ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
4745 window->setSource(testFileUrl(fileName: "addTransitions.qml"));
4746 window->show();
4747
4748 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4749 QTRY_VERIFY(gridview != nullptr);
4750 QQuickItem *contentItem = gridview->contentItem();
4751 QVERIFY(contentItem != nullptr);
4752 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4753
4754 if (contentYRowOffset != 0) {
4755 gridview->setContentY(contentYRowOffset * 60.0);
4756 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4757 }
4758
4759 QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4760
4761 // only target items that will become visible should be animated
4762 QList<QPair<QString, QString> > newData;
4763 QList<QPair<QString, QString> > expectedTargetData;
4764 QList<int> targetIndexes;
4765 if (shouldAnimateTargets) {
4766 for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) {
4767 newData << qMakePair(x: QString("New item %1").arg(a: i), y: QString(""));
4768
4769 // last visible item is the first item of the row beneath the view
4770 if (i >= (gridview->contentY() / 60)*3 && i < qCeil(v: (gridview->contentY() + gridview->height()) / 60.0)*3) {
4771 expectedTargetData << newData.last();
4772 targetIndexes << i;
4773 }
4774 }
4775 QVERIFY(expectedTargetData.count() > 0);
4776 }
4777
4778 // start animation
4779 if (!newData.isEmpty()) {
4780 model.insertItems(index: insertionIndex, items: newData);
4781 gridview->forceLayout();
4782 QTRY_COMPARE(model.count(), gridview->count());
4783 }
4784
4785 QList<QQuickItem *> targetItems = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper", indexes: targetIndexes);
4786
4787 if (shouldAnimateTargets) {
4788 QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4789 QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4790 expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4791
4792 // check the target and displaced items were animated
4793 model_targetItems_transitionFrom.matchAgainst(other: expectedTargetData, error1: "wasn't animated from target 'from' pos", error2: "shouldn't have been animated from target 'from' pos");
4794 model_displacedItems_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with displaced anim", error2: "shouldn't have been animated with displaced anim");
4795
4796 // check attached properties
4797 matchItemsAndIndexes(items: gridview->property(name: "targetTrans_items").toMap(), model, expectedIndexes: targetIndexes);
4798 matchIndexLists(indexLists: gridview->property(name: "targetTrans_targetIndexes").toList(), expectedIndexes: targetIndexes);
4799 matchItemLists(itemLists: gridview->property(name: "targetTrans_targetItems").toList(), expectedItems: targetItems);
4800 if (expectedDisplacedIndexes.isValid()) {
4801 // adjust expectedDisplacedIndexes to their final values after the move
4802 QList<int> displacedIndexes = adjustIndexesForAddDisplaced(indexes: expectedDisplacedIndexes.indexes, index: insertionIndex, count: insertionCount);
4803 matchItemsAndIndexes(items: gridview->property(name: "displacedTrans_items").toMap(), model, expectedIndexes: displacedIndexes);
4804 matchIndexLists(indexLists: gridview->property(name: "displacedTrans_targetIndexes").toList(), expectedIndexes: targetIndexes);
4805 matchItemLists(itemLists: gridview->property(name: "displacedTrans_targetItems").toList(), expectedItems: targetItems);
4806 }
4807 } else {
4808 QTRY_COMPARE(model_targetItems_transitionFrom.count(), 0);
4809 QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
4810 }
4811
4812 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
4813 int firstVisibleIndex = -1;
4814 for (int i=0; i<items.count(); i++) {
4815 if (items[i]->y() >= gridview->contentY()) {
4816 QQmlExpression e(qmlContext(items[i]), items[i], "index");
4817 firstVisibleIndex = e.evaluate().toInt();
4818 break;
4819 }
4820 }
4821 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4822
4823 // verify all items moved to the correct final positions
4824 for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
4825 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
4826 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4827 QCOMPARE(item->x(), (i%3)*80.0);
4828 QCOMPARE(item->y(), (i/3)*60.0);
4829 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
4830 QVERIFY(name != nullptr);
4831 QCOMPARE(name->text(), model.name(i));
4832 }
4833
4834 releaseView(view: window);
4835}
4836
4837void tst_QQuickGridView::addTransitions_data()
4838{
4839 QTest::addColumn<int>(name: "initialItemCount");
4840 QTest::addColumn<qreal>(name: "contentYRowOffset");
4841 QTest::addColumn<bool>(name: "shouldAnimateTargets");
4842 QTest::addColumn<int>(name: "insertionIndex");
4843 QTest::addColumn<int>(name: "insertionCount");
4844 QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes");
4845
4846 // if inserting a full row before visible index, items don't appear or animate in, even if there are > 1 new items
4847 QTest::newRow(dataTag: "insert 1, just before start")
4848 << 30 << 1.0 << false
4849 << 0 << 1 << ListRange();
4850 QTest::newRow(dataTag: "insert 1, way before start")
4851 << 30 << 1.0 << false
4852 << 0 << 1 << ListRange();
4853 QTest::newRow(dataTag: "insert multiple, just before start")
4854 << 30 << 1.0 << false
4855 << 0 << 3 << ListRange();
4856 QTest::newRow(dataTag: "insert multiple (< 1 row), just before start")
4857 << 30 << 1.0 << false
4858 << 0 << 2 << ListRange();
4859 QTest::newRow(dataTag: "insert multiple, way before start")
4860 << 30 << 3.0 << false
4861 << 0 << 3 << ListRange();
4862
4863 QTest::newRow(dataTag: "insert 1 at start")
4864 << 30 << 0.0 << true
4865 << 0 << 1 << ListRange(0, 17);
4866 QTest::newRow(dataTag: "insert multiple at start")
4867 << 30 << 0.0 << true
4868 << 0 << 3 << ListRange(0, 17);
4869 QTest::newRow(dataTag: "insert multiple (> 1 row) at start")
4870 << 30 << 0.0 << true
4871 << 0 << 5 << ListRange(0, 17);
4872 QTest::newRow(dataTag: "insert 1 at start, content y not 0")
4873 << 30 << 1.0 << true // first visible is index 3
4874 << 3 << 1 << ListRange(0 + 3, 17 + 3);
4875 QTest::newRow(dataTag: "insert multiple at start, content y not 0")
4876 << 30 << 1.0 << true // first visible is index 3
4877 << 3 << 3 << ListRange(0 + 3, 17 + 3);
4878 QTest::newRow(dataTag: "insert multiple (> 1 row) at start, content y not 0")
4879 << 30 << 1.0 << true // first visible is index 3
4880 << 3 << 5 << ListRange(0 + 3, 17 + 3);
4881
4882 QTest::newRow(dataTag: "insert 1 at start, to empty grid")
4883 << 0 << 0.0 << true
4884 << 0 << 1 << ListRange();
4885 QTest::newRow(dataTag: "insert multiple at start, to empty grid")
4886 << 0 << 0.0 << true
4887 << 0 << 3 << ListRange();
4888
4889 QTest::newRow(dataTag: "insert 1 at middle")
4890 << 30 << 0.0 << true
4891 << 7 << 1 << ListRange(7, 17);
4892 QTest::newRow(dataTag: "insert multiple at middle")
4893 << 30 << 0.0 << true
4894 << 7 << 3 << ListRange(7, 17);
4895 QTest::newRow(dataTag: "insert multiple (> 1 row) at middle")
4896 << 30 << 0.0 << true
4897 << 7 << 5 << ListRange(7, 17);
4898
4899 QTest::newRow(dataTag: "insert 1 at bottom")
4900 << 30 << 0.0 << true
4901 << 17 << 1 << ListRange(17, 17);
4902 QTest::newRow(dataTag: "insert multiple at bottom")
4903 << 30 << 0.0 << true
4904 << 17 << 3 << ListRange(17, 17);
4905 QTest::newRow(dataTag: "insert 1 at bottom, content y not 0")
4906 << 30 << 1.0 << true
4907 << 17 + 3 << 1 << ListRange(17 + 3, 17 + 3);
4908 QTest::newRow(dataTag: "insert multiple at bottom, content y not 0")
4909 << 30 << 1.0 << true
4910 << 17 + 3 << 3 << ListRange(17 + 3, 17 + 3);
4911
4912
4913 // items added after the last visible will not be animated in, since they
4914 // do not appear in the final view
4915 QTest::newRow(dataTag: "insert 1 after end")
4916 << 30 << 0.0 << false
4917 << 18 << 1 << ListRange();
4918 QTest::newRow(dataTag: "insert multiple after end")
4919 << 30 << 0.0 << false
4920 << 18 << 3 << ListRange();
4921}
4922
4923void tst_QQuickGridView::moveTransitions()
4924{
4925 QFETCH(int, initialItemCount);
4926 QFETCH(qreal, contentYRowOffset);
4927 QFETCH(qreal, rowOffsetAfterMove);
4928 QFETCH(int, moveFrom);
4929 QFETCH(int, moveTo);
4930 QFETCH(int, moveCount);
4931 QFETCH(ListRange, expectedDisplacedIndexes);
4932
4933 // target and displaced items should pass through these points
4934 QPointF targetItems_transitionVia(-50, 50);
4935 QPointF displacedItems_transitionVia(100, 100);
4936
4937 QaimModel model;
4938 for (int i = 0; i < initialItemCount; i++)
4939 model.addItem(name: "Original item" + QString::number(i), number: "");
4940 QaimModel model_targetItems_transitionVia;
4941 QaimModel model_displacedItems_transitionVia;
4942
4943 QQuickView *window = getView();
4944 QQmlContext *ctxt = window->rootContext();
4945 ctxt->setContextProperty("testModel", &model);
4946 ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
4947 ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
4948 ctxt->setContextProperty("targetItems_transitionVia", targetItems_transitionVia);
4949 ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
4950 window->setSource(testFileUrl(fileName: "moveTransitions.qml"));
4951 window->show();
4952
4953 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
4954 QTRY_VERIFY(gridview != nullptr);
4955 QQuickItem *contentItem = gridview->contentItem();
4956 QVERIFY(contentItem != nullptr);
4957 QQuickText *name;
4958
4959 if (contentYRowOffset != 0) {
4960 gridview->setContentY(contentYRowOffset * 60.0);
4961 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
4962 }
4963
4964 QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4965
4966 // Items moving to *or* from visible positions should be animated.
4967 // Otherwise, they should not be animated.
4968 QList<QPair<QString, QString> > expectedTargetData;
4969 QList<int> targetIndexes;
4970 for (int i=moveFrom; i<moveFrom+moveCount; i++) {
4971 int toIndex = moveTo + (i - moveFrom);
4972 int firstVisibleIndex = (gridview->contentY() / 60) * 3;
4973 int lastVisibleIndex = (qCeil(v: (gridview->contentY() + gridview->height()) / 60.0)*3) - 1;
4974 if ((i >= firstVisibleIndex && i <= lastVisibleIndex)
4975 || (toIndex >= firstVisibleIndex && toIndex <= lastVisibleIndex)) {
4976 expectedTargetData << qMakePair(x: model.name(index: i), y: model.number(index: i));
4977 targetIndexes << i;
4978 }
4979 }
4980 // ViewTransition.index provides the indices that items are moving to, not from
4981 targetIndexes = adjustIndexesForMove(indexes: targetIndexes, from: moveFrom, to: moveTo, count: moveCount);
4982
4983 // start animation
4984 model.moveItems(from: moveFrom, to: moveTo, count: moveCount);
4985 gridview->forceLayout();
4986
4987 QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4988 QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4989 expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4990
4991 QList<QQuickItem *> targetItems = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper", indexes: targetIndexes);
4992
4993 // check the target and displaced items were animated
4994 model_targetItems_transitionVia.matchAgainst(other: expectedTargetData, error1: "wasn't animated from target 'from' pos", error2: "shouldn't have been animated from target 'from' pos");
4995 model_displacedItems_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with displaced anim", error2: "shouldn't have been animated with displaced anim");
4996
4997 // check attached properties
4998 matchItemsAndIndexes(items: gridview->property(name: "targetTrans_items").toMap(), model, expectedIndexes: targetIndexes);
4999 matchIndexLists(indexLists: gridview->property(name: "targetTrans_targetIndexes").toList(), expectedIndexes: targetIndexes);
5000 matchItemLists(itemLists: gridview->property(name: "targetTrans_targetItems").toList(), expectedItems: targetItems);
5001 if (expectedDisplacedIndexes.isValid()) {
5002 // adjust expectedDisplacedIndexes to their final values after the move
5003 QList<int> displacedIndexes = adjustIndexesForMove(indexes: expectedDisplacedIndexes.indexes, from: moveFrom, to: moveTo, count: moveCount);
5004 matchItemsAndIndexes(items: gridview->property(name: "displacedTrans_items").toMap(), model, expectedIndexes: displacedIndexes);
5005 matchIndexLists(indexLists: gridview->property(name: "displacedTrans_targetIndexes").toList(), expectedIndexes: targetIndexes);
5006 matchItemLists(itemLists: gridview->property(name: "displacedTrans_targetItems").toList(), expectedItems: targetItems);
5007 }
5008
5009 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
5010 int firstVisibleIndex = -1;
5011 for (int i=0; i<items.count(); i++) {
5012 if (items[i]->y() >= gridview->contentY()) {
5013 QQmlExpression e(qmlContext(items[i]), items[i], "index");
5014 firstVisibleIndex = e.evaluate().toInt();
5015 break;
5016 }
5017 }
5018 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
5019
5020 // verify all items moved to the correct final positions
5021 qreal pixelOffset = 60 * rowOffsetAfterMove;
5022 for (int i=firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
5023 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5024 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5025 QCOMPARE(item->x(), (i%3)*80.0);
5026 QCOMPARE(item->y(), (i/3)*60.0 + pixelOffset);
5027 name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
5028 QVERIFY(name != nullptr);
5029 QTRY_COMPARE(name->text(), model.name(i));
5030 }
5031
5032 releaseView(view: window);
5033}
5034
5035void tst_QQuickGridView::moveTransitions_data()
5036{
5037 QTest::addColumn<int>(name: "initialItemCount");
5038 QTest::addColumn<qreal>(name: "contentYRowOffset");
5039 QTest::addColumn<qreal>(name: "rowOffsetAfterMove");
5040 QTest::addColumn<int>(name: "moveFrom");
5041 QTest::addColumn<int>(name: "moveTo");
5042 QTest::addColumn<int>(name: "moveCount");
5043 QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes");
5044
5045 QTest::newRow(dataTag: "move from above view, outside visible items, move 1")
5046 << 30 << 2.0 << 0.0
5047 << 1 << 10 << 1 << ListRange(6, 10);
5048 QTest::newRow(dataTag: "move from above view, outside visible items, move 1 (first item)")
5049 << 30 << 2.0 << 0.0
5050 << 0 << 10 << 1 << ListRange(6, 10);
5051 QTest::newRow(dataTag: "move from above view, outside visible items, move multiple")
5052 << 30 << 2.0 << 1.0
5053 << 1 << 10 << 3 << ListRange(13, 23);
5054 QTest::newRow(dataTag: "move from above view, mix of visible/non-visible")
5055 << 30 << 2.0 << 1.0
5056 << 1 << 10 << 6 << (ListRange(7, 15) + ListRange(16, 23));
5057 QTest::newRow(dataTag: "move from above view, mix of visible/non-visible (move first)")
5058 << 30 << 2.0 << 2.0
5059 << 0 << 10 << 6 << ListRange(16, 23);
5060
5061 QTest::newRow(dataTag: "move within view, move 1 down")
5062 << 30 << 0.0 << 0.0
5063 << 1 << 10 << 1 << ListRange(2, 10);
5064 QTest::newRow(dataTag: "move within view, move 1 down, move first item")
5065 << 30 << 0.0 << 0.0
5066 << 0 << 10 << 1 << ListRange(1, 10);
5067 QTest::newRow(dataTag: "move within view, move 1 down, move first item, contentY not 0")
5068 << 30 << 2.0 << 0.0
5069 << 0+6 << 10+6 << 1 << ListRange(1+6, 10+6);
5070 QTest::newRow(dataTag: "move within view, move 1 down, to last item")
5071 << 30 << 0.0 << 0.0
5072 << 10 << 17 << 1 << ListRange(11, 17);
5073 QTest::newRow(dataTag: "move within view, move first->last")
5074 << 30 << 0.0 << 0.0
5075 << 0 << 17 << 1 << ListRange(1, 17);
5076
5077 QTest::newRow(dataTag: "move within view, move multiple down")
5078 << 30 << 0.0 << 0.0
5079 << 1 << 10 << 3 << ListRange(4, 12);
5080 QTest::newRow(dataTag: "move within view, move multiple down, move first item")
5081 << 30 << 0.0 << 0.0
5082 << 0 << 10 << 3 << ListRange(3, 12);
5083 QTest::newRow(dataTag: "move within view, move multiple down, move first item, contentY not 0")
5084 << 30 << 1.0 << 0.0
5085 << 0+3 << 10+3 << 3 << ListRange(3+3, 12+3);
5086 QTest::newRow(dataTag: "move within view, move multiple down, displace last item")
5087 << 30 << 0.0 << 0.0
5088 << 5 << 15 << 3 << ListRange(8, 17);
5089 QTest::newRow(dataTag: "move within view, move multiple down, move first->last")
5090 << 30 << 0.0 << 0.0
5091 << 0 << 15 << 3 << ListRange(3, 17);
5092
5093 QTest::newRow(dataTag: "move within view, move 1 up")
5094 << 30 << 0.0 << 0.0
5095 << 10 << 1 << 1 << ListRange(1, 9);
5096 QTest::newRow(dataTag: "move within view, move 1 up, move to first index")
5097 << 30 << 0.0 << 0.0
5098 << 10 << 0 << 1 << ListRange(0, 9);
5099 QTest::newRow(dataTag: "move within view, move 1 up, move to first index, contentY not 0")
5100 << 30 << 2.0 << 0.0
5101 << 10+6 << 0+6 << 1 << ListRange(0+6, 9+6);
5102 QTest::newRow(dataTag: "move within view, move 1 up, move to first index, contentY not on item border")
5103 << 30 << 1.5 << 0.0
5104 << 10+3 << 0+3 << 1 << ListRange(0+3, 9+3);
5105 QTest::newRow(dataTag: "move within view, move 1 up, move last item")
5106 << 30 << 0.0 << 0.0
5107 << 17 << 10 << 1 << ListRange(10, 16);
5108 QTest::newRow(dataTag: "move within view, move 1 up, move last->first")
5109 << 30 << 0.0 << 0.0
5110 << 17 << 0 << 1 << ListRange(0, 16);
5111
5112 QTest::newRow(dataTag: "move within view, move multiple up")
5113 << 30 << 0.0 << 0.0
5114 << 10 << 1 << 3 << ListRange(1, 9);
5115 QTest::newRow(dataTag: "move within view, move multiple (> 1 row) up")
5116 << 30 << 0.0 << 0.0
5117 << 10 << 1 << 5 << ListRange(1, 9);
5118 QTest::newRow(dataTag: "move within view, move multiple up, move to first index")
5119 << 30 << 0.0 << 0.0
5120 << 10 << 0 << 3 << ListRange(0, 9);
5121 QTest::newRow(dataTag: "move within view, move multiple up, move to first index, contentY not 0")
5122 << 30 << 1.0 << 0.0
5123 << 10+3 << 0+3 << 3 << ListRange(0+3, 9+3);
5124 QTest::newRow(dataTag: "move within view, move multiple up (> 1 row), move to first index, contentY not on border")
5125 << 30 << 1.5 << 0.0
5126 << 10+3 << 0+3 << 5 << ListRange(0+3, 9+3);
5127 QTest::newRow(dataTag: "move within view, move multiple up, move last item")
5128 << 30 << 0.0 << 0.0
5129 << 15 << 5 << 3 << ListRange(5, 14);
5130 QTest::newRow(dataTag: "move within view, move multiple up, move last->first")
5131 << 30 << 0.0 << 0.0
5132 << 15 << 0 << 3 << ListRange(0, 14);
5133
5134 QTest::newRow(dataTag: "move from below view, move 1 up")
5135 << 30 << 0.0 << 0.0
5136 << 20 << 5 << 1 << ListRange(5, 17);
5137 QTest::newRow(dataTag: "move from below view, move 1 up, move to top")
5138 << 30 << 0.0 << 0.0
5139 << 20 << 0 << 1 << ListRange(0, 17);
5140 QTest::newRow(dataTag: "move from below view, move 1 up, move to top, contentY not 0")
5141 << 30 << 1.0 << 0.0
5142 << 25 << 3 << 1 << ListRange(0+3, 17+3);
5143 QTest::newRow(dataTag: "move from below view, move multiple (> 1 row) up")
5144 << 30 << 0.0 << 0.0
5145 << 20 << 5 << 5 << ListRange(5, 17);
5146 QTest::newRow(dataTag: "move from below view, move multiple up, move to top")
5147 << 30 << 0.0 << 0.0
5148 << 20 << 0 << 3 << ListRange(0, 17);
5149 QTest::newRow(dataTag: "move from below view, move multiple up, move to top, contentY not 0")
5150 << 30 << 1.0 << 0.0
5151 << 25 << 3 << 3 << ListRange(0+3, 17+3);
5152
5153 QTest::newRow(dataTag: "move from below view, move 1 up, move to bottom")
5154 << 30 << 0.0 << 0.0
5155 << 20 << 17 << 1 << ListRange(17, 17);
5156 QTest::newRow(dataTag: "move from below view, move 1 up, move to bottom, contentY not 0")
5157 << 30 << 1.0 << 0.0
5158 << 25 << 17+3 << 1 << ListRange(17+3, 17+3);
5159 QTest::newRow(dataTag: "move from below view, move multiple up, move to bottom")
5160 << 30 << 0.0 << 0.0
5161 << 20 << 17 << 3 << ListRange(17, 17);
5162 QTest::newRow(dataTag: "move from below view, move multiple up, move to bottom, contentY not 0")
5163 << 30 << 1.0 << 0.0
5164 << 25 << 17+3 << 3 << ListRange(17+3, 17+3);
5165}
5166
5167void tst_QQuickGridView::removeTransitions()
5168{
5169 QFETCH(int, initialItemCount);
5170 QFETCH(bool, shouldAnimateTargets);
5171 QFETCH(qreal, contentYRowOffset);
5172 QFETCH(int, removalIndex);
5173 QFETCH(int, removalCount);
5174 QFETCH(ListRange, expectedDisplacedIndexes);
5175
5176 // added items should end here
5177 QPointF targetItems_transitionTo(-50, -50);
5178
5179 // displaced items should pass through this points
5180 QPointF displacedItems_transitionVia(100, 100);
5181
5182 QaimModel model;
5183 for (int i = 0; i < initialItemCount; i++)
5184 model.addItem(name: "Original item" + QString::number(i), number: "");
5185 QaimModel model_targetItems_transitionTo;
5186 QaimModel model_displacedItems_transitionVia;
5187
5188 QQuickView *window = getView();
5189 QQmlContext *ctxt = window->rootContext();
5190 ctxt->setContextProperty("testModel", &model);
5191 ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
5192 ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
5193 ctxt->setContextProperty("targetItems_transitionTo", targetItems_transitionTo);
5194 ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
5195 window->setSource(testFileUrl(fileName: "removeTransitions.qml"));
5196 window->show();
5197
5198 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
5199 QTRY_VERIFY(gridview != nullptr);
5200 QQuickItem *contentItem = gridview->contentItem();
5201 QVERIFY(contentItem != nullptr);
5202 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5203
5204 if (contentYRowOffset != 0) {
5205 gridview->setContentY(contentYRowOffset * 60.0);
5206 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5207 }
5208
5209 QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
5210
5211 // only target items that are visible should be animated
5212 QList<QPair<QString, QString> > expectedTargetData;
5213 QList<int> targetIndexes;
5214 if (shouldAnimateTargets) {
5215 for (int i=removalIndex; i<removalIndex+removalCount; i++) {
5216 int firstVisibleIndex = (gridview->contentY() / 60.0)*3;
5217 int lastVisibleIndex = (qCeil(v: (gridview->contentY() + gridview->height()) / 60.0)*3) - 1;
5218 if (i >= firstVisibleIndex && i <= lastVisibleIndex) {
5219 expectedTargetData << qMakePair(x: model.name(index: i), y: model.number(index: i));
5220 targetIndexes << i;
5221 }
5222 }
5223 QVERIFY(expectedTargetData.count() > 0);
5224 }
5225
5226 // calculate targetItems and expectedTargets before model changes
5227 QList<QQuickItem *> targetItems = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper", indexes: targetIndexes);
5228 QVariantMap expectedTargets;
5229 for (int i=0; i<targetIndexes.count(); i++)
5230 expectedTargets[model.name(index: targetIndexes[i])] = targetIndexes[i];
5231
5232 // start animation
5233 model.removeItems(index: removalIndex, count: removalCount);
5234 gridview->forceLayout();
5235 QTRY_COMPARE(model.count(), gridview->count());
5236
5237 if (shouldAnimateTargets || expectedDisplacedIndexes.isValid()) {
5238 QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
5239 QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
5240 expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
5241
5242 // check the target and displaced items were animated
5243 model_targetItems_transitionTo.matchAgainst(other: expectedTargetData, error1: "wasn't animated to target 'to' pos", error2: "shouldn't have been animated to target 'to' pos");
5244 model_displacedItems_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with displaced anim", error2: "shouldn't have been animated with displaced anim");
5245
5246 // check attached properties
5247 QCOMPARE(gridview->property("targetTrans_items").toMap(), expectedTargets);
5248 matchIndexLists(indexLists: gridview->property(name: "targetTrans_targetIndexes").toList(), expectedIndexes: targetIndexes);
5249 matchItemLists(itemLists: gridview->property(name: "targetTrans_targetItems").toList(), expectedItems: targetItems);
5250 if (expectedDisplacedIndexes.isValid()) {
5251 // adjust expectedDisplacedIndexes to their final values after the move
5252 QList<int> displacedIndexes = adjustIndexesForRemoveDisplaced(indexes: expectedDisplacedIndexes.indexes, index: removalIndex, count: removalCount);
5253 matchItemsAndIndexes(items: gridview->property(name: "displacedTrans_items").toMap(), model, expectedIndexes: displacedIndexes);
5254 matchIndexLists(indexLists: gridview->property(name: "displacedTrans_targetIndexes").toList(), expectedIndexes: targetIndexes);
5255 matchItemLists(itemLists: gridview->property(name: "displacedTrans_targetItems").toList(), expectedItems: targetItems);
5256 }
5257 } else {
5258 QTRY_COMPARE(model_targetItems_transitionTo.count(), 0);
5259 QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
5260 }
5261
5262 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
5263 int itemCount = items.count();
5264 int firstVisibleIndex = -1;
5265 for (int i=0; i<items.count(); i++) {
5266 QQmlExpression e(qmlContext(items[i]), items[i], "index");
5267 int index = e.evaluate().toInt();
5268 if (firstVisibleIndex < 0 && items[i]->y() >= gridview->contentY())
5269 firstVisibleIndex = index;
5270 else if (index < 0)
5271 itemCount--; // exclude deleted items
5272 }
5273 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
5274
5275 // verify all items moved to the correct final positions
5276 for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
5277 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5278 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5279 QCOMPARE(item->x(), (i%3)*80.0);
5280 QCOMPARE(item->y(), gridview->contentY() + ((i-firstVisibleIndex)/3) * 60.0);
5281 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
5282 QVERIFY(name != nullptr);
5283 QTRY_COMPARE(name->text(), model.name(i));
5284 }
5285
5286 releaseView(view: window);
5287}
5288
5289void tst_QQuickGridView::removeTransitions_data()
5290{
5291 QTest::addColumn<int>(name: "initialItemCount");
5292 QTest::addColumn<qreal>(name: "contentYRowOffset");
5293 QTest::addColumn<bool>(name: "shouldAnimateTargets");
5294 QTest::addColumn<int>(name: "removalIndex");
5295 QTest::addColumn<int>(name: "removalCount");
5296 QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes");
5297
5298 // All items that are visible following the remove operation should be animated.
5299 // Remove targets that are outside of the view should not be animated.
5300
5301 // For a GridView, removing any number of items other than a full row before the start
5302 // should displace all items in the view
5303 QTest::newRow(dataTag: "remove 1 before start")
5304 << 30 << 2.0 << false
5305 << 2 << 1 << ListRange(6, 24); // 6-24 are displaced
5306 QTest::newRow(dataTag: "remove 1 row, before start")
5307 << 30 << 2.0 << false
5308 << 3 << 3 << ListRange();
5309 QTest::newRow(dataTag: "remove between 1-2 rows, before start")
5310 << 30 << 2.0 << false
5311 << 0 << 5 << ListRange(6, 25);
5312 QTest::newRow(dataTag: "remove 2 rows, before start")
5313 << 30 << 2.0 << false
5314 << 0 << 6 << ListRange();
5315 QTest::newRow(dataTag: "remove mix of before and after start")
5316 << 30 << 1.0 << true
5317 << 2 << 3 << ListRange(5, 23); // 5-23 are displaced into view
5318
5319
5320 QTest::newRow(dataTag: "remove 1 from start")
5321 << 30 << 0.0 << true
5322 << 0 << 1 << ListRange(1, 18); // 1-18 are displaced into view
5323 QTest::newRow(dataTag: "remove multiple from start")
5324 << 30 << 0.0 << true
5325 << 0 << 3 << ListRange(3, 20); // 3-18 are displaced into view
5326 QTest::newRow(dataTag: "remove 1 from start, content y not 0")
5327 << 30 << 1.0 << true
5328 << 3 << 1 << ListRange(1 + 3, 18 + 3);
5329 QTest::newRow(dataTag: "remove multiple from start, content y not 0")
5330 << 30 << 1.0 << true
5331 << 3 << 3 << ListRange(3 + 3, 20 + 3);
5332
5333
5334 QTest::newRow(dataTag: "remove 1 from middle")
5335 << 30 << 0.0 << true
5336 << 5 << 1 << ListRange(6, 18);
5337 QTest::newRow(dataTag: "remove multiple from middle")
5338 << 30 << 0.0 << true
5339 << 5 << 3 << ListRange(8, 20);
5340
5341
5342 QTest::newRow(dataTag: "remove 1 from bottom")
5343 << 30 << 0.0 << true
5344 << 17 << 1 << ListRange(18, 18);
5345 QTest::newRow(dataTag: "remove multiple (1 row) from bottom")
5346 << 30 << 0.0 << true
5347 << 15 << 3 << ListRange(18, 20);
5348 QTest::newRow(dataTag: "remove multiple (> 1 row) from bottom")
5349 << 30 << 0.0 << true
5350 << 15 << 5 << ListRange(20, 22);
5351 QTest::newRow(dataTag: "remove 1 from bottom, content y not 0")
5352 << 30 << 1.0 << true
5353 << 17 + 3 << 1 << ListRange(18 + 3, 18 + 3);
5354 QTest::newRow(dataTag: "remove multiple (1 row) from bottom, content y not 0")
5355 << 30 << 1.0 << true
5356 << 15 + 3 << 3 << ListRange(18 + 3, 20 + 3);
5357
5358
5359 QTest::newRow(dataTag: "remove 1 after end")
5360 << 30 << 0.0 << false
5361 << 18 << 1 << ListRange();
5362 QTest::newRow(dataTag: "remove multiple after end")
5363 << 30 << 0.0 << false
5364 << 18 << 3 << ListRange();
5365}
5366
5367void tst_QQuickGridView::displacedTransitions()
5368{
5369 QFETCH(bool, useDisplaced);
5370 QFETCH(bool, displacedEnabled);
5371 QFETCH(bool, useAddDisplaced);
5372 QFETCH(bool, addDisplacedEnabled);
5373 QFETCH(bool, useMoveDisplaced);
5374 QFETCH(bool, moveDisplacedEnabled);
5375 QFETCH(bool, useRemoveDisplaced);
5376 QFETCH(bool, removeDisplacedEnabled);
5377 QFETCH(ListChange, change);
5378 QFETCH(ListRange, expectedDisplacedIndexes);
5379
5380 QaimModel model;
5381 for (int i = 0; i < 30; i++)
5382 model.addItem(name: "Original item" + QString::number(i), number: "");
5383 QaimModel model_displaced_transitionVia;
5384 QaimModel model_addDisplaced_transitionVia;
5385 QaimModel model_moveDisplaced_transitionVia;
5386 QaimModel model_removeDisplaced_transitionVia;
5387
5388 QPointF displaced_transitionVia(-50, -100);
5389 QPointF addDisplaced_transitionVia(-150, 100);
5390 QPointF moveDisplaced_transitionVia(50, -100);
5391 QPointF removeDisplaced_transitionVia(150, 100);
5392
5393 QQuickView *window = getView();
5394 QQmlContext *ctxt = window->rootContext();
5395 ctxt->setContextProperty("testModel", &model);
5396 ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
5397 ctxt->setContextProperty("model_addDisplaced_transitionVia", &model_addDisplaced_transitionVia);
5398 ctxt->setContextProperty("model_moveDisplaced_transitionVia", &model_moveDisplaced_transitionVia);
5399 ctxt->setContextProperty("model_removeDisplaced_transitionVia", &model_removeDisplaced_transitionVia);
5400 ctxt->setContextProperty("displaced_transitionVia", displaced_transitionVia);
5401 ctxt->setContextProperty("addDisplaced_transitionVia", addDisplaced_transitionVia);
5402 ctxt->setContextProperty("moveDisplaced_transitionVia", moveDisplaced_transitionVia);
5403 ctxt->setContextProperty("removeDisplaced_transitionVia", removeDisplaced_transitionVia);
5404 ctxt->setContextProperty("useDisplaced", useDisplaced);
5405 ctxt->setContextProperty("displacedEnabled", displacedEnabled);
5406 ctxt->setContextProperty("useAddDisplaced", useAddDisplaced);
5407 ctxt->setContextProperty("addDisplacedEnabled", addDisplacedEnabled);
5408 ctxt->setContextProperty("useMoveDisplaced", useMoveDisplaced);
5409 ctxt->setContextProperty("moveDisplacedEnabled", moveDisplacedEnabled);
5410 ctxt->setContextProperty("useRemoveDisplaced", useRemoveDisplaced);
5411 ctxt->setContextProperty("removeDisplacedEnabled", removeDisplacedEnabled);
5412 window->setSource(testFileUrl(fileName: "displacedTransitions.qml"));
5413 window->show();
5414 qApp->processEvents();
5415
5416 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
5417 QTRY_VERIFY(gridview != nullptr);
5418 QQuickItem *contentItem = gridview->contentItem();
5419 QVERIFY(contentItem != nullptr);
5420 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5421
5422 QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
5423 gridview->setProperty(name: "displaceTransitionsDone", value: false);
5424
5425 switch (change.type) {
5426 case ListChange::Inserted:
5427 {
5428 QList<QPair<QString, QString> > targetItemData;
5429 for (int i=change.index; i<change.index + change.count; ++i)
5430 targetItemData << qMakePair(x: QString("new item %1").arg(a: i), y: QString::number(i));
5431 model.insertItems(index: change.index, items: targetItemData);
5432 QTRY_COMPARE(model.count(), gridview->count());
5433 break;
5434 }
5435 case ListChange::Removed:
5436 model.removeItems(index: change.index, count: change.count);
5437 QTRY_COMPARE(model.count(), gridview->count());
5438 break;
5439 case ListChange::Moved:
5440 model.moveItems(from: change.index, to: change.to, count: change.count);
5441 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5442 break;
5443 case ListChange::SetCurrent:
5444 case ListChange::SetContentY:
5445 case ListChange::Polish:
5446 break;
5447 }
5448 gridview->forceLayout();
5449
5450 QVariantList resultTargetIndexes = gridview->property(name: "displacedTargetIndexes").toList();
5451 QVariantList resultTargetItems = gridview->property(name: "displacedTargetItems").toList();
5452
5453 if ((useDisplaced && displacedEnabled)
5454 || (useAddDisplaced && addDisplacedEnabled)
5455 || (useMoveDisplaced && moveDisplacedEnabled)
5456 || (useRemoveDisplaced && removeDisplacedEnabled)) {
5457 QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
5458
5459 // check the correct number of target items and indexes were received
5460 QCOMPARE(resultTargetIndexes.count(), expectedDisplacedIndexes.count());
5461 for (int i=0; i<resultTargetIndexes.count(); i++)
5462 QCOMPARE(resultTargetIndexes[i].value<QList<int> >().count(), change.count);
5463 QCOMPARE(resultTargetItems.count(), expectedDisplacedIndexes.count());
5464 for (int i=0; i<resultTargetItems.count(); i++)
5465 QCOMPARE(resultTargetItems[i].toList().count(), change.count);
5466 } else {
5467 QCOMPARE(resultTargetIndexes.count(), 0);
5468 QCOMPARE(resultTargetItems.count(), 0);
5469 }
5470
5471 if (change.type == ListChange::Inserted && useAddDisplaced && addDisplacedEnabled)
5472 model_addDisplaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with add displaced", error2: "shouldn't have been animated with add displaced");
5473 else
5474 QCOMPARE(model_addDisplaced_transitionVia.count(), 0);
5475 if (change.type == ListChange::Moved && useMoveDisplaced && moveDisplacedEnabled)
5476 model_moveDisplaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with move displaced", error2: "shouldn't have been animated with move displaced");
5477 else
5478 QCOMPARE(model_moveDisplaced_transitionVia.count(), 0);
5479 if (change.type == ListChange::Removed && useRemoveDisplaced && removeDisplacedEnabled)
5480 model_removeDisplaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with remove displaced", error2: "shouldn't have been animated with remove displaced");
5481 else
5482 QCOMPARE(model_removeDisplaced_transitionVia.count(), 0);
5483
5484 if (useDisplaced && displacedEnabled
5485 && ( (change.type == ListChange::Inserted && (!useAddDisplaced || !addDisplacedEnabled))
5486 || (change.type == ListChange::Moved && (!useMoveDisplaced || !moveDisplacedEnabled))
5487 || (change.type == ListChange::Removed && (!useRemoveDisplaced || !removeDisplacedEnabled))) ) {
5488 model_displaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with generic displaced", error2: "shouldn't have been animated with generic displaced");
5489 } else {
5490 QCOMPARE(model_displaced_transitionVia.count(), 0);
5491 }
5492
5493 // verify all items moved to the correct final positions
5494 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
5495 for (int i=0; i < model.count() && i < items.count(); ++i) {
5496 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5497 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5498 QCOMPARE(item->x(), (i%3)*80.0);
5499 QCOMPARE(item->y(), (i/3)*60.0);
5500 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
5501 QVERIFY(name != nullptr);
5502 QTRY_COMPARE(name->text(), model.name(i));
5503 }
5504
5505 releaseView(view: window);
5506}
5507
5508void tst_QQuickGridView::displacedTransitions_data()
5509{
5510 QTest::addColumn<bool>(name: "useDisplaced");
5511 QTest::addColumn<bool>(name: "displacedEnabled");
5512 QTest::addColumn<bool>(name: "useAddDisplaced");
5513 QTest::addColumn<bool>(name: "addDisplacedEnabled");
5514 QTest::addColumn<bool>(name: "useMoveDisplaced");
5515 QTest::addColumn<bool>(name: "moveDisplacedEnabled");
5516 QTest::addColumn<bool>(name: "useRemoveDisplaced");
5517 QTest::addColumn<bool>(name: "removeDisplacedEnabled");
5518 QTest::addColumn<ListChange>(name: "change");
5519 QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes");
5520
5521 QTest::newRow(dataTag: "no displaced transitions at all")
5522 << false << false
5523 << false << false
5524 << false << false
5525 << false << false
5526 << ListChange::insert(index: 0, count: 1) << ListRange(0, 17);
5527
5528 QTest::newRow(dataTag: "just displaced")
5529 << true << true
5530 << false << false
5531 << false << false
5532 << false << false
5533 << ListChange::insert(index: 0, count: 1) << ListRange(0, 17);
5534
5535 QTest::newRow(dataTag: "just displaced (not enabled)")
5536 << true << false
5537 << false << false
5538 << false << false
5539 << false << false
5540 << ListChange::insert(index: 0, count: 1) << ListRange(0, 17);
5541
5542 QTest::newRow(dataTag: "displaced + addDisplaced")
5543 << true << true
5544 << true << true
5545 << false << false
5546 << false << false
5547 << ListChange::insert(index: 0, count: 1) << ListRange(0, 17);
5548
5549 QTest::newRow(dataTag: "displaced + addDisplaced (not enabled)")
5550 << true << true
5551 << true << false
5552 << false << false
5553 << false << false
5554 << ListChange::insert(index: 0, count: 1) << ListRange(0, 17);
5555
5556 QTest::newRow(dataTag: "displaced + moveDisplaced")
5557 << true << true
5558 << false << false
5559 << true << true
5560 << false << false
5561 << ListChange::move(index: 0, to: 10, count: 1) << ListRange(1, 10);
5562
5563 QTest::newRow(dataTag: "displaced + moveDisplaced (not enabled)")
5564 << true << true
5565 << false << false
5566 << true << false
5567 << false << false
5568 << ListChange::move(index: 0, to: 10, count: 1) << ListRange(1, 10);
5569
5570 QTest::newRow(dataTag: "displaced + removeDisplaced")
5571 << true << true
5572 << false << false
5573 << false << false
5574 << true << true
5575 << ListChange::remove(index: 0, count: 1) << ListRange(1, 18);
5576
5577 QTest::newRow(dataTag: "displaced + removeDisplaced (not enabled)")
5578 << true << true
5579 << false << false
5580 << false << false
5581 << true << false
5582 << ListChange::remove(index: 0, count: 1) << ListRange(1, 18);
5583
5584
5585 QTest::newRow(dataTag: "displaced + add, should use generic displaced for a remove")
5586 << true << true
5587 << true << true
5588 << false << false
5589 << true << false
5590 << ListChange::remove(index: 0, count: 1) << ListRange(1, 18);
5591}
5592
5593void tst_QQuickGridView::multipleTransitions()
5594{
5595 // Tests that if you interrupt a transition in progress with another action that
5596 // cancels the previous transition, the resulting items are still placed correctly.
5597
5598 QFETCH(int, initialCount);
5599 QFETCH(qreal, contentY);
5600 QFETCH(QList<ListChange>, changes);
5601 QFETCH(bool, enableAddTransitions);
5602 QFETCH(bool, enableMoveTransitions);
5603 QFETCH(bool, enableRemoveTransitions);
5604 QFETCH(bool, rippleAddDisplaced);
5605
5606 // add transitions on the left, moves on the right
5607 QPointF addTargets_transitionFrom(-50, -50);
5608 QPointF addDisplaced_transitionFrom(-50, 50);
5609 QPointF moveTargets_transitionFrom(50, -50);
5610 QPointF moveDisplaced_transitionFrom(50, 50);
5611 QPointF removeTargets_transitionTo(-100, 300);
5612 QPointF removeDisplaced_transitionFrom(100, 300);
5613
5614 QaimModel model;
5615 for (int i = 0; i < initialCount; i++)
5616 model.addItem(name: "Original item" + QString::number(i), number: "");
5617
5618 QQuickView *window = getView();
5619 QQmlContext *ctxt = window->rootContext();
5620 ctxt->setContextProperty("testModel", &model);
5621 ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
5622 ctxt->setContextProperty("addDisplaced_transitionFrom", addDisplaced_transitionFrom);
5623 ctxt->setContextProperty("moveTargets_transitionFrom", moveTargets_transitionFrom);
5624 ctxt->setContextProperty("moveDisplaced_transitionFrom", moveDisplaced_transitionFrom);
5625 ctxt->setContextProperty("removeTargets_transitionTo", removeTargets_transitionTo);
5626 ctxt->setContextProperty("removeDisplaced_transitionFrom", removeDisplaced_transitionFrom);
5627 ctxt->setContextProperty("enableAddTransitions", enableAddTransitions);
5628 ctxt->setContextProperty("enableMoveTransitions", enableMoveTransitions);
5629 ctxt->setContextProperty("enableRemoveTransitions", enableRemoveTransitions);
5630 ctxt->setContextProperty("rippleAddDisplaced", rippleAddDisplaced);
5631 window->setSource(testFileUrl(fileName: "multipleTransitions.qml"));
5632 window->show();
5633 QVERIFY(QTest::qWaitForWindowExposed(window));
5634
5635 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
5636 QTRY_VERIFY(gridview != nullptr);
5637 QQuickItem *contentItem = gridview->contentItem();
5638 QVERIFY(contentItem != nullptr);
5639 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5640
5641 if (contentY != 0) {
5642 gridview->setContentY(contentY);
5643 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5644 }
5645
5646 int timeBetweenActions = window->rootObject()->property(name: "timeBetweenActions").toInt();
5647
5648 for (int i=0; i<changes.count(); i++) {
5649 switch (changes[i].type) {
5650 case ListChange::Inserted:
5651 {
5652 QList<QPair<QString, QString> > targetItems;
5653 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
5654 targetItems << qMakePair(x: QString("new item %1").arg(a: j), y: QString::number(j));
5655 model.insertItems(index: changes[i].index, items: targetItems);
5656 gridview->forceLayout();
5657 QTRY_COMPARE(model.count(), gridview->count());
5658 if (i == changes.count() - 1) {
5659 QTRY_VERIFY(!gridview->property("runningAddTargets").toBool());
5660 QTRY_VERIFY(!gridview->property("runningAddDisplaced").toBool());
5661 } else {
5662 QTest::qWait(ms: timeBetweenActions);
5663 }
5664 break;
5665 }
5666 case ListChange::Removed:
5667 model.removeItems(index: changes[i].index, count: changes[i].count);
5668 gridview->forceLayout();
5669 QTRY_COMPARE(model.count(), gridview->count());
5670 if (i == changes.count() - 1) {
5671 QTRY_VERIFY(!gridview->property("runningRemoveTargets").toBool());
5672 QTRY_VERIFY(!gridview->property("runningRemoveDisplaced").toBool());
5673 } else {
5674 QTest::qWait(ms: timeBetweenActions);
5675 }
5676 break;
5677 case ListChange::Moved:
5678 model.moveItems(from: changes[i].index, to: changes[i].to, count: changes[i].count);
5679 gridview->forceLayout();
5680 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5681 if (i == changes.count() - 1) {
5682 QTRY_VERIFY(!gridview->property("runningMoveTargets").toBool());
5683 QTRY_VERIFY(!gridview->property("runningMoveDisplaced").toBool());
5684 } else {
5685 QTest::qWait(ms: timeBetweenActions);
5686 }
5687 break;
5688 case ListChange::SetCurrent:
5689 gridview->setCurrentIndex(changes[i].index);
5690 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5691 gridview->forceLayout();
5692 break;
5693 case ListChange::SetContentY:
5694 gridview->setContentY(changes[i].pos);
5695 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5696 gridview->forceLayout();
5697 break;
5698 case ListChange::Polish:
5699 break;
5700 }
5701 }
5702
5703 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
5704 int firstVisibleIndex = -1;
5705 for (int i=0; i<items.count(); i++) {
5706 if (items[i]->y() >= contentY) {
5707 QQmlExpression e(qmlContext(items[i]), items[i], "index");
5708 firstVisibleIndex = e.evaluate().toInt();
5709 break;
5710 }
5711 }
5712 QTRY_COMPARE(gridview->count(), model.count());
5713 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
5714
5715 // verify all items moved to the correct final positions
5716 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper").count();
5717 for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
5718 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5719 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5720 QTRY_COMPARE(item->x(), (i%3)*80.0);
5721 QTRY_COMPARE(item->y(), (i/3)*60.0);
5722 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
5723 QVERIFY(name != nullptr);
5724 QTRY_COMPARE(name->text(), model.name(i));
5725 }
5726
5727 releaseView(view: window);
5728}
5729
5730void tst_QQuickGridView::multipleTransitions_data()
5731{
5732 QTest::addColumn<int>(name: "initialCount");
5733 QTest::addColumn<qreal>(name: "contentY");
5734 QTest::addColumn<QList<ListChange> >(name: "changes");
5735 QTest::addColumn<bool>(name: "enableAddTransitions");
5736 QTest::addColumn<bool>(name: "enableMoveTransitions");
5737 QTest::addColumn<bool>(name: "enableRemoveTransitions");
5738 QTest::addColumn<bool>(name: "rippleAddDisplaced");
5739
5740 // the added item and displaced items should move to final dest correctly
5741 QTest::newRow(dataTag: "add item, then move it immediately") << 10 << 0.0 << (QList<ListChange>()
5742 << ListChange::insert(index: 0, count: 1)
5743 << ListChange::move(index: 0, to: 3, count: 1)
5744 )
5745 << true << true << true << false;
5746
5747 // items affected by the add should change from move to add transition
5748 QTest::newRow(dataTag: "move, then insert item before the moved item") << 20 << 0.0 << (QList<ListChange>()
5749 << ListChange::move(index: 1, to: 10, count: 3)
5750 << ListChange::insert(index: 0, count: 1)
5751 )
5752 << true << true << true << false;
5753
5754 // items should be placed correctly if you trigger a transition then refill for that index
5755 QTest::newRow(dataTag: "add at 0, flick down, flick back to top and add at 0 again") << 20 << 0.0 << (QList<ListChange>()
5756 << ListChange::insert(index: 0, count: 1)
5757 << ListChange::setContentY(160.0)
5758 << ListChange::setContentY(0.0)
5759 << ListChange::insert(index: 0, count: 1)
5760 )
5761 << true << true << true << false;
5762
5763 QTest::newRow(dataTag: "insert then remove same index, with ripple effect on add displaced") << 20 << 0.0 << (QList<ListChange>()
5764 << ListChange::insert(index: 1, count: 1)
5765 << ListChange::remove(index: 1, count: 1)
5766 )
5767 << true << true << true << true;
5768
5769 // if item is removed while undergoing a displaced transition, all other items should end up at their correct positions,
5770 // even if a remove-displace transition is not present to re-animate them
5771 QTest::newRow(dataTag: "insert then remove, with remove disabled") << 20 << 0.0 << (QList<ListChange>()
5772 << ListChange::insert(index: 0, count: 1)
5773 << ListChange::remove(index: 2, count: 1)
5774 )
5775 << true << true << false << false;
5776
5777 // if last item is not flush with the edge of the view, it should still be refilled in correctly after a
5778 // remove has changed the position of where it will move to
5779 QTest::newRow(dataTag: "insert twice then remove, with remove disabled") << 20 << 0.0 << (QList<ListChange>()
5780 << ListChange::setContentY(-10.0)
5781 << ListChange::insert(index: 0, count: 1)
5782 << ListChange::insert(index: 0, count: 1)
5783 << ListChange::remove(index: 2, count: 1)
5784 )
5785 << true << true << false << false;
5786}
5787
5788void tst_QQuickGridView::multipleDisplaced()
5789{
5790 // multiple move() operations should only restart displace transitions for items that
5791 // moved from previously set positions, and not those that have moved from their current
5792 // item positions (which may e.g. still be changing from easing bounces in the last transition)
5793
5794 QaimModel model;
5795 for (int i = 0; i < 30; i++)
5796 model.addItem(name: "Original item" + QString::number(i), number: "");
5797
5798 QQuickView *window = createView();
5799 QQmlContext *ctxt = window->rootContext();
5800 ctxt->setContextProperty("testModel", &model);
5801 window->setSource(testFileUrl(fileName: "multipleDisplaced.qml"));
5802 window->show();
5803 QVERIFY(QTest::qWaitForWindowExposed(window));
5804
5805 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
5806 QTRY_VERIFY(gridview != nullptr);
5807 QQuickItem *contentItem = gridview->contentItem();
5808 QVERIFY(contentItem != nullptr);
5809 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5810
5811 model.moveItems(from: 12, to: 8, count: 1);
5812 QTest::qWait(ms: window->rootObject()->property(name: "duration").toInt() / 2);
5813 model.moveItems(from: 8, to: 3, count: 1);
5814 QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
5815
5816 QVariantMap transitionsStarted = gridview->property(name: "displaceTransitionsStarted").toMap();
5817 foreach (const QString &name, transitionsStarted.keys()) {
5818 QVERIFY2(transitionsStarted[name] == 1,
5819 QTest::toString(QString("%1 was displaced %2 times").arg(name).arg(transitionsStarted[name].toInt())));
5820 }
5821
5822 // verify all items moved to the correct final positions
5823 QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper");
5824 for (int i=0; i < model.count() && i < items.count(); ++i) {
5825 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5826 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5827 QTRY_COMPARE(item->x(), (i%3)*80.0);
5828 QTRY_COMPARE(item->y(), (i/3)*60.0);
5829 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName", index: i);
5830 QVERIFY(name != nullptr);
5831 QTRY_COMPARE(name->text(), model.name(i));
5832 }
5833
5834 delete window;
5835}
5836
5837void tst_QQuickGridView::regression_QTBUG_57225()
5838{
5839 QFETCH(int, initialCount);
5840 QFETCH(int, removeIndex);
5841 QFETCH(int, removeCount);
5842 QFETCH(int, expectedDisplaceTransitions);
5843
5844 // deleting all visible items should not cause a repositioning of said items.
5845
5846 QaimModel model;
5847 for (int i = 0; i < initialCount; i++)
5848 model.addItem(name: "Original item" + QString::number(i), number: "");
5849
5850 QQuickView *window = createView();
5851 QQmlContext *ctxt = window->rootContext();
5852 ctxt->setContextProperty("testModel", &model);
5853 window->setSource(testFileUrl(fileName: "qtbug57225.qml"));
5854 window->show();
5855 QVERIFY(QTest::qWaitForWindowExposed(window));
5856
5857 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
5858 QVERIFY(gridview != nullptr);
5859 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
5860
5861 model.removeItems(index: removeIndex, count: removeCount);
5862 QTRY_VERIFY(gridview->property("animationDone").toBool());
5863
5864 // verify that none of the removed items has moved to a negative position
5865 QPoint minimumPosition = gridview->property(name: "minimumPosition").toPoint();
5866 QVERIFY(minimumPosition.x() >= 0);
5867 QVERIFY(minimumPosition.y() >= 0);
5868
5869 // wait some more time to let the displaced transition happen
5870 QTest::qWait(ms: window->rootObject()->property(name: "duration").toInt());
5871 QTRY_VERIFY2(gridview->property("displaceTransitionsDone").toInt() >= expectedDisplaceTransitions,
5872 QByteArray::number(gridview->property("displaceTransitionsDone").toInt()).constData());
5873
5874 delete window;
5875}
5876
5877void tst_QQuickGridView::regression_QTBUG_57225_data()
5878{
5879 QTest::addColumn<int>(name: "initialCount");
5880 QTest::addColumn<int>(name: "removeIndex");
5881 QTest::addColumn<int>(name: "removeCount");
5882 QTest::addColumn<int>(name: "expectedDisplaceTransitions");
5883
5884 // no displace transitions should happen
5885 QTest::newRow(dataTag: "remove all visible items") <<
5886 20 << 0 << 8 << 0;
5887
5888 // check that the removal animation is performed
5889 QTest::newRow(dataTag: "remove items in between") <<
5890 20 << 1 << 2 << 3;
5891
5892 QTest::newRow(dataTag: "remove items in between - 2") <<
5893 20 << 2 << 3 << 1;
5894}
5895
5896void tst_QQuickGridView::cacheBuffer()
5897{
5898 QQuickView *window = createView();
5899
5900 QaimModel model;
5901 for (int i = 0; i < 90; i++)
5902 model.addItem(name: "Item" + QString::number(i), number: "");
5903
5904 window->rootContext()->setContextProperty("testModel", &model);
5905 window->setSource(testFileUrl(fileName: "gridview1.qml"));
5906 window->show();
5907 qApp->processEvents();
5908
5909 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
5910 QVERIFY(gridview != nullptr);
5911
5912 QQuickItem *contentItem = gridview->contentItem();
5913 QVERIFY(contentItem != nullptr);
5914 QVERIFY(gridview->delegate() != nullptr);
5915 QVERIFY(gridview->model() != 0);
5916
5917 // Confirm items positioned correctly
5918 int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper", visibleOnly: false).count();
5919 for (int i = 0; i < model.count() && i < itemCount; ++i) {
5920 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5921 QTRY_COMPARE(item->x(), (i%3)*80.0);
5922 QTRY_COMPARE(item->y(), (i/3)*60.0);
5923 }
5924
5925 QQmlIncubationController controller;
5926 window->engine()->setIncubationController(&controller);
5927
5928 window->rootObject()->setProperty(name: "cacheBuffer", value: 200);
5929 QTRY_COMPARE(gridview->cacheBuffer(), 200);
5930
5931 // items will be created one at a time
5932 for (int i = itemCount; i < qMin(a: itemCount+9,b: model.count()); ++i) {
5933 QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == nullptr);
5934 QQuickItem *item = nullptr;
5935 while (!item) {
5936 bool b = false;
5937 controller.incubateWhile(flag: &b);
5938 item = findItem<QQuickItem>(parent: gridview, objectName: "wrapper", index: i);
5939 }
5940 }
5941
5942 {
5943 bool b = true;
5944 controller.incubateWhile(flag: &b);
5945 }
5946
5947 int newItemCount = 0;
5948 newItemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper", visibleOnly: false).count();
5949
5950 // Confirm items positioned correctly
5951 for (int i = 0; i < model.count() && i < newItemCount; ++i) {
5952 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5953 QVERIFY(item);
5954 QTRY_COMPARE(item->x(), (i%3)*80.0);
5955 QTRY_COMPARE(item->y(), (i/3)*60.0);
5956 }
5957
5958 // move view and confirm items in view are visible immediately and outside are created async
5959 gridview->setContentY(300);
5960
5961 for (int i = 15; i < 34; ++i) { // 34 due to staggered item creation
5962 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
5963 QVERIFY(item);
5964 QTRY_COMPARE(item->x(), (i%3)*80.0);
5965 QTRY_COMPARE(item->y(), (i/3)*60.0);
5966 }
5967
5968 QVERIFY(findItem<QQuickItem>(gridview, "wrapper", 34) == nullptr);
5969
5970 // ensure buffered items are created
5971 for (int i = 34; i < qMin(a: 44,b: model.count()); ++i) {
5972 QQuickItem *item = nullptr;
5973 while (!item) {
5974 qGuiApp->processEvents(); // allow refill to happen
5975 bool b = false;
5976 controller.incubateWhile(flag: &b);
5977 item = findItem<QQuickItem>(parent: gridview, objectName: "wrapper", index: i);
5978 }
5979 }
5980
5981 {
5982 bool b = true;
5983 controller.incubateWhile(flag: &b);
5984 }
5985
5986 delete window;
5987}
5988
5989void tst_QQuickGridView::asynchronous()
5990{
5991 QQuickView *window = createView();
5992 window->show();
5993 QQmlIncubationController controller;
5994 window->engine()->setIncubationController(&controller);
5995
5996 window->setSource(testFileUrl(fileName: "asyncloader.qml"));
5997
5998 QQuickItem *rootObject = qobject_cast<QQuickItem*>(object: window->rootObject());
5999 QVERIFY(rootObject);
6000
6001 QQuickGridView *gridview = nullptr;
6002 while (!gridview) {
6003 bool b = false;
6004 controller.incubateWhile(flag: &b);
6005 gridview = rootObject->findChild<QQuickGridView*>(aName: "view");
6006 }
6007
6008 // items will be created one at a time
6009 for (int i = 0; i < 12; ++i) {
6010 QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == nullptr);
6011 QQuickItem *item = nullptr;
6012 while (!item) {
6013 bool b = false;
6014 controller.incubateWhile(flag: &b);
6015 item = findItem<QQuickItem>(parent: gridview, objectName: "wrapper", index: i);
6016 }
6017 }
6018
6019 {
6020 bool b = true;
6021 controller.incubateWhile(flag: &b);
6022 }
6023
6024 // verify positioning
6025 QQuickItem *contentItem = gridview->contentItem();
6026 for (int i = 0; i < 12; ++i) {
6027 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper", index: i);
6028 if (!item) qWarning() << "Item" << i << "not found";
6029 QCOMPARE(item->x(), qreal((i%3)*100));
6030 QCOMPARE(item->y(), qreal((i/3)*100));
6031 }
6032
6033 delete window;
6034}
6035
6036void tst_QQuickGridView::unrequestedVisibility()
6037{
6038 QaimModel model;
6039 for (int i = 0; i < 30; i++)
6040 model.addItem(name: "Item" + QString::number(i), number: QString::number(i));
6041
6042 QQuickView *window = new QQuickView(nullptr);
6043 window->setGeometry(posx: 0,posy: 0,w: 240,h: 320);
6044
6045 QQmlContext *ctxt = window->rootContext();
6046 ctxt->setContextProperty("testModel", &model);
6047 ctxt->setContextProperty("testWrap", QVariant(false));
6048
6049 window->setSource(testFileUrl(fileName: "unrequestedItems.qml"));
6050
6051 window->show();
6052
6053 qApp->processEvents();
6054
6055 QQuickGridView *leftview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "leftGrid");
6056 QTRY_VERIFY(leftview != nullptr);
6057
6058 QQuickGridView *rightview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "rightGrid");
6059 QTRY_VERIFY(rightview != nullptr);
6060
6061 QQuickItem *leftContent = leftview->contentItem();
6062 QTRY_VERIFY(leftContent != nullptr);
6063
6064 QQuickItem *rightContent = rightview->contentItem();
6065 QTRY_VERIFY(rightContent != nullptr);
6066
6067 rightview->setCurrentIndex(12);
6068
6069 QTRY_COMPARE(leftview->contentY(), 0.0);
6070 QTRY_COMPARE(rightview->contentY(), 240.0);
6071
6072 QQuickItem *item;
6073
6074 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6075 QCOMPARE(delegateVisible(item), true);
6076 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6077 QCOMPARE(delegateVisible(item), false);
6078
6079 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
6080 QCOMPARE(delegateVisible(item), false);
6081 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
6082 QCOMPARE(delegateVisible(item), true);
6083
6084 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 9));
6085 QCOMPARE(delegateVisible(item), true);
6086 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 10));
6087 QCOMPARE(delegateVisible(item), false);
6088 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
6089 QCOMPARE(delegateVisible(item), false);
6090 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
6091 QCOMPARE(delegateVisible(item), true);
6092
6093 rightview->setCurrentIndex(0);
6094
6095 QTRY_COMPARE(leftview->contentY(), 0.0);
6096 QTRY_COMPARE(rightview->contentY(), 0.0);
6097
6098 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6099 QCOMPARE(delegateVisible(item), true);
6100 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6101 QTRY_COMPARE(delegateVisible(item), true);
6102
6103 QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 11));
6104 QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 11));
6105
6106 leftview->setCurrentIndex(12);
6107
6108 QTRY_COMPARE(leftview->contentY(), 240.0);
6109 QTRY_COMPARE(rightview->contentY(), 0.0);
6110
6111 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6112 QTRY_COMPARE(delegateVisible(item), false);
6113 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6114 QCOMPARE(delegateVisible(item), true);
6115
6116 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
6117 QCOMPARE(delegateVisible(item), true);
6118 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
6119 QCOMPARE(delegateVisible(item), false);
6120
6121 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6122 QCOMPARE(delegateVisible(item), false);
6123 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6124 QCOMPARE(delegateVisible(item), true);
6125 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6126 QCOMPARE(delegateVisible(item), true);
6127 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6128 QCOMPARE(delegateVisible(item), false);
6129
6130 // move a non-visible item into view
6131 model.moveItems(from: 10, to: 9, count: 1);
6132 QVERIFY(QQuickTest::qWaitForItemPolished(leftview));
6133
6134 QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6135 QCOMPARE(delegateVisible(item), false);
6136 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6137 QCOMPARE(delegateVisible(item), true);
6138
6139 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
6140 QCOMPARE(delegateVisible(item), true);
6141 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
6142 QCOMPARE(delegateVisible(item), false);
6143
6144 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6145 QCOMPARE(delegateVisible(item), false);
6146 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6147 QCOMPARE(delegateVisible(item), true);
6148 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6149 QCOMPARE(delegateVisible(item), true);
6150 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6151 QCOMPARE(delegateVisible(item), false);
6152
6153 // move a visible item out of view
6154 model.moveItems(from: 5, to: 3, count: 1);
6155 QVERIFY(QQuickTest::qWaitForItemPolished(leftview));
6156
6157 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6158 QCOMPARE(delegateVisible(item), false);
6159 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6160 QCOMPARE(delegateVisible(item), true);
6161 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6162 QCOMPARE(delegateVisible(item), true);
6163 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6164 QCOMPARE(delegateVisible(item), false);
6165
6166 // move a non-visible item into view
6167 model.moveItems(from: 3, to: 5, count: 1);
6168 QVERIFY(QQuickTest::qWaitForItemPolished(leftview));
6169
6170 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6171 QCOMPARE(delegateVisible(item), false);
6172 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6173 QCOMPARE(delegateVisible(item), true);
6174 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6175 QCOMPARE(delegateVisible(item), true);
6176 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6177 QCOMPARE(delegateVisible(item), false);
6178
6179 // move a visible item out of view
6180 model.moveItems(from: 9, to: 10, count: 1);
6181 QVERIFY(QQuickTest::qWaitForItemPolished(leftview));
6182
6183 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6184 QCOMPARE(delegateVisible(item), false);
6185 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6186 QCOMPARE(delegateVisible(item), true);
6187 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6188 QCOMPARE(delegateVisible(item), true);
6189 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6190 QCOMPARE(delegateVisible(item), false);
6191
6192 // move a non-visible item into view
6193 model.moveItems(from: 10, to: 9, count: 1);
6194 QVERIFY(QQuickTest::qWaitForItemPolished(leftview));
6195
6196 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6197 QCOMPARE(delegateVisible(item), false);
6198 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6199 QCOMPARE(delegateVisible(item), true);
6200 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6201 QCOMPARE(delegateVisible(item), true);
6202 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6203 QCOMPARE(delegateVisible(item), false);
6204
6205 delete window;
6206}
6207
6208
6209void tst_QQuickGridView::inserted_leftToRight_RtL_TtB()
6210{
6211 inserted_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::RightToLeft);
6212}
6213
6214void tst_QQuickGridView::inserted_leftToRight_RtL_TtB_data()
6215{
6216 inserted_defaultLayout_data();
6217}
6218
6219void tst_QQuickGridView::inserted_topToBottom_LtR_TtB()
6220{
6221 inserted_defaultLayout(flow: QQuickGridView::FlowTopToBottom);
6222}
6223
6224void tst_QQuickGridView::inserted_topToBottom_LtR_TtB_data()
6225{
6226 inserted_defaultLayout_data();
6227}
6228
6229void tst_QQuickGridView::inserted_topToBottom_RtL_TtB()
6230{
6231 inserted_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::RightToLeft);
6232}
6233
6234void tst_QQuickGridView::inserted_topToBottom_RtL_TtB_data()
6235{
6236 inserted_defaultLayout_data();
6237}
6238
6239void tst_QQuickGridView::inserted_leftToRight_LtR_BtT()
6240{
6241 inserted_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::LeftToRight, verticalLayout: QQuickItemView::BottomToTop);
6242}
6243
6244void tst_QQuickGridView::inserted_leftToRight_LtR_BtT_data()
6245{
6246 inserted_defaultLayout_data();
6247}
6248
6249void tst_QQuickGridView::inserted_leftToRight_RtL_BtT()
6250{
6251 inserted_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::RightToLeft, verticalLayout: QQuickItemView::BottomToTop);
6252}
6253
6254void tst_QQuickGridView::inserted_leftToRight_RtL_BtT_data()
6255{
6256 inserted_defaultLayout_data();
6257}
6258
6259void tst_QQuickGridView::inserted_topToBottom_LtR_BtT()
6260{
6261 inserted_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::LeftToRight, verticalLayout: QQuickItemView::BottomToTop);
6262}
6263
6264void tst_QQuickGridView::inserted_topToBottom_LtR_BtT_data()
6265{
6266 inserted_defaultLayout_data();
6267}
6268
6269void tst_QQuickGridView::inserted_topToBottom_RtL_BtT()
6270{
6271 inserted_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::RightToLeft, verticalLayout: QQuickItemView::BottomToTop);
6272}
6273
6274void tst_QQuickGridView::inserted_topToBottom_RtL_BtT_data()
6275{
6276 inserted_defaultLayout_data();
6277}
6278
6279
6280void tst_QQuickGridView::removed_leftToRight_RtL_TtB()
6281{
6282 removed_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::RightToLeft);
6283}
6284
6285void tst_QQuickGridView::removed_leftToRight_RtL_TtB_data()
6286{
6287 removed_defaultLayout_data();
6288}
6289
6290void tst_QQuickGridView::removed_topToBottom_LtR_TtB()
6291{
6292 removed_defaultLayout(flow: QQuickGridView::FlowTopToBottom);
6293}
6294
6295void tst_QQuickGridView::removed_topToBottom_LtR_TtB_data()
6296{
6297 removed_defaultLayout_data();
6298}
6299
6300void tst_QQuickGridView::removed_topToBottom_RtL_TtB()
6301{
6302 removed_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::RightToLeft);
6303}
6304
6305void tst_QQuickGridView::removed_topToBottom_RtL_TtB_data()
6306{
6307 removed_defaultLayout_data();
6308}
6309
6310void tst_QQuickGridView::removed_leftToRight_LtR_BtT()
6311{
6312 removed_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::LeftToRight, verticalLayout: QQuickItemView::BottomToTop);
6313}
6314
6315void tst_QQuickGridView::removed_leftToRight_LtR_BtT_data()
6316{
6317 removed_defaultLayout_data();
6318}
6319
6320void tst_QQuickGridView::removed_leftToRight_RtL_BtT()
6321{
6322 removed_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::RightToLeft, verticalLayout: QQuickItemView::BottomToTop);
6323}
6324
6325void tst_QQuickGridView::removed_leftToRight_RtL_BtT_data()
6326{
6327 removed_defaultLayout_data();
6328}
6329
6330void tst_QQuickGridView::removed_topToBottom_LtR_BtT()
6331{
6332 removed_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::LeftToRight, verticalLayout: QQuickItemView::BottomToTop);
6333}
6334
6335void tst_QQuickGridView::removed_topToBottom_LtR_BtT_data()
6336{
6337 removed_defaultLayout_data();
6338}
6339
6340void tst_QQuickGridView::removed_topToBottom_RtL_BtT()
6341{
6342 removed_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::RightToLeft, verticalLayout: QQuickItemView::BottomToTop);
6343}
6344
6345void tst_QQuickGridView::removed_topToBottom_RtL_BtT_data()
6346{
6347 removed_defaultLayout_data();
6348}
6349
6350
6351void tst_QQuickGridView::moved_leftToRight_RtL_TtB()
6352{
6353 moved_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::RightToLeft);
6354}
6355
6356void tst_QQuickGridView::moved_leftToRight_RtL_TtB_data()
6357{
6358 moved_defaultLayout_data();
6359}
6360
6361void tst_QQuickGridView::moved_topToBottom_LtR_TtB()
6362{
6363 moved_defaultLayout(flow: QQuickGridView::FlowTopToBottom);
6364}
6365
6366void tst_QQuickGridView::moved_topToBottom_LtR_TtB_data()
6367{
6368 moved_defaultLayout_data();
6369}
6370
6371void tst_QQuickGridView::moved_topToBottom_RtL_TtB()
6372{
6373 moved_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::RightToLeft);
6374}
6375
6376void tst_QQuickGridView::moved_topToBottom_RtL_TtB_data()
6377{
6378 moved_defaultLayout_data();
6379}
6380
6381void tst_QQuickGridView::moved_leftToRight_LtR_BtT()
6382{
6383 moved_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::LeftToRight, verticalLayout: QQuickItemView::BottomToTop);
6384}
6385
6386void tst_QQuickGridView::moved_leftToRight_LtR_BtT_data()
6387{
6388 moved_defaultLayout_data();
6389}
6390
6391void tst_QQuickGridView::moved_leftToRight_RtL_BtT()
6392{
6393 moved_defaultLayout(flow: QQuickGridView::FlowLeftToRight, horizLayout: Qt::RightToLeft, verticalLayout: QQuickItemView::BottomToTop);
6394}
6395
6396void tst_QQuickGridView::moved_leftToRight_RtL_BtT_data()
6397{
6398 moved_defaultLayout_data();
6399}
6400
6401void tst_QQuickGridView::moved_topToBottom_LtR_BtT()
6402{
6403 moved_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::LeftToRight, verticalLayout: QQuickItemView::BottomToTop);
6404}
6405
6406void tst_QQuickGridView::moved_topToBottom_LtR_BtT_data()
6407{
6408 moved_defaultLayout_data();
6409}
6410
6411void tst_QQuickGridView::moved_topToBottom_RtL_BtT()
6412{
6413 moved_defaultLayout(flow: QQuickGridView::FlowTopToBottom, horizLayout: Qt::RightToLeft, verticalLayout: QQuickItemView::BottomToTop);
6414}
6415
6416void tst_QQuickGridView::moved_topToBottom_RtL_BtT_data()
6417{
6418 moved_defaultLayout_data();
6419}
6420
6421
6422QList<int> tst_QQuickGridView::toIntList(const QVariantList &list)
6423{
6424 QList<int> ret;
6425 bool ok = true;
6426 for (int i=0; i<list.count(); i++) {
6427 ret << list[i].toInt(ok: &ok);
6428 if (!ok)
6429 qWarning() << "tst_QQuickGridView::toIntList(): not a number:" << list[i];
6430 }
6431
6432 return ret;
6433}
6434
6435void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
6436{
6437 const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend());
6438 for (int i=0; i<indexLists.count(); i++) {
6439 const auto &currentList = indexLists[i].value<QList<int> >();
6440 const QSet<int> current(currentList.cbegin(), currentList.cend());
6441 if (current != expectedIndexSet)
6442 qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
6443 QCOMPARE(current, expectedIndexSet);
6444 }
6445}
6446
6447void tst_QQuickGridView::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes)
6448{
6449 for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) {
6450 QCOMPARE(it.value().type(), QVariant::Int);
6451 QString name = it.key();
6452 int itemIndex = it.value().toInt();
6453 QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
6454 if (model.name(index: itemIndex) != name)
6455 qDebug() << itemIndex;
6456 QCOMPARE(model.name(itemIndex), name);
6457 }
6458 QCOMPARE(items.count(), expectedIndexes.count());
6459}
6460
6461void tst_QQuickGridView::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems)
6462{
6463 for (int i=0; i<itemLists.count(); i++) {
6464 QVariantList current = itemLists[i].toList();
6465 for (int j=0; j<current.count(); j++) {
6466 QQuickItem *o = qobject_cast<QQuickItem*>(object: current[j].value<QObject*>());
6467 QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
6468 QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
6469 }
6470 QCOMPARE(current.count(), expectedItems.count());
6471 }
6472}
6473
6474void tst_QQuickGridView::displayMargin()
6475{
6476 QQuickView *window = createView();
6477 window->setSource(testFileUrl(fileName: "displayMargin.qml"));
6478 window->show();
6479 QVERIFY(QTest::qWaitForWindowExposed(window));
6480
6481 QQuickGridView *gridview = window->rootObject()->findChild<QQuickGridView*>();
6482 QVERIFY(gridview != nullptr);
6483
6484 QQuickItem *content = gridview->contentItem();
6485 QVERIFY(content != nullptr);
6486
6487 QQuickItem *item0;
6488 QQuickItem *item97;
6489
6490 QVERIFY(item0 = findItem<QQuickItem>(content, "delegate", 0));
6491 QCOMPARE(delegateVisible(item0), true);
6492
6493 // the 97th item should be within the end margin
6494 QVERIFY(item97 = findItem<QQuickItem>(content, "delegate", 96));
6495 QCOMPARE(delegateVisible(item97), true);
6496
6497 // GridView staggers item creation, so the 118th item should be outside the end margin.
6498 QVERIFY(findItem<QQuickItem>(content, "delegate", 117) == nullptr);
6499
6500 // the first delegate should still be within the begin margin
6501 gridview->positionViewAtIndex(index: 20, mode: QQuickGridView::Beginning);
6502 QCOMPARE(delegateVisible(item0), true);
6503
6504 // the first delegate should now be outside the begin margin
6505 gridview->positionViewAtIndex(index: 36, mode: QQuickGridView::Beginning);
6506 QCOMPARE(delegateVisible(item0), false);
6507
6508 delete window;
6509}
6510
6511void tst_QQuickGridView::negativeDisplayMargin()
6512{
6513 QQuickItem *item;
6514 QQuickView *window = createView();
6515 window->setSource(testFileUrl(fileName: "negativeDisplayMargin.qml"));
6516 window->show();
6517 QVERIFY(QTest::qWaitForWindowExposed(window));
6518
6519 QQuickItem *listview = window->rootObject();
6520 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "grid");
6521 QVERIFY(gridview != nullptr);
6522
6523 QTRY_COMPARE(gridview->property("createdItems").toInt(), 11);
6524 QCOMPARE(gridview->property("destroyedItem").toInt(), 0);
6525
6526 QQuickItem *content = gridview->contentItem();
6527 QVERIFY(content != nullptr);
6528
6529 QVERIFY(item = findItem<QQuickItem>(content, "delegate", 0));
6530 QCOMPARE(delegateVisible(item), true);
6531
6532 QVERIFY(item = findItem<QQuickItem>(content, "delegate", 7));
6533 QCOMPARE(delegateVisible(item), true);
6534
6535 QVERIFY(item = findItem<QQuickItem>(content, "delegate", 8));
6536 QCOMPARE(delegateVisible(item), false);
6537
6538 // Flick until contentY means that delegate8 should be visible
6539 listview->setProperty(name: "contentY", value: 500);
6540 QVERIFY(item = findItem<QQuickItem>(content, "delegate", 8));
6541 QTRY_COMPARE(delegateVisible(item), true);
6542
6543 listview->setProperty(name: "contentY", value: 1000);
6544 QTRY_VERIFY(item = findItem<QQuickItem>(content, "delegate", 14));
6545 QTRY_COMPARE(delegateVisible(item), true);
6546
6547 listview->setProperty(name: "contentY", value: 0);
6548 QVERIFY(item = findItem<QQuickItem>(content, "delegate", 4));
6549 QTRY_COMPARE(delegateVisible(item), true);
6550
6551 delete window;
6552}
6553
6554void tst_QQuickGridView::jsArrayChange()
6555{
6556 QQmlEngine engine;
6557 QQmlComponent component(&engine);
6558 component.setData("import QtQuick 2.4; GridView {}", baseUrl: QUrl());
6559
6560 QScopedPointer<QQuickGridView> view(qobject_cast<QQuickGridView *>(object: component.create()));
6561 QVERIFY(!view.isNull());
6562
6563 QSignalSpy spy(view.data(), SIGNAL(modelChanged()));
6564 QVERIFY(spy.isValid());
6565
6566 QJSValue array1 = engine.newArray(length: 3);
6567 QJSValue array2 = engine.newArray(length: 3);
6568 for (int i = 0; i < 3; ++i) {
6569 array1.setProperty(arrayIndex: i, value: i);
6570 array2.setProperty(arrayIndex: i, value: i);
6571 }
6572
6573 view->setModel(QVariant::fromValue(value: array1));
6574 QCOMPARE(spy.count(), 1);
6575
6576 // no change
6577 view->setModel(QVariant::fromValue(value: array2));
6578 QCOMPARE(spy.count(), 1);
6579}
6580
6581void tst_QQuickGridView::contentHeightWithDelayRemove_data()
6582{
6583 QTest::addColumn<bool>(name: "useDelayRemove");
6584 QTest::addColumn<QByteArray>(name: "removeFunc");
6585 QTest::addColumn<int>(name: "countDelta");
6586 QTest::addColumn<qreal>(name: "contentHeightDelta");
6587
6588 QTest::newRow(dataTag: "remove without delayRemove")
6589 << false
6590 << QByteArray("takeOne")
6591 << -1
6592 << qreal(-1 * 100.0);
6593
6594 QTest::newRow(dataTag: "remove with delayRemove")
6595 << true
6596 << QByteArray("takeOne")
6597 << -1
6598 << qreal(-1 * 100.0);
6599
6600 QTest::newRow(dataTag: "remove with multiple delayRemove")
6601 << true
6602 << QByteArray("takeThree")
6603 << -3
6604 << qreal(-2 * 100.0);
6605
6606 QTest::newRow(dataTag: "clear with delayRemove")
6607 << true
6608 << QByteArray("takeAll")
6609 << -5
6610 << qreal(-3 * 100.0);
6611}
6612
6613void tst_QQuickGridView::contentHeightWithDelayRemove()
6614{
6615 QFETCH(bool, useDelayRemove);
6616 QFETCH(QByteArray, removeFunc);
6617 QFETCH(int, countDelta);
6618 QFETCH(qreal, contentHeightDelta);
6619
6620 QQuickView *window = createView();
6621 window->setSource(testFileUrl(fileName: "contentHeightWithDelayRemove.qml"));
6622 window->show();
6623 QVERIFY(QTest::qWaitForWindowExposed(window));
6624
6625 QQuickGridView *gridview = window->rootObject()->findChild<QQuickGridView*>();
6626 QTRY_VERIFY(gridview != nullptr);
6627
6628 const int initialCount(gridview->count());
6629 const int eventualCount(initialCount + countDelta);
6630
6631 const qreal initialContentHeight(gridview->contentHeight());
6632 const int eventualContentHeight(qRound(d: initialContentHeight + contentHeightDelta));
6633
6634 gridview->setProperty(name: "useDelayRemove", value: useDelayRemove);
6635 QMetaObject::invokeMethod(obj: window->rootObject(), member: removeFunc.constData());
6636 QTest::qWait(ms: 50);
6637 QCOMPARE(gridview->count(), eventualCount);
6638
6639 if (useDelayRemove) {
6640 QCOMPARE(qRound(gridview->contentHeight()), qRound(initialContentHeight));
6641 QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
6642 } else {
6643 QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
6644 }
6645
6646 delete window;
6647}
6648
6649void tst_QQuickGridView::QTBUG_45640()
6650{
6651 QQuickView *window = createView();
6652 window->setSource(testFileUrl(fileName: "qtbug45640.qml"));
6653 window->show();
6654 QVERIFY(QTest::qWaitForWindowExposed(window));
6655
6656 QQuickGridView *gridview = qobject_cast<QQuickGridView*>(object: window->rootObject());
6657 QVERIFY(gridview != nullptr);
6658
6659 QCOMPARE(gridview->contentY(), qreal(-50.0));
6660
6661 gridview->moveCurrentIndexDown();
6662
6663 QTRY_VERIFY(gridview->contentY() > qreal(-50.0) && gridview->contentY() < qreal(0.0));
6664
6665 delete window;
6666}
6667
6668void tst_QQuickGridView::QTBUG_49218()
6669{
6670 QQuickView *window = createView();
6671 window->setSource(testFileUrl(fileName: "qtbug49218.qml"));
6672 window->show();
6673 QVERIFY(QTest::qWaitForWindowExposed(window));
6674
6675 QQuickItem *rootItem = qobject_cast<QQuickItem*>(object: window->rootObject());
6676 QQuickGridView *gridview = qobject_cast<QQuickGridView *>(object: rootItem->childItems().first());
6677 QVERIFY(gridview != nullptr);
6678
6679 auto processEventsAndForceLayout = [&gridview] () {
6680 for (int pass = 0; pass < 2; ++pass) {
6681 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
6682 gridview->forceLayout();
6683 }
6684 };
6685 QMetaObject::invokeMethod(obj: rootItem, member: "scrollToTop");
6686 processEventsAndForceLayout();
6687 QMetaObject::invokeMethod(obj: rootItem, member: "changeModel");
6688 processEventsAndForceLayout();
6689 QMetaObject::invokeMethod(obj: rootItem, member: "changeModel");
6690 processEventsAndForceLayout();
6691 QMetaObject::invokeMethod(obj: rootItem, member: "scrollToTop");
6692 processEventsAndForceLayout();
6693
6694 QCOMPARE(gridview->indexAt(gridview->cellWidth() - 10, gridview->cellHeight() - 10), 0);
6695 delete window;
6696}
6697
6698void tst_QQuickGridView::keyNavigationEnabled()
6699{
6700 QScopedPointer<QQuickView> window(createView());
6701 window->setSource(testFileUrl(fileName: "keyNavigationEnabled.qml"));
6702 window->show();
6703 window->requestActivate();
6704 QVERIFY(QTest::qWaitForWindowActive(window.data()));
6705
6706 QQuickGridView *gridView = qobject_cast<QQuickGridView *>(object: window->rootObject());
6707 QVERIFY(gridView);
6708 QCOMPARE(gridView->isKeyNavigationEnabled(), true);
6709
6710 gridView->setFocus(true);
6711 QVERIFY(gridView->hasActiveFocus());
6712
6713 gridView->setHighlightMoveDuration(0);
6714
6715 // If keyNavigationEnabled is not explicitly set to true, respect the original behavior
6716 // of disabling both mouse and keyboard interaction.
6717 QSignalSpy enabledSpy(gridView, SIGNAL(keyNavigationEnabledChanged()));
6718 gridView->setInteractive(false);
6719 QCOMPARE(enabledSpy.count(), 1);
6720 QCOMPARE(gridView->isKeyNavigationEnabled(), false);
6721
6722 flick(window: window.data(), from: QPoint(200, 175), to: QPoint(200, 50), duration: 100);
6723 QVERIFY(!gridView->isMoving());
6724 QCOMPARE(gridView->contentY(), 0.0);
6725 QCOMPARE(gridView->currentIndex(), 0);
6726
6727 QTest::keyClick(window: window.data(), key: Qt::Key_Right);
6728 QCOMPARE(gridView->currentIndex(), 0);
6729
6730 // Check that isKeyNavigationEnabled implicitly follows the value of interactive.
6731 gridView->setInteractive(true);
6732 QCOMPARE(enabledSpy.count(), 2);
6733 QCOMPARE(gridView->isKeyNavigationEnabled(), true);
6734
6735 // Change it back again for the next check.
6736 gridView->setInteractive(false);
6737 QCOMPARE(enabledSpy.count(), 3);
6738 QCOMPARE(gridView->isKeyNavigationEnabled(), false);
6739
6740 // Setting keyNavigationEnabled to true shouldn't enable mouse interaction.
6741 gridView->setKeyNavigationEnabled(true);
6742 QCOMPARE(enabledSpy.count(), 4);
6743 flick(window: window.data(), from: QPoint(200, 175), to: QPoint(200, 50), duration: 100);
6744 QVERIFY(!gridView->isMoving());
6745 QCOMPARE(gridView->contentY(), 0.0);
6746 QCOMPARE(gridView->currentIndex(), 0);
6747
6748 // Should now work.
6749 QTest::keyClick(window: window.data(), key: Qt::Key_Right);
6750 QCOMPARE(gridView->currentIndex(), 1);
6751
6752 // Changing interactive now shouldn't result in keyNavigationEnabled changing,
6753 // since we broke the "binding".
6754 gridView->setInteractive(true);
6755 QCOMPARE(enabledSpy.count(), 4);
6756
6757 // Keyboard interaction shouldn't work now.
6758 gridView->setKeyNavigationEnabled(false);
6759 QTest::keyClick(window: window.data(), key: Qt::Key_Right);
6760 QCOMPARE(gridView->currentIndex(), 1);
6761}
6762
6763void tst_QQuickGridView::QTBUG_48870_fastModelUpdates()
6764{
6765 StressTestModel model;
6766
6767 QScopedPointer<QQuickView> window(createView());
6768 QQmlContext *ctxt = window->rootContext();
6769 ctxt->setContextProperty("testModel", &model);
6770
6771 window->setSource(testFileUrl(fileName: "qtbug48870.qml"));
6772 window->show();
6773 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
6774
6775 QQuickGridView *view = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "view");
6776 QTRY_VERIFY(view != nullptr);
6777
6778 QQuickItemViewPrivate *priv = QQuickItemViewPrivate::get(o: view);
6779 bool nonUnique;
6780 FxViewItem *item = nullptr;
6781 int expectedIdx;
6782 QVERIFY(testVisibleItems(priv, &nonUnique, &item, &expectedIdx));
6783
6784 for (int i = 0; i < 10; i++) {
6785 QTest::qWait(ms: 100);
6786 QVERIFY2(testVisibleItems(priv, &nonUnique, &item, &expectedIdx),
6787 qPrintable(!item ? QString("Unexpected null item")
6788 : nonUnique ? QString("Non-unique item at %1 and %2").arg(item->index).arg(expectedIdx)
6789 : QString("Found index %1, expected index is %3").arg(item->index).arg(expectedIdx)));
6790 if (i % 3 != 0) {
6791 if (i & 1)
6792 flick(window: window.data(), from: QPoint(100, 200), to: QPoint(100, 0), duration: 100);
6793 else
6794 flick(window: window.data(), from: QPoint(100, 200), to: QPoint(100, 400), duration: 100);
6795 }
6796 }
6797}
6798
6799void tst_QQuickGridView::resizeDynamicCellWidthRtL()
6800{
6801 QScopedPointer<QQuickView> window(createView());
6802 window->setSource(testFileUrl(fileName: "qtbug92998.qml"));
6803 window->show();
6804 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
6805
6806 QQuickGridView *gridview = findItem<QQuickGridView>(parent: window->rootObject(), objectName: "gridview");
6807 QTRY_VERIFY(gridview != nullptr);
6808 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
6809 gridview->setWidth(460);
6810 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
6811 QTRY_COMPARE(gridview->contentX(), 0.f);
6812 gridview->setWidth(360);
6813 QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
6814 QTRY_COMPARE(gridview->contentX(), 0.f);
6815}
6816
6817void tst_QQuickGridView::releaseItems()
6818{
6819 QScopedPointer<QQuickView> view(createView());
6820 view->setSource(testFileUrl(fileName: "releaseItems.qml"));
6821
6822 QQuickGridView *gridview = qobject_cast<QQuickGridView *>(object: view->rootObject());
6823 QVERIFY(gridview);
6824
6825 // don't crash (QTBUG-61294)
6826 gridview->setModel(123);
6827}
6828
6829QTEST_MAIN(tst_QQuickGridView)
6830
6831#include "tst_qquickgridview.moc"
6832
6833

source code of qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp