fix: Announce the workspace to VoiceOver via a focus target node#10087
Conversation
VoiceOver doesn't announce a region when focus moves into it from a node
already inside that region, so focusing the workspace region directly
(e.g. pressing "W" from a block) was silent.
Introduce WorkspaceFocusTarget: an ordinary focusable node that represents
the workspace as a whole. The workspace's selection ring (which already
represents the workspace being the active node) doubles as this target,
gaining role=figure, aria-roledescription="workspace" and the stack count.
Focusing the workspace now lands here instead of the region, so the move is
announced. The region keeps a short, stable label ("Blocks workspace.") as
enclosing context, and the stack count moves off the region onto the target.
- W shortcut and fresh-entry (getRestoredFocusableNode) focus the target.
- Add WorkspaceFocusTargetNavigationPolicy so navigating in reaches blocks.
- Block/comment navigation is unchanged.
gonfunko
left a comment
There was a problem hiding this comment.
Did you find that the additional navigation policy was actually necessary? Experimentally it seems to work without it, and I'd generally expect that to be the case (the navigator does some funny business to make cross-stack navigation work that, as a side effect, makes nav work a bit different at the root)
|
As mentioned in chat, totally understand if this approach isn't merged, I'd love a better plan. |
gonfunko
left a comment
There was a problem hiding this comment.
I don't love this, but it does work and none of the other hacks I've thought of (focus something else before focusing the workspace, adding nonbreaking spaces to the label on focus) do. The closest alternative I found was just returning the focus ring element as the workspace's getFocusableElement(), but that seemed to confuse the focus manager (I think it may want the focusable tree's focusable element to actually contain the focusable elements of children of the tree). If you want to explore that feel free (main benefit would be avoiding the focus target wrapper class), but I can live with this as-is particularly since the navigation policy bit wasn't needed. LMK what your preference is!
| @@ -0,0 +1,64 @@ | |||
| /** | |||
| * @license | |||
| * Copyright 2025 Google LLC | |||
There was a problem hiding this comment.
2026 Raspberry Pi Foundation
I'm not optimistic about this. What about e.g. clicking the workspace background? And I also expect trouble from code like this. |
Yeah, I think that's part of what was causing trouble. I'll go ahead and merge this then - thanks for figuring this all out! |
VoiceOver doesn't announce a region when focus moves into it from a node already inside that region, so focusing the workspace region directly (e.g. pressing "W" from a block) was silent.
Introduce WorkspaceFocusTarget: an ordinary focusable node that represents the workspace as a whole. The workspace's selection ring (which already represents the workspace being the active node) doubles as this target, gaining role=figure, aria-roledescription="workspace" and the stack count. Focusing the workspace now lands here instead of the region, so the move is announced. The region keeps a short, stable label ("Blocks workspace.") as enclosing context, and the stack count moves off the region onto the target.
The basics
The details
Resolves
Fixes #9885
Proposed Changes
This restructures what takes the focus when moving to the workspace to be a node inside the workspace region even in the workspace case so we always get readout.
We considered some alternatives and welcome the Blockly team's input here:
New readout:
change in at least NVDA + VoiceOver before and after this change
When testing this we were surprised to find that 'W' in a mutator workspace focuses the main workspace. I expected it to behave with the same scoping as 'T'. But from the tests this behaviour is clearly intentional (and FWIW has no impact on MakeCode). This is relevant because if 'W' did focus mutator workspaces the same design is harder to pull off there: stack count is not part of the readout there so there's less of a case for a separate node. Not an issue as-is but flagging for awareness.
Note that strings change here, not sure of the impact on v13.
Reason for Changes
Test Coverage
Documentation
Additional Information