Skip to content

Commit 6711da3

Browse files
committed
feat: Support allowlisting keyboard shortcuts for mid-move use
1 parent ecb6069 commit 6711da3

2 files changed

Lines changed: 70 additions & 12 deletions

File tree

packages/blockly/core/keyboard_nav/keyboard_mover.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,35 @@ export class KeyboardMover {
3737
* Object responsible for dragging workspace elements in response to move
3838
* commands.
3939
*/
40-
protected dragger?: IDragger;
40+
private dragger?: IDragger;
4141

4242
/**
4343
* The object that is currently being moved.
4444
*/
45-
protected draggable?: IDraggable;
45+
private draggable?: IDraggable;
4646

4747
/**
4848
* Workspace coordinate that the current move started from.
4949
*/
50-
protected startLocation?: Coordinate;
50+
private startLocation?: Coordinate;
5151

5252
/**
5353
* The total distance, in workspace coordinates, that the element being moved
5454
* has been moved since the movement process started.
5555
*/
56-
protected totalDelta = new Coordinate(0, 0);
56+
private totalDelta = new Coordinate(0, 0);
5757

5858
/**
5959
* The distance to move an item in workspace coordinates.
6060
*/
61-
protected stepDistance = 20;
61+
private stepDistance = 20;
6262

6363
/**
6464
* Symbol attached to the item being moved to indicate it is in move mode.
6565
*/
66-
protected moveIndicator?: MoveIndicator;
66+
private moveIndicator?: MoveIndicator;
67+
68+
private allowedShortcuts: string[] = [];
6769

6870
// Set up a blur listener to end the move if the user clicks away
6971
private readonly blurListener = () => {
@@ -242,10 +244,26 @@ export class KeyboardMover {
242244
this.stepDistance = stepDistance;
243245
}
244246

247+
/**
248+
* Returns a list of the names of shortcuts that are allowed to be run while
249+
* a keyboard-driven move is in progress.
250+
*/
251+
getAllowedShortcuts() {
252+
return this.allowedShortcuts;
253+
}
254+
255+
/**
256+
* Adds shortcuts with the given names to the list of shortcuts that are
257+
* allowed to be run while a keyboard-driven move is in progress.
258+
*/
259+
setAllowedShortcuts(shortcutNames: string[]) {
260+
this.allowedShortcuts = shortcutNames;
261+
}
262+
245263
/**
246264
* Repositions the move indicator to the corner of the item being moved.
247265
*/
248-
protected repositionMoveIndicator() {
266+
private repositionMoveIndicator() {
249267
const bounds = this.draggable?.getBoundingRectangle();
250268
if (!bounds) return;
251269

@@ -258,7 +276,7 @@ export class KeyboardMover {
258276
/**
259277
* Common clean-up for finish/abort run before terminating the move.
260278
*/
261-
protected preDragEndCleanup() {
279+
private preDragEndCleanup() {
262280
ShortcutRegistry.registry.unregister(COMMIT_MOVE_SHORTCUT);
263281

264282
// Remove the blur listener before ending the drag
@@ -270,7 +288,7 @@ export class KeyboardMover {
270288
/**
271289
* Common clean-up for finish/abort run after terminating the move.
272290
*/
273-
protected postDragEndCleanup() {
291+
private postDragEndCleanup() {
274292
this.moveIndicator?.dispose();
275293
this.moveIndicator = undefined;
276294
this.draggable = undefined;
@@ -282,15 +300,15 @@ export class KeyboardMover {
282300
/**
283301
* Returns the total distance current element has moved in pixels.
284302
*/
285-
protected totalPixelDelta() {
303+
private totalPixelDelta() {
286304
const scale = this.draggable?.workspace.scale ?? 1;
287305
return new Coordinate(this.totalDelta.x * scale, this.totalDelta.y * scale);
288306
}
289307

290308
/**
291309
* Scrolls the current element into view.
292310
*/
293-
protected scrollCurrentElementIntoView() {
311+
private scrollCurrentElementIntoView() {
294312
if (!this.draggable) return;
295313
const bounds = this.draggable.getBoundingRectangle();
296314
this.draggable.workspace.scrollBoundsIntoView(bounds);
@@ -300,7 +318,7 @@ export class KeyboardMover {
300318
* Recalculates the total movement delta from the starting location and the
301319
* current position of the item being moved.
302320
*/
303-
protected updateTotalDelta() {
321+
private updateTotalDelta() {
304322
if (!this.draggable || !this.startLocation) return;
305323

306324
this.totalDelta = new Coordinate(

packages/blockly/tests/mocha/keyboard_movement_test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,43 @@ suite('Keyboard-driven movement', function () {
352352
});
353353
}
354354

355+
function testExemptedShortcutsAllowed() {
356+
test('is not committed when allowlisted shortcuts are performed', function () {
357+
const hotkey = Blockly.ShortcutRegistry.registry.createSerializedKey(
358+
Blockly.utils.KeyCodes.M,
359+
[Blockly.utils.KeyCodes.CTRL_CMD],
360+
);
361+
362+
let shortcutRun = false;
363+
const testShortcut = {
364+
name: 'test_shortcut',
365+
preconditionFn: () => true,
366+
callback: () => {
367+
shortcutRun = true;
368+
return true;
369+
},
370+
keyCodes: [hotkey],
371+
};
372+
Blockly.ShortcutRegistry.registry.register(testShortcut);
373+
374+
Blockly.getFocusManager().focusNode(this.element);
375+
startMove(this.workspace);
376+
assert.isTrue(Blockly.KeyboardMover.mover.isMoving());
377+
moveRight(this.workspace, this.modifiers);
378+
379+
const event = createKeyDownEvent(Blockly.utils.KeyCodes.M, [
380+
Blockly.utils.KeyCodes.CTRL_CMD,
381+
]);
382+
this.workspace.getInjectionDiv().dispatchEvent(event);
383+
// Move mode should still be active and the shortcut should have toggled
384+
// the `shortcutRun` variable.
385+
assert.isTrue(Blockly.KeyboardMover.mover.isMoving());
386+
assert.isTrue(shortcutRun);
387+
cancelMove(this.workspace);
388+
Blockly.ShortcutRegistry.registry.unregister('test_shortcut');
389+
});
390+
}
391+
355392
suite('of workspace comments', function () {
356393
setup(function () {
357394
this.element = new Blockly.comments.RenderedWorkspaceComment(
@@ -367,6 +404,7 @@ suite('Keyboard-driven movement', function () {
367404
testMoveIndicatorIsDisplayed();
368405
testAdjustingMoveStepSize();
369406
testUnrelatedShortcutCommits();
407+
testExemptedShortcutsAllowed();
370408
});
371409

372410
suite('of blocks', function () {
@@ -386,6 +424,7 @@ suite('Keyboard-driven movement', function () {
386424
testMoveIndicatorIsDisplayed();
387425
testAdjustingMoveStepSize();
388426
testUnrelatedShortcutCommits();
427+
testExemptedShortcutsAllowed();
389428
});
390429

391430
suite('in constrained mode', function () {
@@ -817,5 +856,6 @@ suite('Keyboard-driven movement', function () {
817856
testMoveIndicatorIsDisplayed();
818857
testAdjustingMoveStepSize();
819858
testUnrelatedShortcutCommits();
859+
testExemptedShortcutsAllowed();
820860
});
821861
});

0 commit comments

Comments
 (0)