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 <simon.hausmann@theqtcompany.com>
This commit is contained in:
J-P Nurmi 2015-09-14 21:23:32 +02:00
parent ba7edffda3
commit b7738beda6
14 changed files with 88 additions and 15 deletions

View File

@ -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();

View File

@ -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<QQmlError> parserErrors = customParser->errors();

View File

@ -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;

View File

@ -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();

View File

@ -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<QQmlError> exceptions;
QQmlEnginePrivate *engine;
const QQmlPropertyValidator *validator;
Flags m_flags;
QBiPointer<const QQmlImports, QQmlTypeNameCache> imports;

View File

@ -38,6 +38,7 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qhashedstring_p.h>
#include <private/qqmlimport_p.h>
#include <private/qqmlcompiler_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qstringlist.h>
@ -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();

View File

@ -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;

View File

@ -1101,6 +1101,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (customParser) {
QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.constFind(index);
if (customParserBindings != compiledData->customParserBindings.constEnd()) {
customParser->engine = QQmlEnginePrivate::get(engine);
customParser->imports = compiledData->importCache;
QList<const QV4::CompiledData::Binding *> 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;
}

View File

@ -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();

View File

@ -0,0 +1,4 @@
import Test 1.0
MyCompositeBaseType {
}

View File

@ -0,0 +1,6 @@
import Test 1.0
RegisteredCompositeTypeWithEnum {
property int enumValue0: RegisteredCompositeTypeWithEnum.EnumValue0
property int enumValue42: RegisteredCompositeTypeWithEnum.EnumValue42
}

View File

@ -91,6 +91,8 @@ void registerTypes()
qmlRegisterCustomExtendedType<SimpleObjectWithCustomParser, SimpleObjectExtension>("Test", 1, 0, "SimpleExtendedObjectWithCustomParser", new SimpleObjectCustomParser);
qmlRegisterType<RootObjectInCreationTester>("Test", 1, 0, "RootObjectInCreationTester");
qmlRegisterType<MyCompositeBaseType>("Test", 1, 0, "MyCompositeBaseType");
}
QVariant myCustomVariantTypeConverter(const QString &data)

View File

@ -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
{

View File

@ -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<TestType>("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<int>(MyCompositeBaseType::EnumValue0));
QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42));
delete o;
}
// QTBUG-18268
void tst_qqmllanguage::remoteLoadCrash()
{