From 094884c06364738305c12c2a0d5511e664d0543c Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Mon, 18 May 2026 11:12:33 -0700 Subject: [PATCH 1/2] fix: Fix keyboard navigation for the continuous toolbox --- .../src/ContinuousFlyout.ts | 17 +++++++++++ .../src/ContinuousToolbox.ts | 15 ++++++++++ .../src/ContinuousToolboxNavigator.ts | 29 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts diff --git a/plugins/continuous-toolbox/src/ContinuousFlyout.ts b/plugins/continuous-toolbox/src/ContinuousFlyout.ts index ac638c428e..101605c735 100644 --- a/plugins/continuous-toolbox/src/ContinuousFlyout.ts +++ b/plugins/continuous-toolbox/src/ContinuousFlyout.ts @@ -192,6 +192,23 @@ export class ContinuousFlyout extends Blockly.VerticalFlyout { this.scrollTo(position); } + /** + * Returns the header item in the flyout corresponding to the given + * toolbox category, if any. + */ + headerForCategory( + category: Blockly.ISelectableToolboxItem, + ): Blockly.IFocusableNode | undefined { + return this.getContents() + .find((item) => { + return ( + this.toolboxItemIsLabel(item) && + item.getElement().getButtonText() === category.getName() + ); + }) + ?.getElement(); + } + /** * Step the scrolling animation by scrolling a fraction of the way to * a scroll target, and request the next frame if necessary. diff --git a/plugins/continuous-toolbox/src/ContinuousToolbox.ts b/plugins/continuous-toolbox/src/ContinuousToolbox.ts index 67267b8dde..1bf4dc759c 100644 --- a/plugins/continuous-toolbox/src/ContinuousToolbox.ts +++ b/plugins/continuous-toolbox/src/ContinuousToolbox.ts @@ -10,6 +10,7 @@ import * as Blockly from 'blockly/core'; import {ContinuousFlyout} from './ContinuousFlyout'; +import {ContinuousToolboxNavigator} from './ContinuousToolboxNavigator'; /** * Class for continuous toolbox. @@ -21,6 +22,12 @@ export class ContinuousToolbox extends Blockly.Toolbox { */ private refreshDebouncer?: ReturnType; + /** + * Navigator object responsible for handling keyboard navigation within this + * toolbox. + */ + private continuousToolboxNavigator = new ContinuousToolboxNavigator(this); + /** * Initializes the continuous toolbox. */ @@ -192,4 +199,12 @@ export class ContinuousToolbox extends Blockly.Toolbox { } return super.getClientRect(); } + + /** + * Returns the Navigator object responsible for handling keyboard navigation + * inside this toolbox. + */ + override getNavigator(): Blockly.ToolboxNavigator { + return this.continuousToolboxNavigator; + } } diff --git a/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts b/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts new file mode 100644 index 0000000000..d76d52f916 --- /dev/null +++ b/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from 'blockly/core'; +import {ContinuousToolbox} from './ContinuousToolbox'; +import {ContinuousCategory} from './ContinuousCategory'; + +/** + * A Navigator that handles keyboard navigation within a continuous toolbox. + */ +export class ContinuousToolboxNavigator extends Blockly.ToolboxNavigator { + constructor(protected toolbox: ContinuousToolbox) { + super(toolbox); + } + + /** + * Returns the next node when navigating "in", in this case the first flyout + * item in the toolbox's currently selected category. + */ + override getInNode( + node = Blockly.getFocusManager().getFocusedNode(), + ): Blockly.IFocusableNode | null { + if (!(node instanceof ContinuousCategory)) return null; + return this.toolbox.getFlyout().headerForCategory(node) ?? null; + } +} From fcc088d1521640a080a859542dc81fb6e89e7c9e Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Mon, 18 May 2026 12:11:35 -0700 Subject: [PATCH 2/2] fix: Fix docstrings --- plugins/continuous-toolbox/src/ContinuousFlyout.ts | 3 +++ plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/plugins/continuous-toolbox/src/ContinuousFlyout.ts b/plugins/continuous-toolbox/src/ContinuousFlyout.ts index 101605c735..27f1c63ac0 100644 --- a/plugins/continuous-toolbox/src/ContinuousFlyout.ts +++ b/plugins/continuous-toolbox/src/ContinuousFlyout.ts @@ -195,6 +195,9 @@ export class ContinuousFlyout extends Blockly.VerticalFlyout { /** * Returns the header item in the flyout corresponding to the given * toolbox category, if any. + * + * @param category The toolbox category to retrieve header item for. + * @returns The given category's header item, or undefined if not found. */ headerForCategory( category: Blockly.ISelectableToolboxItem, diff --git a/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts b/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts index d76d52f916..717c7aa8c6 100644 --- a/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts +++ b/plugins/continuous-toolbox/src/ContinuousToolboxNavigator.ts @@ -19,6 +19,9 @@ export class ContinuousToolboxNavigator extends Blockly.ToolboxNavigator { /** * Returns the next node when navigating "in", in this case the first flyout * item in the toolbox's currently selected category. + * + * @param node The node to navigate relative to. + * @returns The node "in" relative to the given node. */ override getInNode( node = Blockly.getFocusManager().getFocusedNode(),