Support (registered) non-local enums for signal/slot params in QML.
authorMichael Brasser <michael.brasser@nokia.com>
Thu, 15 Mar 2012 01:58:58 +0000 (11:58 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 21 Mar 2012 00:10:41 +0000 (01:10 +0100)
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 <glenn.watson@nokia.com>
Task-number: QTBUG-20639
Change-Id: I8c439f2dcc7bfd8ec31914b0c86cd3a1de3c038c
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>
src/qml/qml/qqmlboundsignal.cpp
src/qml/qml/qqmlpropertycache.cpp
tests/auto/qml/qqmlecmascript/testtypes.cpp
tests/auto/qml/qqmllanguage/data/globalEnums.qml [new file with mode: 0644]
tests/auto/qml/qqmllanguage/testtypes.cpp
tests/auto/qml/qqmllanguage/testtypes.h
tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp

index 3200aae..5cc6032 100644 (file)
@@ -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("::");
index 93c6aa1..9ff79a1 100644 (file)
@@ -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);
index 64e91fb..a79207a 100644 (file)
@@ -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<MyQmlObject::MyType>("MyEnum2");
+    qRegisterMetaType<MyQmlObject::MyEnum2>("MyEnum2");
     qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons");
 
     qmlRegisterType<CircularReferenceObject>("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 (file)
index 0000000..102102a
--- /dev/null
@@ -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);
+    }
+}
index 3c7a7c2..5e94237 100644 (file)
@@ -77,6 +77,10 @@ void registerTypes()
 
     qmlRegisterType<MyVersion2Class>("Test.VersionOrder", 2,0, "MyQmlObject");
     qmlRegisterType<MyQmlObject>("Test.VersionOrder", 1,0, "MyQmlObject");
+
+    qmlRegisterType<MyEnum1Class>("Test",1,0,"MyEnum1Class");
+    qmlRegisterType<MyEnum2Class>("Test",1,0,"MyEnum2Class");
+    qmlRegisterType<MyEnumDerivedClass>("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);
 }
-
index e7294f0..383d81f 100644 (file)
@@ -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
index 266cd2a..98f0335 100644 (file)
@@ -46,6 +46,7 @@
 #include <QtCore/qdebug.h>
 #include <QtCore/qfileinfo.h>
 #include <QtCore/qdir.h>
+#include <QSignalSpy>
 
 #include <private/qqmlproperty_p.h>
 #include <private/qqmlmetatype_p.h>
@@ -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<MyEnum1Class::EnumA>();
+    qRegisterMetaType<MyEnum2Class::EnumB>();
+
+    QQmlComponent component(&engine, TEST_FILE("globalEnums.qml"));
+
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+
+    MyEnum1Class *enum1Class = o->findChild<MyEnum1Class *>(QString::fromLatin1("enum1Class"));
+    QVERIFY(enum1Class != 0);
+    QVERIFY(enum1Class->getValue() == -1);
+
+    MyEnumDerivedClass *enum2Class = o->findChild<MyEnumDerivedClass *>(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"