From 5193b48d1a7aa3ff1e41884aba9b0451a0c48bff Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Mon, 12 May 2025 23:25:04 +0000 Subject: [PATCH 1/8] fix: Ensure selection is retained when dragging. --- core/dragging/block_drag_strategy.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/dragging/block_drag_strategy.ts b/core/dragging/block_drag_strategy.ts index 76020f90b5b..b93a4d73110 100644 --- a/core/dragging/block_drag_strategy.ts +++ b/core/dragging/block_drag_strategy.ts @@ -14,6 +14,7 @@ import {ConnectionType} from '../connection_type.js'; import type {BlockMove} from '../events/events_block_move.js'; import {EventType} from '../events/type.js'; import * as eventUtils from '../events/utils.js'; +import {getFocusManager} from '../focus_manager.js'; import {IConnectionPreviewer} from '../interfaces/i_connection_previewer.js'; import {IDragStrategy} from '../interfaces/i_draggable.js'; import * as layers from '../layers.js'; @@ -120,6 +121,7 @@ export class BlockDragStrategy implements IDragStrategy { } this.block.setDragging(true); this.workspace.getLayerManager()?.moveToDragLayer(this.block); + getFocusManager().focusNode(this.block); } /** From 60c9510884cc2c06e8e1f072770c3a2e2c9fc71e Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Mon, 12 May 2025 23:30:43 +0000 Subject: [PATCH 2/8] chore: Empty commit to re-trigger CI. From f519607c3127d4745bc75d1e3b55a5a5ee88612f Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Mon, 12 May 2025 23:31:44 +0000 Subject: [PATCH 3/8] chore: Add comment explaining 'why' for focusNode. --- core/dragging/block_drag_strategy.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/dragging/block_drag_strategy.ts b/core/dragging/block_drag_strategy.ts index b93a4d73110..8d466694753 100644 --- a/core/dragging/block_drag_strategy.ts +++ b/core/dragging/block_drag_strategy.ts @@ -121,6 +121,9 @@ export class BlockDragStrategy implements IDragStrategy { } this.block.setDragging(true); this.workspace.getLayerManager()?.moveToDragLayer(this.block); + + // Since moving the block to the drag layer will cause it to lose focus, + // ensure it regains focus (to enable the block's selection highlight). getFocusManager().focusNode(this.block); } From 134199ac0c6f8a030f9ee504d3e697766697991d Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 13 May 2025 18:55:04 +0000 Subject: [PATCH 4/8] fix: Fix selection & restore in other places, too. --- core/dragging/block_drag_strategy.ts | 4 ++++ core/dragging/bubble_drag_strategy.ts | 9 +++++++++ core/dragging/comment_drag_strategy.ts | 9 +++++++++ 3 files changed, 22 insertions(+) diff --git a/core/dragging/block_drag_strategy.ts b/core/dragging/block_drag_strategy.ts index 8d466694753..71552b62cf8 100644 --- a/core/dragging/block_drag_strategy.ts +++ b/core/dragging/block_drag_strategy.ts @@ -399,6 +399,10 @@ export class BlockDragStrategy implements IDragStrategy { .getLayerManager() ?.moveOffDragLayer(this.block, layers.BLOCK); this.block.setDragging(false); + + // Since moving the block off the drag layer will cause it to lose focus, + // ensure it regains focus (to enable the block's selection highlight). + getFocusManager().focusNode(this.block); } if (this.connectionCandidate) { diff --git a/core/dragging/bubble_drag_strategy.ts b/core/dragging/bubble_drag_strategy.ts index 8a5a6783910..c4c5fb8e506 100644 --- a/core/dragging/bubble_drag_strategy.ts +++ b/core/dragging/bubble_drag_strategy.ts @@ -5,6 +5,7 @@ */ import {IBubble, WorkspaceSvg} from '../blockly.js'; +import {getFocusManager} from '../focus_manager.js'; import {IDragStrategy} from '../interfaces/i_draggable.js'; import * as layers from '../layers.js'; import {Coordinate} from '../utils.js'; @@ -28,6 +29,10 @@ export class BubbleDragStrategy implements IDragStrategy { if (this.bubble.setDragging) { this.bubble.setDragging(true); } + + // Since moving the bubble to the drag layer will cause it to lose focus, + // ensure it regains focus (to fire related bubble selection events). + getFocusManager().focusNode(this.bubble); } drag(newLoc: Coordinate): void { @@ -41,6 +46,10 @@ export class BubbleDragStrategy implements IDragStrategy { .getLayerManager() ?.moveOffDragLayer(this.bubble, layers.BUBBLE); this.bubble.setDragging(false); + + // Since moving the bubble off the drag layer will cause it to lose focus, + // ensure it regains focus (to fire related bubble selection events). + getFocusManager().focusNode(this.bubble); } revertDrag(): void { diff --git a/core/dragging/comment_drag_strategy.ts b/core/dragging/comment_drag_strategy.ts index b7974d8b4ca..ef075c63856 100644 --- a/core/dragging/comment_drag_strategy.ts +++ b/core/dragging/comment_drag_strategy.ts @@ -8,6 +8,7 @@ import {RenderedWorkspaceComment} from '../comments.js'; import {CommentMove} from '../events/events_comment_move.js'; import {EventType} from '../events/type.js'; import * as eventUtils from '../events/utils.js'; +import {getFocusManager} from '../focus_manager.js'; import {IDragStrategy} from '../interfaces/i_draggable.js'; import * as layers from '../layers.js'; import {Coordinate} from '../utils.js'; @@ -36,6 +37,10 @@ export class CommentDragStrategy implements IDragStrategy { this.workspace.setResizesEnabled(false); this.workspace.getLayerManager()?.moveToDragLayer(this.comment); this.comment.setDragging(true); + + // Since moving the comment to the drag layer will cause it to lose focus, + // ensure it regains focus (to enable the comments's selection highlight). + getFocusManager().focusNode(this.comment); } drag(newLoc: Coordinate): void { @@ -54,6 +59,10 @@ export class CommentDragStrategy implements IDragStrategy { this.comment.snapToGrid(); this.workspace.setResizesEnabled(true); + + // Since moving the comment off the drag layer will cause it to lose focus, + // ensure it regains focus (to enable the comments's selection highlight). + getFocusManager().focusNode(this.comment); } /** Fire a UI event at the start of a comment drag. */ From ca880881551f91f7da5501141a171b2f889294a9 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 13 May 2025 19:04:33 +0000 Subject: [PATCH 5/8] chore: Move fix to drag layer for robustness. May address a separate block case that was missed before. --- core/dragging/block_drag_strategy.ts | 9 --------- core/dragging/bubble_drag_strategy.ts | 9 --------- core/dragging/comment_drag_strategy.ts | 9 --------- core/layer_manager.ts | 14 ++++++++++++-- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/core/dragging/block_drag_strategy.ts b/core/dragging/block_drag_strategy.ts index 71552b62cf8..76020f90b5b 100644 --- a/core/dragging/block_drag_strategy.ts +++ b/core/dragging/block_drag_strategy.ts @@ -14,7 +14,6 @@ import {ConnectionType} from '../connection_type.js'; import type {BlockMove} from '../events/events_block_move.js'; import {EventType} from '../events/type.js'; import * as eventUtils from '../events/utils.js'; -import {getFocusManager} from '../focus_manager.js'; import {IConnectionPreviewer} from '../interfaces/i_connection_previewer.js'; import {IDragStrategy} from '../interfaces/i_draggable.js'; import * as layers from '../layers.js'; @@ -121,10 +120,6 @@ export class BlockDragStrategy implements IDragStrategy { } this.block.setDragging(true); this.workspace.getLayerManager()?.moveToDragLayer(this.block); - - // Since moving the block to the drag layer will cause it to lose focus, - // ensure it regains focus (to enable the block's selection highlight). - getFocusManager().focusNode(this.block); } /** @@ -399,10 +394,6 @@ export class BlockDragStrategy implements IDragStrategy { .getLayerManager() ?.moveOffDragLayer(this.block, layers.BLOCK); this.block.setDragging(false); - - // Since moving the block off the drag layer will cause it to lose focus, - // ensure it regains focus (to enable the block's selection highlight). - getFocusManager().focusNode(this.block); } if (this.connectionCandidate) { diff --git a/core/dragging/bubble_drag_strategy.ts b/core/dragging/bubble_drag_strategy.ts index c4c5fb8e506..8a5a6783910 100644 --- a/core/dragging/bubble_drag_strategy.ts +++ b/core/dragging/bubble_drag_strategy.ts @@ -5,7 +5,6 @@ */ import {IBubble, WorkspaceSvg} from '../blockly.js'; -import {getFocusManager} from '../focus_manager.js'; import {IDragStrategy} from '../interfaces/i_draggable.js'; import * as layers from '../layers.js'; import {Coordinate} from '../utils.js'; @@ -29,10 +28,6 @@ export class BubbleDragStrategy implements IDragStrategy { if (this.bubble.setDragging) { this.bubble.setDragging(true); } - - // Since moving the bubble to the drag layer will cause it to lose focus, - // ensure it regains focus (to fire related bubble selection events). - getFocusManager().focusNode(this.bubble); } drag(newLoc: Coordinate): void { @@ -46,10 +41,6 @@ export class BubbleDragStrategy implements IDragStrategy { .getLayerManager() ?.moveOffDragLayer(this.bubble, layers.BUBBLE); this.bubble.setDragging(false); - - // Since moving the bubble off the drag layer will cause it to lose focus, - // ensure it regains focus (to fire related bubble selection events). - getFocusManager().focusNode(this.bubble); } revertDrag(): void { diff --git a/core/dragging/comment_drag_strategy.ts b/core/dragging/comment_drag_strategy.ts index ef075c63856..b7974d8b4ca 100644 --- a/core/dragging/comment_drag_strategy.ts +++ b/core/dragging/comment_drag_strategy.ts @@ -8,7 +8,6 @@ import {RenderedWorkspaceComment} from '../comments.js'; import {CommentMove} from '../events/events_comment_move.js'; import {EventType} from '../events/type.js'; import * as eventUtils from '../events/utils.js'; -import {getFocusManager} from '../focus_manager.js'; import {IDragStrategy} from '../interfaces/i_draggable.js'; import * as layers from '../layers.js'; import {Coordinate} from '../utils.js'; @@ -37,10 +36,6 @@ export class CommentDragStrategy implements IDragStrategy { this.workspace.setResizesEnabled(false); this.workspace.getLayerManager()?.moveToDragLayer(this.comment); this.comment.setDragging(true); - - // Since moving the comment to the drag layer will cause it to lose focus, - // ensure it regains focus (to enable the comments's selection highlight). - getFocusManager().focusNode(this.comment); } drag(newLoc: Coordinate): void { @@ -59,10 +54,6 @@ export class CommentDragStrategy implements IDragStrategy { this.comment.snapToGrid(); this.workspace.setResizesEnabled(true); - - // Since moving the comment off the drag layer will cause it to lose focus, - // ensure it regains focus (to enable the comments's selection highlight). - getFocusManager().focusNode(this.comment); } /** Fire a UI event at the start of a comment drag. */ diff --git a/core/layer_manager.ts b/core/layer_manager.ts index a7cb579348f..1d5afdd74e9 100644 --- a/core/layer_manager.ts +++ b/core/layer_manager.ts @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import {getFocusManager} from './focus_manager.js'; +import type {IFocusableNode} from './interfaces/i_focusable_node.js'; import {IRenderedElement} from './interfaces/i_rendered_element.js'; import * as layerNums from './layers.js'; import {Coordinate} from './utils/coordinate.js'; @@ -99,8 +101,12 @@ export class LayerManager { * * @internal */ - moveToDragLayer(elem: IRenderedElement) { + moveToDragLayer(elem: IRenderedElement & IFocusableNode) { this.dragLayer?.appendChild(elem.getSvgRoot()); + + // Since moving the element to the drag layer will cause it to lose focus, + // ensure it regains focus (to ensure proper highlights & sent events). + getFocusManager().focusNode(elem); } /** @@ -108,8 +114,12 @@ export class LayerManager { * * @internal */ - moveOffDragLayer(elem: IRenderedElement, layerNum: number) { + moveOffDragLayer(elem: IRenderedElement & IFocusableNode, layerNum: number) { this.append(elem, layerNum); + + // Since moving the element off the drag layer will cause it to lose focus, + // ensure it regains focus (to ensure proper highlights & sent events). + getFocusManager().focusNode(elem); } /** From bc009862121548d6b2cb13315d6780ad8cada7a3 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 13 May 2025 21:11:34 +0000 Subject: [PATCH 6/8] fix: Remove redundancy in path object. --- core/renderers/zelos/path_object.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/renderers/zelos/path_object.ts b/core/renderers/zelos/path_object.ts index f40426483a7..3c304fd6bf8 100644 --- a/core/renderers/zelos/path_object.ts +++ b/core/renderers/zelos/path_object.ts @@ -8,6 +8,7 @@ import type {BlockSvg} from '../../block_svg.js'; import type {Connection} from '../../connection.js'; +import {FocusManager} from '../../focus_manager.js'; import type {BlockStyle} from '../../theme.js'; import * as dom from '../../utils/dom.js'; import {Svg} from '../../utils/svg.js'; @@ -91,6 +92,17 @@ export class PathObject extends BasePathObject { if (!this.svgPathSelected) { this.svgPathSelected = this.svgPath.cloneNode(true) as SVGElement; this.svgPathSelected.classList.add('blocklyPathSelected'); + // Ensure focus-specific properties don't overlap with the block's path. + dom.removeClass( + this.svgPathSelected, + FocusManager.ACTIVE_FOCUS_NODE_CSS_CLASS_NAME, + ); + dom.removeClass( + this.svgPathSelected, + FocusManager.PASSIVE_FOCUS_NODE_CSS_CLASS_NAME, + ); + this.svgPathSelected.removeAttribute('tabindex'); + this.svgPathSelected.removeAttribute('id'); this.svgRoot.appendChild(this.svgPathSelected); } } else { From 6c8ab8ff6d7d27de73b53cf54d69a7e4e12f5765 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 13 May 2025 21:29:09 +0000 Subject: [PATCH 7/8] fix: Fix broken test. --- tests/mocha/layering_test.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/mocha/layering_test.js b/tests/mocha/layering_test.js index efc3ef3d632..39b7df63560 100644 --- a/tests/mocha/layering_test.js +++ b/tests/mocha/layering_test.js @@ -24,6 +24,15 @@ suite('Layering', function () { const g = Blockly.utils.dom.createSvgElement('g', {}); return { getSvgRoot: () => g, + getFocusableElement: () => { + throw new Error('Unsupported.'); + }, + getFocusableTree: () => { + throw new Error('Unsupported.'); + }, + onNodeFocus: () => {}, + onNodeBlur: () => {}, + canBeFocused: () => false, }; } @@ -80,7 +89,7 @@ suite('Layering', function () { }); suite('dragging', function () { - test('moving an element to the drag layer adds it to the drag group', function () { + test.only('moving an element to the drag layer adds it to the drag group', function () { const elem = createRenderedElement(); this.layerManager.moveToDragLayer(elem); From a1c05ccaaa160acf211cf335362912eee69d5f95 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 13 May 2025 21:33:17 +0000 Subject: [PATCH 8/8] chore: Remove unintentional 'only' test filter. --- tests/mocha/layering_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mocha/layering_test.js b/tests/mocha/layering_test.js index 39b7df63560..1ef0ee6973d 100644 --- a/tests/mocha/layering_test.js +++ b/tests/mocha/layering_test.js @@ -89,7 +89,7 @@ suite('Layering', function () { }); suite('dragging', function () { - test.only('moving an element to the drag layer adds it to the drag group', function () { + test('moving an element to the drag layer adds it to the drag group', function () { const elem = createRenderedElement(); this.layerManager.moveToDragLayer(elem);