diff --git a/src/engine/adapter.js b/src/engine/adapter.js index 7226c9b25..5874afd55 100644 --- a/src/engine/adapter.js +++ b/src/engine/adapter.js @@ -152,7 +152,7 @@ const domToBlocks = function (blocksDOM) { /** * Adapter between block creation events and block representation which can be * used by the Scratch runtime. - * @param {object} e `Blockly.events.create` + * @param {object} e `Blockly.events.create` or `Blockly.events.endDrag` * @return {Array.} List of blocks from this CREATE event. */ const adapter = function (e) { diff --git a/src/virtual-machine.js b/src/virtual-machine.js index f33f6fb92..1e9de9ea1 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -10,6 +10,7 @@ const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); const formatMessage = require('format-message'); const Variable = require('./engine/variable'); +const adapter = require('./engine/adapter'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); @@ -40,6 +41,11 @@ class VirtualMachine extends EventEmitter { */ this.editingTarget = null; + /** + * ID of sprite that mouse is hovered over in sprite selector, if any + */ + this._hoveredSpriteId = null; + /** * The currently dragging target, for redirecting IO data. * @type {Target} @@ -553,6 +559,16 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Sets the hovered sprite + * @param {string} targetId ID of a target whose sprite to duplicate. + * @returns {Promise} Promise that resolves when duplicated target has + * been added to the runtime. + */ + setHoveredSprite (targetId) { + this._hoveredSpriteId = targetId; + } + /** * Set the audio engine for the VM/runtime * @param {!AudioEngine} audioEngine The audio engine to attach @@ -596,6 +612,26 @@ class VirtualMachine extends EventEmitter { blockListener (e) { if (this.editingTarget) { this.editingTarget.blocks.blocklyListen(e, this.runtime); + + // Validate event + if (typeof e !== 'object') return; + if (typeof e.blockId !== 'string' && typeof e.varId !== 'string') { + return; + } + + // Drag blocks onto another sprite + if (e.type === 'endDrag' && + e.isOutside && + this._hoveredSpriteId && + this._hoveredSpriteId !== this.editingTarget.id) { + const dragTarget = this.runtime.getTargetById(this._hoveredSpriteId); + if (dragTarget) { + const newBlocks = adapter(e); + for (let i = 0; i < newBlocks.length; i++) { + dragTarget.blocks.createBlock(newBlocks[i]); + } + } + } } }