feat(a11y): read Settings rows as named groups with their switches' state

Each row is a group named by its title and description, so the control in
it is heard with its setting's name; switches report on or off, the reset
link is a button, and the Modified only switch has a name of its own.
This commit is contained in:
l0ng-ai
2026-09-30 02:56:02 +08:00
parent 6d07ca41b0
commit cdd3f3c2cd
2 changed files with 28 additions and 0 deletions
+15
View File
@@ -179,6 +179,9 @@ pub(crate) struct Switch {
disabled: bool,
small: bool,
handler: Option<ToggleHandler>,
/// The name a screen reader gives the switch, where no settings row
/// around it already says what it is.
label: Option<SharedString>,
}
pub(crate) fn switch(id: impl Into<ElementId>) -> Switch {
@@ -188,6 +191,7 @@ pub(crate) fn switch(id: impl Into<ElementId>) -> Switch {
disabled: false,
small: false,
handler: None,
label: None,
}
}
@@ -200,6 +204,10 @@ impl Switch {
self.disabled = disabled;
self
}
pub(crate) fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
self
}
pub(crate) fn small(mut self) -> Self {
self.small = true;
self
@@ -222,6 +230,13 @@ impl RenderOnce for Switch {
let left = if on { w - knob - 2. } else { 2. };
div()
.id(self.id)
.role(gpui::Role::Switch)
.when_some(self.label, |d, label| d.aria_label(label))
.aria_toggled(if on {
gpui::Toggled::True
} else {
gpui::Toggled::False
})
.flex_shrink_0()
.relative()
.w(px(w))
+13
View File
@@ -433,6 +433,14 @@ impl Tty7App {
let tk = Tk::of(cx);
let label = label.into();
let desc = desc.into();
// A screen reader enters the row as a group named by the setting, so
// the control inside it — a switch, a dropdown — is heard with the
// name it belongs to rather than as a bare "switch, off".
let a11y_name = match desc.is_empty() {
true => label.clone(),
false => format!("{label}. {desc}"),
};
let reset_name = format!("{} {label}", t(L10nKey::Reset));
let entry = settings_search_entries()
.iter()
.find(|entry| t(entry.title) == label);
@@ -483,6 +491,8 @@ impl Tty7App {
line.child(
div()
.id(SharedString::from(format!("reset-setting-{key:?}")))
.role(gpui::Role::Button)
.aria_label(reset_name.clone())
.flex_shrink_0()
.text_size(fs(11.5))
.text_color(tk.k4)
@@ -518,6 +528,8 @@ impl Tty7App {
});
let row = div()
.id(element_id)
.role(gpui::Role::Group)
.aria_label(a11y_name)
.flex()
.when(stacked, |row| row.flex_col().items_start().gap(px(8.)))
.when(!stacked, |row| {
@@ -1264,6 +1276,7 @@ impl Tty7App {
})
.child(
kit::switch("settings-modified-switch")
.label(t(L10nKey::SettingsModifiedOnly))
.small()
.checked(modified_only),
)