atagen

monolith

public
Star0
Watch0

Commit 1a10e81ae5c1

tarball

atagen <boss@atagen.co>2026-10-17 13:32 UTC

2 files changed+50-0
planner: leave windows with no area at either end out of the plan

Hidden windows are zero-size seeds that still move with their slots. A seed inside a visible window shares no separating relation with it, so every strategy failed and the whole group pinched to a point, and a seed passing near visible windows pulled them into multi-phase plans when nothing visible changed.
Modified file: crates/planner/src/plan.rs+35 additions
@@ -87,12 +87,47 @@ impl GroupPlan {
8787 }
8888
8989 /// Plans the transition from `from` to `to`. Both slices index the same windows.
90+/// Windows with no area at either end, like hidden ones held at their seed,
91+/// jump straight to `to`. Planning them would let an invisible point inside
92+/// a visible window join its group, or block every strategy and pinch.
9093 pub fn plan(from: &[Rect], to: &[Rect], cfg: &Config) -> Plan {
9194 assert_eq!(
9295 from.len(),
9396 to.len(),
9497 "layouts must describe the same windows"
9598 );
99+ let empty = |r: &Rect| r.w <= cfg.eps || r.h <= cfg.eps;
100+ let live: Vec<usize> = (0..from.len())
101+ .filter(|&i| !(empty(&from[i]) && empty(&to[i])))
102+ .collect();
103+ if live.len() == from.len() {
104+ return plan_live(from, to, cfg);
105+ }
106+ let pick = |rects: &[Rect]| live.iter().map(|&i| rects[i]).collect::<Vec<_>>();
107+ let inner = plan_live(&pick(from), &pick(to), cfg);
108+ let keyframes = inner
109+ .keyframes
110+ .iter()
111+ .map(|key| {
112+ let mut frame = to.to_vec();
113+ for (k, &i) in live.iter().enumerate() {
114+ frame[i] = key[k];
115+ }
116+ frame
117+ })
118+ .collect();
119+ let groups = inner
120+ .groups
121+ .into_iter()
122+ .map(|g| Group {
123+ members: g.members.iter().map(|&k| live[k]).collect(),
124+ ..g
125+ })
126+ .collect();
127+ Plan { keyframes, groups }
128+}
129+
130+fn plan_live(from: &[Rect], to: &[Rect], cfg: &Config) -> Plan {
96131 let mut groups = Vec::new();
97132 let mut plans = Vec::new();
98133 for members in motion_groups(from, to, cfg) {
Modified file: crates/planner/src/tests.rs+15 additions
@@ -251,3 +251,18 @@ proptest! {
251251 prop_assert_eq!(&second.keyframes[0], &mid);
252252 }
253253 }
254+
255+/// A hidden window is a zero-size seed. One sitting inside a visible window
256+/// used to join its group and make every strategy fail, so the plan pinched.
257+#[test]
258+fn hidden_seeds_stay_out_of_the_plan() {
259+ let c = cfg(Style::Orthogonal);
260+ let a = cols(region(), 2);
261+ let seed = |r: Rect| Rect::new(r.center_x(), r.center_y(), 0.0, 0.0);
262+ let from = vec![a[0], a[1], seed(a[0])];
263+ let to = vec![a[1], a[0], seed(a[1])];
264+ let p = plan(&from, &to, &c);
265+ assert_clean(&p, &c);
266+ assert!(p.groups.iter().all(|g| !g.members.contains(&2)));
267+ assert!(p.keyframes.iter().all(|k| k[2] == to[2]));
268+}