Add runtime to blocks container since it was getting passed in everywhere where it was being referenced. Update calls to blocks constructor.

This commit is contained in:
Karishma Chadha 2019-01-23 18:18:38 -05:00
parent ae95df553d
commit e5723e299a
7 changed files with 58 additions and 63 deletions

View file

@ -17,11 +17,14 @@ const getMonitorIdForBlockWithArgs = require('../util/get-monitor-id');
/** /**
* Create a block container. * Create a block container.
* @param {Runtime} runtime The runtime this block container operates within
* @param {boolean} optNoGlow Optional flag to indicate that blocks in this container * @param {boolean} optNoGlow Optional flag to indicate that blocks in this container
* should not request glows. This does not affect glows when clicking on a block to execute it. * should not request glows. This does not affect glows when clicking on a block to execute it.
*/ */
class Blocks { class Blocks {
constructor (optNoGlow) { constructor (runtime, optNoGlow) {
this.runtime = runtime;
/** /**
* All blocks in the workspace. * All blocks in the workspace.
* Keys are block IDs, values are metadata about the block. * Keys are block IDs, values are metadata about the block.
@ -84,7 +87,6 @@ class Blocks {
* @type {boolean} * @type {boolean}
*/ */
this.forceNoGlow = optNoGlow || false; this.forceNoGlow = optNoGlow || false;
} }
/** /**
@ -276,7 +278,7 @@ class Blocks {
} }
duplicate () { duplicate () {
const newBlocks = new Blocks(this.forceNoGlow); const newBlocks = new Blocks(this.runtime, this.forceNoGlow);
newBlocks._blocks = Clone.simple(this._blocks); newBlocks._blocks = Clone.simple(this._blocks);
newBlocks._scripts = Clone.simple(this._scripts); newBlocks._scripts = Clone.simple(this._scripts);
return newBlocks; return newBlocks;
@ -288,23 +290,20 @@ class Blocks {
* serves as a generic adapter between the blocks, variables, and the * serves as a generic adapter between the blocks, variables, and the
* runtime interface. * runtime interface.
* @param {object} e Blockly "block" or "variable" event * @param {object} e Blockly "block" or "variable" event
* @param {?Runtime} optRuntime Optional runtime to forward click events to.
*/ */
blocklyListen (e, optRuntime) { blocklyListen (e) {
// Validate event // Validate event
if (typeof e !== 'object') return; if (typeof e !== 'object') return;
if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' && if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' &&
typeof e.commentId !== 'string') { typeof e.commentId !== 'string') {
return; return;
} }
const stage = optRuntime.getTargetForStage(); const stage = this.runtime.getTargetForStage();
const editingTarget = optRuntime.getEditingTarget(); const editingTarget = this.runtime.getEditingTarget();
// UI event: clicked scripts toggle in the runtime. // UI event: clicked scripts toggle in the runtime.
if (e.element === 'stackclick') { if (e.element === 'stackclick') {
if (optRuntime) { this.runtime.toggleScript(e.blockId, {stackClick: true});
optRuntime.toggleScript(e.blockId, {stackClick: true});
}
return; return;
} }
@ -324,7 +323,7 @@ class Blocks {
element: e.element, element: e.element,
name: e.name, name: e.name,
value: e.newValue value: e.newValue
}, optRuntime); });
break; break;
case 'move': case 'move':
this.moveBlock({ this.moveBlock({
@ -337,19 +336,15 @@ class Blocks {
}); });
break; break;
case 'dragOutside': case 'dragOutside':
if (optRuntime) { this.runtime.emitBlockDragUpdate(e.isOutside);
optRuntime.emitBlockDragUpdate(e.isOutside);
}
break; break;
case 'endDrag': case 'endDrag':
if (optRuntime) { this.runtime.emitBlockDragUpdate(false /* areBlocksOverGui */);
optRuntime.emitBlockDragUpdate(false /* areBlocksOverGui */);
// Drag blocks onto another sprite // Drag blocks onto another sprite
if (e.isOutside) { if (e.isOutside) {
const newBlocks = adapter(e); const newBlocks = adapter(e);
optRuntime.emitBlockEndDrag(newBlocks, e.blockId); this.runtime.emitBlockEndDrag(newBlocks, e.blockId);
}
} }
break; break;
case 'delete': case 'delete':
@ -360,8 +355,8 @@ class Blocks {
return; return;
} }
// Inform any runtime to forget about glows on this script. // Inform any runtime to forget about glows on this script.
if (optRuntime && this._blocks[e.blockId].topLevel) { if (this._blocks[e.blockId].topLevel) {
optRuntime.quietGlow(e.blockId); this.runtime.quietGlow(e.blockId);
} }
this.deleteBlock(e.blockId); this.deleteBlock(e.blockId);
break; break;
@ -379,7 +374,7 @@ class Blocks {
} }
} else { } else {
// Check for name conflicts in all of the targets // Check for name conflicts in all of the targets
const allTargets = optRuntime.targets.filter(t => t.isOriginal); const allTargets = this.runtime.targets.filter(t => t.isOriginal);
for (const target of allTargets) { for (const target of allTargets) {
if (target.lookupVariableByNameAndType(e.varName, e.varType, true)) { if (target.lookupVariableByNameAndType(e.varName, e.varType, true)) {
return; return;
@ -399,7 +394,7 @@ class Blocks {
// This is a global variable // This is a global variable
stage.renameVariable(e.varId, e.newName); stage.renameVariable(e.varId, e.newName);
// Update all blocks on all targets that use the renamed variable // Update all blocks on all targets that use the renamed variable
const targets = optRuntime.targets; const targets = this.runtime.targets;
for (let i = 0; i < targets.length; i++) { for (let i = 0; i < targets.length; i++) {
const currTarget = targets[i]; const currTarget = targets[i];
currTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName); currTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName);
@ -413,8 +408,8 @@ class Blocks {
break; break;
} }
case 'comment_create': case 'comment_create':
if (optRuntime && optRuntime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
currTarget.createComment(e.commentId, e.blockId, e.text, currTarget.createComment(e.commentId, e.blockId, e.text,
e.xy.x, e.xy.y, e.width, e.height, e.minimized); e.xy.x, e.xy.y, e.width, e.height, e.minimized);
@ -432,8 +427,8 @@ class Blocks {
} }
break; break;
case 'comment_change': case 'comment_change':
if (optRuntime && optRuntime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
if (!currTarget.comments.hasOwnProperty(e.commentId)) { if (!currTarget.comments.hasOwnProperty(e.commentId)) {
log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`); log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);
return; return;
@ -453,8 +448,8 @@ class Blocks {
} }
break; break;
case 'comment_move': case 'comment_move':
if (optRuntime && optRuntime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
if (currTarget && !currTarget.comments.hasOwnProperty(e.commentId)) { if (currTarget && !currTarget.comments.hasOwnProperty(e.commentId)) {
log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`); log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);
return; return;
@ -466,8 +461,8 @@ class Blocks {
} }
break; break;
case 'comment_delete': case 'comment_delete':
if (optRuntime && optRuntime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
if (!currTarget.comments.hasOwnProperty(e.commentId)) { if (!currTarget.comments.hasOwnProperty(e.commentId)) {
// If we're in this state, we have probably received // If we're in this state, we have probably received
// a delete event from a workspace that we switched from // a delete event from a workspace that we switched from
@ -490,7 +485,7 @@ class Blocks {
// forceNoGlow is set to true on containers that don't affect the project serialization, // forceNoGlow is set to true on containers that don't affect the project serialization,
// e.g., the toolbox or monitor containers. // e.g., the toolbox or monitor containers.
if (optRuntime && !this.forceNoGlow) optRuntime.emitProjectChanged(); if (!this.forceNoGlow) this.runtime.emitProjectChanged();
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -531,9 +526,8 @@ class Blocks {
/** /**
* Block management: change block field values * Block management: change block field values
* @param {!object} args Blockly change event to be processed * @param {!object} args Blockly change event to be processed
* @param {?Runtime} optRuntime Optional runtime to allow changeBlock to change VM state.
*/ */
changeBlock (args, optRuntime) { changeBlock (args) {
// Validate // Validate
if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return; if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return;
let block = this._blocks[args.id]; let block = this._blocks[args.id];
@ -555,7 +549,7 @@ class Blocks {
if (args.name === 'VARIABLE' || args.name === 'LIST' || if (args.name === 'VARIABLE' || args.name === 'LIST' ||
args.name === 'BROADCAST_OPTION') { args.name === 'BROADCAST_OPTION') {
// Get variable name using the id in args.value. // Get variable name using the id in args.value.
const variable = optRuntime.getEditingTarget().lookupVariableById(args.value); const variable = this.runtime.getEditingTarget().lookupVariableById(args.value);
if (variable) { if (variable) {
block.fields[args.name].value = variable.name; block.fields[args.name].value = variable.name;
block.fields[args.name].id = args.value; block.fields[args.name].id = args.value;
@ -564,7 +558,8 @@ class Blocks {
// Changing the value in a dropdown // Changing the value in a dropdown
block.fields[args.name].value = args.value; block.fields[args.name].value = args.value;
if (!optRuntime){ if (!this.runtime){
log.warn('Runtime is not optional, it should get passed in when the block container is created.');
break; break;
} }
// The selected item in the sensing of block menu needs to change based on the // The selected item in the sensing of block menu needs to change based on the
@ -576,12 +571,12 @@ class Blocks {
} else { } else {
this._blocks[block.parent].fields.PROPERTY.value = 'x position'; this._blocks[block.parent].fields.PROPERTY.value = 'x position';
} }
optRuntime.requestBlocksUpdate(); this.runtime.requestBlocksUpdate();
} }
const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block; const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block;
if (flyoutBlock.isMonitored) { if (flyoutBlock.isMonitored) {
optRuntime.requestUpdateMonitor(Map({ this.runtime.requestUpdateMonitor(Map({
id: flyoutBlock.id, id: flyoutBlock.id,
params: this._getBlockParams(flyoutBlock) params: this._getBlockParams(flyoutBlock)
})); }));
@ -592,7 +587,8 @@ class Blocks {
block.mutation = mutationAdapter(args.value); block.mutation = mutationAdapter(args.value);
break; break;
case 'checkbox': { case 'checkbox': {
if (!optRuntime) { if (!this.runtime) {
log.warn('Runtime is not optional, it should get passed in when the block container is created.');
break; break;
} }
@ -609,11 +605,11 @@ class Blocks {
// the checkbox because we're using the id of the block in the flyout as the base // the checkbox because we're using the id of the block in the flyout as the base
// check if a block with the new id already exists, otherwise create // check if a block with the new id already exists, otherwise create
let newBlock = optRuntime.monitorBlocks.getBlock(newId); let newBlock = this.runtime.monitorBlocks.getBlock(newId);
if (!newBlock) { if (!newBlock) {
newBlock = JSON.parse(JSON.stringify(block)); newBlock = JSON.parse(JSON.stringify(block));
newBlock.id = newId; newBlock.id = newId;
optRuntime.monitorBlocks.createBlock(newBlock); this.runtime.monitorBlocks.createBlock(newBlock);
} }
block = newBlock; // Carry on through the rest of this code with newBlock block = newBlock; // Carry on through the rest of this code with newBlock
@ -625,32 +621,32 @@ class Blocks {
// Variable blocks may be sprite specific depending on the owner of the variable // Variable blocks may be sprite specific depending on the owner of the variable
let isSpriteLocalVariable = false; let isSpriteLocalVariable = false;
if (block.opcode === 'data_variable') { if (block.opcode === 'data_variable') {
isSpriteLocalVariable = !(optRuntime.getTargetForStage().variables[block.fields.VARIABLE.id]); isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.VARIABLE.id]);
} else if (block.opcode === 'data_listcontents') { } else if (block.opcode === 'data_listcontents') {
isSpriteLocalVariable = !(optRuntime.getTargetForStage().variables[block.fields.LIST.id]); isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.LIST.id]);
} }
const isSpriteSpecific = isSpriteLocalVariable || const isSpriteSpecific = isSpriteLocalVariable ||
(optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) && (this.runtime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific); this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific);
if (isSpriteSpecific) { if (isSpriteSpecific) {
// If creating a new sprite specific monitor, the only possible target is // If creating a new sprite specific monitor, the only possible target is
// the current editing one b/c you cannot dynamically create monitors. // the current editing one b/c you cannot dynamically create monitors.
// Also, do not change the targetId if it has already been assigned // Also, do not change the targetId if it has already been assigned
block.targetId = block.targetId || optRuntime.getEditingTarget().id; block.targetId = block.targetId || this.runtime.getEditingTarget().id;
} else { } else {
block.targetId = null; block.targetId = null;
} }
if (wasMonitored && !block.isMonitored) { if (wasMonitored && !block.isMonitored) {
optRuntime.requestHideMonitor(block.id); this.runtime.requestHideMonitor(block.id);
} else if (!wasMonitored && block.isMonitored) { } else if (!wasMonitored && block.isMonitored) {
// Tries to show the monitor for specified block. If it doesn't exist, add the monitor. // Tries to show the monitor for specified block. If it doesn't exist, add the monitor.
if (!optRuntime.requestShowMonitor(block.id)) { if (!this.runtime.requestShowMonitor(block.id)) {
optRuntime.requestAddMonitor(MonitorRecord({ this.runtime.requestAddMonitor(MonitorRecord({
id: block.id, id: block.id,
targetId: block.targetId, targetId: block.targetId,
spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null, spriteName: block.targetId ? this.runtime.getTargetById(block.targetId).getName() : null,
opcode: block.opcode, opcode: block.opcode,
params: this._getBlockParams(block), params: this._getBlockParams(block),
// @todo(vm#565) for numerical values with decimals, some countries use comma // @todo(vm#565) for numerical values with decimals, some countries use comma

View file

@ -185,14 +185,14 @@ class Runtime extends EventEmitter {
* These will execute on `_editingTarget.` * These will execute on `_editingTarget.`
* @type {!Blocks} * @type {!Blocks}
*/ */
this.flyoutBlocks = new Blocks(true /* force no glow */); this.flyoutBlocks = new Blocks(this, true /* force no glow */);
/** /**
* Storage container for monitor blocks. * Storage container for monitor blocks.
* These will execute on a target maybe * These will execute on a target maybe
* @type {!Blocks} * @type {!Blocks}
*/ */
this.monitorBlocks = new Blocks(true /* force no glow */); this.monitorBlocks = new Blocks(this, true /* force no glow */);
/** /**
* Currently known editing target for the VM. * Currently known editing target for the VM.

View file

@ -25,7 +25,7 @@ class Target extends EventEmitter {
super(); super();
if (!blocks) { if (!blocks) {
blocks = new Blocks(); blocks = new Blocks(runtime);
} }
/** /**

View file

@ -413,7 +413,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
} }
// Blocks container for this object. // Blocks container for this object.
const blocks = new Blocks(); const blocks = new Blocks(runtime);
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
const sprite = new Sprite(blocks, runtime); const sprite = new Sprite(blocks, runtime);
// Sprite/stage name from JSON. // Sprite/stage name from JSON.

View file

@ -834,7 +834,7 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
return Promise.resolve(null); return Promise.resolve(null);
} }
// Blocks container for this object. // Blocks container for this object.
const blocks = new Blocks(); const blocks = new Blocks(runtime);
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
const sprite = new Sprite(blocks, runtime); const sprite = new Sprite(blocks, runtime);

View file

@ -18,7 +18,7 @@ class Sprite {
this.runtime = runtime; this.runtime = runtime;
if (!blocks) { if (!blocks) {
// Shared set of blocks for all clones. // Shared set of blocks for all clones.
blocks = new Blocks(); blocks = new Blocks(runtime);
} }
this.blocks = blocks; this.blocks = blocks;
/** /**

View file

@ -1105,7 +1105,7 @@ class VirtualMachine extends EventEmitter {
*/ */
blockListener (e) { blockListener (e) {
if (this.editingTarget) { if (this.editingTarget) {
this.editingTarget.blocks.blocklyListen(e, this.runtime); this.editingTarget.blocks.blocklyListen(e);
} }
} }
@ -1114,7 +1114,7 @@ class VirtualMachine extends EventEmitter {
* @param {!Blockly.Event} e Any Blockly event. * @param {!Blockly.Event} e Any Blockly event.
*/ */
flyoutBlockListener (e) { flyoutBlockListener (e) {
this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); this.runtime.flyoutBlocks.blocklyListen(e);
} }
/** /**
@ -1125,7 +1125,7 @@ class VirtualMachine extends EventEmitter {
// Filter events by type, since monitor blocks only need to listen to these events. // Filter events by type, since monitor blocks only need to listen to these events.
// Monitor blocks shouldn't be destroyed when flyout blocks are deleted. // Monitor blocks shouldn't be destroyed when flyout blocks are deleted.
if (['create', 'change'].indexOf(e.type) !== -1) { if (['create', 'change'].indexOf(e.type) !== -1) {
this.runtime.monitorBlocks.blocklyListen(e, this.runtime); this.runtime.monitorBlocks.blocklyListen(e);
} }
} }
@ -1137,8 +1137,7 @@ class VirtualMachine extends EventEmitter {
// Filter events by type, since blocks only needs to listen to these // Filter events by type, since blocks only needs to listen to these
// var events. // var events.
if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) { if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) {
this.runtime.getTargetForStage().blocks.blocklyListen(e, this.runtime.getTargetForStage().blocks.blocklyListen(e);
this.runtime);
} }
} }