#1427: ripple sequence markers by the removed head (they didn't move); params name clips/clip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-13 01:42:53 -04:00
parent 5ae8d50168
commit e6973f5957
2 changed files with 24 additions and 4 deletions

View File

@ -1677,7 +1677,7 @@ fn build() -> Vec<CommandSpec> {
})?;
Ok(Value::Null)
}),
cmd!("edit.rippleDeleteHead", "Ripple Delete Head Frame", ["Edit"], None, r#"{"clip":id}?"#, has_selection, |s, p| {
cmd!("edit.rippleDeleteHead", "Ripple Delete Head Frame", ["Edit"], None, r#"{"clips":[id]?,"clip":id?}"#, has_selection, |s, p| {
// #1357: remove the head — from each selected clip's in point through the playhead —
// and ripple the sequence closed. Linked clips ripple as one group (the engine's
// group trim moves every member's in point by one delta and shifts the synced
@ -1718,11 +1718,11 @@ fn build() -> Vec<CommandSpec> {
}
continue;
}
let before = live[0].1;
let start = live.iter().map(|(_, s, _)| *s).min().unwrap_or(ph);
edit::ripple_trim_group(q, &ids, Edge::In, head, ctx)?;
if st.ripple_sequence_markers {
let after = q.find_item(live[0].0).map(|(_, i)| i.start).unwrap_or(before);
crate::sequence_tools::ripple_markers(&mut q.markers, after, -(after - before));
// the head [start, start + head) closes: later markers move back by it
crate::sequence_tools::ripple_markers(&mut q.markers, start + head, Tick::ZERO - head);
}
}
Ok(())

View File

@ -86,3 +86,23 @@ fn ripple_delete_head_past_the_end_removes_the_whole_clip() {
assert_eq!(range(&s, pic3), (f(&s, 48), f(&s, 96)), "later clips closed the whole 48-frame gap");
assert_eq!(range(&s, snd3), (f(&s, 48), f(&s, 96)));
}
/// With Ripple Sequence Markers on, markers after the removed head move back with the clips, and
/// markers inside the head go with it.
#[test]
fn ripple_delete_head_ripples_sequence_markers() {
let mut s = Session::default();
let [(_, _), (pic2, _), (_, _), (_, _)] = four_clips(&mut s);
s.state.ripple_sequence_markers = true;
let at = |s: &Session, n: i64| s.sequence_rate().tick_of(n).0;
for (frame, name) in [(30, "before"), (52, "inside"), (120, "after")] {
s.execute("markers.add", json!({"time": at(&s, frame), "name": name})).unwrap();
}
s.set_playhead(f(&s, 60));
s.state.selection = vec![pic2];
s.execute("edit.rippleDeleteHead", json!({})).unwrap();
let q = s.active_sequence().unwrap();
let markers: Vec<(String, Tick)> = q.markers.iter().map(|m| (m.name.clone(), m.start)).collect();
assert_eq!(markers, vec![("before".to_string(), f(&s, 30)), ("after".to_string(), f(&s, 108))], "the 12-frame head closes");
}