mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-29 20:47:18 -06:00
fix(script): per-key methods act on keys outside the active comp (#1531)
`exec_on_keys` put the named keys in the key selection, then `exec` made the keys' comp active. Since comps keep their own selections (#1037), activating the comp stored the injected keys with the comp that had been active and brought back the target comp's own key selection, usually empty, so `keys.interpolation` and `keys.velocity` had nothing to act on: setInterpolationTypeAtKey and setTemporalEaseAtKey were silently ignored in any comp other than the active one. The keys' comp is now made active before the keys are selected, and the comp that was active comes back afterwards, as `exec` does for its own target (now `leave_comp`).
This commit is contained in:
@@ -157,11 +157,16 @@ pub(crate) fn exec(s: &mut Session, id: &str, params: J) -> Result<J, String> {
|
||||
s.state.selected_layers.clear();
|
||||
}
|
||||
let r = s.execute_for_script(id, params);
|
||||
if let Some(c) = target
|
||||
&& prev != Some(c)
|
||||
&& s.state.active_comp == Some(c)
|
||||
&& id != "comp.open"
|
||||
{
|
||||
if let Some(c) = target {
|
||||
leave_comp(s, id, c, prev);
|
||||
}
|
||||
r.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// After a script's command on comp `c`, make `prev` (the comp active before it) active again,
|
||||
/// unless the command opened `c` on purpose; with no comp to go back to, `c` stays open.
|
||||
fn leave_comp(s: &mut Session, id: &str, c: effectcraft_engine::project::ItemId, prev: Option<effectcraft_engine::project::ItemId>) {
|
||||
if prev != Some(c) && s.state.active_comp == Some(c) && id != "comp.open" {
|
||||
match prev {
|
||||
Some(p) if s.project.comp(p).is_some() => s.activate_comp(p),
|
||||
_ => {
|
||||
@@ -171,7 +176,6 @@ pub(crate) fn exec(s: &mut Session, id: &str, params: J) -> Result<J, String> {
|
||||
}
|
||||
}
|
||||
}
|
||||
r.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Run a keyframe command that acts on the selected keys (`keys.velocity`, `keys.interpolation`,
|
||||
@@ -195,11 +199,17 @@ fn exec_on_keys(s: &mut Session, id: &str, params: J, targets: &J) -> Result<J,
|
||||
let key = l.props.find(prop).and_then(|pr| pr.keys.get(usize::try_from(index).ok()?)).ok_or("the keyframe no longer exists")?;
|
||||
keys.push(KeyRef { layer: LayerId(layer), prop, time: key.time });
|
||||
}
|
||||
// Make the keys' comp active before selecting them: activating a comp brings back the key
|
||||
// selection it had, which would replace these keys and leave the command nothing to act on
|
||||
// when the comp isn't the active one (#1531).
|
||||
let prev = s.state.active_comp;
|
||||
s.activate_comp(cid);
|
||||
let saved = std::mem::replace(&mut s.state.selected_keys, keys);
|
||||
let r = exec(s, id, params);
|
||||
let comp = s.active_comp();
|
||||
let exists = |k: &KeyRef| comp.and_then(|c| c.layer(k.layer)).and_then(|l| l.props.find(k.prop)).is_some_and(|pr| pr.keys.iter().any(|x| x.time == k.time));
|
||||
s.state.selected_keys = saved.into_iter().filter(exists).collect();
|
||||
leave_comp(s, id, cid, prev);
|
||||
r
|
||||
}
|
||||
|
||||
|
||||
@@ -1258,6 +1258,36 @@ fn effect_parameter_interpolation_and_ease() {
|
||||
assert_eq!(o.result, json!([true, true, 16.67, 33.33, true]));
|
||||
}
|
||||
|
||||
/// #1531: the per-key methods act on a comp that isn't the active one too. Activating the keys'
|
||||
/// comp brought back its own (empty) key selection over the keys named, so the interpolation and
|
||||
/// the ease were silently dropped once another comp with a layer had been made first.
|
||||
#[test]
|
||||
fn key_interpolation_and_ease_apply_outside_the_active_comp() {
|
||||
let mut s = session();
|
||||
let o = ok(
|
||||
&mut s,
|
||||
r#"
|
||||
var a = app.project.items.addComp("A", 400, 400, 1, 10, 20); a.layers.addNull();
|
||||
var c = app.project.items.addComp("C", 400, 400, 1, 10, 20);
|
||||
var n = c.layers.addNull(); n.threeDLayer = true;
|
||||
var p = n.property("ADBE Transform Group").property("ADBE Rotate Y");
|
||||
p.setValueAtTime(1, 0); p.setValueAtTime(2, -178);
|
||||
for (var j = 1; j <= 2; j++) {
|
||||
p.setInterpolationTypeAtKey(j, KeyframeInterpolationType.BEZIER, KeyframeInterpolationType.BEZIER);
|
||||
p.setTemporalEaseAtKey(j, [new KeyframeEase(0, 60)], [new KeyframeEase(0, 60)]);
|
||||
}
|
||||
[p.keyOutInterpolationType(1) == KeyframeInterpolationType.BEZIER,
|
||||
p.keyInInterpolationType(2) == KeyframeInterpolationType.BEZIER,
|
||||
p.keyOutTemporalEase(1)[0].influence,
|
||||
Math.round(p.valueAtTime(1.25, false) * 100) / 100]
|
||||
"#,
|
||||
);
|
||||
let v = o.result;
|
||||
assert_eq!((&v[0], &v[1], &v[2]), (&json!(true), &json!(true), &json!(60)), "{v}");
|
||||
// Eased, not the linear -44.5.
|
||||
assert!(v[3].as_f64().is_some_and(|x| (x - -44.5).abs() > 1.0), "{v}");
|
||||
}
|
||||
|
||||
/// #808: sourcePointToComp and compPointToSource follow the layer's transform.
|
||||
#[test]
|
||||
fn source_point_conversions_follow_the_layer_transform() {
|
||||
|
||||
Reference in New Issue
Block a user