atagen

monolith

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/layout/src/tree.rsRust1132 lines37.8 KB
1
use monolith_planner::Rect;
2
use slotmap::{SlotMap, new_key_type};
3
use std::collections::HashMap;
4
use std::hash::Hash;
5
6
new_key_type! { pub struct NodeId; }
7
8
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
11
pub enum Axis {
12
    Horizontal,
13
    Vertical,
14
}
15
16
impl Axis {
17
    pub fn other(self) -> Self {
18
        match self {
19
            Self::Horizontal => Self::Vertical,
20
            Self::Vertical => Self::Horizontal,
21
        }
22
    }
23
24
    fn index(self) -> usize {
25
        match self {
26
            Self::Horizontal => 0,
27
            Self::Vertical => 1,
28
        }
29
    }
30
}
31
32
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
35
pub enum Dir {
36
    Left,
37
    Right,
38
    Up,
39
    Down,
40
}
41
42
impl Dir {
43
    pub fn axis(self) -> Axis {
44
        match self {
45
            Self::Left | Self::Right => Axis::Horizontal,
46
            Self::Up | Self::Down => Axis::Vertical,
47
        }
48
    }
49
50
    pub fn forward(self) -> bool {
51
        matches!(self, Self::Right | Self::Down)
52
    }
53
}
54
55
const MIN_SHARE: f64 = 0.05;
56
57
/// A window's width and height relative to its neighbours'.
58
type Size = [f64; 2];
59
60
#[derive(Clone, Debug)]
61
enum Kind<W> {
62
    Leaf(W, Size),
63
    Container {
64
        axis: Axis,
65
        children: Vec<NodeId>,
66
        expanded: Option<NodeId>,
67
    },
68
}
69
70
#[derive(Clone, Debug)]
71
struct Node<W> {
72
    parent: Option<NodeId>,
73
    kind: Kind<W>,
74
}
75
76
/// One output's tiling tree. Every tiled window of every tag lives here, the
77
/// caller's visibility filter decides which ones take space.
78
///
79
/// Children run on the axis opposite their parent. Containers hold at least
80
/// two children, hidden ones included, and a container left with one child
81
/// is replaced by it.
82
#[derive(Clone, Debug)]
83
pub struct Tree<W> {
84
    nodes: SlotMap<NodeId, Node<W>>,
85
    root: Option<NodeId>,
86
    leaves: HashMap<W, NodeId>,
87
    stack: Axis,
88
}
89
90
impl<W: Copy + Eq + Hash> Default for Tree<W> {
91
    fn default() -> Self {
92
        Self {
93
            nodes: SlotMap::with_key(),
94
            root: None,
95
            leaves: HashMap::new(),
96
            stack: Axis::Vertical,
97
        }
98
    }
99
}
100
101
impl<W: Copy + Eq + Hash> Tree<W> {
102
    /// The axis tag rows stack along.
103
    pub fn stack_axis(&self) -> Axis {
104
        self.stack
105
    }
106
107
    pub fn contains(&self, w: W) -> bool {
108
        self.leaves.contains_key(&w)
109
    }
110
111
    pub fn is_empty(&self) -> bool {
112
        self.root.is_none()
113
    }
114
115
    pub fn windows(&self) -> impl Iterator<Item = W> + '_ {
116
        self.leaves.keys().copied()
117
    }
118
119
    /// Inserts `w` as the next sibling of `anchor`, or as a new row if
120
    /// `anchor` is not in the tree.
121
    pub fn insert_after(&mut self, w: W, anchor: W, vis: impl Fn(W) -> bool) {
122
        let Some(&a) = self.leaves.get(&anchor) else {
123
            return self.insert_row(w, vis);
124
        };
125
        match self.position(a) {
126
            Some((p, i)) if Some(p) == self.root && self.axis(p) == Some(self.stack) => {
127
                let n = self.new_leaf(w, self.size(a));
128
                self.unlink(p, i);
129
                let c = self.new_container(self.stack.other());
130
                self.attach(c, 0, a);
131
                self.attach(c, 1, n);
132
                self.attach(p, i, c);
133
                self.reveal(n, true);
134
            }
135
            Some((p, i)) => {
136
                let n = self.new_leaf(w, self.size(p));
137
                self.attach(p, i + 1, n);
138
                self.rescale_hidden(n, true, &vis);
139
                self.reveal(n, true);
140
            }
141
            None => {
142
                let n = self.new_leaf(w, self.size(a));
143
                let c = self.new_container(self.stack.other());
144
                self.attach(c, 0, a);
145
                self.attach(c, 1, n);
146
                self.root = Some(c);
147
            }
148
        }
149
    }
150
151
    /// Inserts `w` after `anchor` by splitting `anchor` along `axis`. They
152
    /// share the space `anchor` had, and its siblings keep theirs.
153
    pub fn insert_split(&mut self, w: W, anchor: W, axis: Axis, vis: impl Fn(W) -> bool) {
154
        let Some(&a) = self.leaves.get(&anchor) else {
155
            return self.insert_row(w, vis);
156
        };
157
        let size = self.size(a);
158
        let n = self.new_leaf(w, size);
159
        match self.position(a) {
160
            Some((p, i)) if self.axis(p) == Some(axis) => {
161
                self.halve(a, axis);
162
                self.halve(n, axis);
163
                self.attach(p, i + 1, n);
164
            }
165
            Some((p, i)) => {
166
                self.unlink(p, i);
167
                let c = self.new_container(axis);
168
                self.attach(c, 0, a);
169
                self.attach(c, 1, n);
170
                self.attach(p, i, c);
171
            }
172
            None => {
173
                let c = self.new_container(axis);
174
                self.attach(c, 0, a);
175
                self.attach(c, 1, n);
176
                self.root = Some(c);
177
            }
178
        }
179
        self.reveal(n, true);
180
    }
181
182
    fn halve(&mut self, leaf: NodeId, axis: Axis) {
183
        if let Kind::Leaf(_, size) = &mut self.nodes[leaf].kind {
184
            size[axis.index()] /= 2.0;
185
        }
186
    }
187
188
    /// Scales the hidden siblings of a visible `n` arriving in or leaving its
189
    /// container by as much as the visible ones grow or shrink together, so
190
    /// the share they come back to does not drift as windows come and go.
191
    fn rescale_hidden(&mut self, n: NodeId, arriving: bool, vis: &impl Fn(W) -> bool) {
192
        let (Some((p, _)), true) = (self.position(n), self.visible(n, vis)) else {
193
            return;
194
        };
195
        let axis = self.axis(p).expect("parents are containers");
196
        let (shown, hidden): (Vec<NodeId>, Vec<NodeId>) = self
197
            .children(p)
198
            .iter()
199
            .filter(|&&c| c != n)
200
            .partition(|&&c| self.visible(c, vis));
201
        let rest: f64 = shown.iter().map(|&c| self.extent(c, axis, vis)).sum();
202
        if rest <= 0.0 {
203
            return;
204
        }
205
        let with = rest + self.extent(n, axis, vis);
206
        let by = if arriving { with / rest } else { rest / with };
207
        for c in hidden {
208
            self.scale(c, axis, by);
209
        }
210
    }
211
212
    /// Appends `w` as a row of its own along the stacking axis.
213
    pub fn insert_row(&mut self, w: W, vis: impl Fn(W) -> bool) {
214
        let size = self.root.map_or([1.0; 2], |r| self.size(r));
215
        let n = self.new_leaf(w, size);
216
        match self.root {
217
            None => self.root = Some(n),
218
            Some(r) if self.axis(r) == Some(self.stack) => {
219
                let end = self.children(r).len();
220
                self.attach(r, end, n);
221
                self.rescale_hidden(n, true, &vis);
222
            }
223
            Some(r) => {
224
                let c = self.new_container(self.stack);
225
                self.attach(c, 0, r);
226
                self.attach(c, 1, n);
227
                self.root = Some(c);
228
            }
229
        }
230
        self.reveal(n, true);
231
    }
232
233
    pub fn remove(&mut self, w: W, vis: impl Fn(W) -> bool) -> bool {
234
        let Some(&n) = self.leaves.get(&w) else {
235
            return false;
236
        };
237
        self.rescale_hidden(n, false, &vis);
238
        self.leaves.remove(&w);
239
        self.detach(n);
240
        self.nodes.remove(n);
241
        true
242
    }
243
244
    pub fn retain(&mut self, mut keep: impl FnMut(W) -> bool) {
245
        let gone: Vec<W> = self.windows().filter(|&w| !keep(w)).collect();
246
        for w in gone {
247
            self.remove(w, |_| false);
248
        }
249
    }
250
251
    /// Moves a window one step in `dir`. Leaves swap slots with the next
252
    /// visible sibling, enter a sibling container, or step out beside their
253
    /// container. Hidden windows never move, and an extended window moves
254
    /// its group as a rigid block.
255
    pub fn move_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
256
        let Some(&n) = self.leaves.get(&w).filter(|_| vis(w)) else {
257
            return false;
258
        };
259
        let block = self.group(w, &vis).unwrap_or(n);
260
        self.until_seen(&vis, |t| {
261
            let moved = t.move_node(block, dir, &vis, block == n);
262
            t.reveal(n, moved)
263
        })
264
    }
265
266
    /// Moves the group or container holding `w` as a block, merging it into
267
    /// the next lane in `dir` like `merge_window` when `merge` is set. A
268
    /// window sitting directly in the root is its own block.
269
    pub fn move_container(&mut self, w: W, dir: Dir, merge: bool, vis: impl Fn(W) -> bool) -> bool {
270
        let Some(&n) = self.leaves.get(&w).filter(|_| vis(w)) else {
271
            return false;
272
        };
273
        let group = self.group(w, &vis);
274
        self.until_seen(&vis, |t| {
275
            let block = group.unwrap_or_else(|| {
276
                t.nodes[n]
277
                    .parent
278
                    .filter(|&p| Some(p) != t.root)
279
                    .unwrap_or(n)
280
            });
281
            if merge {
282
                t.merge_node(n, block, dir, &vis)
283
            } else {
284
                let moved = t.move_node(block, dir, &vis, false);
285
                t.reveal(n, moved)
286
            }
287
        })
288
    }
289
290
    /// Moves a window to the end of the next visible lane in `dir` among its
291
    /// siblings. A lone window there becomes a stack with it. A window with no
292
    /// sibling lane that way leaves its stack as `move_window` would, so it
293
    /// takes one press to step out and another to merge. An extended window
294
    /// takes its group along.
295
    pub fn merge_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
296
        let Some(&n) = self.leaves.get(&w).filter(|_| vis(w)) else {
297
            return false;
298
        };
299
        let block = self.group(w, &vis).unwrap_or(n);
300
        self.until_seen(&vis, |t| t.merge_node(n, block, dir, &vis))
301
    }
302
303
    fn merge_node(&mut self, leaf: NodeId, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool) -> bool {
304
        if let Some((p, i)) = self.position(n)
305
            && self.axis(p) == Some(dir.axis())
306
            && let Some(j) = self.next_shown(p, i, dir, vis)
307
        {
308
            self.join_lane(n, p, j);
309
            return self.reveal(leaf, true);
310
        }
311
        let moved = self.move_node(n, dir, vis, true);
312
        self.reveal(leaf, moved)
313
    }
314
315
    /// The outermost container `w` is extended in, the block it moves as.
316
    fn group(&self, w: W, vis: &impl Fn(W) -> bool) -> Option<NodeId> {
317
        self.shown_path(w, vis)
318
            .into_iter()
319
            .take_while(|&(p, c)| self.expanded(p) == Some(c))
320
            .last()
321
            .map(|(p, _)| p)
322
    }
323
324
    /// Whether `w` is extended over windows it shares a container with.
325
    pub fn grouped(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
326
        self.group(w, &vis).is_some()
327
    }
328
329
    /// Whether `w`, or a container holding it, is extended over windows.
330
    pub fn extended(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
331
        self.shown_path(w, &vis)
332
            .into_iter()
333
            .any(|(p, c)| self.expanded(p) == Some(c))
334
    }
335
336
    /// The shown windows in the block `w` moves as, `w` alone when it is not
337
    /// extended.
338
    pub fn group_windows(&self, w: W, vis: impl Fn(W) -> bool) -> Vec<W> {
339
        let Some(g) = self.group(w, &vis) else {
340
            return vec![w];
341
        };
342
        let mut out = Vec::new();
343
        self.shown_leaves(g, &vis, &mut out);
344
        out
345
    }
346
347
    /// Whether a visible window extended over `w` takes all its room.
348
    pub fn eclipsed(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
349
        let Some(&leaf) = self.leaves.get(&w) else {
350
            return false;
351
        };
352
        let mut cur = leaf;
353
        while let Some(p) = self.nodes[cur].parent {
354
            if self
355
                .expanded(p)
356
                .is_some_and(|e| e != cur && self.visible(e, &vis))
357
            {
358
                return true;
359
            }
360
            cur = p;
361
        }
362
        false
363
    }
364
365
    /// Repeats `step` until the visible layout changes, or undoes it all when
366
    /// it never does. Steps around hidden windows otherwise ate keypresses
367
    /// and rearranged windows the user could not see.
368
    fn until_seen(
369
        &mut self,
370
        vis: &impl Fn(W) -> bool,
371
        mut step: impl FnMut(&mut Self) -> bool,
372
    ) -> bool {
373
        let seen = |t: &Self| -> Vec<(W, Rect)> {
374
            t.arrange(Rect::new(0.0, 0.0, 1000.0, 1000.0), 0.0, vis)
375
                .into_iter()
376
                .filter(|p| vis(p.0))
377
                .collect()
378
        };
379
        let same = |a: &[(W, Rect)], b: &[(W, Rect)]| {
380
            a.len() == b.len()
381
                && a.iter().all(|(v, r)| {
382
                    b.iter().any(|(w, s)| {
383
                        v == w
384
                            && [r.x - s.x, r.y - s.y, r.w - s.w, r.h - s.h]
385
                                .iter()
386
                                .all(|d| d.abs() < 1e-6)
387
                    })
388
                })
389
        };
390
        let before = self.clone();
391
        let start = seen(self);
392
        for _ in 0..=before.nodes.len() {
393
            if !step(self) {
394
                break;
395
            }
396
            if !same(&start, &seen(self)) {
397
                return true;
398
            }
399
        }
400
        *self = before;
401
        false
402
    }
403
404
    /// Undoes any expansion along `leaf`'s ancestry that favours another
405
    /// child, once it has `moved` or arrived. An expanded container gives its
406
    /// other children no room, so a window moved into one vanished while
407
    /// staying focused.
408
    fn reveal(&mut self, leaf: NodeId, moved: bool) -> bool {
409
        let mut cur = leaf;
410
        while moved && let Some(p) = self.nodes[cur].parent {
411
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
412
                && expanded.is_some_and(|e| e != cur)
413
            {
414
                *expanded = None;
415
            }
416
            cur = p;
417
        }
418
        moved
419
    }
420
421
    /// Whether every container above `w` is expanded towards it.
422
    pub fn maximized(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
423
        let path = self.shown_path(w, &vis);
424
        !path.is_empty() && path.iter().all(|&(p, c)| self.expanded(p) == Some(c))
425
    }
426
427
    /// `w`'s ancestry as (container, child) pairs, leaving out containers
428
    /// where the child has no visible sibling. Those look like nothing on
429
    /// screen, so expanding there would do nothing the user could see.
430
    fn shown_path(&self, w: W, vis: &impl Fn(W) -> bool) -> Vec<(NodeId, NodeId)> {
431
        let Some(&leaf) = self.leaves.get(&w) else {
432
            return Vec::new();
433
        };
434
        let mut path = Vec::new();
435
        let mut cur = leaf;
436
        while let Some(p) = self.nodes[cur].parent {
437
            let shared = self
438
                .children(p)
439
                .iter()
440
                .any(|&c| c != cur && self.visible(c, vis));
441
            if shared {
442
                path.push((p, cur));
443
            }
444
            cur = p;
445
        }
446
        path
447
    }
448
449
    /// Transposes the whole tree.
450
    pub fn flip(&mut self) {
451
        self.stack = self.stack.other();
452
        for node in self.nodes.values_mut() {
453
            match &mut node.kind {
454
                Kind::Container { axis, .. } => *axis = axis.other(),
455
                Kind::Leaf(_, size) => size.reverse(),
456
            }
457
        }
458
    }
459
460
    /// Toggles expansion for `count` levels of `w`'s visible ancestry,
461
    /// starting `skip` levels up. Level 0 expands the window inside the
462
    /// nearest container where it has a visible sibling.
463
    pub fn toggle_expand(
464
        &mut self,
465
        w: W,
466
        skip: usize,
467
        count: usize,
468
        vis: impl Fn(W) -> bool,
469
    ) -> bool {
470
        let path: Vec<_> = self
471
            .shown_path(w, &vis)
472
            .into_iter()
473
            .skip(skip)
474
            .take(count)
475
            .collect();
476
        if path.is_empty() {
477
            return false;
478
        }
479
        let all = path.iter().all(|&(p, c)| self.expanded(p) == Some(c));
480
        for (p, c) in path {
481
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind {
482
                *expanded = (!all).then_some(c);
483
            }
484
        }
485
        true
486
    }
487
488
    /// Grows `w` by `delta`, a fraction of the visible length of the nearest
489
    /// container along `axis` where it has a visible sibling. Hidden siblings
490
    /// keep their sizes, and with them their share.
491
    pub fn resize(&mut self, w: W, axis: Axis, delta: f64, vis: impl Fn(W) -> bool) -> bool {
492
        let Some(&leaf) = self.leaves.get(&w) else {
493
            return false;
494
        };
495
        let mut cur = leaf;
496
        while let Some(p) = self.nodes[cur].parent {
497
            if self.axis(p) == Some(axis) {
498
                let shown: Vec<(NodeId, f64)> = self
499
                    .children(p)
500
                    .iter()
501
                    .filter(|&&c| self.visible(c, &vis))
502
                    .map(|&c| (c, self.extent(c, axis, &vis)))
503
                    .collect();
504
                if shown.len() >= 2 {
505
                    let sum: f64 = shown.iter().map(|s| s.1).sum();
506
                    let old = self.extent(cur, axis, &vis);
507
                    let max = 1.0 - MIN_SHARE * (shown.len() - 1) as f64;
508
                    let new = (old / sum + delta).clamp(MIN_SHARE, max) * sum;
509
                    let scale = (sum - new) / (sum - old);
510
                    for (c, _) in shown {
511
                        self.scale(c, axis, if c == cur { new / old } else { scale });
512
                    }
513
                    return true;
514
                }
515
            }
516
            cur = p;
517
        }
518
        false
519
    }
520
521
    /// Gives every visible child of the nearest container where `w` has a
522
    /// visible sibling an equal share along that container's axis.
523
    pub fn balance(&mut self, w: W, vis: impl Fn(W) -> bool) -> bool {
524
        let Some(&(p, _)) = self.shown_path(w, &vis).first() else {
525
            return false;
526
        };
527
        let Some(axis) = self.axis(p) else {
528
            return false;
529
        };
530
        let shown: Vec<(NodeId, f64)> = self
531
            .children(p)
532
            .iter()
533
            .filter(|&&c| self.visible(c, &vis))
534
            .map(|&c| (c, self.extent(c, axis, &vis)))
535
            .collect();
536
        let mean = shown.iter().map(|s| s.1).sum::<f64>() / shown.len() as f64;
537
        for (c, e) in shown {
538
            if e > 0.0 {
539
                self.scale(c, axis, mean / e);
540
            }
541
        }
542
        true
543
    }
544
545
    /// The lane a window's split on side `dir` lives in, as the child holding
546
    /// the window, its parent and the index of the visible sibling across
547
    /// the split.
548
    fn split_across(
549
        &self,
550
        w: W,
551
        dir: Dir,
552
        vis: &impl Fn(W) -> bool,
553
    ) -> Option<(NodeId, NodeId, usize)> {
554
        let mut cur = *self.leaves.get(&w)?;
555
        if !self.visible(cur, vis) {
556
            return None;
557
        }
558
        while let Some((p, i)) = self.position(cur) {
559
            if self.axis(p) == Some(dir.axis())
560
                && let Some(j) = self.next_shown(p, i, dir, vis)
561
            {
562
                return Some((cur, p, j));
563
            }
564
            cur = p;
565
        }
566
        None
567
    }
568
569
    /// Whether the split on side `dir` of `w` has a visible window across it.
570
    pub fn has_split(&self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
571
        self.split_across(w, dir, &vis).is_some()
572
    }
573
574
    /// Moves the split on side `dir` of `w` by `px` along that side's axis,
575
    /// positive toward the right or bottom. Only the two children meeting at
576
    /// the split change, so the split follows the pointer.
577
    pub fn move_split(
578
        &mut self,
579
        w: W,
580
        dir: Dir,
581
        px: f64,
582
        area: Rect,
583
        gap: f64,
584
        vis: impl Fn(W) -> bool,
585
    ) -> bool {
586
        let Some((cur, p, j)) = self.split_across(w, dir, &vis) else {
587
            return false;
588
        };
589
        let axis = dir.axis();
590
        let other = self.children(p)[j];
591
        let (first, second) = if dir.forward() {
592
            (cur, other)
593
        } else {
594
            (other, cur)
595
        };
596
        let mut leaves = Vec::new();
597
        self.shown_leaves(first, &vis, &mut leaves);
598
        let (start, end) = self
599
            .arrange(area, gap, &vis)
600
            .iter()
601
            .filter(|(l, _)| leaves.contains(l))
602
            .map(|(_, r)| span(*r, axis))
603
            .fold((f64::MAX, f64::MIN), |(s, e), (rs, rl)| {
604
                (s.min(rs), e.max(rs + rl))
605
            });
606
        let ef = self.extent(first, axis, &vis);
607
        let es = self.extent(second, axis, &vis);
608
        if end <= start || ef <= 0.0 || es <= 0.0 {
609
            return true;
610
        }
611
        let sum: f64 = self
612
            .children(p)
613
            .iter()
614
            .filter(|&&c| self.visible(c, &vis))
615
            .map(|&c| self.extent(c, axis, &vis))
616
            .sum();
617
        let min = MIN_SHARE * sum;
618
        let du = (px * ef / (end - start)).clamp(-(ef - min).max(0.0), (es - min).max(0.0));
619
        self.scale(first, axis, (ef + du) / ef);
620
        self.scale(second, axis, (es - du) / es);
621
        true
622
    }
623
624
    fn shown_leaves(&self, n: NodeId, vis: &impl Fn(W) -> bool, out: &mut Vec<W>) {
625
        match &self.nodes[n].kind {
626
            Kind::Leaf(w, _) => {
627
                if vis(*w) {
628
                    out.push(*w);
629
                }
630
            }
631
            Kind::Container { children, .. } => {
632
                for &c in children {
633
                    self.shown_leaves(c, vis, out);
634
                }
635
            }
636
        }
637
    }
638
639
    /// Scales every window under `n` along `axis`, hidden ones too, so they
640
    /// keep their proportions for when they are shown again.
641
    fn scale(&mut self, n: NodeId, axis: Axis, by: f64) {
642
        match &mut self.nodes[n].kind {
643
            Kind::Leaf(_, size) => size[axis.index()] *= by,
644
            Kind::Container { children, .. } => {
645
                for c in children.clone() {
646
                    self.scale(c, axis, by);
647
                }
648
            }
649
        }
650
    }
651
652
    /// How much room `n` asks for along `axis` among its siblings. A
653
    /// container asks for the mean of what its visible children ask for, so
654
    /// windows of other tags sharing a lane change nothing on screen, and a
655
    /// window carries its own size wherever it moves.
656
    fn extent(&self, n: NodeId, axis: Axis, vis: &impl Fn(W) -> bool) -> f64 {
657
        match &self.nodes[n].kind {
658
            Kind::Leaf(_, size) => size[axis.index()],
659
            Kind::Container { children, .. } => {
660
                let shown: Vec<f64> = children
661
                    .iter()
662
                    .filter(|&&c| self.visible(c, vis))
663
                    .map(|&c| self.extent(c, axis, vis))
664
                    .collect();
665
                if shown.is_empty() {
666
                    children
667
                        .iter()
668
                        .map(|&c| self.extent(c, axis, vis))
669
                        .sum::<f64>()
670
                        / children.len() as f64
671
                } else {
672
                    shown.iter().sum::<f64>() / shown.len() as f64
673
                }
674
            }
675
        }
676
    }
677
678
    /// The size a window placed in `n`'s stead would take, ignoring
679
    /// visibility since new windows land on any tag.
680
    fn size(&self, n: NodeId) -> Size {
681
        let all = |_: W| true;
682
        [
683
            self.extent(n, Axis::Horizontal, &all),
684
            self.extent(n, Axis::Vertical, &all),
685
        ]
686
    }
687
688
    /// Lays the tree out in `area`. Hidden windows come back as zero-size
689
    /// rects on the boundary where they would reappear.
690
    pub fn arrange(&self, area: Rect, gap: f64, vis: impl Fn(W) -> bool) -> Vec<(W, Rect)> {
691
        let mut out = Vec::with_capacity(self.leaves.len());
692
        if let Some(r) = self.root {
693
            let area = if self.visible(r, &vis) {
694
                area
695
            } else if area.w >= area.h {
696
                Rect::new(area.right(), area.y, 0.0, area.h)
697
            } else {
698
                Rect::new(area.x, area.bottom(), area.w, 0.0)
699
            };
700
            self.place(r, area, gap, &vis, &mut out);
701
        }
702
        out
703
    }
704
705
    fn place(
706
        &self,
707
        n: NodeId,
708
        r: Rect,
709
        gap: f64,
710
        vis: &impl Fn(W) -> bool,
711
        out: &mut Vec<(W, Rect)>,
712
    ) {
713
        let (axis, children, expanded) = match &self.nodes[n].kind {
714
            Kind::Leaf(w, _) => return out.push((*w, r)),
715
            Kind::Container {
716
                axis,
717
                children,
718
                expanded,
719
            } => (*axis, children, *expanded),
720
        };
721
        let shown: Vec<bool> = children.iter().map(|&c| self.visible(c, vis)).collect();
722
        let (start, len) = span(r, axis);
723
        if let Some(e) = expanded.and_then(|e| children.iter().position(|&c| c == e))
724
            && shown[e]
725
        {
726
            for (i, &c) in children.iter().enumerate() {
727
                let sub = match i.cmp(&e) {
728
                    std::cmp::Ordering::Equal => r,
729
                    std::cmp::Ordering::Less => with_span(r, axis, start, 0.0),
730
                    std::cmp::Ordering::Greater => with_span(r, axis, start + len, 0.0),
731
                };
732
                self.place(c, sub, gap, vis, out);
733
            }
734
            return;
735
        }
736
        let count = shown.iter().filter(|s| **s).count();
737
        let extents: Vec<f64> = children
738
            .iter()
739
            .zip(&shown)
740
            .map(|(&c, &s)| if s { self.extent(c, axis, vis) } else { 0.0 })
741
            .collect();
742
        let total: f64 = extents.iter().sum();
743
        let gaps = count.saturating_sub(1) as f64;
744
        let gap = if gaps > 0.0 { gap.min(len / gaps) } else { gap };
745
        let avail = len - gap * gaps;
746
        let last = shown.iter().rposition(|s| *s);
747
        let mut pos = start;
748
        let mut seen = false;
749
        for (i, &c) in children.iter().enumerate() {
750
            if shown[i] {
751
                if seen {
752
                    pos += gap;
753
                }
754
                let l = avail * extents[i] / total;
755
                self.place(c, with_span(r, axis, pos, l), gap, vis, out);
756
                pos += l;
757
                seen = true;
758
            } else {
759
                let between = seen && last.is_some_and(|l| l > i);
760
                let at = if between { pos + gap / 2.0 } else { pos };
761
                self.place(c, with_span(r, axis, at, 0.0), gap, vis, out);
762
            }
763
        }
764
    }
765
766
    fn move_node(&mut self, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool, enter: bool) -> bool {
767
        let mut cur = n;
768
        while let Some((p, i)) = self.position(cur) {
769
            if self.axis(p) == Some(dir.axis()) {
770
                let hidden_lane = self
771
                    .adjacent(p, i, dir)
772
                    .filter(|&j| enter && !self.visible(self.children(p)[j], vis));
773
                if cur != n {
774
                    if let Some(j) = hidden_lane {
775
                        self.join_lane(n, p, j);
776
                    } else {
777
                        let at = if dir.forward() { i + 1 } else { i };
778
                        self.relocate(n, p, at);
779
                    }
780
                    return true;
781
                }
782
                if let Some(j) = self.next_shown(p, i, dir, vis) {
783
                    let sib = self.children(p)[j];
784
                    if enter && self.shown_windows(sib, vis) > 1 {
785
                        self.join_lane(n, p, j);
786
                    } else {
787
                        let children = self.children_mut(p);
788
                        children.swap(i, j);
789
                    }
790
                    return true;
791
                }
792
                if let Some(j) = hidden_lane {
793
                    self.join_lane(n, p, j);
794
                    return true;
795
                }
796
            }
797
            cur = p;
798
        }
799
        if cur == n || self.axis(cur) == Some(dir.axis()) {
800
            return false;
801
        }
802
        let (old, i) = self.position(n).expect("n is below the root");
803
        self.unlink(old, i);
804
        self.normalize(old);
805
        let r = self.root.expect("the rest of the tree survives");
806
        if self.axis(r) == Some(dir.axis()) {
807
            let at = if dir.forward() {
808
                self.children(r).len()
809
            } else {
810
                0
811
            };
812
            self.attach(r, at, n);
813
        } else {
814
            let c = self.new_container(dir.axis());
815
            self.attach(c, 0, r);
816
            let at = if dir.forward() {
817
                self.children(c).len()
818
            } else {
819
                0
820
            };
821
            self.attach(c, at, n);
822
            self.root = Some(c);
823
        }
824
        true
825
    }
826
827
    fn next_shown(&self, p: NodeId, i: usize, dir: Dir, vis: &impl Fn(W) -> bool) -> Option<usize> {
828
        let children = self.children(p);
829
        if dir.forward() {
830
            (i + 1..children.len()).find(|&j| self.visible(children[j], vis))
831
        } else {
832
            (0..i).rev().find(|&j| self.visible(children[j], vis))
833
        }
834
    }
835
836
    fn adjacent(&self, p: NodeId, i: usize, dir: Dir) -> Option<usize> {
837
        let j = if dir.forward() {
838
            i + 1
839
        } else {
840
            i.checked_sub(1)?
841
        };
842
        (j < self.children(p).len()).then_some(j)
843
    }
844
845
    /// Moves `n` to the end of the lane at `p`'s child `j`. A lone window
846
    /// there becomes a lane of its own, perpendicular to `p`.
847
    fn join_lane(&mut self, n: NodeId, p: NodeId, j: usize) {
848
        let lane = self.children(p)[j];
849
        let lane = if self.axis(lane).is_some() {
850
            lane
851
        } else {
852
            let c = self.new_container(self.axis(p).expect("p is a container").other());
853
            self.children_mut(p)[j] = c;
854
            self.nodes[c].parent = Some(p);
855
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
856
                && *expanded == Some(lane)
857
            {
858
                *expanded = Some(c);
859
            }
860
            self.attach(c, 0, lane);
861
            c
862
        };
863
        let end = self.children(lane).len();
864
        self.relocate(n, lane, end);
865
    }
866
867
    fn relocate(&mut self, n: NodeId, p: NodeId, at: usize) {
868
        let (old, i) = self.position(n).expect("relocated nodes have a parent");
869
        let at = if old == p && i < at { at - 1 } else { at };
870
        self.unlink(old, i);
871
        self.attach(p, at, n);
872
        self.normalize(old);
873
    }
874
875
    /// Places `node` among `p`'s children. A container running along `p`'s
876
    /// axis is dissolved into `p` so axes keep alternating, unless it holds an
877
    /// extended window, whose group is turned across instead to survive.
878
    fn attach(&mut self, p: NodeId, at: usize, node: NodeId) {
879
        if self.axis(node).is_some() && self.axis(node) == self.axis(p) {
880
            if self.expanded(node).is_some() {
881
                self.transpose(node);
882
                self.nodes[node].parent = Some(p);
883
                self.children_mut(p).insert(at, node);
884
                return;
885
            }
886
            let children = self.children(node).to_vec();
887
            self.nodes.remove(node);
888
            for (k, &c) in children.iter().enumerate() {
889
                self.nodes[c].parent = Some(p);
890
                self.children_mut(p).insert(at + k, c);
891
            }
892
        } else {
893
            self.nodes[node].parent = Some(p);
894
            self.children_mut(p).insert(at, node);
895
        }
896
    }
897
898
    fn unlink(&mut self, p: NodeId, i: usize) {
899
        let child = self.unlink_dead(p, i);
900
        self.nodes[child].parent = None;
901
    }
902
903
    fn detach(&mut self, n: NodeId) {
904
        match self.position(n) {
905
            Some((p, i)) => {
906
                self.unlink(p, i);
907
                self.normalize(p);
908
            }
909
            None => self.root = None,
910
        }
911
    }
912
913
    fn transpose(&mut self, n: NodeId) {
914
        match &mut self.nodes[n].kind {
915
            Kind::Container { axis, children, .. } => {
916
                *axis = axis.other();
917
                for c in children.clone() {
918
                    self.transpose(c);
919
                }
920
            }
921
            Kind::Leaf(_, size) => size.reverse(),
922
        }
923
    }
924
925
    /// Repairs a container that lost a child.
926
    fn normalize(&mut self, c: NodeId) {
927
        if self.children(c).len() >= 2 {
928
            return;
929
        }
930
        let pos = self.position(c);
931
        let was_expanded = pos.is_some_and(|(p, _)| self.expanded(p) == Some(c));
932
        let Some(Node {
933
            kind: Kind::Container { children, .. },
934
            ..
935
        }) = self.nodes.remove(c)
936
        else {
937
            unreachable!("only containers are normalized");
938
        };
939
        let only = children.into_iter().next();
940
        match (only, pos) {
941
            (None, Some((p, i))) => {
942
                self.unlink_dead(p, i);
943
                self.normalize(p);
944
            }
945
            (None, None) => self.root = None,
946
            (Some(only), Some((p, i))) => {
947
                self.unlink_dead(p, i);
948
                self.attach(p, i, only);
949
                if was_expanded
950
                    && self.nodes.contains_key(only)
951
                    && let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
952
                {
953
                    *expanded = Some(only);
954
                }
955
            }
956
            (Some(only), None) => {
957
                self.nodes[only].parent = None;
958
                self.root = Some(only);
959
            }
960
        }
961
    }
962
963
    /// Like `unlink`, for a child whose node is already gone.
964
    fn unlink_dead(&mut self, p: NodeId, i: usize) -> NodeId {
965
        let Kind::Container {
966
            children, expanded, ..
967
        } = &mut self.nodes[p].kind
968
        else {
969
            unreachable!("parents are containers");
970
        };
971
        let child = children.remove(i);
972
        if *expanded == Some(child) {
973
            *expanded = None;
974
        }
975
        child
976
    }
977
978
    fn new_leaf(&mut self, w: W, size: Size) -> NodeId {
979
        let n = self.nodes.insert(Node {
980
            parent: None,
981
            kind: Kind::Leaf(w, size),
982
        });
983
        self.leaves.insert(w, n);
984
        n
985
    }
986
987
    fn new_container(&mut self, axis: Axis) -> NodeId {
988
        self.nodes.insert(Node {
989
            parent: None,
990
            kind: Kind::Container {
991
                axis,
992
                children: Vec::new(),
993
                expanded: None,
994
            },
995
        })
996
    }
997
998
    fn position(&self, n: NodeId) -> Option<(NodeId, usize)> {
999
        let p = self.nodes[n].parent?;
1000
        let i = self.children(p).iter().position(|&c| c == n)?;
1001
        Some((p, i))
1002
    }
1003
1004
    fn axis(&self, n: NodeId) -> Option<Axis> {
1005
        match &self.nodes[n].kind {
1006
            Kind::Container { axis, .. } => Some(*axis),
1007
            Kind::Leaf(..) => None,
1008
        }
1009
    }
1010
1011
    fn expanded(&self, n: NodeId) -> Option<NodeId> {
1012
        match &self.nodes[n].kind {
1013
            Kind::Container { expanded, .. } => *expanded,
1014
            Kind::Leaf(..) => None,
1015
        }
1016
    }
1017
1018
    fn children(&self, n: NodeId) -> &[NodeId] {
1019
        match &self.nodes[n].kind {
1020
            Kind::Container { children, .. } => children,
1021
            Kind::Leaf(..) => &[],
1022
        }
1023
    }
1024
1025
    fn children_mut(&mut self, n: NodeId) -> &mut Vec<NodeId> {
1026
        match &mut self.nodes[n].kind {
1027
            Kind::Container { children, .. } => children,
1028
            Kind::Leaf(..) => unreachable!("leaves have no children"),
1029
        }
1030
    }
1031
1032
    fn visible(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> bool {
1033
        match &self.nodes[n].kind {
1034
            Kind::Leaf(w, _) => vis(*w),
1035
            Kind::Container { children, .. } => children.iter().any(|&c| self.visible(c, vis)),
1036
        }
1037
    }
1038
1039
    fn shown_windows(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> usize {
1040
        match &self.nodes[n].kind {
1041
            Kind::Leaf(w, _) => usize::from(vis(*w)),
1042
            Kind::Container { children, .. } => {
1043
                children.iter().map(|&c| self.shown_windows(c, vis)).sum()
1044
            }
1045
        }
1046
    }
1047
1048
    #[cfg(test)]
1049
    pub(crate) fn shape(&self) -> String
1050
    where
1051
        W: std::fmt::Debug,
1052
    {
1053
        fn go<W: Copy + Eq + Hash + std::fmt::Debug>(t: &Tree<W>, n: NodeId) -> String {
1054
            match &t.nodes[n].kind {
1055
                Kind::Leaf(w, [x, y]) => format!("{w:?}[{x:.6},{y:.6}]"),
1056
                Kind::Container {
1057
                    axis,
1058
                    children,
1059
                    expanded,
1060
                } => {
1061
                    let inner: Vec<String> = children.iter().map(|&c| go(t, c)).collect();
1062
                    let e = expanded.and_then(|e| children.iter().position(|&c| c == e));
1063
                    format!("{axis:?}{e:?}({})", inner.join(" "))
1064
                }
1065
            }
1066
        }
1067
        self.root.map(|r| go(self, r)).unwrap_or_default()
1068
    }
1069
1070
    #[cfg(test)]
1071
    pub(crate) fn check(&self) {
1072
        let reachable = match self.root {
1073
            Some(r) => {
1074
                assert!(self.nodes[r].parent.is_none());
1075
                self.check_node(r)
1076
            }
1077
            None => 0,
1078
        };
1079
        assert_eq!(reachable, self.nodes.len(), "orphaned nodes");
1080
        for (&w, &n) in &self.leaves {
1081
            assert!(matches!(self.nodes[n].kind, Kind::Leaf(x, _) if x == w));
1082
        }
1083
    }
1084
1085
    #[cfg(test)]
1086
    fn check_node(&self, n: NodeId) -> usize {
1087
        match &self.nodes[n].kind {
1088
            Kind::Leaf(_, size) => {
1089
                assert!(
1090
                    size.iter().all(|s| *s > 0.0 && s.is_finite()),
1091
                    "size {size:?}"
1092
                );
1093
                1
1094
            }
1095
            Kind::Container {
1096
                axis,
1097
                children,
1098
                expanded,
1099
            } => {
1100
                assert!(
1101
                    children.len() >= 2,
1102
                    "container with {} children",
1103
                    children.len()
1104
                );
1105
                if let Some(e) = expanded {
1106
                    assert!(children.contains(e));
1107
                }
1108
                let mut count = 1;
1109
                for &c in children {
1110
                    assert_eq!(self.nodes[c].parent, Some(n));
1111
                    assert_ne!(self.axis(c), Some(*axis), "axes must alternate");
1112
                    count += self.check_node(c);
1113
                }
1114
                count
1115
            }
1116
        }
1117
    }
1118
}
1119
1120
fn span(r: Rect, axis: Axis) -> (f64, f64) {
1121
    match axis {
1122
        Axis::Horizontal => (r.x, r.w),
1123
        Axis::Vertical => (r.y, r.h),
1124
    }
1125
}
1126
1127
fn with_span(r: Rect, axis: Axis, start: f64, len: f64) -> Rect {
1128
    match axis {
1129
        Axis::Horizontal => Rect::new(start, r.y, len, r.h),
1130
        Axis::Vertical => Rect::new(r.x, start, r.w, len),
1131
    }
1132
}