From 32f51c25b88df154a8a705c3d19ace855fde19e2 Mon Sep 17 00:00:00 2001 From: Livio Oswald <125505497+Lcoding15@users.noreply.github.com> Date: Sun, 25 Oct 2026 15:00:22 +0100 Subject: [PATCH] M3: the accessible window is named with the window title (#4614) (#4640) The AccessKit root node had no name, so AT-SPI clients saw a top-level frame with an empty accessible name. It now takes the window title each frame and follows it when the title changes. --- crates/ui-egui/src/lib.rs | 3 +++ crates/ui-egui/tests/it/viewer.rs | 23 +++++++++++++++++++++++ parity/acrobat-features.toml | 4 ++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 0300afd46..49b7bb81b 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -3672,6 +3672,9 @@ impl eframe::App for PdfCraftApp { ctx.send_viewport_cmd(egui::ViewportCommand::Title(title.clone())); self.window_title = title; } + // The accessible window takes the same name, so a screen reader (AT-SPI on Linux, UIA on + // Windows) can tell it apart (#4614). egui builds the root node afresh every pass. + ctx.accesskit_node_builder(egui::accesskit_root_id(), |root| root.set_label(self.window_title.as_str())); if self.full_screen && self.active.is_some() { // Full screen: the page on black, nothing else, as Acrobat's Full Screen Mode (Esc // or ⌘L to leave). diff --git a/crates/ui-egui/tests/it/viewer.rs b/crates/ui-egui/tests/it/viewer.rs index 7795e0513..915c47b41 100644 --- a/crates/ui-egui/tests/it/viewer.rs +++ b/crates/ui-egui/tests/it/viewer.rs @@ -2087,3 +2087,26 @@ fn go_to_page_brings_the_page_into_view_horizontally() { let (r, vp) = (v.page_screen_rect(2).expect("page 3 on screen"), v.viewport_rect()); assert!(r.left() >= vp.left() && r.right() <= vp.right(), "page 3 at {r:?}, viewport {vp:?}"); } + +/// #4614: the accessible window is named like the native one, and follows it when the active +/// document changes, so a screen reader can find the window by its title. +#[test] +fn the_accessible_window_has_the_window_title_as_its_name() { + use egui_kittest::kittest::NodeT as _; + let mut h = harness(); + h.run_steps(2); + let name = |h: &Harness<'static, PdfCraftApp>| h.root().accesskit_node().label().map(|l| l.to_string()); + assert_eq!(name(&h).as_deref(), Some("b.pdf — PdfCraft")); + { + use pdfcraft_engine::Edit; + let s = h.state_mut(); + let id = s.views[s.active.unwrap()].id; + s.session.apply(id, Edit::SetInfo { key: "Title".into(), value: "Quarterly report".into() }).unwrap(); + let mut v = s.session.get(id).unwrap().initial_view(); + v.display_title = true; + s.session.apply(id, Edit::SetInitialView(Box::new(v))).unwrap(); + } + h.run_steps(2); + assert_eq!(name(&h).as_deref(), Some("Quarterly report — PdfCraft")); + assert_eq!(name(&h), Some(h.state().window_title.clone())); +} diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index e48831625..f4a729511 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -8484,8 +8484,8 @@ milestone = "M3" status = "partial" commands = [] tools = [] -evidence = ["crates/ui-egui/tests/it/control.rs::inspect_and_click_by_label_and_id"] -notes = "AccessKit labels on all custom widgets; full audit in M14.3" +evidence = ["crates/ui-egui/tests/it/control.rs::inspect_and_click_by_label_and_id", "crates/ui-egui/tests/it/viewer.rs::the_accessible_window_has_the_window_title_as_its_name"] +notes = "AccessKit labels on all custom widgets; the accessible window is named with the window title and follows it (#4614); full audit in M14.3" [[feature]] id = "a11y.keyboard-only"