diff --git a/crates/ui-egui/src/a11y.rs b/crates/ui-egui/src/a11y.rs index 502452765..d0e34d6fe 100644 --- a/crates/ui-egui/src/a11y.rs +++ b/crates/ui-egui/src/a11y.rs @@ -883,9 +883,17 @@ pub fn tab(resp: &Response, label: &str, selected: bool) { } pub fn checkbox(resp: &Response, label: &str, on: bool) { + checkbox_tri(resp, label, Some(on)); +} + +/// A checkbox that can be mixed (`None`, announced as "mixed" rather than unchecked). +pub fn checkbox_tri(resp: &Response, label: &str, state: Option) { node(resp, label, |n| { n.set_role(Role::CheckBox); - n.set_toggled(toggled(on)); + n.set_toggled(match state { + Some(on) => toggled(on), + None => Toggled::Mixed, + }); }); } diff --git a/crates/ui-egui/src/a11y_tests.rs b/crates/ui-egui/src/a11y_tests.rs index 14be637dd..2f0fb5a48 100644 --- a/crates/ui-egui/src/a11y_tests.rs +++ b/crates/ui-egui/src/a11y_tests.rs @@ -431,3 +431,19 @@ fn the_custom_panel_edges_never_enter_the_keyboard_or_focus_order() { } assert!(!seen.contains(&left) && !seen.contains(&right), "Tab never stops on a panel edge: {seen:?}"); } + +#[test] +fn a_mixed_checkbox_is_announced_as_mixed_and_a_checked_one_as_true() { + let ctx = egui::Context::default(); + ctx.enable_accesskit(); + let mut out = ctx.run_ui(egui::RawInput::default(), |ui| { + crate::widgets::checkbox_tri(ui, None, "Mixed one"); + crate::widgets::checkbox_tri(ui, Some(true), "Checked one"); + crate::widgets::checkbox_tri(ui, Some(false), "Plain one"); + }); + out.textures_delta.clear(); + let nodes: Vec = out.platform_output.accesskit_update.expect("an AccessKit update").nodes.into_iter().map(|(_, n)| n).collect(); + assert_eq!(find(&nodes, Role::CheckBox, "Mixed one").toggled(), Some(Toggled::Mixed)); + assert_eq!(find(&nodes, Role::CheckBox, "Checked one").toggled(), Some(Toggled::True)); + assert_eq!(find(&nodes, Role::CheckBox, "Plain one").toggled(), Some(Toggled::False)); +} diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index 05dbff893..b38acbfa4 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -1282,7 +1282,7 @@ pub fn checkbox(ui: &mut Ui, on: bool, label: &str) -> bool { pub fn checkbox_tri(ui: &mut Ui, state: Option, label: &str) -> bool { let t = Tokens::get(ui.ctx()); let (rect, resp) = ui.allocate_exact_size(vec2(ui.available_width().min(200.0), FIELD_H), Sense::click()); - a11y::checkbox(&resp, label, state == Some(true)); + a11y::checkbox_tri(&resp, label, state); let b = Rect::from_min_size(pos2(rect.min.x, rect.center().y - 7.0), vec2(14.0, 14.0)); if state != Some(false) { ui.painter().rect_filled(b, CornerRadius::same(3), t.accent);