From e1f10ccdc8c84fc8b03589b880bb3a115eb75e99 Mon Sep 17 00:00:00 2001 From: Sly LaMenace Date: Sun, 1 Nov 2026 23:03:35 +0000 Subject: [PATCH] fix(edit): preserve cross-track grid snapping --- crates/engine/src/cmd/edit.rs | 9 +++++---- crates/engine/tests/it/edit_modes.rs | 15 +++++++++++++++ crates/ui-egui/src/edit_window/drag.rs | 3 ++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index bdcee181..98772487 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -986,20 +986,21 @@ fn move_clips(e: &mut Engine, p: &Value) -> Result { }; let mode = s.edit.edit_mode; let delta = match first { - Some(_) if mode.is_grid() && p.get("to").is_some() && delta == 0 => 0, + Some(_) if mode.is_grid() && p.get("to").is_some() && delta == 0 && dest.iter().all(|(from, to)| from == to) => 0, Some(c) if mode.is_grid() && p.get("to").is_some() => ops::grid_delta(s, c, delta, mode == EditMode::GridRelative, &ids), _ => delta, }; + let effective_delta = first.map_or(delta, |clip| delta.max(clip.start.saturating_neg())); let s = e.session_mut(); let n = if mode == EditMode::Shuffle { ops::shuffle_clips(s, &ids, delta, &dest).map_err(|why| bad("edit.move_clips", why))? - } else if delta == 0 && dest.iter().all(|(from, to)| from == to) { + } else if effective_delta == 0 && dest.iter().all(|(from, to)| from == to) { 0 } else { - ops::move_clips(s, &ids, delta, &dest) + ops::move_clips(s, &ids, effective_delta, &dest) }; s.edit.selected_clips = ids; - Ok(json!({"moved": n, "by": delta})) + Ok(json!({"moved": n, "by": if mode == EditMode::Shuffle { delta } else { effective_delta }})) } fn trim_clip(e: &mut Engine, p: &Value) -> Result { diff --git a/crates/engine/tests/it/edit_modes.rs b/crates/engine/tests/it/edit_modes.rs index f07cef19..6187a08f 100644 --- a/crates/engine/tests/it/edit_modes.rs +++ b/crates/engine/tests/it/edit_modes.rs @@ -252,6 +252,21 @@ fn zero_distance_move_keeps_overlapping_material_and_quantize_still_moves() { run(&mut e, "edit.select", json!({"clips": [ids[0].0]})); run(&mut e, "clip.quantize_to_grid", json!({})); assert_eq!(start(&e, ids[0]), 0); + + let (mut e, t, ids) = long("slip", &[(0, 1_000, 0), (500, 1_000, 2_000)]); + let before = material(&e, t); + run(&mut e, "edit.move_clips", json!({"clips": [ids[0].0], "by": -1_000})); + assert_eq!(material(&e, t), before); +} + +#[test] +fn zero_horizontal_grid_move_to_another_track_still_snaps() { + let (mut e, _, [_, clip]) = engine("grid"); + let destination = new_track(&mut e); + run(&mut e, "edit.move_clips", json!({"clips": [clip.0], "to": 2_000, "track": destination.0})); + let (track, moved) = e.session().find_clip(clip).unwrap(); + assert_eq!(track, destination); + assert_eq!(moved.start, 0); } #[test] diff --git a/crates/ui-egui/src/edit_window/drag.rs b/crates/ui-egui/src/edit_window/drag.rs index 6266bc03..d46d15ba 100644 --- a/crates/ui-egui/src/edit_window/drag.rs +++ b/crates/ui-egui/src/edit_window/drag.rs @@ -272,7 +272,8 @@ pub(super) fn drag_plan( /// Shuffle lands the earliest clip bound for the target track on a slot there, as /// `edit.move_clips` does; Grid snaps the grabbed clip. fn move_delta(plan: &DragPlan, s: &Session, from: TrackId, raw: Samples, flip: bool) -> Samples { - if raw == 0 { + let track_changed = plan.dest.as_ref().is_some_and(|dest| ops::lands(dest, from) != from); + if raw == 0 && !track_changed { return 0; } let mode = s.edit.edit_mode;