diff --git a/packages/blockly/core/dragging/block_drag_strategy.ts b/packages/blockly/core/dragging/block_drag_strategy.ts index 193e078b9..df309d5c8 100644 --- a/packages/blockly/core/dragging/block_drag_strategy.ts +++ b/packages/blockly/core/dragging/block_drag_strategy.ts @@ -92,6 +92,12 @@ export class BlockDragStrategy implements IDragStrategy { /** Used to persist an event group when snapping is done async. */ private originalEventGroup = ''; + /** + * Map from block IDs to reason(s) why it was disabled, used to restore + * disabled state post-drag. + */ + private lastBlockDisabledReasons: Map> = new Map(); + protected readonly BLOCK_CONNECTION_OFFSET = 10; /** @@ -338,6 +344,9 @@ export class BlockDragStrategy implements IDragStrategy { this.block.setDragging(true); + // Enable all blocks including children. + this.enableAllDraggedBlocks(this.block); + // For keyboard-driven moves, cache a list of valid connection points for // use in constrained moved mode. if (e instanceof KeyboardEvent) { @@ -885,6 +894,9 @@ export class BlockDragStrategy implements IDragStrategy { ); this.block.setDragging(false); + + // Re-disable the block for its original reasons. + this.redisableAllDraggedBlocks(this.block); } if (this.connectionCandidate) { @@ -979,6 +991,8 @@ export class BlockDragStrategy implements IDragStrategy { this.startParentConn = null; this.block.setDragging(false); + // Re-disable the block for its original reasons. + this.redisableAllDraggedBlocks(this.block); this.dragging = false; aria.announceDynamicAriaState(Msg['ANNOUNCE_MOVE_CANCELED']); } @@ -1236,4 +1250,41 @@ export class BlockDragStrategy implements IDragStrategy { ? MoveMode.CONSTRAINED : MoveMode.UNCONSTRAINED; } + + /** + * Enables the given block and its children. + * Stores the reasons each block was disabled so they can be restored. + * + * @param block The block to enable. + */ + private enableAllDraggedBlocks(block: BlockSvg) { + const oldUndo = eventUtils.getRecordUndo(); + eventUtils.setRecordUndo(false); + this.lastBlockDisabledReasons.clear(); + // getDescendants includes the block itself. + block.getDescendants(false).forEach((descendant) => { + const reasons = new Set(descendant.getDisabledReasons()); + this.lastBlockDisabledReasons.set(descendant.id, reasons); + reasons.forEach((reason) => descendant.setDisabledReason(false, reason)); + }); + eventUtils.setRecordUndo(oldUndo); + } + + /** + * Re-disables the given block and its children using their original + * disabled reasons. + * + * @param block The block to re-disable, if applicable. + */ + private redisableAllDraggedBlocks(block: BlockSvg) { + console.log('redisable'); + const oldUndo = eventUtils.getRecordUndo(); + eventUtils.setRecordUndo(false); + block.getDescendants(false).forEach((descendant) => { + this.lastBlockDisabledReasons.get(descendant.id)?.forEach((reason) => { + descendant.setDisabledReason(true, reason); + }); + }); + eventUtils.setRecordUndo(oldUndo); + } } diff --git a/packages/blockly/tests/mocha/keyboard_movement_test.js b/packages/blockly/tests/mocha/keyboard_movement_test.js index afdbc9342..eb47f64b7 100644 --- a/packages/blockly/tests/mocha/keyboard_movement_test.js +++ b/packages/blockly/tests/mocha/keyboard_movement_test.js @@ -733,6 +733,52 @@ suite('Keyboard-driven movement', function () { cancelMove(this.workspace); }); + test('Enables disabled blocks during drag and reenables when committed', function () { + const ifBlock = this.workspace.newBlock('controls_if'); + const repeatBlock = this.workspace.newBlock('controls_repeat'); + ifBlock.initSvg(); + ifBlock.render(); + repeatBlock.initSvg(); + repeatBlock.render(); + repeatBlock.previousConnection.connect( + ifBlock.getInput('DO0').connection, + ); + + ifBlock.setDisabledReason(true, 'test'); + assert.isFalse(ifBlock.isEnabled()); + + Blockly.getFocusManager().focusNode(ifBlock); + startMove(this.workspace); + + assert.isTrue(ifBlock.isEnabled()); + + endMove(this.workspace); + assert.isFalse(ifBlock.isEnabled()); + }); + + test('Enables disabled blocks during drag and reenables when cancelled', function () { + const ifBlock = this.workspace.newBlock('controls_if'); + const repeatBlock = this.workspace.newBlock('controls_repeat'); + ifBlock.initSvg(); + ifBlock.render(); + repeatBlock.initSvg(); + repeatBlock.render(); + repeatBlock.previousConnection.connect( + ifBlock.getInput('DO0').connection, + ); + + ifBlock.setDisabledReason(true, 'test'); + assert.isFalse(ifBlock.isEnabled()); + + Blockly.getFocusManager().focusNode(ifBlock); + startMove(this.workspace); + + assert.isTrue(ifBlock.isEnabled()); + + cancelMove(this.workspace); + assert.isFalse(ifBlock.isEnabled()); + }); + suite('Statement move tests', function () { // Clear the workspace and load start blocks. setup(function () {