Skip to content

Commit 5d01bcc

Browse files
feat: find a connection to start a traversal for constrained drags (#389)
* feat: find a connection to start a traversal for constrained drags * chore: update todo with issue
1 parent d1a9208 commit 5d01bcc

File tree

1 file changed

+68
-27
lines changed

1 file changed

+68
-27
lines changed

src/keyboard_drag_strategy.ts

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy {
5858
this.block.moveDuringDrag(
5959
new utils.Coordinate(neighbour.x + 10, neighbour.y + 10),
6060
);
61+
} else {
62+
// Handle the case when unconstrained drag was far from any candidate.
63+
this.searchNode = null;
6164
}
6265
}
6366

@@ -71,58 +74,96 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy {
7174
*/
7275
private getConstrainedConnectionCandidate(
7376
draggingBlock: BlockSvg,
77+
): ConnectionCandidate | null {
78+
// @ts-expect-error getLocalConnections is private.
79+
const localConns = this.getLocalConnections(draggingBlock);
80+
81+
let candidateConnection = this.findTraversalCandidate(
82+
draggingBlock,
83+
localConns,
84+
);
85+
// Fall back on a coordinate-based search if there was no good starting
86+
// point for the search.
87+
if (!candidateConnection && !this.searchNode) {
88+
candidateConnection = this.findNearestCandidate(localConns);
89+
}
90+
return candidateConnection;
91+
}
92+
93+
/**
94+
* Get the nearest valid candidate connection, regardless of direction.
95+
* TODO(github.com/google/blockly/issues/8869): Replace with an
96+
* override of `getSearchRadius` when implemented in core.
97+
*
98+
* @param localConns The list of connections on the dragging block(s) that are
99+
* available to connect to.
100+
* @returns A candidate connection and radius, or null if none was found.
101+
*/
102+
findNearestCandidate(
103+
localConns: RenderedConnection[],
104+
): ConnectionCandidate | null {
105+
let radius = Infinity;
106+
let candidate = null;
107+
const dxy = new utils.Coordinate(0, 0);
108+
109+
for (const conn of localConns) {
110+
const {connection: neighbour, radius: rad} = conn.closest(radius, dxy);
111+
if (neighbour) {
112+
candidate = {
113+
local: conn,
114+
neighbour: neighbour,
115+
distance: rad,
116+
};
117+
radius = rad;
118+
}
119+
}
120+
return candidate;
121+
}
122+
123+
/**
124+
* Get the nearest valid candidate connection in traversal order.
125+
*
126+
* @param draggingBlock The root block being dragged.
127+
* @param localConns The list of connections on the dragging block(s) that are
128+
* available to connect to.
129+
* @returns A candidate connection and radius, or null if none was found.
130+
*/
131+
findTraversalCandidate(
132+
draggingBlock: BlockSvg,
133+
localConns: RenderedConnection[],
74134
): ConnectionCandidate | null {
75135
// TODO(#385): Make sure this works for any cursor, not just LineCursor.
76136
const cursor = draggingBlock.workspace.getCursor() as LineCursor;
137+
if (!cursor) return null;
77138

78-
const initialNode = this.searchNode;
79-
if (!initialNode || !cursor) return null;
80-
81-
// @ts-expect-error getLocalConnections is private.
82-
const localConns = this.getLocalConnections(draggingBlock);
83139
const connectionChecker = draggingBlock.workspace.connectionChecker;
84-
85140
let candidateConnection: ConnectionCandidate | null = null;
86-
87-
let potential: ASTNode | null = initialNode;
141+
let potential: ASTNode | null = this.searchNode;
142+
const dir = this.currentDragDirection;
88143
while (potential && !candidateConnection) {
89-
if (
90-
this.currentDragDirection === Direction.Up ||
91-
this.currentDragDirection === Direction.Left
92-
) {
144+
if (dir === Direction.Up || dir === Direction.Left) {
93145
potential = cursor.getPreviousNode(potential, (node) => {
94146
// @ts-expect-error isConnectionType is private.
95147
return node && ASTNode.isConnectionType(node.getType());
96148
});
97-
} else if (
98-
this.currentDragDirection === Direction.Down ||
99-
this.currentDragDirection === Direction.Right
100-
) {
149+
} else if (dir === Direction.Down || dir === Direction.Right) {
101150
potential = cursor.getNextNode(potential, (node) => {
102151
// @ts-expect-error isConnectionType is private.
103152
return node && ASTNode.isConnectionType(node.getType());
104153
});
105154
}
106155

107156
localConns.forEach((conn: RenderedConnection) => {
108-
const potentialLocation =
109-
potential?.getLocation() as RenderedConnection;
110-
if (
111-
connectionChecker.canConnect(conn, potentialLocation, true, Infinity)
112-
) {
157+
const location = potential?.getLocation() as RenderedConnection;
158+
if (connectionChecker.canConnect(conn, location, true, Infinity)) {
113159
candidateConnection = {
114160
local: conn,
115-
neighbour: potentialLocation,
161+
neighbour: location,
116162
distance: 0,
117163
};
118164
}
119165
});
120166
}
121-
if (candidateConnection) {
122-
this.searchNode = ASTNode.createConnectionNode(
123-
(candidateConnection as ConnectionCandidate).neighbour,
124-
);
125-
}
126167
return candidateConnection;
127168
}
128169

0 commit comments

Comments
 (0)