diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2013-10-23 13:19:06 +0200 |
---|---|---|
committer | Pierre Rossi <pierre.rossi@gmail.com> | 2013-11-01 13:57:53 +0100 |
commit | 2b744932116e55e32e4740f302ec99bf4f51476e (patch) | |
tree | aefdc2c518061060ac887c3c6867953371d9d892 /src | |
parent | 3f8026932650a2dffb4e7ca82bdedc7b0e814e95 (diff) | |
download | qtwebchannel-2b744932116e55e32e4740f302ec99bf4f51476e.tar.gz |
Make it possible to wrap QObject's on the fly.
This is required for factory-like methods on the C++/QML side,
which we want to access from the HTML side as well.
Change-Id: I2852bbc9c8effb6d6f49b5be784241a6e2320823
Reviewed-by: Pierre Rossi <pierre.rossi@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/MetaObjectPublisher.qml | 59 | ||||
-rw-r--r-- | src/qobject.js | 26 | ||||
-rw-r--r-- | src/qtmetaobjectpublisher.cpp | 55 | ||||
-rw-r--r-- | src/qtmetaobjectpublisher.h | 26 | ||||
-rw-r--r-- | src/src.pri | 10 |
5 files changed, 168 insertions, 8 deletions
diff --git a/src/MetaObjectPublisher.qml b/src/MetaObjectPublisher.qml index 2b97c02..226e0a3 100644 --- a/src/MetaObjectPublisher.qml +++ b/src/MetaObjectPublisher.qml @@ -78,6 +78,25 @@ MetaObjectPublisherImpl // object info map set. property bool propertyUpdatesInitialized: false + /** + * Wrap a result value if it's a Qt QObject + * + * @return object info for wrapped Qt Object, + * or the same value if no wrapping needed + * + */ + function wrapResult(result) + { + if (typeof(result) === "object" + && result["objectName"] !== undefined) + { + var ret = wrapObject(result); + initializePropertyUpdates(ret.id, ret.data, result, webChannel); + return ret; + } + return result; + } + function convertQMLArgsToJSArgs(qmlArgs) { // NOTE: QML arguments is a map not an array it seems... @@ -106,12 +125,26 @@ MetaObjectPublisherImpl } if (payload.object) { + var isWrapped = false; var object = registeredObjects[payload.object]; + if (!object) { + object = unwrapObject(payload.object); + if (object) + isWrapped = true; + else + return false + } if (payload.type === "Qt.invokeMethod") { var method = object[payload.method]; if (method !== undefined) { - webChannel.respond(message.id, method.apply(method, payload.args)); + webChannel.respond(message.id, + wrapResult(method.apply(method, payload.args))); + return true; + } + if (isWrapped && payload.method === "deleteLater") { + // invoke `deleteLater` on wrapped QObject indirectly + deleteWrappedObject(object); return true; } return false; @@ -135,6 +168,11 @@ MetaObjectPublisherImpl } return true; } + // connecting to `destroyed` signal of wrapped QObject + if (isWrapped && payload.signal === "destroyed") { + // is a no-op on this side + return true; + } return false; } if (payload.type === "Qt.setProperty") { @@ -248,6 +286,13 @@ MetaObjectPublisherImpl var data = []; for (var objectName in pendingPropertyUpdates) { var object = registeredObjects[objectName]; + if (!object) { + object = unwrapObject(objectName); + if (!object) { + console.error("Got property update for unknown object " + objectName); + continue; + } + } var signals = pendingPropertyUpdates[objectName]; var propertyMap = {}; for (var signalName in signals) { @@ -291,6 +336,18 @@ MetaObjectPublisherImpl } } + onWrappedObjectDestroyed: { // (const QString& id) + // act as if object had sent `destroyed` signal + webChannel.sendMessage("Qt.signal", { + object: id, + signal: "destroyed", + args: [] + }); + delete subscriberCountMap[id]; + delete pendingPropertyUpdates[id]; + delete signalToPropertyMap[id] + } + /** * Aggregate property updates since we get multiple Qt.idle message when we have multiple * clients. They all share the same QWebProcess though so we must take special care to diff --git a/src/qobject.js b/src/qobject.js index 012332e..5f55334 100644 --- a/src/qobject.js +++ b/src/qobject.js @@ -54,6 +54,26 @@ function QObject(name, data, webChannel) // ---------------------------------------------------------------------- + function unwrapQObject( response ) + { + if (!response["__QObject*__"] + || response["id"] === undefined + || response["data"] === undefined) { + return response; + } + var objectId = response.id; + if (webChannel.objectMap[objectId]) + return webChannel.objectMap[objectId]; + + var qObject = new QObject( objectId, response.data, webChannel ); + qObject.destroyed.connect(function() { + if (webChannel.objectMap[objectId] === qObject) { + delete webChannel.objectMap[objectId]; + } + }); + return qObject; + } + function addSignal(signal, isPropertyNotifySignal) { object[signal] = { @@ -126,7 +146,7 @@ function QObject(name, data, webChannel) webChannel.exec({"type": "Qt.invokeMethod", "object": object.__id__, "method": method, "args": args}, function(response) { if ( (response !== undefined) && callback ) { - (callback)(response); + (callback)(unwrapQObject(response)); } }); }; @@ -199,7 +219,7 @@ window.setupQObjectWebChannel = function(webChannel, doneCallback) webChannel.subscribe( "Qt.signal", function(payload) { - var object = webChannel.objectMap[payload.object]; + var object = window[payload.object] || webChannel.objectMap[payload.object]; if (object) { object.signalEmitted(payload.signal, payload.args); } else { @@ -213,7 +233,7 @@ window.setupQObjectWebChannel = function(webChannel, doneCallback) function(payload) { for (var i in payload) { var data = payload[i]; - var object = webChannel.objectMap[data.object]; + var object = window[data.object] || webChannel.objectMap[data.object]; if (object) { object.propertyUpdate(data.signals, data.propertyMap); } else { diff --git a/src/qtmetaobjectpublisher.cpp b/src/qtmetaobjectpublisher.cpp index 8e871e0..85ba62c 100644 --- a/src/qtmetaobjectpublisher.cpp +++ b/src/qtmetaobjectpublisher.cpp @@ -50,6 +50,10 @@ static const QString KEY_METHODS = QStringLiteral("methods"); static const QString KEY_PROPERTIES = QStringLiteral("properties"); static const QString KEY_ENUMS = QStringLiteral("enums"); +static const QString KEY_QOBJECT = QStringLiteral("__QObject*__"); +static const QString KEY_ID = QStringLiteral("id"); +static const QString KEY_DATA = QStringLiteral("data"); + QtMetaObjectPublisher::QtMetaObjectPublisher(QQuickItem *parent) : QQuickItem(parent) { @@ -142,3 +146,54 @@ QVariantMap QtMetaObjectPublisher::classInfoForObject(QObject *object) const data[KEY_ENUMS] = qtEnums; return data; } + +static QString objectId(QObject *object) +{ + return QString::number(quintptr(object), 16); +} + +QVariant QtMetaObjectPublisher::wrapObject(QObject *object) +{ + if (!object) + return QVariant(); + + const QString& id = objectId(object); + + const WrapMapCIt& p = m_wrappedObjects.constFind(id); + if (p != m_wrappedObjects.constEnd()) + return p.value().second; + + QVariantMap objectInfo; + objectInfo[KEY_QOBJECT] = true; + objectInfo[KEY_ID] = id; + objectInfo[KEY_DATA] = classInfoForObject(object); + + m_wrappedObjects.insert(id, WrapInfo(object, objectInfo)); + connect(object, SIGNAL(destroyed(QObject*)), SLOT(wrappedObjectDestroyed(QObject*))); + + return objectInfo; +} + +QObject *QtMetaObjectPublisher::unwrapObject(const QString& id) const +{ + const WrapMapCIt& p = m_wrappedObjects.constFind(id); + if (p != m_wrappedObjects.constEnd()) + return p.value().first; + return 0; +} + +void QtMetaObjectPublisher::wrappedObjectDestroyed(QObject* object) +{ + const QString& id = objectId(object); + m_wrappedObjects.remove(id); + emit wrappedObjectDestroyed(id); +} + +void QtMetaObjectPublisher::deleteWrappedObject(QObject* object) const +{ + if (!m_wrappedObjects.contains(objectId(object))) { + qWarning() << "Not deleting non-wrapped object" << object; + return; + } + object->deleteLater(); +} diff --git a/src/qtmetaobjectpublisher.h b/src/qtmetaobjectpublisher.h index 0c19374..bd48678 100644 --- a/src/qtmetaobjectpublisher.h +++ b/src/qtmetaobjectpublisher.h @@ -46,8 +46,6 @@ #include <QVariantMap> #include <QQuickItem> -class QObjectWrapper; - // NOTE: QQuickItem inheritance required to enable QML item nesting (i.e. Timer in MetaObjectPublisher) class QtMetaObjectPublisher : public QQuickItem { @@ -57,6 +55,30 @@ public: Q_INVOKABLE QVariantMap classInfoForObjects(const QVariantMap &objects) const; Q_INVOKABLE QVariantMap classInfoForObject(QObject *object) const; + + /// wrap object and return class info + Q_INVOKABLE QVariant wrapObject(QObject *object); + /// Search object by id and return it, or null if it could not be found. + Q_INVOKABLE QObject *unwrapObject(const QString &id) const; + /// Invoke delete later on @p object, but only if it is a wrapped object. + Q_INVOKABLE void deleteWrappedObject(QObject *object) const; + +signals: + void wrappedObjectDestroyed(const QString& id); + +private slots: + void wrappedObjectDestroyed(QObject* object); + +private: + /// Pairs of QObject and generated object info + typedef QPair<QObject *, QVariantMap> WrapInfo; + /// Maps object id to wrap info + typedef QHash<QString, WrapInfo> WrapMap; + /// Const iterator for map + typedef WrapMap::const_iterator WrapMapCIt; + + /// Map of wrapped objects + WrapMap m_wrappedObjects; }; #endif // QTMETAOBJECTPUBLISHER_H diff --git a/src/src.pri b/src/src.pri index dc8670b..408b430 100644 --- a/src/src.pri +++ b/src/src.pri @@ -1,3 +1,9 @@ QT += network -SOURCES += $$PWD/qwebchannel.cpp $$PWD/qtmetaobjectpublisher.cpp $$PWD/qwebsocketserver.cpp -HEADERS += $$PWD/qwebchannel.h $$PWD/qtmetaobjectpublisher.h $$PWD/qwebsocketserver.h +SOURCES += \ + $$PWD/qwebchannel.cpp \ + $$PWD/qtmetaobjectpublisher.cpp \ + $$PWD/qwebsocketserver.cpp +HEADERS += \ + $$PWD/qwebchannel.h \ + $$PWD/qtmetaobjectpublisher.h \ + $$PWD/qwebsocketserver.h |