From ca5e2003a7cca0e45e3bf5b7eb9779dcc495a8aa Mon Sep 17 00:00:00 2001 From: tylerjenningsw Date: Sat, 17 Oct 2026 17:03:20 -0400 Subject: [PATCH] Puppet: a value edit on one selected pin reaches the other selected pins (#1751) --- crates/engine/src/cmd/prop.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/cmd/prop.rs b/crates/engine/src/cmd/prop.rs index 00232967e..0c0d4cd22 100644 --- a/crates/engine/src/cmd/prop.rs +++ b/crates/engine/src/cmd/prop.rs @@ -228,7 +228,8 @@ fn property_reply(p: &Value, layer: &Layer, pr: &Property, time: Tick, value: Va /// property (by match path, `transform/position`, `effects/#n/`) of every other /// selected, unlocked layer of the comp whose value is of the same kind, as (layer, uid). Empty /// when `selected` is not set or the edited layer isn't selected. After Effects applies a value -/// edit, the stopwatch and the keyframe button to all selected layers (#355, #491). +/// edit, the stopwatch and the keyframe button to all selected layers (#355, #491). A parameter +/// of a selected Puppet pin also reaches the same parameter of the other selected pins (#1751). fn selected_twins(s: &Session, p: &Value, cmd: &str) -> Result> { if b_p(p, "selected") != Some(true) { return Ok(vec![]); @@ -241,14 +242,31 @@ fn selected_twins(s: &Session, p: &Value, cmd: &str) -> Result = s + .state .selected_layers .iter() .filter(|o| **o != lid) .filter_map(|o| comp.layer(*o)) .filter(|l| !l.switches.locked) - .filter_map(|l| l.props.prop(&path).filter(|q| (q.value.kind_name(), q.value.components().len()) == kind).map(|q| (l.id, q.uid))) - .collect()) + .filter_map(|l| l.props.prop(&path).filter(same_kind).map(|q| (l.id, q.uid))) + .collect(); + // A parameter of a selected Puppet pin: the same parameter of every other selected pin, on + // this layer too, so several pins move or key together (#1751). + let pin = super::puppet::layer_pins(layer).find(|g| g.props().any(|q| q.uid == uid)).map(|g| g.uid); + if let Some(pin) = pin + && s.state.selected_props.contains(&(lid, pin)) + { + for (ol, ou) in &s.state.selected_props { + let Some(other) = comp.layer(*ol).filter(|l| !l.switches.locked) else { continue }; + let Some(q) = super::puppet::layer_pins(other).find(|g| g.uid == *ou).and_then(|g| g.get(&pr.match_id)).filter(same_kind) else { continue }; + if (*ol, q.uid) != (lid, uid) && !twins.contains(&(*ol, q.uid)) { + twins.push((*ol, q.uid)); + } + } + } + Ok(twins) } /// `p` aimed at property `uid` of layer `lid` instead.