diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-01-15 14:47:35 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-01-23 15:58:10 +0100 |
commit | 684f9df7849bc79f1f02a60844fb43c7a3927d2f (patch) | |
tree | 531be4d102388395e4ddd2d14f38a679e15c283d /tests/auto | |
parent | 020a6e67766595351bcf911e965b26952a7c81b8 (diff) |
Long live QML inline components
[ChangeLog][QtQml] It is now possible to declare new QML components in
a QML file via the component keyword. They can be used just as if they
were declared in another file, with the only difference that the type
name needs to be prefixed with the name of the containing type outside
of the file were the inline component has been declared.
Notably, inline components are not closures: In the following
example, the output would be 42
// MyItem.qml
Item {
property int i: 33
component IC: Item {
Component.onCompleted: console.log(i)
}
}
// user.qml
Item {
property int i: 42
MyItem.IC {}
}
Fixes: QTBUG-79382
Change-Id: I6a5ffc43f093a76323f435cfee9bab217781b8f5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests/auto')
28 files changed, 343 insertions, 5 deletions
diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml new file mode 100644 index 0000000000..0b297a7779 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml @@ -0,0 +1,9 @@ +import QtQml 2.15 + +QtObject { + property alias i: icInstance.i + component IC : QtObject { + property int i: 42 + } + property QtObject ic: IC {id: icInstance} +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml new file mode 100644 index 0000000000..49a90ab7da --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml @@ -0,0 +1,7 @@ +import QtQml 2.15 + +InlineComponentBase { + component IC : QtObject { + property int i: 13 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml new file mode 100644 index 0000000000..6058e32b2f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "red" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml new file mode 100644 index 0000000000..b5a0efaccf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.15 + +Item { + property color myColor: "red" + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: myColor + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml new file mode 100644 index 0000000000..9a4f1fd272 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +Item { + Item { + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "red" + } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml new file mode 100644 index 0000000000..ddb55e55e6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml @@ -0,0 +1 @@ +InlineComponentProvider {} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml new file mode 100644 index 0000000000..24bf6f771e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +QtObject { + component StyledRectangle: InlineComponentProvider.StyledRectangle { + color: "green" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml b/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml new file mode 100644 index 0000000000..c5aa4cfdf5 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component A : Item { + property var test: B {} + } + component B: A {} + A {} +} diff --git a/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml b/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml new file mode 100644 index 0000000000..69e74f7c96 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml @@ -0,0 +1,6 @@ +import QtQuick 2.15 + +Item { + component A : B {} + component B: A {} +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml new file mode 100644 index 0000000000..a96f68e56a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml @@ -0,0 +1,20 @@ +import QtQuick 2.15 + +Item { + width: 600 + height: 480 + IC2 { + objectName: "icInstance" + anchors.centerIn: parent + } + + component IC2: IC1 {} + component IC0: Rectangle { + height: 200 + width: 200 + color: "blue" + } + component IC1: IC0 {} + + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml new file mode 100644 index 0000000000..8968a20112 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml @@ -0,0 +1,13 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property InlineComponentProvider.StyledRectangle myProp: InlineComponentProvider.StyledRectangle {} + InlineComponentProvider.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + color: "blue" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml new file mode 100644 index 0000000000..dc6e3850db --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property InlineComponentProvider.StyledRectangle myProp: InlineComponentReexporter.StyledRectangle { + objectName: "icInstance" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml new file mode 100644 index 0000000000..c57c4cad01 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml @@ -0,0 +1,16 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "blue" + } + StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml new file mode 100644 index 0000000000..9f3903c8df --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml @@ -0,0 +1,12 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property color myColor: "blue" + InlineComponentProvider2.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml new file mode 100644 index 0000000000..150d0c2ded --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +Item { + width: 600 + height: 480 + property var test: InlineComponentProvider3.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt index 3b90f573a2..41cb0eaac1 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt @@ -1 +1 @@ -3:1:Bar.Item - Bar is not a namespace +3:1:Bar.Item - Bar is neither a type nor a namespace diff --git a/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt index 3b90f573a2..41cb0eaac1 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt @@ -1 +1 @@ -3:1:Bar.Item - Bar is not a namespace +3:1:Bar.Item - Bar is neither a type nor a namespace diff --git a/tests/auto/qml/qqmllanguage/data/nestedIC.qml b/tests/auto/qml/qqmllanguage/data/nestedIC.qml new file mode 100644 index 0000000000..04cef64d54 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nestedIC.qml @@ -0,0 +1,7 @@ +import QtQuick 2.15 + +Item { + component Outer : Item { + component Inner : Item {} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml new file mode 100644 index 0000000000..a07a6a9838 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property InlineComponentProvider.NonExisting myProp +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml new file mode 100644 index 0000000000..5c24962def --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + InlineComponentProvider.NotExisting {} +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml new file mode 100644 index 0000000000..34595707fc --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml @@ -0,0 +1,3 @@ +import QtQuick 2.15 + +InlineComponentProvider.NotExisting {} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml new file mode 100644 index 0000000000..2be01ccd96 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "singleton" as MySingleton + +Item { + property MySingleton.SingletonTypeWithIC.NonExisting singletonIC +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml new file mode 100644 index 0000000000..a2ca5db6de --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property InlineComponentProviderChild.StyledRectangle myProp +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml b/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml new file mode 100644 index 0000000000..bd724c4aeb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 +pragma Singleton + +Item { + id: singletonId + component IC1: Item { + property int iProp: 42 + property string sProp: "Hello, world" + property Rectangle myRect: Rectangle {color: "green"} + } + component IC2: Item { + property int iProp: 13 + property string sProp: "Goodbye, world" + property Rectangle myRect: Rectangle {color: "red"} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/qmldir b/tests/auto/qml/qqmllanguage/data/singleton/qmldir index 533fb6999a..727b09b4e8 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/qmldir +++ b/tests/auto/qml/qqmllanguage/data/singleton/qmldir @@ -1,3 +1,4 @@ singleton SingletonType SingletonType.qml +singleton SingletonTypeWithIC SingletonTypeWithIC.qml diff --git a/tests/auto/qml/qqmllanguage/data/singletonICTest.qml b/tests/auto/qml/qqmllanguage/data/singletonICTest.qml new file mode 100644 index 0000000000..d0d3b079be --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singletonICTest.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "singleton" as MySingleton + +Item { + property MySingleton.SingletonTypeWithIC.IC1 singleton1: MySingleton.SingletonTypeWithIC.IC1 {}; +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 089daf3ed5..4d2f773dbf 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -306,6 +306,16 @@ private slots: void extendedForeignTypes(); + void inlineComponent(); + void inlineComponent_data(); + void inlineComponentReferenceCycle_data(); + void inlineComponentReferenceCycle(); + void nestedInlineComponentNotAllowed(); + void inlineComponentStaticTypeResolution(); + void inlineComponentInSingleton(); + void nonExistingInlineComponent_data(); + void nonExistingInlineComponent(); + void selfReference(); void selfReferencingSingleton(); @@ -2780,12 +2790,12 @@ void tst_qqmllanguage::importsLocal_data() QTest::newRow("local import QTBUG-7721 A") << "subdir.Test {}" // no longer allowed (QTBUG-7721) << "" - << "subdir.Test - subdir is not a namespace"; + << "subdir.Test - subdir is neither a type nor a namespace"; QTest::newRow("local import QTBUG-7721 B") << "import \"subdir\" as X\n" "X.subsubdir.SubTest {}" // no longer allowed (QTBUG-7721) << "" - << "X.subsubdir.SubTest - nested namespaces not allowed"; + << "X.subsubdir.SubTest - subsubdir is not a type"; QTest::newRow("local import as") << "import \"subdir\" as T\n" "T.Test {}" @@ -5408,6 +5418,128 @@ void tst_qqmllanguage::overrideSingleton() check("uncreatable", "UncreatableSingleton"); } +void tst_qqmllanguage::inlineComponent() +{ + QFETCH(QUrl, componentUrl); + QFETCH(QColor, color); + QFETCH(int, width); + QQmlEngine engine; + QQmlComponent component(&engine, componentUrl); + QScopedPointer<QObject> o(component.create()); + if (component.isError()) { + qDebug() << component.errorString(); + } + QVERIFY(!o.isNull()); + auto icInstance = o->findChild<QObject *>("icInstance"); + QVERIFY(icInstance); + QCOMPARE(icInstance->property("color").value<QColor>(),color); + QCOMPARE(icInstance->property("width").value<qreal>(), width); +} + +void tst_qqmllanguage::inlineComponent_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QColor>("color"); + QTest::addColumn<int>("width"); + + QTest::newRow("Usage from other component") << testFileUrl("inlineComponentUser1.qml") << QColorConstants::Blue << 24; + QTest::newRow("Reexport") << testFileUrl("inlineComponentUser2.qml") << QColorConstants::Svg::green << 24; + QTest::newRow("Usage in same component") << testFileUrl("inlineComponentUser3.qml") << QColorConstants::Blue << 24; + + QTest::newRow("Resolution happens at instantiation") << testFileUrl("inlineComponentUser4.qml") << QColorConstants::Blue << 24; + QTest::newRow("Non-toplevel IC is found") << testFileUrl("inlineComponentUser5.qml") << QColorConstants::Svg::red << 24; + + QTest::newRow("Resolved in correct order") << testFileUrl("inlineComponentOrder.qml") << QColorConstants::Blue << 200; +} + +void tst_qqmllanguage::inlineComponentReferenceCycle_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + + QTest::newRow("Simple cycle") << testFileUrl("icSimpleCycle.qml"); + QTest::newRow("Via property") << testFileUrl("icCycleViaProperty.qml"); +} + +void tst_qqmllanguage::inlineComponentReferenceCycle() +{ + QFETCH(QUrl, componentUrl); + QQmlEngine engine; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent component(&engine, componentUrl); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o.isNull()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), componentUrl.toString() + QLatin1String(":-1 Inline components form a cycle!\n")); +} + +void tst_qqmllanguage::nestedInlineComponentNotAllowed() +{ + QQmlEngine engine; + auto url = testFileUrl("nestedIC.qml"); + QQmlComponent component(&engine, url); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); + QScopedPointer<QObject> o(component.create()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), QLatin1String("%1:%2").arg(url.toString(), QLatin1String("5 Nested inline components are not supported\n"))); +} + +void tst_qqmllanguage::inlineComponentStaticTypeResolution() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("InlineComponentChild.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QCOMPARE(o->property("i").toInt(), 42); +} + +void tst_qqmllanguage::inlineComponentInSingleton() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("singletonICTest.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + auto untyped = o->property("singleton1"); + QVERIFY(untyped.isValid()); + auto singleton1 = untyped.value<QObject*>(); + QVERIFY(singleton1); + QCOMPARE(singleton1->property("iProp").value<int>(), 42); + QCOMPARE(singleton1->property("sProp").value<QString>(), QString::fromLatin1("Hello, world")); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::nonExistingInlineComponent_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QString>("errorMessage"); + QTest::addColumn<int>("line"); + QTest::addColumn<int>("column"); + + QTest::newRow("Property type") << testFileUrl("nonExistingICUser1.qml") << QString("Type InlineComponentProvider has no inline component type called NonExisting") << 4 << 5; + QTest::newRow("Instantiation") << testFileUrl("nonExistingICUser2.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 4 << 5; + QTest::newRow("Inheritance") << testFileUrl("nonExistingICUser3.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 3 << 1; + QTest::newRow("From singleton") << testFileUrl("nonExistingICUser4.qml") << QString("Type MySingleton.SingletonTypeWithIC has no inline component type called NonExisting") << 5 << 5; + + QTest::newRow("Cannot access parent inline components from child") << testFileUrl("nonExistingICUser5.qml") << QString("Type InlineComponentProviderChild has no inline component type called StyledRectangle") << 4 << 5; +} + +void tst_qqmllanguage::nonExistingInlineComponent() +{ + QFETCH(QUrl, componentUrl); + QFETCH(QString, errorMessage); + QFETCH(int, line); + QFETCH(int, column); + QQmlEngine engine; + QQmlComponent component(&engine, componentUrl); + auto errors = component.errors(); + QCOMPARE(errors.size(), 1); + const auto &error = errors.first(); + QCOMPARE(error.description(), errorMessage); + QCOMPARE(error.line(), line); + QCOMPARE(error.column(), column); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 75a932b6f4..d54e3467b7 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -700,7 +700,7 @@ void tst_qqmllistmodel::error_data() QTest::newRow("unknown qualified ListElement not allowed") << "import QtQuick 2.0\nListModel { Foo.ListElement { a: 123 } }" - << "Foo.ListElement - Foo is not a namespace"; + << "Foo.ListElement - Foo is neither a type nor a namespace"; } void tst_qqmllistmodel::error() |