From 36622feda468b434d44ddd35ecbad3999323b222 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 8 Nov 2026 18:48:41 -0800 Subject: [PATCH] Tools: setting another tool's option leaves a drag's options alone tool.setOption on another tool reloaded every saved option of the active tool. During a shape drag that reload rewrote the options Escape restores, so Escape kept a side count an arrow key had changed. The active tool now reloads only the stores it shares with the tool that was set. --- crates/engine/src/tests/toolsettings.rs | 22 ++++++++++++++++++++++ crates/engine/src/tooling.rs | 14 +++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/tests/toolsettings.rs b/crates/engine/src/tests/toolsettings.rs index 8d5396199..e896a0ab3 100644 --- a/crates/engine/src/tests/toolsettings.rs +++ b/crates/engine/src/tests/toolsettings.rs @@ -268,6 +268,28 @@ fn escape_on_a_shape_drag_saves_the_option_it_put_back() { assert_eq!(s.prefs.corner_radius, radius); } +/// Setting another tool's option during a polygon drag leaves the drag's options alone: Escape +/// still puts back the side count the arrow changed. +#[test] +fn escape_puts_back_the_sides_after_another_tool_s_option_is_set() { + use vectorcraft_tools::{Mods, PointerEvent, PointerKind, ToolKey}; + let mut s = session(); + let v = ViewInfo { smart_guides: false, ..Default::default() }; + select(&mut s, "polygon"); + s.pointer(&PointerEvent::new(PointerKind::Down, 100.0, 100.0), v).unwrap(); + s.pointer(&PointerEvent::new(PointerKind::Drag, 200.0, 180.0), v).unwrap(); + s.tool_key(ToolKey::Up, Mods::default(), v).unwrap(); + // Saves every option, the one the arrow changed included. + s.set_tool_option("length", &json!(50)); + let values = json!({"points": 9}); + s.set_tool_options(Some("star"), values.as_object().unwrap()); + assert_eq!(s.tool_options()["sides"], json!(7), "the drag keeps its side count"); + s.tool_key(ToolKey::Escape, Mods::default(), v).unwrap(); + assert_eq!(s.tool_options()["sides"], json!(6)); + assert_eq!(s.prefs.tool_settings["polygon"]["sides"], json!(6)); + assert_eq!(s.prefs.tool_settings["star"]["points"], json!(9)); +} + /// The Fidelity an Alt smoothing drag uses on a selected path. fn alt_smooth_fidelity(s: &mut Session) -> Option { use vectorcraft_tools::{Action, Mods, PointerEvent, PointerKind}; diff --git a/crates/engine/src/tooling.rs b/crates/engine/src/tooling.rs index d195f74f9..807ef7510 100644 --- a/crates/engine/src/tooling.rs +++ b/crates/engine/src/tooling.rs @@ -257,7 +257,15 @@ impl Session { /// Give `t` the persistent options stored for it in `prefs`. fn restore_tool_settings(prefs: &Prefs, t: &mut dyn Tool) { + Self::restore_stores(prefs, t, |_| true); + } + + /// Give `t` the persistent options stored for it in `prefs`, from the stores `wanted` accepts. + fn restore_stores(prefs: &Prefs, t: &mut dyn Tool, wanted: impl Fn(&str) -> bool) { for (store, keys) in settings::stores(t.id()) { + if !wanted(store) { + continue; + } let Some(saved) = prefs.tool_settings.get(store) else { continue }; for k in keys { if let Some(v) = saved.get(*k) { @@ -267,6 +275,7 @@ impl Session { } // Alt switches the Pencil, Paintbrush or Blob Brush to Smooth, using Smooth Tool Options' Fidelity. if matches!(t.id(), "pencil" | "paintbrush" | "blobBrush") + && wanted("smooth") && let Some(v) = prefs.tool_settings.get("smooth").and_then(|s| s.get("fidelity")) { t.set_option("smoothFidelity", v); @@ -902,7 +911,10 @@ impl Session { } Self::restore_tool_settings(&self.prefs, lender); } - Self::restore_tool_settings(&self.prefs, self.tool.as_mut()); + // Only the stores it shares with the tool set: the active tool's own options stay as they + // are, also in the middle of a drag that Escape can cancel. + let set = other.id(); + Self::restore_stores(&self.prefs, self.tool.as_mut(), |store| settings::stores(set).any(|(s, _)| s == store)); } out }