diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 3d9b44d61..86525627d 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -270,7 +270,7 @@ class Blocks { /** * Block management: change block field values * @param {!object} args Blockly change event to be processed - * @param {?Runtime} optRuntime Optional runtime to allow changeBlock to emit actions. + * @param {?Runtime} optRuntime Optional runtime to allow changeBlock to change VM state. */ changeBlock (args, optRuntime) { // Validate @@ -291,21 +291,23 @@ class Blocks { case 'checkbox': block.isMonitored = args.value; if (optRuntime && wasMonitored && !block.isMonitored) { - optRuntime.removeMonitors([{id: block.id}]); + optRuntime.removeMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { - optRuntime.addMonitors( + optRuntime.addMonitor( // Ensure that value is not undefined, since React requires it - [{ + { // @todo(dd) this will collide if multiple sprites use same block id: block.id, category: 'data', // @todo(dd) how to handle translation here? label: block.opcode, + // @todo(dd) for numerical values with decimals, some countries use comma value: '', x: 0, - // @todo(dd) place below the last monitor instead + // @todo(dd) Don't require sending x and y when instantiating a + // monitor. If it's not preset the GUI should decide. y: 0 - }] + } ); } break; diff --git a/src/engine/execute.js b/src/engine/execute.js index 0d263fba7..a6273fc87 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -91,12 +91,11 @@ const execute = function (sequencer, thread) { if (thread.showVisualReport) { runtime.visualReport(currentBlockId, resolvedValue); } - if (thread.updateMonitor) { - runtime.updateMonitors([{ + runtime.updateMonitor({ id: currentBlockId, value: String(resolvedValue) - }]); + }); } } // Finished any yields. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 48d2da5cd..a1ab579e7 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -104,6 +104,11 @@ class Runtime extends EventEmitter { */ this._cloneCounter = 0; + /** + * List of all monitors. + */ + this._monitorState = {}; + /** * Whether the project is in "turbo mode." * @type {Boolean} @@ -695,6 +700,7 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + this.emit(Runtime.MONITORS_UPDATE, Object.values(this._monitorState)); } /** @@ -874,27 +880,32 @@ class Runtime extends EventEmitter { } /** - * Emit a monitor update which adds or updates if exists the given monitors. - * @param {!Array} monitors Array of monitors to update. + * Add a monitor to the state. If the monitor already exists in the state, + * overwrites it. + * @param {!object} monitor Monitor to add. */ - updateMonitors (monitors) { - this.emit(Runtime.MONITORS_UPDATE, monitors); + addMonitor (monitor) { + this._monitorState[monitor.id] = monitor; } /** - * Emit a monitor update which removes if exists the given monitors. - * @param {!Array} monitors Array of monitors to remove. + * Update a monitor in the state. Does nothing if the monitor does not already + * exist in the state. + * @param {!object} monitor Monitor to update. */ - removeMonitors (monitors) { - this.emit(Runtime.MONITORS_REMOVED, monitors); + updateMonitor (monitor) { + if (this._monitorState.hasOwnProperty(monitor.id)) { + this._monitorState[monitor.id] = Object.assign({}, this._monitorState[monitor.id], monitor); + } } /** - * Emit a monitor update which adds if it doesn't already exist the given monitors. - * @param {!Array} monitors Array of monitors to add. + * Removes a monitor from the state. Does nothing if the monitor already does + * not exist in the state. + * @param {!object} monitorId ID of the monitor to remove. */ - addMonitors (monitors) { - this.emit(Runtime.MONITORS_ADDED, monitors); + removeMonitor (monitorId) { + delete this._monitorState[monitorId]; } /**