Skip to content

Commit

Permalink
Support (registered) non-local enums for signal/slot params in QML.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Brasser authored and Qt by Nokia committed Mar 21, 2012
1 parent fd80285 commit 37cd29b
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 7 deletions.
5 changes: 4 additions & 1 deletion src/qml/qml/qqmlboundsignal.cpp
Expand Up @@ -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("::");
Expand Down
8 changes: 6 additions & 2 deletions src/qml/qml/qqmlpropertycache.cpp
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/auto/qml/qqmlecmascript/testtypes.cpp
Expand Up @@ -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");
Expand Down
31 changes: 31 additions & 0 deletions 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);
}
}
5 changes: 4 additions & 1 deletion tests/auto/qml/qqmllanguage/testtypes.cpp
Expand Up @@ -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)
Expand All @@ -85,4 +89,3 @@ QVariant myCustomVariantTypeConverter(const QString &data)
rv.a = data.toInt();
return QVariant::fromValue(rv);
}

66 changes: 64 additions & 2 deletions tests/auto/qml/qqmllanguage/testtypes.h
Expand Up @@ -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
44 changes: 44 additions & 0 deletions tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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"

0 comments on commit 37cd29b

Please sign in to comment.