mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Timeline: a selected key selects its property and layer (#252)
Clicking (or marquee-selecting) keyframes in the Timeline selected only the keys. Their property and layer stayed unselected, so the Graph Editor, which shows the selected properties or else the animated properties of the selected layers, came up empty until the property was selected separately. As in After Effects, the Timeline's key selection now selects the keys' properties and layers too (Shift adds to them). keys.select takes this as `selectProperties`; the Graph Editor's own key clicks leave what it shows alone. Test: a_key_clicked_in_the_timeline_shows_in_the_graph_editor (ui_keyframes). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
72a2d47131
commit
8d5e4ae0ef
3 files changed
+38
-3
No files matched your search
@@ -387,6 +387,24 @@ fn select_keys(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
} else {
|
||||
s.state.selected_keys = sel;
|
||||
}
|
||||
// From the Timeline, selected keys select their properties and layers too, as in After
|
||||
// Effects, so the Graph Editor shows them (#252).
|
||||
if b_p(p, "selectProperties").unwrap_or(false) && !s.state.selected_keys.is_empty() {
|
||||
let extend = b_p(p, "add").unwrap_or(false) || b_p(p, "toggle").unwrap_or(false);
|
||||
let st = &mut s.state;
|
||||
if !extend {
|
||||
st.selected_props.clear();
|
||||
st.selected_layers.clear();
|
||||
}
|
||||
for k in &st.selected_keys {
|
||||
if !st.selected_props.contains(&(k.layer, k.prop)) {
|
||||
st.selected_props.push((k.layer, k.prop));
|
||||
}
|
||||
if !st.selected_layers.contains(&k.layer) {
|
||||
st.selected_layers.push(k.layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(json!(s.state.selected_keys.len()))
|
||||
}
|
||||
|
||||
@@ -780,7 +798,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Select Keyframes",
|
||||
[],
|
||||
None,
|
||||
"{keys: [{layer, prop: uid | path (or `path`), time (layer s)}], add?, toggle?: bool (Shift+click: in or out of the selection)}",
|
||||
"{keys: [{layer, prop: uid | path (or `path`), time (layer s)}], add?, toggle?: bool (Shift+click: in or out of the selection), selectProperties?: bool (their properties and layers too, as a Timeline click does)}",
|
||||
has_comp,
|
||||
select_keys
|
||||
),
|
||||
|
||||
@@ -2094,7 +2094,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
let kresp = ui.interact(kr, egui::Id::new(("key", uid, k.time.0)), Sense::click_and_drag());
|
||||
app.auto.add(&format!("timeline.key.{uid}.{}", fr.frame_at(ct)), kr, &format!("{} key", prop.name));
|
||||
let mods = ui.input(|i| i.modifiers);
|
||||
let this_key = json!({"keys": [{"layer": layer.id.0, "prop": uid, "time": k.time.seconds()}]});
|
||||
let this_key = json!({"keys": [{"layer": layer.id.0, "prop": uid, "time": k.time.seconds()}], "selectProperties": true});
|
||||
if kresp.clicked() && mods.command {
|
||||
// Ctrl+click: Linear ↔ Auto Bezier; Ctrl+Alt+click: Hold on / off.
|
||||
actions.push(("keys.select".into(), this_key.clone()));
|
||||
@@ -2375,7 +2375,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
}
|
||||
}
|
||||
}
|
||||
actions.push(("keys.select".into(), json!({"keys": keys, "add": ui.input(|i| i.modifiers.shift)})));
|
||||
actions.push(("keys.select".into(), json!({"keys": keys, "add": ui.input(|i| i.modifiers.shift), "selectProperties": true})));
|
||||
}
|
||||
|
||||
// CTI.
|
||||
|
||||
@@ -148,6 +148,23 @@ fn shift_click_toggles_and_ctrl_click_switches_interpolation() {
|
||||
assert_eq!(key(&h).out_interp, effectcraft_engine::keyframe::Interp::Hold);
|
||||
}
|
||||
|
||||
/// Clicking a key in the Timeline selects its property and layer too, as in After Effects, so
|
||||
/// the Graph Editor shows the key's curve (#252).
|
||||
#[test]
|
||||
fn a_key_clicked_in_the_timeline_shows_in_the_graph_editor() {
|
||||
let (mut h, id, uid) = harness();
|
||||
h.state_mut().session.execute("edit.deselectAll", json!({})).unwrap();
|
||||
h.run_steps(2);
|
||||
let ks = keys(&h, uid);
|
||||
click_with(&mut h, ks[1], Default::default());
|
||||
let st = &h.state().session.state;
|
||||
assert_eq!(st.selected_keys.len(), 1);
|
||||
assert_eq!((st.selected_props.clone(), st.selected_layers.clone()), (vec![(id, uid)], vec![id]), "the key's property and layer");
|
||||
h.state_mut().ui.timeline.graph_editor = true;
|
||||
h.run_steps(4);
|
||||
assert!(h.state().auto.find(&format!("timeline.graph.key.{uid}.0.1")).is_some(), "the Graph Editor shows the key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_editor_drags_move_every_selected_key_and_shift_keeps_an_axis() {
|
||||
let (mut h, id, uid) = harness();
|
||||
|
||||
Reference in new issue
Block a user