mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-09 12:01:55 +00:00
Timeline: Alt held as a pick whip drops refers by index (refs #1072)
A property or expression pick whip dropped with Alt held, onto the Timeline or Effect Controls, now sends byIndex: an effect's property is referred to by its index, as in After Effects. Test: an_alt_pick_whip_refers_to_an_effect_property_by_index (Alt and plain drops onto Effect Controls and the Timeline, into the expression being edited with a dimension, one undo step each).
This commit is contained in:
1 parent
f2cdad9edb
commit
25fc51f3ed
2 files changed
+72
No files matched your search
@@ -3052,6 +3052,10 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
if let Some(d) = d {
|
||||
params["target"]["dimension"] = json!(d);
|
||||
}
|
||||
// Alt held as it drops: an effect's properties by their index (#1072).
|
||||
if ctx.input(|i| i.modifiers.alt) {
|
||||
params["byIndex"] = json!(true);
|
||||
}
|
||||
if let Some((text, [a, b])) = into {
|
||||
// Into the expression being edited, which goes on being edited.
|
||||
params["expression"] = json!(text);
|
||||
|
||||
@@ -539,3 +539,71 @@ fn arrow_keys_move_the_caret_in_rename_fields() {
|
||||
let layer_name = h.state().session.active_comp().unwrap().layer(effectcraft_engine::project::LayerId(layer)).unwrap().name.clone();
|
||||
assert_eq!(layer_name, "Pla-te");
|
||||
}
|
||||
|
||||
/// A primary-button drag from `from` to `to` with `modifiers` held from the press to the release.
|
||||
fn drag_with(h: &mut Harness<'_, EffectcraftApp>, from: Pos2, to: Pos2, modifiers: Modifiers) {
|
||||
h.input_mut().events.push(Event::PointerMoved(from));
|
||||
h.input_mut().events.push(Event::ModifiersChanged(modifiers));
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::PointerButton { pos: from, button: PointerButton::Primary, pressed: true, modifiers });
|
||||
h.step();
|
||||
for k in 1..=8 {
|
||||
h.input_mut().events.push(Event::PointerMoved(from + (to - from) * (k as f32 / 8.0)));
|
||||
h.step();
|
||||
}
|
||||
h.input_mut().events.push(Event::PointerButton { pos: to, button: PointerButton::Primary, pressed: false, modifiers });
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::ModifiersChanged(Modifiers::NONE));
|
||||
h.run_steps(3);
|
||||
}
|
||||
|
||||
/// #1072: with Alt held as it drops, a pick whip refers to an effect's property by its index,
|
||||
/// `effect("Slider Control")(1)`, as in After Effects; without Alt, by its name. Onto Effect
|
||||
/// Controls or the Timeline, and into the expression being edited (with a dimension).
|
||||
#[test]
|
||||
fn an_alt_pick_whip_refers_to_an_effect_property_by_index() {
|
||||
let (mut h, _) = harness();
|
||||
let (layer, opacity, transform, effects) = {
|
||||
let s = &mut h.state_mut().session;
|
||||
let l = s.execute("layer.newSolid", json!({"name": "Plate", "color": "#406080"})).unwrap()["layer"].as_u64().unwrap();
|
||||
s.execute("layer.select", json!({"layers": [l]})).unwrap();
|
||||
for fx in ["Slider Control", "Point Control"] {
|
||||
s.execute("effect.apply", json!({"layers": [l], "effect": fx})).unwrap();
|
||||
}
|
||||
let props = s.active_comp().unwrap().layer(effectcraft_engine::project::LayerId(l)).unwrap().props.clone();
|
||||
let effects: Vec<u64> = std::iter::once(props.sub("effects").unwrap().uid).chain(props.sub("effects").unwrap().groups().map(|g| g.uid)).collect();
|
||||
(l, props.sub("transform").unwrap().get("opacity").unwrap().uid, props.sub("transform").unwrap().uid, effects)
|
||||
};
|
||||
h.state_mut().ui.timeline.open_layers.insert(layer);
|
||||
h.state_mut().ui.timeline.open_groups.insert(transform);
|
||||
h.state_mut().ui.timeline.open_groups.extend(effects);
|
||||
h.state_mut().show_panel(effectcraft_ui_egui::dock::PanelKind::EffectControls);
|
||||
h.run_steps(3);
|
||||
let props =
|
||||
|h: &Harness<'_, EffectcraftApp>| h.state().session.active_comp().unwrap().layer(effectcraft_engine::project::LayerId(layer)).unwrap().props.clone();
|
||||
let (slider, point) = (props(&h).prop("effects/#1/slider").unwrap().uid, props(&h).prop("effects/#2/point").unwrap().uid);
|
||||
let expr = |h: &Harness<'_, EffectcraftApp>| props(h).find(opacity).unwrap().expr.clone().map(|e| e.text);
|
||||
let alt = Modifiers { alt: true, ..Modifiers::NONE };
|
||||
let whip = |h: &mut Harness<'_, EffectcraftApp>, target: &str, modifiers: Modifiers| {
|
||||
let (from, to) = (center(h, &format!("timeline.prop.{opacity}.propertyPickWhip")), center(h, target));
|
||||
drag_with(h, from, to, modifiers);
|
||||
};
|
||||
whip(&mut h, &format!("effectControls.prop.{slider}.value"), alt);
|
||||
assert_eq!(expr(&h).as_deref(), Some("effect(\"Slider Control\")(1)"), "Alt onto Effect Controls");
|
||||
let undo = h.state().session.history.undo.len();
|
||||
whip(&mut h, &format!("effectControls.prop.{slider}.value"), Modifiers::NONE);
|
||||
assert_eq!(expr(&h).as_deref(), Some("effect(\"Slider Control\")(\"Slider\")"), "without Alt, by name");
|
||||
assert_eq!(h.state().session.history.undo.len(), undo + 1, "one undo step");
|
||||
whip(&mut h, &format!("timeline.prop.{slider}.name"), alt);
|
||||
assert_eq!(expr(&h).as_deref(), Some("effect(\"Slider Control\")(1)"), "Alt onto the Timeline");
|
||||
// Into the expression being edited, at the cursor, with the value it was dropped on.
|
||||
let p = center(&h, &format!("timeline.prop.{opacity}.expression"));
|
||||
click(&mut h, p, 1);
|
||||
key(&mut h, Key::End);
|
||||
type_text(&mut h, " + ");
|
||||
let (from, to) = (center(&h, &format!("timeline.prop.{opacity}.pickWhip")), center(&h, &format!("effectControls.prop.{point}.value.1")));
|
||||
drag_with(&mut h, from, to, alt);
|
||||
assert_eq!(expr(&h).as_deref(), Some("effect(\"Slider Control\")(1) + effect(\"Point Control\")(1)[1]"));
|
||||
h.state_mut().session.execute("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(expr(&h).as_deref(), Some("effect(\"Slider Control\")(1)"), "undone in one step");
|
||||
}
|
||||
Reference in new issue
Block a user