Filmstrip: show every selected photo as selected, not only the active one (#298)

After Select All in the loupe only the active filmstrip cell was highlighted,
so the selection looked like a single photo. Cells now use Selection::state_of;
selected-but-not-active cells get the selected fill and a grey outline.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Nathaniel Ledford
2026-10-08 11:01:50 -04:00
co-authored by Claude Sonnet 5.5
parent 7f6e4e48f7
commit 14a03052de
2 changed files with 33 additions and 2 deletions
+5 -2
View File
@@ -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)
+28
View File
@@ -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");
}