M5: a locked layer in Select layer says so to assistive technology (#1560)

The padlock in a Select layer row is only paint, so a screen reader could not tell a locked layer from the rest. A locked row now gets the description "locked" (the same string the Layers panel adds), joined with any description the row already has. New a11y::describe sets a description on an existing node without touching its label or role.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
This commit is contained in:
Trevor BrownandClaude Sonnet 5.5 committed 2026-11-12 04:40:16 -05:00
1 parent 87f7b7d944
commit 39e3496fef
3 files changed
+37

No files matched your search

+12
View File
@@ -995,6 +995,18 @@ pub fn disabled(resp: &Response) {
node(resp, "", |n| n.set_disabled());
}
/// `resp`'s node also says `text` (a menu row's state, e.g. "locked"), after any description
/// already set (joined with ", ").
pub fn describe(resp: &Response, text: &str) {
node(resp, "", |n| {
let d = match n.description() {
Some(old) if !old.is_empty() => format!("{old}, {text}"),
_ => text.to_string(),
};
n.set_description(d);
});
}
/// What a layer row says besides its name.
pub struct RowInfo<'a> {
/// The layer's type ("Frame", "Text").
+22
View File
@@ -189,6 +189,28 @@ fn select_layer_marks_locked_layers_with_a_padlock_and_picks_them() {
let _ = lower;
}
/// The padlock is only paint: the locked row says "locked" to assistive technology too.
#[test]
fn select_layer_locked_row_is_described_as_locked() {
let (mut u, _, _) = overlap_ui();
let at = u.app.to_screen(150.0, 150.0);
crate::canvas::open_context_menu(&mut u.app, at, false);
u.app.session.set_selection(vec![]);
crate::menus::open_select_layer(&u.ctx, true);
u.ctx.enable_accesskit();
u.frame(vec![]);
let nodes: Vec<egui::accesskit::Node> =
u.frame_output(vec![]).platform_output.accesskit_update.expect("an AccessKit update").nodes.into_iter().map(|(_, n)| n).collect();
let row = |name: &str| {
nodes
.iter()
.find(|n| n.role() == egui::accesskit::Role::MenuItem && n.label() == Some(name))
.unwrap_or_else(|| panic!("no menu row {name:?}"))
};
assert!(row("Upper").description().is_some_and(|d| d.contains("locked")), "the locked row says locked");
assert!(!row("Lower").description().is_some_and(|d| d.contains("locked")), "the other row doesn't");
}
/// Measured on a single locked layer only (Figma's web app, 2026-11-10): the least certain rule.
#[test]
fn select_layer_needs_more_than_one_layer_under_the_pointer() {
+3
View File
@@ -408,6 +408,9 @@ pub(super) fn indented_item(ui: &mut Ui, label: &str, shortcut: &str, enabled: b
if !enabled {
a11y::disabled(&resp);
}
if locked {
a11y::describe(&resp, i18n::tr("locked"));
}
// The keyboard's item is highlighted like the hovered one.
a11y::own_focus(&resp);
let hov = plain_row_hover(ui, &resp) && enabled;