summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMilian Wolff <milian.wolff@kdab.com>2013-10-23 13:19:06 +0200
committerPierre Rossi <pierre.rossi@gmail.com>2013-11-01 13:57:53 +0100
commit2b744932116e55e32e4740f302ec99bf4f51476e (patch)
treeaefdc2c518061060ac887c3c6867953371d9d892 /src
parent3f8026932650a2dffb4e7ca82bdedc7b0e814e95 (diff)
downloadqtwebchannel-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.qml59
-rw-r--r--src/qobject.js26
-rw-r--r--src/qtmetaobjectpublisher.cpp55
-rw-r--r--src/qtmetaobjectpublisher.h26
-rw-r--r--src/src.pri10
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