1b7adbecda8b92ef8b74f674de142c8c0e095845
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickpositioners / tst_qquickpositioners.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <private/qlistmodelinterface_p.h>
43 #include <QtQuick/qquickview.h>
44 #include <qqmlengine.h>
45 #include <QtQuick/private/qquickrectangle_p.h>
46 #include <QtQuick/private/qquickpositioners_p.h>
47 #include <QtQuick/private/qquicktransition_p.h>
48 #include <private/qquickitem_p.h>
49 #include <qqmlexpression.h>
50 #include "../shared/viewtestutil.h"
51 #include "../shared/visualtestutil.h"
52 #include "../../shared/util.h"
53
54 using namespace QQuickViewTestUtil;
55 using namespace QQuickVisualTestUtil;
56
57 class tst_qquickpositioners : public QQmlDataTest
58 {
59     Q_OBJECT
60 public:
61     tst_qquickpositioners();
62
63 private slots:
64     void test_horizontal();
65     void test_horizontal_rtl();
66     void test_horizontal_spacing();
67     void test_horizontal_spacing_rightToLeft();
68     void test_horizontal_animated();
69     void test_horizontal_animated_rightToLeft();
70     void test_horizontal_animated_disabled();
71     void test_vertical();
72     void test_vertical_spacing();
73     void test_vertical_animated();
74     void test_grid();
75     void test_grid_topToBottom();
76     void test_grid_rightToLeft();
77     void test_grid_spacing();
78     void test_grid_row_column_spacing();
79     void test_grid_animated();
80     void test_grid_animated_rightToLeft();
81     void test_grid_zero_columns();
82     void test_propertychanges();
83     void test_repeater();
84     void test_flow();
85     void test_flow_rightToLeft();
86     void test_flow_topToBottom();
87     void test_flow_resize();
88     void test_flow_resize_rightToLeft();
89     void test_flow_implicit_resize();
90     void test_conflictinganchors();
91     void test_mirroring();
92     void test_allInvisible();
93     void test_attachedproperties();
94     void test_attachedproperties_data();
95     void test_attachedproperties_dynamic();
96
97     void populateTransitions_row();
98     void populateTransitions_row_data();
99     void populateTransitions_column();
100     void populateTransitions_column_data();
101     void populateTransitions_grid();
102     void populateTransitions_grid_data();
103     void populateTransitions_flow();
104     void populateTransitions_flow_data();
105     void addTransitions_row();
106     void addTransitions_row_data();
107     void addTransitions_column();
108     void addTransitions_column_data();
109     void addTransitions_grid();
110     void addTransitions_grid_data();
111     void addTransitions_flow();
112     void addTransitions_flow_data();
113     void moveTransitions_row();
114     void moveTransitions_row_data();
115     void moveTransitions_column();
116     void moveTransitions_column_data();
117     void moveTransitions_grid();
118     void moveTransitions_grid_data();
119     void moveTransitions_flow();
120     void moveTransitions_flow_data();
121
122 private:
123     QQuickView *createView(const QString &filename, bool wait=true);
124
125     void populateTransitions(const QString &positionerObjectName);
126     void populateTransitions_data();
127     void addTransitions(const QString &positionerObjectName);
128     void addTransitions_data();
129     void moveTransitions(const QString &positionerObjectName);
130     void moveTransitions_data();
131     void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
132     void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes);
133     void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems);
134     void checkItemPositions(QQuickItem *positioner, QaimModel *model, qreal incrementalSize);
135 };
136
137 void tst_qquickpositioners::populateTransitions_row()
138 {
139     populateTransitions("row");
140 }
141
142 void tst_qquickpositioners::populateTransitions_row_data()
143 {
144     populateTransitions_data();
145 }
146
147 void tst_qquickpositioners::populateTransitions_column()
148 {
149     populateTransitions("column");
150 }
151
152 void tst_qquickpositioners::populateTransitions_column_data()
153 {
154     populateTransitions_data();
155 }
156
157 void tst_qquickpositioners::populateTransitions_grid()
158 {
159     populateTransitions("grid");
160 }
161
162 void tst_qquickpositioners::populateTransitions_grid_data()
163 {
164     populateTransitions_data();
165 }
166
167 void tst_qquickpositioners::populateTransitions_flow()
168 {
169     populateTransitions("flow");
170 }
171
172 void tst_qquickpositioners::populateTransitions_flow_data()
173 {
174     populateTransitions_data();
175 }
176
177 void tst_qquickpositioners::addTransitions_row()
178 {
179     addTransitions("row");
180 }
181
182 void tst_qquickpositioners::addTransitions_row_data()
183 {
184     addTransitions_data();
185 }
186
187 void tst_qquickpositioners::addTransitions_column()
188 {
189     addTransitions("column");
190 }
191
192 void tst_qquickpositioners::addTransitions_column_data()
193 {
194     addTransitions_data();
195 }
196
197 void tst_qquickpositioners::addTransitions_grid()
198 {
199     addTransitions("grid");
200 }
201
202 void tst_qquickpositioners::addTransitions_grid_data()
203 {
204     // don't use addTransitions_data() because grid displaces items differently
205     // (adding items further down the grid can cause displace transitions at
206     // previous indexes, since grid is auto-resized to tightly fit all of its items)
207
208     QTest::addColumn<int>("initialItemCount");
209     QTest::addColumn<int>("insertionIndex");
210     QTest::addColumn<int>("insertionCount");
211     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
212
213     QTest::newRow("add one @ start") << 10 << 0 << 1 << ListRange(0, 9);
214     QTest::newRow("add one @ middle") << 10 << 5 << 1 << ListRange(3, 3) + ListRange(5, 9);
215     QTest::newRow("add one @ end") << 10 << 10 << 1 << ListRange(3, 3) + ListRange(7, 7);
216
217     QTest::newRow("add multiple @ start") << 10 << 0 << 3 << ListRange(0, 9);
218     QTest::newRow("add multiple @ middle") << 10 << 5 << 3 << ListRange(1, 3) + ListRange(5, 9);
219     QTest::newRow("add multiple @ end") << 10 << 10 << 3 << ListRange(1, 3) + ListRange(5, 7) + ListRange(9, 9);
220 }
221
222 void tst_qquickpositioners::addTransitions_flow()
223 {
224     addTransitions("flow");
225 }
226
227 void tst_qquickpositioners::addTransitions_flow_data()
228 {
229     addTransitions_data();
230 }
231
232 void tst_qquickpositioners::moveTransitions_row()
233 {
234     moveTransitions("row");
235 }
236
237 void tst_qquickpositioners::moveTransitions_row_data()
238 {
239     moveTransitions_data();
240 }
241
242 void tst_qquickpositioners::moveTransitions_column()
243 {
244     moveTransitions("column");
245 }
246
247 void tst_qquickpositioners::moveTransitions_column_data()
248 {
249     moveTransitions_data();
250 }
251
252 void tst_qquickpositioners::moveTransitions_grid()
253 {
254     moveTransitions("grid");
255 }
256
257 void tst_qquickpositioners::moveTransitions_grid_data()
258 {
259     // don't use moveTransitions_data() because grid displaces items differently
260     // (removing items further down the grid can cause displace transitions at
261     // previous indexes, since grid is auto-resized to tightly fit all of its items)
262
263     QTest::addColumn<int>("initialItemCount");
264     QTest::addColumn<ListChange>("change");
265     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
266
267     QTest::newRow("remove one @ start") << 10 << ListChange::remove(0, 1) << ListRange(1, 9);
268     QTest::newRow("remove one @ middle") << 10 << ListChange::remove(4, 1) << ListRange(2, 3) + ListRange(5, 9);
269     QTest::newRow("remove one @ end") << 10 << ListChange::remove(9, 1) << ListRange(2, 3) + ListRange(6, 7);
270
271     QTest::newRow("remove multiple @ start") << 10 << ListChange::remove(0, 3) << ListRange(3, 9);
272     QTest::newRow("remove multiple @ middle") << 10 << ListChange::remove(4, 3) << ListRange(1, 3) + ListRange(7, 9);
273     QTest::newRow("remove multiple @ end") << 10 << ListChange::remove(7, 3) << ListRange(1, 3) + ListRange(5, 6);
274 }
275
276 void tst_qquickpositioners::moveTransitions_flow()
277 {
278     moveTransitions("flow");
279 }
280
281 void tst_qquickpositioners::moveTransitions_flow_data()
282 {
283     moveTransitions_data();
284 }
285
286 tst_qquickpositioners::tst_qquickpositioners()
287 {
288 }
289
290 void tst_qquickpositioners::test_horizontal()
291 {
292     QQuickView *canvas = createView(testFile("horizontal.qml"));
293
294     canvas->rootObject()->setProperty("testRightToLeft", false);
295
296     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
297     QVERIFY(one != 0);
298
299     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
300     QVERIFY(two != 0);
301
302     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
303     QVERIFY(three != 0);
304
305     QCOMPARE(one->x(), 0.0);
306     QCOMPARE(one->y(), 0.0);
307     QCOMPARE(two->x(), 50.0);
308     QCOMPARE(two->y(), 0.0);
309     QCOMPARE(three->x(), 70.0);
310     QCOMPARE(three->y(), 0.0);
311
312     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
313     QCOMPARE(row->width(), 110.0);
314     QCOMPARE(row->height(), 50.0);
315
316     delete canvas;
317 }
318
319 void tst_qquickpositioners::test_horizontal_rtl()
320 {
321     QQuickView *canvas = createView(testFile("horizontal.qml"));
322
323     canvas->rootObject()->setProperty("testRightToLeft", true);
324
325     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
326     QVERIFY(one != 0);
327
328     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
329     QVERIFY(two != 0);
330
331     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
332     QVERIFY(three != 0);
333
334     QCOMPARE(one->x(), 60.0);
335     QCOMPARE(one->y(), 0.0);
336     QCOMPARE(two->x(), 40.0);
337     QCOMPARE(two->y(), 0.0);
338     QCOMPARE(three->x(), 0.0);
339     QCOMPARE(three->y(), 0.0);
340
341     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
342     QCOMPARE(row->width(), 110.0);
343     QCOMPARE(row->height(), 50.0);
344
345     // Change the width of the row and check that items stay to the right
346     row->setWidth(200);
347     QTRY_COMPARE(one->x(), 150.0);
348     QCOMPARE(one->y(), 0.0);
349     QCOMPARE(two->x(), 130.0);
350     QCOMPARE(two->y(), 0.0);
351     QCOMPARE(three->x(), 90.0);
352     QCOMPARE(three->y(), 0.0);
353
354     delete canvas;
355 }
356
357 void tst_qquickpositioners::test_horizontal_spacing()
358 {
359     QQuickView *canvas = createView(testFile("horizontal-spacing.qml"));
360
361     canvas->rootObject()->setProperty("testRightToLeft", false);
362
363     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
364     QVERIFY(one != 0);
365
366     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
367     QVERIFY(two != 0);
368
369     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
370     QVERIFY(three != 0);
371
372     QCOMPARE(one->x(), 0.0);
373     QCOMPARE(one->y(), 0.0);
374     QCOMPARE(two->x(), 60.0);
375     QCOMPARE(two->y(), 0.0);
376     QCOMPARE(three->x(), 90.0);
377     QCOMPARE(three->y(), 0.0);
378
379     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
380     QCOMPARE(row->width(), 130.0);
381     QCOMPARE(row->height(), 50.0);
382
383     delete canvas;
384 }
385
386 void tst_qquickpositioners::test_horizontal_spacing_rightToLeft()
387 {
388     QQuickView *canvas = createView(testFile("horizontal-spacing.qml"));
389
390     canvas->rootObject()->setProperty("testRightToLeft", true);
391
392     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
393     QVERIFY(one != 0);
394
395     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
396     QVERIFY(two != 0);
397
398     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
399     QVERIFY(three != 0);
400
401     QCOMPARE(one->x(), 80.0);
402     QCOMPARE(one->y(), 0.0);
403     QCOMPARE(two->x(), 50.0);
404     QCOMPARE(two->y(), 0.0);
405     QCOMPARE(three->x(), 00.0);
406     QCOMPARE(three->y(), 0.0);
407
408     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
409     QCOMPARE(row->width(), 130.0);
410     QCOMPARE(row->height(), 50.0);
411
412     delete canvas;
413 }
414
415 void tst_qquickpositioners::test_horizontal_animated()
416 {
417     QQuickView *canvas = createView(testFile("horizontal-animated.qml"), false);
418
419     canvas->rootObject()->setProperty("testRightToLeft", false);
420
421     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
422     QVERIFY(one != 0);
423
424     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
425     QVERIFY(two != 0);
426
427     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
428     QVERIFY(three != 0);
429
430     //Note that they animate in
431     QCOMPARE(one->x(), -100.0);
432     QCOMPARE(two->x(), -100.0);
433     QCOMPARE(three->x(), -100.0);
434
435     QTest::qWaitForWindowShown(canvas); //It may not relayout until the next frame, so it needs to be drawn
436
437     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
438     QVERIFY(row);
439     QCOMPARE(row->width(), 100.0);
440     QCOMPARE(row->height(), 50.0);
441
442     //QTRY_COMPARE used instead of waiting for the expected time of animation completion
443     //Note that this means the duration of the animation is NOT tested
444
445     QTRY_COMPARE(one->x(), 0.0);
446     QTRY_COMPARE(one->y(), 0.0);
447     QTRY_COMPARE(two->isVisible(), false);
448     QTRY_COMPARE(two->x(), -100.0);//Not 'in' yet
449     QTRY_COMPARE(two->y(), 0.0);
450     QTRY_COMPARE(three->x(), 50.0);
451     QTRY_COMPARE(three->y(), 0.0);
452
453     //Add 'two'
454     two->setVisible(true);
455     QTRY_COMPARE(two->isVisible(), true);
456     QTRY_COMPARE(row->width(), 150.0);
457     QTRY_COMPARE(row->height(), 50.0);
458
459     QTest::qWait(0);//Let the animation start
460     QVERIFY(two->x() >= -100.0 && two->x() < 50.0);
461     QVERIFY(three->x() >= 50.0 && three->x() < 100.0);
462
463     QTRY_COMPARE(two->x(), 50.0);
464     QTRY_COMPARE(three->x(), 100.0);
465
466     delete canvas;
467 }
468
469 void tst_qquickpositioners::test_horizontal_animated_rightToLeft()
470 {
471     QQuickView *canvas = createView(testFile("horizontal-animated.qml"), false);
472
473     canvas->rootObject()->setProperty("testRightToLeft", true);
474
475     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
476     QVERIFY(one != 0);
477
478     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
479     QVERIFY(two != 0);
480
481     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
482     QVERIFY(three != 0);
483
484     //Note that they animate in
485     QCOMPARE(one->x(), -100.0);
486     QCOMPARE(two->x(), -100.0);
487     QCOMPARE(three->x(), -100.0);
488
489     QTest::qWaitForWindowShown(canvas); //It may not relayout until the next frame, so it needs to be drawn
490
491     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
492     QVERIFY(row);
493     QCOMPARE(row->width(), 100.0);
494     QCOMPARE(row->height(), 50.0);
495
496     //QTRY_COMPARE used instead of waiting for the expected time of animation completion
497     //Note that this means the duration of the animation is NOT tested
498
499     QTRY_COMPARE(one->x(), 50.0);
500     QTRY_COMPARE(one->y(), 0.0);
501     QTRY_COMPARE(two->isVisible(), false);
502     QTRY_COMPARE(two->x(), -100.0);//Not 'in' yet
503     QTRY_COMPARE(two->y(), 0.0);
504     QTRY_COMPARE(three->x(), 0.0);
505     QTRY_COMPARE(three->y(), 0.0);
506
507     //Add 'two'
508     two->setVisible(true);
509     QTRY_COMPARE(two->isVisible(), true);
510
511     // New size should propagate after visible change
512     QTRY_COMPARE(row->width(), 150.0);
513     QTRY_COMPARE(row->height(), 50.0);
514
515     QTest::qWait(0);//Let the animation start
516     QVERIFY(one->x() >= 50.0 && one->x() < 100);
517     QVERIFY(two->x() >= -100.0 && two->x() < 50.0);
518
519     QTRY_COMPARE(one->x(), 100.0);
520     QTRY_COMPARE(two->x(), 50.0);
521
522     delete canvas;
523 }
524
525 void tst_qquickpositioners::test_horizontal_animated_disabled()
526 {
527     QQuickView *canvas = createView(testFile("horizontal-animated-disabled.qml"));
528
529     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
530     QVERIFY(one != 0);
531
532     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
533     QVERIFY(two != 0);
534
535     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
536     QVERIFY(three != 0);
537
538     QQuickItem *row = canvas->rootObject()->findChild<QQuickItem*>("row");
539     QVERIFY(row);
540
541     qApp->processEvents();
542
543     QCOMPARE(one->x(), 0.0);
544     QCOMPARE(one->y(), 0.0);
545     QCOMPARE(two->isVisible(), false);
546     QCOMPARE(two->x(), -100.0);//Not 'in' yet
547     QCOMPARE(two->y(), 0.0);
548     QCOMPARE(three->x(), 50.0);
549     QCOMPARE(three->y(), 0.0);
550
551     //Add 'two'
552     two->setVisible(true);
553     QCOMPARE(two->isVisible(), true);
554     QTRY_COMPARE(row->width(), 150.0);
555     QTRY_COMPARE(row->height(), 50.0);
556
557     QTRY_COMPARE(two->x(), 50.0);
558     QTRY_COMPARE(three->x(), 100.0);
559
560     delete canvas;
561 }
562
563 void tst_qquickpositioners::populateTransitions(const QString &positionerObjectName)
564 {
565     QFETCH(bool, dynamicallyPopulate);
566     QFETCH(bool, usePopulateTransition);
567
568     QPointF targetItems_transitionFrom(-50, -50);
569     QPointF displacedItems_transitionVia(100, 100);
570
571     QaimModel model;
572     if (!dynamicallyPopulate) {
573         for (int i = 0; i < 30; i++)
574             model.addItem("Original item" + QString::number(i), "");
575     }
576
577     QaimModel model_targetItems_transitionFrom;
578     QaimModel model_displacedItems_transitionVia;
579
580     QQuickView *canvas = QQuickViewTestUtil::createView();
581     QQmlContext *ctxt = canvas->rootContext();
582     ctxt->setContextProperty("usePopulateTransition", usePopulateTransition);
583     ctxt->setContextProperty("enableAddTransition", true);
584     ctxt->setContextProperty("dynamicallyPopulate", dynamicallyPopulate);
585     ctxt->setContextProperty("testModel", &model);
586     ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
587     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
588     ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
589     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
590     ctxt->setContextProperty("testedPositioner", positionerObjectName);
591     canvas->setSource(testFileUrl("transitions.qml"));
592
593     QQuickItem *positioner = canvas->rootObject()->findChild<QQuickItem*>(positionerObjectName);
594     QVERIFY(positioner);
595     canvas->show();
596     qApp->processEvents();
597
598     if (!dynamicallyPopulate && usePopulateTransition) {
599         QTRY_COMPARE(canvas->rootObject()->property("populateTransitionsDone").toInt(), model.count());
600         QTRY_COMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), 0);
601
602         QList<QPair<QString, QString> > targetData;
603         QList<int> targetIndexes;
604         for (int i=0; i<model.count(); i++) {
605             targetData << qMakePair(model.name(i), model.number(i));
606             targetIndexes << i;
607         }
608         QList<QQuickItem *> targetItems = findItems<QQuickItem>(positioner, "wrapper", targetIndexes);
609         model_targetItems_transitionFrom.matchAgainst(targetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
610         matchItemsAndIndexes(canvas->rootObject()->property("targetTrans_items").toMap(), model, targetIndexes);
611         matchIndexLists(canvas->rootObject()->property("targetTrans_targetIndexes").toList(), targetIndexes);
612         matchItemLists(canvas->rootObject()->property("targetTrans_targetItems").toList(), targetItems);
613
614     } else if (dynamicallyPopulate) {
615         QTRY_COMPARE(canvas->rootObject()->property("populateTransitionsDone").toInt(), 0);
616         QTRY_COMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), model.count());
617     } else {
618         QTRY_COMPARE(QQuickItemPrivate::get(positioner)->polishScheduled, false);
619         QTRY_COMPARE(canvas->rootObject()->property("populateTransitionsDone").toInt(), 0);
620         QTRY_COMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), 0);
621     }
622
623     checkItemPositions(positioner, &model, canvas->rootObject()->property("incrementalSize").toInt());
624
625     // add an item and check this is done with add transition, not populate
626     canvas->rootObject()->setProperty("populateTransitionsDone", 0);
627     canvas->rootObject()->setProperty("addTransitionsDone", 0);
628     model.insertItem(0, "new item", "");
629     QTRY_COMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), 1);
630     QTRY_COMPARE(canvas->rootObject()->property("populateTransitionsDone").toInt(), 0);
631
632     delete canvas;
633 }
634
635 void tst_qquickpositioners::populateTransitions_data()
636 {
637     QTest::addColumn<bool>("dynamicallyPopulate");
638     QTest::addColumn<bool>("usePopulateTransition");
639
640     QTest::newRow("statically populate") << false << true;
641     QTest::newRow("statically populate, no populate transition") << false << false;
642
643     QTest::newRow("dynamically populate") << true << true;
644     QTest::newRow("dynamically populate, no populate transition") << true << false;
645 }
646
647 void tst_qquickpositioners::addTransitions(const QString &positionerObjectName)
648 {
649     QFETCH(int, initialItemCount);
650     QFETCH(int, insertionIndex);
651     QFETCH(int, insertionCount);
652     QFETCH(ListRange, expectedDisplacedIndexes);
653
654     QPointF targetItems_transitionFrom(-50, -50);
655     QPointF displacedItems_transitionVia(100, 100);
656
657     QaimModel model;
658     QaimModel model_targetItems_transitionFrom;
659     QaimModel model_displacedItems_transitionVia;
660
661     QQuickView *canvas = QQuickViewTestUtil::createView();
662     QQmlContext *ctxt = canvas->rootContext();
663     ctxt->setContextProperty("usePopulateTransition", QVariant(false));
664     ctxt->setContextProperty("enableAddTransition", QVariant(true));
665     ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
666     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
667     ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
668     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
669     canvas->setSource(testFileUrl("transitions.qml"));
670     canvas->show();
671     qApp->processEvents();
672
673     QQuickItem *positioner = canvas->rootObject()->findChild<QQuickItem*>(positionerObjectName);
674     QVERIFY(positioner);
675     positioner->findChild<QQuickItem*>("repeater")->setProperty("model", QVariant::fromValue(&model));
676     QTRY_COMPARE(QQuickItemPrivate::get(positioner)->polishScheduled, false);
677
678     for (int i = 0; i < initialItemCount; i++)
679         model.addItem("Original item" + QString::number(i), "");
680
681     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
682     QList<QPair<QString, QString> > targetData;
683     QList<int> targetIndexes;
684     for (int i=0; i<model.count(); i++) {
685         targetData << qMakePair(model.name(i), model.number(i));
686         targetIndexes << i;
687     }
688     QList<QQuickItem *> targetItems = findItems<QQuickItem>(positioner, "wrapper", targetIndexes);
689
690     // check add transition was run for first lot of added items
691     QTRY_COMPARE(canvas->rootObject()->property("populateTransitionsDone").toInt(), 0);
692     QTRY_COMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), initialItemCount);
693     QTRY_COMPARE(canvas->rootObject()->property("displaceTransitionsDone").toInt(), 0);
694     model_targetItems_transitionFrom.matchAgainst(targetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
695     matchItemsAndIndexes(canvas->rootObject()->property("targetTrans_items").toMap(), model, targetIndexes);
696     matchIndexLists(canvas->rootObject()->property("targetTrans_targetIndexes").toList(), targetIndexes);
697     matchItemLists(canvas->rootObject()->property("targetTrans_targetItems").toList(), targetItems);
698
699     model_targetItems_transitionFrom.clear();
700     canvas->rootObject()->setProperty("addTransitionsDone", 0);
701     canvas->rootObject()->setProperty("targetTrans_items", QVariantMap());
702     canvas->rootObject()->setProperty("targetTrans_targetIndexes", QVariantList());
703     canvas->rootObject()->setProperty("targetTrans_targetItems", QVariantList());
704
705     // do insertion
706     targetData.clear();
707     targetIndexes.clear();
708     for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) {
709         targetData << qMakePair(QString("New item %1").arg(i), QString(""));
710         targetIndexes << i;
711     }
712     model.insertItems(insertionIndex, targetData);
713     QTRY_COMPARE(model.count(), positioner->property("count").toInt());
714
715     targetItems = findItems<QQuickItem>(positioner, "wrapper", targetIndexes);
716
717     QTRY_COMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), targetData.count());
718     QTRY_COMPARE(canvas->rootObject()->property("displaceTransitionsDone").toInt(), expectedDisplacedIndexes.count());
719
720     // check the target and displaced items were animated
721     model_targetItems_transitionFrom.matchAgainst(targetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
722     model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
723
724     // check attached properties
725     matchItemsAndIndexes(canvas->rootObject()->property("targetTrans_items").toMap(), model, targetIndexes);
726     matchIndexLists(canvas->rootObject()->property("targetTrans_targetIndexes").toList(), targetIndexes);
727     matchItemLists(canvas->rootObject()->property("targetTrans_targetItems").toList(), targetItems);
728     if (expectedDisplacedIndexes.isValid()) {
729         // adjust expectedDisplacedIndexes to their final values after the move
730         QList<int> displacedIndexes = adjustIndexesForAddDisplaced(expectedDisplacedIndexes.indexes, insertionIndex, insertionCount);
731         matchItemsAndIndexes(canvas->rootObject()->property("displacedTrans_items").toMap(), model, displacedIndexes);
732         matchIndexLists(canvas->rootObject()->property("displacedTrans_targetIndexes").toList(), targetIndexes);
733         matchItemLists(canvas->rootObject()->property("displacedTrans_targetItems").toList(), targetItems);
734     }
735
736     checkItemPositions(positioner, &model, canvas->rootObject()->property("incrementalSize").toInt());
737
738     delete canvas;
739 }
740
741 void tst_qquickpositioners::addTransitions_data()
742 {
743     // If this data changes, update addTransitions_grid_data() also
744
745     QTest::addColumn<int>("initialItemCount");
746     QTest::addColumn<int>("insertionIndex");
747     QTest::addColumn<int>("insertionCount");
748     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
749
750     QTest::newRow("add one @ start") << 10 << 0 << 1 << ListRange(0, 9);
751     QTest::newRow("add one @ middle") << 10 << 5 << 1 << ListRange(5, 9);
752     QTest::newRow("add one @ end") << 10 << 10 << 1 << ListRange();
753
754     QTest::newRow("add multiple @ start") << 10 << 0 << 3 << ListRange(0, 9);
755     QTest::newRow("add multiple @ middle") << 10 << 5 << 3 << ListRange(5, 9);
756     QTest::newRow("add multiple @ end") << 10 << 10 << 3 << ListRange();
757 }
758
759 void tst_qquickpositioners::moveTransitions(const QString &positionerObjectName)
760 {
761     QFETCH(int, initialItemCount);
762     QFETCH(ListChange, change);
763     QFETCH(ListRange, expectedDisplacedIndexes);
764
765     QPointF targetItems_transitionFrom(-50, -50);
766     QPointF displacedItems_transitionVia(100, 100);
767
768     QaimModel model;
769     for (int i = 0; i < initialItemCount; i++)
770         model.addItem("Item" + QString::number(i), "");
771     QaimModel model_targetItems_transitionFrom;
772     QaimModel model_displacedItems_transitionVia;
773
774     QQuickView *canvas = QQuickViewTestUtil::createView();
775     QQmlContext *ctxt = canvas->rootContext();
776     ctxt->setContextProperty("usePopulateTransition", QVariant(false));
777     ctxt->setContextProperty("enableAddTransition", QVariant(false));
778     ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
779     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
780     ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
781     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
782     canvas->setSource(testFileUrl("transitions.qml"));
783     canvas->show();
784     qApp->processEvents();
785
786     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
787
788     QQuickItem *positioner = canvas->rootObject()->findChild<QQuickItem*>(positionerObjectName);
789     QVERIFY(positioner);
790     positioner->findChild<QQuickItem*>("repeater")->setProperty("model", QVariant::fromValue(&model));
791     QTRY_COMPARE(QQuickItemPrivate::get(positioner)->polishScheduled, false);
792
793     switch (change.type) {
794         case ListChange::Removed:
795             model.removeItems(change.index, change.count);
796             QTRY_COMPARE(model.count(), positioner->property("count").toInt());
797             break;
798         case ListChange::Moved:
799             model.moveItems(change.index, change.to, change.count);
800             QTRY_COMPARE(QQuickItemPrivate::get(positioner)->polishScheduled, false);
801             break;
802         case ListChange::Inserted:
803         case ListChange::SetCurrent:
804         case ListChange::SetContentY:
805             QVERIFY(false);
806             break;
807          case ListChange::Polish:
808             break;
809     }
810
811     QTRY_COMPARE(canvas->rootObject()->property("displaceTransitionsDone").toInt(), expectedDisplacedIndexes.count());
812     QCOMPARE(canvas->rootObject()->property("addTransitionsDone").toInt(), 0);
813
814     // check the target and displaced items were animated
815     QCOMPARE(model_targetItems_transitionFrom.count(), 0);
816     model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
817
818     // check attached properties
819     QCOMPARE(canvas->rootObject()->property("targetTrans_items").toMap().count(), 0);
820     QCOMPARE(canvas->rootObject()->property("targetTrans_targetIndexes").toList().count(), 0);
821     QCOMPARE(canvas->rootObject()->property("targetTrans_targetItems").toList().count(), 0);
822     if (expectedDisplacedIndexes.isValid()) {
823         // adjust expectedDisplacedIndexes to their final values after the move
824         QList<int> displacedIndexes;
825         if (change.type == ListChange::Inserted)
826             displacedIndexes = adjustIndexesForAddDisplaced(expectedDisplacedIndexes.indexes, change.index, change.count);
827         else if (change.type == ListChange::Moved)
828             displacedIndexes = adjustIndexesForMove(expectedDisplacedIndexes.indexes, change.index, change.to, change.count);
829         else if (change.type == ListChange::Removed)
830             displacedIndexes = adjustIndexesForRemoveDisplaced(expectedDisplacedIndexes.indexes, change.index, change.count);
831         else
832             QVERIFY(false);
833         matchItemsAndIndexes(canvas->rootObject()->property("displacedTrans_items").toMap(), model, displacedIndexes);
834
835         QVariantList listOfEmptyIntLists;
836         for (int i=0; i<displacedIndexes.count(); i++)
837             listOfEmptyIntLists << QVariant::fromValue(QList<int>());
838         QCOMPARE(canvas->rootObject()->property("displacedTrans_targetIndexes").toList(), listOfEmptyIntLists);
839         QVariantList listOfEmptyObjectLists;
840         for (int i=0; i<displacedIndexes.count(); i++)
841             listOfEmptyObjectLists.insert(listOfEmptyObjectLists.count(), QVariantList());
842         QCOMPARE(canvas->rootObject()->property("displacedTrans_targetItems").toList(), listOfEmptyObjectLists);
843     }
844
845     checkItemPositions(positioner, &model, canvas->rootObject()->property("incrementalSize").toInt());
846
847     delete canvas;
848 }
849
850 void tst_qquickpositioners::moveTransitions_data()
851 {
852     // If this data changes, update moveTransitions_grid_data() also
853
854     QTest::addColumn<int>("initialItemCount");
855     QTest::addColumn<ListChange>("change");
856     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
857
858     QTest::newRow("remove one @ start") << 10 << ListChange::remove(0, 1) << ListRange(1, 9);
859     QTest::newRow("remove one @ middle") << 10 << ListChange::remove(4, 1) << ListRange(5, 9);
860     QTest::newRow("remove one @ end") << 10 << ListChange::remove(9, 1) << ListRange();
861
862     QTest::newRow("remove multiple @ start") << 10 << ListChange::remove(0, 3) << ListRange(3, 9);
863     QTest::newRow("remove multiple @ middle") << 10 << ListChange::remove(4, 3) << ListRange(7, 9);
864     QTest::newRow("remove multiple @ end") << 10 << ListChange::remove(7, 3) << ListRange();
865 }
866
867
868 void tst_qquickpositioners::checkItemPositions(QQuickItem *positioner, QaimModel *model, qreal incrementalSize)
869 {
870     QVERIFY(model->count() > 0);
871     qreal padding = 0;
872     qreal currentSize = 30;
873     qreal rowX = 0;
874     qreal rowY = 0;
875
876     for (int i=0; i<model->count(); ++i) {
877         QQuickItem *item = findItem<QQuickItem>(positioner, "wrapper", i);
878         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
879
880         QCOMPARE(item->width(), currentSize);
881         QCOMPARE(item->height(), currentSize);
882
883         if (qobject_cast<QQuickRow*>(positioner)) {
884             QCOMPARE(item->x(), (i * 30.0) + padding);
885             QCOMPARE(item->y(), 0.0);
886         } else if (qobject_cast<QQuickColumn*>(positioner)) {
887             QCOMPARE(item->x(), 0.0);
888             QCOMPARE(item->y(), (i * 30.0) + padding);
889         } else if (qobject_cast<QQuickGrid*>(positioner)) {
890             int columns = 4;
891             int rows = qCeil(model->count() / qreal(columns));
892             int lastMatchingRowIndex = (rows * columns) - (columns - i%columns);
893             if (lastMatchingRowIndex >= model->count())
894                 lastMatchingRowIndex -= columns;
895             if (i % columns > 0) {
896                 QQuickItem *finalAlignedRowItem = findItem<QQuickItem>(positioner, "wrapper", lastMatchingRowIndex);
897                 QVERIFY(finalAlignedRowItem);
898                 QCOMPARE(item->x(), finalAlignedRowItem->x());
899             } else {
900                 QCOMPARE(item->x(), 0.0);
901             }
902             if (i / columns > 0) {
903                 QQuickItem *prevRowLastItem = findItem<QQuickItem>(positioner, "wrapper", (i/columns * columns) - 1);
904                 QVERIFY(prevRowLastItem);
905                 QCOMPARE(item->y(), prevRowLastItem->y() + prevRowLastItem->height());
906             } else {
907                 QCOMPARE(item->y(), 0.0);
908             }
909         } else if (qobject_cast<QQuickFlow*>(positioner)) {
910             if (rowX + item->width() > positioner->width()) {
911                 QQuickItem *prevItem = findItem<QQuickItem>(positioner, "wrapper", i-1);
912                 QVERIFY(prevItem);
913                 rowX = 0;
914                 rowY = prevItem->y() + prevItem->height();
915             }
916             QCOMPARE(item->x(), rowX);
917             QCOMPARE(item->y(), rowY);
918             rowX += item->width();
919         } else {
920             QVERIFY2(false, "Unknown positioner type");
921         }
922         QQuickText *name = findItem<QQuickText>(positioner, "name", i);
923         QVERIFY(name != 0);
924         QTRY_COMPARE(name->text(), model->name(i));
925
926         padding += i * incrementalSize;
927         currentSize += incrementalSize;
928     }
929 }
930
931 void tst_qquickpositioners::test_vertical()
932 {
933     QQuickView *canvas = createView(testFile("vertical.qml"));
934
935     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
936     QVERIFY(one != 0);
937
938     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
939     QVERIFY(two != 0);
940
941     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
942     QVERIFY(three != 0);
943
944     QCOMPARE(one->x(), 0.0);
945     QCOMPARE(one->y(), 0.0);
946     QCOMPARE(two->x(), 0.0);
947     QCOMPARE(two->y(), 50.0);
948     QCOMPARE(three->x(), 0.0);
949     QCOMPARE(three->y(), 60.0);
950
951     QQuickItem *column = canvas->rootObject()->findChild<QQuickItem*>("column");
952     QVERIFY(column);
953     QCOMPARE(column->height(), 80.0);
954     QCOMPARE(column->width(), 50.0);
955
956     delete canvas;
957 }
958
959 void tst_qquickpositioners::test_vertical_spacing()
960 {
961     QQuickView *canvas = createView(testFile("vertical-spacing.qml"));
962
963     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
964     QVERIFY(one != 0);
965
966     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
967     QVERIFY(two != 0);
968
969     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
970     QVERIFY(three != 0);
971
972     QCOMPARE(one->x(), 0.0);
973     QCOMPARE(one->y(), 0.0);
974     QCOMPARE(two->x(), 0.0);
975     QCOMPARE(two->y(), 60.0);
976     QCOMPARE(three->x(), 0.0);
977     QCOMPARE(three->y(), 80.0);
978
979     QQuickItem *column = canvas->rootObject()->findChild<QQuickItem*>("column");
980     QCOMPARE(column->height(), 100.0);
981     QCOMPARE(column->width(), 50.0);
982
983     delete canvas;
984 }
985
986 void tst_qquickpositioners::test_vertical_animated()
987 {
988     QQuickView *canvas = createView(testFile("vertical-animated.qml"), false);
989
990     //Note that they animate in
991     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
992     QVERIFY(one != 0);
993     QCOMPARE(one->y(), -100.0);
994
995     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
996     QVERIFY(two != 0);
997     QCOMPARE(two->y(), -100.0);
998
999     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1000     QVERIFY(three != 0);
1001     QCOMPARE(three->y(), -100.0);
1002
1003     QTest::qWaitForWindowShown(canvas); //It may not relayout until the next frame, so it needs to be drawn
1004
1005     QQuickItem *column = canvas->rootObject()->findChild<QQuickItem*>("column");
1006     QVERIFY(column);
1007     QCOMPARE(column->height(), 100.0);
1008     QCOMPARE(column->width(), 50.0);
1009
1010     //QTRY_COMPARE used instead of waiting for the expected time of animation completion
1011     //Note that this means the duration of the animation is NOT tested
1012
1013     QTRY_COMPARE(one->y(), 0.0);
1014     QTRY_COMPARE(one->x(), 0.0);
1015     QTRY_COMPARE(two->isVisible(), false);
1016     QTRY_COMPARE(two->y(), -100.0);//Not 'in' yet
1017     QTRY_COMPARE(two->x(), 0.0);
1018     QTRY_COMPARE(three->y(), 50.0);
1019     QTRY_COMPARE(three->x(), 0.0);
1020
1021     //Add 'two'
1022     two->setVisible(true);
1023     QTRY_COMPARE(two->isVisible(), true);
1024     QTRY_COMPARE(column->height(), 150.0);
1025     QTRY_COMPARE(column->width(), 50.0);
1026     QTest::qWait(0);//Let the animation start
1027     QVERIFY(two->y() >= -100.0 && two->y() < 50.0);
1028     QVERIFY(three->y() >= 50.0 && three->y() < 100.0);
1029
1030     QTRY_COMPARE(two->y(), 50.0);
1031     QTRY_COMPARE(three->y(), 100.0);
1032
1033     delete canvas;
1034 }
1035
1036 void tst_qquickpositioners::test_grid()
1037 {
1038     QQuickView *canvas = createView(testFile("gridtest.qml"));
1039
1040     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1041     QVERIFY(one != 0);
1042     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1043     QVERIFY(two != 0);
1044     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1045     QVERIFY(three != 0);
1046     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1047     QVERIFY(four != 0);
1048     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1049     QVERIFY(five != 0);
1050
1051     QCOMPARE(one->x(), 0.0);
1052     QCOMPARE(one->y(), 0.0);
1053     QCOMPARE(two->x(), 50.0);
1054     QCOMPARE(two->y(), 0.0);
1055     QCOMPARE(three->x(), 70.0);
1056     QCOMPARE(three->y(), 0.0);
1057     QCOMPARE(four->x(), 0.0);
1058     QCOMPARE(four->y(), 50.0);
1059     QCOMPARE(five->x(), 50.0);
1060     QCOMPARE(five->y(), 50.0);
1061
1062     QQuickGrid *grid = canvas->rootObject()->findChild<QQuickGrid*>("grid");
1063     QCOMPARE(grid->flow(), QQuickGrid::LeftToRight);
1064     QCOMPARE(grid->width(), 100.0);
1065     QCOMPARE(grid->height(), 100.0);
1066
1067     delete canvas;
1068 }
1069
1070 void tst_qquickpositioners::test_grid_topToBottom()
1071 {
1072     QQuickView *canvas = createView(testFile("grid-toptobottom.qml"));
1073
1074     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1075     QVERIFY(one != 0);
1076     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1077     QVERIFY(two != 0);
1078     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1079     QVERIFY(three != 0);
1080     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1081     QVERIFY(four != 0);
1082     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1083     QVERIFY(five != 0);
1084
1085     QCOMPARE(one->x(), 0.0);
1086     QCOMPARE(one->y(), 0.0);
1087     QCOMPARE(two->x(), 0.0);
1088     QCOMPARE(two->y(), 50.0);
1089     QCOMPARE(three->x(), 0.0);
1090     QCOMPARE(three->y(), 100.0);
1091     QCOMPARE(four->x(), 50.0);
1092     QCOMPARE(four->y(), 0.0);
1093     QCOMPARE(five->x(), 50.0);
1094     QCOMPARE(five->y(), 50.0);
1095
1096     QQuickGrid *grid = canvas->rootObject()->findChild<QQuickGrid*>("grid");
1097     QCOMPARE(grid->flow(), QQuickGrid::TopToBottom);
1098     QCOMPARE(grid->width(), 100.0);
1099     QCOMPARE(grid->height(), 120.0);
1100
1101     delete canvas;
1102 }
1103
1104 void tst_qquickpositioners::test_grid_rightToLeft()
1105 {
1106     QQuickView *canvas = createView(testFile("gridtest.qml"));
1107
1108     canvas->rootObject()->setProperty("testRightToLeft", true);
1109
1110     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1111     QVERIFY(one != 0);
1112     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1113     QVERIFY(two != 0);
1114     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1115     QVERIFY(three != 0);
1116     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1117     QVERIFY(four != 0);
1118     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1119     QVERIFY(five != 0);
1120
1121     QCOMPARE(one->x(), 50.0);
1122     QCOMPARE(one->y(), 0.0);
1123     QCOMPARE(two->x(), 30.0);
1124     QCOMPARE(two->y(), 0.0);
1125     QCOMPARE(three->x(), 0.0);
1126     QCOMPARE(three->y(), 0.0);
1127     QCOMPARE(four->x(), 50.0);
1128     QCOMPARE(four->y(), 50.0);
1129     QCOMPARE(five->x(), 40.0);
1130     QCOMPARE(five->y(), 50.0);
1131
1132     QQuickGrid *grid = canvas->rootObject()->findChild<QQuickGrid*>("grid");
1133     QCOMPARE(grid->layoutDirection(), Qt::RightToLeft);
1134     QCOMPARE(grid->width(), 100.0);
1135     QCOMPARE(grid->height(), 100.0);
1136
1137     // Change the width of the grid and check that items stay to the right
1138     grid->setWidth(200);
1139     QTRY_COMPARE(one->x(), 150.0);
1140     QCOMPARE(one->y(), 0.0);
1141     QCOMPARE(two->x(), 130.0);
1142     QCOMPARE(two->y(), 0.0);
1143     QCOMPARE(three->x(), 100.0);
1144     QCOMPARE(three->y(), 0.0);
1145     QCOMPARE(four->x(), 150.0);
1146     QCOMPARE(four->y(), 50.0);
1147     QCOMPARE(five->x(), 140.0);
1148     QCOMPARE(five->y(), 50.0);
1149
1150     delete canvas;
1151 }
1152
1153 void tst_qquickpositioners::test_grid_spacing()
1154 {
1155     QQuickView *canvas = createView(testFile("grid-spacing.qml"));
1156
1157     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1158     QVERIFY(one != 0);
1159     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1160     QVERIFY(two != 0);
1161     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1162     QVERIFY(three != 0);
1163     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1164     QVERIFY(four != 0);
1165     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1166     QVERIFY(five != 0);
1167
1168     QCOMPARE(one->x(), 0.0);
1169     QCOMPARE(one->y(), 0.0);
1170     QCOMPARE(two->x(), 54.0);
1171     QCOMPARE(two->y(), 0.0);
1172     QCOMPARE(three->x(), 78.0);
1173     QCOMPARE(three->y(), 0.0);
1174     QCOMPARE(four->x(), 0.0);
1175     QCOMPARE(four->y(), 54.0);
1176     QCOMPARE(five->x(), 54.0);
1177     QCOMPARE(five->y(), 54.0);
1178
1179     QQuickItem *grid = canvas->rootObject()->findChild<QQuickItem*>("grid");
1180     QCOMPARE(grid->width(), 128.0);
1181     QCOMPARE(grid->height(), 104.0);
1182
1183     delete canvas;
1184 }
1185
1186 void tst_qquickpositioners::test_grid_row_column_spacing()
1187 {
1188     QQuickView *canvas = createView(testFile("grid-row-column-spacing.qml"));
1189
1190     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1191     QVERIFY(one != 0);
1192     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1193     QVERIFY(two != 0);
1194     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1195     QVERIFY(three != 0);
1196     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1197     QVERIFY(four != 0);
1198     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1199     QVERIFY(five != 0);
1200
1201     QCOMPARE(one->x(), 0.0);
1202     QCOMPARE(one->y(), 0.0);
1203     QCOMPARE(two->x(), 61.0);
1204     QCOMPARE(two->y(), 0.0);
1205     QCOMPARE(three->x(), 92.0);
1206     QCOMPARE(three->y(), 0.0);
1207     QCOMPARE(four->x(), 0.0);
1208     QCOMPARE(four->y(), 57.0);
1209     QCOMPARE(five->x(), 61.0);
1210     QCOMPARE(five->y(), 57.0);
1211
1212     QQuickItem *grid = canvas->rootObject()->findChild<QQuickItem*>("grid");
1213     QCOMPARE(grid->width(), 142.0);
1214     QCOMPARE(grid->height(), 107.0);
1215
1216     delete canvas;
1217 }
1218
1219 void tst_qquickpositioners::test_grid_animated()
1220 {
1221     QQuickView *canvas = createView(testFile("grid-animated.qml"), false);
1222
1223     canvas->rootObject()->setProperty("testRightToLeft", false);
1224
1225     //Note that all animate in
1226     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1227     QVERIFY(one != 0);
1228     QCOMPARE(one->x(), -100.0);
1229     QCOMPARE(one->y(), -100.0);
1230
1231     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1232     QVERIFY(two != 0);
1233     QCOMPARE(two->x(), -100.0);
1234     QCOMPARE(two->y(), -100.0);
1235
1236     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1237     QVERIFY(three != 0);
1238     QCOMPARE(three->x(), -100.0);
1239     QCOMPARE(three->y(), -100.0);
1240
1241     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1242     QVERIFY(four != 0);
1243     QCOMPARE(four->x(), -100.0);
1244     QCOMPARE(four->y(), -100.0);
1245
1246     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1247     QVERIFY(five != 0);
1248     QCOMPARE(five->x(), -100.0);
1249     QCOMPARE(five->y(), -100.0);
1250
1251     QTest::qWaitForWindowShown(canvas); //It may not relayout until the next frame, so it needs to be drawn
1252
1253     QQuickItem *grid = canvas->rootObject()->findChild<QQuickItem*>("grid");
1254     QVERIFY(grid);
1255     QCOMPARE(grid->width(), 150.0);
1256     QCOMPARE(grid->height(), 100.0);
1257
1258     //QTRY_COMPARE used instead of waiting for the expected time of animation completion
1259     //Note that this means the duration of the animation is NOT tested
1260
1261     QTRY_COMPARE(one->y(), 0.0);
1262     QTRY_COMPARE(one->x(), 0.0);
1263     QTRY_COMPARE(two->isVisible(), false);
1264     QTRY_COMPARE(two->y(), -100.0);
1265     QTRY_COMPARE(two->x(), -100.0);
1266     QTRY_COMPARE(three->y(), 0.0);
1267     QTRY_COMPARE(three->x(), 50.0);
1268     QTRY_COMPARE(four->y(), 0.0);
1269     QTRY_COMPARE(four->x(), 100.0);
1270     QTRY_COMPARE(five->y(), 50.0);
1271     QTRY_COMPARE(five->x(), 0.0);
1272
1273     //Add 'two'
1274     two->setVisible(true);
1275     QCOMPARE(two->isVisible(), true);
1276     QCOMPARE(grid->width(), 150.0);
1277     QCOMPARE(grid->height(), 100.0);
1278     QTest::qWait(0);//Let the animation start
1279     QCOMPARE(two->x(), -100.0);
1280     QCOMPARE(two->y(), -100.0);
1281     QCOMPARE(one->x(), 0.0);
1282     QCOMPARE(one->y(), 0.0);
1283     QCOMPARE(three->x(), 50.0);
1284     QCOMPARE(three->y(), 0.0);
1285     QCOMPARE(four->x(), 100.0);
1286     QCOMPARE(four->y(), 0.0);
1287     QCOMPARE(five->x(), 0.0);
1288     QCOMPARE(five->y(), 50.0);
1289     //Let the animation complete
1290     QTRY_COMPARE(two->x(), 50.0);
1291     QTRY_COMPARE(two->y(), 0.0);
1292     QTRY_COMPARE(one->x(), 0.0);
1293     QTRY_COMPARE(one->y(), 0.0);
1294     QTRY_COMPARE(three->x(), 100.0);
1295     QTRY_COMPARE(three->y(), 0.0);
1296     QTRY_COMPARE(four->x(), 0.0);
1297     QTRY_COMPARE(four->y(), 50.0);
1298     QTRY_COMPARE(five->x(), 50.0);
1299     QTRY_COMPARE(five->y(), 50.0);
1300
1301     delete canvas;
1302 }
1303
1304 void tst_qquickpositioners::test_grid_animated_rightToLeft()
1305 {
1306     QQuickView *canvas = createView(testFile("grid-animated.qml"), false);
1307
1308     canvas->rootObject()->setProperty("testRightToLeft", true);
1309
1310     //Note that all animate in
1311     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1312     QVERIFY(one != 0);
1313     QCOMPARE(one->x(), -100.0);
1314     QCOMPARE(one->y(), -100.0);
1315
1316     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1317     QVERIFY(two != 0);
1318     QCOMPARE(two->x(), -100.0);
1319     QCOMPARE(two->y(), -100.0);
1320
1321     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1322     QVERIFY(three != 0);
1323     QCOMPARE(three->x(), -100.0);
1324     QCOMPARE(three->y(), -100.0);
1325
1326     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1327     QVERIFY(four != 0);
1328     QCOMPARE(four->x(), -100.0);
1329     QCOMPARE(four->y(), -100.0);
1330
1331     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1332     QVERIFY(five != 0);
1333     QCOMPARE(five->x(), -100.0);
1334     QCOMPARE(five->y(), -100.0);
1335
1336     QTest::qWaitForWindowShown(canvas); //It may not relayout until the next frame, so it needs to be drawn
1337
1338     QQuickItem *grid = canvas->rootObject()->findChild<QQuickItem*>("grid");
1339     QVERIFY(grid);
1340     QCOMPARE(grid->width(), 150.0);
1341     QCOMPARE(grid->height(), 100.0);
1342
1343     //QTRY_COMPARE used instead of waiting for the expected time of animation completion
1344     //Note that this means the duration of the animation is NOT tested
1345
1346     QTRY_COMPARE(one->y(), 0.0);
1347     QTRY_COMPARE(one->x(), 100.0);
1348     QTRY_COMPARE(two->isVisible(), false);
1349     QTRY_COMPARE(two->y(), -100.0);
1350     QTRY_COMPARE(two->x(), -100.0);
1351     QTRY_COMPARE(three->y(), 0.0);
1352     QTRY_COMPARE(three->x(), 50.0);
1353     QTRY_COMPARE(four->y(), 0.0);
1354     QTRY_COMPARE(four->x(), 0.0);
1355     QTRY_COMPARE(five->y(), 50.0);
1356     QTRY_COMPARE(five->x(), 100.0);
1357
1358     //Add 'two'
1359     two->setVisible(true);
1360     QCOMPARE(two->isVisible(), true);
1361     QCOMPARE(grid->width(), 150.0);
1362     QCOMPARE(grid->height(), 100.0);
1363     QTest::qWait(0);//Let the animation start
1364     QCOMPARE(two->x(), -100.0);
1365     QCOMPARE(two->y(), -100.0);
1366     QCOMPARE(one->x(), 100.0);
1367     QCOMPARE(one->y(), 0.0);
1368     QCOMPARE(three->x(), 50.0);
1369     QCOMPARE(three->y(), 0.0);
1370     QCOMPARE(four->x(), 0.0);
1371     QCOMPARE(four->y(), 0.0);
1372     QCOMPARE(five->x(), 100.0);
1373     QCOMPARE(five->y(), 50.0);
1374     //Let the animation complete
1375     QTRY_COMPARE(two->x(), 50.0);
1376     QTRY_COMPARE(two->y(), 0.0);
1377     QTRY_COMPARE(one->x(), 100.0);
1378     QTRY_COMPARE(one->y(), 0.0);
1379     QTRY_COMPARE(three->x(), 0.0);
1380     QTRY_COMPARE(three->y(), 0.0);
1381     QTRY_COMPARE(four->x(), 100.0);
1382     QTRY_COMPARE(four->y(), 50.0);
1383     QTRY_COMPARE(five->x(), 50.0);
1384     QTRY_COMPARE(five->y(), 50.0);
1385
1386     delete canvas;
1387 }
1388
1389 void tst_qquickpositioners::test_grid_zero_columns()
1390 {
1391     QQuickView *canvas = createView(testFile("gridzerocolumns.qml"));
1392
1393     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1394     QVERIFY(one != 0);
1395     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1396     QVERIFY(two != 0);
1397     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1398     QVERIFY(three != 0);
1399     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1400     QVERIFY(four != 0);
1401     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1402     QVERIFY(five != 0);
1403
1404     QCOMPARE(one->x(), 0.0);
1405     QCOMPARE(one->y(), 0.0);
1406     QCOMPARE(two->x(), 50.0);
1407     QCOMPARE(two->y(), 0.0);
1408     QCOMPARE(three->x(), 70.0);
1409     QCOMPARE(three->y(), 0.0);
1410     QCOMPARE(four->x(), 120.0);
1411     QCOMPARE(four->y(), 0.0);
1412     QCOMPARE(five->x(), 0.0);
1413     QCOMPARE(five->y(), 50.0);
1414
1415     QQuickItem *grid = canvas->rootObject()->findChild<QQuickItem*>("grid");
1416     QCOMPARE(grid->width(), 170.0);
1417     QCOMPARE(grid->height(), 60.0);
1418
1419     delete canvas;
1420 }
1421
1422 void tst_qquickpositioners::test_propertychanges()
1423 {
1424     QQuickView *canvas = createView(testFile("propertychangestest.qml"));
1425
1426     QQuickGrid *grid = qobject_cast<QQuickGrid*>(canvas->rootObject());
1427     QVERIFY(grid != 0);
1428     QQuickTransition *rowTransition = canvas->rootObject()->findChild<QQuickTransition*>("rowTransition");
1429     QQuickTransition *columnTransition = canvas->rootObject()->findChild<QQuickTransition*>("columnTransition");
1430
1431     QSignalSpy addSpy(grid, SIGNAL(addChanged()));
1432     QSignalSpy moveSpy(grid, SIGNAL(moveChanged()));
1433     QSignalSpy columnsSpy(grid, SIGNAL(columnsChanged()));
1434     QSignalSpy rowsSpy(grid, SIGNAL(rowsChanged()));
1435
1436     QVERIFY(grid);
1437     QVERIFY(rowTransition);
1438     QVERIFY(columnTransition);
1439     QCOMPARE(grid->add(), columnTransition);
1440     QCOMPARE(grid->move(), columnTransition);
1441     QCOMPARE(grid->columns(), 4);
1442     QCOMPARE(grid->rows(), -1);
1443
1444     grid->setAdd(rowTransition);
1445     grid->setMove(rowTransition);
1446     QCOMPARE(grid->add(), rowTransition);
1447     QCOMPARE(grid->move(), rowTransition);
1448     QCOMPARE(addSpy.count(),1);
1449     QCOMPARE(moveSpy.count(),1);
1450
1451     grid->setAdd(rowTransition);
1452     grid->setMove(rowTransition);
1453     QCOMPARE(addSpy.count(),1);
1454     QCOMPARE(moveSpy.count(),1);
1455
1456     grid->setAdd(0);
1457     grid->setMove(0);
1458     QCOMPARE(addSpy.count(),2);
1459     QCOMPARE(moveSpy.count(),2);
1460
1461     grid->setColumns(-1);
1462     grid->setRows(3);
1463     QCOMPARE(grid->columns(), -1);
1464     QCOMPARE(grid->rows(), 3);
1465     QCOMPARE(columnsSpy.count(),1);
1466     QCOMPARE(rowsSpy.count(),1);
1467
1468     grid->setColumns(-1);
1469     grid->setRows(3);
1470     QCOMPARE(columnsSpy.count(),1);
1471     QCOMPARE(rowsSpy.count(),1);
1472
1473     grid->setColumns(2);
1474     grid->setRows(2);
1475     QCOMPARE(columnsSpy.count(),2);
1476     QCOMPARE(rowsSpy.count(),2);
1477
1478     delete canvas;
1479 }
1480
1481 void tst_qquickpositioners::test_repeater()
1482 {
1483     QQuickView *canvas = createView(testFile("repeatertest.qml"));
1484
1485     QQuickRectangle *one = findItem<QQuickRectangle>(canvas->rootItem(), "one");
1486     QVERIFY(one != 0);
1487
1488     QQuickRectangle *two = findItem<QQuickRectangle>(canvas->rootItem(), "two");
1489     QVERIFY(two != 0);
1490
1491     QQuickRectangle *three = findItem<QQuickRectangle>(canvas->rootItem(), "three");
1492     QVERIFY(three != 0);
1493
1494     QCOMPARE(one->x(), 0.0);
1495     QCOMPARE(one->y(), 0.0);
1496     QCOMPARE(two->x(), 50.0);
1497     QCOMPARE(two->y(), 0.0);
1498     QCOMPARE(three->x(), 100.0);
1499     QCOMPARE(three->y(), 0.0);
1500
1501     delete canvas;
1502 }
1503
1504 void tst_qquickpositioners::test_flow()
1505 {
1506     QQuickView *canvas = createView(testFile("flowtest.qml"));
1507
1508     canvas->rootObject()->setProperty("testRightToLeft", false);
1509
1510     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1511     QVERIFY(one != 0);
1512     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1513     QVERIFY(two != 0);
1514     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1515     QVERIFY(three != 0);
1516     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1517     QVERIFY(four != 0);
1518     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1519     QVERIFY(five != 0);
1520
1521     QCOMPARE(one->x(), 0.0);
1522     QCOMPARE(one->y(), 0.0);
1523     QCOMPARE(two->x(), 50.0);
1524     QCOMPARE(two->y(), 0.0);
1525     QCOMPARE(three->x(), 0.0);
1526     QCOMPARE(three->y(), 50.0);
1527     QCOMPARE(four->x(), 0.0);
1528     QCOMPARE(four->y(), 70.0);
1529     QCOMPARE(five->x(), 50.0);
1530     QCOMPARE(five->y(), 70.0);
1531
1532     QQuickItem *flow = canvas->rootObject()->findChild<QQuickItem*>("flow");
1533     QVERIFY(flow);
1534     QCOMPARE(flow->width(), 90.0);
1535     QCOMPARE(flow->height(), 120.0);
1536
1537     delete canvas;
1538 }
1539
1540 void tst_qquickpositioners::test_flow_rightToLeft()
1541 {
1542     QQuickView *canvas = createView(testFile("flowtest.qml"));
1543
1544     canvas->rootObject()->setProperty("testRightToLeft", true);
1545
1546     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1547     QVERIFY(one != 0);
1548     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1549     QVERIFY(two != 0);
1550     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1551     QVERIFY(three != 0);
1552     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1553     QVERIFY(four != 0);
1554     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1555     QVERIFY(five != 0);
1556
1557     QCOMPARE(one->x(), 40.0);
1558     QCOMPARE(one->y(), 0.0);
1559     QCOMPARE(two->x(), 20.0);
1560     QCOMPARE(two->y(), 0.0);
1561     QCOMPARE(three->x(), 40.0);
1562     QCOMPARE(three->y(), 50.0);
1563     QCOMPARE(four->x(), 40.0);
1564     QCOMPARE(four->y(), 70.0);
1565     QCOMPARE(five->x(), 30.0);
1566     QCOMPARE(five->y(), 70.0);
1567
1568     QQuickItem *flow = canvas->rootObject()->findChild<QQuickItem*>("flow");
1569     QVERIFY(flow);
1570     QCOMPARE(flow->width(), 90.0);
1571     QCOMPARE(flow->height(), 120.0);
1572
1573     delete canvas;
1574 }
1575
1576 void tst_qquickpositioners::test_flow_topToBottom()
1577 {
1578     QQuickView *canvas = createView(testFile("flowtest-toptobottom.qml"));
1579
1580     canvas->rootObject()->setProperty("testRightToLeft", false);
1581
1582     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1583     QVERIFY(one != 0);
1584     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1585     QVERIFY(two != 0);
1586     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1587     QVERIFY(three != 0);
1588     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1589     QVERIFY(four != 0);
1590     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1591     QVERIFY(five != 0);
1592
1593     QCOMPARE(one->x(), 0.0);
1594     QCOMPARE(one->y(), 0.0);
1595     QCOMPARE(two->x(), 50.0);
1596     QCOMPARE(two->y(), 0.0);
1597     QCOMPARE(three->x(), 50.0);
1598     QCOMPARE(three->y(), 50.0);
1599     QCOMPARE(four->x(), 100.0);
1600     QCOMPARE(four->y(), 00.0);
1601     QCOMPARE(five->x(), 100.0);
1602     QCOMPARE(five->y(), 50.0);
1603
1604     QQuickItem *flow = canvas->rootObject()->findChild<QQuickItem*>("flow");
1605     QVERIFY(flow);
1606     QCOMPARE(flow->height(), 90.0);
1607     QCOMPARE(flow->width(), 150.0);
1608
1609     canvas->rootObject()->setProperty("testRightToLeft", true);
1610
1611     QVERIFY(flow);
1612     QCOMPARE(flow->height(), 90.0);
1613     QCOMPARE(flow->width(), 150.0);
1614
1615     QCOMPARE(one->x(), 100.0);
1616     QCOMPARE(one->y(), 0.0);
1617     QCOMPARE(two->x(), 80.0);
1618     QCOMPARE(two->y(), 0.0);
1619     QCOMPARE(three->x(), 50.0);
1620     QCOMPARE(three->y(), 50.0);
1621     QCOMPARE(four->x(), 0.0);
1622     QCOMPARE(four->y(), 0.0);
1623     QCOMPARE(five->x(), 40.0);
1624     QCOMPARE(five->y(), 50.0);
1625
1626     delete canvas;
1627 }
1628
1629 void tst_qquickpositioners::test_flow_resize()
1630 {
1631     QQuickView *canvas = createView(testFile("flowtest.qml"));
1632
1633     QQuickItem *root = qobject_cast<QQuickItem*>(canvas->rootObject());
1634     QVERIFY(root);
1635     root->setWidth(125);
1636     root->setProperty("testRightToLeft", false);
1637
1638     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1639     QVERIFY(one != 0);
1640     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1641     QVERIFY(two != 0);
1642     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1643     QVERIFY(three != 0);
1644     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1645     QVERIFY(four != 0);
1646     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1647     QVERIFY(five != 0);
1648
1649     QTRY_COMPARE(one->x(), 0.0);
1650     QTRY_COMPARE(one->y(), 0.0);
1651     QTRY_COMPARE(two->x(), 50.0);
1652     QTRY_COMPARE(two->y(), 0.0);
1653     QTRY_COMPARE(three->x(), 70.0);
1654     QTRY_COMPARE(three->y(), 0.0);
1655     QTRY_COMPARE(four->x(), 0.0);
1656     QTRY_COMPARE(four->y(), 50.0);
1657     QTRY_COMPARE(five->x(), 50.0);
1658     QTRY_COMPARE(five->y(), 50.0);
1659
1660     delete canvas;
1661 }
1662
1663 void tst_qquickpositioners::test_flow_resize_rightToLeft()
1664 {
1665     QQuickView *canvas = createView(testFile("flowtest.qml"));
1666
1667     QQuickItem *root = qobject_cast<QQuickItem*>(canvas->rootObject());
1668     QVERIFY(root);
1669     root->setWidth(125);
1670     root->setProperty("testRightToLeft", true);
1671
1672     QQuickRectangle *one = canvas->rootObject()->findChild<QQuickRectangle*>("one");
1673     QTRY_VERIFY(one != 0);
1674     QQuickRectangle *two = canvas->rootObject()->findChild<QQuickRectangle*>("two");
1675     QVERIFY(two != 0);
1676     QQuickRectangle *three = canvas->rootObject()->findChild<QQuickRectangle*>("three");
1677     QVERIFY(three != 0);
1678     QQuickRectangle *four = canvas->rootObject()->findChild<QQuickRectangle*>("four");
1679     QVERIFY(four != 0);
1680     QQuickRectangle *five = canvas->rootObject()->findChild<QQuickRectangle*>("five");
1681     QVERIFY(five != 0);
1682
1683     QCOMPARE(one->x(), 75.0);
1684     QCOMPARE(one->y(), 0.0);
1685     QCOMPARE(two->x(), 55.0);
1686     QCOMPARE(two->y(), 0.0);
1687     QCOMPARE(three->x(), 5.0);
1688     QCOMPARE(three->y(), 0.0);
1689     QCOMPARE(four->x(), 75.0);
1690     QCOMPARE(four->y(), 50.0);
1691     QCOMPARE(five->x(), 65.0);
1692     QCOMPARE(five->y(), 50.0);
1693
1694     delete canvas;
1695 }
1696
1697 void tst_qquickpositioners::test_flow_implicit_resize()
1698 {
1699     QQuickView *canvas = createView(testFile("flow-testimplicitsize.qml"));
1700     QVERIFY(canvas->rootObject() != 0);
1701
1702     QQuickFlow *flow = canvas->rootObject()->findChild<QQuickFlow*>("flow");
1703     QVERIFY(flow != 0);
1704
1705     QCOMPARE(flow->width(), 100.0);
1706     QCOMPARE(flow->height(), 120.0);
1707
1708     canvas->rootObject()->setProperty("flowLayout", 0);
1709     QCOMPARE(flow->flow(), QQuickFlow::LeftToRight);
1710     QCOMPARE(flow->width(), 220.0);
1711     QCOMPARE(flow->height(), 50.0);
1712
1713     canvas->rootObject()->setProperty("flowLayout", 1);
1714     QCOMPARE(flow->flow(), QQuickFlow::TopToBottom);
1715     QCOMPARE(flow->width(), 100.0);
1716     QCOMPARE(flow->height(), 120.0);
1717
1718     canvas->rootObject()->setProperty("flowLayout", 2);
1719     QCOMPARE(flow->layoutDirection(), Qt::RightToLeft);
1720     QCOMPARE(flow->width(), 220.0);
1721     QCOMPARE(flow->height(), 50.0);
1722
1723     delete canvas;
1724 }
1725
1726 QString warningMessage;
1727
1728 void interceptWarnings(QtMsgType type, const char *msg)
1729 {
1730     Q_UNUSED( type );
1731     warningMessage = msg;
1732 }
1733
1734 void tst_qquickpositioners::test_conflictinganchors()
1735 {
1736     QtMsgHandler oldMsgHandler = qInstallMsgHandler(interceptWarnings);
1737     QQmlEngine engine;
1738     QQmlComponent component(&engine);
1739
1740     component.setData("import QtQuick 2.0\nColumn { Item { width: 100; height: 100; } }", QUrl::fromLocalFile(""));
1741     QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
1742     QVERIFY(item);
1743     QVERIFY(warningMessage.isEmpty());
1744     delete item;
1745
1746     component.setData("import QtQuick 2.0\nRow { Item { width: 100; height: 100; } }", QUrl::fromLocalFile(""));
1747     item = qobject_cast<QQuickItem*>(component.create());
1748     QVERIFY(item);
1749     QVERIFY(warningMessage.isEmpty());
1750     delete item;
1751
1752     component.setData("import QtQuick 2.0\nGrid { Item { width: 100; height: 100; } }", QUrl::fromLocalFile(""));
1753     item = qobject_cast<QQuickItem*>(component.create());
1754     QVERIFY(item);
1755     QVERIFY(warningMessage.isEmpty());
1756     delete item;
1757
1758     component.setData("import QtQuick 2.0\nFlow { Item { width: 100; height: 100; } }", QUrl::fromLocalFile(""));
1759     item = qobject_cast<QQuickItem*>(component.create());
1760     QVERIFY(item);
1761     QVERIFY(warningMessage.isEmpty());
1762     delete item;
1763
1764     component.setData("import QtQuick 2.0\nColumn { Item { width: 100; height: 100; anchors.top: parent.top } }", QUrl::fromLocalFile(""));
1765     item = qobject_cast<QQuickItem*>(component.create());
1766     QVERIFY(item);
1767     QCOMPARE(warningMessage, QString("file::2:1: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function."));
1768     warningMessage.clear();
1769     delete item;
1770
1771     component.setData("import QtQuick 2.0\nColumn { Item { width: 100; height: 100; anchors.centerIn: parent } }", QUrl::fromLocalFile(""));
1772     item = qobject_cast<QQuickItem*>(component.create());
1773     QVERIFY(item);
1774     QCOMPARE(warningMessage, QString("file::2:1: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function."));
1775     warningMessage.clear();
1776     delete item;
1777
1778     component.setData("import QtQuick 2.0\nColumn { Item { width: 100; height: 100; anchors.left: parent.left } }", QUrl::fromLocalFile(""));
1779     item = qobject_cast<QQuickItem*>(component.create());
1780     QVERIFY(item);
1781     QVERIFY(warningMessage.isEmpty());
1782     warningMessage.clear();
1783     delete item;
1784
1785     component.setData("import QtQuick 2.0\nRow { Item { width: 100; height: 100; anchors.left: parent.left } }", QUrl::fromLocalFile(""));
1786     item = qobject_cast<QQuickItem*>(component.create());
1787     QVERIFY(item);
1788     QCOMPARE(warningMessage, QString("file::2:1: QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row. Row will not function."));
1789     warningMessage.clear();
1790     delete item;
1791
1792     component.setData("import QtQuick 2.0\nRow { width: 100; height: 100; Item { anchors.fill: parent } }", QUrl::fromLocalFile(""));
1793     item = qobject_cast<QQuickItem*>(component.create());
1794     QVERIFY(item);
1795     QCOMPARE(warningMessage, QString("file::2:1: QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row. Row will not function."));
1796     warningMessage.clear();
1797     delete item;
1798
1799     component.setData("import QtQuick 2.0\nRow { Item { width: 100; height: 100; anchors.top: parent.top } }", QUrl::fromLocalFile(""));
1800     item = qobject_cast<QQuickItem*>(component.create());
1801     QVERIFY(item);
1802     QVERIFY(warningMessage.isEmpty());
1803     warningMessage.clear();
1804     delete item;
1805
1806     component.setData("import QtQuick 2.0\nGrid { Item { width: 100; height: 100; anchors.horizontalCenter: parent.horizontalCenter } }", QUrl::fromLocalFile(""));
1807     item = qobject_cast<QQuickItem*>(component.create());
1808     QVERIFY(item);
1809     QCOMPARE(warningMessage, QString("file::2:1: QML Grid: Cannot specify anchors for items inside Grid. Grid will not function."));
1810     warningMessage.clear();
1811     delete item;
1812
1813     component.setData("import QtQuick 2.0\nGrid { Item { width: 100; height: 100; anchors.centerIn: parent } }", QUrl::fromLocalFile(""));
1814     item = qobject_cast<QQuickItem*>(component.create());
1815     QVERIFY(item);
1816     QCOMPARE(warningMessage, QString("file::2:1: QML Grid: Cannot specify anchors for items inside Grid. Grid will not function."));
1817     warningMessage.clear();
1818     delete item;
1819
1820     component.setData("import QtQuick 2.0\nFlow { Item { width: 100; height: 100; anchors.verticalCenter: parent.verticalCenter } }", QUrl::fromLocalFile(""));
1821     item = qobject_cast<QQuickItem*>(component.create());
1822     QVERIFY(item);
1823     QCOMPARE(warningMessage, QString("file::2:1: QML Flow: Cannot specify anchors for items inside Flow. Flow will not function."));
1824     delete item;
1825
1826     component.setData("import QtQuick 2.0\nFlow {  width: 100; height: 100; Item { anchors.fill: parent } }", QUrl::fromLocalFile(""));
1827     item = qobject_cast<QQuickItem*>(component.create());
1828     QVERIFY(item);
1829     QCOMPARE(warningMessage, QString("file::2:1: QML Flow: Cannot specify anchors for items inside Flow. Flow will not function."));
1830     qInstallMsgHandler(oldMsgHandler);
1831     delete item;
1832 }
1833
1834 void tst_qquickpositioners::test_mirroring()
1835 {
1836     QList<QString> qmlFiles;
1837     qmlFiles << "horizontal.qml" << "gridtest.qml" << "flowtest.qml";
1838     QList<QString> objectNames;
1839     objectNames << "one" << "two" << "three" << "four" << "five";
1840
1841     foreach (const QString qmlFile, qmlFiles) {
1842         QQuickView *canvasA = createView(testFile(qmlFile));
1843         QQuickItem *rootA = qobject_cast<QQuickItem*>(canvasA->rootObject());
1844
1845         QQuickView *canvasB = createView(testFile(qmlFile));
1846         QQuickItem *rootB = qobject_cast<QQuickItem*>(canvasB->rootObject());
1847
1848         rootA->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft
1849
1850         // LTR != RTL
1851         foreach (const QString objectName, objectNames) {
1852             // horizontal.qml only has three items
1853             if (qmlFile == QString("horizontal.qml") && objectName == QString("four"))
1854                 break;
1855             QQuickItem *itemA = rootA->findChild<QQuickItem*>(objectName);
1856             QQuickItem *itemB = rootB->findChild<QQuickItem*>(objectName);
1857             QTRY_VERIFY(itemA->x() != itemB->x());
1858         }
1859
1860         QQmlProperty enabledProp(rootB, "LayoutMirroring.enabled", qmlContext(rootB));
1861         enabledProp.write(true);
1862         QQmlProperty inheritProp(rootB, "LayoutMirroring.childrenInherit", qmlContext(rootB));
1863         inheritProp.write(true);
1864
1865         // RTL == mirror
1866         foreach (const QString objectName, objectNames) {
1867             // horizontal.qml only has three items
1868             if (qmlFile == QString("horizontal.qml") && objectName == QString("four"))
1869                 break;
1870             QQuickItem *itemA = rootA->findChild<QQuickItem*>(objectName);
1871             QQuickItem *itemB = rootB->findChild<QQuickItem*>(objectName);
1872             QTRY_COMPARE(itemA->x(), itemB->x());
1873         }
1874
1875         rootA->setProperty("testRightToLeft", false); // layoutDirection: Qt.LeftToRight
1876         rootB->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft
1877
1878         // LTR == RTL + mirror
1879         foreach (const QString objectName, objectNames) {
1880             // horizontal.qml only has three items
1881             if (qmlFile == QString("horizontal.qml") && objectName == QString("four"))
1882                 break;
1883             QQuickItem *itemA = rootA->findChild<QQuickItem*>(objectName);
1884             QQuickItem *itemB = rootB->findChild<QQuickItem*>(objectName);
1885             QTRY_COMPARE(itemA->x(), itemB->x());
1886         }
1887         delete canvasA;
1888         delete canvasB;
1889     }
1890 }
1891
1892 void tst_qquickpositioners::test_allInvisible()
1893 {
1894     //QTBUG-19361
1895     QQuickView *canvas = createView(testFile("allInvisible.qml"));
1896
1897     QQuickItem *root = qobject_cast<QQuickItem*>(canvas->rootObject());
1898     QVERIFY(root);
1899
1900     QQuickRow *row = canvas->rootObject()->findChild<QQuickRow*>("row");
1901     QVERIFY(row != 0);
1902     QVERIFY(row->width() == 0);
1903     QVERIFY(row->height() == 0);
1904     QQuickColumn *column = canvas->rootObject()->findChild<QQuickColumn*>("column");
1905     QVERIFY(column != 0);
1906     QVERIFY(column->width() == 0);
1907     QVERIFY(column->height() == 0);
1908 }
1909
1910 void tst_qquickpositioners::test_attachedproperties()
1911 {
1912     QFETCH(QString, filename);
1913
1914     QQuickView *canvas = createView(filename);
1915     QVERIFY(canvas->rootObject() != 0);
1916
1917     QQuickRectangle *greenRect = canvas->rootObject()->findChild<QQuickRectangle *>("greenRect");
1918     QVERIFY(greenRect != 0);
1919
1920     int posIndex = greenRect->property("posIndex").toInt();
1921     QVERIFY(posIndex == 0);
1922     bool isFirst = greenRect->property("isFirstItem").toBool();
1923     QVERIFY(isFirst == true);
1924     bool isLast = greenRect->property("isLastItem").toBool();
1925     QVERIFY(isLast == false);
1926
1927     QQuickRectangle *yellowRect = canvas->rootObject()->findChild<QQuickRectangle *>("yellowRect");
1928     QVERIFY(yellowRect != 0);
1929
1930     posIndex = yellowRect->property("posIndex").toInt();
1931     QVERIFY(posIndex == -1);
1932     isFirst = yellowRect->property("isFirstItem").toBool();
1933     QVERIFY(isFirst == false);
1934     isLast = yellowRect->property("isLastItem").toBool();
1935     QVERIFY(isLast == false);
1936
1937     yellowRect->metaObject()->invokeMethod(yellowRect, "onDemandPositioner");
1938
1939     posIndex = yellowRect->property("posIndex").toInt();
1940     QVERIFY(posIndex == 1);
1941     isFirst = yellowRect->property("isFirstItem").toBool();
1942     QVERIFY(isFirst == false);
1943     isLast = yellowRect->property("isLastItem").toBool();
1944     QVERIFY(isLast == true);
1945
1946     delete canvas;
1947 }
1948
1949 void tst_qquickpositioners::test_attachedproperties_data()
1950 {
1951     QTest::addColumn<QString>("filename");
1952
1953     QTest::newRow("column") << testFile("attachedproperties-column.qml");
1954     QTest::newRow("row") << testFile("attachedproperties-row.qml");
1955     QTest::newRow("grid") << testFile("attachedproperties-grid.qml");
1956     QTest::newRow("flow") << testFile("attachedproperties-flow.qml");
1957 }
1958
1959 void tst_qquickpositioners::test_attachedproperties_dynamic()
1960 {
1961     QQuickView *canvas = createView(testFile("attachedproperties-dynamic.qml"));
1962     QVERIFY(canvas->rootObject() != 0);
1963
1964     QQuickRow *row = canvas->rootObject()->findChild<QQuickRow *>("pos");
1965     QVERIFY(row != 0);
1966
1967     QQuickRectangle *rect0 = canvas->rootObject()->findChild<QQuickRectangle *>("rect0");
1968     QVERIFY(rect0 != 0);
1969
1970     int posIndex = rect0->property("index").toInt();
1971     QVERIFY(posIndex == 0);
1972     bool isFirst = rect0->property("firstItem").toBool();
1973     QVERIFY(isFirst == true);
1974     bool isLast = rect0->property("lastItem").toBool();
1975     QVERIFY(isLast == false);
1976
1977     QQuickRectangle *rect1 = canvas->rootObject()->findChild<QQuickRectangle *>("rect1");
1978     QVERIFY(rect1 != 0);
1979
1980     posIndex = rect1->property("index").toInt();
1981     QVERIFY(posIndex == 1);
1982     isFirst = rect1->property("firstItem").toBool();
1983     QVERIFY(isFirst == false);
1984     isLast = rect1->property("lastItem").toBool();
1985     QVERIFY(isLast == true);
1986
1987     row->metaObject()->invokeMethod(row, "createSubRect");
1988
1989     QTRY_VERIFY(rect1->property("index").toInt() == 1);
1990     QTRY_VERIFY(rect1->property("firstItem").toBool() == false);
1991     QTRY_VERIFY(rect1->property("lastItem").toBool() == false);
1992
1993     QQuickRectangle *rect2 = canvas->rootObject()->findChild<QQuickRectangle *>("rect2");
1994     QVERIFY(rect2 != 0);
1995
1996     posIndex = rect2->property("index").toInt();
1997     QVERIFY(posIndex == 2);
1998     isFirst = rect2->property("firstItem").toBool();
1999     QVERIFY(isFirst == false);
2000     isLast = rect2->property("lastItem").toBool();
2001     QVERIFY(isLast == true);
2002
2003     row->metaObject()->invokeMethod(row, "destroySubRect");
2004
2005     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2006     QCoreApplication::processEvents();
2007
2008     QTRY_VERIFY(rect1->property("index").toInt() == 1);
2009     QTRY_VERIFY(rect1->property("firstItem").toBool() == false);
2010     QTRY_VERIFY(rect1->property("lastItem").toBool() == true);
2011
2012     delete canvas;
2013 }
2014
2015 QQuickView *tst_qquickpositioners::createView(const QString &filename, bool wait)
2016 {
2017     QQuickView *canvas = new QQuickView(0);
2018     qDebug() << "1";
2019
2020     canvas->setSource(QUrl::fromLocalFile(filename));
2021     qDebug() << "2";
2022     canvas->show();
2023     qDebug() << "3";
2024     if (wait)
2025         QTest::qWaitForWindowShown(canvas); //It may not relayout until the next frame, so it needs to be drawn
2026     qDebug() << "4";
2027
2028     return canvas;
2029 }
2030
2031 void tst_qquickpositioners::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
2032 {
2033     for (int i=0; i<indexLists.count(); i++) {
2034         QSet<int> current = indexLists[i].value<QList<int> >().toSet();
2035         if (current != expectedIndexes.toSet())
2036             qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
2037         QCOMPARE(current, expectedIndexes.toSet());
2038     }
2039 }
2040
2041 void tst_qquickpositioners::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes)
2042 {
2043     for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) {
2044         QVERIFY(it.value().type() == QVariant::Int);
2045         QString name = it.key();
2046         int itemIndex = it.value().toInt();
2047         QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
2048         if (model.name(itemIndex) != name)
2049             qDebug() << itemIndex;
2050         QCOMPARE(model.name(itemIndex), name);
2051     }
2052     QCOMPARE(items.count(), expectedIndexes.count());
2053 }
2054
2055 void tst_qquickpositioners::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems)
2056 {
2057     for (int i=0; i<itemLists.count(); i++) {
2058         QVERIFY(itemLists[i].type() == QVariant::List);
2059         QVariantList current = itemLists[i].toList();
2060         for (int j=0; j<current.count(); j++) {
2061             QQuickItem *o = qobject_cast<QQuickItem*>(current[j].value<QObject*>());
2062             QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
2063             QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
2064         }
2065         QCOMPARE(current.count(), expectedItems.count());
2066     }
2067 }
2068
2069 QTEST_MAIN(tst_qquickpositioners)
2070
2071 #include "tst_qquickpositioners.moc"