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.
This commit is contained in:
Livio Oswald
2026-10-25 10:00:22 -04:00
committed by GitHub
parent 6b9785eb00
commit 32f51c25b8
3 changed files with 28 additions and 2 deletions
+3
View File
@@ -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).
+23
View File
@@ -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()));
}
+2 -2
View File
@@ -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"