mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-25 18:47:42 -06:00
LRC-LIB-KEYWORDS: the Keyword List follows its keywords
After a rename (Edit Keyword Tag), a drag or a merge made in the list, the picked keyword still named the old path (the highlight went and − was greyed out for a keyword that was there) and the renamed levels closed. The pick and the open levels now follow the keyword, and the levels containing it open, so a keyword dropped into another stays in sight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
92730c9246
commit
23ce79009d
@@ -1169,7 +1169,15 @@ pub fn confirm_dialog(app: &mut LightcraftApp, dlg: &Dialog) -> Result<serde_jso
|
||||
Some(keyword) => {
|
||||
params["keyword"] = json!(keyword);
|
||||
params["name"] = json!(name);
|
||||
app.run("keyword.edit", params)
|
||||
let r = app.run("keyword.edit", params);
|
||||
if r.is_ok() {
|
||||
let to = match keyword.rsplit_once('|') {
|
||||
Some((parent, _)) => format!("{parent}|{}", name.trim()),
|
||||
None => name.trim().to_string(),
|
||||
};
|
||||
crate::panels::keyword_list::follow(app, keyword, &to);
|
||||
}
|
||||
r
|
||||
}
|
||||
None => {
|
||||
params["name"] = json!(name);
|
||||
@@ -1180,7 +1188,15 @@ pub fn confirm_dialog(app: &mut LightcraftApp, dlg: &Dialog) -> Result<serde_jso
|
||||
}
|
||||
}
|
||||
Dialog::DeleteKeyword { keyword, .. } => app.run("keyword.delete", json!({"keyword": keyword})),
|
||||
Dialog::MoveKeyword { keyword, parent } => app.run("keyword.move", json!({"keyword": keyword, "parent": parent, "merge": true})),
|
||||
Dialog::MoveKeyword { keyword, parent } => {
|
||||
let r = app.run("keyword.move", json!({"keyword": keyword, "parent": parent, "merge": true}));
|
||||
if r.is_ok() {
|
||||
let leaf = keyword.rsplit('|').next().unwrap_or(keyword);
|
||||
let to = parent.as_deref().map_or_else(|| leaf.to_string(), |p| format!("{p}|{leaf}"));
|
||||
crate::panels::keyword_list::follow(app, keyword, &to);
|
||||
}
|
||||
r
|
||||
}
|
||||
Dialog::MergeKeywords { from, into } => app.run("keyword.merge", json!({"from": from, "into": into})),
|
||||
Dialog::AutoStack { gap } => app.run("stack.auto", json!({"gap": gap})),
|
||||
Dialog::DenoiseModel { info, accepted } => crate::panels::denoise::install(app, info, *accepted),
|
||||
|
||||
@@ -89,6 +89,30 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
}
|
||||
|
||||
/// After the keyword `from` became `to` (renamed, moved or merged), the list's pick and open
|
||||
/// levels follow it, and the levels containing it open so that it stays in sight.
|
||||
pub(crate) fn follow(app: &mut LightcraftApp, from: &str, to: &str) {
|
||||
use lightcraft_catalog::keywords::{is_under, reparent};
|
||||
let to = app.session.catalog.keyword_path(to).unwrap_or_else(|| to.to_string());
|
||||
if let Some(k) = app.ui.keyword_list_selected.clone()
|
||||
&& is_under(&k, from)
|
||||
{
|
||||
app.ui.keyword_list_selected = Some(reparent(&k, from, &to));
|
||||
}
|
||||
for o in &mut app.ui.keyword_list_open {
|
||||
if is_under(o, from) {
|
||||
*o = reparent(o, from, &to).to_lowercase();
|
||||
}
|
||||
}
|
||||
let levels: Vec<&str> = to.split('|').collect();
|
||||
for n in 1..levels.len() {
|
||||
let parent = levels.get(..n).map(|l| l.join("|").to_lowercase()).unwrap_or_default();
|
||||
if !app.ui.keyword_list_open.iter().any(|o| same(o, &parent)) {
|
||||
app.ui.keyword_list_open.push(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The pointer is on the part of `rect` that is shown: inside it and inside the panel's visible
|
||||
/// (scrolled) area, so a drop never lands on a row or title hidden under the top bar.
|
||||
fn shown_under(ui: &egui::Ui, rect: Rect, pointer: Option<egui::Pos2>) -> bool {
|
||||
@@ -125,8 +149,9 @@ fn drop_keyword(app: &mut LightcraftApp, ctx: &egui::Context, keyword: &str, par
|
||||
app.ui.dialog = Some(Dialog::MoveKeyword { keyword: keyword.to_string(), parent });
|
||||
return;
|
||||
}
|
||||
if let Err(e) = app.run("keyword.move", json!({"keyword": keyword, "parent": parent})) {
|
||||
app.toast(ctx, e);
|
||||
match app.run("keyword.move", json!({"keyword": keyword, "parent": parent})) {
|
||||
Ok(_) => follow(app, keyword, &to),
|
||||
Err(e) => app.toast(ctx, e),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ fn dragging_a_keyword_nests_it_and_back() {
|
||||
drag(&mut h, "keywordRow:Lisbon", "keywordRow:Portugal");
|
||||
assert_eq!(keywords_of(&h, ids[0]), ["Portugal|Lisbon"]);
|
||||
assert!(h.app.ui.dragging_keyword.is_none(), "the drag ended");
|
||||
ask(&mut h, "ui.clickWidget", json!({"id": "keywordRowToggle:Portugal"}));
|
||||
assert!(has(&h, "keywordRow:Portugal|Lisbon"), "Portugal opened to show it");
|
||||
drag(&mut h, "keywordRow:Portugal|Lisbon", "keywordList:topLevel");
|
||||
assert_eq!(keywords_of(&h, ids[0]), ["Lisbon"]);
|
||||
}
|
||||
@@ -319,3 +319,27 @@ fn return_in_synonyms_creates_the_keyword() {
|
||||
assert_eq!(h.app.ui.dialog, None);
|
||||
assert_eq!(info(&mut h, "Weddings")["synonyms"], json!(["marriage"]));
|
||||
}
|
||||
|
||||
/// The picked keyword and the open levels follow a rename or a move made in the list: renamed,
|
||||
/// Events stays open and picked; dropped into Portugal, Lisbon stays in sight and picked.
|
||||
#[test]
|
||||
fn the_list_follows_its_keywords() {
|
||||
let (mut h, ids) = keywords_panel_at([1400.0, 2400.0]);
|
||||
run(&mut h, "keyword.create", json!({"name": "Weddings", "parent": "Events"}));
|
||||
ask(&mut h, "ui.clickWidget", json!({"id": "keywordRowToggle:Events"}));
|
||||
// a moment later: the triangle's click and the row's two aren't a triple click
|
||||
for _ in 0..40 {
|
||||
h.step();
|
||||
}
|
||||
ask(&mut h, "ui.clickWidget", json!({"id": "keywordRow:Events", "count": 2}));
|
||||
ask(&mut h, "ui.text", json!({"text": "Occasions"}));
|
||||
ask(&mut h, "ui.key", json!({"key": "Enter"}));
|
||||
assert!(has(&h, "keywordRow:Occasions|Weddings"), "still open");
|
||||
assert_eq!(h.app.ui.keyword_list_selected.as_deref(), Some("Occasions"), "still picked");
|
||||
run(&mut h, "photo.setMeta", json!({"ids": [ids[0]], "keywords": ["Lisbon"]}));
|
||||
run(&mut h, "keyword.create", json!({"name": "Portugal", "parent": null}));
|
||||
ask(&mut h, "ui.clickWidget", json!({"id": "keywordRow:Lisbon"}));
|
||||
drag(&mut h, "keywordRow:Lisbon", "keywordRow:Portugal");
|
||||
assert!(has(&h, "keywordRow:Portugal|Lisbon"), "its new parent opened");
|
||||
assert_eq!(h.app.ui.keyword_list_selected.as_deref(), Some("Portugal|Lisbon"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user