mirror of
https://github.com/google/blockly.git
synced 2026-04-27 23:50:21 +02:00
feat: Add keyboard shortcut to focus the workspace (#9615)
* feat: Add keyboard shortcut to focus the workspace * test: Added tests for keyboard shortcut to focus workspace * fix: Disable the focus workspace shortcut while dragging
This commit is contained in:
@@ -38,6 +38,14 @@ export enum names {
|
||||
UNDO = 'undo',
|
||||
REDO = 'redo',
|
||||
MENU = 'menu',
|
||||
FOCUS_WORKSPACE = 'focus_workspace',
|
||||
START_MOVE = 'start_move',
|
||||
FINISH_MOVE = 'finish_move',
|
||||
ABORT_MOVE = 'abort_move',
|
||||
MOVE_UP = 'move_up',
|
||||
MOVE_DOWN = 'move_down',
|
||||
MOVE_LEFT = 'move_left',
|
||||
MOVE_RIGHT = 'move_right',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,7 +399,7 @@ export function registerMovementShortcuts() {
|
||||
|
||||
const shortcuts: ShortcutRegistry.KeyboardShortcut[] = [
|
||||
{
|
||||
name: 'start_move',
|
||||
name: names.START_MOVE,
|
||||
preconditionFn: (workspace) => {
|
||||
const startDraggable = getCurrentDraggable(workspace);
|
||||
return !!startDraggable && KeyboardMover.mover.canMove(startDraggable);
|
||||
@@ -412,7 +420,7 @@ export function registerMovementShortcuts() {
|
||||
keyCodes: [KeyCodes.M],
|
||||
},
|
||||
{
|
||||
name: 'finish_move',
|
||||
name: names.FINISH_MOVE,
|
||||
preconditionFn: () => KeyboardMover.mover.isMoving(),
|
||||
callback: (_workspace, e) =>
|
||||
KeyboardMover.mover.finishMove(e as KeyboardEvent),
|
||||
@@ -420,7 +428,7 @@ export function registerMovementShortcuts() {
|
||||
allowCollision: true,
|
||||
},
|
||||
{
|
||||
name: 'abort_move',
|
||||
name: names.ABORT_MOVE,
|
||||
preconditionFn: () => KeyboardMover.mover.isMoving(),
|
||||
callback: (_workspace, e) =>
|
||||
KeyboardMover.mover.abortMove(e as KeyboardEvent),
|
||||
@@ -428,7 +436,7 @@ export function registerMovementShortcuts() {
|
||||
allowCollision: true,
|
||||
},
|
||||
{
|
||||
name: 'move_left',
|
||||
name: names.MOVE_LEFT,
|
||||
preconditionFn: () => KeyboardMover.mover.isMoving(),
|
||||
callback: (_workspace, e) => {
|
||||
e.preventDefault();
|
||||
@@ -443,7 +451,7 @@ export function registerMovementShortcuts() {
|
||||
allowCollision: true,
|
||||
},
|
||||
{
|
||||
name: 'move_right',
|
||||
name: names.MOVE_RIGHT,
|
||||
preconditionFn: () => KeyboardMover.mover.isMoving(),
|
||||
callback: (_workspace, e) => {
|
||||
e.preventDefault();
|
||||
@@ -458,7 +466,7 @@ export function registerMovementShortcuts() {
|
||||
allowCollision: true,
|
||||
},
|
||||
{
|
||||
name: 'move_up',
|
||||
name: names.MOVE_UP,
|
||||
preconditionFn: () => KeyboardMover.mover.isMoving(),
|
||||
callback: (_workspace, e) => {
|
||||
e.preventDefault();
|
||||
@@ -473,7 +481,7 @@ export function registerMovementShortcuts() {
|
||||
allowCollision: true,
|
||||
},
|
||||
{
|
||||
name: 'move_down',
|
||||
name: names.MOVE_DOWN,
|
||||
preconditionFn: () => KeyboardMover.mover.isMoving(),
|
||||
callback: (_workspace, e) => {
|
||||
e.preventDefault();
|
||||
@@ -508,7 +516,7 @@ export function registerShowContextMenu() {
|
||||
preconditionFn: (workspace) => {
|
||||
return !workspace.isDragging();
|
||||
},
|
||||
callback: (workspace, e) => {
|
||||
callback: (_workspace, e) => {
|
||||
const target = getFocusManager().getFocusedNode();
|
||||
if (hasContextMenu(target)) {
|
||||
target.showContextMenu(e);
|
||||
@@ -523,6 +531,33 @@ export function registerShowContextMenu() {
|
||||
ShortcutRegistry.registry.register(contextMenuShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers keyboard shortcut to focus the workspace.
|
||||
*/
|
||||
export function registerFocusWorkspace() {
|
||||
const resolveWorkspace = (workspace: WorkspaceSvg) => {
|
||||
if (workspace.isFlyout) {
|
||||
const target = workspace.targetWorkspace;
|
||||
if (target) {
|
||||
return resolveWorkspace(target);
|
||||
}
|
||||
}
|
||||
return workspace.getRootWorkspace() ?? workspace;
|
||||
};
|
||||
|
||||
const contextMenuShortcut: KeyboardShortcut = {
|
||||
name: names.FOCUS_WORKSPACE,
|
||||
preconditionFn: (workspace) => !workspace.isDragging(),
|
||||
callback: (workspace) => {
|
||||
keyboardNavigationController.setIsActive(true);
|
||||
getFocusManager().focusNode(resolveWorkspace(workspace));
|
||||
return true;
|
||||
},
|
||||
keyCodes: [KeyCodes.W],
|
||||
};
|
||||
ShortcutRegistry.registry.register(contextMenuShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all default keyboard shortcut item. This should be called once per
|
||||
* instance of KeyboardShortcutRegistry.
|
||||
@@ -537,8 +572,17 @@ export function registerDefaultShortcuts() {
|
||||
registerPaste();
|
||||
registerUndo();
|
||||
registerRedo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an extended set of keyboard shortcuts used to support deep keyboard
|
||||
* navigation of Blockly.
|
||||
*/
|
||||
export function registerKeyboardNavigationShortcuts() {
|
||||
registerShowContextMenu();
|
||||
registerMovementShortcuts();
|
||||
registerFocusWorkspace();
|
||||
}
|
||||
|
||||
registerDefaultShortcuts();
|
||||
registerKeyboardNavigationShortcuts();
|
||||
|
||||
@@ -16,7 +16,8 @@ import {createKeyDownEvent} from './test_helpers/user_input.js';
|
||||
suite('Keyboard Shortcut Items', function () {
|
||||
setup(function () {
|
||||
sharedTestSetup.call(this);
|
||||
this.workspace = Blockly.inject('blocklyDiv', {});
|
||||
const toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox});
|
||||
this.injectionDiv = this.workspace.getInjectionDiv();
|
||||
Blockly.ContextMenuRegistry.registry.reset();
|
||||
Blockly.ContextMenuItems.registerDefaultOptions();
|
||||
@@ -486,4 +487,65 @@ suite('Keyboard Shortcut Items', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus Workspace (W)', function () {
|
||||
setup(function () {
|
||||
this.testFocusChange = (startingElement) => {
|
||||
Blockly.getFocusManager().focusNode(startingElement);
|
||||
assert.strictEqual(
|
||||
Blockly.getFocusManager().getFocusedNode(),
|
||||
startingElement,
|
||||
);
|
||||
const event = createKeyDownEvent(Blockly.utils.KeyCodes.W);
|
||||
this.workspace.getInjectionDiv().dispatchEvent(event);
|
||||
assert.strictEqual(
|
||||
Blockly.getFocusManager().getFocusedNode(),
|
||||
this.workspace,
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
test('Does not change focus when workspace is already focused', function () {
|
||||
this.testFocusChange(this.workspace);
|
||||
});
|
||||
|
||||
test('Focuses workspace when toolbox is focused', function () {
|
||||
this.testFocusChange(this.workspace.getToolbox());
|
||||
});
|
||||
|
||||
test('Focuses workspace when flyout is focused', function () {
|
||||
this.workspace.getToolbox().getFlyout().show();
|
||||
const flyoutWorkspace = this.workspace
|
||||
.getToolbox()
|
||||
.getFlyout()
|
||||
.getWorkspace();
|
||||
this.testFocusChange(flyoutWorkspace);
|
||||
});
|
||||
|
||||
test('Focuses workspace when a block is focused', function () {
|
||||
const block = this.workspace.newBlock('controls_if');
|
||||
this.testFocusChange(block);
|
||||
});
|
||||
|
||||
suite('With mutator', function () {
|
||||
test('Focuses root workspace when a mutator block is focused', async function () {
|
||||
const block = this.workspace.newBlock('controls_if');
|
||||
const icon = block.getIcon(Blockly.icons.MutatorIcon.TYPE);
|
||||
await icon.setBubbleVisible(true);
|
||||
const mutatorWorkspace = icon.getWorkspace();
|
||||
this.testFocusChange(mutatorWorkspace.getAllBlocks()[0]);
|
||||
});
|
||||
|
||||
test("Focuses workspace when a mutator's flyout is focused", async function () {
|
||||
const block = this.workspace.newBlock('controls_if');
|
||||
const icon = block.getIcon(Blockly.icons.MutatorIcon.TYPE);
|
||||
await icon.setBubbleVisible(true);
|
||||
const mutatorFlyoutWorkspace = icon
|
||||
.getWorkspace()
|
||||
.getFlyout()
|
||||
.getWorkspace();
|
||||
this.testFocusChange(mutatorFlyoutWorkspace);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user