Skip to content

Upgrade to latest Blockly keyboard navigation plugin #10512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
574f53d
Upgrade keyboard nav plugin
microbit-matt-hillsdon Apr 9, 2025
2c60e92
Don't show connection lines when moving blocks via keyboard
microbit-robert Apr 11, 2025
8fd7376
Don't show trash icon unless overlapping toolbox when keyboard moving…
microbit-grace Apr 15, 2025
27c8a75
Upgrade keyboard navigation plugin
microbit-robert Apr 16, 2025
f945abb
Override clean_up_workspace shortcut to use flow function (#36)
microbit-grace Apr 16, 2025
893197d
Rebase fix
microbit-robert Apr 22, 2025
0eaa51e
Use latest published version of keyboard experiment plugin
microbit-robert Apr 22, 2025
4a542e9
Revert changes that are not required / workarounds
microbit-robert Apr 22, 2025
aa39ba3
Update ThirdPartyNotice
microbit-robert Apr 22, 2025
ef660be
Tidy and minimize diff
microbit-robert Apr 22, 2025
8e0bb98
Avoid focus-visible style on toolbox categories on click
microbit-robert Apr 22, 2025
ab48b57
Consistent approach to determine a mouse drag vs keyboard move
microbit-robert Apr 22, 2025
8770e21
Enable accessible blocks via settings option instead of experiments (…
microbit-grace Apr 30, 2025
0ae8719
Merge branch 'master' into blockly-12-keyboard-exp
microbit-robert Apr 30, 2025
d5f19f0
Add workaround for flyout cursor after new caching implementation
microbit-robert Apr 30, 2025
4fc0ad4
Disable clipboard overwrite when accessible blocks enabled (#41)
microbit-grace Apr 30, 2025
d0d6bb0
Tidy up following review comments
microbit-robert May 1, 2025
39f657a
Use 'f' as shortcut to format code
microbit-robert May 1, 2025
ae4f56b
Remove unused import
microbit-robert May 2, 2025
e13240a
Fix CSS formatting
microbit-robert May 2, 2025
3312769
Remove stray quotes
microbit-robert May 2, 2025
242de2a
Fix tabs/spaces in function
microbit-robert May 2, 2025
2d62a69
Single quotes -> double quotes
microbit-robert May 2, 2025
4b72884
Add and use method to return SVGElement to CachingFlyout
microbit-robert May 2, 2025
a52729d
Tidy isFlyoutItemDisposed and prevent unnecessary flyout re-render
microbit-robert May 2, 2025
7d14034
Merge branch 'master' into blockly-12-keyboard-exp
riknoll May 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ThirdPartyNotice
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ General Public License.
863. dashjs 4.4.0 (https://www.npmjs.com/package/dashjs/v/4.4.0)
864. @fortawesome/fontawesome-free 5.15.4 (https://www.npmjs.com/package/@fortawesome/fontawesome-free/v/5.15.4)
865. @blockly/plugin-workspace-search 4.0.10 (https://www.npmjs.com/package/@blockly/plugin-workspace-search/v/4.0.10)
866. @blockly/keyboard-navigation 0.1.18 (https://www.npmjs.com/package/@blockly/keyboard-navigation/v/0.1.18)
866. @blockly/keyboard-experiment 0.0.7 (https://www.npmjs.com/package/@blockly/keyboard-experiment/v/0.0.7)



Expand Down Expand Up @@ -28749,7 +28749,7 @@ to represent the company, product, or service to which they refer.**
END OF @blockly/plugin-workspace-search 4.0.10 NOTICES AND INFORMATION


%% @blockly/keyboard-navigation 0.1.18 NOTICES AND INFORMATION BEGIN HERE (https://www.npmjs.com/package/@blockly/keyboard-navigation/v/0.1.18)
%% @blockly/keyboard-experiment 0.0.7 NOTICES AND INFORMATION BEGIN HERE (https://www.npmjs.com/package/@blockly/keyboard-experiment/v/0.0.7)
=========================================

Apache License
Expand Down Expand Up @@ -28954,4 +28954,4 @@ END OF @blockly/plugin-workspace-search 4.0.10 NOTICES AND INFORMATION
See the License for the specific language governing permissions and
limitations under the License.
=========================================
END OF @blockly/keyboard-navigation 0.1.18 NOTICES AND INFORMATION
END OF @blockly/keyboard-experiment 0.0.7 NOTICES AND INFORMATION
7 changes: 7 additions & 0 deletions localtypings/blockly-keyboard-experiment.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module "@blockly/keyboard-experiment" {
import { WorkspaceSvg } from "blockly";

class KeyboardNavigation {
constructor(workspace: WorkspaceSvg)
}
}
20 changes: 0 additions & 20 deletions localtypings/navigationController.d.ts

This file was deleted.

2 changes: 0 additions & 2 deletions localtypings/pxteditor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1036,8 +1036,6 @@ declare namespace pxt.editor {
toggleHighContrast(): void;
setHighContrast(on: boolean): void;
toggleGreenScreen(): void;
toggleAccessibleBlocks(): void;
setAccessibleBlocks(enabled: boolean): void;
launchFullEditor(): void;
resetWorkspace(): void;

Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
},
"dependencies": {
"@blockly/field-colour": "5.0.12",
"@blockly/keyboard-navigation": "0.6.5",
"@blockly/keyboard-experiment": "0.0.7",
"@blockly/plugin-workspace-search": "9.1.0",
"@crowdin/crowdin-api-client": "^1.33.0",
"@fortawesome/fontawesome-free": "^5.15.4",
Expand Down Expand Up @@ -159,9 +159,6 @@
},
"@blockly/plugin-workspace-search": {
"blockly": "^12.0.0-beta.4"
},
"@blockly/keyboard-navigation": {
"blockly": "^12.0.0-beta.4"
}
},
"scripts": {
Expand Down
8 changes: 7 additions & 1 deletion pxtblocks/blockDragger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ export class BlockDragger extends Blockly.dragging.Dragger {
|| document.getElementsByClassName('blocklyFlyout')[0] as HTMLElement;
const trashIcon = document.getElementById("blocklyTrashIcon");
if (blocklyTreeRoot && trashIcon) {
const rect = blocklyTreeRoot.getBoundingClientRect()
const distance = calculateDistance(blocklyTreeRoot.getBoundingClientRect(), e.clientX);
if (distance < 200) {
const isMouseDrag = Blockly.Gesture.inProgress();
if ((isMouseDrag && distance < 200) || (!isMouseDrag && isOverlappingRect(rect, e.clientX))) {
const opacity = distance / 200;
trashIcon.style.opacity = `${1 - opacity}`;
trashIcon.style.display = 'block';
Expand Down Expand Up @@ -45,4 +47,8 @@ export class BlockDragger extends Blockly.dragging.Dragger {

function calculateDistance(elemBounds: DOMRect, mouseX: number) {
return Math.abs(mouseX - (elemBounds.left + (elemBounds.width / 2)));
}

function isOverlappingRect(elemBounds: DOMRect, mouseX: number) {
return (mouseX - (elemBounds.left + (elemBounds.width))) < 0;
}
12 changes: 10 additions & 2 deletions pxtblocks/contextMenu/blockItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,16 @@ export function registerBlockitems() {
registerHelp();

// Fix the weights of the builtin options we do use
Blockly.ContextMenuRegistry.registry.getItem("blockDelete").weight = BlockContextWeight.DeleteBlock;
Blockly.ContextMenuRegistry.registry.getItem("blockComment").weight = BlockContextWeight.AddComment;
// Defensiveness due to action changes in the keyboard navigation plugin.
// Needs revisiting when actions are final.
const blockDelete = Blockly.ContextMenuRegistry.registry.getItem("blockDelete");
if (blockDelete) {
blockDelete.weight = BlockContextWeight.DeleteBlock;
}
const blockComment = Blockly.ContextMenuRegistry.registry.getItem("blockComment");
if (blockComment) {
blockComment.weight = BlockContextWeight.AddComment;
}
}

/**
Expand Down
2 changes: 2 additions & 0 deletions pxtblocks/fields/field_ledmatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom {
// Clear event listeners and selection used for keyboard navigation.
this.removeKeyboardFocusHandlers();
this.clearSelection();
// This enables keyboard navigation in the Blockly workspace if not focused already.
(this.sourceBlock_.workspace as Blockly.WorkspaceSvg).markFocused();
}, false));
}

Expand Down
3 changes: 2 additions & 1 deletion pxtblocks/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,8 @@ function init(blockInfo: pxtc.BlocksInfo) {
initText();
initComments();
initTooltip();
initCopyPaste();
// FIXME: Disabled while we consider conflict with the keyboard nav plugin
// initCopyPaste();
}


Expand Down
1 change: 1 addition & 0 deletions pxtblocks/plugins/duplicateOnDrag/dragStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import * as Blockly from "blockly";
import { ConnectionPreviewer } from "../renderer";

import { DUPLICATE_ON_DRAG_MUTATION_KEY, isAllowlistedShadow, shouldDuplicateOnDrag } from "./duplicateOnDrag";
import eventUtils = Blockly.Events;
Expand Down
2 changes: 1 addition & 1 deletion pxtblocks/plugins/flyout/verticalFlyout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export class VerticalFlyout implements Blockly.IFlyout {
}
}

class CachedFlyout extends Blockly.VerticalFlyout {
export class CachedFlyout extends Blockly.VerticalFlyout {
protected def: Element[];
protected buttonListeners: Blockly.browserEvents.Data[] = [];

Expand Down
7 changes: 4 additions & 3 deletions pxtblocks/plugins/renderer/connectionPreviewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ export class ConnectionPreviewer extends Blockly.InsertionMarkerPreviewer {
const atan = Math.atan2(dy, dx);

const len = Math.sqrt(dx * dx + dy * dy);
// When the indicators are overlapping, we hide the line
if (len < radius * 2 + 1) {
const isMouseDrag = Blockly.Gesture.inProgress();
// When the indicators are overlapping, or if the drag is keyboard driven, we hide the line
if (len < radius * 2 + 1 || !isMouseDrag) {
Blockly.utils.dom.addClass(this.connectionLine, "hidden");
} else {
} else if (isMouseDrag) {
Blockly.utils.dom.removeClass(this.connectionLine, "hidden");
this.connectionLine.setAttribute("x1", String(offset.x + Math.cos(atan) * radius));
this.connectionLine.setAttribute("y1", String(offset.y + Math.sin(atan) * radius));
Expand Down
2 changes: 1 addition & 1 deletion pxteditor/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export function all(): Experiment[] {
{
id: "accessibleBlocks",
name: lf("Accessible Blocks"),
description: lf("Use the WASD keys to move and modify blocks."),
description: lf("Use the arrow keys to move and modify blocks."),
feedbackUrl: "https://github.com/microsoft/pxt/issues/6850"
},
{
Expand Down
62 changes: 62 additions & 0 deletions theme/blockly-core.less
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ svg.blocklySvg {
}
}



.blocklyMainBackground {
stroke: none !important;
}
Expand Down Expand Up @@ -283,6 +285,24 @@ text.blocklyCheckbox {
}
}

/* Keyboard navigation plugin styles */

.passiveBlockFocus.blocklyPath {
stroke-dasharray: 5 3;
stroke-width: 3;
stroke: #ffa200;
}

.passiveNextIndicator {
stroke: #ffa200;
fill: #ffa200;
}

.inputActiveFocus {
stroke-width: 3;
stroke: #ffa200;
}

/*******************************
Scrollbars
*******************************/
Expand Down Expand Up @@ -373,3 +393,45 @@ text.blocklyCheckbox {
font-size: 17pt !important;
}
}


/*******************************
Focus styles
*******************************/

.accessibleBlocks .blocklyWorkspace:focus,.blocklyWorkspaceFocusLayer:focus {
outline: none;
}

.accessibleBlocks .blocklyWorkspaceFocusRingLayer {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
pointer-events: none;
z-index: 99;
}

.accessibleBlocks .blocklyWorkspaceFocusRingLayer[data-focused = 'true'] {
outline: 3px solid black;
outline-offset: -3px;
}

div.blocklyTreeRoot > div > div[role="tree"]:focus-visible {
outline: none;
}

.accessibleBlocks div.blocklyTreeRoot > div > div[role="tree"]:focus-visible {
outline: 3px solid black;
outline-offset: -3px;
}

.accessibleBlocks .blocklyFlyout:focus {
outline: none;
}

.accessibleBlocks .blocklyFlyout:focus-visible {
outline: 3px solid black;
outline-offset: -3px;
}
4 changes: 4 additions & 0 deletions theme/toolbox.less
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ div.blocklyTreeIcon span {
background-color: var(--pxt-target-background3-hover);
}

.blocklyToolbox .blocklyTreeRoot [role="treeitem"] {
outline: none;
}

/*******************************
Inverted Toolbox
*******************************/
Expand Down
6 changes: 6 additions & 0 deletions webapp/src/accessibility.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export class EditorAccessibilityMenu extends data.Component<EditorAccessibilityM
this.showLanguagePicker = this.showLanguagePicker.bind(this);
this.showThemePicker = this.showThemePicker.bind(this);
this.goHome = this.goHome.bind(this);
this.openBlocks = this.openBlocks.bind(this);
}

openBlocks(e: React.MouseEvent<HTMLElement>) {
this.props.parent.openBlocks();
}

openJavaScript() {
Expand Down Expand Up @@ -73,6 +78,7 @@ export class EditorAccessibilityMenu extends data.Component<EditorAccessibilityM
const hasHome = !pxt.shell.isControllerMode();

return <div className="ui accessibleMenu borderless fixed menu" role="menubar">
{targetTheme.accessibleBlocks ? <sui.Item className={`${targetTheme.invertedMenu ? `inverted` : ''} menu`} role="menuitem" icon="xicon blocks" text={lf("Skip to Blocks workspace")} onClick={this.openBlocks} /> : undefined}
<sui.Item className={`${targetTheme.invertedMenu ? `inverted` : ''} menu`} role="menuitem" icon="xicon js" text={lf("Skip to JavaScript editor")} onClick={this.openJavaScript} />
{targetTheme.python ? <sui.Item className={`${targetTheme.invertedMenu ? `inverted` : ''} menu`} role="menuitem" icon="xicon python" text={lf("Skip to Python editor")} onClick={this.openPython} /> : undefined}
{targetTheme.selectLanguage ? <sui.Item className={`${targetTheme.invertedMenu ? `inverted` : ''} menu`} role="menuitem" icon="xicon globe" text={lf("Select Language")} onClick={this.showLanguagePicker} /> : undefined}
Expand Down
25 changes: 10 additions & 15 deletions webapp/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,9 @@ export class ProjectView

if (this.isBlocksActive()) {
if (this.state.embedSimView) this.setState({ embedSimView: false });
// This timeout prevents key events from being handled by Blockly's keyboard
// navigation plugin prematurely.
setTimeout(() => {this.editor.focusWorkspace()}, 0)
return;
}

Expand Down Expand Up @@ -2807,7 +2810,7 @@ export class ProjectView
this.stopSimulator(true); // don't keep simulator around
this.showKeymap(false); // close keymap if open
cmds.disconnectAsync(); // turn off any kind of logging
if (this.editor) this.editor.unloadFileAsync();
if (this.editor) this.editor.unloadFileAsync(home);
this.extensions.unload();
this.editorFile = undefined;

Expand Down Expand Up @@ -4661,7 +4664,7 @@ export class ProjectView
extensionsVisible: false
})

if (this.state.accessibleBlocks) {
if (pxt.appTarget.appTheme.accessibleBlocks) {
this.editor.focusToolbox(CategoryNameID.Extensions);
}
}
Expand Down Expand Up @@ -5170,16 +5173,6 @@ export class ProjectView
this.setState({ greenScreen: greenScreenOn });
}

toggleAccessibleBlocks() {
this.setAccessibleBlocks(!this.state.accessibleBlocks);
}

setAccessibleBlocks(enabled: boolean) {
pxt.tickEvent("app.accessibleblocks", { on: enabled ? 1 : 0 });
// this.blocksEditor.enableAccessibleBlocks(enabled);
this.setState({ accessibleBlocks: enabled })
}

setBannerVisible(b: boolean) {
this.setState({ bannerVisible: b });
}
Expand Down Expand Up @@ -5353,7 +5346,8 @@ export class ProjectView
const inDebugMode = this.state.debugging;
const inHome = this.state.home && !sandbox;
const inEditor = !!this.state.header && !inHome;
const { lightbox, greenScreen, accessibleBlocks } = this.state;
const { lightbox, greenScreen } = this.state;
const accessibleBlocks = pxt.appTarget.appTheme.accessibleBlocks;
const hideTutorialIteration = inTutorial && tutorialOptions.metadata?.hideIteration;
const hideToolbox = inTutorial && tutorialOptions.metadata?.hideToolbox;
// flyoutOnly has become a de facto css class for styling tutorials (especially minecraft HOC), so keep it if hideToolbox is true, even if flyoutOnly is false.
Expand Down Expand Up @@ -5443,12 +5437,13 @@ export class ProjectView
header={this.state.header}
reloadHeaderAsync={async () => {
await this.reloadHeaderAsync()
this.shouldFocusToolbox = !!this.state.accessibleBlocks;
this.shouldFocusToolbox = !!accessibleBlocks;
}}
/>
}
{greenScreen ? <greenscreen.WebCam close={this.toggleGreenScreen} /> : undefined}
{accessibleBlocks && <accessibleblocks.AccessibleBlocksInfo />}
{/* TODO: Discuss when this onboarding dialog should be shown if accessibleBlocks is enabled by default */}
{/* {accessibleBlocks && <accessibleblocks.AccessibleBlocksInfo />} */}
{hideMenuBar || inHome ? undefined :
<header className="menubar" role="banner">
{inEditor ? <accessibility.EditorAccessibilityMenu parent={this} highContrast={hc} /> : undefined}
Expand Down
Loading