Skip to content

Commit 1ec35c2

Browse files
authored
feat: Display action menu, containing insert action, for input connections (#242)
* fix: Insert action on stack nodes * feat: Support action menu on input connections * feat: Position action menu relative to connection, not block * chore: format * fix: Capitalise insert menu option
1 parent 212e176 commit 1ec35c2

File tree

3 files changed

+42
-28
lines changed

3 files changed

+42
-28
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This plugin for Blockly enables keyboard navigation. It is intended to
44
experiment with different actions that might help visually impaired and motor
55
impaired people navigate a Blockly workspace.
66

7-
Keyboard navigation and screenreader support are closely coupled. The Blockly
7+
Keyboard navigation and screenreader support are closely coupled. The Blockly
88
team intends to add screenreader support incrementally in Q2 and Q3 of 2025,
99
as we validate the general approach to navigation.
1010

@@ -15,35 +15,36 @@ on the wiki](https://github.com/google/blockly-keyboard-experimentation/wiki/Jan
1515

1616
You can explore the current state of the plugin on the [test page](https://google.github.io/blockly-keyboard-experimentation/).
1717

18-
To use keyboard navigation, click on the workspace or press tab until you
18+
To use keyboard navigation, click on the workspace or press tab until you
1919
reach the workspace.
2020

21-
Once browser focus is on the Blockly workspace, you can use arrow keys to
22-
move a **cursor** around the workspace. You can use keyboard shortcuts to
23-
take actions at the cursor.
21+
Once browser focus is on the Blockly workspace, you can use arrow keys to
22+
move a **cursor** around the workspace. You can use keyboard shortcuts to
23+
take actions at the cursor.
2424

25-
For instance, you can move the cursor to a
25+
For instance, you can move the cursor to a
2626
dropdown field and press the `Enter` key to edit the field.
2727

28-
The available actions depend on the cursor location. For instance, if the
28+
The available actions depend on the cursor location. For instance, if the
2929
cursor is on a block you can copy it with `Ctrl + C`.
3030

3131
You can open the toolbox by pressing `T` or pressing `Tab` until the toolbox is
3232
highlighted. Just like the workspace, you can use the arrow keys to move around
3333
the toolbox and select a block. Pressing `Enter` will place the block at the
3434
cursor's location on the workspace.
3535

36-
If you don't know which actions are available at your cursor location, you
36+
If you don't know which actions are available at your cursor location, you
3737
can press `Ctrl + Enter` to open the context menu and see a list of actions.
3838

3939
### Giving feedback
4040

4141
If you use the test page and find a bug, please let us know by opening an issue
4242
on this repository! Include information about how to reproduce the bug, what
43-
the bad behaviour was, and what you expected it to do. The Blockly team will
43+
the bad behaviour was, and what you expected it to do. The Blockly team will
4444
triage the bug and add it to the roadmap.
4545

4646
### Note on @blockly/keyboard-navigation plugin
47+
4748
There is also an [existing keyboard navigation plugin](https://www.npmjs.com/package/@blockly/keyboard-navigation). That plugin may be where
4849
a finalized version of keyboard navigation eventually lives. But for now, this
4950
is where experimentation will be done.

src/navigation.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ export class Navigation {
13471347
// case Blockly.ASTNode.types.INPUT:
13481348
case Blockly.ASTNode.types.NEXT:
13491349
case Blockly.ASTNode.types.PREVIOUS:
1350+
case Blockly.ASTNode.types.INPUT:
13501351
const connection = node.getLocation() as Blockly.Connection;
13511352
rtl = connection.getSourceBlock().RTL;
13521353

@@ -1465,7 +1466,8 @@ function fakeEventForNode(node: Blockly.ASTNode): PointerEvent {
14651466
return fakeEventForBlockNode(node);
14661467
case Blockly.ASTNode.types.NEXT:
14671468
case Blockly.ASTNode.types.PREVIOUS:
1468-
return fakeEventForStackNode(node);
1469+
case Blockly.ASTNode.types.INPUT:
1470+
return fakeEventForConnectionNode(node);
14691471
default:
14701472
throw new TypeError('unhandled node type');
14711473
}
@@ -1513,32 +1515,44 @@ function fakeEventForBlockNode(node: Blockly.ASTNode): PointerEvent {
15131515

15141516
/**
15151517
* Create a fake PointerEvent for opening the action menu for the
1516-
* given ASTNode of type NEXT or PREVIOUS.
1518+
* given ASTNode of type NEXT, PREVIOUS or INPUT.
15171519
*
15181520
* For now this just puts the action menu in the same place as the
15191521
* context menu for the source block.
15201522
*
15211523
* @param node The node to open the action menu for.
15221524
* @returns A synthetic pointerdown PointerEvent.
15231525
*/
1524-
function fakeEventForStackNode(node: Blockly.ASTNode): PointerEvent {
1526+
function fakeEventForConnectionNode(node: Blockly.ASTNode): PointerEvent {
15251527
if (
15261528
node.getType() !== Blockly.ASTNode.types.NEXT &&
1527-
node.getType() !== Blockly.ASTNode.types.PREVIOUS
1529+
node.getType() !== Blockly.ASTNode.types.PREVIOUS &&
1530+
node.getType() !== Blockly.ASTNode.types.INPUT
15281531
) {
1529-
throw new TypeError(
1530-
'can only create PointerEvents for NEXT / PREVIOUS nodes',
1531-
);
1532+
throw new TypeError('can only create PointerEvents for connection nodes');
15321533
}
15331534

15341535
const connection = node.getLocation() as Blockly.Connection;
1536+
const block = connection.getSourceBlock();
1537+
const workspace = block.workspace as Blockly.WorkspaceSvg;
15351538

1536-
return fakeEventForBlockNode(
1537-
new Blockly.ASTNode(
1538-
Blockly.ASTNode.types.BLOCK,
1539-
connection.getSourceBlock(),
1540-
),
1539+
if (typeof connection.x !== 'number') {
1540+
// No coordinates for connection? Fall back to the parent block.
1541+
const blockNode = new Blockly.ASTNode(Blockly.ASTNode.types.BLOCK, block);
1542+
return fakeEventForBlockNode(blockNode);
1543+
}
1544+
const connectionWSCoords = new Blockly.utils.Coordinate(
1545+
connection.x,
1546+
connection.y,
1547+
);
1548+
const connectionScreenCoords = Blockly.utils.svgMath.wsToScreenCoordinates(
1549+
workspace,
1550+
connectionWSCoords,
15411551
);
1552+
return new PointerEvent('pointerdown', {
1553+
clientX: connectionScreenCoords.x + 5,
1554+
clientY: connectionScreenCoords.y + 5,
1555+
});
15421556
}
15431557

15441558
/**

src/navigation_controller.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
BlockSvg,
1919
comments,
2020
Connection,
21+
ConnectionType,
2122
ContextMenuRegistry,
2223
ICopyData,
2324
ShortcutRegistry,
@@ -727,20 +728,18 @@ export class NavigationController {
727728
protected registerInsertAction() {
728729
const insertAboveAction: ContextMenuRegistry.RegistryItem = {
729730
displayText: (scope: Scope) =>
730-
scope.block
731-
? 'Insert block above'
732-
: scope.connection
733-
? 'Insert block here'
734-
: 'Insert',
731+
scope.block?.previousConnection ? 'Insert Block Above' : 'Insert Block',
735732
preconditionFn: (scope: Scope) => {
736733
const block = scope.block ?? scope.connection?.getSourceBlock();
737734
const ws = block?.workspace as WorkspaceSvg | null;
738735
if (!ws) return 'hidden';
739736

740737
return this.canCurrentlyEdit(ws) ? 'enabled' : 'hidden';
741738
},
742-
callback: (scope) => {
743-
const ws = scope.block?.workspace;
739+
callback: (scope: Scope) => {
740+
let ws =
741+
scope.block?.workspace ??
742+
(scope.connection?.getSourceBlock().workspace as WorkspaceSvg);
744743
if (!ws) return false;
745744

746745
if (this.navigation.getState(ws) === Constants.STATE.WORKSPACE) {

0 commit comments

Comments
 (0)