atagen

monolith

public
Star0
Watch0
9944e8618a62a996bdd1b8ec56a43ae0f9946e3f
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/layout/src/tree.rs

9baaafa4b167atagen2026-10-16 10:04 UTC1
use monolith_planner::Rect;
9baaafa4b167atagen2026-10-16 10:04 UTC2
use slotmap::{SlotMap, new_key_type};
9baaafa4b167atagen2026-10-16 10:04 UTC3
use std::collections::HashMap;
9baaafa4b167atagen2026-10-16 10:04 UTC4
use std::hash::Hash;
9baaafa4b167atagen2026-10-16 10:04 UTC5
9baaafa4b167atagen2026-10-16 10:04 UTC6
new_key_type! { pub struct NodeId; }
9baaafa4b167atagen2026-10-16 10:04 UTC7
9baaafa4b167atagen2026-10-16 10:04 UTC8
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
b22c0511d5afatagen2026-10-17 03:48 UTC9
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
b22c0511d5afatagen2026-10-17 03:48 UTC10
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
9baaafa4b167atagen2026-10-16 10:04 UTC11
pub enum Axis {
9baaafa4b167atagen2026-10-16 10:04 UTC12
    Horizontal,
9baaafa4b167atagen2026-10-16 10:04 UTC13
    Vertical,
9baaafa4b167atagen2026-10-16 10:04 UTC14
}
9baaafa4b167atagen2026-10-16 10:04 UTC15
9baaafa4b167atagen2026-10-16 10:04 UTC16
impl Axis {
9baaafa4b167atagen2026-10-16 10:04 UTC17
    pub fn other(self) -> Self {
9baaafa4b167atagen2026-10-16 10:04 UTC18
        match self {
9baaafa4b167atagen2026-10-16 10:04 UTC19
            Self::Horizontal => Self::Vertical,
9baaafa4b167atagen2026-10-16 10:04 UTC20
            Self::Vertical => Self::Horizontal,
9baaafa4b167atagen2026-10-16 10:04 UTC21
        }
9baaafa4b167atagen2026-10-16 10:04 UTC22
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC23
e2443dec4dcdatagen2026-10-18 09:58 UTC24
    fn index(self) -> usize {
e2443dec4dcdatagen2026-10-18 09:58 UTC25
        match self {
e2443dec4dcdatagen2026-10-18 09:58 UTC26
            Self::Horizontal => 0,
e2443dec4dcdatagen2026-10-18 09:58 UTC27
            Self::Vertical => 1,
e2443dec4dcdatagen2026-10-18 09:58 UTC28
        }
e2443dec4dcdatagen2026-10-18 09:58 UTC29
    }
9baaafa4b167atagen2026-10-16 10:04 UTC30
}
9baaafa4b167atagen2026-10-16 10:04 UTC31
9baaafa4b167atagen2026-10-16 10:04 UTC32
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
b22c0511d5afatagen2026-10-17 03:48 UTC33
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
b22c0511d5afatagen2026-10-17 03:48 UTC34
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
9baaafa4b167atagen2026-10-16 10:04 UTC35
pub enum Dir {
9baaafa4b167atagen2026-10-16 10:04 UTC36
    Left,
9baaafa4b167atagen2026-10-16 10:04 UTC37
    Right,
9baaafa4b167atagen2026-10-16 10:04 UTC38
    Up,
9baaafa4b167atagen2026-10-16 10:04 UTC39
    Down,
9baaafa4b167atagen2026-10-16 10:04 UTC40
}
9baaafa4b167atagen2026-10-16 10:04 UTC41
9baaafa4b167atagen2026-10-16 10:04 UTC42
impl Dir {
9baaafa4b167atagen2026-10-16 10:04 UTC43
    pub fn axis(self) -> Axis {
9baaafa4b167atagen2026-10-16 10:04 UTC44
        match self {
9baaafa4b167atagen2026-10-16 10:04 UTC45
            Self::Left | Self::Right => Axis::Horizontal,
9baaafa4b167atagen2026-10-16 10:04 UTC46
            Self::Up | Self::Down => Axis::Vertical,
9baaafa4b167atagen2026-10-16 10:04 UTC47
        }
9baaafa4b167atagen2026-10-16 10:04 UTC48
    }
9baaafa4b167atagen2026-10-16 10:04 UTC49
9baaafa4b167atagen2026-10-16 10:04 UTC50
    pub fn forward(self) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC51
        matches!(self, Self::Right | Self::Down)
9baaafa4b167atagen2026-10-16 10:04 UTC52
    }
9baaafa4b167atagen2026-10-16 10:04 UTC53
}
9baaafa4b167atagen2026-10-16 10:04 UTC54
9baaafa4b167atagen2026-10-16 10:04 UTC55
const MIN_SHARE: f64 = 0.05;
9baaafa4b167atagen2026-10-16 10:04 UTC56
e2443dec4dcdatagen2026-10-18 09:58 UTC57
/// A window's width and height relative to its neighbours'.
e2443dec4dcdatagen2026-10-18 09:58 UTC58
type Size = [f64; 2];
9baaafa4b167atagen2026-10-16 10:04 UTC59
9baaafa4b167atagen2026-10-16 10:04 UTC60
#[derive(Clone, Debug)]
9baaafa4b167atagen2026-10-16 10:04 UTC61
enum Kind<W> {
e2443dec4dcdatagen2026-10-18 09:58 UTC62
    Leaf(W, Size),
9baaafa4b167atagen2026-10-16 10:04 UTC63
    Container {
9baaafa4b167atagen2026-10-16 10:04 UTC64
        axis: Axis,
e2443dec4dcdatagen2026-10-18 09:58 UTC65
        children: Vec<NodeId>,
9baaafa4b167atagen2026-10-16 10:04 UTC66
        expanded: Option<NodeId>,
9baaafa4b167atagen2026-10-16 10:04 UTC67
    },
9baaafa4b167atagen2026-10-16 10:04 UTC68
}
9baaafa4b167atagen2026-10-16 10:04 UTC69
9baaafa4b167atagen2026-10-16 10:04 UTC70
#[derive(Clone, Debug)]
9baaafa4b167atagen2026-10-16 10:04 UTC71
struct Node<W> {
9baaafa4b167atagen2026-10-16 10:04 UTC72
    parent: Option<NodeId>,
9baaafa4b167atagen2026-10-16 10:04 UTC73
    kind: Kind<W>,
9baaafa4b167atagen2026-10-16 10:04 UTC74
}
9baaafa4b167atagen2026-10-16 10:04 UTC75
9baaafa4b167atagen2026-10-16 10:04 UTC76
/// One output's tiling tree. Every tiled window of every tag lives here, the
9baaafa4b167atagen2026-10-16 10:04 UTC77
/// caller's visibility filter decides which ones take space.
9baaafa4b167atagen2026-10-16 10:04 UTC78
///
9baaafa4b167atagen2026-10-16 10:04 UTC79
/// Children run on the axis opposite their parent. Containers hold at least
9baaafa4b167atagen2026-10-16 10:04 UTC80
/// two children, hidden ones included, and a container left with one child
9baaafa4b167atagen2026-10-16 10:04 UTC81
/// is replaced by it.
9baaafa4b167atagen2026-10-16 10:04 UTC82
#[derive(Clone, Debug)]
9baaafa4b167atagen2026-10-16 10:04 UTC83
pub struct Tree<W> {
9baaafa4b167atagen2026-10-16 10:04 UTC84
    nodes: SlotMap<NodeId, Node<W>>,
9baaafa4b167atagen2026-10-16 10:04 UTC85
    root: Option<NodeId>,
9baaafa4b167atagen2026-10-16 10:04 UTC86
    leaves: HashMap<W, NodeId>,
9baaafa4b167atagen2026-10-16 10:04 UTC87
    stack: Axis,
9baaafa4b167atagen2026-10-16 10:04 UTC88
}
9baaafa4b167atagen2026-10-16 10:04 UTC89
9baaafa4b167atagen2026-10-16 10:04 UTC90
impl<W: Copy + Eq + Hash> Default for Tree<W> {
9baaafa4b167atagen2026-10-16 10:04 UTC91
    fn default() -> Self {
9baaafa4b167atagen2026-10-16 10:04 UTC92
        Self {
9baaafa4b167atagen2026-10-16 10:04 UTC93
            nodes: SlotMap::with_key(),
9baaafa4b167atagen2026-10-16 10:04 UTC94
            root: None,
9baaafa4b167atagen2026-10-16 10:04 UTC95
            leaves: HashMap::new(),
9baaafa4b167atagen2026-10-16 10:04 UTC96
            stack: Axis::Vertical,
9baaafa4b167atagen2026-10-16 10:04 UTC97
        }
9baaafa4b167atagen2026-10-16 10:04 UTC98
    }
9baaafa4b167atagen2026-10-16 10:04 UTC99
}
9baaafa4b167atagen2026-10-16 10:04 UTC100
9baaafa4b167atagen2026-10-16 10:04 UTC101
impl<W: Copy + Eq + Hash> Tree<W> {
9baaafa4b167atagen2026-10-16 10:04 UTC102
    /// The axis tag rows stack along.
9baaafa4b167atagen2026-10-16 10:04 UTC103
    pub fn stack_axis(&self) -> Axis {
9baaafa4b167atagen2026-10-16 10:04 UTC104
        self.stack
9baaafa4b167atagen2026-10-16 10:04 UTC105
    }
9baaafa4b167atagen2026-10-16 10:04 UTC106
9baaafa4b167atagen2026-10-16 10:04 UTC107
    pub fn contains(&self, w: W) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC108
        self.leaves.contains_key(&w)
9baaafa4b167atagen2026-10-16 10:04 UTC109
    }
9baaafa4b167atagen2026-10-16 10:04 UTC110
9baaafa4b167atagen2026-10-16 10:04 UTC111
    pub fn is_empty(&self) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC112
        self.root.is_none()
9baaafa4b167atagen2026-10-16 10:04 UTC113
    }
9baaafa4b167atagen2026-10-16 10:04 UTC114
9baaafa4b167atagen2026-10-16 10:04 UTC115
    pub fn windows(&self) -> impl Iterator<Item = W> + '_ {
9baaafa4b167atagen2026-10-16 10:04 UTC116
        self.leaves.keys().copied()
9baaafa4b167atagen2026-10-16 10:04 UTC117
    }
9baaafa4b167atagen2026-10-16 10:04 UTC118
9baaafa4b167atagen2026-10-16 10:04 UTC119
    /// Inserts `w` as the next sibling of `anchor`, or as a new row if
9baaafa4b167atagen2026-10-16 10:04 UTC120
    /// `anchor` is not in the tree.
9baaafa4b167atagen2026-10-16 10:04 UTC121
    pub fn insert_after(&mut self, w: W, anchor: W) {
9baaafa4b167atagen2026-10-16 10:04 UTC122
        let Some(&a) = self.leaves.get(&anchor) else {
9baaafa4b167atagen2026-10-16 10:04 UTC123
            return self.insert_row(w);
9baaafa4b167atagen2026-10-16 10:04 UTC124
        };
9baaafa4b167atagen2026-10-16 10:04 UTC125
        match self.position(a) {
9baaafa4b167atagen2026-10-16 10:04 UTC126
            Some((p, i)) if Some(p) == self.root && self.axis(p) == Some(self.stack) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC127
                let n = self.new_leaf(w, self.size(a));
e2443dec4dcdatagen2026-10-18 09:58 UTC128
                self.unlink(p, i);
9baaafa4b167atagen2026-10-16 10:04 UTC129
                let c = self.new_container(self.stack.other());
e2443dec4dcdatagen2026-10-18 09:58 UTC130
                self.attach(c, 0, a);
e2443dec4dcdatagen2026-10-18 09:58 UTC131
                self.attach(c, 1, n);
e2443dec4dcdatagen2026-10-18 09:58 UTC132
                self.attach(p, i, c);
e2443dec4dcdatagen2026-10-18 09:58 UTC133
                self.reveal(n, true);
9baaafa4b167atagen2026-10-16 10:04 UTC134
            }
9baaafa4b167atagen2026-10-16 10:04 UTC135
            Some((p, i)) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC136
                let n = self.new_leaf(w, self.size(p));
e2443dec4dcdatagen2026-10-18 09:58 UTC137
                self.attach(p, i + 1, n);
e2443dec4dcdatagen2026-10-18 09:58 UTC138
                self.reveal(n, true);
9baaafa4b167atagen2026-10-16 10:04 UTC139
            }
9baaafa4b167atagen2026-10-16 10:04 UTC140
            None => {
e2443dec4dcdatagen2026-10-18 09:58 UTC141
                let n = self.new_leaf(w, self.size(a));
9baaafa4b167atagen2026-10-16 10:04 UTC142
                let c = self.new_container(self.stack.other());
e2443dec4dcdatagen2026-10-18 09:58 UTC143
                self.attach(c, 0, a);
e2443dec4dcdatagen2026-10-18 09:58 UTC144
                self.attach(c, 1, n);
9baaafa4b167atagen2026-10-16 10:04 UTC145
                self.root = Some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC146
            }
9baaafa4b167atagen2026-10-16 10:04 UTC147
        }
9baaafa4b167atagen2026-10-16 10:04 UTC148
    }
9baaafa4b167atagen2026-10-16 10:04 UTC149
9baaafa4b167atagen2026-10-16 10:04 UTC150
    /// Appends `w` as a row of its own along the stacking axis.
9baaafa4b167atagen2026-10-16 10:04 UTC151
    pub fn insert_row(&mut self, w: W) {
e2443dec4dcdatagen2026-10-18 09:58 UTC152
        let size = self.root.map_or([1.0; 2], |r| self.size(r));
e2443dec4dcdatagen2026-10-18 09:58 UTC153
        let n = self.new_leaf(w, size);
9baaafa4b167atagen2026-10-16 10:04 UTC154
        match self.root {
9baaafa4b167atagen2026-10-16 10:04 UTC155
            None => self.root = Some(n),
9baaafa4b167atagen2026-10-16 10:04 UTC156
            Some(r) if self.axis(r) == Some(self.stack) => {
9baaafa4b167atagen2026-10-16 10:04 UTC157
                let end = self.children(r).len();
e2443dec4dcdatagen2026-10-18 09:58 UTC158
                self.attach(r, end, n);
9baaafa4b167atagen2026-10-16 10:04 UTC159
            }
9baaafa4b167atagen2026-10-16 10:04 UTC160
            Some(r) => {
9baaafa4b167atagen2026-10-16 10:04 UTC161
                let c = self.new_container(self.stack);
e2443dec4dcdatagen2026-10-18 09:58 UTC162
                self.attach(c, 0, r);
e2443dec4dcdatagen2026-10-18 09:58 UTC163
                self.attach(c, 1, n);
9baaafa4b167atagen2026-10-16 10:04 UTC164
                self.root = Some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC165
            }
9baaafa4b167atagen2026-10-16 10:04 UTC166
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC167
        self.reveal(n, true);
9baaafa4b167atagen2026-10-16 10:04 UTC168
    }
9baaafa4b167atagen2026-10-16 10:04 UTC169
9baaafa4b167atagen2026-10-16 10:04 UTC170
    pub fn remove(&mut self, w: W) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC171
        let Some(n) = self.leaves.remove(&w) else {
9baaafa4b167atagen2026-10-16 10:04 UTC172
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC173
        };
9baaafa4b167atagen2026-10-16 10:04 UTC174
        self.detach(n);
9baaafa4b167atagen2026-10-16 10:04 UTC175
        self.nodes.remove(n);
9baaafa4b167atagen2026-10-16 10:04 UTC176
        true
9baaafa4b167atagen2026-10-16 10:04 UTC177
    }
9baaafa4b167atagen2026-10-16 10:04 UTC178
9baaafa4b167atagen2026-10-16 10:04 UTC179
    pub fn retain(&mut self, mut keep: impl FnMut(W) -> bool) {
9baaafa4b167atagen2026-10-16 10:04 UTC180
        let gone: Vec<W> = self.windows().filter(|&w| !keep(w)).collect();
9baaafa4b167atagen2026-10-16 10:04 UTC181
        for w in gone {
9baaafa4b167atagen2026-10-16 10:04 UTC182
            self.remove(w);
9baaafa4b167atagen2026-10-16 10:04 UTC183
        }
9baaafa4b167atagen2026-10-16 10:04 UTC184
    }
9baaafa4b167atagen2026-10-16 10:04 UTC185
9baaafa4b167atagen2026-10-16 10:04 UTC186
    /// Moves a window one step in `dir`. Leaves swap slots with the next
9baaafa4b167atagen2026-10-16 10:04 UTC187
    /// visible sibling, enter a sibling container, or step out beside their
9baaafa4b167atagen2026-10-16 10:04 UTC188
    /// container. Hidden windows never move.
9baaafa4b167atagen2026-10-16 10:04 UTC189
    pub fn move_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC190
        let Some(&n) = self.leaves.get(&w) else {
9baaafa4b167atagen2026-10-16 10:04 UTC191
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC192
        };
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC193
        let moved = self.move_node(n, dir, &vis, true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC194
        self.reveal(n, moved)
9baaafa4b167atagen2026-10-16 10:04 UTC195
    }
9baaafa4b167atagen2026-10-16 10:04 UTC196
9baaafa4b167atagen2026-10-16 10:04 UTC197
    /// Moves the container holding `w` as a rigid block. A window sitting
9baaafa4b167atagen2026-10-16 10:04 UTC198
    /// directly in the root is its own block.
4444c779eb48atagen2026-10-16 12:07 UTC199
    pub fn move_container(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC200
        let Some(&n) = self.leaves.get(&w) else {
9baaafa4b167atagen2026-10-16 10:04 UTC201
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC202
        };
9baaafa4b167atagen2026-10-16 10:04 UTC203
        let block = self.nodes[n]
9baaafa4b167atagen2026-10-16 10:04 UTC204
            .parent
9baaafa4b167atagen2026-10-16 10:04 UTC205
            .filter(|&p| Some(p) != self.root)
9baaafa4b167atagen2026-10-16 10:04 UTC206
            .unwrap_or(n);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC207
        let moved = self.move_node(block, dir, &vis, false);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC208
        self.reveal(n, moved)
9baaafa4b167atagen2026-10-16 10:04 UTC209
    }
9baaafa4b167atagen2026-10-16 10:04 UTC210
605c44c8c5fcatagen2026-10-17 09:57 UTC211
    /// Moves a window to the end of the next visible lane in `dir` among its
605c44c8c5fcatagen2026-10-17 09:57 UTC212
    /// siblings. A lone window there becomes a stack with it. A window with no
605c44c8c5fcatagen2026-10-17 09:57 UTC213
    /// sibling lane that way leaves its stack as `move_window` would, so it
605c44c8c5fcatagen2026-10-17 09:57 UTC214
    /// takes one press to step out and another to merge.
4444c779eb48atagen2026-10-16 12:07 UTC215
    pub fn merge_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
4444c779eb48atagen2026-10-16 12:07 UTC216
        let Some(&n) = self.leaves.get(&w) else {
4444c779eb48atagen2026-10-16 12:07 UTC217
            return false;
4444c779eb48atagen2026-10-16 12:07 UTC218
        };
605c44c8c5fcatagen2026-10-17 09:57 UTC219
        if let Some((p, i)) = self.position(n)
605c44c8c5fcatagen2026-10-17 09:57 UTC220
            && self.axis(p) == Some(dir.axis())
605c44c8c5fcatagen2026-10-17 09:57 UTC221
            && let Some(j) = self.next_shown(p, i, dir, &vis)
605c44c8c5fcatagen2026-10-17 09:57 UTC222
        {
605c44c8c5fcatagen2026-10-17 09:57 UTC223
            self.join_lane(n, p, j);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC224
            return self.reveal(n, true);
4444c779eb48atagen2026-10-16 12:07 UTC225
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC226
        let moved = self.move_node(n, dir, &vis, true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC227
        self.reveal(n, moved)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC228
    }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC229
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC230
    /// Undoes any expansion along `leaf`'s ancestry that favours another
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC231
    /// child, once it has `moved` or arrived. An expanded container gives its
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC232
    /// other children no room, so a window moved into one vanished while
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC233
    /// staying focused.
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC234
    fn reveal(&mut self, leaf: NodeId, moved: bool) -> bool {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC235
        let mut cur = leaf;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC236
        while moved && let Some(p) = self.nodes[cur].parent {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC237
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC238
                && expanded.is_some_and(|e| e != cur)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC239
            {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC240
                *expanded = None;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC241
            }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC242
            cur = p;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC243
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC244
        moved
4444c779eb48atagen2026-10-16 12:07 UTC245
    }
4444c779eb48atagen2026-10-16 12:07 UTC246
4444c779eb48atagen2026-10-16 12:07 UTC247
    /// Whether every container above `w` is expanded towards it.
54559daf11c3atagen2026-10-17 13:38 UTC248
    pub fn maximized(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
54559daf11c3atagen2026-10-17 13:38 UTC249
        let path = self.shown_path(w, &vis);
54559daf11c3atagen2026-10-17 13:38 UTC250
        !path.is_empty() && path.iter().all(|&(p, c)| self.expanded(p) == Some(c))
54559daf11c3atagen2026-10-17 13:38 UTC251
    }
54559daf11c3atagen2026-10-17 13:38 UTC252
54559daf11c3atagen2026-10-17 13:38 UTC253
    /// `w`'s ancestry as (container, child) pairs, leaving out containers
54559daf11c3atagen2026-10-17 13:38 UTC254
    /// where the child has no visible sibling. Those look like nothing on
54559daf11c3atagen2026-10-17 13:38 UTC255
    /// screen, so expanding there would do nothing the user could see.
54559daf11c3atagen2026-10-17 13:38 UTC256
    fn shown_path(&self, w: W, vis: &impl Fn(W) -> bool) -> Vec<(NodeId, NodeId)> {
4444c779eb48atagen2026-10-16 12:07 UTC257
        let Some(&leaf) = self.leaves.get(&w) else {
54559daf11c3atagen2026-10-17 13:38 UTC258
            return Vec::new();
4444c779eb48atagen2026-10-16 12:07 UTC259
        };
54559daf11c3atagen2026-10-17 13:38 UTC260
        let mut path = Vec::new();
4444c779eb48atagen2026-10-16 12:07 UTC261
        let mut cur = leaf;
4444c779eb48atagen2026-10-16 12:07 UTC262
        while let Some(p) = self.nodes[cur].parent {
54559daf11c3atagen2026-10-17 13:38 UTC263
            let shared = self
54559daf11c3atagen2026-10-17 13:38 UTC264
                .children(p)
54559daf11c3atagen2026-10-17 13:38 UTC265
                .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC266
                .any(|&c| c != cur && self.visible(c, vis));
54559daf11c3atagen2026-10-17 13:38 UTC267
            if shared {
54559daf11c3atagen2026-10-17 13:38 UTC268
                path.push((p, cur));
4444c779eb48atagen2026-10-16 12:07 UTC269
            }
4444c779eb48atagen2026-10-16 12:07 UTC270
            cur = p;
4444c779eb48atagen2026-10-16 12:07 UTC271
        }
54559daf11c3atagen2026-10-17 13:38 UTC272
        path
4444c779eb48atagen2026-10-16 12:07 UTC273
    }
4444c779eb48atagen2026-10-16 12:07 UTC274
9baaafa4b167atagen2026-10-16 10:04 UTC275
    /// Transposes the whole tree.
9baaafa4b167atagen2026-10-16 10:04 UTC276
    pub fn flip(&mut self) {
9baaafa4b167atagen2026-10-16 10:04 UTC277
        self.stack = self.stack.other();
9baaafa4b167atagen2026-10-16 10:04 UTC278
        for node in self.nodes.values_mut() {
e2443dec4dcdatagen2026-10-18 09:58 UTC279
            match &mut node.kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC280
                Kind::Container { axis, .. } => *axis = axis.other(),
e2443dec4dcdatagen2026-10-18 09:58 UTC281
                Kind::Leaf(_, size) => size.reverse(),
9baaafa4b167atagen2026-10-16 10:04 UTC282
            }
9baaafa4b167atagen2026-10-16 10:04 UTC283
        }
9baaafa4b167atagen2026-10-16 10:04 UTC284
    }
9baaafa4b167atagen2026-10-16 10:04 UTC285
54559daf11c3atagen2026-10-17 13:38 UTC286
    /// Toggles expansion for `count` levels of `w`'s visible ancestry,
54559daf11c3atagen2026-10-17 13:38 UTC287
    /// starting `skip` levels up. Level 0 expands the window inside the
54559daf11c3atagen2026-10-17 13:38 UTC288
    /// nearest container where it has a visible sibling.
54559daf11c3atagen2026-10-17 13:38 UTC289
    pub fn toggle_expand(
54559daf11c3atagen2026-10-17 13:38 UTC290
        &mut self,
54559daf11c3atagen2026-10-17 13:38 UTC291
        w: W,
54559daf11c3atagen2026-10-17 13:38 UTC292
        skip: usize,
54559daf11c3atagen2026-10-17 13:38 UTC293
        count: usize,
54559daf11c3atagen2026-10-17 13:38 UTC294
        vis: impl Fn(W) -> bool,
54559daf11c3atagen2026-10-17 13:38 UTC295
    ) -> bool {
54559daf11c3atagen2026-10-17 13:38 UTC296
        let path: Vec<_> = self
54559daf11c3atagen2026-10-17 13:38 UTC297
            .shown_path(w, &vis)
54559daf11c3atagen2026-10-17 13:38 UTC298
            .into_iter()
54559daf11c3atagen2026-10-17 13:38 UTC299
            .skip(skip)
54559daf11c3atagen2026-10-17 13:38 UTC300
            .take(count)
54559daf11c3atagen2026-10-17 13:38 UTC301
            .collect();
9baaafa4b167atagen2026-10-16 10:04 UTC302
        if path.is_empty() {
9baaafa4b167atagen2026-10-16 10:04 UTC303
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC304
        }
9baaafa4b167atagen2026-10-16 10:04 UTC305
        let all = path.iter().all(|&(p, c)| self.expanded(p) == Some(c));
9baaafa4b167atagen2026-10-16 10:04 UTC306
        for (p, c) in path {
9baaafa4b167atagen2026-10-16 10:04 UTC307
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC308
                *expanded = (!all).then_some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC309
            }
9baaafa4b167atagen2026-10-16 10:04 UTC310
        }
9baaafa4b167atagen2026-10-16 10:04 UTC311
        true
9baaafa4b167atagen2026-10-16 10:04 UTC312
    }
9baaafa4b167atagen2026-10-16 10:04 UTC313
9baaafa4b167atagen2026-10-16 10:04 UTC314
    /// Grows `w` by `delta`, a fraction of the visible length of the nearest
9baaafa4b167atagen2026-10-16 10:04 UTC315
    /// container along `axis` where it has a visible sibling. Hidden siblings
e2443dec4dcdatagen2026-10-18 09:58 UTC316
    /// keep their sizes, and with them their share.
9baaafa4b167atagen2026-10-16 10:04 UTC317
    pub fn resize(&mut self, w: W, axis: Axis, delta: f64, vis: impl Fn(W) -> bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC318
        let Some(&leaf) = self.leaves.get(&w) else {
9baaafa4b167atagen2026-10-16 10:04 UTC319
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC320
        };
9baaafa4b167atagen2026-10-16 10:04 UTC321
        let mut cur = leaf;
e2443dec4dcdatagen2026-10-18 09:58 UTC322
        while let Some(p) = self.nodes[cur].parent {
9baaafa4b167atagen2026-10-16 10:04 UTC323
            if self.axis(p) == Some(axis) {
e2443dec4dcdatagen2026-10-18 09:58 UTC324
                let shown: Vec<(NodeId, f64)> = self
e2443dec4dcdatagen2026-10-18 09:58 UTC325
                    .children(p)
e2443dec4dcdatagen2026-10-18 09:58 UTC326
                    .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC327
                    .filter(|&&c| self.visible(c, &vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC328
                    .map(|&c| (c, self.extent(c, axis, &vis)))
9baaafa4b167atagen2026-10-16 10:04 UTC329
                    .collect();
9baaafa4b167atagen2026-10-16 10:04 UTC330
                if shown.len() >= 2 {
e2443dec4dcdatagen2026-10-18 09:58 UTC331
                    let sum: f64 = shown.iter().map(|s| s.1).sum();
e2443dec4dcdatagen2026-10-18 09:58 UTC332
                    let old = self.extent(cur, axis, &vis);
9baaafa4b167atagen2026-10-16 10:04 UTC333
                    let max = 1.0 - MIN_SHARE * (shown.len() - 1) as f64;
9baaafa4b167atagen2026-10-16 10:04 UTC334
                    let new = (old / sum + delta).clamp(MIN_SHARE, max) * sum;
9baaafa4b167atagen2026-10-16 10:04 UTC335
                    let scale = (sum - new) / (sum - old);
e2443dec4dcdatagen2026-10-18 09:58 UTC336
                    for (c, _) in shown {
e2443dec4dcdatagen2026-10-18 09:58 UTC337
                        self.scale(c, axis, if c == cur { new / old } else { scale });
9baaafa4b167atagen2026-10-16 10:04 UTC338
                    }
9baaafa4b167atagen2026-10-16 10:04 UTC339
                    return true;
9baaafa4b167atagen2026-10-16 10:04 UTC340
                }
9baaafa4b167atagen2026-10-16 10:04 UTC341
            }
9baaafa4b167atagen2026-10-16 10:04 UTC342
            cur = p;
9baaafa4b167atagen2026-10-16 10:04 UTC343
        }
9baaafa4b167atagen2026-10-16 10:04 UTC344
        false
9baaafa4b167atagen2026-10-16 10:04 UTC345
    }
9baaafa4b167atagen2026-10-16 10:04 UTC346
e2443dec4dcdatagen2026-10-18 09:58 UTC347
    /// Scales every window under `n` along `axis`, hidden ones too, so they
e2443dec4dcdatagen2026-10-18 09:58 UTC348
    /// keep their proportions for when they are shown again.
e2443dec4dcdatagen2026-10-18 09:58 UTC349
    fn scale(&mut self, n: NodeId, axis: Axis, by: f64) {
e2443dec4dcdatagen2026-10-18 09:58 UTC350
        match &mut self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC351
            Kind::Leaf(_, size) => size[axis.index()] *= by,
e2443dec4dcdatagen2026-10-18 09:58 UTC352
            Kind::Container { children, .. } => {
e2443dec4dcdatagen2026-10-18 09:58 UTC353
                for c in children.clone() {
e2443dec4dcdatagen2026-10-18 09:58 UTC354
                    self.scale(c, axis, by);
e2443dec4dcdatagen2026-10-18 09:58 UTC355
                }
e2443dec4dcdatagen2026-10-18 09:58 UTC356
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC357
        }
e2443dec4dcdatagen2026-10-18 09:58 UTC358
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC359
e2443dec4dcdatagen2026-10-18 09:58 UTC360
    /// How much room `n` asks for along `axis` among its siblings. A
e2443dec4dcdatagen2026-10-18 09:58 UTC361
    /// container asks for the mean of what its visible children ask for, so
e2443dec4dcdatagen2026-10-18 09:58 UTC362
    /// windows of other tags sharing a lane change nothing on screen, and a
e2443dec4dcdatagen2026-10-18 09:58 UTC363
    /// window carries its own size wherever it moves.
e2443dec4dcdatagen2026-10-18 09:58 UTC364
    fn extent(&self, n: NodeId, axis: Axis, vis: &impl Fn(W) -> bool) -> f64 {
e2443dec4dcdatagen2026-10-18 09:58 UTC365
        match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC366
            Kind::Leaf(_, size) => size[axis.index()],
e2443dec4dcdatagen2026-10-18 09:58 UTC367
            Kind::Container { children, .. } => {
e2443dec4dcdatagen2026-10-18 09:58 UTC368
                let shown: Vec<f64> = children
e2443dec4dcdatagen2026-10-18 09:58 UTC369
                    .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC370
                    .filter(|&&c| self.visible(c, vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC371
                    .map(|&c| self.extent(c, axis, vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC372
                    .collect();
e2443dec4dcdatagen2026-10-18 09:58 UTC373
                if shown.is_empty() {
e2443dec4dcdatagen2026-10-18 09:58 UTC374
                    children
e2443dec4dcdatagen2026-10-18 09:58 UTC375
                        .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC376
                        .map(|&c| self.extent(c, axis, vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC377
                        .sum::<f64>()
e2443dec4dcdatagen2026-10-18 09:58 UTC378
                        / children.len() as f64
e2443dec4dcdatagen2026-10-18 09:58 UTC379
                } else {
e2443dec4dcdatagen2026-10-18 09:58 UTC380
                    shown.iter().sum::<f64>() / shown.len() as f64
e2443dec4dcdatagen2026-10-18 09:58 UTC381
                }
e2443dec4dcdatagen2026-10-18 09:58 UTC382
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC383
        }
e2443dec4dcdatagen2026-10-18 09:58 UTC384
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC385
e2443dec4dcdatagen2026-10-18 09:58 UTC386
    /// The size a window placed in `n`'s stead would take, ignoring
e2443dec4dcdatagen2026-10-18 09:58 UTC387
    /// visibility since new windows land on any tag.
e2443dec4dcdatagen2026-10-18 09:58 UTC388
    fn size(&self, n: NodeId) -> Size {
e2443dec4dcdatagen2026-10-18 09:58 UTC389
        let all = |_: W| true;
e2443dec4dcdatagen2026-10-18 09:58 UTC390
        [
e2443dec4dcdatagen2026-10-18 09:58 UTC391
            self.extent(n, Axis::Horizontal, &all),
e2443dec4dcdatagen2026-10-18 09:58 UTC392
            self.extent(n, Axis::Vertical, &all),
e2443dec4dcdatagen2026-10-18 09:58 UTC393
        ]
e2443dec4dcdatagen2026-10-18 09:58 UTC394
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC395
9baaafa4b167atagen2026-10-16 10:04 UTC396
    /// Lays the tree out in `area`. Hidden windows come back as zero-size
9baaafa4b167atagen2026-10-16 10:04 UTC397
    /// rects on the boundary where they would reappear.
9baaafa4b167atagen2026-10-16 10:04 UTC398
    pub fn arrange(&self, area: Rect, gap: f64, vis: impl Fn(W) -> bool) -> Vec<(W, Rect)> {
9baaafa4b167atagen2026-10-16 10:04 UTC399
        let mut out = Vec::with_capacity(self.leaves.len());
9baaafa4b167atagen2026-10-16 10:04 UTC400
        if let Some(r) = self.root {
1c8e1b4009f5atagen2026-10-16 23:19 UTC401
            let area = if self.visible(r, &vis) {
1c8e1b4009f5atagen2026-10-16 23:19 UTC402
                area
1c8e1b4009f5atagen2026-10-16 23:19 UTC403
            } else {
1c8e1b4009f5atagen2026-10-16 23:19 UTC404
                Rect::new(area.x + area.w / 2.0, area.y, 0.0, area.h)
1c8e1b4009f5atagen2026-10-16 23:19 UTC405
            };
9baaafa4b167atagen2026-10-16 10:04 UTC406
            self.place(r, area, gap, &vis, &mut out);
9baaafa4b167atagen2026-10-16 10:04 UTC407
        }
9baaafa4b167atagen2026-10-16 10:04 UTC408
        out
9baaafa4b167atagen2026-10-16 10:04 UTC409
    }
9baaafa4b167atagen2026-10-16 10:04 UTC410
9baaafa4b167atagen2026-10-16 10:04 UTC411
    fn place(
9baaafa4b167atagen2026-10-16 10:04 UTC412
        &self,
9baaafa4b167atagen2026-10-16 10:04 UTC413
        n: NodeId,
9baaafa4b167atagen2026-10-16 10:04 UTC414
        r: Rect,
9baaafa4b167atagen2026-10-16 10:04 UTC415
        gap: f64,
9baaafa4b167atagen2026-10-16 10:04 UTC416
        vis: &impl Fn(W) -> bool,
9baaafa4b167atagen2026-10-16 10:04 UTC417
        out: &mut Vec<(W, Rect)>,
9baaafa4b167atagen2026-10-16 10:04 UTC418
    ) {
9baaafa4b167atagen2026-10-16 10:04 UTC419
        let (axis, children, expanded) = match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC420
            Kind::Leaf(w, _) => return out.push((*w, r)),
9baaafa4b167atagen2026-10-16 10:04 UTC421
            Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC422
                axis,
9baaafa4b167atagen2026-10-16 10:04 UTC423
                children,
9baaafa4b167atagen2026-10-16 10:04 UTC424
                expanded,
9baaafa4b167atagen2026-10-16 10:04 UTC425
            } => (*axis, children, *expanded),
9baaafa4b167atagen2026-10-16 10:04 UTC426
        };
e2443dec4dcdatagen2026-10-18 09:58 UTC427
        let shown: Vec<bool> = children.iter().map(|&c| self.visible(c, vis)).collect();
9baaafa4b167atagen2026-10-16 10:04 UTC428
        let (start, len) = span(r, axis);
e2443dec4dcdatagen2026-10-18 09:58 UTC429
        if let Some(e) = expanded.and_then(|e| children.iter().position(|&c| c == e))
9baaafa4b167atagen2026-10-16 10:04 UTC430
            && shown[e]
9baaafa4b167atagen2026-10-16 10:04 UTC431
        {
e2443dec4dcdatagen2026-10-18 09:58 UTC432
            for (i, &c) in children.iter().enumerate() {
9baaafa4b167atagen2026-10-16 10:04 UTC433
                let sub = match i.cmp(&e) {
9baaafa4b167atagen2026-10-16 10:04 UTC434
                    std::cmp::Ordering::Equal => r,
9baaafa4b167atagen2026-10-16 10:04 UTC435
                    std::cmp::Ordering::Less => with_span(r, axis, start, 0.0),
9baaafa4b167atagen2026-10-16 10:04 UTC436
                    std::cmp::Ordering::Greater => with_span(r, axis, start + len, 0.0),
9baaafa4b167atagen2026-10-16 10:04 UTC437
                };
e2443dec4dcdatagen2026-10-18 09:58 UTC438
                self.place(c, sub, gap, vis, out);
9baaafa4b167atagen2026-10-16 10:04 UTC439
            }
9baaafa4b167atagen2026-10-16 10:04 UTC440
            return;
9baaafa4b167atagen2026-10-16 10:04 UTC441
        }
9baaafa4b167atagen2026-10-16 10:04 UTC442
        let count = shown.iter().filter(|s| **s).count();
e2443dec4dcdatagen2026-10-18 09:58 UTC443
        let extents: Vec<f64> = children
9baaafa4b167atagen2026-10-16 10:04 UTC444
            .iter()
9baaafa4b167atagen2026-10-16 10:04 UTC445
            .zip(&shown)
e2443dec4dcdatagen2026-10-18 09:58 UTC446
            .map(|(&c, &s)| if s { self.extent(c, axis, vis) } else { 0.0 })
e2443dec4dcdatagen2026-10-18 09:58 UTC447
            .collect();
e2443dec4dcdatagen2026-10-18 09:58 UTC448
        let total: f64 = extents.iter().sum();
9baaafa4b167atagen2026-10-16 10:04 UTC449
        let gaps = count.saturating_sub(1) as f64;
9baaafa4b167atagen2026-10-16 10:04 UTC450
        let gap = if gaps > 0.0 { gap.min(len / gaps) } else { gap };
9baaafa4b167atagen2026-10-16 10:04 UTC451
        let avail = len - gap * gaps;
9baaafa4b167atagen2026-10-16 10:04 UTC452
        let last = shown.iter().rposition(|s| *s);
9baaafa4b167atagen2026-10-16 10:04 UTC453
        let mut pos = start;
9baaafa4b167atagen2026-10-16 10:04 UTC454
        let mut seen = false;
e2443dec4dcdatagen2026-10-18 09:58 UTC455
        for (i, &c) in children.iter().enumerate() {
9baaafa4b167atagen2026-10-16 10:04 UTC456
            if shown[i] {
9baaafa4b167atagen2026-10-16 10:04 UTC457
                if seen {
9baaafa4b167atagen2026-10-16 10:04 UTC458
                    pos += gap;
9baaafa4b167atagen2026-10-16 10:04 UTC459
                }
e2443dec4dcdatagen2026-10-18 09:58 UTC460
                let l = avail * extents[i] / total;
e2443dec4dcdatagen2026-10-18 09:58 UTC461
                self.place(c, with_span(r, axis, pos, l), gap, vis, out);
9baaafa4b167atagen2026-10-16 10:04 UTC462
                pos += l;
9baaafa4b167atagen2026-10-16 10:04 UTC463
                seen = true;
9baaafa4b167atagen2026-10-16 10:04 UTC464
            } else {
9baaafa4b167atagen2026-10-16 10:04 UTC465
                let between = seen && last.is_some_and(|l| l > i);
9baaafa4b167atagen2026-10-16 10:04 UTC466
                let at = if between { pos + gap / 2.0 } else { pos };
e2443dec4dcdatagen2026-10-18 09:58 UTC467
                self.place(c, with_span(r, axis, at, 0.0), gap, vis, out);
9baaafa4b167atagen2026-10-16 10:04 UTC468
            }
9baaafa4b167atagen2026-10-16 10:04 UTC469
        }
9baaafa4b167atagen2026-10-16 10:04 UTC470
    }
9baaafa4b167atagen2026-10-16 10:04 UTC471
9baaafa4b167atagen2026-10-16 10:04 UTC472
    fn move_node(&mut self, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool, enter: bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC473
        let mut cur = n;
9baaafa4b167atagen2026-10-16 10:04 UTC474
        while let Some((p, i)) = self.position(cur) {
9baaafa4b167atagen2026-10-16 10:04 UTC475
            if self.axis(p) == Some(dir.axis()) {
60f00dd4f2beatagen2026-10-16 11:50 UTC476
                let hidden_lane = self
60f00dd4f2beatagen2026-10-16 11:50 UTC477
                    .adjacent(p, i, dir)
e2443dec4dcdatagen2026-10-18 09:58 UTC478
                    .filter(|&j| enter && !self.visible(self.children(p)[j], vis));
9baaafa4b167atagen2026-10-16 10:04 UTC479
                if cur != n {
60f00dd4f2beatagen2026-10-16 11:50 UTC480
                    if let Some(j) = hidden_lane {
60f00dd4f2beatagen2026-10-16 11:50 UTC481
                        self.join_lane(n, p, j);
60f00dd4f2beatagen2026-10-16 11:50 UTC482
                    } else {
60f00dd4f2beatagen2026-10-16 11:50 UTC483
                        let at = if dir.forward() { i + 1 } else { i };
e2443dec4dcdatagen2026-10-18 09:58 UTC484
                        self.relocate(n, p, at);
60f00dd4f2beatagen2026-10-16 11:50 UTC485
                    }
9baaafa4b167atagen2026-10-16 10:04 UTC486
                    return true;
9baaafa4b167atagen2026-10-16 10:04 UTC487
                }
9baaafa4b167atagen2026-10-16 10:04 UTC488
                if let Some(j) = self.next_shown(p, i, dir, vis) {
e2443dec4dcdatagen2026-10-18 09:58 UTC489
                    let sib = self.children(p)[j];
9baaafa4b167atagen2026-10-16 10:04 UTC490
                    if enter && self.axis(sib).is_some() {
9c0e9500c151atagen2026-10-18 09:09 UTC491
                        self.join_lane(n, p, j);
9baaafa4b167atagen2026-10-16 10:04 UTC492
                    } else {
9baaafa4b167atagen2026-10-16 10:04 UTC493
                        let children = self.children_mut(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC494
                        children.swap(i, j);
9baaafa4b167atagen2026-10-16 10:04 UTC495
                    }
9baaafa4b167atagen2026-10-16 10:04 UTC496
                    return true;
9baaafa4b167atagen2026-10-16 10:04 UTC497
                }
60f00dd4f2beatagen2026-10-16 11:50 UTC498
                if let Some(j) = hidden_lane {
60f00dd4f2beatagen2026-10-16 11:50 UTC499
                    self.join_lane(n, p, j);
60f00dd4f2beatagen2026-10-16 11:50 UTC500
                    return true;
60f00dd4f2beatagen2026-10-16 11:50 UTC501
                }
9baaafa4b167atagen2026-10-16 10:04 UTC502
            }
9baaafa4b167atagen2026-10-16 10:04 UTC503
            cur = p;
9baaafa4b167atagen2026-10-16 10:04 UTC504
        }
9baaafa4b167atagen2026-10-16 10:04 UTC505
        if cur == n || self.axis(cur) == Some(dir.axis()) {
9baaafa4b167atagen2026-10-16 10:04 UTC506
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC507
        }
9baaafa4b167atagen2026-10-16 10:04 UTC508
        let (old, i) = self.position(n).expect("n is below the root");
9baaafa4b167atagen2026-10-16 10:04 UTC509
        self.unlink(old, i);
9baaafa4b167atagen2026-10-16 10:04 UTC510
        self.normalize(old);
9baaafa4b167atagen2026-10-16 10:04 UTC511
        let r = self.root.expect("the rest of the tree survives");
9baaafa4b167atagen2026-10-16 10:04 UTC512
        if self.axis(r) == Some(dir.axis()) {
9baaafa4b167atagen2026-10-16 10:04 UTC513
            let at = if dir.forward() {
9baaafa4b167atagen2026-10-16 10:04 UTC514
                self.children(r).len()
9baaafa4b167atagen2026-10-16 10:04 UTC515
            } else {
9baaafa4b167atagen2026-10-16 10:04 UTC516
                0
9baaafa4b167atagen2026-10-16 10:04 UTC517
            };
e2443dec4dcdatagen2026-10-18 09:58 UTC518
            self.attach(r, at, n);
9baaafa4b167atagen2026-10-16 10:04 UTC519
        } else {
9baaafa4b167atagen2026-10-16 10:04 UTC520
            let c = self.new_container(dir.axis());
e2443dec4dcdatagen2026-10-18 09:58 UTC521
            self.attach(c, 0, r);
9baaafa4b167atagen2026-10-16 10:04 UTC522
            let at = if dir.forward() {
9baaafa4b167atagen2026-10-16 10:04 UTC523
                self.children(c).len()
9baaafa4b167atagen2026-10-16 10:04 UTC524
            } else {
9baaafa4b167atagen2026-10-16 10:04 UTC525
                0
9baaafa4b167atagen2026-10-16 10:04 UTC526
            };
e2443dec4dcdatagen2026-10-18 09:58 UTC527
            self.attach(c, at, n);
9baaafa4b167atagen2026-10-16 10:04 UTC528
            self.root = Some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC529
        }
9baaafa4b167atagen2026-10-16 10:04 UTC530
        true
9baaafa4b167atagen2026-10-16 10:04 UTC531
    }
9baaafa4b167atagen2026-10-16 10:04 UTC532
9baaafa4b167atagen2026-10-16 10:04 UTC533
    fn next_shown(&self, p: NodeId, i: usize, dir: Dir, vis: &impl Fn(W) -> bool) -> Option<usize> {
9baaafa4b167atagen2026-10-16 10:04 UTC534
        let children = self.children(p);
9baaafa4b167atagen2026-10-16 10:04 UTC535
        if dir.forward() {
e2443dec4dcdatagen2026-10-18 09:58 UTC536
            (i + 1..children.len()).find(|&j| self.visible(children[j], vis))
9baaafa4b167atagen2026-10-16 10:04 UTC537
        } else {
e2443dec4dcdatagen2026-10-18 09:58 UTC538
            (0..i).rev().find(|&j| self.visible(children[j], vis))
9baaafa4b167atagen2026-10-16 10:04 UTC539
        }
9baaafa4b167atagen2026-10-16 10:04 UTC540
    }
9baaafa4b167atagen2026-10-16 10:04 UTC541
60f00dd4f2beatagen2026-10-16 11:50 UTC542
    fn adjacent(&self, p: NodeId, i: usize, dir: Dir) -> Option<usize> {
60f00dd4f2beatagen2026-10-16 11:50 UTC543
        let j = if dir.forward() {
60f00dd4f2beatagen2026-10-16 11:50 UTC544
            i + 1
60f00dd4f2beatagen2026-10-16 11:50 UTC545
        } else {
60f00dd4f2beatagen2026-10-16 11:50 UTC546
            i.checked_sub(1)?
60f00dd4f2beatagen2026-10-16 11:50 UTC547
        };
60f00dd4f2beatagen2026-10-16 11:50 UTC548
        (j < self.children(p).len()).then_some(j)
60f00dd4f2beatagen2026-10-16 11:50 UTC549
    }
60f00dd4f2beatagen2026-10-16 11:50 UTC550
4444c779eb48atagen2026-10-16 12:07 UTC551
    /// Moves `n` to the end of the lane at `p`'s child `j`. A lone window
4444c779eb48atagen2026-10-16 12:07 UTC552
    /// there becomes a lane of its own, perpendicular to `p`.
60f00dd4f2beatagen2026-10-16 11:50 UTC553
    fn join_lane(&mut self, n: NodeId, p: NodeId, j: usize) {
e2443dec4dcdatagen2026-10-18 09:58 UTC554
        let lane = self.children(p)[j];
60f00dd4f2beatagen2026-10-16 11:50 UTC555
        let lane = if self.axis(lane).is_some() {
60f00dd4f2beatagen2026-10-16 11:50 UTC556
            lane
60f00dd4f2beatagen2026-10-16 11:50 UTC557
        } else {
60f00dd4f2beatagen2026-10-16 11:50 UTC558
            let c = self.new_container(self.axis(p).expect("p is a container").other());
e2443dec4dcdatagen2026-10-18 09:58 UTC559
            self.children_mut(p)[j] = c;
60f00dd4f2beatagen2026-10-16 11:50 UTC560
            self.nodes[c].parent = Some(p);
60f00dd4f2beatagen2026-10-16 11:50 UTC561
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
60f00dd4f2beatagen2026-10-16 11:50 UTC562
                && *expanded == Some(lane)
60f00dd4f2beatagen2026-10-16 11:50 UTC563
            {
60f00dd4f2beatagen2026-10-16 11:50 UTC564
                *expanded = Some(c);
60f00dd4f2beatagen2026-10-16 11:50 UTC565
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC566
            self.attach(c, 0, lane);
60f00dd4f2beatagen2026-10-16 11:50 UTC567
            c
60f00dd4f2beatagen2026-10-16 11:50 UTC568
        };
60f00dd4f2beatagen2026-10-16 11:50 UTC569
        let end = self.children(lane).len();
e2443dec4dcdatagen2026-10-18 09:58 UTC570
        self.relocate(n, lane, end);
60f00dd4f2beatagen2026-10-16 11:50 UTC571
    }
60f00dd4f2beatagen2026-10-16 11:50 UTC572
9baaafa4b167atagen2026-10-16 10:04 UTC573
    fn relocate(&mut self, n: NodeId, p: NodeId, at: usize) {
9baaafa4b167atagen2026-10-16 10:04 UTC574
        let (old, i) = self.position(n).expect("relocated nodes have a parent");
9baaafa4b167atagen2026-10-16 10:04 UTC575
        let at = if old == p && i < at { at - 1 } else { at };
9baaafa4b167atagen2026-10-16 10:04 UTC576
        self.unlink(old, i);
e2443dec4dcdatagen2026-10-18 09:58 UTC577
        self.attach(p, at, n);
9baaafa4b167atagen2026-10-16 10:04 UTC578
        self.normalize(old);
9baaafa4b167atagen2026-10-16 10:04 UTC579
    }
9baaafa4b167atagen2026-10-16 10:04 UTC580
9baaafa4b167atagen2026-10-16 10:04 UTC581
    /// Places `node` among `p`'s children. A container running along `p`'s
9baaafa4b167atagen2026-10-16 10:04 UTC582
    /// axis is dissolved into `p` so axes keep alternating.
e2443dec4dcdatagen2026-10-18 09:58 UTC583
    fn attach(&mut self, p: NodeId, at: usize, node: NodeId) {
e2443dec4dcdatagen2026-10-18 09:58 UTC584
        if self.axis(node).is_some() && self.axis(node) == self.axis(p) {
e2443dec4dcdatagen2026-10-18 09:58 UTC585
            let children = self.children(node).to_vec();
e2443dec4dcdatagen2026-10-18 09:58 UTC586
            self.nodes.remove(node);
e2443dec4dcdatagen2026-10-18 09:58 UTC587
            for (k, &c) in children.iter().enumerate() {
e2443dec4dcdatagen2026-10-18 09:58 UTC588
                self.nodes[c].parent = Some(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC589
                self.children_mut(p).insert(at + k, c);
9baaafa4b167atagen2026-10-16 10:04 UTC590
            }
9baaafa4b167atagen2026-10-16 10:04 UTC591
        } else {
9baaafa4b167atagen2026-10-16 10:04 UTC592
            self.nodes[node].parent = Some(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC593
            self.children_mut(p).insert(at, node);
9baaafa4b167atagen2026-10-16 10:04 UTC594
        }
9baaafa4b167atagen2026-10-16 10:04 UTC595
    }
9baaafa4b167atagen2026-10-16 10:04 UTC596
e2443dec4dcdatagen2026-10-18 09:58 UTC597
    fn unlink(&mut self, p: NodeId, i: usize) {
e2443dec4dcdatagen2026-10-18 09:58 UTC598
        let child = self.unlink_dead(p, i);
e2443dec4dcdatagen2026-10-18 09:58 UTC599
        self.nodes[child].parent = None;
9baaafa4b167atagen2026-10-16 10:04 UTC600
    }
9baaafa4b167atagen2026-10-16 10:04 UTC601
9baaafa4b167atagen2026-10-16 10:04 UTC602
    fn detach(&mut self, n: NodeId) {
9baaafa4b167atagen2026-10-16 10:04 UTC603
        match self.position(n) {
9baaafa4b167atagen2026-10-16 10:04 UTC604
            Some((p, i)) => {
9baaafa4b167atagen2026-10-16 10:04 UTC605
                self.unlink(p, i);
9baaafa4b167atagen2026-10-16 10:04 UTC606
                self.normalize(p);
9baaafa4b167atagen2026-10-16 10:04 UTC607
            }
9baaafa4b167atagen2026-10-16 10:04 UTC608
            None => self.root = None,
9baaafa4b167atagen2026-10-16 10:04 UTC609
        }
9baaafa4b167atagen2026-10-16 10:04 UTC610
    }
9baaafa4b167atagen2026-10-16 10:04 UTC611
9baaafa4b167atagen2026-10-16 10:04 UTC612
    /// Repairs a container that lost a child.
9baaafa4b167atagen2026-10-16 10:04 UTC613
    fn normalize(&mut self, c: NodeId) {
9baaafa4b167atagen2026-10-16 10:04 UTC614
        if self.children(c).len() >= 2 {
9baaafa4b167atagen2026-10-16 10:04 UTC615
            return;
9baaafa4b167atagen2026-10-16 10:04 UTC616
        }
9baaafa4b167atagen2026-10-16 10:04 UTC617
        let pos = self.position(c);
9baaafa4b167atagen2026-10-16 10:04 UTC618
        let was_expanded = pos.is_some_and(|(p, _)| self.expanded(p) == Some(c));
9baaafa4b167atagen2026-10-16 10:04 UTC619
        let Some(Node {
9baaafa4b167atagen2026-10-16 10:04 UTC620
            kind: Kind::Container { children, .. },
9baaafa4b167atagen2026-10-16 10:04 UTC621
            ..
9baaafa4b167atagen2026-10-16 10:04 UTC622
        }) = self.nodes.remove(c)
9baaafa4b167atagen2026-10-16 10:04 UTC623
        else {
9baaafa4b167atagen2026-10-16 10:04 UTC624
            unreachable!("only containers are normalized");
9baaafa4b167atagen2026-10-16 10:04 UTC625
        };
9baaafa4b167atagen2026-10-16 10:04 UTC626
        let only = children.into_iter().next();
9baaafa4b167atagen2026-10-16 10:04 UTC627
        match (only, pos) {
9baaafa4b167atagen2026-10-16 10:04 UTC628
            (None, Some((p, i))) => {
9baaafa4b167atagen2026-10-16 10:04 UTC629
                self.unlink_dead(p, i);
9baaafa4b167atagen2026-10-16 10:04 UTC630
                self.normalize(p);
9baaafa4b167atagen2026-10-16 10:04 UTC631
            }
9baaafa4b167atagen2026-10-16 10:04 UTC632
            (None, None) => self.root = None,
9baaafa4b167atagen2026-10-16 10:04 UTC633
            (Some(only), Some((p, i))) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC634
                self.unlink_dead(p, i);
e2443dec4dcdatagen2026-10-18 09:58 UTC635
                let leaf_like = self.axis(only) != self.axis(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC636
                self.attach(p, i, only);
9baaafa4b167atagen2026-10-16 10:04 UTC637
                if was_expanded
9baaafa4b167atagen2026-10-16 10:04 UTC638
                    && leaf_like
9baaafa4b167atagen2026-10-16 10:04 UTC639
                    && let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
9baaafa4b167atagen2026-10-16 10:04 UTC640
                {
e2443dec4dcdatagen2026-10-18 09:58 UTC641
                    *expanded = Some(only);
9baaafa4b167atagen2026-10-16 10:04 UTC642
                }
9baaafa4b167atagen2026-10-16 10:04 UTC643
            }
9baaafa4b167atagen2026-10-16 10:04 UTC644
            (Some(only), None) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC645
                self.nodes[only].parent = None;
e2443dec4dcdatagen2026-10-18 09:58 UTC646
                self.root = Some(only);
9baaafa4b167atagen2026-10-16 10:04 UTC647
            }
9baaafa4b167atagen2026-10-16 10:04 UTC648
        }
9baaafa4b167atagen2026-10-16 10:04 UTC649
    }
9baaafa4b167atagen2026-10-16 10:04 UTC650
9baaafa4b167atagen2026-10-16 10:04 UTC651
    /// Like `unlink`, for a child whose node is already gone.
e2443dec4dcdatagen2026-10-18 09:58 UTC652
    fn unlink_dead(&mut self, p: NodeId, i: usize) -> NodeId {
9baaafa4b167atagen2026-10-16 10:04 UTC653
        let Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC654
            children, expanded, ..
9baaafa4b167atagen2026-10-16 10:04 UTC655
        } = &mut self.nodes[p].kind
9baaafa4b167atagen2026-10-16 10:04 UTC656
        else {
9baaafa4b167atagen2026-10-16 10:04 UTC657
            unreachable!("parents are containers");
9baaafa4b167atagen2026-10-16 10:04 UTC658
        };
9baaafa4b167atagen2026-10-16 10:04 UTC659
        let child = children.remove(i);
e2443dec4dcdatagen2026-10-18 09:58 UTC660
        if *expanded == Some(child) {
9baaafa4b167atagen2026-10-16 10:04 UTC661
            *expanded = None;
9baaafa4b167atagen2026-10-16 10:04 UTC662
        }
9baaafa4b167atagen2026-10-16 10:04 UTC663
        child
9baaafa4b167atagen2026-10-16 10:04 UTC664
    }
9baaafa4b167atagen2026-10-16 10:04 UTC665
e2443dec4dcdatagen2026-10-18 09:58 UTC666
    fn new_leaf(&mut self, w: W, size: Size) -> NodeId {
9baaafa4b167atagen2026-10-16 10:04 UTC667
        let n = self.nodes.insert(Node {
9baaafa4b167atagen2026-10-16 10:04 UTC668
            parent: None,
e2443dec4dcdatagen2026-10-18 09:58 UTC669
            kind: Kind::Leaf(w, size),
9baaafa4b167atagen2026-10-16 10:04 UTC670
        });
9baaafa4b167atagen2026-10-16 10:04 UTC671
        self.leaves.insert(w, n);
9baaafa4b167atagen2026-10-16 10:04 UTC672
        n
9baaafa4b167atagen2026-10-16 10:04 UTC673
    }
9baaafa4b167atagen2026-10-16 10:04 UTC674
9baaafa4b167atagen2026-10-16 10:04 UTC675
    fn new_container(&mut self, axis: Axis) -> NodeId {
9baaafa4b167atagen2026-10-16 10:04 UTC676
        self.nodes.insert(Node {
9baaafa4b167atagen2026-10-16 10:04 UTC677
            parent: None,
9baaafa4b167atagen2026-10-16 10:04 UTC678
            kind: Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC679
                axis,
9baaafa4b167atagen2026-10-16 10:04 UTC680
                children: Vec::new(),
9baaafa4b167atagen2026-10-16 10:04 UTC681
                expanded: None,
9baaafa4b167atagen2026-10-16 10:04 UTC682
            },
9baaafa4b167atagen2026-10-16 10:04 UTC683
        })
9baaafa4b167atagen2026-10-16 10:04 UTC684
    }
9baaafa4b167atagen2026-10-16 10:04 UTC685
9baaafa4b167atagen2026-10-16 10:04 UTC686
    fn position(&self, n: NodeId) -> Option<(NodeId, usize)> {
9baaafa4b167atagen2026-10-16 10:04 UTC687
        let p = self.nodes[n].parent?;
e2443dec4dcdatagen2026-10-18 09:58 UTC688
        let i = self.children(p).iter().position(|&c| c == n)?;
9baaafa4b167atagen2026-10-16 10:04 UTC689
        Some((p, i))
9baaafa4b167atagen2026-10-16 10:04 UTC690
    }
9baaafa4b167atagen2026-10-16 10:04 UTC691
9baaafa4b167atagen2026-10-16 10:04 UTC692
    fn axis(&self, n: NodeId) -> Option<Axis> {
9baaafa4b167atagen2026-10-16 10:04 UTC693
        match &self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC694
            Kind::Container { axis, .. } => Some(*axis),
e2443dec4dcdatagen2026-10-18 09:58 UTC695
            Kind::Leaf(..) => None,
9baaafa4b167atagen2026-10-16 10:04 UTC696
        }
9baaafa4b167atagen2026-10-16 10:04 UTC697
    }
9baaafa4b167atagen2026-10-16 10:04 UTC698
9baaafa4b167atagen2026-10-16 10:04 UTC699
    fn expanded(&self, n: NodeId) -> Option<NodeId> {
9baaafa4b167atagen2026-10-16 10:04 UTC700
        match &self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC701
            Kind::Container { expanded, .. } => *expanded,
e2443dec4dcdatagen2026-10-18 09:58 UTC702
            Kind::Leaf(..) => None,
9baaafa4b167atagen2026-10-16 10:04 UTC703
        }
9baaafa4b167atagen2026-10-16 10:04 UTC704
    }
9baaafa4b167atagen2026-10-16 10:04 UTC705
e2443dec4dcdatagen2026-10-18 09:58 UTC706
    fn children(&self, n: NodeId) -> &[NodeId] {
9baaafa4b167atagen2026-10-16 10:04 UTC707
        match &self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC708
            Kind::Container { children, .. } => children,
e2443dec4dcdatagen2026-10-18 09:58 UTC709
            Kind::Leaf(..) => &[],
9baaafa4b167atagen2026-10-16 10:04 UTC710
        }
9baaafa4b167atagen2026-10-16 10:04 UTC711
    }
9baaafa4b167atagen2026-10-16 10:04 UTC712
e2443dec4dcdatagen2026-10-18 09:58 UTC713
    fn children_mut(&mut self, n: NodeId) -> &mut Vec<NodeId> {
9baaafa4b167atagen2026-10-16 10:04 UTC714
        match &mut self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC715
            Kind::Container { children, .. } => children,
e2443dec4dcdatagen2026-10-18 09:58 UTC716
            Kind::Leaf(..) => unreachable!("leaves have no children"),
9baaafa4b167atagen2026-10-16 10:04 UTC717
        }
9baaafa4b167atagen2026-10-16 10:04 UTC718
    }
9baaafa4b167atagen2026-10-16 10:04 UTC719
9baaafa4b167atagen2026-10-16 10:04 UTC720
    fn visible(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC721
        match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC722
            Kind::Leaf(w, _) => vis(*w),
e2443dec4dcdatagen2026-10-18 09:58 UTC723
            Kind::Container { children, .. } => children.iter().any(|&c| self.visible(c, vis)),
9baaafa4b167atagen2026-10-16 10:04 UTC724
        }
9baaafa4b167atagen2026-10-16 10:04 UTC725
    }
9baaafa4b167atagen2026-10-16 10:04 UTC726
9baaafa4b167atagen2026-10-16 10:04 UTC727
    #[cfg(test)]
9baaafa4b167atagen2026-10-16 10:04 UTC728
    pub(crate) fn check(&self) {
9baaafa4b167atagen2026-10-16 10:04 UTC729
        let reachable = match self.root {
9baaafa4b167atagen2026-10-16 10:04 UTC730
            Some(r) => {
9baaafa4b167atagen2026-10-16 10:04 UTC731
                assert!(self.nodes[r].parent.is_none());
9baaafa4b167atagen2026-10-16 10:04 UTC732
                self.check_node(r)
9baaafa4b167atagen2026-10-16 10:04 UTC733
            }
9baaafa4b167atagen2026-10-16 10:04 UTC734
            None => 0,
9baaafa4b167atagen2026-10-16 10:04 UTC735
        };
9baaafa4b167atagen2026-10-16 10:04 UTC736
        assert_eq!(reachable, self.nodes.len(), "orphaned nodes");
9baaafa4b167atagen2026-10-16 10:04 UTC737
        for (&w, &n) in &self.leaves {
e2443dec4dcdatagen2026-10-18 09:58 UTC738
            assert!(matches!(self.nodes[n].kind, Kind::Leaf(x, _) if x == w));
9baaafa4b167atagen2026-10-16 10:04 UTC739
        }
9baaafa4b167atagen2026-10-16 10:04 UTC740
    }
9baaafa4b167atagen2026-10-16 10:04 UTC741
9baaafa4b167atagen2026-10-16 10:04 UTC742
    #[cfg(test)]
9baaafa4b167atagen2026-10-16 10:04 UTC743
    fn check_node(&self, n: NodeId) -> usize {
9baaafa4b167atagen2026-10-16 10:04 UTC744
        match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC745
            Kind::Leaf(_, size) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC746
                assert!(
e2443dec4dcdatagen2026-10-18 09:58 UTC747
                    size.iter().all(|s| *s > 0.0 && s.is_finite()),
e2443dec4dcdatagen2026-10-18 09:58 UTC748
                    "size {size:?}"
e2443dec4dcdatagen2026-10-18 09:58 UTC749
                );
e2443dec4dcdatagen2026-10-18 09:58 UTC750
                1
e2443dec4dcdatagen2026-10-18 09:58 UTC751
            }
9baaafa4b167atagen2026-10-16 10:04 UTC752
            Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC753
                axis,
9baaafa4b167atagen2026-10-16 10:04 UTC754
                children,
9baaafa4b167atagen2026-10-16 10:04 UTC755
                expanded,
9baaafa4b167atagen2026-10-16 10:04 UTC756
            } => {
9baaafa4b167atagen2026-10-16 10:04 UTC757
                assert!(
9baaafa4b167atagen2026-10-16 10:04 UTC758
                    children.len() >= 2,
9baaafa4b167atagen2026-10-16 10:04 UTC759
                    "container with {} children",
9baaafa4b167atagen2026-10-16 10:04 UTC760
                    children.len()
9baaafa4b167atagen2026-10-16 10:04 UTC761
                );
9baaafa4b167atagen2026-10-16 10:04 UTC762
                if let Some(e) = expanded {
e2443dec4dcdatagen2026-10-18 09:58 UTC763
                    assert!(children.contains(e));
9baaafa4b167atagen2026-10-16 10:04 UTC764
                }
9baaafa4b167atagen2026-10-16 10:04 UTC765
                let mut count = 1;
e2443dec4dcdatagen2026-10-18 09:58 UTC766
                for &c in children {
e2443dec4dcdatagen2026-10-18 09:58 UTC767
                    assert_eq!(self.nodes[c].parent, Some(n));
e2443dec4dcdatagen2026-10-18 09:58 UTC768
                    assert_ne!(self.axis(c), Some(*axis), "axes must alternate");
e2443dec4dcdatagen2026-10-18 09:58 UTC769
                    count += self.check_node(c);
9baaafa4b167atagen2026-10-16 10:04 UTC770
                }
9baaafa4b167atagen2026-10-16 10:04 UTC771
                count
9baaafa4b167atagen2026-10-16 10:04 UTC772
            }
9baaafa4b167atagen2026-10-16 10:04 UTC773
        }
9baaafa4b167atagen2026-10-16 10:04 UTC774
    }
9baaafa4b167atagen2026-10-16 10:04 UTC775
}
9baaafa4b167atagen2026-10-16 10:04 UTC776
9baaafa4b167atagen2026-10-16 10:04 UTC777
fn span(r: Rect, axis: Axis) -> (f64, f64) {
9baaafa4b167atagen2026-10-16 10:04 UTC778
    match axis {
9baaafa4b167atagen2026-10-16 10:04 UTC779
        Axis::Horizontal => (r.x, r.w),
9baaafa4b167atagen2026-10-16 10:04 UTC780
        Axis::Vertical => (r.y, r.h),
9baaafa4b167atagen2026-10-16 10:04 UTC781
    }
9baaafa4b167atagen2026-10-16 10:04 UTC782
}
9baaafa4b167atagen2026-10-16 10:04 UTC783
9baaafa4b167atagen2026-10-16 10:04 UTC784
fn with_span(r: Rect, axis: Axis, start: f64, len: f64) -> Rect {
9baaafa4b167atagen2026-10-16 10:04 UTC785
    match axis {
9baaafa4b167atagen2026-10-16 10:04 UTC786
        Axis::Horizontal => Rect::new(start, r.y, len, r.h),
9baaafa4b167atagen2026-10-16 10:04 UTC787
        Axis::Vertical => Rect::new(r.x, start, r.w, len),
9baaafa4b167atagen2026-10-16 10:04 UTC788
    }
9baaafa4b167atagen2026-10-16 10:04 UTC789
}