diff --git a/crates/ui-egui/src/panels/links.rs b/crates/ui-egui/src/panels/links.rs index af85690e9d..5c95e17bc7 100644 --- a/crates/ui-egui/src/panels/links.rs +++ b/crates/ui-egui/src/panels/links.rs @@ -344,7 +344,31 @@ fn badge(ui: &Ui, r: egui::Rect, status: &str) { } } -fn status_tip(r: &Value) -> String { +/// The tooltip of row `r`. A row outside the layer tree also names where it is stored +/// (`links.list`'s `owner`): the symbol, graph design or pattern whose art holds it, and the object +/// whose opacity mask holds it, by `mask` (that object's name) or else by its id. +pub(super) fn status_tip(r: &Value, mask: Option) -> String { + let tip = file_tip(r); + let owner = &r["owner"]; + let name = owner["name"].as_str().unwrap_or_default(); + let art = match owner["kind"].as_str() { + Some("symbol") => Some(tl!("Symbols")), + Some("graphDesign") => Some(tl!("Graph Design")), + Some("pattern") => Some(tl!("Pattern")), + _ => None, + }; + let tip = match art { + Some(kind) => format!("{tip}\n{kind}: {name}"), + None => tip, + }; + match owner["maskOf"].as_u64() { + Some(id) => format!("{tip}\n{}: {}", tl!("Opacity Mask"), mask.unwrap_or_else(|| id.to_string())), + None => tip, + } +} + +/// The file, status and notes part of [`status_tip`]. +fn file_tip(r: &Value) -> String { let path = r["found"].as_str().or(r["path"].as_str()).unwrap_or_default(); let tip = match r["status"].as_str().unwrap_or_default() { "missing" => crate::i18n::fmt(tl!("Missing: {path}"), &[("path", path)]), @@ -495,7 +519,12 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { egui::FontId::proportional(12.5), t.text, ); - let resp = resp.on_hover_text(status_tip(r)); + let mask = r["owner"]["maskOf"] + .as_u64() + .and_then(|m| doc.stored_node(NodeId(m))) + .map(|(n, _)| n) + .map(|n| super::layers::shown_name(app, n, crate::i18n::current())); + let resp = resp.on_hover_text(status_tip(r, mask)); if resp.double_clicked() { clicked = Some((id, egui::Modifiers::NONE, true)); } else if resp.clicked() { diff --git a/crates/ui-egui/src/panels/tests_links.rs b/crates/ui-egui/src/panels/tests_links.rs index be5e17e374..89dfba5f43 100644 --- a/crates/ui-egui/src/panels/tests_links.rs +++ b/crates/ui-egui/src/panels/tests_links.rs @@ -178,6 +178,24 @@ fn a_link_inside_a_symbol_is_listed_and_picked_in_the_panel() { assert!(selected(&app).is_empty(), "the image in the layers is no longer selected"); } +/// The tooltip of a row outside the layer tree says where the image is stored, so two rows of the +/// same file in different symbols or masks can be told apart. +#[test] +fn a_nested_rows_tooltip_names_what_holds_it() { + let tip = |owner: serde_json::Value, mask: Option<&str>| { + let row = json!({"id": 9, "name": "a.png", "status": "ok", "path": "/x/a.png", "owner": owner}); + super::links::status_tip(&row, mask.map(str::to_string)) + }; + assert_eq!(tip(json!({"kind": "symbol", "name": "Mark"}), None), "/x/a.png\nSymbols: Mark"); + assert_eq!(tip(json!({"kind": "pattern", "name": "Dots"}), None), "/x/a.png\nPattern: Dots"); + assert_eq!(tip(json!({"kind": "graphDesign", "name": "Bar"}), None), "/x/a.png\nGraph Design: Bar"); + assert_eq!(tip(json!({"kind": "layers", "maskOf": 7}), Some("")), "/x/a.png\nOpacity Mask: "); + assert_eq!(tip(json!({"kind": "symbol", "name": "Mark", "maskOf": 7}), None), "/x/a.png\nSymbols: Mark\nOpacity Mask: 7"); + // A row in the layers says only its file. + let row = json!({"id": 9, "name": "a.png", "status": "ok", "path": "/x/a.png"}); + assert_eq!(super::links::status_tip(&row, None), "/x/a.png"); +} + #[test] fn an_empty_document_shows_how_to_add_images() { let mut app = VectorcraftApp::new(Session::new(), Default::default());