atagen

monolith

public
Star0
Watch0

Commit 605c44c8c5fc

tarball

atagen <boss@atagen.co>2026-10-17 09:57 UTC

2 files changed+36-22
layout: merge only into sibling lanes, stepping out of a stack first

Leaving a stack to merge straight into the next lane over skipped the spot beside the stack, so a window at A C / B C moved right ended up under C instead of between B and C. Merging now considers only the window's own container and otherwise steps out like move_window, one press per step.
Modified file: crates/layout/src/tests.rs+26 additions−9 deletions
@@ -263,17 +263,34 @@ fn moving_toward_a_hidden_lane_joins_it() {
263263 }
264264
265265 #[test]
266-fn merging_past_the_last_lane_leaves_the_stack() {
266+fn merging_steps_out_of_a_stack_before_joining_another() {
267267 let (a, b, c) = (1, 2, 3);
268- let mut t: Tree<u32> = Tree::default();
269- t.insert_row(a);
270- t.insert_after(b, a);
271- t.insert_after(c, b);
272- assert!(t.merge_window(b, Dir::Left, |_| true));
268+ let stacked = || {
269+ let mut t: Tree<u32> = Tree::default();
270+ t.insert_row(a);
271+ t.insert_after(b, a);
272+ t.insert_after(c, b);
273+ assert!(t.merge_window(b, Dir::Left, |_| true));
274+ t
275+ };
276+ let rects = |t: &Tree<u32>| {
277+ t.check();
278+ let all = t.arrange(AREA, 0.0, |_| true);
279+ move |w| all.iter().find(|p: &&(u32, Rect)| p.0 == w).unwrap().1
280+ };
281+
282+ let mut t = stacked();
283+ assert!(t.merge_window(a, Dir::Right, |_| true));
284+ let rect = rects(&t);
285+ assert!(rect(b).x < rect(a).x && rect(a).x < rect(c).x);
286+ assert_eq!(rect(a).h, AREA.h);
287+ assert!(t.merge_window(a, Dir::Right, |_| true));
288+ let rect = rects(&t);
289+ assert_eq!(rect(a).x, rect(c).x);
290+
291+ let mut t = stacked();
273292 assert!(t.merge_window(a, Dir::Up, |_| true));
274- t.check();
275- let all = t.arrange(AREA, 0.0, |_| true);
276- let rect = |w| all.iter().find(|p| p.0 == w).unwrap().1;
293+ let rect = rects(&t);
277294 assert_eq!(rect(a).w, AREA.w);
278295 assert!(rect(a).bottom() <= rect(b).y && rect(b).y == rect(c).y);
279296 }
Modified file: crates/layout/src/tree.rs+10 additions−13 deletions
@@ -198,23 +198,20 @@ impl<W: Copy + Eq + Hash> Tree<W> {
198198 self.move_node(block, dir, &vis, false)
199199 }
200200
201- /// Moves a window to the end of the next visible lane in `dir`, stepping
202- /// up a level when its container has none that way. A lone window there
203- /// becomes a stack with it. With no lane anywhere that way, it leaves its
204- /// stack as `move_window` would.
201+ /// Moves a window to the end of the next visible lane in `dir` among its
202+ /// siblings. A lone window there becomes a stack with it. A window with no
203+ /// sibling lane that way leaves its stack as `move_window` would, so it
204+ /// takes one press to step out and another to merge.
205205 pub fn merge_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
206206 let Some(&n) = self.leaves.get(&w) else {
207207 return false;
208208 };
209- let mut cur = n;
210- while let Some((p, i)) = self.position(cur) {
211- if self.axis(p) == Some(dir.axis())
212- && let Some(j) = self.next_shown(p, i, dir, &vis)
213- {
214- self.join_lane(n, p, j);
215- return true;
216- }
217- cur = p;
209+ if let Some((p, i)) = self.position(n)
210+ && self.axis(p) == Some(dir.axis())
211+ && let Some(j) = self.next_shown(p, i, dir, &vis)
212+ {
213+ self.join_lane(n, p, j);
214+ return true;
218215 }
219216 self.move_node(n, dir, &vis, true)
220217 }