diff --git a/src/engine/adapter.js b/src/engine/adapter.js index 7226c9b25..9d17f92b8 100644 --- a/src/engine/adapter.js +++ b/src/engine/adapter.js @@ -1,5 +1,6 @@ const mutationAdapter = require('./mutation-adapter'); const html = require('htmlparser2'); +const uid = require('../util/uid'); /** * Convert and an individual block DOM to the representation tree. @@ -11,6 +12,10 @@ const html = require('htmlparser2'); * @return {undefined} */ const domToBlock = function (blockDOM, blocks, isTopBlock, parent) { + if (!blockDOM.attribs.id) { + blockDOM.attribs.id = uid(); + } + // Block skeleton. const block = { id: blockDOM.attribs.id, // Block ID @@ -152,7 +157,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/engine/blocks.js b/src/engine/blocks.js index 1b0070bf5..1e22bf73e 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -288,6 +288,22 @@ class Blocks { newCoordinate: e.newCoordinate }); break; + case 'dragOutside': + if (optRuntime) { + optRuntime.emitBlockDragUpdate(e.isOutside); + } + break; + case 'endDrag': + if (optRuntime) { + optRuntime.emitBlockDragUpdate(false /* areBlocksOverGui */); + + // Drag blocks onto another sprite + if (e.isOutside) { + const newBlocks = adapter(e); + optRuntime.emitBlockEndDrag(newBlocks); + } + } + break; case 'delete': // Don't accept delete events for missing blocks, // or shadow blocks being obscured. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index aded2ccfb..4da25f7bd 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -379,6 +379,22 @@ class Runtime extends EventEmitter { return 'MONITORS_UPDATE'; } + /** + * Event name for block drag update. + * @const {string} + */ + static get BLOCK_DRAG_UPDATE () { + return 'BLOCK_DRAG_UPDATE'; + } + + /** + * Event name for block drag end. + * @const {string} + */ + static get BLOCK_DRAG_END () { + return 'BLOCK_DRAG_END'; + } + /** * Event name for reporting that an extension was added. * @const {string} @@ -1387,6 +1403,22 @@ class Runtime extends EventEmitter { } } + /** + * Emit whether blocks are being dragged over gui + * @param {boolean} areBlocksOverGui True if blocks are dragged out of blocks workspace, false otherwise + */ + emitBlockDragUpdate (areBlocksOverGui) { + this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); + } + + /** + * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace + * @param {Array.} blocks The set of blocks dragged to the GUI + */ + emitBlockEndDrag (blocks) { + this.emit(Runtime.BLOCK_DRAG_END, blocks); + } + /** * Emit value for reporter to show in the blocks. * @param {string} blockId ID for the block. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index f9fa45be3..3d3c16791 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -74,6 +74,12 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { this.emit(Runtime.MONITORS_UPDATE, monitorList); }); + this.runtime.on(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui => { + this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui); + }); + this.runtime.on(Runtime.BLOCK_DRAG_END, blocks => { + this.emit(Runtime.BLOCK_DRAG_END, blocks); + }); this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); }); @@ -704,6 +710,19 @@ class VirtualMachine extends EventEmitter { } } + /** + * Called when blocks are dragged from one sprite to another. Adds the blocks to the + * workspace of the given target. + * @param {!Array} blocks Blocks to add. + * @param {!string} targetId Id of target to add blocks to. + */ + shareBlocksToTarget (blocks, targetId) { + const target = this.runtime.getTargetById(targetId); + for (let i = 0; i < blocks.length; i++) { + target.blocks.createBlock(blocks[i]); + } + } + /** * Repopulate the workspace with the blocks of the current editingTarget. This * allows us to get around bugs like gui#413.