From 79b558e3b3d1b8c3a44f91b1d00f8974e903d463 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Mon, 21 Apr 2025 10:46:00 -0700 Subject: [PATCH 1/2] refactor: Use localizable messages. --- src/actions/clipboard.ts | 10 +++++-- src/actions/delete.ts | 7 +++-- src/actions/edit.ts | 9 ++++-- src/actions/enter.ts | 1 + src/actions/insert.ts | 7 ++++- src/actions/move.ts | 29 +++++++++++-------- src/constants.ts | 56 +++++++++++++++++++----------------- src/hints.ts | 4 +-- src/navigation_controller.ts | 2 +- src/shortcut_dialog.ts | 12 ++++---- src/shortcut_formatting.ts | 12 ++++---- 11 files changed, 88 insertions(+), 61 deletions(-) diff --git a/src/actions/clipboard.ts b/src/actions/clipboard.ts index 1e666496..f6fe8f21 100644 --- a/src/actions/clipboard.ts +++ b/src/actions/clipboard.ts @@ -9,6 +9,7 @@ import { Gesture, ShortcutRegistry, Events, + Msg, utils as blocklyUtils, clipboard, ICopyData, @@ -104,7 +105,8 @@ export class Clipboard { */ private registerCutContextMenuAction() { const cutAction: ContextMenuRegistry.RegistryItem = { - displayText: (scope) => `Cut (${getShortActionShortcut('cut')})`, + displayText: (scope) => + Msg['CUT_SHORTCUT'].replace('%1', getShortActionShortcut('cut')), preconditionFn: (scope) => { const ws = scope.block?.workspace; if (!ws) return 'hidden'; @@ -199,7 +201,8 @@ export class Clipboard { */ private registerCopyContextMenuAction() { const copyAction: ContextMenuRegistry.RegistryItem = { - displayText: (scope) => `Copy (${getShortActionShortcut('copy')})`, + displayText: (scope) => + Msg['COPY_SHORTCUT'].replace('%1', getShortActionShortcut('copy')), preconditionFn: (scope) => { const ws = scope.block?.workspace; if (!ws) return 'hidden'; @@ -304,7 +307,8 @@ export class Clipboard { */ private registerPasteContextMenuAction() { const pasteAction: ContextMenuRegistry.RegistryItem = { - displayText: (scope) => `Paste (${getShortActionShortcut('paste')})`, + displayText: (scope) => + Msg['PASTE_SHORTCUT'].replace('%1', getShortActionShortcut('paste')), preconditionFn: (scope: ContextMenuRegistry.Scope) => { let block; if (scope.focusedNode instanceof Blockly.Block) { diff --git a/src/actions/delete.ts b/src/actions/delete.ts index 5c5be195..08222377 100644 --- a/src/actions/delete.ts +++ b/src/actions/delete.ts @@ -7,10 +7,12 @@ import { ContextMenuRegistry, Gesture, + Msg, ShortcutRegistry, utils as BlocklyUtils, LineCursor, } from 'blockly'; +import {getShortActionShortcut} from '../shortcut_formatting'; import * as Constants from '../constants'; import type {WorkspaceSvg} from 'blockly'; import {Navigation} from '../navigation'; @@ -102,8 +104,9 @@ export class DeleteAction { const deleteItem: ContextMenuRegistry.RegistryItem = { displayText: (scope) => { + const shortcut = getShortActionShortcut(this.deleteShortcutName); if (!this.oldContextMenuItem) { - return 'Delete block (Del)'; + return Msg['DELETE_BLOCK'].replace('%1', shortcut); } type DisplayTextFn = (p1: ContextMenuRegistry.Scope) => string; @@ -111,7 +114,7 @@ export class DeleteAction { // of blocks that will be deleted. const oldDisplayText = this.oldContextMenuItem .displayText as DisplayTextFn; - return oldDisplayText(scope) + ' (Del)'; + return oldDisplayText(scope) + ` (${shortcut})`; }, preconditionFn: (scope, menuOpenEvent: Event) => { const ws = scope.block?.workspace; diff --git a/src/actions/edit.ts b/src/actions/edit.ts index ef8f34e8..60346080 100644 --- a/src/actions/edit.ts +++ b/src/actions/edit.ts @@ -4,8 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ContextMenuRegistry, LineCursor} from 'blockly'; +import {ContextMenuRegistry, LineCursor, Msg} from 'blockly'; import {Navigation} from 'src/navigation'; +import {getShortActionShortcut} from '../shortcut_formatting'; +import * as Constants from '../constants'; /** * Action to edit a block. This just moves the cursor to the first @@ -50,7 +52,10 @@ export class EditAction { */ private registerContextMenuAction() { const editAboveItem: ContextMenuRegistry.RegistryItem = { - displayText: 'Edit Block contents (→︎)', + displayText: Msg['EDIT_BLOCK_CONTENTS'].replace( + '%1', + getShortActionShortcut(Constants.SHORTCUT_NAMES.RIGHT), + ), preconditionFn: (scope: ContextMenuRegistry.Scope, menuOpenEvent) => { if (menuOpenEvent instanceof PointerEvent) return 'hidden'; const workspace = scope.block?.workspace; diff --git a/src/actions/enter.ts b/src/actions/enter.ts index 3fc4e254..10078406 100644 --- a/src/actions/enter.ts +++ b/src/actions/enter.ts @@ -6,6 +6,7 @@ import { Events, + Msg, ShortcutRegistry, utils as BlocklyUtils, getFocusManager, diff --git a/src/actions/insert.ts b/src/actions/insert.ts index 4b6eded5..c9c815d6 100644 --- a/src/actions/insert.ts +++ b/src/actions/insert.ts @@ -6,9 +6,11 @@ import { ContextMenuRegistry, + Msg, ShortcutRegistry, utils as BlocklyUtils, } from 'blockly'; +import {getShortActionShortcut} from '../shortcut_formatting'; import * as Constants from '../constants'; import type {WorkspaceSvg} from 'blockly'; import {Navigation} from '../navigation'; @@ -69,7 +71,10 @@ export class InsertAction { private registerContextMenuAction() { const insertAboveItem: ContextMenuRegistry.RegistryItem = { displayText: () => { - return 'Insert Block (I)'; + return Msg['INSERT_BLOCK'].replace( + '%1', + getShortActionShortcut(this.insertShortcutName), + ); }, preconditionFn: ( scope: ContextMenuRegistry.Scope, diff --git a/src/actions/move.ts b/src/actions/move.ts index 1ee6b76b..811ac688 100644 --- a/src/actions/move.ts +++ b/src/actions/move.ts @@ -7,12 +7,14 @@ import { BlockSvg, ContextMenuRegistry, + Msg, ShortcutRegistry, utils, WorkspaceSvg, } from 'blockly'; import {Direction} from '../drag_direction'; import {Mover} from './mover'; +import {getShortActionShortcut} from '../shortcut_formatting'; const KeyCodes = utils.KeyCodes; const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind( @@ -28,7 +30,7 @@ export class MoveActions { private shortcuts: ShortcutRegistry.KeyboardShortcut[] = [ // Begin and end move. { - name: 'Start move', + name: Msg['START_MOVE'], preconditionFn: (workspace) => { const startBlock = this.getCurrentBlock(workspace); return !!startBlock && this.mover.canMove(workspace, startBlock); @@ -42,14 +44,14 @@ export class MoveActions { keyCodes: [KeyCodes.M], }, { - name: 'Finish move', + name: Msg['FINISH_MOVE'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.finishMove(workspace), keyCodes: [KeyCodes.ENTER, KeyCodes.SPACE], allowCollision: true, }, { - name: 'Abort move', + name: Msg['ABORT_MOVE'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.abortMove(workspace), keyCodes: [KeyCodes.ESC], @@ -58,7 +60,7 @@ export class MoveActions { // Constrained moves. { - name: 'Move left, constrained', + name: Msg['MOVE_LEFT_CONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveConstrained(workspace, Direction.Left), @@ -66,7 +68,7 @@ export class MoveActions { allowCollision: true, }, { - name: 'Move right constrained', + name: Msg['MOVE_RIGHT_CONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveConstrained(workspace, Direction.Right), @@ -74,7 +76,7 @@ export class MoveActions { allowCollision: true, }, { - name: 'Move up, constrained', + name: Msg['MOVE_UP_CONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveConstrained(workspace, Direction.Up), @@ -82,7 +84,7 @@ export class MoveActions { allowCollision: true, }, { - name: 'Move down constrained', + name: Msg['MOVE_DOWN_CONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveConstrained(workspace, Direction.Down), @@ -92,7 +94,7 @@ export class MoveActions { // Unconstrained moves. { - name: 'Move left, unconstrained', + name: Msg['MOVE_LEFT_UNCONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveUnconstrained(workspace, Direction.Left), @@ -102,7 +104,7 @@ export class MoveActions { ], }, { - name: 'Move right, unconstrained', + name: Msg['MOVE_RIGHT_UNCONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveUnconstrained(workspace, Direction.Right), @@ -112,7 +114,7 @@ export class MoveActions { ], }, { - name: 'Move up unconstrained', + name: Msg['MOVE_UP_UNCONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveUnconstrained(workspace, Direction.Up), @@ -122,7 +124,7 @@ export class MoveActions { ], }, { - name: 'Move down, unconstrained', + name: Msg['MOVE_DOWN_UNCONSTRAINED'], preconditionFn: (workspace) => this.mover.isMoving(workspace), callback: (workspace) => this.mover.moveUnconstrained(workspace, Direction.Down), @@ -135,7 +137,10 @@ export class MoveActions { menuItems: ContextMenuRegistry.RegistryItem[] = [ { - displayText: 'Move Block (M)', + displayText: Msg['MOVE_BLOCK'].replace( + '%1', + getShortActionShortcut(Msg['START_MOVE']), + ), preconditionFn: (scope, menuOpenEvent) => { const workspace = scope.block?.workspace as WorkspaceSvg | null; if (!workspace || menuOpenEvent instanceof PointerEvent) diff --git a/src/constants.ts b/src/constants.ts index 5884cabf..14da6090 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -12,6 +12,8 @@ * @author aschmiedt@google.com (Abby Schmiedt) */ +import {Msg} from 'blockly/core'; + /** * Keyboard navigation states. * The different parts of Blockly that the user navigates between. @@ -70,29 +72,31 @@ export const SHORTCUT_CATEGORIES: Record< // better text because temporarily the name in the table is derived from // these id-like names. Array -> = { - 'General': [ - SHORTCUT_NAMES.MENU, - SHORTCUT_NAMES.EDIT_OR_CONFIRM, - SHORTCUT_NAMES.EXIT, - SHORTCUT_NAMES.TOOLBOX, - SHORTCUT_NAMES.CLEAN_UP, - SHORTCUT_NAMES.LIST_SHORTCUTS, - ], - 'Editing': [ - SHORTCUT_NAMES.INSERT, - 'delete', - SHORTCUT_NAMES.DISCONNECT, - 'cut', - 'copy', - 'paste', - 'undo', - 'redo', - ], - 'Code navigation': [ - SHORTCUT_NAMES.UP, - SHORTCUT_NAMES.DOWN, - SHORTCUT_NAMES.RIGHT, - SHORTCUT_NAMES.LEFT, - ], -}; +> = {}; + +SHORTCUT_CATEGORIES[Msg['SHORTCUTS_GENERAL']] = [ + SHORTCUT_NAMES.MENU, + SHORTCUT_NAMES.EDIT_OR_CONFIRM, + SHORTCUT_NAMES.EXIT, + SHORTCUT_NAMES.TOOLBOX, + SHORTCUT_NAMES.CLEAN_UP, + SHORTCUT_NAMES.LIST_SHORTCUTS, +]; + +SHORTCUT_CATEGORIES[Msg['SHORTCUTS_EDITING']] = [ + SHORTCUT_NAMES.INSERT, + 'delete', + SHORTCUT_NAMES.DISCONNECT, + 'cut', + 'copy', + 'paste', + 'undo', + 'redo', +]; + +SHORTCUT_CATEGORIES[Msg['SHORTCUTS_CODE_NAVIGATION']] = [ + SHORTCUT_NAMES.UP, + SHORTCUT_NAMES.DOWN, + SHORTCUT_NAMES.RIGHT, + SHORTCUT_NAMES.LEFT, +]; diff --git a/src/hints.ts b/src/hints.ts index 70b53000..78b5f6ec 100644 --- a/src/hints.ts +++ b/src/hints.ts @@ -6,7 +6,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {WorkspaceSvg, Toast} from 'blockly'; +import {Msg, WorkspaceSvg, Toast} from 'blockly'; import {SHORTCUT_NAMES} from './constants'; import {getShortActionShortcut} from './shortcut_formatting'; @@ -104,7 +104,7 @@ export function clearPasteHints(workspace: WorkspaceSvg) { */ export function showHelpHint(workspace: WorkspaceSvg) { const shortcut = getShortActionShortcut('list_shortcuts'); - const message = `Press ${shortcut} for help on keyboard controls`; + const message = Msg['HELP_PROMPT'].replace('%1', shortcut); const id = helpHintId; Toast.show(workspace, {message, id}); } diff --git a/src/navigation_controller.ts b/src/navigation_controller.ts index c4301f43..a75c7c2a 100644 --- a/src/navigation_controller.ts +++ b/src/navigation_controller.ts @@ -239,10 +239,10 @@ export class NavigationController { ShortcutRegistry.registry.register(shortcut); } this.deleteAction.install(); - this.editAction.install(); this.insertAction.install(); this.workspaceMovement.install(); this.arrowNavigation.install(); + this.editAction.install(); this.exitAction.install(); this.enterAction.install(); this.disconnectAction.install(); diff --git a/src/shortcut_dialog.ts b/src/shortcut_dialog.ts index a4cf2abd..c8ac9153 100644 --- a/src/shortcut_dialog.ts +++ b/src/shortcut_dialog.ts @@ -6,7 +6,7 @@ import * as Blockly from 'blockly/core'; import * as Constants from './constants'; -import {ShortcutRegistry} from 'blockly/core'; +import {Msg, ShortcutRegistry} from 'blockly/core'; import { getLongActionShortcutsAsKeys, upperCaseFirst, @@ -39,16 +39,16 @@ export class ShortcutDialog { getPlatform() { const {platform, userAgent} = navigator; if (platform.startsWith('Win')) { - return 'Windows'; + return Msg['WINDOWS']; } else if (platform.startsWith('Mac')) { - return 'macOS'; + return Msg['MAC_OS']; } else if (/\bCrOS\b/.test(userAgent)) { // Order is important because platform matches the Linux case below. - return 'ChromeOS'; + return Msg['CHROME_OS']; } else if (platform.includes('Linux')) { - return 'Linux'; + return Msg['LINUX']; } else { - return 'Unknown'; + return Msg['UNKNOWN']; } } diff --git a/src/shortcut_formatting.ts b/src/shortcut_formatting.ts index d0d376c2..b6ca67db 100644 --- a/src/shortcut_formatting.ts +++ b/src/shortcut_formatting.ts @@ -1,4 +1,4 @@ -import {ShortcutRegistry} from 'blockly'; +import {ShortcutRegistry, Msg} from 'blockly'; import {keyNames} from './keynames'; const isMacPlatform = navigator.platform.startsWith('Mac'); @@ -27,15 +27,15 @@ export function getLongActionShortcutsAsKeys(action: string): string[][] { } const longModifierNames: Record = { - 'Control': 'Ctrl', - 'Meta': '⌘ Command', - 'Alt': isMacPlatform ? '⌥ Option' : 'Alt', + 'Control': Msg['CONTROL_KEY'], + 'Meta': Msg['COMMAND_KEY'], + 'Alt': isMacPlatform ? Msg['OPTION_KEY'] : Msg['ALT_KEY'], }; const shortModifierNames: Record = { - 'Control': 'Ctrl', + 'Control': Msg['CONTROL_KEY'], 'Meta': '⌘', - 'Alt': isMacPlatform ? '⌥' : 'Alt', + 'Alt': isMacPlatform ? '⌥' : Msg['ALT_KEY'], }; /** From bf14d9e90447a8f693e3c75433f036c73fd20387 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Thu, 15 May 2025 15:28:42 -0700 Subject: [PATCH 2/2] chore: Fix post-rebase. --- src/actions/move.ts | 293 +++++++++++++++++++++++--------------------- 1 file changed, 153 insertions(+), 140 deletions(-) diff --git a/src/actions/move.ts b/src/actions/move.ts index 811ac688..415fe40c 100644 --- a/src/actions/move.ts +++ b/src/actions/move.ts @@ -27,166 +27,179 @@ const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind( export class MoveActions { constructor(private mover: Mover) {} - private shortcuts: ShortcutRegistry.KeyboardShortcut[] = [ - // Begin and end move. - { - name: Msg['START_MOVE'], - preconditionFn: (workspace) => { - const startBlock = this.getCurrentBlock(workspace); - return !!startBlock && this.mover.canMove(workspace, startBlock); + private shortcutNames: string[] = []; + private menuItemNames: string[] = []; + + private registerShortcuts() { + const shortcuts: ShortcutRegistry.KeyboardShortcut[] = [ + // Begin and end move. + { + name: Msg['START_MOVE'], + preconditionFn: (workspace) => { + const startBlock = this.getCurrentBlock(workspace); + return !!startBlock && this.mover.canMove(workspace, startBlock); + }, + callback: (workspace) => { + const startBlock = this.getCurrentBlock(workspace); + return ( + !!startBlock && this.mover.startMove(workspace, startBlock, null) + ); + }, + keyCodes: [KeyCodes.M], }, - callback: (workspace) => { - const startBlock = this.getCurrentBlock(workspace); - return ( - !!startBlock && this.mover.startMove(workspace, startBlock, null) - ); + { + name: Msg['FINISH_MOVE'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => this.mover.finishMove(workspace), + keyCodes: [KeyCodes.ENTER, KeyCodes.SPACE], + allowCollision: true, + }, + { + name: Msg['ABORT_MOVE'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => this.mover.abortMove(workspace), + keyCodes: [KeyCodes.ESC], + allowCollision: true, }, - keyCodes: [KeyCodes.M], - }, - { - name: Msg['FINISH_MOVE'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => this.mover.finishMove(workspace), - keyCodes: [KeyCodes.ENTER, KeyCodes.SPACE], - allowCollision: true, - }, - { - name: Msg['ABORT_MOVE'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => this.mover.abortMove(workspace), - keyCodes: [KeyCodes.ESC], - allowCollision: true, - }, - // Constrained moves. - { - name: Msg['MOVE_LEFT_CONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveConstrained(workspace, Direction.Left), - keyCodes: [KeyCodes.LEFT], - allowCollision: true, - }, - { - name: Msg['MOVE_RIGHT_CONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveConstrained(workspace, Direction.Right), - keyCodes: [KeyCodes.RIGHT], - allowCollision: true, - }, - { - name: Msg['MOVE_UP_CONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveConstrained(workspace, Direction.Up), - keyCodes: [KeyCodes.UP], - allowCollision: true, - }, - { - name: Msg['MOVE_DOWN_CONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveConstrained(workspace, Direction.Down), - keyCodes: [KeyCodes.DOWN], - allowCollision: true, - }, + // Constrained moves. + { + name: Msg['MOVE_LEFT_CONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveConstrained(workspace, Direction.Left), + keyCodes: [KeyCodes.LEFT], + allowCollision: true, + }, + { + name: Msg['MOVE_RIGHT_CONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveConstrained(workspace, Direction.Right), + keyCodes: [KeyCodes.RIGHT], + allowCollision: true, + }, + { + name: Msg['MOVE_UP_CONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveConstrained(workspace, Direction.Up), + keyCodes: [KeyCodes.UP], + allowCollision: true, + }, + { + name: Msg['MOVE_DOWN_CONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveConstrained(workspace, Direction.Down), + keyCodes: [KeyCodes.DOWN], + allowCollision: true, + }, - // Unconstrained moves. - { - name: Msg['MOVE_LEFT_UNCONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveUnconstrained(workspace, Direction.Left), - keyCodes: [ - createSerializedKey(KeyCodes.LEFT, [KeyCodes.ALT]), - createSerializedKey(KeyCodes.LEFT, [KeyCodes.CTRL]), - ], - }, - { - name: Msg['MOVE_RIGHT_UNCONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveUnconstrained(workspace, Direction.Right), - keyCodes: [ - createSerializedKey(KeyCodes.RIGHT, [KeyCodes.ALT]), - createSerializedKey(KeyCodes.RIGHT, [KeyCodes.CTRL]), - ], - }, - { - name: Msg['MOVE_UP_UNCONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveUnconstrained(workspace, Direction.Up), - keyCodes: [ - createSerializedKey(KeyCodes.UP, [KeyCodes.ALT]), - createSerializedKey(KeyCodes.UP, [KeyCodes.CTRL]), - ], - }, - { - name: Msg['MOVE_DOWN_UNCONSTRAINED'], - preconditionFn: (workspace) => this.mover.isMoving(workspace), - callback: (workspace) => - this.mover.moveUnconstrained(workspace, Direction.Down), - keyCodes: [ - createSerializedKey(KeyCodes.DOWN, [KeyCodes.ALT]), - createSerializedKey(KeyCodes.DOWN, [KeyCodes.CTRL]), - ], - }, - ]; + // Unconstrained moves. + { + name: Msg['MOVE_LEFT_UNCONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveUnconstrained(workspace, Direction.Left), + keyCodes: [ + createSerializedKey(KeyCodes.LEFT, [KeyCodes.ALT]), + createSerializedKey(KeyCodes.LEFT, [KeyCodes.CTRL]), + ], + }, + { + name: Msg['MOVE_RIGHT_UNCONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveUnconstrained(workspace, Direction.Right), + keyCodes: [ + createSerializedKey(KeyCodes.RIGHT, [KeyCodes.ALT]), + createSerializedKey(KeyCodes.RIGHT, [KeyCodes.CTRL]), + ], + }, + { + name: Msg['MOVE_UP_UNCONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveUnconstrained(workspace, Direction.Up), + keyCodes: [ + createSerializedKey(KeyCodes.UP, [KeyCodes.ALT]), + createSerializedKey(KeyCodes.UP, [KeyCodes.CTRL]), + ], + }, + { + name: Msg['MOVE_DOWN_UNCONSTRAINED'], + preconditionFn: (workspace) => this.mover.isMoving(workspace), + callback: (workspace) => + this.mover.moveUnconstrained(workspace, Direction.Down), + keyCodes: [ + createSerializedKey(KeyCodes.DOWN, [KeyCodes.ALT]), + createSerializedKey(KeyCodes.DOWN, [KeyCodes.CTRL]), + ], + }, + ]; - menuItems: ContextMenuRegistry.RegistryItem[] = [ - { - displayText: Msg['MOVE_BLOCK'].replace( - '%1', - getShortActionShortcut(Msg['START_MOVE']), - ), - preconditionFn: (scope, menuOpenEvent) => { - const workspace = scope.block?.workspace as WorkspaceSvg | null; - if (!workspace || menuOpenEvent instanceof PointerEvent) - return 'hidden'; + for (const shortcut of shortcuts) { + ShortcutRegistry.registry.register(shortcut); + this.shortcutNames.push(shortcut.name); + } + } - const startBlock = this.getCurrentBlock(workspace); - return !!startBlock && this.mover.canMove(workspace, startBlock) - ? 'enabled' - : 'disabled'; - }, - callback: (scope) => { - const workspace = scope.block?.workspace as WorkspaceSvg | null; - if (!workspace) return false; - const startBlock = this.getCurrentBlock(workspace); - return ( - !!startBlock && this.mover.startMove(workspace, startBlock, null) - ); + private registerMenuItems() { + const menuItems: ContextMenuRegistry.RegistryItem[] = [ + { + displayText: Msg['MOVE_BLOCK'].replace( + '%1', + getShortActionShortcut(Msg['START_MOVE']), + ), + preconditionFn: (scope, menuOpenEvent) => { + const workspace = scope.block?.workspace as WorkspaceSvg | null; + if (!workspace || menuOpenEvent instanceof PointerEvent) + return 'hidden'; + + const startBlock = this.getCurrentBlock(workspace); + return !!startBlock && this.mover.canMove(workspace, startBlock) + ? 'enabled' + : 'disabled'; + }, + callback: (scope) => { + const workspace = scope.block?.workspace as WorkspaceSvg | null; + if (!workspace) return false; + const startBlock = this.getCurrentBlock(workspace); + return ( + !!startBlock && this.mover.startMove(workspace, startBlock, null) + ); + }, + scopeType: ContextMenuRegistry.ScopeType.BLOCK, + id: 'move', + weight: 8.5, }, - scopeType: ContextMenuRegistry.ScopeType.BLOCK, - id: 'move', - weight: 8.5, - }, - ]; + ]; + + for (const menuItem of menuItems) { + ContextMenuRegistry.registry.register(menuItem); + this.menuItemNames.push(menuItem.id); + } + } /** * Install the actions as both keyboard shortcuts and (where * applicable) context menu items. */ install() { - for (const shortcut of this.shortcuts) { - ShortcutRegistry.registry.register(shortcut); - } - for (const menuItem of this.menuItems) { - ContextMenuRegistry.registry.register(menuItem); - } + this.registerShortcuts(); + this.registerMenuItems(); } /** * Uninstall these actions. */ uninstall() { - for (const shortcut of this.shortcuts) { - ShortcutRegistry.registry.unregister(shortcut.name); + for (const shortcut of this.shortcutNames) { + ShortcutRegistry.registry.unregister(shortcut); } - for (const menuItem of this.menuItems) { - ContextMenuRegistry.registry.unregister(menuItem.id); + for (const menuItem of this.menuItemNames) { + ContextMenuRegistry.registry.unregister(menuItem); } }