From 26522b27b5b7699572b37ddad2aac8ac201e9153 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 8 Nov 2026 23:00:08 -0800 Subject: [PATCH] New Document: screen readers hear each Preset Details field's name The fields under Preset Details had captions drawn above them but no accessible name. Each field is now named by its caption, and the units dropdown beside Width is named Units. Preset Name is also named in More Settings. --- crates/ui-egui/src/dialogs/form.rs | 7 ++--- crates/ui-egui/src/dialogs/new_document.rs | 27 ++++++++++--------- .../src/dialogs/tests_accessibility.rs | 27 +++++++++++++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index 8d4a68ed0..be4f9e80c 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -1109,10 +1109,11 @@ pub(super) fn bleed(ui: &mut egui::Ui, d: &mut Dialog, unit: vectorcraft_doc::Un } } -/// A small grey caption above a field (New Document's details column). -pub(super) fn caption(ui: &mut egui::Ui, text: &str) { +/// A small grey caption above a field (New Document's details column) → its response, so the field +/// can be named by it. +pub(super) fn caption(ui: &mut egui::Ui, text: &str) -> egui::Response { let t = Tokens::get(ui.ctx()); - ui.label(egui::RichText::new(tl!(text)).size(11.0).color(t.text_dim)); + ui.label(egui::RichText::new(tl!(text)).size(11.0).color(t.text_dim)) } #[cfg(test)] diff --git a/crates/ui-egui/src/dialogs/new_document.rs b/crates/ui-egui/src/dialogs/new_document.rs index 4c977ed6a..982dd7c6f 100644 --- a/crates/ui-egui/src/dialogs/new_document.rs +++ b/crates/ui-egui/src/dialogs/new_document.rs @@ -358,9 +358,9 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut let top = ui.cursor().top(); ui.label(egui::RichText::new(tl!("PRESET DETAILS")).font(theme::semibold(11.0)).color(t.text_dim)); ui.add_space(8.0); - form::caption(ui, tl!("Document Name")); + let caption = form::caption(ui, tl!("Document Name")); ui.horizontal(|ui| { - form::text(ui, d, "name", DETAILS - 50.0); + ui.label_inputs_by(caption.id, |ui| form::text(ui, d, "name", DETAILS - 50.0)); save_button(app, ui, d); }); if d.bool(SAVING) { @@ -368,16 +368,16 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut } ui.add_space(8.0); let unit = unit(d); - form::caption(ui, tl!("Width")); + let caption = form::caption(ui, tl!("Width")); ui.horizontal(|ui| { - form::length(ui, d, "width", unit, DETAILS - 124.0); - units_dropdown(ui, d, 112.0); + ui.label_inputs_by(caption.id, |ui| form::length(ui, d, "width", unit, DETAILS - 124.0)); + ui.name_inputs(tl!("Units"), |ui| units_dropdown(ui, d, 112.0)); }); ui.add_space(4.0); ui.horizontal_top(|ui| { ui.vertical(|ui| { - form::caption(ui, tl!("Height")); - form::length(ui, d, "height", unit, 104.0); + let caption = form::caption(ui, tl!("Height")); + ui.label_inputs_by(caption.id, |ui| form::length(ui, d, "height", unit, 104.0)); }); ui.add_space(6.0); ui.vertical(|ui| { @@ -386,16 +386,16 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut }); ui.add_space(6.0); ui.vertical(|ui| { - form::caption(ui, tl!("Artboards")); - artboards(ui, d, 76.0); + let caption = form::caption(ui, tl!("Artboards")); + ui.label_inputs_by(caption.id, |ui| artboards(ui, d, 76.0)); }); }); ui.add_space(8.0); form::caption(ui, tl!("Bleed")); form::bleed(ui, d, unit, 52.0); ui.add_space(8.0); - form::caption(ui, tl!("Background Contents")); - background(ui, d, DETAILS - 34.0); + let caption = form::caption(ui, tl!("Background Contents")); + ui.label_inputs_by(caption.id, |ui| background(ui, d, DETAILS - 34.0)); ui.add_space(8.0); advanced(ui, d, 110.0, DETAILS - 116.0); ui.add_space(12.0); @@ -427,9 +427,10 @@ fn save_button(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { /// Save Preset: the preset's name, Save and Cancel. fn save_row(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { ui.add_space(4.0); - form::caption(ui, tl!("Preset Name")); + let caption = form::caption(ui, tl!("Preset Name")); ui.horizontal(|ui| { - let enter = form::text_edit(ui, d, "presetName", DETAILS - 140.0).lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)); + let name = ui.label_inputs_by(caption.id, |ui| form::text_edit(ui, d, "presetName", DETAILS - 140.0)); + let enter = name.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)); if widgets::flat_button(ui, tl!("Save"), 54.0).clicked() || enter { let mut p: Map = PARAMS.iter().filter_map(|k| Some((k.to_string(), d.fields.get(*k)?.clone()))).collect(); p.insert("name".into(), json!(d.str("presetName"))); diff --git a/crates/ui-egui/src/dialogs/tests_accessibility.rs b/crates/ui-egui/src/dialogs/tests_accessibility.rs index ac469f6b7..8525fd8bb 100644 --- a/crates/ui-egui/src/dialogs/tests_accessibility.rs +++ b/crates/ui-egui/src/dialogs/tests_accessibility.rs @@ -81,6 +81,33 @@ fn document_setup_names_each_bleed_side() { } } +/// New Document's Preset Details column names each field by the caption above it. +#[test] +fn new_document_names_its_fields_by_their_captions() { + let mut app = app(); + app.run("file.newDialog", json!({})).unwrap(); + app.ui.dialog.as_mut().expect("New Document is open").fields.insert("__saving".into(), json!(true)); + let h = strict(app); + for name in ["Document Name", "Preset Name", "Width", "Height", "Artboards"] { + let _ = h.get_by_role_and_label(Role::TextInput, name); + } + for name in ["Units", "Background Contents"] { + let _ = h.get_by_role_and_label(Role::ComboBox, name); + } +} + +/// New Document's More Settings, with Save Preset open, leaves no field unnamed. +#[test] +fn new_document_more_settings_names_its_fields() { + let mut app = app(); + app.run("file.newDialog", json!({})).unwrap(); + let d = app.ui.dialog.as_mut().expect("New Document is open"); + d.kind = super::new_document::MORE.into(); + d.fields.insert("__saving".into(), json!(true)); + let h = strict(app); + let _ = h.get_by_role_and_label(Role::TextInput, "Preset Name"); +} + /// Split Into Grid keeps translated visible labels while naming row and column gutter and total /// inputs by axis and context. #[test]