Skip to content

Commit 8714c2c

Browse files
gonfunkoBenHenning
andauthored
feat: Add support for acting on workspace comments and icons. (#625)
* feat: Add support for acting on workspace comments and icons. * fix: Fix compatibility with renamed CommentBarButtons. * refactor: Improve checking for ability to handle Enter. Co-authored-by: Ben Henning <[email protected]> * chore: Format. * chore: Add tests for navigating workspace comments. * feat: Add support for move mode for workspace comments. * chore: Add braces around wrapped conditional. * refactor: Clarify comment ID variable names. * refactor: Use sendKeyAndWait instead of pauses. --------- Co-authored-by: Ben Henning <[email protected]>
1 parent 4ddbac6 commit 8714c2c

File tree

8 files changed

+409
-90
lines changed

8 files changed

+409
-90
lines changed

src/actions/enter.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
icons,
1717
FocusableTreeTraverser,
1818
renderManagement,
19+
comments,
20+
getFocusManager,
1921
} from 'blockly/core';
2022

2123
import type {Block} from 'blockly/core';
@@ -124,10 +126,13 @@ export class EnterAction {
124126
) {
125127
return !workspace.isReadOnly();
126128
}
127-
if (curNode instanceof BlockSvg) return true;
128129
// Returning true is sometimes incorrect for icons, but there's no API to check.
129-
if (curNode instanceof icons.Icon) return true;
130-
return false;
130+
return (
131+
curNode instanceof BlockSvg ||
132+
curNode instanceof icons.Icon ||
133+
curNode instanceof comments.CommentBarButton ||
134+
curNode instanceof comments.RenderedWorkspaceComment
135+
);
131136
}
132137

133138
/**
@@ -167,6 +172,13 @@ export class EnterAction {
167172
});
168173
}
169174
return true;
175+
} else if (curNode instanceof comments.CommentBarButton) {
176+
curNode.performAction();
177+
return true;
178+
} else if (curNode instanceof comments.RenderedWorkspaceComment) {
179+
curNode.setCollapsed(false);
180+
getFocusManager().focusNode(curNode.getEditorFocusableNode());
181+
return true;
170182
}
171183
return false;
172184
}

src/actions/move.ts

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
*/
66

77
import {
8-
BlockSvg,
98
ContextMenuRegistry,
109
Msg,
1110
ShortcutRegistry,
1211
utils,
1312
WorkspaceSvg,
1413
keyboardNavigationController,
1514
getFocusManager,
15+
comments,
16+
IDraggable,
17+
IFocusableNode,
18+
IBoundedElement,
19+
ISelectable,
1620
} from 'blockly';
1721
import {Direction} from '../drag_direction';
1822
import {Mover} from './mover';
@@ -24,7 +28,7 @@ const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind(
2428
);
2529

2630
/**
27-
* Actions for moving blocks with keyboard shortcuts.
31+
* Actions for moving workspace elements with keyboard shortcuts.
2832
*/
2933
export class MoveActions {
3034
constructor(private mover: Mover) {}
@@ -38,19 +42,22 @@ export class MoveActions {
3842
{
3943
name: 'start_move',
4044
preconditionFn: (workspace) => {
41-
const startBlock = this.getCurrentBlock(workspace);
42-
return !!startBlock && this.mover.canMove(workspace, startBlock);
45+
const startDraggable = this.getCurrentDraggable(workspace);
46+
return (
47+
!!startDraggable && this.mover.canMove(workspace, startDraggable)
48+
);
4349
},
4450
callback: (workspace) => {
4551
keyboardNavigationController.setIsActive(true);
46-
const startBlock = this.getCurrentBlock(workspace);
47-
// Focus the start block in case one of its fields or a shadow block
52+
const startDraggable = this.getCurrentDraggable(workspace);
53+
// Focus the root draggable in case one of its children
4854
// was focused when the move was triggered.
49-
if (startBlock) {
50-
getFocusManager().focusNode(startBlock);
55+
if (startDraggable) {
56+
getFocusManager().focusNode(startDraggable);
5157
}
5258
return (
53-
!!startBlock && this.mover.startMove(workspace, startBlock, null)
59+
!!startDraggable &&
60+
this.mover.startMove(workspace, startDraggable, null)
5461
);
5562
},
5663
keyCodes: [KeyCodes.M],
@@ -162,28 +169,52 @@ export class MoveActions {
162169
if (!workspace || menuOpenEvent instanceof PointerEvent)
163170
return 'hidden';
164171

165-
const startBlock = this.getCurrentBlock(workspace);
166-
return !!startBlock && this.mover.canMove(workspace, startBlock)
172+
const startDraggable = this.getCurrentDraggable(workspace);
173+
return !!startDraggable &&
174+
this.mover.canMove(workspace, startDraggable)
167175
? 'enabled'
168176
: 'disabled';
169177
},
170178
callback: (scope) => {
171179
const workspace = scope.block?.workspace as WorkspaceSvg | null;
172180
if (!workspace) return false;
173-
const startBlock = this.getCurrentBlock(workspace);
181+
const startDraggable = this.getCurrentDraggable(workspace);
174182
// Focus the start block in case one of its fields or a shadow block
175183
// was focused when the move was triggered.
176-
if (startBlock) {
177-
getFocusManager().focusNode(startBlock);
184+
if (startDraggable) {
185+
getFocusManager().focusNode(startDraggable);
178186
}
179187
return (
180-
!!startBlock && this.mover.startMove(workspace, startBlock, null)
188+
!!startDraggable &&
189+
this.mover.startMove(workspace, startDraggable, null)
181190
);
182191
},
183192
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
184193
id: 'move',
185194
weight: 8.5,
186195
},
196+
{
197+
displayText: getMenuItem(
198+
Msg['MOVE_COMMENT'] ?? 'Move Comment',
199+
'start_move',
200+
),
201+
preconditionFn: (scope, menuOpenEvent) => {
202+
const comment = scope.comment;
203+
if (!comment) return 'hidden';
204+
205+
return this.mover.canMove(comment.workspace, comment)
206+
? 'enabled'
207+
: 'disabled';
208+
},
209+
callback: (scope) => {
210+
const comment = scope.comment;
211+
if (!comment) return false;
212+
this.mover.startMove(comment.workspace, comment, null);
213+
},
214+
scopeType: ContextMenuRegistry.ScopeType.COMMENT,
215+
id: 'move_comment',
216+
weight: 8.5,
217+
},
187218
];
188219

189220
for (const menuItem of menuItems) {
@@ -214,16 +245,21 @@ export class MoveActions {
214245
}
215246

216247
/**
217-
* Get the source block for the cursor location, or undefined if no
218-
* source block can be found.
248+
* Get the source draggable for the cursor location, or undefined if no
249+
* source draggable can be found.
219250
* If the cursor is on a shadow block, walks up the tree until it finds
220251
* a non-shadow block to drag.
221252
*
222253
* @param workspace The workspace to inspect for a cursor.
223-
* @returns The source block, or undefined if no appropriate block
254+
* @returns The source draggable, or undefined if no appropriate draggable
224255
* could be found.
225256
*/
226-
getCurrentBlock(workspace: WorkspaceSvg): BlockSvg | undefined {
257+
getCurrentDraggable(
258+
workspace: WorkspaceSvg,
259+
): (IDraggable & IFocusableNode & IBoundedElement & ISelectable) | undefined {
260+
const node = getFocusManager().getFocusedNode();
261+
if (node instanceof comments.RenderedWorkspaceComment) return node;
262+
227263
let block = workspace?.getCursor()?.getSourceBlock();
228264
if (!block) return undefined;
229265
while (block.isShadow()) {

0 commit comments

Comments
 (0)