From 37cd29b2ff024f27f84ef6214ff5403603d522f2 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Thu, 15 Mar 2012 11:58:58 +1000 Subject: [PATCH] Support (registered) non-local enums for signal/slot params in QML. It's now possible to detect whether a registered type is an enum, allowing registered non-local enums to be used as parameters in signals and slots from QML/C++. Author: Glenn Watson Task-number: QTBUG-20639 Change-Id: I8c439f2dcc7bfd8ec31914b0c86cd3a1de3c038c Reviewed-by: Chris Adams Reviewed-by: Martin Jones --- src/qml/qml/qqmlboundsignal.cpp | 5 +- src/qml/qml/qqmlpropertycache.cpp | 8 ++- tests/auto/qml/qqmlecmascript/testtypes.cpp | 2 +- .../qml/qqmllanguage/data/globalEnums.qml | 31 +++++++++ tests/auto/qml/qqmllanguage/testtypes.cpp | 5 +- tests/auto/qml/qqmllanguage/testtypes.h | 66 ++++++++++++++++++- .../qml/qqmllanguage/tst_qqmllanguage.cpp | 44 +++++++++++++ 7 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/globalEnums.qml diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 3200aaea3a..5cc603224a 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -227,7 +227,10 @@ QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, prop.setWritable(false); } else { QByteArray propType = type; - if (t >= QVariant::UserType || t == QVariant::Invalid) { + if ((QMetaType::typeFlags(t) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) { + t = QVariant::Int; + propType = "int"; + } else if (t == QVariant::Invalid) { QByteArray scope; QByteArray name; int scopeIdx = propType.lastIndexOf("::"); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 93c6aa1f00..9ff79a1956 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -734,7 +734,9 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, for (int ii = 0; ii < argTypeNames.count(); ++ii) { int type = QMetaType::type(argTypeNames.at(ii)); - if (type == QVariant::Invalid) + if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QVariant::Invalid) type = EnumType(object->metaObject(), argTypeNames.at(ii)); if (type == QVariant::Invalid) { if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); @@ -757,7 +759,9 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, for (int ii = 0; ii < argTypeNames.count(); ++ii) { int type = QMetaType::type(argTypeNames.at(ii)); - if (type == QVariant::Invalid) + if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QVariant::Invalid) type = EnumType(object->metaObject(), argTypeNames.at(ii)); if (type == QVariant::Invalid) { if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index 64e91fbc95..a79207a1c8 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -196,7 +196,7 @@ void registerTypes() qmlRegisterModuleApi("Qt.test.qobjectApi",2,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, major version set qmlRegisterModuleApi("Qt.test.qobjectApiParented",1,0,qobject_api_engine_parent); // register (parented qobject) module API for a uri which doesn't contain elements - qRegisterMetaType("MyEnum2"); + qRegisterMetaType("MyEnum2"); qRegisterMetaType("Qt::MouseButtons"); qmlRegisterType("Qt.test", 1, 0, "CircularReferenceObject"); diff --git a/tests/auto/qml/qqmllanguage/data/globalEnums.qml b/tests/auto/qml/qqmllanguage/data/globalEnums.qml new file mode 100644 index 0000000000..102102a6c0 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/globalEnums.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 +import Test 1.0 + +Item { + MyEnum1Class { + id: enum1Class + objectName: "enum1Class" + } + + MyEnumDerivedClass { + id: enumDerivedClass + objectName: "enumDerivedClass" + + onValueAChanged: { + aValue = newValue; + } + + onValueBChanged: { + bValue = newValue; + } + + property int aValue: 0 + property int bValue: 0 + } + + function setEnumValues() { + enum1Class.setValue(MyEnum1Class.A_13); + enumDerivedClass.setValueA(MyEnum1Class.A_11); + enumDerivedClass.setValueB(MyEnum2Class.B_37); + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 3c7a7c2058..5e9423761c 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -77,6 +77,10 @@ void registerTypes() qmlRegisterType("Test.VersionOrder", 2,0, "MyQmlObject"); qmlRegisterType("Test.VersionOrder", 1,0, "MyQmlObject"); + + qmlRegisterType("Test",1,0,"MyEnum1Class"); + qmlRegisterType("Test",1,0,"MyEnum2Class"); + qmlRegisterType("Test",1,0,"MyEnumDerivedClass"); } QVariant myCustomVariantTypeConverter(const QString &data) @@ -85,4 +89,3 @@ QVariant myCustomVariantTypeConverter(const QString &data) rv.a = data.toInt(); return QVariant::fromValue(rv); } - diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index e7294f090c..383d81fb1a 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -811,14 +811,76 @@ class MyVersion2Class : public QObject Q_OBJECT }; +class MyEnum1Class : public QObject +{ + Q_OBJECT + Q_ENUMS(EnumA) + +public: + MyEnum1Class() : value(A_Invalid) {} + + enum EnumA + { + A_Invalid = -1, + + A_11 = 11, + A_13 = 13 + }; + + Q_INVOKABLE void setValue(EnumA v) { value = v; } + + EnumA getValue() { return value; } + +private: + EnumA value; +}; + +class MyEnum2Class : public QObject +{ + Q_OBJECT + Q_ENUMS(EnumB) + +public: + MyEnum2Class() : valueA(MyEnum1Class::A_Invalid), valueB(B_Invalid) {} + + enum EnumB + { + B_Invalid = -1, + + B_29 = 29, + B_31 = 31, + B_37 = 37 + }; + + MyEnum1Class::EnumA getValueA() { return valueA; } + EnumB getValueB() { return valueB; } + + Q_INVOKABLE void setValueA(MyEnum1Class::EnumA v) { valueA = v; emit valueAChanged(v); } + Q_INVOKABLE void setValueB(EnumB v) { valueB = v; emit valueBChanged(v); } + +signals: + void valueAChanged(MyEnum1Class::EnumA newValue); + void valueBChanged(MyEnum2Class::EnumB newValue); + +private: + MyEnum1Class::EnumA valueA; + EnumB valueB; +}; + +class MyEnumDerivedClass : public MyEnum2Class +{ + Q_OBJECT +}; + +Q_DECLARE_METATYPE(MyEnum2Class::EnumB) +Q_DECLARE_METATYPE(MyEnum1Class::EnumA) + QML_DECLARE_TYPE(MyRevisionedBaseClassRegistered) QML_DECLARE_TYPE(MyRevisionedBaseClassUnregistered) QML_DECLARE_TYPE(MyRevisionedClass) QML_DECLARE_TYPE(MyRevisionedSubclass) QML_DECLARE_TYPE(MySubclass) - - void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 266cd2a52d..98f0335914 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -176,6 +177,8 @@ private slots: void crash1(); void crash2(); + void globalEnums(); + private: QQmlEngine engine; void testType(const QString& qml, const QString& type, const QString& error); @@ -2285,6 +2288,47 @@ void tst_qqmllanguage::remoteLoadCrash() delete o; } +// QTBUG-20639 +void tst_qqmllanguage::globalEnums() +{ + qRegisterMetaType(); + qRegisterMetaType(); + + QQmlComponent component(&engine, TEST_FILE("globalEnums.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + MyEnum1Class *enum1Class = o->findChild(QString::fromLatin1("enum1Class")); + QVERIFY(enum1Class != 0); + QVERIFY(enum1Class->getValue() == -1); + + MyEnumDerivedClass *enum2Class = o->findChild(QString::fromLatin1("enumDerivedClass")); + QVERIFY(enum2Class != 0); + QVERIFY(enum2Class->getValueA() == -1); + QVERIFY(enum2Class->getValueB() == -1); + + QVERIFY(enum2Class->property("aValue") == 0); + QVERIFY(enum2Class->property("bValue") == 0); + + QSignalSpy signalA(enum2Class, SIGNAL(valueAChanged(MyEnum1Class::EnumA))); + QSignalSpy signalB(enum2Class, SIGNAL(valueBChanged(MyEnum2Class::EnumB))); + + QMetaObject::invokeMethod(o, "setEnumValues"); + + QVERIFY(enum1Class->getValue() == MyEnum1Class::A_13); + QVERIFY(enum2Class->getValueA() == MyEnum1Class::A_11); + QVERIFY(enum2Class->getValueB() == MyEnum2Class::B_37); + + QVERIFY(signalA.count() == 1); + QVERIFY(signalB.count() == 1); + + QVERIFY(enum2Class->property("aValue") == MyEnum1Class::A_11); + QVERIFY(enum2Class->property("bValue") == 37); + + delete o; +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc"