fix(oaktimeline): placement sets the block's in point (drop-at-cursor fix)
TrackPlaceBlockCommand::redo now homes the block's in point to the placement target (capturing the original for undo): the Rust block stores its position on the block, so a fresh clip that never had its in point set always rendered at the timeline zero — the 'drops always land at zero' bug. The original in point is captured on the first redo and restored on undo, keeping the sync re-place round-trip exact. The A/V drop test now asserts the clip lands at the drop frame.
This commit is contained in:
@@ -556,6 +556,9 @@ pub struct TrackPlaceBlockCommand {
|
||||
add_track_commands_: Vec<TimelineAddTrackCommand>,
|
||||
/// Ripple-removal of the area the block occupies (`ripple_remove_command_`).
|
||||
ripple_remove_command_: Option<TrackRippleRemoveAreaCommand>,
|
||||
/// The block's in point captured on the first redo (restored on undo
|
||||
/// so a sync re-place / move round-trips the original position).
|
||||
old_in_: Option<Rational>,
|
||||
}
|
||||
|
||||
impl TrackPlaceBlockCommand {
|
||||
@@ -572,11 +575,22 @@ impl TrackPlaceBlockCommand {
|
||||
block,
|
||||
add_track_commands_: Vec::new(),
|
||||
ripple_remove_command_: None,
|
||||
old_in_: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// `redo`: place the block destructively.
|
||||
pub fn redo(&mut self) {
|
||||
// The module stores the position on the block (no track-order
|
||||
// derivation like C++), so the placement contract is that the
|
||||
// block's in point becomes `in_` — fresh clips otherwise keep
|
||||
// their birth in=0 and every drop lands at the timeline zero. The
|
||||
// original position is captured once so undo restores it (the
|
||||
// sync re-place path round-trips the block's position).
|
||||
if self.old_in_.is_none() {
|
||||
self.old_in_ = Some(block_in(&self.block));
|
||||
}
|
||||
block_set_in(&self.block, self.in_);
|
||||
// Determine if we need to add tracks
|
||||
let track_count = tracklist_track_count(&self.timeline) as i32;
|
||||
|
||||
@@ -655,6 +669,10 @@ impl TrackPlaceBlockCommand {
|
||||
|
||||
// Firstly, remove our insert
|
||||
track_ripple_remove_block(&t, &self.block);
|
||||
// Restore the position the block had before the placement.
|
||||
if let Some(old_in) = self.old_in_ {
|
||||
block_set_in(&self.block, old_in);
|
||||
}
|
||||
|
||||
if self.ripple_remove_command_.is_some() {
|
||||
// If we ripple-removed, just undo that
|
||||
|
||||
+13
-1
@@ -6225,9 +6225,21 @@ mod tests {
|
||||
.expect("imported footage is listed");
|
||||
cx.update(|app| {
|
||||
engine.update(app, |engine, cx| {
|
||||
engine.drop_footage(entry.id, TrackKind::Video, 0, Frame(0), cx)
|
||||
// Drop at a non-zero frame: the placement must land where
|
||||
// the cursor was (the "always lands at zero" regression).
|
||||
engine.drop_footage(entry.id, TrackKind::Video, 0, Frame(40), cx)
|
||||
})
|
||||
});
|
||||
// The clip landed at the drop frame (not the timeline zero).
|
||||
let video_in = cx.read(|app| {
|
||||
let engine = engine.read(app);
|
||||
engine
|
||||
.tracks
|
||||
.iter()
|
||||
.find(|t| t.kind == TrackKind::Video && !t.clips.is_empty())
|
||||
.map(|t| t.clips[0].range.start.0)
|
||||
});
|
||||
assert_eq!(video_in, Some(40), "the clip lands at the drop frame");
|
||||
|
||||
let clip_count = |engine: &RealEngine, kind: TrackKind| -> usize {
|
||||
engine
|
||||
|
||||
Reference in New Issue
Block a user