Skip to content

Commit 1e5b4e9

Browse files
authored
feat: Add support for keyboard navigation into mutator workspaces. (#9151)
* feat: Add support for keyboard navigation into mutators. * fix: Prevent mutator bubbles from jumping wildly during keyboard nav.
1 parent 97ffea7 commit 1e5b4e9

6 files changed

Lines changed: 47 additions & 11 deletions

File tree

core/bubbles/mini_workspace_bubble.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ export class MiniWorkspaceBubble extends Bubble {
153153
* are dealt with by resizing the workspace to show them.
154154
*/
155155
private bumpBlocksIntoBounds() {
156-
if (this.miniWorkspace.isDragging()) return;
156+
if (
157+
this.miniWorkspace.isDragging() &&
158+
!this.miniWorkspace.keyboardMoveInProgress
159+
)
160+
return;
157161

158162
const MARGIN = 20;
159163

@@ -185,7 +189,15 @@ export class MiniWorkspaceBubble extends Bubble {
185189
* mini workspace.
186190
*/
187191
private updateBubbleSize() {
188-
if (this.miniWorkspace.isDragging()) return;
192+
if (
193+
this.miniWorkspace.isDragging() &&
194+
!this.miniWorkspace.keyboardMoveInProgress
195+
)
196+
return;
197+
198+
// Disable autolayout if a keyboard move is in progress to prevent the
199+
// mutator bubble from jumping around.
200+
this.autoLayout &&= !this.miniWorkspace.keyboardMoveInProgress;
189201

190202
const currSize = this.getSize();
191203
const newSize = this.calculateWorkspaceSize();

core/icons/mutator_icon.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {BlockChange} from '../events/events_block_change.js';
1414
import {isBlockChange, isBlockCreate} from '../events/predicates.js';
1515
import {EventType} from '../events/type.js';
1616
import * as eventUtils from '../events/utils.js';
17-
import type {IBubble} from '../interfaces/i_bubble.js';
1817
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
1918
import * as renderManagement from '../render_management.js';
2019
import {Coordinate} from '../utils/coordinate.js';
@@ -205,7 +204,7 @@ export class MutatorIcon extends Icon implements IHasBubble {
205204
}
206205

207206
/** See IHasBubble.getBubble. */
208-
getBubble(): IBubble | null {
207+
getBubble(): MiniWorkspaceBubble | null {
209208
return this.miniWorkspaceBubble;
210209
}
211210

core/keyboard_nav/icon_navigation_policy.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
*/
66

77
import {BlockSvg} from '../block_svg.js';
8+
import {getFocusManager} from '../focus_manager.js';
89
import {Icon} from '../icons/icon.js';
10+
import {MutatorIcon} from '../icons/mutator_icon.js';
911
import type {IFocusableNode} from '../interfaces/i_focusable_node.js';
1012
import type {INavigationPolicy} from '../interfaces/i_navigation_policy.js';
1113
import {navigateBlock} from './block_navigation_policy.js';
@@ -17,10 +19,18 @@ export class IconNavigationPolicy implements INavigationPolicy<Icon> {
1719
/**
1820
* Returns the first child of the given icon.
1921
*
20-
* @param _current The icon to return the first child of.
22+
* @param current The icon to return the first child of.
2123
* @returns Null.
2224
*/
23-
getFirstChild(_current: Icon): IFocusableNode | null {
25+
getFirstChild(current: Icon): IFocusableNode | null {
26+
if (
27+
current instanceof MutatorIcon &&
28+
current.bubbleIsVisible() &&
29+
getFocusManager().getFocusedNode() === current
30+
) {
31+
return current.getBubble()?.getWorkspace() ?? null;
32+
}
33+
2434
return null;
2535
}
2636

core/keyboard_nav/workspace_navigation_policy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class WorkspaceNavigationPolicy
6262
* @returns True if the given workspace can be focused.
6363
*/
6464
isNavigable(current: WorkspaceSvg): boolean {
65-
return current.canBeFocused();
65+
return current.canBeFocused() && !current.isMutator;
6666
}
6767

6868
/**

core/navigator.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@ export class Navigator {
6464
getFirstChild(current: IFocusableNode): IFocusableNode | null {
6565
const result = this.get(current)?.getFirstChild(current);
6666
if (!result) return null;
67-
// If the child isn't navigable, don't traverse into it; check its peers.
6867
if (!this.get(result)?.isNavigable(result)) {
69-
return this.getNextSibling(result);
68+
return this.getFirstChild(result) || this.getNextSibling(result);
7069
}
7170
return result;
7271
}

core/workspace_svg.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import type {FlyoutButton} from './flyout_button.js';
4141
import {getFocusManager} from './focus_manager.js';
4242
import {Gesture} from './gesture.js';
4343
import {Grid} from './grid.js';
44+
import {MutatorIcon} from './icons/mutator_icon.js';
4445
import {isAutoHideable} from './interfaces/i_autohideable.js';
4546
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
4647
import {IContextMenu} from './interfaces/i_contextmenu.js';
@@ -2680,7 +2681,7 @@ export class WorkspaceSvg
26802681

26812682
/** See IFocusableNode.getFocusableTree. */
26822683
getFocusableTree(): IFocusableTree {
2683-
return this;
2684+
return (this.isMutator && this.options.parentWorkspace) || this;
26842685
}
26852686

26862687
/** See IFocusableNode.onNodeFocus. */
@@ -2710,7 +2711,22 @@ export class WorkspaceSvg
27102711

27112712
/** See IFocusableTree.getNestedTrees. */
27122713
getNestedTrees(): Array<IFocusableTree> {
2713-
return [];
2714+
const nestedWorkspaces = this.getAllBlocks()
2715+
.map((block) => block.getIcons())
2716+
.flat()
2717+
.filter(
2718+
(icon): icon is MutatorIcon =>
2719+
icon instanceof MutatorIcon && icon.bubbleIsVisible(),
2720+
)
2721+
.map((icon) => icon.getBubble()?.getWorkspace())
2722+
.filter((workspace) => !!workspace);
2723+
2724+
const ownFlyout = this.getFlyout(true);
2725+
if (ownFlyout) {
2726+
nestedWorkspaces.push(ownFlyout.getWorkspace());
2727+
}
2728+
2729+
return nestedWorkspaces;
27142730
}
27152731

27162732
/** See IFocusableTree.lookUpFocusableNode. */

0 commit comments

Comments
 (0)