Skip to content
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

Add aria menu roles to menu-related widgets #164741

Merged
merged 44 commits into from
Mar 19, 2025

Conversation

QuncCccccc
Copy link
Contributor

@QuncCccccc QuncCccccc commented Mar 6, 2025

Related to #157177

This PR is to add the following changes:

  • Implement SemanticsRole.menu, SemanticsRole.menuItem, SemanticsRole.menuBar, SemanticsRole.menuItemCheckbox, and SemanticsRole.menuItemRadio.
  • Add SemanticsRole.menu role in MenuAnchor, DropdownButton, and PopupMenuButton
    * Add SemanticsRole.menuBar role to MenuBar
  • Add SemanticsRole.menuItem to MenuItemButton, DropdownMenuItem and PopupMenuItem
    * Add SemanticsRole.menuItemCheckbox to CheckboxMenuButton
    * Add SemanticsRole.menuItemRadio to RadioMenuButton

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. engine flutter/engine repository. See also e: labels. f: material design flutter/packages/flutter/material repository. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos platform-web Web applications specifically a: desktop Running on desktop labels Mar 6, 2025
@github-actions github-actions bot removed d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos a: desktop Running on desktop labels Mar 11, 2025
@QuncCccccc QuncCccccc requested a review from chunhtai March 11, 2025 16:55
@QuncCccccc QuncCccccc marked this pull request as ready for review March 11, 2025 16:59
menuItem,

/// A item with a checkbox in a dropdown created by [menu] or [menuBar].
menuItemCheckbox,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order here need to be the same as semantics_node.h , so we have to either add this to the end or the enum in semantics_node.h has to be moved to the middle and shift all number after them by 2.

For now adding enum in the middle is fine since this hasn't yet expose to embedder.h. Once we expose them in embedder.h, moving enum around will be a breaking change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see! Thanks for reminding! I also noticed the dialog, alertDialog and table roles have reversed order. Just matched all of them.


if (semanticsObject.isFlagsDirty) {
String checkedValue;
if (semanticsObject.isCheckable && semanticsObject.hasFlag(ui.SemanticsFlag.isChecked)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this reuse SemanticCheckable behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems SemanticCheckable doesn't have mixed state and only contain on and off status.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SemanticCheckable is a SemanticsRole. Just added a Checkable behavior.

static FlutterError? _semanticsMenuBar(SemanticsNode node) {
if (node.childrenCount < 1) {
return FlutterError('a menu bar cannot be empty');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also check if all children are menu?

Copy link
Contributor Author

@QuncCccccc QuncCccccc Mar 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the description on the mdn website, a menubar's children can be menuItem, menuItemCheckbox, menuItemRadio, group, and separator. It looks pretty flexible, so I didn't add more checks for menu and menuBar, is this okay?

EDIT:
Additionally, in DropdownButton, PopupMenuButton, MenuAnchor, and MenuBar, the menu items are not menu's immediate children, so I added aria-owns on menu and menuBar.

@chunhtai chunhtai requested a review from yjbanov March 12, 2025 20:51
@QuncCccccc
Copy link
Contributor Author

The PR is ready for another round of review:-)

}

bool _isMenuItem(SemanticsObject semanticsObject) {
if (semanticsObject.role == ui.SemanticsRole.menuItem ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just return the condition directly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also since this if for menubar, should this only owns the Menu?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MenuBar in framework side also have a SingleChildScrollView to wrap the children. This _MenuPanel contains a scrollable and it is used by menu bar:)

@QuncCccccc QuncCccccc force-pushed the wire_up_menu_role branch 2 times, most recently from f95b557 to 4e50291 Compare March 13, 2025 22:43
@QuncCccccc QuncCccccc requested a review from chunhtai March 13, 2025 22:47
chunhtai
chunhtai previously approved these changes Mar 17, 2025
@@ -300,6 +300,80 @@ void main() {
});
});

group('menu', () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add failure case for menuitem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// A visible list of items or a widget that can be made to open and close.
menu,

/// A presentation of [menu] that usually remains visible and is usually
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// A presentation of [menu] that usually remains visible and is usually
/// A presentation of [menu]s that usually remains visible and is usually

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the doc.

/// (i.e. [SemanticsObject.isChecked] and [SemanticsObject.isMixed] are
/// false). If the node is not checkable (i.e. [SemanticsObject.isCheckable]
/// is false), then `aria-checked` is unset.
class Checkable extends SemanticBehavior {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can some of the SemanticCheckable's logic to be replaced by this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a very straightforward refactor on SemanticCheckable as it involves all checkbox, radio and switch. Checkable behavior is more relevant to checkbox. I just added more doc for it. Maybe a separate PR for refactoring SemanticCheckable would be better?

}

String attributeValue = '';
for (final int id in ids) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be simplified as

ids.map((id) => 'flt-semantic-node-$id').join(' ')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for helping simplify this.


String attributeValue = '';
for (final int id in ids) {
attributeValue = '$attributeValue flt-semantic-node-$id';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as below

for (final int id in ids) {
attributeValue = '$attributeValue flt-semantic-node-$id';
}
setAttribute('aria-owns', attributeValue.trim());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to trim if using join

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

/// Uses aria menubar role to convey this semantic information to the element.
///
/// Screen-readers takes advantage of "aria-label" to describe the visual.
class SemanticMenuBar extends SemanticRole {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been reading the doc in mdn aria role menubar

Menubar interactions should be similar to the typical menu bar interaction in a desktop graphical user interface. In Google Docs, each of those menu items is a menuitem with a popup submenu, so each has an aria-haspopup attribute set to true. The menubar element does not.

This seems to me suggesting the menu in menubar should have role menuItem, but I am not sure if this is what it means. I am curious what your interpretation is on the doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is menuBar is just a horizontal menu, so they are not a parent-child relationship. And menubar should contain a list of submenu(menuItem with aria-haspopup attribute setting to true).

@chunhtai chunhtai dismissed their stale review March 17, 2025 23:02

accidentally approved

@chunhtai chunhtai self-requested a review March 17, 2025 23:02
@@ -29,6 +29,13 @@ class SemanticButton extends SemanticRole {
} else {
removeAttribute('aria-disabled');
}

// This is especially useful when the button is used to open a menu.
if (semanticsObject.hasExpandedState) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expansion tile can be a button, too.
Also this role already has Expandable behavior which will assign aria-expanded.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense! Reverted!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue for the popup flag: #165505 :)

onPressed: widget.enabled ? showButtonMenu : null,
enableFeedback: enableFeedback,
style: widget.style,
return MergeSemantics(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you wrap the icon with semantics(expanded) instead of wraping the entire iconButton, you don't need the mergesemantics I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@QuncCccccc QuncCccccc added the autosubmit Merge PR when tree becomes green via auto submit App label Mar 19, 2025
@auto-submit auto-submit bot added this pull request to the merge queue Mar 19, 2025
Merged via the queue into flutter:master with commit 32b34ff Mar 19, 2025
171 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Mar 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) engine flutter/engine repository. See also e: labels. f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. platform-web Web applications specifically
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants