atagen

monolith

public
Star0
Watch0
0f513a2420c076877d05c026ee7778cc1e571077
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/layout/src/tree.rsRust803 lines26.5 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
25
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
28
pub enum Dir {
29
    Left,
30
    Right,
31
    Up,
32
    Down,
33
}
34
35
impl Dir {
36
    pub fn axis(self) -> Axis {
37
        match self {
38
            Self::Left | Self::Right => Axis::Horizontal,
39
            Self::Up | Self::Down => Axis::Vertical,
40
        }
41
    }
42
43
    pub fn forward(self) -> bool {
44
        matches!(self, Self::Right | Self::Down)
45
    }
46
}
47
48
const MIN_SHARE: f64 = 0.05;
49
50
#[derive(Clone, Debug)]
51
struct Child {
52
    node: NodeId,
53
    weight: f64,
54
}
55
56
#[derive(Clone, Debug)]
57
enum Kind<W> {
58
    Leaf(W),
59
    Container {
60
        axis: Axis,
61
        children: Vec<Child>,
62
        expanded: Option<NodeId>,
63
    },
64
}
65
66
#[derive(Clone, Debug)]
67
struct Node<W> {
68
    parent: Option<NodeId>,
69
    kind: Kind<W>,
70
}
71
72
/// One output's tiling tree. Every tiled window of every tag lives here, the
73
/// caller's visibility filter decides which ones take space.
74
///
75
/// Children run on the axis opposite their parent. Containers hold at least
76
/// two children, hidden ones included, and a container left with one child
77
/// is replaced by it.
78
#[derive(Clone, Debug)]
79
pub struct Tree<W> {
80
    nodes: SlotMap<NodeId, Node<W>>,
81
    root: Option<NodeId>,
82
    leaves: HashMap<W, NodeId>,
83
    stack: Axis,
84
}
85
86
impl<W: Copy + Eq + Hash> Default for Tree<W> {
87
    fn default() -> Self {
88
        Self {
89
            nodes: SlotMap::with_key(),
90
            root: None,
91
            leaves: HashMap::new(),
92
            stack: Axis::Vertical,
93
        }
94
    }
95
}
96
97
impl<W: Copy + Eq + Hash> Tree<W> {
98
    /// The axis tag rows stack along.
99
    pub fn stack_axis(&self) -> Axis {
100
        self.stack
101
    }
102
103
    pub fn contains(&self, w: W) -> bool {
104
        self.leaves.contains_key(&w)
105
    }
106
107
    pub fn is_empty(&self) -> bool {
108
        self.root.is_none()
109
    }
110
111
    pub fn windows(&self) -> impl Iterator<Item = W> + '_ {
112
        self.leaves.keys().copied()
113
    }
114
115
    /// Inserts `w` as the next sibling of `anchor`, or as a new row if
116
    /// `anchor` is not in the tree.
117
    pub fn insert_after(&mut self, w: W, anchor: W) {
118
        let Some(&a) = self.leaves.get(&anchor) else {
119
            return self.insert_row(w);
120
        };
121
        let n = self.new_leaf(w);
122
        match self.position(a) {
123
            Some((p, i)) if Some(p) == self.root && self.axis(p) == Some(self.stack) => {
124
                let weight = self.unlink(p, i).weight;
125
                let c = self.new_container(self.stack.other());
126
                self.attach(c, 0, a, 1.0);
127
                self.attach(c, 1, n, 1.0);
128
                self.attach(p, i, c, weight);
129
            }
130
            Some((p, i)) => {
131
                let weight = self.mean_weight(p);
132
                self.attach(p, i + 1, n, weight);
133
            }
134
            None => {
135
                let c = self.new_container(self.stack.other());
136
                self.attach(c, 0, a, 1.0);
137
                self.attach(c, 1, n, 1.0);
138
                self.root = Some(c);
139
            }
140
        }
141
        self.reveal(n, true);
142
    }
143
144
    /// Appends `w` as a row of its own along the stacking axis.
145
    pub fn insert_row(&mut self, w: W) {
146
        let n = self.new_leaf(w);
147
        match self.root {
148
            None => self.root = Some(n),
149
            Some(r) if self.axis(r) == Some(self.stack) => {
150
                let weight = self.mean_weight(r);
151
                let end = self.children(r).len();
152
                self.attach(r, end, n, weight);
153
            }
154
            Some(r) => {
155
                let c = self.new_container(self.stack);
156
                self.attach(c, 0, r, 1.0);
157
                self.attach(c, 1, n, 1.0);
158
                self.root = Some(c);
159
            }
160
        }
161
        self.reveal(n, true);
162
    }
163
164
    pub fn remove(&mut self, w: W) -> bool {
165
        let Some(n) = self.leaves.remove(&w) else {
166
            return false;
167
        };
168
        self.detach(n);
169
        self.nodes.remove(n);
170
        true
171
    }
172
173
    pub fn retain(&mut self, mut keep: impl FnMut(W) -> bool) {
174
        let gone: Vec<W> = self.windows().filter(|&w| !keep(w)).collect();
175
        for w in gone {
176
            self.remove(w);
177
        }
178
    }
179
180
    /// Moves a window one step in `dir`. Leaves swap slots with the next
181
    /// visible sibling, enter a sibling container, or step out beside their
182
    /// container. Hidden windows never move.
183
    pub fn move_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
184
        let Some(&n) = self.leaves.get(&w) else {
185
            return false;
186
        };
187
        let moved = self.move_node(n, dir, &vis, true);
188
        self.reveal(n, moved)
189
    }
190
191
    /// Moves the container holding `w` as a rigid block. A window sitting
192
    /// directly in the root is its own block.
193
    pub fn move_container(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
194
        let Some(&n) = self.leaves.get(&w) else {
195
            return false;
196
        };
197
        let block = self.nodes[n]
198
            .parent
199
            .filter(|&p| Some(p) != self.root)
200
            .unwrap_or(n);
201
        let moved = self.move_node(block, dir, &vis, false);
202
        self.reveal(n, moved)
203
    }
204
205
    /// Moves a window to the end of the next visible lane in `dir` among its
206
    /// siblings. A lone window there becomes a stack with it. A window with no
207
    /// sibling lane that way leaves its stack as `move_window` would, so it
208
    /// takes one press to step out and another to merge.
209
    pub fn merge_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
210
        let Some(&n) = self.leaves.get(&w) else {
211
            return false;
212
        };
213
        if let Some((p, i)) = self.position(n)
214
            && self.axis(p) == Some(dir.axis())
215
            && let Some(j) = self.next_shown(p, i, dir, &vis)
216
        {
217
            self.join_lane(n, p, j);
218
            return self.reveal(n, true);
219
        }
220
        let moved = self.move_node(n, dir, &vis, true);
221
        self.reveal(n, moved)
222
    }
223
224
    /// Undoes any expansion along `leaf`'s ancestry that favours another
225
    /// child, once it has `moved` or arrived. An expanded container gives its
226
    /// other children no room, so a window moved into one vanished while
227
    /// staying focused.
228
    fn reveal(&mut self, leaf: NodeId, moved: bool) -> bool {
229
        let mut cur = leaf;
230
        while moved && let Some(p) = self.nodes[cur].parent {
231
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
232
                && expanded.is_some_and(|e| e != cur)
233
            {
234
                *expanded = None;
235
            }
236
            cur = p;
237
        }
238
        moved
239
    }
240
241
    /// Whether every container above `w` is expanded towards it.
242
    pub fn maximized(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
243
        let path = self.shown_path(w, &vis);
244
        !path.is_empty() && path.iter().all(|&(p, c)| self.expanded(p) == Some(c))
245
    }
246
247
    /// `w`'s ancestry as (container, child) pairs, leaving out containers
248
    /// where the child has no visible sibling. Those look like nothing on
249
    /// screen, so expanding there would do nothing the user could see.
250
    fn shown_path(&self, w: W, vis: &impl Fn(W) -> bool) -> Vec<(NodeId, NodeId)> {
251
        let Some(&leaf) = self.leaves.get(&w) else {
252
            return Vec::new();
253
        };
254
        let mut path = Vec::new();
255
        let mut cur = leaf;
256
        while let Some(p) = self.nodes[cur].parent {
257
            let shared = self
258
                .children(p)
259
                .iter()
260
                .any(|c| c.node != cur && self.visible(c.node, vis));
261
            if shared {
262
                path.push((p, cur));
263
            }
264
            cur = p;
265
        }
266
        path
267
    }
268
269
    /// Transposes the whole tree.
270
    pub fn flip(&mut self) {
271
        self.stack = self.stack.other();
272
        for node in self.nodes.values_mut() {
273
            if let Kind::Container { axis, .. } = &mut node.kind {
274
                *axis = axis.other();
275
            }
276
        }
277
    }
278
279
    /// Toggles expansion for `count` levels of `w`'s visible ancestry,
280
    /// starting `skip` levels up. Level 0 expands the window inside the
281
    /// nearest container where it has a visible sibling.
282
    pub fn toggle_expand(
283
        &mut self,
284
        w: W,
285
        skip: usize,
286
        count: usize,
287
        vis: impl Fn(W) -> bool,
288
    ) -> bool {
289
        let path: Vec<_> = self
290
            .shown_path(w, &vis)
291
            .into_iter()
292
            .skip(skip)
293
            .take(count)
294
            .collect();
295
        if path.is_empty() {
296
            return false;
297
        }
298
        let all = path.iter().all(|&(p, c)| self.expanded(p) == Some(c));
299
        for (p, c) in path {
300
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind {
301
                *expanded = (!all).then_some(c);
302
            }
303
        }
304
        true
305
    }
306
307
    /// Grows `w` by `delta`, a fraction of the visible length of the nearest
308
    /// container along `axis` where it has a visible sibling. Hidden siblings
309
    /// keep their weights, and with them their share.
310
    pub fn resize(&mut self, w: W, axis: Axis, delta: f64, vis: impl Fn(W) -> bool) -> bool {
311
        let Some(&leaf) = self.leaves.get(&w) else {
312
            return false;
313
        };
314
        let mut cur = leaf;
315
        while let Some((p, i)) = self.position(cur) {
316
            if self.axis(p) == Some(axis) {
317
                let shown: Vec<usize> = (0..self.children(p).len())
318
                    .filter(|&j| self.visible(self.children(p)[j].node, &vis))
319
                    .collect();
320
                if shown.len() >= 2 {
321
                    let children = self.children_mut(p);
322
                    let sum: f64 = shown.iter().map(|&j| children[j].weight).sum();
323
                    let old = children[i].weight;
324
                    let max = 1.0 - MIN_SHARE * (shown.len() - 1) as f64;
325
                    let new = (old / sum + delta).clamp(MIN_SHARE, max) * sum;
326
                    let scale = (sum - new) / (sum - old);
327
                    for j in shown {
328
                        children[j].weight = if j == i {
329
                            new
330
                        } else {
331
                            children[j].weight * scale
332
                        };
333
                    }
334
                    return true;
335
                }
336
            }
337
            cur = p;
338
        }
339
        false
340
    }
341
342
    /// Lays the tree out in `area`. Hidden windows come back as zero-size
343
    /// rects on the boundary where they would reappear.
344
    pub fn arrange(&self, area: Rect, gap: f64, vis: impl Fn(W) -> bool) -> Vec<(W, Rect)> {
345
        let mut out = Vec::with_capacity(self.leaves.len());
346
        if let Some(r) = self.root {
347
            let area = if self.visible(r, &vis) {
348
                area
349
            } else {
350
                Rect::new(area.x + area.w / 2.0, area.y, 0.0, area.h)
351
            };
352
            self.place(r, area, gap, &vis, &mut out);
353
        }
354
        out
355
    }
356
357
    fn place(
358
        &self,
359
        n: NodeId,
360
        r: Rect,
361
        gap: f64,
362
        vis: &impl Fn(W) -> bool,
363
        out: &mut Vec<(W, Rect)>,
364
    ) {
365
        let (axis, children, expanded) = match &self.nodes[n].kind {
366
            Kind::Leaf(w) => return out.push((*w, r)),
367
            Kind::Container {
368
                axis,
369
                children,
370
                expanded,
371
            } => (*axis, children, *expanded),
372
        };
373
        let shown: Vec<bool> = children.iter().map(|c| self.visible(c.node, vis)).collect();
374
        let (start, len) = span(r, axis);
375
        if let Some(e) = expanded.and_then(|e| children.iter().position(|c| c.node == e))
376
            && shown[e]
377
        {
378
            for (i, c) in children.iter().enumerate() {
379
                let sub = match i.cmp(&e) {
380
                    std::cmp::Ordering::Equal => r,
381
                    std::cmp::Ordering::Less => with_span(r, axis, start, 0.0),
382
                    std::cmp::Ordering::Greater => with_span(r, axis, start + len, 0.0),
383
                };
384
                self.place(c.node, sub, gap, vis, out);
385
            }
386
            return;
387
        }
388
        let count = shown.iter().filter(|s| **s).count();
389
        let total: f64 = children
390
            .iter()
391
            .zip(&shown)
392
            .filter(|(_, s)| **s)
393
            .map(|(c, _)| c.weight)
394
            .sum();
395
        let gaps = count.saturating_sub(1) as f64;
396
        let gap = if gaps > 0.0 { gap.min(len / gaps) } else { gap };
397
        let avail = len - gap * gaps;
398
        let last = shown.iter().rposition(|s| *s);
399
        let mut pos = start;
400
        let mut seen = false;
401
        for (i, c) in children.iter().enumerate() {
402
            if shown[i] {
403
                if seen {
404
                    pos += gap;
405
                }
406
                let l = avail * c.weight / total;
407
                self.place(c.node, with_span(r, axis, pos, l), gap, vis, out);
408
                pos += l;
409
                seen = true;
410
            } else {
411
                let between = seen && last.is_some_and(|l| l > i);
412
                let at = if between { pos + gap / 2.0 } else { pos };
413
                self.place(c.node, with_span(r, axis, at, 0.0), gap, vis, out);
414
            }
415
        }
416
    }
417
418
    fn move_node(&mut self, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool, enter: bool) -> bool {
419
        let mut cur = n;
420
        while let Some((p, i)) = self.position(cur) {
421
            if self.axis(p) == Some(dir.axis()) {
422
                let hidden_lane = self
423
                    .adjacent(p, i, dir)
424
                    .filter(|&j| enter && !self.visible(self.children(p)[j].node, vis));
425
                if cur != n {
426
                    if let Some(j) = hidden_lane {
427
                        self.join_lane(n, p, j);
428
                    } else {
429
                        let at = if dir.forward() { i + 1 } else { i };
430
                        self.step_out(n, p, i, at);
431
                    }
432
                    return true;
433
                }
434
                if let Some(j) = self.next_shown(p, i, dir, vis) {
435
                    let sib = self.children(p)[j].node;
436
                    if enter && self.axis(sib).is_some() {
437
                        let end = self.children(sib).len();
438
                        self.relocate(n, sib, end);
439
                    } else {
440
                        let children = self.children_mut(p);
441
                        children[i].node = sib;
442
                        children[j].node = n;
443
                    }
444
                    return true;
445
                }
446
                if let Some(j) = hidden_lane {
447
                    self.join_lane(n, p, j);
448
                    return true;
449
                }
450
            }
451
            cur = p;
452
        }
453
        if cur == n || self.axis(cur) == Some(dir.axis()) {
454
            return false;
455
        }
456
        let (old, i) = self.position(n).expect("n is below the root");
457
        self.release_share(n);
458
        self.unlink(old, i);
459
        self.normalize(old);
460
        let r = self.root.expect("the rest of the tree survives");
461
        if self.axis(r) == Some(dir.axis()) {
462
            let weight = self.mean_weight(r);
463
            let at = if dir.forward() {
464
                self.children(r).len()
465
            } else {
466
                0
467
            };
468
            self.attach(r, at, n, weight);
469
        } else {
470
            let c = self.new_container(dir.axis());
471
            self.attach(c, 0, r, 1.0);
472
            let at = if dir.forward() {
473
                self.children(c).len()
474
            } else {
475
                0
476
            };
477
            self.attach(c, at, n, 1.0);
478
            self.root = Some(c);
479
        }
480
        true
481
    }
482
483
    fn next_shown(&self, p: NodeId, i: usize, dir: Dir, vis: &impl Fn(W) -> bool) -> Option<usize> {
484
        let children = self.children(p);
485
        if dir.forward() {
486
            (i + 1..children.len()).find(|&j| self.visible(children[j].node, vis))
487
        } else {
488
            (0..i).rev().find(|&j| self.visible(children[j].node, vis))
489
        }
490
    }
491
492
    fn adjacent(&self, p: NodeId, i: usize, dir: Dir) -> Option<usize> {
493
        let j = if dir.forward() {
494
            i + 1
495
        } else {
496
            i.checked_sub(1)?
497
        };
498
        (j < self.children(p).len()).then_some(j)
499
    }
500
501
    /// Moves `n` to the end of the lane at `p`'s child `j`. A lone window
502
    /// there becomes a lane of its own, perpendicular to `p`.
503
    fn join_lane(&mut self, n: NodeId, p: NodeId, j: usize) {
504
        let lane = self.children(p)[j].node;
505
        let lane = if self.axis(lane).is_some() {
506
            lane
507
        } else {
508
            let c = self.new_container(self.axis(p).expect("p is a container").other());
509
            self.children_mut(p)[j].node = c;
510
            self.nodes[c].parent = Some(p);
511
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
512
                && *expanded == Some(lane)
513
            {
514
                *expanded = Some(c);
515
            }
516
            self.attach(c, 0, lane, 1.0);
517
            c
518
        };
519
        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);
523
        if let Some(weight) = weight
524
            && self.nodes.contains_key(lane)
525
            && let Some((q, k)) = self.position(lane)
526
            && q == p
527
        {
528
            self.children_mut(p)[k].weight += weight;
529
        }
530
    }
531
532
    /// Moves `n` out of `p`'s child `i` to sit beside it at `at`, taking
533
    /// along the share of that child's weight it had.
534
    fn step_out(&mut self, n: NodeId, p: NodeId, i: usize, at: usize) {
535
        let cur = self.children(p)[i].node;
536
        let Some((parent, k)) = self.position(n).filter(|&(q, _)| q == cur) else {
537
            self.relocate(n, p, at);
538
            return;
539
        };
540
        let share = self.release_share(n).unwrap_or_else(|| self.mean_weight(p));
541
        self.unlink(parent, k);
542
        self.attach(p, at, n, share);
543
        self.normalize(parent);
544
    }
545
546
    /// Takes `n`'s share of its container's weight away from the container,
547
    /// for `n` to carry out. Joining a lane hands a window's weight to the
548
    /// lane, so leaving gives it back and windows going in and out of
549
    /// stacks do not shrink their neighbours a little more each time.
550
    fn release_share(&mut self, n: NodeId) -> Option<f64> {
551
        let (old, i) = self.position(n)?;
552
        let (q, k) = self.position(old)?;
553
        let total: f64 = self.children(old).iter().map(|c| c.weight).sum();
554
        let share = self.children(q)[k].weight * self.children(old)[i].weight / total;
555
        self.children_mut(q)[k].weight -= share;
556
        Some(share)
557
    }
558
559
    fn relocate(&mut self, n: NodeId, p: NodeId, at: usize) {
560
        let (old, i) = self.position(n).expect("relocated nodes have a parent");
561
        let weight = self.mean_weight(p);
562
        let at = if old == p && i < at { at - 1 } else { at };
563
        self.unlink(old, i);
564
        self.attach(p, at, n, weight);
565
        self.normalize(old);
566
    }
567
568
    /// Places `node` among `p`'s children. A container running along `p`'s
569
    /// axis is dissolved into `p` so axes keep alternating.
570
    fn attach(&mut self, p: NodeId, at: usize, node: NodeId, weight: f64) {
571
        if self.axis(node) == self.axis(p) {
572
            let Some(Node {
573
                kind: Kind::Container { children, .. },
574
                ..
575
            }) = self.nodes.remove(node)
576
            else {
577
                unreachable!("only containers have an axis");
578
            };
579
            let total: f64 = children.iter().map(|c| c.weight).sum();
580
            for (k, c) in children.into_iter().enumerate() {
581
                self.nodes[c.node].parent = Some(p);
582
                let weight = weight * c.weight / total;
583
                self.children_mut(p).insert(
584
                    at + k,
585
                    Child {
586
                        node: c.node,
587
                        weight,
588
                    },
589
                );
590
            }
591
        } else {
592
            self.nodes[node].parent = Some(p);
593
            self.children_mut(p).insert(at, Child { node, weight });
594
        }
595
    }
596
597
    fn unlink(&mut self, p: NodeId, i: usize) -> Child {
598
        let Kind::Container {
599
            children, expanded, ..
600
        } = &mut self.nodes[p].kind
601
        else {
602
            unreachable!("parents are containers");
603
        };
604
        let child = children.remove(i);
605
        if *expanded == Some(child.node) {
606
            *expanded = None;
607
        }
608
        self.nodes[child.node].parent = None;
609
        child
610
    }
611
612
    fn detach(&mut self, n: NodeId) {
613
        match self.position(n) {
614
            Some((p, i)) => {
615
                self.unlink(p, i);
616
                self.normalize(p);
617
            }
618
            None => self.root = None,
619
        }
620
    }
621
622
    /// Repairs a container that lost a child.
623
    fn normalize(&mut self, c: NodeId) {
624
        if self.children(c).len() >= 2 {
625
            return;
626
        }
627
        let pos = self.position(c);
628
        let was_expanded = pos.is_some_and(|(p, _)| self.expanded(p) == Some(c));
629
        let Some(Node {
630
            kind: Kind::Container { children, .. },
631
            ..
632
        }) = self.nodes.remove(c)
633
        else {
634
            unreachable!("only containers are normalized");
635
        };
636
        let only = children.into_iter().next();
637
        match (only, pos) {
638
            (None, Some((p, i))) => {
639
                self.unlink_dead(p, i);
640
                self.normalize(p);
641
            }
642
            (None, None) => self.root = None,
643
            (Some(only), Some((p, i))) => {
644
                let weight = self.unlink_dead(p, i).weight;
645
                let leaf_like = self.axis(only.node) != self.axis(p);
646
                self.attach(p, i, only.node, weight);
647
                if was_expanded
648
                    && leaf_like
649
                    && let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
650
                {
651
                    *expanded = Some(only.node);
652
                }
653
            }
654
            (Some(only), None) => {
655
                self.nodes[only.node].parent = None;
656
                self.root = Some(only.node);
657
            }
658
        }
659
    }
660
661
    /// Like `unlink`, for a child whose node is already gone.
662
    fn unlink_dead(&mut self, p: NodeId, i: usize) -> Child {
663
        let Kind::Container {
664
            children, expanded, ..
665
        } = &mut self.nodes[p].kind
666
        else {
667
            unreachable!("parents are containers");
668
        };
669
        let child = children.remove(i);
670
        if *expanded == Some(child.node) {
671
            *expanded = None;
672
        }
673
        child
674
    }
675
676
    fn new_leaf(&mut self, w: W) -> NodeId {
677
        let n = self.nodes.insert(Node {
678
            parent: None,
679
            kind: Kind::Leaf(w),
680
        });
681
        self.leaves.insert(w, n);
682
        n
683
    }
684
685
    fn new_container(&mut self, axis: Axis) -> NodeId {
686
        self.nodes.insert(Node {
687
            parent: None,
688
            kind: Kind::Container {
689
                axis,
690
                children: Vec::new(),
691
                expanded: None,
692
            },
693
        })
694
    }
695
696
    fn position(&self, n: NodeId) -> Option<(NodeId, usize)> {
697
        let p = self.nodes[n].parent?;
698
        let i = self.children(p).iter().position(|c| c.node == n)?;
699
        Some((p, i))
700
    }
701
702
    fn axis(&self, n: NodeId) -> Option<Axis> {
703
        match &self.nodes[n].kind {
704
            Kind::Container { axis, .. } => Some(*axis),
705
            Kind::Leaf(_) => None,
706
        }
707
    }
708
709
    fn expanded(&self, n: NodeId) -> Option<NodeId> {
710
        match &self.nodes[n].kind {
711
            Kind::Container { expanded, .. } => *expanded,
712
            Kind::Leaf(_) => None,
713
        }
714
    }
715
716
    fn children(&self, n: NodeId) -> &[Child] {
717
        match &self.nodes[n].kind {
718
            Kind::Container { children, .. } => children,
719
            Kind::Leaf(_) => &[],
720
        }
721
    }
722
723
    fn children_mut(&mut self, n: NodeId) -> &mut Vec<Child> {
724
        match &mut self.nodes[n].kind {
725
            Kind::Container { children, .. } => children,
726
            Kind::Leaf(_) => unreachable!("leaves have no children"),
727
        }
728
    }
729
730
    fn mean_weight(&self, p: NodeId) -> f64 {
731
        let children = self.children(p);
732
        if children.is_empty() {
733
            1.0
734
        } else {
735
            children.iter().map(|c| c.weight).sum::<f64>() / children.len() as f64
736
        }
737
    }
738
739
    fn visible(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> bool {
740
        match &self.nodes[n].kind {
741
            Kind::Leaf(w) => vis(*w),
742
            Kind::Container { children, .. } => children.iter().any(|c| self.visible(c.node, vis)),
743
        }
744
    }
745
746
    #[cfg(test)]
747
    pub(crate) fn check(&self) {
748
        let reachable = match self.root {
749
            Some(r) => {
750
                assert!(self.nodes[r].parent.is_none());
751
                self.check_node(r)
752
            }
753
            None => 0,
754
        };
755
        assert_eq!(reachable, self.nodes.len(), "orphaned nodes");
756
        for (&w, &n) in &self.leaves {
757
            assert!(matches!(self.nodes[n].kind, Kind::Leaf(x) if x == w));
758
        }
759
    }
760
761
    #[cfg(test)]
762
    fn check_node(&self, n: NodeId) -> usize {
763
        match &self.nodes[n].kind {
764
            Kind::Leaf(_) => 1,
765
            Kind::Container {
766
                axis,
767
                children,
768
                expanded,
769
            } => {
770
                assert!(
771
                    children.len() >= 2,
772
                    "container with {} children",
773
                    children.len()
774
                );
775
                if let Some(e) = expanded {
776
                    assert!(children.iter().any(|c| c.node == *e));
777
                }
778
                let mut count = 1;
779
                for c in children {
780
                    assert!(c.weight > 0.0 && c.weight.is_finite());
781
                    assert_eq!(self.nodes[c.node].parent, Some(n));
782
                    assert_ne!(self.axis(c.node), Some(*axis), "axes must alternate");
783
                    count += self.check_node(c.node);
784
                }
785
                count
786
            }
787
        }
788
    }
789
}
790
791
fn span(r: Rect, axis: Axis) -> (f64, f64) {
792
    match axis {
793
        Axis::Horizontal => (r.x, r.w),
794
        Axis::Vertical => (r.y, r.h),
795
    }
796
}
797
798
fn with_span(r: Rect, axis: Axis, start: f64, len: f64) -> Rect {
799
    match axis {
800
        Axis::Horizontal => Rect::new(start, r.y, len, r.h),
801
        Axis::Vertical => Rect::new(r.x, start, r.w, len),
802
    }
803
}