aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/declarative
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/declarative')
-rw-r--r--tests/auto/declarative/declarative.pro24
-rw-r--r--tests/auto/declarative/examples/tst_examples.cpp42
-rw-r--r--tests/auto/declarative/geometry/geometry.pro10
-rw-r--r--tests/auto/declarative/geometry/tst_geometry.cpp181
-rw-r--r--tests/auto/declarative/node/nodes.pro10
-rw-r--r--tests/auto/declarative/node/tst_nodestest.cpp354
-rw-r--r--tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp30
-rw-r--r--tests/auto/declarative/qdeclarativedom/data/MyItem.qml4
-rw-r--r--tests/auto/declarative/qdeclarativedom/data/import/Bar.qml2
-rw-r--r--tests/auto/declarative/qdeclarativedom/data/importlib/sublib/Foo.qml2
-rw-r--r--tests/auto/declarative/qdeclarativedom/data/importlib/sublib/qmldir2
-rw-r--r--tests/auto/declarative/qdeclarativedom/data/top.qml6
-rw-r--r--tests/auto/declarative/qdeclarativedom/tst_qdeclarativedom.cpp1326
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/Scope6Nested.qml7
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleOne.qml9
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleTwo.qml9
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFive.js3
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFour.js9
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importOne.js13
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importPragmaLibrary.js9
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importThree.js9
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importTwo.js10
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importWithNoImports.js11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImport.qml14
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportPragmaLibrary.qml20
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportScoping.qml11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testModuleImport.js8
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testScriptImport.js11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFive.qml11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFour.qml7
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failOne.qml7
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failThree.qml8
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failTwo.qml7
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importOne.js7
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importPragmaLibrary.js11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importWithImports.js13
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testImportPragmaLibrary.qml8
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testModuleImport.js8
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testScriptImport.js11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/moduleApi.qml18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/moduleApiCaching.qml12
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMajorVersionFail.qml10
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMinorVersionFail.qml10
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/moduleApiWriting.qml27
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/realToInt.qml11
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/ScarceResourceSignalComponent.qml9
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopy.qml15
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyFromJs.qml15
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.js24
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.qml18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.js18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.qml8
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.js14
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.qml12
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyNoBinding.qml14
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceDestroyedCopy.qml14
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunction.qml23
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunctionFail.qml23
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceSignal.qml29
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.js48
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.qml14
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestMultiple.qml16
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestPreserve.qml15
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scope.5.qml27
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/scope.6.qml10
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp45
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.h51
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp409
-rw-r--r--tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp12
-rw-r--r--tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp8
-rw-r--r--tests/auto/declarative/qdeclarativeinfo/data/NestedComponent.qml23
-rw-r--r--tests/auto/declarative/qdeclarativeinfo/tst_qdeclarativeinfo.cpp24
-rw-r--r--tests/auto/declarative/qdeclarativeitem/data/keynavigationtest_implicit.qml68
-rw-r--r--tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp127
-rw-r--r--tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.errors.txt1
-rw-r--r--tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.qml5
-rw-r--r--tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp1
-rw-r--r--tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp8
-rw-r--r--tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp15
-rw-r--r--tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/qmldir2
-rw-r--r--tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/temptest.qml14
-rw-r--r--tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/Test.qml (renamed from tests/auto/declarative/qdeclarativedom/data/MyComponent.qml)1
-rw-r--r--tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/qmldir3
-rw-r--r--tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/temptest2.qml8
-rw-r--r--tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp49
-rw-r--r--tests/auto/declarative/qdeclarativeparticles/tst_qdeclarativeparticles.cpp8
-rw-r--r--tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp3
-rw-r--r--tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp9
-rw-r--r--tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp14
-rw-r--r--tests/auto/declarative/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp4
-rw-r--r--tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp110
-rw-r--r--tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp8
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/doubleBoolJump.qml18
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/fetchException.qml6
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/logicalOr.2.qml6
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/logicalOr.qml6
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/nestedObjectAccess.qml5
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/nullQObject.qml7
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/qrealToIntRounding.qml10
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/subscriptionsInConditionalExpressions.qml11
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/unaryMinus.qml18
-rw-r--r--tests/auto/declarative/qdeclarativev4/data/unnecessaryReeval.qml7
-rw-r--r--tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro18
-rw-r--r--tests/auto/declarative/qdeclarativev4/testtypes.cpp49
-rw-r--r--tests/auto/declarative/qdeclarativev4/testtypes.h83
-rw-r--r--tests/auto/declarative/qdeclarativev4/tst_qdeclarativev4.cpp226
-rw-r--r--tests/auto/declarative/qdeclarativeview/tst_qdeclarativeview.cpp2
-rw-r--r--tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp2
-rw-r--r--tests/auto/declarative/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp22
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.0.pngbin263 -> 343 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.1.pngbin280 -> 349 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.2.pngbin270 -> 345 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.3.pngbin280 -> 349 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.4.pngbin280 -> 353 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.5.pngbin283 -> 351 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.6.pngbin281 -> 349 bytes
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.qml784
-rw-r--r--tests/auto/declarative/qmlvisual/qdeclarativepositioners/dynamic.qml13
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/colors.gifbin0 -> 505 bytes
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/colors.qml5
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/hearts.gifbin0 -> 6524 bytes
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/hearts.qml6
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/qmldir1
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/qtbug-16520.qml17
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/stickman.gifbin0 -> 164923 bytes
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/stickman.qml5
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/stickmanerror1.qml6
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/stickmanpause.qml7
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/stickmanscaled.qml7
-rw-r--r--tests/auto/declarative/qsganimatedimage/data/stickmanstopped.qml6
-rw-r--r--tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro16
-rw-r--r--tests/auto/declarative/qsganimatedimage/tst_qsganimatedimage.cpp387
-rw-r--r--tests/auto/declarative/qsgborderimage/data/colors-round-remote.sci7
-rw-r--r--tests/auto/declarative/qsgborderimage/data/colors-round.sci7
-rw-r--r--tests/auto/declarative/qsgborderimage/data/colors.pngbin0 -> 1655 bytes
-rw-r--r--tests/auto/declarative/qsgborderimage/data/heart200.pngbin0 -> 7943 bytes
-rw-r--r--tests/auto/declarative/qsgborderimage/data/invalid.sci7
-rw-r--r--tests/auto/declarative/qsgborderimage/qsgborderimage.pro17
-rw-r--r--tests/auto/declarative/qsgborderimage/tst_qsgborderimage.cpp426
-rw-r--r--tests/auto/declarative/qsgcanvas/qsgcanvas.pro7
-rw-r--r--tests/auto/declarative/qsgcanvas/tst_qsgcanvas.cpp437
-rw-r--r--tests/auto/declarative/qsgflickable/data/disabledcontent.qml8
-rw-r--r--tests/auto/declarative/qsgflickable/data/flickable01.qml4
-rw-r--r--tests/auto/declarative/qsgflickable/data/flickable02.qml14
-rw-r--r--tests/auto/declarative/qsgflickable/data/flickable03.qml14
-rw-r--r--tests/auto/declarative/qsgflickable/data/flickable04.qml22
-rw-r--r--tests/auto/declarative/qsgflickable/data/flickableqgraphicswidget.qml7
-rw-r--r--tests/auto/declarative/qsgflickable/data/nestedPressDelay.qml33
-rw-r--r--tests/auto/declarative/qsgflickable/data/resize.qml27
-rw-r--r--tests/auto/declarative/qsgflickable/data/wheel.qml25
-rw-r--r--tests/auto/declarative/qsgflickable/qsgflickable.pro16
-rw-r--r--tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp450
-rw-r--r--tests/auto/declarative/qsgflipable/data/crash.qml9
-rw-r--r--tests/auto/declarative/qsgflipable/data/flipable-abort.qml10
-rw-r--r--tests/auto/declarative/qsgflipable/data/test-flipable.qml9
-rw-r--r--tests/auto/declarative/qsgflipable/qsgflipable.pro16
-rw-r--r--tests/auto/declarative/qsgflipable/tst_qsgflipable.cpp143
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/chain.qml28
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/forceActiveFocus.qml26
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/forcefocus.qml81
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/qtBug13380.qml24
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/signalEmission.qml33
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/test.qml77
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/test2.qml39
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/test3.qml52
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/test4.qml76
-rw-r--r--tests/auto/declarative/qsgfocusscope/data/test5.qml84
-rw-r--r--tests/auto/declarative/qsgfocusscope/qsgfocusscope.pro13
-rw-r--r--tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp540
-rw-r--r--tests/auto/declarative/qsggridview/data/attachedSignals.qml27
-rw-r--r--tests/auto/declarative/qsggridview/data/displaygrid.qml39
-rw-r--r--tests/auto/declarative/qsggridview/data/footer.qml40
-rw-r--r--tests/auto/declarative/qsggridview/data/gridview-enforcerange.qml58
-rw-r--r--tests/auto/declarative/qsggridview/data/gridview-initCurrent.qml52
-rw-r--r--tests/auto/declarative/qsggridview/data/gridview-noCurrent.qml52
-rw-r--r--tests/auto/declarative/qsggridview/data/gridview1.qml65
-rw-r--r--tests/auto/declarative/qsggridview/data/gridview2.qml26
-rw-r--r--tests/auto/declarative/qsggridview/data/gridview3.qml6
-rw-r--r--tests/auto/declarative/qsggridview/data/header.qml40
-rw-r--r--tests/auto/declarative/qsggridview/data/manual-highlight.qml48
-rw-r--r--tests/auto/declarative/qsggridview/data/mirroring.qml43
-rw-r--r--tests/auto/declarative/qsggridview/data/propertychangestest.qml69
-rw-r--r--tests/auto/declarative/qsggridview/data/setindex.qml29
-rw-r--r--tests/auto/declarative/qsggridview/qsggridview.pro (renamed from tests/auto/declarative/qdeclarativedom/qdeclarativedom.pro)2
-rw-r--r--tests/auto/declarative/qsggridview/tst_qsggridview.cpp2171
-rw-r--r--tests/auto/declarative/qsgimage/data/aspectratio.qml6
-rw-r--r--tests/auto/declarative/qsgimage/data/big.jpegbin0 -> 1700081 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/big256.pngbin0 -> 3566 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/colors.pngbin0 -> 1655 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/colors1.pngbin0 -> 1655 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/green.pngbin0 -> 314 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/heart-win32.pngbin0 -> 12621 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/heart.pngbin0 -> 12577 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/heart.svg55
-rw-r--r--tests/auto/declarative/qsgimage/data/heart200-win32.pngbin0 -> 8062 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/heart200.pngbin0 -> 8063 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/qtbug_16389.qml30
-rw-r--r--tests/auto/declarative/qsgimage/data/rect.pngbin0 -> 171 bytes
-rw-r--r--tests/auto/declarative/qsgimage/data/tiling.qml16
-rw-r--r--tests/auto/declarative/qsgimage/qsgimage.pro17
-rw-r--r--tests/auto/declarative/qsgimage/tst_qsgimage.cpp776
-rw-r--r--tests/auto/declarative/qsgitem/qsgitem.pro7
-rw-r--r--tests/auto/declarative/qsgitem/tst_qsgitem.cpp787
-rw-r--r--tests/auto/declarative/qsgitem2/data/childrenProperty.qml14
-rw-r--r--tests/auto/declarative/qsgitem2/data/childrenRect.qml27
-rw-r--r--tests/auto/declarative/qsgitem2/data/childrenRectBug.qml23
-rw-r--r--tests/auto/declarative/qsgitem2/data/childrenRectBug2.qml53
-rw-r--r--tests/auto/declarative/qsgitem2/data/childrenRectBug3.qml15
-rw-r--r--tests/auto/declarative/qsgitem2/data/implicitsize.qml19
-rw-r--r--tests/auto/declarative/qsgitem2/data/keynavigationtest.qml87
-rw-r--r--tests/auto/declarative/qsgitem2/data/keynavigationtest_implicit.qml68
-rw-r--r--tests/auto/declarative/qsgitem2/data/keyspriority.qml9
-rw-r--r--tests/auto/declarative/qsgitem2/data/keystest.qml24
-rw-r--r--tests/auto/declarative/qsgitem2/data/layoutmirroring.qml54
-rw-r--r--tests/auto/declarative/qsgitem2/data/mapCoordinates.qml43
-rw-r--r--tests/auto/declarative/qsgitem2/data/mouseFocus.qml20
-rw-r--r--tests/auto/declarative/qsgitem2/data/propertychanges.qml10
-rw-r--r--tests/auto/declarative/qsgitem2/data/qtbug_16871.qml5
-rw-r--r--tests/auto/declarative/qsgitem2/data/resourcesProperty.qml21
-rw-r--r--tests/auto/declarative/qsgitem2/data/transformCrash.qml13
-rw-r--r--tests/auto/declarative/qsgitem2/qsgitem.pro16
-rw-r--r--tests/auto/declarative/qsgitem2/tst_qsgitem.cpp1322
-rw-r--r--tests/auto/declarative/qsglistview/data/attachedSignals.qml24
-rw-r--r--tests/auto/declarative/qsglistview/data/displaylist.qml50
-rw-r--r--tests/auto/declarative/qsglistview/data/footer.qml38
-rw-r--r--tests/auto/declarative/qsglistview/data/header.qml38
-rw-r--r--tests/auto/declarative/qsglistview/data/header1.qml33
-rw-r--r--tests/auto/declarative/qsglistview/data/headerfooter.qml26
-rw-r--r--tests/auto/declarative/qsglistview/data/itemlist.qml43
-rw-r--r--tests/auto/declarative/qsglistview/data/listview-enforcerange.qml55
-rw-r--r--tests/auto/declarative/qsglistview/data/listview-initCurrent.qml51
-rw-r--r--tests/auto/declarative/qsglistview/data/listview-noCurrent.qml50
-rw-r--r--tests/auto/declarative/qsglistview/data/listview-sections.qml64
-rw-r--r--tests/auto/declarative/qsglistview/data/listview-sections_delegate.qml69
-rw-r--r--tests/auto/declarative/qsglistview/data/listviewtest.qml132
-rw-r--r--tests/auto/declarative/qsglistview/data/manual-highlight.qml47
-rw-r--r--tests/auto/declarative/qsglistview/data/propertychangestest.qml71
-rw-r--r--tests/auto/declarative/qsglistview/data/qtbug14821.qml31
-rw-r--r--tests/auto/declarative/qsglistview/data/qtbug16037.qml37
-rw-r--r--tests/auto/declarative/qsglistview/data/rightToLeft.qml42
-rw-r--r--tests/auto/declarative/qsglistview/data/sizelessthan1.qml26
-rw-r--r--tests/auto/declarative/qsglistview/data/strictlyenforcerange.qml29
-rw-r--r--tests/auto/declarative/qsglistview/incrementalmodel.cpp89
-rw-r--r--tests/auto/declarative/qsglistview/incrementalmodel.h68
-rw-r--r--tests/auto/declarative/qsglistview/qsglistview.pro16
-rw-r--r--tests/auto/declarative/qsglistview/tst_qsglistview.cpp2698
-rw-r--r--tests/auto/declarative/qsgloader/data/AnchoredLoader.qml14
-rw-r--r--tests/auto/declarative/qsgloader/data/BlueRect.qml8
-rw-r--r--tests/auto/declarative/qsgloader/data/CreationContextLoader.qml15
-rw-r--r--tests/auto/declarative/qsgloader/data/GraphicsWidget250x250.qml5
-rw-r--r--tests/auto/declarative/qsgloader/data/GreenRect.qml7
-rw-r--r--tests/auto/declarative/qsgloader/data/NoResize.qml8
-rw-r--r--tests/auto/declarative/qsgloader/data/NoResizeGraphicsWidget.qml9
-rw-r--r--tests/auto/declarative/qsgloader/data/QTBUG_16928.qml23
-rw-r--r--tests/auto/declarative/qsgloader/data/QTBUG_17114.qml18
-rw-r--r--tests/auto/declarative/qsgloader/data/Rect120x60.qml6
-rw-r--r--tests/auto/declarative/qsgloader/data/SetSourceComponent.qml9
-rw-r--r--tests/auto/declarative/qsgloader/data/SizeGraphicsWidgetToLoader.qml7
-rw-r--r--tests/auto/declarative/qsgloader/data/SizeLoaderToGraphicsWidget.qml5
-rw-r--r--tests/auto/declarative/qsgloader/data/SizeToItem.qml5
-rw-r--r--tests/auto/declarative/qsgloader/data/SizeToLoader.qml6
-rw-r--r--tests/auto/declarative/qsgloader/data/VmeError.qml7
-rw-r--r--tests/auto/declarative/qsgloader/data/crash.qml14
-rw-r--r--tests/auto/declarative/qsgloader/data/creationContext.qml8
-rw-r--r--tests/auto/declarative/qsgloader/data/differentorigin.qml3
-rw-r--r--tests/auto/declarative/qsgloader/data/implicitSize.qml28
-rw-r--r--tests/auto/declarative/qsgloader/data/nonItem.qml5
-rw-r--r--tests/auto/declarative/qsgloader/data/qmldir1
-rw-r--r--tests/auto/declarative/qsgloader/data/sameorigin-load.qml3
-rw-r--r--tests/auto/declarative/qsgloader/data/sameorigin.qml3
-rw-r--r--tests/auto/declarative/qsgloader/data/vmeErrors.qml6
-rw-r--r--tests/auto/declarative/qsgloader/qsgloader.pro19
-rw-r--r--tests/auto/declarative/qsgloader/tst_qsgloader.cpp559
-rw-r--r--tests/auto/declarative/qsgmousearea/data/clickThrough.qml23
-rw-r--r--tests/auto/declarative/qsgmousearea/data/clickThrough2.qml32
-rw-r--r--tests/auto/declarative/qsgmousearea/data/clickandhold.qml13
-rw-r--r--tests/auto/declarative/qsgmousearea/data/clicktwice.qml16
-rw-r--r--tests/auto/declarative/qsgmousearea/data/doubleclick.qml16
-rw-r--r--tests/auto/declarative/qsgmousearea/data/dragging.qml28
-rw-r--r--tests/auto/declarative/qsgmousearea/data/dragproperties.qml28
-rw-r--r--tests/auto/declarative/qsgmousearea/data/dragreset.qml28
-rw-r--r--tests/auto/declarative/qsgmousearea/data/hoverPosition.qml17
-rw-r--r--tests/auto/declarative/qsgmousearea/data/pressedOrdering.qml28
-rw-r--r--tests/auto/declarative/qsgmousearea/data/preventstealing.qml24
-rw-r--r--tests/auto/declarative/qsgmousearea/data/rejectEvent.qml28
-rw-r--r--tests/auto/declarative/qsgmousearea/data/updateMousePosOnClick.qml20
-rw-r--r--tests/auto/declarative/qsgmousearea/data/updateMousePosOnResize.qml38
-rw-r--r--tests/auto/declarative/qsgmousearea/qsgmousearea.pro17
-rw-r--r--tests/auto/declarative/qsgmousearea/tst_qsgmousearea.cpp705
-rw-r--r--tests/auto/declarative/qsgpathview/data/closedPath.qml24
-rw-r--r--tests/auto/declarative/qsgpathview/data/datamodel.qml37
-rw-r--r--tests/auto/declarative/qsgpathview/data/displaypath.qml59
-rw-r--r--tests/auto/declarative/qsgpathview/data/dragpath.qml19
-rw-r--r--tests/auto/declarative/qsgpathview/data/emptymodel.qml5
-rw-r--r--tests/auto/declarative/qsgpathview/data/openPath.qml10
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathUpdate.qml18
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathUpdateOnStartChanged.qml38
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathtest.qml14
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathview0.qml83
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathview1.qml4
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathview2.qml57
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathview3.qml59
-rw-r--r--tests/auto/declarative/qsgpathview/data/pathview_package.qml88
-rw-r--r--tests/auto/declarative/qsgpathview/data/propertychanges.qml116
-rw-r--r--tests/auto/declarative/qsgpathview/data/treemodel.qml19
-rw-r--r--tests/auto/declarative/qsgpathview/data/undefinedpath.qml17
-rw-r--r--tests/auto/declarative/qsgpathview/data/vdm.qml28
-rw-r--r--tests/auto/declarative/qsgpathview/qsgpathview.pro16
-rw-r--r--tests/auto/declarative/qsgpathview/tst_qsgpathview.cpp1058
-rw-r--r--tests/auto/declarative/qsgpincharea/data/pinchproperties.qml46
-rw-r--r--tests/auto/declarative/qsgpincharea/qsgpincharea.pro16
-rw-r--r--tests/auto/declarative/qsgpincharea/tst_qsgpincharea.cpp311
-rw-r--r--tests/auto/declarative/qsgpositioners/data/flow-testimplicitsize.qml19
-rw-r--r--tests/auto/declarative/qsgpositioners/data/flowtest-toptobottom.qml44
-rw-r--r--tests/auto/declarative/qsgpositioners/data/flowtest.qml43
-rw-r--r--tests/auto/declarative/qsgpositioners/data/grid-animated.qml64
-rw-r--r--tests/auto/declarative/qsgpositioners/data/grid-spacing.qml41
-rw-r--r--tests/auto/declarative/qsgpositioners/data/grid-toptobottom.qml41
-rw-r--r--tests/auto/declarative/qsgpositioners/data/gridtest.qml42
-rw-r--r--tests/auto/declarative/qsgpositioners/data/gridzerocolumns.qml40
-rw-r--r--tests/auto/declarative/qsgpositioners/data/horizontal-animated.qml44
-rw-r--r--tests/auto/declarative/qsgpositioners/data/horizontal-spacing.qml31
-rw-r--r--tests/auto/declarative/qsgpositioners/data/horizontal.qml29
-rw-r--r--tests/auto/declarative/qsgpositioners/data/propertychangestest.qml39
-rw-r--r--tests/auto/declarative/qsgpositioners/data/repeatertest.qml38
-rw-r--r--tests/auto/declarative/qsgpositioners/data/vertical-animated.qml41
-rw-r--r--tests/auto/declarative/qsgpositioners/data/vertical-spacing.qml28
-rw-r--r--tests/auto/declarative/qsgpositioners/data/vertical.qml27
-rw-r--r--tests/auto/declarative/qsgpositioners/qsgpositioners.pro15
-rw-r--r--tests/auto/declarative/qsgpositioners/tst_qsgpositioners.cpp1268
-rw-r--r--tests/auto/declarative/qsgrepeater/data/intmodel.qml29
-rw-r--r--tests/auto/declarative/qsgrepeater/data/itemlist.qml68
-rw-r--r--tests/auto/declarative/qsgrepeater/data/modelChanged.qml26
-rw-r--r--tests/auto/declarative/qsgrepeater/data/objlist.qml21
-rw-r--r--tests/auto/declarative/qsgrepeater/data/properties.qml11
-rw-r--r--tests/auto/declarative/qsgrepeater/data/repeater1.qml28
-rw-r--r--tests/auto/declarative/qsgrepeater/data/repeater2.qml36
-rw-r--r--tests/auto/declarative/qsgrepeater/qsgrepeater.pro15
-rw-r--r--tests/auto/declarative/qsgrepeater/tst_qsgrepeater.cpp697
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments.qml41
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_cb.pngbin0 -> 496 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_cc.pngbin0 -> 556 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_ct.pngbin0 -> 533 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_lb.pngbin0 -> 496 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_lc.pngbin0 -> 535 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_lt.pngbin0 -> 514 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_rb.pngbin0 -> 505 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_rc.pngbin0 -> 559 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/alignments_rt.pngbin0 -> 539 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/embeddedImagesLocal.qml5
-rw-r--r--tests/auto/declarative/qsgtext/data/embeddedImagesLocalError.qml5
-rw-r--r--tests/auto/declarative/qsgtext/data/embeddedImagesRemote.qml5
-rw-r--r--tests/auto/declarative/qsgtext/data/embeddedImagesRemoteError.qml5
-rw-r--r--tests/auto/declarative/qsgtext/data/horizontalAlignment_RightToLeft.qml23
-rw-r--r--tests/auto/declarative/qsgtext/data/http/exists.pngbin0 -> 2738 bytes
-rw-r--r--tests/auto/declarative/qsgtext/data/lineCount.qml15
-rw-r--r--tests/auto/declarative/qsgtext/data/lineHeight.qml15
-rw-r--r--tests/auto/declarative/qsgtext/data/qtbug_14734.qml10
-rw-r--r--tests/auto/declarative/qsgtext/data/rotated.qml18
-rw-r--r--tests/auto/declarative/qsgtext/qsgtext.pro21
-rw-r--r--tests/auto/declarative/qsgtext/tst_qsgtext.cpp1377
-rw-r--r--tests/auto/declarative/qsgtextedit/data/CursorRect.qml8
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments.qml41
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_cb.pngbin0 -> 496 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_cc.pngbin0 -> 556 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_ct.pngbin0 -> 533 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_lb.pngbin0 -> 496 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_lc.pngbin0 -> 535 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_lt.pngbin0 -> 514 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_rb.pngbin0 -> 505 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_rc.pngbin0 -> 559 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/alignments_rt.pngbin0 -> 539 bytes
-rw-r--r--tests/auto/declarative/qsgtextedit/data/cursorTest.qml8
-rw-r--r--tests/auto/declarative/qsgtextedit/data/cursorVisible.qml6
-rw-r--r--tests/auto/declarative/qsgtextedit/data/geometrySignals.qml12
-rw-r--r--tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml23
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/ErrItem.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/NormItem.qml6
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/cursorHttpTest.qml22
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail1.qml18
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail2.qml18
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestPass.qml18
-rw-r--r--tests/auto/declarative/qsgtextedit/data/http/qmldir4
-rw-r--r--tests/auto/declarative/qsgtextedit/data/httpfail/FailItem.qml5
-rw-r--r--tests/auto/declarative/qsgtextedit/data/httpslow/WaitItem.qml5
-rw-r--r--tests/auto/declarative/qsgtextedit/data/inputContext.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/inputMethodEvent.qml5
-rw-r--r--tests/auto/declarative/qsgtextedit/data/inputmethodhints.qml6
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselection_default.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselection_false.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselection_true.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselectionmode_characters.qml8
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselectionmode_default.qml7
-rw-r--r--tests/auto/declarative/qsgtextedit/data/mouseselectionmode_words.qml8
-rw-r--r--tests/auto/declarative/qsgtextedit/data/navigation.qml24
-rw-r--r--tests/auto/declarative/qsgtextedit/data/openInputPanel.qml6
-rw-r--r--tests/auto/declarative/qsgtextedit/data/positionAt.qml9
-rw-r--r--tests/auto/declarative/qsgtextedit/data/readOnly.qml12
-rw-r--r--tests/auto/declarative/qsgtextedit/qsgtextedit.pro14
-rw-r--r--tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp2388
-rw-r--r--tests/auto/declarative/qsgtextinput/data/cursorTest.qml8
-rw-r--r--tests/auto/declarative/qsgtextinput/data/cursorVisible.qml6
-rw-r--r--tests/auto/declarative/qsgtextinput/data/echoMode.qml11
-rw-r--r--tests/auto/declarative/qsgtextinput/data/geometrySignals.qml12
-rw-r--r--tests/auto/declarative/qsgtextinput/data/halign_center.pngbin0 -> 293 bytes
-rw-r--r--tests/auto/declarative/qsgtextinput/data/halign_left.pngbin0 -> 291 bytes
-rw-r--r--tests/auto/declarative/qsgtextinput/data/halign_right.pngbin0 -> 292 bytes
-rw-r--r--tests/auto/declarative/qsgtextinput/data/horizontalAlignment.qml22
-rw-r--r--tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml23
-rw-r--r--tests/auto/declarative/qsgtextinput/data/inputContext.qml8
-rw-r--r--tests/auto/declarative/qsgtextinput/data/inputMethodEvent.qml5
-rw-r--r--tests/auto/declarative/qsgtextinput/data/inputmethods.qml7
-rw-r--r--tests/auto/declarative/qsgtextinput/data/masks.qml7
-rw-r--r--tests/auto/declarative/qsgtextinput/data/maxLength.qml7
-rw-r--r--tests/auto/declarative/qsgtextinput/data/mouseselection_true.qml7
-rw-r--r--tests/auto/declarative/qsgtextinput/data/mouseselectionmode_characters.qml8
-rw-r--r--tests/auto/declarative/qsgtextinput/data/mouseselectionmode_default.qml7
-rw-r--r--tests/auto/declarative/qsgtextinput/data/mouseselectionmode_words.qml8
-rw-r--r--tests/auto/declarative/qsgtextinput/data/navigation.qml24
-rw-r--r--tests/auto/declarative/qsgtextinput/data/openInputPanel.qml6
-rw-r--r--tests/auto/declarative/qsgtextinput/data/positionAt.qml8
-rw-r--r--tests/auto/declarative/qsgtextinput/data/preeditAutoScroll.qml7
-rw-r--r--tests/auto/declarative/qsgtextinput/data/readOnly.qml12
-rw-r--r--tests/auto/declarative/qsgtextinput/data/validators.qml22
-rw-r--r--tests/auto/declarative/qsgtextinput/qsgtextinput.pro14
-rw-r--r--tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp2471
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/datalist.qml18
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/modelproperties.qml19
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/modelproperties2.qml19
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/objectlist.qml19
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/singlerole1.qml10
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/singlerole2.qml10
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/visualdatamodel.qml11
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro16
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp531
437 files changed, 31100 insertions, 1765 deletions
diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro
index 98e15e8b0e..8db6eb172c 100644
--- a/tests/auto/declarative/declarative.pro
+++ b/tests/auto/declarative/declarative.pro
@@ -26,6 +26,9 @@ SUBDIRS += \
qmlvisual \
moduleqt47
+SUBDIRS += \
+ qsgitem \
+
contains(QT_CONFIG, private_tests) {
SUBDIRS += \
qdeclarativeanchors \
@@ -39,7 +42,6 @@ contains(QT_CONFIG, private_tests) {
qdeclarativedebug \
qdeclarativedebugclient \
qdeclarativedebugservice \
- qdeclarativedom \
qdeclarativeecmascript \
qdeclarativeflickable \
qdeclarativeflipable \
@@ -75,7 +77,25 @@ contains(QT_CONFIG, private_tests) {
qdeclarativevisualdatamodel \
qdeclarativeworkerscript \
qdeclarativexmllistmodel \
- qpacketprotocol
+ qpacketprotocol \
+ qdeclarativev4 \
+ qsgcanvas \
+ qsgflickable \
+ qsgflipable \
+ qsgfocusscope \
+ qsggridview \
+ qsglistview \
+ qsgloader \
+ qsgmousearea \
+ qsgpathview \
+ qsgpincharea \
+ qsgpositioners \
+ qsgrepeater \
+ qsgtext \
+ qsgtextedit \
+ qsgtextinput \
+ qsgvisualdatamodel
+
}
# Tests which should run in Pulse
diff --git a/tests/auto/declarative/examples/tst_examples.cpp b/tests/auto/declarative/examples/tst_examples.cpp
index 1a5ff107a4..72d1ff639a 100644
--- a/tests/auto/declarative/examples/tst_examples.cpp
+++ b/tests/auto/declarative/examples/tst_examples.cpp
@@ -45,6 +45,7 @@
#include <QDebug>
#include "qmlruntime.h"
#include <QDeclarativeView>
+#include <QSGView>
#include <QDeclarativeError>
#ifdef Q_OS_SYMBIAN
@@ -61,10 +62,11 @@ public:
private slots:
void examples_data();
void examples();
+ void sgexamples_data();
+ void sgexamples();
void namingConvention();
private:
- QString qmlruntime;
QStringList excludedDirs;
void namingConvention(const QDir &);
@@ -73,24 +75,15 @@ private:
tst_examples::tst_examples()
{
- QString binaries = QLibraryInfo::location(QLibraryInfo::BinariesPath);
-
-#if defined(Q_WS_MAC)
- qmlruntime = QDir(binaries).absoluteFilePath("qml.app/Contents/MacOS/qml");
-#elif defined(Q_WS_WIN)
- qmlruntime = QDir(binaries).absoluteFilePath("qml.exe");
-#else
- qmlruntime = QDir(binaries).absoluteFilePath("qml");
-#endif
-
-
// Add directories you want excluded here
excludedDirs << "doc/src/snippets/declarative/visualdatamodel_rootindex";
excludedDirs << "doc/src/snippets/declarative/qtbinding";
+ excludedDirs << "doc/src/snippets/declarative/imports";
#ifdef QT_NO_WEBKIT
excludedDirs << "examples/declarative/modelviews/webview";
excludedDirs << "demos/declarative/webbrowser";
+ excludedDirs << "doc/src/snippets/declarative/webview";
#endif
#ifdef QT_NO_XMLPATTERNS
@@ -225,6 +218,31 @@ void tst_examples::examples()
QTest::qWaitForWindowShown(&viewer);
}
+void tst_examples::sgexamples_data()
+{
+ examples_data();
+}
+
+void tst_examples::sgexamples()
+{
+ qputenv("QMLSCENE_IMPORT_NAME", "quick1");
+ QFETCH(QString, file);
+
+ QSGView view;
+
+ QtMsgHandler old = qInstallMsgHandler(silentErrorsMsgHandler);
+ view.setSource(file);
+ qInstallMsgHandler(old);
+
+ if (view.status() == QSGView::Error)
+ qWarning() << view.errors();
+
+ QCOMPARE(view.status(), QSGView::Ready);
+ view.show();
+
+ QTest::qWaitForWindowShown(&view);
+}
+
QTEST_MAIN(tst_examples)
#include "tst_examples.moc"
diff --git a/tests/auto/declarative/geometry/geometry.pro b/tests/auto/declarative/geometry/geometry.pro
new file mode 100644
index 0000000000..93b9bdb8ae
--- /dev/null
+++ b/tests/auto/declarative/geometry/geometry.pro
@@ -0,0 +1,10 @@
+load(qttest_p4)
+QT += opengl declarative
+
+TARGET = tst_geometry
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_geometry.cpp
+
+CONFIG+=parallel_test
+
diff --git a/tests/auto/declarative/geometry/tst_geometry.cpp b/tests/auto/declarative/geometry/tst_geometry.cpp
new file mode 100644
index 0000000000..4df7800cb9
--- /dev/null
+++ b/tests/auto/declarative/geometry/tst_geometry.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt scene graph research project.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QString>
+#include <QtTest/QtTest>
+
+#include <qsggeometry.h>
+
+class GeometryTest : public QObject
+{
+ Q_OBJECT
+
+public:
+
+private Q_SLOTS:
+ void testPoint2D();
+ void testTexturedPoint2D();
+ void testCustomGeometry();
+
+private:
+};
+
+void GeometryTest::testPoint2D()
+{
+ QSGGeometry geometry(QSGGeometry::defaultAttributes_Point2D(), 4, 0);
+
+ QCOMPARE(geometry.attributeCount(), 1);
+ QCOMPARE(geometry.stride(), (int) sizeof(float) * 2);
+ QCOMPARE(geometry.vertexCount(), 4);
+ QCOMPARE(geometry.indexCount(), 0);
+ QVERIFY(geometry.indexData() == 0);
+
+ QSGGeometry::updateRectGeometry(&geometry, QRectF(1, 2, 3, 4));
+
+ QSGGeometry::Point2D *pts = geometry.vertexDataAsPoint2D();
+ QVERIFY(pts != 0);
+
+ QCOMPARE(pts[0].x, (float) 1);
+ QCOMPARE(pts[0].y, (float) 2);
+ QCOMPARE(pts[3].x, (float) 4);
+ QCOMPARE(pts[3].y, (float) 6);
+
+ // Verify that resize gives me enough allocated data without crashing...
+ geometry.allocate(100, 100);
+ pts = geometry.vertexDataAsPoint2D();
+ quint16 *is = geometry.indexDataAsUShort();
+ for (int i=0; i<100; ++i) {
+ pts[i].x = i;
+ pts[i].y = i + 100;
+ is[i] = i;
+ }
+ QVERIFY(true);
+}
+
+
+void GeometryTest::testTexturedPoint2D()
+{
+ QSGGeometry geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4, 0);
+
+ QCOMPARE(geometry.attributeCount(), 2);
+ QCOMPARE(geometry.stride(), (int) sizeof(float) * 4);
+ QCOMPARE(geometry.vertexCount(), 4);
+ QCOMPARE(geometry.indexCount(), 0);
+ QVERIFY(geometry.indexData() == 0);
+
+ QSGGeometry::updateTexturedRectGeometry(&geometry, QRectF(1, 2, 3, 4), QRectF(5, 6, 7, 8));
+
+ QSGGeometry::TexturedPoint2D *pts = geometry.vertexDataAsTexturedPoint2D();
+ QVERIFY(pts != 0);
+
+ QCOMPARE(pts[0].x, (float) 1);
+ QCOMPARE(pts[0].y, (float) 2);
+ QCOMPARE(pts[0].tx, (float) 5);
+ QCOMPARE(pts[0].ty, (float) 6);
+
+ QCOMPARE(pts[3].x, (float) 4);
+ QCOMPARE(pts[3].y, (float) 6);
+ QCOMPARE(pts[3].tx, (float) 12);
+ QCOMPARE(pts[3].ty, (float) 14);
+
+ // Verify that resize gives me enough allocated data without crashing...
+ geometry.allocate(100, 100);
+ pts = geometry.vertexDataAsTexturedPoint2D();
+ quint16 *is = geometry.indexDataAsUShort();
+ for (int i=0; i<100; ++i) {
+ pts[i].x = i;
+ pts[i].y = i + 100;
+ pts[i].tx = i + 200;
+ pts[i].ty = i + 300;
+ is[i] = i;
+ }
+ QVERIFY(true);
+}
+
+void GeometryTest::testCustomGeometry()
+{
+ struct V {
+ float x, y;
+ unsigned char r, g, b, a;
+ float v1, v2, v3, v4;
+ };
+
+ static QSGGeometry::Attribute attributes[] = {
+ { 0, 2, GL_FLOAT },
+ { 1, 4, GL_UNSIGNED_BYTE },
+ { 2, 4, GL_FLOAT },
+ };
+ static QSGGeometry::AttributeSet set = { 4, 6 * sizeof(float) + 4 * sizeof(unsigned char), attributes };
+
+ QSGGeometry geometry(set, 1000, 4000);
+
+ // Verify that space has been allocated.
+ quint16 *ii = geometry.indexDataAsUShort();
+ for (int i=0; i<geometry.indexCount(); ++i) {
+ ii[i] = i;
+ }
+
+ V *v = (V *) geometry.vertexData();
+ for (int i=0; i<geometry.vertexCount(); ++i) {
+ v[i].x = 0;
+ v[i].y = 1;
+ v[i].r = 2;
+ v[i].g = 3;
+ v[i].b = 4;
+ v[i].a = 5;
+ v[i].v1 = 6;
+ v[i].v2 = 7;
+ v[i].v3 = 8;
+ v[i].v4 = 9;
+ }
+
+ // Verify the data's integrity
+ for (int i=0; i<4000; ++i)
+ QCOMPARE(ii[i], (quint16) i);
+ for (int i=0; i<1000; ++i)
+ QVERIFY(v[i].v1 == 6);
+
+}
+
+
+QTEST_MAIN(GeometryTest);
+
+#include "tst_geometry.moc"
diff --git a/tests/auto/declarative/node/nodes.pro b/tests/auto/declarative/node/nodes.pro
new file mode 100644
index 0000000000..4d2c06f495
--- /dev/null
+++ b/tests/auto/declarative/node/nodes.pro
@@ -0,0 +1,10 @@
+load(qttest_p4)
+QT += opengl declarative
+
+TARGET = tst_nodestest
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_nodestest.cpp
+
+CONFIG+=parallel_test
+
diff --git a/tests/auto/declarative/node/tst_nodestest.cpp b/tests/auto/declarative/node/tst_nodestest.cpp
new file mode 100644
index 0000000000..a1b0a6ac91
--- /dev/null
+++ b/tests/auto/declarative/node/tst_nodestest.cpp
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt scene graph research project.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QString>
+#include <QtTest/QtTest>
+
+#include <qsgnode.h>
+#include <private/qsgrenderer_p.h>
+#include <private/qsgnodeupdater_p.h>
+
+#include <qsgsimplerectnode.h>
+
+class NodesTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ NodesTest();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase() {
+ delete widget;
+ }
+
+ // Root nodes
+ void propegate();
+ void propegateWithMultipleRoots();
+ void simulatedEffect_data();
+ void simulatedEffect();
+
+ // Opacity nodes
+ void basicOpacityNode();
+ void opacityPropegation();
+
+ // QSGNodeUpdater
+ void isBlockedCheck();
+
+private:
+ QGLWidget *widget;
+
+ QSGNodeUpdater updater;
+};
+
+void NodesTest::initTestCase()
+{
+ widget = new QGLWidget();
+ widget->resize(100, 30);
+ widget->show();
+}
+
+class DummyRenderer : public QSGRenderer
+{
+public:
+ DummyRenderer(QSGRootNode *root)
+ : QSGRenderer(QSGContext::createDefaultContext())
+ , changedNode(0)
+ , changedFlags(0)
+ , renderCount(0)
+ {
+ setRootNode(root);
+ }
+
+ void render() {
+ ++renderCount;
+ renderingOrder = ++globalRendereringOrder;
+ }
+
+ void nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) {
+ changedNode = node;
+ changedFlags = flags;
+ QSGRenderer::nodeChanged(node, flags);
+ }
+
+ QSGNode *changedNode;
+ QSGNode::DirtyFlags changedFlags;
+
+ int renderCount;
+ int renderingOrder;
+ static int globalRendereringOrder;
+};
+
+int DummyRenderer::globalRendereringOrder;
+
+NodesTest::NodesTest()
+{
+}
+
+
+void NodesTest::propegate()
+{
+ QSGRootNode root;
+ QSGNode child; child.setFlag(QSGNode::OwnedByParent, false);
+ root.appendChildNode(&child);
+
+ DummyRenderer renderer(&root);
+
+ child.markDirty(QSGNode::DirtyGeometry);
+
+ QCOMPARE(&child, renderer.changedNode);
+ QCOMPARE((int) renderer.changedFlags, (int) QSGNode::DirtyGeometry);
+}
+
+
+void NodesTest::propegateWithMultipleRoots()
+{
+ QSGRootNode root1;
+ QSGNode child2; child2.setFlag(QSGNode::OwnedByParent, false);
+ QSGRootNode root3; root3.setFlag(QSGNode::OwnedByParent, false);
+ QSGNode child4; child4.setFlag(QSGNode::OwnedByParent, false);
+
+ root1.appendChildNode(&child2);
+ child2.appendChildNode(&root3);
+ root3.appendChildNode(&child4);
+
+ DummyRenderer ren1(&root1);
+ DummyRenderer ren2(&root3);
+
+ child4.markDirty(QSGNode::DirtyGeometry);
+
+ QCOMPARE(ren1.changedNode, &child4);
+ QCOMPARE(ren2.changedNode, &child4);
+
+ QCOMPARE((int) ren1.changedFlags, (int) QSGNode::DirtyGeometry);
+ QCOMPARE((int) ren2.changedFlags, (int) QSGNode::DirtyGeometry);
+}
+
+
+
+class SimulatedEffectRenderer : public DummyRenderer
+{
+public:
+ SimulatedEffectRenderer(QSGRootNode *root, QSGBasicGeometryNode *c)
+ : DummyRenderer(root)
+ {
+ child = c;
+ }
+
+ void render() {
+ matrix = child->matrix() ? *child->matrix() : QMatrix4x4();
+ DummyRenderer::render();
+ }
+
+ QSGBasicGeometryNode *child;
+ QMatrix4x4 matrix;
+};
+
+
+class PseudoEffectNode : public QSGNode {
+public:
+ PseudoEffectNode(QSGRenderer *r)
+ : renderer(r)
+ {
+ setFlag(UsePreprocess);
+ }
+
+ void preprocess() {
+
+ if (renderer->rootNode()->parent()) {
+ // Mark the root dirty to build a clean state from the root and down
+ renderer->rootNode()->markDirty(QSGNode::DirtyAll);
+ }
+
+ renderer->renderScene();
+
+ if (renderer->rootNode()->parent()) {
+ // Mark the parent of the root dirty to force the root and down to be updated.
+ renderer->rootNode()->parent()->markDirty(QSGNode::DirtyAll);
+ }
+ }
+
+ QSGRenderer *renderer;
+};
+
+void NodesTest::simulatedEffect_data()
+{
+ QTest::addColumn<bool>("connected");
+
+ QTest::newRow("connected") << true;
+ QTest::newRow("disconnected") << false;
+}
+
+void NodesTest::simulatedEffect()
+{
+ QFETCH(bool, connected);
+
+ QSGRootNode root;
+ QSGRootNode source;
+ QSGTransformNode xform;
+ QSGSimpleRectNode geometry;
+ geometry.setRect(QRectF(0, 0, 1, 1));
+ geometry.setColor(Qt::red);
+
+ root.setFlag(QSGNode::OwnedByParent, false);
+ source.setFlag(QSGNode::OwnedByParent, false);
+ xform.setFlag(QSGNode::OwnedByParent, false);
+ geometry.setFlag(QSGNode::OwnedByParent, false);
+
+ SimulatedEffectRenderer rootRenderer(&root, &geometry);
+ SimulatedEffectRenderer sourceRenderer(&source, &geometry);
+
+ PseudoEffectNode effect(&sourceRenderer);
+
+ /*
+ root Source is redirected into effect using the SimulatedEffectRenderer
+ / \
+ xform effect
+ |
+ source
+ |
+ geometry
+ */
+
+ root.appendChildNode(&xform);
+ root.appendChildNode(&effect);
+ if (connected)
+ xform.appendChildNode(&source);
+ source.appendChildNode(&geometry);
+ QMatrix4x4 m; m.translate(1, 2, 3);
+ xform.setMatrix(m);
+
+ // Clear all dirty states...
+ updater.updateStates(&root);
+
+ rootRenderer.renderScene();
+
+ // compare that we got one render call to each
+ QCOMPARE(rootRenderer.renderCount, 1);
+ QCOMPARE(sourceRenderer.renderCount, 1);
+ QVERIFY(sourceRenderer.renderingOrder < rootRenderer.renderingOrder);
+ if (connected) // geometry is not rendered in this case, so skip it...
+ QCOMPARE(rootRenderer.matrix, xform.matrix());
+ QCOMPARE(sourceRenderer.matrix, QMatrix4x4());
+}
+
+void NodesTest::basicOpacityNode()
+{
+ QSGOpacityNode n;
+ QCOMPARE(n.opacity(), 1.);
+
+ n.setOpacity(0.5);
+ QCOMPARE(n.opacity(), 0.5);
+
+ n.setOpacity(-1);
+ QCOMPARE(n.opacity(), 0.);
+
+ n.setOpacity(2);
+ QCOMPARE(n.opacity(), 1.);
+}
+
+void NodesTest::opacityPropegation()
+{
+ QSGRootNode root;
+ QSGOpacityNode *a = new QSGOpacityNode;
+ QSGOpacityNode *b = new QSGOpacityNode;
+ QSGOpacityNode *c = new QSGOpacityNode;
+
+ QSGSimpleRectNode *geometry = new QSGSimpleRectNode;
+ geometry->setRect(0, 0, 100, 100);
+
+ root.appendChildNode(a);
+ a->appendChildNode(b);
+ b->appendChildNode(c);
+ c->appendChildNode(geometry);
+
+ a->setOpacity(0.9);
+ b->setOpacity(0.8);
+ c->setOpacity(0.7);
+
+ updater.updateStates(&root);
+
+ QCOMPARE(a->combinedOpacity(), 0.9);
+ QCOMPARE(b->combinedOpacity(), 0.9 * 0.8);
+ QCOMPARE(c->combinedOpacity(), 0.9 * 0.8 * 0.7);
+ QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.8 * 0.7);
+
+ b->setOpacity(0.1);
+ updater.updateStates(&root);
+
+ QCOMPARE(a->combinedOpacity(), 0.9);
+ QCOMPARE(b->combinedOpacity(), 0.9 * 0.1);
+ QCOMPARE(c->combinedOpacity(), 0.9 * 0.1 * 0.7);
+ QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.1 * 0.7);
+
+ b->setOpacity(0);
+ updater.updateStates(&root);
+
+ QVERIFY(b->isSubtreeBlocked());
+
+ // Verify that geometry did not get updated as it is in a blocked
+ // subtree
+ QCOMPARE(c->combinedOpacity(), 0.9 * 0.1 * 0.7);
+ QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.1 * 0.7);
+}
+
+void NodesTest::isBlockedCheck()
+{
+ QSGRootNode root;
+ QSGOpacityNode *opacity = new QSGOpacityNode();
+ QSGNode *node = new QSGNode();
+
+ root.appendChildNode(opacity);
+ opacity->appendChildNode(node);
+
+ QSGNodeUpdater updater;
+
+ opacity->setOpacity(0);
+ QVERIFY(updater.isNodeBlocked(node, &root));
+
+ opacity->setOpacity(1);
+ QVERIFY(!updater.isNodeBlocked(node, &root));
+}
+
+QTEST_MAIN(NodesTest);
+
+#include "tst_nodestest.moc"
diff --git a/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp b/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
index de4ddcc360..e63b14ef61 100644
--- a/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
+++ b/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
@@ -664,7 +664,13 @@ void tst_QDeclarativeDebug::queryAvailableEngines()
QCOMPARE(e.name(), m_engine->objectName());
}
+ // Make query invalid by deleting client
+ q_engines = m_dbg->queryAvailableEngines(this);
+ QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Waiting);
+ delete m_dbg;
+ QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Error);
delete q_engines;
+ m_dbg = new QDeclarativeEngineDebug(m_conn, this);
}
void tst_QDeclarativeDebug::queryRootContexts()
@@ -672,6 +678,7 @@ void tst_QDeclarativeDebug::queryRootContexts()
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
int engineId = q_engines->engines()[0].debugId();
+ delete q_engines;
QDeclarativeDebugRootContextQuery *q_context;
@@ -703,8 +710,13 @@ void tst_QDeclarativeDebug::queryRootContexts()
QVERIFY(context.contexts()[0].debugId() >= 0);
QCOMPARE(context.contexts()[0].name(), QString("tst_QDeclarativeDebug_childContext"));
- delete q_engines;
+ // Make query invalid by deleting client
+ q_context = m_dbg->queryRootContexts(engineId, this);
+ QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Waiting);
+ delete m_dbg;
+ QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Error);
delete q_context;
+ m_dbg = new QDeclarativeEngineDebug(m_conn, this);
}
void tst_QDeclarativeDebug::queryObject()
@@ -736,7 +748,14 @@ void tst_QDeclarativeDebug::queryObject()
delete q_engines;
delete q_context;
+
+ // Make query invalid by deleting client
+ q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
+ QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Waiting);
+ delete m_dbg;
+ QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Error);
delete q_obj;
+ m_dbg = new QDeclarativeEngineDebug(m_conn, this);
// check source as defined in main()
QDeclarativeDebugFileReference source = obj.source();
@@ -811,7 +830,14 @@ void tst_QDeclarativeDebug::queryExpressionResult()
delete q_engines;
delete q_context;
+
+ // Make query invalid by deleting client
+ q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
+ QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Waiting);
+ delete m_dbg;
+ QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Error);
delete q_expr;
+ m_dbg = new QDeclarativeEngineDebug(m_conn, this);
}
void tst_QDeclarativeDebug::queryExpressionResult_data()
@@ -1001,7 +1027,7 @@ void tst_QDeclarativeDebug::setBindingForObject()
// set handler
//
rootObject = findRootObject();
- QCOMPARE(rootObject.children().size(), 3);
+ QCOMPARE(rootObject.children().size(), 4); // Rectangle, Text, MouseArea, QDeclarativeComponentAttached
QDeclarativeDebugObjectReference mouseAreaObject = rootObject.children().at(2);
QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this);
waitForQuery(q_obj);
diff --git a/tests/auto/declarative/qdeclarativedom/data/MyItem.qml b/tests/auto/declarative/qdeclarativedom/data/MyItem.qml
deleted file mode 100644
index f6760b6a9f..0000000000
--- a/tests/auto/declarative/qdeclarativedom/data/MyItem.qml
+++ /dev/null
@@ -1,4 +0,0 @@
-import QtQuick 1.0
-
-Item {
-}
diff --git a/tests/auto/declarative/qdeclarativedom/data/import/Bar.qml b/tests/auto/declarative/qdeclarativedom/data/import/Bar.qml
deleted file mode 100644
index 86a7176896..0000000000
--- a/tests/auto/declarative/qdeclarativedom/data/import/Bar.qml
+++ /dev/null
@@ -1,2 +0,0 @@
-import QtQuick 1.0
-
diff --git a/tests/auto/declarative/qdeclarativedom/data/importlib/sublib/Foo.qml b/tests/auto/declarative/qdeclarativedom/data/importlib/sublib/Foo.qml
deleted file mode 100644
index 86a7176896..0000000000
--- a/tests/auto/declarative/qdeclarativedom/data/importlib/sublib/Foo.qml
+++ /dev/null
@@ -1,2 +0,0 @@
-import QtQuick 1.0
-
diff --git a/tests/auto/declarative/qdeclarativedom/data/importlib/sublib/qmldir b/tests/auto/declarative/qdeclarativedom/data/importlib/sublib/qmldir
deleted file mode 100644
index 98d6b7431d..0000000000
--- a/tests/auto/declarative/qdeclarativedom/data/importlib/sublib/qmldir
+++ /dev/null
@@ -1,2 +0,0 @@
-Foo 1.1 Foo.qml
-Foo 1.0 Foo.qml
diff --git a/tests/auto/declarative/qdeclarativedom/data/top.qml b/tests/auto/declarative/qdeclarativedom/data/top.qml
deleted file mode 100644
index 56ea9dfaad..0000000000
--- a/tests/auto/declarative/qdeclarativedom/data/top.qml
+++ /dev/null
@@ -1,6 +0,0 @@
-import QtQuick 1.0
-
-MyComponent {
- width: 100
- height: 100
-}
diff --git a/tests/auto/declarative/qdeclarativedom/tst_qdeclarativedom.cpp b/tests/auto/declarative/qdeclarativedom/tst_qdeclarativedom.cpp
deleted file mode 100644
index e62a114b78..0000000000
--- a/tests/auto/declarative/qdeclarativedom/tst_qdeclarativedom.cpp
+++ /dev/null
@@ -1,1326 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qtest.h>
-#include <QtDeclarative/qdeclarativeengine.h>
-#include <QtDeclarative/qdeclarativecomponent.h>
-#include <QtDeclarative/private/qdeclarativedom_p.h>
-
-#include <QtCore/QDebug>
-#include <QtCore/QFile>
-
-#ifdef Q_OS_SYMBIAN
-// In Symbian OS test data is located in applications private dir
-#define SRCDIR "."
-#endif
-
-class tst_qdeclarativedom : public QObject
-{
- Q_OBJECT
-public:
- tst_qdeclarativedom() {}
-
-private slots:
- void loadSimple();
- void loadProperties();
- void loadGroupedProperties();
- void loadChildObject();
- void loadComposite();
- void loadImports();
- void loadErrors();
- void loadSyntaxErrors();
- void loadRemoteErrors();
- void loadDynamicProperty();
- void loadComponent();
-
- void testValueSource();
- void testValueInterceptor();
-
- void object_dynamicProperty();
- void object_property();
- void object_url();
-
- void copy();
- void position();
-private:
- QDeclarativeEngine engine;
-};
-
-
-void tst_qdeclarativedom::loadSimple()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
- QVERIFY(document.errors().isEmpty());
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
- QVERIFY(!rootObject.isComponent());
- QVERIFY(!rootObject.isCustomType());
- QVERIFY(rootObject.objectType() == "QtQuick/Item");
- QVERIFY(rootObject.objectTypeMajorVersion() == 1);
- QVERIFY(rootObject.objectTypeMinorVersion() == 0);
-}
-
-// Test regular properties
-void tst_qdeclarativedom::loadProperties()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Item { id : item; x : 300; visible : true }";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
- QVERIFY(rootObject.objectId() == "item");
- QCOMPARE(rootObject.properties().size(), 3);
-
- QDeclarativeDomProperty xProperty = rootObject.property("x");
- QVERIFY(xProperty.propertyName() == "x");
- QCOMPARE(xProperty.propertyNameParts().count(), 1);
- QVERIFY(xProperty.propertyNameParts().at(0) == "x");
- QCOMPARE(xProperty.position(), 37);
- QCOMPARE(xProperty.length(), 1);
- QVERIFY(xProperty.value().isLiteral());
- QVERIFY(xProperty.value().toLiteral().literal() == "300");
-
- QDeclarativeDomProperty visibleProperty = rootObject.property("visible");
- QVERIFY(visibleProperty.propertyName() == "visible");
- QCOMPARE(visibleProperty.propertyNameParts().count(), 1);
- QVERIFY(visibleProperty.propertyNameParts().at(0) == "visible");
- QCOMPARE(visibleProperty.position(), 46);
- QCOMPARE(visibleProperty.length(), 7);
- QVERIFY(visibleProperty.value().isLiteral());
- QVERIFY(visibleProperty.value().toLiteral().literal() == "true");
-}
-
-// Test grouped properties
-void tst_qdeclarativedom::loadGroupedProperties()
-{
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item { anchors.left: parent.left; anchors.right: parent.right }";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QVERIFY(rootItem.properties().size() == 2);
-
- // Order is not deterministic
- QDeclarativeDomProperty p0 = rootItem.properties().at(0);
- QDeclarativeDomProperty p1 = rootItem.properties().at(1);
- QDeclarativeDomProperty leftProperty;
- QDeclarativeDomProperty rightProperty;
- if (p0.propertyName() == "anchors.left") {
- leftProperty = p0;
- rightProperty = p1;
- } else {
- leftProperty = p1;
- rightProperty = p0;
- }
-
- QVERIFY(leftProperty.propertyName() == "anchors.left");
- QCOMPARE(leftProperty.propertyNameParts().count(), 2);
- QVERIFY(leftProperty.propertyNameParts().at(0) == "anchors");
- QVERIFY(leftProperty.propertyNameParts().at(1) == "left");
- QCOMPARE(leftProperty.position(), 26);
- QCOMPARE(leftProperty.length(), 12);
- QVERIFY(leftProperty.value().isBinding());
- QVERIFY(leftProperty.value().toBinding().binding() == "parent.left");
-
- QVERIFY(rightProperty.propertyName() == "anchors.right");
- QCOMPARE(rightProperty.propertyNameParts().count(), 2);
- QVERIFY(rightProperty.propertyNameParts().at(0) == "anchors");
- QVERIFY(rightProperty.propertyNameParts().at(1) == "right");
- QCOMPARE(rightProperty.position(), 53);
- QCOMPARE(rightProperty.length(), 13);
- QVERIFY(rightProperty.value().isBinding());
- QVERIFY(rightProperty.value().toBinding().binding() == "parent.right");
- }
-
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item { \n"
- " anchors {\n"
- " left: parent.left\n"
- " right: parent.right\n"
- " }\n"
- "}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QVERIFY(rootItem.properties().size() == 2);
-
- // Order is not deterministic
- QDeclarativeDomProperty p0 = rootItem.properties().at(0);
- QDeclarativeDomProperty p1 = rootItem.properties().at(1);
- QDeclarativeDomProperty leftProperty;
- QDeclarativeDomProperty rightProperty;
- if (p0.propertyName() == "anchors.left") {
- leftProperty = p0;
- rightProperty = p1;
- } else {
- leftProperty = p1;
- rightProperty = p0;
- }
-
- QVERIFY(leftProperty.propertyName() == "anchors.left");
- QCOMPARE(leftProperty.propertyNameParts().count(), 2);
- QVERIFY(leftProperty.propertyNameParts().at(0) == "anchors");
- QVERIFY(leftProperty.propertyNameParts().at(1) == "left");
- QCOMPARE(leftProperty.position(), 49);
- QCOMPARE(leftProperty.length(), 4);
- QVERIFY(leftProperty.value().isBinding());
- QVERIFY(leftProperty.value().toBinding().binding() == "parent.left");
-
- QVERIFY(rightProperty.propertyName() == "anchors.right");
- QCOMPARE(rightProperty.propertyNameParts().count(), 2);
- QVERIFY(rightProperty.propertyNameParts().at(0) == "anchors");
- QVERIFY(rightProperty.propertyNameParts().at(1) == "right");
- QCOMPARE(rightProperty.position(), 75);
- QCOMPARE(rightProperty.length(), 5);
- QVERIFY(rightProperty.value().isBinding());
- QVERIFY(rightProperty.value().toBinding().binding() == "parent.right");
- }
-
-}
-
-void tst_qdeclarativedom::loadChildObject()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Item { Item {} }";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QVERIFY(rootItem.properties().size() == 1);
-
- QDeclarativeDomProperty listProperty = rootItem.properties().at(0);
- QVERIFY(listProperty.isDefaultProperty());
- QVERIFY(listProperty.value().isList());
-
- QDeclarativeDomList list = listProperty.value().toList();
- QVERIFY(list.values().size() == 1);
-
- QDeclarativeDomObject childItem = list.values().first().toObject();
- QVERIFY(childItem.isValid());
- QVERIFY(childItem.objectType() == "QtQuick/Item");
-}
-
-void tst_qdeclarativedom::loadComposite()
-{
- QFile file(SRCDIR "/data/top.qml");
- QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, file.readAll(), QUrl::fromLocalFile(file.fileName())));
- QVERIFY(document.errors().isEmpty());
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QCOMPARE(rootItem.objectType(), QByteArray("MyComponent"));
- QCOMPARE(rootItem.properties().size(), 2);
-
- QDeclarativeDomProperty widthProperty = rootItem.property("width");
- QVERIFY(widthProperty.value().isLiteral());
-
- QDeclarativeDomProperty heightProperty = rootItem.property("height");
- QVERIFY(heightProperty.value().isLiteral());
-}
-
-void tst_qdeclarativedom::testValueSource()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Rectangle { SpringAnimation on height { spring: 1.4; damping: .15; to: Math.min(Math.max(-130, value*2.2 - 130), 133); }}";
-
- QDeclarativeEngine freshEngine;
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&freshEngine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QDeclarativeDomProperty heightProperty = rootItem.properties().at(0);
- QVERIFY(heightProperty.propertyName() == "height");
- QVERIFY(heightProperty.value().isValueSource());
-
- const QDeclarativeDomValueValueSource valueSource = heightProperty.value().toValueSource();
- QDeclarativeDomObject valueSourceObject = valueSource.object();
- QVERIFY(valueSourceObject.isValid());
-
- QVERIFY(valueSourceObject.objectType() == "QtQuick/SpringAnimation");
-
- const QDeclarativeDomValue springValue = valueSourceObject.property("spring").value();
- QVERIFY(!springValue.isInvalid());
- QVERIFY(springValue.isLiteral());
- QVERIFY(springValue.toLiteral().literal() == "1.4");
-
- const QDeclarativeDomValue sourceValue = valueSourceObject.property("to").value();
- QVERIFY(!sourceValue.isInvalid());
- QVERIFY(sourceValue.isBinding());
- QVERIFY(sourceValue.toBinding().binding() == "Math.min(Math.max(-130, value*2.2 - 130), 133)");
-}
-
-void tst_qdeclarativedom::testValueInterceptor()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Rectangle { Behavior on height { NumberAnimation { duration: 100 } } }";
-
- QDeclarativeEngine freshEngine;
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&freshEngine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QDeclarativeDomProperty heightProperty = rootItem.properties().at(0);
- QVERIFY(heightProperty.propertyName() == "height");
- QVERIFY(heightProperty.value().isValueInterceptor());
-
- const QDeclarativeDomValueValueInterceptor valueInterceptor = heightProperty.value().toValueInterceptor();
- QDeclarativeDomObject valueInterceptorObject = valueInterceptor.object();
- QVERIFY(valueInterceptorObject.isValid());
-
- QVERIFY(valueInterceptorObject.objectType() == "QtQuick/Behavior");
-
- const QDeclarativeDomValue animationValue = valueInterceptorObject.property("animation").value();
- QVERIFY(!animationValue.isInvalid());
- QVERIFY(animationValue.isObject());
-}
-
-// Test QDeclarativeDomDocument::imports()
-void tst_qdeclarativedom::loadImports()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "import importlib.sublib 1.1\n"
- "import importlib.sublib 1.0 as NewFoo\n"
- "import 'import'\n"
- "import 'import' as X\n"
- "Item {}";
-
- QDeclarativeEngine engine;
- engine.addImportPath(SRCDIR "/data");
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml, QUrl::fromLocalFile(SRCDIR "/data/dummy.qml")));
-
- QCOMPARE(document.imports().size(), 5);
-
- QDeclarativeDomImport import = document.imports().at(0);
- QCOMPARE(import.type(), QDeclarativeDomImport::Library);
- QCOMPARE(import.uri(), QLatin1String("QtQuick"));
- QCOMPARE(import.qualifier(), QString());
- QCOMPARE(import.version(), QLatin1String("1.0"));
-
- import = document.imports().at(1);
- QCOMPARE(import.type(), QDeclarativeDomImport::Library);
- QCOMPARE(import.uri(), QLatin1String("importlib.sublib"));
- QCOMPARE(import.qualifier(), QString());
- QCOMPARE(import.version(), QLatin1String("1.1"));
-
- import = document.imports().at(2);
- QCOMPARE(import.type(), QDeclarativeDomImport::Library);
- QCOMPARE(import.uri(), QLatin1String("importlib.sublib"));
- QCOMPARE(import.qualifier(), QLatin1String("NewFoo"));
- QCOMPARE(import.version(), QLatin1String("1.0"));
-
- import = document.imports().at(3);
- QCOMPARE(import.type(), QDeclarativeDomImport::File);
- QCOMPARE(import.uri(), QLatin1String("import"));
- QCOMPARE(import.qualifier(), QLatin1String(""));
- QCOMPARE(import.version(), QLatin1String(""));
-
- import = document.imports().at(4);
- QCOMPARE(import.type(), QDeclarativeDomImport::File);
- QCOMPARE(import.uri(), QLatin1String("import"));
- QCOMPARE(import.qualifier(), QLatin1String("X"));
- QCOMPARE(import.version(), QLatin1String(""));
-}
-
-// Test loading a file with errors
-void tst_qdeclarativedom::loadErrors()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " foo: 12\n"
- "}";
-
- QDeclarativeDomDocument document;
- QVERIFY(false == document.load(&engine, qml));
-
- QCOMPARE(document.errors().count(), 1);
- QDeclarativeError error = document.errors().first();
-
- QCOMPARE(error.url(), QUrl());
- QCOMPARE(error.line(), 3);
- QCOMPARE(error.column(), 3);
- QCOMPARE(error.description(), QString("Cannot assign to non-existent property \"foo\""));
-}
-
-// Test loading a file with syntax errors
-void tst_qdeclarativedom::loadSyntaxErrors()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "asdf";
-
- QDeclarativeDomDocument document;
- QVERIFY(false == document.load(&engine, qml));
-
- QCOMPARE(document.errors().count(), 1);
- QDeclarativeError error = document.errors().first();
-
- QCOMPARE(error.url(), QUrl());
- QCOMPARE(error.line(), 2);
- QCOMPARE(error.column(), 1);
- QCOMPARE(error.description(), QString("Syntax error"));
-}
-
-// Test attempting to load a file with remote references
-void tst_qdeclarativedom::loadRemoteErrors()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "import \"http://localhost/exampleQmlScript.js\" as Script\n"
- "Item {\n"
- "}";
- QDeclarativeDomDocument document;
- QVERIFY(false == document.load(&engine, qml));
-
- QCOMPARE(document.errors().count(), 1);
- QDeclarativeError error = document.errors().first();
-
- QCOMPARE(error.url(), QUrl());
- QCOMPARE(error.line(), -1);
- QCOMPARE(error.column(), -1);
- QCOMPARE(error.description(), QString("QDeclarativeDomDocument supports local types only"));
-}
-
-// Test dynamic property declarations
-void tst_qdeclarativedom::loadDynamicProperty()
-{
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " property int a\n"
- " property bool b\n"
- " property double c\n"
- " property real d\n"
- " property string e\n"
- " property url f\n"
- " property color g\n"
- " property date h\n"
- " property variant i\n"
- " property QtObject j\n"
- "}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
-
- QCOMPARE(rootObject.dynamicProperties().count(), 10);
-
-#define DP_TEST(index, name, type, test_position, test_length, propTypeName) \
- { \
- QDeclarativeDomDynamicProperty d = rootObject.dynamicProperties().at(index); \
- QVERIFY(d.isValid()); \
- QVERIFY(d.propertyName() == # name ); \
- QVERIFY(d.propertyType() == type); \
- QVERIFY(d.propertyTypeName() == propTypeName); \
- QVERIFY(d.isDefaultProperty() == false); \
- QVERIFY(d.defaultValue().isValid() == false); \
- QCOMPARE(d.position(), test_position); \
- QCOMPARE(d.length(), test_length); \
- } \
-
- DP_TEST(0, a, QVariant::Int, 30, 14, "int");
- DP_TEST(1, b, QVariant::Bool, 49, 15, "bool");
- DP_TEST(2, c, QMetaType::QReal, 69, 17, "double");
- DP_TEST(3, d, QMetaType::QReal, 91, 15, "real");
- DP_TEST(4, e, QVariant::String, 111, 17, "string");
- DP_TEST(5, f, QVariant::Url, 133, 14, "url");
- DP_TEST(6, g, QVariant::Color, 152, 16, "color");
- DP_TEST(7, h, QVariant::DateTime, 173, 15, "date");
- DP_TEST(8, i, qMetaTypeId<QVariant>(), 193, 18, "variant");
- DP_TEST(9, j, -1, 216, 19, "QtObject");
- }
-
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " id: item\n"
- " property int a: 12\n"
- " property int b: a + 6\n"
- " default property QtObject c\n"
- " property alias d: item.a\n"
- "}\n";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
-
- QCOMPARE(rootObject.dynamicProperties().count(), 4);
-
- {
- QDeclarativeDomDynamicProperty d = rootObject.dynamicProperties().at(0);
- QVERIFY(d.isDefaultProperty() == false);
- QVERIFY(d.isAlias() == false);
- QVERIFY(d.defaultValue().isValid());
- QVERIFY(d.defaultValue().propertyName() == "a");
- QVERIFY(d.defaultValue().value().isLiteral());
- }
-
- {
- QDeclarativeDomDynamicProperty d = rootObject.dynamicProperties().at(1);
- QVERIFY(d.isDefaultProperty() == false);
- QVERIFY(d.isAlias() == false);
- QVERIFY(d.defaultValue().isValid());
- QVERIFY(d.defaultValue().propertyName() == "b");
- QVERIFY(d.defaultValue().value().isBinding());
- }
-
- {
- QDeclarativeDomDynamicProperty d = rootObject.dynamicProperties().at(2);
- QVERIFY(d.isDefaultProperty() == true);
- QVERIFY(d.isAlias() == false);
- QVERIFY(d.defaultValue().isValid() == false);
- }
-
- {
- QDeclarativeDomDynamicProperty d = rootObject.dynamicProperties().at(3);
- QVERIFY(d.isDefaultProperty() == false);
- QVERIFY(d.isAlias() == true);
- }
- }
-}
-
-// Test inline components
-void tst_qdeclarativedom::loadComponent()
-{
- // Explicit component
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " Component {\n"
- " id: myComponent\n"
- " Item {}\n"
- " }\n"
- "}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QVERIFY(rootItem.properties().size() == 1);
-
- QDeclarativeDomProperty listProperty = rootItem.properties().at(0);
- QVERIFY(listProperty.isDefaultProperty());
- QVERIFY(listProperty.value().isList());
-
- QDeclarativeDomList list = listProperty.value().toList();
- QVERIFY(list.values().size() == 1);
-
- QDeclarativeDomObject componentObject = list.values().first().toObject();
- QVERIFY(componentObject.isValid());
- QVERIFY(componentObject.objectClassName() == "Component");
- QVERIFY(componentObject.isComponent());
-
- QDeclarativeDomComponent component = componentObject.toComponent();
- QVERIFY(component.isValid());
- QVERIFY(component.objectType() == "QtQuick/Component");
- QVERIFY(component.objectTypeMajorVersion() == 1);
- QVERIFY(component.objectTypeMinorVersion() == 0);
- QVERIFY(component.objectClassName() == "Component");
- QVERIFY(component.objectId() == "myComponent");
- QVERIFY(component.properties().isEmpty());
- QVERIFY(component.dynamicProperties().isEmpty());
- QVERIFY(component.isCustomType() == false);
- QVERIFY(component.customTypeData() == "");
- QVERIFY(component.isComponent());
- QCOMPARE(component.position(), 30);
- QCOMPARE(component.length(), 57);
-
- QVERIFY(component.componentRoot().isValid());
- QVERIFY(component.componentRoot().objectClassName() == "Item");
- }
-
- // Implicit component
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "ListView {\n"
- " delegate: Item {}\n"
- "}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootItem = document.rootObject();
- QVERIFY(rootItem.isValid());
- QVERIFY(rootItem.properties().size() == 1);
-
- QDeclarativeDomProperty delegate = rootItem.property("delegate");
-
- QDeclarativeDomObject componentObject = delegate.value().toObject();
- QVERIFY(componentObject.isValid());
- QVERIFY(componentObject.objectClassName() == "Component");
- QVERIFY(componentObject.isComponent());
-
- QDeclarativeDomComponent component = componentObject.toComponent();
- QVERIFY(component.isValid());
- QVERIFY(component.objectType() == "QtQuick/Component");
- QVERIFY(component.objectClassName() == "Component");
- QVERIFY(component.objectId() == "");
- QVERIFY(component.properties().isEmpty());
- QVERIFY(component.dynamicProperties().isEmpty());
- QVERIFY(component.isCustomType() == false);
- QVERIFY(component.customTypeData() == "");
- QVERIFY(component.isComponent());
- QCOMPARE(component.position(), 44);
- QCOMPARE(component.length(), 7);
-
- QVERIFY(component.componentRoot().isValid());
- QVERIFY(component.componentRoot().objectClassName() == "Item");
- }
-}
-
-// Test QDeclarativeDomObject::dynamicProperty() method
-void tst_qdeclarativedom::object_dynamicProperty()
-{
- // Invalid object
- {
- QDeclarativeDomObject object;
- QVERIFY(object.dynamicProperty("").isValid() == false);
- QVERIFY(object.dynamicProperty("foo").isValid() == false);
- }
-
-
- // Valid object, no dynamic properties
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
-
- QVERIFY(rootObject.dynamicProperty("").isValid() == false);
- QVERIFY(rootObject.dynamicProperty("foo").isValid() == false);
- }
-
- // Valid object, dynamic properties
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " property int a\n"
- "}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
-
- QVERIFY(rootObject.dynamicProperty("").isValid() == false);
- QVERIFY(rootObject.dynamicProperty("foo").isValid() == false);
-
- QDeclarativeDomDynamicProperty p = rootObject.dynamicProperty("a");
- QVERIFY(p.isValid());
- QVERIFY(p.propertyName() == "a");
- QVERIFY(p.propertyType() == QVariant::Int);
- QVERIFY(p.propertyTypeName() == "int");
- QVERIFY(p.isDefaultProperty() == false);
- QCOMPARE(p.position(), 30);
- QCOMPARE(p.length(), 14);
- }
-
-}
-
-// Test QDeclarativeObject::property() method
-void tst_qdeclarativedom::object_property()
-{
- // Invalid object
- {
- QDeclarativeDomObject object;
- QVERIFY(object.property("").isValid() == false);
- QVERIFY(object.property("foo").isValid() == false);
- }
-
- // Valid object - no default
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " x: 10\n"
- " y: 12\n"
- "}\n";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
-
- QVERIFY(rootObject.property("").isValid() == false);
- QVERIFY(rootObject.property("foo").isValid() == false);
-
- QDeclarativeDomProperty x = rootObject.property("x");
- QVERIFY(x.isValid());
- QVERIFY(x.propertyName() == "x");
- QVERIFY(x.propertyNameParts().count() == 1);
- QVERIFY(x.propertyNameParts().at(0) == "x");
- QVERIFY(x.isDefaultProperty() == false);
- QVERIFY(x.value().isLiteral());
- QVERIFY(x.value().toLiteral().literal() == "10");
- QCOMPARE(x.position(), 30);
- QCOMPARE(x.length(), 1);
-
- QDeclarativeDomProperty y = rootObject.property("y");
- QVERIFY(y.isValid());
- QVERIFY(y.propertyName() == "y");
- QVERIFY(y.propertyNameParts().count() == 1);
- QVERIFY(y.propertyNameParts().at(0) == "y");
- QVERIFY(y.isDefaultProperty() == false);
- QVERIFY(y.value().isLiteral());
- QVERIFY(y.value().toLiteral().literal() == "12");
- QCOMPARE(y.position(), 40);
- QCOMPARE(y.length(), 1);
- }
-
- // Valid object - with default
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " x: 10\n"
- " y: 12\n"
- " Item {}\n"
- "}\n";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
-
- QVERIFY(rootObject.property("").isValid() == false);
- QVERIFY(rootObject.property("foo").isValid() == false);
-
- QDeclarativeDomProperty x = rootObject.property("x");
- QVERIFY(x.isValid());
- QVERIFY(x.propertyName() == "x");
- QVERIFY(x.propertyNameParts().count() == 1);
- QVERIFY(x.propertyNameParts().at(0) == "x");
- QVERIFY(x.isDefaultProperty() == false);
- QVERIFY(x.value().isLiteral());
- QVERIFY(x.value().toLiteral().literal() == "10");
- QCOMPARE(x.position(), 30);
- QCOMPARE(x.length(), 1);
-
- QDeclarativeDomProperty y = rootObject.property("y");
- QVERIFY(y.isValid());
- QVERIFY(y.propertyName() == "y");
- QVERIFY(y.propertyNameParts().count() == 1);
- QVERIFY(y.propertyNameParts().at(0) == "y");
- QVERIFY(y.isDefaultProperty() == false);
- QVERIFY(y.value().isLiteral());
- QVERIFY(y.value().toLiteral().literal() == "12");
- QCOMPARE(y.position(), 40);
- QCOMPARE(y.length(), 1);
-
- QDeclarativeDomProperty data = rootObject.property("data");
- QVERIFY(data.isValid());
- QVERIFY(data.propertyName() == "data");
- QVERIFY(data.propertyNameParts().count() == 1);
- QVERIFY(data.propertyNameParts().at(0) == "data");
- QVERIFY(data.isDefaultProperty() == true);
- QVERIFY(data.value().isList());
- QCOMPARE(data.position(), 50);
- QCOMPARE(data.length(), 0);
- }
-}
-
-// Tests the QDeclarativeDomObject::url() method
-void tst_qdeclarativedom::object_url()
-{
- // Invalid object
- {
- QDeclarativeDomObject object;
- QCOMPARE(object.url(), QUrl());
- }
-
- // Valid builtin object
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {}";
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
- QCOMPARE(rootObject.url(), QUrl());
- }
-
- // Valid composite object
- {
- QByteArray qml = "import QtQuick 1.0\n"
- "MyItem {}";
-
- QUrl myUrl = QUrl::fromLocalFile(SRCDIR "/data/main.qml");
- QUrl subUrl = QUrl::fromLocalFile(SRCDIR "/data/MyItem.qml");
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml, myUrl));
-
- QDeclarativeDomObject rootObject = document.rootObject();
- QVERIFY(rootObject.isValid());
- QCOMPARE(rootObject.url(), subUrl);
- }
-}
-
-// Test copy constructors and operators
-void tst_qdeclarativedom::copy()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "MyItem {\n"
- " id: myItem\n"
- " property int a: 10\n"
- " x: 10\n"
- " y: x + 10\n"
- " NumberAnimation on z {}\n"
- " Behavior on opacity {}\n"
- " Component {\n"
- " Item{}\n"
- " }\n"
- " children: [ Item{}, Item{} ]\n"
- "}\n";
-
- QUrl myUrl = QUrl::fromLocalFile(SRCDIR "/data/main.qml");
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml, myUrl));
-
- // QDeclarativeDomDocument
- {
- QDeclarativeDomDocument document2(document);
- QDeclarativeDomDocument document3;
- document3 = document;
-
- QCOMPARE(document.imports().count(), document2.imports().count());
- QCOMPARE(document.errors().count(), document2.errors().count());
- QCOMPARE(document.rootObject().objectClassName(), document2.rootObject().objectClassName());
-
- QCOMPARE(document.imports().count(), document3.imports().count());
- QCOMPARE(document.errors().count(), document3.errors().count());
- QCOMPARE(document.rootObject().objectClassName(), document3.rootObject().objectClassName());
- }
-
- // QDeclarativeDomImport
- {
- QCOMPARE(document.imports().count(), 1);
- QDeclarativeDomImport import = document.imports().at(0);
-
- QDeclarativeDomImport import2(import);
- QDeclarativeDomImport import3;
- import3 = import2;
-
- QCOMPARE(import.type(), import2.type());
- QCOMPARE(import.uri(), import2.uri());
- QCOMPARE(import.version(), import2.version());
- QCOMPARE(import.qualifier(), import2.qualifier());
-
- QCOMPARE(import.type(), import3.type());
- QCOMPARE(import.uri(), import3.uri());
- QCOMPARE(import.version(), import3.version());
- QCOMPARE(import.qualifier(), import3.qualifier());
- }
-
- // QDeclarativeDomObject
- {
- QDeclarativeDomObject object = document.rootObject();
- QVERIFY(object.isValid());
-
- QDeclarativeDomObject object2(object);
- QDeclarativeDomObject object3;
- object3 = object;
-
- QCOMPARE(object.isValid(), object2.isValid());
- QCOMPARE(object.objectType(), object2.objectType());
- QCOMPARE(object.objectClassName(), object2.objectClassName());
- QCOMPARE(object.objectTypeMajorVersion(), object2.objectTypeMajorVersion());
- QCOMPARE(object.objectTypeMinorVersion(), object2.objectTypeMinorVersion());
- QCOMPARE(object.objectId(), object2.objectId());
- QCOMPARE(object.properties().count(), object2.properties().count());
- QCOMPARE(object.dynamicProperties().count(), object2.dynamicProperties().count());
- QCOMPARE(object.isCustomType(), object2.isCustomType());
- QCOMPARE(object.customTypeData(), object2.customTypeData());
- QCOMPARE(object.isComponent(), object2.isComponent());
- QCOMPARE(object.position(), object2.position());
- QCOMPARE(object.length(), object2.length());
- QCOMPARE(object.url(), object2.url());
-
- QCOMPARE(object.isValid(), object3.isValid());
- QCOMPARE(object.objectType(), object3.objectType());
- QCOMPARE(object.objectClassName(), object3.objectClassName());
- QCOMPARE(object.objectTypeMajorVersion(), object3.objectTypeMajorVersion());
- QCOMPARE(object.objectTypeMinorVersion(), object3.objectTypeMinorVersion());
- QCOMPARE(object.objectId(), object3.objectId());
- QCOMPARE(object.properties().count(), object3.properties().count());
- QCOMPARE(object.dynamicProperties().count(), object3.dynamicProperties().count());
- QCOMPARE(object.isCustomType(), object3.isCustomType());
- QCOMPARE(object.customTypeData(), object3.customTypeData());
- QCOMPARE(object.isComponent(), object3.isComponent());
- QCOMPARE(object.position(), object3.position());
- QCOMPARE(object.length(), object3.length());
- QCOMPARE(object.url(), object3.url());
- }
-
- // QDeclarativeDomDynamicProperty
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomDynamicProperty property = object.dynamicProperty("a");
-
- QDeclarativeDomDynamicProperty property2(property);
- QDeclarativeDomDynamicProperty property3;
- property3 = property;
-
- QCOMPARE(property.isValid(), property2.isValid());
- QCOMPARE(property.propertyName(), property2.propertyName());
- QCOMPARE(property.propertyType(), property2.propertyType());
- QCOMPARE(property.propertyTypeName(), property2.propertyTypeName());
- QCOMPARE(property.isDefaultProperty(), property2.isDefaultProperty());
- QCOMPARE(property.defaultValue().propertyName(), property2.defaultValue().propertyName());
- QCOMPARE(property.position(), property2.position());
- QCOMPARE(property.length(), property2.length());
-
- QCOMPARE(property.isValid(), property3.isValid());
- QCOMPARE(property.propertyName(), property3.propertyName());
- QCOMPARE(property.propertyType(), property3.propertyType());
- QCOMPARE(property.propertyTypeName(), property3.propertyTypeName());
- QCOMPARE(property.isDefaultProperty(), property3.isDefaultProperty());
- QCOMPARE(property.defaultValue().propertyName(), property3.defaultValue().propertyName());
- QCOMPARE(property.position(), property3.position());
- QCOMPARE(property.length(), property3.length());
- }
-
- // QDeclarativeDomProperty
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("opacity");
-
- QDeclarativeDomProperty property2(property);
- QDeclarativeDomProperty property3;
- property3 = property;
-
- QCOMPARE(property.isValid(), property2.isValid());
- QCOMPARE(property.propertyName(), property2.propertyName());
- QCOMPARE(property.propertyNameParts(), property2.propertyNameParts());
- QCOMPARE(property.isDefaultProperty(), property2.isDefaultProperty());
- QCOMPARE(property.value().type(), property2.value().type());
- QCOMPARE(property.position(), property2.position());
- QCOMPARE(property.length(), property2.length());
-
- QCOMPARE(property.isValid(), property3.isValid());
- QCOMPARE(property.propertyName(), property3.propertyName());
- QCOMPARE(property.propertyNameParts(), property3.propertyNameParts());
- QCOMPARE(property.isDefaultProperty(), property3.isDefaultProperty());
- QCOMPARE(property.value().type(), property3.value().type());
- QCOMPARE(property.position(), property3.position());
- QCOMPARE(property.length(), property3.length());
- }
-
- // QDeclarativeDomValueLiteral
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("x");
- QDeclarativeDomValueLiteral literal = property.value().toLiteral();
- QCOMPARE(literal.literal(), QString("10"));
-
- QDeclarativeDomValueLiteral literal2(literal);
- QDeclarativeDomValueLiteral literal3;
- literal3 = literal2;
-
- QCOMPARE(literal2.literal(), QString("10"));
- QCOMPARE(literal3.literal(), QString("10"));
- }
-
-
- // QDeclarativeDomValueBinding
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("y");
- QDeclarativeDomValueBinding binding = property.value().toBinding();
- QCOMPARE(binding.binding(), QString("x + 10"));
-
- QDeclarativeDomValueBinding binding2(binding);
- QDeclarativeDomValueBinding binding3;
- binding3 = binding2;
-
- QCOMPARE(binding2.binding(), QString("x + 10"));
- QCOMPARE(binding3.binding(), QString("x + 10"));
- }
-
- // QDeclarativeDomValueValueSource
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("z");
- QDeclarativeDomValueValueSource source = property.value().toValueSource();
- QCOMPARE(source.object().objectClassName(), QByteArray("NumberAnimation"));
-
- QDeclarativeDomValueValueSource source2(source);
- QDeclarativeDomValueValueSource source3;
- source3 = source;
-
- QCOMPARE(source2.object().objectClassName(), QByteArray("NumberAnimation"));
- QCOMPARE(source3.object().objectClassName(), QByteArray("NumberAnimation"));
- }
-
- // QDeclarativeDomValueValueInterceptor
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("opacity");
- QDeclarativeDomValueValueInterceptor interceptor = property.value().toValueInterceptor();
- QCOMPARE(interceptor.object().objectClassName(), QByteArray("Behavior"));
-
- QDeclarativeDomValueValueInterceptor interceptor2(interceptor);
- QDeclarativeDomValueValueInterceptor interceptor3;
- interceptor3 = interceptor;
-
- QCOMPARE(interceptor2.object().objectClassName(), QByteArray("Behavior"));
- QCOMPARE(interceptor3.object().objectClassName(), QByteArray("Behavior"));
- }
-
- // QDeclarativeDomComponent
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("data");
- QCOMPARE(property.value().toList().values().count(), 1);
- QDeclarativeDomComponent component =
- property.value().toList().values().at(0).toObject().toComponent();
- QCOMPARE(component.componentRoot().objectClassName(), QByteArray("Item"));
-
- QDeclarativeDomComponent component2(component);
- QDeclarativeDomComponent component3;
- component3 = component;
-
- QCOMPARE(component.componentRoot().objectClassName(), component2.componentRoot().objectClassName());
- QCOMPARE(component.isValid(), component2.isValid());
- QCOMPARE(component.objectType(), component2.objectType());
- QCOMPARE(component.objectClassName(), component2.objectClassName());
- QCOMPARE(component.objectTypeMajorVersion(), component2.objectTypeMajorVersion());
- QCOMPARE(component.objectTypeMinorVersion(), component2.objectTypeMinorVersion());
- QCOMPARE(component.objectId(), component2.objectId());
- QCOMPARE(component.properties().count(), component2.properties().count());
- QCOMPARE(component.dynamicProperties().count(), component2.dynamicProperties().count());
- QCOMPARE(component.isCustomType(), component2.isCustomType());
- QCOMPARE(component.customTypeData(), component2.customTypeData());
- QCOMPARE(component.isComponent(), component2.isComponent());
- QCOMPARE(component.position(), component2.position());
- QCOMPARE(component.length(), component2.length());
- QCOMPARE(component.url(), component2.url());
-
- QCOMPARE(component.componentRoot().objectClassName(), component3.componentRoot().objectClassName());
- QCOMPARE(component.isValid(), component3.isValid());
- QCOMPARE(component.objectType(), component3.objectType());
- QCOMPARE(component.objectClassName(), component3.objectClassName());
- QCOMPARE(component.objectTypeMajorVersion(), component3.objectTypeMajorVersion());
- QCOMPARE(component.objectTypeMinorVersion(), component3.objectTypeMinorVersion());
- QCOMPARE(component.objectId(), component3.objectId());
- QCOMPARE(component.properties().count(), component3.properties().count());
- QCOMPARE(component.dynamicProperties().count(), component3.dynamicProperties().count());
- QCOMPARE(component.isCustomType(), component3.isCustomType());
- QCOMPARE(component.customTypeData(), component3.customTypeData());
- QCOMPARE(component.isComponent(), component3.isComponent());
- QCOMPARE(component.position(), component3.position());
- QCOMPARE(component.length(), component3.length());
- QCOMPARE(component.url(), component3.url());
- }
-
- // QDeclarativeDomValue
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("data");
- QDeclarativeDomValue value = property.value();
-
- QDeclarativeDomValue value2(value);
- QDeclarativeDomValue value3;
- value3 = value;
-
- QCOMPARE(value.type(), value2.type());
- QCOMPARE(value.isInvalid(), value2.isInvalid());
- QCOMPARE(value.isLiteral(), value2.isLiteral());
- QCOMPARE(value.isBinding(), value2.isBinding());
- QCOMPARE(value.isValueSource(), value2.isValueSource());
- QCOMPARE(value.isValueInterceptor(), value2.isValueInterceptor());
- QCOMPARE(value.isObject(), value2.isObject());
- QCOMPARE(value.isList(), value2.isList());
- QCOMPARE(value.position(), value2.position());
- QCOMPARE(value.length(), value2.length());
-
- QCOMPARE(value.type(), value3.type());
- QCOMPARE(value.isInvalid(), value3.isInvalid());
- QCOMPARE(value.isLiteral(), value3.isLiteral());
- QCOMPARE(value.isBinding(), value3.isBinding());
- QCOMPARE(value.isValueSource(), value3.isValueSource());
- QCOMPARE(value.isValueInterceptor(), value3.isValueInterceptor());
- QCOMPARE(value.isObject(), value3.isObject());
- QCOMPARE(value.isList(), value3.isList());
- QCOMPARE(value.position(), value3.position());
- QCOMPARE(value.length(), value3.length());
- }
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("x");
- QDeclarativeDomValue value = property.value();
-
- QDeclarativeDomValue value2(value);
- QDeclarativeDomValue value3;
- value3 = value;
-
- QCOMPARE(value.type(), value2.type());
- QCOMPARE(value.isInvalid(), value2.isInvalid());
- QCOMPARE(value.isLiteral(), value2.isLiteral());
- QCOMPARE(value.isBinding(), value2.isBinding());
- QCOMPARE(value.isValueSource(), value2.isValueSource());
- QCOMPARE(value.isValueInterceptor(), value2.isValueInterceptor());
- QCOMPARE(value.isObject(), value2.isObject());
- QCOMPARE(value.isList(), value2.isList());
- QCOMPARE(value.position(), value2.position());
- QCOMPARE(value.length(), value2.length());
-
- QCOMPARE(value.type(), value3.type());
- QCOMPARE(value.isInvalid(), value3.isInvalid());
- QCOMPARE(value.isLiteral(), value3.isLiteral());
- QCOMPARE(value.isBinding(), value3.isBinding());
- QCOMPARE(value.isValueSource(), value3.isValueSource());
- QCOMPARE(value.isValueInterceptor(), value3.isValueInterceptor());
- QCOMPARE(value.isObject(), value3.isObject());
- QCOMPARE(value.isList(), value3.isList());
- QCOMPARE(value.position(), value3.position());
- QCOMPARE(value.length(), value3.length());
- }
- {
- QDeclarativeDomValue value;
-
- QDeclarativeDomValue value2(value);
- QDeclarativeDomValue value3;
- value3 = value;
-
- QCOMPARE(value.type(), value2.type());
- QCOMPARE(value.isInvalid(), value2.isInvalid());
- QCOMPARE(value.isLiteral(), value2.isLiteral());
- QCOMPARE(value.isBinding(), value2.isBinding());
- QCOMPARE(value.isValueSource(), value2.isValueSource());
- QCOMPARE(value.isValueInterceptor(), value2.isValueInterceptor());
- QCOMPARE(value.isObject(), value2.isObject());
- QCOMPARE(value.isList(), value2.isList());
- QCOMPARE(value.position(), value2.position());
- QCOMPARE(value.length(), value2.length());
-
- QCOMPARE(value.type(), value3.type());
- QCOMPARE(value.isInvalid(), value3.isInvalid());
- QCOMPARE(value.isLiteral(), value3.isLiteral());
- QCOMPARE(value.isBinding(), value3.isBinding());
- QCOMPARE(value.isValueSource(), value3.isValueSource());
- QCOMPARE(value.isValueInterceptor(), value3.isValueInterceptor());
- QCOMPARE(value.isObject(), value3.isObject());
- QCOMPARE(value.isList(), value3.isList());
- QCOMPARE(value.position(), value3.position());
- QCOMPARE(value.length(), value3.length());
- }
-
- // QDeclarativeDomList
- {
- QDeclarativeDomObject object = document.rootObject();
- QDeclarativeDomProperty property = object.property("children");
- QDeclarativeDomList list = property.value().toList();
- QCOMPARE(list.values().count(), 2);
-
- QDeclarativeDomList list2(list);
- QDeclarativeDomList list3;
- list3 = list2;
-
- QCOMPARE(list.values().count(), list2.values().count());
- QCOMPARE(list.position(), list2.position());
- QCOMPARE(list.length(), list2.length());
- QCOMPARE(list.commaPositions(), list2.commaPositions());
-
- QCOMPARE(list.values().count(), list3.values().count());
- QCOMPARE(list.position(), list3.position());
- QCOMPARE(list.length(), list3.length());
- QCOMPARE(list.commaPositions(), list3.commaPositions());
-
- }
-}
-
-// Tests the position/length of various elements
-void tst_qdeclarativedom::position()
-{
- QByteArray qml = "import QtQuick 1.0\n"
- "Item {\n"
- " id: myItem\n"
- " property int a: 10\n"
- " x: 10\n"
- " y: x + 10\n"
- " NumberAnimation on z {}\n"
- " Behavior on opacity {}\n"
- " Component {\n"
- " Item{}\n"
- " }\n"
- " children: [ Item{}, Item{} ]\n"
- "}\n";
-
-
- QDeclarativeDomDocument document;
- QVERIFY(document.load(&engine, qml));
-
- QDeclarativeDomObject root = document.rootObject();
-
- // All QDeclarativeDomDynamicProperty
- QDeclarativeDomDynamicProperty dynProp = root.dynamicProperty("a");
- QCOMPARE(dynProp.position(), 45);
- QCOMPARE(dynProp.length(), 18);
-
- // All QDeclarativeDomProperty
- QDeclarativeDomProperty x = root.property("x");
- QCOMPARE(x.position(), 68);
- QCOMPARE(x.length(), 1);
-
- QDeclarativeDomProperty y = root.property("y");
- QCOMPARE(y.position(), 78);
- QCOMPARE(y.length(), 1);
-
- QDeclarativeDomProperty z = root.property("z");
- QCOMPARE(z.position(), 111);
- QCOMPARE(z.length(), 1);
-
- QDeclarativeDomProperty opacity = root.property("opacity");
- QCOMPARE(opacity.position(), 132);
- QCOMPARE(opacity.length(), 7);
-
- QDeclarativeDomProperty data = root.property("data");
- QCOMPARE(data.position(), 147);
- QCOMPARE(data.length(), 0);
-
- QDeclarativeDomProperty children = root.property("children");
- QCOMPARE(children.position(), 184);
- QCOMPARE(children.length(), 8);
-
- QDeclarativeDomList dataList = data.value().toList();
- QCOMPARE(dataList.values().count(), 1);
- QDeclarativeDomList childrenList = children.value().toList();
- QCOMPARE(childrenList.values().count(), 2);
-
- // All QDeclarativeDomObject
- QCOMPARE(root.position(), 19);
- QCOMPARE(root.length(), 195);
-
- QDeclarativeDomObject numberAnimation = z.value().toValueSource().object();
- QCOMPARE(numberAnimation.position(), 92);
- QCOMPARE(numberAnimation.length(), 23);
-
- QDeclarativeDomObject behavior = opacity.value().toValueInterceptor().object();
- QCOMPARE(behavior.position(), 120);
- QCOMPARE(behavior.length(), 22);
-
- QDeclarativeDomObject component = dataList.values().at(0).toObject();
- QCOMPARE(component.position(), 147);
- QCOMPARE(component.length(), 32);
-
- QDeclarativeDomObject componentRoot = component.toComponent().componentRoot();
- QCOMPARE(componentRoot.position(), 167);
- QCOMPARE(componentRoot.length(), 6);
-
- QDeclarativeDomObject child1 = childrenList.values().at(0).toObject();
- QCOMPARE(child1.position(), 196);
- QCOMPARE(child1.length(), 6);
-
- QDeclarativeDomObject child2 = childrenList.values().at(1).toObject();
- QCOMPARE(child2.position(), 204);
- QCOMPARE(child2.length(), 6);
-
- // All QDeclarativeDomValue
- QDeclarativeDomValue xValue = x.value();
- QCOMPARE(xValue.position(), 71);
- QCOMPARE(xValue.length(), 2);
-
- QDeclarativeDomValue yValue = y.value();
- QCOMPARE(yValue.position(), 81);
- QCOMPARE(yValue.length(), 6);
-
- QDeclarativeDomValue zValue = z.value();
- QCOMPARE(zValue.position(), 92);
- QCOMPARE(zValue.length(), 23);
-
- QDeclarativeDomValue opacityValue = opacity.value();
- QCOMPARE(opacityValue.position(), 120);
- QCOMPARE(opacityValue.length(), 22);
-
- QDeclarativeDomValue dataValue = data.value();
- QCOMPARE(dataValue.position(), 147);
- QCOMPARE(dataValue.length(), 32);
-
- QDeclarativeDomValue child1Value = childrenList.values().at(0);
- QCOMPARE(child1Value.position(), 196);
- QCOMPARE(child1Value.length(), 6);
-
- QDeclarativeDomValue child2Value = childrenList.values().at(1);
- QCOMPARE(child2Value.position(), 204);
- QCOMPARE(child2Value.length(), 6);
-
- // All QDeclarativeDomList
- QCOMPARE(childrenList.position(), 194);
- QCOMPARE(childrenList.length(), 18);
-}
-
-QTEST_MAIN(tst_qdeclarativedom)
-
-#include "tst_qdeclarativedom.moc"
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/Scope6Nested.qml b/tests/auto/declarative/qdeclarativeecmascript/data/Scope6Nested.qml
new file mode 100644
index 0000000000..a3794df22b
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/Scope6Nested.qml
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+
+MyQmlObject {
+ function runtest(obj) {
+ return obj.MyQmlObject.value == 19;
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleOne.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleOne.qml
new file mode 100644
index 0000000000..698b672259
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleOne.qml
@@ -0,0 +1,9 @@
+import QtQuick 1.0
+
+import "importPragmaLibrary.js" as TestPragmaLibraryImport
+
+Rectangle {
+ width: TestPragmaLibraryImport.importIncrementedValue()
+ height: width + 15
+ color: "red"
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleTwo.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleTwo.qml
new file mode 100644
index 0000000000..581ae671e3
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/SpecialRectangleTwo.qml
@@ -0,0 +1,9 @@
+import QtQuick 1.0
+
+import "importPragmaLibrary.js" as TestPragmaLibraryImport
+
+Rectangle {
+ width: TestPragmaLibraryImport.importIncrementedValue()
+ height: width + 5
+ color: "blue"
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFive.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFive.js
new file mode 100644
index 0000000000..e458094552
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFive.js
@@ -0,0 +1,3 @@
+function importFiveFunction() {
+ return '5';
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFour.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFour.js
new file mode 100644
index 0000000000..faddc15c9d
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importFour.js
@@ -0,0 +1,9 @@
+.pragma library
+
+function importFourFunction() {
+ return '4';
+}
+
+function greetingString() {
+ return 'Hello, World!';
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importOne.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importOne.js
new file mode 100644
index 0000000000..338c4e042f
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importOne.js
@@ -0,0 +1,13 @@
+.import "importTwo.js" as ImportTwoJs
+.import "importThree.js" as ImportThreeJs
+
+function greetingString() {
+ if (ImportTwoJs.greetingString().length > 0) {
+ return ImportTwoJs.greetingString();
+ }
+ return ImportThreeJs.greetingString();
+}
+
+function importOneFunction() {
+ return '1';
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importPragmaLibrary.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importPragmaLibrary.js
new file mode 100644
index 0000000000..c746fef14b
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importPragmaLibrary.js
@@ -0,0 +1,9 @@
+.pragma library
+
+var i = 4;
+
+// .pragma library, so should be callable from multiple .qml with shared i.
+function importIncrementedValue() {
+ i = i + 1;
+ return i;
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importThree.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importThree.js
new file mode 100644
index 0000000000..3917134ee2
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importThree.js
@@ -0,0 +1,9 @@
+.import "importFour.js" as ImportFourJs
+
+function greetingString() {
+ return ImportFourJs.greetingString();
+}
+
+function importThreeFunction() {
+ return '3';
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importTwo.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importTwo.js
new file mode 100644
index 0000000000..45b3c9a74d
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importTwo.js
@@ -0,0 +1,10 @@
+.import "importFour.js" as ImportFourJs
+.import "importFive.js" as ImportFiveJs
+
+function greetingString() {
+ return ImportFourJs.greetingString();
+}
+
+function importTwoFunction() {
+ return '2';
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importWithNoImports.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importWithNoImports.js
new file mode 100644
index 0000000000..83426c425c
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/importWithNoImports.js
@@ -0,0 +1,11 @@
+// This js file has no imports, and so should inherit
+// scope from the QML file which includes it.
+
+function componentError() {
+ var i = 5;
+ var errorIsOne = Component.error == 1;
+ if (errorIsOne == true) {
+ i = i + 7;
+ }
+ return i;
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImport.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImport.qml
new file mode 100644
index 0000000000..4a284ad886
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImport.qml
@@ -0,0 +1,14 @@
+import QtQuick 1.0
+
+import "testScriptImport.js" as TestScriptImport
+import "testModuleImport.js" as TestModuleImport
+
+QtObject {
+ id: testQtObject
+
+ property string importedScriptStringValue: TestScriptImport.greetingText
+ property int importedScriptFunctionValue: TestScriptImport.randomInteger(1, 20)
+
+ property int importedModuleAttachedPropertyValue: TestModuleImport.importedAttachedPropertyValue(testQtObject)
+ property int importedModuleEnumValue: TestModuleImport.importedEnumValue
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportPragmaLibrary.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportPragmaLibrary.qml
new file mode 100644
index 0000000000..7add311326
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportPragmaLibrary.qml
@@ -0,0 +1,20 @@
+import QtQuick 1.0
+
+// We use the components specified in SpecialRectangleOne.qml and SpecialRectangleTwo.qml
+
+QtObject {
+ id: testQtObject
+
+ property SpecialRectangleOne a;
+ property SpecialRectangleTwo b;
+
+ a: SpecialRectangleOne {
+ id: rectangleOne
+ }
+ b: SpecialRectangleTwo {
+ id: rectangleTwo
+ }
+
+ // this should be: (5 + 15) + (6 + 5) == 31
+ property int testValue: rectangleOne.height + rectangleTwo.height
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportScoping.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportScoping.qml
new file mode 100644
index 0000000000..0df841c78c
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testImportScoping.qml
@@ -0,0 +1,11 @@
+import QtQuick 1.0
+
+// For backward compatibility, importing a script which has no imports,
+// should run the script in the parent context. See QTBUG-17518.
+
+import "importWithNoImports.js" as TestNoImportScoping
+
+QtObject {
+ id: testQtObject
+ property int componentError: TestNoImportScoping.componentError()
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testModuleImport.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testModuleImport.js
new file mode 100644
index 0000000000..69bc1c9887
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testModuleImport.js
@@ -0,0 +1,8 @@
+.import Qt.test 1.0 as JsQtTest // test that we can import elements from .js files
+
+function importedAttachedPropertyValue(obj) {
+ return obj.JsQtTest.MyQmlObject.value; // attached property, value = 19.
+}
+
+var importedEnumValue = JsQtTest.MyQmlObject.EnumValue3 // the actual value of this enum value is "2"
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testScriptImport.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testScriptImport.js
new file mode 100644
index 0000000000..2ecccd8816
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimport/testScriptImport.js
@@ -0,0 +1,11 @@
+.import "importOne.js" as ImportOneJs // test that we can import scripts from .js files
+
+var greetingText = ImportOneJs.greetingString()
+
+function randomInteger(min, max) {
+ if (max > min) {
+ if (min > 10) return min;
+ return max;
+ }
+ return min;
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFive.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFive.qml
new file mode 100644
index 0000000000..9bf969cc61
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFive.qml
@@ -0,0 +1,11 @@
+import QtQuick 1.0
+
+// This should fail, since if the script does have imports
+// of its own, it should run in its own context.
+
+import "importWithImports.js" as TestImportScoping
+
+QtObject {
+ id: testQtObject
+ property int componentError: TestImportScoping.componentError()
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFour.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFour.qml
new file mode 100644
index 0000000000..fe7e88a829
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failFour.qml
@@ -0,0 +1,7 @@
+import QtQuick 1.0
+
+import "testModuleImport.js" as TestModuleImport
+
+QtObject {
+ property int importedModuleEnumValue: JsQtTest.MyQmlObject.EnumValue3 // should fail - the typenames available in TestModuleImport should not be available in this scope
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failOne.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failOne.qml
new file mode 100644
index 0000000000..e7fb7656f0
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failOne.qml
@@ -0,0 +1,7 @@
+import QtQuick 1.0
+
+import "testScriptImport.js" as TestScriptImport
+
+QtObject {
+ property string importScriptFunctionValue: TestScriptImport.ImportOneJs.greetingString() // should fail - the context of TestScriptImport is private to TestScriptImport.
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failThree.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failThree.qml
new file mode 100644
index 0000000000..fa720a64eb
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failThree.qml
@@ -0,0 +1,8 @@
+import QtQuick 1.0
+
+import "testModuleImport.js" as TestModuleImport
+
+QtObject {
+ id: testQtObject
+ property int importedModuleAttachedPropertyValue: testQtObject.TestModuleImport.JsQtTest.MyQmlObject.value // should fail - the context of TestScriptImport is private to TestScriptImport.
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failTwo.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failTwo.qml
new file mode 100644
index 0000000000..c2cbce9f80
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/failTwo.qml
@@ -0,0 +1,7 @@
+import QtQuick 1.0
+
+import "testScriptImport.js" as TestScriptImport
+
+QtObject {
+ property string importScriptFunctionValue: ImportOneJs.greetingString() // should fail - the typenames in TestScriptImport should not be visible from this scope
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importOne.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importOne.js
new file mode 100644
index 0000000000..45fd9c75dd
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importOne.js
@@ -0,0 +1,7 @@
+function greetingString() {
+ return 'Hello, World!';
+}
+
+function importOneFunction() {
+ return '1';
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importPragmaLibrary.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importPragmaLibrary.js
new file mode 100644
index 0000000000..ad0e6946a2
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importPragmaLibrary.js
@@ -0,0 +1,11 @@
+.pragma library
+
+// .pragma library, so shouldn't inherit imports from any .qml file.
+function importValue() {
+ var i = 3;
+ var errorIsOne = Component.error == 1; // this line should fail.
+ if (errorIsOne == true) {
+ i = i + 4;
+ }
+ return i;
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importWithImports.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importWithImports.js
new file mode 100644
index 0000000000..6d77ceccb1
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/importWithImports.js
@@ -0,0 +1,13 @@
+.import "importOne.js" as ImportOne
+
+// This js file has imports, so should not inherit
+// scope from the QML file which includes it.
+
+function componentError() {
+ var i = 3;
+ var errorIsOne = Component.error == 1; // this line should fail.
+ if (errorIsOne == true) {
+ i = i + 4;
+ }
+ return i;
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testImportPragmaLibrary.qml b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testImportPragmaLibrary.qml
new file mode 100644
index 0000000000..7e4a73ae42
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testImportPragmaLibrary.qml
@@ -0,0 +1,8 @@
+import QtQuick 1.0
+
+import "importPragmaLibrary.js" as ImportPragmaLibrary
+
+QtObject {
+ id: testQtObject
+ property int testValue: ImportPragmaLibrary.importValue()
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testModuleImport.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testModuleImport.js
new file mode 100644
index 0000000000..69bc1c9887
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testModuleImport.js
@@ -0,0 +1,8 @@
+.import Qt.test 1.0 as JsQtTest // test that we can import elements from .js files
+
+function importedAttachedPropertyValue(obj) {
+ return obj.JsQtTest.MyQmlObject.value; // attached property, value = 19.
+}
+
+var importedEnumValue = JsQtTest.MyQmlObject.EnumValue3 // the actual value of this enum value is "2"
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testScriptImport.js b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testScriptImport.js
new file mode 100644
index 0000000000..2ecccd8816
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/jsimportfail/testScriptImport.js
@@ -0,0 +1,11 @@
+.import "importOne.js" as ImportOneJs // test that we can import scripts from .js files
+
+var greetingText = ImportOneJs.greetingString()
+
+function randomInteger(min, max) {
+ if (max > min) {
+ if (min > 10) return min;
+ return max;
+ }
+ return min;
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/moduleApi.qml b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApi.qml
new file mode 100644
index 0000000000..62abcb7d83
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApi.qml
@@ -0,0 +1,18 @@
+import QtQuick 1.0
+
+import Qt.test 1.0 as QtTest // module API installed into existing uri
+import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri
+import Qt.test.qobjectApi 1.0 as QtTestQObjectApi // qobject module API installed into new uri
+import Qt.test.qobjectApi 1.3 as QtTestMinorVersionQObjectApi // qobject module API installed into existing uri with new minor version
+import Qt.test.qobjectApi 2.0 as QtTestMajorVersionQObjectApi // qobject module API installed into existing uri with new major version
+import Qt.test.qobjectApiParented 1.0 as QtTestParentedQObjectApi // qobject (with parent) module API installed into a new uri
+
+QtObject {
+ property int existingUriTest: QtTest.qobjectTestProperty
+ property int scriptTest: QtTestScriptApi.scriptTestProperty
+ property int qobjectTest: QtTestQObjectApi.qobjectTestProperty
+ property int qobjectMinorVersionTest: QtTestMinorVersionQObjectApi.qobjectTestProperty
+ property int qobjectMajorVersionTest: QtTestMajorVersionQObjectApi.qobjectTestProperty
+ property int qobjectParentedTest: QtTestParentedQObjectApi.qobjectTestProperty
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiCaching.qml b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiCaching.qml
new file mode 100644
index 0000000000..9cee8c3065
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiCaching.qml
@@ -0,0 +1,12 @@
+import QtQuick 1.0
+
+import Qt.test 1.0 as QtTest // module API installed into existing uri
+import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri
+import Qt.test.qobjectApiParented 1.0 as QtTestParentedQObjectApi // qobject (with parent) module API installed into a new uri
+
+QtObject {
+ property int existingUriTest: QtTest.qobjectTestProperty
+ property int scriptTest: QtTestScriptApi.scriptTestProperty
+ property int qobjectParentedTest: QtTestParentedQObjectApi.qobjectTestProperty
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMajorVersionFail.qml b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMajorVersionFail.qml
new file mode 100644
index 0000000000..eca29ab2cf
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMajorVersionFail.qml
@@ -0,0 +1,10 @@
+import QtQuick 1.0
+
+// this qml file attempts to import an invalid version of a qobject module API.
+
+import Qt.test.qobjectApi 4.0 as QtTestMajorVersionQObjectApi // qobject module API installed into existing uri with nonexistent major version
+
+QtObject {
+ property int qobjectMajorVersionTest: QtTestMajorVersionQObjectApi.qobjectTestProperty
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMinorVersionFail.qml b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMinorVersionFail.qml
new file mode 100644
index 0000000000..e360bd1668
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiMinorVersionFail.qml
@@ -0,0 +1,10 @@
+import QtQuick 1.0
+
+// this qml file attempts to import an invalid version of a qobject module API.
+
+import Qt.test.qobjectApi 1.2 as QtTestMinorVersionQObjectApi // qobject module API installed into existing uri with nonexistent minor version
+
+QtObject {
+ property int qobjectMinorVersionTest: QtTestMinorVersionedQObjectApi.qobjectTestProperty
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiWriting.qml b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiWriting.qml
new file mode 100644
index 0000000000..90a674681c
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/moduleApiWriting.qml
@@ -0,0 +1,27 @@
+import QtQuick 1.0
+
+import Qt.test 1.0 as QtTest // module API installed into existing uri
+
+QtObject {
+ property int firstProperty: 1
+ property int secondProperty: 2
+ property int readOnlyProperty: QtTest.qobjectTestProperty
+ property int writableProperty: QtTest.qobjectTestWritableProperty
+
+ onFirstPropertyChanged: {
+ // In this case, we want to attempt to set the module API property.
+ // This should fail, as the module API property is read only.
+ if (firstProperty != QtTest.qobjectTestProperty) {
+ QtTest.qobjectTestProperty = firstProperty; // should silently fail.
+ }
+ }
+
+ onSecondPropertyChanged: {
+ // In this case, we want to attempt to set the module API property.
+ // This should succeed, as the module API property is writable.
+ if (secondProperty != QtTest.qobjectTestWritableProperty) {
+ QtTest.qobjectTestWritableProperty = secondProperty; // should succeed.
+ }
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/realToInt.qml b/tests/auto/declarative/qdeclarativeecmascript/data/realToInt.qml
new file mode 100644
index 0000000000..cbbbbf921a
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/realToInt.qml
@@ -0,0 +1,11 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+MyQmlObject {
+ function test1() {
+ value = 4.2
+ }
+ function test2() {
+ value = 7.9
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/ScarceResourceSignalComponent.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/ScarceResourceSignalComponent.qml
new file mode 100644
index 0000000000..fb40bdc2de
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/ScarceResourceSignalComponent.qml
@@ -0,0 +1,9 @@
+import QtQuick 1.0
+
+QtObject {
+ property variant scarceResourceCopy
+ property int width: 5
+ signal testSignal
+ signal testSignal2
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopy.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopy.qml
new file mode 100644
index 0000000000..82184354d8
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopy.qml
@@ -0,0 +1,15 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+// Here we import a scarce resource directly.
+// The instance has a property which is a copy
+// of the scarce resource, so it should not be
+// detached (but we should automatically release
+// the resource from our engine internal list).
+
+QtObject {
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+ property variant scarceResourceCopy: scarceResourceProvider.scarceResource
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyFromJs.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyFromJs.qml
new file mode 100644
index 0000000000..60c26ac4f2
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyFromJs.qml
@@ -0,0 +1,15 @@
+import QtQuick 1.0
+import Qt.test 1.0
+import "scarceResourceTest.js" as ScarceResourceProviderJs
+
+// Here we import a scarce resource directly, from JS module.
+// It is not preserved or released manually, so it should be
+// automatically released once evaluation of the binding
+// is complete.
+
+QtObject {
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+ property variant scarceResourceCopy: ScarceResourceProviderJs.importScarceResource(scarceResourceProvider)
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.js b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.js
new file mode 100644
index 0000000000..bacc50dcc9
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.js
@@ -0,0 +1,24 @@
+.import Qt.test 1.0 as JsQtTest
+
+// In this case, the "retn" variable will be evaluated during import.
+// Since the "importScarceResource()" function depends on this variable,
+// we must explicitly preserve the "retn" variable or the scarce
+// resource would automatically be released after import completes
+// but before the binding is evaluated.
+
+var component = Qt.createComponent("scarceResourceCopy.qml");
+var scarceResourceElement = component.createObject(null);
+var scarceResourceProvider = scarceResourceElement.a;
+var retn = scarceResourceProvider.scarceResource;
+retn.preserve(); // must preserve manually or it will be released!
+
+function importScarceResource() {
+ // if called prior to calling destroyScarceResource(),
+ // this function should return the preserved scarce resource.
+ // otherwise, it should return an invalid variant.
+ return retn;
+}
+
+function destroyScarceResource() {
+ retn.destroy();
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.qml
new file mode 100644
index 0000000000..0513b0840e
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImport.qml
@@ -0,0 +1,18 @@
+import QtQuick 1.0
+import Qt.test 1.0
+import "scarceResourceCopyImport.js" as ScarceResourceCopyImportJs
+
+QtObject {
+ // this binding is evaluated once, prior to the resource being released
+ property variant scarceResourceCopy: ScarceResourceCopyImportJs.importScarceResource()
+
+ // this code is evaluated on completion, and so copy one should be valid, copy two invalid.
+ property variant scarceResourceAssignedCopyOne;
+ property variant scarceResourceAssignedCopyTwo;
+ Component.onCompleted: {
+ scarceResourceAssignedCopyOne = ScarceResourceCopyImportJs.importScarceResource();
+ ScarceResourceCopyImportJs.destroyScarceResource();
+ scarceResourceAssignedCopyTwo = ScarceResourceCopyImportJs.importScarceResource();
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.js b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.js
new file mode 100644
index 0000000000..6c495863b5
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.js
@@ -0,0 +1,18 @@
+.import Qt.test 1.0 as JsQtTest
+
+// In this case, the "retn" variable will be evaluated during import.
+// Since the importScarceResource() function depends on this variable,
+// because we DO NOT call "retn.preserve()", the scarce resource will
+// be released after the import completes but prior to evaluation of
+// any binding which calls "importScarceResource()".
+// Thus, "importScarceResource()" will return a released (invalid)
+// scarce resource.
+
+var component = Qt.createComponent("scarceResourceCopy.qml");
+var scarceResourceElement = component.createObject(null);
+var scarceResourceProvider = scarceResourceElement.a;
+var retn = scarceResourceProvider.scarceResource;
+
+function importScarceResource() {
+ return retn; // should return a released (invalid) scarce resource
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.qml
new file mode 100644
index 0000000000..d3c4d4ed65
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportFail.qml
@@ -0,0 +1,8 @@
+import QtQuick 1.0
+import Qt.test 1.0
+import "scarceResourceCopyImportFail.js" as ScarceResourceCopyImportFailJs
+
+QtObject {
+ property variant scarceResourceCopy: ScarceResourceCopyImportFailJs.importScarceResource()
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.js b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.js
new file mode 100644
index 0000000000..4a5b6b4427
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.js
@@ -0,0 +1,14 @@
+.import Qt.test 1.0 as JsQtTest
+
+// In this case, the "retn" variable will be evaluated during import.
+// Since the importScarceResource() function depends on this variable,
+// because we DO NOT call "retn.preserve()", the scarce resource will
+// be released after the import completes but prior to evaluation of
+// any binding which calls "importScarceResource()".
+// Thus, "importScarceResource()" will return a released (invalid)
+// scarce resource.
+
+var component = Qt.createComponent("scarceResourceCopyNoBinding.qml");
+var scarceResourceElement = component.createObject(null);
+var scarceResourceProvider = scarceResourceElement.a;
+var retn = scarceResourceProvider.scarceResource;
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.qml
new file mode 100644
index 0000000000..72cd4dac8a
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyImportNoBinding.qml
@@ -0,0 +1,12 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+// the following js import doesn't manually preserve or destroy any resources
+import "scarceResourceCopyImportNoBinding.js" as ScarceResourceCopyImportNoBindingJs
+
+QtObject {
+ // in this case, there is an import but no binding evaluated.
+ // nonetheless, any resources which are not preserved, should
+ // be automatically released by the engine.
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyNoBinding.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyNoBinding.qml
new file mode 100644
index 0000000000..681a382427
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceCopyNoBinding.qml
@@ -0,0 +1,14 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+QtObject {
+ // this component doesn't bind any property to a scarce
+ // resource from the scarce resource provider,
+ // so the binding evaluation resource cleanup
+ // codepath shouldn't be activated; so if the resources
+ // are released, it will be due to the import evaluation
+ // resource cleanup codepath being activated correctly.
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceDestroyedCopy.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceDestroyedCopy.qml
new file mode 100644
index 0000000000..87ceda9d7a
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceDestroyedCopy.qml
@@ -0,0 +1,14 @@
+import QtQuick 1.0
+import Qt.test 1.0
+import "scarceResourceTest.js" as ScarceResourceProviderJs
+
+// In this case, following the evaluation of the binding,
+// the scarceResourceTest value should be an invalid variant,
+// since the scarce resource will have been released.
+
+QtObject {
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+ property variant scarceResourceCopy: ScarceResourceProviderJs.importReleasedScarceResource(scarceResourceProvider);
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunction.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunction.qml
new file mode 100644
index 0000000000..e3e7aed9ee
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunction.qml
@@ -0,0 +1,23 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+// Here we import a scarce resource directly.
+// The copy is only assigned when retrieveScarceResource()
+// is called, and so should be detached prior to that.
+// The copy should be released when releaseScarceResource()
+// is called, and so should be detached after that.
+
+QtObject {
+ id: root
+ property MyScarceResourceObject a: MyScarceResourceObject { id: scarceResourceProvider }
+ property variant scarceResourceCopy;
+
+ function retrieveScarceResource() {
+ root.scarceResourceCopy = scarceResourceProvider.scarceResource;
+ }
+
+ function releaseScarceResource() {
+ root.scarceResourceCopy = null;
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunctionFail.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunctionFail.qml
new file mode 100644
index 0000000000..b1342fea90
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceFunctionFail.qml
@@ -0,0 +1,23 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+// In this example, a common syntax error will only be "caught"
+// when the function is called via:
+// QDeclarativeVMEMetaObject::metaCall->invokeMetaMethod()
+// We would like to ensure that a useful error message is printed,
+// rather than having QScriptValue::call() function fail silently.
+
+QtObject {
+ id: root
+ property MyScarceResourceObject a: MyScarceResourceObject { id: scarceResourceProvider }
+ property variant scarceResourceCopy;
+
+ function retrieveScarceResource() {
+ root.scarceResourceCopy = scarceResourceProvider.scarceResource(); // common syntax error, should throw exception
+ }
+
+ function releaseScarceResource() {
+ root.scarceResourceCopy = null;
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceSignal.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceSignal.qml
new file mode 100644
index 0000000000..9c920b1aa0
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceSignal.qml
@@ -0,0 +1,29 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+QtObject {
+ id: root
+
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+
+ property ScarceResourceSignalComponent b;
+ b: ScarceResourceSignalComponent {
+ objectName: "srsc"
+
+ onTestSignal: {
+ // this signal will be invoked manually in the test.
+ // the scarce resource should be released automatically after evaluation
+ // and since we don't keep a copy of it, the pixmap will be detached.
+ width = (scarceResourceProvider.scarceResource,10)
+ }
+
+ onTestSignal2: {
+ // this signal will be invoked manually in the test.
+ // the scarce resource should be released automatically after evaluation
+ // but since we assign it to a property, the pixmap won't be detached.
+ scarceResourceCopy = scarceResourceProvider.scarceResource
+ }
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.js b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.js
new file mode 100644
index 0000000000..c904eb3564
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.js
@@ -0,0 +1,48 @@
+.import Qt.test 1.0 as JsQtTest
+
+function importScarceResource(scarceResourceProvider) {
+ // the scarce resource should be automatically released
+ // after the binding is evaluated if preserve is not
+ // called.
+ return scarceResourceProvider.scarceResource;
+}
+
+function importPreservedScarceResource(scarceResourceProvider) {
+ // the scarce resource is manually preserved
+ // during the evaluation of the binding.
+ // it should not be released.
+ var scarceResource = scarceResourceProvider.scarceResource;
+ scarceResource.preserve();
+ return scarceResource;
+}
+
+function importReleasedScarceResource(scarceResourceProvider) {
+ // release the scarce resource during the
+ // evaluation of the binding. The returned
+ // variant will therefore be invalid.
+ var scarceResource = scarceResourceProvider.scarceResource;
+ scarceResource.destroy();
+ return scarceResource;
+}
+
+function importPreservedScarceResourceFromMultiple(scarceResourceProvider) {
+ // some scarce resources are manually preserved,
+ // some of them are manually destroyed,
+ // and some are automatically managed.
+ // We return a preserved resource
+ var sr1 = scarceResourceProvider.scarceResource; // preserved/destroyed.
+ sr1.preserve();
+ var sr2 = scarceResourceProvider.scarceResource; // preserved/destroyed
+ sr2.preserve();
+ var sr3 = scarceResourceProvider.scarceResource; // automatic.
+ var sr4 = scarceResourceProvider.scarceResource; // automatic and returned.
+ var sr5 = scarceResourceProvider.scarceResource; // destroyed
+ sr5.destroy();
+ sr2.destroy();
+ var sr6 = scarceResourceProvider.scarceResource; // destroyed
+ var sr7 = scarceResourceProvider.scarceResource; // automatic
+ sr1.destroy();
+ sr6.destroy();
+ return sr4;
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.qml
new file mode 100644
index 0000000000..3775172c04
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTest.qml
@@ -0,0 +1,14 @@
+import QtQuick 1.0
+import Qt.test 1.0
+
+// Here we import a scarce resource directly, and use it in a binding.
+// It is not preserved or released manually, so it should be
+// automatically released once evaluation of the binding
+// is complete.
+
+QtObject {
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+ property int scarceResourceTest: scarceResourceProvider.scarceResource,100 // return 100, but include the scarceResource in the binding to be evaluated.
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestMultiple.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestMultiple.qml
new file mode 100644
index 0000000000..3139382b05
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestMultiple.qml
@@ -0,0 +1,16 @@
+import QtQuick 1.0
+import Qt.test 1.0
+import "scarceResourceTest.js" as ScarceResourceProviderJs
+
+// In this case, multiple scarce resource are explicity preserved
+// and then explicitly destroyed, while others are automatically
+// managed. Since none are manually preserved without subsequently
+// being destroyed, after the evaluation of the binding the
+// scarce resource should be detached.
+
+QtObject {
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+ property int scarceResourceTest: ScarceResourceProviderJs.importPreservedScarceResourceFromMultiple(scarceResourceProvider), 100
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestPreserve.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestPreserve.qml
new file mode 100644
index 0000000000..d810377a51
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scarceresources/scarceResourceTestPreserve.qml
@@ -0,0 +1,15 @@
+import QtQuick 1.0
+import Qt.test 1.0
+import "scarceResourceTest.js" as ScarceResourceProviderJs
+
+// In this case, the scarce resource is explicity preserved.
+// It should not be automatically released after the evaluation
+// of the binding is complete, but instead will be kept in
+// memory until the JS garbage collector runs.
+
+QtObject {
+ property MyScarceResourceObject a;
+ a: MyScarceResourceObject { id: scarceResourceProvider }
+ property int scarceResourceTest: ScarceResourceProviderJs.importPreservedScarceResource(scarceResourceProvider),100 // return 100, but the resource should be preserved.
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scope.5.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scope.5.qml
new file mode 100644
index 0000000000..405746c459
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scope.5.qml
@@ -0,0 +1,27 @@
+import QtQuick 1.0
+
+Item {
+ property bool test1: false;
+ property bool test2: false;
+
+ property int a: 10
+
+ Item {
+ id: nested
+ property int a: 11
+
+ function mynestedfunction() {
+ return a;
+ }
+ }
+
+ function myouterfunction() {
+ return a;
+ }
+
+ Component.onCompleted: {
+ test1 = (myouterfunction() == 10);
+ test2 = (nested.mynestedfunction() == 11);
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/scope.6.qml b/tests/auto/declarative/qdeclarativeecmascript/data/scope.6.qml
new file mode 100644
index 0000000000..1c81e4e945
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/scope.6.qml
@@ -0,0 +1,10 @@
+import QtQuick 1.0
+
+Item {
+ id: me
+ property bool test: nested.runtest(me);
+
+ Scope6Nested {
+ id: nested
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
index 1d65b15476..7256d442e3 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
@@ -41,6 +41,8 @@
#include "testtypes.h"
#include <QWidget>
#include <QPlainTextEdit>
+#include <QDeclarativeEngine>
+#include <QScriptEngine>
class BaseExtensionObject : public QObject
{
@@ -99,6 +101,37 @@ public:
void setWidth(int) { }
};
+static QScriptValue script_api(QDeclarativeEngine *engine, QScriptEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+
+ static int testProperty = 13;
+ QScriptValue v = scriptEngine->newObject();
+ v.setProperty("scriptTestProperty", testProperty++);
+ return v;
+}
+
+static QObject *qobject_api(QDeclarativeEngine *engine, QScriptEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+
+ testQObjectApi *o = new testQObjectApi();
+ o->setQObjectTestProperty(20);
+ o->setQObjectTestWritableProperty(50);
+ return o;
+}
+
+static QObject *qobject_api_engine_parent(QDeclarativeEngine *engine, QScriptEngine *scriptEngine)
+{
+ Q_UNUSED(scriptEngine)
+
+ static int testProperty = 26;
+ testQObjectApi *o = new testQObjectApi(engine);
+ o->setQObjectTestProperty(testProperty++);
+ return o;
+}
void registerTypes()
{
@@ -116,6 +149,9 @@ void registerTypes()
qmlRegisterType<MyRevisionedClass>("Qt.test",1,0,"MyRevisionedClass");
qmlRegisterType<MyRevisionedClass,1>("Qt.test",1,1,"MyRevisionedClass");
+ // test scarce resource property binding post-evaluation optimisation
+ qmlRegisterType<ScarceResourceObject>("Qt.test", 1,0, "MyScarceResourceObject");
+
// Register the uncreatable base class
qmlRegisterRevision<MyRevisionedBaseClassRegistered,1>("Qt.test",1,1);
// MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
@@ -127,6 +163,15 @@ void registerTypes()
qmlRegisterType<QPlainTextEdit>("Qt.test",1,0,"QPlainTextEdit");
qRegisterMetaType<MyQmlObject::MyType>("MyQmlObject::MyType");
+
+ qmlRegisterModuleApi("Qt.test",1,0,script_api); // register (script) module API for an existing uri which contains elements
+ qmlRegisterModuleApi("Qt.test",1,0,qobject_api); // register (qobject) for an existing uri for which another module API was previously regd. Should replace!
+ qmlRegisterModuleApi("Qt.test.scriptApi",1,0,script_api); // register (script) module API for a uri which doesn't contain elements
+ qmlRegisterModuleApi("Qt.test.qobjectApi",1,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements
+ qmlRegisterModuleApi("Qt.test.qobjectApi",1,3,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, minor version set
+ qmlRegisterModuleApi("Qt.test.qobjectApi",2,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, major version set
+ qmlRegisterModuleApi("Qt.test.qobjectApiParented",1,0,qobject_api_engine_parent); // register (parented qobject) module API for a uri which doesn't contain elements
+
qRegisterMetaType<MyQmlObject::MyType>("MyEnum2");
qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons");
}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
index ad38d279bb..320e60c82a 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
@@ -51,6 +51,7 @@
#include <QtGui/qmatrix.h>
#include <QtGui/qcolor.h>
#include <QtGui/qvector3d.h>
+#include <QtGui/QPixmap>
#include <QtCore/qdatetime.h>
#include <QtScript/qscriptvalue.h>
#include <QtDeclarative/qdeclarativescriptstring.h>
@@ -909,6 +910,56 @@ QML_DECLARE_TYPE(MyRevisionedClass)
QML_DECLARE_TYPE(MyRevisionedSubclass)
Q_DECLARE_METATYPE(MyQmlObject::MyType)
+
+class ScarceResourceObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QPixmap scarceResource READ scarceResource WRITE setScarceResource NOTIFY scarceResourceChanged)
+public:
+ ScarceResourceObject(QObject *parent = 0) : QObject(parent), m_value(100, 100) { m_value.fill(Qt::blue); }
+ ~ScarceResourceObject() {}
+
+ QPixmap scarceResource() const { return m_value; }
+ void setScarceResource(QPixmap v) { m_value = v; emit scarceResourceChanged(); }
+
+ bool scarceResourceIsDetached() const { return m_value.isDetached(); }
+
+signals:
+ void scarceResourceChanged();
+
+private:
+ QPixmap m_value;
+};
+
+class testQObjectApi : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY (int qobjectTestProperty READ qobjectTestProperty NOTIFY qobjectTestPropertyChanged)
+ Q_PROPERTY (int qobjectTestWritableProperty READ qobjectTestWritableProperty WRITE setQObjectTestWritableProperty NOTIFY qobjectTestWritablePropertyChanged)
+
+public:
+ testQObjectApi(QObject* parent = 0)
+ : QObject(parent), m_testProperty(0)
+ {
+ }
+
+ ~testQObjectApi() {}
+
+ int qobjectTestProperty() const { return m_testProperty; }
+ void setQObjectTestProperty(int tp) { m_testProperty = tp; emit qobjectTestPropertyChanged(tp); }
+
+ int qobjectTestWritableProperty() const { return m_testWritableProperty; }
+ void setQObjectTestWritableProperty(int tp) { m_testWritableProperty = tp; emit qobjectTestWritablePropertyChanged(tp); }
+
+signals:
+ void qobjectTestPropertyChanged(int testProperty);
+ void qobjectTestWritablePropertyChanged(int testWritableProperty);
+
+private:
+ int m_testProperty;
+ int m_testWritableProperty;
+};
+
void registerTypes();
#endif // TESTTYPES_H
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index 1ec12fec01..efa95d36b6 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -144,6 +144,9 @@ private slots:
void numberAssignment();
void propertySplicing();
void signalWithUnknownTypes();
+ void moduleApi();
+ void importScripts();
+ void scarceResources();
void bug1();
void bug2();
@@ -176,6 +179,7 @@ private slots:
void aliasBindingsOverrideTarget();
void aliasWritesOverrideBindings();
void pushCleanContext();
+ void realToInt();
void include();
@@ -260,6 +264,7 @@ void tst_qdeclarativeecmascript::idShortcutInvalidates()
QVERIFY(object->objectProperty() != 0);
delete object->objectProperty();
QVERIFY(object->objectProperty() == 0);
+ delete object;
}
{
@@ -269,6 +274,7 @@ void tst_qdeclarativeecmascript::idShortcutInvalidates()
QVERIFY(object->objectProperty() != 0);
delete object->objectProperty();
QVERIFY(object->objectProperty() == 0);
+ delete object;
}
}
@@ -279,12 +285,14 @@ void tst_qdeclarativeecmascript::boolPropertiesEvaluateAsBool()
MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
QVERIFY(object != 0);
QCOMPARE(object->stringProperty(), QLatin1String("pass"));
+ delete object;
}
{
QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.2.qml"));
MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
QVERIFY(object != 0);
QCOMPARE(object->stringProperty(), QLatin1String("pass"));
+ delete object;
}
}
@@ -297,6 +305,7 @@ void tst_qdeclarativeecmascript::signalAssignment()
QCOMPARE(object->string(), QString());
emit object->basicSignal();
QCOMPARE(object->string(), QString("pass"));
+ delete object;
}
{
@@ -306,6 +315,7 @@ void tst_qdeclarativeecmascript::signalAssignment()
QCOMPARE(object->string(), QString());
emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
+ delete object;
}
}
@@ -320,6 +330,7 @@ void tst_qdeclarativeecmascript::methods()
emit object->basicSignal();
QCOMPARE(object->methodCalled(), true);
QCOMPARE(object->methodIntCalled(), false);
+ delete object;
}
{
@@ -331,6 +342,7 @@ void tst_qdeclarativeecmascript::methods()
emit object->basicSignal();
QCOMPARE(object->methodCalled(), false);
QCOMPARE(object->methodIntCalled(), true);
+ delete object;
}
{
@@ -338,6 +350,7 @@ void tst_qdeclarativeecmascript::methods()
QObject *object = component.create();
QVERIFY(object != 0);
QCOMPARE(object->property("test").toInt(), 19);
+ delete object;
}
{
@@ -347,6 +360,7 @@ void tst_qdeclarativeecmascript::methods()
QCOMPARE(object->property("test").toInt(), 19);
QCOMPARE(object->property("test2").toInt(), 17);
QCOMPARE(object->property("test3").toInt(), 16);
+ delete object;
}
{
@@ -354,6 +368,7 @@ void tst_qdeclarativeecmascript::methods()
QObject *object = component.create();
QVERIFY(object != 0);
QCOMPARE(object->property("test").toInt(), 9);
+ delete object;
}
}
@@ -364,6 +379,7 @@ void tst_qdeclarativeecmascript::bindingLoop()
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
QObject *object = component.create();
QVERIFY(object != 0);
+ delete object;
}
void tst_qdeclarativeecmascript::basicExpressions_data()
@@ -508,6 +524,7 @@ void tst_qdeclarativeecmascript::contextPropertiesTriggerReeval()
QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
}
+ delete object3;
}
void tst_qdeclarativeecmascript::objectPropertiesTriggerReeval()
@@ -582,6 +599,8 @@ void tst_qdeclarativeecmascript::deferredProperties()
QCOMPARE(qmlObject->value(), 10);
object->setValue(19);
QCOMPARE(qmlObject->value(), 19);
+
+ delete object;
}
// Check errors on deferred properties are correctly emitted
@@ -625,6 +644,7 @@ void tst_qdeclarativeecmascript::extensionObjects()
QCOMPARE(nested->coreProperty(), 11);
QCOMPARE(nested->baseProperty(), 92);
+ delete object;
}
void tst_qdeclarativeecmascript::overrideExtensionProperties()
@@ -635,6 +655,8 @@ void tst_qdeclarativeecmascript::overrideExtensionProperties()
QVERIFY(object != 0);
QVERIFY(object->secondProperty() != 0);
QVERIFY(object->firstProperty() == 0);
+
+ delete object;
}
void tst_qdeclarativeecmascript::attachedProperties()
@@ -647,6 +669,7 @@ void tst_qdeclarativeecmascript::attachedProperties()
QCOMPARE(object->property("b").toInt(), 19);
QCOMPARE(object->property("c").toInt(), 19);
QCOMPARE(object->property("d").toInt(), 19);
+ delete object;
}
{
@@ -671,6 +694,7 @@ void tst_qdeclarativeecmascript::attachedProperties()
QVERIFY(attached != 0);
QCOMPARE(attached->value2(), 9);
+ delete object;
}
}
@@ -692,6 +716,8 @@ void tst_qdeclarativeecmascript::enums()
QCOMPARE(object->property("h").toInt(), 3);
QCOMPARE(object->property("i").toInt(), 19);
QCOMPARE(object->property("j").toInt(), 19);
+
+ delete object;
}
// Non-existent enums
{
@@ -706,6 +732,8 @@ void tst_qdeclarativeecmascript::enums()
QVERIFY(object != 0);
QCOMPARE(object->property("a").toInt(), 0);
QCOMPARE(object->property("b").toInt(), 0);
+
+ delete object;
}
}
@@ -716,6 +744,8 @@ void tst_qdeclarativeecmascript::valueTypeFunctions()
QVERIFY(obj != 0);
QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
+
+ delete obj;
}
/*
@@ -739,6 +769,8 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings()
QCOMPARE(object->property("c2").toInt(), 13);
object->setProperty("c1", QVariant(8));
QCOMPARE(object->property("c2").toInt(), 13);
+
+ delete object;
}
// During construction
@@ -752,6 +784,8 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings()
object->setProperty("c1", QVariant(9));
QCOMPARE(object->property("c1").toInt(), 9);
QCOMPARE(object->property("c2").toInt(), 10);
+
+ delete object;
}
#if 0
@@ -770,6 +804,8 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings()
object->setProperty("c1", QVariant(7));
QCOMPARE(object->property("c1").toInt(), 7);
QCOMPARE(object->property("c2").toInt(), 13);
+
+ delete object;
}
#endif
@@ -784,6 +820,8 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings()
object->setProperty("c1", QVariant(9));
QCOMPARE(object->property("c1").toInt(), 9);
QCOMPARE(object->property("c3").toInt(), 10);
+
+ delete object;
}
}
@@ -811,6 +849,8 @@ void tst_qdeclarativeecmascript::outerBindingOverridesInnerBinding()
QCOMPARE(object->property("c1").toInt(), 9);
QCOMPARE(object->property("c2").toInt(), 8);
QCOMPARE(object->property("c3").toInt(), 8);
+
+ delete object;
}
/*
@@ -827,6 +867,8 @@ void tst_qdeclarativeecmascript::nonExistentAttachedObject()
QObject *object = component.create();
QVERIFY(object != 0);
+
+ delete object;
}
void tst_qdeclarativeecmascript::scope()
@@ -846,6 +888,8 @@ void tst_qdeclarativeecmascript::scope()
QCOMPARE(object->property("test8").toInt(), 2);
QCOMPARE(object->property("test9").toInt(), 1);
QCOMPARE(object->property("test10").toInt(), 3);
+
+ delete object;
}
{
@@ -859,6 +903,8 @@ void tst_qdeclarativeecmascript::scope()
QCOMPARE(object->property("test4").toInt(), 14);
QCOMPARE(object->property("test5").toInt(), 24);
QCOMPARE(object->property("test6").toInt(), 24);
+
+ delete object;
}
{
@@ -869,6 +915,8 @@ void tst_qdeclarativeecmascript::scope()
QCOMPARE(object->property("test1").toBool(), true);
QCOMPARE(object->property("test2").toBool(), true);
QCOMPARE(object->property("test3").toBool(), true);
+
+ delete object;
}
// Signal argument scope
@@ -887,6 +935,27 @@ void tst_qdeclarativeecmascript::scope()
delete object;
}
+
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("scope.5.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+
+ QCOMPARE(object->property("test1").toBool(), true);
+ QCOMPARE(object->property("test2").toBool(), true);
+
+ delete object;
+ }
+
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("scope.6.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+
+ QCOMPARE(object->property("test").toBool(), true);
+
+ delete object;
+ }
}
/*
@@ -907,6 +976,8 @@ void tst_qdeclarativeecmascript::signalParameterTypes()
QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
+
+ delete object;
}
/*
@@ -923,6 +994,8 @@ void tst_qdeclarativeecmascript::objectsCompareAsEqual()
QCOMPARE(object->property("test3").toBool(), true);
QCOMPARE(object->property("test4").toBool(), true);
QCOMPARE(object->property("test5").toBool(), true);
+
+ delete object;
}
/*
@@ -943,6 +1016,8 @@ void tst_qdeclarativeecmascript::aliasPropertyAndBinding()
QCOMPARE(object->property("c2").toInt(), 19);
QCOMPARE(object->property("c3").toInt(), 19);
+
+ delete object;
}
void tst_qdeclarativeecmascript::dynamicCreation_data()
@@ -972,6 +1047,8 @@ void tst_qdeclarativeecmascript::dynamicCreation()
QObject *created = object->objectProperty();
QVERIFY(created);
QCOMPARE(created->objectName(), createdName);
+
+ delete object;
}
/*
@@ -1020,6 +1097,8 @@ void tst_qdeclarativeecmascript::objectToString()
QMetaObject::invokeMethod(object, "testToString");
QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
+
+ delete object;
}
/*
@@ -1034,6 +1113,7 @@ void tst_qdeclarativeecmascript::selfDeletingBinding()
QObject *object = component.create();
QVERIFY(object != 0);
object->setProperty("triggerDelete", true);
+ delete object;
}
{
@@ -1041,6 +1121,7 @@ void tst_qdeclarativeecmascript::selfDeletingBinding()
QObject *object = component.create();
QVERIFY(object != 0);
object->setProperty("triggerDelete", true);
+ delete object;
}
}
@@ -1056,6 +1137,7 @@ void tst_qdeclarativeecmascript::extendedObjectPropertyLookup()
QDeclarativeComponent component(&engine, TEST_FILE("extendedObjectPropertyLookup.qml"));
QObject *object = component.create();
QVERIFY(object != 0);
+ delete object;
}
/*
@@ -1091,6 +1173,8 @@ void tst_qdeclarativeecmascript::scriptErrors()
QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
emit object->thirdBasicSignal();
+
+ delete object;
}
/*
@@ -1108,6 +1192,16 @@ void tst_qdeclarativeecmascript::functionErrors()
QObject *object = component.create();
QVERIFY(object != 0);
delete object;
+
+ // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
+ QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
+ url = componentTwo.url().toString();
+ object = componentTwo.create();
+ QVERIFY(object != 0);
+ warning = url + QLatin1String(":16: TypeError: Result of expression 'scarceResourceProvider.scarceResource' [[object Object]] is not a function.");
+ QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
+ QMetaObject::invokeMethod(object, "retrieveScarceResource");
+ delete object;
}
/*
@@ -1156,6 +1250,8 @@ void tst_qdeclarativeecmascript::signalTriggeredBindings()
QCOMPARE(object->property("base").toReal(), 400.);
QCOMPARE(object->property("test1").toReal(), 400.);
QCOMPARE(object->property("test2").toReal(), 400.);
+
+ delete object;
}
/*
@@ -1171,6 +1267,8 @@ void tst_qdeclarativeecmascript::listProperties()
QCOMPARE(object->property("test2").toInt(), 2);
QCOMPARE(object->property("test3").toBool(), true);
QCOMPARE(object->property("test4").toBool(), true);
+
+ delete object;
}
void tst_qdeclarativeecmascript::exceptionClearsOnReeval()
@@ -1192,6 +1290,8 @@ void tst_qdeclarativeecmascript::exceptionClearsOnReeval()
object->setObjectProperty(&object2);
QCOMPARE(object->property("test").toBool(), true);
+
+ delete object;
}
void tst_qdeclarativeecmascript::exceptionSlotProducesWarning()
@@ -1204,6 +1304,7 @@ void tst_qdeclarativeecmascript::exceptionSlotProducesWarning()
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
QVERIFY(object != 0);
+ delete object;
}
void tst_qdeclarativeecmascript::exceptionBindingProducesWarning()
@@ -1216,6 +1317,7 @@ void tst_qdeclarativeecmascript::exceptionBindingProducesWarning()
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
QVERIFY(object != 0);
+ delete object;
}
static int transientErrorsMsgCount = 0;
@@ -1239,6 +1341,8 @@ void tst_qdeclarativeecmascript::transientErrors()
qInstallMsgHandler(old);
QCOMPARE(transientErrorsMsgCount, 0);
+
+ delete object;
}
// One binding erroring multiple times, but then resolving
@@ -1254,6 +1358,8 @@ void tst_qdeclarativeecmascript::transientErrors()
qInstallMsgHandler(old);
QCOMPARE(transientErrorsMsgCount, 0);
+
+ delete object;
}
}
@@ -1369,6 +1475,8 @@ void tst_qdeclarativeecmascript::dynamicCreationCrash()
QMetaObject::invokeMethod(object, "dontCrash");
QObject *created = object->objectProperty();
QVERIFY(created == 0);
+
+ delete object;
}
//QTBUG-9367
@@ -1378,6 +1486,7 @@ void tst_qdeclarativeecmascript::regExpBug()
MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
QVERIFY(object != 0);
QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
+ delete object;
}
void tst_qdeclarativeecmascript::callQtInvokables()
@@ -2151,6 +2260,8 @@ void tst_qdeclarativeecmascript::ownership()
delete object;
}
+
+ delete context;
}
class CppOwnershipReturnValue : public QObject
@@ -2269,6 +2380,7 @@ void tst_qdeclarativeecmascript::qlistqobjectMethods()
QCOMPARE(object->property("test2").toBool(), true);
delete object;
+ delete context;
}
// QTBUG-9205
@@ -2389,6 +2501,285 @@ void tst_qdeclarativeecmascript::signalWithUnknownTypes()
delete object;
}
+void tst_qdeclarativeecmascript::moduleApi()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("moduleApi.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("existingUriTest").toInt(), 20);
+ QCOMPARE(object->property("scriptTest").toInt(), 13);
+ QCOMPARE(object->property("qobjectTest").toInt(), 20);
+ QCOMPARE(object->property("qobjectMinorVersionTest").toInt(), 20);
+ QCOMPARE(object->property("qobjectMajorVersionTest").toInt(), 20);
+ QCOMPARE(object->property("qobjectParentedTest").toInt(), 26);
+ delete object;
+
+ // test that caching of module apis works correctly.
+ QDeclarativeComponent componentTwo(&engine, TEST_FILE("moduleApiCaching.qml"));
+ object = componentTwo.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("existingUriTest").toInt(), 20);
+ QCOMPARE(object->property("scriptTest").toInt(), 13); // shouldn't have incremented.
+ QCOMPARE(object->property("qobjectParentedTest").toInt(), 26); // shouldn't have incremented.
+ delete object;
+
+ // test that writing to a property of module apis works correctly.
+ QDeclarativeComponent componentThree(&engine, TEST_FILE("moduleApiWriting.qml"));
+ QString expectedWarning = QLatin1String("file://") + TEST_FILE("moduleApiWriting.qml").toLocalFile() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ object = componentThree.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("readOnlyProperty").toInt(), 20);
+ QCOMPARE(object->property("writableProperty").toInt(), 50);
+ QVERIFY(object->setProperty("firstProperty", QVariant(30))); // shouldn't affect value of readOnlyProperty
+ QVERIFY(object->setProperty("writableProperty", QVariant(30))); // SHOULD affect value of writableProperty
+ QCOMPARE(object->property("readOnlyProperty").toInt(), 20);
+ QCOMPARE(object->property("writableProperty").toInt(), 30);
+ delete object;
+
+ QDeclarativeComponent failOne(&engine, TEST_FILE("moduleApiMajorVersionFail.qml"));
+ QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+ object = failOne.create();
+ QVERIFY(object == 0); // should have failed: invalid major version
+
+ QDeclarativeComponent failTwo(&engine, TEST_FILE("moduleApiMinorVersionFail.qml"));
+ QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+ object = failTwo.create();
+ QVERIFY(object == 0); // should have failed: invalid minor version
+}
+
+void tst_qdeclarativeecmascript::importScripts()
+{
+ QObject *object = 0;
+
+ // first, ensure that the required behaviour works.
+ QDeclarativeComponent component(&engine, TEST_FILE("jsimport/testImport.qml"));
+ object = component.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("importedScriptStringValue"), QVariant(QString(QLatin1String("Hello, World!"))));
+ QCOMPARE(object->property("importedScriptFunctionValue"), QVariant(20));
+ QCOMPARE(object->property("importedModuleAttachedPropertyValue"), QVariant(19));
+ QCOMPARE(object->property("importedModuleEnumValue"), QVariant(2));
+ delete object;
+
+ QDeclarativeComponent componentTwo(&engine, TEST_FILE("jsimport/testImportScoping.qml"));
+ object = componentTwo.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("componentError"), QVariant(5));
+ delete object;
+
+ // then, ensure that unintended behaviour does not work.
+ QDeclarativeComponent failOneComponent(&engine, TEST_FILE("jsimportfail/failOne.qml"));
+ QString expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Result of expression 'TestScriptImport.ImportOneJs' [undefined] is not an object.");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ object = failOneComponent.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("importScriptFunctionValue").toString().isEmpty());
+ delete object;
+ QDeclarativeComponent failTwoComponent(&engine, TEST_FILE("jsimportfail/failTwo.qml"));
+ expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ object = failTwoComponent.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("importScriptFunctionValue").toString().isEmpty());
+ delete object;
+ QDeclarativeComponent failThreeComponent(&engine, TEST_FILE("jsimportfail/failThree.qml"));
+ expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Result of expression 'testQtObject.TestModuleImport.JsQtTest' [undefined] is not an object.");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ object = failThreeComponent.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("importedModuleAttachedPropertyValue"), QVariant(false));
+ delete object;
+ QDeclarativeComponent failFourComponent(&engine, TEST_FILE("jsimportfail/failFour.qml"));
+ expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ object = failFourComponent.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("importedModuleEnumValue"), QVariant(0));
+ delete object;
+ QDeclarativeComponent failFiveComponent(&engine, TEST_FILE("jsimportfail/failFive.qml"));
+ expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
+ object = failFiveComponent.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("componentError"), QVariant(0));
+ delete object;
+
+ // also, test that importing scripts with .pragma library works as required
+ QDeclarativeComponent pragmaLibraryComponent(&engine, TEST_FILE("jsimport/testImportPragmaLibrary.qml"));
+ object = pragmaLibraryComponent.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("testValue"), QVariant(31));
+ delete object;
+
+ // and that .pragma library scripts don't inherit imports from any .qml file
+ QDeclarativeComponent pragmaLibraryComponentTwo(&engine, TEST_FILE("jsimportfail/testImportPragmaLibrary.qml"));
+ object = pragmaLibraryComponentTwo.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("testValue"), QVariant(0));
+ delete object;
+}
+
+void tst_qdeclarativeecmascript::scarceResources()
+{
+ QPixmap origPixmap(100, 100);
+ origPixmap.fill(Qt::blue);
+
+ QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
+ ScarceResourceObject *eo = 0;
+ QObject *object = 0;
+
+ // in the following three cases, the instance created from the component
+ // has a property which is a copy of the scarce resource; hence, the
+ // resource should NOT be detached prior to deletion of the object instance,
+ // unless the resource is destroyed explicitly.
+ QDeclarativeComponent component(&engine, TEST_FILE("scarceresources/scarceResourceCopy.qml"));
+ object = component.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("scarceResourceCopy").isValid());
+ QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
+ delete object;
+
+ QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceCopyFromJs.qml"));
+ object = componentTwo.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("scarceResourceCopy").isValid());
+ QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
+ delete object;
+
+ QDeclarativeComponent componentThree(&engine, TEST_FILE("scarceresources/scarceResourceDestroyedCopy.qml"));
+ object = componentThree.create();
+ QVERIFY(object != 0);
+ QVERIFY(!(object->property("scarceResourceCopy").isValid())); // was manually released prior to being returned.
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should have explicitly been released during the evaluation of the binding.
+ delete object;
+
+ // in the following three cases, no other copy should exist in memory,
+ // and so it should be detached (unless explicitly preserved).
+ QDeclarativeComponent componentFour(&engine, TEST_FILE("scarceresources/scarceResourceTest.qml"));
+ object = componentFour.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("scarceResourceTest").isValid());
+ QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // the resource should have been released after the binding was evaluated.
+ delete object;
+
+ QDeclarativeComponent componentFive(&engine, TEST_FILE("scarceresources/scarceResourceTestPreserve.qml"));
+ object = componentFive.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("scarceResourceTest").isValid());
+ QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(!eo->scarceResourceIsDetached()); // this won't be detached since we explicitly preserved it.
+ delete object;
+
+ QDeclarativeComponent componentSix(&engine, TEST_FILE("scarceresources/scarceResourceTestMultiple.qml"));
+ object = componentSix.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("scarceResourceTest").isValid());
+ QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // all resources were released manually or automatically released.
+ delete object;
+
+ // test that scarce resources are handled correctly for imports
+ QDeclarativeComponent componentSeven(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportNoBinding.qml"));
+ object = componentSeven.create();
+ QVERIFY(object != 0); // the import should have caused the addition of a resource to the ScarceResources list
+ QVERIFY(ep->scarceResources == 0); // but they should have been released by this point.
+ delete object;
+
+ QDeclarativeComponent componentEight(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportFail.qml"));
+ object = componentEight.create();
+ QVERIFY(object != 0);
+ QVERIFY(!object->property("scarceResourceCopy").isValid()); // wasn't preserved, so shouldn't be valid.
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ delete object;
+
+ QDeclarativeComponent componentNine(&engine, TEST_FILE("scarceresources/scarceResourceCopyImport.qml"));
+ object = componentNine.create();
+ QVERIFY(object != 0);
+ QVERIFY(object->property("scarceResourceCopy").isValid()); // preserved, so should be valid.
+ QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+ QVERIFY(object->property("scarceResourceAssignedCopyOne").isValid()); // assigned before destroy(), so should be valid.
+ QCOMPARE(object->property("scarceResourceAssignedCopyOne").value<QPixmap>(), origPixmap);
+ QVERIFY(!object->property("scarceResourceAssignedCopyTwo").isValid()); // assigned after destroy(), so should be invalid.
+ QVERIFY(ep->scarceResources == 0); // this will still be zero, because "preserve()" REMOVES it from this list.
+ delete object;
+
+ // test that scarce resources are handled properly in signal invocation
+ QDeclarativeComponent componentTen(&engine, TEST_FILE("scarceresources/scarceResourceSignal.qml"));
+ object = componentTen.create();
+ QVERIFY(object != 0);
+ QObject *srsc = object->findChild<QObject*>("srsc");
+ QVERIFY(srsc);
+ QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
+ QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+ QMetaObject::invokeMethod(srsc, "testSignal");
+ QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
+ QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
+ QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
+ QVERIFY(srsc->property("scarceResourceCopy").isValid());
+ QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ delete object;
+
+ // test that scarce resources are handled properly from js functions in qml files
+ QDeclarativeComponent componentEleven(&engine, TEST_FILE("scarceresources/scarceResourceFunction.qml"));
+ object = componentEleven.create();
+ QVERIFY(object != 0);
+ QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+ QMetaObject::invokeMethod(object, "retrieveScarceResource");
+ QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
+ QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
+ QMetaObject::invokeMethod(object, "releaseScarceResource");
+ QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ delete object;
+
+ // test that if an exception occurs while invoking js function from cpp, that the resources are released.
+ QDeclarativeComponent componentTwelve(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
+ object = componentTwelve.create();
+ QVERIFY(object != 0);
+ QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+ QString expectedWarning = QLatin1String("file://") + TEST_FILE("scarceresources/scarceResourceFunctionFail.qml").toLocalFile() + QLatin1String(":16: TypeError: Result of expression 'scarceResourceProvider.scarceResource' [[object Object]] is not a function.");
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); // we expect a meaningful warning to be printed.
+ QMetaObject::invokeMethod(object, "retrieveScarceResource");
+ QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
+ eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+ QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+ QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+ delete object;
+}
+
// Test that assigning a null object works
// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
void tst_qdeclarativeecmascript::nullObjectBinding()
@@ -2970,6 +3361,7 @@ void tst_qdeclarativeecmascript::revisionErrors()
QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
QVERIFY(object != 0);
+ delete object;
}
{
QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors2.qml"));
@@ -2991,6 +3383,7 @@ void tst_qdeclarativeecmascript::revisionErrors()
QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
QVERIFY(object != 0);
+ delete object;
}
{
QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors3.qml"));
@@ -3006,6 +3399,7 @@ void tst_qdeclarativeecmascript::revisionErrors()
QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
QVERIFY(object != 0);
+ delete object;
}
}
@@ -3017,6 +3411,7 @@ void tst_qdeclarativeecmascript::revision()
MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
QVERIFY(object != 0);
+ delete object;
}
{
QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision2.qml"));
@@ -3024,6 +3419,7 @@ void tst_qdeclarativeecmascript::revision()
MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
QVERIFY(object != 0);
+ delete object;
}
{
QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision3.qml"));
@@ -3031,6 +3427,7 @@ void tst_qdeclarativeecmascript::revision()
MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
QVERIFY(object != 0);
+ delete object;
}
// Test that non-root classes can resolve revisioned methods
{
@@ -3081,6 +3478,18 @@ void tst_qdeclarativeecmascript::pushCleanContext()
QCOMPARE(func2.call().toInt32(), 6);
}
+void tst_qdeclarativeecmascript::realToInt()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml"));
+ MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+ QVERIFY(object != 0);
+
+ QMetaObject::invokeMethod(object, "test1");
+ QCOMPARE(object->value(), int(4));
+ QMetaObject::invokeMethod(object, "test2");
+ QCOMPARE(object->value(), int(8));
+}
+
QTEST_MAIN(tst_qdeclarativeecmascript)
#include "tst_qdeclarativeecmascript.moc"
diff --git a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp
index c8e7817adc..dba12599f9 100644
--- a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp
+++ b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp
@@ -863,6 +863,8 @@ void tst_QDeclarativeGridView::noCurrentIndex()
QCOMPARE(gridview->currentIndex(), 5);
QVERIFY(gridview->currentItem());
QVERIFY(gridview->highlightItem());
+
+ delete canvas;
}
void tst_QDeclarativeGridView::changeFlow()
@@ -1621,6 +1623,8 @@ void tst_QDeclarativeGridView::resetModel()
QTRY_VERIFY(display != 0);
QTRY_COMPARE(display->text(), strings.at(i));
}
+
+ delete canvas;
}
void tst_QDeclarativeGridView::enforceRange()
@@ -1748,6 +1752,8 @@ void tst_QDeclarativeGridView::QTBUG_8456()
QTRY_VERIFY(gridview != 0);
QTRY_COMPARE(gridview->currentIndex(), 0);
+
+ delete canvas;
}
void tst_QDeclarativeGridView::manualHighlight()
@@ -1792,6 +1798,8 @@ void tst_QDeclarativeGridView::manualHighlight()
QTRY_COMPARE(gridview->currentItem(), findItem<QDeclarativeItem>(contentItem, "wrapper", 0));
QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
+
+ delete canvas;
}
void tst_QDeclarativeGridView::footer()
@@ -1839,6 +1847,8 @@ void tst_QDeclarativeGridView::footer()
QCOMPARE(footer->y(), 600.0);
QCOMPARE(footer->height(), 20.0);
QCOMPARE(gridview->contentY(), 0.0);
+
+ delete canvas;
}
void tst_QDeclarativeGridView::header()
@@ -1888,6 +1898,8 @@ void tst_QDeclarativeGridView::header()
QCOMPARE(header->y(), 10.0);
QCOMPARE(header->height(), 20.0);
QCOMPARE(gridview->contentY(), 10.0);
+
+ delete canvas;
}
void tst_QDeclarativeGridView::indexAt()
diff --git a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp
index 87e33474d3..26854ef9a4 100644
--- a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp
+++ b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp
@@ -224,6 +224,8 @@ void tst_qdeclarativeimage::clearSource()
QCOMPARE(obj->width(), 0.);
QCOMPARE(obj->height(), 0.);
QCOMPARE(obj->progress(), 0.0);
+
+ delete obj;
}
void tst_qdeclarativeimage::resized()
@@ -531,6 +533,8 @@ void tst_qdeclarativeimage::tiling_QTBUG_6716()
}
}
}
+
+ delete canvas;
}
void tst_qdeclarativeimage::noLoading()
@@ -579,6 +583,8 @@ void tst_qdeclarativeimage::noLoading()
QTRY_COMPARE(sourceSpy.count(), 4);
QTRY_COMPARE(progressSpy.count(), 2);
QTRY_COMPARE(statusSpy.count(), 2);
+
+ delete obj;
}
void tst_qdeclarativeimage::paintedWidthHeight()
@@ -648,6 +654,8 @@ void tst_qdeclarativeimage::sourceSize_QTBUG_14303()
QTRY_COMPARE(obj->sourceSize().width(), 200);
QTRY_COMPARE(obj->sourceSize().height(), 200);
QTRY_COMPARE(sourceSizeSpy.count(), 2);
+
+ delete obj;
}
void tst_qdeclarativeimage::sourceSize_QTBUG_16389()
diff --git a/tests/auto/declarative/qdeclarativeinfo/data/NestedComponent.qml b/tests/auto/declarative/qdeclarativeinfo/data/NestedComponent.qml
new file mode 100644
index 0000000000..d8ae8ae3ba
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeinfo/data/NestedComponent.qml
@@ -0,0 +1,23 @@
+import QtQuick 1.0
+
+QtObject {
+ property variant nested
+ property variant nested2: nested.nested
+
+ property variant component
+ component: Component {
+ id: myComponent
+ NestedObject { property string testProp: "test" }
+ }
+
+ property variant component2
+ component2: Component {
+ id: myComponent2
+ Image { property string testProp: "test" }
+ }
+
+ Component.onCompleted: {
+ nested = myComponent.createObject(0);
+ nested2 = myComponent2.createObject(0);
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeinfo/tst_qdeclarativeinfo.cpp b/tests/auto/declarative/qdeclarativeinfo/tst_qdeclarativeinfo.cpp
index 42459f10a4..e02ab2d8ca 100644
--- a/tests/auto/declarative/qdeclarativeinfo/tst_qdeclarativeinfo.cpp
+++ b/tests/auto/declarative/qdeclarativeinfo/tst_qdeclarativeinfo.cpp
@@ -60,6 +60,7 @@ public:
private slots:
void qmlObject();
void nestedQmlObject();
+ void nestedComponent();
void nonQmlObject();
void nullObject();
void nonQmlContextedObject();
@@ -82,7 +83,7 @@ void tst_qdeclarativeinfo::qmlObject()
QObject *object = component.create();
QVERIFY(object != 0);
- QString message = component.url().toString() + ":3:1: QML QObject_QML_0: Test Message";
+ QString message = component.url().toString() + ":3:1: QML QtObject: Test Message";
QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
qmlInfo(object) << "Test Message";
@@ -115,6 +116,27 @@ void tst_qdeclarativeinfo::nestedQmlObject()
qmlInfo(nested2) << "Inner Object";
}
+void tst_qdeclarativeinfo::nestedComponent()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("NestedComponent.qml"));
+
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+
+ QObject *nested = qvariant_cast<QObject *>(object->property("nested"));
+ QVERIFY(nested != 0);
+ QObject *nested2 = qvariant_cast<QObject *>(object->property("nested2"));
+ QVERIFY(nested2 != 0);
+
+ QString message = component.url().toString() + ":10:9: QML NestedObject: Complex Object";
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ qmlInfo(nested) << "Complex Object";
+
+ message = component.url().toString() + ":16:9: QML Image: Simple Object";
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ qmlInfo(nested2) << "Simple Object";
+}
+
void tst_qdeclarativeinfo::nonQmlObject()
{
QObject object;
diff --git a/tests/auto/declarative/qdeclarativeitem/data/keynavigationtest_implicit.qml b/tests/auto/declarative/qdeclarativeitem/data/keynavigationtest_implicit.qml
new file mode 100644
index 0000000000..52ffaea0ec
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeitem/data/keynavigationtest_implicit.qml
@@ -0,0 +1,68 @@
+import QtQuick 1.0
+
+Grid {
+ columns: 2
+ width: 100; height: 100
+ function verify() {
+ if (item1.KeyNavigation.tab != item2)
+ return false;
+ if (item1.KeyNavigation.backtab != item4)
+ return false;
+
+ if (item2.KeyNavigation.left != item1)
+ return false;
+ if (item2.KeyNavigation.down != item4)
+ return false;
+ if (item2.KeyNavigation.tab != item3)
+ return false;
+ if (item2.KeyNavigation.backtab != item1)
+ return false;
+
+ if (item3.KeyNavigation.right != item4)
+ return false;
+ if (item3.KeyNavigation.up != item1)
+ return false;
+ if (item3.KeyNavigation.tab != item4)
+ return false;
+ if (item3.KeyNavigation.backtab != item2)
+ return false;
+
+ return true;
+ }
+
+ Rectangle {
+ id: item1
+ objectName: "item1"
+ focus: true
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.tab: item2
+ KeyNavigation.backtab: item4
+ }
+ Rectangle {
+ id: item2
+ objectName: "item2"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.left: item1
+ KeyNavigation.down: item4
+ KeyNavigation.tab: item3
+ KeyNavigation.backtab: item1
+ }
+ Rectangle {
+ id: item3
+ objectName: "item3"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.right: item4
+ KeyNavigation.up: item1
+ KeyNavigation.tab: item4
+ KeyNavigation.backtab: item2
+ }
+ Rectangle {
+ id: item4
+ objectName: "item4"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp b/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp
index 52c9a726ff..7d0a35be23 100644
--- a/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp
+++ b/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp
@@ -67,6 +67,7 @@ private slots:
void keyNavigation();
void keyNavigation_RightToLeft();
void keyNavigation_skipNotVisible();
+ void keyNavigation_implicitSetting();
void layoutMirroring();
void layoutMirroringIllegalParent();
void smooth();
@@ -773,6 +774,131 @@ void tst_QDeclarativeItem::keyNavigation_skipNotVisible()
delete canvas;
}
+void tst_QDeclarativeItem::keyNavigation_implicitSetting()
+{
+ QDeclarativeView *canvas = new QDeclarativeView(0);
+ canvas->setFixedSize(240,320);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keynavigationtest_implicit.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QDeclarativeItem *item = findItem<QDeclarativeItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ QVariant result;
+ QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "verify",
+ Q_RETURN_ARG(QVariant, result)));
+ QVERIFY(result.toBool());
+
+ // right
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item1
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // down
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // move to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // left
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // up
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // tab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // backtab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QDeclarativeItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ delete canvas;
+}
+
void tst_QDeclarativeItem::smooth()
{
QDeclarativeComponent component(&engine);
@@ -1092,6 +1218,7 @@ void tst_QDeclarativeItem::childrenRect()
QCOMPARE(item->height(), qreal(0));
delete o;
+ delete canvas;
}
// QTBUG-11383
diff --git a/tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.errors.txt b/tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.errors.txt
new file mode 100644
index 0000000000..ef34d0ea95
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.errors.txt
@@ -0,0 +1 @@
+3:19:Invalid property assignment: unknown enumeration
diff --git a/tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.qml b/tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.qml
new file mode 100644
index 0000000000..f678fb3136
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativelanguage/data/wrongType.17.qml
@@ -0,0 +1,5 @@
+import Test 1.0
+MyTypeObject {
+ enumProperty: 6
+}
+
diff --git a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp
index 2ce493bd2a..42a02ed720 100644
--- a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp
+++ b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp
@@ -273,6 +273,7 @@ void tst_qdeclarativelanguage::errors_data()
QTest::newRow("wrongType (int for string)") << "wrongType.14.qml" << "wrongType.14.errors.txt" << false;
QTest::newRow("wrongType (int for url)") << "wrongType.15.qml" << "wrongType.15.errors.txt" << false;
QTest::newRow("wrongType (invalid object)") << "wrongType.16.qml" << "wrongType.16.errors.txt" << false;
+ QTest::newRow("wrongType (int for enum)") << "wrongType.17.qml" << "wrongType.17.errors.txt" << false;
QTest::newRow("readOnly.1") << "readOnly.1.qml" << "readOnly.1.errors.txt" << false;
QTest::newRow("readOnly.2") << "readOnly.2.qml" << "readOnly.2.errors.txt" << false;
diff --git a/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp b/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp
index 2f7513f09e..cf053e5fb0 100644
--- a/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp
+++ b/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp
@@ -754,6 +754,8 @@ void tst_qdeclarativelistmodel::get()
QCOMPARE(spyResult.at(0).toInt(), index);
QCOMPARE(spyResult.at(1).toInt(), 1); // only 1 item is modified at a time
QCOMPARE(spyResult.at(2).value<QList<int> >(), (QList<int>() << role));
+
+ delete model;
}
void tst_qdeclarativelistmodel::get_data()
@@ -913,6 +915,8 @@ void tst_qdeclarativelistmodel::get_nested()
QCOMPARE(spyResult.at(1).toInt(), 1); // only 1 item is modified at a time
QCOMPARE(spyResult.at(2).value<QList<int> >(), (QList<int>() << role));
}
+
+ delete model;
}
void tst_qdeclarativelistmodel::get_nested_data()
@@ -933,6 +937,8 @@ void tst_qdeclarativelistmodel::crash_model_with_multiple_roles()
// used to cause a crash in QDeclarativeVisualDataModel
model->setProperty(0, "black", true);
+
+ delete rootItem;
}
//QTBUG-15190
@@ -944,6 +950,8 @@ void tst_qdeclarativelistmodel::set_model_cache()
QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString()));
QVERIFY(model != 0);
QVERIFY(model->property("ok").toBool());
+
+ delete model;
}
void tst_qdeclarativelistmodel::property_changes()
diff --git a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp
index 0c96587b30..58d999e020 100644
--- a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp
+++ b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp
@@ -434,6 +434,7 @@ void tst_QDeclarativeListView::items()
QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
delete canvas;
+ delete testObject;
}
@@ -471,6 +472,7 @@ void tst_QDeclarativeListView::changed()
QTRY_COMPARE(number->text(), model.number(1));
delete canvas;
+ delete testObject;
}
template <class T>
@@ -555,6 +557,7 @@ void tst_QDeclarativeListView::inserted()
// QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
delete canvas;
+ delete testObject;
}
template <class T>
@@ -712,6 +715,7 @@ void tst_QDeclarativeListView::removed(bool animated)
QCOMPARE(name->text(), QString("New"));
delete canvas;
+ delete testObject;
}
template <class T>
@@ -752,6 +756,7 @@ void tst_QDeclarativeListView::clear()
QVERIFY(listview->currentIndex() == 0);
delete canvas;
+ delete testObject;
}
@@ -841,6 +846,7 @@ void tst_QDeclarativeListView::moved()
}
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::enforceRange()
@@ -951,6 +957,7 @@ void tst_QDeclarativeListView::spacing()
}
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::sections()
@@ -1288,6 +1295,8 @@ void tst_QDeclarativeListView::noCurrentIndex()
QCOMPARE(listview->currentIndex(), 2);
QVERIFY(listview->highlightItem());
QVERIFY(listview->currentItem());
+
+ delete canvas;
}
void tst_QDeclarativeListView::itemList()
@@ -1381,6 +1390,7 @@ void tst_QDeclarativeListView::cacheBuffer()
}
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::positionViewAtIndex()
@@ -1540,6 +1550,7 @@ void tst_QDeclarativeListView::positionViewAtIndex()
QTRY_COMPARE(listview->contentY(), 510.);
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::resetModel()
@@ -1844,6 +1855,7 @@ void tst_QDeclarativeListView::QTBUG_11105()
QCOMPARE(itemCount, 5);
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::header()
@@ -2116,6 +2128,7 @@ void tst_QDeclarativeListView::resizeView()
QCOMPARE(heightRatio.toReal(), 0.25);
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::sizeLessThan1()
@@ -2151,6 +2164,7 @@ void tst_QDeclarativeListView::sizeLessThan1()
}
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::QTBUG_14821()
@@ -2299,6 +2313,7 @@ void tst_QDeclarativeListView::indexAt()
QCOMPARE(listview->indexAt(240,20), -1);
delete canvas;
+ delete testObject;
}
void tst_QDeclarativeListView::incrementalModel()
diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/qmldir b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/qmldir
new file mode 100644
index 0000000000..7f5b3a362d
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/qmldir
@@ -0,0 +1,2 @@
+plugin AType
+
diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/temptest.qml b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/temptest.qml
new file mode 100644
index 0000000000..a45ac2dd8c
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit1/temptest.qml
@@ -0,0 +1,14 @@
+import QtQuick 1.0
+
+// this qml file uses a type which is meant to be defined
+// in a plugin which is specified in the qmldir file.
+// however, that plugin doesn't exist, so it cannot be
+// loaded, and hence the AItem type will be an unknown type.
+
+Item {
+ id: root
+
+ AItem {
+ id: unknown
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativedom/data/MyComponent.qml b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/Test.qml
index f6760b6a9f..83c65538ba 100644
--- a/tests/auto/declarative/qdeclarativedom/data/MyComponent.qml
+++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/Test.qml
@@ -1,4 +1,5 @@
import QtQuick 1.0
Item {
+ id: moduleRoot
}
diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/qmldir b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/qmldir
new file mode 100644
index 0000000000..7c4def92fc
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/qmldir
@@ -0,0 +1,3 @@
+foo bar foo bar
+internal foo bar foo
+Test 1.0 Test.qml
diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/temptest2.qml b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/temptest2.qml
new file mode 100644
index 0000000000..3eb29f43da
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/implicit2/temptest2.qml
@@ -0,0 +1,8 @@
+import QtQuick 1.0
+
+// the type loader will implicitly search "." for a qmldir
+// and the qmldir has various syntax errors in it.
+
+Item {
+ id: root
+}
diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp
index 9d1c609616..06b65d3c6f 100644
--- a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp
+++ b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp
@@ -70,6 +70,7 @@ private slots:
void remoteImportWithUnquotedUri();
void versionNotInstalled();
void versionNotInstalled_data();
+ void implicitQmldir();
};
#ifdef Q_OS_SYMBIAN
@@ -119,7 +120,6 @@ inline QUrl TEST_FILE(const QString &filename)
return QUrl::fromLocalFile(fileInfo.absoluteDir().filePath(filename));
}
-
void tst_qdeclarativemoduleplugin::importsPlugin()
{
QDeclarativeEngine engine;
@@ -308,6 +308,53 @@ void tst_qdeclarativemoduleplugin::versionNotInstalled()
VERIFY_ERRORS(errorFile.toLatin1().constData());
}
+
+// test that errors are reporting correctly for plugin loading and qmldir parsing
+void tst_qdeclarativemoduleplugin::implicitQmldir()
+{
+ QDeclarativeEngine engine;
+
+ QObject *obj = 0;
+ QList<QDeclarativeError> errors;
+ QString qmldirUrl;
+ QStringList expectedErrors;
+
+ // parsing qmldir succeeds, but plugin specified in the qmldir file doesn't exist
+ QDeclarativeComponent c(&engine, TEST_FILE("data/implicit1/temptest.qml"));
+ qmldirUrl = TEST_FILE("data/implicit1/qmldir").toString();
+ errors = c.errors();
+ QString moduleName = TEST_FILE("data/implicit1").toString().remove(0,7).replace(QLatin1String("/"), QLatin1String("."));
+ expectedErrors << QString(QLatin1String(": module \"") + moduleName + QLatin1String("\" plugin \"AType\" not found"));
+ QVERIFY(errors.size() == expectedErrors.size());
+ for (int i = 0; i < errors.size(); ++i) {
+ QString msg = qmldirUrl + expectedErrors.at(i);
+ QCOMPARE(errors.at(i).toString(), msg); // ensure that the expected message matches the real message.
+ }
+ QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+ obj = c.create();
+ QVERIFY(!obj);
+ delete obj;
+
+ // parsing qmldir fails due to syntax errors etc.
+ QDeclarativeComponent c2(&engine, TEST_FILE("data/implicit2/temptest2.qml"));
+ qmldirUrl = TEST_FILE("data/implicit2/qmldir").toString();
+ errors = c2.errors();
+ expectedErrors = QStringList();
+ expectedErrors << QLatin1String(":1:12: unexpected token");
+ expectedErrors << QLatin1String(":1: expected '.'");
+ expectedErrors << QLatin1String(":2:17: unexpected token");
+ QVERIFY(errors.size() == expectedErrors.size());
+ for (int i = 0; i < errors.size(); ++i) {
+ QString msg = qmldirUrl + expectedErrors.at(i);
+ QCOMPARE(errors.at(i).toString(), msg); // ensure that the expected message matches the real message.
+ }
+ QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+ obj = c2.create();
+ QVERIFY(!obj);
+ delete obj;
+}
+
+
QTEST_MAIN(tst_qdeclarativemoduleplugin)
#include "tst_qdeclarativemoduleplugin.moc"
diff --git a/tests/auto/declarative/qdeclarativeparticles/tst_qdeclarativeparticles.cpp b/tests/auto/declarative/qdeclarativeparticles/tst_qdeclarativeparticles.cpp
index 6e90cd84ab..31d566fa15 100644
--- a/tests/auto/declarative/qdeclarativeparticles/tst_qdeclarativeparticles.cpp
+++ b/tests/auto/declarative/qdeclarativeparticles/tst_qdeclarativeparticles.cpp
@@ -105,6 +105,8 @@ void tst_QDeclarativeParticles::properties()
particles->setProperty("emissionRate", 12);
QCOMPARE(particles->property("emissionRate").toInt(), 12);
+
+ delete canvas;
}
void tst_QDeclarativeParticles::motionGravity()
@@ -145,6 +147,8 @@ void tst_QDeclarativeParticles::motionGravity()
QCOMPARE(xattractorSpy.count(), 1);
QCOMPARE(yattractorSpy.count(), 1);
QCOMPARE(accelerationSpy.count(), 1);
+
+ delete canvas;
}
void tst_QDeclarativeParticles::motionWander()
@@ -193,6 +197,8 @@ void tst_QDeclarativeParticles::motionWander()
QCOMPARE(xvarianceSpy.count(), 1);
QCOMPARE(yvarianceSpy.count(), 1);
QCOMPARE(paceSpy.count(), 1);
+
+ delete canvas;
}
void tst_QDeclarativeParticles::runs()
@@ -203,6 +209,8 @@ void tst_QDeclarativeParticles::runs()
QObject* particles = canvas->rootObject()->findChild<QObject*>("particles");
QVERIFY(particles);
QTest::qWait(1000);//Run for one second. Test passes if it doesn't crash.
+
+ delete canvas;
}
QDeclarativeView *tst_QDeclarativeParticles::createView(const QString &filename)
diff --git a/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp b/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp
index 46c351985f..fa6093f311 100644
--- a/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp
+++ b/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp
@@ -469,6 +469,7 @@ void tst_QDeclarativePathView::dataModel()
QTRY_COMPARE(pathview->offset(), 2.);
delete canvas;
+ delete testObject;
}
void tst_QDeclarativePathView::pathMoved()
@@ -628,6 +629,8 @@ void tst_QDeclarativePathView::resetModel()
QVERIFY(display != 0);
QCOMPARE(display->text(), strings.at(i));
}
+
+ delete canvas;
}
void tst_QDeclarativePathView::propertyChanges()
diff --git a/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp b/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp
index edb182cdec..725d0238bc 100644
--- a/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp
+++ b/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp
@@ -289,18 +289,19 @@ void tst_qdeclarativepixmapcache::parallel()
void tst_qdeclarativepixmapcache::massive()
{
+ QDeclarativeEngine engine;
QUrl url = thisfile.resolved(QUrl("data/massive.png"));
// Confirm that massive images remain in the cache while they are
// in use by the application.
{
qint64 cachekey = 0;
- QDeclarativePixmap p(0, url);
+ QDeclarativePixmap p(&engine, url);
QVERIFY(p.isReady());
QVERIFY(p.pixmap().size() == QSize(10000, 1000));
cachekey = p.pixmap().cacheKey();
- QDeclarativePixmap p2(0, url);
+ QDeclarativePixmap p2(&engine, url);
QVERIFY(p2.isReady());
QVERIFY(p2.pixmap().size() == QSize(10000, 1000));
@@ -312,13 +313,13 @@ void tst_qdeclarativepixmapcache::massive()
{
qint64 cachekey = 0;
{
- QDeclarativePixmap p(0, url);
+ QDeclarativePixmap p(&engine, url);
QVERIFY(p.isReady());
QVERIFY(p.pixmap().size() == QSize(10000, 1000));
cachekey = p.pixmap().cacheKey();
}
- QDeclarativePixmap p2(0, url);
+ QDeclarativePixmap p2(&engine, url);
QVERIFY(p2.isReady());
QVERIFY(p2.pixmap().size() == QSize(10000, 1000));
diff --git a/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp b/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp
index 78821cb3d3..f1d17d5cb3 100644
--- a/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp
+++ b/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp
@@ -1106,80 +1106,94 @@ void tst_QDeclarativePositioners::test_conflictinganchors()
QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QVERIFY(warningMessage.isEmpty());
+ delete item;
component.setData("import QtQuick 1.0\nRow { Item {} }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QVERIFY(warningMessage.isEmpty());
+ delete item;
component.setData("import QtQuick 1.0\nGrid { Item {} }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QVERIFY(warningMessage.isEmpty());
+ delete item;
component.setData("import QtQuick 1.0\nFlow { Item {} }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QVERIFY(warningMessage.isEmpty());
+ delete item;
component.setData("import QtQuick 1.0\nColumn { Item { anchors.top: parent.top } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"));
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nColumn { Item { anchors.centerIn: parent } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"));
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nColumn { Item { anchors.left: parent.left } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QVERIFY(warningMessage.isEmpty());
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nRow { Item { anchors.left: parent.left } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"));
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nRow { Item { anchors.fill: parent } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"));
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nRow { Item { anchors.top: parent.top } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QVERIFY(warningMessage.isEmpty());
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nGrid { Item { anchors.horizontalCenter: parent.horizontalCenter } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Grid: Cannot specify anchors for items inside Grid"));
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nGrid { Item { anchors.centerIn: parent } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Grid: Cannot specify anchors for items inside Grid"));
warningMessage.clear();
+ delete item;
component.setData("import QtQuick 1.0\nFlow { Item { anchors.verticalCenter: parent.verticalCenter } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Flow: Cannot specify anchors for items inside Flow"));
+ delete item;
component.setData("import QtQuick 1.0\nFlow { Item { anchors.fill: parent } }", QUrl::fromLocalFile(""));
item = qobject_cast<QDeclarativeItem*>(component.create());
QVERIFY(item);
QCOMPARE(warningMessage, QString("file::2:1: QML Flow: Cannot specify anchors for items inside Flow"));
qInstallMsgHandler(oldMsgHandler);
+ delete item;
}
void tst_QDeclarativePositioners::test_vertical_qgraphicswidget()
diff --git a/tests/auto/declarative/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp b/tests/auto/declarative/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp
index 5b225892c5..d5f2396c6e 100644
--- a/tests/auto/declarative/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp
+++ b/tests/auto/declarative/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp
@@ -177,6 +177,8 @@ void tst_qdeclarativesmoothedanimation::valueSource()
QTRY_COMPARE(theRect->x(), qreal(200));
QTRY_COMPARE(theRect->y(), qreal(200));
+
+ delete rect;
}
void tst_qdeclarativesmoothedanimation::behavior()
@@ -205,6 +207,8 @@ void tst_qdeclarativesmoothedanimation::behavior()
QTRY_COMPARE(theRect->x(), qreal(200));
QTRY_COMPARE(theRect->y(), qreal(200));
+
+ delete rect;
}
QTEST_MAIN(tst_qdeclarativesmoothedanimation)
diff --git a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp
index 557603f1f7..eca183a491 100644
--- a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp
+++ b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp
@@ -216,6 +216,8 @@ void tst_qdeclarativetext::text()
QVERIFY(textObject != 0);
QCOMPARE(textObject->text(), standard.at(i));
QVERIFY(textObject->width() > 0);
+
+ delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -229,6 +231,8 @@ void tst_qdeclarativetext::text()
QString expected = richText.at(i);
QCOMPARE(textObject->text(), expected.replace("\\\"", "\""));
QVERIFY(textObject->width() > 0);
+
+ delete textObject;
}
}
@@ -242,6 +246,8 @@ void tst_qdeclarativetext::width()
QVERIFY(textObject != 0);
QCOMPARE(textObject->width(), 0.);
+
+ delete textObject;
}
for (int i = 0; i < standard.size(); i++)
@@ -262,6 +268,8 @@ void tst_qdeclarativetext::width()
QVERIFY(textObject->boundingRect().width() > 0);
QCOMPARE(textObject->width(), qreal(metricWidth));
QVERIFY(textObject->textFormat() == QDeclarativeText::AutoText); // setting text doesn't change format
+
+ delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -282,6 +290,8 @@ void tst_qdeclarativetext::width()
QVERIFY(textObject != 0);
QCOMPARE(textObject->width(), qreal(documentWidth));
QVERIFY(textObject->textFormat() == QDeclarativeText::AutoText); // setting text doesn't change format
+
+ delete textObject;
}
}
@@ -298,6 +308,8 @@ void tst_qdeclarativetext::wrap()
QVERIFY(textObject != 0);
QVERIFY(textObject->wrapMode() == QDeclarativeText::WordWrap);
QCOMPARE(textObject->width(), 300.);
+
+ delete textObject;
}
for (int i = 0; i < standard.size(); i++)
@@ -314,6 +326,8 @@ void tst_qdeclarativetext::wrap()
int oldHeight = textObject->height();
textObject->setWidth(100);
QVERIFY(textObject->height() < oldHeight);
+
+ delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -330,6 +344,8 @@ void tst_qdeclarativetext::wrap()
qreal oldHeight = textObject->height();
textObject->setWidth(100);
QVERIFY(textObject->height() < oldHeight);
+
+ delete textObject;
}
// richtext again with a fixed height
@@ -347,6 +363,8 @@ void tst_qdeclarativetext::wrap()
qreal oldHeight = textObject->implicitHeight();
textObject->setWidth(100);
QVERIFY(textObject->implicitHeight() < oldHeight);
+
+ delete textObject;
}
}
@@ -365,6 +383,8 @@ void tst_qdeclarativetext::elide()
QCOMPARE(textObject->elideMode(), m);
QCOMPARE(textObject->width(), 100.);
+
+ delete textObject;
}
for (int i = 0; i < standard.size(); i++)
@@ -376,6 +396,8 @@ void tst_qdeclarativetext::elide()
QCOMPARE(textObject->elideMode(), m);
QCOMPARE(textObject->width(), 100.);
+
+ delete textObject;
}
// richtext - does nothing
@@ -388,6 +410,8 @@ void tst_qdeclarativetext::elide()
QCOMPARE(textObject->elideMode(), m);
QCOMPARE(textObject->width(), 100.);
+
+ delete textObject;
}
}
@@ -408,6 +432,8 @@ void tst_qdeclarativetext::textFormat()
QVERIFY(textObject != 0);
QVERIFY(textObject->textFormat() == QDeclarativeText::RichText);
+
+ delete textObject;
}
{
QDeclarativeComponent textComponent(&engine);
@@ -416,6 +442,8 @@ void tst_qdeclarativetext::textFormat()
QVERIFY(textObject != 0);
QVERIFY(textObject->textFormat() == QDeclarativeText::PlainText);
+
+ delete textObject;
}
}
@@ -497,6 +525,8 @@ void tst_qdeclarativetext::horizontalAlignment()
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
+
+ delete textObject;
}
}
@@ -510,6 +540,8 @@ void tst_qdeclarativetext::horizontalAlignment()
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
+
+ delete textObject;
}
}
@@ -648,6 +680,8 @@ void tst_qdeclarativetext::verticalAlignment()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
+
+ delete textObject;
}
}
@@ -662,6 +696,8 @@ void tst_qdeclarativetext::verticalAlignment()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
+
+ delete textObject;
}
}
@@ -697,6 +733,8 @@ void tst_qdeclarativetext::font()
QCOMPARE(textObject->font().pointSize(), 40);
QCOMPARE(textObject->font().bold(), false);
QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
}
{
@@ -708,6 +746,8 @@ void tst_qdeclarativetext::font()
QCOMPARE(textObject->font().pixelSize(), 40);
QCOMPARE(textObject->font().bold(), false);
QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
}
{
@@ -718,6 +758,8 @@ void tst_qdeclarativetext::font()
QCOMPARE(textObject->font().bold(), true);
QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
}
{
@@ -728,6 +770,8 @@ void tst_qdeclarativetext::font()
QCOMPARE(textObject->font().italic(), true);
QCOMPARE(textObject->font().bold(), false);
+
+ delete textObject;
}
{
@@ -739,6 +783,8 @@ void tst_qdeclarativetext::font()
QCOMPARE(textObject->font().family(), QString("Helvetica"));
QCOMPARE(textObject->font().bold(), false);
QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
}
{
@@ -748,6 +794,8 @@ void tst_qdeclarativetext::font()
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE(textObject->font().family(), QString(""));
+
+ delete textObject;
}
}
@@ -763,6 +811,8 @@ void tst_qdeclarativetext::style()
QCOMPARE((int)textObject->style(), (int)styles.at(i));
QCOMPARE(textObject->styleColor(), QColor("white"));
+
+ delete textObject;
}
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello World\" }";
QDeclarativeComponent textComponent(&engine);
@@ -775,6 +825,8 @@ void tst_qdeclarativetext::style()
QVERIFY(brPre.width() < brPost.width());
QVERIFY(brPre.height() < brPost.height());
+
+ delete textObject;
}
void tst_qdeclarativetext::color()
@@ -789,6 +841,8 @@ void tst_qdeclarativetext::color()
QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
QCOMPARE(textObject->styleColor(), QColor());
+
+ delete textObject;
}
for (int i = 0; i < colorStrings.size(); i++)
@@ -801,6 +855,8 @@ void tst_qdeclarativetext::color()
QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
// default color to black?
QCOMPARE(textObject->color(), QColor("black"));
+
+ delete textObject;
}
for (int i = 0; i < colorStrings.size(); i++)
@@ -814,6 +870,8 @@ void tst_qdeclarativetext::color()
QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
+
+ delete textObject;
}
}
{
@@ -827,6 +885,8 @@ void tst_qdeclarativetext::color()
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE(textObject->color(), testColor);
+
+ delete textObject;
}
}
@@ -840,6 +900,8 @@ void tst_qdeclarativetext::smooth()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE(textObject->smooth(), true);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"" + standard.at(i) + "\" }";
@@ -847,6 +909,8 @@ void tst_qdeclarativetext::smooth()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE(textObject->smooth(), false);
+
+ delete textObject;
}
}
for (int i = 0; i < richText.size(); i++)
@@ -857,6 +921,8 @@ void tst_qdeclarativetext::smooth()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE(textObject->smooth(), true);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"" + richText.at(i) + "\" }";
@@ -864,6 +930,8 @@ void tst_qdeclarativetext::smooth()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create());
QCOMPARE(textObject->smooth(), false);
+
+ delete textObject;
}
}
}
@@ -878,6 +946,8 @@ void tst_qdeclarativetext::weight()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().weight(), (int)QDeclarativeFontValueType::Normal);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
@@ -887,6 +957,8 @@ void tst_qdeclarativetext::weight()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().weight(), (int)QDeclarativeFontValueType::Bold);
+
+ delete textObject;
}
}
@@ -900,6 +972,8 @@ void tst_qdeclarativetext::underline()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().underline(), false);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { font.underline: true; text: \"Hello world!\" }";
@@ -909,6 +983,8 @@ void tst_qdeclarativetext::underline()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().underline(), true);
+
+ delete textObject;
}
}
@@ -922,6 +998,8 @@ void tst_qdeclarativetext::overline()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().overline(), false);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { font.overline: true; text: \"Hello world!\" }";
@@ -931,6 +1009,8 @@ void tst_qdeclarativetext::overline()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().overline(), true);
+
+ delete textObject;
}
}
@@ -944,6 +1024,8 @@ void tst_qdeclarativetext::strikeout()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().strikeOut(), false);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { font.strikeout: true; text: \"Hello world!\" }";
@@ -953,6 +1035,8 @@ void tst_qdeclarativetext::strikeout()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().strikeOut(), true);
+
+ delete textObject;
}
}
@@ -966,6 +1050,8 @@ void tst_qdeclarativetext::capitalization()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::MixedCase);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.capitalization: \"AllUppercase\" }";
@@ -975,6 +1061,8 @@ void tst_qdeclarativetext::capitalization()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::AllUppercase);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.capitalization: \"AllLowercase\" }";
@@ -984,6 +1072,8 @@ void tst_qdeclarativetext::capitalization()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::AllLowercase);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.capitalization: \"SmallCaps\" }";
@@ -993,6 +1083,8 @@ void tst_qdeclarativetext::capitalization()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::SmallCaps);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.capitalization: \"Capitalize\" }";
@@ -1002,6 +1094,8 @@ void tst_qdeclarativetext::capitalization()
QVERIFY(textObject != 0);
QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::Capitalize);
+
+ delete textObject;
}
}
@@ -1015,6 +1109,8 @@ void tst_qdeclarativetext::letterSpacing()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().letterSpacing(), 0.0);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.letterSpacing: -2 }";
@@ -1024,6 +1120,8 @@ void tst_qdeclarativetext::letterSpacing()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().letterSpacing(), -2.);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.letterSpacing: 3 }";
@@ -1033,6 +1131,8 @@ void tst_qdeclarativetext::letterSpacing()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().letterSpacing(), 3.);
+
+ delete textObject;
}
}
@@ -1046,6 +1146,8 @@ void tst_qdeclarativetext::wordSpacing()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().wordSpacing(), 0.0);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.wordSpacing: -50 }";
@@ -1055,6 +1157,8 @@ void tst_qdeclarativetext::wordSpacing()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().wordSpacing(), -50.);
+
+ delete textObject;
}
{
QString componentStr = "import QtQuick 1.0\nText { text: \"Hello world!\"; font.wordSpacing: 200 }";
@@ -1064,6 +1168,8 @@ void tst_qdeclarativetext::wordSpacing()
QVERIFY(textObject != 0);
QCOMPARE(textObject->font().wordSpacing(), 200.);
+
+ delete textObject;
}
}
@@ -1132,6 +1238,8 @@ void tst_qdeclarativetext::clickLink()
}
QCOMPARE(test.link, QLatin1String("http://qt.nokia.com"));
+
+ delete textObject;
}
}
@@ -1269,6 +1377,8 @@ void tst_qdeclarativetext::implicitSize()
textObject->resetWidth();
QVERIFY(textObject->width() == textObject->implicitWidth());
QVERIFY(textObject->height() == textObject->implicitHeight());
+
+ delete textObject;
}
void tst_qdeclarativetext::testQtQuick11Attributes()
diff --git a/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp b/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp
index a241241505..821c4ca0f7 100644
--- a/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp
+++ b/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp
@@ -713,6 +713,8 @@ void tst_qdeclarativetextinput::moveCursorSelection()
QCOMPARE(textinputObject->selectionStart(), selectionStart);
QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
}
+
+ delete textinputObject;
}
void tst_qdeclarativetextinput::moveCursorSelectionSequence_data()
@@ -915,6 +917,8 @@ void tst_qdeclarativetextinput::moveCursorSelectionSequence()
QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
QCOMPARE(textinputObject->selectionStart(), selection2Start);
QCOMPARE(textinputObject->selectionEnd(), selection2End);
+
+ delete textinputObject;
}
void tst_qdeclarativetextinput::mouseSelection_data()
@@ -1712,6 +1716,8 @@ void tst_qdeclarativetextinput::copyAndPaste() {
}
index++;
}
+
+ delete textInput;
#endif
}
@@ -1764,6 +1770,8 @@ void tst_qdeclarativetextinput::passwordCharacter()
textInput->setPasswordCharacter(".");
// QTBUG-12383 content is updated and redrawn
QVERIFY(contentsSize != textInput->contentsSize());
+
+ delete textInput;
}
void tst_qdeclarativetextinput::cursorDelegate()
diff --git a/tests/auto/declarative/qdeclarativev4/data/doubleBoolJump.qml b/tests/auto/declarative/qdeclarativev4/data/doubleBoolJump.qml
new file mode 100644
index 0000000000..e7fb82ca36
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/doubleBoolJump.qml
@@ -0,0 +1,18 @@
+import QtQuick 1.0
+
+Rectangle {
+ QtObject {
+ property real output: i1.p1 || i2.p2 == "text" ? 0.7 : 0
+ }
+
+ QtObject {
+ id: i2
+ property string p2
+ }
+
+ QtObject {
+ id: i1
+ property bool p1: false
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativev4/data/fetchException.qml b/tests/auto/declarative/qdeclarativev4/data/fetchException.qml
new file mode 100644
index 0000000000..ece8e73199
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/fetchException.qml
@@ -0,0 +1,6 @@
+import QtQuick 1.0
+
+Item {
+ property Item data
+ property int a: data.x, 1
+}
diff --git a/tests/auto/declarative/qdeclarativev4/data/logicalOr.2.qml b/tests/auto/declarative/qdeclarativev4/data/logicalOr.2.qml
new file mode 100644
index 0000000000..54fb78b127
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/logicalOr.2.qml
@@ -0,0 +1,6 @@
+import Qt.v4 1.0
+
+Result {
+ property string s: "foo" || "bar"
+ result: s == "foo"
+}
diff --git a/tests/auto/declarative/qdeclarativev4/data/logicalOr.qml b/tests/auto/declarative/qdeclarativev4/data/logicalOr.qml
new file mode 100644
index 0000000000..406a7d83eb
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/logicalOr.qml
@@ -0,0 +1,6 @@
+import Qt.v4 1.0
+
+Result {
+ property int a: 10
+ result: a == 1 || a == 2
+}
diff --git a/tests/auto/declarative/qdeclarativev4/data/nestedObjectAccess.qml b/tests/auto/declarative/qdeclarativev4/data/nestedObjectAccess.qml
new file mode 100644
index 0000000000..56cd17e41e
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/nestedObjectAccess.qml
@@ -0,0 +1,5 @@
+import Qt.v4 1.0
+
+Result {
+ result: nested.result
+}
diff --git a/tests/auto/declarative/qdeclarativev4/data/nullQObject.qml b/tests/auto/declarative/qdeclarativev4/data/nullQObject.qml
new file mode 100644
index 0000000000..283c1a199b
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/nullQObject.qml
@@ -0,0 +1,7 @@
+import QtQuick 1.0
+
+Item {
+ property QtObject obj
+ property QtObject test
+ test: obj
+}
diff --git a/tests/auto/declarative/qdeclarativev4/data/qrealToIntRounding.qml b/tests/auto/declarative/qdeclarativev4/data/qrealToIntRounding.qml
new file mode 100644
index 0000000000..f961910f15
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/qrealToIntRounding.qml
@@ -0,0 +1,10 @@
+import QtQuick 1.0
+
+QtObject {
+ property int data: 1
+
+ property int test1: 6.6 + data
+ property int test2: 6.2 + data
+ property int test3: 6 + data
+}
+
diff --git a/tests/auto/declarative/qdeclarativev4/data/subscriptionsInConditionalExpressions.qml b/tests/auto/declarative/qdeclarativev4/data/subscriptionsInConditionalExpressions.qml
new file mode 100644
index 0000000000..25483b207d
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/subscriptionsInConditionalExpressions.qml
@@ -0,0 +1,11 @@
+import QtQuick 1.0
+
+Item {
+ id: thisTest
+
+ property bool cond: true
+ property real a: 1
+ property real result: cond ? a : a
+
+ PropertyAction { running: true; target: thisTest; property: "a"; value: 2; }
+}
diff --git a/tests/auto/declarative/qdeclarativev4/data/unaryMinus.qml b/tests/auto/declarative/qdeclarativev4/data/unaryMinus.qml
new file mode 100644
index 0000000000..3cfa0492c0
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/unaryMinus.qml
@@ -0,0 +1,18 @@
+import QtQuick 1.0
+
+Item {
+ property real test1: -i1.p2
+ property int test2: -i1.p2
+ property real test3: -i1.p1
+ property int test4: -i1.p1
+ property real test5: -i1.p3
+ property int test6: -i1.p3
+
+ QtObject {
+ id: i1
+ property real p1: -3.7
+ property int p2: 18
+ property real p3: -3.3
+ }
+ }
+
diff --git a/tests/auto/declarative/qdeclarativev4/data/unnecessaryReeval.qml b/tests/auto/declarative/qdeclarativev4/data/unnecessaryReeval.qml
new file mode 100644
index 0000000000..48662d7a2d
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/data/unnecessaryReeval.qml
@@ -0,0 +1,7 @@
+import Qt.v4 1.0
+
+Result {
+ property int a: 8
+ property int b: 19
+ result: (a == 8)?b:7
+}
diff --git a/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro b/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro
new file mode 100644
index 0000000000..ee22a04629
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro
@@ -0,0 +1,18 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative script network
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qdeclarativev4.cpp \
+ testtypes.cpp
+HEADERS += testtypes.h
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qdeclarativev4/testtypes.cpp b/tests/auto/declarative/qdeclarativev4/testtypes.cpp
new file mode 100644
index 0000000000..f4544b6e21
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/testtypes.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "testtypes.h"
+
+#include <QtDeclarative/qdeclarative.h>
+
+void registerTypes()
+{
+ qmlRegisterType<ResultObject>("Qt.v4", 1,0, "Result");
+ qmlRegisterType<NestedObject>();
+}
diff --git a/tests/auto/declarative/qdeclarativev4/testtypes.h b/tests/auto/declarative/qdeclarativev4/testtypes.h
new file mode 100644
index 0000000000..0933eff8b7
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/testtypes.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef TESTTYPES_H
+#define TESTTYPES_H
+
+#include <QtCore/qobject.h>
+
+class NestedObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int dummy READ dummy);
+ Q_PROPERTY(int result READ result FINAL CONSTANT);
+
+public:
+ int dummy() const { return 7; }
+ int result() const { return 37; }
+};
+
+class ResultObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int result READ result WRITE setResult FINAL)
+ Q_PROPERTY(NestedObject *nested READ nested CONSTANT)
+public:
+ ResultObject() : m_result(0), m_resultCounter(0) {}
+
+ int resultCounter() const { return m_resultCounter; }
+ void resetResultCounter() { m_resultCounter = 0; }
+
+ int result() const { return m_result; }
+ void setResult(int result) { m_result = result; m_resultCounter++; }
+
+ NestedObject *nested() { return &m_nested; }
+
+private:
+ int m_result;
+ int m_resultCounter;
+
+ NestedObject m_nested;
+};
+
+void registerTypes();
+
+#endif // TESTTYPES_H
+
diff --git a/tests/auto/declarative/qdeclarativev4/tst_qdeclarativev4.cpp b/tests/auto/declarative/qdeclarativev4/tst_qdeclarativev4.cpp
new file mode 100644
index 0000000000..0f8c5bcf66
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativev4/tst_qdeclarativev4.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtCore/qdebug.h>
+
+#include <private/qdeclarativev4compiler_p.h>
+
+#include "testtypes.h"
+
+inline QUrl TEST_FILE(const QString &filename)
+{
+ QFileInfo fileInfo(__FILE__);
+ return QUrl::fromLocalFile(fileInfo.absoluteDir().filePath("data/" + filename));
+}
+
+inline QUrl TEST_FILE(const char *filename)
+{
+ return TEST_FILE(QLatin1String(filename));
+}
+
+class tst_qdeclarativev4 : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qdeclarativev4() {}
+
+private slots:
+ void initTestCase();
+
+ void unnecessaryReeval();
+ void logicalOr();
+ void qtscript();
+ void qtscript_data();
+ void nestedObjectAccess();
+ void subscriptionsInConditionalExpressions();
+
+private:
+ QDeclarativeEngine engine;
+};
+
+void tst_qdeclarativev4::initTestCase()
+{
+ registerTypes();
+}
+
+static int v4ErrorsMsgCount = 0;
+static void v4ErrorsMsgHandler(QtMsgType, const char *message)
+{
+ QByteArray m(message);
+ if (m.contains("QDeclarativeV4"))
+ v4ErrorsMsgCount++;
+}
+
+void tst_qdeclarativev4::qtscript()
+{
+ QFETCH(QString, file);
+ QDeclarativeV4Compiler::enableBindingsTest(true);
+
+ QDeclarativeComponent component(&engine, TEST_FILE(file));
+
+ v4ErrorsMsgCount = 0;
+ QtMsgHandler old = qInstallMsgHandler(v4ErrorsMsgHandler);
+
+ QObject *o = component.create();
+ delete o;
+
+ qInstallMsgHandler(old);
+
+ QCOMPARE(v4ErrorsMsgCount, 0);
+
+ QDeclarativeV4Compiler::enableBindingsTest(false);
+}
+
+void tst_qdeclarativev4::qtscript_data()
+{
+ QTest::addColumn<QString>("file");
+
+ QTest::newRow("qreal -> int rounding") << "qrealToIntRounding.qml";
+ QTest::newRow("exception on fetch") << "fetchException.qml";
+ QTest::newRow("logical or") << "logicalOr.qml";
+ QTest::newRow("double bool jump") << "doubleBoolJump.qml";
+ QTest::newRow("unary minus") << "unaryMinus.qml";
+ QTest::newRow("null qobject") << "nullQObject.qml";
+}
+
+void tst_qdeclarativev4::unnecessaryReeval()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("unnecessaryReeval.qml"));
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ ResultObject *ro = qobject_cast<ResultObject *>(o);
+ QVERIFY(ro != 0);
+
+ QCOMPARE(ro->resultCounter(), 1);
+ QCOMPARE(ro->result(), 19);
+ ro->resetResultCounter();
+
+ ro->setProperty("b", 6);
+
+ QCOMPARE(ro->resultCounter(), 1);
+ QCOMPARE(ro->result(), 6);
+ ro->resetResultCounter();
+
+ ro->setProperty("a", 14);
+
+ QCOMPARE(ro->resultCounter(), 1);
+ QCOMPARE(ro->result(), 7);
+ ro->resetResultCounter();
+
+ ro->setProperty("b", 14);
+ QCOMPARE(ro->resultCounter(), 0);
+ QCOMPARE(ro->result(), 7);
+
+ delete o;
+}
+
+void tst_qdeclarativev4::logicalOr()
+{
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("logicalOr.qml"));
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ ResultObject *ro = qobject_cast<ResultObject *>(o);
+ QVERIFY(ro != 0);
+
+ QCOMPARE(ro->result(), 0);
+ delete o;
+ }
+
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("logicalOr.2.qml"));
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ ResultObject *ro = qobject_cast<ResultObject *>(o);
+ QVERIFY(ro != 0);
+
+ QCOMPARE(ro->result(), 1);
+ delete o;
+ }
+}
+
+// This would previously use the metaObject of the root element to result the nested access.
+// That is, the index for accessing "result" would have been RootObject::result, instead of
+// NestedObject::result.
+void tst_qdeclarativev4::nestedObjectAccess()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("nestedObjectAccess.qml"));
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ ResultObject *ro = qobject_cast<ResultObject *>(o);
+ QVERIFY(ro != 0);
+
+ QCOMPARE(ro->result(), 37);
+
+ delete o;
+}
+
+void tst_qdeclarativev4::subscriptionsInConditionalExpressions()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("subscriptionsInConditionalExpressions.qml"));
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ QObject *ro = qobject_cast<QObject *>(o);
+ QVERIFY(ro != 0);
+
+ QCOMPARE(ro->property("result").toReal(), qreal(2));
+
+ delete o;
+}
+
+QTEST_MAIN(tst_qdeclarativev4)
+
+#include "tst_qdeclarativev4.moc"
diff --git a/tests/auto/declarative/qdeclarativeview/tst_qdeclarativeview.cpp b/tests/auto/declarative/qdeclarativeview/tst_qdeclarativeview.cpp
index 83856597f2..bf2e85139f 100644
--- a/tests/auto/declarative/qdeclarativeview/tst_qdeclarativeview.cpp
+++ b/tests/auto/declarative/qdeclarativeview/tst_qdeclarativeview.cpp
@@ -93,6 +93,8 @@ void tst_QDeclarativeView::scene()
QVERIFY(declarativeItem);
QVERIFY(scene.items().count() > 0);
QCOMPARE(scene.items().at(0), declarativeItem);
+
+ delete view;
}
void tst_QDeclarativeView::resizemodedeclarativeitem()
diff --git a/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp b/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp
index d0e8c1c143..7f158fce67 100644
--- a/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp
+++ b/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp
@@ -524,7 +524,7 @@ void tst_qdeclarativexmlhttprequest::send_ignoreData()
QVERIFY(server.isValid());
QVERIFY(server.wait(TEST_FILE("send_ignoreData_PUT.expect"),
TEST_FILE("send_ignoreData.reply"),
- TEST_FILE("testdocument.html")));
+ QUrl()));
QDeclarativeComponent component(&engine, TEST_FILE("send_ignoreData.qml"));
QObject *object = component.beginCreate(engine.rootContext());
diff --git a/tests/auto/declarative/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/declarative/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
index 1b9831c1ad..f12bcc32db 100644
--- a/tests/auto/declarative/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
+++ b/tests/auto/declarative/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
@@ -546,6 +546,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!nullProp.isUser());
QVERIFY(!nullProp.hasStdCppSet());
QVERIFY(!nullProp.isEnumOrFlag());
+ QVERIFY(!nullProp.isConstant());
+ QVERIFY(!nullProp.isFinal());
QCOMPARE(nullProp.index(), 0);
// Add a property and check its attributes.
@@ -563,6 +565,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop1.isUser());
QVERIFY(!prop1.hasStdCppSet());
QVERIFY(!prop1.isEnumOrFlag());
+ QVERIFY(!prop1.isConstant());
+ QVERIFY(!prop1.isFinal());
QCOMPARE(prop1.index(), 0);
QCOMPARE(builder.propertyCount(), 1);
@@ -581,6 +585,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop2.isUser());
QVERIFY(!prop2.hasStdCppSet());
QVERIFY(!prop2.isEnumOrFlag());
+ QVERIFY(!prop2.isConstant());
+ QVERIFY(!prop2.isFinal());
QCOMPARE(prop2.index(), 1);
QCOMPARE(builder.propertyCount(), 2);
@@ -602,6 +608,8 @@ void tst_QMetaObjectBuilder::property()
prop1.setUser(true);
prop1.setStdCppSet(true);
prop1.setEnumOrFlag(true);
+ prop1.setConstant(true);
+ prop1.setFinal(true);
// Check that prop1 is changed, but prop2 is not.
QCOMPARE(prop1.name(), QByteArray("foo"));
@@ -616,6 +624,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(prop1.isUser());
QVERIFY(prop1.hasStdCppSet());
QVERIFY(prop1.isEnumOrFlag());
+ QVERIFY(prop1.isConstant());
+ QVERIFY(prop1.isFinal());
QVERIFY(prop2.isReadable());
QVERIFY(prop2.isWritable());
QCOMPARE(prop2.name(), QByteArray("bar"));
@@ -628,6 +638,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop2.isUser());
QVERIFY(!prop2.hasStdCppSet());
QVERIFY(!prop2.isEnumOrFlag());
+ QVERIFY(!prop2.isConstant());
+ QVERIFY(!prop2.isFinal());
// Remove prop1 and check that prop2 becomes index 0.
builder.removeProperty(0);
@@ -643,6 +655,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop2.isUser());
QVERIFY(!prop2.hasStdCppSet());
QVERIFY(!prop2.isEnumOrFlag());
+ QVERIFY(!prop2.isConstant());
+ QVERIFY(!prop2.isFinal());
QCOMPARE(prop2.index(), 0);
// Perform index-based lookup again.
@@ -666,6 +680,8 @@ void tst_QMetaObjectBuilder::property()
prop2.setUser(false); \
prop2.setStdCppSet(false); \
prop2.setEnumOrFlag(false); \
+ prop2.setConstant(false); \
+ prop2.setFinal(false); \
} while (0)
#define COUNT_FLAGS() \
((prop2.isReadable() ? 1 : 0) + \
@@ -677,7 +693,9 @@ void tst_QMetaObjectBuilder::property()
(prop2.isEditable() ? 1 : 0) + \
(prop2.isUser() ? 1 : 0) + \
(prop2.hasStdCppSet() ? 1 : 0) + \
- (prop2.isEnumOrFlag() ? 1 : 0))
+ (prop2.isEnumOrFlag() ? 1 : 0) + \
+ (prop2.isConstant() ? 1 : 0) + \
+ (prop2.isFinal() ? 1 : 0))
#define CHECK_FLAG(setFunc,isFunc) \
do { \
CLEAR_FLAGS(); \
@@ -696,6 +714,8 @@ void tst_QMetaObjectBuilder::property()
CHECK_FLAG(setUser, isUser);
CHECK_FLAG(setStdCppSet, hasStdCppSet);
CHECK_FLAG(setEnumOrFlag, isEnumOrFlag);
+ CHECK_FLAG(setConstant, isConstant);
+ CHECK_FLAG(setFinal, isFinal);
// Check that nothing else changed.
QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Properties));
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.0.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.0.png
index a02a00dd78..c4722545dc 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.0.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.0.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.1.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.1.png
index be18b8dc2d..58a293bafa 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.1.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.1.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.2.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.2.png
index e4db4bca2e..65a03a1294 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.2.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.2.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.3.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.3.png
index d464e79d00..d736eca904 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.3.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.3.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.4.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.4.png
index b0b93863e1..d7386b3234 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.4.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.4.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.5.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.5.png
index 4ea4b24e16..3500c07ada 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.5.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.5.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.6.png b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.6.png
index a115867d2a..7c27234f2e 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.6.png
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.6.png
Binary files differ
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.qml b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.qml
index 5f1e8be157..a7c7b339b6 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.qml
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/data/dynamic.qml
@@ -10,239 +10,239 @@ VisualTest {
}
Frame {
msec: 32
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "0e4c352da978cc130894d1f4ce0a0eb4"
}
Frame {
msec: 48
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "a2e959463144623ba36524f4d4ba1c50"
}
Frame {
msec: 64
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "db8f96a79852b468bf793ac0a88f662b"
}
Frame {
msec: 80
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "d2bf766a1271d31fb2771efa3b60dc92"
}
Frame {
msec: 96
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "c99a42391eb5f41c8a09ae5376438998"
}
Frame {
msec: 112
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "88a11ce1a21c28c8fe83cce52ac81c87"
}
Frame {
msec: 128
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "ebfeb134db6e2ce0226f61c685614644"
}
Frame {
msec: 144
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "39983ecac9b5a7bbf486c5549a806bf3"
}
Frame {
msec: 160
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "8216e55fe99f2cabec9859793dc3b57e"
}
Frame {
msec: 176
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "e0ba0455c1142fbc17e9dae54ddde345"
}
Frame {
msec: 192
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "a06714fb98a25430d3c5e133af5d4c8b"
}
Frame {
msec: 208
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "c87a55800b77a6880ba5859c83a4f9d0"
}
Frame {
msec: 224
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "3c055c7bfca6687f139bf60823f8148f"
}
Frame {
msec: 240
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "756dbaf1c06e1ab82ff5be4290795d66"
}
Frame {
msec: 256
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f9ad4f26264b7b29294998085b8ed36a"
}
Frame {
msec: 272
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 288
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 304
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 320
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 336
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 352
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 368
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 384
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 400
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 416
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 432
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 448
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 464
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 480
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 496
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 512
- hash: "d203c2cb69cb0841827e14bddc206d1f"
+ hash: "f636a172aec19c2fb586021837095b11"
}
Frame {
msec: 528
- hash: "453d5fb9d38f37bb8c23e376de76db06"
+ hash: "f0260c84eae73c072d7a01226cb5bfeb"
}
Frame {
msec: 544
- hash: "c4a103de3b7207b3c6277e8ecf79f7dc"
+ hash: "f65bbbeadff6cd71e397c6183ccd6036"
}
Frame {
msec: 560
- hash: "f58b0eb42d9b6ace87379f205da57550"
+ hash: "9efe8a2c0ffed561658b6d9987d88d80"
}
Frame {
msec: 576
- hash: "3384c5b5939d8297e0834c7cd347d579"
+ hash: "4b93c2cbce50887122533b239678419e"
}
Frame {
msec: 592
- hash: "420b55371c69c6e1a17ef85a600c75d1"
+ hash: "e3f955bf3f71e905073e43507624f11c"
}
Frame {
msec: 608
- hash: "55cc6fb3424ef69d316ef29f6563a025"
+ hash: "1700198250ef7efba094ba37ec9bde23"
}
Frame {
msec: 624
- hash: "045b5ac545e69777b814423f77575990"
+ hash: "f2e150d7c2fe91ec2754e1fb9a88c6ec"
}
Frame {
msec: 640
- hash: "45b05241e8e83180a8d92a37dc859ce0"
+ hash: "ab5c2939088861810771db378cb73362"
}
Frame {
msec: 656
- hash: "97915dfbe4943e1f583ee134bc7a0117"
+ hash: "d2459ace336f1366951e0c7ae5b8ad9e"
}
Frame {
msec: 672
- hash: "9b4ce5ed20dd81698b4dd8e48f799c5c"
+ hash: "7099bf087bb89c3b8846e24126f49b83"
}
Frame {
msec: 688
- hash: "24bdcea108cdbb3898a4d9216e9f9510"
+ hash: "65df13d3e38fb739a646746cb401db8e"
}
Frame {
msec: 704
- hash: "d1427093b1a375e86a69c6f65cb1f8e5"
+ hash: "71479564df994d8db2fc01302e6261c5"
}
Frame {
msec: 720
- hash: "43dd666b15697ae18eb2410017256e4c"
+ hash: "ceb4091d33a136aecc470d5572c0bdae"
}
Frame {
msec: 736
- hash: "8043755f5a8a528353f1e7c310a46a97"
+ hash: "0078ad79eee838839aca53fedda51310"
}
Frame {
msec: 752
- hash: "d6b2ef0cb81395cd7454392aed4571f0"
+ hash: "7d5385582e19594afa40154cc61ef54d"
}
Frame {
msec: 768
- hash: "c249fd272e02cbdde972e85fc6dac695"
+ hash: "372fc45db91d4507a5c3baec9efb9224"
}
Frame {
msec: 784
- hash: "1df5f8fce7b0c102e9902912600054e7"
+ hash: "400f21d4028c09d351609a73510dcde0"
}
Frame {
msec: 800
- hash: "a9d16b180634620e3fe6caacb730885b"
+ hash: "5a3965480d6703fdfcec241deae14db7"
}
Frame {
msec: 816
- hash: "7228ce597720520bc12911fdef70ca86"
+ hash: "4ef441bb3f8ab9da1c3eb0d0f7c33c00"
}
Frame {
msec: 832
- hash: "1faa5c3e72740941234ff4a93388edc9"
+ hash: "7526f27a13471bcee2e8e8fe24384bdb"
}
Frame {
msec: 848
- hash: "997ee1c6d5838153182473a3724df4ad"
+ hash: "69afb4d4af4479ac5b2d5bd39b8a968b"
}
Frame {
msec: 864
- hash: "6ebbc0a0427825ea701f5bb4758f11a2"
+ hash: "c523ae0a884cb56fe1f72744ccfe7d1e"
}
Frame {
msec: 880
- hash: "a2ac19360d631fd6d58f8a5ee85e40b4"
+ hash: "6974ea16d8c13a032283fa9df314fb87"
}
Frame {
msec: 896
- hash: "17a5842b47a220bb8bd74a368cea6c1f"
+ hash: "1635eb85aedbad17fde10da173d612ab"
}
Frame {
msec: 912
- hash: "b37bae9c3384c068a7dd4f1135d3bfaa"
+ hash: "9be18310fd7cf548edbc976e4a470228"
}
Frame {
msec: 928
- hash: "65ffb0b6629364ebc687da7785601abd"
+ hash: "5b8574aa9fa9b93fae64c5b180075090"
}
Frame {
msec: 944
- hash: "27c7a43515fb2d8cddde42263f6ac9df"
+ hash: "285eba0de99a1e1c9c43049b469dad9e"
}
Frame {
msec: 960
- hash: "cc292df8a090c08d135dedf5e2a0af7c"
+ hash: "eb824394813a847c9e7e532b74b17c8a"
}
Frame {
msec: 976
@@ -250,239 +250,239 @@ VisualTest {
}
Frame {
msec: 992
- hash: "59735c8372774b50052d15232d2f6d01"
+ hash: "984a2cf360a1bf2e598d329991107c66"
}
Frame {
msec: 1008
- hash: "9c239c4439009465dd91606ac84a3bf0"
+ hash: "5c70be0eeee3f88981021334668360c8"
}
Frame {
msec: 1024
- hash: "8b9715d6468c8501895545bd84bf7f57"
+ hash: "6f361a3be917e677ed41212382569fea"
}
Frame {
msec: 1040
- hash: "0f7a123bfff1dbe059b6ceb3a3f44180"
+ hash: "ab9035768e6e9f81af8158ea0c42863f"
}
Frame {
msec: 1056
- hash: "c1bc31a379d15ca67d0ffc7139800b3f"
+ hash: "58f3d5fb29352a6788bc9e13a1cb7603"
}
Frame {
msec: 1072
- hash: "ebf470cd7cff4a836da9e721acfd327d"
+ hash: "7f1a415a6fc970244b024305b10ce91e"
}
Frame {
msec: 1088
- hash: "e2b89846459f8ae48117ab4393d493bf"
+ hash: "213d79d582aa8f4f736f5bbbfc7d1926"
}
Frame {
msec: 1104
- hash: "5b980dcc070faf4ab4099cd5f711259c"
+ hash: "391c2c6005de6800064763fbf88b4dda"
}
Frame {
msec: 1120
- hash: "4640ed55c1608d76109407279a1f02db"
+ hash: "641dd4b888edc40c8d7df0ab89ad400d"
}
Frame {
msec: 1136
- hash: "d839b597a3afef61de7b14ffb7ae518e"
+ hash: "7fde8a9ee9cdc10b045431301b7e15bc"
}
Frame {
msec: 1152
- hash: "2810e01355c32d3f7a9352676e6b5eef"
+ hash: "15066ed9165a83cafbd4f15435ed5689"
}
Frame {
msec: 1168
- hash: "f1ac8b222e0068320827564e759e87ba"
+ hash: "19b685a164648283774167a31b1dff43"
}
Frame {
msec: 1184
- hash: "7da89563319dd4045e7f9c40a712d722"
+ hash: "837e73ba982c119c3a940cdcdbfdf440"
}
Frame {
msec: 1200
- hash: "09c55dba364e484eec1a1badb4319003"
+ hash: "c8af92bf004e392f1dc8d1e61746f286"
}
Frame {
msec: 1216
- hash: "defd5c9a8003c58a7bef1930efdd6f29"
+ hash: "a357285710ea7d94fbc0f181b5e226dc"
}
Frame {
msec: 1232
- hash: "0f84e515b41b5c064ece9002e5edff0d"
+ hash: "657e15bee0eed2686fcaffad86343677"
}
Frame {
msec: 1248
- hash: "d1a0405a18fe5b54e79ca0cadf46743b"
+ hash: "994c039fed74a9bae706efe7261cc42d"
}
Frame {
msec: 1264
- hash: "6046feb2fad386ae25ddd0d0e8ecb673"
+ hash: "135ea105bdefd24e49dfb58ad61fbb05"
}
Frame {
msec: 1280
- hash: "b4374b0d9d709b0d7a9f8949616a16bf"
+ hash: "7ac7945f810cf9647b0f72db78076415"
}
Frame {
msec: 1296
- hash: "4d9d7d28f32ce2acd14c8dca0bc11fa0"
+ hash: "c41434915014b4122d5d396fa74127ee"
}
Frame {
msec: 1312
- hash: "384afb63bdf34729132ac57080fa2988"
+ hash: "944f79f98cd54a89b1e833199536e894"
}
Frame {
msec: 1328
- hash: "44ac2a9783c450a8c39b09387f0439e2"
+ hash: "c11291d032471d347e5f51a5865a52c7"
}
Frame {
msec: 1344
- hash: "26e1dfc2b54370f94881c2341b6e0618"
+ hash: "92cca77635d5e1a843b8957ecb092373"
}
Frame {
msec: 1360
- hash: "be47d72ae7c57e255706a8a5afe1fd3f"
+ hash: "dcb56a485e1fe925ecef6224e55ba5b6"
}
Frame {
msec: 1376
- hash: "92cb490b081bccedf0bbdee86dbc50ed"
+ hash: "b8edb8a9b8d4596a8da50fa262929d58"
}
Frame {
msec: 1392
- hash: "1f0a09601474246e94c5ec3763cfa83e"
+ hash: "3eb2b0644f2d6a1b4ebd7516c12330ce"
}
Frame {
msec: 1408
- hash: "73f1a5c57a2c96e18ba894a7adb9a014"
+ hash: "21acefe69cf2c544102fccdccbbbccd5"
}
Frame {
msec: 1424
- hash: "8aa130cf4b2706afc8d582ee4c5f510d"
+ hash: "831593033242f154f89d68f1aa4b6bd3"
}
Frame {
msec: 1440
- hash: "1c0de0f1f4aa5f44bdf774169296487d"
+ hash: "8f23b9a029fcafe05d1369cc39ec703e"
}
Frame {
msec: 1456
- hash: "1e9b701ee63effb760e733ac623d75d7"
+ hash: "6c00a41d0250313a26527f7a39e9f7f3"
}
Frame {
msec: 1472
- hash: "c30620b6d5d41937217fa9d3e0bf367d"
+ hash: "395a67b9e40ac34d17969a10f607e8b6"
}
Frame {
msec: 1488
- hash: "1f96e1da113d4a6cdb7179771ef7967d"
+ hash: "ac1392862993cecbc0f7839b4e2c5ca2"
}
Frame {
msec: 1504
- hash: "aa31458e44ba42a633421e8688a3af7e"
+ hash: "f477222fb643462eeb16813f0b49a93f"
}
Frame {
msec: 1520
- hash: "a7a560c05566d0bbea3f2bf397a0063a"
+ hash: "3064b5af87e3738ee89cbfa408059b89"
}
Frame {
msec: 1536
- hash: "fdd290bc46b86a11afdffb95570d9a67"
+ hash: "ac33fc35a69fa9f8d875bdcd53cc0f75"
}
Frame {
msec: 1552
- hash: "46574d7bfc15bc5b9124eb0e12741724"
+ hash: "cbaaed4b19f9aecdcba8d58546d63d4d"
}
Frame {
msec: 1568
- hash: "aed2015031da6c7e5064fe5fcd1e86e3"
+ hash: "d2f75520a698dd21702b11efab5e3024"
}
Frame {
msec: 1584
- hash: "dea39f30e686771ca516ac32e3dc4cb0"
+ hash: "d7560c4cc6ed89fda71e18d6c6c16ef4"
}
Frame {
msec: 1600
- hash: "4a9839f52a7ee6732c5e18c0d67534be"
+ hash: "147faf9f698d64be191bed1abe09a592"
}
Frame {
msec: 1616
- hash: "df21723df1031542483684ff92aaf40a"
+ hash: "49026a5a6e677b0f0950ed5e5bebb108"
}
Frame {
msec: 1632
- hash: "53683b7b52d0940aac744f0ef03a4527"
+ hash: "8ef3bc4d3f1965eb6e6407eb26de532f"
}
Frame {
msec: 1648
- hash: "e6177b60c5586e79ca82e1bc7af41737"
+ hash: "86fc1e6fd5f230983d9b0b125d2827ef"
}
Frame {
msec: 1664
- hash: "592a60e226aa6967a8a41bc0e4288583"
+ hash: "dde134ab5ae1b7c1f59d58534c16dd03"
}
Frame {
msec: 1680
- hash: "534512915d800d00350803c3fdcccaf3"
+ hash: "53c13b4ff1241d1faa0a94a0730489f7"
}
Frame {
msec: 1696
- hash: "a01ffd7ab177f850f3d8320da19a03ce"
+ hash: "a46d8748063674d2739e44c7f18e941a"
}
Frame {
msec: 1712
- hash: "15bd47f2c5c8cefe7565790b429aa6a4"
+ hash: "b3ec96287e5df1fe058b7672731e6db9"
}
Frame {
msec: 1728
- hash: "b90692eafe68c2b04057af887617667c"
+ hash: "2e671d9f4266ef19f8632c5efe3897a7"
}
Frame {
msec: 1744
- hash: "edb22bd93a83de0cd3a046ed5a513ece"
+ hash: "a0d17185b04ec84d85fe6349ea9f6b5c"
}
Frame {
msec: 1760
- hash: "f08fa88d05f48c42dd1eba538dc464d4"
+ hash: "4ad02b737bc2ec05f935529914b8b694"
}
Frame {
msec: 1776
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "4c0a7a445520155b5ce590d9566ddde7"
}
Frame {
msec: 1792
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "e4108a09f8d944577d96d4697fa32216"
}
Frame {
msec: 1808
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "8ee31240c836bfd6f18a9d2aac5cb084"
}
Frame {
msec: 1824
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "e58b69388d4b30fa96bfb3ecd7ec4fe6"
}
Frame {
msec: 1840
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "e0fb7420fbdde291be7955476fef7c06"
}
Frame {
msec: 1856
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "d19f92d707cc95007cb77555f2bef31b"
}
Frame {
msec: 1872
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "b1af9c47935dda17e533ac9a4078c66c"
}
Frame {
msec: 1888
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "5fd8ee80a9388ca1ff99fe35a3973eff"
}
Frame {
msec: 1904
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "fac7ddc309b6ae477e8da1d87c56a5f1"
}
Frame {
msec: 1920
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "fbcd446ced1a00cfb0b177c7de5d6e60"
}
Frame {
msec: 1936
@@ -490,239 +490,239 @@ VisualTest {
}
Frame {
msec: 1952
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "6d2827458ac8012bb9468ed453e61331"
}
Frame {
msec: 1968
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "2184f41e9b49f9a58e58dc6843a7741a"
}
Frame {
msec: 1984
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "7bfe1cad3611ccb4c55da75546853b25"
}
Frame {
msec: 2000
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "0c1164b69806cd0281b7aa131d8018eb"
}
Frame {
msec: 2016
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "bb531046028c870de76f0a6cfaf23c31"
}
Frame {
msec: 2032
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2048
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2064
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2080
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2096
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2112
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2128
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2144
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2160
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2176
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2192
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2208
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2224
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2240
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2256
- hash: "ec46803523ee0516ed2c89923ff2ded7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2272
- hash: "6b8b68e359f532729bf25a6851563ad7"
+ hash: "56b9c59557c852242fc02921716b4c29"
}
Frame {
msec: 2288
- hash: "1ee3cf0c3c738a909f1b40b4ef49ac50"
+ hash: "940a8ee9cc80ec237c2a0b3c0284de80"
}
Frame {
msec: 2304
- hash: "71889e0f81eeb252dd91a46af5ce24e7"
+ hash: "39860a23099f80ab2a0c979b77c1e41b"
}
Frame {
msec: 2320
- hash: "99390a696ac524d752672df6f2136fa3"
+ hash: "acc2194a277d69177063d06a5c096bc8"
}
Frame {
msec: 2336
- hash: "00919914f1623bb260e0f99b471aa182"
+ hash: "31cde88c34bcf8540ebf27a0f03d3010"
}
Frame {
msec: 2352
- hash: "64bd7ff1518a3e84c4b40511c5c0ff2d"
+ hash: "203ffd5a6efda14f8d8685a3c294f9de"
}
Frame {
msec: 2368
- hash: "b68da721bf79592e49408b098f72e884"
+ hash: "e9dfff40745663c4938b5d0baa28b5f1"
}
Frame {
msec: 2384
- hash: "6a8ca937b7c961c403ab1662d170c1a5"
+ hash: "1d906fdca4cfd51f371d2203770d2cda"
}
Frame {
msec: 2400
- hash: "3f4034da4cd71738d1130c3baa38cf9b"
+ hash: "2fb9d6175f7350b57350e6ab3707918c"
}
Frame {
msec: 2416
- hash: "97a86d3c04d07508604b46732b121edd"
+ hash: "dbe072fc17ee6bc497efb60354ed3758"
}
Frame {
msec: 2432
- hash: "42e9ab3ed744d1a9a7eb5b7a206f29b3"
+ hash: "27dd91387bbfcb381a3d73e4108f6f82"
}
Frame {
msec: 2448
- hash: "ff86192c1b9c0faabb5563260cb1bff2"
+ hash: "8a3e06d17e657aa659c1185ff761c227"
}
Frame {
msec: 2464
- hash: "e1de0e431b971deb546935b6b2fc78e7"
+ hash: "dd476416c45830b4ad8d02b650af7530"
}
Frame {
msec: 2480
- hash: "b7817a7f15d8e727e25719de8cc7d50a"
+ hash: "7fea8ab086376c3f644e24545f3e5382"
}
Frame {
msec: 2496
- hash: "66772971897fc00d01d067e5fc38f848"
+ hash: "e4e8a1838730f3389a5d6630247cc02e"
}
Frame {
msec: 2512
- hash: "175db8c0324af4c206f9673f0a8d0477"
+ hash: "11cd1b8b7b14913036429c7acc688590"
}
Frame {
msec: 2528
- hash: "f3dea687e0ca335b987b6b3c7d736469"
+ hash: "0ff657aa16a82a86f750c0d56a297fc3"
}
Frame {
msec: 2544
- hash: "44d035dd8e302b75c5a7f98a2005fe75"
+ hash: "e6cf7e1d446652270865857a801f4bc3"
}
Frame {
msec: 2560
- hash: "140cf53cb6873b14e6263537f84b0aa0"
+ hash: "db9021619dd192122857dffd2b25b18d"
}
Frame {
msec: 2576
- hash: "6c9090d4488289e69562747271459d7d"
+ hash: "77772d2e14b85df151085500e45020c2"
}
Frame {
msec: 2592
- hash: "49e92db256f5be8c4e35566eea8fca70"
+ hash: "a49744a9c4c6c1f2392184d13907f404"
}
Frame {
msec: 2608
- hash: "80f41d2eb743ee13fcc486651e310fe2"
+ hash: "2701f2dd7196402e228475ce739fbe5b"
}
Frame {
msec: 2624
- hash: "f581fdcaf30c0efd4518e538e88c2ebf"
+ hash: "71030d54a9a9cf234731d8bfd4985442"
}
Frame {
msec: 2640
- hash: "c028db6753cf60bf587e6c46080a31ea"
+ hash: "c5b6c4bc0b68aff9d589bbc4781c6f4d"
}
Frame {
msec: 2656
- hash: "231b69aa9bdadbaf47cbfbc44a322a51"
+ hash: "3ef8d76256628e782b56b3c6e7d3f7d8"
}
Frame {
msec: 2672
- hash: "f0bcc02aaab3fad2ff53fc2d7541d4aa"
+ hash: "85b0582aa0aaeb72201f2b3284f8fe02"
}
Frame {
msec: 2688
- hash: "80e34154585ba3480e37eaac6bfa396c"
+ hash: "26828739f7a1ed5b7c7823c1cac73b12"
}
Frame {
msec: 2704
- hash: "e1ebf3ba98b2df53ac9f72744034ba6d"
+ hash: "73b85e718117b919b6e3bed087330475"
}
Frame {
msec: 2720
- hash: "b8f749a58888f90ed5cabe7aa2eee1ee"
+ hash: "726deda1001db3f57939a1842c90331d"
}
Frame {
msec: 2736
- hash: "3a78458aa124a331f5b1616be5eea914"
+ hash: "980266e772178b550abb6e24e2a6d659"
}
Frame {
msec: 2752
- hash: "c442c02859bc35a8e5493200e68b1730"
+ hash: "b15bd1cef519d7112edf0f8e97c507ce"
}
Frame {
msec: 2768
- hash: "0cc4d24a1e1fa75a339a5b3dd07f18f3"
+ hash: "1df1a43aed86aedd425846a87c7b5df3"
}
Frame {
msec: 2784
- hash: "0d124bc578058db99e32d58f4b412758"
+ hash: "26cbc843753b51f37a5a84f3b9c7cf56"
}
Frame {
msec: 2800
- hash: "fc174a039606c5457532c9ac27c6faec"
+ hash: "8c3ad31d69f44dc1dbb3bec0476a0cac"
}
Frame {
msec: 2816
- hash: "db5d25d7c01605ec81cdab3e239a1f0f"
+ hash: "9c4243132637e722abee587ae76218c3"
}
Frame {
msec: 2832
- hash: "7dcffdbf9ac992aac0751bed5c38a0eb"
+ hash: "fd99e719c7a6da7c3c309c6a56aeb127"
}
Frame {
msec: 2848
- hash: "b59dc4f39b3e032d5cd34ffca098889f"
+ hash: "4855b585340b25e9cf98c55a9e661a32"
}
Frame {
msec: 2864
- hash: "925d232189a3eee4bae08a8fe86a488b"
+ hash: "d400eb6473515193409d5fe928c17b3b"
}
Frame {
msec: 2880
- hash: "4ab3a889e27de8f45670c240f6d452a6"
+ hash: "d2cabdc2e1b5219242e3768c256cf20a"
}
Frame {
msec: 2896
@@ -730,239 +730,239 @@ VisualTest {
}
Frame {
msec: 2912
- hash: "9b8629b588dcb840fcd32f73f66016ee"
+ hash: "f22d7ebcfecf8ed0aedc10eaf3975a06"
}
Frame {
msec: 2928
- hash: "dca8e45e930314a860f36343f4577738"
+ hash: "71fa22835d65dfd5ded2e3f925a2006f"
}
Frame {
msec: 2944
- hash: "b68f3b38e154b65225211c6a1ca8ddb8"
+ hash: "1fa97a0e9cd3601e830810a397678470"
}
Frame {
msec: 2960
- hash: "d8168aea7962cad60132da9baf66f95c"
+ hash: "3fdb2cf62768af6e89b5fcca4a0fbdcc"
}
Frame {
msec: 2976
- hash: "6f83cd7be71666e08172a2c59e715f2e"
+ hash: "24fa2ed5c9c42eb1013c790c394dccb2"
}
Frame {
msec: 2992
- hash: "f98c68954ed98f340e86c159fcf4f013"
+ hash: "c0b8c6b8d925fac2d5349bab8094ea3a"
}
Frame {
msec: 3008
- hash: "e4692a0e6d82864e9027bcf893e0cf90"
+ hash: "246539b2c38f54cc46ccced49a0964b1"
}
Frame {
msec: 3024
- hash: "ed02ff4d37ad03c0d0d53cf8163ed1c5"
+ hash: "dc27d0928673409db160c25c523b5543"
}
Frame {
msec: 3040
- hash: "fb116353a2ceabae2d93c9aac48727d8"
+ hash: "f6c5694157409606a21455e4ce875e74"
}
Frame {
msec: 3056
- hash: "7b8c99b86838c46db4e756cc039ba045"
+ hash: "3060d75d6ee0d6022d7692aeee90aff2"
}
Frame {
msec: 3072
- hash: "c8d8e194bc957402fe2236b1a472faa6"
+ hash: "0a9bc75adf171200f44892d265f10206"
}
Frame {
msec: 3088
- hash: "f0f3d8c8ac3604cd11b7492fe5ee023e"
+ hash: "30681d928e035188fa25983076b00a24"
}
Frame {
msec: 3104
- hash: "b41cf314e4684423b4708ccd55904d60"
+ hash: "fd007bee6889d3dc8203bf59ff564ba2"
}
Frame {
msec: 3120
- hash: "4f578969386627b6e620e83bad5a6a6c"
+ hash: "559b7f479ae2a2a70288ddde6a18b33e"
}
Frame {
msec: 3136
- hash: "bd9fcfaa4e79f969548af12d072c1ec2"
+ hash: "75d7ce28879ec23f1af3eec3afd94f60"
}
Frame {
msec: 3152
- hash: "a418dc92f8b04fddf95f38bd24825ee6"
+ hash: "f0006440cd257667547b243f69dfdc65"
}
Frame {
msec: 3168
- hash: "4684b3e318a08f0f2331a13143592d18"
+ hash: "9c332ab37fc90bf81cea9eee353352d5"
}
Frame {
msec: 3184
- hash: "1e135a4fd2e7336d8a59ca3497374a3d"
+ hash: "c1313a45a5d85d17b0d82c1b7cb8d4b9"
}
Frame {
msec: 3200
- hash: "d1be76e2c56422b469a9d09e22f62df5"
+ hash: "de50c0522865995d1931f2149494c43c"
}
Frame {
msec: 3216
- hash: "8827523a7f8fa89a56d932102dff7b52"
+ hash: "2a946bfe7a75a519064dc40b27b8b843"
}
Frame {
msec: 3232
- hash: "e12e6b907af5e6feffed0b9e68c71895"
+ hash: "3adb068d2540371cbcd7603a87515716"
}
Frame {
msec: 3248
- hash: "7bc3605f5f241170732aba19ca649896"
+ hash: "11f396595eb3940371a2f8cf497d8184"
}
Frame {
msec: 3264
- hash: "d7da9274f30cacd419f0b0b7c8c8a728"
+ hash: "0c08d7cb54e14120e9f063ed97224e3d"
}
Frame {
msec: 3280
- hash: "154775464235d2a2fb338c27f1490f27"
+ hash: "df9164673edc8c4922696f8999c8d53c"
}
Frame {
msec: 3296
- hash: "1657f65e8759eec3c026262bb271dd1c"
+ hash: "b0fa87c11b235dab2a4361d1c1162866"
}
Frame {
msec: 3312
- hash: "29b4c68846aab3c1dcf4e58861915c33"
+ hash: "746807f46bb1a14890bf7b23ae538a6e"
}
Frame {
msec: 3328
- hash: "fe22b3b991a80b34d6fe12515bfa2fd0"
+ hash: "e856cf08ebebba22e04bcaad1ba75309"
}
Frame {
msec: 3344
- hash: "961343bb9dcc1fbe81b4c20392c28cb9"
+ hash: "edf1ae03a0c60f157f2eb4312226ce3e"
}
Frame {
msec: 3360
- hash: "a2adb3179465e34b517bf906491a1b60"
+ hash: "0c9356e38d0a2b5cbf73b5dd9c76e80d"
}
Frame {
msec: 3376
- hash: "067fb8a2f5043dd4616fb1539e3e9c4a"
+ hash: "eb0fca481da55aeebb0c2ed08e7b2dd0"
}
Frame {
msec: 3392
- hash: "009329915e9027d77218fd83334960ed"
+ hash: "b0c91c68b7c69f3c1124326d8776881c"
}
Frame {
msec: 3408
- hash: "81b05d8aef8152830c6f199d6dd94fd5"
+ hash: "09c76b1c7758e086c1a99198643d29cc"
}
Frame {
msec: 3424
- hash: "b23fa537f88a97490e48fb3a8cd4b507"
+ hash: "294429db9fae8d1a08f34b774ad82124"
}
Frame {
msec: 3440
- hash: "182464f620768efe0253c97cda75d839"
+ hash: "460fbaffccf1cf78b19ab58ed374cec2"
}
Frame {
msec: 3456
- hash: "f1ddbec396cead5d4acf9b65822becb6"
+ hash: "91517b862c3a5c89d8bbc0575b506d3c"
}
Frame {
msec: 3472
- hash: "a73085722d33638517b3f60a16ce9fcd"
+ hash: "4a22f6736a64093790e49a65f3a74923"
}
Frame {
msec: 3488
- hash: "ecce53b0c525834341ee4b3c546e670c"
+ hash: "8ec21c12a499919a46d44b927837db46"
}
Frame {
msec: 3504
- hash: "86f1da737164290a90c1aef9355e2375"
+ hash: "51551a03b495bbd979d2dfbabb33f852"
}
Frame {
msec: 3520
- hash: "722ec874122ad8dcc73820a3a2fb7dca"
+ hash: "700ad73013037cbf10694e712212a799"
}
Frame {
msec: 3536
- hash: "35eb086b11482b752e2c02f1dc4d9099"
+ hash: "bd305b17ba9542ae01a2934bb5c2784f"
}
Frame {
msec: 3552
- hash: "83cf9c0b5d0afd5d3cee4c446274f5c4"
+ hash: "91cf99daf3ff442ea43274ba9ce4bceb"
}
Frame {
msec: 3568
- hash: "e1bbef11fe02adb0756113e1106fe7f1"
+ hash: "7d33d2f8684f1947e01e6144e84ba1f6"
}
Frame {
msec: 3584
- hash: "774c8bb4585954274852d6bb07e64916"
+ hash: "e23e50737d3b70132a0abc91f6900952"
}
Frame {
msec: 3600
- hash: "b0264bcddf313d4e819a608143a86ac9"
+ hash: "4a31ea946579b1a09364d0197aed9943"
}
Frame {
msec: 3616
- hash: "5e3859fd56e5022cbc7831e22447f05d"
+ hash: "d3abb24ef7dea1b32180a34acd1901a0"
}
Frame {
msec: 3632
- hash: "8c2a8b7321d2598b08d483914d4f319c"
+ hash: "19b413d918888038c26109528c8b96da"
}
Frame {
msec: 3648
- hash: "f13913dbc015836e35d5a2ebc94bbeef"
+ hash: "d1a19db5ce841914b6599dd257dc4a6d"
}
Frame {
msec: 3664
- hash: "1309af996f2d7a686f1d9177bc5c9be6"
+ hash: "24e29dd07133ac8c8d51dd6aac07e5ce"
}
Frame {
msec: 3680
- hash: "460b3500b41624486fe8dcfde087d2b5"
+ hash: "db7ae9587b8fcd766e23ffdd8e92cac2"
}
Frame {
msec: 3696
- hash: "de0837d19497021528dc782db4da084a"
+ hash: "0314577dd3497f2cc70e503058ff0c1d"
}
Frame {
msec: 3712
- hash: "18afb8f8e9aa6d4a5db376e26cd9a56d"
+ hash: "ae0dbd41a6e1b7bc87184a059a2b2e61"
}
Frame {
msec: 3728
- hash: "4fc1a8173824c2725160798b7d70aec2"
+ hash: "43c446b3cb9854e35dd21e7da7aa986f"
}
Frame {
msec: 3744
- hash: "87a593f74c946d6af6e31c5a25898766"
+ hash: "675e049ffabad0179f9e94332e457b97"
}
Frame {
msec: 3760
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "ef272f944e95e2c08a856066d2d082c2"
}
Frame {
msec: 3776
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "2d34ec460d21fdeb5daddf9245f8c5cf"
}
Frame {
msec: 3792
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "e94a08e34a7cbd406937c1ffddcbb185"
}
Frame {
msec: 3808
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "22523265a7cbbcfc2182ac1521c8dca8"
}
Frame {
msec: 3824
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "e5f95a7f0d9cd2a4ca54a9e5a6783c8a"
}
Frame {
msec: 3840
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "da7c216b89e1b8f261d2979be8cc61a7"
}
Frame {
msec: 3856
@@ -970,239 +970,239 @@ VisualTest {
}
Frame {
msec: 3872
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "08b3de4eea6ecbe85ab7b104e0571815"
}
Frame {
msec: 3888
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "66b5d695c3fdf6c5fa08241c39c1bff9"
}
Frame {
msec: 3904
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "25f2403ff19575e94010980e02015d64"
}
Frame {
msec: 3920
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "5d8d901818a21965cb54b477592b65a5"
}
Frame {
msec: 3936
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "4cbe77985c9dea4c37f08135ac0e8882"
}
Frame {
msec: 3952
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "8e4dcb566347ecf31c2c7c9262f3c71d"
}
Frame {
msec: 3968
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "71b810ad39750acd84d06526e6ec0767"
}
Frame {
msec: 3984
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 4000
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 4016
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 4032
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "e0aab4701e9039386c6f3f3ae7c86265"
}
Frame {
msec: 4048
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "957cd332444c819fecc4c58149cc6782"
}
Frame {
msec: 4064
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "c7395c37477b4c39bb22d5cb6f0649fe"
}
Frame {
msec: 4080
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "992f62d1cc929e3d92008bf2581c4b4b"
}
Frame {
msec: 4096
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "d8f079d91944796401b86296788a2250"
}
Frame {
msec: 4112
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "7df19eeae14a578760613075e611c9a6"
}
Frame {
msec: 4128
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "998f8f07b857d0c4b740be8a323c6e9a"
}
Frame {
msec: 4144
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "9419aae1cfd8592031dbe26223700ed0"
}
Frame {
msec: 4160
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "fc9d9216599cf59e16f4344cf43c27f5"
}
Frame {
msec: 4176
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e43d779e9edbde4f6e47bf13653464f8"
}
Frame {
msec: 4192
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 4208
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 4224
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 4240
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 4256
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "377a5cd9be5f70975fe74d4de1bf2e70"
}
Frame {
msec: 4272
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 4288
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "afb15151f2bd529817c4a1053dab6e9e"
}
Frame {
msec: 4304
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "113af4013655488c3389abfcfdd25c8a"
}
Frame {
msec: 4320
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "fe92ba0e4552bbfd26858c30ff2dbe78"
}
Frame {
msec: 4336
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "374ca32f24189773ba181d375107a1bd"
}
Frame {
msec: 4352
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "8de169d6ec0a3ecb13b39f93b2ccabbd"
}
Frame {
msec: 4368
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "53c65f25dc0b915d500ef091a45c1b2b"
}
Frame {
msec: 4384
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "846a5983865f87f5058eca31f056d021"
}
Frame {
msec: 4400
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "7243fe48f5cbf172b67ea8658d489a31"
}
Frame {
msec: 4416
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 4432
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 4448
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 4464
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 4480
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 4496
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 4512
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "377a5cd9be5f70975fe74d4de1bf2e70"
}
Frame {
msec: 4528
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 4544
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "bb73c7d58cfca18e8dc8c256ceaa6dba"
}
Frame {
msec: 4560
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "ab2f94beae7447d2c25808e0ff2d3397"
}
Frame {
msec: 4576
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "0e731803c864f753809daa22ff9f76c7"
}
Frame {
msec: 4592
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "48874688b1eb19d67bdf91653f4ce74e"
}
Frame {
msec: 4608
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "3aac3fc7526ad990fcd4a7a4eb162a10"
}
Frame {
msec: 4624
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "72b92331f80f37480ac208b4cce4fdb2"
}
Frame {
msec: 4640
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "4cc992953df7636b9b1b063f87ae36fa"
}
Frame {
msec: 4656
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 4672
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 4688
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 4704
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 4720
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 4736
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 4752
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 4768
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 4784
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "f1cd43dfe4d2846c7f3a8a6af19174d0"
}
Frame {
msec: 4800
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "0eedf77ea428a6ff55507c241588b0a5"
}
Frame {
msec: 4816
@@ -1210,239 +1210,239 @@ VisualTest {
}
Frame {
msec: 4832
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "18625884760a6ecb9b37c609afea0a29"
}
Frame {
msec: 4848
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "2504012907c89b8578b804811bc27d8e"
}
Frame {
msec: 4864
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "559e27ede74f57b25ce889df1c211f4f"
}
Frame {
msec: 4880
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
Frame {
msec: 4896
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 4912
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 4928
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 4944
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 4960
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 4976
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 4992
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 5008
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "377a5cd9be5f70975fe74d4de1bf2e70"
}
Frame {
msec: 5024
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 5040
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "a0bdd440290cc09c86d65b2f74079b97"
}
Frame {
msec: 5056
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "b0f7c2289283b78016394b900fa3dfcb"
}
Frame {
msec: 5072
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "e39fe4762a948bbc412e9b7160f2e439"
}
Frame {
msec: 5088
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "30ae9eaa6b96a00a83faf349d450be3b"
}
Frame {
msec: 5104
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "21178cc44036162ca6f03dd876b38d14"
}
Frame {
msec: 5120
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "cea64ba789fda0f8bdd4ef64321e1c23"
}
Frame {
msec: 5136
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
Frame {
msec: 5152
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 5168
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 5184
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 5200
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 5216
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 5232
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 5248
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 5264
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "377a5cd9be5f70975fe74d4de1bf2e70"
}
Frame {
msec: 5280
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 5296
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "29f1b978ff683024c0be776cc13ad9d4"
}
Frame {
msec: 5312
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "c9660cbe54cb6ada5b6598990d6fb434"
}
Frame {
msec: 5328
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "592fe8fec20701536706623fda7c86d6"
}
Frame {
msec: 5344
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "1ef2067c92e3621fb0e5080f379b551f"
}
Frame {
msec: 5360
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "16e8dbd8061e0a2433300b6ef0396f8a"
}
Frame {
msec: 5376
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "3ed37ab35b06f483b984014266bf59ea"
}
Frame {
msec: 5392
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
Frame {
msec: 5408
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 5424
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 5440
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 5456
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 5472
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 5488
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 5504
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 5520
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 5536
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "231774644c0b105596b37cec696ca287"
}
Frame {
msec: 5552
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "4230f38c6e85a37e809840f46c060e39"
}
Frame {
msec: 5568
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "385e9542036e82f8ca3c390493777c1c"
}
Frame {
msec: 5584
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "814ab09f3c07c6288f774618282d76e2"
}
Frame {
msec: 5600
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "b5d69dff8c1b995749e39e578ea67de4"
}
Frame {
msec: 5616
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "3ed37ab35b06f483b984014266bf59ea"
}
Frame {
msec: 5632
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
Frame {
msec: 5648
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 5664
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 5680
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 5696
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 5712
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 5728
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 5744
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 5760
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "377a5cd9be5f70975fe74d4de1bf2e70"
}
Frame {
msec: 5776
@@ -1450,154 +1450,154 @@ VisualTest {
}
Frame {
msec: 5792
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "b364b0db3d8ded941705179153a0a0d9"
}
Frame {
msec: 5808
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "398a0dc7a138f36dea27dfad528324e4"
}
Frame {
msec: 5824
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "f2bd86fe4532cf41fba7f70d7ee1b36f"
}
Frame {
msec: 5840
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "b905dbc82ff13dee50249268b80067b2"
}
Frame {
msec: 5856
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "02aa690ffbecf879f7f98fecace8e0b6"
}
Frame {
msec: 5872
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "3ed37ab35b06f483b984014266bf59ea"
}
Frame {
msec: 5888
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
Frame {
msec: 5904
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 5920
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 5936
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 5952
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 5968
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 5984
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 6000
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 6016
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 6032
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "2268d0cb4b9a906339c310df870e0f2a"
}
Frame {
msec: 6048
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "d54aa3080e0d7d68cb30efdb2fbb3ff2"
}
Frame {
msec: 6064
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "a53fba1a850cf0f3d1f8d6bc22ae2141"
}
Frame {
msec: 6080
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "64e241917730f914f3866d3bd3f6dc7f"
}
Frame {
msec: 6096
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "02aa690ffbecf879f7f98fecace8e0b6"
}
Frame {
msec: 6112
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "3ed37ab35b06f483b984014266bf59ea"
}
Frame {
msec: 6128
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
Frame {
msec: 6144
- hash: "b692386f34972d80aded2347e64ad2b6"
+ hash: "012078bb87950fdb960e2679a47c4bf6"
}
Frame {
msec: 6160
- hash: "dc65b0a791002efffec05884aa948842"
+ hash: "4fbcfc907361ccbe5ec5d945cb49065e"
}
Frame {
msec: 6176
- hash: "069bab78e29d322894647f81d315184a"
+ hash: "e450930da4d7838a2539a73bb3e8b9e3"
}
Frame {
msec: 6192
- hash: "39c8d804b3caf53845baba4ce98e007d"
+ hash: "d482896bddc89c4739674877be15972c"
}
Frame {
msec: 6208
- hash: "3b477dfd05f07bdf0ba562d6068cafdb"
+ hash: "9cf23658545ac1d744c6c549a86b561d"
}
Frame {
msec: 6224
- hash: "eb84ee75bdbf25dcc32587007f5dc9bd"
+ hash: "79eee90dbf4ba9992dca0e9e4ef02350"
}
Frame {
msec: 6240
- hash: "441d34bff2755e3c30bed80e2bdde69c"
+ hash: "cfc32ce2407a6d8a84fe99393c5d5d9e"
}
Frame {
msec: 6256
- hash: "57e68ec2aa5a21b11d21f388399713e5"
+ hash: "377a5cd9be5f70975fe74d4de1bf2e70"
}
Frame {
msec: 6272
- hash: "4cb9ee1d12b99fb98bedcbcc048867e4"
+ hash: "af880ae1f0eb620a416cea66216f2509"
}
Frame {
msec: 6288
- hash: "3eebd1f4f58210f6b546715997a984c2"
+ hash: "a114b81c8856296087a26129724f9115"
}
Frame {
msec: 6304
- hash: "39eabc07bfcefb2ecd369abf94d706cd"
+ hash: "f989d9099072c32c619061b9cced2a85"
}
Frame {
msec: 6320
- hash: "a0c46402b4700cc2099bdf42c47faf9b"
+ hash: "774663163bef719b9656fbd78316c720"
}
Frame {
msec: 6336
- hash: "3c8b4831583922c7c1c85f227ef2b3dc"
+ hash: "36461d046d6df8b1f85997bc4090d537"
}
Frame {
msec: 6352
- hash: "c92cba3c2825db4293153588c4b7b229"
+ hash: "02aa690ffbecf879f7f98fecace8e0b6"
}
Frame {
msec: 6368
- hash: "e1b0d4cae609f3074fb1ac46c172bf4a"
+ hash: "3ed37ab35b06f483b984014266bf59ea"
}
Frame {
msec: 6384
- hash: "09b76db3e4a95666ba9c37dd89996fa3"
+ hash: "7f788af35c7190d04bdc06cbf921c83d"
}
}
diff --git a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/dynamic.qml b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/dynamic.qml
index b5685d1a9a..a1bb78f3ce 100644
--- a/tests/auto/declarative/qmlvisual/qdeclarativepositioners/dynamic.qml
+++ b/tests/auto/declarative/qmlvisual/qdeclarativepositioners/dynamic.qml
@@ -1,9 +1,9 @@
import QtQuick 1.0
Item {
- property string skip: "Expected to fail until QTBUG-14839 is resolved"
width: 120; height: 60;
property int step: 0
+ property int tickTime: 250;
function tick()
{
step++;
@@ -30,7 +30,7 @@ Item {
//Tests base positioner functionality, so don't need them all.
Column{
- move: Transition{NumberAnimation{properties:"y"}}
+ move: Transition{NumberAnimation{properties:"y"; duration: tickTime}}
Row{
id: row1
height: childrenRect.height
@@ -41,18 +41,19 @@ Item {
Row{
id: row2
height: childrenRect.height
- move: Transition{NumberAnimation{properties:"x"}}
+ move: Transition{NumberAnimation{properties:"x"; duration: tickTime}}
+ add: Transition{NumberAnimation{properties:"x"; duration: tickTime}}
Repeater{
id: repeater
model: 0;
- delegate: Component{ Rectangle { color: "yellow"; width:20; height:20;}}
+ delegate: Component{ Rectangle { color: "yellow"; x:20; width:20; height:20;}}
}
Rectangle{id: r2a; width:20; height:20; color: "red"}
Rectangle{id: r2b; width:20; height:20; color: "green"}
Rectangle{id: r2c; width:20; height:20; color: "blue"}
}
Row{
- move: Transition{NumberAnimation{properties:"x"}}
+ move: Transition{NumberAnimation{properties:"x"; duration: tickTime}}
id: row3
height: childrenRect.height
Rectangle{id: r3a; width:20; height:20; color: "red"}
@@ -61,7 +62,7 @@ Item {
}
}
Timer{
- interval: 250;
+ interval: tickTime;
running: true;
repeat: true;
onTriggered: tick();
diff --git a/tests/auto/declarative/qsganimatedimage/data/colors.gif b/tests/auto/declarative/qsganimatedimage/data/colors.gif
new file mode 100644
index 0000000000..1270bfaa79
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/colors.gif
Binary files differ
diff --git a/tests/auto/declarative/qsganimatedimage/data/colors.qml b/tests/auto/declarative/qsganimatedimage/data/colors.qml
new file mode 100644
index 0000000000..5ccc0148dd
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/colors.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ source: "colors.gif"
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/hearts.gif b/tests/auto/declarative/qsganimatedimage/data/hearts.gif
new file mode 100644
index 0000000000..cfb55f27f5
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/hearts.gif
Binary files differ
diff --git a/tests/auto/declarative/qsganimatedimage/data/hearts.qml b/tests/auto/declarative/qsganimatedimage/data/hearts.qml
new file mode 100644
index 0000000000..717bab430b
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/hearts.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ source: "hearts.gif"
+ playing: false
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/qmldir b/tests/auto/declarative/qsganimatedimage/data/qmldir
new file mode 100644
index 0000000000..ef7c1f44f3
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/qmldir
@@ -0,0 +1 @@
+# No local types
diff --git a/tests/auto/declarative/qsganimatedimage/data/qtbug-16520.qml b/tests/auto/declarative/qsganimatedimage/data/qtbug-16520.qml
new file mode 100644
index 0000000000..da77a4063b
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/qtbug-16520.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 500
+ height: 500
+
+ AnimatedImage {
+ objectName: "anim"
+ anchors.centerIn: parent
+ asynchronous: true
+ opacity: status == AnimatedImage.Ready ? 1 : 0
+
+ Behavior on opacity {
+ NumberAnimation { duration: 1000 }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/stickman.gif b/tests/auto/declarative/qsganimatedimage/data/stickman.gif
new file mode 100644
index 0000000000..7c4cd18687
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/stickman.gif
Binary files differ
diff --git a/tests/auto/declarative/qsganimatedimage/data/stickman.qml b/tests/auto/declarative/qsganimatedimage/data/stickman.qml
new file mode 100644
index 0000000000..a47924de21
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/stickman.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ source: "stickman.gif"
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/stickmanerror1.qml b/tests/auto/declarative/qsganimatedimage/data/stickmanerror1.qml
new file mode 100644
index 0000000000..4f823b3d70
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/stickmanerror1.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ sourceSize: "240x180"
+ source: "stickman.gif"
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/stickmanpause.qml b/tests/auto/declarative/qsganimatedimage/data/stickmanpause.qml
new file mode 100644
index 0000000000..ef771ed56f
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/stickmanpause.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ source: "stickman.gif"
+ paused: true
+ currentFrame: 2
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/stickmanscaled.qml b/tests/auto/declarative/qsganimatedimage/data/stickmanscaled.qml
new file mode 100644
index 0000000000..1ef1f95165
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/stickmanscaled.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ width: 240
+ height: 180
+ source: "stickman.gif"
+}
diff --git a/tests/auto/declarative/qsganimatedimage/data/stickmanstopped.qml b/tests/auto/declarative/qsganimatedimage/data/stickmanstopped.qml
new file mode 100644
index 0000000000..0bf80b8972
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/data/stickmanstopped.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ source: "stickman.gif"
+ playing: false
+}
diff --git a/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro b/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro
new file mode 100644
index 0000000000..f809c22074
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative network
+HEADERS += ../shared/testhttpserver.h
+SOURCES += tst_qsganimatedimage.cpp ../shared/testhttpserver.cpp
+macx:CONFIG -= app_bundle
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsganimatedimage/tst_qsganimatedimage.cpp b/tests/auto/declarative/qsganimatedimage/tst_qsganimatedimage.cpp
new file mode 100644
index 0000000000..24f8cb6114
--- /dev/null
+++ b/tests/auto/declarative/qsganimatedimage/tst_qsganimatedimage.cpp
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgrectangle_p.h>
+#include <private/qsgimage_p.h>
+#include <private/qsganimatedimage_p.h>
+#include <QSignalSpy>
+#include <QtDeclarative/qdeclarativecontext.h>
+
+#include "../shared/testhttpserver.h"
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qsganimatedimage : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsganimatedimage() {}
+
+private slots:
+ void play();
+ void pause();
+ void stopped();
+ void setFrame();
+ void frameCount();
+ void mirror_running();
+ void mirror_notRunning();
+ void mirror_notRunning_data();
+ void remote();
+ void remote_data();
+ void sourceSize();
+ void sourceSizeReadOnly();
+ void invalidSource();
+ void qtbug_16520();
+ void progressAndStatusChanges();
+
+private:
+ QPixmap grabScene(QGraphicsScene *scene, int width, int height);
+};
+
+QPixmap tst_qsganimatedimage::grabScene(QGraphicsScene *scene, int width, int height)
+{
+ QPixmap screenshot(width, height);
+ screenshot.fill();
+ QPainter p_screenshot(&screenshot);
+ scene->render(&p_screenshot, QRect(0, 0, width, height), QRect(0, 0, width, height));
+ return screenshot;
+}
+
+void tst_qsganimatedimage::play()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickman.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+ QVERIFY(anim->isPlaying());
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::pause()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+ QVERIFY(anim->isPlaying());
+ QVERIFY(anim->isPaused());
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::stopped()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanstopped.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+ QVERIFY(!anim->isPlaying());
+ QCOMPARE(anim->currentFrame(), 0);
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::setFrame()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+ QVERIFY(anim->isPlaying());
+ QCOMPARE(anim->currentFrame(), 2);
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::frameCount()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/colors.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+ QVERIFY(anim->isPlaying());
+ QCOMPARE(anim->frameCount(), 3);
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::mirror_running()
+{
+ // test where mirror is set to true after animation has started
+
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/hearts.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+
+ QGraphicsScene scene;
+ int width = anim->property("width").toInt();
+ int height = anim->property("height").toInt();
+ scene.addItem(qobject_cast<QSGItem *>(anim));
+
+ QCOMPARE(anim->currentFrame(), 0);
+ QPixmap frame0 = grabScene(&scene, width, height);
+ anim->setCurrentFrame(1);
+ QPixmap frame1 = grabScene(&scene, width, height);
+
+ anim->setCurrentFrame(0);
+
+ QSignalSpy spy(anim, SIGNAL(frameChanged()));
+ anim->setPlaying(true);
+
+ QTRY_VERIFY(spy.count() == 1); spy.clear();
+ anim->setProperty("mirror", true);
+
+ QCOMPARE(anim->currentFrame(), 1);
+ QPixmap frame1_flipped = grabScene(&scene, width, height);
+
+ QTRY_VERIFY(spy.count() == 1); spy.clear();
+ QCOMPARE(anim->currentFrame(), 0); // animation only has 2 frames, should cycle back to first
+ QPixmap frame0_flipped = grabScene(&scene, width, height);
+
+ QTransform transform;
+ transform.translate(width, 0).scale(-1, 1.0);
+ QPixmap frame0_expected = frame0.transformed(transform);
+ QPixmap frame1_expected = frame1.transformed(transform);
+
+ QCOMPARE(frame0_flipped, frame0_expected);
+ QCOMPARE(frame1_flipped, frame1_expected);
+}
+
+void tst_qsganimatedimage::mirror_notRunning()
+{
+ QFETCH(QUrl, fileUrl);
+
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, fileUrl);
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+
+ QGraphicsScene scene;
+ int width = anim->property("width").toInt();
+ int height = anim->property("height").toInt();
+ scene.addItem(qobject_cast<QSGItem *>(anim));
+ QPixmap screenshot = grabScene(&scene, width, height);
+
+ QTransform transform;
+ transform.translate(width, 0).scale(-1, 1.0);
+ QPixmap expected = screenshot.transformed(transform);
+
+ int frame = anim->currentFrame();
+ bool playing = anim->isPlaying();
+ bool paused = anim->isPlaying();
+
+ anim->setProperty("mirror", true);
+ screenshot = grabScene(&scene, width, height);
+
+ QCOMPARE(screenshot, expected);
+
+ // mirroring should not change the current frame or playing status
+ QCOMPARE(anim->currentFrame(), frame);
+ QCOMPARE(anim->isPlaying(), playing);
+ QCOMPARE(anim->isPaused(), paused);
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::mirror_notRunning_data()
+{
+ QTest::addColumn<QUrl>("fileUrl");
+
+ QTest::newRow("paused") << QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml");
+ QTest::newRow("stopped") << QUrl::fromLocalFile(SRCDIR "/data/stickmanstopped.qml");
+}
+
+void tst_qsganimatedimage::remote()
+{
+ QFETCH(QString, fileName);
+ QFETCH(bool, paused);
+
+ TestHTTPServer server(14449);
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl("http://127.0.0.1:14449/" + fileName));
+ QTRY_VERIFY(component.isReady());
+
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+
+ QTRY_VERIFY(anim->isPlaying());
+ if (paused) {
+ QTRY_VERIFY(anim->isPaused());
+ QCOMPARE(anim->currentFrame(), 2);
+ }
+ QVERIFY(anim->status() != QSGAnimatedImage::Error);
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::sourceSize()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanscaled.qml"));
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+ QCOMPARE(anim->width(),240.0);
+ QCOMPARE(anim->height(),180.0);
+ QCOMPARE(anim->sourceSize(),QSize(160,120));
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::sourceSizeReadOnly()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanerror1.qml"));
+ QVERIFY(component.isError());
+ QCOMPARE(component.errors().at(0).description(), QString("Invalid property assignment: \"sourceSize\" is a read-only property"));
+}
+
+void tst_qsganimatedimage::remote_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<bool>("paused");
+
+ QTest::newRow("playing") << "stickman.qml" << false;
+ QTest::newRow("paused") << "stickmanpause.qml" << true;
+}
+
+void tst_qsganimatedimage::invalidSource()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 1.0\n AnimatedImage { source: \"no-such-file.gif\" }", QUrl::fromLocalFile(""));
+ QVERIFY(component.isReady());
+
+ QTest::ignoreMessage(QtWarningMsg, "file::2:2: QML AnimatedImage: Error Reading Animated Image File file:no-such-file.gif");
+
+ QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
+ QVERIFY(anim);
+
+ QVERIFY(!anim->isPlaying());
+ QVERIFY(!anim->isPaused());
+ QCOMPARE(anim->currentFrame(), 0);
+ QCOMPARE(anim->frameCount(), 0);
+ QTRY_VERIFY(anim->status() == 3);
+}
+
+void tst_qsganimatedimage::qtbug_16520()
+{
+ TestHTTPServer server(14449);
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/qtbug-16520.qml"));
+ QTRY_VERIFY(component.isReady());
+
+ QSGRectangle *root = qobject_cast<QSGRectangle *>(component.create());
+ QVERIFY(root);
+ QSGAnimatedImage *anim = root->findChild<QSGAnimatedImage*>("anim");
+
+ anim->setProperty("source", "http://127.0.0.1:14449/stickman.gif");
+
+ QTRY_VERIFY(anim->opacity() == 0);
+ QTRY_VERIFY(anim->opacity() == 1);
+
+ delete anim;
+}
+
+void tst_qsganimatedimage::progressAndStatusChanges()
+{
+ TestHTTPServer server(14449);
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+
+ QDeclarativeEngine engine;
+ QString componentStr = "import QtQuick 1.0\nAnimatedImage { source: srcImage }";
+ QDeclarativeContext *ctxt = engine.rootContext();
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/stickman.gif"));
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QVERIFY(obj->status() == QSGImage::Ready);
+ QTRY_VERIFY(obj->progress() == 1.0);
+
+ QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &)));
+ QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
+ QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QSGImageBase::Status)));
+
+ // Loading local file
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.gif"));
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+ QTRY_VERIFY(obj->progress() == 1.0);
+ QTRY_COMPARE(sourceSpy.count(), 1);
+ QTRY_COMPARE(progressSpy.count(), 0);
+ QTRY_COMPARE(statusSpy.count(), 0);
+
+ // Loading remote file
+ ctxt->setContextProperty("srcImage", "http://127.0.0.1:14449/stickman.gif");
+ QTRY_VERIFY(obj->status() == QSGImage::Loading);
+ QTRY_VERIFY(obj->progress() == 0.0);
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+ QTRY_VERIFY(obj->progress() == 1.0);
+ QTRY_COMPARE(sourceSpy.count(), 2);
+ QTRY_VERIFY(progressSpy.count() > 1);
+ QTRY_COMPARE(statusSpy.count(), 2);
+
+ ctxt->setContextProperty("srcImage", "");
+ QTRY_VERIFY(obj->status() == QSGImage::Null);
+ QTRY_VERIFY(obj->progress() == 0.0);
+ QTRY_COMPARE(sourceSpy.count(), 3);
+ QTRY_VERIFY(progressSpy.count() > 2);
+ QTRY_COMPARE(statusSpy.count(), 3);
+}
+
+QTEST_MAIN(tst_qsganimatedimage)
+
+#include "tst_qsganimatedimage.moc"
diff --git a/tests/auto/declarative/qsgborderimage/data/colors-round-remote.sci b/tests/auto/declarative/qsgborderimage/data/colors-round-remote.sci
new file mode 100644
index 0000000000..c673bed598
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/data/colors-round-remote.sci
@@ -0,0 +1,7 @@
+border.left:10
+border.top:20
+border.right:30
+border.bottom:40
+horizontalTileRule:Round
+verticalTileRule:Repeat
+source:http://127.0.0.1:14446/colors.png
diff --git a/tests/auto/declarative/qsgborderimage/data/colors-round.sci b/tests/auto/declarative/qsgborderimage/data/colors-round.sci
new file mode 100644
index 0000000000..5d2f49f0e1
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/data/colors-round.sci
@@ -0,0 +1,7 @@
+border.left:10
+border.top:20
+border.right:30
+border.bottom:40
+horizontalTileRule:Round
+verticalTileRule:Repeat
+source:colors.png
diff --git a/tests/auto/declarative/qsgborderimage/data/colors.png b/tests/auto/declarative/qsgborderimage/data/colors.png
new file mode 100644
index 0000000000..dfb62f3d64
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/data/colors.png
Binary files differ
diff --git a/tests/auto/declarative/qsgborderimage/data/heart200.png b/tests/auto/declarative/qsgborderimage/data/heart200.png
new file mode 100644
index 0000000000..5a31ae8f4d
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/data/heart200.png
Binary files differ
diff --git a/tests/auto/declarative/qsgborderimage/data/invalid.sci b/tests/auto/declarative/qsgborderimage/data/invalid.sci
new file mode 100644
index 0000000000..98c72c9bf1
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/data/invalid.sci
@@ -0,0 +1,7 @@
+border.left:10
+border.top:20
+border.down:30
+border.up:40
+horizontalTileRule:Roun
+verticalTileRule:Repea
+source:colors.png
diff --git a/tests/auto/declarative/qsgborderimage/qsgborderimage.pro b/tests/auto/declarative/qsgborderimage/qsgborderimage.pro
new file mode 100644
index 0000000000..7bd8ca24cd
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/qsgborderimage.pro
@@ -0,0 +1,17 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui network
+macx:CONFIG -= app_bundle
+
+HEADERS += ../shared/testhttpserver.h
+SOURCES += tst_qsgborderimage.cpp ../shared/testhttpserver.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgborderimage/tst_qsgborderimage.cpp b/tests/auto/declarative/qsgborderimage/tst_qsgborderimage.cpp
new file mode 100644
index 0000000000..17b9305ac8
--- /dev/null
+++ b/tests/auto/declarative/qsgborderimage/tst_qsgborderimage.cpp
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QTextDocument>
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QDir>
+#include <QGraphicsScene>
+#include <QPainter>
+
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <private/qsgborderimage_p.h>
+#include <private/qsgimagebase_p.h>
+#include <private/qsgscalegrid_p_p.h>
+#include <private/qsgloader_p.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+
+#include "../shared/testhttpserver.h"
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+#define SERVER_PORT 14446
+#define SERVER_ADDR "http://127.0.0.1:14446"
+
+class tst_qsgborderimage : public QObject
+
+{
+ Q_OBJECT
+public:
+ tst_qsgborderimage();
+
+private slots:
+ void noSource();
+ void imageSource();
+ void imageSource_data();
+ void clearSource();
+ void resized();
+ void smooth();
+ void mirror();
+ void tileModes();
+ void sciSource();
+ void sciSource_data();
+ void invalidSciFile();
+ void pendingRemoteRequest();
+ void pendingRemoteRequest_data();
+ void testQtQuick11Attributes();
+ void testQtQuick11Attributes_data();
+
+private:
+ QDeclarativeEngine engine;
+};
+
+tst_qsgborderimage::tst_qsgborderimage()
+{
+}
+
+void tst_qsgborderimage::noSource()
+{
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"\" }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->source(), QUrl());
+ QCOMPARE(obj->width(), 0.);
+ QCOMPARE(obj->height(), 0.);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Stretch);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Stretch);
+
+ delete obj;
+}
+
+void tst_qsgborderimage::imageSource_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<bool>("remote");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/colors.png").toString() << false << "";
+ QTest::newRow("local not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString() << false
+ << "file::2:1: QML BorderImage: Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString();
+ QTest::newRow("remote") << SERVER_ADDR "/colors.png" << true << "";
+ QTest::newRow("remote not found") << SERVER_ADDR "/no-such-file.png" << true
+ << "file::2:1: QML BorderImage: Error downloading " SERVER_ADDR "/no-such-file.png - server replied: Not found";
+}
+
+void tst_qsgborderimage::imageSource()
+{
+ QFETCH(QString, source);
+ QFETCH(bool, remote);
+ QFETCH(QString, error);
+
+ TestHTTPServer *server = 0;
+ if (remote) {
+ server = new TestHTTPServer(SERVER_PORT);
+ QVERIFY(server->isValid());
+ server->serveDirectory(SRCDIR "/data");
+ }
+
+ if (!error.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
+
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" + source + "\" }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+
+ if (remote)
+ QTRY_VERIFY(obj->status() == QSGBorderImage::Loading);
+
+ QCOMPARE(obj->source(), remote ? source : QUrl(source));
+
+ if (error.isEmpty()) {
+ QTRY_VERIFY(obj->status() == QSGBorderImage::Ready);
+ QCOMPARE(obj->width(), 120.);
+ QCOMPARE(obj->height(), 120.);
+ QCOMPARE(obj->sourceSize().width(), 120);
+ QCOMPARE(obj->sourceSize().height(), 120);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Stretch);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Stretch);
+ } else {
+ QTRY_VERIFY(obj->status() == QSGBorderImage::Error);
+ }
+
+ delete obj;
+ delete server;
+}
+
+void tst_qsgborderimage::clearSource()
+{
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: srcImage }";
+ QDeclarativeContext *ctxt = engine.rootContext();
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.png"));
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QVERIFY(obj->status() == QSGBorderImage::Ready);
+ QCOMPARE(obj->width(), 120.);
+ QCOMPARE(obj->height(), 120.);
+
+ ctxt->setContextProperty("srcImage", "");
+ QVERIFY(obj->source().isEmpty());
+ QVERIFY(obj->status() == QSGBorderImage::Null);
+ QCOMPARE(obj->width(), 0.);
+ QCOMPARE(obj->height(), 0.);
+}
+
+void tst_qsgborderimage::resized()
+{
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" + QUrl::fromLocalFile(SRCDIR "/data/colors.png").toString() + "\"; width: 300; height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 300.);
+ QCOMPARE(obj->sourceSize().width(), 120);
+ QCOMPARE(obj->sourceSize().height(), 120);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Stretch);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Stretch);
+
+ delete obj;
+}
+
+void tst_qsgborderimage::smooth()
+{
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" SRCDIR "/data/colors.png\"; smooth: true; width: 300; height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 300.);
+ QCOMPARE(obj->smooth(), true);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Stretch);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Stretch);
+
+ delete obj;
+}
+
+void tst_qsgborderimage::mirror()
+{
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" SRCDIR "/data/heart200.png\"; smooth: true; width: 300; height: 300; border { top: 50; right: 50; bottom: 50; left: 50 } }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+
+ int width = obj->property("width").toInt();
+ int height = obj->property("height").toInt();
+
+ QGraphicsScene scene;
+ scene.addItem(qobject_cast<QSGItem *>(obj));
+ QPixmap screenshot(width, height);
+ screenshot.fill();
+ QPainter p_screenshot(&screenshot);
+ scene.render(&p_screenshot, QRect(0, 0, width, height), QRect(0, 0, width, height));
+
+ QTransform transform;
+ transform.translate(width, 0).scale(-1, 1.0);
+ QPixmap expected = screenshot.transformed(transform);
+
+ obj->setProperty("mirror", true);
+ p_screenshot.fillRect(QRect(0, 0, width, height), Qt::white);
+ scene.render(&p_screenshot, QRect(0, 0, width, height), QRect(0, 0, width, height));
+
+ QCOMPARE(screenshot, expected);
+
+ delete obj;
+}
+
+void tst_qsgborderimage::tileModes()
+{
+ {
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" SRCDIR "/data/colors.png\"; width: 100; height: 300; horizontalTileMode: BorderImage.Repeat; verticalTileMode: BorderImage.Repeat }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 100.);
+ QCOMPARE(obj->height(), 300.);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Repeat);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Repeat);
+
+ delete obj;
+ }
+ {
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" SRCDIR "/data/colors.png\"; width: 300; height: 150; horizontalTileMode: BorderImage.Round; verticalTileMode: BorderImage.Round }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 150.);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Round);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Round);
+
+ delete obj;
+ }
+}
+
+void tst_qsgborderimage::sciSource()
+{
+ QFETCH(QString, source);
+ QFETCH(bool, valid);
+
+ bool remote = source.startsWith("http");
+ TestHTTPServer *server = 0;
+ if (remote) {
+ server = new TestHTTPServer(SERVER_PORT);
+ QVERIFY(server->isValid());
+ server->serveDirectory(SRCDIR "/data");
+ }
+
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" + source + "\"; width: 300; height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+
+ if (remote)
+ QTRY_VERIFY(obj->status() == QSGBorderImage::Loading);
+
+ QCOMPARE(obj->source(), remote ? source : QUrl(source));
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 300.);
+
+ if (valid) {
+ QTRY_VERIFY(obj->status() == QSGBorderImage::Ready);
+ QCOMPARE(obj->border()->left(), 10);
+ QCOMPARE(obj->border()->top(), 20);
+ QCOMPARE(obj->border()->right(), 30);
+ QCOMPARE(obj->border()->bottom(), 40);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Round);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Repeat);
+ } else {
+ QTRY_VERIFY(obj->status() == QSGBorderImage::Error);
+ }
+
+ delete obj;
+ delete server;
+}
+
+void tst_qsgborderimage::sciSource_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<bool>("valid");
+
+ QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/colors-round.sci").toString() << true;
+ QTest::newRow("local not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file.sci").toString() << false;
+ QTest::newRow("remote") << SERVER_ADDR "/colors-round.sci" << true;
+ QTest::newRow("remote image") << SERVER_ADDR "/colors-round-remote.sci" << true;
+ QTest::newRow("remote not found") << SERVER_ADDR "/no-such-file.sci" << false;
+}
+
+void tst_qsgborderimage::invalidSciFile()
+{
+ QTest::ignoreMessage(QtWarningMsg, "QSGGridScaledImage: Invalid tile rule specified. Using Stretch."); // for "Roun"
+ QTest::ignoreMessage(QtWarningMsg, "QSGGridScaledImage: Invalid tile rule specified. Using Stretch."); // for "Repea"
+
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" + QUrl::fromLocalFile(SRCDIR "/data/invalid.sci").toString() +"\"; width: 300; height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 300.);
+ QCOMPARE(obj->status(), QSGImageBase::Error);
+ QCOMPARE(obj->horizontalTileMode(), QSGBorderImage::Stretch);
+ QCOMPARE(obj->verticalTileMode(), QSGBorderImage::Stretch);
+
+ delete obj;
+}
+
+void tst_qsgborderimage::pendingRemoteRequest()
+{
+ QFETCH(QString, source);
+
+ QString componentStr = "import QtQuick 1.0\nBorderImage { source: \"" + source + "\" }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGBorderImage *obj = qobject_cast<QSGBorderImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->status(), QSGBorderImage::Loading);
+
+ // verify no crash
+ // This will cause a delayed "QThread: Destroyed while thread is still running" warning
+ delete obj;
+ QTest::qWait(50);
+}
+
+void tst_qsgborderimage::pendingRemoteRequest_data()
+{
+ QTest::addColumn<QString>("source");
+
+ QTest::newRow("png file") << "http://localhost/none.png";
+ QTest::newRow("sci file") << "http://localhost/none.sci";
+}
+
+void tst_qsgborderimage::testQtQuick11Attributes()
+{
+ QFETCH(QString, code);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ QDeclarativeEngine engine;
+ QObject *obj;
+
+ QDeclarativeComponent valid(&engine);
+ valid.setData("import QtQuick 1.1; BorderImage { " + code.toUtf8() + " }", QUrl(""));
+ obj = valid.create();
+ QVERIFY(obj);
+ QVERIFY(valid.errorString().isEmpty());
+ delete obj;
+
+ QDeclarativeComponent invalid(&engine);
+ invalid.setData("import QtQuick 1.0; BorderImage { " + code.toUtf8() + " }", QUrl(""));
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
+ obj = invalid.create();
+ QCOMPARE(invalid.errorString(), error);
+ delete obj;
+}
+
+void tst_qsgborderimage::testQtQuick11Attributes_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("mirror") << "mirror: true"
+ << "QDeclarativeComponent: Component is not ready"
+ << ":1 \"BorderImage.mirror\" is not available in QtQuick 1.0.\n";
+
+ QTest::newRow("cache") << "cache: true"
+ << "QDeclarativeComponent: Component is not ready"
+ << ":1 \"BorderImage.cache\" is not available in QtQuick 1.0.\n";
+}
+
+QTEST_MAIN(tst_qsgborderimage)
+
+#include "tst_qsgborderimage.moc"
diff --git a/tests/auto/declarative/qsgcanvas/qsgcanvas.pro b/tests/auto/declarative/qsgcanvas/qsgcanvas.pro
new file mode 100644
index 0000000000..126c10b017
--- /dev/null
+++ b/tests/auto/declarative/qsgcanvas/qsgcanvas.pro
@@ -0,0 +1,7 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+SOURCES += tst_qsgcanvas.cpp
+
+macx:CONFIG -= app_bundle
+
+CONFIG += parallel_test
diff --git a/tests/auto/declarative/qsgcanvas/tst_qsgcanvas.cpp b/tests/auto/declarative/qsgcanvas/tst_qsgcanvas.cpp
new file mode 100644
index 0000000000..68552bf88a
--- /dev/null
+++ b/tests/auto/declarative/qsgcanvas/tst_qsgcanvas.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qtest.h>
+#include <QDebug>
+#include <QTouchEvent>
+
+#include "qsgitem.h"
+#include "qsgcanvas.h"
+#include "private/qsgrectangle_p.h"
+#include "../../../shared/util.h"
+
+struct TouchEventData {
+ QEvent::Type type;
+ QWidget *widget;
+ Qt::TouchPointStates states;
+ QList<QTouchEvent::TouchPoint> touchPoints;
+};
+
+static QTouchEvent::TouchPoint makeTouchPoint(QSGItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
+{
+ QPointF last = lastPoint.isNull() ? p : lastPoint;
+
+ QTouchEvent::TouchPoint tp;
+ tp.setPos(p);
+ tp.setLastPos(last);
+ tp.setScenePos(item->mapToScene(p));
+ tp.setLastScenePos(item->mapToScene(last));
+ tp.setScreenPos(item->canvas()->mapToGlobal(tp.scenePos().toPoint()));
+ tp.setLastScreenPos(item->canvas()->mapToGlobal(tp.lastScenePos().toPoint()));
+ return tp;
+}
+
+static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint> &touchPoints)
+{
+ TouchEventData d = { type, w, states, touchPoints };
+ return d;
+}
+
+static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
+{
+ QList<QTouchEvent::TouchPoint> points;
+ points << touchPoint;
+ return makeTouchData(type, w, states, points);
+}
+
+#define COMPARE_TOUCH_POINTS(tp1, tp2) \
+{ \
+ QCOMPARE(tp1.pos(), tp2.pos()); \
+ QCOMPARE(tp1.lastPos(), tp2.lastPos()); \
+ QCOMPARE(tp1.scenePos(), tp2.scenePos()); \
+ QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \
+ QCOMPARE(tp1.screenPos(), tp2.screenPos()); \
+ QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \
+}
+
+#define COMPARE_TOUCH_DATA(d1, d2) \
+{ \
+ QCOMPARE((int)d1.type, (int)d2.type); \
+ QCOMPARE(d1.widget, d2.widget); \
+ QCOMPARE((int)d1.states, (int)d2.states); \
+ QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \
+ for (int i=0; i<d1.touchPoints.count(); i++) { \
+ COMPARE_TOUCH_POINTS(d1.touchPoints[i], d2.touchPoints[i]); \
+ } \
+}
+
+class TestTouchItem : public QSGRectangle
+{
+ Q_OBJECT
+public:
+ TestTouchItem(QSGItem *parent = 0)
+ : QSGRectangle(parent), acceptEvents(true)
+ {
+ border()->setWidth(1);
+ }
+
+ void reset() {
+ acceptEvents = true;
+ setEnabled(true);
+ setOpacity(1.0);
+
+ lastEvent = makeTouchData(QEvent::None, 0, 0, QList<QTouchEvent::TouchPoint>());
+ }
+
+ bool acceptEvents;
+ TouchEventData lastEvent;
+
+protected:
+ virtual void touchEvent(QTouchEvent *event) {
+ if (!acceptEvents) {
+ event->ignore();
+ return;
+ }
+ lastEvent = makeTouchData(event->type(), event->widget(), event->touchPointStates(), event->touchPoints());
+ event->accept();
+ }
+};
+
+
+class ConstantUpdateItem : public QSGItem
+{
+Q_OBJECT
+public:
+ ConstantUpdateItem(QSGItem *parent = 0) : QSGItem(parent), iterations(0) {setFlag(ItemHasContents);}
+
+ int iterations;
+protected:
+ QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
+ iterations++;
+ update();
+ return 0;
+ }
+};
+
+class tst_qsgcanvas : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgcanvas();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void constantUpdates();
+
+ void touchEvent_basic();
+ void touchEvent_propagation();
+ void touchEvent_propagation_data();
+
+ void clearCanvas();
+};
+
+tst_qsgcanvas::tst_qsgcanvas()
+{
+}
+
+void tst_qsgcanvas::initTestCase()
+{
+}
+
+void tst_qsgcanvas::cleanupTestCase()
+{
+}
+
+//If the item calls update inside updatePaintNode, it should schedule another update
+void tst_qsgcanvas::constantUpdates()
+{
+ QSGCanvas canvas;
+ ConstantUpdateItem item(canvas.rootItem());
+ canvas.show();
+ QTRY_VERIFY(item.iterations > 60);
+}
+
+void tst_qsgcanvas::touchEvent_basic()
+{
+ QSGCanvas *canvas = new QSGCanvas;
+ canvas->resize(250, 250);
+ canvas->window()->move(100, 100);
+ canvas->show();
+
+ TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
+ bottomItem->setObjectName("Bottom Item");
+ bottomItem->setSize(QSizeF(150, 150));
+
+ TestTouchItem *middleItem = new TestTouchItem(bottomItem);
+ middleItem->setObjectName("Middle Item");
+ middleItem->setPos(QPointF(50, 50));
+ middleItem->setSize(QSizeF(150, 150));
+
+ TestTouchItem *topItem = new TestTouchItem(middleItem);
+ topItem->setObjectName("Top Item");
+ topItem->setPos(QPointF(50, 50));
+ topItem->setSize(QSizeF(150, 150));
+
+ QPointF pos(10, 10);
+
+ // press single point
+ QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
+ topItem->reset();
+
+ // press multiple points
+ QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint())
+ .press(1, bottomItem->mapToScene(pos).toPoint());
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
+ COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
+ topItem->reset();
+ bottomItem->reset();
+
+ // touch point on top item moves to bottom item, but top item should still receive the event
+ QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
+ QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint());
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
+ makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
+ topItem->reset();
+
+ // touch point on bottom item moves to top item, but bottom item should still receive the event
+ QTest::touchEvent(canvas).press(0, bottomItem->mapToScene(pos).toPoint());
+ QTest::touchEvent(canvas).move(0, topItem->mapToScene(pos).toPoint());
+ QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
+ makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
+ bottomItem->reset();
+
+ // a single stationary press on an item shouldn't cause an event
+ QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
+ QTest::touchEvent(canvas).stationary(0)
+ .press(1, bottomItem->mapToScene(pos).toPoint());
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); // received press only, not stationary
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
+ COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
+ topItem->reset();
+ bottomItem->reset();
+
+ // move touch point from top item to bottom, and release
+ QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
+ QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint());
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
+ makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
+ topItem->reset();
+
+ // release while another point is pressed
+ QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint())
+ .press(1, bottomItem->mapToScene(pos).toPoint());
+ QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint());
+ QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint())
+ .stationary(1);
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
+ makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos))));
+ COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
+ topItem->reset();
+ bottomItem->reset();
+
+ delete topItem;
+ delete middleItem;
+ delete bottomItem;
+ delete canvas;
+}
+
+void tst_qsgcanvas::touchEvent_propagation()
+{
+ QFETCH(bool, acceptEvents);
+ QFETCH(bool, enableItem);
+ QFETCH(qreal, itemOpacity);
+
+ QSGCanvas *canvas = new QSGCanvas;
+ canvas->resize(250, 250);
+ canvas->window()->move(100, 100);
+ canvas->show();
+
+ TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
+ bottomItem->setObjectName("Bottom Item");
+ bottomItem->setSize(QSizeF(150, 150));
+
+ TestTouchItem *middleItem = new TestTouchItem(bottomItem);
+ middleItem->setObjectName("Middle Item");
+ middleItem->setPos(QPointF(50, 50));
+ middleItem->setSize(QSizeF(150, 150));
+
+ TestTouchItem *topItem = new TestTouchItem(middleItem);
+ topItem->setObjectName("Top Item");
+ topItem->setPos(QPointF(50, 50));
+ topItem->setSize(QSizeF(150, 150));
+
+ QPointF pos(10, 10);
+ QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint(); // (10, 10)
+ QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint(); // (60, 60) overlaps with bottomItem
+ QPoint pointInTopItem = topItem->mapToScene(pos).toPoint(); // (110, 110) overlaps with bottom & top items
+
+ // disable topItem
+ topItem->acceptEvents = acceptEvents;
+ topItem->setEnabled(enableItem);
+ topItem->setOpacity(itemOpacity);
+
+ // single touch to top item, should be received by middle item
+ QTest::touchEvent(canvas).press(0, pointInTopItem);
+ QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1);
+ QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
+ COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
+ makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
+
+ // touch top and middle items, middle item should get both events
+ QTest::touchEvent(canvas).press(0, pointInTopItem)
+ .press(1, pointInMiddleItem);
+ QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2);
+ QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
+ COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
+ (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
+ << makeTouchPoint(middleItem, pos) )));
+ middleItem->reset();
+
+ // disable middleItem as well
+ middleItem->acceptEvents = acceptEvents;
+ middleItem->setEnabled(enableItem);
+ middleItem->setOpacity(itemOpacity);
+
+ // touch top and middle items, bottom item should get all events
+ QTest::touchEvent(canvas).press(0, pointInTopItem)
+ .press(1, pointInMiddleItem);
+ QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2);
+ COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
+ (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))
+ << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) )));
+ bottomItem->reset();
+
+ // disable bottom item as well
+ bottomItem->acceptEvents = acceptEvents;
+ bottomItem->setEnabled(enableItem);
+ bottomItem->setOpacity(itemOpacity);
+
+ // no events should be received
+ QTest::touchEvent(canvas).press(0, pointInTopItem)
+ .press(1, pointInMiddleItem)
+ .press(2, pointInBottomItem);
+ QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
+
+ topItem->reset();
+ middleItem->reset();
+ bottomItem->reset();
+
+ // disable middle item, touch on top item
+ middleItem->acceptEvents = acceptEvents;
+ middleItem->setEnabled(enableItem);
+ middleItem->setOpacity(itemOpacity);
+ QTest::touchEvent(canvas).press(0, pointInTopItem);
+ if (!enableItem || itemOpacity == 0) {
+ // middle item is disabled or has 0 opacity, bottom item receives the event
+ QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
+ COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
+ makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))));
+ } else {
+ // middle item ignores event, sends it to the top item (top-most child)
+ QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
+ QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
+ COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
+ makeTouchPoint(topItem, pos)));
+ }
+
+ delete topItem;
+ delete middleItem;
+ delete bottomItem;
+ delete canvas;
+}
+
+void tst_qsgcanvas::touchEvent_propagation_data()
+{
+ QTest::addColumn<bool>("acceptEvents");
+ QTest::addColumn<bool>("enableItem");
+ QTest::addColumn<qreal>("itemOpacity");
+
+ QTest::newRow("disable events") << false << true << 1.0;
+ QTest::newRow("disable item") << true << false << 1.0;
+ QTest::newRow("opacity of 0") << true << true << 0.0;
+}
+
+void tst_qsgcanvas::clearCanvas()
+{
+ QSGCanvas *canvas = new QSGCanvas;
+ QSGItem *item = new QSGItem;
+ item->setParentItem(canvas->rootItem());
+
+ QVERIFY(item->canvas() == canvas);
+
+ delete canvas;
+
+ QVERIFY(item->canvas() == 0);
+
+ delete item;
+}
+
+
+
+QTEST_MAIN(tst_qsgcanvas)
+
+#include "tst_qsgcanvas.moc"
diff --git a/tests/auto/declarative/qsgflickable/data/disabledcontent.qml b/tests/auto/declarative/qsgflickable/data/disabledcontent.qml
new file mode 100644
index 0000000000..f02f08e4ea
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/disabledcontent.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Flickable {
+ width: 100; height: 100
+ contentWidth: 200; contentHeight: 300
+
+ QGraphicsWidget { width: 200; height: 300; enabled: false }
+}
diff --git a/tests/auto/declarative/qsgflickable/data/flickable01.qml b/tests/auto/declarative/qsgflickable/data/flickable01.qml
new file mode 100644
index 0000000000..cbec44bb4f
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/flickable01.qml
@@ -0,0 +1,4 @@
+import QtQuick 2.0
+
+Flickable {
+}
diff --git a/tests/auto/declarative/qsgflickable/data/flickable02.qml b/tests/auto/declarative/qsgflickable/data/flickable02.qml
new file mode 100644
index 0000000000..80caa32da5
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/flickable02.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Flickable {
+ width: 100; height: 100
+ contentWidth: row.width; contentHeight: row.height
+
+ Row {
+ id: row
+ Repeater {
+ model: 4
+ Rectangle { width: 200; height: 300; color: "blue" }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgflickable/data/flickable03.qml b/tests/auto/declarative/qsgflickable/data/flickable03.qml
new file mode 100644
index 0000000000..e34b63b6ac
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/flickable03.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Flickable {
+ width: 100; height: 100
+ contentWidth: column.width; contentHeight: column.height
+
+ Column {
+ id: column
+ Repeater {
+ model: 4
+ Rectangle { width: 200; height: 300; color: "blue" }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgflickable/data/flickable04.qml b/tests/auto/declarative/qsgflickable/data/flickable04.qml
new file mode 100644
index 0000000000..b2f30b84ec
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/flickable04.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Flickable {
+ property bool ok: false
+ function check() {
+ if (column.parent == contentItem)
+ ok = true;
+ }
+
+ width: 100; height: 100
+ contentWidth: column.width; contentHeight: column.height
+ pressDelay: 200; boundsBehavior: Flickable.StopAtBounds; interactive: false
+ maximumFlickVelocity: 2000
+
+ Column {
+ id: column
+ Repeater {
+ model: 4
+ Rectangle { width: 200; height: 300; color: "blue" }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgflickable/data/flickableqgraphicswidget.qml b/tests/auto/declarative/qsgflickable/data/flickableqgraphicswidget.qml
new file mode 100644
index 0000000000..bb8f1eefc6
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/flickableqgraphicswidget.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Flickable {
+ width: 100; height: 100
+
+ QGraphicsWidget { objectName: "widget1"; width: 200; height: 300 }
+}
diff --git a/tests/auto/declarative/qsgflickable/data/nestedPressDelay.qml b/tests/auto/declarative/qsgflickable/data/nestedPressDelay.qml
new file mode 100644
index 0000000000..60dadcc73c
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/nestedPressDelay.qml
@@ -0,0 +1,33 @@
+import QtQuick 2.0
+
+Flickable {
+ property bool pressed: ma.pressed
+ width: 240
+ height: 320
+ contentWidth: 480
+ contentHeight: 320
+ flickableDirection: Flickable.HorizontalFlick
+ pressDelay: 50
+ Flickable {
+ objectName: "innerFlickable"
+ flickableDirection: Flickable.VerticalFlick
+ width: 480
+ height: 320
+ contentWidth: 480
+ contentHeight: 400
+ pressDelay: 10000
+ Rectangle {
+ y: 100
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: 240
+ height: 100
+ color: ma.pressed ? 'blue' : 'green'
+ MouseArea {
+ id: ma
+ objectName: "mouseArea"
+ anchors.fill: parent
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/declarative/qsgflickable/data/resize.qml b/tests/auto/declarative/qsgflickable/data/resize.qml
new file mode 100644
index 0000000000..1a9ef54107
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/resize.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+Rectangle {
+ function resizeContent() {
+ flick.resizeContent(600, 600, Qt.point(100, 100))
+ }
+ function returnToBounds() {
+ flick.returnToBounds()
+ }
+ width: 400
+ height: 360
+ color: "gray"
+
+ Flickable {
+ id: flick
+ objectName: "flick"
+ anchors.fill: parent
+ contentWidth: 300
+ contentHeight: 300
+
+ Rectangle {
+ width: flick.contentWidth
+ height: flick.contentHeight
+ color: "red"
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgflickable/data/wheel.qml b/tests/auto/declarative/qsgflickable/data/wheel.qml
new file mode 100644
index 0000000000..2928bbcd72
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/data/wheel.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400
+ height: 400
+ color: "gray"
+
+ Flickable {
+ id: flick
+ objectName: "flick"
+ anchors.fill: parent
+ contentWidth: 800
+ contentHeight: 800
+
+ Rectangle {
+ width: flick.contentWidth
+ height: flick.contentHeight
+ color: "red"
+ Rectangle {
+ width: 50; height: 50; color: "blue"
+ anchors.centerIn: parent
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgflickable/qsgflickable.pro b/tests/auto/declarative/qsgflickable/qsgflickable.pro
new file mode 100644
index 0000000000..a1ecbe509a
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/qsgflickable.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgflickable.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp b/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp
new file mode 100644
index 0000000000..2cc6b3eac2
--- /dev/null
+++ b/tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp
@@ -0,0 +1,450 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtTest/QSignalSpy>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgflickable_p.h>
+#include <private/qdeclarativevaluetype_p.h>
+#include <math.h>
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qsgflickable : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgflickable();
+
+private slots:
+ void create();
+ void horizontalViewportSize();
+ void verticalViewportSize();
+ void properties();
+ void boundsBehavior();
+ void maximumFlickVelocity();
+ void flickDeceleration();
+ void pressDelay();
+ void disabledContent();
+ void nestedPressDelay();
+ void flickableDirection();
+ void resizeContent();
+ void returnToBounds();
+ void wheel();
+
+private:
+ QDeclarativeEngine engine;
+
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &objectName);
+};
+
+tst_qsgflickable::tst_qsgflickable()
+{
+}
+
+void tst_qsgflickable::create()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/flickable01.qml"));
+ QSGFlickable *obj = qobject_cast<QSGFlickable*>(c.create());
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->isAtXBeginning(), true);
+ QCOMPARE(obj->isAtXEnd(), false);
+ QCOMPARE(obj->isAtYBeginning(), true);
+ QCOMPARE(obj->isAtYEnd(), false);
+ QCOMPARE(obj->contentX(), 0.);
+ QCOMPARE(obj->contentY(), 0.);
+
+ QCOMPARE(obj->horizontalVelocity(), 0.);
+ QCOMPARE(obj->verticalVelocity(), 0.);
+
+ QCOMPARE(obj->isInteractive(), true);
+ QCOMPARE(obj->boundsBehavior(), QSGFlickable::DragAndOvershootBounds);
+ QCOMPARE(obj->pressDelay(), 0);
+ QCOMPARE(obj->maximumFlickVelocity(), 2500.);
+
+ delete obj;
+}
+
+void tst_qsgflickable::horizontalViewportSize()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/flickable02.qml"));
+ QSGFlickable *obj = qobject_cast<QSGFlickable*>(c.create());
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->contentWidth(), 800.);
+ QCOMPARE(obj->contentHeight(), 300.);
+ QCOMPARE(obj->isAtXBeginning(), true);
+ QCOMPARE(obj->isAtXEnd(), false);
+ QCOMPARE(obj->isAtYBeginning(), true);
+ QCOMPARE(obj->isAtYEnd(), false);
+
+ delete obj;
+}
+
+void tst_qsgflickable::verticalViewportSize()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/flickable03.qml"));
+ QSGFlickable *obj = qobject_cast<QSGFlickable*>(c.create());
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->contentWidth(), 200.);
+ QCOMPARE(obj->contentHeight(), 1200.);
+ QCOMPARE(obj->isAtXBeginning(), true);
+ QCOMPARE(obj->isAtXEnd(), false);
+ QCOMPARE(obj->isAtYBeginning(), true);
+ QCOMPARE(obj->isAtYEnd(), false);
+
+ delete obj;
+}
+
+void tst_qsgflickable::properties()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/flickable04.qml"));
+ QSGFlickable *obj = qobject_cast<QSGFlickable*>(c.create());
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->isInteractive(), false);
+ QCOMPARE(obj->boundsBehavior(), QSGFlickable::StopAtBounds);
+ QCOMPARE(obj->pressDelay(), 200);
+ QCOMPARE(obj->maximumFlickVelocity(), 2000.);
+
+ QVERIFY(obj->property("ok").toBool() == false);
+ QMetaObject::invokeMethod(obj, "check");
+ QVERIFY(obj->property("ok").toBool() == true);
+
+ delete obj;
+}
+
+void tst_qsgflickable::boundsBehavior()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; Flickable { boundsBehavior: Flickable.StopAtBounds }", QUrl::fromLocalFile(""));
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(component.create());
+ QSignalSpy spy(flickable, SIGNAL(boundsBehaviorChanged()));
+
+ QVERIFY(flickable);
+ QVERIFY(flickable->boundsBehavior() == QSGFlickable::StopAtBounds);
+
+ flickable->setBoundsBehavior(QSGFlickable::DragAndOvershootBounds);
+ QVERIFY(flickable->boundsBehavior() == QSGFlickable::DragAndOvershootBounds);
+ QCOMPARE(spy.count(),1);
+ flickable->setBoundsBehavior(QSGFlickable::DragAndOvershootBounds);
+ QCOMPARE(spy.count(),1);
+
+ flickable->setBoundsBehavior(QSGFlickable::DragOverBounds);
+ QVERIFY(flickable->boundsBehavior() == QSGFlickable::DragOverBounds);
+ QCOMPARE(spy.count(),2);
+ flickable->setBoundsBehavior(QSGFlickable::DragOverBounds);
+ QCOMPARE(spy.count(),2);
+
+ flickable->setBoundsBehavior(QSGFlickable::StopAtBounds);
+ QVERIFY(flickable->boundsBehavior() == QSGFlickable::StopAtBounds);
+ QCOMPARE(spy.count(),3);
+ flickable->setBoundsBehavior(QSGFlickable::StopAtBounds);
+ QCOMPARE(spy.count(),3);
+}
+
+void tst_qsgflickable::maximumFlickVelocity()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; Flickable { maximumFlickVelocity: 1.0; }", QUrl::fromLocalFile(""));
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(component.create());
+ QSignalSpy spy(flickable, SIGNAL(maximumFlickVelocityChanged()));
+
+ QVERIFY(flickable);
+ QCOMPARE(flickable->maximumFlickVelocity(), 1.0);
+
+ flickable->setMaximumFlickVelocity(2.0);
+ QCOMPARE(flickable->maximumFlickVelocity(), 2.0);
+ QCOMPARE(spy.count(),1);
+ flickable->setMaximumFlickVelocity(2.0);
+ QCOMPARE(spy.count(),1);
+}
+
+void tst_qsgflickable::flickDeceleration()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; Flickable { flickDeceleration: 1.0; }", QUrl::fromLocalFile(""));
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(component.create());
+ QSignalSpy spy(flickable, SIGNAL(flickDecelerationChanged()));
+
+ QVERIFY(flickable);
+ QCOMPARE(flickable->flickDeceleration(), 1.0);
+
+ flickable->setFlickDeceleration(2.0);
+ QCOMPARE(flickable->flickDeceleration(), 2.0);
+ QCOMPARE(spy.count(),1);
+ flickable->setFlickDeceleration(2.0);
+ QCOMPARE(spy.count(),1);
+}
+
+void tst_qsgflickable::pressDelay()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; Flickable { pressDelay: 100; }", QUrl::fromLocalFile(""));
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(component.create());
+ QSignalSpy spy(flickable, SIGNAL(pressDelayChanged()));
+
+ QVERIFY(flickable);
+ QCOMPARE(flickable->pressDelay(), 100);
+
+ flickable->setPressDelay(200);
+ QCOMPARE(flickable->pressDelay(), 200);
+ QCOMPARE(spy.count(),1);
+ flickable->setPressDelay(200);
+ QCOMPARE(spy.count(),1);
+}
+
+// QT-4677
+void tst_qsgflickable::disabledContent()
+{
+ QSGView *canvas = new QSGView;
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/disabledcontent.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(canvas->rootObject());
+ QVERIFY(flickable != 0);
+
+ QVERIFY(flickable->contentX() == 0);
+ QVERIFY(flickable->contentY() == 0);
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50, 50));
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(70,70), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(90,90), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(100,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+
+ QVERIFY(flickable->contentX() < 0);
+ QVERIFY(flickable->contentY() < 0);
+
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(90, 90));
+
+ delete canvas;
+}
+
+
+// QTBUG-17361
+void tst_qsgflickable::nestedPressDelay()
+{
+ QSGView *canvas = new QSGView;
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/nestedPressDelay.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGFlickable *outer = qobject_cast<QSGFlickable*>(canvas->rootObject());
+ QVERIFY(outer != 0);
+
+ QSGFlickable *inner = canvas->rootObject()->findChild<QSGFlickable*>("innerFlickable");
+ QVERIFY(inner != 0);
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(150, 150));
+ // the MouseArea is not pressed immediately
+ QVERIFY(outer->property("pressed").toBool() == false);
+
+ // The outer pressDelay will prevail (50ms, vs. 10sec)
+ // QTRY_VERIFY() has 5sec timeout, so will timeout well within 10sec.
+ QTRY_VERIFY(outer->property("pressed").toBool() == true);
+
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(150, 150));
+
+ delete canvas;
+}
+
+void tst_qsgflickable::flickableDirection()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; Flickable { flickableDirection: Flickable.VerticalFlick; }", QUrl::fromLocalFile(""));
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(component.create());
+ QSignalSpy spy(flickable, SIGNAL(flickableDirectionChanged()));
+
+ QVERIFY(flickable);
+ QCOMPARE(flickable->flickableDirection(), QSGFlickable::VerticalFlick);
+
+ flickable->setFlickableDirection(QSGFlickable::HorizontalAndVerticalFlick);
+ QCOMPARE(flickable->flickableDirection(), QSGFlickable::HorizontalAndVerticalFlick);
+ QCOMPARE(spy.count(),1);
+
+ flickable->setFlickableDirection(QSGFlickable::AutoFlickDirection);
+ QCOMPARE(flickable->flickableDirection(), QSGFlickable::AutoFlickDirection);
+ QCOMPARE(spy.count(),2);
+
+ flickable->setFlickableDirection(QSGFlickable::HorizontalFlick);
+ QCOMPARE(flickable->flickableDirection(), QSGFlickable::HorizontalFlick);
+ QCOMPARE(spy.count(),3);
+
+ flickable->setFlickableDirection(QSGFlickable::HorizontalFlick);
+ QCOMPARE(flickable->flickableDirection(), QSGFlickable::HorizontalFlick);
+ QCOMPARE(spy.count(),3);
+}
+
+// QtQuick 1.1
+void tst_qsgflickable::resizeContent()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/resize.qml"));
+ QSGItem *root = qobject_cast<QSGItem*>(c.create());
+ QSGFlickable *obj = findItem<QSGFlickable>(root, "flick");
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->contentX(), 0.);
+ QCOMPARE(obj->contentY(), 0.);
+ QCOMPARE(obj->contentWidth(), 300.);
+ QCOMPARE(obj->contentHeight(), 300.);
+
+ QMetaObject::invokeMethod(root, "resizeContent");
+
+ QCOMPARE(obj->contentX(), 100.);
+ QCOMPARE(obj->contentY(), 100.);
+ QCOMPARE(obj->contentWidth(), 600.);
+ QCOMPARE(obj->contentHeight(), 600.);
+
+ delete root;
+}
+
+// QtQuick 1.1
+void tst_qsgflickable::returnToBounds()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/resize.qml"));
+ QSGItem *root = qobject_cast<QSGItem*>(c.create());
+ QSGFlickable *obj = findItem<QSGFlickable>(root, "flick");
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->contentX(), 0.);
+ QCOMPARE(obj->contentY(), 0.);
+ QCOMPARE(obj->contentWidth(), 300.);
+ QCOMPARE(obj->contentHeight(), 300.);
+
+ obj->setContentX(100);
+ obj->setContentY(400);
+ QTRY_COMPARE(obj->contentX(), 100.);
+ QTRY_COMPARE(obj->contentY(), 400.);
+
+ QMetaObject::invokeMethod(root, "returnToBounds");
+
+ QTRY_COMPARE(obj->contentX(), 0.);
+ QTRY_COMPARE(obj->contentY(), 0.);
+
+ delete root;
+}
+
+void tst_qsgflickable::wheel()
+{
+ QSGView *canvas = new QSGView;
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/wheel.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGFlickable *flick = canvas->rootObject()->findChild<QSGFlickable*>("flick");
+ QVERIFY(flick != 0);
+
+ {
+ QWheelEvent event(QPoint(200, 200), -120, Qt::NoButton, Qt::NoModifier, Qt::Vertical);
+ event.setAccepted(false);
+ QApplication::sendEvent(canvas, &event);
+ }
+
+ QTRY_VERIFY(flick->contentY() > 0);
+ QVERIFY(flick->contentX() == 0);
+
+ flick->setContentY(0);
+ QVERIFY(flick->contentY() == 0);
+
+ {
+ QWheelEvent event(QPoint(200, 200), -120, Qt::NoButton, Qt::NoModifier, Qt::Horizontal);
+ event.setAccepted(false);
+ QApplication::sendEvent(canvas, &event);
+ }
+
+ QTRY_VERIFY(flick->contentX() > 0);
+ QVERIFY(flick->contentY() == 0);
+
+ delete canvas;
+}
+
+
+template<typename T>
+T *tst_qsgflickable::findItem(QSGItem *parent, const QString &objectName)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ return static_cast<T*>(item);
+ }
+ item = findItem<T>(item, objectName);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+QTEST_MAIN(tst_qsgflickable)
+
+#include "tst_qsgflickable.moc"
diff --git a/tests/auto/declarative/qsgflipable/data/crash.qml b/tests/auto/declarative/qsgflipable/data/crash.qml
new file mode 100644
index 0000000000..a0327918cb
--- /dev/null
+++ b/tests/auto/declarative/qsgflipable/data/crash.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Flipable {
+ transform: Rotation {
+ axis.y: 1
+ axis.z: 0
+ angle: 180
+ }
+}
diff --git a/tests/auto/declarative/qsgflipable/data/flipable-abort.qml b/tests/auto/declarative/qsgflipable/data/flipable-abort.qml
new file mode 100644
index 0000000000..90fc03a5f9
--- /dev/null
+++ b/tests/auto/declarative/qsgflipable/data/flipable-abort.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+
+Rectangle {
+ Flipable {
+ id: flipable
+ }
+ Rectangle {
+ visible: flipable.side == Flipable.Front
+ }
+}
diff --git a/tests/auto/declarative/qsgflipable/data/test-flipable.qml b/tests/auto/declarative/qsgflipable/data/test-flipable.qml
new file mode 100644
index 0000000000..dff6d3fe39
--- /dev/null
+++ b/tests/auto/declarative/qsgflipable/data/test-flipable.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Flipable {
+ id: flipable
+ width: 640; height: 480
+
+ front: Rectangle { anchors.fill: flipable }
+ back: Rectangle { anchors.fill: flipable }
+}
diff --git a/tests/auto/declarative/qsgflipable/qsgflipable.pro b/tests/auto/declarative/qsgflipable/qsgflipable.pro
new file mode 100644
index 0000000000..c87cd4db5c
--- /dev/null
+++ b/tests/auto/declarative/qsgflipable/qsgflipable.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgflipable.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgflipable/tst_qsgflipable.cpp b/tests/auto/declarative/qsgflipable/tst_qsgflipable.cpp
new file mode 100644
index 0000000000..205c2ce607
--- /dev/null
+++ b/tests/auto/declarative/qsgflipable/tst_qsgflipable.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgflipable_p.h>
+#include <private/qdeclarativevaluetype_p.h>
+#include <QFontMetrics>
+#include <private/qsgrectangle_p.h>
+#include <math.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qsgflipable : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgflipable();
+
+private slots:
+ void create();
+ void checkFrontAndBack();
+ void setFrontAndBack();
+
+ // below here task issues
+ void QTBUG_9161_crash();
+ void QTBUG_8474_qgv_abort();
+
+private:
+ QDeclarativeEngine engine;
+};
+
+tst_qsgflipable::tst_qsgflipable()
+{
+}
+
+void tst_qsgflipable::create()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/test-flipable.qml"));
+ QSGFlipable *obj = qobject_cast<QSGFlipable*>(c.create());
+
+ QVERIFY(obj != 0);
+ delete obj;
+}
+
+void tst_qsgflipable::checkFrontAndBack()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/test-flipable.qml"));
+ QSGFlipable *obj = qobject_cast<QSGFlipable*>(c.create());
+
+ QVERIFY(obj != 0);
+ QVERIFY(obj->front() != 0);
+ QVERIFY(obj->back() != 0);
+ delete obj;
+}
+
+void tst_qsgflipable::setFrontAndBack()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/test-flipable.qml"));
+ QSGFlipable *obj = qobject_cast<QSGFlipable*>(c.create());
+
+ QVERIFY(obj != 0);
+ QVERIFY(obj->front() != 0);
+ QVERIFY(obj->back() != 0);
+
+ QString message = c.url().toString() + ":3:1: QML Flipable: front is a write-once property";
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ obj->setFront(new QSGRectangle());
+
+ message = c.url().toString() + ":3:1: QML Flipable: back is a write-once property";
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ obj->setBack(new QSGRectangle());
+ delete obj;
+}
+
+void tst_qsgflipable::QTBUG_9161_crash()
+{
+ QSGView *canvas = new QSGView;
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/crash.qml"));
+ QSGItem *root = canvas->rootObject();
+ QVERIFY(root != 0);
+ canvas->show();
+ delete canvas;
+}
+
+void tst_qsgflipable::QTBUG_8474_qgv_abort()
+{
+ QSGView *canvas = new QSGView;
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/flipable-abort.qml"));
+ QSGItem *root = canvas->rootObject();
+ QVERIFY(root != 0);
+ canvas->show();
+ delete canvas;
+}
+
+QTEST_MAIN(tst_qsgflipable)
+
+#include "tst_qsgflipable.moc"
diff --git a/tests/auto/declarative/qsgfocusscope/data/chain.qml b/tests/auto/declarative/qsgfocusscope/data/chain.qml
new file mode 100644
index 0000000000..4b96662318
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/chain.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ width:300; height:400
+
+ property bool focus1: root.activeFocus
+ property bool focus2: item1.activeFocus
+ property bool focus3: fs1.activeFocus
+ property bool focus4: fs2.activeFocus
+ property bool focus5: theItem.activeFocus
+
+ Item {
+ id: item1
+ FocusScope {
+ id: fs1
+ focus: true
+ FocusScope {
+ id: fs2
+ focus: true
+ Item {
+ id: theItem
+ focus: true
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/forceActiveFocus.qml b/tests/auto/declarative/qsgfocusscope/data/forceActiveFocus.qml
new file mode 100644
index 0000000000..74d2106888
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/forceActiveFocus.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Rectangle {
+ objectName: "root"
+ FocusScope {
+ objectName: "scope"
+ Item {
+ objectName: "item-a1"
+ FocusScope {
+ objectName: "scope-a"
+ Item {
+ objectName: "item-a2"
+ }
+ }
+ }
+ Item {
+ objectName: "item-b1"
+ FocusScope {
+ objectName: "scope-b"
+ Item {
+ objectName: "item-b2"
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/forcefocus.qml b/tests/auto/declarative/qsgfocusscope/data/forcefocus.qml
new file mode 100644
index 0000000000..f41582a951
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/forcefocus.qml
@@ -0,0 +1,81 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 800; height: 600
+
+ FocusScope {
+ focus: true
+
+ FocusScope {
+ id: firstScope
+ objectName: "item0"
+ focus: true
+
+ Rectangle {
+ height: 120; width: 420
+
+ color: "transparent"
+ border.width: 5; border.color: firstScope.activeFocus?"blue":"black"
+
+ Rectangle {
+ id: item1; objectName: "item1"
+ x: 10; y: 10; width: 100; height: 100; color: "green"
+ border.width: 5; border.color: activeFocus?"blue":"black"
+ focus: true
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+
+ Rectangle {
+ id: item2; objectName: "item2"
+ x: 310; y: 10; width: 100; height: 100; color: "green"
+ border.width: 5; border.color: activeFocus?"blue":"black"
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+ }
+ }
+
+ FocusScope {
+ id: secondScope
+ objectName: "item3"
+
+ Rectangle {
+ y: 160; height: 120; width: 420
+
+ color: "transparent"
+ border.width: 5; border.color: secondScope.activeFocus?"blue":"black"
+
+ Rectangle {
+ id: item4; objectName: "item4"
+ x: 10; y: 10; width: 100; height: 100; color: "green"
+ border.width: 5; border.color: activeFocus?"blue":"black"
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+
+ Rectangle {
+ id: item5; objectName: "item5"
+ x: 310; y: 10; width: 100; height: 100; color: "green"
+ border.width: 5; border.color: activeFocus?"blue":"black"
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+ }
+ }
+ }
+ Keys.onDigit4Pressed: item4.focus = true
+ Keys.onDigit5Pressed: item5.forceActiveFocus()
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/qtBug13380.qml b/tests/auto/declarative/qsgfocusscope/data/qtBug13380.qml
new file mode 100644
index 0000000000..29de046b38
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/qtBug13380.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400; height: 400
+
+ property bool showRect: false
+ onShowRectChanged: if (showRect) rect.visible = true
+ property bool noFocus: !fs2.activeFocus
+
+ FocusScope {
+ id: fs1
+ focus: true
+ }
+ Rectangle {
+ id: rect
+ visible: false
+ FocusScope {
+ id: fs2
+ Rectangle {
+ focus: true
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/signalEmission.qml b/tests/auto/declarative/qsgfocusscope/data/signalEmission.qml
new file mode 100644
index 0000000000..999a40c5ad
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/signalEmission.qml
@@ -0,0 +1,33 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 200
+ height: 200
+
+ FocusScope {
+ focus: true
+ Rectangle {
+ objectName: "item1"
+ color: "blue"
+ onFocusChanged: focus ? color = "red" : color = "blue"
+ }
+ Rectangle {
+ objectName: "item2"
+ color: "blue"
+ onFocusChanged: focus ? color = "red" : color = "blue"
+ }
+ }
+
+ FocusScope {
+ Rectangle {
+ objectName: "item3"
+ color: "blue"
+ onFocusChanged: focus ? color = "red" : color = "blue"
+ }
+ Rectangle {
+ objectName: "item4"
+ color: "blue"
+ onFocusChanged: focus ? color = "red" : color = "blue"
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/test.qml b/tests/auto/declarative/qsgfocusscope/data/test.qml
new file mode 100644
index 0000000000..67be29c3fb
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/test.qml
@@ -0,0 +1,77 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "white"
+ width: 800
+ height: 600
+
+ Keys.onDigit9Pressed: console.log("Error - Root")
+
+ FocusScope {
+ id: myScope
+ objectName: "item0"
+ focus: true
+
+ Keys.onDigit9Pressed: console.log("Error - FocusScope")
+
+ Rectangle {
+ height: 120
+ width: 420
+
+ color: "transparent"
+ border.width: 5
+ border.color: myScope.activeFocus?"blue":"black"
+
+ Rectangle {
+ id: item1; objectName: "item1"
+ x: 10; y: 10
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+ Keys.onDigit9Pressed: console.debug("Top Left");
+ KeyNavigation.right: item2
+ focus: true
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+
+ Rectangle {
+ id: item2; objectName: "item2"
+ x: 310; y: 10
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+ KeyNavigation.left: item1
+ Keys.onDigit9Pressed: console.log("Top Right");
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+ }
+ KeyNavigation.down: item3
+ }
+
+ Text { x:100; y:170; text: "Blue border indicates scoped focus\nBlack border indicates NOT scoped focus\nRed box indicates active focus\nUse arrow keys to navigate\nPress \"9\" to print currently focused item" }
+
+ Rectangle {
+ id: item3; objectName: "item3"
+ x: 10; y: 300
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+
+ Keys.onDigit9Pressed: console.log("Bottom Left");
+ KeyNavigation.up: myScope
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/test2.qml b/tests/auto/declarative/qsgfocusscope/data/test2.qml
new file mode 100644
index 0000000000..ad74f3e9f4
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/test2.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "white"
+ width: 800
+ height: 600
+
+ Text { text: "All five rectangles should be red" }
+
+ FocusScope {
+ y: 100
+ focus: true; objectName: "item1"
+ Rectangle { width: 50; height: 50; color: parent.activeFocus?"red":"blue" }
+
+ FocusScope {
+ y: 100
+ focus: true; objectName: "item2"
+ Rectangle { width: 50; height: 50; color: parent.activeFocus?"red":"blue" }
+
+ FocusScope {
+ y: 100
+ focus: true; objectName: "item3"
+ Rectangle { width: 50; height: 50; color: parent.activeFocus?"red":"blue" }
+
+ FocusScope {
+ y: 100
+ focus: true; objectName: "item4"
+ Rectangle { width: 50; height: 50; color: parent.activeFocus?"red":"blue" }
+
+ FocusScope {
+ y: 100
+ focus: true; objectName: "item5"
+ Rectangle { width: 50; height: 50; color: parent.activeFocus?"red":"blue" }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/test3.qml b/tests/auto/declarative/qsgfocusscope/data/test3.qml
new file mode 100644
index 0000000000..537c30816e
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/test3.qml
@@ -0,0 +1,52 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "white"
+ width: 800
+ height: 600
+
+ ListModel {
+ id: model
+ ListElement { name: "1" }
+ ListElement { name: "2" }
+ ListElement { name: "3" }
+ ListElement { name: "4" }
+ ListElement { name: "5" }
+ ListElement { name: "6" }
+ ListElement { name: "7" }
+ ListElement { name: "8" }
+ ListElement { name: "9" }
+ }
+
+ Component {
+ id: verticalDelegate
+ FocusScope {
+ id: root
+ width: 50; height: 50;
+ Keys.onDigit9Pressed: console.log("Error - " + name)
+ Rectangle {
+ focus: true
+ Keys.onDigit9Pressed: console.log(name)
+ width: 50; height: 50;
+ color: root.ListView.isCurrentItem?"red":"green"
+ Text { text: name; anchors.centerIn: parent }
+ }
+ }
+ }
+
+ ListView {
+ width: 800; height: 50; orientation: "Horizontal"
+ focus: true
+ model: model
+ delegate: verticalDelegate
+ preferredHighlightBegin: 100
+ preferredHighlightEnd: 100
+ highlightRangeMode: "StrictlyEnforceRange"
+ }
+
+
+ Text {
+ y: 100; x: 50
+ text: "Currently selected element should be red\nPressing \"9\" should print the number of the currently selected item\nBe sure to scroll all the way to the right, pause, and then all the way to the left."
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/test4.qml b/tests/auto/declarative/qsgfocusscope/data/test4.qml
new file mode 100644
index 0000000000..0eea649f5d
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/test4.qml
@@ -0,0 +1,76 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "white"
+ width: 800
+ height: 600
+
+ Keys.onDigit9Pressed: console.log("Error - Root")
+
+ FocusScope {
+ id: myScope
+
+ Keys.onDigit9Pressed: console.log("Error - FocusScope")
+
+ Rectangle {
+ objectName: "item0"
+ height: 120
+ width: 420
+
+ color: "transparent"
+ border.width: 5
+ border.color: myScope.activeFocus?"blue":"black"
+
+ Rectangle {
+ id: item1; objectName: "item1"
+ x: 10; y: 10
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+ Keys.onDigit9Pressed: console.log("Error - Top Left");
+ KeyNavigation.right: item2
+ focus: true
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+
+ Rectangle {
+ id: item2; objectName: "item2"
+ x: 310; y: 10
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+ KeyNavigation.left: item1
+ Keys.onDigit9Pressed: console.log("Error - Top Right");
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+ }
+ KeyNavigation.down: item3
+ }
+
+ Text { x:100; y:170; text: "There should be no blue borders, or red squares.\nPressing \"9\" should do nothing.\nArrow keys should have no effect." }
+
+ Rectangle {
+ id: item3; objectName: "item3"
+ x: 10; y: 300
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+
+ Keys.onDigit9Pressed: console.log("Error - Bottom Left");
+ KeyNavigation.up: myScope
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+
+}
diff --git a/tests/auto/declarative/qsgfocusscope/data/test5.qml b/tests/auto/declarative/qsgfocusscope/data/test5.qml
new file mode 100644
index 0000000000..9c37cd1303
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/data/test5.qml
@@ -0,0 +1,84 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "white"
+ width: 800
+ height: 600
+
+ Keys.onReturnPressed: console.log("Error - Root")
+
+ FocusScope {
+ id: myScope
+ objectName: "item0"
+ focus: true
+
+ Keys.onReturnPressed: console.log("Error - FocusScope")
+
+ Rectangle {
+ height: 120
+ width: 420
+
+ color: "transparent"
+ border.width: 5
+ border.color: myScope.activeFocus?"blue":"black"
+
+ Rectangle {
+ x: 10; y: 10
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: item1.activeFocus?"blue":"black"
+ }
+
+ TextEdit {
+ id: item1; objectName: "item1"
+ x: 20; y: 20
+ width: 90; height: 90
+ color: "white"
+ font.pixelSize: 20
+ Keys.onReturnPressed: console.log("Top Left");
+ KeyNavigation.right: item2
+ focus: true
+ wrapMode: TextEdit.WordWrap
+ text: "Box 1"
+ }
+
+ Rectangle {
+ id: item2; objectName: "item2"
+ x: 310; y: 10
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: activeFocus?"blue":"black"
+ KeyNavigation.left: item1
+ Keys.onReturnPressed: console.log("Top Right");
+
+ Rectangle {
+ width: 50; height: 50; anchors.centerIn: parent
+ color: parent.activeFocus?"red":"transparent"
+ }
+ }
+ }
+ KeyNavigation.down: item3
+ }
+
+ Text { x:100; y:170; text: "Blue border indicates scoped focus\nBlack border indicates NOT scoped focus\nRed box or flashing cursor indicates active focus\nUse arrow keys to navigate\nPress Ctrl-Return to print currently focused item" }
+
+ Rectangle {
+ x: 10; y: 300
+ width: 100; height: 100; color: "green"
+ border.width: 5
+ border.color: item3.activeFocus?"blue":"black"
+ }
+
+ TextEdit {
+ id: item3; objectName: "item3"
+ x: 20; y: 310
+ width: 90; height: 90
+ color: "white"
+ font.pixelSize: 20
+ text: "Box 3"
+
+ Keys.onReturnPressed: console.log("Bottom Left");
+ KeyNavigation.up: myScope
+ wrapMode: TextEdit.WordWrap
+ }
+}
diff --git a/tests/auto/declarative/qsgfocusscope/qsgfocusscope.pro b/tests/auto/declarative/qsgfocusscope/qsgfocusscope.pro
new file mode 100644
index 0000000000..b34d1dbf74
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/qsgfocusscope.pro
@@ -0,0 +1,13 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+SOURCES += tst_qsgfocusscope.cpp
+macx:CONFIG -= app_bundle
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
diff --git a/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp b/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp
new file mode 100644
index 0000000000..c793ce91c5
--- /dev/null
+++ b/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp
@@ -0,0 +1,540 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QSignalSpy>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgrectangle_p.h>
+#include <private/qsgtextedit_p.h>
+#include <private/qsgtext_p.h>
+#include <QtDeclarative/private/qsgfocusscope_p.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qsgfocusscope : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgfocusscope() {}
+
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &id);
+
+private slots:
+ void basic();
+ void nested();
+ void noFocus();
+ void textEdit();
+ void forceFocus();
+ void noParentFocus();
+ void signalEmission();
+ void qtBug13380();
+ void forceActiveFocus();
+};
+
+/*
+ Find an item with the specified id.
+*/
+template<typename T>
+T *tst_qsgfocusscope::findItem(QSGItem *parent, const QString &objectName)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ QList<QSGItem *> children = parent->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QSGItem *item = children.at(i);
+ if (item) {
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ return static_cast<T*>(item);
+ }
+ item = findItem<T>(item, objectName);
+ if (item)
+ return static_cast<T*>(item);
+ }
+ }
+ return 0;
+}
+
+void tst_qsgfocusscope::basic()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/test.qml"));
+
+ QSGFocusScope *item0 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item0"));
+ QSGRectangle *item1 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item1"));
+ QSGRectangle *item2 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item2"));
+ QSGRectangle *item3 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item3"));
+ QVERIFY(item0 != 0);
+ QVERIFY(item1 != 0);
+ QVERIFY(item2 != 0);
+ QVERIFY(item3 != 0);
+
+ view->show();
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVERIFY(view->hasFocus());
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == true);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Right);
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == true);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Down);
+ QVERIFY(item0->hasActiveFocus() == false);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == true);
+
+ delete view;
+}
+
+void tst_qsgfocusscope::nested()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/test2.qml"));
+
+ QSGFocusScope *item1 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item1"));
+ QSGFocusScope *item2 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item2"));
+ QSGFocusScope *item3 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item3"));
+ QSGFocusScope *item4 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item4"));
+ QSGFocusScope *item5 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item5"));
+ QVERIFY(item1 != 0);
+ QVERIFY(item2 != 0);
+ QVERIFY(item3 != 0);
+ QVERIFY(item4 != 0);
+ QVERIFY(item5 != 0);
+
+ view->show();
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVERIFY(view->hasFocus());
+
+ QVERIFY(item1->hasActiveFocus() == true);
+ QVERIFY(item2->hasActiveFocus() == true);
+ QVERIFY(item3->hasActiveFocus() == true);
+ QVERIFY(item4->hasActiveFocus() == true);
+ QVERIFY(item5->hasActiveFocus() == true);
+ delete view;
+}
+
+void tst_qsgfocusscope::noFocus()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/test4.qml"));
+
+ QSGRectangle *item0 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item0"));
+ QSGRectangle *item1 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item1"));
+ QSGRectangle *item2 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item2"));
+ QSGRectangle *item3 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item3"));
+ QVERIFY(item0 != 0);
+ QVERIFY(item1 != 0);
+ QVERIFY(item2 != 0);
+ QVERIFY(item3 != 0);
+
+ view->show();
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVERIFY(view->hasFocus());
+ QVERIFY(item0->hasActiveFocus() == false);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Right);
+ QVERIFY(item0->hasActiveFocus() == false);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Down);
+ QVERIFY(item0->hasActiveFocus() == false);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ delete view;
+}
+
+void tst_qsgfocusscope::textEdit()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/test5.qml"));
+
+ QSGFocusScope *item0 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item0"));
+ QSGTextEdit *item1 = findItem<QSGTextEdit>(view->rootObject(), QLatin1String("item1"));
+ QSGRectangle *item2 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item2"));
+ QSGTextEdit *item3 = findItem<QSGTextEdit>(view->rootObject(), QLatin1String("item3"));
+ QVERIFY(item0 != 0);
+ QVERIFY(item1 != 0);
+ QVERIFY(item2 != 0);
+ QVERIFY(item3 != 0);
+
+ view->show();
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVERIFY(view->hasFocus());
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == true);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Right);
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == true);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Right);
+ QTest::keyClick(view, Qt::Key_Right);
+ QTest::keyClick(view, Qt::Key_Right);
+ QTest::keyClick(view, Qt::Key_Right);
+ QTest::keyClick(view, Qt::Key_Right);
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == true);
+ QVERIFY(item3->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_Down);
+ QVERIFY(item0->hasActiveFocus() == false);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == true);
+
+ delete view;
+}
+
+void tst_qsgfocusscope::forceFocus()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/forcefocus.qml"));
+
+ QSGFocusScope *item0 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item0"));
+ QSGRectangle *item1 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item1"));
+ QSGRectangle *item2 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item2"));
+ QSGFocusScope *item3 = findItem<QSGFocusScope>(view->rootObject(), QLatin1String("item3"));
+ QSGRectangle *item4 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item4"));
+ QSGRectangle *item5 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item5"));
+ QVERIFY(item0 != 0);
+ QVERIFY(item1 != 0);
+ QVERIFY(item2 != 0);
+ QVERIFY(item3 != 0);
+ QVERIFY(item4 != 0);
+ QVERIFY(item5 != 0);
+
+ view->show();
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVERIFY(view->hasFocus());
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == true);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+ QVERIFY(item4->hasActiveFocus() == false);
+ QVERIFY(item5->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_4);
+ QVERIFY(item0->hasActiveFocus() == true);
+ QVERIFY(item1->hasActiveFocus() == true);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == false);
+ QVERIFY(item4->hasActiveFocus() == false);
+ QVERIFY(item5->hasActiveFocus() == false);
+
+ QTest::keyClick(view, Qt::Key_5);
+ QVERIFY(item0->hasActiveFocus() == false);
+ QVERIFY(item1->hasActiveFocus() == false);
+ QVERIFY(item2->hasActiveFocus() == false);
+ QVERIFY(item3->hasActiveFocus() == true);
+ QVERIFY(item4->hasActiveFocus() == false);
+ QVERIFY(item5->hasActiveFocus() == true);
+
+ delete view;
+}
+
+void tst_qsgfocusscope::noParentFocus()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/chain.qml"));
+ QVERIFY(view->rootObject());
+
+ QVERIFY(view->rootObject()->property("focus1") == false);
+ QVERIFY(view->rootObject()->property("focus2") == false);
+ QVERIFY(view->rootObject()->property("focus3") == true);
+ QVERIFY(view->rootObject()->property("focus4") == true);
+ QVERIFY(view->rootObject()->property("focus5") == true);
+
+ delete view;
+}
+
+void tst_qsgfocusscope::signalEmission()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/signalEmission.qml"));
+
+ QSGRectangle *item1 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item1"));
+ QSGRectangle *item2 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item2"));
+ QSGRectangle *item3 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item3"));
+ QSGRectangle *item4 = findItem<QSGRectangle>(view->rootObject(), QLatin1String("item4"));
+ QVERIFY(item1 != 0);
+ QVERIFY(item2 != 0);
+ QVERIFY(item3 != 0);
+ QVERIFY(item4 != 0);
+
+ view->show();
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVariant blue(QColor("blue"));
+ QVariant red(QColor("red"));
+
+ QVERIFY(view->hasFocus());
+ item1->setFocus(true);
+ QCOMPARE(item1->property("color"), red);
+ QCOMPARE(item2->property("color"), blue);
+ QCOMPARE(item3->property("color"), blue);
+ QCOMPARE(item4->property("color"), blue);
+
+ item2->setFocus(true);
+ QCOMPARE(item1->property("color"), blue);
+ QCOMPARE(item2->property("color"), red);
+ QCOMPARE(item3->property("color"), blue);
+ QCOMPARE(item4->property("color"), blue);
+
+ item3->setFocus(true);
+ QCOMPARE(item1->property("color"), blue);
+ QCOMPARE(item2->property("color"), red);
+ QCOMPARE(item3->property("color"), red);
+ QCOMPARE(item4->property("color"), blue);
+
+ item4->setFocus(true);
+ QCOMPARE(item1->property("color"), blue);
+ QCOMPARE(item2->property("color"), red);
+ QCOMPARE(item3->property("color"), blue);
+ QCOMPARE(item4->property("color"), red);
+
+ item4->setFocus(false);
+ QCOMPARE(item1->property("color"), blue);
+ QCOMPARE(item2->property("color"), red);
+ QCOMPARE(item3->property("color"), blue);
+ QCOMPARE(item4->property("color"), blue);
+
+ delete view;
+}
+
+void tst_qsgfocusscope::qtBug13380()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/qtBug13380.qml"));
+
+ view->show();
+ QVERIFY(view->rootObject());
+ qApp->setActiveWindow(view);
+ qApp->processEvents();
+
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(view);
+#endif
+
+ QVERIFY(view->hasFocus());
+ QVERIFY(view->rootObject()->property("noFocus").toBool());
+
+ view->rootObject()->setProperty("showRect", true);
+ QVERIFY(view->rootObject()->property("noFocus").toBool());
+
+ delete view;
+}
+
+void tst_qsgfocusscope::forceActiveFocus()
+{
+ QSGView *view = new QSGView;
+ view->setSource(QUrl::fromLocalFile(SRCDIR "/data/forceActiveFocus.qml"));
+
+ QSGItem *rootObject = view->rootObject();
+ QVERIFY(rootObject);
+
+ QSGItem *scope = findItem<QSGItem>(rootObject, QLatin1String("scope"));
+ QSGItem *itemA1 = findItem<QSGItem>(rootObject, QLatin1String("item-a1"));
+ QSGItem *scopeA = findItem<QSGItem>(rootObject, QLatin1String("scope-a"));
+ QSGItem *itemA2 = findItem<QSGItem>(rootObject, QLatin1String("item-a2"));
+ QSGItem *itemB1 = findItem<QSGItem>(rootObject, QLatin1String("item-b1"));
+ QSGItem *scopeB = findItem<QSGItem>(rootObject, QLatin1String("scope-b"));
+ QSGItem *itemB2 = findItem<QSGItem>(rootObject, QLatin1String("item-b2"));
+
+ QVERIFY(scope);
+ QVERIFY(itemA1);
+ QVERIFY(scopeA);
+ QVERIFY(itemA2);
+ QVERIFY(itemB1);
+ QVERIFY(scopeB);
+ QVERIFY(itemB2);
+
+ QSignalSpy rootSpy(rootObject, SIGNAL(activeFocusChanged(bool)));
+ QSignalSpy scopeSpy(scope, SIGNAL(activeFocusChanged(bool)));
+ QSignalSpy scopeASpy(scopeA, SIGNAL(activeFocusChanged(bool)));
+ QSignalSpy scopeBSpy(scopeB, SIGNAL(activeFocusChanged(bool)));
+
+ // First, walk the focus from item-a1 down to item-a2 and back again
+ itemA1->forceActiveFocus();
+ QVERIFY(itemA1->hasActiveFocus());
+ QVERIFY(!rootObject->hasActiveFocus());
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ scopeA->forceActiveFocus();
+ QVERIFY(!itemA1->hasActiveFocus());
+ QVERIFY(scopeA->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 1);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ itemA2->forceActiveFocus();
+ QVERIFY(!itemA1->hasActiveFocus());
+ QVERIFY(itemA2->hasActiveFocus());
+ QVERIFY(scopeA->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 1);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ scopeA->forceActiveFocus();
+ QVERIFY(!itemA1->hasActiveFocus());
+ QVERIFY(itemA2->hasActiveFocus());
+ QVERIFY(scopeA->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 1);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ itemA1->forceActiveFocus();
+ QVERIFY(itemA1->hasActiveFocus());
+ QVERIFY(!scopeA->hasActiveFocus());
+ QVERIFY(!itemA2->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 2);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ // Then jump back and forth between branch 'a' and 'b'
+ itemB1->forceActiveFocus();
+ QVERIFY(itemB1->hasActiveFocus());
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ scopeA->forceActiveFocus();
+ QVERIFY(!itemA1->hasActiveFocus());
+ QVERIFY(!itemB1->hasActiveFocus());
+ QVERIFY(scopeA->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 3);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ scopeB->forceActiveFocus();
+ QVERIFY(!scopeA->hasActiveFocus());
+ QVERIFY(!itemB1->hasActiveFocus());
+ QVERIFY(scopeB->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 4);
+ QCOMPARE(scopeBSpy.count(), 1);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ itemA2->forceActiveFocus();
+ QVERIFY(!scopeB->hasActiveFocus());
+ QVERIFY(itemA2->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 5);
+ QCOMPARE(scopeBSpy.count(), 2);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ itemB2->forceActiveFocus();
+ QVERIFY(!itemA2->hasActiveFocus());
+ QVERIFY(itemB2->hasActiveFocus());
+ QCOMPARE(scopeASpy.count(), 6);
+ QCOMPARE(scopeBSpy.count(), 3);
+ QCOMPARE(rootSpy.count(), 0);
+ QCOMPARE(scopeSpy.count(), 1);
+
+ delete view;
+}
+
+QTEST_MAIN(tst_qsgfocusscope)
+
+#include "tst_qsgfocusscope.moc"
diff --git a/tests/auto/declarative/qsggridview/data/attachedSignals.qml b/tests/auto/declarative/qsggridview/data/attachedSignals.qml
new file mode 100644
index 0000000000..73c10d8caf
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/attachedSignals.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+GridView {
+ id: view
+ width: 240; height: 320
+
+ property variant addedDelegates: []
+ property int removedDelegateCount
+
+ model: testModel
+
+ cellWidth: delegateWidth; cellHeight: delegateHeight
+
+ delegate: Rectangle {
+ width: delegateWidth; height: delegateHeight
+ border.width: 1
+ GridView.onAdd: {
+ var obj = GridView.view.addedDelegates
+ obj.push(model.name)
+ GridView.view.addedDelegates = obj
+ }
+ GridView.onRemove: {
+ view.removedDelegateCount += 1
+ }
+ }
+}
+
diff --git a/tests/auto/declarative/qsggridview/data/displaygrid.qml b/tests/auto/declarative/qsggridview/data/displaygrid.qml
new file mode 100644
index 0000000000..1da4fe50ac
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/displaygrid.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.color: "blue"
+ Text {
+ text: index
+ }
+ Text {
+ y: 20
+ id: displayText
+ objectName: "displayText"
+ text: display
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ]
+ GridView {
+ id: grid
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ model: testModel
+ delegate: myDelegate
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/footer.qml b/tests/auto/declarative/qsggridview/data/footer.qml
new file mode 100644
index 0000000000..b0d1117287
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/footer.qml
@@ -0,0 +1,40 @@
+import QtQuick 2.0
+
+Rectangle {
+ function changeFooter() {
+ grid.footer = footer2
+ }
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.color: "blue"
+ Text {
+ text: index
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ GridView {
+ id: grid
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ model: testModel
+ delegate: myDelegate
+ footer: Text { objectName: "footer"; text: "Footer"; height: 30 }
+ }
+
+ Component {
+ id: footer2
+ Text { objectName: "footer2"; text: "Footer 2"; height: 20 }
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/gridview-enforcerange.qml b/tests/auto/declarative/qsggridview/data/gridview-enforcerange.qml
new file mode 100644
index 0000000000..2bfe7da78e
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/gridview-enforcerange.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Item {
+ id: wrapper
+ objectName: "wrapper"
+ height: 100
+ width: 100
+ Text {
+ text: index
+ }
+ Text {
+ y: 25
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ y: 50
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ y: 75
+ text: wrapper.y
+ }
+ }
+ }
+
+ Component {
+ id: myHighlight
+ Rectangle {
+ color: "lightsteelblue"
+ }
+ }
+
+ GridView {
+ id: grid
+ objectName: "grid"
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ highlight: myHighlight
+ flow: (testTopToBottom == true) ? GridView.TopToBottom : GridView.LeftToRight
+ layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
+ preferredHighlightBegin: 100
+ preferredHighlightEnd: 100
+ highlightRangeMode: "StrictlyEnforceRange"
+ focus: true
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/gridview-initCurrent.qml b/tests/auto/declarative/qsggridview/data/gridview-initCurrent.qml
new file mode 100644
index 0000000000..c012b4c481
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/gridview-initCurrent.qml
@@ -0,0 +1,52 @@
+import QtQuick 2.0
+
+Rectangle {
+ property int current: grid.currentIndex
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.color: "blue"
+ Text {
+ text: index
+ }
+ Text {
+ x: 40
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ y: 20
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ y: 40
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ]
+ GridView {
+ id: grid
+ objectName: "grid"
+ focus: true
+ width: 240
+ height: 320
+ currentIndex: 35
+ cellWidth: 80
+ cellHeight: 60
+ delegate: myDelegate
+ model: testModel
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/gridview-noCurrent.qml b/tests/auto/declarative/qsggridview/data/gridview-noCurrent.qml
new file mode 100644
index 0000000000..600716e2d4
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/gridview-noCurrent.qml
@@ -0,0 +1,52 @@
+import QtQuick 2.0
+
+Rectangle {
+ property int current: grid.currentIndex
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.color: "blue"
+ Text {
+ text: index
+ }
+ Text {
+ x: 40
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ y: 20
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ y: 40
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ]
+ GridView {
+ id: grid
+ objectName: "grid"
+ focus: true
+ width: 240
+ height: 320
+ currentIndex: -1
+ cellWidth: 80
+ cellHeight: 60
+ delegate: myDelegate
+ model: testModel
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/gridview1.qml b/tests/auto/declarative/qsggridview/data/gridview1.qml
new file mode 100644
index 0000000000..816fa5f2d1
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/gridview1.qml
@@ -0,0 +1,65 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ property bool showHeader: false
+ property bool showFooter: false
+ property int added: -1
+ property variant removed
+
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.color: "blue"
+ Text {
+ text: index
+ }
+ Text {
+ x: 40
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ y: 20
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ y: 40
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ GridView.onAdd: root.added = index
+ GridView.onRemove: root.removed = name
+ }
+ },
+ Component {
+ id: headerFooter
+ Rectangle { width: 30; height: 320; color: "blue" }
+ }
+ ]
+ GridView {
+ id: grid
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ flow: (testTopToBottom == false) ? GridView.LeftToRight : GridView.TopToBottom
+ layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight
+ model: testModel
+ delegate: myDelegate
+ header: root.showHeader ? headerFooter : null
+ footer: root.showFooter ? headerFooter : null
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/gridview2.qml b/tests/auto/declarative/qsggridview/data/gridview2.qml
new file mode 100644
index 0000000000..5fb45a1613
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/gridview2.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+GridView {
+ anchors.fill: parent
+ width: 320; height: 200
+ cellWidth: 100; cellHeight: 100; cacheBuffer: 200; focus: true
+ keyNavigationWraps: true; highlightFollowsCurrentItem: false
+
+ model: ListModel {
+ id: appModel
+ ListElement { lColor: "red" }
+ ListElement { lColor: "yellow" }
+ ListElement { lColor: "green" }
+ ListElement { lColor: "blue" }
+ }
+
+ delegate: Item {
+ width: 100; height: 100
+ Rectangle {
+ color: lColor; x: 4; y: 4
+ width: 92; height: 92
+ }
+ }
+
+ highlight: Rectangle { width: 100; height: 100; color: "black" }
+}
diff --git a/tests/auto/declarative/qsggridview/data/gridview3.qml b/tests/auto/declarative/qsggridview/data/gridview3.qml
new file mode 100644
index 0000000000..a8c1c5a0f7
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/gridview3.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+GridView {
+ anchors.fill: parent
+ width: 320; height: 200
+}
diff --git a/tests/auto/declarative/qsggridview/data/header.qml b/tests/auto/declarative/qsggridview/data/header.qml
new file mode 100644
index 0000000000..f725b683a2
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/header.qml
@@ -0,0 +1,40 @@
+import QtQuick 2.0
+
+Rectangle {
+ function changeHeader() {
+ grid.header = header2
+ }
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.color: "blue"
+ Text {
+ text: index
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ GridView {
+ id: grid
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ model: testModel
+ delegate: myDelegate
+ header: Text { objectName: "header"; text: "Header"; height: 30 }
+ }
+
+ Component {
+ id: header2
+ Text { objectName: "header2"; text: "Header 2"; height: 20 }
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/manual-highlight.qml b/tests/auto/declarative/qsggridview/data/manual-highlight.qml
new file mode 100644
index 0000000000..c2f1d20fb1
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/manual-highlight.qml
@@ -0,0 +1,48 @@
+import QtQuick 2.0
+
+Item {
+
+ ListModel {
+ id: model
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ ListElement {
+ name: "Bob Brown"
+ number: "555 5845"
+ }
+ }
+
+ Component {
+ id: highlight
+ Rectangle {
+ objectName: "highlight"
+ width: 80; height: 80
+ color: "lightsteelblue"; radius: 5
+ y: grid.currentItem.y+5
+ x: grid.currentItem.x+5
+ }
+ }
+
+ GridView {
+ id: grid
+ objectName: "grid"
+ anchors.fill: parent
+ model: model
+ delegate: Text { objectName: "wrapper"; text: name; width: 80; height: 80 }
+
+ highlight: highlight
+ highlightFollowsCurrentItem: false
+ focus: true
+ }
+
+}
diff --git a/tests/auto/declarative/qsggridview/data/mirroring.qml b/tests/auto/declarative/qsggridview/data/mirroring.qml
new file mode 100644
index 0000000000..b9aff501c1
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/mirroring.qml
@@ -0,0 +1,43 @@
+// This example demonstrates how item positioning
+// changes in right-to-left layout direction
+
+import QtQuick 2.0
+
+Rectangle {
+ color: "lightgray"
+ width: 340
+ height: 370
+
+ VisualItemModel {
+ id: itemModel
+ objectName: "itemModel"
+ Rectangle {
+ objectName: "item1"
+ height: 110; width: 120; color: "#FFFEF0"
+ Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item2"
+ height: 130; width: 150; color: "#F0FFF7"
+ Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item3"
+ height: 170; width: 190; color: "#F4F0FF"
+ Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ }
+
+ GridView {
+ id: view
+ objectName: "view"
+ cellWidth: 190
+ cellHeight: 170
+ anchors.fill: parent
+ anchors.bottomMargin: 30
+ model: itemModel
+ highlightRangeMode: "StrictlyEnforceRange"
+ flow: GridView.TopToBottom
+ flickDeceleration: 2000
+ }
+}
diff --git a/tests/auto/declarative/qsggridview/data/propertychangestest.qml b/tests/auto/declarative/qsggridview/data/propertychangestest.qml
new file mode 100644
index 0000000000..97efbe78f5
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/propertychangestest.qml
@@ -0,0 +1,69 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 360; height: 120; color: "white"
+ Component {
+ id: delegate
+ Item {
+ id: wrapper
+ width: 180; height: 40;
+ Column {
+ x: 5; y: 5
+ Text { text: '<b>Name:</b> ' + name }
+ Text { text: '<b>Number:</b> ' + number }
+ }
+ }
+ }
+ Component {
+ id: highlightRed
+ Rectangle {
+ color: "red"
+ radius: 10
+ opacity: 0.5
+ }
+ }
+ GridView {
+ cellWidth:180
+ cellHeight:40
+ objectName: "gridView"
+ anchors.fill: parent
+ model: listModel
+ delegate: delegate
+ highlight: highlightRed
+ focus: true
+ keyNavigationWraps: true
+ cacheBuffer: 10
+ flow: GridView.LeftToRight
+ }
+
+ data:[
+ ListModel {
+ id: listModel
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ },
+ ListModel {
+ objectName: "alternateModel"
+ ListElement {
+ name: "Jack"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Mary"
+ number: "555 3264"
+ }
+ }
+ ]
+}
+
+
diff --git a/tests/auto/declarative/qsggridview/data/setindex.qml b/tests/auto/declarative/qsggridview/data/setindex.qml
new file mode 100644
index 0000000000..ef80f3a2fb
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/data/setindex.qml
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 200
+ height: 200
+ Component {
+ id: appDelegate
+
+ Item {
+ id : wrapper
+ function startupFunction() {
+ if (index == 5) view.currentIndex = index;
+ }
+ Component.onCompleted: startupFunction();
+ width: 30; height: 30
+ Text { text: index }
+ }
+ }
+
+ GridView {
+ id: view
+ objectName: "grid"
+ anchors.fill: parent
+ cellWidth: 30; cellHeight: 30
+ model: 35
+ delegate: appDelegate
+ focus: true
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativedom/qdeclarativedom.pro b/tests/auto/declarative/qsggridview/qsggridview.pro
index 8ac69aac67..d76983c24a 100644
--- a/tests/auto/declarative/qdeclarativedom/qdeclarativedom.pro
+++ b/tests/auto/declarative/qsggridview/qsggridview.pro
@@ -2,7 +2,7 @@ load(qttest_p4)
contains(QT_CONFIG,declarative): QT += declarative
macx:CONFIG -= app_bundle
-SOURCES += tst_qdeclarativedom.cpp
+SOURCES += tst_qsggridview.cpp
symbian: {
importFiles.files = data
diff --git a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp
new file mode 100644
index 0000000000..931ae7ee49
--- /dev/null
+++ b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp
@@ -0,0 +1,2171 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtGui/qstringlistmodel.h>
+#include <QtDeclarative/qsgview.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/private/qsgitem_p.h>
+#include <QtDeclarative/private/qlistmodelinterface_p.h>
+#include <QtDeclarative/private/qsggridview_p.h>
+#include <QtDeclarative/private/qsgtext_p.h>
+#include <QtDeclarative/private/qdeclarativelistmodel_p.h>
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_QSGGridView : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QSGGridView();
+
+private slots:
+ void items();
+ void changed();
+ void inserted();
+ void removed();
+ void clear();
+ void moved();
+ void changeFlow();
+ void currentIndex();
+ void noCurrentIndex();
+ void defaultValues();
+ void properties();
+ void propertyChanges();
+ void componentChanges();
+ void modelChanges();
+ void positionViewAtIndex();
+ void positionViewAtIndex_rightToLeft();
+ void mirroring();
+ void snapping();
+ void resetModel();
+ void enforceRange();
+ void enforceRange_rightToLeft();
+ void QTBUG_8456();
+ void manualHighlight();
+ void footer();
+ void header();
+ void indexAt();
+ void onAdd();
+ void onAdd_data();
+ void onRemove();
+ void onRemove_data();
+ void testQtQuick11Attributes();
+ void testQtQuick11Attributes_data();
+
+private:
+ QSGView *createView();
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &id, int index=-1);
+ template<typename T>
+ QList<T*> findItems(QSGItem *parent, const QString &objectName);
+ void dumpTree(QSGItem *parent, int depth = 0);
+};
+
+class TestModel : public QAbstractListModel
+{
+public:
+ enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
+
+ TestModel(QObject *parent=0) : QAbstractListModel(parent) {
+ QHash<int, QByteArray> roles;
+ roles[Name] = "name";
+ roles[Number] = "number";
+ setRoleNames(roles);
+ }
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
+ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
+ QVariant rv;
+ if (role == Name)
+ rv = list.at(index.row()).first;
+ else if (role == Number)
+ rv = list.at(index.row()).second;
+
+ return rv;
+ }
+
+ int count() const { return rowCount(); }
+ QString name(int index) const { return list.at(index).first; }
+ QString number(int index) const { return list.at(index).second; }
+
+ void addItem(const QString &name, const QString &number) {
+ emit beginInsertRows(QModelIndex(), list.count(), list.count());
+ list.append(QPair<QString,QString>(name, number));
+ emit endInsertRows();
+ }
+
+ void addItems(const QList<QPair<QString, QString> > &items) {
+ emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
+ for (int i=0; i<items.count(); i++)
+ list.append(QPair<QString,QString>(items[i].first, items[i].second));
+ emit endInsertRows();
+ }
+
+ void insertItem(int index, const QString &name, const QString &number) {
+ emit beginInsertRows(QModelIndex(), index, index);
+ list.insert(index, QPair<QString,QString>(name, number));
+ emit endInsertRows();
+ }
+
+ void removeItem(int index) {
+ emit beginRemoveRows(QModelIndex(), index, index);
+ list.removeAt(index);
+ emit endRemoveRows();
+ }
+
+ void removeItems(int index, int count) {
+ emit beginRemoveRows(QModelIndex(), index, index+count-1);
+ while (count--)
+ list.removeAt(index);
+ emit endRemoveRows();
+ }
+
+ void moveItem(int from, int to) {
+ emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
+ list.move(from, to);
+ emit endMoveRows();
+ }
+
+ void modifyItem(int idx, const QString &name, const QString &number) {
+ list[idx] = QPair<QString,QString>(name, number);
+ emit dataChanged(index(idx,0), index(idx,0));
+ }
+
+ void clear() {
+ int count = list.count();
+ emit beginRemoveRows(QModelIndex(), 0, count-1);
+ list.clear();
+ emit endRemoveRows();
+ }
+
+
+private:
+ QList<QPair<QString,QString> > list;
+};
+
+tst_QSGGridView::tst_QSGGridView()
+{
+}
+
+void tst_QSGGridView::items()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+ model.addItem("Billy", "22345");
+ model.addItem("Sam", "2945");
+ model.addItem("Ben", "04321");
+ model.addItem("Jim", "0780");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(gridview->count(), model.count());
+ QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
+
+ for (int i = 0; i < model.count(); ++i) {
+ QSGText *name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ // set an empty model and confirm that items are destroyed
+ TestModel model2;
+ ctxt->setContextProperty("testModel", &model2);
+
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ QTRY_VERIFY(itemCount == 0);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::changed()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+ model.addItem("Billy", "22345");
+ model.addItem("Sam", "2945");
+ model.addItem("Ben", "04321");
+ model.addItem("Jim", "0780");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGFlickable *gridview = findItem<QSGFlickable>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.modifyItem(1, "Will", "9876");
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ delete canvas;
+}
+
+void tst_QSGGridView::inserted()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ TestModel model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.insertItem(1, "Will", "9876");
+
+ QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ // Checks that onAdd is called
+ int added = canvas->rootObject()->property("added").toInt();
+ QTRY_COMPARE(added, 1);
+
+ // Confirm items positioned correctly
+ for (int i = 0; i < model.count(); ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QTRY_COMPARE(item->x(), (i%3)*80.0);
+ QTRY_COMPARE(item->y(), (i/3)*60.0);
+ }
+
+ model.insertItem(0, "Foo", "1111"); // zero index, and current item
+
+ QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
+
+ name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ QTRY_COMPARE(gridview->currentIndex(), 1);
+
+ // Confirm items positioned correctly
+ for (int i = 0; i < model.count(); ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ for (int i = model.count(); i < 30; ++i)
+ model.insertItem(i, "Hello", QString::number(i));
+
+ gridview->setContentY(120);
+
+ // Insert item outside visible area
+ model.insertItem(1, "Hello", "1324");
+
+ QTRY_VERIFY(gridview->contentY() == 120);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::removed()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.removeItem(1);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ // Checks that onRemove is called
+ QString removed = canvas->rootObject()->property("removed").toString();
+ QTRY_COMPARE(removed, QString("Item1"));
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ // Remove first item (which is the current item);
+ model.removeItem(0);
+
+ name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ // Remove items not visible
+ model.removeItem(25);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ // Remove items before visible
+ gridview->setContentY(120);
+ gridview->setCurrentIndex(10);
+
+ // Setting currentIndex above shouldn't cause view to scroll
+ QTRY_COMPARE(gridview->contentY(), 120.0);
+
+ model.removeItem(1);
+
+ // Confirm items positioned correctly
+ for (int i = 6; i < 18; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ // Remove currentIndex
+ QSGItem *oldCurrent = gridview->currentItem();
+ model.removeItem(9);
+
+ QTRY_COMPARE(gridview->currentIndex(), 9);
+ QTRY_VERIFY(gridview->currentItem() != oldCurrent);
+
+ gridview->setContentY(0);
+ // let transitions settle.
+ QTest::qWait(100);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ // remove item outside current view.
+ gridview->setCurrentIndex(32);
+ gridview->setContentY(240);
+
+ model.removeItem(30);
+ QTRY_VERIFY(gridview->currentIndex() == 31);
+
+ // remove current item beyond visible items.
+ gridview->setCurrentIndex(20);
+ gridview->setContentY(0);
+ model.removeItem(20);
+
+ QTRY_COMPARE(gridview->currentIndex(), 20);
+ QTRY_VERIFY(gridview->currentItem() != 0);
+
+ // remove item before current, but visible
+ gridview->setCurrentIndex(8);
+ gridview->setContentY(240);
+ oldCurrent = gridview->currentItem();
+ model.removeItem(6);
+
+ QTRY_COMPARE(gridview->currentIndex(), 7);
+ QTRY_VERIFY(gridview->currentItem() == oldCurrent);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::clear()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QVERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ model.clear();
+
+ QVERIFY(gridview->count() == 0);
+ QVERIFY(gridview->currentItem() == 0);
+ QVERIFY(gridview->contentY() == 0);
+ QVERIFY(gridview->currentIndex() == -1);
+
+ // confirm sanity when adding an item to cleared list
+ model.addItem("New", "1");
+ QVERIFY(gridview->count() == 1);
+ QVERIFY(gridview->currentItem() != 0);
+ QVERIFY(gridview->currentIndex() == 0);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::moved()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.moveItem(1, 8);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ name = findItem<QSGText>(contentItem, "textName", 8);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(8));
+ number = findItem<QSGText>(contentItem, "textNumber", 8);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(8));
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ }
+
+ gridview->setContentY(120);
+
+ // move outside visible area
+ model.moveItem(1, 25);
+
+ // Confirm items positioned correctly and indexes correct
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count()-1;
+ for (int i = 6; i < model.count()-6 && i < itemCount+6; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal((i%3)*80));
+ QTRY_COMPARE(item->y(), qreal((i/3)*60));
+ name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ // move from outside visible into visible
+ model.moveItem(28, 8);
+
+ // Confirm items positioned correctly and indexes correct
+ for (int i = 6; i < model.count()-6 && i < itemCount+6; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->x() == (i%3)*80);
+ QTRY_VERIFY(item->y() == (i/3)*60);
+ name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ // ensure content position is stable
+ gridview->setContentY(0);
+ model.moveItem(10, 0);
+ QTRY_VERIFY(gridview->contentY() == 0);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::currentIndex()
+{
+ TestModel model;
+ for (int i = 0; i < 60; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i));
+
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ QString filename(SRCDIR "/data/gridview-initCurrent.qml");
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QVERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ // current item should be third item
+ QCOMPARE(gridview->currentIndex(), 35);
+ QCOMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 35));
+ QCOMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
+ QCOMPARE(gridview->contentY(), 400.0);
+
+ gridview->moveCurrentIndexRight();
+ QCOMPARE(gridview->currentIndex(), 36);
+ gridview->moveCurrentIndexDown();
+ QCOMPARE(gridview->currentIndex(), 39);
+ gridview->moveCurrentIndexUp();
+ QCOMPARE(gridview->currentIndex(), 36);
+ gridview->moveCurrentIndexLeft();
+ QCOMPARE(gridview->currentIndex(), 35);
+
+ // no wrap
+ gridview->setCurrentIndex(0);
+ QCOMPARE(gridview->currentIndex(), 0);
+ // confirm that the velocity is updated
+ QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
+
+ gridview->moveCurrentIndexUp();
+ QCOMPARE(gridview->currentIndex(), 0);
+
+ gridview->moveCurrentIndexLeft();
+ QCOMPARE(gridview->currentIndex(), 0);
+
+ gridview->setCurrentIndex(model.count()-1);
+ QCOMPARE(gridview->currentIndex(), model.count()-1);
+
+ gridview->moveCurrentIndexRight();
+ QCOMPARE(gridview->currentIndex(), model.count()-1);
+
+ gridview->moveCurrentIndexDown();
+ QCOMPARE(gridview->currentIndex(), model.count()-1);
+
+ // with wrap
+ gridview->setWrapEnabled(true);
+
+ gridview->setCurrentIndex(0);
+ QCOMPARE(gridview->currentIndex(), 0);
+
+ gridview->moveCurrentIndexLeft();
+ QCOMPARE(gridview->currentIndex(), model.count()-1);
+
+ QTRY_COMPARE(gridview->contentY(), 880.0);
+
+ gridview->moveCurrentIndexRight();
+ QCOMPARE(gridview->currentIndex(), 0);
+
+ QTRY_COMPARE(gridview->contentY(), 0.0);
+
+ // Test keys
+ canvas->show();
+ qApp->setActiveWindow(canvas);
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(canvas);
+#endif
+ QTRY_VERIFY(canvas->hasFocus());
+ qApp->processEvents();
+
+ QTest::keyClick(canvas, Qt::Key_Down);
+ QCOMPARE(gridview->currentIndex(), 3);
+
+ QTest::keyClick(canvas, Qt::Key_Up);
+ QCOMPARE(gridview->currentIndex(), 0);
+
+ gridview->setFlow(QSGGridView::TopToBottom);
+
+ qApp->setActiveWindow(canvas);
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(canvas);
+#endif
+ QTRY_VERIFY(canvas->hasFocus());
+ qApp->processEvents();
+
+ QTest::keyClick(canvas, Qt::Key_Right);
+ QCOMPARE(gridview->currentIndex(), 5);
+
+ QTest::keyClick(canvas, Qt::Key_Left);
+ QCOMPARE(gridview->currentIndex(), 0);
+
+ QTest::keyClick(canvas, Qt::Key_Down);
+ QCOMPARE(gridview->currentIndex(), 1);
+
+ QTest::keyClick(canvas, Qt::Key_Up);
+ QCOMPARE(gridview->currentIndex(), 0);
+
+
+ // turn off auto highlight
+ gridview->setHighlightFollowsCurrentItem(false);
+ QVERIFY(gridview->highlightFollowsCurrentItem() == false);
+ QVERIFY(gridview->highlightItem());
+ qreal hlPosX = gridview->highlightItem()->x();
+ qreal hlPosY = gridview->highlightItem()->y();
+
+ gridview->setCurrentIndex(5);
+ QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
+ QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
+
+ // insert item before currentIndex
+ gridview->setCurrentIndex(28);
+ model.insertItem(0, "Foo", "1111");
+ QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
+
+ // check removing highlight by setting currentIndex to -1;
+ gridview->setCurrentIndex(-1);
+
+ QCOMPARE(gridview->currentIndex(), -1);
+ QVERIFY(!gridview->highlightItem());
+ QVERIFY(!gridview->currentItem());
+
+ gridview->setHighlightFollowsCurrentItem(true);
+
+ gridview->setFlow(QSGGridView::LeftToRight);
+ gridview->setLayoutDirection(Qt::RightToLeft);
+
+ qApp->setActiveWindow(canvas);
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(canvas);
+#endif
+ QTRY_VERIFY(canvas->hasFocus());
+ qApp->processEvents();
+
+ gridview->setCurrentIndex(35);
+
+ QTest::keyClick(canvas, Qt::Key_Right);
+ QCOMPARE(gridview->currentIndex(), 34);
+
+ QTest::keyClick(canvas, Qt::Key_Down);
+ QCOMPARE(gridview->currentIndex(), 37);
+
+ QTest::keyClick(canvas, Qt::Key_Up);
+ QCOMPARE(gridview->currentIndex(), 34);
+
+ QTest::keyClick(canvas, Qt::Key_Left);
+ QCOMPARE(gridview->currentIndex(), 35);
+
+
+ // turn off auto highlight
+ gridview->setHighlightFollowsCurrentItem(false);
+ QVERIFY(gridview->highlightFollowsCurrentItem() == false);
+ QVERIFY(gridview->highlightItem());
+ hlPosX = gridview->highlightItem()->x();
+ hlPosY = gridview->highlightItem()->y();
+
+ gridview->setCurrentIndex(5);
+ QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
+ QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
+
+ // insert item before currentIndex
+ gridview->setCurrentIndex(28);
+ model.insertItem(0, "Foo", "1111");
+ QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
+
+ // check removing highlight by setting currentIndex to -1;
+ gridview->setCurrentIndex(-1);
+
+ QCOMPARE(gridview->currentIndex(), -1);
+ QVERIFY(!gridview->highlightItem());
+ QVERIFY(!gridview->currentItem());
+
+ delete canvas;
+}
+
+void tst_QSGGridView::noCurrentIndex()
+{
+ TestModel model;
+ for (int i = 0; i < 60; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i));
+
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ QString filename(SRCDIR "/data/gridview-noCurrent.qml");
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QVERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ // current index should be -1
+ QCOMPARE(gridview->currentIndex(), -1);
+ QVERIFY(!gridview->currentItem());
+ QVERIFY(!gridview->highlightItem());
+ QCOMPARE(gridview->contentY(), 0.0);
+
+ gridview->setCurrentIndex(5);
+ QCOMPARE(gridview->currentIndex(), 5);
+ QVERIFY(gridview->currentItem());
+ QVERIFY(gridview->highlightItem());
+
+ delete canvas;
+}
+
+void tst_QSGGridView::changeFlow()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i));
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly and indexes correct
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal((i%3)*80));
+ QTRY_COMPARE(item->y(), qreal((i/3)*60));
+ QSGText *name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ ctxt->setContextProperty("testTopToBottom", QVariant(true));
+
+ // Confirm items positioned correctly and indexes correct
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal((i/5)*80));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ QSGText *name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ ctxt->setContextProperty("testRightToLeft", QVariant(true));
+
+ // Confirm items positioned correctly and indexes correct
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width()));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ QSGText *name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+ gridview->setContentX(100);
+ QTRY_COMPARE(gridview->contentX(), 100.);
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+ QTRY_COMPARE(gridview->contentX(), 0.);
+
+ // Confirm items positioned correctly and indexes correct
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80));
+ QTRY_COMPARE(item->y(), qreal((i/3)*60));
+ QSGText *name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ delete canvas;
+}
+
+void tst_QSGGridView::defaultValues()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/gridview3.qml"));
+ QSGGridView *obj = qobject_cast<QSGGridView*>(c.create());
+
+ QTRY_VERIFY(obj != 0);
+ QTRY_VERIFY(obj->model() == QVariant());
+ QTRY_VERIFY(obj->delegate() == 0);
+ QTRY_COMPARE(obj->currentIndex(), -1);
+ QTRY_VERIFY(obj->currentItem() == 0);
+ QTRY_COMPARE(obj->count(), 0);
+ QTRY_VERIFY(obj->highlight() == 0);
+ QTRY_VERIFY(obj->highlightItem() == 0);
+ QTRY_COMPARE(obj->highlightFollowsCurrentItem(), true);
+ QTRY_VERIFY(obj->flow() == 0);
+ QTRY_COMPARE(obj->isWrapEnabled(), false);
+ QTRY_COMPARE(obj->cacheBuffer(), 0);
+ QTRY_COMPARE(obj->cellWidth(), 100); //### Should 100 be the default?
+ QTRY_COMPARE(obj->cellHeight(), 100);
+ delete obj;
+}
+
+void tst_QSGGridView::properties()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/gridview2.qml"));
+ QSGGridView *obj = qobject_cast<QSGGridView*>(c.create());
+
+ QTRY_VERIFY(obj != 0);
+ QTRY_VERIFY(obj->model() != QVariant());
+ QTRY_VERIFY(obj->delegate() != 0);
+ QTRY_COMPARE(obj->currentIndex(), 0);
+ QTRY_VERIFY(obj->currentItem() != 0);
+ QTRY_COMPARE(obj->count(), 4);
+ QTRY_VERIFY(obj->highlight() != 0);
+ QTRY_VERIFY(obj->highlightItem() != 0);
+ QTRY_COMPARE(obj->highlightFollowsCurrentItem(), false);
+ QTRY_VERIFY(obj->flow() == 0);
+ QTRY_COMPARE(obj->isWrapEnabled(), true);
+ QTRY_COMPARE(obj->cacheBuffer(), 200);
+ QTRY_COMPARE(obj->cellWidth(), 100);
+ QTRY_COMPARE(obj->cellHeight(), 100);
+ delete obj;
+}
+
+void tst_QSGGridView::propertyChanges()
+{
+ QSGView *canvas = createView();
+ QTRY_VERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
+
+ QSGGridView *gridView = canvas->rootObject()->findChild<QSGGridView*>("gridView");
+ QTRY_VERIFY(gridView);
+
+ QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged()));
+ QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged()));
+ QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged()));
+ QSignalSpy flowSpy(gridView, SIGNAL(flowChanged()));
+
+ QTRY_COMPARE(gridView->isWrapEnabled(), true);
+ QTRY_COMPARE(gridView->cacheBuffer(), 10);
+ QTRY_COMPARE(gridView->flow(), QSGGridView::LeftToRight);
+
+ gridView->setWrapEnabled(false);
+ gridView->setCacheBuffer(3);
+ gridView->setFlow(QSGGridView::TopToBottom);
+
+ QTRY_COMPARE(gridView->isWrapEnabled(), false);
+ QTRY_COMPARE(gridView->cacheBuffer(), 3);
+ QTRY_COMPARE(gridView->flow(), QSGGridView::TopToBottom);
+
+ QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
+ QTRY_COMPARE(cacheBufferSpy.count(),1);
+ QTRY_COMPARE(flowSpy.count(),1);
+
+ gridView->setWrapEnabled(false);
+ gridView->setCacheBuffer(3);
+ gridView->setFlow(QSGGridView::TopToBottom);
+
+ QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
+ QTRY_COMPARE(cacheBufferSpy.count(),1);
+ QTRY_COMPARE(flowSpy.count(),1);
+
+ gridView->setFlow(QSGGridView::LeftToRight);
+ QTRY_COMPARE(gridView->flow(), QSGGridView::LeftToRight);
+
+ gridView->setWrapEnabled(true);
+ gridView->setCacheBuffer(5);
+ gridView->setLayoutDirection(Qt::RightToLeft);
+
+ QTRY_COMPARE(gridView->isWrapEnabled(), true);
+ QTRY_COMPARE(gridView->cacheBuffer(), 5);
+ QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft);
+
+ QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
+ QTRY_COMPARE(cacheBufferSpy.count(),2);
+ QTRY_COMPARE(layoutSpy.count(),1);
+ QTRY_COMPARE(flowSpy.count(),2);
+
+ gridView->setWrapEnabled(true);
+ gridView->setCacheBuffer(5);
+ gridView->setLayoutDirection(Qt::RightToLeft);
+
+ QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
+ QTRY_COMPARE(cacheBufferSpy.count(),2);
+ QTRY_COMPARE(layoutSpy.count(),1);
+ QTRY_COMPARE(flowSpy.count(),2);
+
+ gridView->setFlow(QSGGridView::TopToBottom);
+ QTRY_COMPARE(gridView->flow(), QSGGridView::TopToBottom);
+ QTRY_COMPARE(flowSpy.count(),3);
+
+ gridView->setFlow(QSGGridView::TopToBottom);
+ QTRY_COMPARE(flowSpy.count(),3);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::componentChanges()
+{
+ QSGView *canvas = createView();
+ QTRY_VERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
+
+ QSGGridView *gridView = canvas->rootObject()->findChild<QSGGridView*>("gridView");
+ QTRY_VERIFY(gridView);
+
+ QDeclarativeComponent component(canvas->engine());
+ component.setData("import QtQuick 1.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
+
+ QDeclarativeComponent delegateComponent(canvas->engine());
+ delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
+
+ QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
+ QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
+ QSignalSpy headerSpy(gridView, SIGNAL(headerChanged()));
+ QSignalSpy footerSpy(gridView, SIGNAL(footerChanged()));
+
+ gridView->setHighlight(&component);
+ gridView->setDelegate(&delegateComponent);
+ gridView->setHeader(&component);
+ gridView->setFooter(&component);
+
+ QTRY_COMPARE(gridView->highlight(), &component);
+ QTRY_COMPARE(gridView->delegate(), &delegateComponent);
+ QTRY_COMPARE(gridView->header(), &component);
+ QTRY_COMPARE(gridView->footer(), &component);
+
+ QTRY_COMPARE(highlightSpy.count(),1);
+ QTRY_COMPARE(delegateSpy.count(),1);
+ QTRY_COMPARE(headerSpy.count(),1);
+ QTRY_COMPARE(footerSpy.count(),1);
+
+ gridView->setHighlight(&component);
+ gridView->setDelegate(&delegateComponent);
+ gridView->setHeader(&component);
+ gridView->setFooter(&component);
+
+ QTRY_COMPARE(highlightSpy.count(),1);
+ QTRY_COMPARE(delegateSpy.count(),1);
+ QTRY_COMPARE(headerSpy.count(),1);
+ QTRY_COMPARE(footerSpy.count(),1);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::modelChanges()
+{
+ QSGView *canvas = createView();
+ QTRY_VERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
+
+ QSGGridView *gridView = canvas->rootObject()->findChild<QSGGridView*>("gridView");
+ QTRY_VERIFY(gridView);
+
+ QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
+ QTRY_VERIFY(alternateModel);
+ QVariant modelVariant = QVariant::fromValue(alternateModel);
+ QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
+
+ gridView->setModel(modelVariant);
+ QTRY_COMPARE(gridView->model(), modelVariant);
+ QTRY_COMPARE(modelSpy.count(),1);
+
+ gridView->setModel(modelVariant);
+ QTRY_COMPARE(modelSpy.count(),1);
+
+ gridView->setModel(QVariant());
+ QTRY_COMPARE(modelSpy.count(),2);
+ delete canvas;
+}
+
+void tst_QSGGridView::positionViewAtIndex()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), (i%3)*80.);
+ QTRY_COMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position on a currently visible item
+ gridview->positionViewAtIndex(4, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->indexAt(120, 90), 4);
+ QTRY_COMPARE(gridview->contentY(), 60.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), (i%3)*80.);
+ QTRY_COMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position on an item beyond the visible items
+ gridview->positionViewAtIndex(21, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->indexAt(40, 450), 21);
+ QTRY_COMPARE(gridview->contentY(), 420.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), (i%3)*80.);
+ QTRY_COMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position on an item that would leave empty space if positioned at the top
+ gridview->positionViewAtIndex(31, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->indexAt(120, 630), 31);
+ QTRY_COMPARE(gridview->contentY(), 520.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), (i%3)*80.);
+ QTRY_COMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position at the beginning again
+ gridview->positionViewAtIndex(0, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->indexAt(0, 0), 0);
+ QTRY_COMPARE(gridview->indexAt(40, 30), 0);
+ QTRY_COMPARE(gridview->indexAt(80, 60), 4);
+ QTRY_COMPARE(gridview->contentY(), 0.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), (i%3)*80.);
+ QTRY_COMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position at End
+ gridview->positionViewAtIndex(30, QSGGridView::End);
+ QTRY_COMPARE(gridview->contentY(), 340.);
+
+ // Position in Center
+ gridview->positionViewAtIndex(15, QSGGridView::Center);
+ QTRY_COMPARE(gridview->contentY(), 170.);
+
+ // Ensure at least partially visible
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentY(), 170.);
+
+ gridview->setContentY(302);
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentY(), 302.);
+
+ gridview->setContentY(360);
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentY(), 300.);
+
+ gridview->setContentY(60);
+ gridview->positionViewAtIndex(20, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentY(), 60.);
+
+ gridview->setContentY(20);
+ gridview->positionViewAtIndex(20, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentY(), 100.);
+
+ // Ensure completely visible
+ gridview->setContentY(120);
+ gridview->positionViewAtIndex(20, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentY(), 120.);
+
+ gridview->setContentY(302);
+ gridview->positionViewAtIndex(15, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentY(), 300.);
+
+ gridview->setContentY(60);
+ gridview->positionViewAtIndex(20, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentY(), 100.);
+
+ // Test for Top To Bottom layout
+ ctxt->setContextProperty("testTopToBottom", QVariant(true));
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), (i/5)*80.);
+ QTRY_COMPARE(item->y(), (i%5)*60.);
+ }
+
+ // Position at End
+ gridview->positionViewAtIndex(30, QSGGridView::End);
+ QTRY_COMPARE(gridview->contentX(), 320.);
+ QTRY_COMPARE(gridview->contentY(), 0.);
+
+ // Position in Center
+ gridview->positionViewAtIndex(15, QSGGridView::Center);
+ QTRY_COMPARE(gridview->contentX(), 160.);
+
+ // Ensure at least partially visible
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), 160.);
+
+ gridview->setContentX(170);
+ gridview->positionViewAtIndex(25, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), 170.);
+
+ gridview->positionViewAtIndex(30, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), 320.);
+
+ gridview->setContentX(170);
+ gridview->positionViewAtIndex(25, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentX(), 240.);
+
+ // positionViewAtBeginning
+ gridview->positionViewAtBeginning();
+ QTRY_COMPARE(gridview->contentX(), 0.);
+
+ gridview->setContentX(80);
+ canvas->rootObject()->setProperty("showHeader", true);
+ gridview->positionViewAtBeginning();
+ QTRY_COMPARE(gridview->contentX(), -30.);
+
+ // positionViewAtEnd
+ gridview->positionViewAtEnd();
+ QTRY_COMPARE(gridview->contentX(), 430.);
+
+ gridview->setContentX(80);
+ canvas->rootObject()->setProperty("showFooter", true);
+ gridview->positionViewAtEnd();
+ QTRY_COMPARE(gridview->contentX(), 460.);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::snapping()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ gridview->setHeight(220);
+ QCOMPARE(gridview->height(), 220.);
+
+ gridview->positionViewAtIndex(12, QSGGridView::Visible);
+ QCOMPARE(gridview->contentY(), 80.);
+
+ gridview->setContentY(0);
+ QCOMPARE(gridview->contentY(), 0.);
+
+ gridview->setSnapMode(QSGGridView::SnapToRow);
+ QCOMPARE(gridview->snapMode(), QSGGridView::SnapToRow);
+
+ gridview->positionViewAtIndex(12, QSGGridView::Visible);
+ QCOMPARE(gridview->contentY(), 60.);
+
+ gridview->positionViewAtIndex(15, QSGGridView::End);
+ QCOMPARE(gridview->contentY(), 120.);
+
+ delete canvas;
+
+}
+
+void tst_QSGGridView::mirroring()
+{
+ QSGView *canvasA = createView();
+ canvasA->setSource(QUrl::fromLocalFile(SRCDIR "/data/mirroring.qml"));
+ QSGGridView *gridviewA = findItem<QSGGridView>(canvasA->rootObject(), "view");
+ QTRY_VERIFY(gridviewA != 0);
+
+ QSGView *canvasB = createView();
+ canvasB->setSource(QUrl::fromLocalFile(SRCDIR "/data/mirroring.qml"));
+ QSGGridView *gridviewB = findItem<QSGGridView>(canvasB->rootObject(), "view");
+ QTRY_VERIFY(gridviewA != 0);
+ qApp->processEvents();
+
+ QList<QString> objectNames;
+ objectNames << "item1" << "item2"; // << "item3"
+
+ gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
+ gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
+ QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
+
+ // LTR != RTL
+ foreach(const QString objectName, objectNames)
+ QVERIFY(findItem<QSGItem>(gridviewA, objectName)->x() != findItem<QSGItem>(gridviewB, objectName)->x());
+
+ gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
+ gridviewB->setProperty("layoutDirection", Qt::LeftToRight);
+
+ // LTR == LTR
+ foreach(const QString objectName, objectNames)
+ QCOMPARE(findItem<QSGItem>(gridviewA, objectName)->x(), findItem<QSGItem>(gridviewB, objectName)->x());
+
+ QVERIFY(gridviewB->layoutDirection() == gridviewB->effectiveLayoutDirection());
+ QSGItemPrivate::get(gridviewB)->setLayoutMirror(true);
+ QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
+
+ // LTR != LTR+mirror
+ foreach(const QString objectName, objectNames)
+ QVERIFY(findItem<QSGItem>(gridviewA, objectName)->x() != findItem<QSGItem>(gridviewB, objectName)->x());
+
+ gridviewA->setProperty("layoutDirection", Qt::RightToLeft);
+
+ // RTL == LTR+mirror
+ foreach(const QString objectName, objectNames)
+ QCOMPARE(findItem<QSGItem>(gridviewA, objectName)->x(), findItem<QSGItem>(gridviewB, objectName)->x());
+
+ gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
+
+ // RTL != RTL+mirror
+ foreach(const QString objectName, objectNames)
+ QVERIFY(findItem<QSGItem>(gridviewA, objectName)->x() != findItem<QSGItem>(gridviewB, objectName)->x());
+
+ gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
+
+ // LTR == RTL+mirror
+ foreach(const QString objectName, objectNames)
+ QCOMPARE(findItem<QSGItem>(gridviewA, objectName)->x(), findItem<QSGItem>(gridviewB, objectName)->x());
+
+ delete canvasA;
+ delete canvasB;
+}
+
+void tst_QSGGridView::positionViewAtIndex_rightToLeft()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testTopToBottom", QVariant(true));
+ ctxt->setContextProperty("testRightToLeft", QVariant(true));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ }
+
+ // Position on a currently visible item
+ gridview->positionViewAtIndex(6, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->contentX(), -320.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ }
+
+ // Position on an item beyond the visible items
+ gridview->positionViewAtIndex(21, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->contentX(), -560.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ }
+
+ // Position on an item that would leave empty space if positioned at the top
+ gridview->positionViewAtIndex(31, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->contentX(), -639.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ }
+
+ // Position at the beginning again
+ gridview->positionViewAtIndex(0, QSGGridView::Beginning);
+ QTRY_COMPARE(gridview->contentX(), -240.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
+ QTRY_COMPARE(item->y(), qreal((i%5)*60));
+ }
+
+ // Position at End
+ gridview->positionViewAtIndex(30, QSGGridView::End);
+ QTRY_COMPARE(gridview->contentX(), -560.);
+
+ // Position in Center
+ gridview->positionViewAtIndex(15, QSGGridView::Center);
+ QTRY_COMPARE(gridview->contentX(), -400.);
+
+ // Ensure at least partially visible
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), -400.);
+
+ gridview->setContentX(-555.);
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), -555.);
+
+ gridview->setContentX(-239);
+ gridview->positionViewAtIndex(15, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), -320.);
+
+ gridview->setContentX(-239);
+ gridview->positionViewAtIndex(20, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), -400.);
+
+ gridview->setContentX(-640);
+ gridview->positionViewAtIndex(20, QSGGridView::Visible);
+ QTRY_COMPARE(gridview->contentX(), -560.);
+
+ // Ensure completely visible
+ gridview->setContentX(-400);
+ gridview->positionViewAtIndex(20, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentX(), -400.);
+
+ gridview->setContentX(-315);
+ gridview->positionViewAtIndex(15, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentX(), -320.);
+
+ gridview->setContentX(-640);
+ gridview->positionViewAtIndex(20, QSGGridView::Contain);
+ QTRY_COMPARE(gridview->contentX(), -560.);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::resetModel()
+{
+ QSGView *canvas = createView();
+
+ QStringList strings;
+ strings << "one" << "two" << "three";
+ QStringListModel model(strings);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaygrid.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(gridview->count(), model.rowCount());
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QSGText *display = findItem<QSGText>(contentItem, "displayText", i);
+ QTRY_VERIFY(display != 0);
+ QTRY_COMPARE(display->text(), strings.at(i));
+ }
+
+ strings.clear();
+ strings << "four" << "five" << "six" << "seven";
+ model.setStringList(strings);
+
+ QTRY_COMPARE(gridview->count(), model.rowCount());
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QSGText *display = findItem<QSGText>(contentItem, "displayText", i);
+ QTRY_VERIFY(display != 0);
+ QTRY_COMPARE(display->text(), strings.at(i));
+ }
+
+ delete canvas;
+}
+
+void tst_QSGGridView::enforceRange()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview-enforcerange.qml"));
+ qApp->processEvents();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
+ QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
+ QTRY_COMPARE(gridview->highlightRangeMode(), QSGGridView::StrictlyEnforceRange);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // view should be positioned at the top of the range.
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(gridview->contentY(), -100.0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ // Check currentIndex is updated when contentItem moves
+ gridview->setContentY(0);
+ QTRY_COMPARE(gridview->currentIndex(), 2);
+
+ gridview->setCurrentIndex(5);
+ QTRY_COMPARE(gridview->contentY(), 100.);
+
+ TestModel model2;
+ for (int i = 0; i < 5; i++)
+ model2.addItem("Item" + QString::number(i), "");
+
+ ctxt->setContextProperty("testModel", &model2);
+ QCOMPARE(gridview->count(), 5);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::enforceRange_rightToLeft()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(true));
+ ctxt->setContextProperty("testTopToBottom", QVariant(true));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview-enforcerange.qml"));
+ qApp->processEvents();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
+ QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
+ QTRY_COMPARE(gridview->highlightRangeMode(), QSGGridView::StrictlyEnforceRange);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // view should be positioned at the top of the range.
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(gridview->contentX(), -100.);
+ QTRY_COMPARE(gridview->contentY(), 0.0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ // Check currentIndex is updated when contentItem moves
+ gridview->setContentX(-200);
+ QTRY_COMPARE(gridview->currentIndex(), 3);
+
+ gridview->setCurrentIndex(7);
+ QTRY_COMPARE(gridview->contentX(), -300.);
+ QTRY_COMPARE(gridview->contentY(), 0.0);
+
+ TestModel model2;
+ for (int i = 0; i < 5; i++)
+ model2.addItem("Item" + QString::number(i), "");
+
+ ctxt->setContextProperty("testModel", &model2);
+ QCOMPARE(gridview->count(), 5);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::QTBUG_8456()
+{
+ QSGView *canvas = createView();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/setindex.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QTRY_COMPARE(gridview->currentIndex(), 0);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::manualHighlight()
+{
+ QSGView *canvas = createView();
+
+ QString filename(SRCDIR "/data/manual-highlight.qml");
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(gridview->currentIndex(), 0);
+ QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 0));
+ QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
+ QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
+
+ gridview->setCurrentIndex(2);
+
+ QTRY_COMPARE(gridview->currentIndex(), 2);
+ QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 2));
+ QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
+ QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
+
+ gridview->positionViewAtIndex(8, QSGGridView::Contain);
+
+ QTRY_COMPARE(gridview->currentIndex(), 2);
+ QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 2));
+ QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
+ QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
+
+ gridview->setFlow(QSGGridView::TopToBottom);
+ QTRY_COMPARE(gridview->flow(), QSGGridView::TopToBottom);
+
+ gridview->setCurrentIndex(0);
+ QTRY_COMPARE(gridview->currentIndex(), 0);
+ QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 0));
+ QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
+ QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
+
+ delete canvas;
+}
+
+void tst_QSGGridView::footer()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ TestModel model;
+ for (int i = 0; i < 7; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/footer.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGText *footer = findItem<QSGText>(contentItem, "footer");
+ QVERIFY(footer);
+
+ QCOMPARE(footer->y(), 180.0);
+ QCOMPARE(footer->height(), 30.0);
+
+ model.removeItem(2);
+ QTRY_COMPARE(footer->y(), 120.0);
+
+ model.clear();
+ QTRY_COMPARE(footer->y(), 0.0);
+
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
+
+ footer = findItem<QSGText>(contentItem, "footer");
+ QVERIFY(!footer);
+ footer = findItem<QSGText>(contentItem, "footer2");
+ QVERIFY(footer);
+
+ QCOMPARE(footer->y(), 600.0);
+ QCOMPARE(footer->height(), 20.0);
+ QCOMPARE(gridview->contentY(), 0.0);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::header()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGText *header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(header);
+
+ QCOMPARE(header->y(), 0.0);
+ QCOMPARE(header->height(), 30.0);
+ QCOMPARE(gridview->contentY(), 0.0);
+
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->y(), 30.0);
+
+ model.clear();
+ QTRY_COMPARE(header->y(), 0.0);
+
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
+
+ header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(!header);
+ header = findItem<QSGText>(contentItem, "header2");
+ QVERIFY(header);
+
+ QCOMPARE(header->y(), 10.0);
+ QCOMPARE(header->height(), 20.0);
+ QCOMPARE(gridview->contentY(), 10.0);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::indexAt()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+ model.addItem("Billy", "22345");
+ model.addItem("Sam", "2945");
+ model.addItem("Ben", "04321");
+ model.addItem("Jim", "0780");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testRightToLeft", QVariant(false));
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+ qApp->processEvents();
+
+ QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+
+ QSGItem *contentItem = gridview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(gridview->count(), model.count());
+
+ QCOMPARE(gridview->indexAt(0, 0), 0);
+ QCOMPARE(gridview->indexAt(79, 59), 0);
+ QCOMPARE(gridview->indexAt(80, 0), 1);
+ QCOMPARE(gridview->indexAt(0, 60), 3);
+ QCOMPARE(gridview->indexAt(240, 0), -1);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::onAdd()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(int, itemsToAdd);
+
+ const int delegateWidth = 50;
+ const int delegateHeight = 100;
+ TestModel model;
+ QSGView *canvas = createView();
+ canvas->setFixedSize(5 * delegateWidth, 5 * delegateHeight); // just ensure all items fit
+
+ // these initial items should not trigger GridView.onAdd
+ for (int i=0; i<initialItemCount; i++)
+ model.addItem("dummy value", "dummy value");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("delegateWidth", delegateWidth);
+ ctxt->setContextProperty("delegateHeight", delegateHeight);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
+
+ QObject *object = canvas->rootObject();
+ object->setProperty("width", canvas->width());
+ object->setProperty("height", canvas->height());
+ qApp->processEvents();
+
+ QList<QPair<QString, QString> > items;
+ for (int i=0; i<itemsToAdd; i++)
+ items << qMakePair(QString("value %1").arg(i), QString::number(i));
+ model.addItems(items);
+
+ qApp->processEvents();
+
+ QVariantList result = object->property("addedDelegates").toList();
+ QCOMPARE(result.count(), items.count());
+ for (int i=0; i<items.count(); i++)
+ QCOMPARE(result[i].toString(), items[i].first);
+
+ delete canvas;
+}
+
+void tst_QSGGridView::onAdd_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<int>("itemsToAdd");
+
+ QTest::newRow("0, add 1") << 0 << 1;
+ QTest::newRow("0, add 2") << 0 << 2;
+ QTest::newRow("0, add 10") << 0 << 10;
+
+ QTest::newRow("1, add 1") << 1 << 1;
+ QTest::newRow("1, add 2") << 1 << 2;
+ QTest::newRow("1, add 10") << 1 << 10;
+
+ QTest::newRow("5, add 1") << 5 << 1;
+ QTest::newRow("5, add 2") << 5 << 2;
+ QTest::newRow("5, add 10") << 5 << 10;
+}
+
+void tst_QSGGridView::onRemove()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(int, indexToRemove);
+ QFETCH(int, removeCount);
+
+ const int delegateWidth = 50;
+ const int delegateHeight = 100;
+ TestModel model;
+ for (int i=0; i<initialItemCount; i++)
+ model.addItem(QString("value %1").arg(i), "dummy value");
+
+ QSGView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("delegateWidth", delegateWidth);
+ ctxt->setContextProperty("delegateHeight", delegateHeight);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
+ QObject *object = canvas->rootObject();
+
+ qApp->processEvents();
+
+ model.removeItems(indexToRemove, removeCount);
+ qApp->processEvents();
+ QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
+
+ delete canvas;
+}
+
+void tst_QSGGridView::onRemove_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<int>("indexToRemove");
+ QTest::addColumn<int>("removeCount");
+
+ QTest::newRow("remove first") << 1 << 0 << 1;
+ QTest::newRow("two items, remove first") << 2 << 0 << 1;
+ QTest::newRow("two items, remove last") << 2 << 1 << 1;
+ QTest::newRow("two items, remove all") << 2 << 0 << 2;
+
+ QTest::newRow("four items, remove first") << 4 << 0 << 1;
+ QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
+ QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
+ QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
+ QTest::newRow("four items, remove last") << 4 << 3 << 1;
+ QTest::newRow("four items, remove all") << 4 << 0 << 4;
+
+ QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
+ QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
+ QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
+}
+
+void tst_QSGGridView::testQtQuick11Attributes()
+{
+ QFETCH(QString, code);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ QDeclarativeEngine engine;
+ QObject *obj;
+
+ QDeclarativeComponent valid(&engine);
+ valid.setData("import QtQuick 1.1; GridView { " + code.toUtf8() + " }", QUrl(""));
+ obj = valid.create();
+ QVERIFY(obj);
+ QVERIFY(valid.errorString().isEmpty());
+ delete obj;
+
+ QDeclarativeComponent invalid(&engine);
+ invalid.setData("import QtQuick 1.0; GridView { " + code.toUtf8() + " }", QUrl(""));
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
+ obj = invalid.create();
+ QCOMPARE(invalid.errorString(), error);
+ delete obj;
+}
+
+void tst_QSGGridView::testQtQuick11Attributes_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("positionViewAtBeginning") << "Component.onCompleted: positionViewAtBeginning()"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: positionViewAtBeginning"
+ << "";
+
+ QTest::newRow("positionViewAtEnd") << "Component.onCompleted: positionViewAtEnd()"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: positionViewAtEnd"
+ << "";
+}
+
+QSGView *tst_QSGGridView::createView()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ return canvas;
+}
+
+/*
+ Find an item with the specified objectName. If index is supplied then the
+ item must also evaluate the {index} expression equal to index
+*/
+template<typename T>
+T *tst_QSGGridView::findItem(QSGItem *parent, const QString &objectName, int index)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ if (index != -1) {
+ QDeclarativeContext *context = QDeclarativeEngine::contextForObject(item);
+ if (context) {
+ if (context->contextProperty("index").toInt() == index) {
+ return static_cast<T*>(item);
+ }
+ }
+ } else {
+ return static_cast<T*>(item);
+ }
+ }
+ item = findItem<T>(item, objectName, index);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+template<typename T>
+QList<T*> tst_QSGGridView::findItems(QSGItem *parent, const QString &objectName)
+{
+ QList<T*> items;
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ items.append(static_cast<T*>(item));
+ //qDebug() << " found:" << item;
+ }
+ items += findItems<T>(item, objectName);
+ }
+
+ return items;
+}
+
+void tst_QSGGridView::dumpTree(QSGItem *parent, int depth)
+{
+ static QString padding(" ");
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ QDeclarativeContext *context = QDeclarativeEngine::contextForObject(item);
+ qDebug() << padding.left(depth*2) << item << (context ? context->contextProperty("index").toInt() : -1);
+ dumpTree(item, depth+1);
+ }
+}
+
+
+QTEST_MAIN(tst_QSGGridView)
+
+#include "tst_qsggridview.moc"
diff --git a/tests/auto/declarative/qsgimage/data/aspectratio.qml b/tests/auto/declarative/qsgimage/data/aspectratio.qml
new file mode 100644
index 0000000000..b26f0e1f04
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/aspectratio.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Image {
+ source: "heart.png"
+ fillMode: Image.PreserveAspectFit;
+}
diff --git a/tests/auto/declarative/qsgimage/data/big.jpeg b/tests/auto/declarative/qsgimage/data/big.jpeg
new file mode 100644
index 0000000000..bed7bd65c3
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/big.jpeg
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/big256.png b/tests/auto/declarative/qsgimage/data/big256.png
new file mode 100644
index 0000000000..1dc1596d03
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/big256.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/colors.png b/tests/auto/declarative/qsgimage/data/colors.png
new file mode 100644
index 0000000000..dfb62f3d64
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/colors.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/colors1.png b/tests/auto/declarative/qsgimage/data/colors1.png
new file mode 100644
index 0000000000..dfb62f3d64
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/colors1.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/green.png b/tests/auto/declarative/qsgimage/data/green.png
new file mode 100644
index 0000000000..0a2e153ba1
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/green.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/heart-win32.png b/tests/auto/declarative/qsgimage/data/heart-win32.png
new file mode 100644
index 0000000000..351da13772
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/heart-win32.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/heart.png b/tests/auto/declarative/qsgimage/data/heart.png
new file mode 100644
index 0000000000..abe97fee4b
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/heart.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/heart.svg b/tests/auto/declarative/qsgimage/data/heart.svg
new file mode 100644
index 0000000000..8c982cd93c
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/heart.svg
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) --><svg viewBox="100 200 550 500" height="841.88976pt" id="svg1" inkscape:version="0.40+cvs" sodipodi:docbase="C:\Documents and Settings\Jon Phillips\My Documents\projects\clipart-project\submissions" sodipodi:docname="heart-left-highlight.svg" sodipodi:version="0.32" width="595.27559pt" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+<metadata>
+<rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<cc:Work rdf:about="">
+<dc:title>Heart Left-Highlight</dc:title>
+<dc:description>This is a normal valentines day heart.</dc:description>
+<dc:subject>
+<rdf:Bag>
+<rdf:li>holiday</rdf:li>
+<rdf:li>valentines</rdf:li>
+<rdf:li></rdf:li>
+<rdf:li>valentine</rdf:li>
+<rdf:li>hash(0x8a091c0)</rdf:li>
+<rdf:li>hash(0x8a0916c)</rdf:li>
+<rdf:li>signs_and_symbols</rdf:li>
+<rdf:li>hash(0x8a091f0)</rdf:li>
+<rdf:li>day</rdf:li>
+</rdf:Bag>
+</dc:subject>
+<dc:publisher>
+<cc:Agent rdf:about="http://www.openclipart.org">
+<dc:title>Jon Phillips</dc:title>
+</cc:Agent>
+</dc:publisher>
+<dc:creator>
+<cc:Agent>
+<dc:title>Jon Phillips</dc:title>
+</cc:Agent>
+</dc:creator>
+<dc:rights>
+<cc:Agent>
+<dc:title>Jon Phillips</dc:title>
+</cc:Agent>
+</dc:rights>
+<dc:date></dc:date>
+<dc:format>image/svg+xml</dc:format>
+<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+<cc:license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
+<dc:language>en</dc:language>
+</cc:Work>
+<cc:License rdf:about="http://web.resource.org/cc/PublicDomain">
+<cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
+<cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
+<cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
+</cc:License>
+</rdf:RDF>
+</metadata>
+<defs id="defs3"/>
+<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="549.40674" inkscape:cy="596.00159" inkscape:document-units="px" inkscape:guide-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="615" inkscape:window-width="866" inkscape:window-x="88" inkscape:window-y="116" inkscape:zoom="0.35000000" pagecolor="#ffffff" showguides="true"/>
+<g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1">
+<path d="M 263.41570,235.14588 C 197.17570,235.14588 143.41575,288.90587 143.41575,355.14588 C 143.41575,489.90139 279.34890,525.23318 371.97820,658.45392 C 459.55244,526.05056 600.54070,485.59932 600.54070,355.14588 C 600.54070,288.90588 546.78080,235.14587 480.54070,235.14588 C 432.49280,235.14588 391.13910,263.51631 371.97820,304.33338 C 352.81740,263.51630 311.46370,235.14587 263.41570,235.14588 z " id="path7" sodipodi:nodetypes="ccccccc" style="fill:#e60000;fill-opacity:1.0000000;stroke:#000000;stroke-width:18.700001;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"/>
+<path d="M 265.00000,253.59375 C 207.04033,253.59375 160.00000,300.63407 160.00000,358.59375 C 160.00000,476.50415 278.91857,507.43251 359.96875,624.00000 C 366.52868,614.08205 220.00000,478.47309 220.00000,378.59375 C 220.00000,320.63407 267.04033,273.59375 325.00000,273.59375 C 325.50453,273.59375 325.99718,273.64912 326.50000,273.65625 C 309.22436,261.07286 288.00557,253.59374 265.00000,253.59375 z " id="path220" sodipodi:nodetypes="ccccccc" style="fill:#e6e6e6;fill-opacity:0.64556962;stroke:none;stroke-width:18.700001;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"/>
+</g>
+</svg>
diff --git a/tests/auto/declarative/qsgimage/data/heart200-win32.png b/tests/auto/declarative/qsgimage/data/heart200-win32.png
new file mode 100644
index 0000000000..4976ff98ba
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/heart200-win32.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/heart200.png b/tests/auto/declarative/qsgimage/data/heart200.png
new file mode 100644
index 0000000000..7fbb13c5bb
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/heart200.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/qtbug_16389.qml b/tests/auto/declarative/qsgimage/data/qtbug_16389.qml
new file mode 100644
index 0000000000..7b8adecb11
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/qtbug_16389.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.0
+Rectangle {
+ width: 400
+ height: 400
+
+ Item {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.bottom: blueHandle.top
+ anchors.right: blueHandle.left
+
+ Image {
+ id: iconImage
+ objectName: "iconImage"
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ source: "heart200.png"
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ }
+ }
+
+ Rectangle {
+ id: blueHandle
+ objectName: "blueHandle"
+ color: "blue"
+ width: 25
+ height: 25
+ }
+}
diff --git a/tests/auto/declarative/qsgimage/data/rect.png b/tests/auto/declarative/qsgimage/data/rect.png
new file mode 100644
index 0000000000..d564a2d5a5
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/rect.png
Binary files differ
diff --git a/tests/auto/declarative/qsgimage/data/tiling.qml b/tests/auto/declarative/qsgimage/data/tiling.qml
new file mode 100644
index 0000000000..986b7708a2
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/data/tiling.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 800; height: 600
+
+ Image {
+ objectName: "vTiling"; height: 550; width: 200
+ source: "green.png"; fillMode: Image.TileVertically
+ }
+
+ Image {
+ objectName: "hTiling"; x: 225; height: 250; width: 550
+ source: "green.png"; fillMode: Image.TileHorizontally
+ }
+}
+
diff --git a/tests/auto/declarative/qsgimage/qsgimage.pro b/tests/auto/declarative/qsgimage/qsgimage.pro
new file mode 100644
index 0000000000..9c33889330
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/qsgimage.pro
@@ -0,0 +1,17 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui network
+macx:CONFIG -= app_bundle
+
+HEADERS += ../shared/testhttpserver.h
+SOURCES += tst_qsgimage.cpp ../shared/testhttpserver.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgimage/tst_qsgimage.cpp b/tests/auto/declarative/qsgimage/tst_qsgimage.cpp
new file mode 100644
index 0000000000..e7366ff1b8
--- /dev/null
+++ b/tests/auto/declarative/qsgimage/tst_qsgimage.cpp
@@ -0,0 +1,776 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QTextDocument>
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QDir>
+
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgimage_p.h>
+#include <private/qsgimagebase_p.h>
+#include <private/qsgloader_p.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtTest/QSignalSpy>
+
+#include "../../../shared/util.h"
+#include "../shared/testhttpserver.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+#define SERVER_PORT 14451
+#define SERVER_ADDR "http://127.0.0.1:14451"
+
+class tst_qsgimage : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgimage();
+
+private slots:
+ void noSource();
+ void imageSource();
+ void imageSource_data();
+ void clearSource();
+ void resized();
+ void preserveAspectRatio();
+ void smooth();
+ void mirror();
+ void mirror_data();
+ void svg();
+ void geometry();
+ void geometry_data();
+ void big();
+ void tiling_QTBUG_6716();
+ void noLoading();
+ void paintedWidthHeight();
+ void sourceSize_QTBUG_14303();
+ void sourceSize_QTBUG_16389();
+ void nullPixmapPaint();
+ void testQtQuick11Attributes();
+ void testQtQuick11Attributes_data();
+
+private:
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &id, int index=-1);
+
+ QDeclarativeEngine engine;
+};
+
+tst_qsgimage::tst_qsgimage()
+{
+}
+
+void tst_qsgimage::noSource()
+{
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"\" }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->source(), QUrl());
+ QVERIFY(obj->status() == QSGImage::Null);
+ QCOMPARE(obj->width(), 0.);
+ QCOMPARE(obj->height(), 0.);
+ QCOMPARE(obj->fillMode(), QSGImage::Stretch);
+ QCOMPARE(obj->progress(), 0.0);
+
+ delete obj;
+}
+
+void tst_qsgimage::imageSource_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<double>("width");
+ QTest::addColumn<double>("height");
+ QTest::addColumn<bool>("remote");
+ QTest::addColumn<bool>("async");
+ QTest::addColumn<bool>("cache");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/colors.png").toString() << 120.0 << 120.0 << false << false << true << "";
+ QTest::newRow("local no cache") << QUrl::fromLocalFile(SRCDIR "/data/colors.png").toString() << 120.0 << 120.0 << false << false << false << "";
+ QTest::newRow("local async") << QUrl::fromLocalFile(SRCDIR "/data/colors1.png").toString() << 120.0 << 120.0 << false << true << true << "";
+ QTest::newRow("local not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString() << 0.0 << 0.0 << false
+ << false << true << "file::2:1: QML Image: Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString();
+ QTest::newRow("local async not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file-1.png").toString() << 0.0 << 0.0 << false
+ << true << true << "file::2:1: QML Image: Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/no-such-file-1.png").toString();
+ QTest::newRow("remote") << SERVER_ADDR "/colors.png" << 120.0 << 120.0 << true << false << true << "";
+ QTest::newRow("remote redirected") << SERVER_ADDR "/oldcolors.png" << 120.0 << 120.0 << true << false << false << "";
+ QTest::newRow("remote svg") << SERVER_ADDR "/heart.svg" << 550.0 << 500.0 << true << false << false << "";
+ QTest::newRow("remote not found") << SERVER_ADDR "/no-such-file.png" << 0.0 << 0.0 << true
+ << false << true << "file::2:1: QML Image: Error downloading " SERVER_ADDR "/no-such-file.png - server replied: Not found";
+
+}
+
+void tst_qsgimage::imageSource()
+{
+ QFETCH(QString, source);
+ QFETCH(double, width);
+ QFETCH(double, height);
+ QFETCH(bool, remote);
+ QFETCH(bool, async);
+ QFETCH(bool, cache);
+ QFETCH(QString, error);
+
+ TestHTTPServer server(SERVER_PORT);
+ if (remote) {
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+ server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png");
+ }
+
+ if (!error.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
+
+ QString componentStr = "import QtQuick 1.1\nImage { source: \"" + source + "\"; asynchronous: "
+ + (async ? QLatin1String("true") : QLatin1String("false")) + "; cache: "
+ + (cache ? QLatin1String("true") : QLatin1String("false")) + " }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+
+ if (async)
+ QVERIFY(obj->asynchronous() == true);
+ else
+ QVERIFY(obj->asynchronous() == false);
+
+ if (cache)
+ QVERIFY(obj->cache() == true);
+ else
+ QVERIFY(obj->cache() == false);
+
+ if (remote || async)
+ QTRY_VERIFY(obj->status() == QSGImage::Loading);
+
+ QCOMPARE(obj->source(), remote ? source : QUrl(source));
+
+ if (error.isEmpty()) {
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+ QCOMPARE(obj->width(), qreal(width));
+ QCOMPARE(obj->height(), qreal(height));
+ QCOMPARE(obj->fillMode(), QSGImage::Stretch);
+ QCOMPARE(obj->progress(), 1.0);
+ } else {
+ QTRY_VERIFY(obj->status() == QSGImage::Error);
+ }
+
+ delete obj;
+}
+
+void tst_qsgimage::clearSource()
+{
+ QString componentStr = "import QtQuick 1.0\nImage { source: srcImage }";
+ QDeclarativeContext *ctxt = engine.rootContext();
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.png"));
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QVERIFY(obj->status() == QSGImage::Ready);
+ QCOMPARE(obj->width(), 120.);
+ QCOMPARE(obj->height(), 120.);
+ QCOMPARE(obj->progress(), 1.0);
+
+ ctxt->setContextProperty("srcImage", "");
+ QVERIFY(obj->source().isEmpty());
+ QVERIFY(obj->status() == QSGImage::Null);
+ QCOMPARE(obj->width(), 0.);
+ QCOMPARE(obj->height(), 0.);
+ QCOMPARE(obj->progress(), 0.0);
+
+ delete obj;
+}
+
+void tst_qsgimage::resized()
+{
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" SRCDIR "/data/colors.png\"; width: 300; height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 300.);
+ QCOMPARE(obj->fillMode(), QSGImage::Stretch);
+ delete obj;
+}
+
+
+void tst_qsgimage::preserveAspectRatio()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->show();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/aspectratio.qml"));
+ QSGImage *image = qobject_cast<QSGImage*>(canvas->rootObject());
+ QVERIFY(image != 0);
+ image->setWidth(80.0);
+ QCOMPARE(image->width(), 80.);
+ QCOMPARE(image->height(), 80.);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/aspectratio.qml"));
+ image = qobject_cast<QSGImage*>(canvas->rootObject());
+ image->setHeight(60.0);
+ QVERIFY(image != 0);
+ QCOMPARE(image->height(), 60.);
+ QCOMPARE(image->width(), 60.);
+ delete canvas;
+}
+
+void tst_qsgimage::smooth()
+{
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" SRCDIR "/data/colors.png\"; smooth: true; width: 300; height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->width(), 300.);
+ QCOMPARE(obj->height(), 300.);
+ QCOMPARE(obj->smooth(), true);
+ QCOMPARE(obj->fillMode(), QSGImage::Stretch);
+
+ delete obj;
+}
+
+void tst_qsgimage::mirror()
+{
+ QFETCH(int, fillMode);
+
+ qreal width = 300;
+ qreal height = 250;
+
+ QString src = QUrl::fromLocalFile(SRCDIR "/data/heart200.png").toString();
+ QString componentStr = "import QtQuick 1.1\nImage { source: \"" + src + "\"; }";
+
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+
+ obj->setProperty("width", width);
+ obj->setProperty("height", height);
+ obj->setFillMode((QSGImage::FillMode)fillMode);
+ obj->setProperty("mirror", true);
+
+ QGraphicsScene scene;
+ scene.addItem(qobject_cast<QSGItem *>(obj));
+ QPixmap screenshot(width, height);
+ screenshot.fill();
+ QPainter p_screenshot(&screenshot);
+ scene.render(&p_screenshot, QRect(0, 0, width, height), QRect(0, 0, width, height));
+
+ QPixmap srcPixmap;
+ QVERIFY(srcPixmap.load(SRCDIR "/data/heart200.png"));
+
+ QPixmap expected(width, height);
+ expected.fill();
+ QPainter p_e(&expected);
+ QTransform transform;
+ transform.translate(width, 0).scale(-1, 1.0);
+ p_e.setTransform(transform);
+
+ switch (fillMode) {
+ case QSGImage::Stretch:
+ p_e.drawPixmap(QRect(0, 0, width, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
+ break;
+ case QSGImage::PreserveAspectFit:
+ p_e.drawPixmap(QRect(25, 0, width / (width/height), height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
+ break;
+ case QSGImage::PreserveAspectCrop:
+ {
+ qreal ratio = width/srcPixmap.width(); // width is the longer side
+ QRect rect(0, 0, srcPixmap.width()*ratio, srcPixmap.height()*ratio);
+ rect.moveCenter(QRect(0, 0, width, height).center());
+ p_e.drawPixmap(rect, srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
+ break;
+ }
+ case QSGImage::Tile:
+ p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap);
+ break;
+ case QSGImage::TileVertically:
+ transform.scale(width / srcPixmap.width(), 1.0);
+ p_e.setTransform(transform);
+ p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap);
+ break;
+ case QSGImage::TileHorizontally:
+ transform.scale(1.0, height / srcPixmap.height());
+ p_e.setTransform(transform);
+ p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap);
+ break;
+ }
+
+ QCOMPARE(screenshot, expected);
+
+ delete obj;
+}
+
+void tst_qsgimage::mirror_data()
+{
+ QTest::addColumn<int>("fillMode");
+
+ QTest::newRow("Stretch") << int(QSGImage::Stretch);
+ QTest::newRow("PreserveAspectFit") << int(QSGImage::PreserveAspectFit);
+ QTest::newRow("PreserveAspectCrop") << int(QSGImage::PreserveAspectCrop);
+ QTest::newRow("Tile") << int(QSGImage::Tile);
+ QTest::newRow("TileVertically") << int(QSGImage::TileVertically);
+ QTest::newRow("TileHorizontally") << int(QSGImage::TileHorizontally);
+}
+
+void tst_qsgimage::svg()
+{
+ QString src = QUrl::fromLocalFile(SRCDIR "/data/heart.svg").toString();
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" + src + "\"; sourceSize.width: 300; sourceSize.height: 300 }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->pixmap().width(), 300);
+ QCOMPARE(obj->pixmap().height(), 300);
+ QCOMPARE(obj->width(), 300.0);
+ QCOMPARE(obj->height(), 300.0);
+#if defined(Q_OS_LINUX)
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart.png"));
+#elif defined(Q_OS_WIN32)
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart-win32.png"));
+#endif
+
+ obj->setSourceSize(QSize(200,200));
+
+ QCOMPARE(obj->pixmap().width(), 200);
+ QCOMPARE(obj->pixmap().height(), 200);
+ QCOMPARE(obj->width(), 200.0);
+ QCOMPARE(obj->height(), 200.0);
+#if defined(Q_OS_LINUX)
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart200.png"));
+#elif defined(Q_OS_WIN32)
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart200-win32.png"));
+#endif
+ delete obj;
+}
+
+void tst_qsgimage::geometry_data()
+{
+ QTest::addColumn<QString>("fillMode");
+ QTest::addColumn<bool>("explicitWidth");
+ QTest::addColumn<bool>("explicitHeight");
+ QTest::addColumn<double>("itemWidth");
+ QTest::addColumn<double>("paintedWidth");
+ QTest::addColumn<double>("boundingWidth");
+ QTest::addColumn<double>("itemHeight");
+ QTest::addColumn<double>("paintedHeight");
+ QTest::addColumn<double>("boundingHeight");
+
+ // tested image has width 200, height 100
+
+ // bounding rect and item rect are equal with fillMode PreserveAspectFit, painted rect may be smaller if the aspect ratio doesn't match
+ QTest::newRow("PreserveAspectFit") << "PreserveAspectFit" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
+ QTest::newRow("PreserveAspectFit explicit width 300") << "PreserveAspectFit" << true << false << 300.0 << 200.0 << 300.0 << 100.0 << 100.0 << 100.0;
+ QTest::newRow("PreserveAspectFit explicit height 400") << "PreserveAspectFit" << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 100.0 << 400.0;
+ QTest::newRow("PreserveAspectFit explicit width 300, height 400") << "PreserveAspectFit" << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 150.0 << 400.0;
+
+ // bounding rect and painted rect are equal with fillMode PreserveAspectCrop, item rect may be smaller if the aspect ratio doesn't match
+ QTest::newRow("PreserveAspectCrop") << "PreserveAspectCrop" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
+ QTest::newRow("PreserveAspectCrop explicit width 300") << "PreserveAspectCrop" << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 150.0 << 150.0;
+ QTest::newRow("PreserveAspectCrop explicit height 400") << "PreserveAspectCrop" << false << true << 200.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
+ QTest::newRow("PreserveAspectCrop explicit width 300, height 400") << "PreserveAspectCrop" << true << true << 300.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
+
+ // bounding rect, painted rect and item rect are equal in stretching and tiling images
+ QStringList fillModes;
+ fillModes << "Stretch" << "Tile" << "TileVertically" << "TileHorizontally";
+ foreach (QString fillMode, fillModes) {
+ QTest::newRow(fillMode.toLatin1()) << fillMode << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
+ QTest::newRow(QString(fillMode + " explicit width 300").toLatin1()) << fillMode << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 100.0 << 100.0;
+ QTest::newRow(QString(fillMode + " explicit height 400").toLatin1()) << fillMode << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 400.0 << 400.0;
+ QTest::newRow(QString(fillMode + " explicit width 300, height 400").toLatin1()) << fillMode << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 400.0 << 400.0;
+ }
+}
+
+void tst_qsgimage::geometry()
+{
+ QFETCH(QString, fillMode);
+ QFETCH(bool, explicitWidth);
+ QFETCH(bool, explicitHeight);
+ QFETCH(double, itemWidth);
+ QFETCH(double, itemHeight);
+ QFETCH(double, paintedWidth);
+ QFETCH(double, paintedHeight);
+ QFETCH(double, boundingWidth);
+ QFETCH(double, boundingHeight);
+
+ QString src = QUrl::fromLocalFile(SRCDIR "/data/rect.png").toString();
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" + src + "\"; fillMode: Image." + fillMode + "; ";
+
+ if (explicitWidth)
+ componentStr.append("width: 300; ");
+ if (explicitHeight)
+ componentStr.append("height: 400; ");
+ componentStr.append("}");
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+
+ QCOMPARE(obj->width(), itemWidth);
+ QCOMPARE(obj->paintedWidth(), paintedWidth);
+ QCOMPARE(obj->boundingRect().width(), boundingWidth);
+
+ QCOMPARE(obj->height(), itemHeight);
+ QCOMPARE(obj->paintedHeight(), paintedHeight);
+ QCOMPARE(obj->boundingRect().height(), boundingHeight);
+ delete obj;
+}
+
+void tst_qsgimage::big()
+{
+ // If the JPEG loader does not implement scaling efficiently, it would
+ // have to build a 400 MB image. That would be a bug in the JPEG loader.
+
+ QString src = QUrl::fromLocalFile(SRCDIR "/data/big.jpeg").toString();
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" + src + "\"; width: 100; sourceSize.height: 256 }";
+
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->pixmap().width(), 256);
+ QCOMPARE(obj->pixmap().height(), 256);
+ QCOMPARE(obj->width(), 100.0);
+ QCOMPARE(obj->height(), 256.0);
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/big256.png"));
+
+ delete obj;
+}
+
+void tst_qsgimage::tiling_QTBUG_6716()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/tiling.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QSGImage *vTiling = findItem<QSGImage>(canvas->rootObject(), "vTiling");
+ QSGImage *hTiling = findItem<QSGImage>(canvas->rootObject(), "hTiling");
+
+ QVERIFY(vTiling != 0);
+ QVERIFY(hTiling != 0);
+
+ {
+ QPixmap pm(vTiling->width(), vTiling->height());
+ QPainter p(&pm);
+ vTiling->paint(&p, 0, 0);
+
+ QImage img = pm.toImage();
+ for (int x = 0; x < vTiling->width(); ++x) {
+ for (int y = 0; y < vTiling->height(); ++y) {
+ QVERIFY(img.pixel(x, y) == qRgb(0, 255, 0));
+ }
+ }
+ }
+
+ {
+ QPixmap pm(hTiling->width(), hTiling->height());
+ QPainter p(&pm);
+ hTiling->paint(&p, 0, 0);
+
+ QImage img = pm.toImage();
+ for (int x = 0; x < hTiling->width(); ++x) {
+ for (int y = 0; y < hTiling->height(); ++y) {
+ QVERIFY(img.pixel(x, y) == qRgb(0, 255, 0));
+ }
+ }
+ }
+
+ delete canvas;
+}
+
+void tst_qsgimage::noLoading()
+{
+ TestHTTPServer server(SERVER_PORT);
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+ server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png");
+
+ QString componentStr = "import QtQuick 1.0\nImage { source: srcImage }";
+ QDeclarativeContext *ctxt = engine.rootContext();
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/heart.png"));
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QVERIFY(obj->status() == QSGImage::Ready);
+
+ QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &)));
+ QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
+ QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QSGImageBase::Status)));
+
+ // Loading local file
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.png"));
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+ QTRY_VERIFY(obj->progress() == 1.0);
+ QTRY_COMPARE(sourceSpy.count(), 1);
+ QTRY_COMPARE(progressSpy.count(), 0);
+ QTRY_COMPARE(statusSpy.count(), 0);
+
+ // Loading remote file
+ ctxt->setContextProperty("srcImage", QString(SERVER_ADDR) + "/heart200.png");
+ QTRY_VERIFY(obj->status() == QSGImage::Loading);
+ QTRY_VERIFY(obj->progress() == 0.0);
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+ QTRY_VERIFY(obj->progress() == 1.0);
+ QTRY_COMPARE(sourceSpy.count(), 2);
+ QTRY_COMPARE(progressSpy.count(), 2);
+ QTRY_COMPARE(statusSpy.count(), 2);
+
+ // Loading remote file again - should not go through 'Loading' state.
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.png"));
+ ctxt->setContextProperty("srcImage", QString(SERVER_ADDR) + "/heart200.png");
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+ QTRY_VERIFY(obj->progress() == 1.0);
+ QTRY_COMPARE(sourceSpy.count(), 4);
+ QTRY_COMPARE(progressSpy.count(), 2);
+ QTRY_COMPARE(statusSpy.count(), 2);
+
+ delete obj;
+}
+
+void tst_qsgimage::paintedWidthHeight()
+{
+ {
+ QString src = QUrl::fromLocalFile(SRCDIR "/data/heart.png").toString();
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" + src + "\"; width: 200; height: 25; fillMode: Image.PreserveAspectFit }";
+
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->pixmap().width(), 300);
+ QCOMPARE(obj->pixmap().height(), 300);
+ QCOMPARE(obj->width(), 200.0);
+ QCOMPARE(obj->height(), 25.0);
+ QCOMPARE(obj->paintedWidth(), 25.0);
+ QCOMPARE(obj->paintedHeight(), 25.0);
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart.png"));
+
+ delete obj;
+ }
+
+ {
+ QString src = QUrl::fromLocalFile(SRCDIR "/data/heart.png").toString();
+ QString componentStr = "import QtQuick 1.0\nImage { source: \"" + src + "\"; width: 26; height: 175; fillMode: Image.PreserveAspectFit }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->pixmap().width(), 300);
+ QCOMPARE(obj->pixmap().height(), 300);
+ QCOMPARE(obj->width(), 26.0);
+ QCOMPARE(obj->height(), 175.0);
+ QCOMPARE(obj->paintedWidth(), 26.0);
+ QCOMPARE(obj->paintedHeight(), 26.0);
+ QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart.png"));
+
+ delete obj;
+ }
+}
+
+void tst_qsgimage::sourceSize_QTBUG_14303()
+{
+ QString componentStr = "import QtQuick 1.0\nImage { source: srcImage }";
+ QDeclarativeContext *ctxt = engine.rootContext();
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/heart200.png"));
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *obj = qobject_cast<QSGImage*>(component.create());
+
+ QSignalSpy sourceSizeSpy(obj, SIGNAL(sourceSizeChanged()));
+
+ QTRY_VERIFY(obj != 0);
+ QTRY_VERIFY(obj->status() == QSGImage::Ready);
+
+ QTRY_COMPARE(obj->sourceSize().width(), 200);
+ QTRY_COMPARE(obj->sourceSize().height(), 200);
+ QTRY_COMPARE(sourceSizeSpy.count(), 0);
+
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.png"));
+ QTRY_COMPARE(obj->sourceSize().width(), 120);
+ QTRY_COMPARE(obj->sourceSize().height(), 120);
+ QTRY_COMPARE(sourceSizeSpy.count(), 1);
+
+ ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/heart200.png"));
+ QTRY_COMPARE(obj->sourceSize().width(), 200);
+ QTRY_COMPARE(obj->sourceSize().height(), 200);
+ QTRY_COMPARE(sourceSizeSpy.count(), 2);
+
+ delete obj;
+}
+
+void tst_qsgimage::sourceSize_QTBUG_16389()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/qtbug_16389.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QSGImage *image = findItem<QSGImage>(canvas->rootObject(), "iconImage");
+ QSGItem *handle = findItem<QSGItem>(canvas->rootObject(), "blueHandle");
+
+ QCOMPARE(image->sourceSize().width(), 200);
+ QCOMPARE(image->sourceSize().height(), 200);
+ QCOMPARE(image->paintedWidth(), 0.0);
+ QCOMPARE(image->paintedHeight(), 0.0);
+
+ handle->setY(20);
+
+ QCOMPARE(image->sourceSize().width(), 200);
+ QCOMPARE(image->sourceSize().height(), 200);
+ QCOMPARE(image->paintedWidth(), 20.0);
+ QCOMPARE(image->paintedHeight(), 20.0);
+}
+
+static int numberOfWarnings = 0;
+static void checkWarnings(QtMsgType, const char *)
+{
+ numberOfWarnings++;
+}
+
+// QTBUG-15690
+void tst_qsgimage::nullPixmapPaint()
+{
+ QString componentStr = QString("import QtQuick 1.0\nImage { width: 10; height:10; fillMode: Image.PreserveAspectFit; source: \"")
+ + SERVER_ADDR + QString("/no-such-file.png\" }");
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGImage *image = qobject_cast<QSGImage*>(component.create());
+
+ QTRY_VERIFY(image != 0);
+
+ QtMsgHandler previousMsgHandler = qInstallMsgHandler(checkWarnings);
+
+ QPixmap pm(100, 100);
+ QPainter p(&pm);
+
+ // used to print "QTransform::translate with NaN called"
+ image->paint(&p, 0, 0);
+ qInstallMsgHandler(previousMsgHandler);
+ QVERIFY(numberOfWarnings == 0);
+ delete image;
+}
+
+void tst_qsgimage::testQtQuick11Attributes()
+{
+ QFETCH(QString, code);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ QDeclarativeEngine engine;
+ QObject *obj;
+
+ QDeclarativeComponent valid(&engine);
+ valid.setData("import QtQuick 1.1; Image { " + code.toUtf8() + " }", QUrl(""));
+ obj = valid.create();
+ QVERIFY(obj);
+ QVERIFY(valid.errorString().isEmpty());
+ delete obj;
+
+ QDeclarativeComponent invalid(&engine);
+ invalid.setData("import QtQuick 1.0; Image { " + code.toUtf8() + " }", QUrl(""));
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
+ obj = invalid.create();
+ QCOMPARE(invalid.errorString(), error);
+ delete obj;
+}
+
+void tst_qsgimage::testQtQuick11Attributes_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("mirror") << "mirror: true"
+ << "QDeclarativeComponent: Component is not ready"
+ << ":1 \"Image.mirror\" is not available in QtQuick 1.0.\n";
+
+ QTest::newRow("cache") << "cache: true"
+ << "QDeclarativeComponent: Component is not ready"
+ << ":1 \"Image.cache\" is not available in QtQuick 1.0.\n";
+}
+
+/*
+ Find an item with the specified objectName. If index is supplied then the
+ item must also evaluate the {index} expression equal to index
+*/
+template<typename T>
+T *tst_qsgimage::findItem(QSGItem *parent, const QString &objectName, int index)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ if (index != -1) {
+ QDeclarativeExpression e(qmlContext(item), item, "index");
+ if (e.evaluate().toInt() == index)
+ return static_cast<T*>(item);
+ } else {
+ return static_cast<T*>(item);
+ }
+ }
+ item = findItem<T>(item, objectName, index);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+QTEST_MAIN(tst_qsgimage)
+
+#include "tst_qsgimage.moc"
diff --git a/tests/auto/declarative/qsgitem/qsgitem.pro b/tests/auto/declarative/qsgitem/qsgitem.pro
new file mode 100644
index 0000000000..6c659a395f
--- /dev/null
+++ b/tests/auto/declarative/qsgitem/qsgitem.pro
@@ -0,0 +1,7 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+SOURCES += tst_qsgitem.cpp
+
+macx:CONFIG -= app_bundle
+
+CONFIG += parallel_test
diff --git a/tests/auto/declarative/qsgitem/tst_qsgitem.cpp b/tests/auto/declarative/qsgitem/tst_qsgitem.cpp
new file mode 100644
index 0000000000..746b186c1e
--- /dev/null
+++ b/tests/auto/declarative/qsgitem/tst_qsgitem.cpp
@@ -0,0 +1,787 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qtest.h>
+
+#include "qsgitem.h"
+#include "qsgcanvas.h"
+#include "private/qsgfocusscope_p.h"
+#include <QDebug>
+
+class TestItem : public QSGItem
+{
+Q_OBJECT
+public:
+ TestItem(QSGItem *parent = 0) : QSGItem(parent), focused(false), pressCount(0), releaseCount(0) {}
+
+ bool focused;
+ int pressCount;
+ int releaseCount;
+protected:
+ virtual void focusInEvent(QFocusEvent *) { Q_ASSERT(!focused); focused = true; }
+ virtual void focusOutEvent(QFocusEvent *) { Q_ASSERT(focused); focused = false; }
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) { event->accept(); ++pressCount; }
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { event->accept(); ++releaseCount; }
+};
+
+class TestFocusScope : public QSGFocusScope
+{
+Q_OBJECT
+public:
+ TestFocusScope(QSGItem *parent = 0) : QSGFocusScope(parent), focused(false) {}
+
+ bool focused;
+protected:
+ virtual void focusInEvent(QFocusEvent *) { Q_ASSERT(!focused); focused = true; }
+ virtual void focusOutEvent(QFocusEvent *) { Q_ASSERT(focused); focused = false; }
+};
+
+class tst_qsgitem : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgitem();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void noCanvas();
+ void simpleFocus();
+ void scopedFocus();
+ void addedToCanvas();
+ void changeParent();
+
+ void constructor();
+ void setParentItem();
+
+ void visible();
+ void enabled();
+
+ void mouseGrab();
+};
+
+tst_qsgitem::tst_qsgitem()
+{
+}
+
+void tst_qsgitem::initTestCase()
+{
+}
+
+void tst_qsgitem::cleanupTestCase()
+{
+}
+
+// Focus has no effect when outside a canvas
+void tst_qsgitem::noCanvas()
+{
+ QSGItem *root = new TestItem;
+ QSGItem *child = new TestItem(root);
+ QSGItem *scope = new TestItem(root);
+ QSGFocusScope *scopedChild = new TestFocusScope(scope);
+ QSGFocusScope *scopedChild2 = new TestFocusScope(scope);
+
+ QCOMPARE(root->hasFocus(), false);
+ QCOMPARE(child->hasFocus(), false);
+ QCOMPARE(scope->hasFocus(), false);
+ QCOMPARE(scopedChild->hasFocus(), false);
+ QCOMPARE(scopedChild2->hasFocus(), false);
+
+ root->setFocus(true);
+ scope->setFocus(true);
+ scopedChild2->setFocus(true);
+ QCOMPARE(root->hasFocus(), true);
+ QCOMPARE(child->hasFocus(), false);
+ QCOMPARE(scope->hasFocus(), true);
+ QCOMPARE(scopedChild->hasFocus(), false);
+ QCOMPARE(scopedChild2->hasFocus(), true);
+
+ root->setFocus(false);
+ child->setFocus(true);
+ scopedChild->setFocus(true);
+ scope->setFocus(false);
+ QCOMPARE(root->hasFocus(), false);
+ QCOMPARE(child->hasFocus(), true);
+ QCOMPARE(scope->hasFocus(), false);
+ QCOMPARE(scopedChild->hasFocus(), true);
+ QCOMPARE(scopedChild2->hasFocus(), true);
+
+ delete root;
+}
+
+struct FocusData {
+ FocusData() : focus(false), activeFocus(false) {}
+
+ void set(bool f, bool af) { focus = f; activeFocus = af; }
+ bool focus;
+ bool activeFocus;
+};
+struct FocusState : public QHash<QSGItem *, FocusData>
+{
+ FocusState() : activeFocusItem(0) {}
+ FocusState &operator<<(QSGItem *item) {
+ insert(item, FocusData());
+ return *this;
+ }
+
+ void active(QSGItem *i) {
+ activeFocusItem = i;
+ }
+ QSGItem *activeFocusItem;
+};
+
+#define FVERIFY() \
+ do { \
+ if (focusState.activeFocusItem) { \
+ QCOMPARE(canvas.activeFocusItem(), focusState.activeFocusItem); \
+ if (qobject_cast<TestItem *>(canvas.activeFocusItem())) \
+ QCOMPARE(qobject_cast<TestItem *>(canvas.activeFocusItem())->focused, true); \
+ else if (qobject_cast<TestFocusScope *>(canvas.activeFocusItem())) \
+ QCOMPARE(qobject_cast<TestFocusScope *>(canvas.activeFocusItem())->focused, true); \
+ } else { \
+ QCOMPARE(canvas.activeFocusItem(), canvas.rootItem()); \
+ } \
+ for(QHash<QSGItem *, FocusData>::Iterator iter = focusState.begin(); \
+ iter != focusState.end(); \
+ iter++) { \
+ QCOMPARE(iter.key()->hasFocus(), iter.value().focus); \
+ QCOMPARE(iter.key()->hasActiveFocus(), iter.value().activeFocus); \
+ } \
+ } while(false)
+
+// Tests a simple set of top-level scoped items
+void tst_qsgitem::simpleFocus()
+{
+ QSGCanvas canvas;
+
+ QSGItem *l1c1 = new TestItem(canvas.rootItem());
+ QSGItem *l1c2 = new TestItem(canvas.rootItem());
+ QSGItem *l1c3 = new TestItem(canvas.rootItem());
+
+ QSGItem *l2c1 = new TestItem(l1c1);
+ QSGItem *l2c2 = new TestItem(l1c1);
+ QSGItem *l2c3 = new TestItem(l1c3);
+
+ FocusState focusState;
+ focusState << l1c1 << l1c2 << l1c3
+ << l2c1 << l2c2 << l2c3;
+ FVERIFY();
+
+ l1c1->setFocus(true);
+ focusState[l1c1].set(true, true);
+ focusState.active(l1c1);
+ FVERIFY();
+
+ l2c3->setFocus(true);
+ focusState[l1c1].set(false, false);
+ focusState[l2c3].set(true, true);
+ focusState.active(l2c3);
+ FVERIFY();
+
+ l1c3->setFocus(true);
+ focusState[l2c3].set(false, false);
+ focusState[l1c3].set(true, true);
+ focusState.active(l1c3);
+ FVERIFY();
+
+ l1c2->setFocus(false);
+ FVERIFY();
+
+ l1c3->setFocus(false);
+ focusState[l1c3].set(false, false);
+ focusState.active(0);
+ FVERIFY();
+
+ l2c1->setFocus(true);
+ focusState[l2c1].set(true, true);
+ focusState.active(l2c1);
+ FVERIFY();
+}
+
+// Items with a focus scope
+void tst_qsgitem::scopedFocus()
+{
+ QSGCanvas canvas;
+
+ QSGItem *l1c1 = new TestItem(canvas.rootItem());
+ QSGItem *l1c2 = new TestItem(canvas.rootItem());
+ QSGItem *l1c3 = new TestItem(canvas.rootItem());
+
+ QSGItem *l2c1 = new TestItem(l1c1);
+ QSGItem *l2c2 = new TestItem(l1c1);
+ QSGItem *l2c3 = new TestFocusScope(l1c3);
+
+ QSGItem *l3c1 = new TestItem(l2c3);
+ QSGItem *l3c2 = new TestFocusScope(l2c3);
+
+ QSGItem *l4c1 = new TestItem(l3c2);
+ QSGItem *l4c2 = new TestItem(l3c2);
+
+ FocusState focusState;
+ focusState << l1c1 << l1c2 << l1c3
+ << l2c1 << l2c2 << l2c3
+ << l3c1 << l3c2
+ << l4c1 << l4c2;
+ FVERIFY();
+
+ l4c2->setFocus(true);
+ focusState[l4c2].set(true, false);
+ FVERIFY();
+
+ l4c1->setFocus(true);
+ focusState[l4c2].set(false, false);
+ focusState[l4c1].set(true, false);
+ FVERIFY();
+
+ l1c1->setFocus(true);
+ focusState[l1c1].set(true, true);
+ focusState.active(l1c1);
+ FVERIFY();
+
+ l3c2->setFocus(true);
+ focusState[l3c2].set(true, false);
+ FVERIFY();
+
+ l2c3->setFocus(true);
+ focusState[l1c1].set(false, false);
+ focusState[l2c3].set(true, true);
+ focusState[l3c2].set(true, true);
+ focusState[l4c1].set(true, true);
+ focusState.active(l4c1);
+ FVERIFY();
+
+ l3c2->setFocus(false);
+ focusState[l3c2].set(false, false);
+ focusState[l4c1].set(true, false);
+ focusState.active(l2c3);
+ FVERIFY();
+
+ l3c2->setFocus(true);
+ focusState[l3c2].set(true, true);
+ focusState[l4c1].set(true, true);
+ focusState.active(l4c1);
+ FVERIFY();
+
+ l4c1->setFocus(false);
+ focusState[l4c1].set(false, false);
+ focusState.active(l3c2);
+ FVERIFY();
+
+ l1c3->setFocus(true);
+ focusState[l1c3].set(true, true);
+ focusState[l2c3].set(false, false);
+ focusState[l3c2].set(true, false);
+ focusState.active(l1c3);
+ FVERIFY();
+}
+
+// Tests focus corrects itself when a tree is added to a canvas for the first time
+void tst_qsgitem::addedToCanvas()
+{
+ {
+ QSGCanvas canvas;
+
+ QSGItem *item = new TestItem;
+
+ FocusState focusState;
+ focusState << item;
+
+ item->setFocus(true);
+ focusState[item].set(true, false);
+ FVERIFY();
+
+ item->setParentItem(canvas.rootItem());
+ focusState[item].set(true, true);
+ focusState.active(item);
+ FVERIFY();
+ }
+
+ {
+ QSGCanvas canvas;
+
+ QSGItem *item = new TestItem(canvas.rootItem());
+
+ QSGItem *tree = new TestItem;
+ QSGItem *c1 = new TestItem(tree);
+ QSGItem *c2 = new TestItem(tree);
+
+ FocusState focusState;
+ focusState << item << tree << c1 << c2;
+
+ item->setFocus(true);
+ c1->setFocus(true);
+ c2->setFocus(true);
+ focusState[item].set(true, true);
+ focusState[c1].set(true, false);
+ focusState[c2].set(true, false);
+ focusState.active(item);
+ FVERIFY();
+
+ tree->setParentItem(item);
+ focusState[c1].set(false, false);
+ focusState[c2].set(false, false);
+ FVERIFY();
+ }
+
+ {
+ QSGCanvas canvas;
+
+ QSGItem *tree = new TestItem;
+ QSGItem *c1 = new TestItem(tree);
+ QSGItem *c2 = new TestItem(tree);
+
+ FocusState focusState;
+ focusState << tree << c1 << c2;
+ c1->setFocus(true);
+ c2->setFocus(true);
+ focusState[c1].set(true, false);
+ focusState[c2].set(true, false);
+ FVERIFY();
+
+ tree->setParentItem(canvas.rootItem());
+ focusState[c1].set(true, true);
+ focusState[c2].set(false, false);
+ focusState.active(c1);
+ FVERIFY();
+ }
+
+ {
+ QSGCanvas canvas;
+ QSGItem *tree = new TestFocusScope;
+ QSGItem *c1 = new TestItem(tree);
+ QSGItem *c2 = new TestItem(tree);
+
+ FocusState focusState;
+ focusState << tree << c1 << c2;
+ c1->setFocus(true);
+ c2->setFocus(true);
+ focusState[c1].set(true, false);
+ focusState[c2].set(true, false);
+ FVERIFY();
+
+ tree->setParentItem(canvas.rootItem());
+ focusState[c1].set(true, false);
+ focusState[c2].set(false, false);
+ FVERIFY();
+
+ tree->setFocus(true);
+ focusState[tree].set(true, true);
+ focusState[c1].set(true, true);
+ focusState.active(c1);
+ FVERIFY();
+ }
+
+ {
+ QSGCanvas canvas;
+ QSGItem *tree = new TestFocusScope;
+ QSGItem *c1 = new TestItem(tree);
+ QSGItem *c2 = new TestItem(tree);
+
+ FocusState focusState;
+ focusState << tree << c1 << c2;
+ tree->setFocus(true);
+ c1->setFocus(true);
+ c2->setFocus(true);
+ focusState[tree].set(true, false);
+ focusState[c1].set(true, false);
+ focusState[c2].set(true, false);
+ FVERIFY();
+
+ tree->setParentItem(canvas.rootItem());
+ focusState[tree].set(true, true);
+ focusState[c1].set(true, true);
+ focusState[c2].set(false, false);
+ focusState.active(c1);
+ FVERIFY();
+ }
+
+ {
+ QSGCanvas canvas;
+ QSGItem *child = new TestItem(canvas.rootItem());
+ QSGItem *tree = new TestFocusScope;
+ QSGItem *c1 = new TestItem(tree);
+ QSGItem *c2 = new TestItem(tree);
+
+ FocusState focusState;
+ focusState << child << tree << c1 << c2;
+ child->setFocus(true);
+ tree->setFocus(true);
+ c1->setFocus(true);
+ c2->setFocus(true);
+ focusState[child].set(true, true);
+ focusState[tree].set(true, false);
+ focusState[c1].set(true, false);
+ focusState[c2].set(true, false);
+ focusState.active(child);
+ FVERIFY();
+
+ tree->setParentItem(canvas.rootItem());
+ focusState[tree].set(false, false);
+ focusState[c1].set(true, false);
+ focusState[c2].set(false, false);
+ FVERIFY();
+
+ tree->setFocus(true);
+ focusState[child].set(false, false);
+ focusState[tree].set(true, true);
+ focusState[c1].set(true, true);
+ focusState.active(c1);
+ FVERIFY();
+ }
+}
+
+void tst_qsgitem::changeParent()
+{
+ // Parent to no parent
+ {
+ QSGCanvas canvas;
+ QSGItem *child = new TestItem(canvas.rootItem());
+
+ FocusState focusState;
+ focusState << child;
+ FVERIFY();
+
+ child->setFocus(true);
+ focusState[child].set(true, true);
+ focusState.active(child);
+ FVERIFY();
+
+ child->setParentItem(0);
+ focusState[child].set(true, false);
+ focusState.active(0);
+ FVERIFY();
+ }
+
+ // Different parent, same focus scope
+ {
+ QSGCanvas canvas;
+ QSGItem *child = new TestItem(canvas.rootItem());
+ QSGItem *child2 = new TestItem(canvas.rootItem());
+
+ FocusState focusState;
+ focusState << child << child2;
+ FVERIFY();
+
+ child->setFocus(true);
+ focusState[child].set(true, true);
+ focusState.active(child);
+ FVERIFY();
+
+ child->setParentItem(child2);
+ FVERIFY();
+ }
+
+ // Different parent, different focus scope
+ {
+ QSGCanvas canvas;
+ QSGItem *child = new TestItem(canvas.rootItem());
+ QSGItem *child2 = new TestFocusScope(canvas.rootItem());
+ QSGItem *item = new TestItem(child);
+
+ FocusState focusState;
+ focusState << child << child2 << item;
+ FVERIFY();
+
+ item->setFocus(true);
+ focusState[item].set(true, true);
+ focusState.active(item);
+ FVERIFY();
+
+ item->setParentItem(child2);
+ focusState[item].set(true, false);
+ focusState.active(0);
+ FVERIFY();
+ }
+ {
+ QSGCanvas canvas;
+ QSGItem *child = new TestItem(canvas.rootItem());
+ QSGItem *child2 = new TestFocusScope(canvas.rootItem());
+ QSGItem *item = new TestItem(child2);
+
+ FocusState focusState;
+ focusState << child << child2 << item;
+ FVERIFY();
+
+ item->setFocus(true);
+ focusState[item].set(true, false);
+ focusState.active(0);
+ FVERIFY();
+
+ item->setParentItem(child);
+ focusState[item].set(true, true);
+ focusState.active(item);
+ FVERIFY();
+ }
+ {
+ QSGCanvas canvas;
+ QSGItem *child = new TestItem(canvas.rootItem());
+ QSGItem *child2 = new TestFocusScope(canvas.rootItem());
+ QSGItem *item = new TestItem(child2);
+
+ FocusState focusState;
+ focusState << child << child2 << item;
+ FVERIFY();
+
+ child->setFocus(true);
+ item->setFocus(true);
+ focusState[child].set(true, true);
+ focusState[item].set(true, false);
+ focusState.active(child);
+ FVERIFY();
+
+ item->setParentItem(child);
+ focusState[item].set(false, false);
+ FVERIFY();
+ }
+
+}
+
+void tst_qsgitem::constructor()
+{
+ QSGItem *root = new QSGItem;
+ QVERIFY(root->parent() == 0);
+ QVERIFY(root->parentItem() == 0);
+
+ QSGItem *child1 = new QSGItem(root);
+ QVERIFY(child1->parent() == root);
+ QVERIFY(child1->parentItem() == root);
+ QCOMPARE(root->childItems().count(), 1);
+ QCOMPARE(root->childItems().at(0), child1);
+
+ QSGItem *child2 = new QSGItem(root);
+ QVERIFY(child2->parent() == root);
+ QVERIFY(child2->parentItem() == root);
+ QCOMPARE(root->childItems().count(), 2);
+ QCOMPARE(root->childItems().at(0), child1);
+ QCOMPARE(root->childItems().at(1), child2);
+
+ delete root;
+}
+
+void tst_qsgitem::setParentItem()
+{
+ QSGItem *root = new QSGItem;
+ QVERIFY(root->parent() == 0);
+ QVERIFY(root->parentItem() == 0);
+
+ QSGItem *child1 = new QSGItem;
+ QVERIFY(child1->parent() == 0);
+ QVERIFY(child1->parentItem() == 0);
+
+ child1->setParentItem(root);
+ QVERIFY(child1->parent() == 0);
+ QVERIFY(child1->parentItem() == root);
+ QCOMPARE(root->childItems().count(), 1);
+ QCOMPARE(root->childItems().at(0), child1);
+
+ QSGItem *child2 = new QSGItem;
+ QVERIFY(child2->parent() == 0);
+ QVERIFY(child2->parentItem() == 0);
+ child2->setParentItem(root);
+ QVERIFY(child2->parent() == 0);
+ QVERIFY(child2->parentItem() == root);
+ QCOMPARE(root->childItems().count(), 2);
+ QCOMPARE(root->childItems().at(0), child1);
+ QCOMPARE(root->childItems().at(1), child2);
+
+ child1->setParentItem(0);
+ QVERIFY(child1->parent() == 0);
+ QVERIFY(child1->parentItem() == 0);
+ QCOMPARE(root->childItems().count(), 1);
+ QCOMPARE(root->childItems().at(0), child2);
+
+ delete root;
+
+ QVERIFY(child1->parent() == 0);
+ QVERIFY(child1->parentItem() == 0);
+ QVERIFY(child2->parent() == 0);
+ QVERIFY(child2->parentItem() == 0);
+
+ delete child1;
+ delete child2;
+}
+
+void tst_qsgitem::visible()
+{
+ QSGItem *root = new QSGItem;
+
+ QSGItem *child1 = new QSGItem;
+ child1->setParentItem(root);
+
+ QSGItem *child2 = new QSGItem;
+ child2->setParentItem(root);
+
+ QVERIFY(child1->isVisible());
+ QVERIFY(child2->isVisible());
+
+ root->setVisible(false);
+ QVERIFY(!child1->isVisible());
+ QVERIFY(!child2->isVisible());
+
+ root->setVisible(true);
+ QVERIFY(child1->isVisible());
+ QVERIFY(child2->isVisible());
+
+ child1->setVisible(false);
+ QVERIFY(!child1->isVisible());
+ QVERIFY(child2->isVisible());
+
+ child2->setParentItem(child1);
+ QVERIFY(!child1->isVisible());
+ QVERIFY(!child2->isVisible());
+
+ child2->setParentItem(root);
+ QVERIFY(!child1->isVisible());
+ QVERIFY(child2->isVisible());
+
+ delete root;
+ delete child1;
+ delete child2;
+}
+
+void tst_qsgitem::enabled()
+{
+ QSGItem *root = new QSGItem;
+
+ QSGItem *child1 = new QSGItem;
+ child1->setParentItem(root);
+
+ QSGItem *child2 = new QSGItem;
+ child2->setParentItem(root);
+
+ QVERIFY(child1->isEnabled());
+ QVERIFY(child2->isEnabled());
+
+ root->setEnabled(false);
+ QVERIFY(!child1->isEnabled());
+ QVERIFY(!child2->isEnabled());
+
+ root->setEnabled(true);
+ QVERIFY(child1->isEnabled());
+ QVERIFY(child2->isEnabled());
+
+ child1->setEnabled(false);
+ QVERIFY(!child1->isEnabled());
+ QVERIFY(child2->isEnabled());
+
+ child2->setParentItem(child1);
+ QVERIFY(!child1->isEnabled());
+ QVERIFY(!child2->isEnabled());
+
+ child2->setParentItem(root);
+ QVERIFY(!child1->isEnabled());
+ QVERIFY(child2->isEnabled());
+
+ delete root;
+ delete child1;
+ delete child2;
+}
+
+void tst_qsgitem::mouseGrab()
+{
+ QSGCanvas *canvas = new QSGCanvas;
+ canvas->resize(200, 200);
+ canvas->show();
+
+ TestItem *child1 = new TestItem;
+ child1->setAcceptedMouseButtons(Qt::LeftButton);
+ child1->setSize(QSizeF(200, 100));
+ child1->setParentItem(canvas->rootItem());
+
+ TestItem *child2 = new TestItem;
+ child2->setAcceptedMouseButtons(Qt::LeftButton);
+ child2->setY(100);
+ child2->setSize(QSizeF(200, 100));
+ child2->setParentItem(canvas->rootItem());
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QVERIFY(canvas->mouseGrabberItem() == child1);
+ QCOMPARE(child1->pressCount, 1);
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QVERIFY(canvas->mouseGrabberItem() == 0);
+ QCOMPARE(child1->releaseCount, 1);
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QVERIFY(canvas->mouseGrabberItem() == child1);
+ QCOMPARE(child1->pressCount, 2);
+ child1->setEnabled(false);
+ QVERIFY(canvas->mouseGrabberItem() == 0);
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QCOMPARE(child1->releaseCount, 1);
+ child1->setEnabled(true);
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QVERIFY(canvas->mouseGrabberItem() == child1);
+ QCOMPARE(child1->pressCount, 3);
+ child1->setVisible(false);
+ QVERIFY(canvas->mouseGrabberItem() == 0);
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QCOMPARE(child1->releaseCount, 1);
+ child1->setVisible(true);
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QVERIFY(canvas->mouseGrabberItem() == child1);
+ QCOMPARE(child1->pressCount, 4);
+ child2->grabMouse();
+ QVERIFY(canvas->mouseGrabberItem() == child2);
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QCOMPARE(child1->releaseCount, 1);
+ QCOMPARE(child2->releaseCount, 1);
+
+ child2->grabMouse();
+ QVERIFY(canvas->mouseGrabberItem() == child2);
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QCOMPARE(child1->pressCount, 4);
+ QCOMPARE(child2->pressCount, 1);
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50,50));
+ QCOMPARE(child1->releaseCount, 1);
+ QCOMPARE(child2->releaseCount, 2);
+
+ delete child1;
+ delete child2;
+ delete canvas;
+}
+
+
+QTEST_MAIN(tst_qsgitem)
+
+#include "tst_qsgitem.moc"
diff --git a/tests/auto/declarative/qsgitem2/data/childrenProperty.qml b/tests/auto/declarative/qsgitem2/data/childrenProperty.qml
new file mode 100644
index 0000000000..85ddbc1446
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/childrenProperty.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ property bool test1: root.children.length == 3
+ property bool test2: root.children[0] == item1
+ property bool test3: root.children[1] == item2
+ property bool test4: root.children[2] == item3
+ property bool test5: root.children[3] == null
+
+ children: [ Item { id: item1 }, Item { id: item2 }, Item { id: item3 } ]
+}
+
diff --git a/tests/auto/declarative/qsgitem2/data/childrenRect.qml b/tests/auto/declarative/qsgitem2/data/childrenRect.qml
new file mode 100644
index 0000000000..ebc57aefbe
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/childrenRect.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400
+ height: 400
+
+ property int childCount: 0;
+
+ Item {
+ objectName: "testItem"
+ width: childrenRect.width
+ height: childrenRect.height
+
+ Repeater {
+ id: repeater
+ model: childCount
+ delegate: Rectangle {
+ x: index*10
+ y: index*20
+ width: 10
+ height: 20
+
+ color: "red"
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/childrenRectBug.qml b/tests/auto/declarative/qsgitem2/data/childrenRectBug.qml
new file mode 100644
index 0000000000..86a4f19c5c
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/childrenRectBug.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400
+ height: 200
+
+ Item {
+ objectName: "theItem"
+ anchors.centerIn: parent
+ width: childrenRect.width
+ height: childrenRect.height
+ Rectangle {
+ id: text1
+ anchors.verticalCenter: parent.verticalCenter
+ width: 100; height: 100; color: "green"
+ }
+ Rectangle {
+ anchors.left: text1.right
+ anchors.verticalCenter: parent.verticalCenter
+ width: 100; height: 100; color: "green"
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/childrenRectBug2.qml b/tests/auto/declarative/qsgitem2/data/childrenRectBug2.qml
new file mode 100644
index 0000000000..6e80ed28af
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/childrenRectBug2.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.0
+
+Rectangle {
+ width:360;
+ height: 200
+
+ Item {
+ objectName: "theItem"
+ anchors.centerIn: parent
+ width: childrenRect.width
+ height: childrenRect.height
+ Rectangle {
+ id: header1
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ width: 100; height: 50
+ color: "green"
+ }
+ Rectangle {
+ id: text1
+ anchors.top: header1.bottom
+ anchors.topMargin: 10
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: 100; height: 50
+ color: "blue"
+ }
+ }
+
+ states: [
+ State {
+ name: "row"
+ AnchorChanges {
+ target: header1
+ anchors.horizontalCenter: undefined
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.top: undefined
+ }
+ AnchorChanges {
+ target: text1
+ anchors.horizontalCenter: undefined
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.top: undefined
+ anchors.left: header1.right
+ }
+ PropertyChanges {
+ target: text1
+ anchors.leftMargin: 10
+ anchors.topMargin: 0
+ }
+ }
+ ]
+}
diff --git a/tests/auto/declarative/qsgitem2/data/childrenRectBug3.qml b/tests/auto/declarative/qsgitem2/data/childrenRectBug3.qml
new file mode 100644
index 0000000000..518e76509e
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/childrenRectBug3.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 300
+ height: 300
+
+ Rectangle {
+ height: childrenRect.height
+
+ Repeater {
+ model: 1
+ Rectangle { }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/implicitsize.qml b/tests/auto/declarative/qsgitem2/data/implicitsize.qml
new file mode 100644
index 0000000000..cc6aaf7d60
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/implicitsize.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Item {
+ implicitWidth: 200
+ implicitHeight: 100
+
+ width: 80
+ height: 60
+
+ function resetSize() {
+ width = undefined
+ height = undefined
+ }
+
+ function changeImplicit() {
+ implicitWidth = 150
+ implicitHeight = 80
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/keynavigationtest.qml b/tests/auto/declarative/qsgitem2/data/keynavigationtest.qml
new file mode 100644
index 0000000000..aacb621fb0
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/keynavigationtest.qml
@@ -0,0 +1,87 @@
+import QtQuick 2.0
+
+Grid {
+ columns: 2
+ width: 100; height: 100
+ function verify() {
+ if (item1.KeyNavigation.right != item2)
+ return false;
+ if (item1.KeyNavigation.down != item3)
+ return false;
+ if (item1.KeyNavigation.tab != item2)
+ return false;
+ if (item1.KeyNavigation.backtab != item4)
+ return false;
+
+ if (item2.KeyNavigation.left != item1)
+ return false;
+ if (item2.KeyNavigation.down != item4)
+ return false;
+ if (item2.KeyNavigation.tab != item3)
+ return false;
+ if (item2.KeyNavigation.backtab != item1)
+ return false;
+
+ if (item3.KeyNavigation.right != item4)
+ return false;
+ if (item3.KeyNavigation.up != item1)
+ return false;
+ if (item3.KeyNavigation.tab != item4)
+ return false;
+ if (item3.KeyNavigation.backtab != item2)
+ return false;
+
+ if (item4.KeyNavigation.left != item3)
+ return false;
+ if (item4.KeyNavigation.up != item2)
+ return false;
+ if (item4.KeyNavigation.tab != item1)
+ return false;
+ if (item4.KeyNavigation.backtab != item3)
+ return false;
+
+ return true;
+ }
+
+ Rectangle {
+ id: item1
+ objectName: "item1"
+ focus: true
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.right: item2
+ KeyNavigation.down: item3
+ KeyNavigation.tab: item2
+ KeyNavigation.backtab: item4
+ }
+ Rectangle {
+ id: item2
+ objectName: "item2"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.left: item1
+ KeyNavigation.down: item4
+ KeyNavigation.tab: item3
+ KeyNavigation.backtab: item1
+ }
+ Rectangle {
+ id: item3
+ objectName: "item3"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.right: item4
+ KeyNavigation.up: item1
+ KeyNavigation.tab: item4
+ KeyNavigation.backtab: item2
+ }
+ Rectangle {
+ id: item4
+ objectName: "item4"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.left: item3
+ KeyNavigation.up: item2
+ KeyNavigation.tab: item1
+ KeyNavigation.backtab: item3
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/keynavigationtest_implicit.qml b/tests/auto/declarative/qsgitem2/data/keynavigationtest_implicit.qml
new file mode 100644
index 0000000000..92d4ae23de
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/keynavigationtest_implicit.qml
@@ -0,0 +1,68 @@
+import QtQuick 2.0
+
+Grid {
+ columns: 2
+ width: 100; height: 100
+ function verify() {
+ if (item1.KeyNavigation.tab != item2)
+ return false;
+ if (item1.KeyNavigation.backtab != item4)
+ return false;
+
+ if (item2.KeyNavigation.left != item1)
+ return false;
+ if (item2.KeyNavigation.down != item4)
+ return false;
+ if (item2.KeyNavigation.tab != item3)
+ return false;
+ if (item2.KeyNavigation.backtab != item1)
+ return false;
+
+ if (item3.KeyNavigation.right != item4)
+ return false;
+ if (item3.KeyNavigation.up != item1)
+ return false;
+ if (item3.KeyNavigation.tab != item4)
+ return false;
+ if (item3.KeyNavigation.backtab != item2)
+ return false;
+
+ return true;
+ }
+
+ Rectangle {
+ id: item1
+ objectName: "item1"
+ focus: true
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.tab: item2
+ KeyNavigation.backtab: item4
+ }
+ Rectangle {
+ id: item2
+ objectName: "item2"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.left: item1
+ KeyNavigation.down: item4
+ KeyNavigation.tab: item3
+ KeyNavigation.backtab: item1
+ }
+ Rectangle {
+ id: item3
+ objectName: "item3"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ KeyNavigation.right: item4
+ KeyNavigation.up: item1
+ KeyNavigation.tab: item4
+ KeyNavigation.backtab: item2
+ }
+ Rectangle {
+ id: item4
+ objectName: "item4"
+ width: 50; height: 50
+ color: focus ? "red" : "lightgray"
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/keyspriority.qml b/tests/auto/declarative/qsgitem2/data/keyspriority.qml
new file mode 100644
index 0000000000..114cf0488a
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/keyspriority.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+import Test 1.0
+
+KeyTestItem {
+ focus: true
+ Keys.onPressed: keysTestObject.keyPress(event.key, event.text, event.modifiers)
+ Keys.onReleased: { keysTestObject.keyRelease(event.key, event.text, event.modifiers); event.accepted = true; }
+ Keys.priority: keysTestObject.processLast ? Keys.AfterItem : Keys.BeforeItem
+}
diff --git a/tests/auto/declarative/qsgitem2/data/keystest.qml b/tests/auto/declarative/qsgitem2/data/keystest.qml
new file mode 100644
index 0000000000..c70e0061f5
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/keystest.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Item {
+ focus: true
+
+ property bool isEnabled: Keys.enabled
+
+ Keys.onPressed: keysTestObject.keyPress(event.key, event.text, event.modifiers)
+ Keys.onReleased: { keysTestObject.keyRelease(event.key, event.text, event.modifiers); event.accepted = true; }
+ Keys.onReturnPressed: keysTestObject.keyPress(event.key, "Return", event.modifiers)
+ Keys.onDigit0Pressed: keysTestObject.keyPress(event.key, event.text, event.modifiers)
+ Keys.onDigit9Pressed: { event.accepted = false; keysTestObject.keyPress(event.key, event.text, event.modifiers) }
+ Keys.onTabPressed: keysTestObject.keyPress(event.key, "Tab", event.modifiers)
+ Keys.onBacktabPressed: keysTestObject.keyPress(event.key, "Backtab", event.modifiers)
+ Keys.forwardTo: [ item2 ]
+ Keys.enabled: enableKeyHanding
+
+ Item {
+ id: item2
+ visible: forwardeeVisible
+ Keys.onPressed: keysTestObject.forwardedKey(event.key)
+ Keys.onReleased: keysTestObject.forwardedKey(event.key)
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/layoutmirroring.qml b/tests/auto/declarative/qsgitem2/data/layoutmirroring.qml
new file mode 100644
index 0000000000..036819740c
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/layoutmirroring.qml
@@ -0,0 +1,54 @@
+import QtQuick 2.0
+
+Item {
+ property bool childrenInherit: true
+ Item {
+ objectName: "mirrored1"
+ LayoutMirroring.enabled: true
+ LayoutMirroring.childrenInherit: parent.childrenInherit
+ Item {
+ Item {
+ objectName: "notMirrored1"
+ LayoutMirroring.enabled: false
+ Item {
+ objectName: "inheritedMirror1"
+ }
+ }
+ Item {
+ objectName: "inheritedMirror2"
+ }
+ }
+ }
+ Item {
+ objectName: "mirrored2"
+ LayoutMirroring.enabled: true
+ LayoutMirroring.childrenInherit: false
+ Item {
+ objectName: "notMirrored2"
+ }
+ }
+ Item {
+ LayoutMirroring.enabled: true
+ LayoutMirroring.childrenInherit: true
+ Loader {
+ id: loader
+ }
+ }
+ states: State {
+ name: "newContent"
+ PropertyChanges {
+ target: loader
+ sourceComponent: component
+ }
+ }
+ Component {
+ id: component
+ Item {
+ objectName: "notMirrored3"
+ LayoutMirroring.enabled: false
+ Item {
+ objectName: "inheritedMirror3"
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/mapCoordinates.qml b/tests/auto/declarative/qsgitem2/data/mapCoordinates.qml
new file mode 100644
index 0000000000..a5a073c1a0
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/mapCoordinates.qml
@@ -0,0 +1,43 @@
+import QtQuick 2.0
+
+Item {
+ id: root; objectName: "root"
+ width: 200; height: 200
+
+ Item { id: itemA; objectName: "itemA"; x: 50; y: 50 }
+
+ Item {
+ x: 50; y: 50
+ Item { id: itemB; objectName: "itemB"; x: 100; y: 100 }
+ }
+
+ function mapAToB(x, y) {
+ var pos = itemA.mapToItem(itemB, x, y)
+ return Qt.point(pos.x, pos.y)
+ }
+
+ function mapAFromB(x, y) {
+ var pos = itemA.mapFromItem(itemB, x, y)
+ return Qt.point(pos.x, pos.y)
+ }
+
+ function mapAToNull(x, y) {
+ var pos = itemA.mapToItem(null, x, y)
+ return Qt.point(pos.x, pos.y)
+ }
+
+ function mapAFromNull(x, y) {
+ var pos = itemA.mapFromItem(null, x, y)
+ return Qt.point(pos.x, pos.y)
+ }
+
+ function checkMapAToInvalid(x, y) {
+ var pos = itemA.mapToItem(1122, x, y)
+ return pos.x == undefined && pos.y == undefined
+ }
+
+ function checkMapAFromInvalid(x, y) {
+ var pos = itemA.mapFromItem(1122, x, y)
+ return pos.x == undefined && pos.y == undefined
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/mouseFocus.qml b/tests/auto/declarative/qsgitem2/data/mouseFocus.qml
new file mode 100644
index 0000000000..b120cc0263
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/mouseFocus.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+QGraphicsWidget {
+ size: "200x100"
+ focusPolicy: QGraphicsWidget.ClickFocus
+ Item {
+ objectName: "declarativeItem"
+ id: item
+ width: 200
+ height: 100
+ MouseArea {
+ anchors.fill: parent
+ onPressed: {
+ if (!item.focus) {
+ item.focus = true;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/propertychanges.qml b/tests/auto/declarative/qsgitem2/data/propertychanges.qml
new file mode 100644
index 0000000000..3fa5ea9c23
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/propertychanges.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+
+Item {
+ Item {
+ objectName: "item"
+ }
+ Item {
+ objectName: "parentItem"
+ }
+}
diff --git a/tests/auto/declarative/qsgitem2/data/qtbug_16871.qml b/tests/auto/declarative/qsgitem2/data/qtbug_16871.qml
new file mode 100644
index 0000000000..f1e7377730
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/qtbug_16871.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+ children: [ 10 ]
+}
diff --git a/tests/auto/declarative/qsgitem2/data/resourcesProperty.qml b/tests/auto/declarative/qsgitem2/data/resourcesProperty.qml
new file mode 100644
index 0000000000..b8f18bb375
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/resourcesProperty.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ property bool test1
+ property bool test2
+ property bool test3
+ property bool test4
+ property bool test5
+
+ Component.onCompleted: {
+ test1 = (root.resources.length >= 3)
+ test2 = root.resources[0] == item1
+ test3 = root.resources[1] == item2
+ test4 = root.resources[2] == item3
+ test5 = root.resources[10] == null
+ }
+
+ resources: [ Item { id: item1 }, Item { id: item2 }, Item { id: item3 } ]
+}
diff --git a/tests/auto/declarative/qsgitem2/data/transformCrash.qml b/tests/auto/declarative/qsgitem2/data/transformCrash.qml
new file mode 100644
index 0000000000..284e85f0e0
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/data/transformCrash.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+Item {
+ id: wrapper
+ width: 200
+ height: 200
+
+ QtObject {
+ id: object
+ }
+
+ Component.onCompleted: wrapper.transform = object
+}
diff --git a/tests/auto/declarative/qsgitem2/qsgitem.pro b/tests/auto/declarative/qsgitem2/qsgitem.pro
new file mode 100644
index 0000000000..b56eec3114
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/qsgitem.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgitem.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgitem2/tst_qsgitem.cpp b/tests/auto/declarative/qsgitem2/tst_qsgitem.cpp
new file mode 100644
index 0000000000..641abefa94
--- /dev/null
+++ b/tests/auto/declarative/qsgitem2/tst_qsgitem.cpp
@@ -0,0 +1,1322 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtTest/QSignalSpy>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgrectangle_p.h>
+#include <private/qsgitem_p.h>
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_QSGItem : public QObject
+
+{
+ Q_OBJECT
+public:
+ tst_QSGItem();
+
+private slots:
+ void initTestCase();
+ void keys();
+ void keysProcessingOrder();
+ void keyNavigation();
+ void keyNavigation_RightToLeft();
+ void keyNavigation_skipNotVisible();
+ void keyNavigation_implicitSetting();
+ void layoutMirroring();
+ void layoutMirroringIllegalParent();
+ void smooth();
+ void clip();
+ void mapCoordinates();
+ void mapCoordinates_data();
+ void propertyChanges();
+ void transforms();
+ void transforms_data();
+ void childrenRect();
+ void childrenRectBug();
+ void childrenRectBug2();
+ void childrenRectBug3();
+
+ void childrenProperty();
+ void resourcesProperty();
+ void mouseFocus();
+
+ void transformCrash();
+ void implicitSize();
+ void qtbug_16871();
+private:
+ QDeclarativeEngine engine;
+};
+
+template<typename T>
+T *findItem(QSGItem *parent, const QString &objectName)
+{
+ if (!parent)
+ return 0;
+
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->QSGItem::children().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
+ return static_cast<T*>(item);
+ item = findItem<T>(item, objectName);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+class KeysTestObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool processLast READ processLast NOTIFY processLastChanged)
+
+public:
+ KeysTestObject() : mKey(0), mModifiers(0), mForwardedKey(0), mLast(false) {}
+
+ void reset() {
+ mKey = 0;
+ mText = QString();
+ mModifiers = 0;
+ mForwardedKey = 0;
+ }
+
+ bool processLast() const { return mLast; }
+ void setProcessLast(bool b) {
+ if (b != mLast) {
+ mLast = b;
+ emit processLastChanged();
+ }
+ }
+
+public slots:
+ void keyPress(int key, QString text, int modifiers) {
+ mKey = key;
+ mText = text;
+ mModifiers = modifiers;
+ }
+ void keyRelease(int key, QString text, int modifiers) {
+ mKey = key;
+ mText = text;
+ mModifiers = modifiers;
+ }
+ void forwardedKey(int key) {
+ mForwardedKey = key;
+ }
+
+signals:
+ void processLastChanged();
+
+public:
+ int mKey;
+ QString mText;
+ int mModifiers;
+ int mForwardedKey;
+ bool mLast;
+
+private:
+};
+
+class KeyTestItem : public QSGItem
+{
+ Q_OBJECT
+public:
+ KeyTestItem(QSGItem *parent=0) : QSGItem(parent), mKey(0) {}
+
+protected:
+ void keyPressEvent(QKeyEvent *e) {
+ keyPressPreHandler(e);
+ if (e->isAccepted())
+ return;
+
+ mKey = e->key();
+
+ if (e->key() == Qt::Key_A)
+ e->accept();
+ else
+ e->ignore();
+
+ if (!e->isAccepted())
+ QSGItem::keyPressEvent(e);
+ }
+
+ void keyReleaseEvent(QKeyEvent *e) {
+ keyReleasePreHandler(e);
+
+ if (e->isAccepted())
+ return;
+
+ if (e->key() == Qt::Key_B)
+ e->accept();
+ else
+ e->ignore();
+
+ if (!e->isAccepted())
+ QSGItem::keyReleaseEvent(e);
+ }
+
+public:
+ int mKey;
+};
+
+QML_DECLARE_TYPE(KeyTestItem);
+
+
+tst_QSGItem::tst_QSGItem()
+{
+}
+
+void tst_QSGItem::initTestCase()
+{
+ qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem");
+}
+
+void tst_QSGItem::keys()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ KeysTestObject *testObject = new KeysTestObject;
+ canvas->rootContext()->setContextProperty("keysTestObject", testObject);
+
+ canvas->rootContext()->setContextProperty("enableKeyHanding", QVariant(true));
+ canvas->rootContext()->setContextProperty("forwardeeVisible", QVariant(true));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keystest.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QVERIFY(canvas->rootObject());
+ QCOMPARE(canvas->rootObject()->property("isEnabled").toBool(), true);
+
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_A));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_A));
+ QCOMPARE(testObject->mText, QLatin1String("A"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(!key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::ShiftModifier, "A", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_A));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_A));
+ QCOMPARE(testObject->mText, QLatin1String("A"));
+ QVERIFY(testObject->mModifiers == Qt::ShiftModifier);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_Return));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_Return));
+ QCOMPARE(testObject->mText, QLatin1String("Return"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, "0", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_0));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_0));
+ QCOMPARE(testObject->mText, QLatin1String("0"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_9, Qt::NoModifier, "9", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_9));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_9));
+ QCOMPARE(testObject->mText, QLatin1String("9"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(!key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_Tab));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_Tab));
+ QCOMPARE(testObject->mText, QLatin1String("Tab"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_Backtab));
+ QCOMPARE(testObject->mForwardedKey, int(Qt::Key_Backtab));
+ QCOMPARE(testObject->mText, QLatin1String("Backtab"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ canvas->rootContext()->setContextProperty("forwardeeVisible", QVariant(false));
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_A));
+ QCOMPARE(testObject->mForwardedKey, 0);
+ QCOMPARE(testObject->mText, QLatin1String("A"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(!key.isAccepted());
+
+ testObject->reset();
+
+ canvas->rootContext()->setContextProperty("enableKeyHanding", QVariant(false));
+ QCOMPARE(canvas->rootObject()->property("isEnabled").toBool(), false);
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, 0);
+ QVERIFY(!key.isAccepted());
+
+ canvas->rootContext()->setContextProperty("enableKeyHanding", QVariant(true));
+ QCOMPARE(canvas->rootObject()->property("isEnabled").toBool(), true);
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_Return));
+ QVERIFY(key.isAccepted());
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGItem::keysProcessingOrder()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ KeysTestObject *testObject = new KeysTestObject;
+ canvas->rootContext()->setContextProperty("keysTestObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keyspriority.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ KeyTestItem *testItem = qobject_cast<KeyTestItem*>(canvas->rootObject());
+ QVERIFY(testItem);
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_A));
+ QCOMPARE(testObject->mText, QLatin1String("A"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ testObject->setProcessLast(true);
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, 0);
+ QVERIFY(key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier, "B", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, int(Qt::Key_B));
+ QCOMPARE(testObject->mText, QLatin1String("B"));
+ QVERIFY(testObject->mModifiers == Qt::NoModifier);
+ QVERIFY(!key.isAccepted());
+
+ testObject->reset();
+
+ key = QKeyEvent(QEvent::KeyRelease, Qt::Key_B, Qt::NoModifier, "B", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QCOMPARE(testObject->mKey, 0);
+ QVERIFY(key.isAccepted());
+
+ delete canvas;
+ delete testObject;
+}
+
+QSGItemPrivate *childPrivate(QSGItem *rootItem, const char * itemString)
+{
+ QSGItem *item = findItem<QSGItem>(rootItem, QString(QLatin1String(itemString)));
+ QSGItemPrivate* itemPrivate = QSGItemPrivate::get(item);
+ return itemPrivate;
+}
+
+QVariant childProperty(QSGItem *rootItem, const char * itemString, const char * property)
+{
+ QSGItem *item = findItem<QSGItem>(rootItem, QString(QLatin1String(itemString)));
+ return item->property(property);
+}
+
+bool anchorsMirrored(QSGItem *rootItem, const char * itemString)
+{
+ QSGItem *item = findItem<QSGItem>(rootItem, QString(QLatin1String(itemString)));
+ QSGItemPrivate* itemPrivate = QSGItemPrivate::get(item);
+ return itemPrivate->anchors()->mirrored();
+}
+
+void tst_QSGItem::layoutMirroring()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/layoutmirroring.qml"));
+ canvas->show();
+
+ QSGItem *rootItem = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(rootItem);
+ QSGItemPrivate *rootPrivate = QSGItemPrivate::get(rootItem);
+ QVERIFY(rootPrivate);
+
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true);
+
+ QCOMPARE(anchorsMirrored(rootItem, "mirrored1"), true);
+ QCOMPARE(anchorsMirrored(rootItem, "mirrored2"), true);
+ QCOMPARE(anchorsMirrored(rootItem, "notMirrored1"), false);
+ QCOMPARE(anchorsMirrored(rootItem, "notMirrored2"), false);
+ QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror1"), true);
+ QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror2"), true);
+
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true);
+
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->isMirrorImplicit, false);
+ QCOMPARE(childPrivate(rootItem, "mirrored2")->isMirrorImplicit, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->isMirrorImplicit, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit, true);
+
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent, true);
+ QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent, true);
+ QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent, true);
+
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem, true);
+ QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem, false);
+
+ // load dynamic content using Loader that needs to inherit mirroring
+ rootItem->setProperty("state", "newContent");
+ QCOMPARE(childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror, true);
+
+ QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror, true);
+
+ QCOMPARE(childPrivate(rootItem, "notMirrored3")->isMirrorImplicit, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit, true);
+
+ QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent, true);
+
+ QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false);
+
+ // disable inheritance
+ rootItem->setProperty("childrenInherit", false);
+
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
+
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, false);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, false);
+
+ // re-enable inheritance
+ rootItem->setProperty("childrenInherit", true);
+
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
+
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true);
+ QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true);
+
+ //
+ // dynamic parenting
+ //
+ QSGItem *parentItem1 = new QSGItem();
+ QSGItemPrivate::get(parentItem1)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true
+ QSGItemPrivate::get(parentItem1)->isMirrorImplicit = false;
+ QSGItemPrivate::get(parentItem1)->inheritMirrorFromItem = true; // LayoutMirroring.childrenInherit: true
+ QSGItemPrivate::get(parentItem1)->resolveLayoutMirror();
+
+ // inherit in constructor
+ QSGItem *childItem1 = new QSGItem(parentItem1);
+ QCOMPARE(QSGItemPrivate::get(childItem1)->effectiveLayoutMirror, true);
+ QCOMPARE(QSGItemPrivate::get(childItem1)->inheritMirrorFromParent, true);
+
+ // inherit through a parent change
+ QSGItem *childItem2 = new QSGItem();
+ QCOMPARE(QSGItemPrivate::get(childItem2)->effectiveLayoutMirror, false);
+ QCOMPARE(QSGItemPrivate::get(childItem2)->inheritMirrorFromParent, false);
+ childItem2->setParentItem(parentItem1);
+ QCOMPARE(QSGItemPrivate::get(childItem2)->effectiveLayoutMirror, true);
+ QCOMPARE(QSGItemPrivate::get(childItem2)->inheritMirrorFromParent, true);
+
+ // stop inherting through a parent change
+ QSGItem *parentItem2 = new QSGItem();
+ QSGItemPrivate::get(parentItem2)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true
+ QSGItemPrivate::get(parentItem2)->resolveLayoutMirror();
+ childItem2->setParentItem(parentItem2);
+ QCOMPARE(QSGItemPrivate::get(childItem2)->effectiveLayoutMirror, false);
+ QCOMPARE(QSGItemPrivate::get(childItem2)->inheritMirrorFromParent, false);
+
+ delete parentItem1;
+ delete parentItem2;
+}
+
+void tst_QSGItem::layoutMirroringIllegalParent()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; QtObject { LayoutMirroring.enabled: true; LayoutMirroring.childrenInherit: true }", QUrl::fromLocalFile(""));
+ QTest::ignoreMessage(QtWarningMsg, "file::1:21: QML QtObject: LayoutDirection attached property only works with Items");
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+}
+
+void tst_QSGItem::keyNavigation()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keynavigationtest.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QSGItem *item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ QVariant result;
+ QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "verify",
+ Q_RETURN_ARG(QVariant, result)));
+ QVERIFY(result.toBool());
+
+ // right
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // down
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // left
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // up
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // tab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // backtab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ delete canvas;
+}
+
+void tst_QSGItem::keyNavigation_RightToLeft()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keynavigationtest.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QSGItem *rootItem = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(rootItem);
+ QSGItemPrivate* rootItemPrivate = QSGItemPrivate::get(rootItem);
+
+ rootItemPrivate->effectiveLayoutMirror = true; // LayoutMirroring.mirror: true
+ rootItemPrivate->isMirrorImplicit = false;
+ rootItemPrivate->inheritMirrorFromItem = true; // LayoutMirroring.inherit: true
+ rootItemPrivate->resolveLayoutMirror();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QSGItem *item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ QVariant result;
+ QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "verify",
+ Q_RETURN_ARG(QVariant, result)));
+ QVERIFY(result.toBool());
+
+ // right
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // left
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ delete canvas;
+}
+
+void tst_QSGItem::keyNavigation_skipNotVisible()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keynavigationtest.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QSGItem *item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // Set item 2 to not visible
+ item = findItem<QSGItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ item->setVisible(false);
+ QVERIFY(!item->isVisible());
+
+ // right
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // tab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // backtab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ //Set item 3 to not visible
+ item = findItem<QSGItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ item->setVisible(false);
+ QVERIFY(!item->isVisible());
+
+ // tab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // backtab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ delete canvas;
+}
+
+void tst_QSGItem::keyNavigation_implicitSetting()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keynavigationtest_implicit.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QSGItem *item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ QVariant result;
+ QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "verify",
+ Q_RETURN_ARG(QVariant, result)));
+ QVERIFY(result.toBool());
+
+ // right
+ QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item1
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // down
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // move to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // left
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // up
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item2");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // tab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // back to item4
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item4");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // backtab
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
+ QApplication::sendEvent(canvas, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QSGItem>(canvas->rootObject(), "item3");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ delete canvas;
+}
+
+void tst_QSGItem::smooth()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; Item { smooth: false; }", QUrl::fromLocalFile(""));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QSignalSpy spy(item, SIGNAL(smoothChanged(bool)));
+
+ QVERIFY(item);
+ QVERIFY(!item->smooth());
+
+ item->setSmooth(true);
+ QVERIFY(item->smooth());
+ QCOMPARE(spy.count(),1);
+ QList<QVariant> arguments = spy.first();
+ QVERIFY(arguments.count() == 1);
+ QVERIFY(arguments.at(0).toBool() == true);
+
+ item->setSmooth(true);
+ QCOMPARE(spy.count(),1);
+
+ item->setSmooth(false);
+ QVERIFY(!item->smooth());
+ QCOMPARE(spy.count(),2);
+ item->setSmooth(false);
+ QCOMPARE(spy.count(),2);
+
+ delete item;
+}
+
+void tst_QSGItem::clip()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0\nItem { clip: false\n }", QUrl::fromLocalFile(""));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QSignalSpy spy(item, SIGNAL(clipChanged(bool)));
+
+ QVERIFY(item);
+ QVERIFY(!item->clip());
+
+ item->setClip(true);
+ QVERIFY(item->clip());
+
+ QList<QVariant> arguments = spy.first();
+ QVERIFY(arguments.count() == 1);
+ QVERIFY(arguments.at(0).toBool() == true);
+
+ QCOMPARE(spy.count(),1);
+ item->setClip(true);
+ QCOMPARE(spy.count(),1);
+
+ item->setClip(false);
+ QVERIFY(!item->clip());
+ QCOMPARE(spy.count(),2);
+ item->setClip(false);
+ QCOMPARE(spy.count(),2);
+
+ delete item;
+}
+
+void tst_QSGItem::mapCoordinates()
+{
+ QFETCH(int, x);
+ QFETCH(int, y);
+
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(300, 300);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/mapCoordinates.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QSGItem *root = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(root != 0);
+ QSGItem *a = findItem<QSGItem>(canvas->rootObject(), "itemA");
+ QVERIFY(a != 0);
+ QSGItem *b = findItem<QSGItem>(canvas->rootObject(), "itemB");
+ QVERIFY(b != 0);
+
+ QVariant result;
+
+ QVERIFY(QMetaObject::invokeMethod(root, "mapAToB",
+ Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
+ QCOMPARE(result.value<QPointF>(), qobject_cast<QSGItem*>(a)->mapToItem(b, QPointF(x, y)));
+
+ QVERIFY(QMetaObject::invokeMethod(root, "mapAFromB",
+ Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
+ QCOMPARE(result.value<QPointF>(), qobject_cast<QSGItem*>(a)->mapFromItem(b, QPointF(x, y)));
+
+ QVERIFY(QMetaObject::invokeMethod(root, "mapAToNull",
+ Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
+ QCOMPARE(result.value<QPointF>(), qobject_cast<QSGItem*>(a)->mapToScene(QPointF(x, y)));
+
+ QVERIFY(QMetaObject::invokeMethod(root, "mapAFromNull",
+ Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
+ QCOMPARE(result.value<QPointF>(), qobject_cast<QSGItem*>(a)->mapFromScene(QPointF(x, y)));
+
+ QString warning1 = QUrl::fromLocalFile(SRCDIR "/data/mapCoordinates.qml").toString() + ":7:5: QML Item: mapToItem() given argument \"1122\" which is neither null nor an Item";
+ QString warning2 = QUrl::fromLocalFile(SRCDIR "/data/mapCoordinates.qml").toString() + ":7:5: QML Item: mapFromItem() given argument \"1122\" which is neither null nor an Item";
+
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
+ QVERIFY(QMetaObject::invokeMethod(root, "checkMapAToInvalid",
+ Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
+ QVERIFY(result.toBool());
+
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
+ QVERIFY(QMetaObject::invokeMethod(root, "checkMapAFromInvalid",
+ Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
+ QVERIFY(result.toBool());
+
+ delete canvas;
+}
+
+void tst_QSGItem::mapCoordinates_data()
+{
+ QTest::addColumn<int>("x");
+ QTest::addColumn<int>("y");
+
+ for (int i=-20; i<=20; i+=10)
+ QTest::newRow(QTest::toString(i)) << i << i;
+}
+
+void tst_QSGItem::transforms_data()
+{
+ QTest::addColumn<QByteArray>("qml");
+ QTest::addColumn<QMatrix>("matrix");
+ QTest::newRow("translate") << QByteArray("Translate { x: 10; y: 20 }")
+ << QMatrix(1,0,0,1,10,20);
+ QTest::newRow("rotation") << QByteArray("Rotation { angle: 90 }")
+ << QMatrix(0,1,-1,0,0,0);
+ QTest::newRow("scale") << QByteArray("Scale { xScale: 1.5; yScale: -2 }")
+ << QMatrix(1.5,0,0,-2,0,0);
+ QTest::newRow("sequence") << QByteArray("[ Translate { x: 10; y: 20 }, Scale { xScale: 1.5; yScale: -2 } ]")
+ << QMatrix(1,0,0,1,10,20) * QMatrix(1.5,0,0,-2,0,0);
+}
+
+void tst_QSGItem::transforms()
+{
+ QFAIL("This test has not been ported yet");
+ /*QFETCH(QByteArray, qml);
+ QFETCH(QMatrix, matrix);
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0\nItem { transform: "+qml+"}", QUrl::fromLocalFile(""));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(item->sceneMatrix(), matrix);*/
+}
+
+void tst_QSGItem::childrenProperty()
+{
+ QDeclarativeComponent component(&engine, SRCDIR "/data/childrenProperty.qml");
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ QCOMPARE(o->property("test1").toBool(), true);
+ QCOMPARE(o->property("test2").toBool(), true);
+ QCOMPARE(o->property("test3").toBool(), true);
+ QCOMPARE(o->property("test4").toBool(), true);
+ QCOMPARE(o->property("test5").toBool(), true);
+ delete o;
+}
+
+void tst_QSGItem::resourcesProperty()
+{
+ QDeclarativeComponent component(&engine, SRCDIR "/data/resourcesProperty.qml");
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ QCOMPARE(o->property("test1").toBool(), true);
+ QCOMPARE(o->property("test2").toBool(), true);
+ QCOMPARE(o->property("test3").toBool(), true);
+ QCOMPARE(o->property("test4").toBool(), true);
+ QCOMPARE(o->property("test5").toBool(), true);
+ delete o;
+}
+
+void tst_QSGItem::mouseFocus()
+{
+ QSGView *canvas = new QSGView(0);
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/mouseFocus.qml"));
+ canvas->show();
+ QVERIFY(canvas->rootObject());
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
+
+ QSGItem *item = findItem<QSGItem>(canvas->rootObject(), "declarativeItem");
+ QVERIFY(item);
+ QSignalSpy focusSpy(item, SIGNAL(activeFocusChanged(bool)));
+
+ QTest::mouseClick(canvas, Qt::LeftButton, 0, item->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ QCOMPARE(focusSpy.count(), 1);
+ QVERIFY(item->hasActiveFocus());
+
+ // make sure focusable graphics widget underneath does not steal focus
+ QTest::mouseClick(canvas, Qt::LeftButton, 0, item->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ QCOMPARE(focusSpy.count(), 1);
+ QVERIFY(item->hasActiveFocus());
+
+ item->setFocus(false);
+ QVERIFY(!item->hasActiveFocus());
+ QCOMPARE(focusSpy.count(), 2);
+ item->setFocus(true);
+ QCOMPARE(focusSpy.count(), 3);
+
+ delete canvas;
+}
+
+void tst_QSGItem::propertyChanges()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
+ canvas->show();
+
+ QEvent wa(QEvent::WindowActivate);
+ QApplication::sendEvent(canvas, &wa);
+ QFocusEvent fe(QEvent::FocusIn);
+ QApplication::sendEvent(canvas, &fe);
+
+ QSGItem *item = findItem<QSGItem>(canvas->rootObject(), "item");
+ QSGItem *parentItem = findItem<QSGItem>(canvas->rootObject(), "parentItem");
+
+ QVERIFY(item);
+ QVERIFY(parentItem);
+
+ QSignalSpy parentSpy(item, SIGNAL(parentChanged(QSGItem *)));
+ QSignalSpy widthSpy(item, SIGNAL(widthChanged()));
+ QSignalSpy heightSpy(item, SIGNAL(heightChanged()));
+ QSignalSpy baselineOffsetSpy(item, SIGNAL(baselineOffsetChanged(qreal)));
+ QSignalSpy childrenRectSpy(parentItem, SIGNAL(childrenRectChanged(QRectF)));
+ QSignalSpy focusSpy(item, SIGNAL(focusChanged(bool)));
+ QSignalSpy wantsFocusSpy(parentItem, SIGNAL(activeFocusChanged(bool)));
+ QSignalSpy childrenChangedSpy(parentItem, SIGNAL(childrenChanged()));
+ QSignalSpy xSpy(item, SIGNAL(xChanged()));
+ QSignalSpy ySpy(item, SIGNAL(yChanged()));
+
+ item->setParentItem(parentItem);
+ item->setWidth(100.0);
+ item->setHeight(200.0);
+ item->setFocus(true);
+ item->setBaselineOffset(10.0);
+
+ QCOMPARE(item->parentItem(), parentItem);
+ QCOMPARE(parentSpy.count(),1);
+ QList<QVariant> parentArguments = parentSpy.first();
+ QVERIFY(parentArguments.count() == 1);
+ QCOMPARE(item->parentItem(), qvariant_cast<QSGItem *>(parentArguments.at(0)));
+ QCOMPARE(childrenChangedSpy.count(),1);
+
+ item->setParentItem(parentItem);
+ QCOMPARE(childrenChangedSpy.count(),1);
+
+ QCOMPARE(item->width(), 100.0);
+ QCOMPARE(widthSpy.count(),1);
+
+ QCOMPARE(item->height(), 200.0);
+ QCOMPARE(heightSpy.count(),1);
+
+ QCOMPARE(item->baselineOffset(), 10.0);
+ QCOMPARE(baselineOffsetSpy.count(),1);
+ QList<QVariant> baselineOffsetArguments = baselineOffsetSpy.first();
+ QVERIFY(baselineOffsetArguments.count() == 1);
+ QCOMPARE(item->baselineOffset(), baselineOffsetArguments.at(0).toReal());
+
+ QCOMPARE(parentItem->childrenRect(), QRectF(0.0,0.0,100.0,200.0));
+ QCOMPARE(childrenRectSpy.count(),2);
+ QList<QVariant> childrenRectArguments = childrenRectSpy.at(1);
+ QVERIFY(childrenRectArguments.count() == 1);
+ QCOMPARE(parentItem->childrenRect(), childrenRectArguments.at(0).toRectF());
+
+ QCOMPARE(item->hasActiveFocus(), true);
+ QCOMPARE(focusSpy.count(),1);
+ QList<QVariant> focusArguments = focusSpy.first();
+ QVERIFY(focusArguments.count() == 1);
+ QCOMPARE(focusArguments.at(0).toBool(), true);
+
+ QCOMPARE(parentItem->hasActiveFocus(), false);
+ QCOMPARE(parentItem->hasFocus(), false);
+ QCOMPARE(wantsFocusSpy.count(),0);
+
+ item->setX(10.0);
+ QCOMPARE(item->x(), 10.0);
+ QCOMPARE(xSpy.count(), 1);
+
+ item->setY(10.0);
+ QCOMPARE(item->y(), 10.0);
+ QCOMPARE(ySpy.count(), 1);
+
+ delete canvas;
+}
+
+void tst_QSGItem::childrenRect()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/childrenRect.qml"));
+ canvas->show();
+
+ QSGItem *o = canvas->rootObject();
+ QSGItem *item = o->findChild<QSGItem*>("testItem");
+ QCOMPARE(item->width(), qreal(0));
+ QCOMPARE(item->height(), qreal(0));
+
+ o->setProperty("childCount", 1);
+ QCOMPARE(item->width(), qreal(10));
+ QCOMPARE(item->height(), qreal(20));
+
+ o->setProperty("childCount", 5);
+ QCOMPARE(item->width(), qreal(50));
+ QCOMPARE(item->height(), qreal(100));
+
+ o->setProperty("childCount", 0);
+ QCOMPARE(item->width(), qreal(0));
+ QCOMPARE(item->height(), qreal(0));
+
+ delete o;
+ delete canvas;
+}
+
+// QTBUG-11383
+void tst_QSGItem::childrenRectBug()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/childrenRectBug.qml"));
+ canvas->show();
+
+ QSGItem *o = canvas->rootObject();
+ QSGItem *item = o->findChild<QSGItem*>("theItem");
+ QCOMPARE(item->width(), qreal(200));
+ QCOMPARE(item->height(), qreal(100));
+ QCOMPARE(item->x(), qreal(100));
+
+ delete canvas;
+}
+
+// QTBUG-11465
+void tst_QSGItem::childrenRectBug2()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/childrenRectBug2.qml"));
+ canvas->show();
+
+ QSGRectangle *rect = qobject_cast<QSGRectangle*>(canvas->rootObject());
+ QVERIFY(rect);
+ QSGItem *item = rect->findChild<QSGItem*>("theItem");
+ QCOMPARE(item->width(), qreal(100));
+ QCOMPARE(item->height(), qreal(110));
+ QCOMPARE(item->x(), qreal(130));
+
+ QSGItemPrivate *rectPrivate = QSGItemPrivate::get(rect);
+ rectPrivate->setState("row");
+ QCOMPARE(item->width(), qreal(210));
+ QCOMPARE(item->height(), qreal(50));
+ QCOMPARE(item->x(), qreal(75));
+
+ delete canvas;
+}
+
+// QTBUG-12722
+void tst_QSGItem::childrenRectBug3()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/childrenRectBug3.qml"));
+ canvas->show();
+
+ //don't crash on delete
+ delete canvas;
+}
+
+// QTBUG-13893
+void tst_QSGItem::transformCrash()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/transformCrash.qml"));
+ canvas->show();
+
+ delete canvas;
+}
+
+void tst_QSGItem::implicitSize()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/implicitsize.qml"));
+ canvas->show();
+
+ QSGItem *item = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(item);
+ QCOMPARE(item->width(), qreal(80));
+ QCOMPARE(item->height(), qreal(60));
+
+ QCOMPARE(item->implicitWidth(), qreal(200));
+ QCOMPARE(item->implicitHeight(), qreal(100));
+
+ QMetaObject::invokeMethod(item, "resetSize");
+
+ QCOMPARE(item->width(), qreal(200));
+ QCOMPARE(item->height(), qreal(100));
+
+ QMetaObject::invokeMethod(item, "changeImplicit");
+
+ QCOMPARE(item->implicitWidth(), qreal(150));
+ QCOMPARE(item->implicitHeight(), qreal(80));
+ QCOMPARE(item->width(), qreal(150));
+ QCOMPARE(item->height(), qreal(80));
+
+ delete canvas;
+}
+
+void tst_QSGItem::qtbug_16871()
+{
+ QDeclarativeComponent component(&engine, SRCDIR "/data/qtbug_16871.qml");
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+ delete o;
+}
+
+QTEST_MAIN(tst_QSGItem)
+
+#include "tst_qsgitem.moc"
diff --git a/tests/auto/declarative/qsglistview/data/attachedSignals.qml b/tests/auto/declarative/qsglistview/data/attachedSignals.qml
new file mode 100644
index 0000000000..2c3c0bbada
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/attachedSignals.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+ListView {
+ id: view
+ width: 240; height: 320
+
+ property variant addedDelegates: []
+ property int removedDelegateCount
+
+ model: testModel
+
+ delegate: Rectangle {
+ width: 200; height: delegateHeight
+ border.width: 1
+ ListView.onAdd: {
+ var obj = ListView.view.addedDelegates
+ obj.push(model.name)
+ ListView.view.addedDelegates = obj
+ }
+ ListView.onRemove: {
+ view.removedDelegateCount += 1
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/displaylist.qml b/tests/auto/declarative/qsglistview/data/displaylist.qml
new file mode 100644
index 0000000000..c083da5aa5
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/displaylist.qml
@@ -0,0 +1,50 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ property real delegateHeight: 20
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: root.delegateHeight
+ Behavior on height { NumberAnimation {} }
+ width: 240
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ objectName: "displayText"
+ text: display
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ },
+ Component {
+ id: myHighlight
+ Rectangle { color: "green" }
+ }
+ ]
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ highlight: myHighlight
+ highlightMoveSpeed: 1000
+ highlightResizeSpeed: 1000
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/footer.qml b/tests/auto/declarative/qsglistview/data/footer.qml
new file mode 100644
index 0000000000..49e1944b6a
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/footer.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Rectangle {
+ function changeFooter() {
+ list.footer = footer2
+ }
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 240
+ Text {
+ text: index
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ footer: Text { objectName: "footer"; text: "Footer"; height: 30 }
+ }
+
+ Component {
+ id: footer2
+ Text { objectName: "footer2"; text: "Footer 2"; height: 20 }
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/header.qml b/tests/auto/declarative/qsglistview/data/header.qml
new file mode 100644
index 0000000000..455159f39d
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/header.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Rectangle {
+ function changeHeader() {
+ list.header = header2
+ }
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 30
+ width: 240
+ Text {
+ text: index
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 320
+ snapMode: ListView.SnapToItem
+ model: testModel
+ delegate: myDelegate
+ header: Text { objectName: "header"; text: "Header"; height: 20 }
+ }
+ Component {
+ id: header2
+ Text { objectName: "header2"; text: "Header 2"; height: 10 }
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/header1.qml b/tests/auto/declarative/qsglistview/data/header1.qml
new file mode 100644
index 0000000000..8ba6e57594
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/header1.qml
@@ -0,0 +1,33 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+
+ ListModel { id: testModel }
+
+ ListView {
+ id: list
+ objectName: "list"
+ width: parent.width
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ model: testModel
+ delegate: Text {
+ objectName: "wrapper"
+ font.pointSize: 20
+ text: index
+ }
+ footer: Rectangle {
+ width: parent.width
+ height: 40
+ color: "green"
+ }
+ header: Text { objectName: "header"; text: "Header" }
+ }
+
+ Component.onCompleted: {
+ for (var i=0; i<30; i++) testModel.append({"name" : i, "val": i})
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/headerfooter.qml b/tests/auto/declarative/qsglistview/data/headerfooter.qml
new file mode 100644
index 0000000000..30b7199445
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/headerfooter.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+ListView {
+ id: view
+ property bool horizontal: false
+ property bool rtl: false
+ width: 240
+ height: 320
+
+ orientation: horizontal ? ListView.Horizontal : ListView.Vertical
+ header: Rectangle {
+ objectName: "header"
+ width: horizontal ? 20 : view.width
+ height: horizontal ? view.height : 20
+ color: "red"
+ }
+ footer: Rectangle {
+ objectName: "footer"
+ width: horizontal ? 30 : view.width
+ height: horizontal ? view.height : 30
+ color: "blue"
+ }
+// model: testModel
+ delegate: Text { width: 30; height: 30; text: index + "(" + x + ")" }
+ layoutDirection: rtl ? Qt.RightToLeft : Qt.LeftToRight
+}
diff --git a/tests/auto/declarative/qsglistview/data/itemlist.qml b/tests/auto/declarative/qsglistview/data/itemlist.qml
new file mode 100644
index 0000000000..90dd59795b
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/itemlist.qml
@@ -0,0 +1,43 @@
+// This example demonstrates placing items in a view using
+// a VisualItemModel
+
+import QtQuick 2.0
+
+Rectangle {
+ color: "lightgray"
+ width: 240
+ height: 320
+
+ VisualItemModel {
+ id: itemModel
+ objectName: "itemModel"
+ Rectangle {
+ objectName: "item1"
+ height: ListView.view.height; width: view.width; color: "#FFFEF0"
+ Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item2"
+ height: ListView.view.height; width: view.width; color: "#F0FFF7"
+ Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item3"
+ height: ListView.view.height; width: view.width; color: "#F4F0FF"
+ Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ }
+
+ ListView {
+ id: view
+ objectName: "view"
+ anchors.fill: parent
+ anchors.bottomMargin: 30
+ model: itemModel
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightRangeMode: "StrictlyEnforceRange"
+ orientation: ListView.Horizontal
+ flickDeceleration: 2000
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/listview-enforcerange.qml b/tests/auto/declarative/qsglistview/data/listview-enforcerange.qml
new file mode 100644
index 0000000000..f1bf6c2b57
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/listview-enforcerange.qml
@@ -0,0 +1,55 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Item {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 240
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 120
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ }
+ }
+
+ Component {
+ id: myHighlight
+ Rectangle {
+ color: "lightsteelblue"
+ }
+ }
+
+ ListView {
+ id: list
+ objectName: "list"
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ highlight: myHighlight
+ preferredHighlightBegin: 100
+ preferredHighlightEnd: 100
+ highlightRangeMode: "StrictlyEnforceRange"
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/listview-initCurrent.qml b/tests/auto/declarative/qsglistview/data/listview-initCurrent.qml
new file mode 100644
index 0000000000..ee1a333de0
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/listview-initCurrent.qml
@@ -0,0 +1,51 @@
+import QtQuick 2.0
+
+Rectangle {
+ property int current: list.currentIndex
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 240
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 120
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ]
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ currentIndex: 20
+ width: 240
+ height: 320
+ keyNavigationWraps: testWrap
+ delegate: myDelegate
+ highlightMoveSpeed: 1000
+ model: testModel
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/listview-noCurrent.qml b/tests/auto/declarative/qsglistview/data/listview-noCurrent.qml
new file mode 100644
index 0000000000..079966d8e4
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/listview-noCurrent.qml
@@ -0,0 +1,50 @@
+import QtQuick 2.0
+
+Rectangle {
+ property int current: list.currentIndex
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 240
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 120
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+ ]
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ currentIndex: -1
+ width: 240
+ height: 320
+ delegate: myDelegate
+ highlightMoveSpeed: 1000
+ model: testModel
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/listview-sections.qml b/tests/auto/declarative/qsglistview/data/listview-sections.qml
new file mode 100644
index 0000000000..d5b8a4400d
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/listview-sections.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Item {
+ id: wrapper
+ objectName: "wrapper"
+ height: ListView.previousSection != ListView.section ? 40 : 20;
+ width: 240
+ Rectangle {
+ y: wrapper.ListView.previousSection != wrapper.ListView.section ? 20 : 0
+ height: 20
+ width: parent.width
+ color: wrapper.ListView.isCurrentItem ? "lightsteelblue" : "white"
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 100
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ objectName: "nextSection"
+ x: 150
+ text: wrapper.ListView.nextSection
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ }
+ Rectangle {
+ color: "#99bb99"
+ height: wrapper.ListView.previousSection != wrapper.ListView.section ? 20 : 0
+ width: parent.width
+ visible: wrapper.ListView.previousSection != wrapper.ListView.section ? true : false
+ Text { text: wrapper.ListView.section }
+ }
+ }
+ }
+ ]
+ ListView {
+ id: list
+ objectName: "list"
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ section.property: "number"
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/listview-sections_delegate.qml b/tests/auto/declarative/qsglistview/data/listview-sections_delegate.qml
new file mode 100644
index 0000000000..82f332c951
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/listview-sections_delegate.qml
@@ -0,0 +1,69 @@
+import QtQuick 2.0
+
+Rectangle {
+ property string sectionProperty: "number"
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: myDelegate
+ Item {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20;
+ width: 240
+ Rectangle {
+ height: 20
+ width: parent.width
+ color: wrapper.ListView.isCurrentItem ? "lightsteelblue" : "white"
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 100
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ objectName: "nextSection"
+ x: 150
+ text: wrapper.ListView.nextSection
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ }
+ ListView.onRemove: SequentialAnimation {
+ PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true }
+ NumberAnimation { target: wrapper; property: "height"; to: 0; duration: 100; easing.type: Easing.InOutQuad }
+ PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
+ }
+ }
+ }
+ ]
+ ListView {
+ id: list
+ objectName: "list"
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ section.property: sectionProperty
+ section.delegate: Rectangle {
+ objectName: "sect_" + section
+ color: "#99bb99"
+ height: 20
+ width: list.width
+ Text { text: section }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/listviewtest.qml b/tests/auto/declarative/qsglistview/data/listviewtest.qml
new file mode 100644
index 0000000000..832eaafa0f
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/listviewtest.qml
@@ -0,0 +1,132 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ width: 240
+ height: 320
+ color: "#ffffff"
+
+ property bool showHeader: false
+ property bool showFooter: false
+ property real hr: list.visibleArea.heightRatio
+ function heightRatio() {
+ return list.visibleArea.heightRatio
+ }
+
+ function checkProperties() {
+ testObject.error = false;
+ if (list.model != testModel) {
+ console.log("model property incorrect");
+ testObject.error = true;
+ }
+ if (!testObject.animate && list.delegate != myDelegate) {
+ console.log("delegate property incorrect - expected myDelegate");
+ testObject.error = true;
+ }
+ if (testObject.animate && list.delegate != animatedDelegate) {
+ console.log("delegate property incorrect - expected animatedDelegate");
+ testObject.error = true;
+ }
+ if (testObject.invalidHighlight && list.highlight != invalidHl) {
+ console.log("highlight property incorrect - expected invalidHl");
+ testObject.error = true;
+ }
+ if (!testObject.invalidHighlight && list.highlight != myHighlight) {
+ console.log("highlight property incorrect - expected myHighlight");
+ testObject.error = true;
+ }
+ }
+ resources: [
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 240
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 120
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ },
+ Component {
+ id: animatedDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 240
+ Text {
+ text: index
+ }
+ Text {
+ x: 30
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 120
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ Text {
+ x: 200
+ text: wrapper.y
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ ListView.onRemove: SequentialAnimation {
+ PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true }
+ NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 250; easing.type: "InOutQuad" }
+ PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
+
+ }
+ }
+ },
+ Component {
+ id: myHighlight
+ Rectangle { color: "green" }
+ },
+ Component {
+ id: invalidHl
+ SmoothedAnimation {}
+ },
+ Component {
+ id: headerFooter
+ Rectangle { height: 30; width: 240; color: "blue" }
+ }
+ ]
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 320
+ model: testModel
+ delegate: testObject.animate ? animatedDelegate : myDelegate
+ highlight: testObject.invalidHighlight ? invalidHl : myHighlight
+ highlightMoveSpeed: 1000
+ highlightResizeSpeed: 1000
+ cacheBuffer: testObject.cacheBuffer
+ header: root.showHeader ? headerFooter : null
+ footer: root.showFooter ? headerFooter : null
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/manual-highlight.qml b/tests/auto/declarative/qsglistview/data/manual-highlight.qml
new file mode 100644
index 0000000000..aac4599f01
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/manual-highlight.qml
@@ -0,0 +1,47 @@
+import QtQuick 2.0
+
+Item {
+
+ ListModel {
+ id: model
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ ListElement {
+ name: "Bob Brown"
+ number: "555 5845"
+ }
+ }
+
+ Component {
+ id: highlight
+ Rectangle {
+ objectName: "highlight"
+ width: 180; height: 20
+ color: "lightsteelblue"; radius: 5
+ y: list.currentItem.y+5
+ }
+ }
+
+ ListView {
+ id: list
+ objectName: "list"
+ anchors.fill: parent
+ model: model
+ delegate: Text { objectName: "wrapper"; text: name }
+
+ highlight: highlight
+ highlightFollowsCurrentItem: false
+ focus: true
+ }
+
+}
diff --git a/tests/auto/declarative/qsglistview/data/propertychangestest.qml b/tests/auto/declarative/qsglistview/data/propertychangestest.qml
new file mode 100644
index 0000000000..146f3f13b0
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/propertychangestest.qml
@@ -0,0 +1,71 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 180; height: 120; color: "white"
+ Component {
+ id: delegate
+ Item {
+ id: wrapper
+ width: 180; height: 40;
+ Column {
+ x: 5; y: 5
+ Text { text: '<b>Name:</b> ' + name }
+ Text { text: '<b>Number:</b> ' + number }
+ }
+ }
+ }
+ Component {
+ id: highlightRed
+ Rectangle {
+ color: "red"
+ radius: 10
+ opacity: 0.5
+ }
+ }
+ ListView {
+ objectName: "listView"
+ anchors.fill: parent
+ model: listModel
+ delegate: delegate
+ highlight: highlightRed
+ focus: true
+ highlightFollowsCurrentItem: true
+ preferredHighlightBegin: 0.0
+ preferredHighlightEnd: 0.0
+ highlightRangeMode: ListView.ApplyRange
+ keyNavigationWraps: true
+ cacheBuffer: 10
+ snapMode: ListView.SnapToItem
+ }
+
+ data:[
+ ListModel {
+ id: listModel
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ },
+ ListModel {
+ objectName: "alternateModel"
+ ListElement {
+ name: "Jack"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Mary"
+ number: "555 3264"
+ }
+ }
+ ]
+}
+
+
diff --git a/tests/auto/declarative/qsglistview/data/qtbug14821.qml b/tests/auto/declarative/qsglistview/data/qtbug14821.qml
new file mode 100644
index 0000000000..0a5e0acbb4
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/qtbug14821.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+ListView {
+ id: view
+ width: 300; height: 200
+ focus: true
+ keyNavigationWraps: true
+
+ model: 100
+
+ preferredHighlightBegin: 90
+ preferredHighlightEnd: 110
+
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ highlight: Component {
+ Rectangle {
+ border.color: "blue"
+ border.width: 3
+ color: "transparent"
+ width: 300; height: 15
+ }
+ }
+
+ delegate: Component {
+ Item {
+ height: 15 + (view.currentIndex == index ? 20 : 0)
+ width: 200
+ Text { text: 'Index: ' + index; anchors.verticalCenter: parent.verticalCenter }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/qtbug16037.qml b/tests/auto/declarative/qsglistview/data/qtbug16037.qml
new file mode 100644
index 0000000000..21faeb3f32
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/qtbug16037.qml
@@ -0,0 +1,37 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+
+ function setModel() {
+ listView.model = listModel1
+ }
+
+ ListModel {
+ id: listModel1
+ ListElement { text: "Apple" }
+ ListElement { text: "Banana" }
+ ListElement { text: "Orange" }
+ ListElement { text: "Coconut" }
+ }
+
+ Rectangle {
+ width: 200
+ height: listView.contentHeight
+ color: "yellow"
+ anchors.centerIn: parent
+
+ ListView {
+ id: listView
+ objectName: "listview"
+ anchors.fill: parent
+
+ delegate: Item {
+ width: 200
+ height: 20
+ Text { text: model.text; anchors.centerIn: parent }
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/rightToLeft.qml b/tests/auto/declarative/qsglistview/data/rightToLeft.qml
new file mode 100644
index 0000000000..6d77de26f4
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/rightToLeft.qml
@@ -0,0 +1,42 @@
+// This example demonstrates how item positioning
+// changes in right-to-left layout direction
+
+import QtQuick 2.0
+
+Rectangle {
+ color: "lightgray"
+ width: 640
+ height: 320
+
+ VisualItemModel {
+ id: itemModel
+ objectName: "itemModel"
+ Rectangle {
+ objectName: "item1"
+ height: view.height; width: 100; color: "#FFFEF0"
+ Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item2"
+ height: view.height; width: 200; color: "#F0FFF7"
+ Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item3"
+ height: view.height; width: 240; color: "#F4F0FF"
+ Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ }
+
+ ListView {
+ id: view
+ objectName: "view"
+ anchors.fill: parent
+ anchors.bottomMargin: 30
+ model: itemModel
+ highlightRangeMode: "StrictlyEnforceRange"
+ orientation: ListView.Horizontal
+ flickDeceleration: 2000
+ layoutDirection: Qt.RightToLeft
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/sizelessthan1.qml b/tests/auto/declarative/qsglistview/data/sizelessthan1.qml
new file mode 100644
index 0000000000..aa9dc20ae9
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/sizelessthan1.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 0.5
+ width: 240
+ color: ((index % 2) == 1 ? "red" : "blue")
+ }
+ }
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+ }
+}
diff --git a/tests/auto/declarative/qsglistview/data/strictlyenforcerange.qml b/tests/auto/declarative/qsglistview/data/strictlyenforcerange.qml
new file mode 100644
index 0000000000..7960ac4abb
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/data/strictlyenforcerange.qml
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+
+ListView {
+ id: list
+ objectName: "list"
+ width: 320
+ height: 480
+
+ function fillModel() {
+ list.model.append({"col": "red"});
+ list.currentIndex = list.count-1
+ list.model.append({"col": "blue"});
+ list.currentIndex = list.count-1
+ list.model.append({"col": "green"});
+ list.currentIndex = list.count-1
+ }
+
+ model: ListModel { id: listModel } // empty model
+ delegate: Rectangle { id: wrapper; objectName: "wrapper"; color: col; width: 300; height: 400 }
+ orientation: "Horizontal"
+ snapMode: "SnapToItem"
+ cacheBuffer: 1000
+
+ preferredHighlightBegin: 10
+ preferredHighlightEnd: 10
+
+ highlightRangeMode: "StrictlyEnforceRange"
+ focus: true
+}
diff --git a/tests/auto/declarative/qsglistview/incrementalmodel.cpp b/tests/auto/declarative/qsglistview/incrementalmodel.cpp
new file mode 100644
index 0000000000..cd0512fb06
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/incrementalmodel.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "incrementalmodel.h"
+#include <QApplication>
+#include <QDebug>
+
+IncrementalModel::IncrementalModel(QObject *parent)
+ : QAbstractListModel(parent), count(0)
+{
+ for (int i = 0; i < 100; ++i)
+ list.append("Item " + QString::number(i));
+}
+
+int IncrementalModel::rowCount(const QModelIndex & /* parent */) const
+{
+ return count;
+}
+
+QVariant IncrementalModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() >= list.size() || index.row() < 0)
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ return list.at(index.row());
+ return QVariant();
+}
+
+bool IncrementalModel::canFetchMore(const QModelIndex & /* index */) const
+{
+ if (count < list.size())
+ return true;
+ else
+ return false;
+}
+
+void IncrementalModel::fetchMore(const QModelIndex & /* index */)
+{
+ int remainder = list.size() - count;
+ int itemsToFetch = qMin(5, remainder);
+
+ beginInsertRows(QModelIndex(), count, count+itemsToFetch-1);
+
+ count += itemsToFetch;
+
+ endInsertRows();
+}
diff --git a/tests/auto/declarative/qsglistview/incrementalmodel.h b/tests/auto/declarative/qsglistview/incrementalmodel.h
new file mode 100644
index 0000000000..9c4e7ddd4a
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/incrementalmodel.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IncrementalModel_H
+#define IncrementalModel_H
+
+#include <QAbstractListModel>
+#include <QList>
+#include <QStringList>
+
+class IncrementalModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ IncrementalModel(QObject *parent = 0);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+protected:
+ bool canFetchMore(const QModelIndex &parent) const;
+ void fetchMore(const QModelIndex &parent);
+
+private:
+ QStringList list;
+ int count;
+};
+
+#endif
diff --git a/tests/auto/declarative/qsglistview/qsglistview.pro b/tests/auto/declarative/qsglistview/qsglistview.pro
new file mode 100644
index 0000000000..84b955eb55
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/qsglistview.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+macx:CONFIG -= app_bundle
+
+HEADERS += incrementalmodel.h
+SOURCES += tst_qsglistview.cpp incrementalmodel.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
diff --git a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp
new file mode 100644
index 0000000000..f697e61208
--- /dev/null
+++ b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp
@@ -0,0 +1,2698 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtGui/QStringListModel>
+#include <QtDeclarative/qsgview.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/private/qsgitem_p.h>
+#include <QtDeclarative/private/qsglistview_p.h>
+#include <QtDeclarative/private/qsgtext_p.h>
+#include <QtDeclarative/private/qsgvisualitemmodel_p.h>
+#include <QtDeclarative/private/qdeclarativelistmodel_p.h>
+#include <QtDeclarative/private/qlistmodelinterface_p.h>
+#include "../../../shared/util.h"
+#include "incrementalmodel.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_QSGListView : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QSGListView();
+
+private slots:
+ // Test both QListModelInterface and QAbstractItemModel model types
+ void qListModelInterface_items();
+ void qAbstractItemModel_items();
+
+ void qListModelInterface_changed();
+ void qAbstractItemModel_changed();
+
+ void qListModelInterface_inserted();
+ void qAbstractItemModel_inserted();
+
+ void qListModelInterface_removed();
+ void qAbstractItemModel_removed();
+
+ void qListModelInterface_moved();
+ void qAbstractItemModel_moved();
+
+ void qListModelInterface_clear();
+ void qAbstractItemModel_clear();
+
+ void itemList();
+ void currentIndex();
+ void noCurrentIndex();
+ void enforceRange();
+ void spacing();
+ void sections();
+ void sectionsDelegate();
+ void cacheBuffer();
+ void positionViewAtIndex();
+ void resetModel();
+ void propertyChanges();
+ void componentChanges();
+ void modelChanges();
+ void QTBUG_9791();
+ void manualHighlight();
+ void QTBUG_11105();
+ void header();
+ void footer();
+ void headerFooter();
+ void resizeView();
+ void sizeLessThan1();
+ void QTBUG_14821();
+ void resizeDelegate();
+ void QTBUG_16037();
+ void indexAt();
+ void incrementalModel();
+ void onAdd();
+ void onAdd_data();
+ void onRemove();
+ void onRemove_data();
+ void rightToLeft();
+ void test_mirroring();
+
+private:
+ template <class T> void items();
+ template <class T> void changed();
+ template <class T> void inserted();
+ template <class T> void removed(bool animated);
+ template <class T> void moved();
+ template <class T> void clear();
+ QSGView *createView();
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &id, int index=-1);
+ template<typename T>
+ QList<T*> findItems(QSGItem *parent, const QString &objectName);
+ void dumpTree(QSGItem *parent, int depth = 0);
+};
+
+class TestObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool error READ error WRITE setError NOTIFY changedError)
+ Q_PROPERTY(bool animate READ animate NOTIFY changedAnim)
+ Q_PROPERTY(bool invalidHighlight READ invalidHighlight NOTIFY changedHl)
+ Q_PROPERTY(int cacheBuffer READ cacheBuffer NOTIFY changedCacheBuffer)
+
+public:
+ TestObject(QObject *parent = 0)
+ : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false)
+ , mCacheBuffer(0) {}
+
+ bool error() const { return mError; }
+ void setError(bool err) { mError = err; emit changedError(); }
+
+ bool animate() const { return mAnimate; }
+ void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); }
+
+ bool invalidHighlight() const { return mInvalidHighlight; }
+ void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); }
+
+ int cacheBuffer() const { return mCacheBuffer; }
+ void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); }
+
+signals:
+ void changedError();
+ void changedAnim();
+ void changedHl();
+ void changedCacheBuffer();
+
+public:
+ bool mError;
+ bool mAnimate;
+ bool mInvalidHighlight;
+ int mCacheBuffer;
+};
+
+class TestModel : public QListModelInterface
+{
+ Q_OBJECT
+public:
+ TestModel(QObject *parent = 0) : QListModelInterface(parent) {}
+ ~TestModel() {}
+
+ enum Roles { Name, Number };
+
+ QString name(int index) const { return list.at(index).first; }
+ QString number(int index) const { return list.at(index).second; }
+
+ int count() const { return list.count(); }
+
+ QList<int> roles() const { return QList<int>() << Name << Number; }
+ QString toString(int role) const {
+ switch(role) {
+ case Name:
+ return "name";
+ case Number:
+ return "number";
+ default:
+ return "";
+ }
+ }
+
+ QVariant data(int index, int role) const
+ {
+ if (role==0)
+ return list.at(index).first;
+ if (role==1)
+ return list.at(index).second;
+ return QVariant();
+ }
+ QHash<int, QVariant> data(int index, const QList<int> &roles) const {
+ QHash<int,QVariant> returnHash;
+
+ for (int i = 0; i < roles.size(); ++i) {
+ int role = roles.at(i);
+ QVariant info;
+ switch(role) {
+ case Name:
+ info = list.at(index).first;
+ break;
+ case Number:
+ info = list.at(index).second;
+ break;
+ default:
+ break;
+ }
+ returnHash.insert(role, info);
+ }
+ return returnHash;
+ }
+
+ void addItem(const QString &name, const QString &number) {
+ list.append(QPair<QString,QString>(name, number));
+ emit itemsInserted(list.count()-1, 1);
+ }
+
+ void insertItem(int index, const QString &name, const QString &number) {
+ list.insert(index, QPair<QString,QString>(name, number));
+ emit itemsInserted(index, 1);
+ }
+
+ void removeItem(int index) {
+ list.removeAt(index);
+ emit itemsRemoved(index, 1);
+ }
+
+ void removeItems(int index, int count) {
+ int c = count;
+ while (c--)
+ list.removeAt(index);
+ emit itemsRemoved(index, count);
+ }
+
+ void moveItem(int from, int to) {
+ list.move(from, to);
+ emit itemsMoved(from, to, 1);
+ }
+
+ void modifyItem(int index, const QString &name, const QString &number) {
+ list[index] = QPair<QString,QString>(name, number);
+ emit itemsChanged(index, 1, roles());
+ }
+
+ void clear() {
+ int count = list.count();
+ list.clear();
+ emit itemsRemoved(0, count);
+ }
+
+private:
+ QList<QPair<QString,QString> > list;
+};
+
+
+class TestModel2 : public QAbstractListModel
+{
+public:
+ enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
+
+ TestModel2(QObject *parent=0) : QAbstractListModel(parent) {
+ QHash<int, QByteArray> roles;
+ roles[Name] = "name";
+ roles[Number] = "number";
+ setRoleNames(roles);
+ }
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
+ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
+ QVariant rv;
+ if (role == Name)
+ rv = list.at(index.row()).first;
+ else if (role == Number)
+ rv = list.at(index.row()).second;
+
+ return rv;
+ }
+
+ int count() const { return rowCount(); }
+ QString name(int index) const { return list.at(index).first; }
+ QString number(int index) const { return list.at(index).second; }
+
+ void addItem(const QString &name, const QString &number) {
+ emit beginInsertRows(QModelIndex(), list.count(), list.count());
+ list.append(QPair<QString,QString>(name, number));
+ emit endInsertRows();
+ }
+
+ void addItems(const QList<QPair<QString, QString> > &items) {
+ emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
+ for (int i=0; i<items.count(); i++)
+ list.append(QPair<QString,QString>(items[i].first, items[i].second));
+ emit endInsertRows();
+ }
+
+ void insertItem(int index, const QString &name, const QString &number) {
+ emit beginInsertRows(QModelIndex(), index, index);
+ list.insert(index, QPair<QString,QString>(name, number));
+ emit endInsertRows();
+ }
+
+ void removeItem(int index) {
+ emit beginRemoveRows(QModelIndex(), index, index);
+ list.removeAt(index);
+ emit endRemoveRows();
+ }
+
+ void removeItems(int index, int count) {
+ emit beginRemoveRows(QModelIndex(), index, index+count-1);
+ while (count--)
+ list.removeAt(index);
+ emit endRemoveRows();
+ }
+
+ void moveItem(int from, int to) {
+ emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
+ list.move(from, to);
+ emit endMoveRows();
+ }
+
+ void modifyItem(int idx, const QString &name, const QString &number) {
+ list[idx] = QPair<QString,QString>(name, number);
+ emit dataChanged(index(idx,0), index(idx,0));
+ }
+
+ void clear() {
+ int count = list.count();
+ emit beginRemoveRows(QModelIndex(), 0, count-1);
+ list.clear();
+ emit endRemoveRows();
+ }
+
+private:
+ QList<QPair<QString,QString> > list;
+};
+
+tst_QSGListView::tst_QSGListView()
+{
+}
+
+template <class T>
+void tst_QSGListView::items()
+{
+ QSGView *canvas = createView();
+
+ T model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QTRY_VERIFY(testObject->error() == false);
+
+ QTRY_VERIFY(listview->highlightItem() != 0);
+ QTRY_COMPARE(listview->count(), model.count());
+ QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
+
+ // current item should be first item
+ QTRY_COMPARE(listview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 0));
+
+ for (int i = 0; i < model.count(); ++i) {
+ QSGText *name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ // switch to other delegate
+ testObject->setAnimate(true);
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QTRY_VERIFY(testObject->error() == false);
+ QTRY_VERIFY(listview->currentItem());
+
+ // set invalid highlight
+ testObject->setInvalidHighlight(true);
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QTRY_VERIFY(testObject->error() == false);
+ QTRY_VERIFY(listview->currentItem());
+ QTRY_VERIFY(listview->highlightItem() == 0);
+
+ // back to normal highlight
+ testObject->setInvalidHighlight(false);
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QTRY_VERIFY(testObject->error() == false);
+ QTRY_VERIFY(listview->currentItem());
+ QTRY_VERIFY(listview->highlightItem() != 0);
+
+ // set an empty model and confirm that items are destroyed
+ T model2;
+ ctxt->setContextProperty("testModel", &model2);
+
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ QTRY_VERIFY(itemCount == 0);
+
+ QTRY_COMPARE(listview->highlightResizeSpeed(), 1000.0);
+ QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
+
+ delete canvas;
+ delete testObject;
+}
+
+
+template <class T>
+void tst_QSGListView::changed()
+{
+ QSGView *canvas = createView();
+
+ T model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGFlickable *listview = findItem<QSGFlickable>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.modifyItem(1, "Will", "9876");
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ delete canvas;
+ delete testObject;
+}
+
+template <class T>
+void tst_QSGListView::inserted()
+{
+ QSGView *canvas = createView();
+
+ T model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.insertItem(1, "Will", "9876");
+
+ QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ // Confirm items positioned correctly
+ for (int i = 0; i < model.count(); ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QTRY_COMPARE(item->y(), i*20.0);
+ }
+
+ model.insertItem(0, "Foo", "1111"); // zero index, and current item
+
+ QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
+
+ name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ QTRY_COMPARE(listview->currentIndex(), 1);
+
+ // Confirm items positioned correctly
+ for (int i = 0; i < model.count(); ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QTRY_COMPARE(item->y(), i*20.0);
+ }
+
+ for (int i = model.count(); i < 30; ++i)
+ model.insertItem(i, "Hello", QString::number(i));
+
+ listview->setContentY(80);
+
+ // Insert item outside visible area
+ model.insertItem(1, "Hello", "1324");
+
+ QTRY_VERIFY(listview->contentY() == 80);
+
+ // Confirm items positioned correctly
+ for (int i = 5; i < 5+15; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.0 - 20.0);
+ }
+
+// QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
+
+ delete canvas;
+ delete testObject;
+}
+
+template <class T>
+void tst_QSGListView::removed(bool animated)
+{
+ QSGView *canvas = createView();
+
+ T model;
+ for (int i = 0; i < 50; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ testObject->setAnimate(animated);
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.removeItem(1);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*20);
+ }
+
+ // Remove first item (which is the current item);
+ model.removeItem(0); // post: top item starts at 20
+
+ QTest::qWait(300);
+
+ name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(),i*20.0 + 20.0);
+ }
+
+ // Remove items not visible
+ model.removeItem(18);
+ qApp->processEvents();
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(),i*20.0+20.0);
+ }
+
+ // Remove items before visible
+ listview->setContentY(80);
+ listview->setCurrentIndex(10);
+
+ model.removeItem(1); // post: top item will be at 40
+ qApp->processEvents();
+
+ // Confirm items positioned correctly
+ for (int i = 2; i < 18; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(),40+i*20.0);
+ }
+
+ // Remove current index
+ QTRY_VERIFY(listview->currentIndex() == 9);
+ QSGItem *oldCurrent = listview->currentItem();
+ model.removeItem(9);
+
+ QTRY_COMPARE(listview->currentIndex(), 9);
+ QTRY_VERIFY(listview->currentItem() != oldCurrent);
+
+ listview->setContentY(40); // That's the top now
+ // let transitions settle.
+ QTest::qWait(300);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(),40+i*20.0);
+ }
+
+ // remove current item beyond visible items.
+ listview->setCurrentIndex(20);
+ listview->setContentY(40);
+ model.removeItem(20);
+
+ QTRY_COMPARE(listview->currentIndex(), 20);
+ QTRY_VERIFY(listview->currentItem() != 0);
+
+ // remove item before current, but visible
+ listview->setCurrentIndex(8);
+ oldCurrent = listview->currentItem();
+ model.removeItem(6);
+
+ QTRY_COMPARE(listview->currentIndex(), 7);
+ QTRY_VERIFY(listview->currentItem() == oldCurrent);
+
+ listview->setContentY(80);
+ QTest::qWait(300);
+
+ // remove all visible items
+ model.removeItems(1, 18);
+ QTest::qWait(300);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i+2);
+ if (!item) qWarning() << "Item" << i+2 << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(),80+i*20.0);
+ }
+
+ model.removeItems(1, 17);
+// QTest::qWait(300);
+
+ model.removeItems(2, 1);
+ model.addItem("New", "1");
+
+ QTRY_VERIFY(name = findItem<QSGText>(contentItem, "textName", model.count()-1));
+ QCOMPARE(name->text(), QString("New"));
+
+ delete canvas;
+ delete testObject;
+}
+
+template <class T>
+void tst_QSGListView::clear()
+{
+ QSGView *canvas = createView();
+
+ T model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.clear();
+
+ QTRY_VERIFY(listview->count() == 0);
+ QTRY_VERIFY(listview->currentItem() == 0);
+ QTRY_VERIFY(listview->contentY() == 0);
+ QVERIFY(listview->currentIndex() == -1);
+
+ // confirm sanity when adding an item to cleared list
+ model.addItem("New", "1");
+ QTRY_VERIFY(listview->count() == 1);
+ QVERIFY(listview->currentItem() != 0);
+ QVERIFY(listview->currentIndex() == 0);
+
+ delete canvas;
+ delete testObject;
+}
+
+
+template <class T>
+void tst_QSGListView::moved()
+{
+ QSGView *canvas = createView();
+
+ T model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ model.moveItem(1, 4);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(1));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(1));
+
+ name = findItem<QSGText>(contentItem, "textName", 4);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(4));
+ number = findItem<QSGText>(contentItem, "textNumber", 4);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(4));
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*20);
+ }
+
+ listview->setContentY(80);
+
+ // move outside visible area
+ model.moveItem(1, 18);
+
+ // Confirm items positioned correctly and indexes correct
+ for (int i = 3; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.0 + 20);
+ name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ // move from outside visible into visible
+ model.moveItem(20, 4);
+
+ // Confirm items positioned correctly and indexes correct
+ for (int i = 3; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.0 + 20);
+ name = findItem<QSGText>(contentItem, "textName", i);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ number = findItem<QSGText>(contentItem, "textNumber", i);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(i));
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::enforceRange()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listview-enforcerange.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0);
+ QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0);
+ QTRY_COMPARE(listview->highlightRangeMode(), QSGListView::StrictlyEnforceRange);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // view should be positioned at the top of the range.
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(listview->contentY(), -100.0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "textName", 0);
+ QTRY_VERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(0));
+ QSGText *number = findItem<QSGText>(contentItem, "textNumber", 0);
+ QTRY_VERIFY(number != 0);
+ QTRY_COMPARE(number->text(), model.number(0));
+
+ // Check currentIndex is updated when contentItem moves
+ listview->setContentY(20);
+
+ QTRY_COMPARE(listview->currentIndex(), 6);
+
+ // change model
+ TestModel model2;
+ for (int i = 0; i < 5; i++)
+ model2.addItem("Item" + QString::number(i), "");
+
+ ctxt->setContextProperty("testModel", &model2);
+ QCOMPARE(listview->count(), 5);
+
+ delete canvas;
+}
+
+void tst_QSGListView::spacing()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*20);
+ }
+
+ listview->setSpacing(10);
+ QTRY_VERIFY(listview->spacing() == 10);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*30);
+ }
+
+ listview->setSpacing(0);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.0);
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::sections()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i/5));
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listview-sections.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20));
+ QSGText *next = findItem<QSGText>(item, "nextSection");
+ QCOMPARE(next->text().toInt(), (i+1)/5);
+ }
+
+ QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged()));
+
+ // Remove section boundary
+ model.removeItem(5);
+
+ // New section header created
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 5);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 40.0);
+
+ model.insertItem(3, "New Item", "0");
+
+ // Section header moved
+ item = findItem<QSGItem>(contentItem, "wrapper", 5);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 20.0);
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 6);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 40.0);
+
+ // insert item which will become a section header
+ model.insertItem(6, "Replace header", "1");
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 6);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 40.0);
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 7);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 20.0);
+
+ QTRY_COMPARE(listview->currentSection(), QString("0"));
+
+ listview->setContentY(140);
+ QTRY_COMPARE(listview->currentSection(), QString("1"));
+
+ QTRY_COMPARE(currentSectionChangedSpy.count(), 1);
+
+ listview->setContentY(20);
+ QTRY_COMPARE(listview->currentSection(), QString("0"));
+
+ QTRY_COMPARE(currentSectionChangedSpy.count(), 2);
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 1);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 20.0);
+
+ // check that headers change when item changes
+ listview->setContentY(0);
+ model.modifyItem(0, "changed", "2");
+
+ item = findItem<QSGItem>(contentItem, "wrapper", 1);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->height(), 40.0);
+
+ delete canvas;
+}
+
+void tst_QSGListView::sectionsDelegate()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i/5));
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listview-sections_delegate.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20));
+ QSGText *next = findItem<QSGText>(item, "nextSection");
+ QCOMPARE(next->text().toInt(), (i+1)/5);
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "sect_" + QString::number(i));
+ QVERIFY(item);
+ QTRY_COMPARE(item->y(), qreal(i*20*6));
+ }
+
+ model.modifyItem(0, "One", "aaa");
+ model.modifyItem(1, "Two", "aaa");
+ model.modifyItem(2, "Three", "aaa");
+ model.modifyItem(3, "Four", "aaa");
+ model.modifyItem(4, "Five", "aaa");
+
+ for (int i = 0; i < 3; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem,
+ "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
+ QVERIFY(item);
+ QTRY_COMPARE(item->y(), qreal(i*20*6));
+ }
+
+ // remove section boundary
+ model.removeItem(5);
+ qApp->processEvents();
+ for (int i = 0; i < 3; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem,
+ "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
+ QVERIFY(item);
+ }
+
+ // QTBUG-17606
+ QList<QSGItem*> items = findItems<QSGItem>(contentItem, "sect_1");
+ QCOMPARE(items.count(), 1);
+
+ // QTBUG-17759
+ model.modifyItem(0, "One", "aaa");
+ model.modifyItem(1, "One", "aaa");
+ model.modifyItem(2, "One", "aaa");
+ model.modifyItem(3, "Four", "aaa");
+ model.modifyItem(4, "Four", "aaa");
+ model.modifyItem(5, "Four", "aaa");
+ model.modifyItem(6, "Five", "aaa");
+ model.modifyItem(7, "Five", "aaa");
+ model.modifyItem(8, "Five", "aaa");
+ model.modifyItem(9, "Two", "aaa");
+ model.modifyItem(10, "Two", "aaa");
+ model.modifyItem(11, "Two", "aaa");
+ QTRY_COMPARE(findItems<QSGItem>(contentItem, "sect_aaa").count(), 1);
+ canvas->rootObject()->setProperty("sectionProperty", "name");
+ // ensure view has settled.
+ QTRY_COMPARE(findItems<QSGItem>(contentItem, "sect_Four").count(), 1);
+ for (int i = 0; i < 4; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem,
+ "sect_" + model.name(i*3));
+ QVERIFY(item);
+ QTRY_COMPARE(item->y(), qreal(i*20*4));
+ }
+
+ // QTBUG-17769
+ model.removeItems(10, 20);
+ // ensure view has settled.
+ QTRY_COMPARE(findItems<QSGItem>(contentItem, "wrapper").count(), 10);
+ // Drag view up beyond bounds
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(20,20));
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(20,0), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(20,-50), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(20,-200), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(20,-200));
+ // view should settle back at 0
+ QTRY_COMPARE(listview->contentY(), 0.0);
+
+ delete canvas;
+}
+
+void tst_QSGListView::currentIndex()
+{
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i));
+
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testWrap", QVariant(false));
+
+ QString filename(SRCDIR "/data/listview-initCurrent.qml");
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // current item should be 20th item at startup
+ // and current item should be in view
+ QCOMPARE(listview->currentIndex(), 20);
+ QCOMPARE(listview->contentY(), 100.0);
+ QCOMPARE(listview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 20));
+ QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
+
+ // no wrap
+ listview->setCurrentIndex(0);
+ QCOMPARE(listview->currentIndex(), 0);
+ // confirm that the velocity is updated
+ QTRY_VERIFY(listview->verticalVelocity() != 0.0);
+
+ listview->incrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), 1);
+ listview->decrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), 0);
+
+ listview->decrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), 0);
+
+ // with wrap
+ ctxt->setContextProperty("testWrap", QVariant(true));
+ QVERIFY(listview->isWrapEnabled());
+
+ listview->decrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), model.count()-1);
+
+ QTRY_COMPARE(listview->contentY(), 280.0);
+
+ listview->incrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), 0);
+
+ QTRY_COMPARE(listview->contentY(), 0.0);
+
+ // Test keys
+ canvas->show();
+ qApp->setActiveWindow(canvas);
+#ifdef Q_WS_X11
+ // to be safe and avoid failing setFocus with window managers
+ qt_x11_wait_for_window_manager(canvas);
+#endif
+ QTRY_VERIFY(canvas->hasFocus());
+ qApp->processEvents();
+
+ QTest::keyClick(canvas, Qt::Key_Down);
+ QCOMPARE(listview->currentIndex(), 1);
+
+ QTest::keyClick(canvas, Qt::Key_Up);
+ QCOMPARE(listview->currentIndex(), 0);
+
+ // turn off auto highlight
+ listview->setHighlightFollowsCurrentItem(false);
+ QVERIFY(listview->highlightFollowsCurrentItem() == false);
+
+ QVERIFY(listview->highlightItem());
+ qreal hlPos = listview->highlightItem()->y();
+
+ listview->setCurrentIndex(4);
+ QTRY_COMPARE(listview->highlightItem()->y(), hlPos);
+
+ // insert item before currentIndex
+ listview->setCurrentIndex(28);
+ model.insertItem(0, "Foo", "1111");
+ QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
+
+ // check removing highlight by setting currentIndex to -1;
+ listview->setCurrentIndex(-1);
+
+ QCOMPARE(listview->currentIndex(), -1);
+ QVERIFY(!listview->highlightItem());
+ QVERIFY(!listview->currentItem());
+
+ delete canvas;
+}
+
+void tst_QSGListView::noCurrentIndex()
+{
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), QString::number(i));
+
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ QString filename(SRCDIR "/data/listview-noCurrent.qml");
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // current index should be -1 at startup
+ // and we should not have a currentItem or highlightItem
+ QCOMPARE(listview->currentIndex(), -1);
+ QCOMPARE(listview->contentY(), 0.0);
+ QVERIFY(!listview->highlightItem());
+ QVERIFY(!listview->currentItem());
+
+ listview->setCurrentIndex(2);
+ QCOMPARE(listview->currentIndex(), 2);
+ QVERIFY(listview->highlightItem());
+ QVERIFY(listview->currentItem());
+
+ delete canvas;
+}
+
+void tst_QSGListView::itemList()
+{
+ QSGView *canvas = createView();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/itemlist.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "view");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGVisualItemModel *model = canvas->rootObject()->findChild<QSGVisualItemModel*>("itemModel");
+ QTRY_VERIFY(model != 0);
+
+ QTRY_VERIFY(model->count() == 3);
+ QTRY_COMPARE(listview->currentIndex(), 0);
+
+ QSGItem *item = findItem<QSGItem>(contentItem, "item1");
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), 0.0);
+ QCOMPARE(item->height(), listview->height());
+
+ QSGText *text = findItem<QSGText>(contentItem, "text1");
+ QTRY_VERIFY(text);
+ QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
+
+ listview->setCurrentIndex(2);
+
+ item = findItem<QSGItem>(contentItem, "item3");
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), 480.0);
+
+ text = findItem<QSGText>(contentItem, "text3");
+ QTRY_VERIFY(text);
+ QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
+
+ delete canvas;
+}
+
+void tst_QSGListView::cacheBuffer()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+ QTRY_VERIFY(listview->delegate() != 0);
+ QTRY_VERIFY(listview->model() != 0);
+ QTRY_VERIFY(listview->highlight() != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*20);
+ }
+
+ testObject->setCacheBuffer(400);
+ QTRY_VERIFY(listview->cacheBuffer() == 400);
+
+ int newItemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ QTRY_VERIFY(newItemCount > itemCount);
+
+ // Confirm items positioned correctly
+ for (int i = 0; i < model.count() && i < newItemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*20);
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::positionViewAtIndex()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ // Position on a currently visible item
+ listview->positionViewAtIndex(3, QSGListView::Beginning);
+ QTRY_COMPARE(listview->contentY(), 60.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ // Position on an item beyond the visible items
+ listview->positionViewAtIndex(22, QSGListView::Beginning);
+ QTRY_COMPARE(listview->contentY(), 440.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ // Position on an item that would leave empty space if positioned at the top
+ listview->positionViewAtIndex(28, QSGListView::Beginning);
+ QTRY_COMPARE(listview->contentY(), 480.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ // Position at the beginning again
+ listview->positionViewAtIndex(0, QSGListView::Beginning);
+ QTRY_COMPARE(listview->contentY(), 0.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ // Position at End using last index
+ listview->positionViewAtIndex(model.count()-1, QSGListView::End);
+ QTRY_COMPARE(listview->contentY(), 480.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 24; i < model.count(); ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ // Position at End
+ listview->positionViewAtIndex(20, QSGListView::End);
+ QTRY_COMPARE(listview->contentY(), 100.);
+
+ // Position in Center
+ listview->positionViewAtIndex(15, QSGListView::Center);
+ QTRY_COMPARE(listview->contentY(), 150.);
+
+ // Ensure at least partially visible
+ listview->positionViewAtIndex(15, QSGListView::Visible);
+ QTRY_COMPARE(listview->contentY(), 150.);
+
+ listview->setContentY(302);
+ listview->positionViewAtIndex(15, QSGListView::Visible);
+ QTRY_COMPARE(listview->contentY(), 302.);
+
+ listview->setContentY(320);
+ listview->positionViewAtIndex(15, QSGListView::Visible);
+ QTRY_COMPARE(listview->contentY(), 300.);
+
+ listview->setContentY(85);
+ listview->positionViewAtIndex(20, QSGListView::Visible);
+ QTRY_COMPARE(listview->contentY(), 85.);
+
+ listview->setContentY(75);
+ listview->positionViewAtIndex(20, QSGListView::Visible);
+ QTRY_COMPARE(listview->contentY(), 100.);
+
+ // Ensure completely visible
+ listview->setContentY(120);
+ listview->positionViewAtIndex(20, QSGListView::Contain);
+ QTRY_COMPARE(listview->contentY(), 120.);
+
+ listview->setContentY(302);
+ listview->positionViewAtIndex(15, QSGListView::Contain);
+ QTRY_COMPARE(listview->contentY(), 300.);
+
+ listview->setContentY(85);
+ listview->positionViewAtIndex(20, QSGListView::Contain);
+ QTRY_COMPARE(listview->contentY(), 100.);
+
+ // positionAtBeginnging
+ listview->positionViewAtBeginning();
+ QTRY_COMPARE(listview->contentY(), 0.);
+
+ listview->setContentY(80);
+ canvas->rootObject()->setProperty("showHeader", true);
+ listview->positionViewAtBeginning();
+ QTRY_COMPARE(listview->contentY(), -30.);
+
+ // positionAtEnd
+ listview->positionViewAtEnd();
+ QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320
+
+ listview->setContentY(80);
+ canvas->rootObject()->setProperty("showFooter", true);
+ listview->positionViewAtEnd();
+ QTRY_COMPARE(listview->contentY(), 510.);
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::resetModel()
+{
+ QSGView *canvas = createView();
+
+ QStringList strings;
+ strings << "one" << "two" << "three";
+ QStringListModel model(strings);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaylist.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(listview->count(), model.rowCount());
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QSGText *display = findItem<QSGText>(contentItem, "displayText", i);
+ QTRY_VERIFY(display != 0);
+ QTRY_COMPARE(display->text(), strings.at(i));
+ }
+
+ strings.clear();
+ strings << "four" << "five" << "six" << "seven";
+ model.setStringList(strings);
+
+ QTRY_COMPARE(listview->count(), model.rowCount());
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QSGText *display = findItem<QSGText>(contentItem, "displayText", i);
+ QTRY_VERIFY(display != 0);
+ QTRY_COMPARE(display->text(), strings.at(i));
+ }
+
+ delete canvas;
+}
+
+void tst_QSGListView::propertyChanges()
+{
+ QSGView *canvas = createView();
+ QTRY_VERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
+
+ QSGListView *listView = canvas->rootObject()->findChild<QSGListView*>("listView");
+ QTRY_VERIFY(listView);
+
+ QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged()));
+ QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged()));
+ QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged()));
+ QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged()));
+ QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged()));
+ QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged()));
+ QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged()));
+
+ QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true);
+ QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0);
+ QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0);
+ QTRY_COMPARE(listView->highlightRangeMode(), QSGListView::ApplyRange);
+ QTRY_COMPARE(listView->isWrapEnabled(), true);
+ QTRY_COMPARE(listView->cacheBuffer(), 10);
+ QTRY_COMPARE(listView->snapMode(), QSGListView::SnapToItem);
+
+ listView->setHighlightFollowsCurrentItem(false);
+ listView->setPreferredHighlightBegin(1.0);
+ listView->setPreferredHighlightEnd(1.0);
+ listView->setHighlightRangeMode(QSGListView::StrictlyEnforceRange);
+ listView->setWrapEnabled(false);
+ listView->setCacheBuffer(3);
+ listView->setSnapMode(QSGListView::SnapOneItem);
+
+ QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false);
+ QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0);
+ QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0);
+ QTRY_COMPARE(listView->highlightRangeMode(), QSGListView::StrictlyEnforceRange);
+ QTRY_COMPARE(listView->isWrapEnabled(), false);
+ QTRY_COMPARE(listView->cacheBuffer(), 3);
+ QTRY_COMPARE(listView->snapMode(), QSGListView::SnapOneItem);
+
+ QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
+ QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
+ QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
+ QTRY_COMPARE(highlightRangeModeSpy.count(),1);
+ QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
+ QTRY_COMPARE(cacheBufferSpy.count(),1);
+ QTRY_COMPARE(snapModeSpy.count(),1);
+
+ listView->setHighlightFollowsCurrentItem(false);
+ listView->setPreferredHighlightBegin(1.0);
+ listView->setPreferredHighlightEnd(1.0);
+ listView->setHighlightRangeMode(QSGListView::StrictlyEnforceRange);
+ listView->setWrapEnabled(false);
+ listView->setCacheBuffer(3);
+ listView->setSnapMode(QSGListView::SnapOneItem);
+
+ QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
+ QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
+ QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
+ QTRY_COMPARE(highlightRangeModeSpy.count(),1);
+ QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
+ QTRY_COMPARE(cacheBufferSpy.count(),1);
+ QTRY_COMPARE(snapModeSpy.count(),1);
+
+ delete canvas;
+}
+
+void tst_QSGListView::componentChanges()
+{
+ QSGView *canvas = createView();
+ QTRY_VERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
+
+ QSGListView *listView = canvas->rootObject()->findChild<QSGListView*>("listView");
+ QTRY_VERIFY(listView);
+
+ QDeclarativeComponent component(canvas->engine());
+ component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
+
+ QDeclarativeComponent delegateComponent(canvas->engine());
+ delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
+
+ QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged()));
+ QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged()));
+ QSignalSpy headerSpy(listView, SIGNAL(headerChanged()));
+ QSignalSpy footerSpy(listView, SIGNAL(footerChanged()));
+
+ listView->setHighlight(&component);
+ listView->setHeader(&component);
+ listView->setFooter(&component);
+ listView->setDelegate(&delegateComponent);
+
+ QTRY_COMPARE(listView->highlight(), &component);
+ QTRY_COMPARE(listView->header(), &component);
+ QTRY_COMPARE(listView->footer(), &component);
+ QTRY_COMPARE(listView->delegate(), &delegateComponent);
+
+ QTRY_COMPARE(highlightSpy.count(),1);
+ QTRY_COMPARE(delegateSpy.count(),1);
+ QTRY_COMPARE(headerSpy.count(),1);
+ QTRY_COMPARE(footerSpy.count(),1);
+
+ listView->setHighlight(&component);
+ listView->setHeader(&component);
+ listView->setFooter(&component);
+ listView->setDelegate(&delegateComponent);
+
+ QTRY_COMPARE(highlightSpy.count(),1);
+ QTRY_COMPARE(delegateSpy.count(),1);
+ QTRY_COMPARE(headerSpy.count(),1);
+ QTRY_COMPARE(footerSpy.count(),1);
+
+ delete canvas;
+}
+
+void tst_QSGListView::modelChanges()
+{
+ QSGView *canvas = createView();
+ QTRY_VERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
+
+ QSGListView *listView = canvas->rootObject()->findChild<QSGListView*>("listView");
+ QTRY_VERIFY(listView);
+
+ QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
+ QTRY_VERIFY(alternateModel);
+ QVariant modelVariant = QVariant::fromValue(alternateModel);
+ QSignalSpy modelSpy(listView, SIGNAL(modelChanged()));
+
+ listView->setModel(modelVariant);
+ QTRY_COMPARE(listView->model(), modelVariant);
+ QTRY_COMPARE(modelSpy.count(),1);
+
+ listView->setModel(modelVariant);
+ QTRY_COMPARE(modelSpy.count(),1);
+
+ listView->setModel(QVariant());
+ QTRY_COMPARE(modelSpy.count(),2);
+
+// delete canvas;
+}
+
+void tst_QSGListView::QTBUG_9791()
+{
+ QSGView *canvas = createView();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/strictlyenforcerange.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = qobject_cast<QSGListView*>(canvas->rootObject());
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+ QTRY_VERIFY(listview->delegate() != 0);
+ QTRY_VERIFY(listview->model() != 0);
+
+ QMetaObject::invokeMethod(listview, "fillModel");
+ qApp->processEvents();
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ QCOMPARE(itemCount, 3);
+
+ for (int i = 0; i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), i*300.0);
+ }
+
+ // check that view is positioned correctly
+ QTRY_COMPARE(listview->contentX(), 590.0);
+
+ delete canvas;
+}
+
+void tst_QSGListView::manualHighlight()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ QString filename(SRCDIR "/data/manual-highlight.qml");
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(listview->currentIndex(), 0);
+ QTRY_COMPARE(listview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 0));
+ QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
+
+ listview->setCurrentIndex(2);
+
+ QTRY_COMPARE(listview->currentIndex(), 2);
+ QTRY_COMPARE(listview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 2));
+ QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
+
+ // QTBUG-15972
+ listview->positionViewAtIndex(3, QSGListView::Contain);
+
+ QTRY_COMPARE(listview->currentIndex(), 2);
+ QTRY_COMPARE(listview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 2));
+ QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
+
+ delete canvas;
+}
+
+void tst_QSGListView::QTBUG_11105()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_VERIFY(item->y() == i*20);
+ }
+
+ listview->positionViewAtIndex(20, QSGListView::Beginning);
+ QCOMPARE(listview->contentY(), 280.);
+
+ TestModel model2;
+ for (int i = 0; i < 5; i++)
+ model2.addItem("Item" + QString::number(i), "");
+
+ ctxt->setContextProperty("testModel", &model2);
+
+ itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ QCOMPARE(itemCount, 5);
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::header()
+{
+ {
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGText *header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(header);
+ QCOMPARE(header->y(), 0.0);
+ QCOMPARE(header->height(), 20.0);
+
+ QCOMPARE(listview->contentY(), 0.0);
+
+ model.clear();
+ QTRY_COMPARE(header->y(), 0.0);
+
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
+
+ header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(!header);
+ header = findItem<QSGText>(contentItem, "header2");
+ QVERIFY(header);
+
+ QCOMPARE(header->y(), 10.0);
+ QCOMPARE(header->height(), 10.0);
+ QCOMPARE(listview->contentY(), 10.0);
+
+ delete canvas;
+ }
+ {
+ QSGView *canvas = createView();
+
+ TestModel model;
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header1.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGText *header = findItem<QSGText>(contentItem, "header");
+ QVERIFY(header);
+ QCOMPARE(header->y(), 0.0);
+
+ QCOMPARE(listview->contentY(), 0.0);
+
+ model.clear();
+ QTRY_COMPARE(header->y(), 0.0);
+
+ delete canvas;
+ }
+}
+
+void tst_QSGListView::footer()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 3; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/footer.qml"));
+ canvas->show();
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGText *footer = findItem<QSGText>(contentItem, "footer");
+ QVERIFY(footer);
+ QCOMPARE(footer->y(), 60.0);
+ QCOMPARE(footer->height(), 30.0);
+
+ model.removeItem(1);
+ QTRY_COMPARE(footer->y(), 40.0);
+
+ model.clear();
+ QTRY_COMPARE(footer->y(), 0.0);
+
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
+
+ footer = findItem<QSGText>(contentItem, "footer");
+ QVERIFY(!footer);
+ footer = findItem<QSGText>(contentItem, "footer2");
+ QVERIFY(footer);
+
+ QCOMPARE(footer->y(), 600.0);
+ QCOMPARE(footer->height(), 20.0);
+ QCOMPARE(listview->contentY(), 0.0);
+
+ delete canvas;
+}
+
+class LVAccessor : public QSGListView
+{
+public:
+ qreal minY() const { return minYExtent(); }
+ qreal maxY() const { return maxYExtent(); }
+ qreal minX() const { return minXExtent(); }
+ qreal maxX() const { return maxXExtent(); }
+};
+
+void tst_QSGListView::headerFooter()
+{
+ {
+ // Vertical
+ QSGView *canvas = createView();
+
+ TestModel model;
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/headerfooter.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = qobject_cast<QSGListView*>(canvas->rootObject());
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGItem *header = findItem<QSGItem>(contentItem, "header");
+ QVERIFY(header);
+ QCOMPARE(header->y(), 0.0);
+
+ QSGItem *footer = findItem<QSGItem>(contentItem, "footer");
+ QVERIFY(footer);
+ QCOMPARE(footer->y(), 20.0);
+
+ QVERIFY(static_cast<LVAccessor*>(listview)->minY() == 0);
+ QVERIFY(static_cast<LVAccessor*>(listview)->maxY() == 0);
+
+ delete canvas;
+ }
+ {
+ // Horizontal
+ QSGView *canvas = createView();
+
+ TestModel model;
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/headerfooter.qml"));
+ canvas->rootObject()->setProperty("horizontal", true);
+ qApp->processEvents();
+
+ QSGListView *listview = qobject_cast<QSGListView*>(canvas->rootObject());
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGItem *header = findItem<QSGItem>(contentItem, "header");
+ QVERIFY(header);
+ QCOMPARE(header->x(), 0.0);
+
+ QSGItem *footer = findItem<QSGItem>(contentItem, "footer");
+ QVERIFY(footer);
+ QCOMPARE(footer->x(), 20.0);
+
+ QVERIFY(static_cast<LVAccessor*>(listview)->minX() == 0);
+ QVERIFY(static_cast<LVAccessor*>(listview)->maxX() == 0);
+
+ delete canvas;
+ }
+ {
+ // Horizontal RTL
+ QSGView *canvas = createView();
+
+ TestModel model;
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/headerfooter.qml"));
+ canvas->rootObject()->setProperty("horizontal", true);
+ canvas->rootObject()->setProperty("rtl", true);
+ qApp->processEvents();
+
+ QSGListView *listview = qobject_cast<QSGListView*>(canvas->rootObject());
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGItem *header = findItem<QSGItem>(contentItem, "header");
+ QVERIFY(header);
+ QCOMPARE(header->x(), -20.0);
+
+ QSGItem *footer = findItem<QSGItem>(contentItem, "footer");
+ QVERIFY(footer);
+ QCOMPARE(footer->x(), -50.0);
+
+ QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240.);
+ QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240.);
+
+ delete canvas;
+ }
+}
+
+void tst_QSGListView::resizeView()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*20.);
+ }
+
+ QVariant heightRatio;
+ QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
+ QCOMPARE(heightRatio.toReal(), 0.4);
+
+ listview->setHeight(200);
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
+ QCOMPARE(heightRatio.toReal(), 0.25);
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::sizeLessThan1()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/sizelessthan1.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->y(), i*0.5);
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::QTBUG_14821()
+{
+ QSGView *canvas = createView();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/qtbug14821.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = qobject_cast<QSGListView*>(canvas->rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ listview->decrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), 99);
+
+ listview->incrementCurrentIndex();
+ QCOMPARE(listview->currentIndex(), 0);
+
+ delete canvas;
+}
+
+void tst_QSGListView::resizeDelegate()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ QStringList strings;
+ for (int i = 0; i < 30; ++i)
+ strings << QString::number(i);
+ QStringListModel model(strings);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaylist.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(listview->count(), model.rowCount());
+
+ listview->setCurrentIndex(25);
+ listview->setContentY(0);
+
+ for (int i = 0; i < 16; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QVERIFY(item != 0);
+ QCOMPARE(item->y(), i*20.0);
+ }
+
+ QCOMPARE(listview->currentItem()->y(), 500.0);
+ QTRY_COMPARE(listview->highlightItem()->y(), 500.0);
+
+ canvas->rootObject()->setProperty("delegateHeight", 30);
+ qApp->processEvents();
+
+ for (int i = 0; i < 11; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QVERIFY(item != 0);
+ QTRY_COMPARE(item->y(), i*30.0);
+ }
+
+ QTRY_COMPARE(listview->currentItem()->y(), 750.0);
+ QTRY_COMPARE(listview->highlightItem()->y(), 750.0);
+
+ listview->setCurrentIndex(1);
+ listview->positionViewAtIndex(25, QSGListView::Beginning);
+ listview->positionViewAtIndex(5, QSGListView::Beginning);
+
+ for (int i = 5; i < 16; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QVERIFY(item != 0);
+ QCOMPARE(item->y(), i*30.0);
+ }
+
+ QTRY_COMPARE(listview->currentItem()->y(), 30.0);
+ QTRY_COMPARE(listview->highlightItem()->y(), 30.0);
+
+ canvas->rootObject()->setProperty("delegateHeight", 20);
+ qApp->processEvents();
+
+ for (int i = 5; i < 11; ++i) {
+ QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
+ QVERIFY(item != 0);
+ QTRY_COMPARE(item->y(), 150 + (i-5)*20.0);
+ }
+
+ QTRY_COMPARE(listview->currentItem()->y(), 70.0);
+ QTRY_COMPARE(listview->highlightItem()->y(), 70.0);
+
+ delete canvas;
+}
+
+void tst_QSGListView::QTBUG_16037()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/qtbug16037.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "listview");
+ QTRY_VERIFY(listview != 0);
+
+ QVERIFY(listview->contentHeight() <= 0.0);
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "setModel");
+
+ QTRY_COMPARE(listview->contentHeight(), 80.0);
+
+ delete canvas;
+}
+
+void tst_QSGListView::indexAt()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ for (int i = 0; i < 30; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QCOMPARE(listview->indexAt(0,0), 0);
+ QCOMPARE(listview->indexAt(0,19), 0);
+ QCOMPARE(listview->indexAt(239,19), 0);
+ QCOMPARE(listview->indexAt(0,20), 1);
+ QCOMPARE(listview->indexAt(240,20), -1);
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGListView::incrementalModel()
+{
+ QSGView *canvas = createView();
+
+ IncrementalModel model;
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaylist.qml"));
+ qApp->processEvents();
+
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QTRY_COMPARE(listview->count(), 20);
+
+ listview->positionViewAtIndex(10, QSGListView::Beginning);
+
+ QTRY_COMPARE(listview->count(), 25);
+
+ delete canvas;
+}
+
+void tst_QSGListView::onAdd()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(int, itemsToAdd);
+
+ const int delegateHeight = 10;
+ TestModel2 model;
+
+ // these initial items should not trigger ListView.onAdd
+ for (int i=0; i<initialItemCount; i++)
+ model.addItem("dummy value", "dummy value");
+
+ QSGView *canvas = createView();
+ canvas->setFixedSize(200, delegateHeight * (initialItemCount + itemsToAdd));
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("delegateHeight", delegateHeight);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
+
+ QObject *object = canvas->rootObject();
+ object->setProperty("width", canvas->width());
+ object->setProperty("height", canvas->height());
+ qApp->processEvents();
+
+ QList<QPair<QString, QString> > items;
+ for (int i=0; i<itemsToAdd; i++)
+ items << qMakePair(QString("value %1").arg(i), QString::number(i));
+ model.addItems(items);
+
+ qApp->processEvents();
+
+ QVariantList result = object->property("addedDelegates").toList();
+ QCOMPARE(result.count(), items.count());
+ for (int i=0; i<items.count(); i++)
+ QCOMPARE(result[i].toString(), items[i].first);
+
+ delete canvas;
+}
+
+void tst_QSGListView::onAdd_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<int>("itemsToAdd");
+
+ QTest::newRow("0, add 1") << 0 << 1;
+ QTest::newRow("0, add 2") << 0 << 2;
+ QTest::newRow("0, add 10") << 0 << 10;
+
+ QTest::newRow("1, add 1") << 1 << 1;
+ QTest::newRow("1, add 2") << 1 << 2;
+ QTest::newRow("1, add 10") << 1 << 10;
+
+ QTest::newRow("5, add 1") << 5 << 1;
+ QTest::newRow("5, add 2") << 5 << 2;
+ QTest::newRow("5, add 10") << 5 << 10;
+}
+
+void tst_QSGListView::onRemove()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(int, indexToRemove);
+ QFETCH(int, removeCount);
+
+ const int delegateHeight = 10;
+ TestModel2 model;
+ for (int i=0; i<initialItemCount; i++)
+ model.addItem(QString("value %1").arg(i), "dummy value");
+
+ QSGView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("delegateHeight", delegateHeight);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
+ QObject *object = canvas->rootObject();
+
+ qApp->processEvents();
+
+ model.removeItems(indexToRemove, removeCount);
+ qApp->processEvents();
+ QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
+
+ delete canvas;
+}
+
+void tst_QSGListView::onRemove_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<int>("indexToRemove");
+ QTest::addColumn<int>("removeCount");
+
+ QTest::newRow("remove first") << 1 << 0 << 1;
+ QTest::newRow("two items, remove first") << 2 << 0 << 1;
+ QTest::newRow("two items, remove last") << 2 << 1 << 1;
+ QTest::newRow("two items, remove all") << 2 << 0 << 2;
+
+ QTest::newRow("four items, remove first") << 4 << 0 << 1;
+ QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
+ QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
+ QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
+ QTest::newRow("four items, remove last") << 4 << 3 << 1;
+ QTest::newRow("four items, remove all") << 4 << 0 << 4;
+
+ QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
+ QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
+ QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
+}
+
+void tst_QSGListView::rightToLeft()
+{
+ QSGView *canvas = createView();
+ canvas->setFixedSize(640,320);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml"));
+ qApp->processEvents();
+
+ QVERIFY(canvas->rootObject() != 0);
+ QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "view");
+ QTRY_VERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem != 0);
+
+ QSGVisualItemModel *model = canvas->rootObject()->findChild<QSGVisualItemModel*>("itemModel");
+ QTRY_VERIFY(model != 0);
+
+ QTRY_VERIFY(model->count() == 3);
+ QTRY_COMPARE(listview->currentIndex(), 0);
+
+ // initial position at first item, right edge aligned
+ QCOMPARE(listview->contentX(), -640.);
+
+ QSGItem *item = findItem<QSGItem>(contentItem, "item1");
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), -100.0);
+ QCOMPARE(item->height(), listview->height());
+
+ QSGText *text = findItem<QSGText>(contentItem, "text1");
+ QTRY_VERIFY(text);
+ QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
+
+ listview->setCurrentIndex(2);
+
+ item = findItem<QSGItem>(contentItem, "item3");
+ QTRY_VERIFY(item);
+ QTRY_COMPARE(item->x(), -540.0);
+
+ text = findItem<QSGText>(contentItem, "text3");
+ QTRY_VERIFY(text);
+ QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
+
+ QCOMPARE(listview->contentX(), -640.);
+
+ // Ensure resizing maintains position relative to right edge
+ qobject_cast<QSGItem*>(canvas->rootObject())->setWidth(600);
+ QTRY_COMPARE(listview->contentX(), -600.);
+
+ delete canvas;
+}
+
+void tst_QSGListView::test_mirroring()
+{
+ QSGView *canvasA = createView();
+ canvasA->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml"));
+ QSGListView *listviewA = findItem<QSGListView>(canvasA->rootObject(), "view");
+ QTRY_VERIFY(listviewA != 0);
+
+ QSGView *canvasB = createView();
+ canvasB->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml"));
+ QSGListView *listviewB = findItem<QSGListView>(canvasB->rootObject(), "view");
+ QTRY_VERIFY(listviewA != 0);
+ qApp->processEvents();
+
+ QList<QString> objectNames;
+ objectNames << "item1" << "item2"; // << "item3"
+
+ listviewA->setProperty("layoutDirection", Qt::LeftToRight);
+ listviewB->setProperty("layoutDirection", Qt::RightToLeft);
+ QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
+
+ // LTR != RTL
+ foreach(const QString objectName, objectNames)
+ QVERIFY(findItem<QSGItem>(listviewA, objectName)->x() != findItem<QSGItem>(listviewB, objectName)->x());
+
+ listviewA->setProperty("layoutDirection", Qt::LeftToRight);
+ listviewB->setProperty("layoutDirection", Qt::LeftToRight);
+
+ // LTR == LTR
+ foreach(const QString objectName, objectNames)
+ QCOMPARE(findItem<QSGItem>(listviewA, objectName)->x(), findItem<QSGItem>(listviewB, objectName)->x());
+
+ QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection());
+ QSGItemPrivate::get(listviewB)->setLayoutMirror(true);
+ QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
+
+ // LTR != LTR+mirror
+ foreach(const QString objectName, objectNames)
+ QVERIFY(findItem<QSGItem>(listviewA, objectName)->x() != findItem<QSGItem>(listviewB, objectName)->x());
+
+ listviewA->setProperty("layoutDirection", Qt::RightToLeft);
+
+ // RTL == LTR+mirror
+ foreach(const QString objectName, objectNames)
+ QCOMPARE(findItem<QSGItem>(listviewA, objectName)->x(), findItem<QSGItem>(listviewB, objectName)->x());
+
+ listviewB->setProperty("layoutDirection", Qt::RightToLeft);
+
+ // RTL != RTL+mirror
+ foreach(const QString objectName, objectNames)
+ QVERIFY(findItem<QSGItem>(listviewA, objectName)->x() != findItem<QSGItem>(listviewB, objectName)->x());
+
+ listviewA->setProperty("layoutDirection", Qt::LeftToRight);
+
+ // LTR == RTL+mirror
+ foreach(const QString objectName, objectNames)
+ QCOMPARE(findItem<QSGItem>(listviewA, objectName)->x(), findItem<QSGItem>(listviewB, objectName)->x());
+
+ delete canvasA;
+ delete canvasB;
+}
+
+void tst_QSGListView::qListModelInterface_items()
+{
+ items<TestModel>();
+}
+
+void tst_QSGListView::qAbstractItemModel_items()
+{
+ items<TestModel2>();
+}
+
+void tst_QSGListView::qListModelInterface_changed()
+{
+ changed<TestModel>();
+}
+
+void tst_QSGListView::qAbstractItemModel_changed()
+{
+ changed<TestModel2>();
+}
+
+void tst_QSGListView::qListModelInterface_inserted()
+{
+ inserted<TestModel>();
+}
+
+void tst_QSGListView::qAbstractItemModel_inserted()
+{
+ inserted<TestModel2>();
+}
+
+void tst_QSGListView::qListModelInterface_removed()
+{
+ removed<TestModel>(false);
+ removed<TestModel>(true);
+}
+
+void tst_QSGListView::qAbstractItemModel_removed()
+{
+ removed<TestModel2>(false);
+ removed<TestModel2>(true);
+}
+
+void tst_QSGListView::qListModelInterface_moved()
+{
+ moved<TestModel>();
+}
+
+void tst_QSGListView::qAbstractItemModel_moved()
+{
+ moved<TestModel2>();
+}
+
+void tst_QSGListView::qListModelInterface_clear()
+{
+ clear<TestModel>();
+}
+
+void tst_QSGListView::qAbstractItemModel_clear()
+{
+ clear<TestModel2>();
+}
+
+QSGView *tst_QSGListView::createView()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ return canvas;
+}
+
+/*
+ Find an item with the specified objectName. If index is supplied then the
+ item must also evaluate the {index} expression equal to index
+*/
+template<typename T>
+T *tst_QSGListView::findItem(QSGItem *parent, const QString &objectName, int index)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ if (index != -1) {
+ QDeclarativeExpression e(qmlContext(item), item, "index");
+ if (e.evaluate().toInt() == index)
+ return static_cast<T*>(item);
+ } else {
+ return static_cast<T*>(item);
+ }
+ }
+ item = findItem<T>(item, objectName, index);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+template<typename T>
+QList<T*> tst_QSGListView::findItems(QSGItem *parent, const QString &objectName)
+{
+ QList<T*> items;
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item || !item->isVisible())
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
+ items.append(static_cast<T*>(item));
+ items += findItems<T>(item, objectName);
+ }
+
+ return items;
+}
+
+void tst_QSGListView::dumpTree(QSGItem *parent, int depth)
+{
+ static QString padding(" ");
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ qDebug() << padding.left(depth*2) << item;
+ dumpTree(item, depth+1);
+ }
+}
+
+
+QTEST_MAIN(tst_QSGListView)
+
+#include "tst_qsglistview.moc"
diff --git a/tests/auto/declarative/qsgloader/data/AnchoredLoader.qml b/tests/auto/declarative/qsgloader/data/AnchoredLoader.qml
new file mode 100644
index 0000000000..1a2a620d7f
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/AnchoredLoader.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 300
+ height: 200
+ color: "blue"
+ Loader {
+ objectName: "loader"
+ anchors.fill: parent
+ sourceComponent: Component {
+ Rectangle { color: "red"; objectName: "sourceElement" }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/BlueRect.qml b/tests/auto/declarative/qsgloader/data/BlueRect.qml
new file mode 100644
index 0000000000..e96ac00f21
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/BlueRect.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Rectangle {
+ objectName: "blue"
+ width: 100
+ height: 100
+ color: "blue"
+}
diff --git a/tests/auto/declarative/qsgloader/data/CreationContextLoader.qml b/tests/auto/declarative/qsgloader/data/CreationContextLoader.qml
new file mode 100644
index 0000000000..4dd73e797c
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/CreationContextLoader.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.0
+
+Loader {
+ id: myLoader
+ property int testProperty: 1912
+ sourceComponent: loaderComponent
+ Component {
+ id: loaderComponent
+ Item {
+ Component.onCompleted: {
+ test = (myLoader.testProperty == 1912);
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/GraphicsWidget250x250.qml b/tests/auto/declarative/qsgloader/data/GraphicsWidget250x250.qml
new file mode 100644
index 0000000000..dae8e3fbbb
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/GraphicsWidget250x250.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+QGraphicsWidget {
+ size: "250x250"
+}
diff --git a/tests/auto/declarative/qsgloader/data/GreenRect.qml b/tests/auto/declarative/qsgloader/data/GreenRect.qml
new file mode 100644
index 0000000000..99cefaf176
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/GreenRect.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 100; height: 100
+ color: "green"
+ Component.onCompleted: myLoader.source = "BlueRect.qml"
+}
diff --git a/tests/auto/declarative/qsgloader/data/NoResize.qml b/tests/auto/declarative/qsgloader/data/NoResize.qml
new file mode 100644
index 0000000000..9b3ea6410b
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/NoResize.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Item {
+ width: 200; height: 80
+ Loader {
+ source: "Rect120x60.qml"
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/NoResizeGraphicsWidget.qml b/tests/auto/declarative/qsgloader/data/NoResizeGraphicsWidget.qml
new file mode 100644
index 0000000000..c0f51d8c35
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/NoResizeGraphicsWidget.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Item {
+ width: 200
+ height: 80
+ Loader {
+ source: "GraphicsWidget250x250.qml"
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/QTBUG_16928.qml b/tests/auto/declarative/qsgloader/data/QTBUG_16928.qml
new file mode 100644
index 0000000000..903d7f0812
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/QTBUG_16928.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "green"
+ width: loader.implicitWidth+50
+ height: loader.implicitHeight+50
+
+ Loader {
+ id: loader
+ sourceComponent: Item {
+ anchors.centerIn: parent
+
+ implicitWidth: 200
+ implicitHeight: 200
+ Rectangle {
+ color: "red"
+ anchors.fill: parent
+ }
+ }
+ anchors.fill: parent
+ anchors.margins: 15
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/QTBUG_17114.qml b/tests/auto/declarative/qsgloader/data/QTBUG_17114.qml
new file mode 100644
index 0000000000..7402037553
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/QTBUG_17114.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Rectangle {
+ property real loaderWidth: loader.width
+ property real loaderHeight: loader.height
+ width: 200
+ height: 200
+
+ Loader {
+ id: loader
+ sourceComponent: Item {
+ property real iwidth: 32
+ property real iheight: 32
+ width: iwidth
+ height: iheight
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/Rect120x60.qml b/tests/auto/declarative/qsgloader/data/Rect120x60.qml
new file mode 100644
index 0000000000..fc9e447e69
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/Rect120x60.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 120
+ height:60
+}
diff --git a/tests/auto/declarative/qsgloader/data/SetSourceComponent.qml b/tests/auto/declarative/qsgloader/data/SetSourceComponent.qml
new file mode 100644
index 0000000000..83cc358f7d
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/SetSourceComponent.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Item {
+ function clear() {
+ loader.sourceComponent = undefined
+ }
+ Component { id: comp; Rectangle { width: 100; height: 50 } }
+ Loader { id: loader; sourceComponent: comp }
+}
diff --git a/tests/auto/declarative/qsgloader/data/SizeGraphicsWidgetToLoader.qml b/tests/auto/declarative/qsgloader/data/SizeGraphicsWidgetToLoader.qml
new file mode 100644
index 0000000000..2a63b4d34f
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/SizeGraphicsWidgetToLoader.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Loader {
+ width: 200
+ height: 80
+ source: "GraphicsWidget250x250.qml"
+}
diff --git a/tests/auto/declarative/qsgloader/data/SizeLoaderToGraphicsWidget.qml b/tests/auto/declarative/qsgloader/data/SizeLoaderToGraphicsWidget.qml
new file mode 100644
index 0000000000..a9875d8e21
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/SizeLoaderToGraphicsWidget.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Loader {
+ source: "GraphicsWidget250x250.qml"
+}
diff --git a/tests/auto/declarative/qsgloader/data/SizeToItem.qml b/tests/auto/declarative/qsgloader/data/SizeToItem.qml
new file mode 100644
index 0000000000..866365754f
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/SizeToItem.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Loader {
+ source: "Rect120x60.qml"
+}
diff --git a/tests/auto/declarative/qsgloader/data/SizeToLoader.qml b/tests/auto/declarative/qsgloader/data/SizeToLoader.qml
new file mode 100644
index 0000000000..dad18c6939
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/SizeToLoader.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Loader {
+ width: 200; height: 80
+ source: "Rect120x60.qml"
+}
diff --git a/tests/auto/declarative/qsgloader/data/VmeError.qml b/tests/auto/declarative/qsgloader/data/VmeError.qml
new file mode 100644
index 0000000000..0443aa9054
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/VmeError.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 100; height: 100; color: "red"
+ signal somethingHappened
+ onSomethingHappened: QtObject {}
+}
diff --git a/tests/auto/declarative/qsgloader/data/crash.qml b/tests/auto/declarative/qsgloader/data/crash.qml
new file mode 100644
index 0000000000..e6ddc33a10
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/crash.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400
+ height: 400
+
+ function setLoaderSource() {
+ myLoader.source = "GreenRect.qml"
+ }
+
+ Loader {
+ id: myLoader
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/creationContext.qml b/tests/auto/declarative/qsgloader/data/creationContext.qml
new file mode 100644
index 0000000000..17a596cc74
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/creationContext.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+
+ CreationContextLoader {
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/differentorigin.qml b/tests/auto/declarative/qsgloader/data/differentorigin.qml
new file mode 100644
index 0000000000..56a3034fe0
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/differentorigin.qml
@@ -0,0 +1,3 @@
+import QtQuick 2.0
+
+Loader { source: "http://evil.place/evil.qml" }
diff --git a/tests/auto/declarative/qsgloader/data/implicitSize.qml b/tests/auto/declarative/qsgloader/data/implicitSize.qml
new file mode 100644
index 0000000000..5c8c8348ed
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/implicitSize.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Rectangle {
+ property real implWidth: 0
+ property real implHeight: 0
+ color: "green"
+ width: loader.implicitWidth+50
+ height: loader.implicitHeight+50
+
+ Loader {
+ id: loader
+ sourceComponent: Item {
+ anchors.centerIn: parent
+
+ implicitWidth: 100
+ implicitHeight: 100
+ Rectangle {
+ color: "red"
+ anchors.fill: parent
+ }
+ }
+
+ anchors.fill: parent
+ anchors.margins: 50
+ onImplicitWidthChanged: implWidth = implicitWidth
+ onImplicitHeightChanged: implHeight = loader.implicitHeight
+ }
+}
diff --git a/tests/auto/declarative/qsgloader/data/nonItem.qml b/tests/auto/declarative/qsgloader/data/nonItem.qml
new file mode 100644
index 0000000000..8cfa0d8efb
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/nonItem.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Loader {
+ sourceComponent: QtObject {}
+}
diff --git a/tests/auto/declarative/qsgloader/data/qmldir b/tests/auto/declarative/qsgloader/data/qmldir
new file mode 100644
index 0000000000..bf42b507c0
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/qmldir
@@ -0,0 +1 @@
+# For tst_QDeclarativeLoader::networkRequestUrl; no types needed though.
diff --git a/tests/auto/declarative/qsgloader/data/sameorigin-load.qml b/tests/auto/declarative/qsgloader/data/sameorigin-load.qml
new file mode 100644
index 0000000000..3332500be6
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/sameorigin-load.qml
@@ -0,0 +1,3 @@
+import QtQuick 2.0
+
+Item { }
diff --git a/tests/auto/declarative/qsgloader/data/sameorigin.qml b/tests/auto/declarative/qsgloader/data/sameorigin.qml
new file mode 100644
index 0000000000..84846b6aba
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/sameorigin.qml
@@ -0,0 +1,3 @@
+import QtQuick 2.0
+
+Loader { source: "sameorigin-load.qml" }
diff --git a/tests/auto/declarative/qsgloader/data/vmeErrors.qml b/tests/auto/declarative/qsgloader/data/vmeErrors.qml
new file mode 100644
index 0000000000..8e6c89dc8e
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/data/vmeErrors.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Loader {
+ source: "VmeError.qml"
+}
+
diff --git a/tests/auto/declarative/qsgloader/qsgloader.pro b/tests/auto/declarative/qsgloader/qsgloader.pro
new file mode 100644
index 0000000000..25093658de
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/qsgloader.pro
@@ -0,0 +1,19 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui network
+macx:CONFIG -= app_bundle
+
+INCLUDEPATH += ../shared/
+HEADERS += ../shared/testhttpserver.h
+SOURCES += tst_qsgloader.cpp \
+ ../shared/testhttpserver.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgloader/tst_qsgloader.cpp b/tests/auto/declarative/qsgloader/tst_qsgloader.cpp
new file mode 100644
index 0000000000..5f4adcbd95
--- /dev/null
+++ b/tests/auto/declarative/qsgloader/tst_qsgloader.cpp
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+
+#include <QSignalSpy>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <private/qsgloader_p.h>
+#include "testhttpserver.h"
+#include "../../../shared/util.h"
+
+#define SERVER_PORT 14450
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+inline QUrl TEST_FILE(const QString &filename)
+{
+ return QUrl::fromLocalFile(QLatin1String(SRCDIR) + QLatin1String("/data/") + filename);
+}
+
+class tst_QSGLoader : public QObject
+
+{
+ Q_OBJECT
+public:
+ tst_QSGLoader();
+
+private slots:
+ void sourceOrComponent();
+ void sourceOrComponent_data();
+ void clear();
+ void urlToComponent();
+ void componentToUrl();
+ void anchoredLoader();
+ void sizeLoaderToItem();
+ void sizeItemToLoader();
+ void noResize();
+ void networkRequestUrl();
+ void failNetworkRequest();
+// void networkComponent();
+
+ void deleteComponentCrash();
+ void nonItem();
+ void vmeErrors();
+ void creationContext();
+ void QTBUG_16928();
+ void implicitSize();
+ void QTBUG_17114();
+
+private:
+ QDeclarativeEngine engine;
+};
+
+
+tst_QSGLoader::tst_QSGLoader()
+{
+}
+
+void tst_QSGLoader::sourceOrComponent()
+{
+ QFETCH(QString, sourceDefinition);
+ QFETCH(QUrl, sourceUrl);
+ QFETCH(QString, errorString);
+
+ bool error = !errorString.isEmpty();
+ if (error)
+ QTest::ignoreMessage(QtWarningMsg, errorString.toUtf8().constData());
+
+ QDeclarativeComponent component(&engine);
+ component.setData(QByteArray(
+ "import QtQuick 2.0\n"
+ "Loader {\n"
+ " property int onItemChangedCount: 0\n"
+ " property int onSourceChangedCount: 0\n"
+ " property int onStatusChangedCount: 0\n"
+ " property int onProgressChangedCount: 0\n"
+ " property int onLoadedCount: 0\n")
+ + sourceDefinition.toUtf8()
+ + QByteArray(
+ " onItemChanged: onItemChangedCount += 1\n"
+ " onSourceChanged: onSourceChangedCount += 1\n"
+ " onStatusChanged: onStatusChangedCount += 1\n"
+ " onProgressChanged: onProgressChangedCount += 1\n"
+ " onLoaded: onLoadedCount += 1\n"
+ "}")
+ , TEST_FILE(""));
+
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader != 0);
+ QCOMPARE(loader->item() == 0, error);
+ QCOMPARE(loader->source(), sourceUrl);
+ QCOMPARE(loader->progress(), 1.0);
+
+ QCOMPARE(loader->status(), error ? QSGLoader::Error : QSGLoader::Ready);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), error ? 0: 1);
+
+ if (!error) {
+ QDeclarativeComponent *c = qobject_cast<QDeclarativeComponent*>(loader->children().at(0));
+ QVERIFY(c);
+ QCOMPARE(loader->sourceComponent(), c);
+ }
+
+ QCOMPARE(loader->property("onSourceChangedCount").toInt(), 1);
+ QCOMPARE(loader->property("onStatusChangedCount").toInt(), 1);
+ QCOMPARE(loader->property("onProgressChangedCount").toInt(), 1);
+
+ QCOMPARE(loader->property("onItemChangedCount").toInt(), error ? 0 : 1);
+ QCOMPARE(loader->property("onLoadedCount").toInt(), error ? 0 : 1);
+
+ delete loader;
+}
+
+void tst_QSGLoader::sourceOrComponent_data()
+{
+ QTest::addColumn<QString>("sourceDefinition");
+ QTest::addColumn<QUrl>("sourceUrl");
+ QTest::addColumn<QString>("errorString");
+
+ QTest::newRow("source") << "source: 'Rect120x60.qml'\n" << QUrl::fromLocalFile(SRCDIR "/data/Rect120x60.qml") << "";
+ QTest::newRow("sourceComponent") << "Component { id: comp; Rectangle { width: 100; height: 50 } }\n sourceComponent: comp\n" << QUrl() << "";
+
+ QTest::newRow("invalid source") << "source: 'IDontExist.qml'\n" << QUrl::fromLocalFile(SRCDIR "/data/IDontExist.qml")
+ << QString(QUrl::fromLocalFile(SRCDIR "/data/IDontExist.qml").toString() + ": File not found");
+}
+
+void tst_QSGLoader::clear()
+{
+ {
+ QDeclarativeComponent component(&engine);
+ component.setData(QByteArray(
+ "import QtQuick 2.0\n"
+ " Loader { id: loader\n"
+ " source: 'Rect120x60.qml'\n"
+ " Timer { interval: 200; running: true; onTriggered: loader.source = '' }\n"
+ " }")
+ , TEST_FILE(""));
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader != 0);
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+
+ QTRY_VERIFY(loader->item() == 0);
+ QCOMPARE(loader->progress(), 0.0);
+ QCOMPARE(loader->status(), QSGLoader::Null);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 0);
+
+ delete loader;
+ }
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("/SetSourceComponent.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QSGLoader *loader = qobject_cast<QSGLoader*>(item->QSGItem::childItems().at(0));
+ QVERIFY(loader);
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+
+ loader->setSourceComponent(0);
+
+ QVERIFY(loader->item() == 0);
+ QCOMPARE(loader->progress(), 0.0);
+ QCOMPARE(loader->status(), QSGLoader::Null);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 0);
+
+ delete item;
+ }
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("/SetSourceComponent.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QSGLoader *loader = qobject_cast<QSGLoader*>(item->QSGItem::childItems().at(0));
+ QVERIFY(loader);
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+
+ QMetaObject::invokeMethod(item, "clear");
+
+ QVERIFY(loader->item() == 0);
+ QCOMPARE(loader->progress(), 0.0);
+ QCOMPARE(loader->status(), QSGLoader::Null);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 0);
+
+ delete item;
+ }
+}
+
+void tst_QSGLoader::urlToComponent()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData(QByteArray("import QtQuick 2.0\n"
+ "Loader {\n"
+ " id: loader\n"
+ " Component { id: myComp; Rectangle { width: 10; height: 10 } }\n"
+ " source: \"Rect120x60.qml\"\n"
+ " Timer { interval: 100; running: true; onTriggered: loader.sourceComponent = myComp }\n"
+ "}" )
+ , TEST_FILE(""));
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QTest::qWait(200);
+ QTRY_VERIFY(loader != 0);
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+ QCOMPARE(loader->width(), 10.0);
+ QCOMPARE(loader->height(), 10.0);
+
+ delete loader;
+}
+
+void tst_QSGLoader::componentToUrl()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("/SetSourceComponent.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QSGLoader *loader = qobject_cast<QSGLoader*>(item->QSGItem::childItems().at(0));
+ QVERIFY(loader);
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+
+ loader->setSource(TEST_FILE("/Rect120x60.qml"));
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+ QCOMPARE(loader->width(), 120.0);
+ QCOMPARE(loader->height(), 60.0);
+
+ delete item;
+}
+
+void tst_QSGLoader::anchoredLoader()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("/AnchoredLoader.qml"));
+ QSGItem *rootItem = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(rootItem != 0);
+ QSGItem *loader = rootItem->findChild<QSGItem*>("loader");
+ QSGItem *sourceElement = rootItem->findChild<QSGItem*>("sourceElement");
+
+ QVERIFY(loader != 0);
+ QVERIFY(sourceElement != 0);
+
+ QCOMPARE(rootItem->width(), 300.0);
+ QCOMPARE(rootItem->height(), 200.0);
+
+ QCOMPARE(loader->width(), 300.0);
+ QCOMPARE(loader->height(), 200.0);
+
+ QCOMPARE(sourceElement->width(), 300.0);
+ QCOMPARE(sourceElement->height(), 200.0);
+}
+
+void tst_QSGLoader::sizeLoaderToItem()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("/SizeToItem.qml"));
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader != 0);
+ QCOMPARE(loader->width(), 120.0);
+ QCOMPARE(loader->height(), 60.0);
+
+ // Check resize
+ QSGItem *rect = qobject_cast<QSGItem*>(loader->item());
+ QVERIFY(rect);
+ rect->setWidth(150);
+ rect->setHeight(45);
+ QCOMPARE(loader->width(), 150.0);
+ QCOMPARE(loader->height(), 45.0);
+
+ // Check explicit width
+ loader->setWidth(200.0);
+ QCOMPARE(loader->width(), 200.0);
+ QCOMPARE(rect->width(), 200.0);
+ rect->setWidth(100.0); // when rect changes ...
+ QCOMPARE(rect->width(), 100.0); // ... it changes
+ QCOMPARE(loader->width(), 200.0); // ... but loader stays the same
+
+ // Check explicit height
+ loader->setHeight(200.0);
+ QCOMPARE(loader->height(), 200.0);
+ QCOMPARE(rect->height(), 200.0);
+ rect->setHeight(100.0); // when rect changes ...
+ QCOMPARE(rect->height(), 100.0); // ... it changes
+ QCOMPARE(loader->height(), 200.0); // ... but loader stays the same
+
+ // Switch mode
+ loader->setWidth(180);
+ loader->setHeight(30);
+ QCOMPARE(rect->width(), 180.0);
+ QCOMPARE(rect->height(), 30.0);
+
+ delete loader;
+}
+
+void tst_QSGLoader::sizeItemToLoader()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("/SizeToLoader.qml"));
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader != 0);
+ QCOMPARE(loader->width(), 200.0);
+ QCOMPARE(loader->height(), 80.0);
+
+ QSGItem *rect = qobject_cast<QSGItem*>(loader->item());
+ QVERIFY(rect);
+ QCOMPARE(rect->width(), 200.0);
+ QCOMPARE(rect->height(), 80.0);
+
+ // Check resize
+ loader->setWidth(180);
+ loader->setHeight(30);
+ QCOMPARE(rect->width(), 180.0);
+ QCOMPARE(rect->height(), 30.0);
+
+ // Switch mode
+ loader->resetWidth(); // reset explicit size
+ loader->resetHeight();
+ rect->setWidth(160);
+ rect->setHeight(45);
+ QCOMPARE(loader->width(), 160.0);
+ QCOMPARE(loader->height(), 45.0);
+
+ delete loader;
+}
+
+void tst_QSGLoader::noResize()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("/NoResize.qml"));
+ QSGItem* item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item != 0);
+ QCOMPARE(item->width(), 200.0);
+ QCOMPARE(item->height(), 80.0);
+
+ delete item;
+}
+
+void tst_QSGLoader::networkRequestUrl()
+{
+ TestHTTPServer server(SERVER_PORT);
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+
+ QDeclarativeComponent component(&engine);
+ component.setData(QByteArray("import QtQuick 2.0\nLoader { property int signalCount : 0; source: \"http://127.0.0.1:14450/Rect120x60.qml\"; onLoaded: signalCount += 1 }"), QUrl::fromLocalFile(SRCDIR "/dummy.qml"));
+ if (component.isError())
+ qDebug() << component.errors();
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader != 0);
+
+ QTRY_VERIFY(loader->status() == QSGLoader::Ready);
+
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(loader->property("signalCount").toInt(), 1);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+
+ delete loader;
+}
+
+/* XXX Component waits until all dependencies are loaded. Is this actually possible?
+void tst_QSGLoader::networkComponent()
+{
+ TestHTTPServer server(SERVER_PORT);
+ QVERIFY(server.isValid());
+ server.serveDirectory("slowdata", TestHTTPServer::Delay);
+
+ QDeclarativeComponent component(&engine);
+ component.setData(QByteArray(
+ "import QtQuick 2.0\n"
+ "import \"http://127.0.0.1:14450/\" as NW\n"
+ "Item {\n"
+ " Component { id: comp; NW.SlowRect {} }\n"
+ " Loader { sourceComponent: comp } }")
+ , TEST_FILE(""));
+
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QSGLoader *loader = qobject_cast<QSGLoader*>(item->QSGItem::children().at(1));
+ QVERIFY(loader);
+ QTRY_VERIFY(loader->status() == QSGLoader::Ready);
+
+ QVERIFY(loader->item());
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(loader->status(), QSGLoader::Ready);
+ QCOMPARE(static_cast<QSGItem*>(loader)->children().count(), 1);
+
+ delete loader;
+}
+*/
+
+void tst_QSGLoader::failNetworkRequest()
+{
+ TestHTTPServer server(SERVER_PORT);
+ QVERIFY(server.isValid());
+ server.serveDirectory(SRCDIR "/data");
+
+ QTest::ignoreMessage(QtWarningMsg, "http://127.0.0.1:14450/IDontExist.qml: File not found");
+
+ QDeclarativeComponent component(&engine);
+ component.setData(QByteArray("import QtQuick 2.0\nLoader { property int did_load: 123; source: \"http://127.0.0.1:14450/IDontExist.qml\"; onLoaded: did_load=456 }"), QUrl::fromLocalFile("http://127.0.0.1:14450/dummy.qml"));
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader != 0);
+
+ QTRY_VERIFY(loader->status() == QSGLoader::Error);
+
+ QVERIFY(loader->item() == 0);
+ QCOMPARE(loader->progress(), 0.0);
+ QCOMPARE(loader->property("did_load").toInt(), 123);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 0);
+
+ delete loader;
+}
+
+// QTBUG-9241
+void tst_QSGLoader::deleteComponentCrash()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("crash.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ item->metaObject()->invokeMethod(item, "setLoaderSource");
+
+ QSGLoader *loader = qobject_cast<QSGLoader*>(item->QSGItem::childItems().at(0));
+ QVERIFY(loader);
+ QVERIFY(loader->item());
+ QCOMPARE(loader->item()->objectName(), QLatin1String("blue"));
+ QCOMPARE(loader->progress(), 1.0);
+ QCOMPARE(loader->status(), QSGLoader::Ready);
+ QCOMPARE(static_cast<QSGItem*>(loader)->childItems().count(), 1);
+ QVERIFY(loader->source() == QUrl::fromLocalFile(SRCDIR "/data/BlueRect.qml"));
+
+ delete item;
+}
+
+void tst_QSGLoader::nonItem()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("nonItem.qml"));
+ QString err = QUrl::fromLocalFile(SRCDIR).toString() + "/data/nonItem.qml:3:1: QML Loader: Loader does not support loading non-visual elements.";
+
+ QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData());
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader);
+ QVERIFY(loader->item() == 0);
+
+ delete loader;
+}
+
+void tst_QSGLoader::vmeErrors()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("vmeErrors.qml"));
+ QString err = QUrl::fromLocalFile(SRCDIR).toString() + "/data/VmeError.qml:6: Cannot assign object type QObject with no default method";
+ QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData());
+ QSGLoader *loader = qobject_cast<QSGLoader*>(component.create());
+ QVERIFY(loader);
+ QVERIFY(loader->item() == 0);
+
+ delete loader;
+}
+
+// QTBUG-13481
+void tst_QSGLoader::creationContext()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("creationContext.qml"));
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ QCOMPARE(o->property("test").toBool(), true);
+
+ delete o;
+}
+
+void tst_QSGLoader::QTBUG_16928()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("QTBUG_16928.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QCOMPARE(item->width(), 250.);
+ QCOMPARE(item->height(), 250.);
+
+ delete item;
+}
+
+void tst_QSGLoader::implicitSize()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("implicitSize.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QCOMPARE(item->width(), 150.);
+ QCOMPARE(item->height(), 150.);
+
+ QCOMPARE(item->property("implHeight").toReal(), 100.);
+ QCOMPARE(item->property("implWidth").toReal(), 100.);
+
+ delete item;
+}
+
+void tst_QSGLoader::QTBUG_17114()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("QTBUG_17114.qml"));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+
+ QCOMPARE(item->property("loaderWidth").toReal(), 32.);
+ QCOMPARE(item->property("loaderHeight").toReal(), 32.);
+
+ delete item;
+}
+
+QTEST_MAIN(tst_QSGLoader)
+
+#include "tst_qsgloader.moc"
diff --git a/tests/auto/declarative/qsgmousearea/data/clickThrough.qml b/tests/auto/declarative/qsgmousearea/data/clickThrough.qml
new file mode 100644
index 0000000000..0d954f8511
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/clickThrough.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Item{
+ width: 200
+ height: 200
+ property int doubleClicks: 0
+ property int clicks: 0
+ property int pressAndHolds: 0
+ property int presses: 0
+ MouseArea{
+ z: 0
+ anchors.fill: parent
+ onPressed: presses++
+ onClicked: clicks++
+ onPressAndHold: pressAndHolds++
+ onDoubleClicked: doubleClicks++
+ }
+ MouseArea{
+ z: 1
+ enabled: true
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/clickThrough2.qml b/tests/auto/declarative/qsgmousearea/data/clickThrough2.qml
new file mode 100644
index 0000000000..bc73a1bf8a
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/clickThrough2.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+
+Item{
+ width: 300
+ height: 300
+ property int doubleClicks: 0
+ property int clicks: 0
+ property int pressAndHolds: 0
+ property int presses: 0
+ property bool letThrough: false
+ Rectangle{
+ z: 0
+ color: "lightsteelblue"
+ width: 150
+ height: 150
+ MouseArea{
+ anchors.fill: parent
+ onPressed: presses++
+ onClicked: clicks++
+ onPressAndHold: pressAndHolds++
+ onDoubleClicked: doubleClicks++
+ }
+ }
+ MouseArea{
+ z: 1
+ enabled: true
+ anchors.fill: parent
+ onClicked: mouse.accepted = !letThrough;
+ onDoubleClicked: mouse.accepted = !letThrough;
+ onPressAndHold: mouse.accepted = !letThrough;
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/clickandhold.qml b/tests/auto/declarative/qsgmousearea/data/clickandhold.qml
new file mode 100644
index 0000000000..5e4e48f6db
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/clickandhold.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property bool clicked: false
+ property bool held: false
+
+ MouseArea {
+ width: 200; height: 200
+ onClicked: { root.clicked = true }
+ onPressAndHold: { root.held = true }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/clicktwice.qml b/tests/auto/declarative/qsgmousearea/data/clicktwice.qml
new file mode 100644
index 0000000000..002d1b9047
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/clicktwice.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int clicked: 0
+ property int pressed: 0
+ property int released: 0
+
+ MouseArea {
+ width: 200; height: 200
+ onPressed: { root.pressed++ }
+ onClicked: { root.clicked++ }
+ onReleased: { root.released++ }
+ }
+}
+
diff --git a/tests/auto/declarative/qsgmousearea/data/doubleclick.qml b/tests/auto/declarative/qsgmousearea/data/doubleclick.qml
new file mode 100644
index 0000000000..1030d0c33e
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/doubleclick.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int clicked: 0
+ property int doubleClicked: 0
+ property int released: 0
+
+ MouseArea {
+ width: 200; height: 200
+ onClicked: { root.clicked++ }
+ onDoubleClicked: { root.doubleClicked++ }
+ onReleased: { root.released++ }
+ }
+}
+
diff --git a/tests/auto/declarative/qsgmousearea/data/dragging.qml b/tests/auto/declarative/qsgmousearea/data/dragging.qml
new file mode 100644
index 0000000000..d9b6ac4083
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/dragging.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+Rectangle {
+ id: whiteRect
+ width: 200
+ height: 200
+ color: "white"
+ Rectangle {
+ id: blackRect
+ objectName: "blackrect"
+ color: "black"
+ y: 50
+ x: 50
+ width: 100
+ height: 100
+ opacity: (whiteRect.width-blackRect.x+whiteRect.height-blackRect.y-199)/200
+ Text { text: blackRect.opacity}
+ MouseArea {
+ objectName: "mouseregion"
+ anchors.fill: parent
+ drag.target: blackRect
+ drag.axis: Drag.XandYAxis
+ drag.minimumX: 0
+ drag.maximumX: whiteRect.width-blackRect.width
+ drag.minimumY: 0
+ drag.maximumY: whiteRect.height-blackRect.height
+ }
+ }
+ }
diff --git a/tests/auto/declarative/qsgmousearea/data/dragproperties.qml b/tests/auto/declarative/qsgmousearea/data/dragproperties.qml
new file mode 100644
index 0000000000..421dfe26b7
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/dragproperties.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+Rectangle {
+ id: whiteRect
+ width: 200
+ height: 200
+ color: "white"
+ Rectangle {
+ id: blackRect
+ objectName: "blackrect"
+ color: "black"
+ y: 50
+ x: 50
+ width: 100
+ height: 100
+ opacity: (whiteRect.width-blackRect.x+whiteRect.height-blackRect.y-199)/200
+ Text { text: blackRect.opacity}
+ MouseArea {
+ objectName: "mouseregion"
+ anchors.fill: parent
+ drag.target: blackRect
+ drag.axis: Drag.XandYAxis
+ drag.minimumX: 0
+ drag.maximumX: whiteRect.width-blackRect.width
+ drag.minimumY: 0
+ drag.maximumY: whiteRect.height-blackRect.height
+ }
+ }
+ }
diff --git a/tests/auto/declarative/qsgmousearea/data/dragreset.qml b/tests/auto/declarative/qsgmousearea/data/dragreset.qml
new file mode 100644
index 0000000000..d7949f9139
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/dragreset.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+Rectangle {
+ id: whiteRect
+ width: 200
+ height: 200
+ color: "white"
+ Rectangle {
+ id: blackRect
+ objectName: "blackrect"
+ color: "black"
+ y: 50
+ x: 50
+ width: 100
+ height: 100
+ opacity: (whiteRect.width-blackRect.x+whiteRect.height-blackRect.y-199)/200
+ Text { text: blackRect.opacity}
+ MouseArea {
+ objectName: "mouseregion"
+ anchors.fill: parent
+ drag.target: haveTarget ? blackRect : undefined
+ drag.axis: Drag.XandYAxis
+ drag.minimumX: 0
+ drag.maximumX: whiteRect.width-blackRect.width
+ drag.minimumY: 0
+ drag.maximumY: whiteRect.height-blackRect.height
+ }
+ }
+ }
diff --git a/tests/auto/declarative/qsgmousearea/data/hoverPosition.qml b/tests/auto/declarative/qsgmousearea/data/hoverPosition.qml
new file mode 100644
index 0000000000..834f91ff29
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/hoverPosition.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400; height: 400;
+
+ property real mouseX: mousetracker.mouseX
+ property real mouseY: mousetracker.mouseY
+
+ Rectangle {
+ width: 100; height: 100;
+ MouseArea {
+ id: mousetracker;
+ anchors.fill: parent;
+ hoverEnabled: true
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/pressedOrdering.qml b/tests/auto/declarative/qsgmousearea/data/pressedOrdering.qml
new file mode 100644
index 0000000000..7aa3098100
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/pressedOrdering.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property string value: "base"
+
+ MouseArea {
+ id: mouseArea
+ width: 200; height: 200
+ onClicked: toggleState.state = "toggled"
+ }
+
+ StateGroup {
+ states: State {
+ name: "pressed"
+ when: mouseArea.pressed
+ PropertyChanges { target: root; value: "pressed" }
+ }
+ }
+
+ StateGroup {
+ id: toggleState
+ states: State {
+ name: "toggled"
+ PropertyChanges { target: root; value: "toggled" }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/preventstealing.qml b/tests/auto/declarative/qsgmousearea/data/preventstealing.qml
new file mode 100644
index 0000000000..fb0d6955c1
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/preventstealing.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Flickable {
+ property bool stealing: true
+ width: 200
+ height: 200
+ contentWidth: 400
+ contentHeight: 400
+ Rectangle {
+ color: "black"
+ width: 400
+ height: 400
+ Rectangle {
+ x: 50; y: 50
+ width: 100; height: 100
+ color: "steelblue"
+ MouseArea {
+ objectName: "mousearea"
+ anchors.fill: parent
+ preventStealing: stealing
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/rejectEvent.qml b/tests/auto/declarative/qsgmousearea/data/rejectEvent.qml
new file mode 100644
index 0000000000..816fc76fac
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/rejectEvent.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ color: "#ffffff"
+ width: 320; height: 240
+ property bool mr1_pressed: false
+ property bool mr1_released: false
+ property bool mr1_canceled: false
+ property bool mr2_pressed: false
+ property bool mr2_released: false
+ property bool mr2_canceled: false
+
+ MouseArea {
+ id: mouseRegion1
+ anchors.fill: parent
+ onPressed: { root.mr1_pressed = true }
+ onReleased: { root.mr1_released = true }
+ onCanceled: { root.mr1_canceled = true }
+ }
+ MouseArea {
+ id: mouseRegion2
+ width: 120; height: 120
+ onPressed: { root.mr2_pressed = true; mouse.accepted = false }
+ onReleased: { root.mr2_released = true }
+ onCanceled: { root.mr2_canceled = true }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/updateMousePosOnClick.qml b/tests/auto/declarative/qsgmousearea/data/updateMousePosOnClick.qml
new file mode 100644
index 0000000000..7377a2e86c
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/updateMousePosOnClick.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "#ffffff"
+ width: 320; height: 240
+ MouseArea {
+ id: mouseRegion
+ objectName: "mouseregion"
+ anchors.fill: parent
+ Rectangle {
+ id: ball
+ objectName: "ball"
+ width: 20; height: 20
+ radius: 10
+ color: "#0000ff"
+ x: { mouseRegion.mouseX }
+ y: mouseRegion.mouseY
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/data/updateMousePosOnResize.qml b/tests/auto/declarative/qsgmousearea/data/updateMousePosOnResize.qml
new file mode 100644
index 0000000000..ad52ef3820
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/data/updateMousePosOnResize.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Rectangle {
+ color: "#ffffff"
+ width: 320; height: 240
+ Rectangle {
+ id: brother
+ objectName: "brother"
+ color: "lightgreen"
+ x: 200; y: 100
+ width: 120; height: 120
+ }
+ MouseArea {
+ id: mouseRegion
+ objectName: "mouseregion"
+
+ property int x1
+ property int y1
+ property int x2
+ property int y2
+ property bool emitPositionChanged: false
+ property bool mouseMatchesPos: true
+
+ anchors.fill: brother
+ onPressed: {
+ if (mouse.x != mouseX || mouse.y != mouseY)
+ mouseMatchesPos = false
+ x1 = mouseX; y1 = mouseY
+ anchors.fill = parent
+ }
+ onPositionChanged: { emitPositionChanged = true }
+ onMousePositionChanged: {
+ if (mouse.x != mouseX || mouse.y != mouseY)
+ mouseMatchesPos = false
+ x2 = mouseX; y2 = mouseY
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgmousearea/qsgmousearea.pro b/tests/auto/declarative/qsgmousearea/qsgmousearea.pro
new file mode 100644
index 0000000000..7d47ce3ae3
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/qsgmousearea.pro
@@ -0,0 +1,17 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui network
+macx:CONFIG -= app_bundle
+
+HEADERS += ../shared/testhttpserver.h
+SOURCES += tst_qsgmousearea.cpp ../shared/testhttpserver.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgmousearea/tst_qsgmousearea.cpp b/tests/auto/declarative/qsgmousearea/tst_qsgmousearea.cpp
new file mode 100644
index 0000000000..857f888c29
--- /dev/null
+++ b/tests/auto/declarative/qsgmousearea/tst_qsgmousearea.cpp
@@ -0,0 +1,705 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtTest/QSignalSpy>
+#include <private/qsgmousearea_p.h>
+#include <private/qsgrectangle_p.h>
+#include <private/qsgflickable_p.h>
+#include <QtDeclarative/qsgview.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_QSGMouseArea: public QObject
+{
+ Q_OBJECT
+private slots:
+ void dragProperties();
+ void resetDrag();
+ void dragging();
+ void updateMouseAreaPosOnClick();
+ void updateMouseAreaPosOnResize();
+ void noOnClickedWithPressAndHold();
+ void onMousePressRejected();
+ void doubleClick();
+ void clickTwice();
+ void pressedOrdering();
+ void preventStealing();
+ void clickThrough();
+ void testQtQuick11Attributes();
+ void testQtQuick11Attributes_data();
+ void hoverPosition();
+
+private:
+ QSGView *createView();
+};
+
+void tst_QSGMouseArea::dragProperties()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragproperties.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGMouseArea *mouseRegion = canvas->rootObject()->findChild<QSGMouseArea*>("mouseregion");
+ QSGDrag *drag = mouseRegion->drag();
+ QVERIFY(mouseRegion != 0);
+ QVERIFY(drag != 0);
+
+ // target
+ QSGItem *blackRect = canvas->rootObject()->findChild<QSGItem*>("blackrect");
+ QVERIFY(blackRect != 0);
+ QVERIFY(blackRect == drag->target());
+ QSGItem *rootItem = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(rootItem != 0);
+ QSignalSpy targetSpy(drag, SIGNAL(targetChanged()));
+ drag->setTarget(rootItem);
+ QCOMPARE(targetSpy.count(),1);
+ drag->setTarget(rootItem);
+ QCOMPARE(targetSpy.count(),1);
+
+ // axis
+ QCOMPARE(drag->axis(), QSGDrag::XandYAxis);
+ QSignalSpy axisSpy(drag, SIGNAL(axisChanged()));
+ drag->setAxis(QSGDrag::XAxis);
+ QCOMPARE(drag->axis(), QSGDrag::XAxis);
+ QCOMPARE(axisSpy.count(),1);
+ drag->setAxis(QSGDrag::XAxis);
+ QCOMPARE(axisSpy.count(),1);
+
+ // minimum and maximum properties
+ QSignalSpy xminSpy(drag, SIGNAL(minimumXChanged()));
+ QSignalSpy xmaxSpy(drag, SIGNAL(maximumXChanged()));
+ QSignalSpy yminSpy(drag, SIGNAL(minimumYChanged()));
+ QSignalSpy ymaxSpy(drag, SIGNAL(maximumYChanged()));
+
+ QCOMPARE(drag->xmin(), 0.0);
+ QCOMPARE(drag->xmax(), rootItem->width()-blackRect->width());
+ QCOMPARE(drag->ymin(), 0.0);
+ QCOMPARE(drag->ymax(), rootItem->height()-blackRect->height());
+
+ drag->setXmin(10);
+ drag->setXmax(10);
+ drag->setYmin(10);
+ drag->setYmax(10);
+
+ QCOMPARE(drag->xmin(), 10.0);
+ QCOMPARE(drag->xmax(), 10.0);
+ QCOMPARE(drag->ymin(), 10.0);
+ QCOMPARE(drag->ymax(), 10.0);
+
+ QCOMPARE(xminSpy.count(),1);
+ QCOMPARE(xmaxSpy.count(),1);
+ QCOMPARE(yminSpy.count(),1);
+ QCOMPARE(ymaxSpy.count(),1);
+
+ drag->setXmin(10);
+ drag->setXmax(10);
+ drag->setYmin(10);
+ drag->setYmax(10);
+
+ QCOMPARE(xminSpy.count(),1);
+ QCOMPARE(xmaxSpy.count(),1);
+ QCOMPARE(yminSpy.count(),1);
+ QCOMPARE(ymaxSpy.count(),1);
+
+ // filterChildren
+ QSignalSpy filterChildrenSpy(drag, SIGNAL(filterChildrenChanged()));
+
+ drag->setFilterChildren(true);
+
+ QVERIFY(drag->filterChildren());
+ QCOMPARE(filterChildrenSpy.count(), 1);
+
+ drag->setFilterChildren(true);
+ QCOMPARE(filterChildrenSpy.count(), 1);
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::resetDrag()
+{
+ QSGView *canvas = createView();
+
+ canvas->rootContext()->setContextProperty("haveTarget", QVariant(true));
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragreset.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGMouseArea *mouseRegion = canvas->rootObject()->findChild<QSGMouseArea*>("mouseregion");
+ QSGDrag *drag = mouseRegion->drag();
+ QVERIFY(mouseRegion != 0);
+ QVERIFY(drag != 0);
+
+ // target
+ QSGItem *blackRect = canvas->rootObject()->findChild<QSGItem*>("blackrect");
+ QVERIFY(blackRect != 0);
+ QVERIFY(blackRect == drag->target());
+ QSGItem *rootItem = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(rootItem != 0);
+ QSignalSpy targetSpy(drag, SIGNAL(targetChanged()));
+ QVERIFY(drag->target() != 0);
+ canvas->rootContext()->setContextProperty("haveTarget", QVariant(false));
+ QCOMPARE(targetSpy.count(),1);
+ QVERIFY(drag->target() == 0);
+
+ delete canvas;
+}
+
+
+void tst_QSGMouseArea::dragging()
+{
+ QSGView *canvas = createView();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragging.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGMouseArea *mouseRegion = canvas->rootObject()->findChild<QSGMouseArea*>("mouseregion");
+ QSGDrag *drag = mouseRegion->drag();
+ QVERIFY(mouseRegion != 0);
+ QVERIFY(drag != 0);
+
+ // target
+ QSGItem *blackRect = canvas->rootObject()->findChild<QSGItem*>("blackrect");
+ QVERIFY(blackRect != 0);
+ QVERIFY(blackRect == drag->target());
+
+ QVERIFY(!drag->active());
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QVERIFY(!drag->active());
+ QCOMPARE(blackRect->x(), 50.0);
+ QCOMPARE(blackRect->y(), 50.0);
+
+ // First move event triggers drag, second is acted upon.
+ // This is due to possibility of higher stacked area taking precedence.
+ QMouseEvent moveEvent(QEvent::MouseMove, QPoint(106, 106), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+ moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(110, 110), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ QVERIFY(drag->active());
+ QCOMPARE(blackRect->x(), 60.0);
+ QCOMPARE(blackRect->y(), 60.0);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(110, 110), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QVERIFY(!drag->active());
+ QCOMPARE(blackRect->x(), 60.0);
+ QCOMPARE(blackRect->y(), 60.0);
+
+ delete canvas;
+}
+
+QSGView *tst_QSGMouseArea::createView()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ return canvas;
+}
+
+void tst_QSGMouseArea::updateMouseAreaPosOnClick()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/updateMousePosOnClick.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGMouseArea *mouseRegion = canvas->rootObject()->findChild<QSGMouseArea*>("mouseregion");
+ QVERIFY(mouseRegion != 0);
+
+ QSGRectangle *rect = canvas->rootObject()->findChild<QSGRectangle*>("ball");
+ QVERIFY(rect != 0);
+
+ QCOMPARE(mouseRegion->mouseX(), rect->x());
+ QCOMPARE(mouseRegion->mouseY(), rect->y());
+
+ QMouseEvent event(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &event);
+
+ QCOMPARE(mouseRegion->mouseX(), 100.0);
+ QCOMPARE(mouseRegion->mouseY(), 100.0);
+
+ QCOMPARE(mouseRegion->mouseX(), rect->x());
+ QCOMPARE(mouseRegion->mouseY(), rect->y());
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::updateMouseAreaPosOnResize()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/updateMousePosOnResize.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGMouseArea *mouseRegion = canvas->rootObject()->findChild<QSGMouseArea*>("mouseregion");
+ QVERIFY(mouseRegion != 0);
+
+ QSGRectangle *rect = canvas->rootObject()->findChild<QSGRectangle*>("brother");
+ QVERIFY(rect != 0);
+
+ QCOMPARE(mouseRegion->mouseX(), 0.0);
+ QCOMPARE(mouseRegion->mouseY(), 0.0);
+
+ QMouseEvent event(QEvent::MouseButtonPress, rect->pos().toPoint(), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &event);
+
+ QVERIFY(!mouseRegion->property("emitPositionChanged").toBool());
+ QVERIFY(mouseRegion->property("mouseMatchesPos").toBool());
+
+ QCOMPARE(mouseRegion->property("x1").toInt(), 0);
+ QCOMPARE(mouseRegion->property("y1").toInt(), 0);
+
+ // XXX: is it on purpose that mouseX is real and mouse.x is int?
+ QCOMPARE(mouseRegion->property("x2").toInt(), (int) rect->x());
+ QCOMPARE(mouseRegion->property("y2").toInt(), (int) rect->y());
+
+ QCOMPARE(mouseRegion->mouseX(), rect->x());
+ QCOMPARE(mouseRegion->mouseY(), rect->y());
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::noOnClickedWithPressAndHold()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/clickandhold.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QVERIFY(!canvas->rootObject()->property("clicked").toBool());
+ QVERIFY(!canvas->rootObject()->property("held").toBool());
+
+ QTest::qWait(1000);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QVERIFY(!canvas->rootObject()->property("clicked").toBool());
+ QVERIFY(canvas->rootObject()->property("held").toBool());
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::onMousePressRejected()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/rejectEvent.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+ QVERIFY(canvas->rootObject()->property("enabled").toBool());
+
+ QVERIFY(!canvas->rootObject()->property("mr1_pressed").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr1_released").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr1_canceled").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr2_pressed").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr2_released").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr2_canceled").toBool());
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QVERIFY(canvas->rootObject()->property("mr1_pressed").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr1_released").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr1_canceled").toBool());
+ QVERIFY(canvas->rootObject()->property("mr2_pressed").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr2_released").toBool());
+ QVERIFY(canvas->rootObject()->property("mr2_canceled").toBool());
+
+ QTest::qWait(200);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QVERIFY(canvas->rootObject()->property("mr1_released").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr1_canceled").toBool());
+ QVERIFY(!canvas->rootObject()->property("mr2_released").toBool());
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::doubleClick()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/doubleclick.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("released").toInt(), 1);
+
+ pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("clicked").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("doubleClicked").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("released").toInt(), 2);
+
+ delete canvas;
+}
+
+// QTBUG-14832
+void tst_QSGMouseArea::clickTwice()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/clicktwice.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("pressed").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("released").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("clicked").toInt(), 1);
+
+ pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("pressed").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("released").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("clicked").toInt(), 2);
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::pressedOrdering()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pressedOrdering.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QCOMPARE(canvas->rootObject()->property("value").toString(), QLatin1String("base"));
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QCOMPARE(canvas->rootObject()->property("value").toString(), QLatin1String("pressed"));
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("value").toString(), QLatin1String("toggled"));
+
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QCOMPARE(canvas->rootObject()->property("value").toString(), QLatin1String("pressed"));
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::preventStealing()
+{
+ QSGView *canvas = createView();
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/preventstealing.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGFlickable *flickable = qobject_cast<QSGFlickable*>(canvas->rootObject());
+ QVERIFY(flickable != 0);
+
+ QSGMouseArea *mouseArea = canvas->rootObject()->findChild<QSGMouseArea*>("mousearea");
+ QVERIFY(mouseArea != 0);
+
+ QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QSGMouseEvent*)));
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(80, 80));
+
+ // Without preventStealing, mouse movement over MouseArea would
+ // cause the Flickable to steal mouse and trigger content movement.
+
+ QMouseEvent moveEvent(QEvent::MouseMove, QPoint(70, 70), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(60, 60), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(50, 50), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ // We should have received all three move events
+ QCOMPARE(mousePositionSpy.count(), 3);
+ QVERIFY(mouseArea->pressed());
+
+ // Flickable content should not have moved.
+ QCOMPARE(flickable->contentX(), 0.);
+ QCOMPARE(flickable->contentY(), 0.);
+
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 50));
+
+ // Now allow stealing and confirm Flickable does its thing.
+ canvas->rootObject()->setProperty("stealing", false);
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(80, 80));
+
+ // Without preventStealing, mouse movement over MouseArea would
+ // cause the Flickable to steal mouse and trigger content movement.
+ moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(70, 70), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(60, 60), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(50, 50), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ // We should only have received the first move event
+ QCOMPARE(mousePositionSpy.count(), 4);
+ // Our press should be taken away
+ QVERIFY(!mouseArea->pressed());
+
+ // Flickable content should have moved.
+ QCOMPARE(flickable->contentX(), 10.);
+ QCOMPARE(flickable->contentY(), 10.);
+
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 50));
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::clickThrough()
+{
+ //With no handlers defined click, doubleClick and PressAndHold should propagate to those with handlers
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/clickThrough.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 1);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QTest::qWait(1000);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("pressAndHolds").toInt(), 1);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+ pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("doubleClicks").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("pressAndHolds").toInt(), 1);
+
+ delete canvas;
+
+ //With handlers defined click, doubleClick and PressAndHold should propagate only when explicitly ignored
+ canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/clickThrough2.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ pressEvent = QMouseEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ releaseEvent = QMouseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 0);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QTest::qWait(1000);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("pressAndHolds").toInt(), 0);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("doubleClicks").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("pressAndHolds").toInt(), 0);
+
+ canvas->rootObject()->setProperty("letThrough", QVariant(true));
+
+ pressEvent = QMouseEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &pressEvent);
+
+ releaseEvent = QMouseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 1);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QTest::qWait(1000);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("pressAndHolds").toInt(), 1);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+ pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
+
+ QApplication::sendEvent(canvas, &pressEvent);
+ QApplication::sendEvent(canvas, &releaseEvent);
+
+ QCOMPARE(canvas->rootObject()->property("presses").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("clicks").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("doubleClicks").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("pressAndHolds").toInt(), 1);
+
+ delete canvas;
+}
+
+void tst_QSGMouseArea::testQtQuick11Attributes()
+{
+ QFETCH(QString, code);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ QDeclarativeEngine engine;
+ QObject *obj;
+
+ QDeclarativeComponent valid(&engine);
+ valid.setData("import QtQuick 1.1; MouseArea { " + code.toUtf8() + " }", QUrl(""));
+ obj = valid.create();
+ QVERIFY(obj);
+ QVERIFY(valid.errorString().isEmpty());
+ delete obj;
+
+ QDeclarativeComponent invalid(&engine);
+ invalid.setData("import QtQuick 1.0; MouseArea { " + code.toUtf8() + " }", QUrl(""));
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
+ obj = invalid.create();
+ QCOMPARE(invalid.errorString(), error);
+ delete obj;
+}
+
+void tst_QSGMouseArea::testQtQuick11Attributes_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("preventStealing") << "preventStealing: true"
+ << "QDeclarativeComponent: Component is not ready"
+ << ":1 \"MouseArea.preventStealing\" is not available in QtQuick 1.0.\n";
+}
+
+void tst_QSGMouseArea::hoverPosition()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/hoverPosition.qml"));
+
+ QSGItem *root = canvas->rootObject();
+ QVERIFY(root != 0);
+
+ QCOMPARE(root->property("mouseX").toReal(), qreal(0));
+ QCOMPARE(root->property("mouseY").toReal(), qreal(0));
+
+ QMouseEvent moveEvent(QEvent::MouseMove, QPoint(10, 32), Qt::NoButton, Qt::NoButton, 0);
+ QApplication::sendEvent(canvas, &moveEvent);
+
+ QCOMPARE(root->property("mouseX").toReal(), qreal(10));
+ QCOMPARE(root->property("mouseY").toReal(), qreal(32));
+
+ delete canvas;
+}
+
+QTEST_MAIN(tst_QSGMouseArea)
+
+#include "tst_qsgmousearea.moc"
diff --git a/tests/auto/declarative/qsgpathview/data/closedPath.qml b/tests/auto/declarative/qsgpathview/data/closedPath.qml
new file mode 100644
index 0000000000..3ca34056c8
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/closedPath.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Path {
+ startY: 120
+ startX: 160
+ PathQuad {
+ y: 120
+ x: 80
+ controlY: 330
+ controlX: 100
+ }
+ PathLine {
+ y: 160
+ x: 20
+ }
+ PathCubic {
+ y: 120
+ x: 160
+ control1Y: 0
+ control1X: 100
+ control2Y: 000
+ control2X: 200
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/datamodel.qml b/tests/auto/declarative/qsgpathview/data/datamodel.qml
new file mode 100644
index 0000000000..839049f1fc
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/datamodel.qml
@@ -0,0 +1,37 @@
+import QtQuick 2.0
+
+PathView {
+ id: pathview
+ objectName: "pathview"
+ width: 240; height: 320
+ pathItemCount: testObject.pathItemCount
+
+ function checkProperties() {
+ testObject.error = false;
+ if (testObject.useModel && pathview.model != testData) {
+ console.log("model property incorrect");
+ testObject.error = true;
+ }
+ }
+
+ model: testObject.useModel ? testData : 0
+
+ delegate: Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ property bool onPath: PathView.onPath
+ width: 20; height: 20; color: name
+ Text {
+ objectName: "myText"
+ text: name
+ }
+ }
+ }
+
+ path: Path {
+ startX: 120; startY: 20;
+ PathLine { x: 120; y: 300 }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/displaypath.qml b/tests/auto/declarative/qsgpathview/data/displaypath.qml
new file mode 100644
index 0000000000..af0f381fc4
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/displaypath.qml
@@ -0,0 +1,59 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: delegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: 20
+ width: 60
+ color: "white"
+ border.color: "black"
+ Text {
+ text: index
+ }
+ Text {
+ x: 20
+ id: displayText
+ objectName: "displayText"
+ text: display
+ }
+ }
+ }
+ ]
+ PathView {
+ id: view
+ objectName: "view"
+ width: 240
+ height: 320
+ model: testModel
+ delegate: delegate
+ path: Path {
+ startY: 120
+ startX: 160
+ PathQuad {
+ y: 120
+ x: 80
+ controlY: 330
+ controlX: 100
+ }
+ PathLine {
+ y: 160
+ x: 20
+ }
+ PathCubic {
+ y: 120
+ x: 160
+ control1Y: 0
+ control1X: 100
+ control2Y: 000
+ control2X: 200
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/dragpath.qml b/tests/auto/declarative/qsgpathview/data/dragpath.qml
new file mode 100644
index 0000000000..f9c6615b04
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/dragpath.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+PathView {
+ width: 400
+ height: 200
+ model: 100
+ pathItemCount: 20
+ path: Path {
+ startX: 0; startY: 100
+ PathLine { x: 400; y: 100 }
+ }
+ delegate: Rectangle { objectName: "wrapper"; height: 100; width: 2; color: PathView.isCurrentItem?"red" : "black" }
+ dragMargin: 100
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ Text {
+ text: "current index: " + parent.currentIndex
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/emptymodel.qml b/tests/auto/declarative/qsgpathview/data/emptymodel.qml
new file mode 100644
index 0000000000..eb4d3006f4
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/emptymodel.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+PathView {
+ model: emptyModel
+}
diff --git a/tests/auto/declarative/qsgpathview/data/openPath.qml b/tests/auto/declarative/qsgpathview/data/openPath.qml
new file mode 100644
index 0000000000..1bd8375d9e
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/openPath.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+
+Path {
+ startY: 120
+ startX: 160
+ PathLine {
+ y: 160
+ x: 20
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathUpdate.qml b/tests/auto/declarative/qsgpathview/data/pathUpdate.qml
new file mode 100644
index 0000000000..e729291025
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathUpdate.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 400
+ height: 400
+
+ PathView {
+ id: view
+ objectName: "pathView"
+ anchors.fill: parent
+ model: 10
+ delegate: Rectangle { objectName: "wrapper"; color: "green"; width: 100; height: 100 }
+ path: Path {
+ startX: view.width/2; startY: 0
+ PathLine { x: view.width/2; y: view.height }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathUpdateOnStartChanged.qml b/tests/auto/declarative/qsgpathview/data/pathUpdateOnStartChanged.qml
new file mode 100644
index 0000000000..89084b2a37
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathUpdateOnStartChanged.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 800
+ height: 480
+ color: "black"
+ resources: [
+ ListModel {
+ id: appModel
+ ListElement { color: "green" }
+ },
+ Component {
+ id: appDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ color: "green"
+ width: 100
+ height: 100
+ }
+ }
+ ]
+ PathView {
+ id: pathView
+ objectName: "pathView"
+ model: appModel
+ anchors.fill: parent
+
+ transformOrigin: "Top"
+ delegate: appDelegate
+ path: Path {
+ objectName: "path"
+ startX: pathView.width / 2 // startX: 400 <- this works as expected
+ startY: 300
+ PathLine { x: 400; y: 120 }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathtest.qml b/tests/auto/declarative/qsgpathview/data/pathtest.qml
new file mode 100644
index 0000000000..736d58d2a9
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathtest.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Path {
+ startX: 120; startY: 100
+
+ PathAttribute { name: "scale"; value: 1.0 }
+ PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 }
+ PathPercent { value: 0.3 }
+ PathLine { x: 120; y: 100 }
+ PathCubic {
+ x: 180; y: 0; control1X: -10; control1Y: 90
+ control2X: 210; control2Y: 90
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathview0.qml b/tests/auto/declarative/qsgpathview/data/pathview0.qml
new file mode 100644
index 0000000000..0204112812
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathview0.qml
@@ -0,0 +1,83 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ property int currentA: -1
+ property int currentB: -1
+ property real delegateWidth: 60
+ property real delegateHeight: 20
+ property real delegateScale: 1.0
+ width: 240
+ height: 320
+ color: "#ffffff"
+ resources: [
+ Component {
+ id: delegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ height: root.delegateHeight
+ width: root.delegateWidth
+ scale: root.delegateScale
+ color: PathView.isCurrentItem ? "lightsteelblue" : "white"
+ border.color: "black"
+ Text {
+ text: index
+ }
+ Text {
+ x: 20
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ Text {
+ x: 40
+ id: textNumber
+ objectName: "textNumber"
+ text: number
+ }
+ PathView.onCurrentItemChanged: {
+ if (PathView.isCurrentItem) {
+ root.currentA = index;
+ root.currentB = wrapper.PathView.view.currentIndex;
+ }
+ }
+ }
+ }
+ ]
+ PathView {
+ id: view
+ objectName: "view"
+ width: 240
+ height: 320
+ model: testModel
+ delegate: delegate
+ highlight: Rectangle {
+ width: 60
+ height: 20
+ color: "yellow"
+ }
+ path: Path {
+ startY: 120
+ startX: 160
+ PathQuad {
+ y: 120
+ x: 80
+ controlY: 330
+ controlX: 100
+ }
+ PathLine {
+ y: 160
+ x: 20
+ }
+ PathCubic {
+ y: 120
+ x: 160
+ control1Y: 0
+ control1X: 100
+ control2Y: 000
+ control2X: 200
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathview1.qml b/tests/auto/declarative/qsgpathview/data/pathview1.qml
new file mode 100644
index 0000000000..53d375e596
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathview1.qml
@@ -0,0 +1,4 @@
+import QtQuick 2.0
+
+PathView {
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathview2.qml b/tests/auto/declarative/qsgpathview/data/pathview2.qml
new file mode 100644
index 0000000000..1d279c42a0
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathview2.qml
@@ -0,0 +1,57 @@
+import QtQuick 2.0
+
+PathView {
+ id: photoPathView
+ y: 100; width: 800; height: 330; pathItemCount: 10; z: 1
+
+ path: Path {
+ startX: -50; startY: 40;
+
+ PathAttribute { name: "scale"; value: 0.5 }
+ PathAttribute { name: "angle"; value: -45 }
+
+ PathCubic {
+ x: 400; y: 220
+ control1X: 140; control1Y: 40
+ control2X: 210; control2Y: 220
+ }
+
+ PathAttribute { name: "scale"; value: 1.2 }
+ PathAttribute { name: "angle"; value: 0 }
+
+ PathCubic {
+ x: 850; y: 40
+ control2X: 660; control2Y: 40
+ control1X: 590; control1Y: 220
+ }
+
+ PathAttribute { name: "scale"; value: 0.5 }
+ PathAttribute { name: "angle"; value: 45 }
+ }
+
+ model: ListModel {
+ id: rssModel
+ ListElement { lColor: "red" }
+ ListElement { lColor: "green" }
+ ListElement { lColor: "yellow" }
+ ListElement { lColor: "blue" }
+ ListElement { lColor: "purple" }
+ ListElement { lColor: "gray" }
+ ListElement { lColor: "brown" }
+ ListElement { lColor: "thistle" }
+ }
+
+ delegate: Component {
+ id: photoDelegate
+ Rectangle {
+ id: wrapper
+ width: 85; height: 85; color: lColor
+ scale: wrapper.PathView.scale
+
+ transform: Rotation {
+ id: itemRotation; origin.x: wrapper.width/2; origin.y: wrapper.height/2
+ axis.y: 1; axis.z: 0; angle: wrapper.PathView.angle
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathview3.qml b/tests/auto/declarative/qsgpathview/data/pathview3.qml
new file mode 100644
index 0000000000..ded5a3911c
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathview3.qml
@@ -0,0 +1,59 @@
+import QtQuick 2.0
+
+PathView {
+ id: photoPathView
+ y: 100; width: 800; height: 330; pathItemCount: 4; offset: 1
+ dragMargin: 24
+ preferredHighlightBegin: 0.50
+ preferredHighlightEnd: 0.50
+
+ path: Path {
+ startX: -50; startY: 40;
+
+ PathAttribute { name: "scale"; value: 0.5 }
+ PathAttribute { name: "angle"; value: -45 }
+
+ PathCubic {
+ x: 400; y: 220
+ control1X: 140; control1Y: 40
+ control2X: 210; control2Y: 220
+ }
+
+ PathAttribute { name: "scale"; value: 1.2 }
+ PathAttribute { name: "angle"; value: 0 }
+
+ PathCubic {
+ x: 850; y: 40
+ control2X: 660; control2Y: 40
+ control1X: 590; control1Y: 220
+ }
+
+ PathAttribute { name: "scale"; value: 0.5 }
+ PathAttribute { name: "angle"; value: 45 }
+ }
+
+ model: ListModel {
+ id: rssModel
+ ListElement { lColor: "red" }
+ ListElement { lColor: "green" }
+ ListElement { lColor: "yellow" }
+ ListElement { lColor: "blue" }
+ ListElement { lColor: "purple" }
+ ListElement { lColor: "gray" }
+ ListElement { lColor: "brown" }
+ ListElement { lColor: "thistle" }
+ }
+
+ delegate: Component {
+ id: photoDelegate
+ Rectangle {
+ id: wrapper
+ width: 85; height: 85; color: lColor
+
+ transform: Rotation {
+ id: itemRotation; origin.x: wrapper.width/2; origin.y: wrapper.height/2
+ axis.y: 1; axis.z: 0
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/pathview_package.qml b/tests/auto/declarative/qsgpathview/data/pathview_package.qml
new file mode 100644
index 0000000000..2af57e6bb1
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/pathview_package.qml
@@ -0,0 +1,88 @@
+import QtQuick 2.0
+
+Item {
+ width: 800; height: 600
+ Component {
+ id: photoDelegate
+ Package {
+ Item { id: pathItem; objectName: "pathItem"; Package.name: 'path'; width: 85; height: 85; scale: pathItem.PathView.scale }
+ Item { id: linearItem; Package.name: 'linear'; width: 85; height: 85 }
+ Rectangle {
+ id: wrapper
+ width: 85; height: 85; color: lColor
+
+ transform: Rotation {
+ id: itemRotation; origin.x: wrapper.width/2; origin.y: wrapper.height/2
+ axis.y: 1; axis.z: 0
+ }
+ state: 'path'
+ states: [
+ State {
+ name: 'path'
+ ParentChange { target: wrapper; parent: pathItem; x: 0; y: 0 }
+ PropertyChanges { target: wrapper; opacity: pathItem.PathView.onPath ? 1.0 : 0 }
+ }
+ ]
+ }
+ }
+ }
+ ListModel {
+ id: rssModel
+ ListElement { lColor: "red" }
+ ListElement { lColor: "green" }
+ ListElement { lColor: "yellow" }
+ ListElement { lColor: "blue" }
+ ListElement { lColor: "purple" }
+ ListElement { lColor: "gray" }
+ ListElement { lColor: "brown" }
+ ListElement { lColor: "thistle" }
+ }
+ VisualDataModel { id: visualModel; model: rssModel; delegate: photoDelegate }
+
+ PathView {
+ id: photoPathView
+ objectName: "photoPathView"
+ width: 800; height: 330; pathItemCount: 4; offset: 1
+ dragMargin: 24
+ preferredHighlightBegin: 0.50
+ preferredHighlightEnd: 0.50
+
+ path: Path {
+ startX: -50; startY: 40;
+
+ PathAttribute { name: "scale"; value: 0.5 }
+ PathAttribute { name: "angle"; value: -45 }
+
+ PathCubic {
+ x: 400; y: 220
+ control1X: 140; control1Y: 40
+ control2X: 210; control2Y: 220
+ }
+
+ PathAttribute { name: "scale"; value: 1.2 }
+ PathAttribute { name: "angle"; value: 0 }
+
+ PathCubic {
+ x: 850; y: 40
+ control2X: 660; control2Y: 40
+ control1X: 590; control1Y: 220
+ }
+
+ PathAttribute { name: "scale"; value: 0.5 }
+ PathAttribute { name: "angle"; value: 45 }
+ }
+
+ model: visualModel.parts.path
+ }
+
+ PathView {
+ y: 400; width: 800; height: 330; pathItemCount: 8
+
+ path: Path {
+ startX: 0; startY: 40;
+ PathLine { x: 800; y: 40 }
+ }
+
+ model: visualModel.parts.linear
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/propertychanges.qml b/tests/auto/declarative/qsgpathview/data/propertychanges.qml
new file mode 100644
index 0000000000..09b309f86f
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/propertychanges.qml
@@ -0,0 +1,116 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 350; height: 220; color: "white"
+ Component {
+ id: myDelegate
+ Item {
+ id: wrapper
+ width: 180; height: 40;
+ opacity: PathView.opacity
+ Column {
+ x: 5; y: 5
+ Text { text: '<b>Name:</b> ' + name }
+ Text { text: '<b>Number:</b> ' + number }
+ }
+ }
+ }
+
+ PathView {
+ preferredHighlightBegin: 0.1
+ preferredHighlightEnd: 0.1
+ dragMargin: 5.0
+ id: pathView
+ objectName: "pathView"
+ anchors.fill: parent
+ model: listModel
+ delegate: myDelegate
+ focus: true
+ path: Path {
+ id: myPath
+ objectName: "path"
+ startX: 220; startY: 200
+ PathAttribute { name: "opacity"; value: 1.0; objectName: "pathAttribute"; }
+ PathQuad { x: 220; y: 25; controlX: 260; controlY: 75 }
+ PathAttribute { name: "opacity"; value: 0.3 }
+ PathQuad { x: 220; y: 200; controlX: -20; controlY: 75 }
+ }
+ Timer {
+ interval: 2000; running: true; repeat: true
+ onTriggered: {
+ if (pathView.path == alternatePath)
+ pathView.path = myPath;
+ else
+ pathView.path = alternatePath;
+ }
+ }
+ }
+
+ data:[
+ ListModel {
+ id: listModel
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ ListElement {
+ name: "Bill Smith"
+ number: "555 3264"
+ }
+ ListElement {
+ name: "John Brown"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Sam Wise"
+ number: "555 0473"
+ }
+ },
+ ListModel {
+ objectName: "alternateModel"
+ ListElement {
+ name: "Jack"
+ number: "555 8426"
+ }
+ ListElement {
+ name: "Mary"
+ number: "555 3264"
+ }
+ },
+ Path {
+ id: alternatePath
+ objectName: "alternatePath"
+ startX: 100; startY: 40
+ PathAttribute { name: "opacity"; value: 0.0 }
+ PathLine { x: 100; y: 160 }
+ PathAttribute { name: "opacity"; value: 0.2 }
+ PathLine { x: 300; y: 160 }
+ PathAttribute { name: "opacity"; value: 0.0 }
+ PathLine { x: 300; y: 40 }
+ PathAttribute { name: "opacity"; value: 0.2 }
+ PathLine { x: 100; y: 40 }
+ }
+ ]
+}
+
+
diff --git a/tests/auto/declarative/qsgpathview/data/treemodel.qml b/tests/auto/declarative/qsgpathview/data/treemodel.qml
new file mode 100644
index 0000000000..fcf6922d00
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/treemodel.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+PathView {
+ width: 320
+ height: 240
+ function setRoot(index) {
+ vdm.rootIndex = vdm.modelIndex(index);
+ }
+ model: VisualDataModel {
+ id: vdm
+ model: myModel
+ delegate: Text { objectName: "wrapper"; text: display }
+ }
+
+ path: Path {
+ startX: 0; startY: 120
+ PathLine { x: 320; y: 120 }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/undefinedpath.qml b/tests/auto/declarative/qsgpathview/data/undefinedpath.qml
new file mode 100644
index 0000000000..674e7cca8d
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/undefinedpath.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.0
+
+PathView {
+ id: pathView
+ width: 240; height: 200
+ path: Path {
+ startX: pathView.undef/2.0; startY: 0
+ PathLine { x: pathView.undef/2.0; y: 0 }
+ }
+
+ delegate: Text { text: value }
+ model: ListModel {
+ ListElement { value: "one" }
+ ListElement { value: "two" }
+ ListElement { value: "three" }
+ }
+}
diff --git a/tests/auto/declarative/qsgpathview/data/vdm.qml b/tests/auto/declarative/qsgpathview/data/vdm.qml
new file mode 100644
index 0000000000..839393d9bd
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/data/vdm.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+PathView {
+ id: pathView
+ width: 240; height: 320
+
+ pathItemCount: 4
+ preferredHighlightBegin : 0.5
+ preferredHighlightEnd : 0.5
+
+ path: Path {
+ startX: 120; startY: 20;
+ PathLine { x: 120; y: 300 }
+ }
+
+ ListModel {
+ id: mo
+ ListElement { value: "one" }
+ ListElement { value: "two" }
+ ListElement { value: "three" }
+ }
+
+ model: VisualDataModel {
+ delegate: Text { text: model.value }
+ model : mo
+ }
+}
+
diff --git a/tests/auto/declarative/qsgpathview/qsgpathview.pro b/tests/auto/declarative/qsgpathview/qsgpathview.pro
new file mode 100644
index 0000000000..4380b557fb
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/qsgpathview.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgpathview.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgpathview/tst_qsgpathview.cpp b/tests/auto/declarative/qsgpathview/tst_qsgpathview.cpp
new file mode 100644
index 0000000000..df8057c4c4
--- /dev/null
+++ b/tests/auto/declarative/qsgpathview/tst_qsgpathview.cpp
@@ -0,0 +1,1058 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtDeclarative/qsgview.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/private/qsgpathview_p.h>
+#include <QtDeclarative/private/qdeclarativepath_p.h>
+#include <QtDeclarative/private/qsgtext_p.h>
+#include <QtDeclarative/private/qsgrectangle_p.h>
+#include <QtDeclarative/private/qdeclarativelistmodel_p.h>
+#include <QtDeclarative/private/qdeclarativevaluetype_p.h>
+#include <QAbstractListModel>
+#include <QStringListModel>
+#include <QStandardItemModel>
+#include <QFile>
+
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+static void initStandardTreeModel(QStandardItemModel *model)
+{
+ QStandardItem *item;
+ item = new QStandardItem(QLatin1String("Row 1 Item"));
+ model->insertRow(0, item);
+
+ item = new QStandardItem(QLatin1String("Row 2 Item"));
+ item->setCheckable(true);
+ model->insertRow(1, item);
+
+ QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
+ item->setChild(0, childItem);
+
+ item = new QStandardItem(QLatin1String("Row 3 Item"));
+ item->setIcon(QIcon());
+ model->insertRow(2, item);
+}
+
+
+class tst_QSGPathView : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QSGPathView();
+
+private slots:
+ void initValues();
+ void items();
+ void dataModel();
+ void pathview2();
+ void pathview3();
+ void path();
+ void pathMoved();
+ void setCurrentIndex();
+ void resetModel();
+ void propertyChanges();
+ void pathChanges();
+ void componentChanges();
+ void modelChanges();
+ void pathUpdateOnStartChanged();
+ void package();
+ void emptyModel();
+ void closed();
+ void pathUpdate();
+ void visualDataModel();
+ void undefinedPath();
+ void mouseDrag();
+ void treeModel();
+ void changePreferredHighlight();
+
+private:
+ QSGView *createView();
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &objectName, int index=-1);
+ template<typename T>
+ QList<T*> findItems(QSGItem *parent, const QString &objectName);
+};
+
+class TestObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool error READ error WRITE setError)
+ Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
+ Q_PROPERTY(int pathItemCount READ pathItemCount NOTIFY pathItemCountChanged)
+
+public:
+ TestObject() : QObject(), mError(true), mUseModel(true), mPathItemCount(-1) {}
+
+ bool error() const { return mError; }
+ void setError(bool err) { mError = err; }
+
+ bool useModel() const { return mUseModel; }
+ void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
+
+ int pathItemCount() const { return mPathItemCount; }
+ void setPathItemCount(int count) { mPathItemCount = count; emit pathItemCountChanged(); }
+
+signals:
+ void useModelChanged();
+ void pathItemCountChanged();
+
+private:
+ bool mError;
+ bool mUseModel;
+ int mPathItemCount;
+};
+
+class TestModel : public QAbstractListModel
+{
+public:
+ enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
+
+ TestModel(QObject *parent=0) : QAbstractListModel(parent) {
+ QHash<int, QByteArray> roles;
+ roles[Name] = "name";
+ roles[Number] = "number";
+ setRoleNames(roles);
+ }
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
+ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
+ QVariant rv;
+ if (role == Name)
+ rv = list.at(index.row()).first;
+ else if (role == Number)
+ rv = list.at(index.row()).second;
+
+ return rv;
+ }
+
+ int count() const { return rowCount(); }
+ QString name(int index) const { return list.at(index).first; }
+ QString number(int index) const { return list.at(index).second; }
+
+ void addItem(const QString &name, const QString &number) {
+ beginInsertRows(QModelIndex(), list.count(), list.count());
+ list.append(QPair<QString,QString>(name, number));
+ endInsertRows();
+ }
+
+ void insertItem(int index, const QString &name, const QString &number) {
+ beginInsertRows(QModelIndex(), index, index);
+ list.insert(index, QPair<QString,QString>(name, number));
+ endInsertRows();
+ }
+
+ void removeItem(int index) {
+ beginRemoveRows(QModelIndex(), index, index);
+ list.removeAt(index);
+ endRemoveRows();
+ }
+
+ void moveItem(int from, int to) {
+ beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
+ list.move(from, to);
+ endMoveRows();
+ }
+
+ void modifyItem(int idx, const QString &name, const QString &number) {
+ list[idx] = QPair<QString,QString>(name, number);
+ emit dataChanged(index(idx,0), index(idx,0));
+ }
+
+private:
+ QList<QPair<QString,QString> > list;
+};
+
+
+tst_QSGPathView::tst_QSGPathView()
+{
+}
+
+void tst_QSGPathView::initValues()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathview1.qml"));
+ QSGPathView *obj = qobject_cast<QSGPathView*>(c.create());
+
+ QVERIFY(obj != 0);
+ QVERIFY(obj->path() == 0);
+ QVERIFY(obj->delegate() == 0);
+ QCOMPARE(obj->model(), QVariant());
+ QCOMPARE(obj->currentIndex(), 0);
+ QCOMPARE(obj->offset(), 0.);
+ QCOMPARE(obj->preferredHighlightBegin(), 0.);
+ QCOMPARE(obj->dragMargin(), 0.);
+ QCOMPARE(obj->count(), 0);
+ QCOMPARE(obj->pathItemCount(), -1);
+}
+
+void tst_QSGPathView::items()
+{
+ QSGView *canvas = createView();
+
+ TestModel model;
+ model.addItem("Fred", "12345");
+ model.addItem("John", "2345");
+ model.addItem("Bob", "54321");
+ model.addItem("Bill", "4321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview0.qml"));
+ qApp->processEvents();
+
+ QSGPathView *pathview = findItem<QSGPathView>(canvas->rootObject(), "view");
+ QVERIFY(pathview != 0);
+
+ QCOMPARE(pathview->childItems().count(), model.count()+1); // assumes all are visible, including highlight
+
+ for (int i = 0; i < model.count(); ++i) {
+ QSGText *name = findItem<QSGText>(pathview, "textName", i);
+ QVERIFY(name != 0);
+ QCOMPARE(name->text(), model.name(i));
+ QSGText *number = findItem<QSGText>(pathview, "textNumber", i);
+ QVERIFY(number != 0);
+ QCOMPARE(number->text(), model.number(i));
+ }
+
+ QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
+ QVERIFY(path);
+
+ QVERIFY(pathview->highlightItem());
+ QPointF start = path->pointAt(0.0);
+ QPointF offset;
+ offset.setX(pathview->highlightItem()->width()/2);
+ offset.setY(pathview->highlightItem()->height()/2);
+ QCOMPARE(pathview->highlightItem()->pos() + offset, start);
+
+ delete canvas;
+}
+
+void tst_QSGPathView::pathview2()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathview2.qml"));
+ QSGPathView *obj = qobject_cast<QSGPathView*>(c.create());
+
+ QVERIFY(obj != 0);
+ QVERIFY(obj->path() != 0);
+ QVERIFY(obj->delegate() != 0);
+ QVERIFY(obj->model() != QVariant());
+ QCOMPARE(obj->currentIndex(), 0);
+ QCOMPARE(obj->offset(), 0.);
+ QCOMPARE(obj->preferredHighlightBegin(), 0.);
+ QCOMPARE(obj->dragMargin(), 0.);
+ QCOMPARE(obj->count(), 8);
+ QCOMPARE(obj->pathItemCount(), 10);
+}
+
+void tst_QSGPathView::pathview3()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathview3.qml"));
+ QSGPathView *obj = qobject_cast<QSGPathView*>(c.create());
+
+ QVERIFY(obj != 0);
+ QVERIFY(obj->path() != 0);
+ QVERIFY(obj->delegate() != 0);
+ QVERIFY(obj->model() != QVariant());
+ QCOMPARE(obj->currentIndex(), 0);
+ QCOMPARE(obj->offset(), 1.0);
+ QCOMPARE(obj->preferredHighlightBegin(), 0.5);
+ QCOMPARE(obj->dragMargin(), 24.);
+ QCOMPARE(obj->count(), 8);
+ QCOMPARE(obj->pathItemCount(), 4);
+}
+
+void tst_QSGPathView::path()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathtest.qml"));
+ QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
+
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->startX(), 120.);
+ QCOMPARE(obj->startY(), 100.);
+ QVERIFY(obj->path() != QPainterPath());
+
+ QDeclarativeListReference list(obj, "pathElements");
+ QCOMPARE(list.count(), 5);
+
+ QDeclarativePathAttribute* attr = qobject_cast<QDeclarativePathAttribute*>(list.at(0));
+ QVERIFY(attr != 0);
+ QCOMPARE(attr->name(), QString("scale"));
+ QCOMPARE(attr->value(), 1.0);
+
+ QDeclarativePathQuad* quad = qobject_cast<QDeclarativePathQuad*>(list.at(1));
+ QVERIFY(quad != 0);
+ QCOMPARE(quad->x(), 120.);
+ QCOMPARE(quad->y(), 25.);
+ QCOMPARE(quad->controlX(), 260.);
+ QCOMPARE(quad->controlY(), 75.);
+
+ QDeclarativePathPercent* perc = qobject_cast<QDeclarativePathPercent*>(list.at(2));
+ QVERIFY(perc != 0);
+ QCOMPARE(perc->value(), 0.3);
+
+ QDeclarativePathLine* line = qobject_cast<QDeclarativePathLine*>(list.at(3));
+ QVERIFY(line != 0);
+ QCOMPARE(line->x(), 120.);
+ QCOMPARE(line->y(), 100.);
+
+ QDeclarativePathCubic* cubic = qobject_cast<QDeclarativePathCubic*>(list.at(4));
+ QVERIFY(cubic != 0);
+ QCOMPARE(cubic->x(), 180.);
+ QCOMPARE(cubic->y(), 0.);
+ QCOMPARE(cubic->control1X(), -10.);
+ QCOMPARE(cubic->control1Y(), 90.);
+ QCOMPARE(cubic->control2X(), 210.);
+ QCOMPARE(cubic->control2Y(), 90.);
+}
+
+void tst_QSGPathView::dataModel()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ TestModel model;
+ model.addItem("red", "1");
+ model.addItem("green", "2");
+ model.addItem("blue", "3");
+ model.addItem("purple", "4");
+ model.addItem("gray", "5");
+ model.addItem("brown", "6");
+ model.addItem("yellow", "7");
+ model.addItem("thistle", "8");
+ model.addItem("cyan", "9");
+ model.addItem("peachpuff", "10");
+ model.addItem("powderblue", "11");
+ model.addItem("gold", "12");
+ model.addItem("sandybrown", "13");
+
+ ctxt->setContextProperty("testData", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/datamodel.qml"));
+ qApp->processEvents();
+
+ QSGPathView *pathview = qobject_cast<QSGPathView*>(canvas->rootObject());
+ QVERIFY(pathview != 0);
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QVERIFY(testObject->error() == false);
+
+ QSGItem *item = findItem<QSGItem>(pathview, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->x(), 110.0);
+ QCOMPARE(item->y(), 10.0);
+
+ model.insertItem(4, "orange", "10");
+ QTest::qWait(100);
+
+ QTRY_COMPARE(findItems<QSGItem>(pathview, "wrapper").count(), 14);
+
+ QVERIFY(pathview->currentIndex() == 0);
+
+ QSGText *text = findItem<QSGText>(pathview, "myText", 4);
+ QVERIFY(text);
+ QCOMPARE(text->text(), model.name(4));
+
+ model.removeItem(2);
+ text = findItem<QSGText>(pathview, "myText", 2);
+ QVERIFY(text);
+ QCOMPARE(text->text(), model.name(2));
+
+ testObject->setPathItemCount(5);
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QVERIFY(testObject->error() == false);
+
+ QTRY_COMPARE(findItems<QSGItem>(pathview, "wrapper").count(), 5);
+
+ QSGRectangle *testItem = findItem<QSGRectangle>(pathview, "wrapper", 4);
+ QVERIFY(testItem != 0);
+ testItem = findItem<QSGRectangle>(pathview, "wrapper", 5);
+ QVERIFY(testItem == 0);
+
+ pathview->setCurrentIndex(1);
+
+ model.insertItem(2, "pink", "2");
+ QTest::qWait(100);
+
+ QTRY_COMPARE(findItems<QSGItem>(pathview, "wrapper").count(), 5);
+ QVERIFY(pathview->currentIndex() == 1);
+
+ text = findItem<QSGText>(pathview, "myText", 2);
+ QVERIFY(text);
+ QCOMPARE(text->text(), model.name(2));
+
+ model.removeItem(3);
+ QTRY_COMPARE(findItems<QSGItem>(pathview, "wrapper").count(), 5);
+ text = findItem<QSGText>(pathview, "myText", 3);
+ QVERIFY(text);
+ QCOMPARE(text->text(), model.name(3));
+
+ model.moveItem(3, 5);
+ QTRY_COMPARE(findItems<QSGItem>(pathview, "wrapper").count(), 5);
+ QList<QSGItem*> items = findItems<QSGItem>(pathview, "wrapper");
+ foreach (QSGItem *item, items) {
+ QVERIFY(item->property("onPath").toBool());
+ }
+
+ // QTBUG-14199
+ pathview->setOffset(7);
+ pathview->setOffset(0);
+ QCOMPARE(findItems<QSGItem>(pathview, "wrapper").count(), 5);
+
+ pathview->setCurrentIndex(model.count()-1);
+ model.removeItem(model.count()-1);
+ QCOMPARE(pathview->currentIndex(), model.count()-1);
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QSGPathView::pathMoved()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ TestModel model;
+ model.addItem("Ben", "12345");
+ model.addItem("Bohn", "2345");
+ model.addItem("Bob", "54321");
+ model.addItem("Bill", "4321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview0.qml"));
+ qApp->processEvents();
+
+ QSGPathView *pathview = findItem<QSGPathView>(canvas->rootObject(), "view");
+ QVERIFY(pathview != 0);
+
+ QSGRectangle *firstItem = findItem<QSGRectangle>(pathview, "wrapper", 0);
+ QVERIFY(firstItem);
+ QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
+ QVERIFY(path);
+ QPointF start = path->pointAt(0.0);
+ QPointF offset;//Center of item is at point, but pos is from corner
+ offset.setX(firstItem->width()/2);
+ offset.setY(firstItem->height()/2);
+ QCOMPARE(firstItem->pos() + offset, start);
+ pathview->setOffset(1.0);
+
+ for(int i=0; i<model.count(); i++){
+ QSGRectangle *curItem = findItem<QSGRectangle>(pathview, "wrapper", i);
+ QPointF itemPos(path->pointAt(0.25 + i*0.25));
+ QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y())));
+ }
+
+ pathview->setOffset(0.0);
+ QCOMPARE(firstItem->pos() + offset, start);
+
+ // Change delegate size
+ pathview->setOffset(0.1);
+ pathview->setOffset(0.0);
+ canvas->rootObject()->setProperty("delegateWidth", 30);
+ QCOMPARE(firstItem->width(), 30.0);
+ offset.setX(firstItem->width()/2);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ // Change delegate scale
+ pathview->setOffset(0.1);
+ pathview->setOffset(0.0);
+ canvas->rootObject()->setProperty("delegateScale", 1.2);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ delete canvas;
+}
+
+void tst_QSGPathView::setCurrentIndex()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ TestModel model;
+ model.addItem("Ben", "12345");
+ model.addItem("Bohn", "2345");
+ model.addItem("Bob", "54321");
+ model.addItem("Bill", "4321");
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview0.qml"));
+ qApp->processEvents();
+
+ QSGPathView *pathview = findItem<QSGPathView>(canvas->rootObject(), "view");
+ QVERIFY(pathview != 0);
+
+ QSGRectangle *firstItem = findItem<QSGRectangle>(pathview, "wrapper", 0);
+ QVERIFY(firstItem);
+ QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
+ QVERIFY(path);
+ QPointF start = path->pointAt(0.0);
+ QPointF offset;//Center of item is at point, but pos is from corner
+ offset.setX(firstItem->width()/2);
+ offset.setY(firstItem->height()/2);
+ QCOMPARE(firstItem->pos() + offset, start);
+ QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 0);
+ QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 0);
+
+ pathview->setCurrentIndex(2);
+
+ firstItem = findItem<QSGRectangle>(pathview, "wrapper", 2);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+ QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 2);
+
+ pathview->decrementCurrentIndex();
+ QTRY_COMPARE(pathview->currentIndex(), 1);
+ firstItem = findItem<QSGRectangle>(pathview, "wrapper", 1);
+ QVERIFY(firstItem);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ pathview->decrementCurrentIndex();
+ QTRY_COMPARE(pathview->currentIndex(), 0);
+ firstItem = findItem<QSGRectangle>(pathview, "wrapper", 0);
+ QVERIFY(firstItem);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ pathview->decrementCurrentIndex();
+ QTRY_COMPARE(pathview->currentIndex(), 3);
+ firstItem = findItem<QSGRectangle>(pathview, "wrapper", 3);
+ QVERIFY(firstItem);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ pathview->incrementCurrentIndex();
+ QTRY_COMPARE(pathview->currentIndex(), 0);
+ firstItem = findItem<QSGRectangle>(pathview, "wrapper", 0);
+ QVERIFY(firstItem);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ delete canvas;
+}
+
+void tst_QSGPathView::resetModel()
+{
+ QSGView *canvas = createView();
+
+ QStringList strings;
+ strings << "one" << "two" << "three";
+ QStringListModel model(strings);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaypath.qml"));
+ qApp->processEvents();
+
+ QSGPathView *pathview = findItem<QSGPathView>(canvas->rootObject(), "view");
+ QVERIFY(pathview != 0);
+
+ QCOMPARE(pathview->count(), model.rowCount());
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QSGText *display = findItem<QSGText>(pathview, "displayText", i);
+ QVERIFY(display != 0);
+ QCOMPARE(display->text(), strings.at(i));
+ }
+
+ strings.clear();
+ strings << "four" << "five" << "six" << "seven";
+ model.setStringList(strings);
+
+ QCOMPARE(pathview->count(), model.rowCount());
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QSGText *display = findItem<QSGText>(pathview, "displayText", i);
+ QVERIFY(display != 0);
+ QCOMPARE(display->text(), strings.at(i));
+ }
+
+ delete canvas;
+}
+
+void tst_QSGPathView::propertyChanges()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("pathView");
+ QVERIFY(pathView);
+
+ QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
+ QSignalSpy dragMarginSpy(pathView, SIGNAL(dragMarginChanged()));
+
+ QCOMPARE(pathView->preferredHighlightBegin(), 0.1);
+ QCOMPARE(pathView->dragMargin(), 5.0);
+
+ pathView->setPreferredHighlightBegin(0.4);
+ pathView->setPreferredHighlightEnd(0.4);
+ pathView->setDragMargin(20.0);
+
+ QCOMPARE(pathView->preferredHighlightBegin(), 0.4);
+ QCOMPARE(pathView->preferredHighlightEnd(), 0.4);
+ QCOMPARE(pathView->dragMargin(), 20.0);
+
+ QCOMPARE(snapPositionSpy.count(), 1);
+ QCOMPARE(dragMarginSpy.count(), 1);
+
+ pathView->setPreferredHighlightBegin(0.4);
+ pathView->setPreferredHighlightEnd(0.4);
+ pathView->setDragMargin(20.0);
+
+ QCOMPARE(snapPositionSpy.count(), 1);
+ QCOMPARE(dragMarginSpy.count(), 1);
+ delete canvas;
+}
+
+void tst_QSGPathView::pathChanges()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("pathView");
+ QVERIFY(pathView);
+
+ QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
+ QVERIFY(path);
+
+ QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
+ QSignalSpy startYSpy(path, SIGNAL(startYChanged()));
+
+ QCOMPARE(path->startX(), 220.0);
+ QCOMPARE(path->startY(), 200.0);
+
+ path->setStartX(240.0);
+ path->setStartY(220.0);
+
+ QCOMPARE(path->startX(), 240.0);
+ QCOMPARE(path->startY(), 220.0);
+
+ QCOMPARE(startXSpy.count(),1);
+ QCOMPARE(startYSpy.count(),1);
+
+ path->setStartX(240);
+ path->setStartY(220);
+
+ QCOMPARE(startXSpy.count(),1);
+ QCOMPARE(startYSpy.count(),1);
+
+ QDeclarativePath *alternatePath = canvas->rootObject()->findChild<QDeclarativePath*>("alternatePath");
+ QVERIFY(alternatePath);
+
+ QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
+
+ QCOMPARE(pathView->path(), path);
+
+ pathView->setPath(alternatePath);
+ QCOMPARE(pathView->path(), alternatePath);
+ QCOMPARE(pathSpy.count(),1);
+
+ pathView->setPath(alternatePath);
+ QCOMPARE(pathSpy.count(),1);
+
+ QDeclarativePathAttribute *pathAttribute = canvas->rootObject()->findChild<QDeclarativePathAttribute*>("pathAttribute");
+ QVERIFY(pathAttribute);
+
+ QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
+ QCOMPARE(pathAttribute->name(), QString("opacity"));
+
+ pathAttribute->setName("scale");
+ QCOMPARE(pathAttribute->name(), QString("scale"));
+ QCOMPARE(nameSpy.count(),1);
+
+ pathAttribute->setName("scale");
+ QCOMPARE(nameSpy.count(),1);
+ delete canvas;
+}
+
+void tst_QSGPathView::componentChanges()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("pathView");
+ QVERIFY(pathView);
+
+ QDeclarativeComponent delegateComponent(canvas->engine());
+ delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
+
+ QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
+
+ pathView->setDelegate(&delegateComponent);
+ QCOMPARE(pathView->delegate(), &delegateComponent);
+ QCOMPARE(delegateSpy.count(),1);
+
+ pathView->setDelegate(&delegateComponent);
+ QCOMPARE(delegateSpy.count(),1);
+ delete canvas;
+}
+
+void tst_QSGPathView::modelChanges()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("pathView");
+ QVERIFY(pathView);
+
+ QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
+ QVERIFY(alternateModel);
+ QVariant modelVariant = QVariant::fromValue(alternateModel);
+ QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
+
+ pathView->setModel(modelVariant);
+ QCOMPARE(pathView->model(), modelVariant);
+ QCOMPARE(modelSpy.count(),1);
+
+ pathView->setModel(modelVariant);
+ QCOMPARE(modelSpy.count(),1);
+
+ pathView->setModel(QVariant());
+ QCOMPARE(modelSpy.count(),2);
+
+ delete canvas;
+}
+
+void tst_QSGPathView::pathUpdateOnStartChanged()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathUpdateOnStartChanged.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("pathView");
+ QVERIFY(pathView);
+
+ QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
+ QVERIFY(path);
+ QCOMPARE(path->startX(), 400.0);
+ QCOMPARE(path->startY(), 300.0);
+
+ QSGItem *item = findItem<QSGItem>(pathView, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
+ QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
+
+ delete canvas;
+}
+
+void tst_QSGPathView::package()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview_package.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("photoPathView");
+ QVERIFY(pathView);
+
+ QSGItem *item = findItem<QSGItem>(pathView, "pathItem");
+ QVERIFY(item);
+ QVERIFY(item->scale() != 1.0);
+
+ delete canvas;
+}
+
+//QTBUG-13017
+void tst_QSGPathView::emptyModel()
+{
+ QSGView *canvas = createView();
+
+ QStringListModel model;
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("emptyModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/emptymodel.qml"));
+ qApp->processEvents();
+
+ QSGPathView *pathview = qobject_cast<QSGPathView*>(canvas->rootObject());
+ QVERIFY(pathview != 0);
+
+ QCOMPARE(pathview->offset(), qreal(0.0));
+
+ delete canvas;
+}
+
+void tst_QSGPathView::closed()
+{
+ QDeclarativeEngine engine;
+
+ {
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/openPath.qml"));
+ QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
+ QVERIFY(obj);
+ QCOMPARE(obj->isClosed(), false);
+ delete obj;
+ }
+
+ {
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/closedPath.qml"));
+ QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
+ QVERIFY(obj);
+ QCOMPARE(obj->isClosed(), true);
+ delete obj;
+ }
+}
+
+// QTBUG-14239
+void tst_QSGPathView::pathUpdate()
+{
+ QSGView *canvas = createView();
+ QVERIFY(canvas);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathUpdate.qml"));
+
+ QSGPathView *pathView = canvas->rootObject()->findChild<QSGPathView*>("pathView");
+ QVERIFY(pathView);
+
+ QSGItem *item = findItem<QSGItem>(pathView, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->x(), 150.0);
+
+ delete canvas;
+}
+
+void tst_QSGPathView::visualDataModel()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/vdm.qml"));
+
+ QSGPathView *obj = qobject_cast<QSGPathView*>(c.create());
+ QVERIFY(obj != 0);
+
+ QCOMPARE(obj->count(), 3);
+
+ delete obj;
+}
+
+void tst_QSGPathView::undefinedPath()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/undefinedpath.qml"));
+
+ QSGPathView *obj = qobject_cast<QSGPathView*>(c.create());
+ QVERIFY(obj != 0);
+
+ QCOMPARE(obj->count(), 3);
+
+ delete obj;
+}
+
+void tst_QSGPathView::mouseDrag()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragpath.qml"));
+ canvas->show();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
+
+ QSGPathView *pathview = qobject_cast<QSGPathView*>(canvas->rootObject());
+ QVERIFY(pathview != 0);
+
+ int current = pathview->currentIndex();
+
+ QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
+
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(canvas, &mv);
+ }
+
+ QVERIFY(pathview->currentIndex() != current);
+
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
+
+ delete canvas;
+}
+
+void tst_QSGPathView::treeModel()
+{
+ QSGView *canvas = createView();
+ canvas->show();
+
+ QStandardItemModel model;
+ initStandardTreeModel(&model);
+ canvas->engine()->rootContext()->setContextProperty("myModel", &model);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/treemodel.qml"));
+
+ QSGPathView *pathview = qobject_cast<QSGPathView*>(canvas->rootObject());
+ QVERIFY(pathview != 0);
+ QCOMPARE(pathview->count(), 3);
+
+ QSGText *item = findItem<QSGText>(pathview, "wrapper", 0);
+ QVERIFY(item);
+ QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
+
+ QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
+ QCOMPARE(pathview->count(), 1);
+
+ QTRY_VERIFY(item = findItem<QSGText>(pathview, "wrapper", 0));
+ QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
+
+ delete canvas;
+}
+
+void tst_QSGPathView::changePreferredHighlight()
+{
+ QSGView *canvas = createView();
+ canvas->setFixedSize(400,200);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragpath.qml"));
+ canvas->show();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
+
+ QSGPathView *pathview = qobject_cast<QSGPathView*>(canvas->rootObject());
+ QVERIFY(pathview != 0);
+
+ int current = pathview->currentIndex();
+ QCOMPARE(current, 0);
+
+ QSGRectangle *firstItem = findItem<QSGRectangle>(pathview, "wrapper", 0);
+ QVERIFY(firstItem);
+ QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
+ QVERIFY(path);
+ QPointF start = path->pointAt(0.5);
+ start.setX(qRound(start.x()));
+ start.setY(qRound(start.y()));
+ QPointF offset;//Center of item is at point, but pos is from corner
+ offset.setX(firstItem->width()/2);
+ offset.setY(firstItem->height()/2);
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+
+ pathview->setPreferredHighlightBegin(0.8);
+ pathview->setPreferredHighlightEnd(0.8);
+ start = path->pointAt(0.8);
+ start.setX(qRound(start.x()));
+ start.setY(qRound(start.y()));
+ QTRY_COMPARE(firstItem->pos() + offset, start);
+ QCOMPARE(pathview->currentIndex(), 0);
+
+ delete canvas;
+}
+
+QSGView *tst_QSGPathView::createView()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ return canvas;
+}
+
+/*
+ Find an item with the specified objectName. If index is supplied then the
+ item must also evaluate the {index} expression equal to index
+ */
+template<typename T>
+T *tst_QSGPathView::findItem(QSGItem *parent, const QString &objectName, int index)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ if (index != -1) {
+ QDeclarativeExpression e(qmlContext(item), item, "index");
+ if (e.evaluate().toInt() == index)
+ return static_cast<T*>(item);
+ } else {
+ return static_cast<T*>(item);
+ }
+ }
+ item = findItem<T>(item, objectName, index);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+template<typename T>
+QList<T*> tst_QSGPathView::findItems(QSGItem *parent, const QString &objectName)
+{
+ QList<T*> items;
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->QSGItem::children().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
+ items.append(static_cast<T*>(item));
+ items += findItems<T>(item, objectName);
+ }
+
+ return items;
+}
+
+QTEST_MAIN(tst_QSGPathView)
+
+#include "tst_qsgpathview.moc"
diff --git a/tests/auto/declarative/qsgpincharea/data/pinchproperties.qml b/tests/auto/declarative/qsgpincharea/data/pinchproperties.qml
new file mode 100644
index 0000000000..a1cd11302f
--- /dev/null
+++ b/tests/auto/declarative/qsgpincharea/data/pinchproperties.qml
@@ -0,0 +1,46 @@
+import QtQuick 2.0
+Rectangle {
+ id: whiteRect
+ property variant center
+ property real scale
+ width: 240; height: 320
+ color: "white"
+ Rectangle {
+ id: blackRect
+ objectName: "blackrect"
+ color: "black"
+ y: 50
+ x: 50
+ width: 100
+ height: 100
+ opacity: (whiteRect.width-blackRect.x+whiteRect.height-blackRect.y-199)/200
+ Text { text: blackRect.opacity}
+ PinchArea {
+ id: pincharea
+ objectName: "pincharea"
+ anchors.fill: parent
+ pinch.target: blackRect
+ pinch.dragAxis: Drag.XandYAxis
+ pinch.minimumX: 0
+ pinch.maximumX: whiteRect.width-blackRect.width
+ pinch.minimumY: 0
+ pinch.maximumY: whiteRect.height-blackRect.height
+ pinch.minimumScale: 1.0
+ pinch.maximumScale: 2.0
+ pinch.minimumRotation: 0.0
+ pinch.maximumRotation: 90.0
+ onPinchStarted: {
+ whiteRect.center = pinch.center
+ whiteRect.scale = pinch.scale
+ }
+ onPinchUpdated: {
+ whiteRect.center = pinch.center
+ whiteRect.scale = pinch.scale
+ }
+ onPinchFinished: {
+ whiteRect.center = pinch.center
+ whiteRect.scale = pinch.scale
+ }
+ }
+ }
+ }
diff --git a/tests/auto/declarative/qsgpincharea/qsgpincharea.pro b/tests/auto/declarative/qsgpincharea/qsgpincharea.pro
new file mode 100644
index 0000000000..6f785abf63
--- /dev/null
+++ b/tests/auto/declarative/qsgpincharea/qsgpincharea.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgpincharea.cpp
+
+symbian: {
+ importFiles.sources = data
+ importFiles.path = .
+ DEPLOYMENT = importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgpincharea/tst_qsgpincharea.cpp b/tests/auto/declarative/qsgpincharea/tst_qsgpincharea.cpp
new file mode 100644
index 0000000000..01895a69bd
--- /dev/null
+++ b/tests/auto/declarative/qsgpincharea/tst_qsgpincharea.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtTest/QSignalSpy>
+#include <private/qsgpincharea_p.h>
+#include <private/qsgrectangle_p.h>
+#include <QtDeclarative/qsgview.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_QSGPinchArea: public QObject
+{
+ Q_OBJECT
+private slots:
+ void pinchProperties();
+ void scale();
+ void pan();
+
+private:
+ QSGView *createView();
+};
+
+void tst_QSGPinchArea::pinchProperties()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pinchproperties.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGPinchArea *pinchArea = canvas->rootObject()->findChild<QSGPinchArea*>("pincharea");
+ QSGPinch *pinch = pinchArea->pinch();
+ QVERIFY(pinchArea != 0);
+ QVERIFY(pinch != 0);
+
+ // target
+ QSGItem *blackRect = canvas->rootObject()->findChild<QSGItem*>("blackrect");
+ QVERIFY(blackRect != 0);
+ QVERIFY(blackRect == pinch->target());
+ QSGItem *rootItem = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(rootItem != 0);
+ QSignalSpy targetSpy(pinch, SIGNAL(targetChanged()));
+ pinch->setTarget(rootItem);
+ QCOMPARE(targetSpy.count(),1);
+ pinch->setTarget(rootItem);
+ QCOMPARE(targetSpy.count(),1);
+
+ // axis
+ QCOMPARE(pinch->axis(), QSGPinch::XandYAxis);
+ QSignalSpy axisSpy(pinch, SIGNAL(dragAxisChanged()));
+ pinch->setAxis(QSGPinch::XAxis);
+ QCOMPARE(pinch->axis(), QSGPinch::XAxis);
+ QCOMPARE(axisSpy.count(),1);
+ pinch->setAxis(QSGPinch::XAxis);
+ QCOMPARE(axisSpy.count(),1);
+
+ // minimum and maximum drag properties
+ QSignalSpy xminSpy(pinch, SIGNAL(minimumXChanged()));
+ QSignalSpy xmaxSpy(pinch, SIGNAL(maximumXChanged()));
+ QSignalSpy yminSpy(pinch, SIGNAL(minimumYChanged()));
+ QSignalSpy ymaxSpy(pinch, SIGNAL(maximumYChanged()));
+
+ QCOMPARE(pinch->xmin(), 0.0);
+ QCOMPARE(pinch->xmax(), rootItem->width()-blackRect->width());
+ QCOMPARE(pinch->ymin(), 0.0);
+ QCOMPARE(pinch->ymax(), rootItem->height()-blackRect->height());
+
+ pinch->setXmin(10);
+ pinch->setXmax(10);
+ pinch->setYmin(10);
+ pinch->setYmax(10);
+
+ QCOMPARE(pinch->xmin(), 10.0);
+ QCOMPARE(pinch->xmax(), 10.0);
+ QCOMPARE(pinch->ymin(), 10.0);
+ QCOMPARE(pinch->ymax(), 10.0);
+
+ QCOMPARE(xminSpy.count(),1);
+ QCOMPARE(xmaxSpy.count(),1);
+ QCOMPARE(yminSpy.count(),1);
+ QCOMPARE(ymaxSpy.count(),1);
+
+ pinch->setXmin(10);
+ pinch->setXmax(10);
+ pinch->setYmin(10);
+ pinch->setYmax(10);
+
+ QCOMPARE(xminSpy.count(),1);
+ QCOMPARE(xmaxSpy.count(),1);
+ QCOMPARE(yminSpy.count(),1);
+ QCOMPARE(ymaxSpy.count(),1);
+
+ // minimum and maximum scale properties
+ QSignalSpy scaleMinSpy(pinch, SIGNAL(minimumScaleChanged()));
+ QSignalSpy scaleMaxSpy(pinch, SIGNAL(maximumScaleChanged()));
+
+ QCOMPARE(pinch->minimumScale(), 1.0);
+ QCOMPARE(pinch->maximumScale(), 2.0);
+
+ pinch->setMinimumScale(0.5);
+ pinch->setMaximumScale(1.5);
+
+ QCOMPARE(pinch->minimumScale(), 0.5);
+ QCOMPARE(pinch->maximumScale(), 1.5);
+
+ QCOMPARE(scaleMinSpy.count(),1);
+ QCOMPARE(scaleMaxSpy.count(),1);
+
+ pinch->setMinimumScale(0.5);
+ pinch->setMaximumScale(1.5);
+
+ QCOMPARE(scaleMinSpy.count(),1);
+ QCOMPARE(scaleMaxSpy.count(),1);
+
+ // minimum and maximum rotation properties
+ QSignalSpy rotMinSpy(pinch, SIGNAL(minimumRotationChanged()));
+ QSignalSpy rotMaxSpy(pinch, SIGNAL(maximumRotationChanged()));
+
+ QCOMPARE(pinch->minimumRotation(), 0.0);
+ QCOMPARE(pinch->maximumRotation(), 90.0);
+
+ pinch->setMinimumRotation(-90.0);
+ pinch->setMaximumRotation(45.0);
+
+ QCOMPARE(pinch->minimumRotation(), -90.0);
+ QCOMPARE(pinch->maximumRotation(), 45.0);
+
+ QCOMPARE(rotMinSpy.count(),1);
+ QCOMPARE(rotMaxSpy.count(),1);
+
+ pinch->setMinimumRotation(-90.0);
+ pinch->setMaximumRotation(45.0);
+
+ QCOMPARE(rotMinSpy.count(),1);
+ QCOMPARE(rotMaxSpy.count(),1);
+
+ delete canvas;
+}
+
+QTouchEvent::TouchPoint makeTouchPoint(int id, QPoint p, QSGView *v, QSGItem *i)
+{
+ QTouchEvent::TouchPoint touchPoint(id);
+ touchPoint.setPos(i->mapFromScene(p));
+ touchPoint.setScreenPos(v->mapToGlobal(p));
+ touchPoint.setScenePos(p);
+ return touchPoint;
+}
+
+void tst_QSGPinchArea::scale()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pinchproperties.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QTest::qWaitForWindowShown(canvas);
+ QVERIFY(canvas->rootObject() != 0);
+ qApp->processEvents();
+
+ QSGPinchArea *pinchArea = canvas->rootObject()->findChild<QSGPinchArea*>("pincharea");
+ QSGPinch *pinch = pinchArea->pinch();
+ QVERIFY(pinchArea != 0);
+ QVERIFY(pinch != 0);
+
+ QSGItem *root = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(root != 0);
+
+ // target
+ QSGItem *blackRect = canvas->rootObject()->findChild<QSGItem*>("blackrect");
+ QVERIFY(blackRect != 0);
+
+ QPoint p1(80, 80);
+ QPoint p2(100, 100);
+
+ QTest::touchEvent(canvas).press(0, p1);
+ QTest::touchEvent(canvas).stationary(0).press(1, p2);
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ QTest::touchEvent(canvas).move(0, p1).move(1, p2);
+
+ QCOMPARE(root->property("scale").toReal(), 1.0);
+
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ QTest::touchEvent(canvas).move(0, p1).move(1, p2);
+
+ QCOMPARE(root->property("scale").toReal(), 1.5);
+ QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
+ QCOMPARE(blackRect->scale(), 1.5);
+
+ // scale beyond bound
+ p1 -= QPoint(50,50);
+ p2 += QPoint(50,50);
+ QTest::touchEvent(canvas).move(0, p1).move(1, p2);
+
+ QCOMPARE(blackRect->scale(), 2.0);
+
+ QTest::touchEvent(canvas).release(0, p1).release(1, p2);
+
+ delete canvas;
+}
+
+void tst_QSGPinchArea::pan()
+{
+ QSGView *canvas = createView();
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pinchproperties.qml"));
+ canvas->show();
+ canvas->setFocus();
+ QTest::qWaitForWindowShown(canvas);
+ QVERIFY(canvas->rootObject() != 0);
+ qApp->processEvents();
+
+ QSGPinchArea *pinchArea = canvas->rootObject()->findChild<QSGPinchArea*>("pincharea");
+ QSGPinch *pinch = pinchArea->pinch();
+ QVERIFY(pinchArea != 0);
+ QVERIFY(pinch != 0);
+
+ QSGItem *root = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(root != 0);
+
+ // target
+ QSGItem *blackRect = canvas->rootObject()->findChild<QSGItem*>("blackrect");
+ QVERIFY(blackRect != 0);
+
+ QPoint p1(80, 80);
+ QPoint p2(100, 100);
+
+ QTest::touchEvent(canvas).press(0, p1);
+ QTest::touchEvent(canvas).stationary(0).press(1, p2);
+ p1 += QPoint(10,10);
+ p2 += QPoint(10,10);
+ QTest::touchEvent(canvas).move(0, p1).move(1, p2);
+
+ QCOMPARE(root->property("scale").toReal(), 1.0);
+
+ p1 += QPoint(10,10);
+ p2 += QPoint(10,10);
+ QTest::touchEvent(canvas).move(0, p1).move(1, p2);
+
+ QCOMPARE(root->property("center").toPointF(), QPointF(60, 60)); // blackrect is at 50,50
+
+ QCOMPARE(blackRect->x(), 60.0);
+ QCOMPARE(blackRect->y(), 60.0);
+
+ // pan x beyond bound
+ p1 += QPoint(100,100);
+ p2 += QPoint(100,100);
+ QTest::touchEvent(canvas).move(0, p1).move(1, p2);
+
+ QCOMPARE(blackRect->x(), 140.0);
+ QCOMPARE(blackRect->y(), 160.0);
+
+ QTest::touchEvent(canvas).release(0, p1).release(1, p2);
+
+ delete canvas;
+}
+
+QSGView *tst_QSGPinchArea::createView()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setAttribute(Qt::WA_AcceptTouchEvents);
+ canvas->setFixedSize(240,320);
+
+ return canvas;
+}
+
+QTEST_MAIN(tst_QSGPinchArea)
+
+#include "tst_qsgpincharea.moc"
diff --git a/tests/auto/declarative/qsgpositioners/data/flow-testimplicitsize.qml b/tests/auto/declarative/qsgpositioners/data/flow-testimplicitsize.qml
new file mode 100644
index 0000000000..c32b78676c
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/flow-testimplicitsize.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 300; height: 200;
+
+ property int flowLayout: 1
+
+ Flow {
+ objectName: "flow"
+ layoutDirection: (flowLayout == 2) ? Qt.RightToLeft : Qt.LeftToRight
+ flow: (flowLayout == 1) ? Flow.TopToBottom : Flow.LeftToRight;
+
+ spacing: 20
+ anchors.horizontalCenter: parent.horizontalCenter
+ Rectangle { color: "red"; width: 100; height: 50 }
+ Rectangle { color: "blue"; width: 100; height: 50 }
+ }
+}
+
diff --git a/tests/auto/declarative/qsgpositioners/data/flowtest-toptobottom.qml b/tests/auto/declarative/qsgpositioners/data/flowtest-toptobottom.qml
new file mode 100644
index 0000000000..a7d3ee13c7
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/flowtest-toptobottom.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.0
+
+Item {
+ height: 90
+ width: 480
+ property bool testRightToLeft: false
+
+ Flow {
+ objectName: "flow"
+ height: parent.height
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ flow: Flow.TopToBottom
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "green"
+ width: 20
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ width: 50
+ height: 20
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ width: 10
+ height: 10
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/flowtest.qml b/tests/auto/declarative/qsgpositioners/data/flowtest.qml
new file mode 100644
index 0000000000..40b042dd79
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/flowtest.qml
@@ -0,0 +1,43 @@
+import QtQuick 2.0
+
+Item {
+ width: 90
+ height: 480
+ property bool testRightToLeft: false
+
+ Flow {
+ objectName: "flow"
+ width: parent.width
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "green"
+ width: 20
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ width: 50
+ height: 20
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ width: 10
+ height: 10
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/grid-animated.qml b/tests/auto/declarative/qsgpositioners/data/grid-animated.qml
new file mode 100644
index 0000000000..56e8f26953
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/grid-animated.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ property bool testRightToLeft: true
+
+ Grid {
+ objectName: "grid"
+ columns: 3
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ add: Transition {
+ NumberAnimation {
+ properties: "x,y";
+ }
+ }
+ move: Transition {
+ NumberAnimation {
+ properties: "x,y";
+ }
+ }
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ x: -100
+ y: -100
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ x: -100
+ y: -100
+ opacity: 0
+ color: "green"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ x: -100
+ y: -100
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ x: -100
+ y: -100
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ x: -100
+ y: -100
+ width: 50
+ height: 50
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/grid-spacing.qml b/tests/auto/declarative/qsgpositioners/data/grid-spacing.qml
new file mode 100644
index 0000000000..535a39037f
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/grid-spacing.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Grid {
+ objectName: "grid"
+ columns: 3
+ spacing: 4
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "green"
+ width: 20
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ width: 50
+ height: 20
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ width: 10
+ height: 10
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/grid-toptobottom.qml b/tests/auto/declarative/qsgpositioners/data/grid-toptobottom.qml
new file mode 100644
index 0000000000..45559aab5d
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/grid-toptobottom.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Grid {
+ objectName: "grid"
+ rows: 3
+ flow: Grid.TopToBottom
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "green"
+ width: 20
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ width: 50
+ height: 20
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ width: 10
+ height: 10
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/gridtest.qml b/tests/auto/declarative/qsgpositioners/data/gridtest.qml
new file mode 100644
index 0000000000..50bec1377b
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/gridtest.qml
@@ -0,0 +1,42 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ property bool testRightToLeft: false
+ Grid {
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ objectName: "grid"
+ columns: 3
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "green"
+ width: 20
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ width: 30
+ height: 20
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ width: 10
+ height: 10
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/gridzerocolumns.qml b/tests/auto/declarative/qsgpositioners/data/gridzerocolumns.qml
new file mode 100644
index 0000000000..a252f279c3
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/gridzerocolumns.qml
@@ -0,0 +1,40 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Grid {
+ objectName: "grid"
+ columns: 0
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "green"
+ width: 20
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "blue"
+ width: 50
+ height: 20
+ }
+ Rectangle {
+ objectName: "four"
+ color: "cyan"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "five"
+ color: "magenta"
+ width: 10
+ height: 10
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/horizontal-animated.qml b/tests/auto/declarative/qsgpositioners/data/horizontal-animated.qml
new file mode 100644
index 0000000000..d19cc46c8b
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/horizontal-animated.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ property bool testRightToLeft: false
+
+ Row {
+ objectName: "row"
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ add: Transition {
+ NumberAnimation {
+ properties: "x";
+ }
+ }
+ move: Transition {
+ NumberAnimation {
+ properties: "x";
+ }
+ }
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ x: -100;
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "blue"
+ x: -100;
+ opacity: 0
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ x: -100;
+ color: "green"
+ width: 50
+ height: 50
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/horizontal-spacing.qml b/tests/auto/declarative/qsgpositioners/data/horizontal-spacing.qml
new file mode 100644
index 0000000000..c6ff75ac6b
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/horizontal-spacing.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ property bool testRightToLeft: false
+
+ Row {
+ objectName: "row"
+ spacing: 10
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "red"
+ width: 20
+ height: 10
+ }
+ Rectangle {
+ objectName: "three"
+ color: "red"
+ width: 40
+ height: 20
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/horizontal.qml b/tests/auto/declarative/qsgpositioners/data/horizontal.qml
new file mode 100644
index 0000000000..235ee78c9b
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/horizontal.qml
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ property bool testRightToLeft: false
+ Row {
+ objectName: "row"
+ layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "red"
+ width: 20
+ height: 10
+ }
+ Rectangle {
+ objectName: "three"
+ color: "red"
+ width: 40
+ height: 20
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/propertychangestest.qml b/tests/auto/declarative/qsgpositioners/data/propertychangestest.qml
new file mode 100644
index 0000000000..c9fd62b012
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/propertychangestest.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.0
+
+Grid {
+ id: myGrid
+
+ width: 270
+ height: 270
+ x: 3
+ y: 3
+ columns: 4
+ spacing: 3
+
+ add: columnTransition
+ move: columnTransition
+
+ Repeater {
+ model: 20
+ Rectangle { color: "black"; width: 50; height: 50 }
+ }
+
+ data: [
+ Transition {
+ id: rowTransition
+ objectName: "rowTransition"
+ NumberAnimation {
+ properties: "x,y";
+ easing.type: "OutInCubic"
+ }
+ },
+ Transition {
+ id: columnTransition
+ objectName: "columnTransition"
+ NumberAnimation {
+ properties: "x,y";
+ easing.type: "OutInCubic"
+ }
+ }
+ ]
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/repeatertest.qml b/tests/auto/declarative/qsgpositioners/data/repeatertest.qml
new file mode 100644
index 0000000000..d90e1cf160
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/repeatertest.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Row {
+ Repeater{ model: 3;
+ delegate: Component {
+ Rectangle {
+ color: "red"
+ width: 50
+ height: 50
+ z: {if(index == 0){2;}else if(index == 1){1;} else{3;}}
+ objectName: {if(index == 0){"one";}else if(index == 1){"two";} else{"three";}}
+ }
+ }
+ }
+ }
+
+ //This crashed once (QTBUG-16959) because the repeater ended up on the end of the list
+ //If this grid just instantiates without crashing, then it has not regressed.
+ Grid {
+ id: grid
+ rows: 2
+ flow: Grid.TopToBottom
+
+ Repeater {
+ model: 13
+ Rectangle {
+ color: "goldenrod"
+ width: 100
+ height: 100
+ radius: 10
+ border.width: 1
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/vertical-animated.qml b/tests/auto/declarative/qsgpositioners/data/vertical-animated.qml
new file mode 100644
index 0000000000..69f1b7eb25
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/vertical-animated.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Column {
+ objectName: "column"
+ add: Transition {
+ NumberAnimation {
+ properties: "y";
+ }
+ }
+ move: Transition {
+ NumberAnimation {
+ properties: "y";
+ }
+ }
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ y: -100
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "blue"
+ y: -100
+ opacity: 0
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "three"
+ color: "red"
+ y: -100
+ width: 50
+ height: 50
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/vertical-spacing.qml b/tests/auto/declarative/qsgpositioners/data/vertical-spacing.qml
new file mode 100644
index 0000000000..7087961651
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/vertical-spacing.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Column {
+ objectName: "column"
+ spacing: 10
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "red"
+ width: 20
+ height: 10
+ }
+ Rectangle {
+ objectName: "three"
+ color: "red"
+ width: 40
+ height: 20
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/data/vertical.qml b/tests/auto/declarative/qsgpositioners/data/vertical.qml
new file mode 100644
index 0000000000..0c3a81f008
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/data/vertical.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+Item {
+ width: 640
+ height: 480
+ Column {
+ objectName: "column"
+ Rectangle {
+ objectName: "one"
+ color: "red"
+ width: 50
+ height: 50
+ }
+ Rectangle {
+ objectName: "two"
+ color: "red"
+ width: 20
+ height: 10
+ }
+ Rectangle {
+ objectName: "three"
+ color: "red"
+ width: 40
+ height: 20
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgpositioners/qsgpositioners.pro b/tests/auto/declarative/qsgpositioners/qsgpositioners.pro
new file mode 100644
index 0000000000..f1ba7c0505
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/qsgpositioners.pro
@@ -0,0 +1,15 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+SOURCES += tst_qsgpositioners.cpp
+macx:CONFIG -= app_bundle
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgpositioners/tst_qsgpositioners.cpp b/tests/auto/declarative/qsgpositioners/tst_qsgpositioners.cpp
new file mode 100644
index 0000000000..4e29b65d16
--- /dev/null
+++ b/tests/auto/declarative/qsgpositioners/tst_qsgpositioners.cpp
@@ -0,0 +1,1268 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <private/qlistmodelinterface_p.h>
+#include <qsgview.h>
+#include <qdeclarativeengine.h>
+#include <private/qsgrectangle_p.h>
+#include <private/qsgpositioners_p.h>
+#include <private/qdeclarativetransition_p.h>
+#include <private/qsgitem_p.h>
+#include <qdeclarativeexpression.h>
+#include <QtGui/qgraphicswidget.h>
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qsgpositioners : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgpositioners();
+
+private slots:
+ void test_horizontal();
+ void test_horizontal_rtl();
+ void test_horizontal_spacing();
+ void test_horizontal_spacing_rightToLeft();
+ void test_horizontal_animated();
+ void test_horizontal_animated_rightToLeft();
+ void test_vertical();
+ void test_vertical_spacing();
+ void test_vertical_animated();
+ void test_grid();
+ void test_grid_topToBottom();
+ void test_grid_rightToLeft();
+ void test_grid_spacing();
+ void test_grid_animated();
+ void test_grid_animated_rightToLeft();
+ void test_grid_zero_columns();
+ void test_propertychanges();
+ void test_repeater();
+ void test_flow();
+ void test_flow_rightToLeft();
+ void test_flow_topToBottom();
+ void test_flow_resize();
+ void test_flow_resize_rightToLeft();
+ void test_flow_implicit_resize();
+ void test_conflictinganchors();
+ void test_mirroring();
+private:
+ QSGView *createView(const QString &filename);
+};
+
+tst_qsgpositioners::tst_qsgpositioners()
+{
+}
+
+void tst_qsgpositioners::test_horizontal()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontal.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", false);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 70.0);
+ QCOMPARE(three->y(), 0.0);
+
+ QSGItem *row = canvas->rootObject()->findChild<QSGItem*>("row");
+ QCOMPARE(row->width(), 110.0);
+ QCOMPARE(row->height(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_horizontal_rtl()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontal.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 60.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 40.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 0.0);
+ QCOMPARE(three->y(), 0.0);
+
+ QSGItem *row = canvas->rootObject()->findChild<QSGItem*>("row");
+ QCOMPARE(row->width(), 110.0);
+ QCOMPARE(row->height(), 50.0);
+
+ // Change the width of the row and check that items stay to the right
+ row->setWidth(200);
+ QCOMPARE(one->x(), 150.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 130.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 90.0);
+ QCOMPARE(three->y(), 0.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_horizontal_spacing()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontal-spacing.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", false);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 60.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 90.0);
+ QCOMPARE(three->y(), 0.0);
+
+ QSGItem *row = canvas->rootObject()->findChild<QSGItem*>("row");
+ QCOMPARE(row->width(), 130.0);
+ QCOMPARE(row->height(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_horizontal_spacing_rightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontal-spacing.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 80.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 00.0);
+ QCOMPARE(three->y(), 0.0);
+
+ QSGItem *row = canvas->rootObject()->findChild<QSGItem*>("row");
+ QCOMPARE(row->width(), 130.0);
+ QCOMPARE(row->height(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_horizontal_animated()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontal-animated.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", false);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ //Note that they animate in
+ QCOMPARE(one->x(), -100.0);
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(three->x(), -100.0);
+
+ QSGItem *row = canvas->rootObject()->findChild<QSGItem*>("row");
+ QVERIFY(row);
+ QCOMPARE(row->width(), 100.0);
+ QCOMPARE(row->height(), 50.0);
+
+ //QTRY_COMPARE used instead of waiting for the expected time of animation completion
+ //Note that this means the duration of the animation is NOT tested
+
+ QTRY_COMPARE(one->x(), 0.0);
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(two->opacity(), 0.0);
+ QTRY_COMPARE(two->x(), -100.0);//Not 'in' yet
+ QTRY_COMPARE(two->y(), 0.0);
+ QTRY_COMPARE(three->x(), 50.0);
+ QTRY_COMPARE(three->y(), 0.0);
+
+ //Add 'two'
+ two->setOpacity(1.0);
+ QCOMPARE(two->opacity(), 1.0);
+
+ // New size should be immediate
+ QCOMPARE(row->width(), 150.0);
+ QCOMPARE(row->height(), 50.0);
+
+ QTest::qWait(0);//Let the animation start
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(three->x(), 50.0);
+
+ QTRY_COMPARE(two->x(), 50.0);
+ QTRY_COMPARE(three->x(), 100.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_horizontal_animated_rightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontal-animated.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ //Note that they animate in
+ QCOMPARE(one->x(), -100.0);
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(three->x(), -100.0);
+
+ QSGItem *row = canvas->rootObject()->findChild<QSGItem*>("row");
+ QVERIFY(row);
+ QCOMPARE(row->width(), 100.0);
+ QCOMPARE(row->height(), 50.0);
+
+ //QTRY_COMPARE used instead of waiting for the expected time of animation completion
+ //Note that this means the duration of the animation is NOT tested
+
+ QTRY_COMPARE(one->x(), 50.0);
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(two->opacity(), 0.0);
+ QTRY_COMPARE(two->x(), -100.0);//Not 'in' yet
+ QTRY_COMPARE(two->y(), 0.0);
+ QTRY_COMPARE(three->x(), 0.0);
+ QTRY_COMPARE(three->y(), 0.0);
+
+ //Add 'two'
+ two->setOpacity(1.0);
+ QCOMPARE(two->opacity(), 1.0);
+
+ // New size should be immediate
+ QCOMPARE(row->width(), 150.0);
+ QCOMPARE(row->height(), 50.0);
+
+ QTest::qWait(0);//Let the animation start
+ QCOMPARE(one->x(), 50.0);
+ QCOMPARE(two->x(), -100.0);
+
+ QTRY_COMPARE(one->x(), 100.0);
+ QTRY_COMPARE(two->x(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_vertical()
+{
+ QSGView *canvas = createView(SRCDIR "/data/vertical.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 0.0);
+ QCOMPARE(two->y(), 50.0);
+ QCOMPARE(three->x(), 0.0);
+ QCOMPARE(three->y(), 60.0);
+
+ QSGItem *column = canvas->rootObject()->findChild<QSGItem*>("column");
+ QVERIFY(column);
+ QCOMPARE(column->height(), 80.0);
+ QCOMPARE(column->width(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_vertical_spacing()
+{
+ QSGView *canvas = createView(SRCDIR "/data/vertical-spacing.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 0.0);
+ QCOMPARE(two->y(), 60.0);
+ QCOMPARE(three->x(), 0.0);
+ QCOMPARE(three->y(), 80.0);
+
+ QSGItem *column = canvas->rootObject()->findChild<QSGItem*>("column");
+ QCOMPARE(column->height(), 100.0);
+ QCOMPARE(column->width(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_vertical_animated()
+{
+ QSGView *canvas = createView(SRCDIR "/data/vertical-animated.qml");
+
+ //Note that they animate in
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QCOMPARE(one->y(), -100.0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QCOMPARE(two->y(), -100.0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QCOMPARE(three->y(), -100.0);
+
+ QSGItem *column = canvas->rootObject()->findChild<QSGItem*>("column");
+ QVERIFY(column);
+ QCOMPARE(column->height(), 100.0);
+ QCOMPARE(column->width(), 50.0);
+
+ //QTRY_COMPARE used instead of waiting for the expected time of animation completion
+ //Note that this means the duration of the animation is NOT tested
+
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(one->x(), 0.0);
+ QTRY_COMPARE(two->opacity(), 0.0);
+ QTRY_COMPARE(two->y(), -100.0);//Not 'in' yet
+ QTRY_COMPARE(two->x(), 0.0);
+ QTRY_COMPARE(three->y(), 50.0);
+ QTRY_COMPARE(three->x(), 0.0);
+
+ //Add 'two'
+ two->setOpacity(1.0);
+ QTRY_COMPARE(two->opacity(), 1.0);
+ QCOMPARE(column->height(), 150.0);
+ QCOMPARE(column->width(), 50.0);
+ QTest::qWait(0);//Let the animation start
+ QCOMPARE(two->y(), -100.0);
+ QCOMPARE(three->y(), 50.0);
+
+ QTRY_COMPARE(two->y(), 50.0);
+ QTRY_COMPARE(three->y(), 100.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid()
+{
+ QSGView *canvas = createView(SRCDIR "/data/gridtest.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 70.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 0.0);
+ QCOMPARE(four->y(), 50.0);
+ QCOMPARE(five->x(), 50.0);
+ QCOMPARE(five->y(), 50.0);
+
+ QSGGrid *grid = canvas->rootObject()->findChild<QSGGrid*>("grid");
+ QCOMPARE(grid->flow(), QSGGrid::LeftToRight);
+ QCOMPARE(grid->width(), 100.0);
+ QCOMPARE(grid->height(), 100.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid_topToBottom()
+{
+ QSGView *canvas = createView(SRCDIR "/data/grid-toptobottom.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 0.0);
+ QCOMPARE(two->y(), 50.0);
+ QCOMPARE(three->x(), 0.0);
+ QCOMPARE(three->y(), 100.0);
+ QCOMPARE(four->x(), 50.0);
+ QCOMPARE(four->y(), 0.0);
+ QCOMPARE(five->x(), 50.0);
+ QCOMPARE(five->y(), 50.0);
+
+ QSGGrid *grid = canvas->rootObject()->findChild<QSGGrid*>("grid");
+ QCOMPARE(grid->flow(), QSGGrid::TopToBottom);
+ QCOMPARE(grid->width(), 100.0);
+ QCOMPARE(grid->height(), 120.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid_rightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/gridtest.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 50.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 30.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 0.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 50.0);
+ QCOMPARE(four->y(), 50.0);
+ QCOMPARE(five->x(), 40.0);
+ QCOMPARE(five->y(), 50.0);
+
+ QSGGrid *grid = canvas->rootObject()->findChild<QSGGrid*>("grid");
+ QCOMPARE(grid->layoutDirection(), Qt::RightToLeft);
+ QCOMPARE(grid->width(), 100.0);
+ QCOMPARE(grid->height(), 100.0);
+
+ // Change the width of the grid and check that items stay to the right
+ grid->setWidth(200);
+ QCOMPARE(one->x(), 150.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 130.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 100.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 150.0);
+ QCOMPARE(four->y(), 50.0);
+ QCOMPARE(five->x(), 140.0);
+ QCOMPARE(five->y(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid_spacing()
+{
+ QSGView *canvas = createView(SRCDIR "/data/grid-spacing.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 54.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 78.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 0.0);
+ QCOMPARE(four->y(), 54.0);
+ QCOMPARE(five->x(), 54.0);
+ QCOMPARE(five->y(), 54.0);
+
+ QSGItem *grid = canvas->rootObject()->findChild<QSGItem*>("grid");
+ QCOMPARE(grid->width(), 128.0);
+ QCOMPARE(grid->height(), 104.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid_animated()
+{
+ QSGView *canvas = createView(SRCDIR "/data/grid-animated.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", false);
+
+ //Note that all animate in
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QCOMPARE(one->x(), -100.0);
+ QCOMPARE(one->y(), -100.0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(two->y(), -100.0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QCOMPARE(three->x(), -100.0);
+ QCOMPARE(three->y(), -100.0);
+
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QCOMPARE(four->x(), -100.0);
+ QCOMPARE(four->y(), -100.0);
+
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+ QCOMPARE(five->x(), -100.0);
+ QCOMPARE(five->y(), -100.0);
+
+ QSGItem *grid = canvas->rootObject()->findChild<QSGItem*>("grid");
+ QVERIFY(grid);
+ QCOMPARE(grid->width(), 150.0);
+ QCOMPARE(grid->height(), 100.0);
+
+ //QTRY_COMPARE used instead of waiting for the expected time of animation completion
+ //Note that this means the duration of the animation is NOT tested
+
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(one->x(), 0.0);
+ QTRY_COMPARE(two->opacity(), 0.0);
+ QTRY_COMPARE(two->y(), -100.0);
+ QTRY_COMPARE(two->x(), -100.0);
+ QTRY_COMPARE(three->y(), 0.0);
+ QTRY_COMPARE(three->x(), 50.0);
+ QTRY_COMPARE(four->y(), 0.0);
+ QTRY_COMPARE(four->x(), 100.0);
+ QTRY_COMPARE(five->y(), 50.0);
+ QTRY_COMPARE(five->x(), 0.0);
+
+ //Add 'two'
+ two->setOpacity(1.0);
+ QCOMPARE(two->opacity(), 1.0);
+ QCOMPARE(grid->width(), 150.0);
+ QCOMPARE(grid->height(), 100.0);
+ QTest::qWait(0);//Let the animation start
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(two->y(), -100.0);
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(three->x(), 50.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 100.0);
+ QCOMPARE(four->y(), 0.0);
+ QCOMPARE(five->x(), 0.0);
+ QCOMPARE(five->y(), 50.0);
+ //Let the animation complete
+ QTRY_COMPARE(two->x(), 50.0);
+ QTRY_COMPARE(two->y(), 0.0);
+ QTRY_COMPARE(one->x(), 0.0);
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(three->x(), 100.0);
+ QTRY_COMPARE(three->y(), 0.0);
+ QTRY_COMPARE(four->x(), 0.0);
+ QTRY_COMPARE(four->y(), 50.0);
+ QTRY_COMPARE(five->x(), 50.0);
+ QTRY_COMPARE(five->y(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid_animated_rightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/grid-animated.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ //Note that all animate in
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QCOMPARE(one->x(), -100.0);
+ QCOMPARE(one->y(), -100.0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(two->y(), -100.0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QCOMPARE(three->x(), -100.0);
+ QCOMPARE(three->y(), -100.0);
+
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QCOMPARE(four->x(), -100.0);
+ QCOMPARE(four->y(), -100.0);
+
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+ QCOMPARE(five->x(), -100.0);
+ QCOMPARE(five->y(), -100.0);
+
+ QSGItem *grid = canvas->rootObject()->findChild<QSGItem*>("grid");
+ QVERIFY(grid);
+ QCOMPARE(grid->width(), 150.0);
+ QCOMPARE(grid->height(), 100.0);
+
+ //QTRY_COMPARE used instead of waiting for the expected time of animation completion
+ //Note that this means the duration of the animation is NOT tested
+
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(one->x(), 100.0);
+ QTRY_COMPARE(two->opacity(), 0.0);
+ QTRY_COMPARE(two->y(), -100.0);
+ QTRY_COMPARE(two->x(), -100.0);
+ QTRY_COMPARE(three->y(), 0.0);
+ QTRY_COMPARE(three->x(), 50.0);
+ QTRY_COMPARE(four->y(), 0.0);
+ QTRY_COMPARE(four->x(), 0.0);
+ QTRY_COMPARE(five->y(), 50.0);
+ QTRY_COMPARE(five->x(), 100.0);
+
+ //Add 'two'
+ two->setOpacity(1.0);
+ QCOMPARE(two->opacity(), 1.0);
+ QCOMPARE(grid->width(), 150.0);
+ QCOMPARE(grid->height(), 100.0);
+ QTest::qWait(0);//Let the animation start
+ QCOMPARE(two->x(), -100.0);
+ QCOMPARE(two->y(), -100.0);
+ QCOMPARE(one->x(), 100.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(three->x(), 50.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 0.0);
+ QCOMPARE(four->y(), 0.0);
+ QCOMPARE(five->x(), 100.0);
+ QCOMPARE(five->y(), 50.0);
+ //Let the animation complete
+ QTRY_COMPARE(two->x(), 50.0);
+ QTRY_COMPARE(two->y(), 0.0);
+ QTRY_COMPARE(one->x(), 100.0);
+ QTRY_COMPARE(one->y(), 0.0);
+ QTRY_COMPARE(three->x(), 0.0);
+ QTRY_COMPARE(three->y(), 0.0);
+ QTRY_COMPARE(four->x(), 100.0);
+ QTRY_COMPARE(four->y(), 50.0);
+ QTRY_COMPARE(five->x(), 50.0);
+ QTRY_COMPARE(five->y(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_grid_zero_columns()
+{
+ QSGView *canvas = createView(SRCDIR "/data/gridzerocolumns.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 70.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 120.0);
+ QCOMPARE(four->y(), 0.0);
+ QCOMPARE(five->x(), 0.0);
+ QCOMPARE(five->y(), 50.0);
+
+ QSGItem *grid = canvas->rootObject()->findChild<QSGItem*>("grid");
+ QCOMPARE(grid->width(), 170.0);
+ QCOMPARE(grid->height(), 60.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_propertychanges()
+{
+ QSGView *canvas = createView(SRCDIR "/data/propertychangestest.qml");
+
+ QSGGrid *grid = qobject_cast<QSGGrid*>(canvas->rootObject());
+ QVERIFY(grid != 0);
+ QDeclarativeTransition *rowTransition = canvas->rootObject()->findChild<QDeclarativeTransition*>("rowTransition");
+ QDeclarativeTransition *columnTransition = canvas->rootObject()->findChild<QDeclarativeTransition*>("columnTransition");
+
+ QSignalSpy addSpy(grid, SIGNAL(addChanged()));
+ QSignalSpy moveSpy(grid, SIGNAL(moveChanged()));
+ QSignalSpy columnsSpy(grid, SIGNAL(columnsChanged()));
+ QSignalSpy rowsSpy(grid, SIGNAL(rowsChanged()));
+
+ QVERIFY(grid);
+ QVERIFY(rowTransition);
+ QVERIFY(columnTransition);
+ QCOMPARE(grid->add(), columnTransition);
+ QCOMPARE(grid->move(), columnTransition);
+ QCOMPARE(grid->columns(), 4);
+ QCOMPARE(grid->rows(), -1);
+
+ grid->setAdd(rowTransition);
+ grid->setMove(rowTransition);
+ QCOMPARE(grid->add(), rowTransition);
+ QCOMPARE(grid->move(), rowTransition);
+ QCOMPARE(addSpy.count(),1);
+ QCOMPARE(moveSpy.count(),1);
+
+ grid->setAdd(rowTransition);
+ grid->setMove(rowTransition);
+ QCOMPARE(addSpy.count(),1);
+ QCOMPARE(moveSpy.count(),1);
+
+ grid->setAdd(0);
+ grid->setMove(0);
+ QCOMPARE(addSpy.count(),2);
+ QCOMPARE(moveSpy.count(),2);
+
+ grid->setColumns(-1);
+ grid->setRows(3);
+ QCOMPARE(grid->columns(), -1);
+ QCOMPARE(grid->rows(), 3);
+ QCOMPARE(columnsSpy.count(),1);
+ QCOMPARE(rowsSpy.count(),1);
+
+ grid->setColumns(-1);
+ grid->setRows(3);
+ QCOMPARE(columnsSpy.count(),1);
+ QCOMPARE(rowsSpy.count(),1);
+
+ grid->setColumns(2);
+ grid->setRows(2);
+ QCOMPARE(columnsSpy.count(),2);
+ QCOMPARE(rowsSpy.count(),2);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_repeater()
+{
+ QSGView *canvas = createView(SRCDIR "/data/repeatertest.qml");
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 100.0);
+ QCOMPARE(three->y(), 0.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_flow()
+{
+ QSGView *canvas = createView(SRCDIR "/data/flowtest.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", false);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 0.0);
+ QCOMPARE(three->y(), 50.0);
+ QCOMPARE(four->x(), 0.0);
+ QCOMPARE(four->y(), 70.0);
+ QCOMPARE(five->x(), 50.0);
+ QCOMPARE(five->y(), 70.0);
+
+ QSGItem *flow = canvas->rootObject()->findChild<QSGItem*>("flow");
+ QVERIFY(flow);
+ QCOMPARE(flow->width(), 90.0);
+ QCOMPARE(flow->height(), 120.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_flow_rightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/flowtest.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 40.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 20.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 40.0);
+ QCOMPARE(three->y(), 50.0);
+ QCOMPARE(four->x(), 40.0);
+ QCOMPARE(four->y(), 70.0);
+ QCOMPARE(five->x(), 30.0);
+ QCOMPARE(five->y(), 70.0);
+
+ QSGItem *flow = canvas->rootObject()->findChild<QSGItem*>("flow");
+ QVERIFY(flow);
+ QCOMPARE(flow->width(), 90.0);
+ QCOMPARE(flow->height(), 120.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_flow_topToBottom()
+{
+ QSGView *canvas = createView(SRCDIR "/data/flowtest-toptobottom.qml");
+
+ canvas->rootObject()->setProperty("testRightToLeft", false);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 50.0);
+ QCOMPARE(three->y(), 50.0);
+ QCOMPARE(four->x(), 100.0);
+ QCOMPARE(four->y(), 00.0);
+ QCOMPARE(five->x(), 100.0);
+ QCOMPARE(five->y(), 50.0);
+
+ QSGItem *flow = canvas->rootObject()->findChild<QSGItem*>("flow");
+ QVERIFY(flow);
+ QCOMPARE(flow->height(), 90.0);
+ QCOMPARE(flow->width(), 150.0);
+
+ canvas->rootObject()->setProperty("testRightToLeft", true);
+
+ QVERIFY(flow);
+ QCOMPARE(flow->height(), 90.0);
+ QCOMPARE(flow->width(), 150.0);
+
+ QCOMPARE(one->x(), 100.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 80.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 50.0);
+ QCOMPARE(three->y(), 50.0);
+ QCOMPARE(four->x(), 0.0);
+ QCOMPARE(four->y(), 0.0);
+ QCOMPARE(five->x(), 40.0);
+ QCOMPARE(five->y(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_flow_resize()
+{
+ QSGView *canvas = createView(SRCDIR "/data/flowtest.qml");
+
+ QSGItem *root = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(root);
+ root->setWidth(125);
+ root->setProperty("testRightToLeft", false);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 0.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 50.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 70.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 0.0);
+ QCOMPARE(four->y(), 50.0);
+ QCOMPARE(five->x(), 50.0);
+ QCOMPARE(five->y(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_flow_resize_rightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/flowtest.qml");
+
+ QSGItem *root = qobject_cast<QSGItem*>(canvas->rootObject());
+ QVERIFY(root);
+ root->setWidth(125);
+ root->setProperty("testRightToLeft", true);
+
+ QSGRectangle *one = canvas->rootObject()->findChild<QSGRectangle*>("one");
+ QVERIFY(one != 0);
+ QSGRectangle *two = canvas->rootObject()->findChild<QSGRectangle*>("two");
+ QVERIFY(two != 0);
+ QSGRectangle *three = canvas->rootObject()->findChild<QSGRectangle*>("three");
+ QVERIFY(three != 0);
+ QSGRectangle *four = canvas->rootObject()->findChild<QSGRectangle*>("four");
+ QVERIFY(four != 0);
+ QSGRectangle *five = canvas->rootObject()->findChild<QSGRectangle*>("five");
+ QVERIFY(five != 0);
+
+ QCOMPARE(one->x(), 75.0);
+ QCOMPARE(one->y(), 0.0);
+ QCOMPARE(two->x(), 55.0);
+ QCOMPARE(two->y(), 0.0);
+ QCOMPARE(three->x(), 5.0);
+ QCOMPARE(three->y(), 0.0);
+ QCOMPARE(four->x(), 75.0);
+ QCOMPARE(four->y(), 50.0);
+ QCOMPARE(five->x(), 65.0);
+ QCOMPARE(five->y(), 50.0);
+
+ delete canvas;
+}
+
+void tst_qsgpositioners::test_flow_implicit_resize()
+{
+ QSGView *canvas = createView(SRCDIR "/data/flow-testimplicitsize.qml");
+ QVERIFY(canvas->rootObject() != 0);
+
+ QSGFlow *flow = canvas->rootObject()->findChild<QSGFlow*>("flow");
+ QVERIFY(flow != 0);
+
+ QCOMPARE(flow->width(), 100.0);
+ QCOMPARE(flow->height(), 120.0);
+
+ canvas->rootObject()->setProperty("flowLayout", 0);
+ QCOMPARE(flow->flow(), QSGFlow::LeftToRight);
+ QCOMPARE(flow->width(), 220.0);
+ QCOMPARE(flow->height(), 50.0);
+
+ canvas->rootObject()->setProperty("flowLayout", 1);
+ QCOMPARE(flow->flow(), QSGFlow::TopToBottom);
+ QCOMPARE(flow->width(), 100.0);
+ QCOMPARE(flow->height(), 120.0);
+
+ canvas->rootObject()->setProperty("flowLayout", 2);
+ QCOMPARE(flow->layoutDirection(), Qt::RightToLeft);
+ QCOMPARE(flow->width(), 220.0);
+ QCOMPARE(flow->height(), 50.0);
+
+ delete canvas;
+}
+
+QString warningMessage;
+
+void interceptWarnings(QtMsgType type, const char *msg)
+{
+ Q_UNUSED( type );
+ warningMessage = msg;
+}
+
+void tst_qsgpositioners::test_conflictinganchors()
+{
+ QtMsgHandler oldMsgHandler = qInstallMsgHandler(interceptWarnings);
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine);
+
+ component.setData("import QtQuick 2.0\nColumn { Item {} }", QUrl::fromLocalFile(""));
+ QSGItem *item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QVERIFY(warningMessage.isEmpty());
+ delete item;
+
+ component.setData("import QtQuick 2.0\nRow { Item {} }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QVERIFY(warningMessage.isEmpty());
+ delete item;
+
+ component.setData("import QtQuick 2.0\nGrid { Item {} }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QVERIFY(warningMessage.isEmpty());
+ delete item;
+
+ component.setData("import QtQuick 2.0\nFlow { Item {} }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QVERIFY(warningMessage.isEmpty());
+ delete item;
+
+ component.setData("import QtQuick 2.0\nColumn { Item { anchors.top: parent.top } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"));
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nColumn { Item { anchors.centerIn: parent } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"));
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nColumn { Item { anchors.left: parent.left } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QVERIFY(warningMessage.isEmpty());
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nRow { Item { anchors.left: parent.left } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"));
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nRow { Item { anchors.fill: parent } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"));
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nRow { Item { anchors.top: parent.top } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QVERIFY(warningMessage.isEmpty());
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nGrid { Item { anchors.horizontalCenter: parent.horizontalCenter } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Grid: Cannot specify anchors for items inside Grid"));
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nGrid { Item { anchors.centerIn: parent } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Grid: Cannot specify anchors for items inside Grid"));
+ warningMessage.clear();
+ delete item;
+
+ component.setData("import QtQuick 2.0\nFlow { Item { anchors.verticalCenter: parent.verticalCenter } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Flow: Cannot specify anchors for items inside Flow"));
+ delete item;
+
+ component.setData("import QtQuick 2.0\nFlow { Item { anchors.fill: parent } }", QUrl::fromLocalFile(""));
+ item = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(item);
+ QCOMPARE(warningMessage, QString("file::2:1: QML Flow: Cannot specify anchors for items inside Flow"));
+ qInstallMsgHandler(oldMsgHandler);
+ delete item;
+}
+
+void tst_qsgpositioners::test_mirroring()
+{
+ QList<QString> qmlFiles;
+ qmlFiles << "horizontal.qml" << "gridtest.qml" << "flowtest.qml";
+ QList<QString> objectNames;
+ objectNames << "one" << "two" << "three" << "four" << "five";
+
+ foreach(const QString qmlFile, qmlFiles) {
+ QSGView *canvasA = createView(QString(SRCDIR) + "/data/" + qmlFile);
+ QSGItem *rootA = qobject_cast<QSGItem*>(canvasA->rootObject());
+
+ QSGView *canvasB = createView(QString(SRCDIR) + "/data/" + qmlFile);
+ QSGItem *rootB = qobject_cast<QSGItem*>(canvasB->rootObject());
+
+ rootA->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft
+
+ // LTR != RTL
+ foreach(const QString objectName, objectNames) {
+ // horizontal.qml only has three items
+ if (qmlFile == QString("horizontal.qml") && objectName == QString("four"))
+ break;
+ QSGItem *itemA = rootA->findChild<QSGItem*>(objectName);
+ QSGItem *itemB = rootB->findChild<QSGItem*>(objectName);
+ QVERIFY(itemA->x() != itemB->x());
+ }
+
+ QSGItemPrivate* rootPrivateB = QSGItemPrivate::get(rootB);
+
+ rootPrivateB->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true
+ rootPrivateB->isMirrorImplicit = false;
+ rootPrivateB->inheritMirrorFromItem = true; // LayoutMirroring.childrenInherit: true
+ rootPrivateB->resolveLayoutMirror();
+
+ // RTL == mirror
+ foreach(const QString objectName, objectNames) {
+ // horizontal.qml only has three items
+ if (qmlFile == QString("horizontal.qml") && objectName == QString("four"))
+ break;
+ QSGItem *itemA = rootA->findChild<QSGItem*>(objectName);
+ QSGItem *itemB = rootB->findChild<QSGItem*>(objectName);
+ QCOMPARE(itemA->x(), itemB->x());
+ }
+
+ rootA->setProperty("testRightToLeft", false); // layoutDirection: Qt.LeftToRight
+ rootB->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft
+
+ // LTR == RTL + mirror
+ foreach(const QString objectName, objectNames) {
+ // horizontal.qml only has three items
+ if (qmlFile == QString("horizontal.qml") && objectName == QString("four"))
+ break;
+ QSGItem *itemA = rootA->findChild<QSGItem*>(objectName);
+ QSGItem *itemB = rootB->findChild<QSGItem*>(objectName);
+ QCOMPARE(itemA->x(), itemB->x());
+ }
+ delete canvasA;
+ delete canvasB;
+ }
+}
+
+QSGView *tst_qsgpositioners::createView(const QString &filename)
+{
+ QSGView *canvas = new QSGView(0);
+
+ canvas->setSource(QUrl::fromLocalFile(filename));
+
+ return canvas;
+}
+
+
+QTEST_MAIN(tst_qsgpositioners)
+
+#include "tst_qsgpositioners.moc"
diff --git a/tests/auto/declarative/qsgrepeater/data/intmodel.qml b/tests/auto/declarative/qsgrepeater/data/intmodel.qml
new file mode 100644
index 0000000000..30a650dd52
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/intmodel.qml
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: container
+ objectName: "container"
+ width: 240
+ height: 320
+ color: "white"
+
+ function checkProperties() {
+ testObject.error = false;
+ if (repeater.delegate != comp) {
+ console.log("delegate property incorrect");
+ testObject.error = true;
+ }
+ }
+
+ Component {
+ id: comp
+ Item{}
+ }
+
+ Repeater {
+ id: repeater
+ objectName: "repeater"
+ model: testData
+ delegate: comp
+ }
+}
diff --git a/tests/auto/declarative/qsgrepeater/data/itemlist.qml b/tests/auto/declarative/qsgrepeater/data/itemlist.qml
new file mode 100644
index 0000000000..174bfd4d18
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/itemlist.qml
@@ -0,0 +1,68 @@
+// This example demonstrates placing items in a view using
+// a VisualItemModel
+
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ color: "lightgray"
+ width: 240
+ height: 320
+ property variant itemModel: itemModel1
+
+ function checkProperties() {
+ testObject.error = false;
+ if (testObject.useModel && view.model != root.itemModel) {
+ console.log("model property incorrect");
+ testObject.error = true;
+ }
+ }
+
+ function switchModel() {
+ root.itemModel = itemModel2
+ }
+
+ VisualItemModel {
+ id: itemModel1
+ objectName: "itemModel1"
+ Rectangle {
+ objectName: "item1"
+ height: 50; width: 100; color: "#FFFEF0"
+ Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item2"
+ height: 50; width: 100; color: "#F0FFF7"
+ Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item3"
+ height: 50; width: 100; color: "#F4F0FF"
+ Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ }
+
+ VisualItemModel {
+ id: itemModel2
+ objectName: "itemModel2"
+ Rectangle {
+ objectName: "item4"
+ height: 50; width: 100; color: "#FFFEF0"
+ Text { objectName: "text4"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ Rectangle {
+ objectName: "item5"
+ height: 50; width: 100; color: "#F0FFF7"
+ Text { objectName: "text5"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent }
+ }
+ }
+
+ Column {
+ objectName: "container"
+ Repeater {
+ id: view
+ objectName: "repeater"
+ model: testObject.useModel ? root.itemModel : 0
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgrepeater/data/modelChanged.qml b/tests/auto/declarative/qsgrepeater/data/modelChanged.qml
new file mode 100644
index 0000000000..23af127e79
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/modelChanged.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Column {
+ Repeater {
+ id: repeater
+ objectName: "repeater"
+
+ property int itemsCount
+ property variant itemsFound: []
+
+ delegate: Rectangle {
+ color: "red"
+ width: (index+1)*50
+ height: 50
+ }
+
+ onModelChanged: {
+ repeater.itemsCount = repeater.count
+ var items = []
+ for (var i=0; i<repeater.count; i++)
+ items.push(repeater.itemAt(i))
+ repeater.itemsFound = items
+ }
+ }
+}
+
diff --git a/tests/auto/declarative/qsgrepeater/data/objlist.qml b/tests/auto/declarative/qsgrepeater/data/objlist.qml
new file mode 100644
index 0000000000..c49d5926e5
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/objlist.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: container
+ objectName: "container"
+ width: 240
+ height: 320
+ color: "white"
+ Repeater {
+ id: repeater
+ objectName: "repeater"
+ model: testData
+ property int errors: 0
+ property int instantiated: 0
+ Component {
+ Item{
+ Component.onCompleted: {if(index!=modelData.idx) repeater.errors += 1; repeater.instantiated++}
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgrepeater/data/properties.qml b/tests/auto/declarative/qsgrepeater/data/properties.qml
new file mode 100644
index 0000000000..035431c784
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/properties.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Row {
+ Repeater {
+ objectName: "repeater"
+ model: 5
+ Text {
+ text: "I'm item " + index
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgrepeater/data/repeater1.qml b/tests/auto/declarative/qsgrepeater/data/repeater1.qml
new file mode 100644
index 0000000000..596dc9131e
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/repeater1.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: container
+ objectName: "container"
+ width: 240
+ height: 320
+ color: "white"
+ Text {
+ text: "Zero"
+ }
+ Repeater {
+ id: repeater
+ objectName: "repeater"
+ width: 240
+ height: 320
+ model: testData
+ Component {
+ Text {
+ y: index*20
+ text: modelData
+ }
+ }
+ }
+ Text {
+ text: "Last"
+ }
+}
diff --git a/tests/auto/declarative/qsgrepeater/data/repeater2.qml b/tests/auto/declarative/qsgrepeater/data/repeater2.qml
new file mode 100644
index 0000000000..691fbda1e5
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/data/repeater2.qml
@@ -0,0 +1,36 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 240
+ height: 320
+ color: "white"
+ Component {
+ id: myDelegate
+ Item {
+ objectName: "myDelegate"
+ height: 20
+ width: 240
+ Text {
+ objectName: "myName"
+ text: name
+ }
+ Text {
+ objectName: "myNumber"
+ x: 100
+ text: number
+ }
+ }
+ }
+ Column {
+ id: container
+ objectName: "container"
+ Repeater {
+ id: repeater
+ objectName: "repeater"
+ width: 240
+ height: 320
+ delegate: myDelegate
+ model: testData
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgrepeater/qsgrepeater.pro b/tests/auto/declarative/qsgrepeater/qsgrepeater.pro
new file mode 100644
index 0000000000..99c3e1830b
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/qsgrepeater.pro
@@ -0,0 +1,15 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgrepeater.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
diff --git a/tests/auto/declarative/qsgrepeater/tst_qsgrepeater.cpp b/tests/auto/declarative/qsgrepeater/tst_qsgrepeater.cpp
new file mode 100644
index 0000000000..e9d79629c6
--- /dev/null
+++ b/tests/auto/declarative/qsgrepeater/tst_qsgrepeater.cpp
@@ -0,0 +1,697 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtTest/QSignalSpy>
+#include <private/qlistmodelinterface_p.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qsgview.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <private/qsgrepeater_p.h>
+#include <private/qsgtext_p.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+inline QUrl TEST_FILE(const QString &filename)
+{
+ return QUrl::fromLocalFile(QLatin1String(SRCDIR) + QLatin1String("/data/") + filename);
+}
+
+class tst_QSGRepeater : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QSGRepeater();
+
+private slots:
+ void numberModel();
+ void objectList();
+ void stringList();
+ void dataModel_adding();
+ void dataModel_removing();
+ void dataModel_changes();
+ void itemModel();
+ void resetModel();
+ void modelChanged();
+ void properties();
+
+private:
+ QSGView *createView();
+ template<typename T>
+ T *findItem(QObject *parent, const QString &objectName, int index);
+ template<typename T>
+ T *findItem(QObject *parent, const QString &id);
+};
+
+class TestObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool error READ error WRITE setError)
+ Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
+
+public:
+ TestObject() : QObject(), mError(true), mUseModel(false) {}
+
+ bool error() const { return mError; }
+ void setError(bool err) { mError = err; }
+
+ bool useModel() const { return mUseModel; }
+ void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
+
+signals:
+ void useModelChanged();
+
+private:
+ bool mError;
+ bool mUseModel;
+};
+
+class TestModel : public QAbstractListModel
+{
+public:
+ enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
+
+ TestModel(QObject *parent=0) : QAbstractListModel(parent) {
+ QHash<int, QByteArray> roles;
+ roles[Name] = "name";
+ roles[Number] = "number";
+ setRoleNames(roles);
+ }
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
+ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
+ QVariant rv;
+ if (role == Name)
+ rv = list.at(index.row()).first;
+ else if (role == Number)
+ rv = list.at(index.row()).second;
+
+ return rv;
+ }
+
+ int count() const { return rowCount(); }
+ QString name(int index) const { return list.at(index).first; }
+ QString number(int index) const { return list.at(index).second; }
+
+ void addItem(const QString &name, const QString &number) {
+ emit beginInsertRows(QModelIndex(), list.count(), list.count());
+ list.append(QPair<QString,QString>(name, number));
+ emit endInsertRows();
+ }
+
+ void insertItem(int index, const QString &name, const QString &number) {
+ emit beginInsertRows(QModelIndex(), index, index);
+ list.insert(index, QPair<QString,QString>(name, number));
+ emit endInsertRows();
+ }
+
+ void removeItem(int index) {
+ emit beginRemoveRows(QModelIndex(), index, index);
+ list.removeAt(index);
+ emit endRemoveRows();
+ }
+
+ void moveItem(int from, int to) {
+ emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
+ list.move(from, to);
+ emit endMoveRows();
+ }
+
+ void modifyItem(int idx, const QString &name, const QString &number) {
+ list[idx] = QPair<QString,QString>(name, number);
+ emit dataChanged(index(idx,0), index(idx,0));
+ }
+
+private:
+ QList<QPair<QString,QString> > list;
+};
+
+
+tst_QSGRepeater::tst_QSGRepeater()
+{
+}
+
+void tst_QSGRepeater::numberModel()
+{
+ QSGView *canvas = createView();
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testData", 5);
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/intmodel.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+ QCOMPARE(repeater->parentItem()->childItems().count(), 5+1);
+
+ QVERIFY(!repeater->itemAt(-1));
+ for (int i=0; i<repeater->count(); i++)
+ QCOMPARE(repeater->itemAt(i), repeater->parentItem()->childItems().at(i));
+ QVERIFY(!repeater->itemAt(repeater->count()));
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QVERIFY(testObject->error() == false);
+
+ delete testObject;
+ delete canvas;
+}
+
+class MyObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int idx READ idx CONSTANT)
+public:
+ MyObject(int i) : QObject(), m_idx(i) {}
+
+ int idx() const { return m_idx; }
+
+ int m_idx;
+};
+
+void tst_QSGRepeater::objectList()
+{
+ QSGView *canvas = createView();
+ QObjectList data;
+ for(int i=0; i<100; i++)
+ data << new MyObject(i);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testData", QVariant::fromValue(data));
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/objlist.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+ QCOMPARE(repeater->property("errors").toInt(), 0);//If this fails either they are out of order or can't find the object's data
+ QCOMPARE(repeater->property("instantiated").toInt(), 100);
+
+ QVERIFY(!repeater->itemAt(-1));
+ for (int i=0; i<data.count(); i++)
+ QCOMPARE(repeater->itemAt(i), repeater->parentItem()->childItems().at(i));
+ QVERIFY(!repeater->itemAt(data.count()));
+
+ QSignalSpy addedSpy(repeater, SIGNAL(itemAdded(int,QSGItem*)));
+ QSignalSpy removedSpy(repeater, SIGNAL(itemRemoved(int,QSGItem*)));
+ ctxt->setContextProperty("testData", QVariant::fromValue(data));
+ QCOMPARE(addedSpy.count(), data.count());
+ QCOMPARE(removedSpy.count(), data.count());
+
+ qDeleteAll(data);
+ delete canvas;
+}
+
+/*
+The Repeater element creates children at its own position in its parent's
+stacking order. In this test we insert a repeater between two other Text
+elements to test this.
+*/
+void tst_QSGRepeater::stringList()
+{
+ QSGView *canvas = createView();
+
+ QStringList data;
+ data << "One";
+ data << "Two";
+ data << "Three";
+ data << "Four";
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testData", data);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/repeater1.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+
+ QSGItem *container = findItem<QSGItem>(canvas->rootObject(), "container");
+ QVERIFY(container != 0);
+
+ QCOMPARE(container->childItems().count(), data.count() + 3);
+
+ bool saw_repeater = false;
+ for (int i = 0; i < container->childItems().count(); ++i) {
+
+ if (i == 0) {
+ QSGText *name = qobject_cast<QSGText*>(container->childItems().at(i));
+ QVERIFY(name != 0);
+ QCOMPARE(name->text(), QLatin1String("Zero"));
+ } else if (i == container->childItems().count() - 2) {
+ // The repeater itself
+ QSGRepeater *rep = qobject_cast<QSGRepeater*>(container->childItems().at(i));
+ QCOMPARE(rep, repeater);
+ saw_repeater = true;
+ continue;
+ } else if (i == container->childItems().count() - 1) {
+ QSGText *name = qobject_cast<QSGText*>(container->childItems().at(i));
+ QVERIFY(name != 0);
+ QCOMPARE(name->text(), QLatin1String("Last"));
+ } else {
+ QSGText *name = qobject_cast<QSGText*>(container->childItems().at(i));
+ QVERIFY(name != 0);
+ QCOMPARE(name->text(), data.at(i-1));
+ }
+ }
+ QVERIFY(saw_repeater);
+
+ delete canvas;
+}
+
+void tst_QSGRepeater::dataModel_adding()
+{
+ QSGView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ TestModel testModel;
+ ctxt->setContextProperty("testData", &testModel);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/repeater2.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+ QSGItem *container = findItem<QSGItem>(canvas->rootObject(), "container");
+ QVERIFY(container != 0);
+
+ QVERIFY(!repeater->itemAt(0));
+
+ QSignalSpy countSpy(repeater, SIGNAL(countChanged()));
+ QSignalSpy addedSpy(repeater, SIGNAL(itemAdded(int,QSGItem*)));
+
+ // add to empty model
+ testModel.addItem("two", "2");
+ QCOMPARE(repeater->itemAt(0), container->childItems().at(0));
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(addedSpy.count(), 1);
+ QCOMPARE(addedSpy.at(0).at(0).toInt(), 0);
+ QCOMPARE(addedSpy.at(0).at(1).value<QSGItem*>(), container->childItems().at(0));
+ addedSpy.clear();
+
+ // insert at start
+ testModel.insertItem(0, "one", "1");
+ QCOMPARE(repeater->itemAt(0), container->childItems().at(0));
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(addedSpy.count(), 1);
+ QCOMPARE(addedSpy.at(0).at(0).toInt(), 0);
+ QCOMPARE(addedSpy.at(0).at(1).value<QSGItem*>(), container->childItems().at(0));
+ addedSpy.clear();
+
+ // insert at end
+ testModel.insertItem(2, "four", "4");
+ QCOMPARE(repeater->itemAt(2), container->childItems().at(2));
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(addedSpy.count(), 1);
+ QCOMPARE(addedSpy.at(0).at(0).toInt(), 2);
+ QCOMPARE(addedSpy.at(0).at(1).value<QSGItem*>(), container->childItems().at(2));
+ addedSpy.clear();
+
+ // insert in middle
+ testModel.insertItem(2, "three", "3");
+ QCOMPARE(repeater->itemAt(2), container->childItems().at(2));
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(addedSpy.count(), 1);
+ QCOMPARE(addedSpy.at(0).at(0).toInt(), 2);
+ QCOMPARE(addedSpy.at(0).at(1).value<QSGItem*>(), container->childItems().at(2));
+ addedSpy.clear();
+
+ delete testObject;
+ addedSpy.clear();
+ countSpy.clear();
+ delete canvas;
+}
+
+void tst_QSGRepeater::dataModel_removing()
+{
+ QSGView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ TestModel testModel;
+ testModel.addItem("one", "1");
+ testModel.addItem("two", "2");
+ testModel.addItem("three", "3");
+ testModel.addItem("four", "4");
+ testModel.addItem("five", "5");
+
+ ctxt->setContextProperty("testData", &testModel);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/repeater2.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+ QSGItem *container = findItem<QSGItem>(canvas->rootObject(), "container");
+ QVERIFY(container != 0);
+ QCOMPARE(container->childItems().count(), repeater->count()+1);
+
+ QSignalSpy countSpy(repeater, SIGNAL(countChanged()));
+ QSignalSpy removedSpy(repeater, SIGNAL(itemRemoved(int,QSGItem*)));
+
+ // remove at start
+ QSGItem *item = repeater->itemAt(0);
+ QCOMPARE(item, container->childItems().at(0));
+
+ testModel.removeItem(0);
+ QVERIFY(repeater->itemAt(0) != item);
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(removedSpy.count(), 1);
+ QCOMPARE(removedSpy.at(0).at(0).toInt(), 0);
+ QCOMPARE(removedSpy.at(0).at(1).value<QSGItem*>(), item);
+ removedSpy.clear();
+
+ // remove at end
+ int lastIndex = testModel.count()-1;
+ item = repeater->itemAt(lastIndex);
+ QCOMPARE(item, container->childItems().at(lastIndex));
+
+ testModel.removeItem(lastIndex);
+ QVERIFY(repeater->itemAt(lastIndex) != item);
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(removedSpy.count(), 1);
+ QCOMPARE(removedSpy.at(0).at(0).toInt(), lastIndex);
+ QCOMPARE(removedSpy.at(0).at(1).value<QSGItem*>(), item);
+ removedSpy.clear();
+
+ // remove from middle
+ item = repeater->itemAt(1);
+ QCOMPARE(item, container->childItems().at(1));
+
+ testModel.removeItem(1);
+ QVERIFY(repeater->itemAt(lastIndex) != item);
+ QCOMPARE(countSpy.count(), 1); countSpy.clear();
+ QCOMPARE(removedSpy.count(), 1);
+ QCOMPARE(removedSpy.at(0).at(0).toInt(), 1);
+ QCOMPARE(removedSpy.at(0).at(1).value<QSGItem*>(), item);
+ removedSpy.clear();
+
+ delete testObject;
+ delete canvas;
+}
+
+void tst_QSGRepeater::dataModel_changes()
+{
+ QSGView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ TestModel testModel;
+ testModel.addItem("one", "1");
+ testModel.addItem("two", "2");
+ testModel.addItem("three", "3");
+
+ ctxt->setContextProperty("testData", &testModel);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/repeater2.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+ QSGItem *container = findItem<QSGItem>(canvas->rootObject(), "container");
+ QVERIFY(container != 0);
+ QCOMPARE(container->childItems().count(), repeater->count()+1);
+
+ // Check that model changes are propagated
+ QSGText *text = findItem<QSGText>(canvas->rootObject(), "myName", 1);
+ QVERIFY(text);
+ QCOMPARE(text->text(), QString("two"));
+
+ testModel.modifyItem(1, "Item two", "_2");
+ text = findItem<QSGText>(canvas->rootObject(), "myName", 1);
+ QVERIFY(text);
+ QCOMPARE(text->text(), QString("Item two"));
+
+ text = findItem<QSGText>(canvas->rootObject(), "myNumber", 1);
+ QVERIFY(text);
+ QCOMPARE(text->text(), QString("_2"));
+
+ delete testObject;
+ delete canvas;
+}
+
+void tst_QSGRepeater::itemModel()
+{
+ QSGView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testObject", testObject);
+
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/itemlist.qml"));
+ qApp->processEvents();
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+
+ QSGItem *container = findItem<QSGItem>(canvas->rootObject(), "container");
+ QVERIFY(container != 0);
+
+ QCOMPARE(container->childItems().count(), 1);
+
+ testObject->setUseModel(true);
+ QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
+ QVERIFY(testObject->error() == false);
+
+ QCOMPARE(container->childItems().count(), 4);
+ QVERIFY(qobject_cast<QObject*>(container->childItems().at(0))->objectName() == "item1");
+ QVERIFY(qobject_cast<QObject*>(container->childItems().at(1))->objectName() == "item2");
+ QVERIFY(qobject_cast<QObject*>(container->childItems().at(2))->objectName() == "item3");
+ QVERIFY(container->childItems().at(3) == repeater);
+
+ QMetaObject::invokeMethod(canvas->rootObject(), "switchModel");
+ QCOMPARE(container->childItems().count(), 3);
+ QVERIFY(qobject_cast<QObject*>(container->childItems().at(0))->objectName() == "item4");
+ QVERIFY(qobject_cast<QObject*>(container->childItems().at(1))->objectName() == "item5");
+ QVERIFY(container->childItems().at(2) == repeater);
+
+ testObject->setUseModel(false);
+ QCOMPARE(container->childItems().count(), 1);
+
+ delete testObject;
+ delete canvas;
+}
+
+void tst_QSGRepeater::resetModel()
+{
+ QSGView *canvas = createView();
+
+ QStringList dataA;
+ for (int i=0; i<10; i++)
+ dataA << QString::number(i);
+
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testData", dataA);
+ canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/repeater1.qml"));
+ qApp->processEvents();
+ QSGRepeater *repeater = findItem<QSGRepeater>(canvas->rootObject(), "repeater");
+ QVERIFY(repeater != 0);
+ QSGItem *container = findItem<QSGItem>(canvas->rootObject(), "container");
+ QVERIFY(container != 0);
+
+ QCOMPARE(repeater->count(), dataA.count());
+ for (int i=0; i<repeater->count(); i++)
+ QCOMPARE(repeater->itemAt(i), container->childItems().at(i+1)); // +1 to skip first Text object
+
+ QSignalSpy modelChangedSpy(repeater, SIGNAL(modelChanged()));
+ QSignalSpy countSpy(repeater, SIGNAL(countChanged()));
+ QSignalSpy addedSpy(repeater, SIGNAL(itemAdded(int,QSGItem*)));
+ QSignalSpy removedSpy(repeater, SIGNAL(itemRemoved(int,QSGItem*)));
+
+ QStringList dataB;
+ for (int i=0; i<20; i++)
+ dataB << QString::number(i);
+
+ // reset context property
+ ctxt->setContextProperty("testData", dataB);
+ QCOMPARE(repeater->count(), dataB.count());
+
+ QCOMPARE(modelChangedSpy.count(), 1);
+ QCOMPARE(countSpy.count(), 1);
+ QCOMPARE(removedSpy.count(), dataA.count());
+ QCOMPARE(addedSpy.count(), dataB.count());
+ for (int i=0; i<dataB.count(); i++) {
+ QCOMPARE(addedSpy.at(i).at(0).toInt(), i);
+ QCOMPARE(addedSpy.at(i).at(1).value<QSGItem*>(), repeater->itemAt(i));
+ }
+ modelChangedSpy.clear();
+ countSpy.clear();
+ removedSpy.clear();
+ addedSpy.clear();
+
+ // reset via setModel()
+ repeater->setModel(dataA);
+ QCOMPARE(repeater->count(), dataA.count());
+
+ QCOMPARE(modelChangedSpy.count(), 1);
+ QCOMPARE(countSpy.count(), 1);
+ QCOMPARE(removedSpy.count(), dataB.count());
+ QCOMPARE(addedSpy.count(), dataA.count());
+ for (int i=0; i<dataA.count(); i++) {
+ QCOMPARE(addedSpy.at(i).at(0).toInt(), i);
+ QCOMPARE(addedSpy.at(i).at(1).value<QSGItem*>(), repeater->itemAt(i));
+ }
+
+ modelChangedSpy.clear();
+ countSpy.clear();
+ removedSpy.clear();
+ addedSpy.clear();
+
+ delete canvas;
+}
+
+// QTBUG-17156
+void tst_QSGRepeater::modelChanged()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, TEST_FILE("/modelChanged.qml"));
+
+ QSGItem *rootObject = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(rootObject);
+ QSGRepeater *repeater = findItem<QSGRepeater>(rootObject, "repeater");
+ QVERIFY(repeater);
+
+ repeater->setModel(4);
+ QCOMPARE(repeater->count(), 4);
+ QCOMPARE(repeater->property("itemsCount").toInt(), 4);
+ QCOMPARE(repeater->property("itemsFound").toList().count(), 4);
+
+ repeater->setModel(10);
+ QCOMPARE(repeater->count(), 10);
+ QCOMPARE(repeater->property("itemsCount").toInt(), 10);
+ QCOMPARE(repeater->property("itemsFound").toList().count(), 10);
+
+ delete rootObject;
+}
+
+void tst_QSGRepeater::properties()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, TEST_FILE("/properties.qml"));
+
+ QSGItem *rootObject = qobject_cast<QSGItem*>(component.create());
+ QVERIFY(rootObject);
+
+ QSGRepeater *repeater = findItem<QSGRepeater>(rootObject, "repeater");
+ QVERIFY(repeater);
+
+ QSignalSpy modelSpy(repeater, SIGNAL(modelChanged()));
+ repeater->setModel(3);
+ QCOMPARE(modelSpy.count(),1);
+ repeater->setModel(3);
+ QCOMPARE(modelSpy.count(),1);
+
+ QSignalSpy delegateSpy(repeater, SIGNAL(delegateChanged()));
+
+ QDeclarativeComponent rectComponent(&engine);
+ rectComponent.setData("import QtQuick 2.0; Rectangle {}", QUrl::fromLocalFile(""));
+
+ repeater->setDelegate(&rectComponent);
+ QCOMPARE(delegateSpy.count(),1);
+ repeater->setDelegate(&rectComponent);
+ QCOMPARE(delegateSpy.count(),1);
+
+ delete rootObject;
+}
+
+QSGView *tst_QSGRepeater::createView()
+{
+ QSGView *canvas = new QSGView(0);
+ canvas->setFixedSize(240,320);
+
+ return canvas;
+}
+
+template<typename T>
+T *tst_QSGRepeater::findItem(QObject *parent, const QString &objectName, int index)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->children().count() << "children";
+ for (int i = 0; i < parent->children().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->children().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ if (index != -1) {
+ QDeclarativeExpression e(qmlContext(item), item, "index");
+ if (e.evaluate().toInt() == index)
+ return static_cast<T*>(item);
+ } else {
+ return static_cast<T*>(item);
+ }
+ }
+ item = findItem<T>(item, objectName, index);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+template<typename T>
+T *tst_QSGRepeater::findItem(QObject *parent, const QString &objectName)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ if (mo.cast(parent) && (objectName.isEmpty() || parent->objectName() == objectName))
+ return static_cast<T*>(parent);
+ for (int i = 0; i < parent->children().count(); ++i) {
+ QSGItem *child = qobject_cast<QSGItem*>(parent->children().at(i));
+ if (!child)
+ continue;
+ QSGItem *item = findItem<T>(child, objectName);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+QTEST_MAIN(tst_QSGRepeater)
+
+#include "tst_qsgrepeater.moc"
diff --git a/tests/auto/declarative/qsgtext/data/alignments.qml b/tests/auto/declarative/qsgtext/data/alignments.qml
new file mode 100644
index 0000000000..9798d9c736
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: top
+ width: 70; height: 70;
+
+ property alias horizontalAlignment: t.horizontalAlignment
+ property alias verticalAlignment: t.verticalAlignment
+ property alias wrapMode: t.wrapMode
+ property alias running: timer.running
+ property string txt: "Test"
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: 40
+ height: 40
+ color: "green"
+
+ Text {
+ id: t
+
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignBottom
+ wrapMode: Text.WordWrap
+ text: top.txt
+ }
+ Timer {
+ id: timer
+
+ interval: 1
+ running: true
+ repeat: true
+ onTriggered: {
+ top.txt = top.txt + "<br>more " + top.txt.length;
+ if (top.txt.length > 50)
+ running = false
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgtext/data/alignments_cb.png b/tests/auto/declarative/qsgtext/data/alignments_cb.png
new file mode 100644
index 0000000000..cf6199a418
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_cb.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_cc.png b/tests/auto/declarative/qsgtext/data/alignments_cc.png
new file mode 100644
index 0000000000..f81ccb4238
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_cc.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_ct.png b/tests/auto/declarative/qsgtext/data/alignments_ct.png
new file mode 100644
index 0000000000..9ba64125d5
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_ct.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_lb.png b/tests/auto/declarative/qsgtext/data/alignments_lb.png
new file mode 100644
index 0000000000..1b50a81f3d
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_lb.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_lc.png b/tests/auto/declarative/qsgtext/data/alignments_lc.png
new file mode 100644
index 0000000000..f041b868f8
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_lc.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_lt.png b/tests/auto/declarative/qsgtext/data/alignments_lt.png
new file mode 100644
index 0000000000..c75e0d158e
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_lt.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_rb.png b/tests/auto/declarative/qsgtext/data/alignments_rb.png
new file mode 100644
index 0000000000..b06a5da715
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_rb.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_rc.png b/tests/auto/declarative/qsgtext/data/alignments_rc.png
new file mode 100644
index 0000000000..e468857cd0
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_rc.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/alignments_rt.png b/tests/auto/declarative/qsgtext/data/alignments_rt.png
new file mode 100644
index 0000000000..576715ffce
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/alignments_rt.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/embeddedImagesLocal.qml b/tests/auto/declarative/qsgtext/data/embeddedImagesLocal.qml
new file mode 100644
index 0000000000..d71e9bb5bf
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/embeddedImagesLocal.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Text {
+ text: "<img src='http/exists.png'>"
+}
diff --git a/tests/auto/declarative/qsgtext/data/embeddedImagesLocalError.qml b/tests/auto/declarative/qsgtext/data/embeddedImagesLocalError.qml
new file mode 100644
index 0000000000..e6719481db
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/embeddedImagesLocalError.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Text {
+ text: "<img src='http/notexists.png'>"
+}
diff --git a/tests/auto/declarative/qsgtext/data/embeddedImagesRemote.qml b/tests/auto/declarative/qsgtext/data/embeddedImagesRemote.qml
new file mode 100644
index 0000000000..e524d028b5
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/embeddedImagesRemote.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Text {
+ text: "<img src='http://127.0.0.1:14453/exists.png'>"
+}
diff --git a/tests/auto/declarative/qsgtext/data/embeddedImagesRemoteError.qml b/tests/auto/declarative/qsgtext/data/embeddedImagesRemoteError.qml
new file mode 100644
index 0000000000..f541e0e497
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/embeddedImagesRemoteError.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Text {
+ text: "<img src='http://127.0.0.1:14453/notexists.png'>"
+}
diff --git a/tests/auto/declarative/qsgtext/data/horizontalAlignment_RightToLeft.qml b/tests/auto/declarative/qsgtext/data/horizontalAlignment_RightToLeft.qml
new file mode 100644
index 0000000000..5ba4d35684
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/horizontalAlignment_RightToLeft.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: top
+ width: 200; height: 70;
+
+ property alias horizontalAlignment: text.horizontalAlignment
+ property string text: "اختبا"
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: 180
+ height: 20
+ color: "green"
+
+ Text {
+ id: text
+ objectName: "text"
+ anchors.fill: parent
+ text: top.text
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgtext/data/http/exists.png b/tests/auto/declarative/qsgtext/data/http/exists.png
new file mode 100644
index 0000000000..399bd0b1d9
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/http/exists.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtext/data/lineCount.qml b/tests/auto/declarative/qsgtext/data/lineCount.qml
new file mode 100644
index 0000000000..b672863684
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/lineCount.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.0
+
+Item {
+ width: 200
+ height: 200
+
+ Text {
+ id: myText
+ objectName: "myText"
+ width: 200
+ wrapMode: Text.WordWrap
+ maximumLineCount: undefined
+ text: "Testing that maximumLines, visibleLines, and totalLines works properly in the autotests. The quick brown fox jumped over the lazy anything with the letter 'g'."
+ }
+}
diff --git a/tests/auto/declarative/qsgtext/data/lineHeight.qml b/tests/auto/declarative/qsgtext/data/lineHeight.qml
new file mode 100644
index 0000000000..c1f337aa05
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/lineHeight.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.0
+
+Item {
+ width: 200
+ height: 200
+
+ Text {
+ id: myText
+ objectName: "myText"
+ width: 200
+ wrapMode: Text.WordWrap
+ font.pixelSize: 13
+ text: "Lorem ipsum sit amet, consectetur adipiscing elit. Integer felis nisl, varius in pretium nec, venenatis non erat. Proin lobortis interdum dictum."
+ }
+}
diff --git a/tests/auto/declarative/qsgtext/data/qtbug_14734.qml b/tests/auto/declarative/qsgtext/data/qtbug_14734.qml
new file mode 100644
index 0000000000..e71a798421
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/qtbug_14734.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 640
+ height: 480
+
+ Text {
+ text: "í "
+ }
+}
diff --git a/tests/auto/declarative/qsgtext/data/rotated.qml b/tests/auto/declarative/qsgtext/data/rotated.qml
new file mode 100644
index 0000000000..fecf64b249
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/data/rotated.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Rectangle {
+ width : 200
+ height : 100
+
+ Text {
+ objectName: "text"
+ x: 20
+ y: 20
+ height : 20
+ width : 80
+ text : "Something"
+ rotation : 30
+ transformOrigin : Item.TopLeft
+ }
+}
+
diff --git a/tests/auto/declarative/qsgtext/qsgtext.pro b/tests/auto/declarative/qsgtext/qsgtext.pro
new file mode 100644
index 0000000000..132cec4cdc
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/qsgtext.pro
@@ -0,0 +1,21 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+QT += network
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgtext.cpp
+
+INCLUDEPATH += ../shared/
+HEADERS += ../shared/testhttpserver.h
+SOURCES += ../shared/testhttpserver.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgtext/tst_qsgtext.cpp b/tests/auto/declarative/qsgtext/tst_qsgtext.cpp
new file mode 100644
index 0000000000..2861dd9a9f
--- /dev/null
+++ b/tests/auto/declarative/qsgtext/tst_qsgtext.cpp
@@ -0,0 +1,1377 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QTextDocument>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <private/qsgtext_p.h>
+#include <private/qsgtext_p_p.h>
+#include <private/qdeclarativevaluetype_p.h>
+#include <QFontMetrics>
+#include <QGraphicsSceneMouseEvent>
+#include <qmath.h>
+#include <QSGView>
+#include <private/qapplication_p.h>
+#include <limits.h>
+
+#include "../../../shared/util.h"
+#include "testhttpserver.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qsgtext : public QObject
+
+{
+ Q_OBJECT
+public:
+ tst_qsgtext();
+
+private slots:
+ void text();
+ void width();
+ void wrap();
+ void elide();
+ void textFormat();
+
+ void alignments_data();
+ void alignments();
+
+ void embeddedImages_data();
+ void embeddedImages();
+
+ void lineCount();
+ void lineHeight();
+
+ // ### these tests may be trivial
+ void horizontalAlignment();
+ void horizontalAlignment_RightToLeft();
+ void verticalAlignment();
+ void font();
+ void style();
+ void color();
+ void smooth();
+
+ // QDeclarativeFontValueType
+ void weight();
+ void underline();
+ void overline();
+ void strikeout();
+ void capitalization();
+ void letterSpacing();
+ void wordSpacing();
+
+ void clickLink();
+
+ void QTBUG_12291();
+ void implicitSize_data();
+ void implicitSize();
+
+ void qtbug_14734();
+private:
+ QStringList standard;
+ QStringList richText;
+
+ QStringList horizontalAlignmentmentStrings;
+ QStringList verticalAlignmentmentStrings;
+
+ QList<Qt::Alignment> verticalAlignmentments;
+ QList<Qt::Alignment> horizontalAlignmentments;
+
+ QStringList styleStrings;
+ QList<QSGText::TextStyle> styles;
+
+ QStringList colorStrings;
+
+ QDeclarativeEngine engine;
+
+ QSGView *createView(const QString &filename);
+};
+
+tst_qsgtext::tst_qsgtext()
+{
+ standard << "the quick brown fox jumped over the lazy dog"
+ << "the quick brown fox\n jumped over the lazy dog";
+
+ richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
+ << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
+
+ horizontalAlignmentmentStrings << "AlignLeft"
+ << "AlignRight"
+ << "AlignHCenter";
+
+ verticalAlignmentmentStrings << "AlignTop"
+ << "AlignBottom"
+ << "AlignVCenter";
+
+ horizontalAlignmentments << Qt::AlignLeft
+ << Qt::AlignRight
+ << Qt::AlignHCenter;
+
+ verticalAlignmentments << Qt::AlignTop
+ << Qt::AlignBottom
+ << Qt::AlignVCenter;
+
+ styleStrings << "Normal"
+ << "Outline"
+ << "Raised"
+ << "Sunken";
+
+ styles << QSGText::Normal
+ << QSGText::Outline
+ << QSGText::Raised
+ << QSGText::Sunken;
+
+ colorStrings << "aliceblue"
+ << "antiquewhite"
+ << "aqua"
+ << "darkkhaki"
+ << "darkolivegreen"
+ << "dimgray"
+ << "palevioletred"
+ << "lightsteelblue"
+ << "#000000"
+ << "#AAAAAA"
+ << "#FFFFFF"
+ << "#2AC05F";
+ //
+ // need a different test to do alpha channel test
+ // << "#AA0011DD"
+ // << "#00F16B11";
+ //
+}
+
+QSGView *tst_qsgtext::createView(const QString &filename)
+{
+ QSGView *canvas = new QSGView(0);
+
+ canvas->setSource(QUrl::fromLocalFile(filename));
+ return canvas;
+}
+
+void tst_qsgtext::text()
+{
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->text(), QString(""));
+ QVERIFY(textObject->width() == 0);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->text(), standard.at(i));
+ QVERIFY(textObject->width() > 0);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QString expected = richText.at(i);
+ QCOMPARE(textObject->text(), expected.replace("\\\"", "\""));
+ QVERIFY(textObject->width() > 0);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::width()
+{
+ // uses Font metrics to find the width for standard and document to find the width for rich
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->width(), 0.);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QVERIFY(!Qt::mightBeRichText(standard.at(i))); // self-test
+
+ QFont f;
+ QFontMetricsF fm(f);
+ qreal metricWidth = fm.size(Qt::TextExpandTabs && Qt::TextShowMnemonic, standard.at(i)).width();
+ metricWidth = qCeil(metricWidth);
+
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->boundingRect().width() > 0);
+ QCOMPARE(textObject->width(), qreal(metricWidth));
+ QVERIFY(textObject->textFormat() == QSGText::AutoText); // setting text doesn't change format
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QVERIFY(Qt::mightBeRichText(richText.at(i))); // self-test
+
+ QTextDocument document;
+ document.setHtml(richText.at(i));
+ document.setDocumentMargin(0);
+
+ int documentWidth = document.idealWidth();
+
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->width(), qreal(documentWidth));
+ QVERIFY(textObject->textFormat() == QSGText::AutoText); // setting text doesn't change format
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::wrap()
+{
+ int textHeight = 0;
+ // for specified width and wrap set true
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; wrapMode: Text.WordWrap; width: 300 }", QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+ textHeight = textObject->height();
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->wrapMode() == QSGText::WordWrap);
+ QCOMPARE(textObject->width(), 300.);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->width(), 30.);
+ QVERIFY(textObject->height() > textHeight);
+
+ int oldHeight = textObject->height();
+ textObject->setWidth(100);
+ QVERIFY(textObject->height() < oldHeight);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->width(), 30.);
+ QVERIFY(textObject->height() > textHeight);
+
+ qreal oldHeight = textObject->height();
+ textObject->setWidth(100);
+ QVERIFY(textObject->height() < oldHeight);
+
+ delete textObject;
+ }
+
+ // richtext again with a fixed height
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; height: 50; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->width(), 30.);
+ QVERIFY(textObject->implicitHeight() > textHeight);
+
+ qreal oldHeight = textObject->implicitHeight();
+ textObject->setWidth(100);
+ QVERIFY(textObject->implicitHeight() < oldHeight);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::elide()
+{
+ for (QSGText::TextElideMode m = QSGText::ElideLeft; m<=QSGText::ElideNone; m=QSGText::TextElideMode(int(m)+1)) {
+ const char* elidename[]={"ElideLeft", "ElideRight", "ElideMiddle", "ElideNone"};
+ QString elide = "elide: Text." + QString(elidename[int(m)]) + ";";
+
+ // XXX Poor coverage.
+
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(("import QtQuick 2.0\nText { text: \"\"; "+elide+" width: 100 }").toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->elideMode(), m);
+ QCOMPARE(textObject->width(), 100.);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->elideMode(), m);
+ QCOMPARE(textObject->width(), 100.);
+
+ delete textObject;
+ }
+
+ // richtext - does nothing
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->elideMode(), m);
+ QCOMPARE(textObject->width(), 100.);
+
+ delete textObject;
+ }
+ }
+}
+
+void tst_qsgtext::textFormat()
+{
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->textFormat() == QSGText::RichText);
+
+ delete textObject;
+ }
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->textFormat() == QSGText::PlainText);
+
+ delete textObject;
+ }
+}
+
+
+void tst_qsgtext::alignments_data()
+{
+ QTest::addColumn<int>("hAlign");
+ QTest::addColumn<int>("vAlign");
+ QTest::addColumn<QString>("expectfile");
+
+ QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << SRCDIR "/data/alignments_lt.png";
+ QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << SRCDIR "/data/alignments_rt.png";
+ QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << SRCDIR "/data/alignments_ct.png";
+
+ QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << SRCDIR "/data/alignments_lb.png";
+ QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << SRCDIR "/data/alignments_rb.png";
+ QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << SRCDIR "/data/alignments_cb.png";
+
+ QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << SRCDIR "/data/alignments_lc.png";
+ QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << SRCDIR "/data/alignments_rc.png";
+ QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << SRCDIR "/data/alignments_cc.png";
+}
+
+
+void tst_qsgtext::alignments()
+{
+ QFETCH(int, hAlign);
+ QFETCH(int, vAlign);
+ QFETCH(QString, expectfile);
+
+#ifdef Q_WS_X11
+ // Font-specific, but not likely platform-specific, so only test on one platform
+ QFont fn;
+ fn.setRawName("-misc-fixed-medium-r-*-*-8-*-*-*-*-*-*-*");
+ QApplication::setFont(fn);
+#endif
+
+ QSGView *canvas = createView(SRCDIR "/data/alignments.qml");
+
+ canvas->show();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
+
+ QObject *ob = canvas->rootObject();
+ QVERIFY(ob != 0);
+ ob->setProperty("horizontalAlignment",hAlign);
+ ob->setProperty("verticalAlignment",vAlign);
+ QTRY_COMPARE(ob->property("running").toBool(),false);
+ QImage actual(canvas->width(), canvas->height(), QImage::Format_RGB32);
+ actual.fill(qRgb(255,255,255));
+ QPainter p(&actual);
+ canvas->render(&p);
+
+ QImage expect(expectfile);
+
+#ifdef Q_WS_X11
+ // Font-specific, but not likely platform-specific, so only test on one platform
+ if (QApplicationPrivate::graphics_system_name == "raster" || QApplicationPrivate::graphics_system_name == "") {
+ QCOMPARE(actual,expect);
+ }
+#endif
+
+ delete canvas;
+}
+
+//the alignment tests may be trivial o.oa
+void tst_qsgtext::horizontalAlignment()
+{
+ //test one align each, and then test if two align fails.
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
+
+ delete textObject;
+ }
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
+
+ delete textObject;
+ }
+ }
+
+}
+
+void tst_qsgtext::horizontalAlignment_RightToLeft()
+{
+ QSGView *canvas = createView(SRCDIR "/data/horizontalAlignment_RightToLeft.qml");
+ QSGText *text = canvas->rootObject()->findChild<QSGText*>("text");
+ QVERIFY(text != 0);
+ canvas->show();
+
+ QSGTextPrivate *textPrivate = QSGTextPrivate::get(text);
+ QVERIFY(textPrivate != 0);
+
+ // implicit alignment should follow the reading direction of RTL text
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+
+ // explicitly left aligned text
+ text->setHAlign(QSGText::AlignLeft);
+ QCOMPARE(text->hAlign(), QSGText::AlignLeft);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+
+ // explicitly right aligned text
+ text->setHAlign(QSGText::AlignRight);
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+
+ // change to rich text
+ QString textString = text->text();
+ text->setText(QString("<i>") + textString + QString("</i>"));
+ text->setTextFormat(QSGText::RichText);
+ text->resetHAlign();
+
+ // implicitly aligned rich text should follow the reading direction of text
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignLeft);
+
+ // explicitly left aligned rich text
+ text->setHAlign(QSGText::AlignLeft);
+ QCOMPARE(text->hAlign(), QSGText::AlignLeft);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignRight);
+
+ // explicitly right aligned rich text
+ text->setHAlign(QSGText::AlignRight);
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignLeft);
+
+ text->setText(textString);
+ text->setTextFormat(QSGText::PlainText);
+
+ // explicitly center aligned
+ text->setHAlign(QSGText::AlignHCenter);
+ QCOMPARE(text->hAlign(), QSGText::AlignHCenter);
+ QCOMPARE(text->effectiveHAlign(), text->hAlign());
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > canvas->width()/2);
+
+ // reseted alignment should go back to following the text reading direction
+ text->resetHAlign();
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+
+ // mirror the text item
+ QSGItemPrivate::get(text)->setLayoutMirror(true);
+
+ // mirrored implicit alignment should continue to follow the reading direction of the text
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QCOMPARE(text->effectiveHAlign(), QSGText::AlignRight);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+
+ // mirrored explicitly right aligned behaves as left aligned
+ text->setHAlign(QSGText::AlignRight);
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+ QCOMPARE(text->effectiveHAlign(), QSGText::AlignLeft);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+
+ // mirrored explicitly left aligned behaves as right aligned
+ text->setHAlign(QSGText::AlignLeft);
+ QCOMPARE(text->hAlign(), QSGText::AlignLeft);
+ QCOMPARE(text->effectiveHAlign(), QSGText::AlignRight);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+
+ // disable mirroring
+ QSGItemPrivate::get(text)->setLayoutMirror(false);
+ text->resetHAlign();
+
+ // English text should be implicitly left aligned
+ text->setText("Hello world!");
+ QCOMPARE(text->hAlign(), QSGText::AlignLeft);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+
+#ifndef Q_OS_MAC // QTBUG-18040
+ // empty text with implicit alignment follows the system locale-based
+ // keyboard input direction from QApplication::keyboardInputDirection
+ text->setText("");
+ QCOMPARE(text->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
+ QSGText::AlignLeft : QSGText::AlignRight);
+ text->setHAlign(QSGText::AlignRight);
+ QCOMPARE(text->hAlign(), QSGText::AlignRight);
+#endif
+
+ delete canvas;
+
+#ifndef Q_OS_MAC // QTBUG-18040
+ // alignment of Text with no text set to it
+ QString componentStr = "import QtQuick 2.0\nText {}";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+ QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
+ QSGText::AlignLeft : QSGText::AlignRight);
+ delete textObject;
+#endif
+}
+
+void tst_qsgtext::verticalAlignment()
+{
+ //test one align each, and then test if two align fails.
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
+
+ delete textObject;
+ }
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
+
+ delete textObject;
+ }
+ }
+
+}
+
+void tst_qsgtext::font()
+{
+ //test size, then bold, then italic, then family
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.pointSize: 40; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->font().pointSize(), 40);
+ QCOMPARE(textObject->font().bold(), false);
+ QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.pixelSize: 40; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->font().pixelSize(), 40);
+ QCOMPARE(textObject->font().bold(), false);
+ QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.bold: true; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->font().bold(), true);
+ QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.italic: true; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->font().italic(), true);
+ QCOMPARE(textObject->font().bold(), false);
+
+ delete textObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.family: \"Helvetica\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->font().family(), QString("Helvetica"));
+ QCOMPARE(textObject->font().bold(), false);
+ QCOMPARE(textObject->font().italic(), false);
+
+ delete textObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.family: \"\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->font().family(), QString(""));
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::style()
+{
+ //test style
+ for (int i = 0; i < styles.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { style: \"" + styleStrings.at(i) + "\"; styleColor: \"white\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE((int)textObject->style(), (int)styles.at(i));
+ QCOMPARE(textObject->styleColor(), QColor("white"));
+
+ delete textObject;
+ }
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QRectF brPre = textObject->boundingRect();
+ textObject->setStyle(QSGText::Outline);
+ QRectF brPost = textObject->boundingRect();
+
+ QVERIFY(brPre.width() < brPost.width());
+ QVERIFY(brPre.height() < brPost.height());
+
+ delete textObject;
+}
+
+void tst_qsgtext::color()
+{
+ //test style
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
+ QCOMPARE(textObject->styleColor(), QColor());
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
+ // default color to black?
+ QCOMPARE(textObject->color(), QColor("black"));
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ for (int j = 0; j < colorStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; styleColor: \"" + colorStrings.at(j) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
+ QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
+
+ delete textObject;
+ }
+ }
+ {
+ QString colorStr = "#AA001234";
+ QColor testColor("#001234");
+ testColor.setAlpha(170);
+
+ QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QCOMPARE(textObject->color(), testColor);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::smooth()
+{
+ for (int i = 0; i < standard.size(); i++)
+ {
+ {
+ QString componentStr = "import QtQuick 2.0\nText { smooth: true; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+ QCOMPARE(textObject->smooth(), true);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+ QCOMPARE(textObject->smooth(), false);
+
+ delete textObject;
+ }
+ }
+ for (int i = 0; i < richText.size(); i++)
+ {
+ {
+ QString componentStr = "import QtQuick 2.0\nText { smooth: true; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+ QCOMPARE(textObject->smooth(), true);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+ QCOMPARE(textObject->smooth(), false);
+
+ delete textObject;
+ }
+ }
+}
+
+void tst_qsgtext::weight()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().weight(), (int)QDeclarativeFontValueType::Normal);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().weight(), (int)QDeclarativeFontValueType::Bold);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::underline()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().underline(), false);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.underline: true; text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().underline(), true);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::overline()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().overline(), false);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.overline: true; text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().overline(), true);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::strikeout()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().strikeOut(), false);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { font.strikeout: true; text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().strikeOut(), true);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::capitalization()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::MixedCase);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllUppercase\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::AllUppercase);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllLowercase\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::AllLowercase);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"SmallCaps\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::SmallCaps);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"Capitalize\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::Capitalize);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::letterSpacing()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().letterSpacing(), 0.0);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: -2 }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().letterSpacing(), -2.);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: 3 }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().letterSpacing(), 3.);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::wordSpacing()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().wordSpacing(), 0.0);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: -50 }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().wordSpacing(), -50.);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: 200 }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().wordSpacing(), 200.);
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::QTBUG_12291()
+{
+ QSGView *canvas = createView(SRCDIR "/data/rotated.qml");
+
+ canvas->show();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
+
+ QObject *ob = canvas->rootObject();
+ QVERIFY(ob != 0);
+
+ QSGText *text = ob->findChild<QSGText*>("text");
+ QVERIFY(text);
+ QVERIFY(text->boundingRect().isValid());
+
+ delete canvas;
+}
+
+class EventSender : public QSGItem
+{
+public:
+ void sendEvent(QEvent *event) {
+ if (event->type() == QEvent::GraphicsSceneMousePress)
+ mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event));
+ else if (event->type() == QEvent::GraphicsSceneMouseRelease)
+ mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event));
+ else
+ qWarning() << "Trying to send unsupported event type";
+ }
+};
+
+class LinkTest : public QObject
+{
+ Q_OBJECT
+public:
+ LinkTest() {}
+
+ QString link;
+
+public slots:
+ void linkClicked(QString l) { link = l; }
+};
+
+void tst_qsgtext::clickLink()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"<a href=\\\"http://qt.nokia.com\\\">Hello world!</a>\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+
+ LinkTest test;
+ QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
+
+ {
+ QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMousePress);
+ me.setPos(QPointF(textObject->x()/2, textObject->y()/2));
+ me.setButton(Qt::LeftButton);
+ static_cast<EventSender*>(static_cast<QSGItem*>(textObject))->sendEvent(&me);
+ }
+
+ {
+ QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMouseRelease);
+ me.setPos(QPointF(textObject->x()/2, textObject->y()/2));
+ me.setButton(Qt::LeftButton);
+ static_cast<EventSender*>(static_cast<QSGItem*>(textObject))->sendEvent(&me);
+ }
+
+ QCOMPARE(test.link, QLatin1String("http://qt.nokia.com"));
+
+ delete textObject;
+ }
+}
+
+void tst_qsgtext::embeddedImages_data()
+{
+ QTest::addColumn<QUrl>("qmlfile");
+ QTest::addColumn<QString>("error");
+ QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocal.qml") << "";
+ QTest::newRow("local-error") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocalError.qml")
+ << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocalError.qml").toString()+":3:1: QML Text: Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/http/notexists.png").toString();
+ QTest::newRow("remote") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemote.qml") << "";
+ QTest::newRow("remote-error") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemoteError.qml")
+ << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found";
+}
+
+void tst_qsgtext::embeddedImages()
+{
+ // Tests QTBUG-9900
+
+ QFETCH(QUrl, qmlfile);
+ QFETCH(QString, error);
+
+ TestHTTPServer server(14453);
+ server.serveDirectory(SRCDIR "/data/http");
+
+ if (!error.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
+
+ QDeclarativeComponent textComponent(&engine, qmlfile);
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+
+ QTRY_COMPARE(textObject->resourcesLoading(), 0);
+
+ QPixmap pm(SRCDIR "/data/http/exists.png");
+ if (error.isEmpty()) {
+ QCOMPARE(textObject->width(), double(pm.width()));
+ QCOMPARE(textObject->height(), double(pm.height()));
+ } else {
+ QVERIFY(16 != pm.width()); // check test is effective
+ QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
+ QCOMPARE(textObject->height(), 16.0);
+ }
+
+ delete textObject;
+}
+
+void tst_qsgtext::lineCount()
+{
+ QSGView *canvas = createView(SRCDIR "/data/lineCount.qml");
+
+ QSGText *myText = canvas->rootObject()->findChild<QSGText*>("myText");
+ QVERIFY(myText != 0);
+
+ QVERIFY(myText->lineCount() > 1);
+ QVERIFY(!myText->truncated());
+ QCOMPARE(myText->maximumLineCount(), INT_MAX);
+
+ myText->setMaximumLineCount(2);
+ QCOMPARE(myText->lineCount(), 2);
+ QCOMPARE(myText->truncated(), true);
+ QCOMPARE(myText->maximumLineCount(), 2);
+
+ myText->resetMaximumLineCount();
+ QCOMPARE(myText->maximumLineCount(), INT_MAX);
+ QCOMPARE(myText->truncated(), false);
+
+ myText->setElideMode(QSGText::ElideRight);
+ myText->setMaximumLineCount(2);
+ QCOMPARE(myText->lineCount(), 2);
+ QCOMPARE(myText->truncated(), true);
+ QCOMPARE(myText->maximumLineCount(), 2);
+
+ delete canvas;
+}
+
+void tst_qsgtext::lineHeight()
+{
+ QSGView *canvas = createView(SRCDIR "/data/lineHeight.qml");
+
+ QSGText *myText = canvas->rootObject()->findChild<QSGText*>("myText");
+ QVERIFY(myText != 0);
+
+ QVERIFY(myText->lineHeight() == 1);
+ QVERIFY(myText->lineHeightMode() == QSGText::ProportionalHeight);
+
+ qreal h = myText->height();
+ myText->setLineHeight(1.5);
+ QVERIFY(myText->height() == h * 1.5);
+
+ myText->setLineHeightMode(QSGText::FixedHeight);
+ myText->setLineHeight(20);
+ QCOMPARE(myText->height(), myText->lineCount() * 20.0);
+
+ myText->setText("Lorem ipsum sit <b>amet</b>, consectetur adipiscing elit. Integer felis nisl, varius in pretium nec, venenatis non erat. Proin lobortis interdum dictum.");
+ myText->setLineHeightMode(QSGText::ProportionalHeight);
+ myText->setLineHeight(1.0);
+
+ qreal h2 = myText->height();
+ myText->setLineHeight(2.0);
+ QVERIFY(myText->height() == h2 * 2.0);
+
+ myText->setLineHeightMode(QSGText::FixedHeight);
+ myText->setLineHeight(10);
+ QCOMPARE(myText->height(), myText->lineCount() * 10.0);
+
+ delete canvas;
+}
+
+void tst_qsgtext::implicitSize_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<QString>("wrap");
+ QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "Text.NoWrap";
+ QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "Text.NoWrap";
+ QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "Text.Wrap";
+ QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "Text.Wrap";
+}
+
+void tst_qsgtext::implicitSize()
+{
+ QFETCH(QString, text);
+ QFETCH(QString, wrap);
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
+
+ QVERIFY(textObject->width() < textObject->implicitWidth());
+ QVERIFY(textObject->height() == textObject->implicitHeight());
+
+ textObject->resetWidth();
+ QVERIFY(textObject->width() == textObject->implicitWidth());
+ QVERIFY(textObject->height() == textObject->implicitHeight());
+
+ delete textObject;
+}
+
+void tst_qsgtext::qtbug_14734()
+{
+ QSGView *canvas = createView(SRCDIR "/data/qtbug_14734.qml");
+ QVERIFY(canvas);
+
+ canvas->show();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
+
+ delete canvas;
+}
+
+QTEST_MAIN(tst_qsgtext)
+
+#include "tst_qsgtext.moc"
diff --git a/tests/auto/declarative/qsgtextedit/data/CursorRect.qml b/tests/auto/declarative/qsgtextedit/data/CursorRect.qml
new file mode 100644
index 0000000000..cae3e63b72
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/CursorRect.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ objectName: "myEdit"
+ width: 50
+ text: "This is a long piece of text"
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments.qml b/tests/auto/declarative/qsgtextedit/data/alignments.qml
new file mode 100644
index 0000000000..7d365da8cb
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: top
+ width: 70; height: 70;
+
+ property alias horizontalAlignment: t.horizontalAlignment
+ property alias verticalAlignment: t.verticalAlignment
+ property alias wrapMode: t.wrapMode
+ property alias running: timer.running
+ property string txt: "Test"
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: 40
+ height: 40
+ color: "green"
+
+ TextEdit {
+ id: t
+
+ anchors.fill: parent
+ horizontalAlignment: TextEdit.AlignRight
+ verticalAlignment: TextEdit.AlignBottom
+ wrapMode: TextEdit.WordWrap
+ text: top.txt
+ }
+ Timer {
+ id: timer
+
+ interval: 1
+ running: true
+ repeat: true
+ onTriggered: {
+ top.txt = top.txt + "<br>more " + top.txt.length;
+ if (top.txt.length > 50)
+ running = false
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_cb.png b/tests/auto/declarative/qsgtextedit/data/alignments_cb.png
new file mode 100644
index 0000000000..99de2192de
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_cb.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_cc.png b/tests/auto/declarative/qsgtextedit/data/alignments_cc.png
new file mode 100644
index 0000000000..cb85251180
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_cc.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_ct.png b/tests/auto/declarative/qsgtextedit/data/alignments_ct.png
new file mode 100644
index 0000000000..ddca549c82
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_ct.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_lb.png b/tests/auto/declarative/qsgtextedit/data/alignments_lb.png
new file mode 100644
index 0000000000..1b50a81f3d
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_lb.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_lc.png b/tests/auto/declarative/qsgtextedit/data/alignments_lc.png
new file mode 100644
index 0000000000..f041b868f8
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_lc.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_lt.png b/tests/auto/declarative/qsgtextedit/data/alignments_lt.png
new file mode 100644
index 0000000000..c75e0d158e
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_lt.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_rb.png b/tests/auto/declarative/qsgtextedit/data/alignments_rb.png
new file mode 100644
index 0000000000..b06a5da715
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_rb.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_rc.png b/tests/auto/declarative/qsgtextedit/data/alignments_rc.png
new file mode 100644
index 0000000000..e468857cd0
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_rc.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/alignments_rt.png b/tests/auto/declarative/qsgtextedit/data/alignments_rt.png
new file mode 100644
index 0000000000..576715ffce
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/alignments_rt.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextedit/data/cursorTest.qml b/tests/auto/declarative/qsgtextedit/data/cursorTest.qml
new file mode 100644
index 0000000000..e734fc141c
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/cursorTest.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Rectangle { width: 300; height: 300; color: "white"
+ TextEdit { text: "Hello world!"; id: textEditObject; objectName: "textEditObject"
+ resources: [ Component { id:cursor; Item { id:cursorInstance; objectName: "cursorInstance" } } ]
+ cursorDelegate: cursor
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/cursorVisible.qml b/tests/auto/declarative/qsgtextedit/data/cursorVisible.qml
new file mode 100644
index 0000000000..49e9386947
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/cursorVisible.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+ width: 100
+ height: 20
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/geometrySignals.qml b/tests/auto/declarative/qsgtextedit/data/geometrySignals.qml
new file mode 100644
index 0000000000..3dbe61c74b
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/geometrySignals.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Item {
+ width: 400; height: 500;
+ property int bindingWidth: text.width
+ property int bindingHeight: text.height
+
+ TextInput {
+ id: text
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml b/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml
new file mode 100644
index 0000000000..74592fed7f
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: top
+ width: 200; height: 70;
+
+ property alias horizontalAlignment: text.horizontalAlignment
+ property string text: "اختبا"
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: 200
+ height: 20
+ color: "green"
+
+ TextEdit {
+ id: text
+ objectName: "text"
+ anchors.fill: parent
+ text: top.text
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/ErrItem.qml b/tests/auto/declarative/qsgtextedit/data/http/ErrItem.qml
new file mode 100644
index 0000000000..68c0e0c093
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/ErrItem.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Item{
+ Fungus{
+ palatable: false;
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/NormItem.qml b/tests/auto/declarative/qsgtextedit/data/http/NormItem.qml
new file mode 100644
index 0000000000..2e4c1ed440
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/NormItem.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+ objectName: "delegateOkay"
+ Rectangle { }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTest.qml b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTest.qml
new file mode 100644
index 0000000000..be4526e22b
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTest.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Rectangle { width: 300; height: 300; color: "white"
+ resources: [
+ Component { id:cursorFail; FailItem { objectName: "delegateFail" } },
+ Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } },
+ Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } },
+ Component { id:cursorErr; ErrItem { objectName: "delegateErrorA" } }
+ ]
+ TextEdit {
+ cursorDelegate: cursorFail
+ }
+ TextEdit {
+ cursorDelegate: cursorWait
+ }
+ TextEdit {
+ cursorDelegate: cursorNorm
+ }
+ TextEdit {
+ cursorDelegate: cursorErr
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail1.qml b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail1.qml
new file mode 100644
index 0000000000..1d7763f913
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail1.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Rectangle { width: 300; height: 300; color: "white"
+ resources: [
+ Component { id:cursorFail; FailItem { objectName: "delegateFail" } },
+ Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } },
+ Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } }
+ ]
+ TextEdit {
+ cursorDelegate: cursorFail
+ }
+ TextEdit {
+ cursorDelegate: cursorWait
+ }
+ TextEdit {
+ cursorDelegate: cursorNorm
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail2.qml b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail2.qml
new file mode 100644
index 0000000000..c82ec02e68
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestFail2.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Rectangle { width: 300; height: 300; color: "white"
+ resources: [
+ Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } },
+ Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } },
+ Component { id:cursorErr; ErrItem { objectName: "delegateErrorA" } }
+ ]
+ TextEdit {
+ cursorDelegate: cursorWait
+ }
+ TextEdit {
+ cursorDelegate: cursorNorm
+ }
+ TextEdit {
+ cursorDelegate: cursorErr
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestPass.qml b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestPass.qml
new file mode 100644
index 0000000000..96d582c95d
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/cursorHttpTestPass.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Rectangle { width: 300; height: 300; color: "white"
+ resources: [
+ Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } },
+ Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } }
+ ]
+ TextEdit {
+ cursorDelegate: cursorWait
+ text: "Hello"
+ }
+ TextEdit {
+ objectName: "textEditObject"
+ cursorDelegate: cursorNorm
+ focus: true;
+ text: "Hello"
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/http/qmldir b/tests/auto/declarative/qsgtextedit/data/http/qmldir
new file mode 100644
index 0000000000..886e6ffec0
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/http/qmldir
@@ -0,0 +1,4 @@
+ErrItem ErrItem.qml
+NormItem NormItem.qml
+FailItem FailItem.qml
+WaitItem WaitItem.qml
diff --git a/tests/auto/declarative/qsgtextedit/data/httpfail/FailItem.qml b/tests/auto/declarative/qsgtextedit/data/httpfail/FailItem.qml
new file mode 100644
index 0000000000..8161843479
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/httpfail/FailItem.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+ Rectangle { }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/httpslow/WaitItem.qml b/tests/auto/declarative/qsgtextedit/data/httpslow/WaitItem.qml
new file mode 100644
index 0000000000..8161843479
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/httpslow/WaitItem.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+ Rectangle { }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/inputContext.qml b/tests/auto/declarative/qsgtextedit/data/inputContext.qml
new file mode 100644
index 0000000000..a37c77e3bf
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/inputContext.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ width: 200
+ text: "supercalifra"
+ focus: true
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/inputMethodEvent.qml b/tests/auto/declarative/qsgtextedit/data/inputMethodEvent.qml
new file mode 100644
index 0000000000..e3f629ce3e
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/inputMethodEvent.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/inputmethodhints.qml b/tests/auto/declarative/qsgtextedit/data/inputmethodhints.qml
new file mode 100644
index 0000000000..dec3b978e7
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/inputmethodhints.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+TextEdit {
+ text: "Hello world!"
+ inputMethodHints: Qt.ImhNoPredictiveText
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_default.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_default.qml
new file mode 100644
index 0000000000..ac32f4ced7
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_default.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: false
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_false.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_false.qml
new file mode 100644
index 0000000000..ac32f4ced7
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_false.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: false
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml
new file mode 100644
index 0000000000..ac32f4ced7
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: false
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_true.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_true.qml
new file mode 100644
index 0000000000..7c7cb0b6fc
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_true.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml
new file mode 100644
index 0000000000..7c7cb0b6fc
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_characters.qml b/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_characters.qml
new file mode 100644
index 0000000000..c1fe42fd57
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_characters.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+ mouseSelectionMode: TextEdit.SelectCharacters
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_default.qml b/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_default.qml
new file mode 100644
index 0000000000..7c7cb0b6fc
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_default.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_words.qml b/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_words.qml
new file mode 100644
index 0000000000..0a372bbf83
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/mouseselectionmode_words.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+ mouseSelectionMode: TextEdit.SelectWords
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/navigation.qml b/tests/auto/declarative/qsgtextedit/data/navigation.qml
new file mode 100644
index 0000000000..0201c62b3c
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/navigation.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Rectangle {
+ property variant myInput: input
+
+ width: 800; height: 600; color: "blue"
+
+ Item {
+ id: firstItem;
+ KeyNavigation.right: input
+ }
+
+ TextEdit { id: input; focus: true
+ KeyNavigation.left: firstItem
+ KeyNavigation.right: lastItem
+ KeyNavigation.up: firstItem
+ KeyNavigation.down: lastItem
+ text: "a"
+ }
+ Item {
+ id: lastItem
+ KeyNavigation.left: input
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/openInputPanel.qml b/tests/auto/declarative/qsgtextedit/data/openInputPanel.qml
new file mode 100644
index 0000000000..8998e55abb
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/openInputPanel.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+TextEdit {
+ text: "Hello world"
+ focus: false
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/positionAt.qml b/tests/auto/declarative/qsgtextedit/data/positionAt.qml
new file mode 100644
index 0000000000..19093281fe
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/positionAt.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+TextEdit {
+ focus: true
+ objectName: "myInput"
+ width: 50
+ height: 25
+ text: "This is\n a long piece of text"
+}
diff --git a/tests/auto/declarative/qsgtextedit/data/readOnly.qml b/tests/auto/declarative/qsgtextedit/data/readOnly.qml
new file mode 100644
index 0000000000..085adba5fb
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/data/readOnly.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Rectangle {
+ property variant myInput: input
+
+ width: 800; height: 600; color: "blue"
+
+ TextEdit { id: input; focus: true
+ readOnly: true
+ text: "I am the very model of a modern major general.\n"
+ }
+}
diff --git a/tests/auto/declarative/qsgtextedit/qsgtextedit.pro b/tests/auto/declarative/qsgtextedit/qsgtextedit.pro
new file mode 100644
index 0000000000..a67658efa0
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/qsgtextedit.pro
@@ -0,0 +1,14 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui network
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgtextedit.cpp ../shared/testhttpserver.cpp
+HEADERS += ../shared/testhttpserver.h
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
diff --git a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp
new file mode 100644
index 0000000000..5510701962
--- /dev/null
+++ b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp
@@ -0,0 +1,2388 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtTest/QSignalSpy>
+#include "../../../shared/util.h"
+#include "../shared/testhttpserver.h"
+#include <math.h>
+#include <QFile>
+#include <QTextDocument>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <private/qsgtextedit_p.h>
+#include <private/qsgtextedit_p_p.h>
+#include <QFontMetrics>
+#include <QSGView>
+#include <QDir>
+#include <QStyle>
+#include <QInputContext>
+#include <QClipboard>
+#include <QMimeData>
+#include <private/qapplication_p.h>
+#include <private/qtextcontrol_p.h>
+
+#ifdef Q_WS_MAC
+#include <Carbon/Carbon.h>
+#endif
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+Q_DECLARE_METATYPE(QSGTextEdit::SelectionMode)
+
+QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
+{
+ // XXX This will be replaced by some clever persistent platform image store.
+ QString persistent_dir = SRCDIR "/data";
+ QString arch = "unknown-architecture"; // QTest needs to help with this.
+
+ QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
+
+ if (!QFile::exists(expectfile)) {
+ actual.save(expectfile);
+ qWarning() << "created" << expectfile;
+ }
+
+ return expectfile;
+}
+
+
+class tst_qsgtextedit : public QObject
+
+{
+ Q_OBJECT
+public:
+ tst_qsgtextedit();
+
+private slots:
+ void text();
+ void width();
+ void wrap();
+ void textFormat();
+ void alignments();
+ void alignments_data();
+
+ // ### these tests may be trivial
+ void hAlign();
+ void hAlign_RightToLeft();
+ void vAlign();
+ void font();
+ void color();
+ void textMargin();
+ void persistentSelection();
+ void focusOnPress();
+ void selection();
+ void isRightToLeft_data();
+ void isRightToLeft();
+ void keySelection();
+ void moveCursorSelection_data();
+ void moveCursorSelection();
+ void moveCursorSelectionSequence_data();
+ void moveCursorSelectionSequence();
+ void mouseSelection_data();
+ void mouseSelection();
+ void mouseSelectionMode_data();
+ void mouseSelectionMode();
+ void dragMouseSelection();
+ void inputMethodHints();
+
+ void positionAt();
+
+ void cursorDelegate();
+ void cursorVisible();
+ void delegateLoading_data();
+ void delegateLoading();
+ void navigation();
+ void readOnly();
+ void copyAndPaste();
+ void canPaste();
+ void canPasteEmpty();
+ void textInput();
+ void openInputPanelOnClick();
+ void openInputPanelOnFocus();
+ void geometrySignals();
+ void pastingRichText_QTBUG_14003();
+ void implicitSize_data();
+ void implicitSize();
+ void testQtQuick11Attributes();
+ void testQtQuick11Attributes_data();
+
+ void preeditMicroFocus();
+ void inputContextMouseHandler();
+ void inputMethodComposing();
+ void cursorRectangleSize();
+
+private:
+ void simulateKey(QSGView *, int key, Qt::KeyboardModifiers modifiers = 0);
+
+ QStringList standard;
+ QStringList richText;
+
+ QStringList hAlignmentStrings;
+ QStringList vAlignmentStrings;
+
+ QList<Qt::Alignment> vAlignments;
+ QList<Qt::Alignment> hAlignments;
+
+ QStringList colorStrings;
+
+ QDeclarativeEngine engine;
+};
+
+tst_qsgtextedit::tst_qsgtextedit()
+{
+ standard << "the quick brown fox jumped over the lazy dog"
+ << "the quick brown fox\n jumped over the lazy dog"
+ << "Hello, world!"
+ << "!dlrow ,olleH";
+
+ richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
+ << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
+
+ hAlignmentStrings << "AlignLeft"
+ << "AlignRight"
+ << "AlignHCenter";
+
+ vAlignmentStrings << "AlignTop"
+ << "AlignBottom"
+ << "AlignVCenter";
+
+ hAlignments << Qt::AlignLeft
+ << Qt::AlignRight
+ << Qt::AlignHCenter;
+
+ vAlignments << Qt::AlignTop
+ << Qt::AlignBottom
+ << Qt::AlignVCenter;
+
+ colorStrings << "aliceblue"
+ << "antiquewhite"
+ << "aqua"
+ << "darkkhaki"
+ << "darkolivegreen"
+ << "dimgray"
+ << "palevioletred"
+ << "lightsteelblue"
+ << "#000000"
+ << "#AAAAAA"
+ << "#FFFFFF"
+ << "#2AC05F";
+ //
+ // need a different test to do alpha channel test
+ // << "#AA0011DD"
+ // << "#00F16B11";
+ //
+}
+
+void tst_qsgtextedit::text()
+{
+ {
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->text(), QString(""));
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->text(), standard.at(i));
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QString actual = textEditObject->text();
+ QString expected = richText.at(i);
+ actual.replace(QRegExp(".*<body[^>]*>"),"");
+ actual.replace(QRegExp("(<[^>]*>)+"),"<>");
+ expected.replace(QRegExp("(<[^>]*>)+"),"<>");
+ QCOMPARE(actual.simplified(),expected.simplified());
+ }
+}
+
+void tst_qsgtextedit::width()
+{
+ // uses Font metrics to find the width for standard and document to find the width for rich
+ {
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->width(), 0.0);
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QFont f;
+ QFontMetricsF fm(f);
+ qreal metricWidth = fm.size(Qt::TextExpandTabs && Qt::TextShowMnemonic, standard.at(i)).width();
+ metricWidth = ceil(metricWidth);
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->width(), qreal(metricWidth));
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QTextDocument document;
+ document.setHtml(richText.at(i));
+ document.setDocumentMargin(0);
+
+ int documentWidth = ceil(document.idealWidth());
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->width(), qreal(documentWidth));
+ }
+}
+
+void tst_qsgtextedit::wrap()
+{
+ // for specified width and wrap set true
+ {
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->width(), 300.);
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->width(), 300.);
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->width(), 300.);
+ }
+
+}
+
+void tst_qsgtextedit::textFormat()
+{
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
+ QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->textFormat() == QSGTextEdit::RichText);
+ }
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
+ QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->textFormat() == QSGTextEdit::PlainText);
+ }
+}
+
+void tst_qsgtextedit::alignments_data()
+{
+ QTest::addColumn<int>("hAlign");
+ QTest::addColumn<int>("vAlign");
+ QTest::addColumn<QString>("expectfile");
+
+ QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt";
+ QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt";
+ QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct";
+
+ QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb";
+ QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb";
+ QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb";
+
+ QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc";
+ QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc";
+ QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc";
+}
+
+
+void tst_qsgtextedit::alignments()
+{
+ QFETCH(int, hAlign);
+ QFETCH(int, vAlign);
+ QFETCH(QString, expectfile);
+
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/alignments.qml"));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QObject *ob = canvas.rootObject();
+ QVERIFY(ob != 0);
+ ob->setProperty("horizontalAlignment",hAlign);
+ ob->setProperty("verticalAlignment",vAlign);
+ QTRY_COMPARE(ob->property("running").toBool(),false);
+ QImage actual(canvas.width(), canvas.height(), QImage::Format_RGB32);
+ actual.fill(qRgb(255,255,255));
+ QPainter p(&actual);
+ canvas.render(&p);
+
+ expectfile = createExpectedFileIfNotFound(expectfile, actual);
+
+ QImage expect(expectfile);
+
+ QCOMPARE(actual,expect);
+}
+
+
+//the alignment tests may be trivial o.oa
+void tst_qsgtextedit::hAlign()
+{
+ //test one align each, and then test if two align fails.
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ for (int j=0; j < hAlignmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
+ }
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ for (int j=0; j < hAlignmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
+ }
+ }
+
+}
+
+void tst_qsgtextedit::hAlign_RightToLeft()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment_RightToLeft.qml"));
+ QSGTextEdit *textEdit = canvas.rootObject()->findChild<QSGTextEdit*>("text");
+ QVERIFY(textEdit != 0);
+ canvas.show();
+
+ // implicit alignment should follow the reading direction of text
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ // explicitly left aligned
+ textEdit->setHAlign(QSGTextEdit::AlignLeft);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
+ QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
+
+ // explicitly right aligned
+ textEdit->setHAlign(QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ QString textString = textEdit->text();
+ textEdit->setText(QString("<i>") + textString + QString("</i>"));
+ textEdit->resetHAlign();
+
+ // implicitly aligned rich text should follow the reading direction of RTL text
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ // explicitly left aligned rich text
+ textEdit->setHAlign(QSGTextEdit::AlignLeft);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
+ QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
+ QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
+
+ // explicitly right aligned rich text
+ textEdit->setHAlign(QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ textEdit->setText(textString);
+
+ // explicitly center aligned
+ textEdit->setHAlign(QSGTextEdit::AlignHCenter);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignHCenter);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ // reseted alignment should go back to following the text reading direction
+ textEdit->resetHAlign();
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ // mirror the text item
+ QSGItemPrivate::get(textEdit)->setLayoutMirror(true);
+
+ // mirrored implicit alignment should continue to follow the reading direction of the text
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignRight);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ // mirrored explicitly right aligned behaves as left aligned
+ textEdit->setHAlign(QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignLeft);
+ QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
+
+ // mirrored explicitly left aligned behaves as right aligned
+ textEdit->setHAlign(QSGTextEdit::AlignLeft);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
+ QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignRight);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+
+ // disable mirroring
+ QSGItemPrivate::get(textEdit)->setLayoutMirror(false);
+ textEdit->resetHAlign();
+
+ // English text should be implicitly left aligned
+ textEdit->setText("Hello world!");
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
+ QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
+
+#ifndef Q_OS_MAC // QTBUG-18040
+ // empty text with implicit alignment follows the system locale-based
+ // keyboard input direction from QApplication::keyboardInputDirection
+ textEdit->setText("");
+ QCOMPARE(textEdit->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
+ QSGTextEdit::AlignLeft : QSGTextEdit::AlignRight);
+ if (QApplication::keyboardInputDirection() == Qt::LeftToRight)
+ QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
+ else
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+ textEdit->setHAlign(QSGTextEdit::AlignRight);
+ QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
+ QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
+#endif
+
+#ifndef Q_OS_MAC // QTBUG-18040
+ // alignment of TextEdit with no text set to it
+ QString componentStr = "import QtQuick 2.0\nTextEdit {}";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
+ QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
+ QSGTextEdit::AlignLeft : QSGTextEdit::AlignRight);
+ delete textObject;
+#endif
+}
+
+void tst_qsgtextedit::vAlign()
+{
+ //test one align each, and then test if two align fails.
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ for (int j=0; j < vAlignmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
+ }
+ }
+
+ for (int i = 0; i < richText.size(); i++)
+ {
+ for (int j=0; j < vAlignmentStrings.size(); j++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
+ }
+ }
+
+}
+
+void tst_qsgtextedit::font()
+{
+ //test size, then bold, then italic, then family
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->font().pointSize(), 40);
+ QCOMPARE(textEditObject->font().bold(), false);
+ QCOMPARE(textEditObject->font().italic(), false);
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->font().bold(), true);
+ QCOMPARE(textEditObject->font().italic(), false);
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->font().italic(), true);
+ QCOMPARE(textEditObject->font().bold(), false);
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
+ QCOMPARE(textEditObject->font().bold(), false);
+ QCOMPARE(textEditObject->font().italic(), false);
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->font().family(), QString(""));
+ }
+}
+
+void tst_qsgtextedit::color()
+{
+ //test initial color
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QSGTextEditPrivate *textEditPrivate = static_cast<QSGTextEditPrivate*>(QSGItemPrivate::get(textEditObject));
+
+ QVERIFY(textEditObject);
+ QVERIFY(textEditPrivate);
+ QVERIFY(textEditPrivate->control);
+
+ QPalette pal = textEditPrivate->control->palette();
+ QCOMPARE(textEditPrivate->color, QColor("black"));
+ QCOMPARE(textEditPrivate->color, pal.color(QPalette::Text));
+ }
+ //test normal
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
+ }
+
+ //test selection
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
+ }
+
+ //test selected text
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
+ }
+
+ {
+ QString colorStr = "#AA001234";
+ QColor testColor("#001234");
+ testColor.setAlpha(170);
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->color(), testColor);
+ }
+}
+
+void tst_qsgtextedit::textMargin()
+{
+ for(qreal i=0; i<=10; i+=0.3){
+ QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->textMargin(), i);
+ }
+}
+
+void tst_qsgtextedit::persistentSelection()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: true; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->persistentSelection(), true);
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: false; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->persistentSelection(), false);
+ }
+}
+
+void tst_qsgtextedit::focusOnPress()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: true; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->focusOnPress(), true);
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: false; text: \"Hello World\" }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+ QCOMPARE(textEditObject->focusOnPress(), false);
+ }
+}
+
+void tst_qsgtextedit::selection()
+{
+ QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(textEditObject != 0);
+
+
+ //Test selection follows cursor
+ for(int i=0; i<= testStr.size(); i++) {
+ textEditObject->setCursorPosition(i);
+ QCOMPARE(textEditObject->cursorPosition(), i);
+ QCOMPARE(textEditObject->selectionStart(), i);
+ QCOMPARE(textEditObject->selectionEnd(), i);
+ QVERIFY(textEditObject->selectedText().isNull());
+ }
+
+ textEditObject->setCursorPosition(0);
+ QVERIFY(textEditObject->cursorPosition() == 0);
+ QVERIFY(textEditObject->selectionStart() == 0);
+ QVERIFY(textEditObject->selectionEnd() == 0);
+ QVERIFY(textEditObject->selectedText().isNull());
+
+ // Verify invalid positions are ignored.
+ textEditObject->setCursorPosition(-1);
+ QVERIFY(textEditObject->cursorPosition() == 0);
+ QVERIFY(textEditObject->selectionStart() == 0);
+ QVERIFY(textEditObject->selectionEnd() == 0);
+ QVERIFY(textEditObject->selectedText().isNull());
+
+ textEditObject->setCursorPosition(textEditObject->text().count()+1);
+ QVERIFY(textEditObject->cursorPosition() == 0);
+ QVERIFY(textEditObject->selectionStart() == 0);
+ QVERIFY(textEditObject->selectionEnd() == 0);
+ QVERIFY(textEditObject->selectedText().isNull());
+
+ //Test selection
+ for(int i=0; i<= testStr.size(); i++) {
+ textEditObject->select(0,i);
+ QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
+ }
+ for(int i=0; i<= testStr.size(); i++) {
+ textEditObject->select(i,testStr.size());
+ QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
+ }
+
+ textEditObject->setCursorPosition(0);
+ QVERIFY(textEditObject->cursorPosition() == 0);
+ QVERIFY(textEditObject->selectionStart() == 0);
+ QVERIFY(textEditObject->selectionEnd() == 0);
+ QVERIFY(textEditObject->selectedText().isNull());
+
+ //Test Error Ignoring behaviour
+ textEditObject->setCursorPosition(0);
+ QVERIFY(textEditObject->selectedText().isNull());
+ textEditObject->select(-10,0);
+ QVERIFY(textEditObject->selectedText().isNull());
+ textEditObject->select(100,101);
+ QVERIFY(textEditObject->selectedText().isNull());
+ textEditObject->select(0,-10);
+ QVERIFY(textEditObject->selectedText().isNull());
+ textEditObject->select(0,100);
+ QVERIFY(textEditObject->selectedText().isNull());
+ textEditObject->select(0,10);
+ QVERIFY(textEditObject->selectedText().size() == 10);
+ textEditObject->select(-10,0);
+ QVERIFY(textEditObject->selectedText().size() == 10);
+ textEditObject->select(100,101);
+ QVERIFY(textEditObject->selectedText().size() == 10);
+ textEditObject->select(0,-10);
+ QVERIFY(textEditObject->selectedText().size() == 10);
+ textEditObject->select(0,100);
+ QVERIFY(textEditObject->selectedText().size() == 10);
+
+ textEditObject->deselect();
+ QVERIFY(textEditObject->selectedText().isNull());
+ textEditObject->select(0,10);
+ QVERIFY(textEditObject->selectedText().size() == 10);
+ textEditObject->deselect();
+ QVERIFY(textEditObject->selectedText().isNull());
+}
+
+void tst_qsgtextedit::isRightToLeft_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<bool>("emptyString");
+ QTest::addColumn<bool>("firstCharacter");
+ QTest::addColumn<bool>("lastCharacter");
+ QTest::addColumn<bool>("middleCharacter");
+ QTest::addColumn<bool>("startString");
+ QTest::addColumn<bool>("midString");
+ QTest::addColumn<bool>("endString");
+
+ const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
+ QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
+ QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
+ QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
+ QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
+ QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true;
+ QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
+}
+
+void tst_qsgtextedit::isRightToLeft()
+{
+ QFETCH(QString, text);
+ QFETCH(bool, emptyString);
+ QFETCH(bool, firstCharacter);
+ QFETCH(bool, lastCharacter);
+ QFETCH(bool, middleCharacter);
+ QFETCH(bool, startString);
+ QFETCH(bool, midString);
+ QFETCH(bool, endString);
+
+ QSGTextEdit textEdit;
+ textEdit.setText(text);
+
+ // first test that the right string is delivered to the QString::isRightToLeft()
+ QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
+ QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
+ QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
+ QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
+ QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
+ QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
+ if (text.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
+ QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
+
+ // then test that the feature actually works
+ QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
+ QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
+ QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
+ QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
+ QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
+ QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
+ if (text.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
+ QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
+}
+
+void tst_qsgtextedit::keySelection()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextEdit *input = qobject_cast<QSGTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(input != 0);
+ QTRY_VERIFY(input->hasActiveFocus() == true);
+
+ QSignalSpy spy(input, SIGNAL(selectionChanged()));
+
+ simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
+ QVERIFY(input->hasActiveFocus() == true);
+ QCOMPARE(input->selectedText(), QString("a"));
+ QCOMPARE(spy.count(), 1);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == true);
+ QCOMPARE(input->selectedText(), QString());
+ QCOMPARE(spy.count(), 2);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == false);
+ QCOMPARE(input->selectedText(), QString());
+ QCOMPARE(spy.count(), 2);
+
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == true);
+ QCOMPARE(spy.count(), 2);
+ simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
+ QVERIFY(input->hasActiveFocus() == true);
+ QCOMPARE(input->selectedText(), QString("a"));
+ QCOMPARE(spy.count(), 3);
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == true);
+ QCOMPARE(input->selectedText(), QString());
+ QCOMPARE(spy.count(), 4);
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == false);
+ QCOMPARE(input->selectedText(), QString());
+ QCOMPARE(spy.count(), 4);
+}
+
+void tst_qsgtextedit::moveCursorSelection_data()
+{
+ QTest::addColumn<QString>("testStr");
+ QTest::addColumn<int>("cursorPosition");
+ QTest::addColumn<int>("movePosition");
+ QTest::addColumn<QSGTextEdit::SelectionMode>("mode");
+ QTest::addColumn<int>("selectionStart");
+ QTest::addColumn<int>("selectionEnd");
+ QTest::addColumn<bool>("reversible");
+
+ QTest::newRow("(t)he|characters")
+ << standard[0] << 0 << 1 << QSGTextEdit::SelectCharacters << 0 << 1 << true;
+ QTest::newRow("do(g)|characters")
+ << standard[0] << 43 << 44 << QSGTextEdit::SelectCharacters << 43 << 44 << true;
+ QTest::newRow("jum(p)ed|characters")
+ << standard[0] << 23 << 24 << QSGTextEdit::SelectCharacters << 23 << 24 << true;
+ QTest::newRow("jumped( )over|characters")
+ << standard[0] << 26 << 27 << QSGTextEdit::SelectCharacters << 26 << 27 << true;
+ QTest::newRow("(the )|characters")
+ << standard[0] << 0 << 4 << QSGTextEdit::SelectCharacters << 0 << 4 << true;
+ QTest::newRow("( dog)|characters")
+ << standard[0] << 40 << 44 << QSGTextEdit::SelectCharacters << 40 << 44 << true;
+ QTest::newRow("( jumped )|characters")
+ << standard[0] << 19 << 27 << QSGTextEdit::SelectCharacters << 19 << 27 << true;
+ QTest::newRow("th(e qu)ick|characters")
+ << standard[0] << 2 << 6 << QSGTextEdit::SelectCharacters << 2 << 6 << true;
+ QTest::newRow("la(zy d)og|characters")
+ << standard[0] << 38 << 42 << QSGTextEdit::SelectCharacters << 38 << 42 << true;
+ QTest::newRow("jum(ped ov)er|characters")
+ << standard[0] << 23 << 29 << QSGTextEdit::SelectCharacters << 23 << 29 << true;
+ QTest::newRow("()the|characters")
+ << standard[0] << 0 << 0 << QSGTextEdit::SelectCharacters << 0 << 0 << true;
+ QTest::newRow("dog()|characters")
+ << standard[0] << 44 << 44 << QSGTextEdit::SelectCharacters << 44 << 44 << true;
+ QTest::newRow("jum()ped|characters")
+ << standard[0] << 23 << 23 << QSGTextEdit::SelectCharacters << 23 << 23 << true;
+
+ QTest::newRow("<(t)he>|words")
+ << standard[0] << 0 << 1 << QSGTextEdit::SelectWords << 0 << 3 << true;
+ QTest::newRow("<do(g)>|words")
+ << standard[0] << 43 << 44 << QSGTextEdit::SelectWords << 41 << 44 << true;
+ QTest::newRow("<jum(p)ed>|words")
+ << standard[0] << 23 << 24 << QSGTextEdit::SelectWords << 20 << 26 << true;
+ QTest::newRow("<jumped( )>over|words")
+ << standard[0] << 26 << 27 << QSGTextEdit::SelectWords << 20 << 27 << false;
+ QTest::newRow("jumped<( )over>|words,reversed")
+ << standard[0] << 27 << 26 << QSGTextEdit::SelectWords << 26 << 31 << false;
+ QTest::newRow("<(the )>quick|words")
+ << standard[0] << 0 << 4 << QSGTextEdit::SelectWords << 0 << 4 << false;
+ QTest::newRow("<(the )quick>|words,reversed")
+ << standard[0] << 4 << 0 << QSGTextEdit::SelectWords << 0 << 9 << false;
+ QTest::newRow("<lazy( dog)>|words")
+ << standard[0] << 40 << 44 << QSGTextEdit::SelectWords << 36 << 44 << false;
+ QTest::newRow("lazy<( dog)>|words,reversed")
+ << standard[0] << 44 << 40 << QSGTextEdit::SelectWords << 40 << 44 << false;
+ QTest::newRow("<fox( jumped )>over|words")
+ << standard[0] << 19 << 27 << QSGTextEdit::SelectWords << 16 << 27 << false;
+ QTest::newRow("fox<( jumped )over>|words,reversed")
+ << standard[0] << 27 << 19 << QSGTextEdit::SelectWords << 19 << 31 << false;
+ QTest::newRow("<th(e qu)ick>|words")
+ << standard[0] << 2 << 6 << QSGTextEdit::SelectWords << 0 << 9 << true;
+ QTest::newRow("<la(zy d)og|words>")
+ << standard[0] << 38 << 42 << QSGTextEdit::SelectWords << 36 << 44 << true;
+ QTest::newRow("<jum(ped ov)er>|words")
+ << standard[0] << 23 << 29 << QSGTextEdit::SelectWords << 20 << 31 << true;
+ QTest::newRow("<()>the|words")
+ << standard[0] << 0 << 0 << QSGTextEdit::SelectWords << 0 << 0 << true;
+ QTest::newRow("dog<()>|words")
+ << standard[0] << 44 << 44 << QSGTextEdit::SelectWords << 44 << 44 << true;
+ QTest::newRow("jum<()>ped|words")
+ << standard[0] << 23 << 23 << QSGTextEdit::SelectWords << 23 << 23 << true;
+
+ QTest::newRow("Hello<(,)> |words")
+ << standard[2] << 5 << 6 << QSGTextEdit::SelectWords << 5 << 6 << true;
+ QTest::newRow("Hello<(, )>world|words")
+ << standard[2] << 5 << 7 << QSGTextEdit::SelectWords << 5 << 7 << false;
+ QTest::newRow("Hello<(, )world>|words,reversed")
+ << standard[2] << 7 << 5 << QSGTextEdit::SelectWords << 5 << 12 << false;
+ QTest::newRow("<Hel(lo, )>world|words")
+ << standard[2] << 3 << 7 << QSGTextEdit::SelectWords << 0 << 7 << false;
+ QTest::newRow("<Hel(lo, )world>|words,reversed")
+ << standard[2] << 7 << 3 << QSGTextEdit::SelectWords << 0 << 12 << false;
+ QTest::newRow("<Hel(lo)>,|words")
+ << standard[2] << 3 << 5 << QSGTextEdit::SelectWords << 0 << 5 << true;
+ QTest::newRow("Hello<()>,|words")
+ << standard[2] << 5 << 5 << QSGTextEdit::SelectWords << 5 << 5 << true;
+ QTest::newRow("Hello,<()>|words")
+ << standard[2] << 6 << 6 << QSGTextEdit::SelectWords << 6 << 6 << true;
+ QTest::newRow("Hello<,( )>world|words")
+ << standard[2] << 6 << 7 << QSGTextEdit::SelectWords << 5 << 7 << false;
+ QTest::newRow("Hello,<( )world>|words,reversed")
+ << standard[2] << 7 << 6 << QSGTextEdit::SelectWords << 6 << 12 << false;
+ QTest::newRow("Hello<,( world)>|words")
+ << standard[2] << 6 << 12 << QSGTextEdit::SelectWords << 5 << 12 << false;
+ QTest::newRow("Hello,<( world)>|words,reversed")
+ << standard[2] << 12 << 6 << QSGTextEdit::SelectWords << 6 << 12 << false;
+ QTest::newRow("Hello<,( world!)>|words")
+ << standard[2] << 6 << 13 << QSGTextEdit::SelectWords << 5 << 13 << false;
+ QTest::newRow("Hello,<( world!)>|words,reversed")
+ << standard[2] << 13 << 6 << QSGTextEdit::SelectWords << 6 << 13 << false;
+ QTest::newRow("Hello<(, world!)>|words")
+ << standard[2] << 5 << 13 << QSGTextEdit::SelectWords << 5 << 13 << true;
+ QTest::newRow("world<(!)>|words")
+ << standard[2] << 12 << 13 << QSGTextEdit::SelectWords << 12 << 13 << true;
+ QTest::newRow("world!<()>)|words")
+ << standard[2] << 13 << 13 << QSGTextEdit::SelectWords << 13 << 13 << true;
+ QTest::newRow("world<()>!)|words")
+ << standard[2] << 12 << 12 << QSGTextEdit::SelectWords << 12 << 12 << true;
+
+ QTest::newRow("<(,)>olleH |words")
+ << standard[3] << 7 << 8 << QSGTextEdit::SelectWords << 7 << 8 << true;
+ QTest::newRow("<dlrow( ,)>olleH|words")
+ << standard[3] << 6 << 8 << QSGTextEdit::SelectWords << 1 << 8 << false;
+ QTest::newRow("dlrow<( ,)>olleH|words,reversed")
+ << standard[3] << 8 << 6 << QSGTextEdit::SelectWords << 6 << 8 << false;
+ QTest::newRow("<dlrow( ,ol)leH>|words")
+ << standard[3] << 6 << 10 << QSGTextEdit::SelectWords << 1 << 13 << false;
+ QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
+ << standard[3] << 10 << 6 << QSGTextEdit::SelectWords << 6 << 13 << false;
+ QTest::newRow(",<(ol)leH>,|words")
+ << standard[3] << 8 << 10 << QSGTextEdit::SelectWords << 8 << 13 << true;
+ QTest::newRow(",<()>olleH|words")
+ << standard[3] << 8 << 8 << QSGTextEdit::SelectWords << 8 << 8 << true;
+ QTest::newRow("<()>,olleH|words")
+ << standard[3] << 7 << 7 << QSGTextEdit::SelectWords << 7 << 7 << true;
+ QTest::newRow("<dlrow( )>,olleH|words")
+ << standard[3] << 6 << 7 << QSGTextEdit::SelectWords << 1 << 7 << false;
+ QTest::newRow("dlrow<( ),>olleH|words,reversed")
+ << standard[3] << 7 << 6 << QSGTextEdit::SelectWords << 6 << 8 << false;
+ QTest::newRow("<(dlrow )>,olleH|words")
+ << standard[3] << 1 << 7 << QSGTextEdit::SelectWords << 1 << 7 << false;
+ QTest::newRow("<(dlrow ),>olleH|words,reversed")
+ << standard[3] << 7 << 1 << QSGTextEdit::SelectWords << 1 << 8 << false;
+ QTest::newRow("<(!dlrow )>,olleH|words")
+ << standard[3] << 0 << 7 << QSGTextEdit::SelectWords << 0 << 7 << false;
+ QTest::newRow("<(!dlrow ),>olleH|words,reversed")
+ << standard[3] << 7 << 0 << QSGTextEdit::SelectWords << 0 << 8 << false;
+ QTest::newRow("(!dlrow ,)olleH|words")
+ << standard[3] << 0 << 8 << QSGTextEdit::SelectWords << 0 << 8 << true;
+ QTest::newRow("<(!)>dlrow|words")
+ << standard[3] << 0 << 1 << QSGTextEdit::SelectWords << 0 << 1 << true;
+ QTest::newRow("<()>!dlrow|words")
+ << standard[3] << 0 << 0 << QSGTextEdit::SelectWords << 0 << 0 << true;
+ QTest::newRow("!<()>dlrow|words")
+ << standard[3] << 1 << 1 << QSGTextEdit::SelectWords << 1 << 1 << true;
+}
+
+void tst_qsgtextedit::moveCursorSelection()
+{
+ QFETCH(QString, testStr);
+ QFETCH(int, cursorPosition);
+ QFETCH(int, movePosition);
+ QFETCH(QSGTextEdit::SelectionMode, mode);
+ QFETCH(int, selectionStart);
+ QFETCH(int, selectionEnd);
+ QFETCH(bool, reversible);
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit*>(textinputComponent.create());
+ QVERIFY(texteditObject != 0);
+
+ texteditObject->setCursorPosition(cursorPosition);
+ texteditObject->moveCursorSelection(movePosition, mode);
+
+ QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
+ QCOMPARE(texteditObject->selectionStart(), selectionStart);
+ QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
+
+ if (reversible) {
+ texteditObject->setCursorPosition(movePosition);
+ texteditObject->moveCursorSelection(cursorPosition, mode);
+
+ QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
+ QCOMPARE(texteditObject->selectionStart(), selectionStart);
+ QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
+ }
+}
+
+void tst_qsgtextedit::moveCursorSelectionSequence_data()
+{
+ QTest::addColumn<QString>("testStr");
+ QTest::addColumn<int>("cursorPosition");
+ QTest::addColumn<int>("movePosition1");
+ QTest::addColumn<int>("movePosition2");
+ QTest::addColumn<int>("selection1Start");
+ QTest::addColumn<int>("selection1End");
+ QTest::addColumn<int>("selection2Start");
+ QTest::addColumn<int>("selection2End");
+
+ QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
+ << standard[0]
+ << 9 << 13 << 17
+ << 4 << 15
+ << 4 << 19;
+ QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
+ << standard[0]
+ << 13 << 9 << 17
+ << 9 << 15
+ << 10 << 19;
+ QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
+ << standard[0]
+ << 9 << 13 << 16
+ << 4 << 15
+ << 4 << 16;
+ QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
+ << standard[0]
+ << 13 << 9 << 16
+ << 9 << 15
+ << 10 << 16;
+ QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
+ << standard[0]
+ << 9 << 13 << 15
+ << 4 << 15
+ << 4 << 15;
+ QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
+ << standard[0]
+ << 13 << 9 << 15
+ << 9 << 15
+ << 10 << 15;
+ QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 10
+ << 4 << 15
+ << 4 << 10;
+ QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
+ << standard[0]
+ << 13 << 9 << 10
+ << 9 << 15
+ << 10 << 15;
+ QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 9
+ << 4 << 15
+ << 4 << 9;
+ QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
+ << standard[0]
+ << 13 << 9 << 9
+ << 9 << 15
+ << 9 << 15;
+ QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 7
+ << 4 << 15
+ << 4 << 9;
+ QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 7
+ << 9 << 15
+ << 4 << 15;
+ QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 4
+ << 4 << 15
+ << 4 << 9;
+ QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 4
+ << 9 << 15
+ << 4 << 15;
+ QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 3
+ << 4 << 15
+ << 3 << 9;
+ QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 3
+ << 9 << 15
+ << 3 << 15;
+ QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 1
+ << 4 << 15
+ << 0 << 9;
+ QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 1
+ << 9 << 15
+ << 0 << 15;
+
+ QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
+ << standard[2]
+ << 2 << 4 << 8
+ << 0 << 5
+ << 0 << 12;
+ QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
+ << standard[2]
+ << 4 << 2 << 8
+ << 0 << 5
+ << 0 << 12;
+
+ QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
+ << standard[3]
+ << 9 << 11 << 5
+ << 8 << 13
+ << 1 << 13;
+ QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
+ << standard[3]
+ << 11 << 9 << 5
+ << 8 << 13
+ << 1 << 13;
+}
+
+void tst_qsgtextedit::moveCursorSelectionSequence()
+{
+ QFETCH(QString, testStr);
+ QFETCH(int, cursorPosition);
+ QFETCH(int, movePosition1);
+ QFETCH(int, movePosition2);
+ QFETCH(int, selection1Start);
+ QFETCH(int, selection1End);
+ QFETCH(int, selection2Start);
+ QFETCH(int, selection2End);
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
+ QDeclarativeComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
+ QVERIFY(texteditObject != 0);
+
+ texteditObject->setCursorPosition(cursorPosition);
+
+ texteditObject->moveCursorSelection(movePosition1, QSGTextEdit::SelectWords);
+ QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
+ QCOMPARE(texteditObject->selectionStart(), selection1Start);
+ QCOMPARE(texteditObject->selectionEnd(), selection1End);
+
+ texteditObject->moveCursorSelection(movePosition2, QSGTextEdit::SelectWords);
+ QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
+ QCOMPARE(texteditObject->selectionStart(), selection2Start);
+ QCOMPARE(texteditObject->selectionEnd(), selection2End);
+}
+
+
+void tst_qsgtextedit::mouseSelection_data()
+{
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<bool>("expectSelection");
+
+ // import installed
+ QTest::newRow("on") << SRCDIR "/data/mouseselection_true.qml" << true;
+ QTest::newRow("off") << SRCDIR "/data/mouseselection_false.qml" << false;
+ QTest::newRow("default") << SRCDIR "/data/mouseselection_default.qml" << false;
+ QTest::newRow("on word selection") << SRCDIR "/data/mouseselection_true_words.qml" << true;
+ QTest::newRow("off word selection") << SRCDIR "/data/mouseselection_false_words.qml" << false;
+}
+
+void tst_qsgtextedit::mouseSelection()
+{
+ QFETCH(QString, qmlfile);
+ QFETCH(bool, expectSelection);
+
+ QSGView canvas(QUrl::fromLocalFile(qmlfile));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
+ QVERIFY(textEditObject != 0);
+
+ // press-and-drag-and-release from x1 to x2
+ int x1 = 10;
+ int x2 = 70;
+ int y = textEditObject->height()/2;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+ QString str = textEditObject->selectedText();
+ if (expectSelection)
+ QVERIFY(str.length() > 3); // don't reallly care *what* was selected (and it's too sensitive to platform)
+ else
+ QVERIFY(str.isEmpty());
+}
+
+void tst_qsgtextedit::dragMouseSelection()
+{
+ QString qmlfile = SRCDIR "/data/mouseselection_true.qml";
+
+ QSGView canvas(QUrl::fromLocalFile(qmlfile));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
+ QVERIFY(textEditObject != 0);
+
+ // press-and-drag-and-release from x1 to x2
+ int x1 = 10;
+ int x2 = 70;
+ int y = textEditObject->height()/2;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ }
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+ QString str1 = textEditObject->selectedText();
+ QVERIFY(str1.length() > 3);
+
+ // press and drag the current selection.
+ x1 = 40;
+ x2 = 100;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ }
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+ QString str2 = textEditObject->selectedText();
+ QVERIFY(str2.length() > 3);
+
+ QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
+}
+
+void tst_qsgtextedit::mouseSelectionMode_data()
+{
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<bool>("selectWords");
+
+ // import installed
+ QTest::newRow("SelectWords") << SRCDIR "/data/mouseselectionmode_words.qml" << true;
+ QTest::newRow("SelectCharacters") << SRCDIR "/data/mouseselectionmode_characters.qml" << false;
+ QTest::newRow("default") << SRCDIR "/data/mouseselectionmode_default.qml" << false;
+}
+
+void tst_qsgtextedit::mouseSelectionMode()
+{
+ QFETCH(QString, qmlfile);
+ QFETCH(bool, selectWords);
+
+ QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ QSGView canvas(QUrl::fromLocalFile(qmlfile));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
+ QVERIFY(textEditObject != 0);
+
+ // press-and-drag-and-release from x1 to x2
+ int x1 = 10;
+ int x2 = 70;
+ int y = textEditObject->height()/2;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+ QString str = textEditObject->selectedText();
+ if (selectWords) {
+ QCOMPARE(str, text);
+ } else {
+ QVERIFY(str.length() > 3);
+ QVERIFY(str != text);
+ }
+}
+
+void tst_qsgtextedit::inputMethodHints()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/inputmethodhints.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
+ QVERIFY(textEditObject != 0);
+ QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
+ textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
+ QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
+}
+
+void tst_qsgtextedit::positionAt()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
+ QVERIFY(canvas.rootObject() != 0);
+ canvas.show();
+ canvas.setFocus();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+
+ QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
+ QVERIFY(texteditObject != 0);
+
+ QFontMetrics fm(texteditObject->font());
+ const int y0 = fm.height() / 2;
+ const int y1 = fm.height() * 3 / 2;
+
+ int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
+ int diff = abs(int(fm.width(texteditObject->text().left(pos))-texteditObject->width()/2));
+
+ // some tollerance for different fonts.
+#ifdef Q_OS_LINUX
+ QVERIFY(diff < 2);
+#else
+ QVERIFY(diff < 5);
+#endif
+
+ const qreal x0 = texteditObject->positionToRectangle(pos).x();
+ const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
+
+ QString preeditText = texteditObject->text().mid(0, pos);
+ texteditObject->setText(texteditObject->text().mid(pos));
+ texteditObject->setCursorPosition(0);
+
+ QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
+ QApplication::sendEvent(&canvas, &inputEvent);
+
+ // Check all points within the preedit text return the same position.
+ QCOMPARE(texteditObject->positionAt(0, y0), 0);
+ QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
+ QCOMPARE(texteditObject->positionAt(x0, y0), 0);
+
+ // Verify positioning returns to normal after the preedit text.
+ QCOMPARE(texteditObject->positionAt(x1, y0), 1);
+ QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
+
+ QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
+}
+
+void tst_qsgtextedit::cursorDelegate()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorTest.qml"));
+ view.show();
+ view.setFocus();
+ QSGTextEdit *textEditObject = view.rootObject()->findChild<QSGTextEdit*>("textEditObject");
+ QVERIFY(textEditObject != 0);
+ QVERIFY(textEditObject->findChild<QSGItem*>("cursorInstance"));
+ //Test Delegate gets created
+ textEditObject->setFocus(true);
+ QSGItem* delegateObject = textEditObject->findChild<QSGItem*>("cursorInstance");
+ QVERIFY(delegateObject);
+ //Test Delegate gets moved
+ for(int i=0; i<= textEditObject->text().length(); i++){
+ textEditObject->setCursorPosition(i);
+ QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
+ QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
+ }
+ textEditObject->setCursorPosition(0);
+ QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
+ QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
+ //Test Delegate gets deleted
+ textEditObject->setCursorDelegate(0);
+ QVERIFY(!textEditObject->findChild<QSGItem*>("cursorInstance"));
+}
+
+void tst_qsgtextedit::cursorVisible()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorVisible.qml"));
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ view.setFocus();
+
+ QSGTextEdit edit;
+ QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
+
+ QCOMPARE(edit.isCursorVisible(), false);
+
+ edit.setCursorVisible(true);
+ QCOMPARE(edit.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 1);
+
+ edit.setCursorVisible(false);
+ QCOMPARE(edit.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 2);
+
+ edit.setFocus(true);
+ QCOMPARE(edit.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 2);
+
+ edit.setParentItem(view.rootObject());
+ QCOMPARE(edit.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 3);
+
+ edit.setFocus(false);
+ QCOMPARE(edit.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 4);
+
+ edit.setFocus(true);
+ QCOMPARE(edit.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 5);
+
+ view.clearFocus();
+ QCOMPARE(edit.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 6);
+
+ view.setFocus();
+ QCOMPARE(edit.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 7);
+
+ // on mac, setActiveWindow(0) on mac does not deactivate the current application
+ // (you have to switch to a different app or hide the current app to trigger this)
+#if !defined(Q_WS_MAC)
+ QApplication::setActiveWindow(0);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(0));
+ QCOMPARE(edit.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 8);
+
+ QApplication::setActiveWindow(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QCOMPARE(edit.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 9);
+#endif
+}
+
+void tst_qsgtextedit::delegateLoading_data()
+{
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<QString>("error");
+
+ // import installed
+ QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
+ QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
+ QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
+}
+
+void tst_qsgtextedit::delegateLoading()
+{
+ QFETCH(QString, qmlfile);
+ QFETCH(QString, error);
+
+ TestHTTPServer server(42332);
+ server.serveDirectory(SRCDIR "/data/httpfail", TestHTTPServer::Disconnect);
+ server.serveDirectory(SRCDIR "/data/httpslow", TestHTTPServer::Delay);
+ server.serveDirectory(SRCDIR "/data/http");
+
+ QSGView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile));
+ view.show();
+ view.setFocus();
+
+ if (!error.isEmpty()) {
+ QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
+ QTRY_VERIFY(view.status()==QSGView::Error);
+ QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
+ } else {
+ QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
+ QSGTextEdit *textEditObject = view.rootObject()->findChild<QSGTextEdit*>("textEditObject");
+ // view.rootObject()->dumpObjectTree();
+ QVERIFY(textEditObject != 0);
+ textEditObject->setFocus(true);
+ QSGItem *delegate;
+ delegate = view.rootObject()->findChild<QSGItem*>("delegateOkay");
+ QVERIFY(delegate);
+ delegate = view.rootObject()->findChild<QSGItem*>("delegateSlow");
+ QVERIFY(delegate);
+
+ delete delegate;
+ }
+
+
+ //A test should be added here with a component which is ready but component.create() returns null
+ //Not sure how to accomplish this with QSGTextEdits cursor delegate
+ //###This was only needed for code coverage, and could be a case of overzealous defensive programming
+ //delegate = view.rootObject()->findChild<QSGItem*>("delegateErrorB");
+ //QVERIFY(!delegate);
+}
+
+/*
+TextEdit element should only handle left/right keys until the cursor reaches
+the extent of the text, then they should ignore the keys.
+*/
+void tst_qsgtextedit::navigation()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGItem *input = qobject_cast<QSGItem *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(input != 0);
+ QTRY_VERIFY(input->hasActiveFocus() == true);
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == false);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == true);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == true);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == false);
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == true);
+}
+
+void tst_qsgtextedit::copyAndPaste() {
+#ifndef QT_NO_CLIPBOARD
+
+#ifdef Q_WS_MAC
+ {
+ PasteboardRef pasteboard;
+ OSStatus status = PasteboardCreate(0, &pasteboard);
+ if (status == noErr)
+ CFRelease(pasteboard);
+ else
+ QSKIP("This machine doesn't support the clipboard", SkipAll);
+ }
+#endif
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
+ QDeclarativeComponent textEditComponent(&engine);
+ textEditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
+ QVERIFY(textEdit != 0);
+
+ // copy and paste
+ QCOMPARE(textEdit->text().length(), 12);
+ textEdit->select(0, textEdit->text().length());;
+ textEdit->copy();
+ QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
+ QCOMPARE(textEdit->selectedText().length(), 12);
+ textEdit->setCursorPosition(0);
+ QVERIFY(textEdit->canPaste());
+ textEdit->paste();
+ QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
+ QCOMPARE(textEdit->text().length(), 24);
+
+ // canPaste
+ QVERIFY(textEdit->canPaste());
+ textEdit->setReadOnly(true);
+ QVERIFY(!textEdit->canPaste());
+ textEdit->setReadOnly(false);
+ QVERIFY(textEdit->canPaste());
+
+ // QTBUG-12339
+ // test that document and internal text attribute are in sync
+ QSGItemPrivate* pri = QSGItemPrivate::get(textEdit);
+ QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(pri);
+ QCOMPARE(textEdit->text(), editPrivate->text);
+
+ // select word
+ textEdit->setCursorPosition(0);
+ textEdit->selectWord();
+ QCOMPARE(textEdit->selectedText(), QString("Hello"));
+
+ // select all and cut
+ textEdit->selectAll();
+ textEdit->cut();
+ QCOMPARE(textEdit->text().length(), 0);
+ textEdit->paste();
+ QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
+ QCOMPARE(textEdit->text().length(), 24);
+#endif
+}
+
+void tst_qsgtextedit::canPaste() {
+#ifndef QT_NO_CLIPBOARD
+
+ QApplication::clipboard()->setText("Some text");
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
+ QDeclarativeComponent textEditComponent(&engine);
+ textEditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
+ QVERIFY(textEdit != 0);
+
+ // check initial value - QTBUG-17765
+ QTextControl tc;
+ QCOMPARE(textEdit->canPaste(), tc.canPaste());
+
+#endif
+}
+
+void tst_qsgtextedit::canPasteEmpty() {
+#ifndef QT_NO_CLIPBOARD
+
+ QApplication::clipboard()->clear();
+
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
+ QDeclarativeComponent textEditComponent(&engine);
+ textEditComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
+ QVERIFY(textEdit != 0);
+
+ // check initial value - QTBUG-17765
+ QTextControl tc;
+ QCOMPARE(textEdit->canPaste(), tc.canPaste());
+
+#endif
+}
+
+void tst_qsgtextedit::readOnly()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/readOnly.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(edit != 0);
+ QTRY_VERIFY(edit->hasActiveFocus() == true);
+ QVERIFY(edit->isReadOnly() == true);
+ QString initial = edit->text();
+ for(int k=Qt::Key_0; k<=Qt::Key_Z; k++)
+ simulateKey(&canvas, k);
+ simulateKey(&canvas, Qt::Key_Return);
+ simulateKey(&canvas, Qt::Key_Space);
+ simulateKey(&canvas, Qt::Key_Escape);
+ QCOMPARE(edit->text(), initial);
+}
+
+void tst_qsgtextedit::simulateKey(QSGView *view, int key, Qt::KeyboardModifiers modifiers)
+{
+ QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
+ QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
+
+ QApplication::sendEvent(view, &press);
+ QApplication::sendEvent(view, &release);
+}
+
+class MyInputContext : public QInputContext
+{
+public:
+ MyInputContext() : openInputPanelReceived(false), closeInputPanelReceived(false), updateReceived(false), eventType(QEvent::None) {}
+ ~MyInputContext() {}
+
+ QString identifierName() { return QString(); }
+ QString language() { return QString(); }
+
+ void reset() {}
+
+ bool isComposing() const { return false; }
+
+ bool filterEvent( const QEvent *event )
+ {
+ if (event->type() == QEvent::RequestSoftwareInputPanel)
+ openInputPanelReceived = true;
+ if (event->type() == QEvent::CloseSoftwareInputPanel)
+ closeInputPanelReceived = true;
+ return QInputContext::filterEvent(event);
+ }
+
+ void update() { updateReceived = true; }
+
+ void sendPreeditText(const QString &text, int cursor)
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(QInputMethodEvent::Attribute(
+ QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
+
+ QInputMethodEvent event(text, attributes);
+ sendEvent(event);
+ }
+
+ void mouseHandler(int x, QMouseEvent *event)
+ {
+ cursor = x;
+ eventType = event->type();
+ eventPosition = event->pos();
+ eventGlobalPosition = event->globalPos();
+ eventButton = event->button();
+ eventButtons = event->buttons();
+ eventModifiers = event->modifiers();
+ }
+
+ bool openInputPanelReceived;
+ bool closeInputPanelReceived;
+ bool updateReceived;
+ int cursor;
+ QEvent::Type eventType;
+ QPoint eventPosition;
+ QPoint eventGlobalPosition;
+ Qt::MouseButton eventButton;
+ Qt::MouseButtons eventButtons;
+ Qt::KeyboardModifiers eventModifiers;
+};
+
+void tst_qsgtextedit::textInput()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputMethodEvent.qml"));
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
+ QVERIFY(edit);
+ QVERIFY(edit->hasActiveFocus() == true);
+
+ // test that input method event is committed
+ QInputMethodEvent event;
+ event.setCommitString( "Hello world!", 0, 0);
+ QApplication::sendEvent(&view, &event);
+ QCOMPARE(edit->text(), QString("Hello world!"));
+
+ // QTBUG-12339
+ // test that document and internal text attribute are in sync
+ QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(QSGItemPrivate::get(edit));
+ QCOMPARE(editPrivate->text, QString("Hello world!"));
+}
+
+void tst_qsgtextedit::openInputPanelOnClick()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
+ MyInputContext ic;
+ // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
+ // and QWidget won't allow an input context to be set when the flag is not set.
+ view.setAttribute(Qt::WA_InputMethodEnabled, true);
+ view.setInputContext(&ic);
+ view.setAttribute(Qt::WA_InputMethodEnabled, false);
+ view.show();
+
+ qApp->setAutoSipEnabled(true);
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
+ QVERIFY(edit);
+ QSignalSpy focusOnPressSpy(edit, SIGNAL(activeFocusOnPressChanged(bool)));
+
+ QSGItemPrivate* pri = QSGItemPrivate::get(edit);
+ QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(pri);
+
+ // input panel on click
+ editPrivate->showInputPanelOnFocus = false;
+
+ QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
+ view.style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
+ QTest::mouseClick(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ if (behavior == QStyle::RSIP_OnMouseClickAndAlreadyFocused) {
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QTest::mouseClick(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ } else if (behavior == QStyle::RSIP_OnMouseClick) {
+ QCOMPARE(ic.openInputPanelReceived, true);
+ }
+ ic.openInputPanelReceived = false;
+
+ // focus should not cause input panels to open or close
+ edit->setFocus(false);
+ edit->setFocus(true);
+ edit->setFocus(false);
+ edit->setFocus(true);
+ edit->setFocus(false);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+}
+
+void tst_qsgtextedit::openInputPanelOnFocus()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
+ MyInputContext ic;
+ // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
+ // and QWidget won't allow an input context to be set when the flag is not set.
+ view.setAttribute(Qt::WA_InputMethodEnabled, true);
+ view.setInputContext(&ic);
+ view.setAttribute(Qt::WA_InputMethodEnabled, false);
+ view.show();
+
+ qApp->setAutoSipEnabled(true);
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
+ QVERIFY(edit);
+ QSignalSpy focusOnPressSpy(edit, SIGNAL(activeFocusOnPressChanged(bool)));
+
+ QSGItemPrivate* pri = QSGItemPrivate::get(edit);
+ QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(pri);
+ editPrivate->showInputPanelOnFocus = true;
+
+ // test default values
+ QVERIFY(edit->focusOnPress());
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+
+ // focus on press, input panel on focus
+ QTest::mousePress(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ QVERIFY(edit->hasActiveFocus());
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+
+ // no events on release
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QCOMPARE(ic.openInputPanelReceived, false);
+ ic.openInputPanelReceived = false;
+
+ // if already focused, input panel can be opened on press
+ QVERIFY(edit->hasActiveFocus());
+ QTest::mousePress(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+
+ // input method should stay enabled if focus
+ // is lost to an item that also accepts inputs
+ QSGTextEdit anotherEdit;
+ anotherEdit.setParentItem(view.rootObject());
+ anotherEdit.setFocus(true);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+ QCOMPARE(view.inputContext(), (QInputContext*)&ic);
+ QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // input method should be disabled if focus
+ // is lost to an item that doesn't accept inputs
+ QSGItem item;
+ item.setParentItem(view.rootObject());
+ item.setFocus(true);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QVERIFY(view.inputContext() == 0);
+ QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // no automatic input panel events should
+ // be sent if activeFocusOnPress is false
+ edit->setFocusOnPress(false);
+ QCOMPARE(focusOnPressSpy.count(),1);
+ edit->setFocusOnPress(false);
+ QCOMPARE(focusOnPressSpy.count(),1);
+ edit->setFocus(false);
+ edit->setFocus(true);
+ QTest::mousePress(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, edit->mapToScene(QPointF(0,0)).toPoint());
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+
+ // one show input panel event should
+ // be set when openSoftwareInputPanel is called
+ edit->openSoftwareInputPanel();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+ ic.openInputPanelReceived = false;
+
+ // one close input panel event should
+ // be sent when closeSoftwareInputPanel is called
+ edit->closeSoftwareInputPanel();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, true);
+ ic.closeInputPanelReceived = false;
+
+ // set activeFocusOnPress back to true
+ edit->setFocusOnPress(true);
+ QCOMPARE(focusOnPressSpy.count(),2);
+ edit->setFocusOnPress(true);
+ QCOMPARE(focusOnPressSpy.count(),2);
+ edit->setFocus(false);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+ ic.closeInputPanelReceived = false;
+
+ // input panel should not re-open
+ // if focus has already been set
+ edit->setFocus(true);
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+ edit->setFocus(true);
+ QCOMPARE(ic.openInputPanelReceived, false);
+
+ // input method should be disabled
+ // if TextEdit loses focus
+ edit->setFocus(false);
+ QApplication::processEvents();
+ QVERIFY(view.inputContext() == 0);
+ QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // input method should not be enabled
+ // if TextEdit is read only.
+ edit->setReadOnly(true);
+ ic.openInputPanelReceived = false;
+ edit->setFocus(true);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QVERIFY(view.inputContext() == 0);
+ QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
+}
+
+void tst_qsgtextedit::geometrySignals()
+{
+ QDeclarativeComponent component(&engine, SRCDIR "/data/geometrySignals.qml");
+ QObject *o = component.create();
+ QVERIFY(o);
+ QCOMPARE(o->property("bindingWidth").toInt(), 400);
+ QCOMPARE(o->property("bindingHeight").toInt(), 500);
+ delete o;
+}
+
+void tst_qsgtextedit::pastingRichText_QTBUG_14003()
+{
+#ifndef QT_NO_CLIPBOARD
+ QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
+ QDeclarativeComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGTextEdit *obj = qobject_cast<QSGTextEdit*>(component.create());
+
+ QTRY_VERIFY(obj != 0);
+ QTRY_VERIFY(obj->textFormat() == QSGTextEdit::PlainText);
+
+ QMimeData *mData = new QMimeData;
+ mData->setHtml("<font color=\"red\">Hello</font>");
+ QApplication::clipboard()->setMimeData(mData);
+
+ obj->paste();
+ QTRY_VERIFY(obj->text() == "");
+ QTRY_VERIFY(obj->textFormat() == QSGTextEdit::PlainText);
+#endif
+}
+
+void tst_qsgtextedit::implicitSize_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<QString>("wrap");
+ QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap";
+ QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap";
+ QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap";
+ QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap";
+}
+
+void tst_qsgtextedit::implicitSize()
+{
+ QFETCH(QString, text);
+ QFETCH(QString, wrap);
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
+
+ QVERIFY(textObject->width() < textObject->implicitWidth());
+ QVERIFY(textObject->height() == textObject->implicitHeight());
+
+ textObject->resetWidth();
+ QVERIFY(textObject->width() == textObject->implicitWidth());
+ QVERIFY(textObject->height() == textObject->implicitHeight());
+}
+
+void tst_qsgtextedit::testQtQuick11Attributes()
+{
+ QFETCH(QString, code);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ QDeclarativeEngine engine;
+ QObject *obj;
+
+ QDeclarativeComponent valid(&engine);
+ valid.setData("import QtQuick 2.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
+ obj = valid.create();
+ QVERIFY(obj);
+ QVERIFY(valid.errorString().isEmpty());
+ delete obj;
+
+ QDeclarativeComponent invalid(&engine);
+ invalid.setData("import QtQuick 1.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
+ obj = invalid.create();
+ QCOMPARE(invalid.errorString(), error);
+ delete obj;
+}
+
+void tst_qsgtextedit::testQtQuick11Attributes_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("canPaste") << "property bool foo: canPaste"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
+ << "";
+
+ QTest::newRow("lineCount") << "property int foo: lineCount"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: lineCount"
+ << "";
+
+ QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
+ << "";
+
+ QTest::newRow("deselect") << "Component.onCompleted: deselect()"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
+ << "";
+
+ QTest::newRow("onLinkActivated") << "onLinkActivated: {}"
+ << "QDeclarativeComponent: Component is not ready"
+ << ":1 \"TextEdit.onLinkActivated\" is not available in QtQuick 1.0.\n";
+}
+
+void tst_qsgtextedit::preeditMicroFocus()
+{
+ QString preeditText = "super";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputMethodEvent.qml"));
+ MyInputContext ic;
+ view.setInputContext(&ic);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
+ QVERIFY(edit);
+
+ QRect currentRect;
+ QRect previousRect = edit->inputMethodQuery(Qt::ImMicroFocus).toRect();
+
+ // Verify that the micro focus rect is positioned the same for position 0 as
+ // it would be if there was no preedit text.
+ ic.updateReceived = false;
+ ic.sendPreeditText(preeditText, 0);
+ currentRect = edit->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QCOMPARE(currentRect, previousRect);
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
+ QCOMPARE(ic.updateReceived, true);
+#endif
+
+ // Verify that the micro focus rect moves to the left as the cursor position
+ // is incremented.
+ for (int i = 1; i <= 5; ++i) {
+ ic.updateReceived = false;
+ ic.sendPreeditText(preeditText, i);
+ currentRect = edit->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QVERIFY(previousRect.left() < currentRect.left());
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
+ QCOMPARE(ic.updateReceived, true);
+#endif
+ previousRect = currentRect;
+ }
+
+ // Verify that if there is no preedit cursor then the micro focus rect is the
+ // same as it would be if it were positioned at the end of the preedit text.
+ ic.sendPreeditText(preeditText, 0);
+ ic.updateReceived = false;
+ ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
+ currentRect = edit->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QCOMPARE(currentRect, previousRect);
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
+ QCOMPARE(ic.updateReceived, true);
+#endif
+}
+
+void tst_qsgtextedit::inputContextMouseHandler()
+{
+ QString text = "supercalifragisiticexpialidocious!";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
+ MyInputContext ic;
+ view.setInputContext(&ic);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
+ QVERIFY(edit);
+ edit->setCursorPosition(12);
+
+ QFontMetricsF fm(edit->font());
+ const qreal y = fm.height() / 2;
+
+ QPoint position2 = edit->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
+ QPoint position8 = edit->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
+ QPoint position20 = edit->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
+ QPoint position27 = edit->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
+ QPoint globalPosition2 = view.mapToGlobal(position2);
+ QPoint globalposition8 = view.mapToGlobal(position8);
+ QPoint globalposition20 = view.mapToGlobal(position20);
+ QPoint globalposition27 = view.mapToGlobal(position27);
+
+ ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
+
+ QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
+ QCOMPARE(ic.eventPosition, position2);
+ QCOMPARE(ic.eventGlobalPosition, globalPosition2);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor < 0);
+ ic.eventType = QEvent::None;
+
+ QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
+ QCOMPARE(ic.eventPosition, position2);
+ QCOMPARE(ic.eventGlobalPosition, globalPosition2);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor < 0);
+ ic.eventType = QEvent::None;
+
+ { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::None);
+
+ { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::MouseMove);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
+ ic.eventType = QEvent::None;
+
+ QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
+ ic.eventType = QEvent::None;
+
+ // And in the other direction.
+ QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
+ ic.eventType = QEvent::None;
+
+ QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::RightButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
+ ic.eventType = QEvent::None;
+
+ { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::MouseMove);
+ QCOMPARE(ic.eventPosition, position20);
+ QCOMPARE(ic.eventGlobalPosition, globalposition20);
+ QCOMPARE(ic.eventButton, Qt::RightButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
+ ic.eventType = QEvent::None;
+
+ { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::None);
+
+ QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
+ QCOMPARE(ic.eventPosition, position2);
+ QCOMPARE(ic.eventGlobalPosition, globalPosition2);
+ QCOMPARE(ic.eventButton, Qt::RightButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor < 0);
+ ic.eventType = QEvent::None;
+}
+
+void tst_qsgtextedit::inputMethodComposing()
+{
+ QString text = "supercalifragisiticexpialidocious!";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
+ QVERIFY(edit);
+ QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
+ edit->setCursorPosition(12);
+
+ QCOMPARE(edit->isInputMethodComposing(), false);
+
+ {
+ QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
+ QApplication::sendEvent(&view, &event);
+ }
+ QCOMPARE(edit->isInputMethodComposing(), true);
+ QCOMPARE(spy.count(), 1);
+
+ {
+ QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
+ QApplication::sendEvent(&view, &event);
+ }
+ QCOMPARE(spy.count(), 1);
+
+ {
+ QInputMethodEvent event;
+ QApplication::sendEvent(&view, &event);
+ }
+ QCOMPARE(edit->isInputMethodComposing(), false);
+ QCOMPARE(spy.count(), 2);
+}
+
+void tst_qsgtextedit::cursorRectangleSize()
+{
+ QSGView *canvas = new QSGView(QUrl::fromLocalFile(SRCDIR "/data/CursorRect.qml"));
+ QVERIFY(canvas->rootObject() != 0);
+ canvas->show();
+ canvas->setFocus();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+
+ QSGTextEdit *textEdit = qobject_cast<QSGTextEdit *>(canvas->rootObject());
+ QVERIFY(textEdit != 0);
+ textEdit->setFocus(Qt::OtherFocusReason);
+ QRectF cursorRect = textEdit->positionToRectangle(textEdit->cursorPosition());
+ QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImMicroFocus).toRectF();
+ QRectF microFocusFromApp= QApplication::focusWidget()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
+
+ QCOMPARE(microFocusFromScene.size(), cursorRect.size());
+ QCOMPARE(microFocusFromApp.size(), cursorRect.size());
+
+ delete canvas;
+}
+
+QTEST_MAIN(tst_qsgtextedit)
+
+#include "tst_qsgtextedit.moc"
diff --git a/tests/auto/declarative/qsgtextinput/data/cursorTest.qml b/tests/auto/declarative/qsgtextinput/data/cursorTest.qml
new file mode 100644
index 0000000000..01858fba77
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/cursorTest.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Rectangle { width: 300; height: 300; color: "white"
+ TextInput { text: "Hello world!"; id: textInputObject; objectName: "textInputObject"
+ resources: [ Component { id:cursor; Item { id:cursorInstance; objectName: "cursorInstance";} } ]
+ cursorDelegate: cursor
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/cursorVisible.qml b/tests/auto/declarative/qsgtextinput/data/cursorVisible.qml
new file mode 100644
index 0000000000..49e9386947
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/cursorVisible.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+ width: 100
+ height: 20
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/echoMode.qml b/tests/auto/declarative/qsgtextinput/data/echoMode.qml
new file mode 100644
index 0000000000..f8a6cf1c89
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/echoMode.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Rectangle {
+ property QtObject myInput: input
+
+ width: 400; height: 200; color: "green"
+
+ TextInput { id: input; focus: true
+ text: "ABCDefgh"
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/geometrySignals.qml b/tests/auto/declarative/qsgtextinput/data/geometrySignals.qml
new file mode 100644
index 0000000000..90855a61cf
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/geometrySignals.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Item {
+ width: 400; height: 500;
+ property int bindingWidth: text.width
+ property int bindingHeight: text.height
+
+ TextEdit {
+ id: text
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/halign_center.png b/tests/auto/declarative/qsgtextinput/data/halign_center.png
new file mode 100644
index 0000000000..53e09a8e5b
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/halign_center.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextinput/data/halign_left.png b/tests/auto/declarative/qsgtextinput/data/halign_left.png
new file mode 100644
index 0000000000..247acbc9df
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/halign_left.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextinput/data/halign_right.png b/tests/auto/declarative/qsgtextinput/data/halign_right.png
new file mode 100644
index 0000000000..691bc75c89
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/halign_right.png
Binary files differ
diff --git a/tests/auto/declarative/qsgtextinput/data/horizontalAlignment.qml b/tests/auto/declarative/qsgtextinput/data/horizontalAlignment.qml
new file mode 100644
index 0000000000..e0fef4c11e
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/horizontalAlignment.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: top
+ width: 70; height: 70;
+
+ property alias horizontalAlignment: text.horizontalAlignment
+ property string text: "Test"
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: 60
+ height: 20
+ color: "green"
+
+ TextInput {
+ id: text
+ anchors.fill: parent
+ text: top.text
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml b/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml
new file mode 100644
index 0000000000..15fbabe28c
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: top
+ width: 200; height: 70;
+
+ property alias horizontalAlignment: text.horizontalAlignment
+ property string text: "اختبا"
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: 180
+ height: 20
+ color: "green"
+
+ TextInput {
+ id: text
+ objectName: "text"
+ anchors.fill: parent
+ text: top.text
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/inputContext.qml b/tests/auto/declarative/qsgtextinput/data/inputContext.qml
new file mode 100644
index 0000000000..dfc80990c6
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/inputContext.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextInput {
+ width: 200
+ text: "supercalifra"
+ focus: true
+ cursorPosition: 12
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/inputMethodEvent.qml b/tests/auto/declarative/qsgtextinput/data/inputMethodEvent.qml
new file mode 100644
index 0000000000..f8446ab7b9
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/inputMethodEvent.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+TextInput {
+ focus: true
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/inputmethods.qml b/tests/auto/declarative/qsgtextinput/data/inputmethods.qml
new file mode 100644
index 0000000000..711e89144c
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/inputmethods.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextInput {
+ text: "Hello world!"
+ inputMethodHints: Qt.ImhNoPredictiveText
+ Keys.onLeftPressed: {}
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/masks.qml b/tests/auto/declarative/qsgtextinput/data/masks.qml
new file mode 100644
index 0000000000..589b6a3c15
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/masks.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextInput{
+ focus: true
+ objectName: "myInput"
+ inputMask: "HHHHhhhh; "
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/maxLength.qml b/tests/auto/declarative/qsgtextinput/data/maxLength.qml
new file mode 100644
index 0000000000..cca537ed6b
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/maxLength.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextInput{
+ focus: true
+ objectName: "myInput"
+ maximumLength: 10
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/mouseselection_true.qml b/tests/auto/declarative/qsgtextinput/data/mouseselection_true.qml
new file mode 100644
index 0000000000..974041b04a
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/mouseselection_true.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextInput {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_characters.qml b/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_characters.qml
new file mode 100644
index 0000000000..f7c658b618
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_characters.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextInput {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+ mouseSelectionMode: TextInput.SelectCharacters
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_default.qml b/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_default.qml
new file mode 100644
index 0000000000..974041b04a
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_default.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextInput {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_words.qml b/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_words.qml
new file mode 100644
index 0000000000..20e777e470
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/mouseselectionmode_words.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextInput {
+ focus: true
+ text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ selectByMouse: true
+ mouseSelectionMode: TextInput.SelectWords
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/navigation.qml b/tests/auto/declarative/qsgtextinput/data/navigation.qml
new file mode 100644
index 0000000000..3a7d07b3c7
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/navigation.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Rectangle {
+ property variant myInput: input
+
+ width: 800; height: 600; color: "blue"
+
+ Item {
+ id: firstItem;
+ KeyNavigation.right: input
+ }
+
+ TextInput { id: input; focus: true
+ text: "Needs some text"
+ KeyNavigation.left: firstItem
+ KeyNavigation.right: lastItem
+ KeyNavigation.up: firstItem
+ KeyNavigation.down: lastItem
+ }
+ Item {
+ id: lastItem
+ KeyNavigation.left: input
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/openInputPanel.qml b/tests/auto/declarative/qsgtextinput/data/openInputPanel.qml
new file mode 100644
index 0000000000..3924b2ab99
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/openInputPanel.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+TextInput {
+ text: "Hello world"
+ focus: false
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/positionAt.qml b/tests/auto/declarative/qsgtextinput/data/positionAt.qml
new file mode 100644
index 0000000000..7611376e13
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/positionAt.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+TextInput{
+ focus: true
+ objectName: "myInput"
+ width: 50
+ text: "This is a long piece of text"
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/preeditAutoScroll.qml b/tests/auto/declarative/qsgtextinput/data/preeditAutoScroll.qml
new file mode 100644
index 0000000000..9d98a2e220
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/preeditAutoScroll.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextInput {
+ focus: true
+ text: "super"
+ autoScroll: true
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/readOnly.qml b/tests/auto/declarative/qsgtextinput/data/readOnly.qml
new file mode 100644
index 0000000000..9cda7fbd1d
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/readOnly.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Rectangle {
+ property variant myInput: input
+
+ width: 800; height: 600; color: "blue"
+
+ TextInput { id: input; focus: true
+ readOnly: true
+ text: "I am the very model of a modern major general.\n"
+ }
+}
diff --git a/tests/auto/declarative/qsgtextinput/data/validators.qml b/tests/auto/declarative/qsgtextinput/data/validators.qml
new file mode 100644
index 0000000000..0a074ce7dc
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/data/validators.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Item {
+ property variant intInput: intInput
+ property variant dblInput: dblInput
+ property variant strInput: strInput
+
+ width: 800; height: 600;
+
+ Column{
+ TextInput { id: intInput;
+ validator: IntValidator{top: 11; bottom: 2}
+ }
+ TextInput { id: dblInput;
+ validator: DoubleValidator{top: 12.12; bottom: 2.93; decimals: 2; notation: DoubleValidator.StandardNotation}
+ }
+ TextInput { id: strInput;
+ validator: RegExpValidator { regExp: /[a-zA-z]{2,4}/ }
+ }
+ }
+
+}
diff --git a/tests/auto/declarative/qsgtextinput/qsgtextinput.pro b/tests/auto/declarative/qsgtextinput/qsgtextinput.pro
new file mode 100644
index 0000000000..599c2a8e43
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/qsgtextinput.pro
@@ -0,0 +1,14 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgtextinput.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
diff --git a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp
new file mode 100644
index 0000000000..b349de9800
--- /dev/null
+++ b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp
@@ -0,0 +1,2471 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtTest/QSignalSpy>
+#include "../../../shared/util.h"
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QFile>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsgtextinput_p.h>
+#include <private/qsgtextinput_p_p.h>
+#include <QDebug>
+#include <QDir>
+#include <QStyle>
+#include <QInputContext>
+#include <private/qapplication_p.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+Q_DECLARE_METATYPE(QSGTextInput::SelectionMode)
+
+QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
+{
+ // XXX This will be replaced by some clever persistent platform image store.
+ QString persistent_dir = SRCDIR "/data";
+ QString arch = "unknown-architecture"; // QTest needs to help with this.
+
+ QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
+
+ if (!QFile::exists(expectfile)) {
+ actual.save(expectfile);
+ qWarning() << "created" << expectfile;
+ }
+
+ return expectfile;
+}
+
+class tst_qsgtextinput : public QObject
+
+{
+ Q_OBJECT
+public:
+ tst_qsgtextinput();
+
+private slots:
+
+ void text();
+ void width();
+ void font();
+ void color();
+ void selection();
+ void isRightToLeft_data();
+ void isRightToLeft();
+ void moveCursorSelection_data();
+ void moveCursorSelection();
+ void moveCursorSelectionSequence_data();
+ void moveCursorSelectionSequence();
+ void dragMouseSelection();
+ void mouseSelectionMode_data();
+ void mouseSelectionMode();
+
+ void horizontalAlignment_data();
+ void horizontalAlignment();
+ void horizontalAlignment_RightToLeft();
+
+ void positionAt();
+
+ void maxLength();
+ void masks();
+ void validators();
+ void inputMethods();
+
+ void passwordCharacter();
+ void cursorDelegate();
+ void cursorVisible();
+ void cursorRectangle();
+ void navigation();
+ void navigation_RTL();
+ void copyAndPaste();
+ void canPasteEmpty();
+ void canPaste();
+ void readOnly();
+
+ void openInputPanelOnClick();
+ void openInputPanelOnFocus();
+ void setHAlignClearCache();
+ void focusOutClearSelection();
+
+ void echoMode();
+ void geometrySignals();
+ void testQtQuick11Attributes();
+ void testQtQuick11Attributes_data();
+
+ void preeditAutoScroll();
+ void preeditMicroFocus();
+ void inputContextMouseHandler();
+ void inputMethodComposing();
+ void cursorRectangleSize();
+
+private:
+ void simulateKey(QSGView *, int key);
+
+ QDeclarativeEngine engine;
+ QStringList standard;
+ QStringList colorStrings;
+};
+
+tst_qsgtextinput::tst_qsgtextinput()
+{
+ standard << "the quick brown fox jumped over the lazy dog"
+ << "It's supercalifragisiticexpialidocious!"
+ << "Hello, world!"
+ << "!dlrow ,olleH"
+ << " spacey text ";
+
+ colorStrings << "aliceblue"
+ << "antiquewhite"
+ << "aqua"
+ << "darkkhaki"
+ << "darkolivegreen"
+ << "dimgray"
+ << "palevioletred"
+ << "lightsteelblue"
+ << "#000000"
+ << "#AAAAAA"
+ << "#FFFFFF"
+ << "#2AC05F";
+}
+
+void tst_qsgtextinput::text()
+{
+ {
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->text(), QString(""));
+
+ delete textinputObject;
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->text(), standard.at(i));
+
+ delete textinputObject;
+ }
+
+}
+
+void tst_qsgtextinput::width()
+{
+ // uses Font metrics to find the width for standard
+ {
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->width(), 0.0);
+
+ delete textinputObject;
+ }
+
+ for (int i = 0; i < standard.size(); i++)
+ {
+ QFont f;
+ QFontMetricsF fm(f);
+ qreal metricWidth = fm.width(standard.at(i));
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ int delta = abs(int(int(textinputObject->width()) - metricWidth));
+ QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
+
+ delete textinputObject;
+ }
+}
+
+void tst_qsgtextinput::font()
+{
+ //test size, then bold, then italic, then family
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->font().pointSize(), 40);
+ QCOMPARE(textinputObject->font().bold(), false);
+ QCOMPARE(textinputObject->font().italic(), false);
+
+ delete textinputObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->font().bold(), true);
+ QCOMPARE(textinputObject->font().italic(), false);
+
+ delete textinputObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->font().italic(), true);
+ QCOMPARE(textinputObject->font().bold(), false);
+
+ delete textinputObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
+ QCOMPARE(textinputObject->font().bold(), false);
+ QCOMPARE(textinputObject->font().italic(), false);
+
+ delete textinputObject;
+ }
+
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->font().family(), QString(""));
+
+ delete textinputObject;
+ }
+}
+
+void tst_qsgtextinput::color()
+{
+ //test color
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
+
+ delete textinputObject;
+ }
+
+ //test selection color
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
+
+ delete textinputObject;
+ }
+
+ //test selected text color
+ for (int i = 0; i < colorStrings.size(); i++)
+ {
+ QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
+
+ delete textinputObject;
+ }
+
+ {
+ QString colorStr = "#AA001234";
+ QColor testColor("#001234");
+ testColor.setAlpha(170);
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+
+ QVERIFY(textinputObject != 0);
+ QCOMPARE(textinputObject->color(), testColor);
+
+ delete textinputObject;
+ }
+}
+
+void tst_qsgtextinput::selection()
+{
+ QString testStr = standard[0];
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+ QVERIFY(textinputObject != 0);
+
+
+ //Test selection follows cursor
+ for(int i=0; i<= testStr.size(); i++) {
+ textinputObject->setCursorPosition(i);
+ QCOMPARE(textinputObject->cursorPosition(), i);
+ QCOMPARE(textinputObject->selectionStart(), i);
+ QCOMPARE(textinputObject->selectionEnd(), i);
+ QVERIFY(textinputObject->selectedText().isNull());
+ }
+
+ textinputObject->setCursorPosition(0);
+ QVERIFY(textinputObject->cursorPosition() == 0);
+ QVERIFY(textinputObject->selectionStart() == 0);
+ QVERIFY(textinputObject->selectionEnd() == 0);
+ QVERIFY(textinputObject->selectedText().isNull());
+
+ // Verify invalid positions are ignored.
+ textinputObject->setCursorPosition(-1);
+ QVERIFY(textinputObject->cursorPosition() == 0);
+ QVERIFY(textinputObject->selectionStart() == 0);
+ QVERIFY(textinputObject->selectionEnd() == 0);
+ QVERIFY(textinputObject->selectedText().isNull());
+
+ textinputObject->setCursorPosition(textinputObject->text().count()+1);
+ QVERIFY(textinputObject->cursorPosition() == 0);
+ QVERIFY(textinputObject->selectionStart() == 0);
+ QVERIFY(textinputObject->selectionEnd() == 0);
+ QVERIFY(textinputObject->selectedText().isNull());
+
+ //Test selection
+ for(int i=0; i<= testStr.size(); i++) {
+ textinputObject->select(0,i);
+ QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
+ }
+ for(int i=0; i<= testStr.size(); i++) {
+ textinputObject->select(i,testStr.size());
+ QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
+ }
+
+ textinputObject->setCursorPosition(0);
+ QVERIFY(textinputObject->cursorPosition() == 0);
+ QVERIFY(textinputObject->selectionStart() == 0);
+ QVERIFY(textinputObject->selectionEnd() == 0);
+ QVERIFY(textinputObject->selectedText().isNull());
+
+ //Test Error Ignoring behaviour
+ textinputObject->setCursorPosition(0);
+ QVERIFY(textinputObject->selectedText().isNull());
+ textinputObject->select(-10,0);
+ QVERIFY(textinputObject->selectedText().isNull());
+ textinputObject->select(100,110);
+ QVERIFY(textinputObject->selectedText().isNull());
+ textinputObject->select(0,-10);
+ QVERIFY(textinputObject->selectedText().isNull());
+ textinputObject->select(0,100);
+ QVERIFY(textinputObject->selectedText().isNull());
+ textinputObject->select(0,10);
+ QVERIFY(textinputObject->selectedText().size() == 10);
+ textinputObject->select(-10,10);
+ QVERIFY(textinputObject->selectedText().size() == 10);
+ textinputObject->select(100,101);
+ QVERIFY(textinputObject->selectedText().size() == 10);
+ textinputObject->select(0,-10);
+ QVERIFY(textinputObject->selectedText().size() == 10);
+ textinputObject->select(0,100);
+ QVERIFY(textinputObject->selectedText().size() == 10);
+
+ textinputObject->deselect();
+ QVERIFY(textinputObject->selectedText().isNull());
+ textinputObject->select(0,10);
+ QVERIFY(textinputObject->selectedText().size() == 10);
+ textinputObject->deselect();
+ QVERIFY(textinputObject->selectedText().isNull());
+
+ delete textinputObject;
+}
+
+void tst_qsgtextinput::isRightToLeft_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<bool>("emptyString");
+ QTest::addColumn<bool>("firstCharacter");
+ QTest::addColumn<bool>("lastCharacter");
+ QTest::addColumn<bool>("middleCharacter");
+ QTest::addColumn<bool>("startString");
+ QTest::addColumn<bool>("midString");
+ QTest::addColumn<bool>("endString");
+
+ const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
+ QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
+ QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
+ QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
+ QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
+ QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true;
+ QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
+}
+
+void tst_qsgtextinput::isRightToLeft()
+{
+ QFETCH(QString, text);
+ QFETCH(bool, emptyString);
+ QFETCH(bool, firstCharacter);
+ QFETCH(bool, lastCharacter);
+ QFETCH(bool, middleCharacter);
+ QFETCH(bool, startString);
+ QFETCH(bool, midString);
+ QFETCH(bool, endString);
+
+ QSGTextInput textInput;
+ textInput.setText(text);
+
+ // first test that the right string is delivered to the QString::isRightToLeft()
+ QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
+ QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
+ QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
+ QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
+ QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
+ QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
+ if (text.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
+ QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
+
+ // then test that the feature actually works
+ QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
+ QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
+ QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
+ QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
+ QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
+ QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
+ if (text.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
+ QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
+}
+
+void tst_qsgtextinput::moveCursorSelection_data()
+{
+ QTest::addColumn<QString>("testStr");
+ QTest::addColumn<int>("cursorPosition");
+ QTest::addColumn<int>("movePosition");
+ QTest::addColumn<QSGTextInput::SelectionMode>("mode");
+ QTest::addColumn<int>("selectionStart");
+ QTest::addColumn<int>("selectionEnd");
+ QTest::addColumn<bool>("reversible");
+
+ // () contains the text selected by the cursor.
+ // <> contains the actual selection.
+
+ QTest::newRow("(t)he|characters")
+ << standard[0] << 0 << 1 << QSGTextInput::SelectCharacters << 0 << 1 << true;
+ QTest::newRow("do(g)|characters")
+ << standard[0] << 43 << 44 << QSGTextInput::SelectCharacters << 43 << 44 << true;
+ QTest::newRow("jum(p)ed|characters")
+ << standard[0] << 23 << 24 << QSGTextInput::SelectCharacters << 23 << 24 << true;
+ QTest::newRow("jumped( )over|characters")
+ << standard[0] << 26 << 27 << QSGTextInput::SelectCharacters << 26 << 27 << true;
+ QTest::newRow("(the )|characters")
+ << standard[0] << 0 << 4 << QSGTextInput::SelectCharacters << 0 << 4 << true;
+ QTest::newRow("( dog)|characters")
+ << standard[0] << 40 << 44 << QSGTextInput::SelectCharacters << 40 << 44 << true;
+ QTest::newRow("( jumped )|characters")
+ << standard[0] << 19 << 27 << QSGTextInput::SelectCharacters << 19 << 27 << true;
+ QTest::newRow("th(e qu)ick|characters")
+ << standard[0] << 2 << 6 << QSGTextInput::SelectCharacters << 2 << 6 << true;
+ QTest::newRow("la(zy d)og|characters")
+ << standard[0] << 38 << 42 << QSGTextInput::SelectCharacters << 38 << 42 << true;
+ QTest::newRow("jum(ped ov)er|characters")
+ << standard[0] << 23 << 29 << QSGTextInput::SelectCharacters << 23 << 29 << true;
+ QTest::newRow("()the|characters")
+ << standard[0] << 0 << 0 << QSGTextInput::SelectCharacters << 0 << 0 << true;
+ QTest::newRow("dog()|characters")
+ << standard[0] << 44 << 44 << QSGTextInput::SelectCharacters << 44 << 44 << true;
+ QTest::newRow("jum()ped|characters")
+ << standard[0] << 23 << 23 << QSGTextInput::SelectCharacters << 23 << 23 << true;
+
+ QTest::newRow("<(t)he>|words")
+ << standard[0] << 0 << 1 << QSGTextInput::SelectWords << 0 << 3 << true;
+ QTest::newRow("<do(g)>|words")
+ << standard[0] << 43 << 44 << QSGTextInput::SelectWords << 41 << 44 << true;
+ QTest::newRow("<jum(p)ed>|words")
+ << standard[0] << 23 << 24 << QSGTextInput::SelectWords << 20 << 26 << true;
+ QTest::newRow("<jumped( )>over|words,ltr")
+ << standard[0] << 26 << 27 << QSGTextInput::SelectWords << 20 << 27 << false;
+ QTest::newRow("jumped<( )over>|words,rtl")
+ << standard[0] << 27 << 26 << QSGTextInput::SelectWords << 26 << 31 << false;
+ QTest::newRow("<(the )>quick|words,ltr")
+ << standard[0] << 0 << 4 << QSGTextInput::SelectWords << 0 << 4 << false;
+ QTest::newRow("<(the )quick>|words,rtl")
+ << standard[0] << 4 << 0 << QSGTextInput::SelectWords << 0 << 9 << false;
+ QTest::newRow("<lazy( dog)>|words,ltr")
+ << standard[0] << 40 << 44 << QSGTextInput::SelectWords << 36 << 44 << false;
+ QTest::newRow("lazy<( dog)>|words,rtl")
+ << standard[0] << 44 << 40 << QSGTextInput::SelectWords << 40 << 44 << false;
+ QTest::newRow("<fox( jumped )>over|words,ltr")
+ << standard[0] << 19 << 27 << QSGTextInput::SelectWords << 16 << 27 << false;
+ QTest::newRow("fox<( jumped )over>|words,rtl")
+ << standard[0] << 27 << 19 << QSGTextInput::SelectWords << 19 << 31 << false;
+ QTest::newRow("<th(e qu)ick>|words")
+ << standard[0] << 2 << 6 << QSGTextInput::SelectWords << 0 << 9 << true;
+ QTest::newRow("<la(zy d)og|words>")
+ << standard[0] << 38 << 42 << QSGTextInput::SelectWords << 36 << 44 << true;
+ QTest::newRow("<jum(ped ov)er>|words")
+ << standard[0] << 23 << 29 << QSGTextInput::SelectWords << 20 << 31 << true;
+ QTest::newRow("<()>the|words")
+ << standard[0] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << true;
+ QTest::newRow("dog<()>|words")
+ << standard[0] << 44 << 44 << QSGTextInput::SelectWords << 44 << 44 << true;
+ QTest::newRow("jum<()>ped|words")
+ << standard[0] << 23 << 23 << QSGTextInput::SelectWords << 23 << 23 << true;
+
+ QTest::newRow("Hello<(,)> |words")
+ << standard[2] << 5 << 6 << QSGTextInput::SelectWords << 5 << 6 << true;
+ QTest::newRow("Hello<(, )>world|words,ltr")
+ << standard[2] << 5 << 7 << QSGTextInput::SelectWords << 5 << 7 << false;
+ QTest::newRow("Hello<(, )world>|words,rtl")
+ << standard[2] << 7 << 5 << QSGTextInput::SelectWords << 5 << 12 << false;
+ QTest::newRow("<Hel(lo, )>world|words,ltr")
+ << standard[2] << 3 << 7 << QSGTextInput::SelectWords << 0 << 7 << false;
+ QTest::newRow("<Hel(lo, )world>|words,rtl")
+ << standard[2] << 7 << 3 << QSGTextInput::SelectWords << 0 << 12 << false;
+ QTest::newRow("<Hel(lo)>,|words")
+ << standard[2] << 3 << 5 << QSGTextInput::SelectWords << 0 << 5 << true;
+ QTest::newRow("Hello<()>,|words")
+ << standard[2] << 5 << 5 << QSGTextInput::SelectWords << 5 << 5 << true;
+ QTest::newRow("Hello,<()>|words")
+ << standard[2] << 6 << 6 << QSGTextInput::SelectWords << 6 << 6 << true;
+ QTest::newRow("Hello<,( )>world|words,ltr")
+ << standard[2] << 6 << 7 << QSGTextInput::SelectWords << 5 << 7 << false;
+ QTest::newRow("Hello,<( )world>|words,rtl")
+ << standard[2] << 7 << 6 << QSGTextInput::SelectWords << 6 << 12 << false;
+ QTest::newRow("Hello<,( world)>|words,ltr")
+ << standard[2] << 6 << 12 << QSGTextInput::SelectWords << 5 << 12 << false;
+ QTest::newRow("Hello,<( world)>|words,rtl")
+ << standard[2] << 12 << 6 << QSGTextInput::SelectWords << 6 << 12 << false;
+ QTest::newRow("Hello<,( world!)>|words,ltr")
+ << standard[2] << 6 << 13 << QSGTextInput::SelectWords << 5 << 13 << false;
+ QTest::newRow("Hello,<( world!)>|words,rtl")
+ << standard[2] << 13 << 6 << QSGTextInput::SelectWords << 6 << 13 << false;
+ QTest::newRow("Hello<(, world!)>|words")
+ << standard[2] << 5 << 13 << QSGTextInput::SelectWords << 5 << 13 << true;
+ // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
+ // QTBUG-11365
+ // QTest::newRow("world<(!)>|words")
+ // << standard[2] << 12 << 13 << QSGTextInput::SelectWords << 12 << 13 << true;
+ QTest::newRow("world!<()>)|words")
+ << standard[2] << 13 << 13 << QSGTextInput::SelectWords << 13 << 13 << true;
+ QTest::newRow("world<()>!)|words")
+ << standard[2] << 12 << 12 << QSGTextInput::SelectWords << 12 << 12 << true;
+
+ QTest::newRow("<(,)>olleH |words")
+ << standard[3] << 7 << 8 << QSGTextInput::SelectWords << 7 << 8 << true;
+ QTest::newRow("<dlrow( ,)>olleH|words,ltr")
+ << standard[3] << 6 << 8 << QSGTextInput::SelectWords << 1 << 8 << false;
+ QTest::newRow("dlrow<( ,)>olleH|words,rtl")
+ << standard[3] << 8 << 6 << QSGTextInput::SelectWords << 6 << 8 << false;
+ QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
+ << standard[3] << 6 << 10 << QSGTextInput::SelectWords << 1 << 13 << false;
+ QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
+ << standard[3] << 10 << 6 << QSGTextInput::SelectWords << 6 << 13 << false;
+ QTest::newRow(",<(ol)leH>,|words")
+ << standard[3] << 8 << 10 << QSGTextInput::SelectWords << 8 << 13 << true;
+ QTest::newRow(",<()>olleH|words")
+ << standard[3] << 8 << 8 << QSGTextInput::SelectWords << 8 << 8 << true;
+ QTest::newRow("<()>,olleH|words")
+ << standard[3] << 7 << 7 << QSGTextInput::SelectWords << 7 << 7 << true;
+ QTest::newRow("<dlrow( )>,olleH|words,ltr")
+ << standard[3] << 6 << 7 << QSGTextInput::SelectWords << 1 << 7 << false;
+ QTest::newRow("dlrow<( ),>olleH|words,rtl")
+ << standard[3] << 7 << 6 << QSGTextInput::SelectWords << 6 << 8 << false;
+ QTest::newRow("<(dlrow )>,olleH|words,ltr")
+ << standard[3] << 1 << 7 << QSGTextInput::SelectWords << 1 << 7 << false;
+ QTest::newRow("<(dlrow ),>olleH|words,rtl")
+ << standard[3] << 7 << 1 << QSGTextInput::SelectWords << 1 << 8 << false;
+ QTest::newRow("<(!dlrow )>,olleH|words,ltr")
+ << standard[3] << 0 << 7 << QSGTextInput::SelectWords << 0 << 7 << false;
+ QTest::newRow("<(!dlrow ),>olleH|words,rtl")
+ << standard[3] << 7 << 0 << QSGTextInput::SelectWords << 0 << 8 << false;
+ QTest::newRow("(!dlrow ,)olleH|words")
+ << standard[3] << 0 << 8 << QSGTextInput::SelectWords << 0 << 8 << true;
+ QTest::newRow("<(!)>dlrow|words")
+ << standard[3] << 0 << 1 << QSGTextInput::SelectWords << 0 << 1 << true;
+ QTest::newRow("<()>!dlrow|words")
+ << standard[3] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << true;
+ QTest::newRow("!<()>dlrow|words")
+ << standard[3] << 1 << 1 << QSGTextInput::SelectWords << 1 << 1 << true;
+
+ QTest::newRow(" <s(pac)ey> text |words")
+ << standard[4] << 1 << 4 << QSGTextInput::SelectWords << 1 << 7 << true;
+ QTest::newRow(" spacey <t(ex)t> |words")
+ << standard[4] << 11 << 13 << QSGTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
+ QTest::newRow("<( )>spacey text |words|ltr")
+ << standard[4] << 0 << 1 << QSGTextInput::SelectWords << 0 << 1 << false;
+ QTest::newRow("<( )spacey> text |words|rtl")
+ << standard[4] << 1 << 0 << QSGTextInput::SelectWords << 0 << 7 << false;
+ QTest::newRow("spacey <text( )>|words|ltr")
+ << standard[4] << 14 << 15 << QSGTextInput::SelectWords << 10 << 15 << false;
+// QTBUG-11365
+// QTest::newRow("spacey text<( )>|words|rtl")
+// << standard[4] << 15 << 14 << QSGTextInput::SelectWords << 14 << 15 << false;
+ QTest::newRow("<()> spacey text |words")
+ << standard[4] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << false;
+ QTest::newRow(" spacey text <()>|words")
+ << standard[4] << 15 << 15 << QSGTextInput::SelectWords << 15 << 15 << false;
+}
+
+void tst_qsgtextinput::moveCursorSelection()
+{
+ QFETCH(QString, testStr);
+ QFETCH(int, cursorPosition);
+ QFETCH(int, movePosition);
+ QFETCH(QSGTextInput::SelectionMode, mode);
+ QFETCH(int, selectionStart);
+ QFETCH(int, selectionEnd);
+ QFETCH(bool, reversible);
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+ QVERIFY(textinputObject != 0);
+
+ textinputObject->setCursorPosition(cursorPosition);
+ textinputObject->moveCursorSelection(movePosition, mode);
+
+ QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
+ QCOMPARE(textinputObject->selectionStart(), selectionStart);
+ QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
+
+ if (reversible) {
+ textinputObject->setCursorPosition(movePosition);
+ textinputObject->moveCursorSelection(cursorPosition, mode);
+
+ QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
+ QCOMPARE(textinputObject->selectionStart(), selectionStart);
+ QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
+ }
+
+ delete textinputObject;
+}
+
+void tst_qsgtextinput::moveCursorSelectionSequence_data()
+{
+ QTest::addColumn<QString>("testStr");
+ QTest::addColumn<int>("cursorPosition");
+ QTest::addColumn<int>("movePosition1");
+ QTest::addColumn<int>("movePosition2");
+ QTest::addColumn<int>("selection1Start");
+ QTest::addColumn<int>("selection1End");
+ QTest::addColumn<int>("selection2Start");
+ QTest::addColumn<int>("selection2End");
+
+ // () contains the text selected by the cursor.
+ // <> contains the actual selection.
+ // ^ is the revised cursor position.
+ // {} contains the revised selection.
+
+ QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
+ << standard[0]
+ << 9 << 13 << 17
+ << 4 << 15
+ << 4 << 19;
+ QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
+ << standard[0]
+ << 13 << 9 << 17
+ << 9 << 15
+ << 10 << 19;
+ QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
+ << standard[0]
+ << 9 << 13 << 16
+ << 4 << 15
+ << 4 << 16;
+ QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
+ << standard[0]
+ << 13 << 9 << 16
+ << 9 << 15
+ << 10 << 16;
+ QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
+ << standard[0]
+ << 9 << 13 << 15
+ << 4 << 15
+ << 4 << 15;
+ QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
+ << standard[0]
+ << 13 << 9 << 15
+ << 9 << 15
+ << 10 << 15;
+ QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 10
+ << 4 << 15
+ << 4 << 10;
+ QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
+ << standard[0]
+ << 13 << 9 << 10
+ << 9 << 15
+ << 10 << 15;
+ QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 9
+ << 4 << 15
+ << 4 << 9;
+ QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
+ << standard[0]
+ << 13 << 9 << 9
+ << 9 << 15
+ << 9 << 15;
+ QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 7
+ << 4 << 15
+ << 4 << 9;
+ QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 7
+ << 9 << 15
+ << 4 << 15;
+ QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 4
+ << 4 << 15
+ << 4 << 9;
+ QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 4
+ << 9 << 15
+ << 4 << 15;
+ QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 3
+ << 4 << 15
+ << 3 << 9;
+ QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 3
+ << 9 << 15
+ << 3 << 15;
+ QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
+ << standard[0]
+ << 9 << 13 << 1
+ << 4 << 15
+ << 0 << 9;
+ QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
+ << standard[0]
+ << 13 << 9 << 1
+ << 9 << 15
+ << 0 << 15;
+
+ QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
+ << standard[2]
+ << 2 << 4 << 8
+ << 0 << 5
+ << 0 << 12;
+ QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
+ << standard[2]
+ << 4 << 2 << 8
+ << 0 << 5
+ << 0 << 12;
+
+ QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
+ << standard[3]
+ << 9 << 11 << 5
+ << 8 << 13
+ << 1 << 13;
+ QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
+ << standard[3]
+ << 11 << 9 << 5
+ << 8 << 13
+ << 1 << 13;
+
+ QTest::newRow("{<(^} sp)acey> text |ltr")
+ << standard[4]
+ << 0 << 3 << 0
+ << 0 << 7
+ << 0 << 0;
+ QTest::newRow("{<( ^}sp)acey> text |ltr")
+ << standard[4]
+ << 0 << 3 << 1
+ << 0 << 7
+ << 0 << 1;
+ QTest::newRow("<( {s^p)acey>} text |rtl")
+ << standard[4]
+ << 3 << 0 << 2
+ << 0 << 7
+ << 1 << 7;
+ QTest::newRow("<( {^sp)acey>} text |rtl")
+ << standard[4]
+ << 3 << 0 << 1
+ << 0 << 7
+ << 1 << 7;
+
+ QTest::newRow(" spacey <te(xt {^)>}|rtl")
+ << standard[4]
+ << 15 << 12 << 15
+ << 10 << 15
+ << 15 << 15;
+// QTBUG-11365
+// QTest::newRow(" spacey <te(xt{^ )>}|rtl")
+// << standard[4]
+// << 15 << 12 << 14
+// << 10 << 15
+// << 14 << 15;
+ QTest::newRow(" spacey {<te(x^t} )>|ltr")
+ << standard[4]
+ << 12 << 15 << 13
+ << 10 << 15
+ << 10 << 14;
+// QTBUG-11365
+// QTest::newRow(" spacey {<te(xt^} )>|ltr")
+// << standard[4]
+// << 12 << 15 << 14
+// << 10 << 15
+// << 10 << 14;
+}
+
+void tst_qsgtextinput::moveCursorSelectionSequence()
+{
+ QFETCH(QString, testStr);
+ QFETCH(int, cursorPosition);
+ QFETCH(int, movePosition1);
+ QFETCH(int, movePosition2);
+ QFETCH(int, selection1Start);
+ QFETCH(int, selection1End);
+ QFETCH(int, selection2Start);
+ QFETCH(int, selection2End);
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
+ QDeclarativeComponent textinputComponent(&engine);
+ textinputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
+ QVERIFY(textinputObject != 0);
+
+ textinputObject->setCursorPosition(cursorPosition);
+
+ textinputObject->moveCursorSelection(movePosition1, QSGTextInput::SelectWords);
+ QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
+ QCOMPARE(textinputObject->selectionStart(), selection1Start);
+ QCOMPARE(textinputObject->selectionEnd(), selection1End);
+
+ textinputObject->moveCursorSelection(movePosition2, QSGTextInput::SelectWords);
+ QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
+ QCOMPARE(textinputObject->selectionStart(), selection2Start);
+ QCOMPARE(textinputObject->selectionEnd(), selection2End);
+
+ delete textinputObject;
+}
+
+void tst_qsgtextinput::dragMouseSelection()
+{
+ QString qmlfile = SRCDIR "/data/mouseselection_true.qml";
+
+ QSGView canvas(QUrl::fromLocalFile(qmlfile));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextInput *textInputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
+ QVERIFY(textInputObject != 0);
+
+ // press-and-drag-and-release from x1 to x2
+ int x1 = 10;
+ int x2 = 70;
+ int y = textInputObject->height()/2;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ }
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+
+ QString str1 = textInputObject->selectedText();
+ QVERIFY(str1.length() > 3);
+
+ // press and drag the current selection.
+ x1 = 40;
+ x2 = 100;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ {
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ }
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+ QString str2 = textInputObject->selectedText();
+ QVERIFY(str2.length() > 3);
+
+ QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and doesn't not the first moved.
+}
+
+void tst_qsgtextinput::mouseSelectionMode_data()
+{
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<bool>("selectWords");
+
+ // import installed
+ QTest::newRow("SelectWords") << SRCDIR "/data/mouseselectionmode_words.qml" << true;
+ QTest::newRow("SelectCharacters") << SRCDIR "/data/mouseselectionmode_characters.qml" << false;
+ QTest::newRow("default") << SRCDIR "/data/mouseselectionmode_default.qml" << false;
+}
+
+void tst_qsgtextinput::mouseSelectionMode()
+{
+ QFETCH(QString, qmlfile);
+ QFETCH(bool, selectWords);
+
+ QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ QSGView canvas(QUrl::fromLocalFile(qmlfile));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextInput *textInputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
+ QVERIFY(textInputObject != 0);
+
+ // press-and-drag-and-release from x1 to x2
+ int x1 = 10;
+ int x2 = 70;
+ int y = textInputObject->height()/2;
+ QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
+ //QTest::mouseMove(&canvas, canvas.mapFromScene(QPoint(x2,y))); // doesn't work
+ QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&canvas, &mv);
+ QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
+ QString str = textInputObject->selectedText();
+ if (selectWords) {
+ QCOMPARE(str, text);
+ } else {
+ QVERIFY(str.length() > 3);
+ QVERIFY(str != text);
+ }
+}
+
+void tst_qsgtextinput::horizontalAlignment_data()
+{
+ QTest::addColumn<int>("hAlign");
+ QTest::addColumn<QString>("expectfile");
+
+ QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
+ QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
+ QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
+}
+
+void tst_qsgtextinput::horizontalAlignment()
+{
+ QFETCH(int, hAlign);
+ QFETCH(QString, expectfile);
+
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment.qml"));
+
+ canvas.show();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+ QObject *ob = canvas.rootObject();
+ QVERIFY(ob != 0);
+ ob->setProperty("horizontalAlignment",hAlign);
+ QImage actual(canvas.width(), canvas.height(), QImage::Format_RGB32);
+ actual.fill(qRgb(255,255,255));
+ {
+ QPainter p(&actual);
+ canvas.render(&p);
+ }
+
+ expectfile = createExpectedFileIfNotFound(expectfile, actual);
+
+ QImage expect(expectfile);
+
+ QCOMPARE(actual,expect);
+}
+
+void tst_qsgtextinput::horizontalAlignment_RightToLeft()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment_RightToLeft.qml"));
+ QSGTextInput *textInput = canvas.rootObject()->findChild<QSGTextInput*>("text");
+ QVERIFY(textInput != 0);
+ canvas.show();
+
+ QSGTextInputPrivate *textInputPrivate = QSGTextInputPrivate::get(textInput);
+ QVERIFY(textInputPrivate != 0);
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+
+ // implicit alignment should follow the reading direction of RTL text
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+
+ // explicitly left aligned
+ textInput->setHAlign(QSGTextInput::AlignLeft);
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+
+ // explicitly right aligned
+ textInput->setHAlign(QSGTextInput::AlignRight);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+
+ // explicitly center aligned
+ textInput->setHAlign(QSGTextInput::AlignHCenter);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignHCenter);
+ QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+ QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width > canvas.width()/2);
+
+ // reseted alignment should go back to following the text reading direction
+ textInput->resetHAlign();
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+
+ // mirror the text item
+ QSGItemPrivate::get(textInput)->setLayoutMirror(true);
+
+ // mirrored implicit alignment should continue to follow the reading direction of the text
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+
+ // explicitly right aligned behaves as left aligned
+ textInput->setHAlign(QSGTextInput::AlignRight);
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
+ QCOMPARE(textInput->effectiveHAlign(), QSGTextInput::AlignLeft);
+ QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+
+ // mirrored explicitly left aligned behaves as right aligned
+ textInput->setHAlign(QSGTextInput::AlignLeft);
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
+ QCOMPARE(textInput->effectiveHAlign(), QSGTextInput::AlignRight);
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+
+ // disable mirroring
+ QSGItemPrivate::get(textInput)->setLayoutMirror(false);
+ QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
+ textInput->resetHAlign();
+
+ // English text should be implicitly left aligned
+ textInput->setText("Hello world!");
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
+ QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+
+#ifndef Q_OS_MAC // QTBUG-18040
+ // empty text with implicit alignment follows the system locale-based
+ // keyboard input direction from QApplication::keyboardInputDirection
+ textInput->setText("");
+ QCOMPARE(textInput->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
+ QSGTextInput::AlignLeft : QSGTextInput::AlignRight);
+ if (QApplication::keyboardInputDirection() == Qt::LeftToRight)
+ QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+ else
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ textInput->setHAlign(QSGTextInput::AlignRight);
+ QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
+ QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+#endif
+
+#ifndef Q_OS_MAC // QTBUG-18040
+ // alignment of TextInput with no text set to it
+ QString componentStr = "import QtQuick 2.0\nTextInput {}";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QSGTextInput *textObject = qobject_cast<QSGTextInput*>(textComponent.create());
+ QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
+ QSGTextInput::AlignLeft : QSGTextInput::AlignRight);
+ delete textObject;
+#endif
+}
+
+void tst_qsgtextinput::positionAt()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
+ QVERIFY(canvas.rootObject() != 0);
+ canvas.show();
+ canvas.setFocus();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
+ QVERIFY(textinputObject != 0);
+
+ // Check autoscrolled...
+ QFontMetrics fm(textinputObject->font());
+
+ int pos = textinputObject->positionAt(textinputObject->width()/2);
+ int diff = abs(int(fm.width(textinputObject->text()) - (fm.width(textinputObject->text().left(pos))+textinputObject->width()/2)));
+
+ // some tollerance for different fonts.
+#ifdef Q_OS_LINUX
+ QVERIFY(diff < 2);
+#else
+ QVERIFY(diff < 5);
+#endif
+
+ int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
+ QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorBetweenCharacters), pos + 1);
+ QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorOnCharacter), pos);
+
+ // Check without autoscroll...
+ textinputObject->setAutoScroll(false);
+ pos = textinputObject->positionAt(textinputObject->width()/2);
+ diff = abs(int(fm.width(textinputObject->text().left(pos))-textinputObject->width()/2));
+
+ // some tollerance for different fonts.
+#ifdef Q_OS_LINUX
+ QVERIFY(diff < 2);
+#else
+ QVERIFY(diff < 5);
+#endif
+
+ x = textinputObject->positionToRectangle(pos + 1).x() - 1;
+ QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorBetweenCharacters), pos + 1);
+ QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorOnCharacter), pos);
+
+ const qreal x0 = textinputObject->positionToRectangle(pos).x();
+ const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
+
+ QString preeditText = textinputObject->text().mid(0, pos);
+ textinputObject->setText(textinputObject->text().mid(pos));
+ textinputObject->setCursorPosition(0);
+
+ QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
+ QApplication::sendEvent(&canvas, &inputEvent);
+
+ // Check all points within the preedit text return the same position.
+ QCOMPARE(textinputObject->positionAt(0), 0);
+ QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
+ QCOMPARE(textinputObject->positionAt(x0), 0);
+
+ // Verify positioning returns to normal after the preedit text.
+ QCOMPARE(textinputObject->positionAt(x1), 1);
+ QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
+}
+
+void tst_qsgtextinput::maxLength()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/maxLength.qml"));
+ QVERIFY(canvas.rootObject() != 0);
+ canvas.show();
+ canvas.setFocus();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
+ QVERIFY(textinputObject != 0);
+ QVERIFY(textinputObject->text().isEmpty());
+ QVERIFY(textinputObject->maxLength() == 10);
+ foreach(const QString &str, standard){
+ QVERIFY(textinputObject->text().length() <= 10);
+ textinputObject->setText(str);
+ QVERIFY(textinputObject->text().length() <= 10);
+ }
+
+ textinputObject->setText("");
+ QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
+ for(int i=0; i<20; i++){
+ QCOMPARE(textinputObject->text().length(), qMin(i,10));
+ //simulateKey(&canvas, Qt::Key_A);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ }
+}
+
+void tst_qsgtextinput::masks()
+{
+ //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
+ //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/masks.qml"));
+ canvas.show();
+ canvas.setFocus();
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
+ QVERIFY(textinputObject != 0);
+ QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
+ QVERIFY(textinputObject->text().length() == 0);
+ QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
+ for(int i=0; i<10; i++){
+ QCOMPARE(qMin(i,8), textinputObject->text().length());
+ QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
+ //simulateKey(&canvas, Qt::Key_A);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ }
+}
+
+void tst_qsgtextinput::validators()
+{
+ // Note that this test assumes that the validators are working properly
+ // so you may need to run their tests first. All validators are checked
+ // here to ensure that their exposure to QML is working.
+
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/validators.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextInput *intInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
+ QVERIFY(intInput);
+ intInput->setFocus(true);
+ QTRY_VERIFY(intInput->hasActiveFocus());
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(intInput->text(), QLatin1String("1"));
+ QCOMPARE(intInput->hasAcceptableInput(), false);
+ QTest::keyPress(&canvas, Qt::Key_2);
+ QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
+ QCOMPARE(intInput->text(), QLatin1String("1"));
+ QCOMPARE(intInput->hasAcceptableInput(), false);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(intInput->text(), QLatin1String("11"));
+ QCOMPARE(intInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_0);
+ QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
+ QCOMPARE(intInput->text(), QLatin1String("11"));
+ QCOMPARE(intInput->hasAcceptableInput(), true);
+
+ QSGTextInput *dblInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
+ QTRY_VERIFY(dblInput);
+ dblInput->setFocus(true);
+ QVERIFY(dblInput->hasActiveFocus() == true);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(dblInput->text(), QLatin1String("1"));
+ QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QTest::keyPress(&canvas, Qt::Key_2);
+ QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
+ QCOMPARE(dblInput->text(), QLatin1String("12"));
+ QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_Period);
+ QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
+ QCOMPARE(dblInput->text(), QLatin1String("12."));
+ QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(dblInput->text(), QLatin1String("12.1"));
+ QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(dblInput->text(), QLatin1String("12.11"));
+ QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(dblInput->text(), QLatin1String("12.11"));
+ QCOMPARE(dblInput->hasAcceptableInput(), true);
+
+ QSGTextInput *strInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
+ QTRY_VERIFY(strInput);
+ strInput->setFocus(true);
+ QVERIFY(strInput->hasActiveFocus() == true);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QCOMPARE(strInput->text(), QLatin1String(""));
+ QCOMPARE(strInput->hasAcceptableInput(), false);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QCOMPARE(strInput->text(), QLatin1String("a"));
+ QCOMPARE(strInput->hasAcceptableInput(), false);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QCOMPARE(strInput->text(), QLatin1String("aa"));
+ QCOMPARE(strInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QCOMPARE(strInput->text(), QLatin1String("aaa"));
+ QCOMPARE(strInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QCOMPARE(strInput->text(), QLatin1String("aaaa"));
+ QCOMPARE(strInput->hasAcceptableInput(), true);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QCOMPARE(strInput->text(), QLatin1String("aaaa"));
+ QCOMPARE(strInput->hasAcceptableInput(), true);
+}
+
+void tst_qsgtextinput::inputMethods()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/inputmethods.qml"));
+ canvas.show();
+ canvas.setFocus();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+
+ // test input method hints
+ QVERIFY(canvas.rootObject() != 0);
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(canvas.rootObject());
+ QVERIFY(input != 0);
+ QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
+ input->setInputMethodHints(Qt::ImhUppercaseOnly);
+ QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
+
+ input->setFocus(true);
+ QVERIFY(input->hasActiveFocus() == true);
+ // test that input method event is committed
+ QInputMethodEvent event;
+ event.setCommitString( "My ", -12, 0);
+ QApplication::sendEvent(&canvas, &event);
+ QCOMPARE(input->text(), QString("My Hello world!"));
+
+ input->setCursorPosition(2);
+ event.setCommitString("Your", -2, 2);
+ QApplication::sendEvent(&canvas, &event);
+ QCOMPARE(input->text(), QString("Your Hello world!"));
+ QCOMPARE(input->cursorPosition(), 4);
+
+ input->setCursorPosition(7);
+ event.setCommitString("Goodbye", -2, 5);
+ QApplication::sendEvent(&canvas, &event);
+ QCOMPARE(input->text(), QString("Your Goodbye world!"));
+ QCOMPARE(input->cursorPosition(), 12);
+
+ input->setCursorPosition(8);
+ event.setCommitString("Our", -8, 4);
+ QApplication::sendEvent(&canvas, &event);
+ QCOMPARE(input->text(), QString("Our Goodbye world!"));
+ QCOMPARE(input->cursorPosition(), 7);
+}
+
+/*
+TextInput element should only handle left/right keys until the cursor reaches
+the extent of the text, then they should ignore the keys.
+
+*/
+void tst_qsgtextinput::navigation()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(input != 0);
+ input->setCursorPosition(0);
+ QTRY_VERIFY(input->hasActiveFocus() == true);
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == false);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == true);
+ //QT-2944: If text is selected, ensure we deselect upon cursor motion
+ input->setCursorPosition(input->text().length());
+ input->select(0,input->text().length());
+ QVERIFY(input->selectionStart() != input->selectionEnd());
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->selectionStart() == input->selectionEnd());
+ QVERIFY(input->selectionStart() == input->text().length());
+ QVERIFY(input->hasActiveFocus() == true);
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == false);
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == true);
+
+ // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
+ input->setCursorPosition(2);
+ QCOMPARE(input->cursorPosition(),2);
+ simulateKey(&canvas, Qt::Key_Up);
+ QCOMPARE(input->cursorPosition(),2);
+ simulateKey(&canvas, Qt::Key_Down);
+ QCOMPARE(input->cursorPosition(),2);
+}
+
+void tst_qsgtextinput::navigation_RTL()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(input != 0);
+ const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
+ input->setText(QString::fromUtf16(arabic_str, 11));
+
+ input->setCursorPosition(0);
+ QTRY_VERIFY(input->hasActiveFocus() == true);
+
+ // move off
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == false);
+
+ // move back
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == true);
+
+ input->setCursorPosition(input->text().length());
+ QVERIFY(input->hasActiveFocus() == true);
+
+ // move off
+ simulateKey(&canvas, Qt::Key_Left);
+ QVERIFY(input->hasActiveFocus() == false);
+
+ // move back
+ simulateKey(&canvas, Qt::Key_Right);
+ QVERIFY(input->hasActiveFocus() == true);
+}
+
+void tst_qsgtextinput::copyAndPaste() {
+#ifndef QT_NO_CLIPBOARD
+
+#ifdef Q_WS_MAC
+ {
+ PasteboardRef pasteboard;
+ OSStatus status = PasteboardCreate(0, &pasteboard);
+ if (status == noErr)
+ CFRelease(pasteboard);
+ else
+ QSKIP("This machine doesn't support the clipboard", SkipAll);
+ }
+#endif
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
+ QDeclarativeComponent textInputComponent(&engine);
+ textInputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
+ QVERIFY(textInput != 0);
+
+ // copy and paste
+ QCOMPARE(textInput->text().length(), 12);
+ textInput->select(0, textInput->text().length());;
+ textInput->copy();
+ QCOMPARE(textInput->selectedText(), QString("Hello world!"));
+ QCOMPARE(textInput->selectedText().length(), 12);
+ textInput->setCursorPosition(0);
+ QVERIFY(textInput->canPaste());
+ textInput->paste();
+ QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
+ QCOMPARE(textInput->text().length(), 24);
+
+ // can paste
+ QVERIFY(textInput->canPaste());
+ textInput->setReadOnly(true);
+ QVERIFY(!textInput->canPaste());
+ textInput->setReadOnly(false);
+ QVERIFY(textInput->canPaste());
+
+ // select word
+ textInput->setCursorPosition(0);
+ textInput->selectWord();
+ QCOMPARE(textInput->selectedText(), QString("Hello"));
+
+ // select all and cut
+ textInput->selectAll();
+ textInput->cut();
+ QCOMPARE(textInput->text().length(), 0);
+ textInput->paste();
+ QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
+ QCOMPARE(textInput->text().length(), 24);
+
+ // clear copy buffer
+ QClipboard *clipboard = QApplication::clipboard();
+ QVERIFY(clipboard);
+ clipboard->clear();
+ QVERIFY(!textInput->canPaste());
+
+ // test that copy functionality is disabled
+ // when echo mode is set to hide text/password mode
+ int index = 0;
+ while (index < 4) {
+ QSGTextInput::EchoMode echoMode = QSGTextInput::EchoMode(index);
+ textInput->setEchoMode(echoMode);
+ textInput->setText("My password");
+ textInput->select(0, textInput->text().length());;
+ textInput->copy();
+ if (echoMode == QSGTextInput::Normal) {
+ QVERIFY(!clipboard->text().isEmpty());
+ QCOMPARE(clipboard->text(), QString("My password"));
+ clipboard->clear();
+ } else {
+ QVERIFY(clipboard->text().isEmpty());
+ }
+ index++;
+ }
+
+ delete textInput;
+#endif
+}
+
+void tst_qsgtextinput::canPasteEmpty() {
+#ifndef QT_NO_CLIPBOARD
+
+ QApplication::clipboard()->clear();
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
+ QDeclarativeComponent textInputComponent(&engine);
+ textInputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
+ QVERIFY(textInput != 0);
+
+ QLineControl lc;
+ bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
+ QCOMPARE(textInput->canPaste(), cp);
+
+#endif
+}
+
+void tst_qsgtextinput::canPaste() {
+#ifndef QT_NO_CLIPBOARD
+
+ QApplication::clipboard()->setText("Some text");
+
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
+ QDeclarativeComponent textInputComponent(&engine);
+ textInputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
+ QVERIFY(textInput != 0);
+
+ QLineControl lc;
+ bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
+ QCOMPARE(textInput->canPaste(), cp);
+
+#endif
+}
+
+void tst_qsgtextinput::passwordCharacter()
+{
+ QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
+ QDeclarativeComponent textInputComponent(&engine);
+ textInputComponent.setData(componentStr.toLatin1(), QUrl());
+ QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
+ QVERIFY(textInput != 0);
+
+ textInput->setPasswordCharacter("X");
+ QSize contentsSize = textInput->contentsSize();
+ qreal implicitWidth = textInput->implicitWidth();
+ textInput->setPasswordCharacter(".");
+
+ QEXPECT_FAIL("", "QSGPaintedItem::contentSize()/setContentSize() not implemented", Continue);
+ // QTBUG-12383 content is updated and redrawn
+ QVERIFY(contentsSize != textInput->contentsSize());
+ QVERIFY(textInput->implicitWidth() < implicitWidth);
+
+ delete textInput;
+}
+
+void tst_qsgtextinput::cursorDelegate()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorTest.qml"));
+ view.show();
+ view.setFocus();
+ QSGTextInput *textInputObject = view.rootObject()->findChild<QSGTextInput*>("textInputObject");
+ QVERIFY(textInputObject != 0);
+ QVERIFY(textInputObject->findChild<QSGItem*>("cursorInstance"));
+ //Test Delegate gets created
+ textInputObject->setFocus(true);
+ QSGItem* delegateObject = textInputObject->findChild<QSGItem*>("cursorInstance");
+ QVERIFY(delegateObject);
+ //Test Delegate gets moved
+ for(int i=0; i<= textInputObject->text().length(); i++){
+ textInputObject->setCursorPosition(i);
+ QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
+ QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
+ }
+ textInputObject->setCursorPosition(0);
+ QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
+ QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
+ //Test Delegate gets deleted
+ textInputObject->setCursorDelegate(0);
+ QVERIFY(!textInputObject->findChild<QSGItem*>("cursorInstance"));
+}
+
+void tst_qsgtextinput::cursorVisible()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorVisible.qml"));
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ view.setFocus();
+
+ QSGTextInput input;
+ QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
+
+ QCOMPARE(input.isCursorVisible(), false);
+
+ input.setCursorVisible(true);
+ QCOMPARE(input.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 1);
+
+ input.setCursorVisible(false);
+ QCOMPARE(input.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 2);
+
+ input.setFocus(true);
+ QCOMPARE(input.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 2);
+
+ input.setParentItem(view.rootObject());
+ QCOMPARE(input.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 3);
+
+ input.setFocus(false);
+ QCOMPARE(input.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 4);
+
+ input.setFocus(true);
+ QCOMPARE(input.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 5);
+
+ view.clearFocus();
+ QCOMPARE(input.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 6);
+
+ view.setFocus();
+ QCOMPARE(input.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 7);
+
+ // on mac, setActiveWindow(0) on mac does not deactivate the current application
+ // (you have to switch to a different app or hide the current app to trigger this)
+#if !defined(Q_WS_MAC)
+ QApplication::setActiveWindow(0);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(0));
+ QCOMPARE(input.isCursorVisible(), false);
+ QCOMPARE(spy.count(), 8);
+
+ QApplication::setActiveWindow(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QCOMPARE(input.isCursorVisible(), true);
+ QCOMPARE(spy.count(), 9);
+#endif
+}
+
+void tst_qsgtextinput::cursorRectangle()
+{
+ QString text = "Hello World!";
+
+ QSGTextInput input;
+ input.setText(text);
+ QFontMetricsF fm(input.font());
+ input.setWidth(fm.width(text.mid(0, 5)));
+
+ QRect r;
+
+ // some tolerance for different fonts.
+#ifdef Q_OS_LINUX
+ const int error = 2;
+#else
+ const int error = 5;
+#endif
+
+
+ for (int i = 0; i <= 5; ++i) {
+ input.setCursorPosition(i);
+ r = input.cursorRectangle();
+ int textWidth = fm.width(text.mid(0, i));
+
+ QVERIFY(r.left() < textWidth + error);
+ QVERIFY(r.right() > textWidth - error);
+ QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
+ }
+
+ // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
+ QVERIFY(r.left() < input.boundingRect().width());
+ QVERIFY(r.right() >= input.width());
+
+ for (int i = 6; i < text.length(); ++i) {
+ input.setCursorPosition(i);
+ QCOMPARE(r, input.cursorRectangle());
+ QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
+ }
+
+ for (int i = text.length() - 2; i >= 0; --i) {
+ input.setCursorPosition(i);
+ r = input.cursorRectangle();
+ QVERIFY(r.right() >= 0);
+ QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
+ }
+}
+
+void tst_qsgtextinput::readOnly()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/readOnly.qml"));
+ canvas.show();
+ canvas.setFocus();
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(input != 0);
+ QTRY_VERIFY(input->hasActiveFocus() == true);
+ QVERIFY(input->isReadOnly() == true);
+ QString initial = input->text();
+ for(int k=Qt::Key_0; k<=Qt::Key_Z; k++)
+ simulateKey(&canvas, k);
+ simulateKey(&canvas, Qt::Key_Return);
+ simulateKey(&canvas, Qt::Key_Space);
+ simulateKey(&canvas, Qt::Key_Escape);
+ QCOMPARE(input->text(), initial);
+}
+
+void tst_qsgtextinput::echoMode()
+{
+ QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/echoMode.qml"));
+ canvas.show();
+ canvas.setFocus();
+ QApplication::setActiveWindow(&canvas);
+ QTest::qWaitForWindowShown(&canvas);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
+
+ QVERIFY(canvas.rootObject() != 0);
+
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
+
+ QVERIFY(input != 0);
+ QTRY_VERIFY(input->hasActiveFocus() == true);
+ QString initial = input->text();
+ Qt::InputMethodHints ref;
+ QCOMPARE(initial, QLatin1String("ABCDefgh"));
+ QCOMPARE(input->echoMode(), QSGTextInput::Normal);
+ QCOMPARE(input->displayText(), input->text());
+ //Normal
+ ref &= ~Qt::ImhHiddenText;
+ ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
+ QCOMPARE(input->inputMethodHints(), ref);
+ input->setEchoMode(QSGTextInput::NoEcho);
+ QCOMPARE(input->text(), initial);
+ QCOMPARE(input->displayText(), QLatin1String(""));
+ QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
+ //NoEcho
+ ref |= Qt::ImhHiddenText;
+ ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
+ QCOMPARE(input->inputMethodHints(), ref);
+ input->setEchoMode(QSGTextInput::Password);
+ //Password
+ ref |= Qt::ImhHiddenText;
+ ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
+ QCOMPARE(input->text(), initial);
+ QCOMPARE(input->displayText(), QLatin1String("********"));
+ QCOMPARE(input->inputMethodHints(), ref);
+ input->setPasswordCharacter(QChar('Q'));
+ QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
+ QCOMPARE(input->text(), initial);
+ QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
+ input->setEchoMode(QSGTextInput::PasswordEchoOnEdit);
+ //PasswordEchoOnEdit
+ ref &= ~Qt::ImhHiddenText;
+ ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
+ QCOMPARE(input->inputMethodHints(), ref);
+ QCOMPARE(input->text(), initial);
+ QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
+ QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
+ QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QCOMPARE(input->text(), QLatin1String("a"));
+ QCOMPARE(input->displayText(), QLatin1String("a"));
+ QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
+ input->setFocus(false);
+ QVERIFY(input->hasActiveFocus() == false);
+ QCOMPARE(input->displayText(), QLatin1String("Q"));
+ QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
+ input->setFocus(true);
+ QInputMethodEvent inputEvent;
+ inputEvent.setCommitString(initial);
+ QApplication::sendEvent(&canvas, &inputEvent);
+ QCOMPARE(input->text(), initial);
+ QCOMPARE(input->displayText(), initial);
+ QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
+}
+
+void tst_qsgtextinput::simulateKey(QSGView *view, int key)
+{
+ QKeyEvent press(QKeyEvent::KeyPress, key, 0);
+ QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
+
+ QApplication::sendEvent(view, &press);
+ QApplication::sendEvent(view, &release);
+}
+
+class MyInputContext : public QInputContext
+{
+public:
+ MyInputContext() : openInputPanelReceived(false), closeInputPanelReceived(false), updateReceived(false), eventType(QEvent::None) {}
+ ~MyInputContext() {}
+
+ QString identifierName() { return QString(); }
+ QString language() { return QString(); }
+
+ void reset() {}
+
+ bool isComposing() const { return false; }
+
+ bool filterEvent( const QEvent *event )
+ {
+ if (event->type() == QEvent::RequestSoftwareInputPanel)
+ openInputPanelReceived = true;
+ if (event->type() == QEvent::CloseSoftwareInputPanel)
+ closeInputPanelReceived = true;
+ return QInputContext::filterEvent(event);
+ }
+
+ void update() { updateReceived = true; }
+
+ void mouseHandler(int x, QMouseEvent *event)
+ {
+ cursor = x;
+ eventType = event->type();
+ eventPosition = event->pos();
+ eventGlobalPosition = event->globalPos();
+ eventButton = event->button();
+ eventButtons = event->buttons();
+ eventModifiers = event->modifiers();
+ }
+
+ void sendPreeditText(const QString &text, int cursor)
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(QInputMethodEvent::Attribute(
+ QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
+
+ QInputMethodEvent event(text, attributes);
+ sendEvent(event);
+ }
+
+ bool openInputPanelReceived;
+ bool closeInputPanelReceived;
+ bool updateReceived;
+ int cursor;
+ QEvent::Type eventType;
+ QPoint eventPosition;
+ QPoint eventGlobalPosition;
+ Qt::MouseButton eventButton;
+ Qt::MouseButtons eventButtons;
+ Qt::KeyboardModifiers eventModifiers;
+};
+
+void tst_qsgtextinput::openInputPanelOnClick()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
+ MyInputContext ic;
+ // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
+ // and QWidget won't allow an input context to be set when the flag is not set.
+ view.setAttribute(Qt::WA_InputMethodEnabled, true);
+ view.setInputContext(&ic);
+ view.setAttribute(Qt::WA_InputMethodEnabled, false);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
+ QVERIFY(input);
+
+ QSGItemPrivate* pri = QSGItemPrivate::get(input);
+ QSGTextInputPrivate *inputPrivate = static_cast<QSGTextInputPrivate*>(pri);
+
+ // input panel on click
+ inputPrivate->showInputPanelOnFocus = false;
+
+ QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
+ view.style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
+ QTest::mouseClick(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QApplication::processEvents();
+ if (behavior == QStyle::RSIP_OnMouseClickAndAlreadyFocused) {
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QTest::mouseClick(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ } else if (behavior == QStyle::RSIP_OnMouseClick) {
+ QCOMPARE(ic.openInputPanelReceived, true);
+ }
+ ic.openInputPanelReceived = false;
+
+ // focus should not cause input panels to open or close
+ input->setFocus(false);
+ input->setFocus(true);
+ input->setFocus(false);
+ input->setFocus(true);
+ input->setFocus(false);
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+}
+
+void tst_qsgtextinput::openInputPanelOnFocus()
+{
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
+ MyInputContext ic;
+ // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
+ // and QWidget won't allow an input context to be set when the flag is not set.
+ view.setAttribute(Qt::WA_InputMethodEnabled, true);
+ view.setInputContext(&ic);
+ view.setAttribute(Qt::WA_InputMethodEnabled, false);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
+ QVERIFY(input);
+ QSignalSpy focusOnPressSpy(input, SIGNAL(activeFocusOnPressChanged(bool)));
+
+ QSGItemPrivate* pri = QSGItemPrivate::get(input);
+ QSGTextInputPrivate *inputPrivate = static_cast<QSGTextInputPrivate*>(pri);
+ inputPrivate->showInputPanelOnFocus = true;
+
+ // test default values
+ QVERIFY(input->focusOnPress());
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+
+ // focus on press, input panel on focus
+ QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QApplication::processEvents();
+ QVERIFY(input->hasActiveFocus());
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+
+ // no events on release
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QCOMPARE(ic.openInputPanelReceived, false);
+ ic.openInputPanelReceived = false;
+
+ // if already focused, input panel can be opened on press
+ QVERIFY(input->hasActiveFocus());
+ QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+
+ // input method should stay enabled if focus
+ // is lost to an item that also accepts inputs
+ QSGTextInput anotherInput;
+ anotherInput.setParentItem(view.rootItem());
+ anotherInput.setFocus(true);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+ QCOMPARE(view.inputContext(), (QInputContext*)&ic);
+ QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // input method should be disabled if focus
+ // is lost to an item that doesn't accept inputs
+ QSGItem item;
+ item.setParentItem(view.rootItem());
+ item.setFocus(true);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QVERIFY(view.inputContext() == 0);
+ QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // no automatic input panel events should
+ // be sent if activeFocusOnPress is false
+ input->setFocusOnPress(false);
+ QCOMPARE(focusOnPressSpy.count(),1);
+ input->setFocusOnPress(false);
+ QCOMPARE(focusOnPressSpy.count(),1);
+ input->setFocus(false);
+ input->setFocus(true);
+ QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, input->pos().toPoint());
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+
+ // one show input panel event should
+ // be set when openSoftwareInputPanel is called
+ input->openSoftwareInputPanel();
+ QCOMPARE(ic.openInputPanelReceived, true);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+ ic.openInputPanelReceived = false;
+
+ // one close input panel event should
+ // be sent when closeSoftwareInputPanel is called
+ input->closeSoftwareInputPanel();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, true);
+ ic.closeInputPanelReceived = false;
+
+ // set activeFocusOnPress back to true
+ input->setFocusOnPress(true);
+ QCOMPARE(focusOnPressSpy.count(),2);
+ input->setFocusOnPress(true);
+ QCOMPARE(focusOnPressSpy.count(),2);
+ input->setFocus(false);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QCOMPARE(ic.closeInputPanelReceived, false);
+ ic.closeInputPanelReceived = false;
+
+ // input panel should not re-open
+ // if focus has already been set
+ input->setFocus(true);
+ QCOMPARE(ic.openInputPanelReceived, true);
+ ic.openInputPanelReceived = false;
+ input->setFocus(true);
+ QCOMPARE(ic.openInputPanelReceived, false);
+
+ // input method should be disabled
+ // if TextInput loses focus
+ input->setFocus(false);
+ QApplication::processEvents();
+ QVERIFY(view.inputContext() == 0);
+ QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
+
+ // input method should not be enabled
+ // if TextEdit is read only.
+ input->setReadOnly(true);
+ ic.openInputPanelReceived = false;
+ input->setFocus(true);
+ QApplication::processEvents();
+ QCOMPARE(ic.openInputPanelReceived, false);
+ QVERIFY(view.inputContext() == 0);
+ QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
+}
+
+class MyTextInput : public QSGTextInput
+{
+public:
+ MyTextInput(QSGItem *parent = 0) : QSGTextInput(parent)
+ {
+ nbPaint = 0;
+ }
+ void paint(QPainter *painter)
+ {
+ nbPaint++;
+ QSGTextInput::paint(painter);
+ }
+ int nbPaint;
+};
+
+void tst_qsgtextinput::setHAlignClearCache()
+{
+ QSGView view;
+ MyTextInput input;
+ input.setText("Hello world");
+ input.setParentItem(view.rootItem());
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(input.nbPaint, 1);
+ input.setHAlign(QSGTextInput::AlignRight);
+ QApplication::processEvents();
+ //Changing the alignment should trigger a repaint
+ QCOMPARE(input.nbPaint, 2);
+}
+
+void tst_qsgtextinput::focusOutClearSelection()
+{
+ QSGView view;
+ QSGTextInput input;
+ QSGTextInput input2;
+ input.setText(QLatin1String("Hello world"));
+ input.setFocus(true);
+ input2.setParentItem(view.rootItem());
+ input.setParentItem(view.rootItem());
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ input.select(2,5);
+ //The selection should work
+ QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
+ input2.setFocus(true);
+ QApplication::processEvents();
+ //The input lost the focus selection should be cleared
+ QTRY_COMPARE(input.selectedText(), QLatin1String(""));
+}
+
+void tst_qsgtextinput::geometrySignals()
+{
+ QDeclarativeComponent component(&engine, SRCDIR "/data/geometrySignals.qml");
+ QObject *o = component.create();
+ QVERIFY(o);
+ QCOMPARE(o->property("bindingWidth").toInt(), 400);
+ QCOMPARE(o->property("bindingHeight").toInt(), 500);
+ delete o;
+}
+
+void tst_qsgtextinput::testQtQuick11Attributes()
+{
+ QFETCH(QString, code);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ QDeclarativeEngine engine;
+ QObject *obj;
+
+ QDeclarativeComponent valid(&engine);
+ valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
+ obj = valid.create();
+ QVERIFY(obj);
+ QVERIFY(valid.errorString().isEmpty());
+ delete obj;
+
+ QDeclarativeComponent invalid(&engine);
+ invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
+ obj = invalid.create();
+ QCOMPARE(invalid.errorString(), error);
+ delete obj;
+}
+
+void tst_qsgtextinput::testQtQuick11Attributes_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("canPaste") << "property bool foo: canPaste"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
+ << "";
+
+ QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
+ << "";
+
+ QTest::newRow("deselect") << "Component.onCompleted: deselect()"
+ << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
+ << "";
+}
+
+void tst_qsgtextinput::preeditAutoScroll()
+{
+ QString preeditText = "califragisiticexpialidocious!";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/preeditAutoScroll.qml"));
+ MyInputContext ic;
+ view.setInputContext(&ic);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
+ QVERIFY(input);
+
+ QFontMetricsF fm(input->font());
+ input->setWidth(fm.width(input->text()));
+
+ // test the text is scrolled so the preedit is visible.
+ ic.sendPreeditText(preeditText.mid(0, 3), 1);
+ QVERIFY(input->positionAt(0) != 0);
+ QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
+
+ // test the text is scrolled back when the preedit is removed.
+ ic.sendEvent(QInputMethodEvent());
+ QCOMPARE(input->positionAt(0), 0);
+ QCOMPARE(input->positionAt(input->width()), 5);
+
+ // test if the preedit is larger than the text input that the
+ // character preceding the cursor is still visible.
+ qreal x = input->positionToRectangle(0).x();
+ for (int i = 0; i < 3; ++i) {
+ ic.sendPreeditText(preeditText, i + 1);
+ QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)));
+ QVERIFY(input->positionToRectangle(0).x() < x);
+ x = input->positionToRectangle(0).x();
+ }
+ for (int i = 1; i >= 0; --i) {
+ ic.sendPreeditText(preeditText, i + 1);
+ QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)));
+ QVERIFY(input->positionToRectangle(0).x() > x);
+ x = input->positionToRectangle(0).x();
+ }
+
+ // Test incrementing the preedit cursor doesn't cause further
+ // scrolling when right most text is visible.
+ ic.sendPreeditText(preeditText, preeditText.length() - 3);
+ x = input->positionToRectangle(0).x();
+ for (int i = 2; i >= 0; --i) {
+ ic.sendPreeditText(preeditText, preeditText.length() - i);
+ QCOMPARE(input->positionToRectangle(0).x(), x);
+ }
+ for (int i = 1; i < 3; ++i) {
+ ic.sendPreeditText(preeditText, preeditText.length() - i);
+ QCOMPARE(input->positionToRectangle(0).x(), x);
+ }
+
+ // Test disabling auto scroll.
+ ic.sendEvent(QInputMethodEvent());
+
+ input->setAutoScroll(false);
+ ic.sendPreeditText(preeditText.mid(0, 3), 1);
+ QCOMPARE(input->positionAt(0), 0);
+ QCOMPARE(input->positionAt(input->width()), 5);
+}
+
+void tst_qsgtextinput::preeditMicroFocus()
+{
+ QString preeditText = "super";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputMethodEvent.qml"));
+ MyInputContext ic;
+ view.setInputContext(&ic);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
+ QVERIFY(input);
+
+ QRect currentRect;
+ QRect previousRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
+
+ // Verify that the micro focus rect is positioned the same for position 0 as
+ // it would be if there was no preedit text.
+ ic.updateReceived = false;
+ ic.sendPreeditText(preeditText, 0);
+ currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QCOMPARE(currentRect, previousRect);
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
+ QCOMPARE(ic.updateReceived, true);
+#endif
+
+ // Verify that the micro focus rect moves to the left as the cursor position
+ // is incremented.
+ for (int i = 1; i <= 5; ++i) {
+ ic.updateReceived = false;
+ ic.sendPreeditText(preeditText, i);
+ currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QVERIFY(previousRect.left() < currentRect.left());
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
+ QCOMPARE(ic.updateReceived, true);
+#endif
+ previousRect = currentRect;
+ }
+
+ // Verify that if there is no preedit cursor then the micro focus rect is the
+ // same as it would be if it were positioned at the end of the preedit text.
+ ic.sendPreeditText(preeditText, 0);
+ ic.updateReceived = false;
+ ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
+ currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QCOMPARE(currentRect, previousRect);
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
+ QCOMPARE(ic.updateReceived, true);
+#endif
+}
+
+void tst_qsgtextinput::inputContextMouseHandler()
+{
+ QString text = "supercalifragisiticexpialidocious!";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
+ MyInputContext ic;
+ view.setInputContext(&ic);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
+ QVERIFY(input);
+
+ QFontMetricsF fm(input->font());
+ const qreal y = fm.height() / 2;
+
+ QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
+ QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
+ QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
+ QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
+ QPoint globalPosition2 = view.mapToGlobal(position2);
+ QPoint globalposition8 = view.mapToGlobal(position8);
+ QPoint globalposition20 = view.mapToGlobal(position20);
+ QPoint globalposition27 = view.mapToGlobal(position27);
+
+ ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
+
+ QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
+ QCOMPARE(ic.eventPosition, position2);
+ QCOMPARE(ic.eventGlobalPosition, globalPosition2);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor < 0);
+ ic.eventType = QEvent::None;
+
+ QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
+ QCOMPARE(ic.eventPosition, position2);
+ QCOMPARE(ic.eventGlobalPosition, globalPosition2);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor < 0);
+ ic.eventType = QEvent::None;
+
+ { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::None);
+
+ { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::MouseMove);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
+ ic.eventType = QEvent::None;
+
+ QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::NoModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
+ ic.eventType = QEvent::None;
+
+ // And in the other direction.
+ QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::LeftButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
+ ic.eventType = QEvent::None;
+
+ QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
+ QCOMPARE(ic.eventPosition, position27);
+ QCOMPARE(ic.eventGlobalPosition, globalposition27);
+ QCOMPARE(ic.eventButton, Qt::RightButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
+ ic.eventType = QEvent::None;
+
+ { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::MouseMove);
+ QCOMPARE(ic.eventPosition, position20);
+ QCOMPARE(ic.eventGlobalPosition, globalposition20);
+ QCOMPARE(ic.eventButton, Qt::RightButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
+ ic.eventType = QEvent::None;
+
+ { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
+ QApplication::sendEvent(&view, &mv); }
+ QCOMPARE(ic.eventType, QEvent::None);
+
+ QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
+ QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
+ QCOMPARE(ic.eventPosition, position2);
+ QCOMPARE(ic.eventGlobalPosition, globalPosition2);
+ QCOMPARE(ic.eventButton, Qt::RightButton);
+ QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
+ QVERIFY(ic.cursor < 0);
+ ic.eventType = QEvent::None;
+}
+
+void tst_qsgtextinput::inputMethodComposing()
+{
+ QString text = "supercalifragisiticexpialidocious!";
+
+ QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
+ QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
+ QVERIFY(input);
+ QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
+
+ QCOMPARE(input->isInputMethodComposing(), false);
+ {
+ QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
+ QApplication::sendEvent(&view, &event);
+ }
+ QCOMPARE(input->isInputMethodComposing(), true);
+ QCOMPARE(spy.count(), 1);
+
+ {
+ QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
+ QApplication::sendEvent(&view, &event);
+ }
+ QCOMPARE(spy.count(), 1);
+
+ {
+ QInputMethodEvent event;
+ QApplication::sendEvent(&view, &event);
+ }
+ QCOMPARE(input->isInputMethodComposing(), false);
+ QCOMPARE(spy.count(), 2);
+}
+
+void tst_qsgtextinput::cursorRectangleSize()
+{
+ QSGView *canvas = new QSGView(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
+ QVERIFY(canvas->rootObject() != 0);
+ canvas->show();
+ canvas->setFocus();
+ QApplication::setActiveWindow(canvas);
+ QTest::qWaitForWindowShown(canvas);
+
+ QSGTextInput *textInput = qobject_cast<QSGTextInput *>(canvas->rootObject());
+ QVERIFY(textInput != 0);
+ textInput->setFocus(Qt::OtherFocusReason);
+ QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
+ QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImMicroFocus).toRectF();
+ QRectF microFocusFromApp= QApplication::focusWidget()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
+
+ QCOMPARE(microFocusFromScene.size(), cursorRect.size());
+ QCOMPARE(microFocusFromApp.size(), cursorRect.size());
+
+ delete canvas;
+}
+
+QTEST_MAIN(tst_qsgtextinput)
+
+#include "tst_qsgtextinput.moc"
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/datalist.qml b/tests/auto/declarative/qsgvisualdatamodel/data/datalist.qml
new file mode 100644
index 0000000000..8ce59caddc
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/datalist.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ model: VisualDataModel {
+ id: visualModel
+ objectName: "visualModel"
+ model: myModel
+ delegate: Component {
+ Rectangle {
+ height: 25
+ width: 100
+ Text { objectName: "display"; text: display }
+ }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/modelproperties.qml b/tests/auto/declarative/qsgvisualdatamodel/data/modelproperties.qml
new file mode 100644
index 0000000000..6d86cdea2e
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/modelproperties.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ model: myModel
+ delegate: Item {
+ objectName: "delegate"
+ property variant test1: name
+ property variant test2: model.name
+ property variant test3: modelData
+ property variant test4: model.modelData
+ property variant test5: modelData.name
+ property variant test6: model
+ property variant test7: index
+ property variant test8: model.index
+ property variant test9: model.modelData.name
+ }
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/modelproperties2.qml b/tests/auto/declarative/qsgvisualdatamodel/data/modelproperties2.qml
new file mode 100644
index 0000000000..6a92431cdf
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/modelproperties2.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ model: myModel
+ delegate: Item {
+ objectName: "delegate"
+ property variant test1: display
+ property variant test2: model.display
+ property variant test3: modelData
+ property variant test4: model.modelData
+ property variant test5: modelData.display
+ property variant test6: model
+ property variant test7: index
+ property variant test8: model.index
+ property variant test9: model.modelData.display
+ }
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/objectlist.qml b/tests/auto/declarative/qsgvisualdatamodel/data/objectlist.qml
new file mode 100644
index 0000000000..9086e5ab57
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/objectlist.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ anchors.fill: parent
+ model: myModel
+ delegate: Component {
+ Rectangle {
+ height: 25
+ width: 100
+ color: model.modelData.color
+ Text { objectName: "name"; text: name }
+ Text { objectName: "section"; text: parent.ListView.section }
+ }
+ }
+ section.property: "name"
+ section.criteria: ViewSection.FullString
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/singlerole1.qml b/tests/auto/declarative/qsgvisualdatamodel/data/singlerole1.qml
new file mode 100644
index 0000000000..d5b0fcf09b
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/singlerole1.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ model: myModel
+ delegate: Component {
+ Text { objectName: "name"; text: name }
+ }
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/singlerole2.qml b/tests/auto/declarative/qsgvisualdatamodel/data/singlerole2.qml
new file mode 100644
index 0000000000..c6d3413dfd
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/singlerole2.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ model: myModel
+ delegate: Component {
+ Text { objectName: "name"; text: modelData }
+ }
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/visualdatamodel.qml b/tests/auto/declarative/qsgvisualdatamodel/data/visualdatamodel.qml
new file mode 100644
index 0000000000..0bb5cd13e0
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/visualdatamodel.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+VisualDataModel {
+ function setRoot() {
+ rootIndex = modelIndex(0);
+ }
+ function setRootToParent() {
+ rootIndex = parentModelIndex();
+ }
+ model: myModel
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro b/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro
new file mode 100644
index 0000000000..7d0df4dc7b
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qsgvisualdatamodel.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp
new file mode 100644
index 0000000000..23e629538d
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp
@@ -0,0 +1,531 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QtTest/QSignalSpy>
+#include <QStandardItemModel>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qsgview.h>
+#include <private/qsglistview_p.h>
+#include <private/qsgtext_p.h>
+#include <private/qsgvisualitemmodel_p.h>
+#include <private/qdeclarativevaluetype_p.h>
+#include <math.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+static void initStandardTreeModel(QStandardItemModel *model)
+{
+ QStandardItem *item;
+ item = new QStandardItem(QLatin1String("Row 1 Item"));
+ model->insertRow(0, item);
+
+ item = new QStandardItem(QLatin1String("Row 2 Item"));
+ item->setCheckable(true);
+ model->insertRow(1, item);
+
+ QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
+ item->setChild(0, childItem);
+
+ item = new QStandardItem(QLatin1String("Row 3 Item"));
+ item->setIcon(QIcon());
+ model->insertRow(2, item);
+}
+
+class SingleRoleModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ SingleRoleModel(QObject *parent = 0) {
+ QHash<int, QByteArray> roles;
+ roles.insert(Qt::DisplayRole , "name");
+ setRoleNames(roles);
+ list << "one" << "two" << "three" << "four";
+ }
+
+public slots:
+ void set(int idx, QString string) {
+ list[idx] = string;
+ emit dataChanged(index(idx,0), index(idx,0));
+ }
+
+protected:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const {
+ return list.count();
+ }
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
+ if (role == Qt::DisplayRole)
+ return list.at(index.row());
+ return QVariant();
+ }
+
+private:
+ QStringList list;
+};
+
+
+class tst_qsgvisualdatamodel : public QObject
+{
+ Q_OBJECT
+public:
+ tst_qsgvisualdatamodel();
+
+private slots:
+ void rootIndex();
+ void updateLayout();
+ void childChanged();
+ void objectListModel();
+ void singleRole();
+ void modelProperties();
+ void noDelegate();
+
+private:
+ QDeclarativeEngine engine;
+ template<typename T>
+ T *findItem(QSGItem *parent, const QString &objectName, int index);
+};
+
+class DataObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
+
+public:
+ DataObject(QObject *parent=0) : QObject(parent) {}
+ DataObject(const QString &name, const QString &color, QObject *parent=0)
+ : QObject(parent), m_name(name), m_color(color) { }
+
+
+ QString name() const { return m_name; }
+ void setName(const QString &name) {
+ if (name != m_name) {
+ m_name = name;
+ emit nameChanged();
+ }
+ }
+
+ QString color() const { return m_color; }
+ void setColor(const QString &color) {
+ if (color != m_color) {
+ m_color = color;
+ emit colorChanged();
+ }
+ }
+
+signals:
+ void nameChanged();
+ void colorChanged();
+
+private:
+ QString m_name;
+ QString m_color;
+};
+
+tst_qsgvisualdatamodel::tst_qsgvisualdatamodel()
+{
+}
+
+void tst_qsgvisualdatamodel::rootIndex()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/visualdatamodel.qml"));
+
+ QStandardItemModel model;
+ initStandardTreeModel(&model);
+
+ engine.rootContext()->setContextProperty("myModel", &model);
+
+ QSGVisualDataModel *obj = qobject_cast<QSGVisualDataModel*>(c.create());
+ QVERIFY(obj != 0);
+
+ QMetaObject::invokeMethod(obj, "setRoot");
+ QVERIFY(qvariant_cast<QModelIndex>(obj->rootIndex()) == model.index(0,0));
+
+ QMetaObject::invokeMethod(obj, "setRootToParent");
+ QVERIFY(qvariant_cast<QModelIndex>(obj->rootIndex()) == QModelIndex());
+
+ delete obj;
+}
+
+void tst_qsgvisualdatamodel::updateLayout()
+{
+ QSGView view;
+
+ QStandardItemModel model;
+ initStandardTreeModel(&model);
+
+ view.rootContext()->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/datalist.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "display", 0);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 1 Item"));
+ name = findItem<QSGText>(contentItem, "display", 1);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 2 Item"));
+ name = findItem<QSGText>(contentItem, "display", 2);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 3 Item"));
+
+ model.invisibleRootItem()->sortChildren(0, Qt::DescendingOrder);
+
+ name = findItem<QSGText>(contentItem, "display", 0);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 3 Item"));
+ name = findItem<QSGText>(contentItem, "display", 1);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 2 Item"));
+ name = findItem<QSGText>(contentItem, "display", 2);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 1 Item"));
+}
+
+void tst_qsgvisualdatamodel::childChanged()
+{
+ QSGView view;
+
+ QStandardItemModel model;
+ initStandardTreeModel(&model);
+
+ view.rootContext()->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/datalist.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGVisualDataModel *vdm = listview->findChild<QSGVisualDataModel*>("visualModel");
+ vdm->setRootIndex(QVariant::fromValue(model.indexFromItem(model.item(1,0))));
+
+ QSGText *name = findItem<QSGText>(contentItem, "display", 0);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 2 Child Item"));
+
+ model.item(1,0)->child(0,0)->setText("Row 2 updated child");
+
+ name = findItem<QSGText>(contentItem, "display", 0);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 2 updated child"));
+
+ model.item(1,0)->appendRow(new QStandardItem(QLatin1String("Row 2 Child Item 2")));
+ QTest::qWait(300);
+
+ name = findItem<QSGText>(contentItem, "display", 1);
+ QVERIFY(name != 0);
+ QCOMPARE(name->text(), QString("Row 2 Child Item 2"));
+
+ model.item(1,0)->takeRow(1);
+ name = findItem<QSGText>(contentItem, "display", 1);
+ QVERIFY(name == 0);
+
+ vdm->setRootIndex(QVariant::fromValue(QModelIndex()));
+ QTest::qWait(300);
+ name = findItem<QSGText>(contentItem, "display", 0);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 1 Item"));
+ name = findItem<QSGText>(contentItem, "display", 1);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 2 Item"));
+ name = findItem<QSGText>(contentItem, "display", 2);
+ QVERIFY(name);
+ QCOMPARE(name->text(), QString("Row 3 Item"));
+}
+
+void tst_qsgvisualdatamodel::objectListModel()
+{
+ QSGView view;
+
+ QList<QObject*> dataList;
+ dataList.append(new DataObject("Item 1", "red"));
+ dataList.append(new DataObject("Item 2", "green"));
+ dataList.append(new DataObject("Item 3", "blue"));
+ dataList.append(new DataObject("Item 4", "yellow"));
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/objectlist.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "name", 0);
+ QCOMPARE(name->text(), QString("Item 1"));
+
+ QSGText *section = findItem<QSGText>(contentItem, "section", 0);
+ QCOMPARE(section->text(), QString("Item 1"));
+
+ dataList[0]->setProperty("name", QLatin1String("Changed"));
+ QCOMPARE(name->text(), QString("Changed"));
+}
+
+void tst_qsgvisualdatamodel::singleRole()
+{
+ {
+ QSGView view;
+
+ SingleRoleModel model;
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/singlerole1.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "name", 1);
+ QCOMPARE(name->text(), QString("two"));
+
+ model.set(1, "Changed");
+ QCOMPARE(name->text(), QString("Changed"));
+ }
+ {
+ QSGView view;
+
+ SingleRoleModel model;
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/singlerole2.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGText *name = findItem<QSGText>(contentItem, "name", 1);
+ QCOMPARE(name->text(), QString("two"));
+
+ model.set(1, "Changed");
+ QCOMPARE(name->text(), QString("Changed"));
+ }
+}
+
+void tst_qsgvisualdatamodel::modelProperties()
+{
+ {
+ QSGView view;
+
+ SingleRoleModel model;
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/modelproperties.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", 1);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(),QString("two"));
+ QCOMPARE(delegate->property("test2").toString(),QString("two"));
+ QCOMPARE(delegate->property("test3").toString(),QString("two"));
+ QCOMPARE(delegate->property("test4").toString(),QString("two"));
+ QVERIFY(!delegate->property("test9").isValid());
+ QCOMPARE(delegate->property("test5").toString(),QString(""));
+ QVERIFY(delegate->property("test6").value<QObject*>() != 0);
+ QCOMPARE(delegate->property("test7").toInt(),1);
+ QCOMPARE(delegate->property("test8").toInt(),1);
+ }
+
+ {
+ QSGView view;
+
+ QList<QObject*> dataList;
+ dataList.append(new DataObject("Item 1", "red"));
+ dataList.append(new DataObject("Item 2", "green"));
+ dataList.append(new DataObject("Item 3", "blue"));
+ dataList.append(new DataObject("Item 4", "yellow"));
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/modelproperties.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", 1);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(),QString("Item 2"));
+ QEXPECT_FAIL("", "QTBUG-13576", Continue);
+ QCOMPARE(delegate->property("test2").toString(),QString("Item 2"));
+ QVERIFY(qobject_cast<DataObject*>(delegate->property("test3").value<QObject*>()) != 0);
+ QVERIFY(qobject_cast<DataObject*>(delegate->property("test4").value<QObject*>()) != 0);
+ QCOMPARE(delegate->property("test5").toString(),QString("Item 2"));
+ QCOMPARE(delegate->property("test9").toString(),QString("Item 2"));
+ QVERIFY(delegate->property("test6").value<QObject*>() != 0);
+ QCOMPARE(delegate->property("test7").toInt(),1);
+ QCOMPARE(delegate->property("test8").toInt(),1);
+ }
+
+ {
+ QSGView view;
+
+ QStandardItemModel model;
+ initStandardTreeModel(&model);
+
+ view.rootContext()->setContextProperty("myModel", &model);
+
+ QUrl source(QUrl::fromLocalFile(SRCDIR "/data/modelproperties2.qml"));
+
+ //3 items, 3 warnings each
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: ReferenceError: Can't find variable: modelData");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: ReferenceError: Can't find variable: modelData");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: ReferenceError: Can't find variable: modelData");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":11: ReferenceError: Can't find variable: modelData");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":11: ReferenceError: Can't find variable: modelData");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":11: ReferenceError: Can't find variable: modelData");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":17: TypeError: Result of expression 'model.modelData' [undefined] is not an object.");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":17: TypeError: Result of expression 'model.modelData' [undefined] is not an object.");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":17: TypeError: Result of expression 'model.modelData' [undefined] is not an object.");
+
+ view.setSource(source);
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", 1);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(),QString("Row 2 Item"));
+ QCOMPARE(delegate->property("test2").toString(),QString("Row 2 Item"));
+ QVERIFY(!delegate->property("test3").isValid());
+ QVERIFY(!delegate->property("test4").isValid());
+ QVERIFY(!delegate->property("test5").isValid());
+ QVERIFY(!delegate->property("test9").isValid());
+ QVERIFY(delegate->property("test6").value<QObject*>() != 0);
+ QCOMPARE(delegate->property("test7").toInt(),1);
+ QCOMPARE(delegate->property("test8").toInt(),1);
+ }
+
+ //### should also test QStringList and QVariantList
+}
+
+void tst_qsgvisualdatamodel::noDelegate()
+{
+ QSGView view;
+
+ QStandardItemModel model;
+ initStandardTreeModel(&model);
+
+ view.rootContext()->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/datalist.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGVisualDataModel *vdm = listview->findChild<QSGVisualDataModel*>("visualModel");
+ QVERIFY(vdm != 0);
+ QCOMPARE(vdm->count(), 3);
+
+ vdm->setDelegate(0);
+ QCOMPARE(vdm->count(), 0);
+}
+
+
+template<typename T>
+T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index)
+{
+ const QMetaObject &mo = T::staticMetaObject;
+ //qDebug() << parent->childItems().count() << "children";
+ for (int i = 0; i < parent->childItems().count(); ++i) {
+ QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
+ if(!item)
+ continue;
+ //qDebug() << "try" << item;
+ if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
+ if (index != -1) {
+ QDeclarativeExpression e(qmlContext(item), item, "index");
+ if (e.evaluate().toInt() == index)
+ return static_cast<T*>(item);
+ } else {
+ return static_cast<T*>(item);
+ }
+ }
+ item = findItem<T>(item, objectName, index);
+ if (item)
+ return static_cast<T*>(item);
+ }
+
+ return 0;
+}
+
+QTEST_MAIN(tst_qsgvisualdatamodel)
+
+#include "tst_qsgvisualdatamodel.moc"