aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Webster <awebster@arcx.com>2019-07-24 13:17:06 -0400
committerAndrew Webster <awebster@arcx.com>2019-09-20 08:33:52 -0400
commite535b24adf28ad6f0a93b7ecb37fccbef3a5d016 (patch)
treeceee54039ace8d23027851a57cee8b67e8294d9f
parent26fbc4038223f5571dd048c1d800458708b6972c (diff)
Unwrap QObjects embedded inside arguments
This converts QObjects that are embedded inside ECMAScript objects into the actual QObject that they represent. For example, if an array of QObjects is passed as an argument to a method, the method will received a QVariantList where the items are QVariants of type QObjectStar. Method: void processObjects(const QVariantList &list); Call from ECMAScript: theObj.processObjects([qobj1, qobj2]) Prior to this patch, the method would have received a list containing QVariantMaps with keys from the QObject EMCAScript wrapper (e.g. __objectSignals__, __propertyCache__, etc.). After this patch, it will receive a list containing QVariants with QObject* inside. QVariantMaps are converted to QObjects if they have a property called "__QObject*__" set to true and an "id" property. "__QObject*__" is the same identifier for retuned objects and is now added in a toJSON method at the client. Change-Id: I5cafcddb9df0141977a574aaed4ce7c3ea2d0767 Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
-rw-r--r--examples/webchannel/shared/qwebchannel.js14
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp40
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h3
-rw-r--r--tests/auto/qml/testobject.cpp8
-rw-r--r--tests/auto/qml/testobject.h1
-rw-r--r--tests/auto/qml/tst_webchannel.qml34
6 files changed, 91 insertions, 9 deletions
diff --git a/examples/webchannel/shared/qwebchannel.js b/examples/webchannel/shared/qwebchannel.js
index fca45d9..37ac9eb 100644
--- a/examples/webchannel/shared/qwebchannel.js
+++ b/examples/webchannel/shared/qwebchannel.js
@@ -351,10 +351,6 @@ function QObject(name, data, webChannel)
var argument = arguments[i];
if (typeof argument === "function")
callback = argument;
- else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined)
- args.push({
- "id": argument.__id__
- });
else
args.push(argument);
}
@@ -426,8 +422,6 @@ function QObject(name, data, webChannel)
}
object.__propertyCache__[propertyIndex] = value;
var valueToSend = value;
- if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined)
- valueToSend = { "id": valueToSend.__id__ };
webChannel.exec({
"type": QWebChannelMessageTypes.setProperty,
"object": object.__id__,
@@ -452,6 +446,14 @@ function QObject(name, data, webChannel)
}
}
+QObject.prototype.toJSON = function() {
+ if (this.__id__ === undefined) return {};
+ return {
+ id: this.__id__,
+ "__QObject*__": true
+ };
+};
+
//required for use with nodejs
if (typeof module === 'object') {
module.exports = {
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 3907dfe..124b5c6 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -612,6 +612,44 @@ QObject *QMetaObjectPublisher::unwrapObject(const QString &objectId) const
return Q_NULLPTR;
}
+QVariant QMetaObjectPublisher::unwrapMap(QVariantMap map) const
+{
+ const auto qobj = map.value(KEY_QOBJECT).toBool();
+ const auto id = qobj ? map.value(KEY_ID).toString() : QString();
+
+ if (!id.isEmpty()) // it's probably a QObject
+ return QVariant::fromValue(unwrapObject(id));
+
+ // it's probably just a normal JS object, continue searching for objects
+ // that look like QObject*
+ for (auto &value : map)
+ value = unwrapVariant(value);
+
+ return map;
+}
+
+QVariant QMetaObjectPublisher::unwrapList(QVariantList list) const
+{
+ for (auto &value : list)
+ value = unwrapVariant(value);
+
+ return list;
+}
+
+QVariant QMetaObjectPublisher::unwrapVariant(const QVariant &value) const
+{
+ switch (value.type())
+ {
+ case QMetaType::QVariantList:
+ return unwrapList(value.toList());
+ case QMetaType::QVariantMap:
+ return unwrapMap(value.toMap());
+ default:
+ break;
+ }
+ return value;
+}
+
QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType) const
{
if (targetType == QMetaType::QJsonValue) {
@@ -636,7 +674,7 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType
// this converts QJsonObjects to QVariantMaps, which is not desired when
// we want to get a QJsonObject or QJsonValue (see above)
- QVariant variant = value.toVariant();
+ QVariant variant = unwrapVariant(value.toVariant());
if (targetType != QMetaType::QVariant && !variant.convert(targetType)) {
qWarning() << "Could not convert argument" << value << "to target type" << QVariant::typeToName(targetType) << '.';
}
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 6030de2..2ffeca3 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -191,6 +191,9 @@ public:
void objectDestroyed(const QObject *object);
QObject *unwrapObject(const QString &objectId) const;
+ QVariant unwrapMap(QVariantMap map) const;
+ QVariant unwrapList(QVariantList list) const;
+ QVariant unwrapVariant(const QVariant &value) const;
QVariant toVariant(const QJsonValue &value, int targetType) const;
diff --git a/tests/auto/qml/testobject.cpp b/tests/auto/qml/testobject.cpp
index 8f565ee..bd9db04 100644
--- a/tests/auto/qml/testobject.cpp
+++ b/tests/auto/qml/testobject.cpp
@@ -82,4 +82,12 @@ int TestObject::testVariantType(const QVariant &val)
return val.type();
}
+bool TestObject::testEmbeddedObjects(const QVariantList &list)
+{
+ return list.size() == 2 &&
+ QMetaType::Type(list[0].type()) == QMetaType::QObjectStar &&
+ QMetaType::Type(list[1].type()) == QMetaType::QVariantMap &&
+ QMetaType::Type(list[1].toMap()["obj"].type()) == QMetaType::QObjectStar;
+}
+
QT_END_NAMESPACE
diff --git a/tests/auto/qml/testobject.h b/tests/auto/qml/testobject.h
index a8d8f7f..9889523 100644
--- a/tests/auto/qml/testobject.h
+++ b/tests/auto/qml/testobject.h
@@ -52,6 +52,7 @@ public slots:
QString testOverload(const QString &str);
QString testOverload(const QString &str, int i);
int testVariantType(const QVariant &val);
+ bool testEmbeddedObjects(const QVariantList &list);
signals:
void testSignalBool(bool testBool);
diff --git a/tests/auto/qml/tst_webchannel.qml b/tests/auto/qml/tst_webchannel.qml
index fe9aa35..9c9b6d0 100644
--- a/tests/auto/qml/tst_webchannel.qml
+++ b/tests/auto/qml/tst_webchannel.qml
@@ -568,6 +568,7 @@ TestCase {
testObject.testVariantType(0, logReturnValue);
testObject.testVariantType("0", logReturnValue);
testObject.testVariantType(null, logReturnValue);
+ testObject.testVariantType(testObject, logReturnValue);
});
client.awaitInit();
@@ -584,10 +585,39 @@ TestCase {
awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
console.log("null arg");
awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
+ console.log("QObject arg");
+ awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
+
+ client.awaitIdle();
+
+ // QMetaType::Double: 6, QMetaType::QString: 10, QMetaType::Nullptr: 51,
+ // QMetaType::QObjectStar: 39
+ compare(returnValues, [6, 10, 51, 39]);
+ }
+
+ function test_embeddedQObject()
+ {
+ var success = false;
+ function logReturnValue(value) {
+ success = value;
+ }
+ var channel = client.createChannel(function(channel) {
+ var testObject = channel.objects.testObject;
+ testObject.testEmbeddedObjects([testObject, { obj: testObject }], logReturnValue);
+ });
+ client.awaitInit();
+
+ function awaitMessage(type)
+ {
+ var msg = client.awaitMessage();
+ compare(msg.type, type);
+ compare(msg.object, "testObject");
+ }
+
+ awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod);
client.awaitIdle();
- // QMetaType::Double: 6, QMetaType::QString: 10, QMetaType::Nullptr: 51
- compare(returnValues, [6, 10, 51]);
+ compare(success, true);
}
}