diff options
-rw-r--r-- | src/qml/doc/src/cppintegration/data.qdoc | 41 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 14 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml | 11 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/testtypes.h | 24 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 37 |
6 files changed, 136 insertions, 1 deletions
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 7fb4724f73..6159ffe20b 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -420,6 +420,47 @@ To use an enum as a \l {QFlags}{flags} type in QML, see \l Q_FLAG(). \note The names of enum values must begin with a capital letter in order to be accessible from QML. +\code +... +enum class Status { + Ready, + Loading, + Error +} +Q_ENUM(Status) +... +\endcode + +Enum classes are registered in QML as scoped and unscoped properties. +The \c Ready value will be registered at \c Message.Status.Ready and \c Message.Ready . + +When using enum classes, there can be multiple enums using the same identifiers. +The unscoped registration will be overwriten by the last registered enum. For classes +that contain such name conficts it is possible to disable the unscoped registration by +annotating your class with a special Q_CLASSINFO macro. +Use the name \c RegisterEnumClassesUnscoped with the value \c false to prevent scoped +enums from being merged into the same name space. + +\code +class Message : public QObject + { + Q_OBJECT + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + Q_ENUM(ScopedEnum) + Q_ENUM(OtherValue) + + public: + enum class ScopedEnum { + Value1, + Value2, + OtherValue + }; + enum class OtherValue { + Value1, + Value2 + }; + }; +\endcode \section2 Enumeration Types as Signal and Method Parameters diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index ed43d1829c..75241ab99c 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -218,6 +218,7 @@ public: int attachedPropertiesId; int propertyValueSourceCast; int propertyValueInterceptorCast; + bool registerEnumClassesUnscoped; }; struct QQmlSingletonTypeData @@ -369,6 +370,7 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) extraData.cd->attachedPropertiesType = nullptr; extraData.cd->propertyValueSourceCast = -1; extraData.cd->propertyValueInterceptorCast = -1; + extraData.cd->registerEnumClassesUnscoped = true; break; case QQmlType::SingletonType: case QQmlType::CompositeSingletonType: @@ -495,9 +497,15 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; d->extraData.cd->extFunc = type.extensionObjectCreate; d->extraData.cd->customParser = type.customParser; + d->extraData.cd->registerEnumClassesUnscoped = true; if (type.extensionMetaObject) d->extraData.cd->extMetaObject = type.extensionMetaObject; + + // Check if the user wants only scoped enum classes + auto indexOfClassInfo = metaObject()->indexOfClassInfo("RegisterEnumClassesUnscoped"); + if (indexOfClassInfo != -1 && QString::fromUtf8(metaObject()->classInfo(indexOfClassInfo).value()) == QLatin1String("false")) + d->extraData.cd->registerEnumClassesUnscoped = false; } QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) @@ -834,7 +842,11 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const for (int jj = 0; jj < e.keyCount(); ++jj) { const QString key = QString::fromUtf8(e.key(jj)); const int value = e.value(jj); - enums.insert(key, value); + if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) { + if (enums.contains(key)) + qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData()); + enums.insert(key, value); + } if (isScoped) scoped->insert(key, value); } diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml new file mode 100644 index 0000000000..4f8174a52d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 +import ScopedEnumsWithNameClashTest 1.0 + +QtObject { + property bool success: false + + Component.onCompleted: { + success = (ScopedEnum.ScopedEnum.OtherScopedEnum === 3) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml new file mode 100644 index 0000000000..84efa6859b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import ScopedEnumsWithResolvedNameClashTest 1.0 + +QtObject { + property bool success: false + + Component.onCompleted: { + success = (ScopedEnum.ScopedEnum.OtherScopedEnum === 3) + && (ScopedEnum.OtherScopedEnum.ScopedVal2 === 1) + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 3219701f9d..d890668655 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1384,6 +1384,30 @@ private: QObjectList m_list; }; +class ScopedEnumsWithNameClash +{ + Q_GADGET + Q_ENUMS(ScopedEnum) + Q_ENUMS(OtherScopedEnum) + +public: + enum class ScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3, OtherScopedEnum }; + enum class OtherScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 }; +}; + +class ScopedEnumsWithResolvedNameClash +{ + Q_GADGET + Q_ENUMS(ScopedEnum) + Q_ENUMS(OtherScopedEnum) + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +public: + enum class ScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3, OtherScopedEnum }; + enum class OtherScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 }; +}; + + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 97727c1794..157fd500ad 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -34,6 +34,7 @@ #include <QtCore/qdebug.h> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> +#include <QtCore/qscopeguard.h> #include <QSignalSpy> #include <QFont> #include <QQmlFileSelector> @@ -218,6 +219,8 @@ private slots: void lowercaseEnumCompileTime_data(); void lowercaseEnumCompileTime(); void scopedEnum(); + void scopedEnumsWithNameClash(); + void scopedEnumsWithResolvedNameClash(); void qmlEnums(); void literals_data(); void literals(); @@ -3793,6 +3796,40 @@ void tst_qqmllanguage::scopedEnum() QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); } +void tst_qqmllanguage::scopedEnumsWithNameClash() +{ + auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithNameClash>("ScopedEnumsWithNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); + auto registryGuard = qScopeGuard([typeId]() { + qmlUnregisterType(typeId); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("scopedEnumsWithNameClash.qml")); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal1"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal2"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal3"); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); + QVERIFY(obj->property("success").toBool()); +} + +void tst_qqmllanguage::scopedEnumsWithResolvedNameClash() +{ + auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithResolvedNameClash>("ScopedEnumsWithResolvedNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); + auto registryGuard = qScopeGuard([typeId]() { + qmlUnregisterType(typeId); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("scopedEnumsWithResolvedNameClash.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); + QVERIFY(obj->property("success").toBool()); +} + void tst_qqmllanguage::qmlEnums() { QQmlEngine engine; |