Skip to content

Commit b2d2124

Browse files
rachel-fenichelgonfunko
authored andcommitted
chore: move disconnect action to its own file (#284)
1 parent f48256e commit b2d2124

File tree

3 files changed

+142
-74
lines changed

3 files changed

+142
-74
lines changed

src/actions/disconnect.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {
8+
ASTNode,
9+
RenderedConnection,
10+
ShortcutRegistry,
11+
utils as BlocklyUtils,
12+
} from 'blockly';
13+
import * as Constants from '../constants';
14+
import type {WorkspaceSvg} from 'blockly';
15+
import {Navigation} from '../navigation';
16+
17+
const KeyCodes = BlocklyUtils.KeyCodes;
18+
19+
/**
20+
* Action to insert a block into the workspace.
21+
*
22+
* This action registers itself as both a keyboard shortcut and a context menu
23+
* item.
24+
*/
25+
export class DisconnectAction {
26+
/**
27+
* Function provided by the navigation controller to say whether editing
28+
* is allowed.
29+
*/
30+
private canCurrentlyEdit: (ws: WorkspaceSvg) => boolean;
31+
32+
/**
33+
* Registration name for the keyboard shortcut.
34+
*/
35+
private shortcutName = Constants.SHORTCUT_NAMES.DISCONNECT;
36+
37+
constructor(
38+
private navigation: Navigation,
39+
canEdit: (ws: WorkspaceSvg) => boolean,
40+
) {
41+
this.canCurrentlyEdit = canEdit;
42+
}
43+
44+
/**
45+
* Install this action as both a keyboard shortcut and a context menu item.
46+
*/
47+
install() {
48+
this.registerShortcut();
49+
}
50+
51+
/**
52+
* Uninstall this action as both a keyboard shortcut and a context menu item.
53+
* Reinstall the original context menu action if possible.
54+
*/
55+
uninstall() {
56+
ShortcutRegistry.registry.unregister(this.shortcutName);
57+
}
58+
59+
/**
60+
* Create and register the keyboard shortcut for this action.
61+
*/
62+
private registerShortcut() {
63+
const disconnectShortcut: ShortcutRegistry.KeyboardShortcut = {
64+
name: this.shortcutName,
65+
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
66+
callback: (workspace) => {
67+
switch (this.navigation.getState(workspace)) {
68+
case Constants.STATE.WORKSPACE:
69+
this.disconnectBlocks(workspace);
70+
return true;
71+
default:
72+
return false;
73+
}
74+
},
75+
keyCodes: [KeyCodes.X],
76+
};
77+
ShortcutRegistry.registry.register(disconnectShortcut);
78+
}
79+
80+
/**
81+
* Disconnects the connection that the cursor is pointing to, and bump blocks.
82+
* This is a no-op if the connection cannot be broken or if the cursor is not
83+
* pointing to a connection.
84+
*
85+
* @param workspace The workspace.
86+
*/
87+
disconnectBlocks(workspace: WorkspaceSvg) {
88+
const cursor = workspace.getCursor();
89+
if (!cursor) {
90+
return;
91+
}
92+
let curNode: ASTNode | null = cursor.getCurNode();
93+
let wasVisitingConnection = true;
94+
while (curNode && !curNode.isConnection()) {
95+
curNode = curNode.out();
96+
wasVisitingConnection = false;
97+
}
98+
if (!curNode) {
99+
console.log('Unable to find a connection to disconnect');
100+
return;
101+
}
102+
const curConnection = curNode.getLocation() as RenderedConnection;
103+
if (!curConnection.isConnected()) {
104+
return;
105+
}
106+
const superiorConnection = curConnection.isSuperior()
107+
? curConnection
108+
: curConnection.targetConnection!;
109+
110+
const inferiorConnection = curConnection.isSuperior()
111+
? curConnection.targetConnection!
112+
: curConnection;
113+
114+
if (inferiorConnection.getSourceBlock().isShadow()) {
115+
return;
116+
}
117+
118+
if (!inferiorConnection.getSourceBlock().isMovable()) {
119+
return;
120+
}
121+
122+
superiorConnection.disconnect();
123+
inferiorConnection.bumpAwayFrom(superiorConnection);
124+
125+
const rootBlock = superiorConnection.getSourceBlock().getRootBlock();
126+
rootBlock.bringToFront();
127+
128+
if (wasVisitingConnection) {
129+
const connectionNode = ASTNode.createConnectionNode(superiorConnection);
130+
workspace.getCursor()!.setCurNode(connectionNode!);
131+
}
132+
}
133+
}

src/navigation.ts

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,64 +1054,6 @@ export class Navigation {
10541054
return false;
10551055
}
10561056

1057-
/**
1058-
* Disconnects the connection that the cursor is pointing to, and bump blocks.
1059-
* This is a no-op if the connection cannot be broken or if the cursor is not
1060-
* pointing to a connection.
1061-
*
1062-
* @param workspace The workspace.
1063-
*/
1064-
disconnectBlocks(workspace: Blockly.WorkspaceSvg) {
1065-
const cursor = workspace.getCursor();
1066-
if (!cursor) {
1067-
return;
1068-
}
1069-
let curNode: Blockly.ASTNode | null = cursor.getCurNode();
1070-
let wasVisitingConnection = true;
1071-
while (curNode && !curNode.isConnection()) {
1072-
curNode = curNode.out();
1073-
wasVisitingConnection = false;
1074-
}
1075-
if (!curNode) {
1076-
this.log('Unable to find a connection to disconnect');
1077-
return;
1078-
}
1079-
const curConnection = curNode.getLocation() as Blockly.RenderedConnection;
1080-
if (!curConnection.isConnected()) {
1081-
this.log('Cannot disconnect unconnected connection');
1082-
return;
1083-
}
1084-
const superiorConnection = curConnection.isSuperior()
1085-
? curConnection
1086-
: curConnection.targetConnection!;
1087-
1088-
const inferiorConnection = curConnection.isSuperior()
1089-
? curConnection.targetConnection!
1090-
: curConnection;
1091-
1092-
if (inferiorConnection.getSourceBlock().isShadow()) {
1093-
this.log('Cannot disconnect a shadow block');
1094-
return;
1095-
}
1096-
1097-
if (!inferiorConnection.getSourceBlock().isMovable()) {
1098-
this.log('Cannot disconnect an immovable block');
1099-
return;
1100-
}
1101-
1102-
superiorConnection.disconnect();
1103-
inferiorConnection.bumpAwayFrom(superiorConnection);
1104-
1105-
const rootBlock = superiorConnection.getSourceBlock().getRootBlock();
1106-
rootBlock.bringToFront();
1107-
1108-
if (wasVisitingConnection) {
1109-
const connectionNode =
1110-
Blockly.ASTNode.createConnectionNode(superiorConnection);
1111-
workspace.getCursor()!.setCurNode(connectionNode!);
1112-
}
1113-
}
1114-
11151057
/**
11161058
* Moves the passive focus indicator to the cursor's current location.
11171059
*

src/navigation_controller.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {DeleteAction} from './actions/delete';
3131
import {InsertAction} from './actions/insert';
3232
import {Clipboard} from './actions/clipboard';
3333
import {WorkspaceMovement} from './actions/ws_movement';
34+
import {DisconnectAction} from './actions/disconnect';
3435

3536
const KeyCodes = BlocklyUtils.KeyCodes;
3637
const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind(
@@ -67,6 +68,12 @@ export class NavigationController {
6768
this.canCurrentlyEdit.bind(this),
6869
);
6970

71+
/** Keyboard shortcut for disconnection. */
72+
disconnectAction: DisconnectAction = new DisconnectAction(
73+
this.navigation,
74+
this.canCurrentlyEdit.bind(this),
75+
);
76+
7077
clipboard: Clipboard = new Clipboard(
7178
this.navigation,
7279
this.canCurrentlyEdit.bind(this),
@@ -501,22 +508,6 @@ export class NavigationController {
501508
],
502509
},
503510

504-
/** Disconnect two blocks. */
505-
disconnect: {
506-
name: Constants.SHORTCUT_NAMES.DISCONNECT,
507-
preconditionFn: (workspace) => this.canCurrentlyEdit(workspace),
508-
callback: (workspace) => {
509-
switch (this.navigation.getState(workspace)) {
510-
case Constants.STATE.WORKSPACE:
511-
this.navigation.disconnectBlocks(workspace);
512-
return true;
513-
default:
514-
return false;
515-
}
516-
},
517-
keyCodes: [KeyCodes.X],
518-
},
519-
520511
/** Move focus to or from the toolbox. */
521512
focusToolbox: {
522513
name: Constants.SHORTCUT_NAMES.TOOLBOX,
@@ -703,6 +694,7 @@ export class NavigationController {
703694
this.deleteAction.install();
704695
this.insertAction.install();
705696
this.workspaceMovement.install();
697+
this.disconnectAction.install();
706698

707699
this.clipboard.install();
708700
this.shortcutDialog.install();
@@ -723,6 +715,7 @@ export class NavigationController {
723715

724716
this.deleteAction.uninstall();
725717
this.insertAction.uninstall();
718+
this.disconnectAction.uninstall();
726719
this.clipboard.uninstall();
727720
this.workspaceMovement.uninstall();
728721
this.shortcutDialog.uninstall();

0 commit comments

Comments
 (0)