mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-09 14:32:07 -05:00
Update sensing_of fields if variable gets renamed
Thanks @adroitwhiz and @fsih for some advice!
This commit is contained in:
parent
7af161f1a2
commit
03db30d400
3 changed files with 183 additions and 1 deletions
|
@ -946,6 +946,32 @@ class Blocks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update sensing_of blocks after a variable gets renamed.
|
||||||
|
* @param {string} oldName The old name of the variable that was renamed.
|
||||||
|
* @param {string} newName The new name of the variable that was renamed.
|
||||||
|
* @param {string} targetName The name of the target the variable belongs to.
|
||||||
|
* @return {boolean} Returns true if any of the blocks were updated.
|
||||||
|
*/
|
||||||
|
updateSensingOfReference (oldName, newName, targetName) {
|
||||||
|
const blocks = this._blocks;
|
||||||
|
let blockUpdated = false;
|
||||||
|
for (const blockId in blocks) {
|
||||||
|
const block = blocks[blockId];
|
||||||
|
if (block.opcode === 'sensing_of' &&
|
||||||
|
block.fields.PROPERTY.value === oldName &&
|
||||||
|
// If block and shadow are different, it means a block is inserted to OBJECT, and should be ignored.
|
||||||
|
block.inputs.OBJECT.block === block.inputs.OBJECT.shadow) {
|
||||||
|
const inputBlock = this.getBlock(block.inputs.OBJECT.block);
|
||||||
|
if (inputBlock.fields.OBJECT.value === targetName) {
|
||||||
|
block.fields.PROPERTY.value = newName;
|
||||||
|
blockUpdated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blockUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to retrieve a costume menu field from a block given its id.
|
* Helper function to retrieve a costume menu field from a block given its id.
|
||||||
* @param {string} blockId A unique identifier for a block
|
* @param {string} blockId A unique identifier for a block
|
||||||
|
|
|
@ -322,11 +322,26 @@ class Target extends EventEmitter {
|
||||||
this.runtime.ioDevices.cloud.requestRenameVariable(oldName, newName);
|
this.runtime.ioDevices.cloud.requestRenameVariable(oldName, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (variable.type === Variable.SCALAR_TYPE) {
|
||||||
|
// sensing__of may be referencing to this variable.
|
||||||
|
// Change the reference.
|
||||||
|
let blockUpdated = false;
|
||||||
|
this.runtime.targets.forEach(t => {
|
||||||
|
blockUpdated = t.blocks.updateSensingOfReference(
|
||||||
|
oldName,
|
||||||
|
newName,
|
||||||
|
this.isStage ? '__stage__' : this.getName()
|
||||||
|
) || blockUpdated;
|
||||||
|
});
|
||||||
|
// Request workspace change only if sensing_of blocks were actually updated.
|
||||||
|
if (blockUpdated) this.runtime.requestBlocksUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
const blocks = this.runtime.monitorBlocks;
|
const blocks = this.runtime.monitorBlocks;
|
||||||
blocks.changeBlock({
|
blocks.changeBlock({
|
||||||
id: id,
|
id: id,
|
||||||
element: 'field',
|
element: 'field',
|
||||||
name: variable.type === 'list' ? 'LIST' : 'VARIABLE',
|
name: variable.type === Variable.LIST_TYPE ? 'LIST' : 'VARIABLE',
|
||||||
value: id
|
value: id
|
||||||
}, this.runtime);
|
}, this.runtime);
|
||||||
const monitorBlock = blocks.getBlock(variable.id);
|
const monitorBlock = blocks.getBlock(variable.id);
|
||||||
|
|
|
@ -26,6 +26,7 @@ test('spec', t => {
|
||||||
t.type(b.getBranch, 'function');
|
t.type(b.getBranch, 'function');
|
||||||
t.type(b.getOpcode, 'function');
|
t.type(b.getOpcode, 'function');
|
||||||
t.type(b.mutationToXML, 'function');
|
t.type(b.mutationToXML, 'function');
|
||||||
|
t.type(b.updateSensingOfReference, 'function');
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
@ -807,6 +808,146 @@ test('updateAssetName doesn\'t update name if name isn\'t being used', t => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('updateSensingOfReference renames variables in sensing_of block', t => {
|
||||||
|
const b = new Blocks(new Runtime());
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id1',
|
||||||
|
opcode: 'sensing_of',
|
||||||
|
fields: {
|
||||||
|
PROPERTY: {
|
||||||
|
name: 'PROPERTY',
|
||||||
|
value: 'foo'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputs: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
block: 'id2',
|
||||||
|
shadow: 'id2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id2',
|
||||||
|
fields: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
value: '_stage_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
b.updateSensingOfReference('foo', 'bar', '_stage_');
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'bar');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateSensingOfReference doesn\'t rename if block is inserted', t => {
|
||||||
|
const b = new Blocks(new Runtime());
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id1',
|
||||||
|
opcode: 'sensing_of',
|
||||||
|
fields: {
|
||||||
|
PROPERTY: {
|
||||||
|
name: 'PROPERTY',
|
||||||
|
value: 'foo'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputs: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
block: 'id3',
|
||||||
|
shadow: 'id2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id2',
|
||||||
|
fields: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
value: '_stage_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id3',
|
||||||
|
opcode: 'answer'
|
||||||
|
});
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
b.updateSensingOfReference('foo', 'bar', '_stage_');
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateSensingOfReference doesn\'t rename if name is not being used', t => {
|
||||||
|
const b = new Blocks(new Runtime());
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id1',
|
||||||
|
opcode: 'sensing_of',
|
||||||
|
fields: {
|
||||||
|
PROPERTY: {
|
||||||
|
name: 'PROPERTY',
|
||||||
|
value: 'foo'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputs: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
block: 'id2',
|
||||||
|
shadow: 'id2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id2',
|
||||||
|
fields: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
value: '_stage_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
b.updateSensingOfReference('meow', 'meow2', '_stage_');
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateSensingOfReference doesn\'t rename other targets\' variables', t => {
|
||||||
|
const b = new Blocks(new Runtime());
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id1',
|
||||||
|
opcode: 'sensing_of',
|
||||||
|
fields: {
|
||||||
|
PROPERTY: {
|
||||||
|
name: 'PROPERTY',
|
||||||
|
value: 'foo'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputs: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
block: 'id2',
|
||||||
|
shadow: 'id2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
b.createBlock({
|
||||||
|
id: 'id2',
|
||||||
|
fields: {
|
||||||
|
OBJECT: {
|
||||||
|
name: 'OBJECT',
|
||||||
|
value: '_stage_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
b.updateSensingOfReference('foo', 'bar', 'Cat');
|
||||||
|
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for stage', t => {
|
test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for stage', t => {
|
||||||
const b = new Blocks(new Runtime());
|
const b = new Blocks(new Runtime());
|
||||||
b.createBlock({
|
b.createBlock({
|
||||||
|
|
Loading…
Reference in a new issue