diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2020-02-12 17:49:31 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2020-02-25 09:58:56 +0100 |
commit | 11ff5c314682258b6eb26e90847210662eb0f533 (patch) | |
tree | 4b0b2f6bed8f25423bec5a64a1458207822662b5 | |
parent | 94901065ddd0c0c65e0db7c2151c0eb9eb3f64c5 (diff) |
Provide a way to statically register namespaces
Previously, qmltyperegistrar would stumble over any QML_* macros in
namespaces, as the namespaces could not be used as template arguments.
However, namespaces are intended to be usable by QML as uncreatable
"types".
Now, qmltyperegistrar checks the namespace flag that moc records in the
JSON output, and if that is given, registers an uncreatable metaobject
instead of a type. Therefore you can use QML_ELEMENT and friends to
register namespaces, just like you would register classes (except that
they're implicitly uncreatable, of course).
Task-number: QTBUG-68796
Change-Id: I186d7e9425471c32fb1a1f29c0c0b946afb2a9d2
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qml/qml/qqml.h | 32 | ||||
-rw-r--r-- | src/qmltyperegistrar/qmltyperegistrar.cpp | 38 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml | 5 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/qqmllanguage.pro | 5 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/testtypes.h | 41 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 31 |
7 files changed, 138 insertions, 20 deletions
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index ef7fdd1945..a3e3f1c584 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -848,6 +848,38 @@ void qmlRegisterTypesAndRevisions(const char *uri, int versionMajor) template<> inline void qmlRegisterTypesAndRevisions<>(const char *, int) {} +inline void qmlRegisterNamespaceAndRevisions(const QMetaObject *metaObject, + const char *uri, int versionMajor) +{ + QQmlPrivate::RegisterTypeAndRevisions type = { + 0, + 0, + 0, + 0, + nullptr, + + uri, + versionMajor, + + metaObject, + metaObject, + + nullptr, + nullptr, + + -1, + -1, + -1, + + nullptr, + nullptr, + + &qmlCreateCustomParser<void> + }; + + qmlregister(QQmlPrivate::TypeAndRevisionsRegistration, &type); +} + int Q_QML_EXPORT qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName); QT_END_NAMESPACE diff --git a/src/qmltyperegistrar/qmltyperegistrar.cpp b/src/qmltyperegistrar/qmltyperegistrar.cpp index 49c9fccb66..6fe5a20092 100644 --- a/src/qmltyperegistrar/qmltyperegistrar.cpp +++ b/src/qmltyperegistrar/qmltyperegistrar.cpp @@ -47,14 +47,23 @@ struct ScopedPointerFileCloser static inline void cleanup(FILE *handle) { if (handle) fclose(handle); } }; -static bool acceptClassForQmlTypeRegistration(const QJsonObject &classDef) +enum RegistrationMode { + NoRegistration, + ClassRegistration, + NamespaceRegistration +}; + +static RegistrationMode qmlTypeRegistrationMode(const QJsonObject &classDef) { const QJsonArray classInfos = classDef[QLatin1String("classInfos")].toArray(); for (const QJsonValue &info: classInfos) { - if (info[QLatin1String("name")].toString() == QLatin1String("QML.Element")) - return true; + const QString name = info[QLatin1String("name")].toString(); + if (name == QLatin1String("QML.Element")) { + return classDef[QLatin1String("namespace")].toBool() ? NamespaceRegistration + : ClassRegistration; + } } - return false; + return NoRegistration; } static QVector<QJsonObject> foreignRelatedTypes(const QVector<QJsonObject> &types, @@ -284,7 +293,11 @@ int main(int argc, char **argv) const QJsonArray classes = metaObject[QLatin1String("classes")].toArray(); for (const auto &cls : classes) { QJsonObject classDef = cls.toObject(); - if (acceptClassForQmlTypeRegistration(classDef)) { + switch (qmlTypeRegistrationMode(classDef)) { + case NamespaceRegistration: + classDef.insert(QLatin1String("namespace"), true); + Q_FALLTHROUGH(); + case ClassRegistration: { const QString include = metaObject[QLatin1String("inputFile")].toString(); const bool declaredInHeader = include.endsWith(QLatin1String(".h")); if (declaredInHeader) { @@ -297,8 +310,11 @@ int main(int argc, char **argv) .toString())); } types.append(classDef); - } else { + break; + } + case NoRegistration: foreignTypes.append(classDef); + break; } } }; @@ -360,8 +376,14 @@ int main(int argc, char **argv) continue; const QString className = classDef[QLatin1String("qualifiedClassName")].toString(); - fprintf(output, "\n qmlRegisterTypesAndRevisions<%s>(\"%s\", %s);", qPrintable(className), - qPrintable(module), qPrintable(majorVersion)); + + if (classDef.value(QLatin1String("namespace")).toBool()) { + fprintf(output, "\n qmlRegisterNamespaceAndRevisions(&%s::staticMetaObject, \"%s\", %s);", + qPrintable(className), qPrintable(module), qPrintable(majorVersion)); + } else { + fprintf(output, "\n qmlRegisterTypesAndRevisions<%s>(\"%s\", %s);", + qPrintable(className), qPrintable(module), qPrintable(majorVersion)); + } } fprintf(output, "\n qmlRegisterModule(\"%s\", %s, %s);", diff --git a/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml new file mode 100644 index 0000000000..3b37c29b18 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml @@ -0,0 +1,5 @@ +import StaticTest 1.0 + +MyStaticSecondNamespacedType { + list: [ MyStaticNamespacedType {} ] +} diff --git a/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml new file mode 100644 index 0000000000..2778baadb9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml @@ -0,0 +1,6 @@ +import StaticTest 1.0 + +MyStaticNamespacedType { + myEnum: MyStaticNamespace.Key5 + property int intProperty: MyStaticNamespace.MyOtherNSEnum.OtherKey2 +} diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.pro b/tests/auto/qml/qqmllanguage/qqmllanguage.pro index 724a27320c..6c54525544 100644 --- a/tests/auto/qml/qqmllanguage/qqmllanguage.pro +++ b/tests/auto/qml/qqmllanguage/qqmllanguage.pro @@ -1,4 +1,7 @@ -CONFIG += testcase +CONFIG += testcase qmltypes +QML_IMPORT_NAME = StaticTest +QML_IMPORT_VERSION = 1.0 + TARGET = tst_qqmllanguage macx:CONFIG -= app_bundle diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 148179cb9c..8852bf7af9 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -750,6 +750,47 @@ private: bool m_ownRWObj; }; +namespace MyStaticNamespace { + Q_NAMESPACE + QML_ELEMENT + + enum MyNSEnum { + Key1 = 1, + Key2, + Key5 = 5 + }; + Q_ENUM_NS(MyNSEnum); + + enum class MyOtherNSEnum { + OtherKey1 = 1, + OtherKey2 + }; + Q_ENUM_NS(MyOtherNSEnum); + + + class MyNamespacedType : public QObject + { + Q_OBJECT + Q_PROPERTY(MyStaticNamespace::MyNSEnum myEnum MEMBER m_myEnum) + QML_NAMED_ELEMENT(MyStaticNamespacedType) + MyStaticNamespace::MyNSEnum m_myEnum = MyNSEnum::Key1; + }; + + class MySecondNamespacedType : public QObject + { + Q_OBJECT + Q_PROPERTY(QQmlListProperty<MyStaticNamespace::MyNamespacedType> list READ list) + QML_NAMED_ELEMENT(MyStaticSecondNamespacedType) + public: + QQmlListProperty<MyNamespacedType> list() + { + return QQmlListProperty<MyNamespacedType>(this, &m_list); + } + + private: + QList<MyNamespacedType *> m_list; + }; +} namespace MyNamespace { Q_NAMESPACE diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index bd23806e3a..4d4056ba3f 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -1858,21 +1858,30 @@ void tst_qqmllanguage::valueTypes() void tst_qqmllanguage::cppnamespace() { - { - QQmlComponent component(&engine, testFileUrl("cppnamespace.qml")); + QScopedPointer<QObject> object; + + auto create = [&](const char *file) { + QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); - QScopedPointer<QObject> object(component.create()); + object.reset(component.create()); QVERIFY(object != nullptr); + }; - QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2); - } + auto createAndCheck = [&](const char *file) { + create(file); + return !QTest::currentTestFailed(); + }; - { - QQmlComponent component(&engine, testFileUrl("cppnamespace.2.qml")); - VERIFY_ERRORS(0); - QScopedPointer<QObject> object(component.create()); - QVERIFY(object != nullptr); - } + QVERIFY(createAndCheck("cppnamespace.qml")); + QCOMPARE(object->property("intProperty").toInt(), + (int)MyNamespace::MyOtherNSEnum::OtherKey2); + + QVERIFY(createAndCheck("cppstaticnamespace.qml")); + QCOMPARE(object->property("intProperty").toInt(), + (int)MyStaticNamespace::MyOtherNSEnum::OtherKey2); + + QVERIFY(createAndCheck("cppnamespace.2.qml")); + QVERIFY(createAndCheck("cppstaticnamespace.2.qml")); } void tst_qqmllanguage::aliasProperties() |