diff --git a/src/actions/action_menu.ts b/src/actions/action_menu.ts index 257426ba..ae04671d 100644 --- a/src/actions/action_menu.ts +++ b/src/actions/action_menu.ts @@ -6,7 +6,6 @@ import { ASTNode, - Connection, ContextMenu, ContextMenuRegistry, ShortcutRegistry, @@ -22,10 +21,6 @@ const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind( ShortcutRegistry.registry, ); -export interface ScopeWithConnection extends ContextMenuRegistry.Scope { - connection?: Connection; -} - /** * Keyboard shortcut to show the action menu on Cmr/Ctrl/Alt+Enter key. */ @@ -114,10 +109,10 @@ export class ActionMenu { const connection = node.getLocation() as RenderedConnection; rtl = connection.getSourceBlock().RTL; - // Slightly hacky: get insert action from registry. Hacky - // because registry typings don't include {connection: ...} as - // a possible kind of scope. - const menuOptions = this.addConnectionItems(connection, menuOpenEvent); + const menuOptions = ContextMenuRegistry.registry.getContextMenuOptions( + {focusedNode: connection}, + menuOpenEvent, + ); // If no valid options, don't show a menu if (!menuOptions?.length) return true; const location = this.calculateLocationForConnectionMenu(connection); @@ -153,45 +148,6 @@ export class ActionMenu { return true; } - /** - * Add menu items for a context menu on a connection scope. - * - * @param connection The connection on which the menu is shown. - * @param menuOpenEvent The event that opened this context menu. - */ - private addConnectionItems(connection: Connection, menuOpenEvent: Event) { - const menuOptions: Array< - | ContextMenuRegistry.ContextMenuOption - | ContextMenuRegistry.LegacyContextMenuOption - > = []; - const possibleOptions = [ - this.getContextMenuAction('insert'), - this.getContextMenuAction('blockPasteFromContextMenu'), - ]; - - // Check preconditions and get menu texts. - const scope = { - connection, - } as unknown as ContextMenuRegistry.Scope; - - for (const option of possibleOptions) { - const precondition = option.preconditionFn?.(scope, menuOpenEvent); - if (precondition === 'hidden') continue; - const displayText = - (typeof option.displayText === 'function' - ? option.displayText(scope) - : option.displayText) ?? ''; - menuOptions.push({ - text: displayText, - enabled: precondition === 'enabled', - callback: option.callback, - scope, - weight: option.weight, - }); - } - return menuOptions; - } - /** * Find a context menu action, throwing an `Error` if it is not present or * not an action. This usefully narrows the type to `ActionRegistryItem` diff --git a/src/actions/clipboard.ts b/src/actions/clipboard.ts index 8215142c..cd1dd838 100644 --- a/src/actions/clipboard.ts +++ b/src/actions/clipboard.ts @@ -17,8 +17,8 @@ import { import * as Constants from '../constants'; import type {BlockSvg, WorkspaceSvg} from 'blockly'; import {Navigation} from '../navigation'; -import {ScopeWithConnection} from './action_menu'; import {getShortActionShortcut} from '../shortcut_formatting'; +import * as Blockly from 'blockly'; const KeyCodes = blocklyUtils.KeyCodes; const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind( @@ -306,19 +306,28 @@ export class Clipboard { private registerPasteContextMenuAction() { const pasteAction: ContextMenuRegistry.RegistryItem = { displayText: (scope) => `Paste (${getShortActionShortcut('paste')})`, - preconditionFn: (scope: ScopeWithConnection) => { - const block = scope.block ?? scope.connection?.getSourceBlock(); + preconditionFn: (scope: ContextMenuRegistry.Scope) => { + let block; + if (scope.focusedNode instanceof Blockly.Block) { + block = scope.focusedNode; + } else if (scope.focusedNode instanceof Blockly.Connection) { + block = scope.focusedNode.getSourceBlock(); + } const ws = block?.workspace as WorkspaceSvg | null; if (!ws) return 'hidden'; return this.pastePrecondition(ws) ? 'enabled' : 'disabled'; }, - callback: (scope: ScopeWithConnection) => { - const block = scope.block ?? scope.connection?.getSourceBlock(); + callback: (scope: ContextMenuRegistry.Scope) => { + let block; + if (scope.focusedNode instanceof Blockly.Block) { + block = scope.focusedNode; + } else if (scope.focusedNode instanceof Blockly.Connection) { + block = scope.focusedNode.getSourceBlock(); + } const ws = block?.workspace as WorkspaceSvg | null; if (!ws) return; return this.pasteCallback(ws); }, - scopeType: ContextMenuRegistry.ScopeType.BLOCK, id: 'blockPasteFromContextMenu', weight: BASE_WEIGHT + 2, }; diff --git a/src/actions/insert.ts b/src/actions/insert.ts index 0d2e7a39..aece7712 100644 --- a/src/actions/insert.ts +++ b/src/actions/insert.ts @@ -12,7 +12,8 @@ import { import * as Constants from '../constants'; import type {WorkspaceSvg} from 'blockly'; import {Navigation} from '../navigation'; -import {ScopeWithConnection} from './action_menu'; + +import * as Blockly from 'blockly/core'; const KeyCodes = BlocklyUtils.KeyCodes; @@ -70,21 +71,25 @@ export class InsertAction { displayText: () => { return 'Insert Block (I)'; }, - preconditionFn: (scope: ScopeWithConnection) => { - const block = scope.block ?? scope.connection?.getSourceBlock(); + preconditionFn: (scope: ContextMenuRegistry.Scope) => { + let block; + if (scope.focusedNode instanceof Blockly.Block) { + block = scope.focusedNode; + } else if (scope.focusedNode instanceof Blockly.Connection) { + block = scope.focusedNode.getSourceBlock(); + } const ws = block?.workspace as WorkspaceSvg | null; if (!ws) return 'hidden'; return this.insertPrecondition(ws) ? 'enabled' : 'hidden'; }, - callback: (scope: ScopeWithConnection) => { + callback: (scope: ContextMenuRegistry.Scope) => { const ws = - scope.block?.workspace ?? - (scope.connection?.getSourceBlock().workspace as WorkspaceSvg); + scope.focusedNode?.workspace ?? + (scope.focusedNode?.getSourceBlock().workspace as WorkspaceSvg); if (!ws) return false; this.insertCallback(ws); }, - scopeType: ContextMenuRegistry.ScopeType.BLOCK, id: 'insert', weight: 9, }; diff --git a/src/navigation.ts b/src/navigation.ts index 6bb1caf3..c9b1f83b 100644 --- a/src/navigation.ts +++ b/src/navigation.ts @@ -404,8 +404,8 @@ export class Navigation { const passiveFocusNode = this.passiveFocusIndicator.getCurNode(); this.passiveFocusIndicator.hide(); const disposed = passiveFocusNode?.getSourceBlock()?.disposed; - // If there's a gesture then it will either set the node if it has not - // been disposed (which can happen when blocks are reloaded) or be a click + // If there's a gesture then it will either set the node if it has not + // been disposed (which can happen when blocks are reloaded) or be a click // that should not set one. if (!Blockly.Gesture.inProgress() && passiveFocusNode && !disposed) { cursor.setCurNode(passiveFocusNode);