Plug-ins: hit tests re-evaluate live art when a swatch or color group changes

A painted live effect's pieces name swatches, so a kept evaluation could outlive a swatch that was renamed or removed and answer a click with art the document no longer draws. The kept evaluation now holds the document's swatches and color groups and is stale when they change.
This commit is contained in:
dotCooCoo
2026-11-08 19:13:37 -08:00
parent 3123b4af74
commit f29c75a231
2 changed files with 49 additions and 5 deletions
+18 -4
View File
@@ -128,11 +128,14 @@ fn live_inputs() -> u64 {
}
/// A live object evaluated by [`live_art`] on this thread: the object, its document's symbols (by
/// name and art) and patterns and the [`LiveInputs`] as they were, the art and its [`art_size`].
/// name and art), patterns, swatches and color groups and the [`LiveInputs`] as they were, the art
/// and its [`art_size`].
struct EvaluatedLive {
node: Node,
symbols: Vec<(String, Arc<Node>)>,
patterns: Vec<crate::PatternDef>,
swatches: crate::Shared<Vec<vectorcraft_color::Swatch>>,
swatch_groups: crate::Shared<Vec<vectorcraft_color::SwatchGroup>>,
inputs: u64,
art: Rc<Vec<Node>>,
size: usize,
@@ -146,6 +149,8 @@ impl EvaluatedLive {
&& self.symbols.iter().zip(&doc.symbols).all(|((name, art), s)| *name == s.name && Arc::ptr_eq(art, &s.art))
&& self.node == *n
&& self.patterns == doc.patterns
&& self.swatches == doc.swatches
&& self.swatch_groups == doc.swatch_groups
}
}
@@ -184,8 +189,8 @@ thread_local! {
/// The art live object `n` of `doc` draws ([`set_live_art`]); [`crate::live::expand_live`] while
/// nothing is registered. The art is evaluated again when `n`, a symbol of `doc`, a pattern of
/// `doc` or the [`LiveInputs`] have changed since, and on every click while it or the document's
/// symbols or patterns hold a placed document ([`holds_placed`]).
/// `doc`, its swatches or color groups, or the [`LiveInputs`] have changed since, and on every
/// click while it or the document's symbols or patterns hold a placed document ([`holds_placed`]).
fn live_art(doc: &Document, n: &Node) -> Rc<Vec<Node>> {
let inputs = live_inputs();
let kept = EVALUATED.with_borrow_mut(|kept| {
@@ -206,7 +211,16 @@ fn live_art(doc: &Document, n: &Node) -> Rc<Vec<Node>> {
return art;
}
let symbols = doc.symbols.iter().map(|s| (s.name.clone(), s.art.clone())).collect();
let e = EvaluatedLive { node: n.clone(), symbols, patterns: doc.patterns.clone(), inputs, art: art.clone(), size };
let e = EvaluatedLive {
node: n.clone(),
symbols,
patterns: doc.patterns.clone(),
swatches: doc.swatches.clone(),
swatch_groups: doc.swatch_groups.clone(),
inputs,
art: art.clone(),
size,
};
EVALUATED.with_borrow_mut(|kept| {
kept.insert(0, e);
kept.truncate(LIVE_KEPT);
+31 -1
View File
@@ -605,11 +605,41 @@ fn evaluated_art_is_not_kept_while_a_placed_document_may_show_in_it() {
fn a_kept_evaluation_is_stale_after_the_live_inputs_change() {
let d = Document::new(100.0, 100.0);
let n = Node::path(NodeId(3), shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0)), Appearance::default_art());
let e = EvaluatedLive { node: n.clone(), symbols: vec![], patterns: vec![], inputs: 7, art: std::rc::Rc::new(vec![]), size: 0 };
let e = kept_evaluation(&d, &n);
assert!(e.is_of(&d, &n, 7));
assert!(!e.is_of(&d, &n, 8));
}
/// An evaluation of `n` in `d`, with inputs 7, holding no symbols, patterns or art.
fn kept_evaluation(d: &Document, n: &Node) -> EvaluatedLive {
EvaluatedLive {
node: n.clone(),
symbols: vec![],
patterns: vec![],
swatches: d.swatches.clone(),
swatch_groups: d.swatch_groups.clone(),
inputs: 7,
art: std::rc::Rc::new(vec![]),
size: 0,
}
}
/// A kept evaluation is stale once a swatch changes, in a color group too: a painted live effect's
/// pieces name swatches.
#[test]
fn a_kept_evaluation_is_stale_after_a_swatch_changes() {
let mut d = Document::new(100.0, 100.0);
let n = Node::path(NodeId(3), shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0)), Appearance::default_art());
let e = kept_evaluation(&d, &n);
assert!(e.is_of(&d, &n, 7));
d.swatches.first_mut().unwrap().name = "Renamed".into();
assert!(!e.is_of(&d, &n, 7));
let mut d = Document::new(100.0, 100.0);
let e = kept_evaluation(&d, &n);
d.swatch_groups.push(vectorcraft_color::SwatchGroup { name: "Added".into(), swatches: vec![] });
assert!(!e.is_of(&d, &n, 7));
}
/// A rectangle (40, 40)–(140, 140) clipping a filled 200 × 200 square drawn over its edges, in a
/// clip group (or, `layer`, as the first layer's clipping mask): the group (or the layer), the
/// clipping path and the art.