aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Keller <Rainer.Keller@qt.io>2018-08-20 13:48:37 +0200
committerRainer Keller <Rainer.Keller@qt.io>2018-08-21 11:43:36 +0000
commitbb296168f426d7e646c0208427f25687f96e2693 (patch)
treef1683300d7b2aac946de627a87f8e69681a54b99
parentb858833a262aeef005e4e3533194db7e6c280c4c (diff)
Provide option to skip registration of enum classes unscoped
Enums classes are registered unscoped which leads to clashes in the following cases: 1. Two different enums contain the same values 2. The name of an enum class is the same as an enum value In the 2nd case you can not even access the scoped enum at all because it will be overwritten by the primitive type. Users can now add a class info to the meta type to disable the unscoped registration which solves all clashes. The default is kept as is. class MyClass : public QObject { Q_OBJECT Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") public: ... }; [ChangeLog][QtQml] Added option to disable unscoped registration of enum classes. Change-Id: Ifa4197a14a252575e8a25ae56fb6ee479addf80b Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc41
-rw-r--r--src/qml/qml/qqmlmetatype.cpp14
-rw-r--r--tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h24
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp37
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;