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#include <qtest.h>
29#include <QtQml/qqmlengine.h>
30#include <QtQml/qqmlcomponent.h>
31#include <QtQuick/qquickview.h>
32#include <private/qquickstateoperations_p.h>
33#include <private/qquickanchors_p_p.h>
34#include <QtQuick/private/qquickrectangle_p.h>
35#include <private/qquickimage_p.h>
36#include <QtQuick/private/qquickpropertychanges_p.h>
37#include <QtQuick/private/qquickstategroup_p.h>
38#include <private/qquickitem_p.h>
39#include <private/qqmlproperty_p.h>
40#include "../../shared/util.h"
41
42class MyAttached : public QObject
43{
44 Q_OBJECT
45 Q_PROPERTY(int foo READ foo WRITE setFoo)
46public:
47 MyAttached(QObject *parent) : QObject(parent), m_foo(13) {}
48
49 int foo() const { return m_foo; }
50 void setFoo(int f) { m_foo = f; }
51
52private:
53 int m_foo;
54};
55
56class MyRect : public QQuickRectangle
57{
58 Q_OBJECT
59 Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal)
60public:
61 MyRect() {}
62
63 void doSomething() { emit didSomething(); }
64
65 int propertyWithNotify() const { return m_prop; }
66 void setPropertyWithNotify(int i) { m_prop = i; emit oddlyNamedNotifySignal(); }
67
68 static MyAttached *qmlAttachedProperties(QObject *o) {
69 return new MyAttached(o);
70 }
71Q_SIGNALS:
72 void didSomething();
73 void oddlyNamedNotifySignal();
74
75private:
76 int m_prop;
77};
78
79QML_DECLARE_TYPE(MyRect)
80QML_DECLARE_TYPEINFO(MyRect, QML_HAS_ATTACHED_PROPERTIES)
81
82class RemovableObj : public QObject
83{
84 Q_OBJECT
85 Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged)
86
87public:
88 RemovableObj(QObject *parent) : QObject(parent), m_prop(4321) { }
89 int prop() const { return m_prop; }
90
91public slots:
92 void setProp(int prop)
93 {
94 if (m_prop == prop)
95 return;
96
97 m_prop = prop;
98 emit propChanged(prop: m_prop);
99 }
100
101signals:
102 void propChanged(int prop);
103
104private:
105 int m_prop;
106};
107
108class ContainingObj : public QObject
109{
110 Q_OBJECT
111 Q_PROPERTY(RemovableObj *obj READ obj NOTIFY objChanged)
112 RemovableObj *m_obj;
113
114public:
115 ContainingObj() : m_obj(new RemovableObj(this)) { }
116 RemovableObj *obj() const { return m_obj; }
117
118 Q_INVOKABLE void reset()
119 {
120 if (m_obj) {
121 m_obj->deleteLater();
122 }
123
124 m_obj = new RemovableObj(this);
125 emit objChanged();
126 }
127signals:
128 void objChanged();
129};
130
131class tst_qquickstates : public QQmlDataTest
132{
133 Q_OBJECT
134public:
135 tst_qquickstates() {}
136
137private:
138 QByteArray fullDataPath(const QString &path) const;
139
140private slots:
141 void initTestCase();
142
143 void basicChanges();
144 void attachedPropertyChanges();
145 void basicExtension();
146 void basicBinding();
147 void signalOverride();
148 void signalOverrideCrash();
149 void signalOverrideCrash2();
150 void signalOverrideCrash3();
151 void signalOverrideCrash4();
152 void parentChange();
153 void parentChangeErrors();
154 void anchorChanges();
155 void anchorChanges2();
156 void anchorChanges3();
157 void anchorChanges4();
158 void anchorChanges5();
159 void anchorChangesRTL();
160 void anchorChangesRTL2();
161 void anchorChangesRTL3();
162 void anchorChangesCrash();
163 void anchorRewindBug();
164 void anchorRewindBug2();
165 void script();
166 void restoreEntryValues();
167 void explicitChanges();
168 void propertyErrors();
169 void incorrectRestoreBug();
170 void autoStateAtStartupRestoreBug();
171 void deletingChange();
172 void deletingState();
173 void tempState();
174 void illegalTempState();
175 void nonExistantProperty();
176 void reset();
177 void illegalObjectCreation();
178 void whenOrdering();
179 void urlResolution();
180 void unnamedWhen();
181 void returnToBase();
182 void extendsBug();
183 void editProperties();
184 void QTBUG_14830();
185 void avoidFastForward();
186 void revertListBug();
187 void QTBUG_38492();
188 void revertListMemoryLeak();
189 void duplicateStateName();
190 void trivialWhen();
191 void noStateOsciallation();
192 void parentChangeCorrectReversal();
193 void revertNullObjectBinding();
194};
195
196void tst_qquickstates::initTestCase()
197{
198 QQmlDataTest::initTestCase();
199 qmlRegisterType<MyRect>(uri: "Qt.test", versionMajor: 1, versionMinor: 0, qmlName: "MyRectangle");
200 qmlRegisterSingletonType<ContainingObj>(
201 uri: "Qt.test", versionMajor: 1, versionMinor: 0, typeName: "ContainingObj", callback: [](QQmlEngine *engine, QJSEngine *) {
202 static ContainingObj instance;
203 engine->setObjectOwnership(&instance, QQmlEngine::CppOwnership);
204 return &instance;
205 });
206 qmlRegisterUncreatableType<RemovableObj>(uri: "Qt.test", versionMajor: 1, versionMinor: 0, qmlName: "RemovableObj", reason: "Uncreatable");
207}
208
209QByteArray tst_qquickstates::fullDataPath(const QString &path) const
210{
211 return testFileUrl(fileName: path).toString().toUtf8();
212}
213
214void tst_qquickstates::basicChanges()
215{
216 QQmlEngine engine;
217
218 {
219 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicChanges.qml"));
220 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
221 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
222 QVERIFY(rect != nullptr);
223
224 QCOMPARE(rect->color(),QColor("red"));
225
226 rectPrivate->setState("blue");
227 QCOMPARE(rect->color(),QColor("blue"));
228
229 rectPrivate->setState("");
230 QCOMPARE(rect->color(),QColor("red"));
231 }
232
233 {
234 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicChanges2.qml"));
235 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
236 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
237 QVERIFY(rect != nullptr);
238
239 QCOMPARE(rect->color(),QColor("red"));
240
241 rectPrivate->setState("blue");
242 QCOMPARE(rect->color(),QColor("blue"));
243
244 rectPrivate->setState("green");
245 QCOMPARE(rect->color(),QColor("green"));
246
247 rectPrivate->setState("");
248 QCOMPARE(rect->color(),QColor("red"));
249
250 rectPrivate->setState("green");
251 QCOMPARE(rect->color(),QColor("green"));
252 }
253
254 {
255 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicChanges3.qml"));
256 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
257 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
258 QVERIFY(rect != nullptr);
259
260 QCOMPARE(rect->color(),QColor("red"));
261 QCOMPARE(rect->border()->width(),1.0);
262
263 rectPrivate->setState("blue");
264 QCOMPARE(rect->color(),QColor("blue"));
265 QCOMPARE(rect->border()->width(),1.0);
266
267 rectPrivate->setState("bordered");
268 QCOMPARE(rect->color(),QColor("red"));
269 QCOMPARE(rect->border()->width(),2.0);
270
271 rectPrivate->setState("");
272 QCOMPARE(rect->color(),QColor("red"));
273 QCOMPARE(rect->border()->width(),1.0);
274 //### we should be checking that this is an implicit rather than explicit 1 (which currently fails)
275
276 rectPrivate->setState("bordered");
277 QCOMPARE(rect->color(),QColor("red"));
278 QCOMPARE(rect->border()->width(),2.0);
279
280 rectPrivate->setState("blue");
281 QCOMPARE(rect->color(),QColor("blue"));
282 QCOMPARE(rect->border()->width(),1.0);
283
284 }
285
286 {
287 // Test basicChanges4.qml can magically connect to propertyWithNotify's notify
288 // signal using 'onPropertyWithNotifyChanged' even though the signal name is
289 // actually 'oddlyNamedNotifySignal'
290
291 QQmlComponent component(&engine, testFileUrl(fileName: "basicChanges4.qml"));
292 QVERIFY(component.isReady());
293
294 MyRect *rect = qobject_cast<MyRect*>(object: component.create());
295 QVERIFY(rect != nullptr);
296
297 QMetaProperty prop = rect->metaObject()->property(index: rect->metaObject()->indexOfProperty(name: "propertyWithNotify"));
298 QVERIFY(prop.hasNotifySignal());
299 QString notifySignal = prop.notifySignal().methodSignature();
300 QVERIFY(!notifySignal.startsWith("propertyWithNotifyChanged("));
301
302 QCOMPARE(rect->color(), QColor(Qt::red));
303
304 rect->setPropertyWithNotify(100);
305 QCOMPARE(rect->color(), QColor(Qt::blue));
306 }
307}
308
309void tst_qquickstates::attachedPropertyChanges()
310{
311 QQmlEngine engine;
312
313 QQmlComponent component(&engine, testFileUrl(fileName: "attachedPropertyChanges.qml"));
314 QVERIFY(component.isReady());
315
316 QQuickItem *item = qobject_cast<QQuickItem*>(object: component.create());
317 QVERIFY(item != nullptr);
318 QCOMPARE(item->width(), 50.0);
319
320 // Ensure attached property has been changed
321 QObject *attObj = qmlAttachedPropertiesObject<MyRect>(obj: item, create: false);
322 QVERIFY(attObj);
323
324 MyAttached *att = qobject_cast<MyAttached*>(object: attObj);
325 QVERIFY(att);
326
327 QCOMPARE(att->foo(), 1);
328}
329
330void tst_qquickstates::basicExtension()
331{
332 QQmlEngine engine;
333
334 {
335 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicExtension.qml"));
336 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
337 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
338 QVERIFY(rect != nullptr);
339
340 QCOMPARE(rect->color(),QColor("red"));
341 QCOMPARE(rect->border()->width(),1.0);
342
343 rectPrivate->setState("blue");
344 QCOMPARE(rect->color(),QColor("blue"));
345 QCOMPARE(rect->border()->width(),1.0);
346
347 rectPrivate->setState("bordered");
348 QCOMPARE(rect->color(),QColor("blue"));
349 QCOMPARE(rect->border()->width(),2.0);
350
351 rectPrivate->setState("blue");
352 QCOMPARE(rect->color(),QColor("blue"));
353 QCOMPARE(rect->border()->width(),1.0);
354
355 rectPrivate->setState("");
356 QCOMPARE(rect->color(),QColor("red"));
357 QCOMPARE(rect->border()->width(),1.0);
358
359 rectPrivate->setState("bordered");
360 QCOMPARE(rect->color(),QColor("blue"));
361 QCOMPARE(rect->border()->width(),2.0);
362
363 rectPrivate->setState("");
364 QCOMPARE(rect->color(),QColor("red"));
365 QCOMPARE(rect->border()->width(),1.0);
366 }
367
368 {
369 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "fakeExtension.qml"));
370 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
371 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
372 QVERIFY(rect != nullptr);
373
374 QCOMPARE(rect->color(),QColor("red"));
375
376 rectPrivate->setState("blue");
377 QCOMPARE(rect->color(),QColor("blue"));
378
379 rectPrivate->setState("green");
380 QCOMPARE(rect->color(),QColor("green"));
381
382 rectPrivate->setState("blue");
383 QCOMPARE(rect->color(),QColor("blue"));
384
385 rectPrivate->setState("green");
386 QCOMPARE(rect->color(),QColor("green"));
387
388 rectPrivate->setState("");
389 QCOMPARE(rect->color(),QColor("red"));
390
391 rectPrivate->setState("green");
392 QCOMPARE(rect->color(),QColor("green"));
393 }
394}
395
396void tst_qquickstates::basicBinding()
397{
398 QQmlEngine engine;
399
400 {
401 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicBinding.qml"));
402 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
403 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
404 QVERIFY(rect != nullptr);
405
406 QCOMPARE(rect->color(),QColor("red"));
407
408 rectPrivate->setState("blue");
409 QCOMPARE(rect->color(),QColor("blue"));
410
411 rectPrivate->setState("");
412 QCOMPARE(rect->color(),QColor("red"));
413
414 rectPrivate->setState("blue");
415 QCOMPARE(rect->color(),QColor("blue"));
416 rect->setProperty(name: "sourceColor", value: QColor("green"));
417 QCOMPARE(rect->color(),QColor("green"));
418
419 rectPrivate->setState("");
420 QCOMPARE(rect->color(),QColor("red"));
421 rect->setProperty(name: "sourceColor", value: QColor("yellow"));
422 QCOMPARE(rect->color(),QColor("red"));
423
424 rectPrivate->setState("blue");
425 QCOMPARE(rect->color(),QColor("yellow"));
426 }
427
428 {
429 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicBinding2.qml"));
430 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
431 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
432 QVERIFY(rect != nullptr);
433
434 QCOMPARE(rect->color(),QColor("red"));
435
436 rectPrivate->setState("blue");
437 QCOMPARE(rect->color(),QColor("blue"));
438
439 rectPrivate->setState("");
440 QCOMPARE(rect->color(),QColor("red"));
441
442 rectPrivate->setState("blue");
443 QCOMPARE(rect->color(),QColor("blue"));
444 rect->setProperty(name: "sourceColor", value: QColor("green"));
445 QCOMPARE(rect->color(),QColor("blue"));
446
447 rectPrivate->setState("");
448 QCOMPARE(rect->color(),QColor("green"));
449 rect->setProperty(name: "sourceColor", value: QColor("yellow"));
450 QCOMPARE(rect->color(),QColor("yellow"));
451
452 rectPrivate->setState("blue");
453 QCOMPARE(rect->color(),QColor("blue"));
454
455 rectPrivate->setState("");
456 QCOMPARE(rect->color(),QColor("yellow"));
457 }
458
459 {
460 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicBinding3.qml"));
461 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
462 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
463 QVERIFY(rect != nullptr);
464
465 QCOMPARE(rect->color(),QColor("red"));
466 rect->setProperty(name: "sourceColor", value: QColor("green"));
467 QCOMPARE(rect->color(),QColor("green"));
468
469 rectPrivate->setState("blue");
470 QCOMPARE(rect->color(),QColor("blue"));
471 rect->setProperty(name: "sourceColor", value: QColor("red"));
472 QCOMPARE(rect->color(),QColor("blue"));
473 rect->setProperty(name: "sourceColor2", value: QColor("yellow"));
474 QCOMPARE(rect->color(),QColor("yellow"));
475
476 rectPrivate->setState("");
477 QCOMPARE(rect->color(),QColor("red"));
478 rect->setProperty(name: "sourceColor2", value: QColor("green"));
479 QCOMPARE(rect->color(),QColor("red"));
480 rect->setProperty(name: "sourceColor", value: QColor("yellow"));
481 QCOMPARE(rect->color(),QColor("yellow"));
482 }
483
484 {
485 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicBinding4.qml"));
486 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
487 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
488 QVERIFY(rect != nullptr);
489
490 QCOMPARE(rect->color(),QColor("red"));
491
492 rectPrivate->setState("blue");
493 QCOMPARE(rect->color(),QColor("blue"));
494 rect->setProperty(name: "sourceColor", value: QColor("yellow"));
495 QCOMPARE(rect->color(),QColor("yellow"));
496
497 rectPrivate->setState("green");
498 QCOMPARE(rect->color(),QColor("green"));
499 rect->setProperty(name: "sourceColor", value: QColor("purple"));
500 QCOMPARE(rect->color(),QColor("green"));
501
502 rectPrivate->setState("blue");
503 QCOMPARE(rect->color(),QColor("purple"));
504
505 rectPrivate->setState("green");
506 QCOMPARE(rect->color(),QColor("green"));
507
508 rectPrivate->setState("");
509 QCOMPARE(rect->color(),QColor("red"));
510 }
511}
512
513void tst_qquickstates::signalOverride()
514{
515 QQmlEngine engine;
516
517 {
518 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "signalOverride.qml"));
519 MyRect *rect = qobject_cast<MyRect*>(object: rectComponent.create());
520 QVERIFY(rect != nullptr);
521
522 QCOMPARE(rect->color(),QColor("red"));
523 rect->doSomething();
524 QCOMPARE(rect->color(),QColor("blue"));
525
526 QQuickItemPrivate::get(item: rect)->setState("green");
527 rect->doSomething();
528 QCOMPARE(rect->color(),QColor("green"));
529
530 delete rect;
531 }
532
533 {
534 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "signalOverride2.qml"));
535 MyRect *rect = qobject_cast<MyRect*>(object: rectComponent.create());
536 QVERIFY(rect != nullptr);
537
538 QCOMPARE(rect->color(),QColor("white"));
539 rect->doSomething();
540 QCOMPARE(rect->color(),QColor("blue"));
541
542 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "extendedRect"));
543 QQuickItemPrivate::get(item: innerRect)->setState("green");
544 rect->doSomething();
545 QCOMPARE(rect->color(),QColor("blue"));
546 QCOMPARE(innerRect->color(),QColor("green"));
547 QCOMPARE(innerRect->property("extendedColor").value<QColor>(),QColor("green"));
548
549 delete rect;
550 }
551}
552
553void tst_qquickstates::signalOverrideCrash()
554{
555 QQmlEngine engine;
556
557 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "signalOverrideCrash.qml"));
558 MyRect *rect = qobject_cast<MyRect*>(object: rectComponent.create());
559 QVERIFY(rect != nullptr);
560
561 QQuickItemPrivate::get(item: rect)->setState("overridden");
562 rect->doSomething();
563}
564
565void tst_qquickstates::signalOverrideCrash2()
566{
567 QQmlEngine engine;
568
569 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "signalOverrideCrash2.qml"));
570 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
571 QVERIFY(rect != nullptr);
572
573 QQuickItemPrivate::get(item: rect)->setState("state1");
574 QQuickItemPrivate::get(item: rect)->setState("state2");
575 QQuickItemPrivate::get(item: rect)->setState("state1");
576
577 delete rect;
578}
579
580void tst_qquickstates::signalOverrideCrash3()
581{
582 QQmlEngine engine;
583
584 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "signalOverrideCrash3.qml"));
585 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
586 QVERIFY(rect != nullptr);
587
588 QQuickItemPrivate::get(item: rect)->setState("state1");
589 QQuickItemPrivate::get(item: rect)->setState("");
590 QQuickItemPrivate::get(item: rect)->setState("state2");
591 QQuickItemPrivate::get(item: rect)->setState("");
592
593 delete rect;
594}
595
596void tst_qquickstates::signalOverrideCrash4()
597{
598 QQmlEngine engine;
599 QQmlComponent c(&engine, testFileUrl(fileName: "signalOverrideCrash4.qml"));
600 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
601 QVERIFY(rect != nullptr);
602
603 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
604
605 rectPrivate->setState("state1");
606 rectPrivate->setState("state2");
607 rectPrivate->setState("state1");
608 rectPrivate->setState("state2");
609 rectPrivate->setState("");
610
611 delete rect;
612}
613
614void tst_qquickstates::parentChange()
615{
616 QQmlEngine engine;
617
618 {
619 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "parentChange1.qml"));
620 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
621 QVERIFY(rect != nullptr);
622
623 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
624 QVERIFY(innerRect != nullptr);
625
626 QQmlListReference list(rect, "states");
627 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
628 QVERIFY(state != nullptr);
629
630 qmlExecuteDeferred(state);
631 QQuickParentChange *pChange = qobject_cast<QQuickParentChange*>(object: state->operationAt(0));
632 QVERIFY(pChange != nullptr);
633 QQuickItem *nParent = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "NewParent"));
634 QVERIFY(nParent != nullptr);
635
636 QCOMPARE(pChange->parent(), nParent);
637
638 QQuickItemPrivate::get(item: rect)->setState("reparented");
639 QCOMPARE(innerRect->rotation(), qreal(0));
640 QCOMPARE(innerRect->scale(), qreal(1));
641 QCOMPARE(innerRect->x(), qreal(-133));
642 QCOMPARE(innerRect->y(), qreal(-300));
643 }
644
645 {
646 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "parentChange2.qml"));
647 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
648 QVERIFY(rect != nullptr);
649 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
650 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
651 QVERIFY(innerRect != nullptr);
652
653 rectPrivate->setState("reparented");
654 QCOMPARE(innerRect->rotation(), qreal(15));
655 QCOMPARE(innerRect->scale(), qreal(.5));
656 QCOMPARE(QString("%1").arg(innerRect->x()), QString("%1").arg(-19.9075));
657 QCOMPARE(QString("%1").arg(innerRect->y()), QString("%1").arg(-8.73433));
658 }
659
660 {
661 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "parentChange3.qml"));
662 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
663 QVERIFY(rect != nullptr);
664 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
665 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
666 QVERIFY(innerRect != nullptr);
667
668 rectPrivate->setState("reparented");
669 QCOMPARE(innerRect->rotation(), qreal(-37));
670 QCOMPARE(innerRect->scale(), qreal(.25));
671 QCOMPARE(QString("%1").arg(innerRect->x()), QString("%1").arg(-217.305));
672 QCOMPARE(QString("%1").arg(innerRect->y()), QString("%1").arg(-164.413));
673
674 rectPrivate->setState("");
675 QCOMPARE(innerRect->rotation(), qreal(0));
676 QCOMPARE(innerRect->scale(), qreal(1));
677 QCOMPARE(innerRect->x(), qreal(5));
678 //do a non-qFuzzyCompare fuzzy compare
679 QVERIFY(innerRect->y() < qreal(0.00001) && innerRect->y() > qreal(-0.00001));
680 }
681
682 {
683 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "parentChange6.qml"));
684 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
685 QVERIFY(rect != nullptr);
686
687 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
688 QVERIFY(innerRect != nullptr);
689
690 QQuickItemPrivate::get(item: rect)->setState("reparented");
691 QCOMPARE(innerRect->rotation(), qreal(180));
692 QCOMPARE(innerRect->scale(), qreal(1));
693 QCOMPARE(innerRect->x(), qreal(-105));
694 QCOMPARE(innerRect->y(), qreal(-105));
695 }
696}
697
698void tst_qquickstates::parentChangeErrors()
699{
700 QQmlEngine engine;
701
702 {
703 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "parentChange4.qml"));
704 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
705 QVERIFY(rect != nullptr);
706
707 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
708 QVERIFY(innerRect != nullptr);
709
710 QTest::ignoreMessage(type: QtWarningMsg, message: fullDataPath(path: "parentChange4.qml") + ":25:9: QML ParentChange: Unable to preserve appearance under non-uniform scale");
711 QQuickItemPrivate::get(item: rect)->setState("reparented");
712 QCOMPARE(innerRect->rotation(), qreal(0));
713 QCOMPARE(innerRect->scale(), qreal(1));
714 QCOMPARE(innerRect->x(), qreal(5));
715 QCOMPARE(innerRect->y(), qreal(5));
716 }
717
718 {
719 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "parentChange5.qml"));
720 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
721 QVERIFY(rect != nullptr);
722
723 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
724 QVERIFY(innerRect != nullptr);
725
726 QTest::ignoreMessage(type: QtWarningMsg, message: fullDataPath(path: "parentChange5.qml") + ":25:9: QML ParentChange: Unable to preserve appearance under complex transform");
727 QQuickItemPrivate::get(item: rect)->setState("reparented");
728 QCOMPARE(innerRect->rotation(), qreal(0));
729 QCOMPARE(innerRect->scale(), qreal(1));
730 QCOMPARE(innerRect->x(), qreal(5));
731 QCOMPARE(innerRect->y(), qreal(5));
732 }
733}
734
735void tst_qquickstates::anchorChanges()
736{
737 QQmlEngine engine;
738
739 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges1.qml"));
740 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
741 QVERIFY(rect != nullptr);
742 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
743
744 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
745 QVERIFY(innerRect != nullptr);
746
747 QQmlListReference list(rect, "states");
748 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
749 QVERIFY(state != nullptr);
750
751 qmlExecuteDeferred(state);
752 QQuickAnchorChanges *aChanges = qobject_cast<QQuickAnchorChanges*>(object: state->operationAt(0));
753 QVERIFY(aChanges != nullptr);
754
755 QCOMPARE(aChanges->anchors()->left().isUndefinedLiteral(), true);
756 QVERIFY(!aChanges->anchors()->left().isEmpty());
757 QVERIFY(!aChanges->anchors()->right().isEmpty());
758
759 rectPrivate->setState("right");
760 QCOMPARE(innerRect->x(), qreal(150));
761 QCOMPARE(aChanges->object(), qobject_cast<QQuickItem*>(innerRect));
762 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->left().anchorLine, QQuickAnchors::InvalidAnchor); //### was reset (how do we distinguish from not set at all)
763 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().item, rectPrivate->right().item);
764 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().anchorLine, rectPrivate->right().anchorLine);
765
766 rectPrivate->setState("");
767 QCOMPARE(innerRect->x(), qreal(5));
768
769 delete rect;
770}
771
772void tst_qquickstates::anchorChanges2()
773{
774 QQmlEngine engine;
775
776 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges2.qml"));
777 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
778 QVERIFY(rect != nullptr);
779 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
780
781 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
782 QVERIFY(innerRect != nullptr);
783
784 rectPrivate->setState("right");
785 QCOMPARE(innerRect->x(), qreal(150));
786
787 rectPrivate->setState("");
788 QCOMPARE(innerRect->x(), qreal(5));
789
790 delete rect;
791}
792
793void tst_qquickstates::anchorChanges3()
794{
795 QQmlEngine engine;
796
797 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges3.qml"));
798 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
799 QVERIFY(rect != nullptr);
800 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
801
802 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
803 QVERIFY(innerRect != nullptr);
804
805 QQuickItem *leftGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "LeftGuideline"));
806 QVERIFY(leftGuideline != nullptr);
807
808 QQuickItem *bottomGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "BottomGuideline"));
809 QVERIFY(bottomGuideline != nullptr);
810
811 QQmlListReference list(rect, "states");
812 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
813 QVERIFY(state != nullptr);
814
815 qmlExecuteDeferred(state);
816 QQuickAnchorChanges *aChanges = qobject_cast<QQuickAnchorChanges*>(object: state->operationAt(0));
817 QVERIFY(aChanges != nullptr);
818
819 QVERIFY(!aChanges->anchors()->top().isEmpty());
820 QVERIFY(!aChanges->anchors()->bottom().isEmpty());
821
822 rectPrivate->setState("reanchored");
823 QCOMPARE(aChanges->object(), qobject_cast<QQuickItem*>(innerRect));
824 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->left().item, QQuickItemPrivate::get(leftGuideline)->left().item);
825 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->left().anchorLine, QQuickItemPrivate::get(leftGuideline)->left().anchorLine);
826 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().item, rectPrivate->right().item);
827 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().anchorLine, rectPrivate->right().anchorLine);
828 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->top().item, rectPrivate->top().item);
829 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->top().anchorLine, rectPrivate->top().anchorLine);
830 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->bottom().item, QQuickItemPrivate::get(bottomGuideline)->bottom().item);
831 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->bottom().anchorLine, QQuickItemPrivate::get(bottomGuideline)->bottom().anchorLine);
832
833 QCOMPARE(innerRect->x(), qreal(10));
834 QCOMPARE(innerRect->y(), qreal(0));
835 QCOMPARE(innerRect->width(), qreal(190));
836 QCOMPARE(innerRect->height(), qreal(150));
837
838 rectPrivate->setState("");
839 QCOMPARE(innerRect->x(), qreal(0));
840 QCOMPARE(innerRect->y(), qreal(10));
841 QCOMPARE(innerRect->width(), qreal(150));
842 QCOMPARE(innerRect->height(), qreal(190));
843
844 delete rect;
845}
846
847void tst_qquickstates::anchorChanges4()
848{
849 QQmlEngine engine;
850
851 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges4.qml"));
852 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
853 QVERIFY(rect != nullptr);
854
855 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
856 QVERIFY(innerRect != nullptr);
857
858 QQuickItem *leftGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "LeftGuideline"));
859 QVERIFY(leftGuideline != nullptr);
860
861 QQuickItem *bottomGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "BottomGuideline"));
862 QVERIFY(bottomGuideline != nullptr);
863
864 QQmlListReference list(rect, "states");
865 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
866 QVERIFY(state != nullptr);
867
868 qmlExecuteDeferred(state);
869 QQuickAnchorChanges *aChanges = qobject_cast<QQuickAnchorChanges*>(object: state->operationAt(0));
870 QVERIFY(aChanges != nullptr);
871
872 QVERIFY(!aChanges->anchors()->horizontalCenter().isEmpty());
873 QVERIFY(!aChanges->anchors()->verticalCenter().isEmpty());
874
875 QQuickItemPrivate::get(item: rect)->setState("reanchored");
876 QCOMPARE(aChanges->object(), qobject_cast<QQuickItem*>(innerRect));
877 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->horizontalCenter().item, QQuickItemPrivate::get(bottomGuideline)->horizontalCenter().item);
878 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->horizontalCenter().anchorLine, QQuickItemPrivate::get(bottomGuideline)->horizontalCenter().anchorLine);
879 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->verticalCenter().item, QQuickItemPrivate::get(leftGuideline)->verticalCenter().item);
880 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->verticalCenter().anchorLine, QQuickItemPrivate::get(leftGuideline)->verticalCenter().anchorLine);
881
882 delete rect;
883}
884
885void tst_qquickstates::anchorChanges5()
886{
887 QQmlEngine engine;
888
889 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges5.qml"));
890 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
891 QVERIFY(rect != nullptr);
892
893 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
894 QVERIFY(innerRect != nullptr);
895
896 QQuickItem *leftGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "LeftGuideline"));
897 QVERIFY(leftGuideline != nullptr);
898
899 QQuickItem *bottomGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "BottomGuideline"));
900 QVERIFY(bottomGuideline != nullptr);
901
902 QQmlListReference list(rect, "states");
903 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
904 QVERIFY(state != nullptr);
905
906 qmlExecuteDeferred(state);
907 QQuickAnchorChanges *aChanges = qobject_cast<QQuickAnchorChanges*>(object: state->operationAt(0));
908 QVERIFY(aChanges != nullptr);
909
910 QVERIFY(!aChanges->anchors()->baseline().isEmpty());
911
912 QQuickItemPrivate::get(item: rect)->setState("reanchored");
913 QCOMPARE(aChanges->object(), qobject_cast<QQuickItem*>(innerRect));
914 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->horizontalCenter().item, QQuickItemPrivate::get(bottomGuideline)->horizontalCenter().item);
915 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->horizontalCenter().anchorLine, QQuickItemPrivate::get(bottomGuideline)->horizontalCenter().anchorLine);
916 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->baseline().item, QQuickItemPrivate::get(leftGuideline)->baseline().item);
917 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->baseline().anchorLine, QQuickItemPrivate::get(leftGuideline)->baseline().anchorLine);
918
919 delete rect;
920}
921
922void mirrorAnchors(QQuickItem *item) {
923 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
924 itemPrivate->setLayoutMirror(true);
925}
926
927qreal offsetRTL(QQuickItem *anchorItem, QQuickItem *item) {
928 return anchorItem->width()+2*anchorItem->x()-item->width();
929}
930
931void tst_qquickstates::anchorChangesRTL()
932{
933 QQmlEngine engine;
934
935 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges1.qml"));
936 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
937 QVERIFY(rect != nullptr);
938 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
939
940 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
941 QVERIFY(innerRect != nullptr);
942 mirrorAnchors(item: innerRect);
943
944 QQmlListReference list(rect, "states");
945 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
946 QVERIFY(state != nullptr);
947
948 qmlExecuteDeferred(state);
949 QQuickAnchorChanges *aChanges = qobject_cast<QQuickAnchorChanges*>(object: state->operationAt(0));
950 QVERIFY(aChanges != nullptr);
951
952 rectPrivate->setState("right");
953 QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(150));
954 QCOMPARE(aChanges->object(), qobject_cast<QQuickItem*>(innerRect));
955 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->left().anchorLine, QQuickAnchors::InvalidAnchor); //### was reset (how do we distinguish from not set at all)
956 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().item, rectPrivate->right().item);
957 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().anchorLine, rectPrivate->right().anchorLine);
958
959 rectPrivate->setState("");
960 QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) -qreal(5));
961
962 delete rect;
963}
964
965void tst_qquickstates::anchorChangesRTL2()
966{
967 QQmlEngine engine;
968
969 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges2.qml"));
970 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
971 QVERIFY(rect != nullptr);
972 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
973
974 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
975 QVERIFY(innerRect != nullptr);
976 mirrorAnchors(item: innerRect);
977
978 rectPrivate->setState("right");
979 QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(150));
980
981 rectPrivate->setState("");
982 QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(5));
983
984 delete rect;
985}
986
987void tst_qquickstates::anchorChangesRTL3()
988{
989 QQmlEngine engine;
990
991 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChanges3.qml"));
992 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
993 QVERIFY(rect != nullptr);
994 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
995
996 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
997 QVERIFY(innerRect != nullptr);
998 mirrorAnchors(item: innerRect);
999
1000 QQuickItem *leftGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "LeftGuideline"));
1001 QVERIFY(leftGuideline != nullptr);
1002
1003 QQuickItem *bottomGuideline = qobject_cast<QQuickItem*>(object: rect->findChild<QQuickItem*>(aName: "BottomGuideline"));
1004 QVERIFY(bottomGuideline != nullptr);
1005
1006 QQmlListReference list(rect, "states");
1007 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
1008 QVERIFY(state != nullptr);
1009
1010 qmlExecuteDeferred(state);
1011 QQuickAnchorChanges *aChanges = qobject_cast<QQuickAnchorChanges*>(object: state->operationAt(0));
1012 QVERIFY(aChanges != nullptr);
1013
1014 rectPrivate->setState("reanchored");
1015 QCOMPARE(aChanges->object(), qobject_cast<QQuickItem*>(innerRect));
1016 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->left().item, QQuickItemPrivate::get(leftGuideline)->left().item);
1017 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->left().anchorLine, QQuickItemPrivate::get(leftGuideline)->left().anchorLine);
1018 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().item, rectPrivate->right().item);
1019 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->right().anchorLine, rectPrivate->right().anchorLine);
1020 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->top().item, rectPrivate->top().item);
1021 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->top().anchorLine, rectPrivate->top().anchorLine);
1022 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->bottom().item, QQuickItemPrivate::get(bottomGuideline)->bottom().item);
1023 QCOMPARE(QQuickItemPrivate::get(aChanges->object())->anchors()->bottom().anchorLine, QQuickItemPrivate::get(bottomGuideline)->bottom().anchorLine);
1024
1025 QCOMPARE(innerRect->x(), offsetRTL(leftGuideline, innerRect) - qreal(10));
1026 QCOMPARE(innerRect->y(), qreal(0));
1027 // between left side of parent and leftGuideline.x: 10, which has width 0
1028 QCOMPARE(innerRect->width(), qreal(10));
1029 QCOMPARE(innerRect->height(), qreal(150));
1030
1031 rectPrivate->setState("");
1032 QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(0));
1033 QCOMPARE(innerRect->y(), qreal(10));
1034 // between right side of parent and left side of rightGuideline.x: 150, which has width 0
1035 QCOMPARE(innerRect->width(), qreal(50));
1036 QCOMPARE(innerRect->height(), qreal(190));
1037
1038 delete rect;
1039}
1040
1041//QTBUG-9609
1042void tst_qquickstates::anchorChangesCrash()
1043{
1044 QQmlEngine engine;
1045
1046 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorChangesCrash.qml"));
1047 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1048 QVERIFY(rect != nullptr);
1049
1050 QQuickItemPrivate::get(item: rect)->setState("reanchored");
1051
1052 delete rect;
1053}
1054
1055// QTBUG-12273
1056void tst_qquickstates::anchorRewindBug()
1057{
1058 QQuickView *view = new QQuickView;
1059 view->setSource(testFileUrl(fileName: "anchorRewindBug.qml"));
1060
1061 view->show();
1062
1063 QVERIFY(QTest::qWaitForWindowExposed(view));
1064
1065 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: view->rootObject());
1066 QVERIFY(rect != nullptr);
1067
1068 QQuickItem * column = rect->findChild<QQuickItem*>(aName: "column");
1069
1070 QVERIFY(column != nullptr);
1071 QVERIFY(!QQuickItemPrivate::get(column)->heightValid);
1072 QVERIFY(!QQuickItemPrivate::get(column)->widthValid);
1073 QCOMPARE(column->height(), 200.0);
1074 QQuickItemPrivate::get(item: rect)->setState("reanchored");
1075
1076 // column height and width should stay implicit
1077 // and column's implicit resizing should still work
1078 QVERIFY(!QQuickItemPrivate::get(column)->heightValid);
1079 QVERIFY(!QQuickItemPrivate::get(column)->widthValid);
1080 QTRY_COMPARE(column->height(), 100.0);
1081
1082 QQuickItemPrivate::get(item: rect)->setState("");
1083
1084 // column height and width should stay implicit
1085 // and column's implicit resizing should still work
1086 QVERIFY(!QQuickItemPrivate::get(column)->heightValid);
1087 QVERIFY(!QQuickItemPrivate::get(column)->widthValid);
1088 QTRY_COMPARE(column->height(), 200.0);
1089
1090 delete view;
1091}
1092
1093// QTBUG-11834
1094void tst_qquickstates::anchorRewindBug2()
1095{
1096 QQmlEngine engine;
1097
1098 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "anchorRewindBug2.qml"));
1099 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1100 QVERIFY(rect != nullptr);
1101
1102 QQuickRectangle *mover = rect->findChild<QQuickRectangle*>(aName: "mover");
1103
1104 QVERIFY(mover != nullptr);
1105 QCOMPARE(mover->y(), qreal(0.0));
1106 QCOMPARE(mover->width(), qreal(50.0));
1107
1108 QQuickItemPrivate::get(item: rect)->setState("anchored");
1109 QCOMPARE(mover->y(), qreal(250.0));
1110 QCOMPARE(mover->width(), qreal(200.0));
1111
1112 QQuickItemPrivate::get(item: rect)->setState("");
1113 QCOMPARE(mover->y(), qreal(0.0));
1114 QCOMPARE(mover->width(), qreal(50.0));
1115
1116 delete rect;
1117}
1118
1119void tst_qquickstates::script()
1120{
1121 QQmlEngine engine;
1122
1123 {
1124 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "script.qml"));
1125 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1126 QVERIFY(rect != nullptr);
1127 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1128 QCOMPARE(rect->color(),QColor("red"));
1129
1130 rectPrivate->setState("blue");
1131 QCOMPARE(rect->color(),QColor("blue"));
1132
1133 rectPrivate->setState("");
1134 QCOMPARE(rect->color(),QColor("blue")); // a script isn't reverted
1135 }
1136}
1137
1138void tst_qquickstates::restoreEntryValues()
1139{
1140 QQmlEngine engine;
1141
1142 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "restoreEntryValues.qml"));
1143 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1144 QVERIFY(rect != nullptr);
1145 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1146 QCOMPARE(rect->color(),QColor("red"));
1147
1148 rectPrivate->setState("blue");
1149 QCOMPARE(rect->color(),QColor("blue"));
1150
1151 rectPrivate->setState("");
1152 QCOMPARE(rect->color(),QColor("blue"));
1153}
1154
1155void tst_qquickstates::explicitChanges()
1156{
1157 QQmlEngine engine;
1158
1159 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "explicit.qml"));
1160 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1161 QVERIFY(rect != nullptr);
1162 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1163 QQmlListReference list(rect, "states");
1164 QQuickState *state = qobject_cast<QQuickState*>(object: list.at(0));
1165 QVERIFY(state != nullptr);
1166
1167 qmlExecuteDeferred(state);
1168 QQuickPropertyChanges *changes = qobject_cast<QQuickPropertyChanges*>(object: rect->findChild<QQuickPropertyChanges*>(aName: "changes"));
1169 QVERIFY(changes != nullptr);
1170 QVERIFY(changes->isExplicit());
1171
1172 QCOMPARE(rect->color(),QColor("red"));
1173
1174 rectPrivate->setState("blue");
1175 QCOMPARE(rect->color(),QColor("blue"));
1176
1177 rect->setProperty(name: "sourceColor", value: QColor("green"));
1178 QCOMPARE(rect->color(),QColor("blue"));
1179
1180 rectPrivate->setState("");
1181 QCOMPARE(rect->color(),QColor("red"));
1182 rect->setProperty(name: "sourceColor", value: QColor("yellow"));
1183 QCOMPARE(rect->color(),QColor("red"));
1184
1185 rectPrivate->setState("blue");
1186 QCOMPARE(rect->color(),QColor("yellow"));
1187}
1188
1189void tst_qquickstates::propertyErrors()
1190{
1191 QQmlEngine engine;
1192 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "propertyErrors.qml"));
1193 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1194 QVERIFY(rect != nullptr);
1195
1196 QCOMPARE(rect->color(),QColor("red"));
1197
1198 QTest::ignoreMessage(type: QtWarningMsg, message: fullDataPath(path: "propertyErrors.qml") + ":8:9: QML PropertyChanges: Cannot assign to non-existent property \"colr\"");
1199 QTest::ignoreMessage(type: QtWarningMsg, message: fullDataPath(path: "propertyErrors.qml") + ":8:9: QML PropertyChanges: Cannot assign to read-only property \"activeFocus\"");
1200 QQuickItemPrivate::get(item: rect)->setState("blue");
1201}
1202
1203void tst_qquickstates::incorrectRestoreBug()
1204{
1205 QQmlEngine engine;
1206
1207 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "basicChanges.qml"));
1208 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1209 QVERIFY(rect != nullptr);
1210 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1211 QCOMPARE(rect->color(),QColor("red"));
1212
1213 rectPrivate->setState("blue");
1214 QCOMPARE(rect->color(),QColor("blue"));
1215
1216 rectPrivate->setState("");
1217 QCOMPARE(rect->color(),QColor("red"));
1218
1219 // make sure if we change the base state value, we then restore to it correctly
1220 rect->setColor(QColor("green"));
1221
1222 rectPrivate->setState("blue");
1223 QCOMPARE(rect->color(),QColor("blue"));
1224
1225 rectPrivate->setState("");
1226 QCOMPARE(rect->color(),QColor("green"));
1227}
1228
1229void tst_qquickstates::autoStateAtStartupRestoreBug()
1230{
1231 QQmlEngine engine;
1232
1233 QQmlComponent component(&engine, testFileUrl(fileName: "autoStateAtStartupRestoreBug.qml"));
1234 QObject *obj = component.create();
1235
1236 QVERIFY(obj != nullptr);
1237 QCOMPARE(obj->property("test").toInt(), 3);
1238
1239 obj->setProperty(name: "input", value: 2);
1240
1241 QCOMPARE(obj->property("test").toInt(), 9);
1242
1243 delete obj;
1244}
1245
1246void tst_qquickstates::deletingChange()
1247{
1248 QQmlEngine engine;
1249
1250 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "deleting.qml"));
1251 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1252 QVERIFY(rect != nullptr);
1253 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1254 rectPrivate->setState("blue");
1255 QCOMPARE(rect->color(),QColor("blue"));
1256 QCOMPARE(rect->radius(),qreal(5));
1257
1258 rectPrivate->setState("");
1259 QCOMPARE(rect->color(),QColor("red"));
1260 QCOMPARE(rect->radius(),qreal(0));
1261
1262 QQuickPropertyChanges *pc = rect->findChild<QQuickPropertyChanges*>(aName: "pc1");
1263 QVERIFY(pc != nullptr);
1264 delete pc;
1265
1266 QQuickState *state = rect->findChild<QQuickState*>();
1267 QVERIFY(state != nullptr);
1268 qmlExecuteDeferred(state);
1269 QCOMPARE(state->operationCount(), 1);
1270
1271 rectPrivate->setState("blue");
1272 QCOMPARE(rect->color(),QColor("red"));
1273 QCOMPARE(rect->radius(),qreal(5));
1274
1275 delete rect;
1276}
1277
1278void tst_qquickstates::deletingState()
1279{
1280 QQmlEngine engine;
1281
1282 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "deletingState.qml"));
1283 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1284 QVERIFY(rect != nullptr);
1285
1286 QQuickStateGroup *sg = rect->findChild<QQuickStateGroup*>();
1287 QVERIFY(sg != nullptr);
1288 QVERIFY(sg->findState("blue") != nullptr);
1289
1290 sg->setState("blue");
1291 QCOMPARE(rect->color(),QColor("blue"));
1292
1293 sg->setState("");
1294 QCOMPARE(rect->color(),QColor("red"));
1295
1296 QQuickState *state = rect->findChild<QQuickState*>();
1297 QVERIFY(state != nullptr);
1298 delete state;
1299
1300 QVERIFY(!sg->findState("blue"));
1301
1302 //### should we warn that state doesn't exist
1303 sg->setState("blue");
1304 QCOMPARE(rect->color(),QColor("red"));
1305
1306 delete rect;
1307}
1308
1309void tst_qquickstates::tempState()
1310{
1311 QQmlEngine engine;
1312
1313 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "legalTempState.qml"));
1314 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1315 QVERIFY(rect != nullptr);
1316 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1317 QTest::ignoreMessage(type: QtDebugMsg, message: "entering placed");
1318 QTest::ignoreMessage(type: QtDebugMsg, message: "entering idle");
1319 rectPrivate->setState("placed");
1320 QCOMPARE(rectPrivate->state(), QLatin1String("idle"));
1321}
1322
1323void tst_qquickstates::illegalTempState()
1324{
1325 QQmlEngine engine;
1326
1327 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "illegalTempState.qml"));
1328 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1329 QVERIFY(rect != nullptr);
1330 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1331 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: QML StateGroup: Can't apply a state change as part of a state definition.");
1332 rectPrivate->setState("placed");
1333 QCOMPARE(rectPrivate->state(), QLatin1String("placed"));
1334}
1335
1336void tst_qquickstates::nonExistantProperty()
1337{
1338 QQmlEngine engine;
1339
1340 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "nonExistantProp.qml"));
1341 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: rectComponent.create());
1342 QVERIFY(rect != nullptr);
1343 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1344 QTest::ignoreMessage(type: QtWarningMsg, message: fullDataPath(path: "nonExistantProp.qml") + ":9:9: QML PropertyChanges: Cannot assign to non-existent property \"colr\"");
1345 rectPrivate->setState("blue");
1346 QCOMPARE(rectPrivate->state(), QLatin1String("blue"));
1347}
1348
1349void tst_qquickstates::reset()
1350{
1351 QQmlEngine engine;
1352
1353 QQmlComponent c(&engine, testFileUrl(fileName: "reset.qml"));
1354 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1355 QVERIFY(rect != nullptr);
1356
1357 QQuickImage *image = rect->findChild<QQuickImage*>();
1358 QVERIFY(image != nullptr);
1359 QCOMPARE(image->width(), qreal(40.));
1360 QCOMPARE(image->height(), qreal(20.));
1361
1362 QQuickItemPrivate::get(item: rect)->setState("state1");
1363
1364 QCOMPARE(image->width(), 20.0);
1365 QCOMPARE(image->height(), qreal(20.));
1366
1367 delete rect;
1368}
1369
1370void tst_qquickstates::illegalObjectCreation()
1371{
1372 QQmlEngine engine;
1373
1374 QQmlComponent component(&engine, testFileUrl(fileName: "illegalObj.qml"));
1375 QList<QQmlError> errors = component.errors();
1376 QCOMPARE(errors.count(), 1);
1377 const QQmlError &error = errors.at(i: 0);
1378 QCOMPARE(error.line(), 9);
1379 QCOMPARE(error.column(), 23);
1380 QCOMPARE(error.description().toUtf8().constData(), "PropertyChanges does not support creating state-specific objects.");
1381}
1382
1383void tst_qquickstates::whenOrdering()
1384{
1385 QQmlEngine engine;
1386
1387 QQmlComponent c(&engine, testFileUrl(fileName: "whenOrdering.qml"));
1388 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1389 QVERIFY(rect != nullptr);
1390 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1391
1392 QCOMPARE(rectPrivate->state(), QLatin1String(""));
1393 rect->setProperty(name: "condition2", value: true);
1394 QCOMPARE(rectPrivate->state(), QLatin1String("state2"));
1395 rect->setProperty(name: "condition1", value: true);
1396 QCOMPARE(rectPrivate->state(), QLatin1String("state1"));
1397 rect->setProperty(name: "condition2", value: false);
1398 QCOMPARE(rectPrivate->state(), QLatin1String("state1"));
1399 rect->setProperty(name: "condition2", value: true);
1400 QCOMPARE(rectPrivate->state(), QLatin1String("state1"));
1401 rect->setProperty(name: "condition1", value: false);
1402 rect->setProperty(name: "condition2", value: false);
1403 QCOMPARE(rectPrivate->state(), QLatin1String(""));
1404}
1405
1406void tst_qquickstates::urlResolution()
1407{
1408 QQmlEngine engine;
1409
1410 QQmlComponent c(&engine, testFileUrl(fileName: "urlResolution.qml"));
1411 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1412 QVERIFY(rect != nullptr);
1413
1414 QQuickItem *myType = rect->findChild<QQuickItem*>(aName: "MyType");
1415 QQuickImage *image1 = rect->findChild<QQuickImage*>(aName: "image1");
1416 QQuickImage *image2 = rect->findChild<QQuickImage*>(aName: "image2");
1417 QQuickImage *image3 = rect->findChild<QQuickImage*>(aName: "image3");
1418 QVERIFY(myType != nullptr && image1 != nullptr && image2 != nullptr && image3 != nullptr);
1419
1420 QQuickItemPrivate::get(item: myType)->setState("SetImageState");
1421 QUrl resolved = testFileUrl(fileName: "Implementation/images/qt-logo.png");
1422 QCOMPARE(image1->source(), resolved);
1423 QCOMPARE(image2->source(), resolved);
1424 QCOMPARE(image3->source(), resolved);
1425
1426 delete rect;
1427}
1428
1429void tst_qquickstates::unnamedWhen()
1430{
1431 QQmlEngine engine;
1432
1433 QQmlComponent c(&engine, testFileUrl(fileName: "unnamedWhen.qml"));
1434 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1435 QVERIFY(rect != nullptr);
1436 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1437
1438 QCOMPARE(rectPrivate->state(), QLatin1String(""));
1439 QCOMPARE(rect->property("stateString").toString(), QLatin1String(""));
1440 rect->setProperty(name: "triggerState", value: true);
1441 QCOMPARE(rectPrivate->state(), QLatin1String("anonymousState1"));
1442 QCOMPARE(rect->property("stateString").toString(), QLatin1String("inState"));
1443 rect->setProperty(name: "triggerState", value: false);
1444 QCOMPARE(rectPrivate->state(), QLatin1String(""));
1445 QCOMPARE(rect->property("stateString").toString(), QLatin1String(""));
1446}
1447
1448void tst_qquickstates::returnToBase()
1449{
1450 QQmlEngine engine;
1451
1452 QQmlComponent c(&engine, testFileUrl(fileName: "returnToBase.qml"));
1453 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1454 QVERIFY(rect != nullptr);
1455 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1456
1457 QCOMPARE(rectPrivate->state(), QLatin1String(""));
1458 QCOMPARE(rect->property("stateString").toString(), QLatin1String(""));
1459 rect->setProperty(name: "triggerState", value: true);
1460 QCOMPARE(rectPrivate->state(), QLatin1String("anonymousState1"));
1461 QCOMPARE(rect->property("stateString").toString(), QLatin1String("inState"));
1462 rect->setProperty(name: "triggerState", value: false);
1463 QCOMPARE(rectPrivate->state(), QLatin1String(""));
1464 QCOMPARE(rect->property("stateString").toString(), QLatin1String("originalState"));
1465}
1466
1467//QTBUG-12559
1468void tst_qquickstates::extendsBug()
1469{
1470 QQmlEngine engine;
1471
1472 QQmlComponent c(&engine, testFileUrl(fileName: "extendsBug.qml"));
1473 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1474 QVERIFY(rect != nullptr);
1475 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1476 QQuickRectangle *greenRect = rect->findChild<QQuickRectangle*>(aName: "greenRect");
1477
1478 rectPrivate->setState("b");
1479 QCOMPARE(greenRect->x(), qreal(100));
1480 QCOMPARE(greenRect->y(), qreal(100));
1481}
1482
1483void tst_qquickstates::editProperties()
1484{
1485 QQmlEngine engine;
1486
1487 QQmlComponent c(&engine, testFileUrl(fileName: "editProperties.qml"));
1488 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1489 QVERIFY(rect != nullptr);
1490
1491 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1492
1493 QQuickStateGroup *stateGroup = rectPrivate->_states();
1494 QVERIFY(stateGroup != nullptr);
1495 qmlExecuteDeferred(stateGroup);
1496
1497 QQuickState *blueState = stateGroup->findState(name: "blue");
1498 QVERIFY(blueState != nullptr);
1499 qmlExecuteDeferred(blueState);
1500
1501 QQuickPropertyChanges *propertyChangesBlue = qobject_cast<QQuickPropertyChanges*>(object: blueState->operationAt(0));
1502 QVERIFY(propertyChangesBlue != nullptr);
1503
1504 QQuickState *greenState = stateGroup->findState(name: "green");
1505 QVERIFY(greenState != nullptr);
1506 qmlExecuteDeferred(greenState);
1507
1508 QQuickPropertyChanges *propertyChangesGreen = qobject_cast<QQuickPropertyChanges*>(object: greenState->operationAt(0));
1509 QVERIFY(propertyChangesGreen != nullptr);
1510
1511 QQuickRectangle *childRect = rect->findChild<QQuickRectangle*>(aName: "rect2");
1512 QVERIFY(childRect != nullptr);
1513 QCOMPARE(childRect->width(), qreal(402));
1514 QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1515 QCOMPARE(childRect->height(), qreal(200));
1516
1517 rectPrivate->setState("blue");
1518 QCOMPARE(childRect->width(), qreal(50));
1519 QCOMPARE(childRect->height(), qreal(40));
1520 QVERIFY(!QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1521 QVERIFY(blueState->bindingInRevertList(childRect, "width"));
1522
1523
1524 rectPrivate->setState("green");
1525 QCOMPARE(childRect->width(), qreal(200));
1526 QCOMPARE(childRect->height(), qreal(100));
1527 QVERIFY(greenState->bindingInRevertList(childRect, "width"));
1528
1529
1530 rectPrivate->setState("");
1531
1532
1533 QCOMPARE(propertyChangesBlue->actions().length(), 2);
1534 QVERIFY(propertyChangesBlue->containsValue("width"));
1535 QVERIFY(!propertyChangesBlue->containsProperty("x"));
1536 QCOMPARE(propertyChangesBlue->value("width").toInt(), 50);
1537 QVERIFY(!propertyChangesBlue->value("x").isValid());
1538
1539 propertyChangesBlue->changeValue(name: "width", value: 60);
1540 QCOMPARE(propertyChangesBlue->value("width").toInt(), 60);
1541 QCOMPARE(propertyChangesBlue->actions().length(), 2);
1542
1543
1544 propertyChangesBlue->changeExpression(name: "width", expression: "myRectangle.width / 2");
1545 QVERIFY(!propertyChangesBlue->containsValue("width"));
1546 QVERIFY(propertyChangesBlue->containsExpression("width"));
1547 QCOMPARE(propertyChangesBlue->value("width").toInt(), 0);
1548 QCOMPARE(propertyChangesBlue->actions().length(), 2);
1549
1550 propertyChangesBlue->changeValue(name: "width", value: 50);
1551 QVERIFY(propertyChangesBlue->containsValue("width"));
1552 QVERIFY(!propertyChangesBlue->containsExpression("width"));
1553 QCOMPARE(propertyChangesBlue->value("width").toInt(), 50);
1554 QCOMPARE(propertyChangesBlue->actions().length(), 2);
1555
1556 QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1557 rectPrivate->setState("blue");
1558 QCOMPARE(childRect->width(), qreal(50));
1559 QCOMPARE(childRect->height(), qreal(40));
1560
1561 propertyChangesBlue->changeValue(name: "width", value: 60);
1562 QCOMPARE(propertyChangesBlue->value("width").toInt(), 60);
1563 QCOMPARE(propertyChangesBlue->actions().length(), 2);
1564 QCOMPARE(childRect->width(), qreal(60));
1565 QVERIFY(!QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1566
1567 propertyChangesBlue->changeExpression(name: "width", expression: "myRectangle.width / 2");
1568 QVERIFY(!propertyChangesBlue->containsValue("width"));
1569 QVERIFY(propertyChangesBlue->containsExpression("width"));
1570 QCOMPARE(propertyChangesBlue->value("width").toInt(), 0);
1571 QCOMPARE(propertyChangesBlue->actions().length(), 2);
1572 QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1573 QCOMPARE(childRect->width(), qreal(200));
1574
1575 propertyChangesBlue->changeValue(name: "width", value: 50);
1576 QCOMPARE(childRect->width(), qreal(50));
1577
1578 rectPrivate->setState("");
1579 QCOMPARE(childRect->width(), qreal(402));
1580 QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1581
1582 QCOMPARE(propertyChangesGreen->actions().length(), 2);
1583 rectPrivate->setState("green");
1584 QCOMPARE(childRect->width(), qreal(200));
1585 QCOMPARE(childRect->height(), qreal(100));
1586 QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1587 QVERIFY(greenState->bindingInRevertList(childRect, "width"));
1588 QCOMPARE(propertyChangesGreen->actions().length(), 2);
1589
1590
1591 propertyChangesGreen->removeProperty(name: "height");
1592 QVERIFY(!QQmlPropertyPrivate::binding(QQmlProperty(childRect, "height")));
1593 QCOMPARE(childRect->height(), qreal(200));
1594
1595 QVERIFY(greenState->bindingInRevertList(childRect, "width"));
1596 QVERIFY(greenState->containsPropertyInRevertList(childRect, "width"));
1597 propertyChangesGreen->removeProperty(name: "width");
1598 QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width")));
1599 QCOMPARE(childRect->width(), qreal(402));
1600 QVERIFY(!greenState->bindingInRevertList(childRect, "width"));
1601 QVERIFY(!greenState->containsPropertyInRevertList(childRect, "width"));
1602
1603 propertyChangesBlue->removeProperty(name: "width");
1604 QCOMPARE(childRect->width(), qreal(402));
1605
1606 rectPrivate->setState("blue");
1607 QCOMPARE(childRect->width(), qreal(402));
1608 QCOMPARE(childRect->height(), qreal(40));
1609}
1610
1611void tst_qquickstates::QTBUG_14830()
1612{
1613 QQmlEngine engine;
1614
1615 QQmlComponent c(&engine, testFileUrl(fileName: "QTBUG-14830.qml"));
1616 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1617 QVERIFY(rect != nullptr);
1618 QQuickItem *item = rect->findChild<QQuickItem*>(aName: "area");
1619
1620 QCOMPARE(item->width(), qreal(170));
1621}
1622
1623void tst_qquickstates::avoidFastForward()
1624{
1625 QQmlEngine engine;
1626
1627 //shouldn't fast forward if there isn't a transition
1628 QQmlComponent c(&engine, testFileUrl(fileName: "avoidFastForward.qml"));
1629 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1630 QVERIFY(rect != nullptr);
1631
1632 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1633 rectPrivate->setState("a");
1634 QCOMPARE(rect->property("updateCount").toInt(), 1);
1635}
1636
1637//QTBUG-22583
1638void tst_qquickstates::revertListBug()
1639{
1640 QQmlEngine engine;
1641
1642 QQmlComponent c(&engine, testFileUrl(fileName: "revertListBug.qml"));
1643 QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(object: c.create());
1644 QVERIFY(rect != nullptr);
1645
1646 QQuickRectangle *rect1 = rect->findChild<QQuickRectangle*>(aName: "rect1");
1647 QQuickRectangle *rect2 = rect->findChild<QQuickRectangle*>(aName: "rect2");
1648 QQuickItem *origParent1 = rect->findChild<QQuickItem*>(aName: "originalParent1");
1649 QQuickItem *origParent2 = rect->findChild<QQuickItem*>(aName: "originalParent2");
1650 QQuickItem *newParent = rect->findChild<QQuickItem*>(aName: "newParent");
1651
1652 QCOMPARE(rect1->parentItem(), origParent1);
1653 QCOMPARE(rect2->parentItem(), origParent2);
1654
1655 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1656 rectPrivate->setState("reparented");
1657
1658 QCOMPARE(rect1->parentItem(), newParent);
1659 QCOMPARE(rect2->parentItem(), origParent2);
1660
1661 rectPrivate->setState("");
1662
1663 QCOMPARE(rect1->parentItem(), origParent1);
1664 QCOMPARE(rect2->parentItem(), origParent2);
1665
1666 QMetaObject::invokeMethod(obj: rect, member: "switchTargetItem");
1667
1668 rectPrivate->setState("reparented");
1669
1670 QCOMPARE(rect1->parentItem(), origParent1);
1671 QCOMPARE(rect2->parentItem(), newParent);
1672
1673 rectPrivate->setState("");
1674
1675 QCOMPARE(rect1->parentItem(), origParent1);
1676 QCOMPARE(rect2->parentItem(), origParent2); //QTBUG-22583 causes rect2's parent item to be origParent1
1677}
1678
1679void tst_qquickstates::QTBUG_38492()
1680{
1681 QQmlEngine engine;
1682
1683 QQmlComponent rectComponent(&engine, testFileUrl(fileName: "QTBUG-38492.qml"));
1684 QQuickItem *item = qobject_cast<QQuickItem*>(object: rectComponent.create());
1685 QVERIFY(item != nullptr);
1686
1687 QQuickItemPrivate::get(item)->setState("apply");
1688
1689 QCOMPARE(item->property("text").toString(), QString("Test"));
1690
1691 delete item;
1692}
1693
1694void tst_qquickstates::revertListMemoryLeak()
1695{
1696 QQmlAbstractBinding::Ptr bindingPtr;
1697 {
1698 QQmlEngine engine;
1699
1700 QQmlComponent c(&engine, testFileUrl(fileName: "revertListMemoryLeak.qml"));
1701 QQuickItem *item = qobject_cast<QQuickItem *>(object: c.create());
1702 QVERIFY(item);
1703 QQuickState *state = item->findChild<QQuickState*>(aName: "testState");
1704 QVERIFY(state);
1705
1706 item->setState("testState");
1707
1708 QQmlAbstractBinding *binding = state->bindingInRevertList(target: item, name: "height");
1709 QVERIFY(binding);
1710 bindingPtr = binding;
1711 QVERIFY(bindingPtr->ref > 1);
1712
1713 delete item;
1714 }
1715 QVERIFY(bindingPtr->ref == 1);
1716}
1717
1718void tst_qquickstates::duplicateStateName()
1719{
1720 QQmlEngine engine;
1721
1722 QQmlComponent c(&engine, testFileUrl(fileName: "duplicateStateName.qml"));
1723 QTest::ignoreMessage(type: QtWarningMsg, message: fullDataPath(path: "duplicateStateName.qml") + ":3:1: QML Rectangle: Found duplicate state name: state1");
1724 QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem *>(object: c.create()));
1725 QVERIFY(!item.isNull());
1726}
1727
1728// QTBUG-76838
1729void tst_qquickstates::trivialWhen()
1730{
1731 QQmlEngine engine;
1732
1733 QQmlComponent c(&engine, testFileUrl(fileName: "trivialWhen.qml"));
1734 QVERIFY(c.create());
1735}
1736
1737void tst_qquickstates::noStateOsciallation()
1738{
1739 QQmlEngine engine;
1740 QQmlComponent component(&engine, testFileUrl(fileName: "noStateOsciallation.qml"));
1741 QScopedPointer<QObject> root {component.create()};
1742 QVERIFY(root);
1743 // set to 1 on initial transition from "" to "n2"
1744 QCOMPARE(root->property("stateChangeCounter").toInt(), 1);
1745 root->setProperty(name: "number", value: 1);
1746 // setting number to 1 changes directly from "n2" to "n1"
1747 // without any intermediate transition to ""
1748 QCOMPARE(root->property("stateChangeCounter").toInt(), 2);
1749}
1750
1751void tst_qquickstates::parentChangeCorrectReversal()
1752{
1753 QQmlEngine engine;
1754 QQmlComponent c(&engine, testFileUrl(fileName: "parentChangeCorrectReversal.qml"));
1755 QScopedPointer<QObject> root {c.create()};
1756 QVERIFY(root);
1757 QQmlProperty stayingRectX(root.get(), "stayingRectX");
1758 qreal oldX = stayingRectX.read().toDouble();
1759 QQmlProperty switchToRight(root.get(), "switchToRight");
1760 switchToRight.write(true);
1761 qreal newX = stayingRectX.read().toDouble();
1762 QVERIFY(newX != oldX);
1763 switchToRight.write(false);
1764 QCOMPARE(oldX, stayingRectX.read().toDouble());
1765}
1766
1767void tst_qquickstates::revertNullObjectBinding()
1768{
1769 QQmlEngine engine;
1770
1771 QQmlComponent c(&engine, testFileUrl(fileName: "revertNullObjectBinding.qml"));
1772 QScopedPointer<QObject> root { c.create() };
1773 QVERIFY(root);
1774 QTest::qWait(ms: 10);
1775 QQmlProperty state2Active(root.get(), "state2Active");
1776 state2Active.write(false);
1777}
1778
1779QTEST_MAIN(tst_qquickstates)
1780
1781#include "tst_qquickstates.moc"
1782

source code of qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp