From 0ad4e91a0ec02422886e87536735ed232dc93121 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:09:44 +0800 Subject: [PATCH] test(reorder): require the drag preview to be the reordered list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A drag preview exists to make the strip look exactly like the list already reordered. Every test here samples that with a few pixel values read off one particular layout, which pins the arithmetic without ever saying what the arithmetic is for — and the case where reading numbers off by hand stops being convincing is chips of different widths, which is the case a tab strip actually has. So this builds the reordered list, lays it out from nothing, and requires the displaced positions to be that layout: eight shapes, even and uneven, two to six chips, three gaps, every (from, target) pair — 1,968 positions. Caught: dropping the gap from `shift`, and narrowing the range of slots that step aside by one. Not caught, and correctly so: adding `from` back into the crossed range changes nothing, because the filter on the next line removes it again. That `+ 1` is redundant, not load-bearing. --- src/ui/reorder.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/ui/reorder.rs b/src/ui/reorder.rs index 134fdda1..9434f98e 100644 --- a/src/ui/reorder.rs +++ b/src/ui/reorder.rs @@ -299,6 +299,85 @@ mod tests { ) } + /// A list laid out with the given heights, and where each item starts. + fn strip(heights: &[f32], gap: f32, from: usize) -> (Reorder, Vec) { + let mut y = 0.0f32; + let mut origins = Vec::new(); + let rects: Vec<_> = heights + .iter() + .map(|&h| { + origins.push(y); + let b = Bounds { + origin: point(px(0.), px(y)), + size: size(px(200.), px(h)), + }; + y += h + gap; + b + }) + .collect(); + ( + Reorder::new( + Surface::Strip, + from, + rects, + Axis::Vertical, + px(gap), + point(px(100.), px(1.)), + ), + origins, + ) + } + + /// What a drag preview is *for*: while the pointer is at `target`, the + /// strip has to look exactly like the list already reordered. Every other + /// test here samples that with a few numbers read off a particular layout, + /// which pins the arithmetic without ever stating what it is arithmetic + /// for — and the interesting case, chips of different widths, is where + /// reading numbers off by hand stops being convincing. + /// + /// So: build the reordered list, lay it out from scratch, and require the + /// displaced positions to be that layout. Eight shapes — even and uneven, + /// two to six chips — across three gaps and every (from, target) pair. + #[test] + fn a_dragged_slot_lays_the_strip_out_as_the_reordered_list() { + let shapes: [&[f32]; 8] = [ + &[30., 30.], + &[30., 30., 30.], + &[30., 30., 30., 30.], + &[30., 30., 30., 30., 30.], + &[30., 30., 30., 30., 30., 30.], + &[10., 40., 25.], + &[50., 10., 30., 20.], + &[12., 12., 60., 8., 33.], + ]; + for heights in shapes { + let n = heights.len(); + for gap in [0.0f32, 2.0, 7.5] { + for from in 0..n { + let (r, origins) = strip(heights, gap, from); + for target in 0..n { + // Lay the reordered list out from nothing. + let mut want = vec![0f32; n]; + let mut y = 0.0f32; + for &original in &r.order(target) { + want[original] = y; + y += heights[original] + gap; + } + for slot in 0..n { + let got = origins[slot] + f32::from(r.displacement(slot, target)); + assert!( + (got - want[slot]).abs() < 0.01, + "{heights:?} gap {gap}: dragging {from} to {target} puts slot \ + {slot} at {got}, but the reordered list has it at {}", + want[slot] + ); + } + } + } + } + } + } + #[test] fn target_follows_the_pointer_across_neighbours() { let r = column(4, 30., 2., 0);