mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-06-28 15:20:28 -04:00
Merge 0f211b76c2
into bb9ac40252
This commit is contained in:
commit
872fde9316
7 changed files with 97 additions and 4 deletions
src
test/unit
|
@ -964,6 +964,14 @@ class Blocks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete comments attached to the block.
|
||||||
|
if (block.comment) {
|
||||||
|
const editingTarget = this.runtime.getEditingTarget();
|
||||||
|
if (editingTarget && editingTarget.comments.hasOwnProperty(block.comment)) {
|
||||||
|
delete editingTarget.comments[block.comment];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete any script starting with this block.
|
// Delete any script starting with this block.
|
||||||
this._deleteScript(blockId);
|
this._deleteScript(blockId);
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,10 @@ class Target extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {Runtime} runtime Reference to the runtime.
|
* @param {Runtime} runtime Reference to the runtime.
|
||||||
* @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
|
* @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
|
||||||
|
* @param {?Object.<string, Comment>} comments Array of comments owned by this target.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor (runtime, blocks) {
|
constructor (runtime, blocks, comments) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (!blocks) {
|
if (!blocks) {
|
||||||
|
@ -55,7 +56,7 @@ class Target extends EventEmitter {
|
||||||
* Key is the comment id.
|
* Key is the comment id.
|
||||||
* @type {Object.<string,*>}
|
* @type {Object.<string,*>}
|
||||||
*/
|
*/
|
||||||
this.comments = {};
|
this.comments = comments || {};
|
||||||
/**
|
/**
|
||||||
* Dictionary of custom state for this target.
|
* Dictionary of custom state for this target.
|
||||||
* This can be used to store target-specific custom state for blocks which need it.
|
* This can be used to store target-specific custom state for blocks which need it.
|
||||||
|
|
|
@ -15,7 +15,7 @@ class RenderedTarget extends Target {
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor (sprite, runtime) {
|
constructor (sprite, runtime) {
|
||||||
super(runtime, sprite.blocks);
|
super(runtime, sprite.blocks, sprite.comments);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to the sprite that this is a render of.
|
* Reference to the sprite that this is a render of.
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
const RenderedTarget = require('./rendered-target');
|
const RenderedTarget = require('./rendered-target');
|
||||||
const Blocks = require('../engine/blocks');
|
const Blocks = require('../engine/blocks');
|
||||||
|
const Comment = require('../engine/comment');
|
||||||
const {loadSoundFromAsset} = require('../import/load-sound');
|
const {loadSoundFromAsset} = require('../import/load-sound');
|
||||||
const {loadCostumeFromAsset} = require('../import/load-costume');
|
const {loadCostumeFromAsset} = require('../import/load-costume');
|
||||||
const newBlockIds = require('../util/new-block-ids');
|
const newBlockIds = require('../util/new-block-ids');
|
||||||
const StringUtil = require('../util/string-util');
|
const StringUtil = require('../util/string-util');
|
||||||
const StageLayering = require('../engine/stage-layering');
|
const StageLayering = require('../engine/stage-layering');
|
||||||
|
const log = require('../util/log');
|
||||||
|
|
||||||
class Sprite {
|
class Sprite {
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +56,13 @@ class Sprite {
|
||||||
if (this.runtime && this.runtime.audioEngine) {
|
if (this.runtime && this.runtime.audioEngine) {
|
||||||
this.soundBank = this.runtime.audioEngine.createBank();
|
this.soundBank = this.runtime.audioEngine.createBank();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dictionary of comments for this target.
|
||||||
|
* Key is the comment id.
|
||||||
|
* @type {Object.<string, Comment>}
|
||||||
|
*/
|
||||||
|
this.comments = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,11 +149,48 @@ class Sprite {
|
||||||
const blocksContainer = this.blocks._blocks;
|
const blocksContainer = this.blocks._blocks;
|
||||||
const originalBlocks = Object.keys(blocksContainer).map(key => blocksContainer[key]);
|
const originalBlocks = Object.keys(blocksContainer).map(key => blocksContainer[key]);
|
||||||
const copiedBlocks = JSON.parse(JSON.stringify(originalBlocks));
|
const copiedBlocks = JSON.parse(JSON.stringify(originalBlocks));
|
||||||
newBlockIds(copiedBlocks);
|
const oldToNew = newBlockIds(copiedBlocks);
|
||||||
copiedBlocks.forEach(block => {
|
copiedBlocks.forEach(block => {
|
||||||
newSprite.blocks.createBlock(block);
|
newSprite.blocks.createBlock(block);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.keys(this.comments).forEach(commentId => {
|
||||||
|
const comment = this.comments[commentId];
|
||||||
|
const newComment = new Comment(
|
||||||
|
null, // generate new comment id
|
||||||
|
comment.text,
|
||||||
|
comment.x,
|
||||||
|
comment.y,
|
||||||
|
comment.width,
|
||||||
|
comment.height,
|
||||||
|
comment.minimized
|
||||||
|
);
|
||||||
|
// If the comment is attached to a block, it has a blockId property for the block it's attached to.
|
||||||
|
// Because we generate new block IDs for all the duplicated blocks, change all the comments' attached-block
|
||||||
|
// IDs from the old block IDs to the new ones.
|
||||||
|
if (comment.blockId) {
|
||||||
|
const newBlockId = oldToNew[comment.blockId];
|
||||||
|
if (newBlockId) {
|
||||||
|
newComment.blockId = newBlockId;
|
||||||
|
const newBlock = newSprite.blocks.getBlock(newBlockId);
|
||||||
|
if (newBlock) {
|
||||||
|
newBlock.comment = newComment.id;
|
||||||
|
} else {
|
||||||
|
log.warn(`Could not find block with id ${newBlockId} associated with comment ${newComment.id}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Comments did not get deleted when the block got deleted
|
||||||
|
// Such comments have blockId, but because the block is deleted
|
||||||
|
// oldToNew mapping does not include that blockId
|
||||||
|
// Handle it as workspace comment
|
||||||
|
// TODO do not load such comments when deserializing
|
||||||
|
log.warn(`Could not find block with id ${comment.blockId
|
||||||
|
} associated with comment ${comment.id} in the block ID mapping`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newSprite.comments[newComment.id] = newComment;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const allNames = this.runtime.targets.map(t => t.sprite.name);
|
const allNames = this.runtime.targets.map(t => t.sprite.name);
|
||||||
newSprite.name = StringUtil.unusedName(this.name, allNames);
|
newSprite.name = StringUtil.unusedName(this.name, allNames);
|
||||||
|
|
|
@ -4,6 +4,7 @@ const uid = require('./uid');
|
||||||
* Mutate the given blocks to have new IDs and update all internal ID references.
|
* Mutate the given blocks to have new IDs and update all internal ID references.
|
||||||
* Does not return anything to make it clear that the blocks are updated in-place.
|
* Does not return anything to make it clear that the blocks are updated in-place.
|
||||||
* @param {array} blocks - blocks to be mutated.
|
* @param {array} blocks - blocks to be mutated.
|
||||||
|
* @returns {object.<string, string>} - mapping of old block ID to new block ID
|
||||||
*/
|
*/
|
||||||
module.exports = blocks => {
|
module.exports = blocks => {
|
||||||
const oldToNew = {};
|
const oldToNew = {};
|
||||||
|
@ -30,4 +31,7 @@ module.exports = blocks => {
|
||||||
blocks[i].next = oldToNew[blocks[i].next];
|
blocks[i].next = oldToNew[blocks[i].next];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There are other things that need this mapping e.g. comments
|
||||||
|
return oldToNew;
|
||||||
};
|
};
|
||||||
|
|
|
@ -600,6 +600,25 @@ test('delete inputs', t => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('delete block with comment', t => {
|
||||||
|
const b = new Blocks(new Runtime());
|
||||||
|
const fakeTarget = {
|
||||||
|
comments: {
|
||||||
|
bar: {
|
||||||
|
blockId: 'foo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
b.runtime.getEditingTarget = () => fakeTarget;
|
||||||
|
b.createBlock({
|
||||||
|
id: 'foo',
|
||||||
|
comment: 'bar'
|
||||||
|
});
|
||||||
|
b.deleteBlock('foo');
|
||||||
|
t.notOk(fakeTarget.comments.hasOwnProperty('bar'));
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('updateAssetName function updates name in sound field', t => {
|
test('updateAssetName function updates name in sound field', t => {
|
||||||
const b = new Blocks(new Runtime());
|
const b = new Blocks(new Runtime());
|
||||||
b.createBlock({
|
b.createBlock({
|
||||||
|
|
|
@ -45,6 +45,21 @@ test('blocks get new id on duplicate', t => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('comments are duplicated when duplicating target', t => {
|
||||||
|
const r = new Runtime();
|
||||||
|
const s = new Sprite(null, r);
|
||||||
|
const rt = new RenderedTarget(s, r);
|
||||||
|
rt.createComment('commentid', null, 'testcomment', 0, 0, 100, 100, false);
|
||||||
|
t.ok(s.comments.hasOwnProperty('commentid'));
|
||||||
|
return rt.duplicate().then(duplicate => {
|
||||||
|
// Not ok because comment id should be re-generated
|
||||||
|
t.notOk(duplicate.comments.hasOwnProperty('commentid'));
|
||||||
|
t.ok(Object.keys(duplicate.comments).length === 1);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('direction', t => {
|
test('direction', t => {
|
||||||
const r = new Runtime();
|
const r = new Runtime();
|
||||||
const s = new Sprite(null, r);
|
const s = new Sprite(null, r);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue