mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 05:57:09 -06:00
Merge PR #2830: LR-BEHAV-MULTISELECT: replacing the selection with a repeated id keeps each photo once
docs/parity-checklist.md: kept both revision rows.
This commit is contained in:
@@ -347,7 +347,10 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
s.end_interaction()?;
|
||||
match mode {
|
||||
"replace" => {
|
||||
s.selection = Selection { ids: ids.clone(), active: active.or(ids.first().copied()) };
|
||||
// a repeated id is one photo: keep the first of each, in the order given
|
||||
let mut seen = std::collections::HashSet::with_capacity(ids.len());
|
||||
let ids: Vec<PhotoId> = ids.into_iter().filter(|id| seen.insert(*id)).collect();
|
||||
s.selection = Selection { active: active.or(ids.first().copied()), ids };
|
||||
}
|
||||
"add" => {
|
||||
for id in ids {
|
||||
|
||||
@@ -66,6 +66,20 @@ fn library_select_refuses_ids_that_are_not_photo_ids() {
|
||||
assert_eq!(s.selection.ids, vec![vis[2]]);
|
||||
}
|
||||
|
||||
/// Replacing the selection with a repeated id holds each photo once, in the order first given.
|
||||
#[test]
|
||||
fn library_select_replace_drops_repeated_ids() {
|
||||
let (mut s, vis, _) = session();
|
||||
let (a, b) = (vis[0], vis[1]);
|
||||
s.execute("library.select", &json!({"ids": [a.0, b.0, a.0]})).unwrap();
|
||||
assert_eq!(s.selection.ids, vec![a, b]);
|
||||
assert_eq!(s.selection.active, Some(a));
|
||||
let r = s.execute("library.select", &json!({"ids": [b.0, a.0, b.0, b.0], "active": a.0})).unwrap();
|
||||
assert_eq!(s.selection.ids, vec![b, a]);
|
||||
assert_eq!(s.selection.active, Some(a));
|
||||
assert_eq!(r["selected"], 2);
|
||||
}
|
||||
|
||||
/// The same for a single `id`: a value that isn't a photo id used to mean the active photo
|
||||
/// (`photo.relink` then relinked the active photo to the file, `photo.removeRegion` removed its face
|
||||
/// box), or, for `denoise.cancel`, every photo. (The stack commands share the helper; `in_stack`
|
||||
|
||||
@@ -150,6 +150,21 @@ fn photos_dropped_on_a_folder_move_there_on_disk() {
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// A selection set with a repeated id holds each photo once, so the drag carries it once.
|
||||
#[test]
|
||||
fn a_drag_after_selecting_with_a_repeated_id_carries_each_photo_once() {
|
||||
let (root, trip, home) = folders("dedupe");
|
||||
let mut h = app(&[(trip.join("beach.png"), false), (home.join("garden.png"), false)]);
|
||||
let (beach, garden) = (id_of(&h, "beach.png"), id_of(&h, "garden.png"));
|
||||
h.app.run("library.select", json!({"ids": [beach, garden, beach], "active": beach})).unwrap();
|
||||
let from = widget(&h, &format!("thumb:{beach}")).unwrap().center();
|
||||
press(&mut h, from);
|
||||
glide(&mut h, from, from + egui::vec2(60.0, 60.0));
|
||||
assert_eq!(h.app.ui.dragging_photos, Some(vec![beach, garden]));
|
||||
release(&mut h, from);
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_photos_are_refused_with_the_reason() {
|
||||
let (root, trip, home) = folders("local");
|
||||
|
||||
@@ -457,7 +457,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
|
||||
|---|---|---|---|---|---|
|
||||
| LR-BEHAV-AUTOSAVE | Instant autosave | P0 | ✅ | `crates/catalog/src/journal.rs`, `crates/engine/src/library.rs` | every change fsynced before the command returns; a failed write fails the command ("saved in memory but not written to disk"), keeps the change queued and retries; top-bar warning until saved; a write that fails part-way is cut back to the last whole record so the retry never reads back as damage (logs from older builds with such a fragment load in full); quitting saves queued changes in the closing snapshot even when the log can't be appended to; versioned catalog format (`journal::VERSION`): older libraries are upgraded on open, a library from a newer LightCraft is refused untouched ("written by a newer version"); a damaged or hand-edited snapshot that still reads is made consistent on open (`Catalog::repair`: records filed under another id, stacks of missing, repeated or too few photos, folders inside themselves, id counters behind the ids in use) instead of panicking or hanging later |
|
||||
| LR-BEHAV-UNDO | Global undo | P0 | ✅ | `cmd:edit.undo`, `crates/engine/src/tests.rs` (`rating_flag_undo_redo`), `crates/engine/src/tests/filesystem_history.rs` | covers ratings, albums, deletes, edits; partial file/XMP moves keep unfinished repairs across retries and discarded history; merged renames move directly to their final catalog path |
|
||||
| LR-BEHAV-MULTISELECT | Multi-selection | P0 | ✅ | `cmd:library.select` (`replace`/`add`/`toggle`/`range`), `cmd:library.selectAll` | |
|
||||
| LR-BEHAV-MULTISELECT | Multi-selection | P0 | ✅ | `cmd:library.select` (`replace`/`add`/`toggle`/`range`), `cmd:library.selectAll`, `crates/engine/src/tests/ids_params.rs` (`library_select_replace_drops_repeated_ids`) | `replace` keeps each photo once, in the order first given, so a drag of the selection carries it once |
|
||||
| LR-BEHAV-BATCH | Batch apply to selection | P0 | ✅ | `cmd:photo.rate`, `cmd:develop.paste`, `cmd:preset.apply`, `cmd:app.export` | |
|
||||
| LR-BEHAV-PREVIEW-HOVER | Hover previews | P1 | ✅ | `crates/ui-egui/src/panels/presets.rs`, `crates/ui-egui/src/panels/profiles.rs`, `crates/ui-egui/src/panels/right.rs` (`versions`), `crates/ui-egui/src/panels/detail.rs` | presets, profile menu, profile browser and versions preview in the loupe |
|
||||
| LR-BEHAV-PROGRESSIVE | Progressive rendering | P0 | ✅ | `crates/engine/src/media.rs`, `crates/preview/src/lib.rs` | |
|
||||
@@ -961,3 +961,4 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
|
||||
| 2026-10-23 | patch | LRC-LIB-FILTERBAR: showing an album (`library.source`) while the filter holds an album drops the filter's album, as showing a library folder already dropped its folder filter; before, the old album stayed in the filter unseen (the filter bar has no album chip) and narrowed the next source such as All Photos; the row's note says so; status unchanged (✅) |
|
||||
| 2026-10-27 | patch | MENU-EDIT-SELECTNONE, MENU-EDIT-PASTESELECTED: Select Only Active Photo and Paste Settings from Previous were not in the Edit menu's layout and ended up after Settings…, near the end of the menu; they now sit with the other Select and Paste rows. On macOS the Edit menu no longer shows two separators in a row where Settings… moved to the app menu; status unchanged (✅) |
|
||||
| 2026-10-27 | patch | MENU-FILE-CLOSE, KEY-CLOSEWINDOW: the notes say that Close Window and the red close button of the main window quit the app, after the check Quit makes; kept on purpose; a user page says so; status unchanged (✅) |
|
||||
| 2026-10-24 | patch | LR-BEHAV-MULTISELECT: `library.select` in `replace` mode keeps each photo once, in the order first given; status unchanged (✅) |
|
||||
|
||||
Reference in New Issue
Block a user