From b7738beda651c2927e1a9d58c592148b1dc99576 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 14 Sep 2015 21:23:32 +0200 Subject: Make QML composite types inherit enums Problem: in Qt Quick Controls 2, enums declared in the abstract C++ base types were not accessible with the concrete QML type name, but had to be referenced using the base type name: Slider { snapMode: AbstractSlider.SnapOnRelease } Solution: this change resolves the C++ base type and creates the missing link between the composite type and its base type's meta- object. This allows referencing enums using the concrete/composite QML type name: Slider { snapMode: Slider.SnapOnRelease } Change-Id: Icefdec91b012b12728367fd54b4d16796233ee12 Task-number: QTBUG-43582 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 10 +++++--- src/qml/compiler/qqmltypecompiler_p.h | 1 + src/qml/qml/qqmlcustomparser.cpp | 2 +- src/qml/qml/qqmlcustomparser_p.h | 6 +++-- src/qml/qml/qqmlmetatype.cpp | 30 +++++++++++++++++++--- src/qml/qml/qqmlmetatype_p.h | 8 +++--- src/qml/qml/qqmlobjectcreator.cpp | 2 ++ src/qml/qml/qqmltypewrapper.cpp | 2 +- .../qqmllanguage/data/CompositeTypeWithEnum.qml | 4 +++ .../data/registeredCompositeTypeWithEnum.qml | 6 +++++ tests/auto/qml/qqmllanguage/testtypes.cpp | 2 ++ tests/auto/qml/qqmllanguage/testtypes.h | 11 ++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 17 ++++++++++++ 14 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnum.qml create mode 100644 tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 4b1e3601dc..7b0d4240c4 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1590,7 +1590,7 @@ static QV4::IR::Type resolveQmlType(QQmlEnginePrivate *qmlEngine, QV4::IR::Membe if (member->name->constData()->isUpper()) { bool ok = false; - int value = type->enumValue(*member->name, &ok); + int value = type->enumValue(qmlEngine, *member->name, &ok); if (ok) { member->setEnumValue(value); resolver->clear(); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 7f62446c22..e36ba76326 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1163,8 +1163,6 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, if (!type && typeName != QLatin1String("Qt")) return true; - if (type && type->isComposite()) //No enums on composite (or composite singleton) types - return true; int value = 0; bool ok = false; @@ -1182,7 +1180,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, } else { // Otherwise we have to search the whole type if (type) { - value = type->enumValue(QHashedStringRef(enumValue), &ok); + value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); } else { QByteArray enumName = enumValue.toUtf8(); const QMetaObject *metaObject = StaticQtMetaObject::get(); @@ -1210,7 +1208,9 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &e if (scope != QLatin1String("Qt")) { QQmlType *type = 0; imports->resolveType(scope, &type, 0, 0, 0); - return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + if (!type) + return -1; + return type ? type->enumValue(compiler->enginePrivate(), QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; } const QMetaObject *mo = StaticQtMetaObject::get(); @@ -1988,9 +1988,11 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (customParser && !customBindings.isEmpty()) { customParser->clearErrors(); customParser->validator = this; + customParser->engine = enginePrivate; customParser->imports = compiler->imports(); customParser->verifyBindings(qmlUnit, customBindings); customParser->validator = 0; + customParser->engine = 0; customParser->imports = (QQmlImports*)0; customParserBindingsPerObject->insert(objectIndex, customParserBindings); const QList parserErrors = customParser->errors(); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index cbc68280b7..09ef7aad67 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -273,6 +273,7 @@ public: bool validate(); const QQmlImports &imports() const; + QQmlEnginePrivate *engine() const { return enginePrivate; } private: bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index ec88ee015a..517f8d42ed 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -141,7 +141,7 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const type = result.type; } - return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + return type ? type->enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; } const QMetaObject *mo = StaticQtMetaObject::get(); diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index dd461ba299..8bdc73fab1 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE class QQmlCompiledData; class QQmlPropertyValidator; +class QQmlEnginePrivate; class Q_QML_PRIVATE_EXPORT QQmlCustomParser { @@ -67,8 +68,8 @@ public: }; Q_DECLARE_FLAGS(Flags, Flag) - QQmlCustomParser() : validator(0), m_flags(NoFlag) {} - QQmlCustomParser(Flags f) : validator(0), m_flags(f) {} + QQmlCustomParser() : engine(0), validator(0), m_flags(NoFlag) {} + QQmlCustomParser(Flags f) : engine(0), validator(0), m_flags(f) {} virtual ~QQmlCustomParser() {} void clearErrors(); @@ -92,6 +93,7 @@ protected: private: QList exceptions; + QQmlEnginePrivate *engine; const QQmlPropertyValidator *validator; Flags m_flags; QBiPointer imports; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index f0debb8e59..b173bc8bec 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -473,6 +474,23 @@ QQmlType *QQmlType::superType() const return d->superType; } +int QQmlType::resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const +{ + Q_ASSERT(isComposite()); + *ok = false; + if (!engine) + return -1; + QQmlTypeData *td = engine->typeLoader.getType(sourceUrl()); + if (!td || !td->isComplete()) + return -1; + QQmlCompiledData *cd = td->compiledData(); + const QMetaObject *mo = cd->rootPropertyCache->firstCppMetaObject(); + QQmlType *type = QQmlMetaType::qmlType(mo); + if (!type) + return -1; + return type->enumValue(engine, name, ok); +} + static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) { @@ -907,9 +925,11 @@ QUrl QQmlType::sourceUrl() const return QUrl(); } -int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); + if (isComposite()) + return resolveCompositeEnumValue(engine, name.toString(), ok); *ok = true; d->initEnums(); @@ -922,9 +942,11 @@ int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const return -1; } -int QQmlType::enumValue(const QHashedCStringRef &name, bool *ok) const +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const { Q_ASSERT(ok); + if (isComposite()) + return resolveCompositeEnumValue(engine, name.toUtf16(), ok); *ok = true; d->initEnums(); @@ -937,9 +959,11 @@ int QQmlType::enumValue(const QHashedCStringRef &name, bool *ok) const return -1; } -int QQmlType::enumValue(const QV4::String *name, bool *ok) const +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const { Q_ASSERT(ok); + if (isComposite()) + return resolveCompositeEnumValue(engine, name->toQString(), ok); *ok = true; d->initEnums(); diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 61a6567f1d..08f19bcf09 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE class QQmlType; class QQmlEngine; +class QQmlEnginePrivate; class QQmlCustomParser; class QQmlTypePrivate; class QQmlTypeModule; @@ -204,11 +205,12 @@ public: QUrl sourceUrl() const; - int enumValue(const QHashedStringRef &, bool *ok) const; - int enumValue(const QHashedCStringRef &, bool *ok) const; - int enumValue(const QV4::String *, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; private: QQmlType *superType() const; + int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; friend class QQmlTypePrivate; friend struct QQmlMetaTypeData; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index f371a369a1..171fa048ce 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1101,6 +1101,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (customParser) { QHash::ConstIterator customParserBindings = compiledData->customParserBindings.constFind(index); if (customParserBindings != compiledData->customParserBindings.constEnd()) { + customParser->engine = QQmlEnginePrivate::get(engine); customParser->imports = compiledData->importCache; QList bindings; @@ -1110,6 +1111,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo bindings << obj->bindingTable() + i; customParser->applyBindings(instance, compiledData, bindings); + customParser->engine = 0; customParser->imports = (QQmlTypeNameCache*)0; bindingsToSkip = *customParserBindings; } diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 1d72b2da0d..1145f1b64f 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -182,7 +182,7 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope if (name->startsWithUpper()) { bool ok = false; - int value = type->enumValue(name, &ok); + int value = type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); if (ok) return QV4::Primitive::fromInt32(value).asReturnedValue(); diff --git a/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnum.qml new file mode 100644 index 0000000000..6a14e72a31 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnum.qml @@ -0,0 +1,4 @@ +import Test 1.0 + +MyCompositeBaseType { +} diff --git a/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml new file mode 100644 index 0000000000..5f8c11e5f6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml @@ -0,0 +1,6 @@ +import Test 1.0 + +RegisteredCompositeTypeWithEnum { + property int enumValue0: RegisteredCompositeTypeWithEnum.EnumValue0 + property int enumValue42: RegisteredCompositeTypeWithEnum.EnumValue42 +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 0b44daca30..95a98788c3 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -91,6 +91,8 @@ void registerTypes() qmlRegisterCustomExtendedType("Test", 1, 0, "SimpleExtendedObjectWithCustomParser", new SimpleObjectCustomParser); qmlRegisterType("Test", 1, 0, "RootObjectInCreationTester"); + + qmlRegisterType("Test", 1, 0, "MyCompositeBaseType"); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index b8792a892f..985acc2539 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1079,9 +1079,19 @@ class MyEnumDerivedClass : public MyEnum2Class Q_OBJECT }; +class MyCompositeBaseType : public QObject +{ + Q_OBJECT + Q_ENUMS(CompositeEnum) + +public: + enum CompositeEnum { EnumValue0, EnumValue42 = 42 }; +}; + Q_DECLARE_METATYPE(MyEnum2Class::EnumB) Q_DECLARE_METATYPE(MyEnum1Class::EnumA) Q_DECLARE_METATYPE(Qt::TextFormat) +Q_DECLARE_METATYPE(MyCompositeBaseType::CompositeEnum) QML_DECLARE_TYPE(MyRevisionedBaseClassRegistered) QML_DECLARE_TYPE(MyRevisionedBaseClassUnregistered) @@ -1089,6 +1099,7 @@ QML_DECLARE_TYPE(MyRevisionedClass) QML_DECLARE_TYPE(MyRevisionedSubclass) QML_DECLARE_TYPE(MySubclass) QML_DECLARE_TYPE(MyReceiversTestObject) +QML_DECLARE_TYPE(MyCompositeBaseType) class CustomBinding : public QObject, public QQmlParserStatus { diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 97501118dd..b48f3640f4 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -159,6 +159,7 @@ private slots: void readonlyObjectProperties(); void receivers(); void registeredCompositeType(); + void registeredCompositeTypeWithEnum(); void implicitImportsLast(); void basicRemote_data(); @@ -3174,6 +3175,7 @@ void tst_qqmllanguage::initTestCase() qmlRegisterType(testFileUrl("CompositeType.qml"), "Test", 1, 0, "RegisteredCompositeType"); qmlRegisterType(testFileUrl("CompositeType.DoesNotExist.qml"), "Test", 1, 0, "RegisteredCompositeType2"); qmlRegisterType(testFileUrl("invalidRoot.1.qml"), "Test", 1, 0, "RegisteredCompositeType3"); + qmlRegisterType(testFileUrl("CompositeTypeWithEnum.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithEnum"); // Registering the TestType class in other modules should have no adverse effects qmlRegisterType("org.qtproject.TestPre", 1, 0, "Test"); @@ -3350,6 +3352,21 @@ void tst_qqmllanguage::registeredCompositeType() delete o; } +// QTBUG-43582 +void tst_qqmllanguage::registeredCompositeTypeWithEnum() +{ + QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithEnum.qml")); + + VERIFY_ERRORS(0); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("enumValue0").toInt(), static_cast(MyCompositeBaseType::EnumValue0)); + QCOMPARE(o->property("enumValue42").toInt(), static_cast(MyCompositeBaseType::EnumValue42)); + + delete o; +} + // QTBUG-18268 void tst_qqmllanguage::remoteLoadCrash() { -- cgit v1.2.3