Allow conversion of QObject Module API to QVariant
authorChris Adams <christopher.adams@nokia.com>
Mon, 22 Aug 2011 05:51:28 +0000 (15:51 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 1 Sep 2011 23:34:21 +0000 (01:34 +0200)
This commit adds a conversion codepath for QV8TypeResource to QVariant
where the resource contains a QObject module API.  This allows such a
module API to be used as the "target" in a Connections element.

Task-number: QTBUG-20937
Change-Id: I9214b531968f2e6981a86e643859a97297c6a02a
Reviewed-on: http://codereview.qt.nokia.com/3286
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>

src/declarative/qml/v8/qv8engine.cpp
src/declarative/qml/v8/qv8typewrapper.cpp
src/declarative/qml/v8/qv8typewrapper_p.h
tests/auto/declarative/qdeclarativeconnection/data/moduleapi-target.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp

index 8eaf902..5b74f48 100644 (file)
@@ -208,7 +208,6 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
         if (r) {
             switch (r->resourceType()) {
             case QV8ObjectResource::ContextType:
-            case QV8ObjectResource::TypeType:
             case QV8ObjectResource::XMLHttpRequestType:
             case QV8ObjectResource::DOMNodeType:
             case QV8ObjectResource::SQLDatabaseType:
@@ -216,6 +215,8 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
             case QV8ObjectResource::Context2DType:
             case QV8ObjectResource::ParticleDataType:
                 return QVariant();
+            case QV8ObjectResource::TypeType:
+                return m_typeWrapper.toVariant(r);
             case QV8ObjectResource::QObjectType:
                 return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r));
             case QV8ObjectResource::ListType:
index f46aaab..c89d5ab 100644 (file)
@@ -128,6 +128,34 @@ v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QDeclarativeTypeName
     return rv;
 }
 
+QVariant QV8TypeWrapper::toVariant(QV8ObjectResource *r)
+{
+    Q_ASSERT(r->resourceType() == QV8ObjectResource::TypeType);
+    QV8TypeResource *resource = static_cast<QV8TypeResource *>(r);
+    QV8Engine *v8engine = resource->engine;
+
+    if (resource->typeNamespace) {
+        if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) {
+            if (moduleApi->scriptCallback) {
+                moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine());
+                moduleApi->scriptCallback = 0;
+                moduleApi->qobjectCallback = 0;
+            } else if (moduleApi->qobjectCallback) {
+                moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine());
+                moduleApi->scriptCallback = 0;
+                moduleApi->qobjectCallback = 0;
+            }
+
+            if (moduleApi->qobjectApi) {
+                return QVariant::fromValue<QObject*>(moduleApi->qobjectApi);
+            }
+        }
+    }
+
+    // only QObject Module API can be converted to a variant.
+    return QVariant();
+}
+
 v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, 
                                              const v8::AccessorInfo &info)
 {
index 2e79946..f9309f8 100644 (file)
@@ -75,6 +75,7 @@ public:
     v8::Local<v8::Object> newObject(QObject *, QDeclarativeType *, TypeNameMode = IncludeEnums);
     v8::Local<v8::Object> newObject(QObject *, QDeclarativeTypeNameCache *, const void *, 
                                     TypeNameMode = IncludeEnums);
+    QVariant toVariant(QV8ObjectResource *);
 
 private:
     static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, 
diff --git a/tests/auto/declarative/qdeclarativeconnection/data/moduleapi-target.qml b/tests/auto/declarative/qdeclarativeconnection/data/moduleapi-target.qml
new file mode 100644 (file)
index 0000000..8803f24
--- /dev/null
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+import MyTestModuleApi 1.0 as MyTestModuleApi
+
+Item {
+    id: rootObject
+    objectName: "rootObject"
+    property int newIntPropValue: 12
+
+    property int moduleIntPropChangedCount: 0
+    property int moduleOtherSignalCount: 0
+
+    function setModuleIntProp() {
+        MyTestModuleApi.intProp = newIntPropValue;
+        newIntPropValue = newIntPropValue + 1;
+    }
+
+    Connections {
+        target: MyTestModuleApi
+        onIntPropChanged: moduleIntPropChangedCount = moduleIntPropChangedCount + 1;
+        onOtherSignal: moduleOtherSignalCount = moduleOtherSignalCount + 1;
+    }
+}
index 37cce5c..c726fde 100644 (file)
@@ -68,6 +68,7 @@ private slots:
     void unknownSignals();
     void errors_data();
     void errors();
+    void moduleApiTarget();
 
 private:
     QDeclarativeEngine engine;
@@ -229,6 +230,69 @@ void tst_qdeclarativeconnection::errors()
     QCOMPARE(errors.at(0).description(), error);
 }
 
+
+class MyTestModuleApi : public QObject
+{
+Q_OBJECT
+Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged)
+
+public:
+    MyTestModuleApi(QObject *parent = 0) : QObject(parent), m_intProp(0), m_changeCount(0) {}
+    ~MyTestModuleApi() {}
+
+    Q_INVOKABLE int otherMethod(int val) { return val + 4; }
+
+    int intProp() const { return m_intProp; }
+    void setIntProp(int val)
+    {
+        if (++m_changeCount % 3 == 0) emit otherSignal();
+        m_intProp = val; emit intPropChanged();
+    }
+
+signals:
+    void intPropChanged();
+    void otherSignal();
+
+private:
+    int m_intProp;
+    int m_changeCount;
+};
+
+static QObject *module_api_factory(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
+{
+   Q_UNUSED(engine)
+   Q_UNUSED(scriptEngine)
+   MyTestModuleApi *api = new MyTestModuleApi();
+   return api;
+}
+
+// QTBUG-20937
+void tst_qdeclarativeconnection::moduleApiTarget()
+{
+    qmlRegisterModuleApi("MyTestModuleApi", 1, 0, module_api_factory);
+    QDeclarativeComponent component(&engine, QUrl(SRCDIR "/data/moduleapi-target.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 0);
+    QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0);
+
+    QMetaObject::invokeMethod(object, "setModuleIntProp");
+    QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 1);
+    QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0);
+
+    QMetaObject::invokeMethod(object, "setModuleIntProp");
+    QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 2);
+    QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0);
+
+    // the module API emits otherSignal every 3 times the int property changes.
+    QMetaObject::invokeMethod(object, "setModuleIntProp");
+    QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 3);
+    QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 1);
+
+    delete object;
+}
+
 QTEST_MAIN(tst_qdeclarativeconnection)
 
 #include "tst_qdeclarativeconnection.moc"