Skip to content

Add reference for runtime to blocks container #1941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 48 additions & 52 deletions src/engine/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ const getMonitorIdForBlockWithArgs = require('../util/get-monitor-id');

/**
* 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
* should not request glows. This does not affect glows when clicking on a block to execute it.
*/
class Blocks {
constructor (optNoGlow) {
constructor (runtime, optNoGlow) {
this.runtime = runtime;

/**
* All blocks in the workspace.
* Keys are block IDs, values are metadata about the block.
Expand Down Expand Up @@ -84,7 +87,6 @@ class Blocks {
* @type {boolean}
*/
this.forceNoGlow = optNoGlow || false;

}

/**
Expand Down Expand Up @@ -276,7 +278,7 @@ class Blocks {
}

duplicate () {
const newBlocks = new Blocks(this.forceNoGlow);
const newBlocks = new Blocks(this.runtime, this.forceNoGlow);
newBlocks._blocks = Clone.simple(this._blocks);
newBlocks._scripts = Clone.simple(this._scripts);
return newBlocks;
Expand All @@ -288,23 +290,20 @@ class Blocks {
* serves as a generic adapter between the blocks, variables, and the
* runtime interface.
* @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
if (typeof e !== 'object') return;
if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' &&
typeof e.commentId !== 'string') {
return;
}
const stage = optRuntime.getTargetForStage();
const editingTarget = optRuntime.getEditingTarget();
const stage = this.runtime.getTargetForStage();
const editingTarget = this.runtime.getEditingTarget();

// UI event: clicked scripts toggle in the runtime.
if (e.element === 'stackclick') {
if (optRuntime) {
optRuntime.toggleScript(e.blockId, {stackClick: true});
}
this.runtime.toggleScript(e.blockId, {stackClick: true});
return;
}

Expand All @@ -324,7 +323,7 @@ class Blocks {
element: e.element,
name: e.name,
value: e.newValue
}, optRuntime);
});
break;
case 'move':
this.moveBlock({
Expand All @@ -337,19 +336,15 @@ class Blocks {
});
break;
case 'dragOutside':
if (optRuntime) {
optRuntime.emitBlockDragUpdate(e.isOutside);
}
this.runtime.emitBlockDragUpdate(e.isOutside);
break;
case 'endDrag':
if (optRuntime) {
optRuntime.emitBlockDragUpdate(false /* areBlocksOverGui */);
this.runtime.emitBlockDragUpdate(false /* areBlocksOverGui */);

// Drag blocks onto another sprite
if (e.isOutside) {
const newBlocks = adapter(e);
optRuntime.emitBlockEndDrag(newBlocks, e.blockId);
}
// Drag blocks onto another sprite
if (e.isOutside) {
const newBlocks = adapter(e);
this.runtime.emitBlockEndDrag(newBlocks, e.blockId);
}
break;
case 'delete':
Expand All @@ -360,8 +355,8 @@ class Blocks {
return;
}
// Inform any runtime to forget about glows on this script.
if (optRuntime && this._blocks[e.blockId].topLevel) {
optRuntime.quietGlow(e.blockId);
if (this._blocks[e.blockId].topLevel) {
this.runtime.quietGlow(e.blockId);
}
this.deleteBlock(e.blockId);
break;
Expand All @@ -379,7 +374,7 @@ class Blocks {
}
} else {
// 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) {
if (target.lookupVariableByNameAndType(e.varName, e.varType, true)) {
return;
Expand All @@ -399,7 +394,7 @@ class Blocks {
// This is a global variable
stage.renameVariable(e.varId, e.newName);
// 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++) {
const currTarget = targets[i];
currTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName);
Expand All @@ -413,8 +408,8 @@ class Blocks {
break;
}
case 'comment_create':
if (optRuntime && optRuntime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget();
if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget();
currTarget.createComment(e.commentId, e.blockId, e.text,
e.xy.x, e.xy.y, e.width, e.height, e.minimized);

Expand All @@ -432,8 +427,8 @@ class Blocks {
}
break;
case 'comment_change':
if (optRuntime && optRuntime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget();
if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget();
if (!currTarget.comments.hasOwnProperty(e.commentId)) {
log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);
return;
Expand All @@ -453,8 +448,8 @@ class Blocks {
}
break;
case 'comment_move':
if (optRuntime && optRuntime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget();
if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget();
if (currTarget && !currTarget.comments.hasOwnProperty(e.commentId)) {
log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);
return;
Expand All @@ -466,8 +461,8 @@ class Blocks {
}
break;
case 'comment_delete':
if (optRuntime && optRuntime.getEditingTarget()) {
const currTarget = optRuntime.getEditingTarget();
if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget();
if (!currTarget.comments.hasOwnProperty(e.commentId)) {
// If we're in this state, we have probably received
// a delete event from a workspace that we switched from
Expand All @@ -490,7 +485,7 @@ class Blocks {

// forceNoGlow is set to true on containers that don't affect the project serialization,
// e.g., the toolbox or monitor containers.
if (optRuntime && !this.forceNoGlow) optRuntime.emitProjectChanged();
if (!this.forceNoGlow) this.runtime.emitProjectChanged();
}

// ---------------------------------------------------------------------
Expand Down Expand Up @@ -531,9 +526,8 @@ 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 change VM state.
*/
changeBlock (args, optRuntime) {
changeBlock (args) {
// Validate
if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return;
let block = this._blocks[args.id];
Expand All @@ -555,7 +549,7 @@ class Blocks {
if (args.name === 'VARIABLE' || args.name === 'LIST' ||
args.name === 'BROADCAST_OPTION') {
// 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) {
block.fields[args.name].value = variable.name;
block.fields[args.name].id = args.value;
Expand All @@ -564,7 +558,8 @@ class Blocks {
// Changing the value in a dropdown
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;
}
// The selected item in the sensing of block menu needs to change based on the
Expand All @@ -576,12 +571,12 @@ class Blocks {
} else {
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;
if (flyoutBlock.isMonitored) {
optRuntime.requestUpdateMonitor(Map({
this.runtime.requestUpdateMonitor(Map({
id: flyoutBlock.id,
params: this._getBlockParams(flyoutBlock)
}));
Expand All @@ -592,7 +587,8 @@ class Blocks {
block.mutation = mutationAdapter(args.value);
break;
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;
}

Expand All @@ -609,11 +605,11 @@ class Blocks {
// 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
let newBlock = optRuntime.monitorBlocks.getBlock(newId);
let newBlock = this.runtime.monitorBlocks.getBlock(newId);
if (!newBlock) {
newBlock = JSON.parse(JSON.stringify(block));
newBlock.id = newId;
optRuntime.monitorBlocks.createBlock(newBlock);
this.runtime.monitorBlocks.createBlock(newBlock);
}

block = newBlock; // Carry on through the rest of this code with newBlock
Expand All @@ -625,32 +621,32 @@ class Blocks {
// Variable blocks may be sprite specific depending on the owner of the variable
let isSpriteLocalVariable = false;
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') {
isSpriteLocalVariable = !(optRuntime.getTargetForStage().variables[block.fields.LIST.id]);
isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.LIST.id]);
}

const isSpriteSpecific = isSpriteLocalVariable ||
(optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific);
(this.runtime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific);
if (isSpriteSpecific) {
// If creating a new sprite specific monitor, the only possible target is
// the current editing one b/c you cannot dynamically create monitors.
// 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 {
block.targetId = null;
}

if (wasMonitored && !block.isMonitored) {
optRuntime.requestHideMonitor(block.id);
this.runtime.requestHideMonitor(block.id);
} else if (!wasMonitored && block.isMonitored) {
// Tries to show the monitor for specified block. If it doesn't exist, add the monitor.
if (!optRuntime.requestShowMonitor(block.id)) {
optRuntime.requestAddMonitor(MonitorRecord({
if (!this.runtime.requestShowMonitor(block.id)) {
this.runtime.requestAddMonitor(MonitorRecord({
id: block.id,
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,
params: this._getBlockParams(block),
// @todo(vm#565) for numerical values with decimals, some countries use comma
Expand Down
4 changes: 2 additions & 2 deletions src/engine/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,14 @@ class Runtime extends EventEmitter {
* These will execute on `_editingTarget.`
* @type {!Blocks}
*/
this.flyoutBlocks = new Blocks(true /* force no glow */);
this.flyoutBlocks = new Blocks(this, true /* force no glow */);

/**
* Storage container for monitor blocks.
* These will execute on a target maybe
* @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.
Expand Down
2 changes: 1 addition & 1 deletion src/engine/target.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Target extends EventEmitter {
super();

if (!blocks) {
blocks = new Blocks();
blocks = new Blocks(runtime);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/serialization/sb2.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
}

// 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.
const sprite = new Sprite(blocks, runtime);
// Sprite/stage name from JSON.
Expand Down
2 changes: 1 addition & 1 deletion src/serialization/sb3.js
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
return Promise.resolve(null);
}
// 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.
const sprite = new Sprite(blocks, runtime);
Expand Down
2 changes: 1 addition & 1 deletion src/sprites/sprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Sprite {
this.runtime = runtime;
if (!blocks) {
// Shared set of blocks for all clones.
blocks = new Blocks();
blocks = new Blocks(runtime);
}
this.blocks = blocks;
/**
Expand Down
11 changes: 5 additions & 6 deletions src/virtual-machine.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class VirtualMachine extends EventEmitter {
const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget);
// Remove the target key, since it's a circular reference.
const filteredThreadData = JSON.stringify(threadData, (key, value) => {
if (key === 'target') return;
if (key === 'target' || key === 'blockContainer') return;
return value;
}, 2);
this.emit('playgroundData', {
Expand Down Expand Up @@ -1105,7 +1105,7 @@ class VirtualMachine extends EventEmitter {
*/
blockListener (e) {
if (this.editingTarget) {
this.editingTarget.blocks.blocklyListen(e, this.runtime);
this.editingTarget.blocks.blocklyListen(e);
}
}

Expand All @@ -1114,7 +1114,7 @@ class VirtualMachine extends EventEmitter {
* @param {!Blockly.Event} e Any Blockly event.
*/
flyoutBlockListener (e) {
this.runtime.flyoutBlocks.blocklyListen(e, this.runtime);
this.runtime.flyoutBlocks.blocklyListen(e);
}

/**
Expand All @@ -1125,7 +1125,7 @@ class VirtualMachine extends EventEmitter {
// 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.
if (['create', 'change'].indexOf(e.type) !== -1) {
this.runtime.monitorBlocks.blocklyListen(e, this.runtime);
this.runtime.monitorBlocks.blocklyListen(e);
}
}

Expand All @@ -1137,8 +1137,7 @@ class VirtualMachine extends EventEmitter {
// Filter events by type, since blocks only needs to listen to these
// var events.
if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) {
this.runtime.getTargetForStage().blocks.blocklyListen(e,
this.runtime);
this.runtime.getTargetForStage().blocks.blocklyListen(e);
}
}

Expand Down
Loading