Skip to content

Commit

Permalink
feat: Press enter on block to open action menu (#158)
Browse files Browse the repository at this point in the history
* feat: Show context menu when enter pressed on block

* fix: Open context menu in correct location

* refactor: Introduce action menu

For now the action menu is identical to the context menu, but
now we can add additional menu items of our choice to it
(that the block itself gets no say in).
  • Loading branch information
cpcallen authored Jan 17, 2025
1 parent ddcd2ce commit 59fa664
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 9 deletions.
89 changes: 84 additions & 5 deletions src/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1231,13 +1231,13 @@ export class Navigation {
*/
handleEnterForWS(workspace: Blockly.WorkspaceSvg) {
const cursor = workspace.getCursor();
if (!cursor) {
return;
}
if (!cursor) return;
const curNode = cursor.getCurNode();
const nodeType = curNode.getType();
if (nodeType == Blockly.ASTNode.types.FIELD) {
(curNode.getLocation() as Blockly.Field).showEditor();
} else if (nodeType == Blockly.ASTNode.types.BLOCK) {
this.openActionMenu(curNode);
} else if (
curNode.isConnection() ||
nodeType == Blockly.ASTNode.types.WORKSPACE
Expand All @@ -1248,13 +1248,63 @@ export class Navigation {
} else {
this.focusFlyout(workspace);
}
} else if (nodeType == Blockly.ASTNode.types.BLOCK) {
this.warn('Cannot mark a block.');
} else if (nodeType == Blockly.ASTNode.types.STACK) {
this.warn('Cannot mark a stack.');
}
}

/**
* Show the action menu for a given node.
*
* The action menu will contain entries for relevant actions for the
* node's location. If the location is a block, this will include
* the contents of the block's context menu (if any).
*/
openActionMenu(node: Blockly.ASTNode) {
const fakeEvent = fakeEventForNode(node);
(node.getLocation() as Blockly.BlockSvg).showContextMenu(fakeEvent);

let menuOptions: Array<
| Blockly.ContextMenuRegistry.ContextMenuOption
| Blockly.ContextMenuRegistry.LegacyContextMenuOption
> | null = null;
let rtl: boolean;
let workspace: Blockly.WorkspaceSvg;

const nodeType = node.getType();
switch (nodeType) {
case Blockly.ASTNode.types.BLOCK:
const block = node.getLocation() as Blockly.BlockSvg;
workspace = block.workspace as Blockly.WorkspaceSvg;
rtl = block.RTL;

// Reimplement BlockSvg.prototype.generateContextMenu as that
// method is protected.
if (!workspace.options.readOnly && !block.contextMenu) {
menuOptions =
Blockly.ContextMenuRegistry.registry.getContextMenuOptions(
Blockly.ContextMenuRegistry.ScopeType.BLOCK,
{block},
);

// Allow the block to add or modify menuOptions.
if (block.customContextMenu) {
block.customContextMenu(menuOptions);
}
}
// End reimplement.
break;
default:
throw new TypeError(
`unable to show action menu for ASTNode of type ${nodeType}`,
);
}

if (!menuOptions || !menuOptions.length) return;

Blockly.ContextMenu.show(fakeEvent, menuOptions, rtl, workspace);
}

/**
* Pastes the copied block to the marked location if possible or
* onto the workspace otherwise.
Expand Down Expand Up @@ -1333,3 +1383,32 @@ export class Navigation {
}
}
}

/**
* Create a fake PointerEvent for opening the action menu for the
* given ASTNode.
*
* Currently only works for block nodes.
*
* @param node The node to open the action menu for.
* @returns A synthetic pointerdown PointerEvent.
*/
function fakeEventForNode(node: Blockly.ASTNode): PointerEvent {
if (node.getType() !== Blockly.ASTNode.types.BLOCK) {
throw new TypeError('can only create PointerEvents for BLOCK nodes');
}

// Get the location of the top-left corner of the block in
// screen coordinates.
const block = node.getLocation() as Blockly.BlockSvg;
const coords = Blockly.utils.svgMath.wsToScreenCoordinates(
block.workspace,
block.getRelativeToSurfaceXY(),
);

// Create a fake event for the action menu code to work from.
return new PointerEvent('pointerdown', {
clientX: coords.x,
clientY: coords.y,
});
}
11 changes: 7 additions & 4 deletions src/navigation_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,14 @@ export class NavigationController {
},

/**
* Mark the current location, insert a block from the flyout at
* the marked location, or press a flyout button.
* Enter key:
*
* - On the flyout: press a button or choose a block to place.
* - On a stack: open a block's context menu or field's editor.
* - On the workspace: open the context menu.
*/
mark: {
name: Constants.SHORTCUT_NAMES.MARK,
enter: {
name: Constants.SHORTCUT_NAMES.MARK, // FIXME
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
callback: (workspace) => {
let flyoutCursor;
Expand Down

0 comments on commit 59fa664

Please sign in to comment.