Skip to content

Commit 7f75eaa

Browse files
Merge branch 'insert-connection-april-ut' into april-ut
2 parents 98e2451 + 81a0626 commit 7f75eaa

File tree

4 files changed

+108
-48
lines changed

4 files changed

+108
-48
lines changed

src/actions/enter.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,10 @@ export class EnterAction {
133133
const stationaryNode = this.navigation.getStationaryNode(workspace);
134134
const newBlock = this.createNewBlock(workspace);
135135
if (!newBlock) return;
136-
if (stationaryNode) {
137-
if (!this.navigation.tryToConnectBlock(stationaryNode, newBlock)) {
138-
console.warn(
139-
'Something went wrong while inserting a block from the flyout.',
140-
);
141-
}
142-
}
143136

137+
const insertStartPoint = stationaryNode
138+
? this.navigation.findInsertStartPoint(stationaryNode, newBlock)
139+
: null;
144140
if (workspace.getTopBlocks().includes(newBlock)) {
145141
this.positionNewTopLevelBlock(workspace, newBlock);
146142
}
@@ -151,7 +147,7 @@ export class EnterAction {
151147
this.navigation.focusWorkspace(workspace);
152148
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
153149
workspace.getCursor()?.setCurNode(ASTNode.createBlockNode(newBlock)!);
154-
this.mover.startMove(workspace);
150+
this.mover.startMove(workspace, insertStartPoint);
155151

156152
const isTopLevelBlock =
157153
!newBlock.outputConnection &&

src/actions/mover.ts

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

7-
import type {BlockSvg, IDragger, IDragStrategy, Gesture} from 'blockly';
7+
import type {
8+
BlockSvg,
9+
IDragger,
10+
IDragStrategy,
11+
Gesture,
12+
RenderedConnection,
13+
} from 'blockly';
814
import {
915
ASTNode,
1016
common,
1117
Connection,
18+
Events,
1219
registry,
1320
utils,
1421
WorkspaceSvg,
@@ -101,9 +108,14 @@ export class Mover {
101108
* Should only be called if canMove has returned true.
102109
*
103110
* @param workspace The workspace we might be moving on.
111+
* @param insertStartPoint The starting point for the move in the insert case,
112+
* when the block should be deleted if aborted rather than reverted.
104113
* @returns True iff a move has successfully begun.
105114
*/
106-
startMove(workspace: WorkspaceSvg) {
115+
startMove(
116+
workspace: WorkspaceSvg,
117+
insertStartPoint: RenderedConnection | null = null,
118+
) {
107119
const cursor = workspace?.getCursor();
108120
const block = this.getCurrentBlock(workspace);
109121
if (!cursor || !block) throw new Error('precondition failure');
@@ -114,7 +126,7 @@ export class Mover {
114126
cursor.setCurNode(ASTNode.createBlockNode(block));
115127

116128
this.patchWorkspace(workspace);
117-
this.patchDragStrategy(block);
129+
this.patchDragStrategy(block, insertStartPoint);
118130
// Begin dragging block.
119131
const DraggerClass = registry.getClassFromOptions(
120132
registry.Type.BLOCK_DRAGGER,
@@ -154,6 +166,7 @@ export class Mover {
154166
this.unpatchWorkspace(workspace);
155167
this.unpatchDragStrategy(info.block);
156168
this.moves.delete(workspace);
169+
157170
return true;
158171
}
159172

@@ -172,17 +185,31 @@ export class Mover {
172185
const info = this.moves.get(workspace);
173186
if (!info) throw new Error('no move info for workspace');
174187

175-
// Monkey patch dragger to trigger call to draggable.revertDrag.
188+
// If it's an insert-style move then we delete the block.
189+
const keyboardDragStrategy =
190+
info.block.getDragStrategy() as KeyboardDragStrategy;
191+
const {insertStartPoint} = keyboardDragStrategy;
192+
193+
// Monkey patch dragger to trigger delete.
176194
// eslint-disable-next-line @typescript-eslint/no-explicit-any
177-
(info.dragger as any).shouldReturnToStart = () => true;
195+
(info.dragger as any).wouldDeleteDraggable = () => !!insertStartPoint;
196+
if (!insertStartPoint) {
197+
// Monkey patch dragger to trigger call to draggable.revertDrag.
198+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
199+
(info.dragger as any).shouldReturnToStart = () => true;
200+
}
178201
const blockSvg = info.block;
179202

180203
// Explicitly call `hidePreview` because it is not called in revertDrag.
181204
// @ts-expect-error Access to private property dragStrategy.
182205
blockSvg.dragStrategy.connectionPreviewer.hidePreview();
206+
// Prevent the stragegy connecting the block so we just delete one block.
207+
// @ts-expect-error Access to private property dragStrategy.
208+
blockSvg.dragStrategy.connectionCandidate = null;
209+
183210
info.dragger.onDragEnd(
184211
info.fakePointerEvent('pointerup'),
185-
new utils.Coordinate(0, 0),
212+
info.startLocation,
186213
);
187214

188215
this.unpatchWorkspace(workspace);
@@ -302,11 +329,16 @@ export class Mover {
302329
* Monkeypatch: replace the block's drag strategy and cache the old value.
303330
*
304331
* @param block The block to patch.
332+
* @param insertStartPoint The starting point for the move in the insert case,
333+
* when the block should be deleted if aborted rather than reverted.
305334
*/
306-
private patchDragStrategy(block: BlockSvg) {
335+
private patchDragStrategy(
336+
block: BlockSvg,
337+
insertStartPoint: RenderedConnection | null,
338+
) {
307339
// @ts-expect-error block.dragStrategy is private.
308340
this.oldDragStrategy = block.dragStrategy;
309-
block.setDragStrategy(new KeyboardDragStrategy(block));
341+
block.setDragStrategy(new KeyboardDragStrategy(block, insertStartPoint));
310342
}
311343

312344
/**

src/keyboard_drag_strategy.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy {
3636
/** Where a constrained movement should start when traversing the tree. */
3737
private searchNode: ASTNode | null = null;
3838

39+
constructor(
40+
block: BlockSvg,
41+
public insertStartPoint: RenderedConnection | null,
42+
) {
43+
super(block);
44+
}
45+
3946
override startDrag(e?: PointerEvent) {
4047
super.startDrag(e);
4148
// Set position of the dragging block, so that it doesn't pop
@@ -269,7 +276,7 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy {
269276
*/
270277
private createInitialCandidate(): ConnectionCandidate | null {
271278
// @ts-expect-error startParentConn is private.
272-
const neighbour = this.startParentConn;
279+
const neighbour = this.insertStartPoint ?? this.startParentConn;
273280
if (neighbour) {
274281
this.searchNode = ASTNode.createConnectionNode(neighbour);
275282
switch (neighbour.type) {

src/navigation.ts

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,8 @@ export class Navigation {
404404
const passiveFocusNode = this.passiveFocusIndicator.getCurNode();
405405
this.passiveFocusIndicator.hide();
406406
const disposed = passiveFocusNode?.getSourceBlock()?.disposed;
407-
// If there's a gesture then it will either set the node if it has not
408-
// been disposed (which can happen when blocks are reloaded) or be a click
407+
// If there's a gesture then it will either set the node if it has not
408+
// been disposed (which can happen when blocks are reloaded) or be a click
409409
// that should not set one.
410410
if (!Blockly.Gesture.inProgress() && passiveFocusNode && !disposed) {
411411
cursor.setCurNode(passiveFocusNode);
@@ -662,25 +662,25 @@ export class Navigation {
662662
}
663663

664664
/**
665-
* Tries to intelligently connect the blocks or connections
666-
* represented by the given nodes, based on node types and locations.
665+
* Finds the starting point for an insert.
667666
*
668-
* @param stationaryNode The first node to connect.
667+
* @param stationaryNode The first node to find a connection for.
669668
* @param movingBlock The block we're moving.
670-
* @returns True if the key was handled; false if something went
671-
* wrong.
669+
* @returns The initial connection to use or begin move mode at.
672670
*/
673-
tryToConnectBlock(
671+
findInsertStartPoint(
674672
stationaryNode: Blockly.ASTNode,
675673
movingBlock: Blockly.BlockSvg,
676-
): boolean {
674+
): Blockly.RenderedConnection | null {
677675
const stationaryType = stationaryNode.getType();
678676
const stationaryLoc = stationaryNode.getLocation();
677+
const movingHasOutput = !!movingBlock.outputConnection;
679678

680679
if (stationaryNode.getType() === Blockly.ASTNode.types.FIELD) {
680+
// Can't connect a block to a field, so try going up to the source block.
681681
const sourceBlock = stationaryNode.getSourceBlock();
682-
if (!sourceBlock) return false;
683-
return this.tryToConnectBlock(
682+
if (!sourceBlock) return null;
683+
return this.findInsertStartPoint(
684684
Blockly.ASTNode.createBlockNode(sourceBlock),
685685
movingBlock,
686686
);
@@ -691,27 +691,27 @@ export class Navigation {
691691
// Move to the block if we're trying to insert a statement block into
692692
// a value connection.
693693
if (
694-
!movingBlock.outputConnection &&
694+
!movingHasOutput &&
695695
stationaryAsConnection.type === Blockly.ConnectionType.INPUT_VALUE
696696
) {
697697
const sourceBlock = stationaryNode.getSourceBlock();
698-
if (!sourceBlock) return false;
699-
return this.tryToConnectBlock(
698+
if (!sourceBlock) return null;
699+
return this.findInsertStartPoint(
700700
Blockly.ASTNode.createBlockNode(sourceBlock),
701701
movingBlock,
702702
);
703703
}
704704

705705
// Connect the moving block to the stationary connection using
706706
// the most plausible connection on the moving block.
707-
return this.insertBlock(movingBlock, stationaryAsConnection);
707+
return stationaryAsConnection;
708708
} else if (stationaryType === Blockly.ASTNode.types.WORKSPACE) {
709-
return this.moveBlockToWorkspace(movingBlock, stationaryNode);
709+
return null;
710710
} else if (stationaryType === Blockly.ASTNode.types.BLOCK) {
711711
const stationaryBlock = stationaryLoc as Blockly.BlockSvg;
712712

713713
// 1. Connect blocks to first compatible input
714-
const inputType = movingBlock.outputConnection
714+
const inputType = movingHasOutput
715715
? Blockly.inputs.inputTypes.VALUE
716716
: Blockly.inputs.inputTypes.STATEMENT;
717717
const compatibleInputs = stationaryBlock.inputList.filter(
@@ -726,36 +726,61 @@ export class Navigation {
726726
connection = connection.targetBlock()!.nextConnection!;
727727
}
728728
}
729-
return this.insertBlock(
730-
movingBlock,
731-
connection as Blockly.RenderedConnection,
732-
);
729+
return connection as Blockly.RenderedConnection;
733730
}
734731

735732
// 2. Connect statement blocks to next connection.
736-
if (stationaryBlock.nextConnection && !movingBlock.outputConnection) {
737-
return this.insertBlock(movingBlock, stationaryBlock.nextConnection);
733+
if (stationaryBlock.nextConnection && !movingHasOutput) {
734+
return stationaryBlock.nextConnection;
738735
}
739736

740737
// 3. Output connection. This will wrap around or displace.
741738
if (stationaryBlock.outputConnection) {
742-
// Move to parent if we're trying to insert a statement block.
743-
if (
744-
!movingBlock.outputConnection &&
739+
// Try to wrap.
740+
const target = stationaryBlock.outputConnection.targetConnection;
741+
if (movingHasOutput && target) {
742+
const sourceNode = Blockly.ASTNode.createConnectionNode(target);
743+
if (sourceNode) {
744+
return this.findInsertStartPoint(sourceNode, movingBlock);
745+
}
746+
} else if (
747+
!movingHasOutput &&
745748
stationaryNode.getType() === Blockly.ASTNode.types.BLOCK
746749
) {
750+
// Move to parent if we're trying to insert a statement block.
747751
const parent = stationaryNode.getSourceBlock()?.getParent();
748-
if (!parent) return false;
749-
return this.tryToConnectBlock(
752+
if (!parent) return null;
753+
return this.findInsertStartPoint(
750754
Blockly.ASTNode.createBlockNode(parent),
751755
movingBlock,
752756
);
753757
}
754-
return this.insertBlock(movingBlock, stationaryBlock.outputConnection);
758+
return stationaryBlock.outputConnection;
755759
}
756760
}
757-
this.warn(`Unexpected case in tryToConnectBlock ${stationaryType}.`);
758-
return false;
761+
this.warn(`Unexpected case in findInsertStartPoint ${stationaryType}.`);
762+
return null;
763+
}
764+
765+
/**
766+
* Tries to intelligently connect the blocks or connections
767+
* represented by the given nodes, based on node types and locations.
768+
*
769+
* @param stationaryNode The first node to connect.
770+
* @param movingBlock The block we're moving.
771+
* @returns True if the key was handled; false if something went
772+
* wrong.
773+
*/
774+
tryToConnectBlock(
775+
stationaryNode: Blockly.ASTNode,
776+
movingBlock: Blockly.BlockSvg,
777+
): boolean {
778+
const destConnection = this.findInsertStartPoint(
779+
stationaryNode,
780+
movingBlock,
781+
);
782+
if (!destConnection) return false;
783+
return this.insertBlock(movingBlock, destConnection);
759784
}
760785

761786
/**

0 commit comments

Comments
 (0)