diff --git a/crates/ui-egui/src/panels/detail.rs b/crates/ui-egui/src/panels/detail.rs index 3b3622bed..176047b6b 100644 --- a/crates/ui-egui/src/panels/detail.rs +++ b/crates/ui-egui/src/panels/detail.rs @@ -1526,9 +1526,10 @@ pub(crate) fn filmstrip(app: &mut LightcraftApp, ui: &mut egui::Ui, r: Rect) { } let resp = ui.interact(cr, egui::Id::new(("film", id.0)), Sense::click()); register(ui.ctx(), format!("film:{}", id.0), cr); - let sel = Some(*id) == active; + let state = app.session.selection.state_of(*id); + let sel = state == lightcraft_engine::SelectionState::Active; let p = ui.painter(); - if sel { + if state != lightcraft_engine::SelectionState::NotSelected { p.rect_filled(cr, 0.0, t.cell_selected); } else if resp.hovered() { p.rect_filled(cr, 0.0, t.cell_selected.gamma_multiply(0.6)); @@ -1549,6 +1550,8 @@ pub(crate) fn filmstrip(app: &mut LightcraftApp, ui: &mut egui::Ui, r: Rect) { p.image(tex.tex.id(), fr, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); if sel { p.rect_stroke(fr, 0.0, Stroke::new(1.5, Color32::WHITE), StrokeKind::Outside); + } else if state == lightcraft_engine::SelectionState::Selected { + p.rect_stroke(fr, 0.0, Stroke::new(1.5, Color32::from_gray(170)), StrokeKind::Outside); } if app.ui.settings.film_badges && let Some(ph) = app.session.catalog.photo(*id) diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index a289e2ee2..2bb14207b 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -199,3 +199,31 @@ fn sidebar_sections_collapse_and_remember_it() { click(&mut h, "icon:albumNew"); assert!(!h.app.ui.sidebar_section_collapsed("albums"), "the plus does not fold Albums"); } + +/// Select All in the loupe: every filmstrip cell of a selected photo is drawn selected, not just +/// the active one (issue #298). +#[test] +fn filmstrip_marks_every_selected_photo() { + let mut h = demo([1400.0, 900.0], json!({"view": "detail"})); + let r = h.request("ui.key", json!({"key": "a", "cmd": true}), T); + assert_eq!(r["ok"], true, "{r}"); + let img = h.snapshot(SETTLE); + assert_eq!(h.view.ctx.pixels_per_point(), 1.0, "the test samples pixels at point coordinates"); + let active = h.app.session.selection.active.expect("active photo"); + let t = crate::theme::Tokens::get(&h.view.ctx); + let ids = h.app.session.visible_cloned(); + assert!(ids.len() > 3 && h.app.session.selection.ids.len() == ids.len()); + let mut checked = 0; + for id in ids.iter().filter(|id| **id != active) { + let Some(r) = h.app.widgets.iter().find(|(w, _)| *w == format!("film:{}", id.0)).map(|(_, r)| *r) else { continue }; + // cells scrolled under the right panel (default width plus the tool rail) are not visible + if r.max.x > 1400.0 - RIGHT_WIDTH.default - 48.0 { + continue; + } + // a pixel in the cell's left margin at mid height, clear of the name text and the thumbnail + let (x, y) = ((r.min.x + 4.0) as usize, r.center().y as usize); + assert_eq!(img[(x, y)], t.cell_selected, "film cell of selected photo {} is drawn selected", id.0); + checked += 1; + } + assert!(checked >= 3, "checked {checked} cells"); +}