Merge pull request #992 from paulkaplan/stage-clicked

Make "when clicked" blocks  target dependent.
This commit is contained in:
Paul Kaplan 2018-03-23 09:20:22 -04:00 committed by GitHub
commit a27ed888d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 82 additions and 0 deletions

View file

@ -32,6 +32,9 @@ class Scratch3EventBlocks {
event_whenthisspriteclicked: { event_whenthisspriteclicked: {
restartExistingThreads: true restartExistingThreads: true
}, },
event_whenstageclicked: {
restartExistingThreads: true
},
event_whenbackdropswitchesto: { event_whenbackdropswitchesto: {
restartExistingThreads: true restartExistingThreads: true
}, },

View file

@ -593,6 +593,21 @@ class Blocks {
} }
} }
/**
* Keep blocks up to date after they are shared between targets.
* @param {boolean} isStage If the new target is a stage.
*/
updateTargetSpecificBlocks (isStage) {
const blocks = this._blocks;
for (const blockId in blocks) {
if (isStage && blocks[blockId].opcode === 'event_whenthisspriteclicked') {
blocks[blockId].opcode = 'event_whenstageclicked';
} else if (!isStage && blocks[blockId].opcode === 'event_whenstageclicked') {
blocks[blockId].opcode = 'event_whenthisspriteclicked';
}
}
}
/** /**
* Update blocks after a sound, costume, or backdrop gets renamed. * Update blocks after a sound, costume, or backdrop gets renamed.
* Any block referring to the old name of the asset should get updated * Any block referring to the old name of the asset should get updated

View file

@ -31,8 +31,15 @@ class Mouse {
// only activate click hat if the mouse up event wasn't // only activate click hat if the mouse up event wasn't
// the result of a drag ending // the result of a drag ending
if (!wasDragged) { if (!wasDragged) {
// Activate both "this sprite clicked" and "stage clicked"
// They were separated into two opcodes for labeling,
// but should act the same way.
// Intentionally not checking isStage to make it work when sharing blocks.
// @todo the blocks should be converted from one to another when shared
this.runtime.startHats('event_whenthisspriteclicked', this.runtime.startHats('event_whenthisspriteclicked',
null, target); null, target);
this.runtime.startHats('event_whenstageclicked',
null, target);
} }
return; return;
} }

View file

@ -275,6 +275,9 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions); parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions);
} }
// Update stage specific blocks (e.g. sprite clicked <=> stage clicked)
blocks.updateTargetSpecificBlocks(topLevel); // topLevel = isStage
if (object.hasOwnProperty('lists')) { if (object.hasOwnProperty('lists')) {
for (let k = 0; k < object.lists.length; k++) { for (let k = 0; k < object.lists.length; k++) {
const list = object.lists[k]; const list = object.lists[k];

View file

@ -778,6 +778,7 @@ class VirtualMachine extends EventEmitter {
for (let i = 0; i < blocks.length; i++) { for (let i = 0; i < blocks.length; i++) {
target.blocks.createBlock(blocks[i]); target.blocks.createBlock(blocks[i]);
} }
target.blocks.updateTargetSpecificBlocks(target.isStage);
} }
/** /**

BIN
test/fixtures/when-clicked.sb2 vendored Normal file

Binary file not shown.

View file

@ -746,3 +746,33 @@ test('updateAssetName doesn\'t update name if name isn\'t being used', t => {
t.equals(b.getBlock('id1').fields.BACKDROP.value, 'foo'); t.equals(b.getBlock('id1').fields.BACKDROP.value, 'foo');
t.end(); t.end();
}); });
test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for stage', t => {
const b = new Blocks();
b.createBlock({
id: 'originallySpriteClicked',
opcode: 'event_whenthisspriteclicked'
});
b.createBlock({
id: 'originallyStageClicked',
opcode: 'event_whenstageclicked'
});
// originallySpriteClicked does not update when on a non-stage target
b.updateTargetSpecificBlocks(false /* isStage */);
t.equals(b.getBlock('originallySpriteClicked').opcode, 'event_whenthisspriteclicked');
// originallySpriteClicked does update when on a stage target
b.updateTargetSpecificBlocks(true /* isStage */);
t.equals(b.getBlock('originallySpriteClicked').opcode, 'event_whenstageclicked');
// originallyStageClicked does not update when on a stage target
b.updateTargetSpecificBlocks(true /* isStage */);
t.equals(b.getBlock('originallyStageClicked').opcode, 'event_whenstageclicked');
// originallyStageClicked does update when on a non-stage target
b.updateTargetSpecificBlocks(false/* isStage */);
t.equals(b.getBlock('originallyStageClicked').opcode, 'event_whenthisspriteclicked');
t.end();
});

View file

@ -68,3 +68,26 @@ test('data scoping', t => {
t.end(); t.end();
}); });
}); });
test('whenclicked blocks imported separately', t => {
// This sb2 fixture has a single "whenClicked" block on both sprite and stage
const uri = path.resolve(__dirname, '../fixtures/when-clicked.sb2');
const file = extract(uri);
const json = JSON.parse(file);
// Create runtime instance & load SB2 into it
const rt = new Runtime();
sb2.deserialize(json, rt).then(({targets}) => {
const stage = targets[0];
t.equal(stage.isStage, true); // Make sure we have the correct target
const stageOpcode = stage.blocks.getBlock(stage.blocks.getScripts()[0]).opcode;
t.equal(stageOpcode, 'event_whenstageclicked');
const sprite = targets[1];
t.equal(sprite.isStage, false); // Make sure we have the correct target
const spriteOpcode = sprite.blocks.getBlock(sprite.blocks.getScripts()[0]).opcode;
t.equal(spriteOpcode, 'event_whenthisspriteclicked');
t.end();
});
});