diff options
-rw-r--r-- | examples/webchannel/shared/qwebchannel.js | 14 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher.cpp | 41 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher_p.h | 3 | ||||
-rw-r--r-- | src/webchannel/variantargument_p.h | 4 | ||||
-rw-r--r-- | tests/auto/qml/testobject.cpp | 13 | ||||
-rw-r--r-- | tests/auto/qml/testobject.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/tst_webchannel.qml | 64 |
7 files changed, 134 insertions, 7 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 e23921d..124b5c6 100644 --- a/src/webchannel/qmetaobjectpublisher.cpp +++ b/src/webchannel/qmetaobjectpublisher.cpp @@ -457,6 +457,7 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMe VariantArgument arguments[10]; for (int i = 0; i < qMin(args.size(), method.parameterCount()); ++i) { arguments[i].value = toVariant(args.at(i), method.parameterType(i)); + arguments[i].type = method.parameterType(i); } // construct QGenericReturnArgument QVariant returnValue; @@ -611,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) { @@ -635,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/src/webchannel/variantargument_p.h b/src/webchannel/variantargument_p.h index 263a742..afac507 100644 --- a/src/webchannel/variantargument_p.h +++ b/src/webchannel/variantargument_p.h @@ -62,6 +62,9 @@ struct VariantArgument { operator QGenericArgument() const { + if (type == QMetaType::QVariant) { + return Q_ARG(QVariant, value); + } if (!value.isValid()) { return QGenericArgument(); } @@ -69,6 +72,7 @@ struct VariantArgument } QVariant value; + int type; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/testobject.cpp b/tests/auto/qml/testobject.cpp index ad302e7..bd9db04 100644 --- a/tests/auto/qml/testobject.cpp +++ b/tests/auto/qml/testobject.cpp @@ -77,4 +77,17 @@ QString TestObject::testOverload(const QString &str, int i) return str.toUpper() + QString::number(i + 1); } +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 b9c5ecc..9889523 100644 --- a/tests/auto/qml/testobject.h +++ b/tests/auto/qml/testobject.h @@ -51,6 +51,8 @@ public slots: int testOverload(int i); 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 ed1c4a1..9c9b6d0 100644 --- a/tests/auto/qml/tst_webchannel.qml +++ b/tests/auto/qml/tst_webchannel.qml @@ -556,4 +556,68 @@ TestCase { compare(signalArgs_explicit3, [["the answer is ", 41]]); compare(returnValues, [100, 42, "HELLO WORLD", "THE ANSWER IS 42"]); } + + function test_variantType() + { + var returnValues = []; + function logReturnValue(value) { + returnValues.push(value); + } + var channel = client.createChannel(function(channel) { + var testObject = channel.objects.testObject; + testObject.testVariantType(0, logReturnValue); + testObject.testVariantType("0", logReturnValue); + testObject.testVariantType(null, logReturnValue); + testObject.testVariantType(testObject, logReturnValue); + }); + client.awaitInit(); + + function awaitMessage(type) + { + var msg = client.awaitMessage(); + compare(msg.type, type); + compare(msg.object, "testObject"); + } + + console.log("double arg"); + awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod); + console.log("string arg"); + 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(); + + compare(success, true); + } } |