atagen

monolith

public
Star0
Watch0
9d1128e3c22797988dafcb931e07cd2a3ea9ad2f
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.
9d1128e3c227atagen2026-10-24 06:53 UTC121
    pub fn insert_after(&mut self, w: W, anchor: W, vis: impl Fn(W) -> bool) {
9baaafa4b167atagen2026-10-16 10:04 UTC122
        let Some(&a) = self.leaves.get(&anchor) else {
9d1128e3c227atagen2026-10-24 06:53 UTC123
            return self.insert_row(w, vis);
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);
9d1128e3c227atagen2026-10-24 06:53 UTC138
                self.rescale_hidden(n, true, &vis);
e2443dec4dcdatagen2026-10-18 09:58 UTC139
                self.reveal(n, true);
9baaafa4b167atagen2026-10-16 10:04 UTC140
            }
9baaafa4b167atagen2026-10-16 10:04 UTC141
            None => {
e2443dec4dcdatagen2026-10-18 09:58 UTC142
                let n = self.new_leaf(w, self.size(a));
9baaafa4b167atagen2026-10-16 10:04 UTC143
                let c = self.new_container(self.stack.other());
e2443dec4dcdatagen2026-10-18 09:58 UTC144
                self.attach(c, 0, a);
e2443dec4dcdatagen2026-10-18 09:58 UTC145
                self.attach(c, 1, n);
9baaafa4b167atagen2026-10-16 10:04 UTC146
                self.root = Some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC147
            }
9baaafa4b167atagen2026-10-16 10:04 UTC148
        }
9baaafa4b167atagen2026-10-16 10:04 UTC149
    }
9baaafa4b167atagen2026-10-16 10:04 UTC150
86ef802692f5atagen2026-10-19 07:43 UTC151
    /// Inserts `w` after `anchor` by splitting `anchor` along `axis`. They
86ef802692f5atagen2026-10-19 07:43 UTC152
    /// share the space `anchor` had, and its siblings keep theirs.
9d1128e3c227atagen2026-10-24 06:53 UTC153
    pub fn insert_split(&mut self, w: W, anchor: W, axis: Axis, vis: impl Fn(W) -> bool) {
86ef802692f5atagen2026-10-19 07:43 UTC154
        let Some(&a) = self.leaves.get(&anchor) else {
9d1128e3c227atagen2026-10-24 06:53 UTC155
            return self.insert_row(w, vis);
86ef802692f5atagen2026-10-19 07:43 UTC156
        };
86ef802692f5atagen2026-10-19 07:43 UTC157
        let size = self.size(a);
86ef802692f5atagen2026-10-19 07:43 UTC158
        let n = self.new_leaf(w, size);
86ef802692f5atagen2026-10-19 07:43 UTC159
        match self.position(a) {
86ef802692f5atagen2026-10-19 07:43 UTC160
            Some((p, i)) if self.axis(p) == Some(axis) => {
86ef802692f5atagen2026-10-19 07:43 UTC161
                self.halve(a, axis);
86ef802692f5atagen2026-10-19 07:43 UTC162
                self.halve(n, axis);
86ef802692f5atagen2026-10-19 07:43 UTC163
                self.attach(p, i + 1, n);
86ef802692f5atagen2026-10-19 07:43 UTC164
            }
86ef802692f5atagen2026-10-19 07:43 UTC165
            Some((p, i)) => {
86ef802692f5atagen2026-10-19 07:43 UTC166
                self.unlink(p, i);
86ef802692f5atagen2026-10-19 07:43 UTC167
                let c = self.new_container(axis);
86ef802692f5atagen2026-10-19 07:43 UTC168
                self.attach(c, 0, a);
86ef802692f5atagen2026-10-19 07:43 UTC169
                self.attach(c, 1, n);
86ef802692f5atagen2026-10-19 07:43 UTC170
                self.attach(p, i, c);
86ef802692f5atagen2026-10-19 07:43 UTC171
            }
86ef802692f5atagen2026-10-19 07:43 UTC172
            None => {
86ef802692f5atagen2026-10-19 07:43 UTC173
                let c = self.new_container(axis);
86ef802692f5atagen2026-10-19 07:43 UTC174
                self.attach(c, 0, a);
86ef802692f5atagen2026-10-19 07:43 UTC175
                self.attach(c, 1, n);
86ef802692f5atagen2026-10-19 07:43 UTC176
                self.root = Some(c);
86ef802692f5atagen2026-10-19 07:43 UTC177
            }
86ef802692f5atagen2026-10-19 07:43 UTC178
        }
86ef802692f5atagen2026-10-19 07:43 UTC179
        self.reveal(n, true);
86ef802692f5atagen2026-10-19 07:43 UTC180
    }
86ef802692f5atagen2026-10-19 07:43 UTC181
86ef802692f5atagen2026-10-19 07:43 UTC182
    fn halve(&mut self, leaf: NodeId, axis: Axis) {
86ef802692f5atagen2026-10-19 07:43 UTC183
        if let Kind::Leaf(_, size) = &mut self.nodes[leaf].kind {
86ef802692f5atagen2026-10-19 07:43 UTC184
            size[axis.index()] /= 2.0;
86ef802692f5atagen2026-10-19 07:43 UTC185
        }
86ef802692f5atagen2026-10-19 07:43 UTC186
    }
86ef802692f5atagen2026-10-19 07:43 UTC187
9d1128e3c227atagen2026-10-24 06:53 UTC188
    /// Scales the hidden siblings of a visible `n` arriving in or leaving its
9d1128e3c227atagen2026-10-24 06:53 UTC189
    /// container by as much as the visible ones grow or shrink together, so
9d1128e3c227atagen2026-10-24 06:53 UTC190
    /// the share they come back to does not drift as windows come and go.
9d1128e3c227atagen2026-10-24 06:53 UTC191
    fn rescale_hidden(&mut self, n: NodeId, arriving: bool, vis: &impl Fn(W) -> bool) {
9d1128e3c227atagen2026-10-24 06:53 UTC192
        let (Some((p, _)), true) = (self.position(n), self.visible(n, vis)) else {
9d1128e3c227atagen2026-10-24 06:53 UTC193
            return;
9d1128e3c227atagen2026-10-24 06:53 UTC194
        };
9d1128e3c227atagen2026-10-24 06:53 UTC195
        let axis = self.axis(p).expect("parents are containers");
9d1128e3c227atagen2026-10-24 06:53 UTC196
        let (shown, hidden): (Vec<NodeId>, Vec<NodeId>) = self
9d1128e3c227atagen2026-10-24 06:53 UTC197
            .children(p)
9d1128e3c227atagen2026-10-24 06:53 UTC198
            .iter()
9d1128e3c227atagen2026-10-24 06:53 UTC199
            .filter(|&&c| c != n)
9d1128e3c227atagen2026-10-24 06:53 UTC200
            .partition(|&&c| self.visible(c, vis));
9d1128e3c227atagen2026-10-24 06:53 UTC201
        let rest: f64 = shown.iter().map(|&c| self.extent(c, axis, vis)).sum();
9d1128e3c227atagen2026-10-24 06:53 UTC202
        if rest <= 0.0 {
9d1128e3c227atagen2026-10-24 06:53 UTC203
            return;
9d1128e3c227atagen2026-10-24 06:53 UTC204
        }
9d1128e3c227atagen2026-10-24 06:53 UTC205
        let with = rest + self.extent(n, axis, vis);
9d1128e3c227atagen2026-10-24 06:53 UTC206
        let by = if arriving { with / rest } else { rest / with };
9d1128e3c227atagen2026-10-24 06:53 UTC207
        for c in hidden {
9d1128e3c227atagen2026-10-24 06:53 UTC208
            self.scale(c, axis, by);
9d1128e3c227atagen2026-10-24 06:53 UTC209
        }
9d1128e3c227atagen2026-10-24 06:53 UTC210
    }
9d1128e3c227atagen2026-10-24 06:53 UTC211
9baaafa4b167atagen2026-10-16 10:04 UTC212
    /// Appends `w` as a row of its own along the stacking axis.
9d1128e3c227atagen2026-10-24 06:53 UTC213
    pub fn insert_row(&mut self, w: W, vis: impl Fn(W) -> bool) {
e2443dec4dcdatagen2026-10-18 09:58 UTC214
        let size = self.root.map_or([1.0; 2], |r| self.size(r));
e2443dec4dcdatagen2026-10-18 09:58 UTC215
        let n = self.new_leaf(w, size);
9baaafa4b167atagen2026-10-16 10:04 UTC216
        match self.root {
9baaafa4b167atagen2026-10-16 10:04 UTC217
            None => self.root = Some(n),
9baaafa4b167atagen2026-10-16 10:04 UTC218
            Some(r) if self.axis(r) == Some(self.stack) => {
9baaafa4b167atagen2026-10-16 10:04 UTC219
                let end = self.children(r).len();
e2443dec4dcdatagen2026-10-18 09:58 UTC220
                self.attach(r, end, n);
9d1128e3c227atagen2026-10-24 06:53 UTC221
                self.rescale_hidden(n, true, &vis);
9baaafa4b167atagen2026-10-16 10:04 UTC222
            }
9baaafa4b167atagen2026-10-16 10:04 UTC223
            Some(r) => {
9baaafa4b167atagen2026-10-16 10:04 UTC224
                let c = self.new_container(self.stack);
e2443dec4dcdatagen2026-10-18 09:58 UTC225
                self.attach(c, 0, r);
e2443dec4dcdatagen2026-10-18 09:58 UTC226
                self.attach(c, 1, n);
9baaafa4b167atagen2026-10-16 10:04 UTC227
                self.root = Some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC228
            }
9baaafa4b167atagen2026-10-16 10:04 UTC229
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC230
        self.reveal(n, true);
9baaafa4b167atagen2026-10-16 10:04 UTC231
    }
9baaafa4b167atagen2026-10-16 10:04 UTC232
9d1128e3c227atagen2026-10-24 06:53 UTC233
    pub fn remove(&mut self, w: W, vis: impl Fn(W) -> bool) -> bool {
9d1128e3c227atagen2026-10-24 06:53 UTC234
        let Some(&n) = self.leaves.get(&w) else {
9baaafa4b167atagen2026-10-16 10:04 UTC235
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC236
        };
9d1128e3c227atagen2026-10-24 06:53 UTC237
        self.rescale_hidden(n, false, &vis);
9d1128e3c227atagen2026-10-24 06:53 UTC238
        self.leaves.remove(&w);
9baaafa4b167atagen2026-10-16 10:04 UTC239
        self.detach(n);
9baaafa4b167atagen2026-10-16 10:04 UTC240
        self.nodes.remove(n);
9baaafa4b167atagen2026-10-16 10:04 UTC241
        true
9baaafa4b167atagen2026-10-16 10:04 UTC242
    }
9baaafa4b167atagen2026-10-16 10:04 UTC243
9baaafa4b167atagen2026-10-16 10:04 UTC244
    pub fn retain(&mut self, mut keep: impl FnMut(W) -> bool) {
9baaafa4b167atagen2026-10-16 10:04 UTC245
        let gone: Vec<W> = self.windows().filter(|&w| !keep(w)).collect();
9baaafa4b167atagen2026-10-16 10:04 UTC246
        for w in gone {
9d1128e3c227atagen2026-10-24 06:53 UTC247
            self.remove(w, |_| false);
9baaafa4b167atagen2026-10-16 10:04 UTC248
        }
9baaafa4b167atagen2026-10-16 10:04 UTC249
    }
9baaafa4b167atagen2026-10-16 10:04 UTC250
9baaafa4b167atagen2026-10-16 10:04 UTC251
    /// Moves a window one step in `dir`. Leaves swap slots with the next
9baaafa4b167atagen2026-10-16 10:04 UTC252
    /// visible sibling, enter a sibling container, or step out beside their
21e8e8e5aa3datagen2026-10-20 11:44 UTC253
    /// container. Hidden windows never move, and an extended window moves
21e8e8e5aa3datagen2026-10-20 11:44 UTC254
    /// its group as a rigid block.
9baaafa4b167atagen2026-10-16 10:04 UTC255
    pub fn move_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
ae47743b5745atagen2026-10-19 03:49 UTC256
        let Some(&n) = self.leaves.get(&w).filter(|_| vis(w)) else {
9baaafa4b167atagen2026-10-16 10:04 UTC257
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC258
        };
21e8e8e5aa3datagen2026-10-20 11:44 UTC259
        let block = self.group(w, &vis).unwrap_or(n);
8f1f743a2f6batagen2026-10-19 03:43 UTC260
        self.until_seen(&vis, |t| {
21e8e8e5aa3datagen2026-10-20 11:44 UTC261
            let moved = t.move_node(block, dir, &vis, block == n);
8f1f743a2f6batagen2026-10-19 03:43 UTC262
            t.reveal(n, moved)
8f1f743a2f6batagen2026-10-19 03:43 UTC263
        })
9baaafa4b167atagen2026-10-16 10:04 UTC264
    }
9baaafa4b167atagen2026-10-16 10:04 UTC265
21e8e8e5aa3datagen2026-10-20 11:44 UTC266
    /// Moves the group or container holding `w` as a block, merging it into
21e8e8e5aa3datagen2026-10-20 11:44 UTC267
    /// the next lane in `dir` like `merge_window` when `merge` is set. A
21e8e8e5aa3datagen2026-10-20 11:44 UTC268
    /// window sitting directly in the root is its own block.
21e8e8e5aa3datagen2026-10-20 11:44 UTC269
    pub fn move_container(&mut self, w: W, dir: Dir, merge: bool, vis: impl Fn(W) -> bool) -> bool {
ae47743b5745atagen2026-10-19 03:49 UTC270
        let Some(&n) = self.leaves.get(&w).filter(|_| vis(w)) else {
9baaafa4b167atagen2026-10-16 10:04 UTC271
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC272
        };
21e8e8e5aa3datagen2026-10-20 11:44 UTC273
        let group = self.group(w, &vis);
8f1f743a2f6batagen2026-10-19 03:43 UTC274
        self.until_seen(&vis, |t| {
21e8e8e5aa3datagen2026-10-20 11:44 UTC275
            let block = group.unwrap_or_else(|| {
21e8e8e5aa3datagen2026-10-20 11:44 UTC276
                t.nodes[n]
21e8e8e5aa3datagen2026-10-20 11:44 UTC277
                    .parent
21e8e8e5aa3datagen2026-10-20 11:44 UTC278
                    .filter(|&p| Some(p) != t.root)
21e8e8e5aa3datagen2026-10-20 11:44 UTC279
                    .unwrap_or(n)
21e8e8e5aa3datagen2026-10-20 11:44 UTC280
            });
21e8e8e5aa3datagen2026-10-20 11:44 UTC281
            if merge {
21e8e8e5aa3datagen2026-10-20 11:44 UTC282
                t.merge_node(n, block, dir, &vis)
21e8e8e5aa3datagen2026-10-20 11:44 UTC283
            } else {
21e8e8e5aa3datagen2026-10-20 11:44 UTC284
                let moved = t.move_node(block, dir, &vis, false);
21e8e8e5aa3datagen2026-10-20 11:44 UTC285
                t.reveal(n, moved)
21e8e8e5aa3datagen2026-10-20 11:44 UTC286
            }
8f1f743a2f6batagen2026-10-19 03:43 UTC287
        })
9baaafa4b167atagen2026-10-16 10:04 UTC288
    }
9baaafa4b167atagen2026-10-16 10:04 UTC289
605c44c8c5fcatagen2026-10-17 09:57 UTC290
    /// Moves a window to the end of the next visible lane in `dir` among its
605c44c8c5fcatagen2026-10-17 09:57 UTC291
    /// siblings. A lone window there becomes a stack with it. A window with no
605c44c8c5fcatagen2026-10-17 09:57 UTC292
    /// sibling lane that way leaves its stack as `move_window` would, so it
21e8e8e5aa3datagen2026-10-20 11:44 UTC293
    /// takes one press to step out and another to merge. An extended window
21e8e8e5aa3datagen2026-10-20 11:44 UTC294
    /// takes its group along.
4444c779eb48atagen2026-10-16 12:07 UTC295
    pub fn merge_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
ae47743b5745atagen2026-10-19 03:49 UTC296
        let Some(&n) = self.leaves.get(&w).filter(|_| vis(w)) else {
4444c779eb48atagen2026-10-16 12:07 UTC297
            return false;
4444c779eb48atagen2026-10-16 12:07 UTC298
        };
21e8e8e5aa3datagen2026-10-20 11:44 UTC299
        let block = self.group(w, &vis).unwrap_or(n);
21e8e8e5aa3datagen2026-10-20 11:44 UTC300
        self.until_seen(&vis, |t| t.merge_node(n, block, dir, &vis))
21e8e8e5aa3datagen2026-10-20 11:44 UTC301
    }
21e8e8e5aa3datagen2026-10-20 11:44 UTC302
21e8e8e5aa3datagen2026-10-20 11:44 UTC303
    fn merge_node(&mut self, leaf: NodeId, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool) -> bool {
21e8e8e5aa3datagen2026-10-20 11:44 UTC304
        if let Some((p, i)) = self.position(n)
21e8e8e5aa3datagen2026-10-20 11:44 UTC305
            && self.axis(p) == Some(dir.axis())
21e8e8e5aa3datagen2026-10-20 11:44 UTC306
            && let Some(j) = self.next_shown(p, i, dir, vis)
21e8e8e5aa3datagen2026-10-20 11:44 UTC307
        {
21e8e8e5aa3datagen2026-10-20 11:44 UTC308
            self.join_lane(n, p, j);
21e8e8e5aa3datagen2026-10-20 11:44 UTC309
            return self.reveal(leaf, true);
21e8e8e5aa3datagen2026-10-20 11:44 UTC310
        }
21e8e8e5aa3datagen2026-10-20 11:44 UTC311
        let moved = self.move_node(n, dir, vis, true);
21e8e8e5aa3datagen2026-10-20 11:44 UTC312
        self.reveal(leaf, moved)
21e8e8e5aa3datagen2026-10-20 11:44 UTC313
    }
21e8e8e5aa3datagen2026-10-20 11:44 UTC314
21e8e8e5aa3datagen2026-10-20 11:44 UTC315
    /// The outermost container `w` is extended in, the block it moves as.
21e8e8e5aa3datagen2026-10-20 11:44 UTC316
    fn group(&self, w: W, vis: &impl Fn(W) -> bool) -> Option<NodeId> {
21e8e8e5aa3datagen2026-10-20 11:44 UTC317
        self.shown_path(w, vis)
21e8e8e5aa3datagen2026-10-20 11:44 UTC318
            .into_iter()
21e8e8e5aa3datagen2026-10-20 11:44 UTC319
            .take_while(|&(p, c)| self.expanded(p) == Some(c))
21e8e8e5aa3datagen2026-10-20 11:44 UTC320
            .last()
21e8e8e5aa3datagen2026-10-20 11:44 UTC321
            .map(|(p, _)| p)
21e8e8e5aa3datagen2026-10-20 11:44 UTC322
    }
21e8e8e5aa3datagen2026-10-20 11:44 UTC323
21e8e8e5aa3datagen2026-10-20 11:44 UTC324
    /// Whether `w` is extended over windows it shares a container with.
21e8e8e5aa3datagen2026-10-20 11:44 UTC325
    pub fn grouped(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
21e8e8e5aa3datagen2026-10-20 11:44 UTC326
        self.group(w, &vis).is_some()
21e8e8e5aa3datagen2026-10-20 11:44 UTC327
    }
21e8e8e5aa3datagen2026-10-20 11:44 UTC328
1c1f70798e98atagen2026-10-22 05:39 UTC329
    /// The shown windows in the block `w` moves as, `w` alone when it is not
1c1f70798e98atagen2026-10-22 05:39 UTC330
    /// extended.
1c1f70798e98atagen2026-10-22 05:39 UTC331
    pub fn group_windows(&self, w: W, vis: impl Fn(W) -> bool) -> Vec<W> {
1c1f70798e98atagen2026-10-22 05:39 UTC332
        let Some(g) = self.group(w, &vis) else {
1c1f70798e98atagen2026-10-22 05:39 UTC333
            return vec![w];
1c1f70798e98atagen2026-10-22 05:39 UTC334
        };
1c1f70798e98atagen2026-10-22 05:39 UTC335
        let mut out = Vec::new();
1c1f70798e98atagen2026-10-22 05:39 UTC336
        self.shown_leaves(g, &vis, &mut out);
1c1f70798e98atagen2026-10-22 05:39 UTC337
        out
1c1f70798e98atagen2026-10-22 05:39 UTC338
    }
1c1f70798e98atagen2026-10-22 05:39 UTC339
21e8e8e5aa3datagen2026-10-20 11:44 UTC340
    /// Whether a visible window extended over `w` takes all its room.
21e8e8e5aa3datagen2026-10-20 11:44 UTC341
    pub fn eclipsed(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
21e8e8e5aa3datagen2026-10-20 11:44 UTC342
        let Some(&leaf) = self.leaves.get(&w) else {
21e8e8e5aa3datagen2026-10-20 11:44 UTC343
            return false;
21e8e8e5aa3datagen2026-10-20 11:44 UTC344
        };
21e8e8e5aa3datagen2026-10-20 11:44 UTC345
        let mut cur = leaf;
21e8e8e5aa3datagen2026-10-20 11:44 UTC346
        while let Some(p) = self.nodes[cur].parent {
21e8e8e5aa3datagen2026-10-20 11:44 UTC347
            if self
21e8e8e5aa3datagen2026-10-20 11:44 UTC348
                .expanded(p)
21e8e8e5aa3datagen2026-10-20 11:44 UTC349
                .is_some_and(|e| e != cur && self.visible(e, &vis))
8f1f743a2f6batagen2026-10-19 03:43 UTC350
            {
21e8e8e5aa3datagen2026-10-20 11:44 UTC351
                return true;
8f1f743a2f6batagen2026-10-19 03:43 UTC352
            }
21e8e8e5aa3datagen2026-10-20 11:44 UTC353
            cur = p;
21e8e8e5aa3datagen2026-10-20 11:44 UTC354
        }
21e8e8e5aa3datagen2026-10-20 11:44 UTC355
        false
8f1f743a2f6batagen2026-10-19 03:43 UTC356
    }
8f1f743a2f6batagen2026-10-19 03:43 UTC357
8f1f743a2f6batagen2026-10-19 03:43 UTC358
    /// Repeats `step` until the visible layout changes, or undoes it all when
8f1f743a2f6batagen2026-10-19 03:43 UTC359
    /// it never does. Steps around hidden windows otherwise ate keypresses
8f1f743a2f6batagen2026-10-19 03:43 UTC360
    /// and rearranged windows the user could not see.
8f1f743a2f6batagen2026-10-19 03:43 UTC361
    fn until_seen(
8f1f743a2f6batagen2026-10-19 03:43 UTC362
        &mut self,
8f1f743a2f6batagen2026-10-19 03:43 UTC363
        vis: &impl Fn(W) -> bool,
8f1f743a2f6batagen2026-10-19 03:43 UTC364
        mut step: impl FnMut(&mut Self) -> bool,
8f1f743a2f6batagen2026-10-19 03:43 UTC365
    ) -> bool {
8f1f743a2f6batagen2026-10-19 03:43 UTC366
        let seen = |t: &Self| -> Vec<(W, Rect)> {
8f1f743a2f6batagen2026-10-19 03:43 UTC367
            t.arrange(Rect::new(0.0, 0.0, 1000.0, 1000.0), 0.0, vis)
8f1f743a2f6batagen2026-10-19 03:43 UTC368
                .into_iter()
8f1f743a2f6batagen2026-10-19 03:43 UTC369
                .filter(|p| vis(p.0))
8f1f743a2f6batagen2026-10-19 03:43 UTC370
                .collect()
8f1f743a2f6batagen2026-10-19 03:43 UTC371
        };
8f1f743a2f6batagen2026-10-19 03:43 UTC372
        let same = |a: &[(W, Rect)], b: &[(W, Rect)]| {
8f1f743a2f6batagen2026-10-19 03:43 UTC373
            a.len() == b.len()
8f1f743a2f6batagen2026-10-19 03:43 UTC374
                && a.iter().all(|(v, r)| {
8f1f743a2f6batagen2026-10-19 03:43 UTC375
                    b.iter().any(|(w, s)| {
8f1f743a2f6batagen2026-10-19 03:43 UTC376
                        v == w
8f1f743a2f6batagen2026-10-19 03:43 UTC377
                            && [r.x - s.x, r.y - s.y, r.w - s.w, r.h - s.h]
8f1f743a2f6batagen2026-10-19 03:43 UTC378
                                .iter()
8f1f743a2f6batagen2026-10-19 03:43 UTC379
                                .all(|d| d.abs() < 1e-6)
8f1f743a2f6batagen2026-10-19 03:43 UTC380
                    })
8f1f743a2f6batagen2026-10-19 03:43 UTC381
                })
8f1f743a2f6batagen2026-10-19 03:43 UTC382
        };
8f1f743a2f6batagen2026-10-19 03:43 UTC383
        let before = self.clone();
8f1f743a2f6batagen2026-10-19 03:43 UTC384
        let start = seen(self);
8f1f743a2f6batagen2026-10-19 03:43 UTC385
        for _ in 0..=before.nodes.len() {
8f1f743a2f6batagen2026-10-19 03:43 UTC386
            if !step(self) {
8f1f743a2f6batagen2026-10-19 03:43 UTC387
                break;
8f1f743a2f6batagen2026-10-19 03:43 UTC388
            }
8f1f743a2f6batagen2026-10-19 03:43 UTC389
            if !same(&start, &seen(self)) {
8f1f743a2f6batagen2026-10-19 03:43 UTC390
                return true;
8f1f743a2f6batagen2026-10-19 03:43 UTC391
            }
4444c779eb48atagen2026-10-16 12:07 UTC392
        }
8f1f743a2f6batagen2026-10-19 03:43 UTC393
        *self = before;
8f1f743a2f6batagen2026-10-19 03:43 UTC394
        false
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC395
    }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC396
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC397
    /// Undoes any expansion along `leaf`'s ancestry that favours another
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC398
    /// child, once it has `moved` or arrived. An expanded container gives its
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC399
    /// other children no room, so a window moved into one vanished while
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC400
    /// staying focused.
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC401
    fn reveal(&mut self, leaf: NodeId, moved: bool) -> bool {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC402
        let mut cur = leaf;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC403
        while moved && let Some(p) = self.nodes[cur].parent {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC404
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC405
                && expanded.is_some_and(|e| e != cur)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC406
            {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC407
                *expanded = None;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC408
            }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC409
            cur = p;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC410
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC411
        moved
4444c779eb48atagen2026-10-16 12:07 UTC412
    }
4444c779eb48atagen2026-10-16 12:07 UTC413
4444c779eb48atagen2026-10-16 12:07 UTC414
    /// Whether every container above `w` is expanded towards it.
54559daf11c3atagen2026-10-17 13:38 UTC415
    pub fn maximized(&self, w: W, vis: impl Fn(W) -> bool) -> bool {
54559daf11c3atagen2026-10-17 13:38 UTC416
        let path = self.shown_path(w, &vis);
54559daf11c3atagen2026-10-17 13:38 UTC417
        !path.is_empty() && path.iter().all(|&(p, c)| self.expanded(p) == Some(c))
54559daf11c3atagen2026-10-17 13:38 UTC418
    }
54559daf11c3atagen2026-10-17 13:38 UTC419
54559daf11c3atagen2026-10-17 13:38 UTC420
    /// `w`'s ancestry as (container, child) pairs, leaving out containers
54559daf11c3atagen2026-10-17 13:38 UTC421
    /// where the child has no visible sibling. Those look like nothing on
54559daf11c3atagen2026-10-17 13:38 UTC422
    /// screen, so expanding there would do nothing the user could see.
54559daf11c3atagen2026-10-17 13:38 UTC423
    fn shown_path(&self, w: W, vis: &impl Fn(W) -> bool) -> Vec<(NodeId, NodeId)> {
4444c779eb48atagen2026-10-16 12:07 UTC424
        let Some(&leaf) = self.leaves.get(&w) else {
54559daf11c3atagen2026-10-17 13:38 UTC425
            return Vec::new();
4444c779eb48atagen2026-10-16 12:07 UTC426
        };
54559daf11c3atagen2026-10-17 13:38 UTC427
        let mut path = Vec::new();
4444c779eb48atagen2026-10-16 12:07 UTC428
        let mut cur = leaf;
4444c779eb48atagen2026-10-16 12:07 UTC429
        while let Some(p) = self.nodes[cur].parent {
54559daf11c3atagen2026-10-17 13:38 UTC430
            let shared = self
54559daf11c3atagen2026-10-17 13:38 UTC431
                .children(p)
54559daf11c3atagen2026-10-17 13:38 UTC432
                .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC433
                .any(|&c| c != cur && self.visible(c, vis));
54559daf11c3atagen2026-10-17 13:38 UTC434
            if shared {
54559daf11c3atagen2026-10-17 13:38 UTC435
                path.push((p, cur));
4444c779eb48atagen2026-10-16 12:07 UTC436
            }
4444c779eb48atagen2026-10-16 12:07 UTC437
            cur = p;
4444c779eb48atagen2026-10-16 12:07 UTC438
        }
54559daf11c3atagen2026-10-17 13:38 UTC439
        path
4444c779eb48atagen2026-10-16 12:07 UTC440
    }
4444c779eb48atagen2026-10-16 12:07 UTC441
9baaafa4b167atagen2026-10-16 10:04 UTC442
    /// Transposes the whole tree.
9baaafa4b167atagen2026-10-16 10:04 UTC443
    pub fn flip(&mut self) {
9baaafa4b167atagen2026-10-16 10:04 UTC444
        self.stack = self.stack.other();
9baaafa4b167atagen2026-10-16 10:04 UTC445
        for node in self.nodes.values_mut() {
e2443dec4dcdatagen2026-10-18 09:58 UTC446
            match &mut node.kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC447
                Kind::Container { axis, .. } => *axis = axis.other(),
e2443dec4dcdatagen2026-10-18 09:58 UTC448
                Kind::Leaf(_, size) => size.reverse(),
9baaafa4b167atagen2026-10-16 10:04 UTC449
            }
9baaafa4b167atagen2026-10-16 10:04 UTC450
        }
9baaafa4b167atagen2026-10-16 10:04 UTC451
    }
9baaafa4b167atagen2026-10-16 10:04 UTC452
54559daf11c3atagen2026-10-17 13:38 UTC453
    /// Toggles expansion for `count` levels of `w`'s visible ancestry,
54559daf11c3atagen2026-10-17 13:38 UTC454
    /// starting `skip` levels up. Level 0 expands the window inside the
54559daf11c3atagen2026-10-17 13:38 UTC455
    /// nearest container where it has a visible sibling.
54559daf11c3atagen2026-10-17 13:38 UTC456
    pub fn toggle_expand(
54559daf11c3atagen2026-10-17 13:38 UTC457
        &mut self,
54559daf11c3atagen2026-10-17 13:38 UTC458
        w: W,
54559daf11c3atagen2026-10-17 13:38 UTC459
        skip: usize,
54559daf11c3atagen2026-10-17 13:38 UTC460
        count: usize,
54559daf11c3atagen2026-10-17 13:38 UTC461
        vis: impl Fn(W) -> bool,
54559daf11c3atagen2026-10-17 13:38 UTC462
    ) -> bool {
54559daf11c3atagen2026-10-17 13:38 UTC463
        let path: Vec<_> = self
54559daf11c3atagen2026-10-17 13:38 UTC464
            .shown_path(w, &vis)
54559daf11c3atagen2026-10-17 13:38 UTC465
            .into_iter()
54559daf11c3atagen2026-10-17 13:38 UTC466
            .skip(skip)
54559daf11c3atagen2026-10-17 13:38 UTC467
            .take(count)
54559daf11c3atagen2026-10-17 13:38 UTC468
            .collect();
9baaafa4b167atagen2026-10-16 10:04 UTC469
        if path.is_empty() {
9baaafa4b167atagen2026-10-16 10:04 UTC470
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC471
        }
9baaafa4b167atagen2026-10-16 10:04 UTC472
        let all = path.iter().all(|&(p, c)| self.expanded(p) == Some(c));
9baaafa4b167atagen2026-10-16 10:04 UTC473
        for (p, c) in path {
9baaafa4b167atagen2026-10-16 10:04 UTC474
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC475
                *expanded = (!all).then_some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC476
            }
9baaafa4b167atagen2026-10-16 10:04 UTC477
        }
9baaafa4b167atagen2026-10-16 10:04 UTC478
        true
9baaafa4b167atagen2026-10-16 10:04 UTC479
    }
9baaafa4b167atagen2026-10-16 10:04 UTC480
9baaafa4b167atagen2026-10-16 10:04 UTC481
    /// Grows `w` by `delta`, a fraction of the visible length of the nearest
9baaafa4b167atagen2026-10-16 10:04 UTC482
    /// container along `axis` where it has a visible sibling. Hidden siblings
e2443dec4dcdatagen2026-10-18 09:58 UTC483
    /// keep their sizes, and with them their share.
9baaafa4b167atagen2026-10-16 10:04 UTC484
    pub fn resize(&mut self, w: W, axis: Axis, delta: f64, vis: impl Fn(W) -> bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC485
        let Some(&leaf) = self.leaves.get(&w) else {
9baaafa4b167atagen2026-10-16 10:04 UTC486
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC487
        };
9baaafa4b167atagen2026-10-16 10:04 UTC488
        let mut cur = leaf;
e2443dec4dcdatagen2026-10-18 09:58 UTC489
        while let Some(p) = self.nodes[cur].parent {
9baaafa4b167atagen2026-10-16 10:04 UTC490
            if self.axis(p) == Some(axis) {
e2443dec4dcdatagen2026-10-18 09:58 UTC491
                let shown: Vec<(NodeId, f64)> = self
e2443dec4dcdatagen2026-10-18 09:58 UTC492
                    .children(p)
e2443dec4dcdatagen2026-10-18 09:58 UTC493
                    .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC494
                    .filter(|&&c| self.visible(c, &vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC495
                    .map(|&c| (c, self.extent(c, axis, &vis)))
9baaafa4b167atagen2026-10-16 10:04 UTC496
                    .collect();
9baaafa4b167atagen2026-10-16 10:04 UTC497
                if shown.len() >= 2 {
e2443dec4dcdatagen2026-10-18 09:58 UTC498
                    let sum: f64 = shown.iter().map(|s| s.1).sum();
e2443dec4dcdatagen2026-10-18 09:58 UTC499
                    let old = self.extent(cur, axis, &vis);
9baaafa4b167atagen2026-10-16 10:04 UTC500
                    let max = 1.0 - MIN_SHARE * (shown.len() - 1) as f64;
9baaafa4b167atagen2026-10-16 10:04 UTC501
                    let new = (old / sum + delta).clamp(MIN_SHARE, max) * sum;
9baaafa4b167atagen2026-10-16 10:04 UTC502
                    let scale = (sum - new) / (sum - old);
e2443dec4dcdatagen2026-10-18 09:58 UTC503
                    for (c, _) in shown {
e2443dec4dcdatagen2026-10-18 09:58 UTC504
                        self.scale(c, axis, if c == cur { new / old } else { scale });
9baaafa4b167atagen2026-10-16 10:04 UTC505
                    }
9baaafa4b167atagen2026-10-16 10:04 UTC506
                    return true;
9baaafa4b167atagen2026-10-16 10:04 UTC507
                }
9baaafa4b167atagen2026-10-16 10:04 UTC508
            }
9baaafa4b167atagen2026-10-16 10:04 UTC509
            cur = p;
9baaafa4b167atagen2026-10-16 10:04 UTC510
        }
9baaafa4b167atagen2026-10-16 10:04 UTC511
        false
9baaafa4b167atagen2026-10-16 10:04 UTC512
    }
9baaafa4b167atagen2026-10-16 10:04 UTC513
b2274115d1c6atagen2026-10-22 10:05 UTC514
    /// Gives every visible child of the nearest container where `w` has a
b2274115d1c6atagen2026-10-22 10:05 UTC515
    /// visible sibling an equal share along that container's axis.
b2274115d1c6atagen2026-10-22 10:05 UTC516
    pub fn balance(&mut self, w: W, vis: impl Fn(W) -> bool) -> bool {
b2274115d1c6atagen2026-10-22 10:05 UTC517
        let Some(&(p, _)) = self.shown_path(w, &vis).first() else {
b2274115d1c6atagen2026-10-22 10:05 UTC518
            return false;
b2274115d1c6atagen2026-10-22 10:05 UTC519
        };
b2274115d1c6atagen2026-10-22 10:05 UTC520
        let Some(axis) = self.axis(p) else {
b2274115d1c6atagen2026-10-22 10:05 UTC521
            return false;
b2274115d1c6atagen2026-10-22 10:05 UTC522
        };
b2274115d1c6atagen2026-10-22 10:05 UTC523
        let shown: Vec<(NodeId, f64)> = self
b2274115d1c6atagen2026-10-22 10:05 UTC524
            .children(p)
b2274115d1c6atagen2026-10-22 10:05 UTC525
            .iter()
b2274115d1c6atagen2026-10-22 10:05 UTC526
            .filter(|&&c| self.visible(c, &vis))
b2274115d1c6atagen2026-10-22 10:05 UTC527
            .map(|&c| (c, self.extent(c, axis, &vis)))
b2274115d1c6atagen2026-10-22 10:05 UTC528
            .collect();
b2274115d1c6atagen2026-10-22 10:05 UTC529
        let mean = shown.iter().map(|s| s.1).sum::<f64>() / shown.len() as f64;
b2274115d1c6atagen2026-10-22 10:05 UTC530
        for (c, e) in shown {
b2274115d1c6atagen2026-10-22 10:05 UTC531
            if e > 0.0 {
b2274115d1c6atagen2026-10-22 10:05 UTC532
                self.scale(c, axis, mean / e);
b2274115d1c6atagen2026-10-22 10:05 UTC533
            }
b2274115d1c6atagen2026-10-22 10:05 UTC534
        }
b2274115d1c6atagen2026-10-22 10:05 UTC535
        true
b2274115d1c6atagen2026-10-22 10:05 UTC536
    }
b2274115d1c6atagen2026-10-22 10:05 UTC537
abe5e42a95d6atagen2026-10-19 07:52 UTC538
    /// The lane a window's split on side `dir` lives in, as the child holding
abe5e42a95d6atagen2026-10-19 07:52 UTC539
    /// the window, its parent and the index of the visible sibling across
abe5e42a95d6atagen2026-10-19 07:52 UTC540
    /// the split.
abe5e42a95d6atagen2026-10-19 07:52 UTC541
    fn split_across(
abe5e42a95d6atagen2026-10-19 07:52 UTC542
        &self,
abe5e42a95d6atagen2026-10-19 07:52 UTC543
        w: W,
abe5e42a95d6atagen2026-10-19 07:52 UTC544
        dir: Dir,
abe5e42a95d6atagen2026-10-19 07:52 UTC545
        vis: &impl Fn(W) -> bool,
abe5e42a95d6atagen2026-10-19 07:52 UTC546
    ) -> Option<(NodeId, NodeId, usize)> {
abe5e42a95d6atagen2026-10-19 07:52 UTC547
        let mut cur = *self.leaves.get(&w)?;
abe5e42a95d6atagen2026-10-19 07:52 UTC548
        if !self.visible(cur, vis) {
abe5e42a95d6atagen2026-10-19 07:52 UTC549
            return None;
abe5e42a95d6atagen2026-10-19 07:52 UTC550
        }
abe5e42a95d6atagen2026-10-19 07:52 UTC551
        while let Some((p, i)) = self.position(cur) {
abe5e42a95d6atagen2026-10-19 07:52 UTC552
            if self.axis(p) == Some(dir.axis())
abe5e42a95d6atagen2026-10-19 07:52 UTC553
                && let Some(j) = self.next_shown(p, i, dir, vis)
abe5e42a95d6atagen2026-10-19 07:52 UTC554
            {
abe5e42a95d6atagen2026-10-19 07:52 UTC555
                return Some((cur, p, j));
abe5e42a95d6atagen2026-10-19 07:52 UTC556
            }
abe5e42a95d6atagen2026-10-19 07:52 UTC557
            cur = p;
abe5e42a95d6atagen2026-10-19 07:52 UTC558
        }
abe5e42a95d6atagen2026-10-19 07:52 UTC559
        None
abe5e42a95d6atagen2026-10-19 07:52 UTC560
    }
abe5e42a95d6atagen2026-10-19 07:52 UTC561
abe5e42a95d6atagen2026-10-19 07:52 UTC562
    /// Whether the split on side `dir` of `w` has a visible window across it.
abe5e42a95d6atagen2026-10-19 07:52 UTC563
    pub fn has_split(&self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
abe5e42a95d6atagen2026-10-19 07:52 UTC564
        self.split_across(w, dir, &vis).is_some()
abe5e42a95d6atagen2026-10-19 07:52 UTC565
    }
abe5e42a95d6atagen2026-10-19 07:52 UTC566
abe5e42a95d6atagen2026-10-19 07:52 UTC567
    /// Moves the split on side `dir` of `w` by `px` along that side's axis,
abe5e42a95d6atagen2026-10-19 07:52 UTC568
    /// positive toward the right or bottom. Only the two children meeting at
abe5e42a95d6atagen2026-10-19 07:52 UTC569
    /// the split change, so the split follows the pointer.
abe5e42a95d6atagen2026-10-19 07:52 UTC570
    pub fn move_split(
abe5e42a95d6atagen2026-10-19 07:52 UTC571
        &mut self,
abe5e42a95d6atagen2026-10-19 07:52 UTC572
        w: W,
abe5e42a95d6atagen2026-10-19 07:52 UTC573
        dir: Dir,
abe5e42a95d6atagen2026-10-19 07:52 UTC574
        px: f64,
abe5e42a95d6atagen2026-10-19 07:52 UTC575
        area: Rect,
abe5e42a95d6atagen2026-10-19 07:52 UTC576
        gap: f64,
abe5e42a95d6atagen2026-10-19 07:52 UTC577
        vis: impl Fn(W) -> bool,
abe5e42a95d6atagen2026-10-19 07:52 UTC578
    ) -> bool {
abe5e42a95d6atagen2026-10-19 07:52 UTC579
        let Some((cur, p, j)) = self.split_across(w, dir, &vis) else {
abe5e42a95d6atagen2026-10-19 07:52 UTC580
            return false;
abe5e42a95d6atagen2026-10-19 07:52 UTC581
        };
abe5e42a95d6atagen2026-10-19 07:52 UTC582
        let axis = dir.axis();
abe5e42a95d6atagen2026-10-19 07:52 UTC583
        let other = self.children(p)[j];
abe5e42a95d6atagen2026-10-19 07:52 UTC584
        let (first, second) = if dir.forward() {
abe5e42a95d6atagen2026-10-19 07:52 UTC585
            (cur, other)
abe5e42a95d6atagen2026-10-19 07:52 UTC586
        } else {
abe5e42a95d6atagen2026-10-19 07:52 UTC587
            (other, cur)
abe5e42a95d6atagen2026-10-19 07:52 UTC588
        };
abe5e42a95d6atagen2026-10-19 07:52 UTC589
        let mut leaves = Vec::new();
abe5e42a95d6atagen2026-10-19 07:52 UTC590
        self.shown_leaves(first, &vis, &mut leaves);
abe5e42a95d6atagen2026-10-19 07:52 UTC591
        let (start, end) = self
abe5e42a95d6atagen2026-10-19 07:52 UTC592
            .arrange(area, gap, &vis)
abe5e42a95d6atagen2026-10-19 07:52 UTC593
            .iter()
abe5e42a95d6atagen2026-10-19 07:52 UTC594
            .filter(|(l, _)| leaves.contains(l))
abe5e42a95d6atagen2026-10-19 07:52 UTC595
            .map(|(_, r)| span(*r, axis))
abe5e42a95d6atagen2026-10-19 07:52 UTC596
            .fold((f64::MAX, f64::MIN), |(s, e), (rs, rl)| {
abe5e42a95d6atagen2026-10-19 07:52 UTC597
                (s.min(rs), e.max(rs + rl))
abe5e42a95d6atagen2026-10-19 07:52 UTC598
            });
abe5e42a95d6atagen2026-10-19 07:52 UTC599
        let ef = self.extent(first, axis, &vis);
abe5e42a95d6atagen2026-10-19 07:52 UTC600
        let es = self.extent(second, axis, &vis);
abe5e42a95d6atagen2026-10-19 07:52 UTC601
        if end <= start || ef <= 0.0 || es <= 0.0 {
abe5e42a95d6atagen2026-10-19 07:52 UTC602
            return true;
abe5e42a95d6atagen2026-10-19 07:52 UTC603
        }
abe5e42a95d6atagen2026-10-19 07:52 UTC604
        let sum: f64 = self
abe5e42a95d6atagen2026-10-19 07:52 UTC605
            .children(p)
abe5e42a95d6atagen2026-10-19 07:52 UTC606
            .iter()
abe5e42a95d6atagen2026-10-19 07:52 UTC607
            .filter(|&&c| self.visible(c, &vis))
abe5e42a95d6atagen2026-10-19 07:52 UTC608
            .map(|&c| self.extent(c, axis, &vis))
abe5e42a95d6atagen2026-10-19 07:52 UTC609
            .sum();
abe5e42a95d6atagen2026-10-19 07:52 UTC610
        let min = MIN_SHARE * sum;
abe5e42a95d6atagen2026-10-19 07:52 UTC611
        let du = (px * ef / (end - start)).clamp(-(ef - min).max(0.0), (es - min).max(0.0));
abe5e42a95d6atagen2026-10-19 07:52 UTC612
        self.scale(first, axis, (ef + du) / ef);
abe5e42a95d6atagen2026-10-19 07:52 UTC613
        self.scale(second, axis, (es - du) / es);
abe5e42a95d6atagen2026-10-19 07:52 UTC614
        true
abe5e42a95d6atagen2026-10-19 07:52 UTC615
    }
abe5e42a95d6atagen2026-10-19 07:52 UTC616
abe5e42a95d6atagen2026-10-19 07:52 UTC617
    fn shown_leaves(&self, n: NodeId, vis: &impl Fn(W) -> bool, out: &mut Vec<W>) {
abe5e42a95d6atagen2026-10-19 07:52 UTC618
        match &self.nodes[n].kind {
abe5e42a95d6atagen2026-10-19 07:52 UTC619
            Kind::Leaf(w, _) => {
abe5e42a95d6atagen2026-10-19 07:52 UTC620
                if vis(*w) {
abe5e42a95d6atagen2026-10-19 07:52 UTC621
                    out.push(*w);
abe5e42a95d6atagen2026-10-19 07:52 UTC622
                }
abe5e42a95d6atagen2026-10-19 07:52 UTC623
            }
abe5e42a95d6atagen2026-10-19 07:52 UTC624
            Kind::Container { children, .. } => {
abe5e42a95d6atagen2026-10-19 07:52 UTC625
                for &c in children {
abe5e42a95d6atagen2026-10-19 07:52 UTC626
                    self.shown_leaves(c, vis, out);
abe5e42a95d6atagen2026-10-19 07:52 UTC627
                }
abe5e42a95d6atagen2026-10-19 07:52 UTC628
            }
abe5e42a95d6atagen2026-10-19 07:52 UTC629
        }
abe5e42a95d6atagen2026-10-19 07:52 UTC630
    }
abe5e42a95d6atagen2026-10-19 07:52 UTC631
e2443dec4dcdatagen2026-10-18 09:58 UTC632
    /// Scales every window under `n` along `axis`, hidden ones too, so they
e2443dec4dcdatagen2026-10-18 09:58 UTC633
    /// keep their proportions for when they are shown again.
e2443dec4dcdatagen2026-10-18 09:58 UTC634
    fn scale(&mut self, n: NodeId, axis: Axis, by: f64) {
e2443dec4dcdatagen2026-10-18 09:58 UTC635
        match &mut self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC636
            Kind::Leaf(_, size) => size[axis.index()] *= by,
e2443dec4dcdatagen2026-10-18 09:58 UTC637
            Kind::Container { children, .. } => {
e2443dec4dcdatagen2026-10-18 09:58 UTC638
                for c in children.clone() {
e2443dec4dcdatagen2026-10-18 09:58 UTC639
                    self.scale(c, axis, by);
e2443dec4dcdatagen2026-10-18 09:58 UTC640
                }
e2443dec4dcdatagen2026-10-18 09:58 UTC641
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC642
        }
e2443dec4dcdatagen2026-10-18 09:58 UTC643
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC644
e2443dec4dcdatagen2026-10-18 09:58 UTC645
    /// How much room `n` asks for along `axis` among its siblings. A
e2443dec4dcdatagen2026-10-18 09:58 UTC646
    /// container asks for the mean of what its visible children ask for, so
e2443dec4dcdatagen2026-10-18 09:58 UTC647
    /// windows of other tags sharing a lane change nothing on screen, and a
e2443dec4dcdatagen2026-10-18 09:58 UTC648
    /// window carries its own size wherever it moves.
e2443dec4dcdatagen2026-10-18 09:58 UTC649
    fn extent(&self, n: NodeId, axis: Axis, vis: &impl Fn(W) -> bool) -> f64 {
e2443dec4dcdatagen2026-10-18 09:58 UTC650
        match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC651
            Kind::Leaf(_, size) => size[axis.index()],
e2443dec4dcdatagen2026-10-18 09:58 UTC652
            Kind::Container { children, .. } => {
e2443dec4dcdatagen2026-10-18 09:58 UTC653
                let shown: Vec<f64> = children
e2443dec4dcdatagen2026-10-18 09:58 UTC654
                    .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC655
                    .filter(|&&c| self.visible(c, vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC656
                    .map(|&c| self.extent(c, axis, vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC657
                    .collect();
e2443dec4dcdatagen2026-10-18 09:58 UTC658
                if shown.is_empty() {
e2443dec4dcdatagen2026-10-18 09:58 UTC659
                    children
e2443dec4dcdatagen2026-10-18 09:58 UTC660
                        .iter()
e2443dec4dcdatagen2026-10-18 09:58 UTC661
                        .map(|&c| self.extent(c, axis, vis))
e2443dec4dcdatagen2026-10-18 09:58 UTC662
                        .sum::<f64>()
e2443dec4dcdatagen2026-10-18 09:58 UTC663
                        / children.len() as f64
e2443dec4dcdatagen2026-10-18 09:58 UTC664
                } else {
e2443dec4dcdatagen2026-10-18 09:58 UTC665
                    shown.iter().sum::<f64>() / shown.len() as f64
e2443dec4dcdatagen2026-10-18 09:58 UTC666
                }
e2443dec4dcdatagen2026-10-18 09:58 UTC667
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC668
        }
e2443dec4dcdatagen2026-10-18 09:58 UTC669
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC670
e2443dec4dcdatagen2026-10-18 09:58 UTC671
    /// The size a window placed in `n`'s stead would take, ignoring
e2443dec4dcdatagen2026-10-18 09:58 UTC672
    /// visibility since new windows land on any tag.
e2443dec4dcdatagen2026-10-18 09:58 UTC673
    fn size(&self, n: NodeId) -> Size {
e2443dec4dcdatagen2026-10-18 09:58 UTC674
        let all = |_: W| true;
e2443dec4dcdatagen2026-10-18 09:58 UTC675
        [
e2443dec4dcdatagen2026-10-18 09:58 UTC676
            self.extent(n, Axis::Horizontal, &all),
e2443dec4dcdatagen2026-10-18 09:58 UTC677
            self.extent(n, Axis::Vertical, &all),
e2443dec4dcdatagen2026-10-18 09:58 UTC678
        ]
e2443dec4dcdatagen2026-10-18 09:58 UTC679
    }
e2443dec4dcdatagen2026-10-18 09:58 UTC680
9baaafa4b167atagen2026-10-16 10:04 UTC681
    /// Lays the tree out in `area`. Hidden windows come back as zero-size
9baaafa4b167atagen2026-10-16 10:04 UTC682
    /// rects on the boundary where they would reappear.
9baaafa4b167atagen2026-10-16 10:04 UTC683
    pub fn arrange(&self, area: Rect, gap: f64, vis: impl Fn(W) -> bool) -> Vec<(W, Rect)> {
9baaafa4b167atagen2026-10-16 10:04 UTC684
        let mut out = Vec::with_capacity(self.leaves.len());
9baaafa4b167atagen2026-10-16 10:04 UTC685
        if let Some(r) = self.root {
1c8e1b4009f5atagen2026-10-16 23:19 UTC686
            let area = if self.visible(r, &vis) {
1c8e1b4009f5atagen2026-10-16 23:19 UTC687
                area
6d227f43182fatagen2026-10-19 08:40 UTC688
            } else if area.w >= area.h {
6d227f43182fatagen2026-10-19 08:40 UTC689
                Rect::new(area.right(), area.y, 0.0, area.h)
1c8e1b4009f5atagen2026-10-16 23:19 UTC690
            } else {
6d227f43182fatagen2026-10-19 08:40 UTC691
                Rect::new(area.x, area.bottom(), area.w, 0.0)
1c8e1b4009f5atagen2026-10-16 23:19 UTC692
            };
9baaafa4b167atagen2026-10-16 10:04 UTC693
            self.place(r, area, gap, &vis, &mut out);
9baaafa4b167atagen2026-10-16 10:04 UTC694
        }
9baaafa4b167atagen2026-10-16 10:04 UTC695
        out
9baaafa4b167atagen2026-10-16 10:04 UTC696
    }
9baaafa4b167atagen2026-10-16 10:04 UTC697
9baaafa4b167atagen2026-10-16 10:04 UTC698
    fn place(
9baaafa4b167atagen2026-10-16 10:04 UTC699
        &self,
9baaafa4b167atagen2026-10-16 10:04 UTC700
        n: NodeId,
9baaafa4b167atagen2026-10-16 10:04 UTC701
        r: Rect,
9baaafa4b167atagen2026-10-16 10:04 UTC702
        gap: f64,
9baaafa4b167atagen2026-10-16 10:04 UTC703
        vis: &impl Fn(W) -> bool,
9baaafa4b167atagen2026-10-16 10:04 UTC704
        out: &mut Vec<(W, Rect)>,
9baaafa4b167atagen2026-10-16 10:04 UTC705
    ) {
9baaafa4b167atagen2026-10-16 10:04 UTC706
        let (axis, children, expanded) = match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC707
            Kind::Leaf(w, _) => return out.push((*w, r)),
9baaafa4b167atagen2026-10-16 10:04 UTC708
            Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC709
                axis,
9baaafa4b167atagen2026-10-16 10:04 UTC710
                children,
9baaafa4b167atagen2026-10-16 10:04 UTC711
                expanded,
9baaafa4b167atagen2026-10-16 10:04 UTC712
            } => (*axis, children, *expanded),
9baaafa4b167atagen2026-10-16 10:04 UTC713
        };
e2443dec4dcdatagen2026-10-18 09:58 UTC714
        let shown: Vec<bool> = children.iter().map(|&c| self.visible(c, vis)).collect();
9baaafa4b167atagen2026-10-16 10:04 UTC715
        let (start, len) = span(r, axis);
e2443dec4dcdatagen2026-10-18 09:58 UTC716
        if let Some(e) = expanded.and_then(|e| children.iter().position(|&c| c == e))
9baaafa4b167atagen2026-10-16 10:04 UTC717
            && shown[e]
9baaafa4b167atagen2026-10-16 10:04 UTC718
        {
e2443dec4dcdatagen2026-10-18 09:58 UTC719
            for (i, &c) in children.iter().enumerate() {
9baaafa4b167atagen2026-10-16 10:04 UTC720
                let sub = match i.cmp(&e) {
9baaafa4b167atagen2026-10-16 10:04 UTC721
                    std::cmp::Ordering::Equal => r,
9baaafa4b167atagen2026-10-16 10:04 UTC722
                    std::cmp::Ordering::Less => with_span(r, axis, start, 0.0),
9baaafa4b167atagen2026-10-16 10:04 UTC723
                    std::cmp::Ordering::Greater => with_span(r, axis, start + len, 0.0),
9baaafa4b167atagen2026-10-16 10:04 UTC724
                };
e2443dec4dcdatagen2026-10-18 09:58 UTC725
                self.place(c, sub, gap, vis, out);
9baaafa4b167atagen2026-10-16 10:04 UTC726
            }
9baaafa4b167atagen2026-10-16 10:04 UTC727
            return;
9baaafa4b167atagen2026-10-16 10:04 UTC728
        }
9baaafa4b167atagen2026-10-16 10:04 UTC729
        let count = shown.iter().filter(|s| **s).count();
e2443dec4dcdatagen2026-10-18 09:58 UTC730
        let extents: Vec<f64> = children
9baaafa4b167atagen2026-10-16 10:04 UTC731
            .iter()
9baaafa4b167atagen2026-10-16 10:04 UTC732
            .zip(&shown)
e2443dec4dcdatagen2026-10-18 09:58 UTC733
            .map(|(&c, &s)| if s { self.extent(c, axis, vis) } else { 0.0 })
e2443dec4dcdatagen2026-10-18 09:58 UTC734
            .collect();
e2443dec4dcdatagen2026-10-18 09:58 UTC735
        let total: f64 = extents.iter().sum();
9baaafa4b167atagen2026-10-16 10:04 UTC736
        let gaps = count.saturating_sub(1) as f64;
9baaafa4b167atagen2026-10-16 10:04 UTC737
        let gap = if gaps > 0.0 { gap.min(len / gaps) } else { gap };
9baaafa4b167atagen2026-10-16 10:04 UTC738
        let avail = len - gap * gaps;
9baaafa4b167atagen2026-10-16 10:04 UTC739
        let last = shown.iter().rposition(|s| *s);
9baaafa4b167atagen2026-10-16 10:04 UTC740
        let mut pos = start;
9baaafa4b167atagen2026-10-16 10:04 UTC741
        let mut seen = false;
e2443dec4dcdatagen2026-10-18 09:58 UTC742
        for (i, &c) in children.iter().enumerate() {
9baaafa4b167atagen2026-10-16 10:04 UTC743
            if shown[i] {
9baaafa4b167atagen2026-10-16 10:04 UTC744
                if seen {
9baaafa4b167atagen2026-10-16 10:04 UTC745
                    pos += gap;
9baaafa4b167atagen2026-10-16 10:04 UTC746
                }
e2443dec4dcdatagen2026-10-18 09:58 UTC747
                let l = avail * extents[i] / total;
e2443dec4dcdatagen2026-10-18 09:58 UTC748
                self.place(c, with_span(r, axis, pos, l), gap, vis, out);
9baaafa4b167atagen2026-10-16 10:04 UTC749
                pos += l;
9baaafa4b167atagen2026-10-16 10:04 UTC750
                seen = true;
9baaafa4b167atagen2026-10-16 10:04 UTC751
            } else {
9baaafa4b167atagen2026-10-16 10:04 UTC752
                let between = seen && last.is_some_and(|l| l > i);
9baaafa4b167atagen2026-10-16 10:04 UTC753
                let at = if between { pos + gap / 2.0 } else { pos };
e2443dec4dcdatagen2026-10-18 09:58 UTC754
                self.place(c, with_span(r, axis, at, 0.0), gap, vis, out);
9baaafa4b167atagen2026-10-16 10:04 UTC755
            }
9baaafa4b167atagen2026-10-16 10:04 UTC756
        }
9baaafa4b167atagen2026-10-16 10:04 UTC757
    }
9baaafa4b167atagen2026-10-16 10:04 UTC758
9baaafa4b167atagen2026-10-16 10:04 UTC759
    fn move_node(&mut self, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool, enter: bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC760
        let mut cur = n;
9baaafa4b167atagen2026-10-16 10:04 UTC761
        while let Some((p, i)) = self.position(cur) {
9baaafa4b167atagen2026-10-16 10:04 UTC762
            if self.axis(p) == Some(dir.axis()) {
60f00dd4f2beatagen2026-10-16 11:50 UTC763
                let hidden_lane = self
60f00dd4f2beatagen2026-10-16 11:50 UTC764
                    .adjacent(p, i, dir)
e2443dec4dcdatagen2026-10-18 09:58 UTC765
                    .filter(|&j| enter && !self.visible(self.children(p)[j], vis));
9baaafa4b167atagen2026-10-16 10:04 UTC766
                if cur != n {
60f00dd4f2beatagen2026-10-16 11:50 UTC767
                    if let Some(j) = hidden_lane {
60f00dd4f2beatagen2026-10-16 11:50 UTC768
                        self.join_lane(n, p, j);
60f00dd4f2beatagen2026-10-16 11:50 UTC769
                    } else {
60f00dd4f2beatagen2026-10-16 11:50 UTC770
                        let at = if dir.forward() { i + 1 } else { i };
e2443dec4dcdatagen2026-10-18 09:58 UTC771
                        self.relocate(n, p, at);
60f00dd4f2beatagen2026-10-16 11:50 UTC772
                    }
9baaafa4b167atagen2026-10-16 10:04 UTC773
                    return true;
9baaafa4b167atagen2026-10-16 10:04 UTC774
                }
9baaafa4b167atagen2026-10-16 10:04 UTC775
                if let Some(j) = self.next_shown(p, i, dir, vis) {
e2443dec4dcdatagen2026-10-18 09:58 UTC776
                    let sib = self.children(p)[j];
00c7b3db8a3fatagen2026-10-18 10:19 UTC777
                    if enter && self.shown_windows(sib, vis) > 1 {
9c0e9500c151atagen2026-10-18 09:09 UTC778
                        self.join_lane(n, p, j);
9baaafa4b167atagen2026-10-16 10:04 UTC779
                    } else {
9baaafa4b167atagen2026-10-16 10:04 UTC780
                        let children = self.children_mut(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC781
                        children.swap(i, j);
9baaafa4b167atagen2026-10-16 10:04 UTC782
                    }
9baaafa4b167atagen2026-10-16 10:04 UTC783
                    return true;
9baaafa4b167atagen2026-10-16 10:04 UTC784
                }
60f00dd4f2beatagen2026-10-16 11:50 UTC785
                if let Some(j) = hidden_lane {
60f00dd4f2beatagen2026-10-16 11:50 UTC786
                    self.join_lane(n, p, j);
60f00dd4f2beatagen2026-10-16 11:50 UTC787
                    return true;
60f00dd4f2beatagen2026-10-16 11:50 UTC788
                }
9baaafa4b167atagen2026-10-16 10:04 UTC789
            }
9baaafa4b167atagen2026-10-16 10:04 UTC790
            cur = p;
9baaafa4b167atagen2026-10-16 10:04 UTC791
        }
9baaafa4b167atagen2026-10-16 10:04 UTC792
        if cur == n || self.axis(cur) == Some(dir.axis()) {
9baaafa4b167atagen2026-10-16 10:04 UTC793
            return false;
9baaafa4b167atagen2026-10-16 10:04 UTC794
        }
9baaafa4b167atagen2026-10-16 10:04 UTC795
        let (old, i) = self.position(n).expect("n is below the root");
9baaafa4b167atagen2026-10-16 10:04 UTC796
        self.unlink(old, i);
9baaafa4b167atagen2026-10-16 10:04 UTC797
        self.normalize(old);
9baaafa4b167atagen2026-10-16 10:04 UTC798
        let r = self.root.expect("the rest of the tree survives");
9baaafa4b167atagen2026-10-16 10:04 UTC799
        if self.axis(r) == Some(dir.axis()) {
9baaafa4b167atagen2026-10-16 10:04 UTC800
            let at = if dir.forward() {
9baaafa4b167atagen2026-10-16 10:04 UTC801
                self.children(r).len()
9baaafa4b167atagen2026-10-16 10:04 UTC802
            } else {
9baaafa4b167atagen2026-10-16 10:04 UTC803
                0
9baaafa4b167atagen2026-10-16 10:04 UTC804
            };
e2443dec4dcdatagen2026-10-18 09:58 UTC805
            self.attach(r, at, n);
9baaafa4b167atagen2026-10-16 10:04 UTC806
        } else {
9baaafa4b167atagen2026-10-16 10:04 UTC807
            let c = self.new_container(dir.axis());
e2443dec4dcdatagen2026-10-18 09:58 UTC808
            self.attach(c, 0, r);
9baaafa4b167atagen2026-10-16 10:04 UTC809
            let at = if dir.forward() {
9baaafa4b167atagen2026-10-16 10:04 UTC810
                self.children(c).len()
9baaafa4b167atagen2026-10-16 10:04 UTC811
            } else {
9baaafa4b167atagen2026-10-16 10:04 UTC812
                0
9baaafa4b167atagen2026-10-16 10:04 UTC813
            };
e2443dec4dcdatagen2026-10-18 09:58 UTC814
            self.attach(c, at, n);
9baaafa4b167atagen2026-10-16 10:04 UTC815
            self.root = Some(c);
9baaafa4b167atagen2026-10-16 10:04 UTC816
        }
9baaafa4b167atagen2026-10-16 10:04 UTC817
        true
9baaafa4b167atagen2026-10-16 10:04 UTC818
    }
9baaafa4b167atagen2026-10-16 10:04 UTC819
9baaafa4b167atagen2026-10-16 10:04 UTC820
    fn next_shown(&self, p: NodeId, i: usize, dir: Dir, vis: &impl Fn(W) -> bool) -> Option<usize> {
9baaafa4b167atagen2026-10-16 10:04 UTC821
        let children = self.children(p);
9baaafa4b167atagen2026-10-16 10:04 UTC822
        if dir.forward() {
e2443dec4dcdatagen2026-10-18 09:58 UTC823
            (i + 1..children.len()).find(|&j| self.visible(children[j], vis))
9baaafa4b167atagen2026-10-16 10:04 UTC824
        } else {
e2443dec4dcdatagen2026-10-18 09:58 UTC825
            (0..i).rev().find(|&j| self.visible(children[j], vis))
9baaafa4b167atagen2026-10-16 10:04 UTC826
        }
9baaafa4b167atagen2026-10-16 10:04 UTC827
    }
9baaafa4b167atagen2026-10-16 10:04 UTC828
60f00dd4f2beatagen2026-10-16 11:50 UTC829
    fn adjacent(&self, p: NodeId, i: usize, dir: Dir) -> Option<usize> {
60f00dd4f2beatagen2026-10-16 11:50 UTC830
        let j = if dir.forward() {
60f00dd4f2beatagen2026-10-16 11:50 UTC831
            i + 1
60f00dd4f2beatagen2026-10-16 11:50 UTC832
        } else {
60f00dd4f2beatagen2026-10-16 11:50 UTC833
            i.checked_sub(1)?
60f00dd4f2beatagen2026-10-16 11:50 UTC834
        };
60f00dd4f2beatagen2026-10-16 11:50 UTC835
        (j < self.children(p).len()).then_some(j)
60f00dd4f2beatagen2026-10-16 11:50 UTC836
    }
60f00dd4f2beatagen2026-10-16 11:50 UTC837
4444c779eb48atagen2026-10-16 12:07 UTC838
    /// Moves `n` to the end of the lane at `p`'s child `j`. A lone window
4444c779eb48atagen2026-10-16 12:07 UTC839
    /// there becomes a lane of its own, perpendicular to `p`.
60f00dd4f2beatagen2026-10-16 11:50 UTC840
    fn join_lane(&mut self, n: NodeId, p: NodeId, j: usize) {
e2443dec4dcdatagen2026-10-18 09:58 UTC841
        let lane = self.children(p)[j];
60f00dd4f2beatagen2026-10-16 11:50 UTC842
        let lane = if self.axis(lane).is_some() {
60f00dd4f2beatagen2026-10-16 11:50 UTC843
            lane
60f00dd4f2beatagen2026-10-16 11:50 UTC844
        } else {
60f00dd4f2beatagen2026-10-16 11:50 UTC845
            let c = self.new_container(self.axis(p).expect("p is a container").other());
e2443dec4dcdatagen2026-10-18 09:58 UTC846
            self.children_mut(p)[j] = c;
60f00dd4f2beatagen2026-10-16 11:50 UTC847
            self.nodes[c].parent = Some(p);
60f00dd4f2beatagen2026-10-16 11:50 UTC848
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
60f00dd4f2beatagen2026-10-16 11:50 UTC849
                && *expanded == Some(lane)
60f00dd4f2beatagen2026-10-16 11:50 UTC850
            {
60f00dd4f2beatagen2026-10-16 11:50 UTC851
                *expanded = Some(c);
60f00dd4f2beatagen2026-10-16 11:50 UTC852
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC853
            self.attach(c, 0, lane);
60f00dd4f2beatagen2026-10-16 11:50 UTC854
            c
60f00dd4f2beatagen2026-10-16 11:50 UTC855
        };
60f00dd4f2beatagen2026-10-16 11:50 UTC856
        let end = self.children(lane).len();
e2443dec4dcdatagen2026-10-18 09:58 UTC857
        self.relocate(n, lane, end);
60f00dd4f2beatagen2026-10-16 11:50 UTC858
    }
60f00dd4f2beatagen2026-10-16 11:50 UTC859
9baaafa4b167atagen2026-10-16 10:04 UTC860
    fn relocate(&mut self, n: NodeId, p: NodeId, at: usize) {
9baaafa4b167atagen2026-10-16 10:04 UTC861
        let (old, i) = self.position(n).expect("relocated nodes have a parent");
9baaafa4b167atagen2026-10-16 10:04 UTC862
        let at = if old == p && i < at { at - 1 } else { at };
9baaafa4b167atagen2026-10-16 10:04 UTC863
        self.unlink(old, i);
e2443dec4dcdatagen2026-10-18 09:58 UTC864
        self.attach(p, at, n);
9baaafa4b167atagen2026-10-16 10:04 UTC865
        self.normalize(old);
9baaafa4b167atagen2026-10-16 10:04 UTC866
    }
9baaafa4b167atagen2026-10-16 10:04 UTC867
9baaafa4b167atagen2026-10-16 10:04 UTC868
    /// Places `node` among `p`'s children. A container running along `p`'s
21e8e8e5aa3datagen2026-10-20 11:44 UTC869
    /// axis is dissolved into `p` so axes keep alternating, unless it holds an
21e8e8e5aa3datagen2026-10-20 11:44 UTC870
    /// extended window, whose group is turned across instead to survive.
e2443dec4dcdatagen2026-10-18 09:58 UTC871
    fn attach(&mut self, p: NodeId, at: usize, node: NodeId) {
e2443dec4dcdatagen2026-10-18 09:58 UTC872
        if self.axis(node).is_some() && self.axis(node) == self.axis(p) {
21e8e8e5aa3datagen2026-10-20 11:44 UTC873
            if self.expanded(node).is_some() {
21e8e8e5aa3datagen2026-10-20 11:44 UTC874
                self.transpose(node);
21e8e8e5aa3datagen2026-10-20 11:44 UTC875
                self.nodes[node].parent = Some(p);
21e8e8e5aa3datagen2026-10-20 11:44 UTC876
                self.children_mut(p).insert(at, node);
21e8e8e5aa3datagen2026-10-20 11:44 UTC877
                return;
21e8e8e5aa3datagen2026-10-20 11:44 UTC878
            }
e2443dec4dcdatagen2026-10-18 09:58 UTC879
            let children = self.children(node).to_vec();
e2443dec4dcdatagen2026-10-18 09:58 UTC880
            self.nodes.remove(node);
e2443dec4dcdatagen2026-10-18 09:58 UTC881
            for (k, &c) in children.iter().enumerate() {
e2443dec4dcdatagen2026-10-18 09:58 UTC882
                self.nodes[c].parent = Some(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC883
                self.children_mut(p).insert(at + k, c);
9baaafa4b167atagen2026-10-16 10:04 UTC884
            }
9baaafa4b167atagen2026-10-16 10:04 UTC885
        } else {
9baaafa4b167atagen2026-10-16 10:04 UTC886
            self.nodes[node].parent = Some(p);
e2443dec4dcdatagen2026-10-18 09:58 UTC887
            self.children_mut(p).insert(at, node);
9baaafa4b167atagen2026-10-16 10:04 UTC888
        }
9baaafa4b167atagen2026-10-16 10:04 UTC889
    }
9baaafa4b167atagen2026-10-16 10:04 UTC890
e2443dec4dcdatagen2026-10-18 09:58 UTC891
    fn unlink(&mut self, p: NodeId, i: usize) {
e2443dec4dcdatagen2026-10-18 09:58 UTC892
        let child = self.unlink_dead(p, i);
e2443dec4dcdatagen2026-10-18 09:58 UTC893
        self.nodes[child].parent = None;
9baaafa4b167atagen2026-10-16 10:04 UTC894
    }
9baaafa4b167atagen2026-10-16 10:04 UTC895
9baaafa4b167atagen2026-10-16 10:04 UTC896
    fn detach(&mut self, n: NodeId) {
9baaafa4b167atagen2026-10-16 10:04 UTC897
        match self.position(n) {
9baaafa4b167atagen2026-10-16 10:04 UTC898
            Some((p, i)) => {
9baaafa4b167atagen2026-10-16 10:04 UTC899
                self.unlink(p, i);
9baaafa4b167atagen2026-10-16 10:04 UTC900
                self.normalize(p);
9baaafa4b167atagen2026-10-16 10:04 UTC901
            }
9baaafa4b167atagen2026-10-16 10:04 UTC902
            None => self.root = None,
9baaafa4b167atagen2026-10-16 10:04 UTC903
        }
9baaafa4b167atagen2026-10-16 10:04 UTC904
    }
9baaafa4b167atagen2026-10-16 10:04 UTC905
21e8e8e5aa3datagen2026-10-20 11:44 UTC906
    fn transpose(&mut self, n: NodeId) {
21e8e8e5aa3datagen2026-10-20 11:44 UTC907
        match &mut self.nodes[n].kind {
21e8e8e5aa3datagen2026-10-20 11:44 UTC908
            Kind::Container { axis, children, .. } => {
21e8e8e5aa3datagen2026-10-20 11:44 UTC909
                *axis = axis.other();
21e8e8e5aa3datagen2026-10-20 11:44 UTC910
                for c in children.clone() {
21e8e8e5aa3datagen2026-10-20 11:44 UTC911
                    self.transpose(c);
21e8e8e5aa3datagen2026-10-20 11:44 UTC912
                }
21e8e8e5aa3datagen2026-10-20 11:44 UTC913
            }
21e8e8e5aa3datagen2026-10-20 11:44 UTC914
            Kind::Leaf(_, size) => size.reverse(),
21e8e8e5aa3datagen2026-10-20 11:44 UTC915
        }
21e8e8e5aa3datagen2026-10-20 11:44 UTC916
    }
21e8e8e5aa3datagen2026-10-20 11:44 UTC917
9baaafa4b167atagen2026-10-16 10:04 UTC918
    /// Repairs a container that lost a child.
9baaafa4b167atagen2026-10-16 10:04 UTC919
    fn normalize(&mut self, c: NodeId) {
9baaafa4b167atagen2026-10-16 10:04 UTC920
        if self.children(c).len() >= 2 {
9baaafa4b167atagen2026-10-16 10:04 UTC921
            return;
9baaafa4b167atagen2026-10-16 10:04 UTC922
        }
9baaafa4b167atagen2026-10-16 10:04 UTC923
        let pos = self.position(c);
9baaafa4b167atagen2026-10-16 10:04 UTC924
        let was_expanded = pos.is_some_and(|(p, _)| self.expanded(p) == Some(c));
9baaafa4b167atagen2026-10-16 10:04 UTC925
        let Some(Node {
9baaafa4b167atagen2026-10-16 10:04 UTC926
            kind: Kind::Container { children, .. },
9baaafa4b167atagen2026-10-16 10:04 UTC927
            ..
9baaafa4b167atagen2026-10-16 10:04 UTC928
        }) = self.nodes.remove(c)
9baaafa4b167atagen2026-10-16 10:04 UTC929
        else {
9baaafa4b167atagen2026-10-16 10:04 UTC930
            unreachable!("only containers are normalized");
9baaafa4b167atagen2026-10-16 10:04 UTC931
        };
9baaafa4b167atagen2026-10-16 10:04 UTC932
        let only = children.into_iter().next();
9baaafa4b167atagen2026-10-16 10:04 UTC933
        match (only, pos) {
9baaafa4b167atagen2026-10-16 10:04 UTC934
            (None, Some((p, i))) => {
9baaafa4b167atagen2026-10-16 10:04 UTC935
                self.unlink_dead(p, i);
9baaafa4b167atagen2026-10-16 10:04 UTC936
                self.normalize(p);
9baaafa4b167atagen2026-10-16 10:04 UTC937
            }
9baaafa4b167atagen2026-10-16 10:04 UTC938
            (None, None) => self.root = None,
9baaafa4b167atagen2026-10-16 10:04 UTC939
            (Some(only), Some((p, i))) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC940
                self.unlink_dead(p, i);
e2443dec4dcdatagen2026-10-18 09:58 UTC941
                self.attach(p, i, only);
9baaafa4b167atagen2026-10-16 10:04 UTC942
                if was_expanded
21e8e8e5aa3datagen2026-10-20 11:44 UTC943
                    && self.nodes.contains_key(only)
9baaafa4b167atagen2026-10-16 10:04 UTC944
                    && let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
9baaafa4b167atagen2026-10-16 10:04 UTC945
                {
e2443dec4dcdatagen2026-10-18 09:58 UTC946
                    *expanded = Some(only);
9baaafa4b167atagen2026-10-16 10:04 UTC947
                }
9baaafa4b167atagen2026-10-16 10:04 UTC948
            }
9baaafa4b167atagen2026-10-16 10:04 UTC949
            (Some(only), None) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC950
                self.nodes[only].parent = None;
e2443dec4dcdatagen2026-10-18 09:58 UTC951
                self.root = Some(only);
9baaafa4b167atagen2026-10-16 10:04 UTC952
            }
9baaafa4b167atagen2026-10-16 10:04 UTC953
        }
9baaafa4b167atagen2026-10-16 10:04 UTC954
    }
9baaafa4b167atagen2026-10-16 10:04 UTC955
9baaafa4b167atagen2026-10-16 10:04 UTC956
    /// Like `unlink`, for a child whose node is already gone.
e2443dec4dcdatagen2026-10-18 09:58 UTC957
    fn unlink_dead(&mut self, p: NodeId, i: usize) -> NodeId {
9baaafa4b167atagen2026-10-16 10:04 UTC958
        let Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC959
            children, expanded, ..
9baaafa4b167atagen2026-10-16 10:04 UTC960
        } = &mut self.nodes[p].kind
9baaafa4b167atagen2026-10-16 10:04 UTC961
        else {
9baaafa4b167atagen2026-10-16 10:04 UTC962
            unreachable!("parents are containers");
9baaafa4b167atagen2026-10-16 10:04 UTC963
        };
9baaafa4b167atagen2026-10-16 10:04 UTC964
        let child = children.remove(i);
e2443dec4dcdatagen2026-10-18 09:58 UTC965
        if *expanded == Some(child) {
9baaafa4b167atagen2026-10-16 10:04 UTC966
            *expanded = None;
9baaafa4b167atagen2026-10-16 10:04 UTC967
        }
9baaafa4b167atagen2026-10-16 10:04 UTC968
        child
9baaafa4b167atagen2026-10-16 10:04 UTC969
    }
9baaafa4b167atagen2026-10-16 10:04 UTC970
e2443dec4dcdatagen2026-10-18 09:58 UTC971
    fn new_leaf(&mut self, w: W, size: Size) -> NodeId {
9baaafa4b167atagen2026-10-16 10:04 UTC972
        let n = self.nodes.insert(Node {
9baaafa4b167atagen2026-10-16 10:04 UTC973
            parent: None,
e2443dec4dcdatagen2026-10-18 09:58 UTC974
            kind: Kind::Leaf(w, size),
9baaafa4b167atagen2026-10-16 10:04 UTC975
        });
9baaafa4b167atagen2026-10-16 10:04 UTC976
        self.leaves.insert(w, n);
9baaafa4b167atagen2026-10-16 10:04 UTC977
        n
9baaafa4b167atagen2026-10-16 10:04 UTC978
    }
9baaafa4b167atagen2026-10-16 10:04 UTC979
9baaafa4b167atagen2026-10-16 10:04 UTC980
    fn new_container(&mut self, axis: Axis) -> NodeId {
9baaafa4b167atagen2026-10-16 10:04 UTC981
        self.nodes.insert(Node {
9baaafa4b167atagen2026-10-16 10:04 UTC982
            parent: None,
9baaafa4b167atagen2026-10-16 10:04 UTC983
            kind: Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC984
                axis,
9baaafa4b167atagen2026-10-16 10:04 UTC985
                children: Vec::new(),
9baaafa4b167atagen2026-10-16 10:04 UTC986
                expanded: None,
9baaafa4b167atagen2026-10-16 10:04 UTC987
            },
9baaafa4b167atagen2026-10-16 10:04 UTC988
        })
9baaafa4b167atagen2026-10-16 10:04 UTC989
    }
9baaafa4b167atagen2026-10-16 10:04 UTC990
9baaafa4b167atagen2026-10-16 10:04 UTC991
    fn position(&self, n: NodeId) -> Option<(NodeId, usize)> {
9baaafa4b167atagen2026-10-16 10:04 UTC992
        let p = self.nodes[n].parent?;
e2443dec4dcdatagen2026-10-18 09:58 UTC993
        let i = self.children(p).iter().position(|&c| c == n)?;
9baaafa4b167atagen2026-10-16 10:04 UTC994
        Some((p, i))
9baaafa4b167atagen2026-10-16 10:04 UTC995
    }
9baaafa4b167atagen2026-10-16 10:04 UTC996
9baaafa4b167atagen2026-10-16 10:04 UTC997
    fn axis(&self, n: NodeId) -> Option<Axis> {
9baaafa4b167atagen2026-10-16 10:04 UTC998
        match &self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC999
            Kind::Container { axis, .. } => Some(*axis),
e2443dec4dcdatagen2026-10-18 09:58 UTC1000
            Kind::Leaf(..) => None,
9baaafa4b167atagen2026-10-16 10:04 UTC1001
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1002
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1003
9baaafa4b167atagen2026-10-16 10:04 UTC1004
    fn expanded(&self, n: NodeId) -> Option<NodeId> {
9baaafa4b167atagen2026-10-16 10:04 UTC1005
        match &self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC1006
            Kind::Container { expanded, .. } => *expanded,
e2443dec4dcdatagen2026-10-18 09:58 UTC1007
            Kind::Leaf(..) => None,
9baaafa4b167atagen2026-10-16 10:04 UTC1008
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1009
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1010
e2443dec4dcdatagen2026-10-18 09:58 UTC1011
    fn children(&self, n: NodeId) -> &[NodeId] {
9baaafa4b167atagen2026-10-16 10:04 UTC1012
        match &self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC1013
            Kind::Container { children, .. } => children,
e2443dec4dcdatagen2026-10-18 09:58 UTC1014
            Kind::Leaf(..) => &[],
9baaafa4b167atagen2026-10-16 10:04 UTC1015
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1016
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1017
e2443dec4dcdatagen2026-10-18 09:58 UTC1018
    fn children_mut(&mut self, n: NodeId) -> &mut Vec<NodeId> {
9baaafa4b167atagen2026-10-16 10:04 UTC1019
        match &mut self.nodes[n].kind {
9baaafa4b167atagen2026-10-16 10:04 UTC1020
            Kind::Container { children, .. } => children,
e2443dec4dcdatagen2026-10-18 09:58 UTC1021
            Kind::Leaf(..) => unreachable!("leaves have no children"),
9baaafa4b167atagen2026-10-16 10:04 UTC1022
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1023
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1024
9baaafa4b167atagen2026-10-16 10:04 UTC1025
    fn visible(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> bool {
9baaafa4b167atagen2026-10-16 10:04 UTC1026
        match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC1027
            Kind::Leaf(w, _) => vis(*w),
e2443dec4dcdatagen2026-10-18 09:58 UTC1028
            Kind::Container { children, .. } => children.iter().any(|&c| self.visible(c, vis)),
9baaafa4b167atagen2026-10-16 10:04 UTC1029
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1030
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1031
00c7b3db8a3fatagen2026-10-18 10:19 UTC1032
    fn shown_windows(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> usize {
00c7b3db8a3fatagen2026-10-18 10:19 UTC1033
        match &self.nodes[n].kind {
00c7b3db8a3fatagen2026-10-18 10:19 UTC1034
            Kind::Leaf(w, _) => usize::from(vis(*w)),
00c7b3db8a3fatagen2026-10-18 10:19 UTC1035
            Kind::Container { children, .. } => {
00c7b3db8a3fatagen2026-10-18 10:19 UTC1036
                children.iter().map(|&c| self.shown_windows(c, vis)).sum()
00c7b3db8a3fatagen2026-10-18 10:19 UTC1037
            }
00c7b3db8a3fatagen2026-10-18 10:19 UTC1038
        }
00c7b3db8a3fatagen2026-10-18 10:19 UTC1039
    }
00c7b3db8a3fatagen2026-10-18 10:19 UTC1040
9baaafa4b167atagen2026-10-16 10:04 UTC1041
    #[cfg(test)]
ae47743b5745atagen2026-10-19 03:49 UTC1042
    pub(crate) fn shape(&self) -> String
ae47743b5745atagen2026-10-19 03:49 UTC1043
    where
ae47743b5745atagen2026-10-19 03:49 UTC1044
        W: std::fmt::Debug,
ae47743b5745atagen2026-10-19 03:49 UTC1045
    {
ae47743b5745atagen2026-10-19 03:49 UTC1046
        fn go<W: Copy + Eq + Hash + std::fmt::Debug>(t: &Tree<W>, n: NodeId) -> String {
ae47743b5745atagen2026-10-19 03:49 UTC1047
            match &t.nodes[n].kind {
ae47743b5745atagen2026-10-19 03:49 UTC1048
                Kind::Leaf(w, [x, y]) => format!("{w:?}[{x:.6},{y:.6}]"),
ae47743b5745atagen2026-10-19 03:49 UTC1049
                Kind::Container {
ae47743b5745atagen2026-10-19 03:49 UTC1050
                    axis,
ae47743b5745atagen2026-10-19 03:49 UTC1051
                    children,
ae47743b5745atagen2026-10-19 03:49 UTC1052
                    expanded,
ae47743b5745atagen2026-10-19 03:49 UTC1053
                } => {
ae47743b5745atagen2026-10-19 03:49 UTC1054
                    let inner: Vec<String> = children.iter().map(|&c| go(t, c)).collect();
ae47743b5745atagen2026-10-19 03:49 UTC1055
                    let e = expanded.and_then(|e| children.iter().position(|&c| c == e));
ae47743b5745atagen2026-10-19 03:49 UTC1056
                    format!("{axis:?}{e:?}({})", inner.join(" "))
ae47743b5745atagen2026-10-19 03:49 UTC1057
                }
ae47743b5745atagen2026-10-19 03:49 UTC1058
            }
ae47743b5745atagen2026-10-19 03:49 UTC1059
        }
ae47743b5745atagen2026-10-19 03:49 UTC1060
        self.root.map(|r| go(self, r)).unwrap_or_default()
ae47743b5745atagen2026-10-19 03:49 UTC1061
    }
ae47743b5745atagen2026-10-19 03:49 UTC1062
ae47743b5745atagen2026-10-19 03:49 UTC1063
    #[cfg(test)]
9baaafa4b167atagen2026-10-16 10:04 UTC1064
    pub(crate) fn check(&self) {
9baaafa4b167atagen2026-10-16 10:04 UTC1065
        let reachable = match self.root {
9baaafa4b167atagen2026-10-16 10:04 UTC1066
            Some(r) => {
9baaafa4b167atagen2026-10-16 10:04 UTC1067
                assert!(self.nodes[r].parent.is_none());
9baaafa4b167atagen2026-10-16 10:04 UTC1068
                self.check_node(r)
9baaafa4b167atagen2026-10-16 10:04 UTC1069
            }
9baaafa4b167atagen2026-10-16 10:04 UTC1070
            None => 0,
9baaafa4b167atagen2026-10-16 10:04 UTC1071
        };
9baaafa4b167atagen2026-10-16 10:04 UTC1072
        assert_eq!(reachable, self.nodes.len(), "orphaned nodes");
9baaafa4b167atagen2026-10-16 10:04 UTC1073
        for (&w, &n) in &self.leaves {
e2443dec4dcdatagen2026-10-18 09:58 UTC1074
            assert!(matches!(self.nodes[n].kind, Kind::Leaf(x, _) if x == w));
9baaafa4b167atagen2026-10-16 10:04 UTC1075
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1076
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1077
9baaafa4b167atagen2026-10-16 10:04 UTC1078
    #[cfg(test)]
9baaafa4b167atagen2026-10-16 10:04 UTC1079
    fn check_node(&self, n: NodeId) -> usize {
9baaafa4b167atagen2026-10-16 10:04 UTC1080
        match &self.nodes[n].kind {
e2443dec4dcdatagen2026-10-18 09:58 UTC1081
            Kind::Leaf(_, size) => {
e2443dec4dcdatagen2026-10-18 09:58 UTC1082
                assert!(
e2443dec4dcdatagen2026-10-18 09:58 UTC1083
                    size.iter().all(|s| *s > 0.0 && s.is_finite()),
e2443dec4dcdatagen2026-10-18 09:58 UTC1084
                    "size {size:?}"
e2443dec4dcdatagen2026-10-18 09:58 UTC1085
                );
e2443dec4dcdatagen2026-10-18 09:58 UTC1086
                1
e2443dec4dcdatagen2026-10-18 09:58 UTC1087
            }
9baaafa4b167atagen2026-10-16 10:04 UTC1088
            Kind::Container {
9baaafa4b167atagen2026-10-16 10:04 UTC1089
                axis,
9baaafa4b167atagen2026-10-16 10:04 UTC1090
                children,
9baaafa4b167atagen2026-10-16 10:04 UTC1091
                expanded,
9baaafa4b167atagen2026-10-16 10:04 UTC1092
            } => {
9baaafa4b167atagen2026-10-16 10:04 UTC1093
                assert!(
9baaafa4b167atagen2026-10-16 10:04 UTC1094
                    children.len() >= 2,
9baaafa4b167atagen2026-10-16 10:04 UTC1095
                    "container with {} children",
9baaafa4b167atagen2026-10-16 10:04 UTC1096
                    children.len()
9baaafa4b167atagen2026-10-16 10:04 UTC1097
                );
9baaafa4b167atagen2026-10-16 10:04 UTC1098
                if let Some(e) = expanded {
e2443dec4dcdatagen2026-10-18 09:58 UTC1099
                    assert!(children.contains(e));
9baaafa4b167atagen2026-10-16 10:04 UTC1100
                }
9baaafa4b167atagen2026-10-16 10:04 UTC1101
                let mut count = 1;
e2443dec4dcdatagen2026-10-18 09:58 UTC1102
                for &c in children {
e2443dec4dcdatagen2026-10-18 09:58 UTC1103
                    assert_eq!(self.nodes[c].parent, Some(n));
e2443dec4dcdatagen2026-10-18 09:58 UTC1104
                    assert_ne!(self.axis(c), Some(*axis), "axes must alternate");
e2443dec4dcdatagen2026-10-18 09:58 UTC1105
                    count += self.check_node(c);
9baaafa4b167atagen2026-10-16 10:04 UTC1106
                }
9baaafa4b167atagen2026-10-16 10:04 UTC1107
                count
9baaafa4b167atagen2026-10-16 10:04 UTC1108
            }
9baaafa4b167atagen2026-10-16 10:04 UTC1109
        }
9baaafa4b167atagen2026-10-16 10:04 UTC1110
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1111
}
9baaafa4b167atagen2026-10-16 10:04 UTC1112
9baaafa4b167atagen2026-10-16 10:04 UTC1113
fn span(r: Rect, axis: Axis) -> (f64, f64) {
9baaafa4b167atagen2026-10-16 10:04 UTC1114
    match axis {
9baaafa4b167atagen2026-10-16 10:04 UTC1115
        Axis::Horizontal => (r.x, r.w),
9baaafa4b167atagen2026-10-16 10:04 UTC1116
        Axis::Vertical => (r.y, r.h),
9baaafa4b167atagen2026-10-16 10:04 UTC1117
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1118
}
9baaafa4b167atagen2026-10-16 10:04 UTC1119
9baaafa4b167atagen2026-10-16 10:04 UTC1120
fn with_span(r: Rect, axis: Axis, start: f64, len: f64) -> Rect {
9baaafa4b167atagen2026-10-16 10:04 UTC1121
    match axis {
9baaafa4b167atagen2026-10-16 10:04 UTC1122
        Axis::Horizontal => Rect::new(start, r.y, len, r.h),
9baaafa4b167atagen2026-10-16 10:04 UTC1123
        Axis::Vertical => Rect::new(r.x, start, r.w, len),
9baaafa4b167atagen2026-10-16 10:04 UTC1124
    }
9baaafa4b167atagen2026-10-16 10:04 UTC1125
}