diff options
Diffstat (limited to 'src/qobject.js')
-rw-r--r-- | src/qobject.js | 204 |
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"}); }; |