Skip to content

Commit fe651dc

Browse files
feat: add block copy as context menu item (#166)
* feat: add block copy as context menu item * chore: reviewer-suggested code cleanup Co-authored-by: Christopher Allen <[email protected]> * chore: reviewer-suggested code cleanup Co-authored-by: Christopher Allen <[email protected]> --------- Co-authored-by: Christopher Allen <[email protected]>
1 parent a250e90 commit fe651dc

File tree

1 file changed

+90
-40
lines changed

1 file changed

+90
-40
lines changed

src/navigation_controller.ts

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export class NavigationController {
239239
protected deletePreconditionFn(workspace: WorkspaceSvg) {
240240
if (!this.canCurrentlyEdit(workspace)) return false;
241241
const sourceBlock = workspace.getCursor()?.getCurNode().getSourceBlock();
242-
return !!(sourceBlock?.isDeletable());
242+
return !!sourceBlock?.isDeletable();
243243
}
244244

245245
/**
@@ -275,6 +275,67 @@ export class NavigationController {
275275
return true;
276276
}
277277

278+
/**
279+
* Precondition function for copying a block from keyboard
280+
* navigation. This precondition is shared between keyboard shortcuts
281+
* and context menu items.
282+
*
283+
* FIXME: This should be better encapsulated.
284+
*
285+
* @param workspace The `WorkspaceSvg` where the shortcut was
286+
* invoked.
287+
* @returns True iff `deleteCallbackFn` function should be called.
288+
*/
289+
protected blockCopyPreconditionFn(workspace: WorkspaceSvg) {
290+
if (!this.canCurrentlyEdit(workspace)) return false;
291+
switch (this.navigation.getState(workspace)) {
292+
case Constants.STATE.WORKSPACE:
293+
const curNode = workspace?.getCursor()?.getCurNode();
294+
const source = curNode?.getSourceBlock();
295+
return !!(
296+
source?.isDeletable() &&
297+
source?.isMovable() &&
298+
!Blockly.Gesture.inProgress()
299+
);
300+
case Constants.STATE.FLYOUT:
301+
const flyoutWorkspace = workspace.getFlyout()?.getWorkspace();
302+
const sourceBlock = flyoutWorkspace
303+
?.getCursor()
304+
?.getCurNode()
305+
?.getSourceBlock();
306+
return !!(sourceBlock && !Blockly.Gesture.inProgress());
307+
default:
308+
return false;
309+
}
310+
}
311+
312+
/**
313+
* Callback function for copying a block from keyboard
314+
* navigation. This callback is shared between keyboard shortcuts
315+
* and context menu items.
316+
*
317+
* FIXME: This should be better encapsulated.
318+
*
319+
* @param workspace The `WorkspaceSvg` where the shortcut was
320+
* invoked.
321+
* @returns True if this function successfully handled copying.
322+
*/
323+
protected blockCopyCallbackFn(workspace: WorkspaceSvg) {
324+
const navigationState = this.navigation.getState(workspace);
325+
let activeWorkspace: Blockly.WorkspaceSvg | undefined = workspace;
326+
if (navigationState == Constants.STATE.FLYOUT) {
327+
activeWorkspace = workspace.getFlyout()?.getWorkspace();
328+
}
329+
const sourceBlock = activeWorkspace
330+
?.getCursor()
331+
?.getCurNode()
332+
.getSourceBlock() as BlockSvg;
333+
workspace.hideChaff();
334+
this.copyData = sourceBlock.toCopyData();
335+
this.copyWorkspace = sourceBlock.workspace;
336+
return !!this.copyData;
337+
}
338+
278339
/**
279340
* List all the currently registered shortcuts.
280341
*/
@@ -590,45 +651,8 @@ export class NavigationController {
590651
/** Copy the block the cursor is currently on. */
591652
copy: {
592653
name: Constants.SHORTCUT_NAMES.COPY,
593-
preconditionFn: (workspace) => {
594-
if (this.canCurrentlyEdit(workspace)) {
595-
switch (this.navigation.getState(workspace)) {
596-
case Constants.STATE.WORKSPACE:
597-
const curNode = workspace?.getCursor()?.getCurNode();
598-
const source = curNode?.getSourceBlock();
599-
return !!(
600-
source?.isDeletable() &&
601-
source?.isMovable() &&
602-
!Blockly.Gesture.inProgress()
603-
);
604-
case Constants.STATE.FLYOUT:
605-
const flyoutWorkspace = workspace.getFlyout()?.getWorkspace();
606-
const sourceBlock = flyoutWorkspace
607-
?.getCursor()
608-
?.getCurNode()
609-
?.getSourceBlock();
610-
return !!(sourceBlock && !Blockly.Gesture.inProgress());
611-
default:
612-
return false;
613-
}
614-
}
615-
return false;
616-
},
617-
callback: (workspace) => {
618-
const navigationState = this.navigation.getState(workspace);
619-
let activeWorkspace: Blockly.WorkspaceSvg | undefined = workspace;
620-
if (navigationState == Constants.STATE.FLYOUT) {
621-
activeWorkspace = workspace.getFlyout()?.getWorkspace();
622-
}
623-
const sourceBlock = activeWorkspace
624-
?.getCursor()
625-
?.getCurNode()
626-
.getSourceBlock() as BlockSvg;
627-
workspace.hideChaff();
628-
this.copyData = sourceBlock.toCopyData();
629-
this.copyWorkspace = sourceBlock.workspace;
630-
return !!this.copyData;
631-
},
654+
preconditionFn: this.blockCopyPreconditionFn,
655+
callback: this.blockCopyCallbackFn,
632656
keyCodes: [
633657
createSerializedKey(KeyCodes.C, [KeyCodes.CTRL]),
634658
createSerializedKey(KeyCodes.C, [KeyCodes.ALT]),
@@ -901,6 +925,31 @@ export class NavigationController {
901925
ContextMenuRegistry.registry.register(deleteItem);
902926
}
903927

928+
/**
929+
* Register the block copy action as a context menu item on blocks.
930+
*/
931+
protected registerCopyAction() {
932+
const copyAction: ContextMenuRegistry.RegistryItem = {
933+
displayText: (scope) => 'Keyboard Navigation: copy',
934+
preconditionFn: (scope) => {
935+
const ws = scope.block?.workspace;
936+
if (!ws) return 'hidden';
937+
938+
return this.blockCopyPreconditionFn(ws) ? 'enabled' : 'disabled';
939+
},
940+
callback: (scope) => {
941+
const ws = scope.block?.workspace;
942+
if (!ws) return;
943+
return this.blockCopyCallbackFn(ws);
944+
},
945+
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
946+
id: 'blockCopyFromContextMenu',
947+
weight: 11,
948+
};
949+
950+
ContextMenuRegistry.registry.register(copyAction);
951+
}
952+
904953
/**
905954
* Registers all default keyboard shortcut items for keyboard
906955
* navigation. This should be called once per instance of
@@ -912,6 +961,7 @@ export class NavigationController {
912961
}
913962

914963
this.registerDeleteAction();
964+
this.registerCopyAction();
915965

916966
// Initalise the shortcut modal with available shortcuts. Needs
917967
// to be done separately rather at construction, as many shortcuts

0 commit comments

Comments
 (0)