LRC-LIB-KEYWORDS: the Add keyword box is a shared text field

It gets the Cut / Copy / Paste / Select All menu (paste a list of
keywords from elsewhere), Return gives the keywords to every selected
photo as before, and Esc gives up what was typed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nathaniel Ledford
2026-10-09 15:18:10 -04:00
co-authored by Claude Opus 5.5
parent a3b7c4caf4
commit f83ac3377e
2 changed files with 28 additions and 3 deletions
+7 -3
View File
@@ -773,9 +773,13 @@ fn keywords(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
padded(ui, |ui| {
let kid = egui::Id::new("kw-input");
let mut text = ui.data_mut(|d| d.get_temp::<String>(kid).unwrap_or_default());
let r = ui.add(egui::TextEdit::singleline(&mut text).hint_text(crate::i18n::tr("Add keyword")).desired_width(f32::INFINITY));
register(ui.ctx(), "field:keyword", r.rect);
if r.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) && !text.trim().is_empty() {
let r =
crate::text_field::TextField::singleline("field:keyword", &mut text).hint(crate::i18n::tr("Add keyword")).width(f32::INFINITY).show(ui);
// Return gives the keywords to the selected photos; Esc gives up what was typed
if r.cancelled() {
text.clear();
}
if r.ending == Some(crate::text_field::Ending::Return) && !text.trim().is_empty() {
// a new name goes inside the default parent (Put New Keywords Inside This Keyword)
let kws: Vec<String> = text.split(',').map(|s| app.session.catalog.typed_keyword(s)).filter(|s| !s.is_empty()).collect();
let _ = app.run("photo.setMeta", json!({"addKeywords": kws}));
+21
View File
@@ -86,3 +86,24 @@ fn a_chips_menu_acts_on_the_selection() {
ask(&mut h, "ui.clickWidget", json!({"id": "keywordChipMenu:edit"}));
assert!(matches!(&h.app.ui.dialog, Some(crate::state::Dialog::KeywordTag { editing: Some(k), .. }) if k == "Weddings"), "{:?}", h.app.ui.dialog);
}
/// The "Add keyword" box is a shared text field: right-click ▸ Paste fills it, Return gives the
/// keywords to every selected photo, and Esc gives up what was typed.
#[test]
fn the_add_keyword_box_pastes_adds_and_gives_up() {
let (mut h, ids) = two_selected();
h.view.clipboard = "travel, sunset".into();
ask(&mut h, "ui.clickWidget", json!({"id": "field:keyword", "button": "right"}));
ask(&mut h, "ui.clickWidget", json!({"id": "field:keyword:paste"}));
ask(&mut h, "ui.key", json!({"key": "Enter"}));
for id in &ids {
let k = keywords_of(&h, *id);
assert!(k.contains(&"travel".to_string()) && k.contains(&"sunset".to_string()), "{k:?}");
}
ask(&mut h, "ui.clickWidget", json!({"id": "field:keyword"}));
ask(&mut h, "ui.text", json!({"text": "harbour"}));
ask(&mut h, "ui.key", json!({"key": "Escape"}));
ask(&mut h, "ui.clickWidget", json!({"id": "field:keyword"}));
ask(&mut h, "ui.key", json!({"key": "Enter"}));
assert!(ids.iter().all(|id| !keywords_of(&h, *id).contains(&"harbour".to_string())), "Esc gave it up");
}