summaryrefslogtreecommitdiff
path: root/src/qobject.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/qobject.js')
-rw-r--r--src/qobject.js204
1 files changed, 154 insertions, 50 deletions
diff --git a/src/qobject.js b/src/qobject.js
index d221525..012332e 100644
--- a/src/qobject.js
+++ b/src/qobject.js
@@ -42,17 +42,78 @@
function QObject(name, data, webChannel)
{
this.__id__ = name;
+ webChannel.objectMap[name] = this;
+
+ // List of callbacks that get invoked upon signal emission
this.__objectSignals__ = {};
- var methodsAndSignals = [];
- for (var i in data.methods)
- methodsAndSignals.push(data.methods[i]);
- for (var i in data.signals)
- methodsAndSignals.push(data.signals[i]);
+ // Cache of all properties, updated when a notify signal is emitted
+ this.__propertyCache__ = {};
var object = this;
- methodsAndSignals.forEach(function(method) {
+ // ----------------------------------------------------------------------
+
+ function addSignal(signal, isPropertyNotifySignal)
+ {
+ object[signal] = {
+ connect: function(callback) {
+ if (typeof(callback) !== "function") {
+ console.error("Bad callback given to connect to signal " + signal);
+ return;
+ }
+
+ object.__objectSignals__[signal] = object.__objectSignals__[signal] || [];
+ object.__objectSignals__[signal].push(callback);
+
+ if (!isPropertyNotifySignal) {
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ webChannel.exec({
+ type: "Qt.connectToSignal",
+ object: object.__id__,
+ signal: signal
+ });
+ }
+ }
+ // TODO: disconnect eventually
+ };
+ }
+
+ /**
+ * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
+ */
+ function invokeSignalCallbacks(signalName, signalArgs)
+ {
+ var connections = object.__objectSignals__[signalName];
+ if (connections) {
+ connections.forEach(function(callback) {
+ callback.apply(callback, signalArgs);
+ });
+ }
+ }
+
+ this.propertyUpdate = function(signals, propertyMap)
+ {
+ // update property cache
+ for (var propertyName in propertyMap) {
+ var propertyValue = propertyMap[propertyName];
+ object.__propertyCache__[propertyName] = propertyValue;
+ }
+
+ for (var signalName in signals) {
+ // Invoke all callbacks, as signalEmitted() does not. This ensures the
+ // property cache is updated before the callbacks are invoked.
+ invokeSignalCallbacks(signalName, signals[signalName]);
+ }
+ }
+
+ this.signalEmitted = function(signalName, signalArgs)
+ {
+ invokeSignalCallbacks(signalName, signalArgs);
+ }
+
+ function addMethod(method)
+ {
object[method] = function() {
var args = [];
var callback;
@@ -64,80 +125,123 @@ function QObject(name, data, webChannel)
}
webChannel.exec({"type": "Qt.invokeMethod", "object": object.__id__, "method": method, "args": args}, function(response) {
- if ((response != undefined) && callback) {
+ if ( (response !== undefined) && callback ) {
(callback)(response);
}
});
};
- });
+ }
- function connectToSignal(signal)
+ function bindGetterSetter(propertyInfo)
{
- object[signal].connect = function(callback) {
- if (typeof(callback) !== "function") {
- console.error("Bad callback given to connect to signal " + signal);
+ var propertyName = propertyInfo[0];
+ var notifySignal = propertyInfo[1];
+ // initialize property cache with current value
+ object.__propertyCache__[propertyName] = propertyInfo[2]
+
+ if (notifySignal) {
+ if (notifySignal === 1) {
+ /// signal name is optimized away, reconstruct the actual name
+ notifySignal = propertyName + "Changed";
+ }
+ addSignal(notifySignal, true);
+ }
+
+ object.__defineSetter__(propertyName, function(value) {
+ if (value === undefined) {
+ console.warn("Property setter for " + propertyName + " called with undefined value!");
return;
}
- object.__objectSignals__[signal] = object.__objectSignals__[signal] || [];
- webChannel.exec({"type": "Qt.connectToSignal", "object": object.__id__, "signal": signal});
- object.__objectSignals__[signal].push(callback);
- };
- }
- for (var i in data.signals) {
- var signal = data.signals[i];
- connectToSignal(data.signals[i]);
- }
+ object.__propertyCache__[propertyName] = value;
+ webChannel.exec({"type": "Qt.setProperty", "object": object.__id__, "property": propertyName, "value": value });
- function bindGetterSetter(property)
- {
- object.__defineSetter__(property, function(value) {
- webChannel.exec({"type": "Qt.setProperty", "object": object.__id__, "property": property, "value": value });
});
- object.__defineGetter__(property, function() {
- return (function(callback) {
- webChannel.exec({"type": "Qt.getProperty", "object": object.__id__, "property": property}, function(response) {
+ object.__defineGetter__(propertyName, function () {
+ return (function (callback) {
+ var propertyValue = object.__propertyCache__[propertyName];
+ if (propertyValue === undefined) {
+ // This shouldn't happen
+ console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
+ }
+
+ // TODO: A callback is not required here anymore, but is kept for backwards compatibility
+ if (callback !== undefined) {
if (typeof(callback) !== "function") {
console.error("Bad callback given to get property " + property);
return;
}
- callback(response);
- });
+ callback(propertyValue);
+ } else {
+ return propertyValue;
+ }
});
});
}
- for (i in data.properties) {
- bindGetterSetter(data.properties[i]);
- }
- for (i in data.enums) {
- object[i] = data.enums[i];
+ // ----------------------------------------------------------------------
+
+ data.methods.forEach(addMethod);
+
+ data.properties.forEach(bindGetterSetter);
+
+ data.signals.forEach(function(signal) { addSignal(signal, false); });
+
+ for (var name in data.enums) {
+ object[name] = data.enums[name];
}
}
window.setupQObjectWebChannel = function(webChannel, doneCallback)
{
+ // prevent multiple initialization which might happen with multiple webchannel clients.
+ var initialized = false;
+
webChannel.subscribe(
"Qt.signal",
function(payload) {
- var object = window[payload.object];
+ var object = webChannel.objectMap[payload.object];
if (object) {
- var connections = object.__objectSignals__[payload.signal];
- if (connections) {
- connections.forEach(function(callback) {
- callback.apply(callback, payload.args);
- });
- }
+ object.signalEmitted(payload.signal, payload.args);
+ } else {
+ console.warn("Unhandled signal: " + payload.object + "::" + payload.signal);
}
}
);
- webChannel.exec({type:"Qt.getObjects"}, function(payload) {
- for (var objectName in payload) {
- var data = payload[objectName];
- var object = new QObject(objectName, data, webChannel);
- window[objectName] = object;
+
+ webChannel.subscribe(
+ "Qt.propertyUpdate",
+ function(payload) {
+ for (var i in payload) {
+ var data = payload[i];
+ var object = webChannel.objectMap[data.object];
+ if (object) {
+ object.propertyUpdate(data.signals, data.propertyMap);
+ } else {
+ console.warn("Unhandled property update: " + data.object + "::" + data.signal);
+ }
+ }
+ setTimeout(function() { webChannel.exec({type: "Qt.idle"}); }, 0);
}
- if (doneCallback) {
- doneCallback();
+ );
+
+ webChannel.subscribe(
+ "Qt.init",
+ function(payload) {
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+ for (var objectName in payload) {
+ var data = payload[objectName];
+ var object = new QObject(objectName, data, webChannel);
+ window[objectName] = object;
+ }
+ if (doneCallback) {
+ doneCallback();
+ }
+ setTimeout(function() { webChannel.exec({type: "Qt.idle"}); }, 0);
}
- });
+ );
+
+ webChannel.exec({type:"Qt.init"});
};