Skip to content

Commit 32b34ff

Browse files
authored
Add aria menu roles to menu-related widgets (#164741)
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 - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent bf3826d commit 32b34ff

File tree

17 files changed

+1118
-61
lines changed

17 files changed

+1118
-61
lines changed

engine/src/flutter/ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42640,6 +42640,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart + ../../../flu
4264042640
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart + ../../../flutter/LICENSE
4264142641
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/alert.dart + ../../../flutter/LICENSE
4264242642
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart + ../../../flutter/LICENSE
42643+
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/disable.dart + ../../../flutter/LICENSE
4264342644
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/expandable.dart + ../../../flutter/LICENSE
4264442645
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart + ../../../flutter/LICENSE
4264542646
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/header.dart + ../../../flutter/LICENSE
@@ -42650,6 +42651,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/label_and_value.dar
4265042651
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/link.dart + ../../../flutter/LICENSE
4265142652
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/list.dart + ../../../flutter/LICENSE
4265242653
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/live_region.dart + ../../../flutter/LICENSE
42654+
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/menus.dart + ../../../flutter/LICENSE
4265342655
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/platform_view.dart + ../../../flutter/LICENSE
4265442656
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/requirable.dart + ../../../flutter/LICENSE
4265542657
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/route.dart + ../../../flutter/LICENSE
@@ -45615,6 +45617,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart
4561545617
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart
4561645618
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/alert.dart
4561745619
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart
45620+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/disable.dart
4561845621
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/expandable.dart
4561945622
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart
4562045623
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/header.dart
@@ -45625,6 +45628,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/label_and_value.dart
4562545628
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/link.dart
4562645629
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/list.dart
4562745630
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/live_region.dart
45631+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/menus.dart
4562845632
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/platform_view.dart
4562945633
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/requirable.dart
4563045634
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/route.dart

engine/src/flutter/lib/ui/semantics.dart

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,22 +412,45 @@ enum SemanticsRole {
412412

413413
/// A input field with a dropdown list box attached.
414414
///
415-
/// For example, a [DropDownMenu]
415+
/// For example, a [DropdownMenu]
416416
comboBox,
417417

418-
/// Contains a list of [menu]s.
418+
/// A presentation of [menu] that usually remains visible and is usually
419+
/// presented horizontally.
419420
///
420421
/// For example, a [MenuBar].
421422
menuBar,
422423

423-
/// A button that opens a dropdown that contains multiple [menuItem]s.
424+
/// A permanently visible list of controls or a widget that can be made to
425+
/// open and close.
424426
///
425-
/// For example, a [MenuAnchor] or [DropDownButton].
427+
/// For example, a [MenuAnchor] or [DropdownButton].
426428
menu,
427429

428-
/// A item in a dropdown created by [menu] or [comboBox].
430+
/// An item in a dropdown created by [menu] or [menuBar].
431+
///
432+
/// See also:
433+
///
434+
/// * [menuItemCheckbox], a menu item with a checkbox. The [menuItemCheckbox]
435+
/// can also be used within [menu] and [menuBar].
436+
/// * [menuItemRadio], a menu item with a radio button. This role is used by
437+
/// [menu] or [menuBar] as well.
429438
menuItem,
430439

440+
/// An item with a checkbox in a dropdown created by [menu] or [menuBar].
441+
///
442+
/// See also:
443+
///
444+
/// * [menuItem] and [menuItemRadio] for menu related roles.
445+
menuItemCheckbox,
446+
447+
/// An item with a radio button in a dropdown created by [menu] or [menuBar].
448+
///
449+
/// See also:
450+
///
451+
/// * [menuItem] and [menuItemCheckbox] for menu related roles.
452+
menuItemRadio,
453+
431454
/// A container to display multiple [listItem]s in vertical or horizontal
432455
/// layout.
433456
///

engine/src/flutter/lib/ui/semantics/semantics_node.h

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,29 +73,31 @@ enum class SemanticsRole : int32_t {
7373
kTab = 1,
7474
kTabBar = 2,
7575
kTabPanel = 3,
76-
kTable = 4,
77-
kCell = 5,
78-
kRow = 6,
79-
kColumnHeader = 7,
80-
kDialog = 8,
81-
kAlertDialog = 9,
76+
kDialog = 4,
77+
kAlertDialog = 5,
78+
kTable = 6,
79+
kCell = 7,
80+
kRow = 8,
81+
kColumnHeader = 9,
8282
kSearchBox = 10,
8383
kDragHandle = 11,
8484
kSpinButton = 12,
8585
kComboBox = 13,
8686
kMenuBar = 14,
8787
kMenu = 15,
8888
kMenuItem = 16,
89-
kList = 17,
90-
kListItem = 18,
91-
kForm = 19,
92-
kTooltip = 20,
93-
kLoadingSpinner = 21,
94-
kProgressBar = 22,
95-
kHotKey = 23,
96-
kRadioGroup = 24,
97-
kStatus = 25,
98-
kAlert = 26,
89+
kMenuItemCheckbox = 17,
90+
kMenuItemRadio = 18,
91+
kList = 19,
92+
kListItem = 20,
93+
kForm = 21,
94+
kTooltip = 22,
95+
kLoadingSpinner = 23,
96+
kProgressBar = 24,
97+
kHotKey = 25,
98+
kRadioGroup = 26,
99+
kStatus = 27,
100+
kAlert = 28,
99101
};
100102

101103
/// C/C++ representation of `SemanticsFlags` defined in

engine/src/flutter/lib/web_ui/lib/semantics.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ enum SemanticsRole {
283283
menuBar,
284284
menu,
285285
menuItem,
286+
menuItemCheckbox,
287+
menuItemRadio,
286288
list,
287289
listItem,
288290
form,

engine/src/flutter/lib/web_ui/lib/src/engine.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export 'engine/scene_view.dart';
106106
export 'engine/semantics/accessibility.dart';
107107
export 'engine/semantics/alert.dart';
108108
export 'engine/semantics/checkable.dart';
109+
export 'engine/semantics/disable.dart';
109110
export 'engine/semantics/expandable.dart';
110111
export 'engine/semantics/focusable.dart';
111112
export 'engine/semantics/header.dart';
@@ -116,6 +117,7 @@ export 'engine/semantics/label_and_value.dart';
116117
export 'engine/semantics/link.dart';
117118
export 'engine/semantics/list.dart';
118119
export 'engine/semantics/live_region.dart';
120+
export 'engine/semantics/menus.dart';
119121
export 'engine/semantics/platform_view.dart';
120122
export 'engine/semantics/requirable.dart';
121123
export 'engine/semantics/route.dart';

engine/src/flutter/lib/web_ui/lib/src/engine/semantics.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
export 'semantics/accessibility.dart';
66
export 'semantics/alert.dart';
77
export 'semantics/checkable.dart';
8+
export 'semantics/disable.dart';
89
export 'semantics/expandable.dart';
910
export 'semantics/focusable.dart';
1011
export 'semantics/header.dart';
@@ -15,6 +16,7 @@ export 'semantics/label_and_value.dart';
1516
export 'semantics/link.dart';
1617
export 'semantics/list.dart';
1718
export 'semantics/live_region.dart';
19+
export 'semantics/menus.dart';
1820
export 'semantics/platform_view.dart';
1921
export 'semantics/requirable.dart';
2022
export 'semantics/scrollable.dart';

engine/src/flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,35 @@ class Selectable extends SemanticBehavior {
158158
}
159159
}
160160
}
161+
162+
/// Adds checkability behavior to a semantic node.
163+
///
164+
/// A checkable node would have the `aria-checked` set to "true" if the node
165+
/// is currently checked (i.e. [SemanticsObject.isChecked] is true), set to
166+
/// "mixed" if the node is in a mixed state (i.e. [SemanticsObject.isMixed]) and
167+
/// set to "false" if it's not checked or mixed
168+
/// (i.e. [SemanticsObject.isChecked] and [SemanticsObject.isMixed] are
169+
/// false). If the node is not checkable (i.e. [SemanticsObject.isCheckable]
170+
/// is false), then `aria-checked` is unset.
171+
///
172+
/// This behavior is typically used for a checkbox or a radio button.
173+
class Checkable extends SemanticBehavior {
174+
Checkable(super.semanticsObject, super.owner);
175+
176+
@override
177+
void update() {
178+
if (semanticsObject.isFlagsDirty) {
179+
if (semanticsObject.isCheckable) {
180+
if (semanticsObject.isChecked) {
181+
owner.setAttribute('aria-checked', 'true');
182+
} else if (semanticsObject.isMixed) {
183+
owner.setAttribute('aria-checked', 'mixed');
184+
} else {
185+
owner.setAttribute('aria-checked', 'false');
186+
}
187+
} else {
188+
owner.removeAttribute('aria-checked');
189+
}
190+
}
191+
}
192+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'semantics.dart';
6+
7+
/// Adds disable behavior to a semantic node.
8+
///
9+
/// A disabled node would have the `aria-disabled` set to "true" if the node
10+
/// is currently disabled (i.e. [SemanticsObject.isEnabled] is false). If the
11+
/// node is enabled (i.e. [SemanticsObject.isEnabled]
12+
/// is true), then `aria-disabled` is unset.
13+
class CanDisable extends SemanticBehavior {
14+
CanDisable(super.semanticsObject, super.owner);
15+
16+
@override
17+
void update() {
18+
if (semanticsObject.isFlagsDirty) {
19+
if (semanticsObject.enabledState() == EnabledState.disabled) {
20+
owner.setAttribute('aria-disabled', 'true');
21+
} else {
22+
owner.removeAttribute('aria-disabled');
23+
}
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)