atagen

monolith

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