Skip to content

Commit 0fda717

Browse files
authored
Merge branch 'main' into mutator-nav
2 parents f5e18d6 + 8e2b18e commit 0fda717

File tree

16 files changed

+331
-72
lines changed

16 files changed

+331
-72
lines changed

src/actions/action_menu.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class ActionMenu {
5252
);
5353
},
5454
callback: (workspace) => {
55-
switch (this.navigation.getState(workspace)) {
55+
switch (this.navigation.getState()) {
5656
case Constants.STATE.WORKSPACE:
5757
case Constants.STATE.FLYOUT:
5858
return this.openActionMenu(workspace);

src/actions/arrow_navigation.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class ArrowNavigation {
6363
? workspace.targetWorkspace?.getFlyout()
6464
: workspace.getFlyout();
6565
let isHandled = false;
66-
switch (this.navigation.getState(workspace)) {
66+
switch (this.navigation.getState()) {
6767
case Constants.STATE.WORKSPACE:
6868
isHandled = this.fieldShortcutHandler(workspace, shortcut);
6969
if (!isHandled && workspace) {
@@ -79,7 +79,6 @@ export class ArrowNavigation {
7979
// @ts-expect-error private method
8080
isHandled = toolbox && toolbox.selectChild();
8181
if (!isHandled && flyout) {
82-
Blockly.getFocusManager().focusTree(flyout.getWorkspace());
8382
this.navigation.defaultFlyoutCursorIfNeeded(workspace);
8483
}
8584
return true;
@@ -97,7 +96,7 @@ export class ArrowNavigation {
9796
? workspace.targetWorkspace?.getToolbox()
9897
: workspace.getToolbox();
9998
let isHandled = false;
100-
switch (this.navigation.getState(workspace)) {
99+
switch (this.navigation.getState()) {
101100
case Constants.STATE.WORKSPACE:
102101
isHandled = this.fieldShortcutHandler(workspace, shortcut);
103102
if (!isHandled && workspace) {
@@ -161,7 +160,7 @@ export class ArrowNavigation {
161160
callback: (workspace, e, shortcut) => {
162161
keyboardNavigationController.setIsActive(true);
163162
let isHandled = false;
164-
switch (this.navigation.getState(workspace)) {
163+
switch (this.navigation.getState()) {
165164
case Constants.STATE.WORKSPACE:
166165
isHandled = this.fieldShortcutHandler(workspace, shortcut);
167166
if (!isHandled && workspace) {
@@ -223,7 +222,7 @@ export class ArrowNavigation {
223222
callback: (workspace, e, shortcut) => {
224223
keyboardNavigationController.setIsActive(true);
225224
let isHandled = false;
226-
switch (this.navigation.getState(workspace)) {
225+
switch (this.navigation.getState()) {
227226
case Constants.STATE.WORKSPACE:
228227
isHandled = this.fieldShortcutHandler(workspace, shortcut);
229228
if (!isHandled) {

src/actions/clipboard.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ export class Clipboard {
285285
!!this.oldCopyShortcut?.callback &&
286286
this.oldCopyShortcut.callback(workspace, e, shortcut, scope);
287287
if (didCopy) {
288-
this.copyWorkspace = workspace;
288+
this.copyWorkspace = workspace.isFlyout
289+
? workspace.targetWorkspace
290+
: workspace;
289291
showCopiedHint(workspace);
290292
}
291293
return didCopy;

src/actions/disconnect.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {
99
Events,
1010
ShortcutRegistry,
1111
utils as BlocklyUtils,
12-
Connection,
13-
ConnectionType,
1412
keyboardNavigationController,
1513
} from 'blockly';
1614
import * as Constants from '../constants';
@@ -58,7 +56,7 @@ export class DisconnectAction {
5856
this.navigation.canCurrentlyEdit(workspace),
5957
callback: (workspace) => {
6058
keyboardNavigationController.setIsActive(true);
61-
switch (this.navigation.getState(workspace)) {
59+
switch (this.navigation.getState()) {
6260
case Constants.STATE.WORKSPACE:
6361
this.disconnectBlocks(workspace);
6462
return true;

src/actions/enter.ts

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66

77
import {
88
Events,
9-
Msg,
109
ShortcutRegistry,
1110
utils as BlocklyUtils,
12-
getFocusManager,
1311
BlockSvg,
1412
FlyoutButton,
1513
RenderedConnection,
@@ -55,33 +53,48 @@ export class EnterAction {
5553
*/
5654
ShortcutRegistry.registry.register({
5755
name: Constants.SHORTCUT_NAMES.EDIT_OR_CONFIRM,
58-
preconditionFn: (workspace) =>
59-
this.navigation.canCurrentlyEdit(workspace),
60-
callback: (workspace, event) => {
56+
preconditionFn: (workspace): boolean => {
57+
switch (this.navigation.getState()) {
58+
case Constants.STATE.WORKSPACE:
59+
return this.shouldHandleEnterForWS(workspace);
60+
case Constants.STATE.FLYOUT: {
61+
// If we're in the flyout the only supported actions are inserting
62+
// blocks or clicking buttons, so don't handle this if the
63+
// main workspace is read only.
64+
const targetWorkspace = workspace.isFlyout
65+
? workspace.targetWorkspace
66+
: workspace;
67+
return !!targetWorkspace && !targetWorkspace.isReadOnly();
68+
}
69+
default:
70+
return false;
71+
}
72+
},
73+
callback: (workspace, event): boolean => {
6174
event.preventDefault();
6275

76+
const targetWorkspace = workspace.isFlyout
77+
? workspace.targetWorkspace
78+
: workspace;
79+
if (!targetWorkspace) return false;
80+
6381
let flyoutCursor;
6482
let curNode;
6583

66-
switch (this.navigation.getState(workspace)) {
84+
switch (this.navigation.getState()) {
6785
case Constants.STATE.WORKSPACE:
68-
this.handleEnterForWS(workspace);
69-
return true;
86+
return this.handleEnterForWS(workspace);
7087
case Constants.STATE.FLYOUT:
71-
if (!workspace.targetWorkspace) return false;
72-
flyoutCursor = this.navigation.getFlyoutCursor(
73-
workspace.targetWorkspace,
74-
);
88+
flyoutCursor = this.navigation.getFlyoutCursor(targetWorkspace);
7589
if (!flyoutCursor) {
7690
return false;
7791
}
7892
curNode = flyoutCursor.getCurNode();
7993
if (curNode instanceof BlockSvg) {
80-
this.insertFromFlyout(workspace.targetWorkspace);
94+
this.insertFromFlyout(targetWorkspace);
8195
} else if (curNode instanceof FlyoutButton) {
82-
this.triggerButtonCallback(workspace);
96+
this.triggerButtonCallback(targetWorkspace);
8397
}
84-
8598
return true;
8699
default:
87100
return false;
@@ -91,33 +104,61 @@ export class EnterAction {
91104
});
92105
}
93106

107+
/**
108+
* Checks if the enter key should do anything for this ws.
109+
*
110+
* @param workspace The workspace to check.
111+
* @returns True if the enter action should be handled.
112+
*/
113+
private shouldHandleEnterForWS(workspace: WorkspaceSvg): boolean {
114+
const cursor = workspace.getCursor();
115+
const curNode = cursor?.getCurNode();
116+
if (!curNode) return false;
117+
if (curNode instanceof Field) return curNode.isClickable();
118+
if (
119+
curNode instanceof RenderedConnection ||
120+
curNode instanceof WorkspaceSvg
121+
) {
122+
return !workspace.isReadOnly();
123+
}
124+
if (curNode instanceof BlockSvg) return true;
125+
// Returning true is sometimes incorrect for icons, but there's no API to check.
126+
if (curNode instanceof icons.Icon) return true;
127+
return false;
128+
}
129+
94130
/**
95131
* Handles hitting the enter key on the workspace.
96132
*
97133
* @param workspace The workspace.
134+
* @returns True if the enter was handled, false otherwise.
98135
*/
99-
private handleEnterForWS(workspace: WorkspaceSvg) {
136+
private handleEnterForWS(workspace: WorkspaceSvg): boolean {
100137
const cursor = workspace.getCursor();
101-
if (!cursor) return;
102-
const curNode = cursor.getCurNode();
103-
if (!curNode) return;
138+
const curNode = cursor?.getCurNode();
139+
if (!curNode) return false;
104140
if (curNode instanceof Field) {
105141
curNode.showEditor();
142+
return true;
106143
} else if (curNode instanceof BlockSvg) {
107144
if (!this.tryShowFullBlockFieldEditor(curNode)) {
108145
showHelpHint(workspace);
109146
}
147+
return true;
110148
} else if (
111149
curNode instanceof RenderedConnection ||
112150
curNode instanceof WorkspaceSvg
113151
) {
114152
this.navigation.openToolboxOrFlyout(workspace);
153+
return true;
115154
} else if (curNode instanceof icons.Icon) {
116155
curNode.onClick();
117156
renderManagement.finishQueuedRenders().then(() => {
118157
cursor.in();
119158
});
159+
return true;
120160
}
161+
return false;
121162
}
122163

123164
/**
@@ -150,8 +191,6 @@ export class EnterAction {
150191

151192
workspace.setResizesEnabled(true);
152193

153-
getFocusManager().focusTree(workspace);
154-
workspace.getCursor()?.setCurNode(newBlock);
155194
this.mover.startMove(workspace, newBlock, insertStartPoint);
156195

157196
const isStartBlock =

src/actions/exit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class ExitAction {
3232
preconditionFn: (workspace) =>
3333
this.navigation.canCurrentlyNavigate(workspace),
3434
callback: (workspace) => {
35-
switch (this.navigation.getState(workspace)) {
35+
switch (this.navigation.getState()) {
3636
case Constants.STATE.FLYOUT:
3737
case Constants.STATE.TOOLBOX:
3838
getFocusManager().focusTree(workspace.targetWorkspace ?? workspace);

src/actions/mover.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class Mover {
8585
*/
8686
canMove(workspace: WorkspaceSvg, block: BlockSvg) {
8787
return !!(
88-
this.navigation.getState(workspace) === Constants.STATE.WORKSPACE &&
88+
this.navigation.getState() === Constants.STATE.WORKSPACE &&
8989
this.navigation.canCurrentlyEdit(workspace) &&
9090
!this.moves.has(workspace) && // No move in progress.
9191
block?.isMovable()
@@ -143,8 +143,9 @@ export class Mover {
143143
dragger.onDragStart(info.fakePointerEvent('pointerdown'));
144144
info.updateTotalDelta();
145145
// In case the block is detached, ensure that it still retains focus
146-
// (otherwise dragging will break).
147-
getFocusManager().focusNode(block);
146+
// (otherwise dragging will break). This is also the point a new block's
147+
// initial insert position is scrolled into view.
148+
workspace.getCursor()?.setCurNode(block);
148149
block.getFocusableElement().addEventListener('blur', blurListener);
149150

150151
// Register a keyboard shortcut under the key combos of all existing
@@ -301,7 +302,7 @@ export class Mover {
301302

302303
info.dragger.onDrag(
303304
info.fakePointerEvent('pointermove', direction),
304-
info.totalDelta,
305+
info.totalDelta.clone().scale(workspace.scale),
305306
);
306307

307308
info.updateTotalDelta();
@@ -326,7 +327,10 @@ export class Mover {
326327
info.totalDelta.x += x * UNCONSTRAINED_MOVE_DISTANCE * workspace.scale;
327328
info.totalDelta.y += y * UNCONSTRAINED_MOVE_DISTANCE * workspace.scale;
328329

329-
info.dragger.onDrag(info.fakePointerEvent('pointermove'), info.totalDelta);
330+
info.dragger.onDrag(
331+
info.fakePointerEvent('pointermove'),
332+
info.totalDelta.clone().scale(workspace.scale),
333+
);
330334
this.scrollCurrentBlockIntoView(workspace);
331335
return true;
332336
}

src/actions/ws_movement.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind(
1818
ShortcutRegistry.registry,
1919
);
2020

21-
/**
22-
* The distance to move the cursor when the cursor is on the workspace.
23-
*/
24-
const WS_MOVE_DISTANCE = 40;
25-
2621
/**
2722
* Logic for free movement of the cursor on the workspace with keyboard
2823
* shortcuts.
@@ -68,11 +63,16 @@ export class WorkspaceMovement {
6863
/** Move the cursor to the workspace. */
6964
{
7065
name: Constants.SHORTCUT_NAMES.CREATE_WS_CURSOR,
71-
preconditionFn: (workspace) =>
72-
this.navigation.canCurrentlyEdit(workspace),
66+
preconditionFn: (workspace) => {
67+
return true;
68+
},
7369
callback: (workspace) => {
70+
const targetWorkspace = workspace.isFlyout
71+
? workspace.targetWorkspace
72+
: workspace;
73+
if (!targetWorkspace) return false;
7474
keyboardNavigationController.setIsActive(true);
75-
return this.createWSCursor(workspace);
75+
return this.createWSCursor(targetWorkspace);
7676
},
7777
keyCodes: [KeyCodes.W],
7878
},

src/move_indicator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class MoveIndicatorBubble
5353
Blockly.utils.Svg.PATH,
5454
{
5555
'fill': 'none',
56-
'stroke': 'currentColor',
56+
'stroke': 'black',
5757
'stroke-linecap': 'round',
5858
'stroke-linejoin': 'round',
5959
'stroke-width': '2',

src/navigation.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,9 @@ export class Navigation {
9393
* Note that this assumes a workspace with passive focus (including for its
9494
* toolbox or flyout) has a state of NOWHERE.
9595
*
96-
* @param workspace The workspace to get the state of.
9796
* @returns The state of the given workspace.
9897
*/
99-
getState(workspace: Blockly.WorkspaceSvg): Constants.STATE {
98+
getState(): Constants.STATE {
10099
const focusedTree = Blockly.getFocusManager().getFocusedTree();
101100
if (focusedTree instanceof Blockly.WorkspaceSvg) {
102101
if (focusedTree.isFlyout) {
@@ -105,9 +104,7 @@ export class Navigation {
105104
return Constants.STATE.WORKSPACE;
106105
}
107106
} else if (focusedTree instanceof Blockly.Toolbox) {
108-
if (workspace === focusedTree.getWorkspace()) {
109-
return Constants.STATE.TOOLBOX;
110-
}
107+
return Constants.STATE.TOOLBOX;
111108
} else if (focusedTree instanceof Blockly.Flyout) {
112109
return Constants.STATE.FLYOUT;
113110
}
@@ -222,7 +219,7 @@ export class Navigation {
222219
}
223220
} else if (
224221
e.type === Blockly.Events.BLOCK_CREATE &&
225-
this.getState(mainWorkspace) === Constants.STATE.FLYOUT
222+
this.getState() === Constants.STATE.FLYOUT
226223
) {
227224
// When variables are created, that recreates the flyout contents, leaving the
228225
// cursor in an invalid state.
@@ -306,8 +303,15 @@ export class Navigation {
306303

307304
const curNode = flyoutCursor.getCurNode();
308305
const sourceBlock = flyoutCursor.getSourceBlock();
309-
if (curNode && !this.isFlyoutItemDisposed(curNode, sourceBlock))
306+
// If the current node is a child of the flyout, nothing needs to be done.
307+
if (
308+
curNode &&
309+
curNode !== flyout.getWorkspace() &&
310+
curNode.getFocusableTree() === flyout.getWorkspace() &&
311+
!this.isFlyoutItemDisposed(curNode, sourceBlock)
312+
) {
310313
return false;
314+
}
311315

312316
const flyoutContents = flyout.getContents();
313317
const defaultFlyoutItem =
@@ -828,7 +832,7 @@ export class Navigation {
828832
).keyboardAccessibilityMode;
829833
return (
830834
!!accessibilityMode &&
831-
this.getState(workspace) !== Constants.STATE.NOWHERE &&
835+
this.getState() !== Constants.STATE.NOWHERE &&
832836
!Blockly.getFocusManager().ephemeralFocusTaken()
833837
);
834838
}

0 commit comments

Comments
 (0)