LRC-LIB-KEYWORDS: the active photo alone is the selection

Keyword commands act on the selected photos, or on the active photo
when the selection is empty (`Session::targets`), but the Keywording
box, the keyword set's buttons, the suggestions and the Keyword List's
tick boxes read the selection only: with just an active photo the box
showed nothing while typed keywords went to it. They now all use the
photos the commands use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nathaniel Ledford
2026-10-09 15:32:40 -04:00
co-authored by Claude Opus 5.5
parent 03aa2edc9d
commit 69c91013e0
4 changed files with 19 additions and 5 deletions
+1 -1
View File
@@ -72,7 +72,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
ui.label(egui::RichText::new(crate::i18n::tr(none)).color(t.text_dim));
});
}
let selection = app.session.selection.ids.clone();
let selection = app.session.targets(&serde_json::Value::Null);
let ticks = app.caches.keyword_ticks(&app.session.catalog, &selection);
for r in &rows {
row(app, ui, r, &selection, &ticks, filter.trim().is_empty());
+2 -2
View File
@@ -26,7 +26,7 @@ pub fn view_switch(app: &mut LightcraftApp, ui: &mut egui::Ui) {
/// carry is marked with an asterisk.
pub fn export_row(app: &mut LightcraftApp, ui: &mut egui::Ui) {
let t = Tokens::get(ui.ctx());
let selection = app.session.selection.ids.clone();
let selection = app.session.targets(&serde_json::Value::Null);
let names = app.caches.keyword_export(&app.session.catalog, &selection);
if names.is_empty() {
ui.label(egui::RichText::new(crate::i18n::tr("No keywords are exported")).color(t.text_dim));
@@ -105,7 +105,7 @@ fn menu(app: &mut LightcraftApp, ui: &mut egui::Ui, chip: &Chip) {
/// The selection's keywords as chips: the name (right-click for its menu), and × to take it off
/// every selected photo. One only some of them have is marked with an asterisk.
pub fn chip_row(app: &mut LightcraftApp, ui: &mut egui::Ui) {
let selection = app.session.selection.ids.clone();
let selection = app.session.targets(&serde_json::Value::Null);
let chips = app.caches.keyword_chips(&app.session.catalog, &selection);
ui.horizontal_wrapped(|ui| {
for c in chips.iter() {
+2 -2
View File
@@ -831,7 +831,7 @@ fn keywords(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
let typed = ui.data(|d| d.get_temp::<String>(kid).unwrap_or_default());
let last = typed.rsplit(',').next().unwrap_or("").trim().to_string();
// the keywords every selected photo has: those only some have are still suggested
let selection = app.session.selection.ids.clone();
let selection = app.session.targets(&serde_json::Value::Null);
let have: Vec<String> =
app.caches.keyword_chips(&app.session.catalog, &selection).iter().filter(|c| c.on_all()).map(|c| c.path.clone()).collect();
let suggestions = (*app.caches.suggestions(&app.session.catalog, &have, &last, 12)).clone();
@@ -859,7 +859,7 @@ fn keywords(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
/// selected photos; "Save as Set…" keeps the current nine under a name.
fn keyword_set(app: &mut LightcraftApp, ui: &mut egui::Ui) {
// on: every selected photo has it (⌥1–⌥9 then take it off them); partly on: only some do
let selection = app.session.selection.ids.clone();
let selection = app.session.targets(&serde_json::Value::Null);
let chips = app.caches.keyword_chips(&app.session.catalog, &selection);
let t = Tokens::get(ui.ctx());
let sets = lightcraft_engine::cmd::keywords::keyword_sets_json(&app.session);
+14
View File
@@ -176,3 +176,17 @@ fn chips_stay_on_one_line() {
assert!((x.center().y - r.center().y).abs() < 4.0, "{id}: its × is on its line");
}
}
/// With an active photo and nothing else selected, the box is that photo's, as the commands are:
/// its chips show (keywords typed go to it), and the Keyword List ticks what it has.
#[test]
fn the_active_photo_alone_is_the_selection() {
let (mut h, ids) = two_selected();
ask(&mut h, "engine.execute", json!({"command": "library.select", "params": {"ids": [], "active": ids[0]}}));
assert_eq!(h.app.session.selection.active.map(|p| p.0), Some(ids[0]));
assert!(h.app.session.selection.ids.is_empty());
assert!(has(&h, "keywordChip:Weddings") && !has(&h, "keywordChipPartial:Weddings"), "the active photo's keywords");
let ticks = crate::panels::keyword_list::Ticks::of(&h.app.session.catalog, &h.app.session.targets(&json!({})));
assert_eq!(ticks.tick("Weddings"), crate::panels::keyword_list::Tick::All);
assert!(has(&h, "keywordCheck:beach"));
}