From b940d87880e3cefb7d03ca7f33b6263d4548f6a4 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Fri, 23 Oct 2026 21:10:27 -0400 Subject: [PATCH] KEYC-RATING: deleting the only keyword of the filter Enable Filters put aside leaves filtering on keyword.delete emptied the put-aside filter's keyword but kept the empty filter, so filters_off() stayed true and View > Enable Filters showed unchecked with nothing to bring back. commit_keywords now drops a put-aside filter that has become empty, as library.removeFolder already does. --- crates/engine/src/cmd/keywords.rs | 8 +++ .../src/cmd/library/tests_enable_filters.rs | 69 +++++++++++++++++++ docs/parity-checklist.md | 3 +- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/keywords.rs b/crates/engine/src/cmd/keywords.rs index ebed426fa..54cb30d59 100644 --- a/crates/engine/src/cmd/keywords.rs +++ b/crates/engine/src/cmd/keywords.rs @@ -1,6 +1,7 @@ //! Library-wide keyword commands: list (tree with counts), suggestions, rename, delete, merge; //! keyword sets (nine keywords a keystroke away: ⌥1–⌥9) and Recent Keywords. +use lightcraft_catalog::Filter; use lightcraft_catalog::keywords::{KeywordInfo, clean, closest, is_under, reparent}; use serde_json::{Value, json}; @@ -33,10 +34,17 @@ fn commit_keywords(s: &mut Session, label: &str, op: lightcraft_catalog::Op, fol s.filter.keyword = follow(&k); } // a filter put aside by Enable Filters follows the keyword too + let mut gone_from_put_aside = false; if let Some(f) = s.shelved_filter.as_mut() && let Some(k) = f.keyword.clone() { f.keyword = follow(&k); + // the keyword was deleted: if that emptied the filter, nothing is left to bring back. The rating + // operator filters nothing without stars, so it alone does not count as something to bring back. + gone_from_put_aside = f.keyword.is_none() && (Filter { rating_op: Default::default(), ..f.clone() }) == Filter::default(); + } + if gone_from_put_aside { + s.shelved_filter = None; } Ok(json!({"changed": photos, "listed": listed})) } diff --git a/crates/engine/src/cmd/library/tests_enable_filters.rs b/crates/engine/src/cmd/library/tests_enable_filters.rs index 9eb3c1ad0..1a40a1792 100644 --- a/crates/engine/src/cmd/library/tests_enable_filters.rs +++ b/crates/engine/src/cmd/library/tests_enable_filters.rs @@ -152,3 +152,72 @@ fn a_keyword_renamed_while_off_is_followed_by_the_filter_put_aside() { assert_eq!(s.filter.keyword.as_deref(), Some("dog"), "the put-aside filter follows the rename"); assert_eq!(s.visible().len(), 1); } + +/// The session's photos plus one photo that carries the keyword "cat". +fn session_with_a_cat() -> Session { + let mut s = session().unwrap(); + let mut photo = Photo::new(PhotoId(5), Source::File { path: "/lightcraft-enable-filters/5.jpg".into() }, "5.jpg", "JPEG", 40, 30, "2026-10-23"); + photo.meta.keywords = vec!["cat".into()]; + s.catalog.apply(Op::AddPhoto { photo: Box::new(photo) }).unwrap(); + s +} + +#[test] +fn deleting_the_only_keyword_put_aside_leaves_filtering_on() { + let mut s = session_with_a_cat(); + s.execute("library.filter", &json!({"keyword": "cat"})).unwrap(); + assert_eq!(s.visible().len(), 1); + s.execute("library.toggleFilters", &json!({})).unwrap(); + assert!(s.filters_off()); + s.execute("keyword.delete", &json!({"keyword": "cat"})).unwrap(); + assert!(!s.filters_off(), "an empty filter is not left put aside"); + let r = s.execute("library.toggleFilters", &json!({})).unwrap(); + assert_eq!((r["enabled"].clone(), r["count"].clone()), (json!(true), json!(5)), "nothing was put aside, so nothing is switched off"); + assert_eq!(s.filter, Filter::default()); +} + +#[test] +fn deleting_a_keyword_put_aside_keeps_the_rest_of_the_filter() { + let mut s = session_with_a_cat(); + s.execute("library.filter", &json!({"keyword": "cat", "rating": 4})).unwrap(); + s.execute("library.toggleFilters", &json!({})).unwrap(); + s.execute("keyword.delete", &json!({"keyword": "cat"})).unwrap(); + assert!(s.filters_off(), "the stars are still put aside"); + s.execute("library.toggleFilters", &json!({})).unwrap(); + assert_eq!(s.filter, Filter { rating: 4, ..Default::default() }); +} + +#[test] +fn a_rating_operator_without_stars_does_not_keep_an_emptied_filter_put_aside() { + let mut s = session_with_a_cat(); + // the filter bar's operator picker can be set with no stars chosen; it filters nothing + s.execute("library.filter", &json!({"keyword": "cat", "ratingOp": "exactly"})).unwrap(); + s.execute("library.toggleFilters", &json!({})).unwrap(); + assert!(s.filters_off()); + s.execute("keyword.delete", &json!({"keyword": "cat"})).unwrap(); + assert!(!s.filters_off(), "only an operator with no stars is left: nothing to bring back"); + let r = s.execute("library.toggleFilters", &json!({})).unwrap(); + assert_eq!(r["enabled"], json!(true)); +} + +#[test] +fn a_rating_operator_put_aside_survives_a_keyword_change_that_does_not_touch_it() { + let mut s = session_with_a_cat(); + s.execute("library.filter", &json!({"ratingOp": "exactly"})).unwrap(); + s.execute("library.toggleFilters", &json!({})).unwrap(); + assert!(s.filters_off()); + s.execute("keyword.rename", &json!({"from": "cat", "to": "dog"})).unwrap(); + assert!(s.filters_off(), "the filter put aside held no keyword, so the rename leaves it alone"); + s.execute("library.toggleFilters", &json!({})).unwrap(); + assert_eq!(s.filter.rating_op, lightcraft_catalog::RatingOp::Exactly); +} + +#[test] +fn a_rating_operator_with_stars_is_kept_when_the_keyword_goes() { + let mut s = session_with_a_cat(); + s.execute("library.filter", &json!({"keyword": "cat", "rating": 4, "ratingOp": "exactly"})).unwrap(); + s.execute("library.toggleFilters", &json!({})).unwrap(); + s.execute("keyword.delete", &json!({"keyword": "cat"})).unwrap(); + s.execute("library.toggleFilters", &json!({})).unwrap(); + assert_eq!((s.filter.rating, s.filter.rating_op), (4, lightcraft_catalog::RatingOp::Exactly)); +} diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md index afbed8675..b326ec0ca 100644 --- a/docs/parity-checklist.md +++ b/docs/parity-checklist.md @@ -1,6 +1,6 @@ # Lightroom parity checklist -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (KEYC-RATING: deleting a keyword while Enable Filters has the filter put aside no longer leaves an empty one there; LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good @@ -959,3 +959,4 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove | 2026-10-23 | patch | LR-BEHAV-ACCESS: the name field of New Album, New Smart Album, Rename and the other one-field dialogs takes the keyboard focus once when the dialog opens instead of every frame, so Tab and Shift+Tab reach the check box, Cancel and OK; status unchanged (🟡) | | 2026-10-22 | minor | LR-CROP-AUTO: Auto straighten turns by at most 10° and only on at least 3 agreeing lines, and says when it finds nothing to level by; measured on 68 photos straightened by hand and 300 left as shot, unwanted turns of more than 1° fell from 79 to 19 of the 300. Downgraded ✅ → 🟡: it still leaves most tilted photos alone | | 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-23 | patch | KEYC-RATING: deleting a keyword while ⌘L (Ctrl+L) Enable Filters has the filter put aside, when that keyword was all the filter held, no longer leaves an empty filter put aside (the menu showed Enable Filters unchecked with nothing to bring back); status unchanged (🟡) |