diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 8234a6e24..16bcd35b1 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -522,18 +522,21 @@ class Blocks { block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null; if (wasMonitored && !block.isMonitored) { - optRuntime.requestRemoveMonitor(block.id); + optRuntime.requestHideMonitor(block.id); } else if (!wasMonitored && block.isMonitored) { - optRuntime.requestAddMonitor(MonitorRecord({ - id: block.id, - targetId: block.targetId, - spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null, - opcode: block.opcode, - params: this._getBlockParams(block), - // @todo(vm#565) for numerical values with decimals, some countries use comma - value: '', - mode: block.opcode === 'data_listcontents' ? 'list' : 'default' - })); + // Tries to show the monitor for specified block. If it doesn't exist, add the monitor. + if (!optRuntime.requestShowMonitor(block.id)) { + optRuntime.requestAddMonitor(MonitorRecord({ + id: block.id, + targetId: block.targetId, + spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null, + opcode: block.opcode, + params: this._getBlockParams(block), + // @todo(vm#565) for numerical values with decimals, some countries use comma + value: '', + mode: block.opcode === 'data_listcontents' ? 'list' : 'default' + })); + } } break; } diff --git a/src/engine/runtime.js b/src/engine/runtime.js index ab38955e4..cf6d90a67 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1518,26 +1518,38 @@ class Runtime extends EventEmitter { /** * Add a monitor to the state. If the monitor already exists in the state, - * overwrites it. + * updates those properties that are defined in the given monitor record. * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState = this._monitorState.set(monitor.get('id'), monitor); + const id = monitor.get('id'); + if (!this.requestUpdateMonitor(monitor)) { // update monitor if it exists in the state + // if the monitor did not exist in the state, add it + this._monitorState = this._monitorState.set(id, monitor); + } } /** - * Update a monitor in the state. Does nothing if the monitor does not already - * exist in the state. + * Update a monitor in the state and report success/failure of update. * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite * values on the old monitor with the same ID. If a value isn't defined on the new monitor, * the old monitor will keep its old value. + * @return {boolean} true if monitor exists in the state and was updated, false if it did not exist. */ requestUpdateMonitor (monitor) { const id = monitor.get('id'); if (this._monitorState.has(id)) { this._monitorState = - this._monitorState.set(id, this._monitorState.get(id).merge(monitor)); + // Use mergeWith here to prevent undefined values from overwriting existing ones + this._monitorState.set(id, this._monitorState.get(id).mergeWith((prev, next) => { + if (typeof next === 'undefined' || next === null) { + return prev; + } + return next; + }, monitor)); + return true; } + return false; } /** @@ -1549,6 +1561,31 @@ class Runtime extends EventEmitter { this._monitorState = this._monitorState.delete(monitorId); } + /** + * Hides a monitor and returns success/failure of action. + * @param {!string} monitorId ID of the monitor to hide. + * @return {boolean} true if monitor exists and was updated, false otherwise + */ + requestHideMonitor (monitorId) { + return this.requestUpdateMonitor(new Map([ + ['id', monitorId], + ['visible', false] + ])); + } + + /** + * Shows a monitor and returns success/failure of action. + * not exist in the state. + * @param {!string} monitorId ID of the monitor to show. + * @return {boolean} true if monitor exists and was updated, false otherwise + */ + requestShowMonitor (monitorId) { + return this.requestUpdateMonitor(new Map([ + ['id', monitorId], + ['visible', true] + ])); + } + /** * Removes all monitors with the given target ID from the state. Does nothing if * the monitor already does not exist in the state.