Skip to content

Commit ece662a

Browse files
authored
Fix: don't visit connections with the cursor. (#9030)
1 parent e7af75e commit ece662a

File tree

6 files changed

+220
-297
lines changed

6 files changed

+220
-297
lines changed

core/keyboard_nav/block_navigation_policy.ts

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import type {BlockSvg} from '../block_svg.js';
7+
import {BlockSvg} from '../block_svg.js';
8+
import type {Field} from '../field.js';
89
import type {INavigable} from '../interfaces/i_navigable.js';
910
import type {INavigationPolicy} from '../interfaces/i_navigation_policy.js';
10-
import type {RenderedConnection} from '../rendered_connection.js';
11+
import {WorkspaceSvg} from '../workspace_svg.js';
1112

1213
/**
1314
* Set of rules controlling keyboard navigation from a block.
@@ -24,7 +25,8 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
2425
for (const field of input.fieldRow) {
2526
return field;
2627
}
27-
if (input.connection) return input.connection as RenderedConnection;
28+
if (input.connection?.targetBlock())
29+
return input.connection.targetBlock() as BlockSvg;
2830
}
2931

3032
return null;
@@ -38,12 +40,14 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
3840
* which it is attached.
3941
*/
4042
getParent(current: BlockSvg): INavigable<unknown> | null {
41-
const topBlock = current.getTopStackBlock();
43+
if (current.previousConnection?.targetBlock()) {
44+
const surroundParent = current.getSurroundParent();
45+
if (surroundParent) return surroundParent;
46+
} else if (current.outputConnection?.targetBlock()) {
47+
return current.outputConnection.targetBlock();
48+
}
4249

43-
return (
44-
(this.getParentConnection(topBlock)?.targetConnection?.getParentInput()
45-
?.connection as RenderedConnection) ?? topBlock
46-
);
50+
return current.workspace;
4751
}
4852

4953
/**
@@ -54,21 +58,40 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
5458
* block, or its next connection.
5559
*/
5660
getNextSibling(current: BlockSvg): INavigable<unknown> | null {
57-
const nextConnection = current.nextConnection;
58-
if (!current.outputConnection?.targetConnection && !nextConnection) {
59-
// If this block has no connected output connection and no next
60-
// connection, it must be the last block in the stack, so its next sibling
61-
// is the first block of the next stack on the workspace.
62-
const topBlocks = current.workspace.getTopBlocks(true);
63-
let targetIndex = topBlocks.indexOf(current.getRootBlock()) + 1;
64-
if (targetIndex >= topBlocks.length) {
65-
targetIndex = 0;
61+
if (current.nextConnection?.targetBlock()) {
62+
return current.nextConnection?.targetBlock();
63+
}
64+
65+
const parent = this.getParent(current);
66+
let navigatingCrossStacks = false;
67+
let siblings: (BlockSvg | Field)[] = [];
68+
if (parent instanceof BlockSvg) {
69+
for (let i = 0, input; (input = parent.inputList[i]); i++) {
70+
if (input.connection) {
71+
siblings.push(...input.fieldRow);
72+
const child = input.connection.targetBlock();
73+
if (child) {
74+
siblings.push(child as BlockSvg);
75+
}
76+
}
6677
}
67-
const previousBlock = topBlocks[targetIndex];
68-
return this.getParentConnection(previousBlock) ?? previousBlock;
78+
} else if (parent instanceof WorkspaceSvg) {
79+
siblings = parent.getTopBlocks(true);
80+
navigatingCrossStacks = true;
81+
} else {
82+
return null;
6983
}
7084

71-
return nextConnection;
85+
const currentIndex = siblings.indexOf(
86+
navigatingCrossStacks ? current.getRootBlock() : current,
87+
);
88+
if (currentIndex >= 0 && currentIndex < siblings.length - 1) {
89+
return siblings[currentIndex + 1];
90+
} else if (currentIndex === siblings.length - 1 && navigatingCrossStacks) {
91+
return siblings[0];
92+
}
93+
94+
return null;
7295
}
7396

7497
/**
@@ -79,40 +102,45 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
79102
* connection/block of the previous block stack if it is a root block.
80103
*/
81104
getPreviousSibling(current: BlockSvg): INavigable<unknown> | null {
82-
const parentConnection = this.getParentConnection(current);
83-
if (parentConnection) return parentConnection;
84-
85-
// If this block has no output/previous connection, it must be a root block,
86-
// so its previous sibling is the last connection of the last block of the
87-
// previous stack on the workspace.
88-
const topBlocks = current.workspace.getTopBlocks(true);
89-
let targetIndex = topBlocks.indexOf(current.getRootBlock()) - 1;
90-
if (targetIndex < 0) {
91-
targetIndex = topBlocks.length - 1;
105+
if (current.previousConnection?.targetBlock()) {
106+
return current.previousConnection?.targetBlock();
92107
}
93108

94-
const lastBlock = topBlocks[targetIndex]
95-
.getDescendants(true)
96-
.reverse()
97-
.pop();
109+
const parent = this.getParent(current);
110+
let navigatingCrossStacks = false;
111+
let siblings: (BlockSvg | Field)[] = [];
112+
if (parent instanceof BlockSvg) {
113+
for (let i = 0, input; (input = parent.inputList[i]); i++) {
114+
if (input.connection) {
115+
siblings.push(...input.fieldRow);
116+
const child = input.connection.targetBlock();
117+
if (child) {
118+
siblings.push(child as BlockSvg);
119+
}
120+
}
121+
}
122+
} else if (parent instanceof WorkspaceSvg) {
123+
siblings = parent.getTopBlocks(true);
124+
navigatingCrossStacks = true;
125+
} else {
126+
return null;
127+
}
98128

99-
return lastBlock?.nextConnection ?? lastBlock ?? null;
100-
}
129+
const currentIndex = siblings.indexOf(current);
130+
let result: INavigable<any> | null = null;
131+
if (currentIndex >= 1) {
132+
result = siblings[currentIndex - 1];
133+
} else if (currentIndex === 0 && navigatingCrossStacks) {
134+
result = siblings[siblings.length - 1];
135+
}
101136

102-
/**
103-
* Gets the parent connection on a block.
104-
* This is either an output connection, previous connection or undefined.
105-
* If both connections exist return the one that is actually connected
106-
* to another block.
107-
*
108-
* @param block The block to find the parent connection on.
109-
* @returns The connection connecting to the parent of the block.
110-
*/
111-
protected getParentConnection(block: BlockSvg) {
112-
if (!block.outputConnection || block.previousConnection?.isConnected()) {
113-
return block.previousConnection;
137+
// If navigating to a previous stack, our previous sibling is the last
138+
// block in it.
139+
if (navigatingCrossStacks && result instanceof BlockSvg) {
140+
return result.lastConnectionInStack(false)?.getSourceBlock() ?? result;
114141
}
115-
return block.outputConnection;
142+
143+
return result;
116144
}
117145

118146
/**

core/keyboard_nav/field_navigation_policy.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type {BlockSvg} from '../block_svg.js';
88
import type {Field} from '../field.js';
99
import type {INavigable} from '../interfaces/i_navigable.js';
1010
import type {INavigationPolicy} from '../interfaces/i_navigation_policy.js';
11-
import type {RenderedConnection} from '../rendered_connection.js';
1211

1312
/**
1413
* Set of rules controlling keyboard navigation from a field.
@@ -52,8 +51,8 @@ export class FieldNavigationPolicy implements INavigationPolicy<Field<any>> {
5251
const fieldRow = newInput.fieldRow;
5352
if (fieldIdx < fieldRow.length) return fieldRow[fieldIdx];
5453
fieldIdx = 0;
55-
if (newInput.connection) {
56-
return newInput.connection as RenderedConnection;
54+
if (newInput.connection?.targetBlock()) {
55+
return newInput.connection.targetBlock() as BlockSvg;
5756
}
5857
}
5958
return null;
@@ -74,8 +73,8 @@ export class FieldNavigationPolicy implements INavigationPolicy<Field<any>> {
7473
let fieldIdx = parentInput.fieldRow.indexOf(current) - 1;
7574
for (let i = curIdx; i >= 0; i--) {
7675
const input = block.inputList[i];
77-
if (input.connection && input !== parentInput) {
78-
return input.connection as RenderedConnection;
76+
if (input.connection?.targetBlock() && input !== parentInput) {
77+
return input.connection.targetBlock() as BlockSvg;
7978
}
8079
const fieldRow = input.fieldRow;
8180
if (fieldIdx > -1) return fieldRow[fieldIdx];

0 commit comments

Comments
 (0)