fix(timeline): restore the C++ block length anchors and repair pointer edits
The two BlockCore length setters swapped their anchors relative to the C++ semantics they document, so the ported edit commands produced wrong geometry on the live UI paths: roll edits kept the seam still, slides left negative in-points, and trims wrote the timeline in-point into media_in (playing the wrong media content). Adopt three stored-range primitives in block.rs: - set_length_and_media_out: in fixed, out moves, media untouched (resize, trim-out, gaps growing rightward). - set_length_and_media_in: in fixed, out moves, media_in += old-new (resize-with-media-in, splice right half, ripple trim-in). - set_length_keeping_out (new): out fixed, in moves, media_in += old-new (trim-in body and out-neighbour, slide out-neighbour, ripple trim-in of the trailing block). Point every command at the primitive matching its intent (undopointer, undogeneral, undoripple, undosplit, graphops, cli, nodeops) and fix the two real defects the swap hid: - TrackReplaceBlockWithGapCommand grew a following gap rightward, swallowing whatever followed it: the "dragging one clip moves unrelated clips" regression. The gap now grows leftward over the removed block's span; regression test in domain_test. - The ripple/splice trims now advance media_in instead of rewriting it, and BlockSplitCommand writes both halves' ranges and media explicitly (the second half continues from the split point). Rewrite the KNOWN-SWAP expectations to the correct geometry (roll moves the seam, slide has no negative in-point, insert-gaps grows rightward, resize-with-media-in yields media_in = 20) and add the missing media assertions. TrackSlideCommand documents that the caller positions the sliding blocks (the stored model has no track layout).
This commit is contained in:
@@ -90,20 +90,35 @@ impl BlockCore {
|
||||
self.range = TimeRange::new(self.in_(), out);
|
||||
}
|
||||
|
||||
/// Set the length, keeping the media out anchored (C++
|
||||
/// `Block::set_length_and_media_out`): the timeline in-point shifts
|
||||
/// so the out-point stays put, and the media in follows it.
|
||||
/// Set the length, keeping the timeline in-point and the media in-point
|
||||
/// anchored (C++ `Block::set_length_and_media_out`): the out-point
|
||||
/// shifts (and the media out follows it). The stored-range model has no
|
||||
/// track layout to derive the in-point from, so the caller arranges the
|
||||
/// neighbouring blocks explicitly when a different anchor is wanted.
|
||||
pub fn set_length_and_media_out(&mut self, length: Rational) {
|
||||
let out = self.in_() + self.length();
|
||||
self.range = TimeRange::new(out - length, out);
|
||||
self.media_in = self.range.in_();
|
||||
self.range = TimeRange::new(self.in_(), self.in_() + length);
|
||||
}
|
||||
|
||||
/// Set the length, keeping the media in anchored (C++
|
||||
/// `Block::set_length_and_media_in`): the in-point stays, the
|
||||
/// out-point shifts.
|
||||
/// Set the length, keeping the timeline in-point and the MEDIA OUT
|
||||
/// anchored (C++ `Block::set_length_and_media_in`): the media in-point
|
||||
/// moves by `old_length - length` so the media content end stays put
|
||||
/// while the timeline out-point follows the length.
|
||||
pub fn set_length_and_media_in(&mut self, length: Rational) {
|
||||
let delta = self.length() - length;
|
||||
self.range = TimeRange::new(self.in_(), self.in_() + length);
|
||||
self.media_in = self.media_in + delta;
|
||||
}
|
||||
|
||||
/// Set the length, keeping the timeline OUT-point and the media content
|
||||
/// between the two ends anchored: the in-point shifts by the length
|
||||
/// delta and the media in-point follows it, so the media out stays put
|
||||
/// too (the stored-range model's net for a head trim, where the C++
|
||||
/// derives the new in-point from the resized previous block).
|
||||
pub fn set_length_keeping_out(&mut self, length: Rational) {
|
||||
let delta = self.length() - length;
|
||||
let out = self.out();
|
||||
self.range = TimeRange::new(out - length, out);
|
||||
self.media_in = self.media_in + delta;
|
||||
}
|
||||
|
||||
/// Media out (in + length; C++ `Block::media_out`).
|
||||
|
||||
@@ -46,16 +46,27 @@ fn block_core_ranges() {
|
||||
core.set_out(Rational::new(9, 1));
|
||||
assert_eq!(core.length(), Rational::new(4, 1));
|
||||
|
||||
// media_out anchored: in shifts so out stays.
|
||||
// `set_length_and_media_out`: the in point (and the media in point)
|
||||
// stays, the out point follows the length.
|
||||
core.media_in = Rational::new(2, 1);
|
||||
core.set_length_and_media_out(Rational::new(3, 1));
|
||||
assert_eq!(core.out(), Rational::new(9, 1), "out stays put");
|
||||
assert_eq!(core.length(), Rational::new(3, 1));
|
||||
assert_eq!(core.in_(), Rational::new(5, 1), "in stays put");
|
||||
assert_eq!(core.out(), Rational::new(8, 1), "out follows the length");
|
||||
assert_eq!(core.media_in, Rational::new(2, 1), "media in untouched");
|
||||
|
||||
// media_in anchored: in stays, out shifts.
|
||||
// `set_length_and_media_in`: the in point stays while the media in
|
||||
// moves with the length (the media out is what stays anchored).
|
||||
core.set_length_and_media_in(Rational::new(4, 1));
|
||||
assert_eq!(core.in_(), Rational::new(6, 1), "in stays put");
|
||||
assert_eq!(core.in_(), Rational::new(5, 1), "in stays put");
|
||||
assert_eq!(core.length(), Rational::new(4, 1));
|
||||
assert_eq!(core.media_in, Rational::new(1, 1), "media in follows the length");
|
||||
|
||||
// `set_length_keeping_out`: the out point (and the media content end)
|
||||
// stays, the in point shifts.
|
||||
core.set_length_keeping_out(Rational::new(2, 1));
|
||||
assert_eq!(core.out(), Rational::new(9, 1), "out stays put");
|
||||
assert_eq!(core.in_(), Rational::new(7, 1), "in shifts");
|
||||
assert_eq!(core.media_in, Rational::new(3, 1), "media in follows the in point");
|
||||
}
|
||||
|
||||
/// block.rs: behavior constructors + node inputs.
|
||||
|
||||
Reference in New Issue
Block a user