From fd4c187ab5212400deda73e997454449b14b6344 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 25 Oct 2026 10:02:03 -0700 Subject: [PATCH] Image Trace: settings that wait stay with their object when a plain image is selected With Preview off, selecting a plain image after an Image Trace object kept the panel on that object without saving its waiting settings. A trace of the image then made the image's trace the panel's object, and selecting the first object again showed its stored settings and lost the change. The panel now keeps the waiting settings for the object it leaves whatever is selected next. --- crates/ui-egui/src/panels/image_trace.rs | 44 ++++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/panels/image_trace.rs b/crates/ui-egui/src/panels/image_trace.rs index d2e21e7bb..83ca93893 100644 --- a/crates/ui-egui/src/panels/image_trace.rs +++ b/crates/ui-egui/src/panels/image_trace.rs @@ -302,14 +302,21 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { let object = target_key(app); let same = |a: &Value, b: &Value| a["preset"] == b["preset"] && a["params"] == b["params"]; let mut restored = false; + // Another object: the settings that wait for the one left are kept until it is selected again, + // also when a plain image or nothing is selected next. + if object != st.object + && let Some(left) = st.object + && !st.synced.is_null() + && st.params != st.synced["params"] + { + st.drafts.insert(left, (st.preset.clone(), st.params.clone(), st.synced.clone())); + } + // A plain image (or nothing) keeps the panel's settings, which no longer wait for the object left. + if stored.is_none() && object != st.object { + st.synced = Value::Null; + st.object = object; + } if let Some(t) = stored.filter(|t| *t != st.synced || object != st.object) { - // Another object: the settings that wait for the one left are kept until it is selected again. - if object != st.object - && let Some(left) = st.object - && st.params != st.synced["params"] - { - st.drafts.insert(left, (std::mem::take(&mut st.preset), std::mem::take(&mut st.params), st.synced.clone())); - } // A change of the object's view alone (here or in the Control bar) keeps the settings that // wait in the panel with Preview off. if object != st.object || !same(&t, &st.synced) { @@ -891,6 +898,29 @@ mod tests { assert_eq!(undo(&app), steps + 1, "traced once"); } + #[test] + fn settings_that_wait_are_kept_when_a_plain_image_is_traced_in_between() { + let mut app = traced_app(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let a = first_selected(&app).unwrap().id; + click(&mut app, &ctx, "Preview"); + click(&mut app, &ctx, "Strokes"); + let img = image::RgbaImage::from_fn(20, 20, |_, y| image::Rgba(if y < 10 { [0, 0, 0, 255] } else { [255, 255, 255, 255] })); + let mut png = vec![]; + img.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap(); + let data = vectorcraft_format::base64_encode(&png); + app.run("file.place", json!({"name": "plain.png", "dataBase64": data, "link": false})).unwrap(); + assert!(target(&app).1, "a plain image is selected"); + frame(&mut app, &ctx, vec![]); + click(&mut app, &ctx, "Strokes"); + click(&mut app, &ctx, "Trace"); + assert_eq!(target(&app).2.unwrap()["params"]["strokes"], json!(false), "the image is traced with the panel's settings"); + app.run("select.set", json!({"ids": [a.0]})).unwrap(); + frame(&mut app, &ctx, vec![]); + assert_eq!(pstate::(&ctx, "image-trace").params["strokes"], json!(true), "A's change still waits"); + } + #[test] fn settings_that_wait_are_dropped_when_their_object_changed_since() { let mut app = traced_app();