Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions src/actions/ws_movement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import {ASTNode, ShortcutRegistry, utils as BlocklyUtils} from 'blockly';
import * as Constants from '../constants';
import type {WorkspaceSvg} from 'blockly';

const KeyCodes = BlocklyUtils.KeyCodes;
const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind(
ShortcutRegistry.registry,
);

/**
* Logic for free movement of the cursor on the workspace with keyboard
* shortcuts.
*/
export class WorkspaceMovement {
/**
* Function provided by the navigation controller to say whether editing
* is allowed.
*/
private canCurrentlyEdit: (ws: WorkspaceSvg) => boolean;

/**
* The distance to move the cursor when the cursor is on the workspace.
*/
WS_MOVE_DISTANCE = 40;

constructor(canEdit: (ws: WorkspaceSvg) => boolean) {
this.canCurrentlyEdit = canEdit;
}

/**
* Install these actions as both keyboard shortcuts and context menu items.
*/
install() {
const shortcutList: {
[name: string]: ShortcutRegistry.KeyboardShortcut;
} = {
/** Move the cursor on the workspace to the left. */
wsMoveLeft: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_LEFT,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => this.moveWSCursor(workspace, -1, 0),
keyCodes: [createSerializedKey(KeyCodes.A, [KeyCodes.SHIFT])],
},
/** Move the cursor on the workspace to the right. */
wsMoveRight: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_RIGHT,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => this.moveWSCursor(workspace, 1, 0),
keyCodes: [createSerializedKey(KeyCodes.D, [KeyCodes.SHIFT])],
},

/** Move the cursor on the workspace up. */
wsMoveUp: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_UP,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => this.moveWSCursor(workspace, 0, -1),
keyCodes: [createSerializedKey(KeyCodes.W, [KeyCodes.SHIFT])],
},

/** Move the cursor on the workspace down. */
wsMoveDown: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_DOWN,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => this.moveWSCursor(workspace, 0, 1),
keyCodes: [createSerializedKey(KeyCodes.S, [KeyCodes.SHIFT])],
},
};
for (const shortcut of Object.values(shortcutList)) {
ShortcutRegistry.registry.register(shortcut);
}
}

/**
* Uninstall these actions.
*/
uninstall() {
ShortcutRegistry.registry.unregister(
Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_LEFT,
);
ShortcutRegistry.registry.unregister(
Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_RIGHT,
);
ShortcutRegistry.registry.unregister(
Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_UP,
);
ShortcutRegistry.registry.unregister(
Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_DOWN,
);
}

/**
* Moves the workspace cursor in the given direction.
*
* @param workspace The workspace the cursor is on.
* @param xDirection -1 to move cursor left. 1 to move cursor right.
* @param yDirection -1 to move cursor up. 1 to move cursor down.
* @returns True if the current node is a workspace, false
* otherwise.
*/
moveWSCursor(
workspace: WorkspaceSvg,
xDirection: number,
yDirection: number,
): boolean {
const cursor = workspace.getCursor();
if (!cursor) {
return false;
}
const curNode = cursor.getCurNode();

if (curNode.getType() !== ASTNode.types.WORKSPACE) {
return false;
}

const wsCoord = curNode.getWsCoordinate();
const newX = xDirection * this.WS_MOVE_DISTANCE + wsCoord.x;
const newY = yDirection * this.WS_MOVE_DISTANCE + wsCoord.y;

cursor.setCurNode(
ASTNode.createWorkspaceNode(
workspace,
new BlocklyUtils.Coordinate(newX, newY),
)!,
);
return true;
}
}
42 changes: 0 additions & 42 deletions src/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ export class Navigation {
*/
workspaceStates: {[index: string]: Constants.STATE} = {};

/**
* The distance to move the cursor when the cursor is on the workspace.
*/
WS_MOVE_DISTANCE = 40;

/**
* The default coordinate to use when focusing on the workspace and no
* blocks are present. In pixel coordinates, but will be converted to
Expand Down Expand Up @@ -1173,43 +1168,6 @@ export class Navigation {
console.error(msg);
}

/**
* Moves the workspace cursor in the given direction.
*
* @param workspace The workspace the cursor is on.
* @param xDirection -1 to move cursor left. 1 to move cursor right.
* @param yDirection -1 to move cursor up. 1 to move cursor down.
* @returns True if the current node is a workspace, false
* otherwise.
*/
moveWSCursor(
workspace: Blockly.WorkspaceSvg,
xDirection: number,
yDirection: number,
): boolean {
const cursor = workspace.getCursor();
if (!cursor) {
return false;
}
const curNode = cursor.getCurNode();

if (curNode.getType() !== Blockly.ASTNode.types.WORKSPACE) {
return false;
}

const wsCoord = curNode.getWsCoordinate();
const newX = xDirection * this.WS_MOVE_DISTANCE + wsCoord.x;
const newY = yDirection * this.WS_MOVE_DISTANCE + wsCoord.y;

cursor.setCurNode(
Blockly.ASTNode.createWorkspaceNode(
workspace,
new Blockly.utils.Coordinate(newX, newY),
)!,
);
return true;
}

/**
* Handles hitting the enter key on the workspace.
*
Expand Down
47 changes: 7 additions & 40 deletions src/navigation_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {ShortcutDialog} from './shortcut_dialog';
import {DeleteAction} from './actions/delete';
import {InsertAction} from './actions/insert';
import {Clipboard} from './actions/clipboard';
import {WorkspaceMovement} from './actions/ws_movement';

const KeyCodes = BlocklyUtils.KeyCodes;
const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind(
Expand Down Expand Up @@ -60,6 +61,10 @@ export class NavigationController {
this.canCurrentlyEdit.bind(this),
);

workspaceMovement: WorkspaceMovement = new WorkspaceMovement(
this.canCurrentlyEdit.bind(this),
);

hasNavigationFocus: boolean = false;

/**
Expand Down Expand Up @@ -519,46 +524,6 @@ export class NavigationController {
allowCollision: true,
},

/** Move the cursor on the workspace to the left. */
wsMoveLeft: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_LEFT,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => {
return this.navigation.moveWSCursor(workspace, -1, 0);
},
keyCodes: [createSerializedKey(KeyCodes.A, [KeyCodes.SHIFT])],
},

/** Move the cursor on the workspace to the right. */
wsMoveRight: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_RIGHT,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => {
return this.navigation.moveWSCursor(workspace, 1, 0);
},
keyCodes: [createSerializedKey(KeyCodes.D, [KeyCodes.SHIFT])],
},

/** Move the cursor on the workspace up. */
wsMoveUp: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_UP,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => {
return this.navigation.moveWSCursor(workspace, 0, -1);
},
keyCodes: [createSerializedKey(KeyCodes.W, [KeyCodes.SHIFT])],
},

/** Move the cursor on the workspace down. */
wsMoveDown: {
name: Constants.SHORTCUT_NAMES.MOVE_WS_CURSOR_DOWN,
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => {
return this.navigation.moveWSCursor(workspace, 0, 1);
},
keyCodes: [createSerializedKey(KeyCodes.S, [KeyCodes.SHIFT])],
},

/** List all of the currently registered shortcuts. */
announceShortcuts: {
name: Constants.SHORTCUT_NAMES.LIST_SHORTCUTS,
Expand Down Expand Up @@ -714,6 +679,7 @@ export class NavigationController {
}
this.deleteAction.install();
this.insertAction.install();
this.workspaceMovement.install();

this.clipboard.install();

Expand All @@ -734,6 +700,7 @@ export class NavigationController {
this.deleteAction.uninstall();
this.insertAction.uninstall();
this.clipboard.uninstall();
this.workspaceMovement.uninstall();

this.removeShortcutHandlers();
this.navigation.dispose();
Expand Down