@@ -308,7 +308,9 @@ impl Node { |
| 308 | 308 | match self { |
| 309 | 309 | Node::Leaf { window: Some(w) } => vec![(*w, padded)], |
| 310 | 310 | Node::Leaf { window: None } => vec![], |
| 311 | | - Node::Stack { windows, .. } => { |
| 311 | + Node::Stack { |
| 312 | + windows, active, .. |
| 313 | + } => { |
| 312 | 314 | // All stack members share the tile's full rect. Non-active |
| 313 | 315 | // windows sit fully behind the active in z-order. The |
| 314 | 316 | // previous "Slack-style reveal" offset (origin += depth * |
@@ -318,7 +320,27 @@ impl Node { |
| 318 | 320 | // larger offsets and visibly drifted away from the tile |
| 319 | 321 | // (user-reported as "the windows are not sized and |
| 320 | 322 | // positioned as if they are members of the stack"). |
| 321 | | - windows.iter().map(|wid| (*wid, padded)).collect() |
| 323 | + // |
| 324 | + // Order matters here even though every member shares the |
| 325 | + // same rect: apply_layout walks this vec and issues AX |
| 326 | + // position writes per element, and macOS z-orders whichever |
| 327 | + // window was positioned last to the top of its level. |
| 328 | + // Iterating in insertion order would make the last-inserted |
| 329 | + // member visible regardless of `active`. Putting the active |
| 330 | + // member last keeps it on top — including when focus leaves |
| 331 | + // the stack to another monitor and apply_layout re-runs on |
| 332 | + // the source workspace (visible artifact: the "wrong" stack |
| 333 | + // member jumps to the top after mod+arrow off the stack). |
| 334 | + let mut ordered: Vec<(WindowId, Rect)> = Vec::with_capacity(windows.len()); |
| 335 | + for (i, wid) in windows.iter().enumerate() { |
| 336 | + if i != *active { |
| 337 | + ordered.push((*wid, padded)); |
| 338 | + } |
| 339 | + } |
| 340 | + if let Some(active_wid) = windows.get(*active) { |
| 341 | + ordered.push((*active_wid, padded)); |
| 342 | + } |
| 343 | + ordered |
| 322 | 344 | } |
| 323 | 345 | Node::Internal { |
| 324 | 346 | split, |