atagen

monolith

public
Star0
Watch0

Commit 9c0e9500c151

tarball

atagen <boss@atagen.co>2026-10-18 09:38 UTC

2 files changed+47-6
layout: carry weight into lanes a window joins from beside them

join_lane only carried weight when the window sat directly in the lane's parent, and entering a sibling container carried none. Moving out of a row into a lane of hidden windows and back left half of the share behind each trip, so the window shrank geometrically and the hidden lane with it. The in-lane weight is now scaled so release_share hands back exactly what was carried.
Modified file: crates/layout/src/tests.rs+25 additions
@@ -351,3 +351,28 @@ fn stacking_in_and_out_does_not_drift() {
351351 assert_eq!(t.arrange(AREA, 0.0, |_| true), once, "stepping out {out:?}");
352352 }
353353 }
354+
355+/// Hidden windows in the rows around a window must not soak up its weight
356+/// as it passes through their lanes, or it shrinks on every round trip.
357+#[test]
358+fn moving_past_hidden_windows_does_not_drift() {
359+ let (a, b, h, h2) = (1, 2, 3, 4);
360+ let mut t: Tree<u32> = Tree::default();
361+ t.insert_row(h);
362+ t.insert_row(a);
363+ t.insert_after(h2, a);
364+ t.insert_after(b, h2);
365+ let vis = |w: u32| w != h && w != h2;
366+ let once = t.arrange(AREA, 0.0, |_| true);
367+ for _ in 0..4 {
368+ for d in [Dir::Up, Dir::Up, Dir::Down] {
369+ assert!(t.move_window(b, d, vis));
370+ }
371+ t.check();
372+ }
373+ let after = t.arrange(AREA, 0.0, |_| true);
374+ for ((w, r), (v, s)) in once.iter().zip(&after) {
375+ assert_eq!(w, v);
376+ assert!((r.y - s.y).abs() < 1e-6 && (r.h - s.h).abs() < 1e-6, "{w}: {r:?} became {s:?}");
377+ }
378+}
Modified file: crates/layout/src/tree.rs+22 additions−6 deletions
@@ -434,8 +434,7 @@ impl<W: Copy + Eq + Hash> Tree<W> {
434434 if let Some(j) = self.next_shown(p, i, dir, vis) {
435435 let sib = self.children(p)[j].node;
436436 if enter && self.axis(sib).is_some() {
437- let end = self.children(sib).len();
438- self.relocate(n, sib, end);
437+ self.join_lane(n, p, j);
439438 } else {
440439 let children = self.children_mut(p);
441440 children[i].node = sib;
@@ -517,9 +516,23 @@ impl<W: Copy + Eq + Hash> Tree<W> {
517516 c
518517 };
519518 let end = self.children(lane).len();
520- let from_p = self.position(n).filter(|&(old, _)| old == p);
521- let weight = from_p.map(|(_, i)| self.children(p)[i].weight);
522- self.relocate(n, lane, end);
519+ let (old, i) = self.position(n).expect("n is below p");
520+ let weight = if old == p {
521+ Some(self.children(p)[i].weight)
522+ } else if self.position(old).is_some_and(|(q, _)| q == p) {
523+ self.release_share(n)
524+ } else {
525+ None
526+ };
527+ let inner = match weight {
528+ Some(weight) => {
529+ let k = self.position(lane).expect("lanes sit in p").1;
530+ let total: f64 = self.children(lane).iter().map(|c| c.weight).sum();
531+ total * weight / self.children(p)[k].weight
532+ }
533+ None => self.mean_weight(lane),
534+ };
535+ self.relocate_weighted(n, lane, end, inner);
523536 if let Some(weight) = weight
524537 && self.nodes.contains_key(lane)
525538 && let Some((q, k)) = self.position(lane)
@@ -557,8 +570,11 @@ impl<W: Copy + Eq + Hash> Tree<W> {
557570 }
558571
559572 fn relocate(&mut self, n: NodeId, p: NodeId, at: usize) {
573+ self.relocate_weighted(n, p, at, self.mean_weight(p));
574+ }
575+
576+ fn relocate_weighted(&mut self, n: NodeId, p: NodeId, at: usize, weight: f64) {
560577 let (old, i) = self.position(n).expect("relocated nodes have a parent");
561- let weight = self.mean_weight(p);
562578 let at = if old == p && i < at { at - 1 } else { at };
563579 self.unlink(old, i);
564580 self.attach(p, at, n, weight);