diff --git a/crates/ui-egui/src/panels/detail/filmstrip.rs b/crates/ui-egui/src/panels/detail/filmstrip.rs index ec5364cd9..a03178556 100644 --- a/crates/ui-egui/src/panels/detail/filmstrip.rs +++ b/crates/ui-egui/src/panels/detail/filmstrip.rs @@ -65,7 +65,10 @@ pub(crate) fn filmstrip(app: &mut LightcraftApp, ui: &mut egui::Ui, r: Rect) { let ppp = ui.ctx().pixels_per_point(); if ids.is_empty() { let why = if app.session.filter != Default::default() { "No photos match the filters (View → Clear Filters)" } else { "No photos" }; - ui.painter().text(r.center(), Align2::CENTER_CENTER, why, t.font(12.5), t.text_dim); + // a label, not painted text, so a screen reader reads the reason; the region keeps the strip navigable + let mut empty = ui.new_child(egui::UiBuilder::new().max_rect(r).layout(egui::Layout::centered_and_justified(egui::Direction::TopDown))); + crate::widgets::announce_region(&empty, egui::accesskit::Role::Region, crate::i18n::tr("Filmstrip")); + empty.add(egui::Label::new(egui::RichText::new(why).font(t.font(12.5)).color(t.text_dim)).selectable(false)); return; } let mut child = ui.new_child(egui::UiBuilder::new().max_rect(r.shrink2(vec2(0.0, 4.0)))); diff --git a/crates/ui-egui/tests/it/landmarks.rs b/crates/ui-egui/tests/it/landmarks.rs index 69a92cdfc..dd98e4c88 100644 --- a/crates/ui-egui/tests/it/landmarks.rs +++ b/crates/ui-egui/tests/it/landmarks.rs @@ -60,3 +60,31 @@ fn the_grid_is_the_main_landmark_in_the_photo_grid() { assert_eq!(count(&h, Role::Main, "Photos"), 1); assert_eq!(count(&h, Role::Region, "Filmstrip"), 0, "the grid has no filmstrip"); } + +/// A filter that hides every photo leaves the filmstrip drawn with its reason: it stays a landmark +/// and the reason is text a screen reader reads, not paint. +#[test] +fn an_empty_filmstrip_is_still_a_landmark_and_says_why() { + let mut session = lightcraft_engine::Session::with_demo(); + session.execute("library.filter", &serde_json::json!({"keyword": "no-such-keyword"})).expect("the filter is set"); + assert!(session.visible_cloned().is_empty(), "the filter hides every photo"); + let mut app = LightcraftApp::new(session, Services { png: None, ..Default::default() }); + app.ui.left_panel = true; + app.ui.view = ViewMode::Detail; + app.ui.right = RightPanel::Edit; + let mut h = Harness::builder().with_size(vec2(1500.0, 950.0)).build_ui_state( + |ui, app: &mut LightcraftApp| { + app.logic(&ui.ctx().clone()); + app.ui(ui); + }, + app, + ); + h.run_steps(10); + assert_eq!(count(&h, Role::Region, "Filmstrip"), 1, "the empty strip is still a landmark"); + let said = h + .root() + .children_recursive() + .filter_map(|n| n.accesskit_node().value().or_else(|| n.accesskit_node().label())) + .any(|t| t.starts_with("No photos match the filters")); + assert!(said, "the reason is read, not only painted"); +}