Skip to content

Comments

Implement accessibility for ListBox#20733

Open
kirb wants to merge 11 commits intoAvaloniaUI:masterfrom
kirb:feature/accessibility-listbox-paging
Open

Implement accessibility for ListBox#20733
kirb wants to merge 11 commits intoAvaloniaUI:masterfrom
kirb:feature/accessibility-listbox-paging

Conversation

@kirb
Copy link
Contributor

@kirb kirb commented Feb 23, 2026

What does the pull request do?

Implements missing accessibility support in ListBox by adding an automation peer (see below for why). Also implements ISelectionItemProvider on macOS.

What is the current behavior?

  1. The user can only use VoiceOver to navigate through items visible in the ListBox viewport. When reaching the end of the viewport, the “boundary reached” sound is played. Items beyond the viewport can only be reached by using tab navigation or arrow keys.
  2. VoiceOver incorrectly states the “X of Y” position of the selected item in a ListBox as being the visual position in the viewport, rather than the position in the overall list. For example, “5 of 14”, being the 5th of 14 visible items, when it should be “42 of 80”.

What is the updated/expected behavior with this PR?

  1. The user can navigate the entire contents of a ListBox using VoiceOver.
  2. VoiceOver knows the exact number of items in the list, and the index of the currently selected item.
  3. Items can be toggled between selected and unselected using VO-Cmd-Return, and items can be selected (resetting any multiple selection) using VO-Space, matching native NSTableView behavior.

How was the solution implemented (if it's not obvious)?

ListBoxAutomationPeer has been added. This subclasses SelectingItemsControlAutomationPeer so selection events are correctly raised. It overrides to return a custom list of children, where any that are not in viewport are of type UnrealizedListItemAutomationPeer. This provides just enough info to stand in for the ListItem’s actual automation peer. The screen reader now knows exactly how many items are in the list, and can navigate through all of them, including non-sequentially via features such as Find. For the latter, GetNameCore() evaluates the DisplayMemberBinding to get a placeholder name.

The remainder of ISelectionItemProvider has been implemented on macOS, although the relevant NSAccessibility methods end up working differently from how they seem to have been originally thought out. “Pick” is invoked by VO-Space, and calls Select(). List items become selected ([view setAccessibilitySelected:YES] is called) as soon as the user lands on it. However, by pressing VO-Cmd-Return, the list enters a mode where it stops immediately selecting items. It then will toggle selection by pressing VO-Cmd-Return again.

Checklist

Breaking changes

None

Obsoletions / Deprecations

None

Fixed issues

Fixes #20685.


I haven’t yet tested this on Windows. Will update when I have a chance to do so.

I also can’t get the control catalog ListBox demo to correctly speak row indexes - it says “1 of 10,000” for all rows. I tried adding an implementation of accessibilityIndex, but it seems to not get used, so I didn’t include it in this PR. In future, it would make sense to implement the NSAccessibilityTable/NSAccessibilityRow protocols (and NSAccessibilityList, which is identical to NSAccessibilityTable). Lists seem to work informally, with the system figuring out item index and count itself based on the children array, while tables require formal implementation of every method in those protocols. There may be a case for switching back to NSAccessibilityTableRole once this is implemented - it would match NSTableView/NSCollectionView as used by native apps, including sidebars in Finder and System Settings.

kirb added 11 commits February 21, 2026 02:03
The mapping proposed in the doc comments is modified because there is no
direct concept of multiple selection in VoiceOver. `Select()` maps to both
`-accessibilityPerformPick` and `-setAccessibilitySelected:YES`, with
`RemoveFromSelection()` mapped to `-setAccessibilitySelected:NO`. This
feels like it better maps to what the API intends.
This creates an automation peer for ListBox that returns actual or virtual
automation peers for the items in the list. This allows VoiceOver to be
aware of the number of items, and be able to navigate between them,
even if not visible in the current viewport.

Fixes AvaloniaUI#20685.
These serve similar behaviors, and the unrealized/virtual element peer can
subclass from UnrealizedElementAutomationPeer.
This ensures the screen reader follows the item when it transitions from
unrealised to realised. Otherwise, navigation may be random.
Exposing -accessibilitySelectedChildren allows VoiceOver to enter a
multiple selection mode. This is triggered the first time the user presses
VO-Cmd-Return. In this mode, it stops calling -setAccessibilitySelected:
with a parameter of YES each time the user lands on an item. Instead, it
toggles the selection when the user presses VO-Cmd-Return.
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0062448-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@codecat
Copy link
Contributor

codecat commented Feb 23, 2026

This creates a regression in Windows, where list items are no longer announced after scrolling.

@MrJul MrJul added enhancement area-accessibility backport-candidate-11.3.x Consider this PR for backporting to 11.3 branch labels Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-accessibility backport-candidate-11.3.x Consider this PR for backporting to 11.3 branch enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accessibility: Can’t scroll a ListView beyond visible items on macOS

4 participants