atagen

monolith

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

Blame: crates/layout/src/tests.rs

9baaafa4b167atagen2026-10-16 10:04 UTC1
use super::*;
9baaafa4b167atagen2026-10-16 10:04 UTC2
use proptest::prelude::*;
9baaafa4b167atagen2026-10-16 10:04 UTC3
9baaafa4b167atagen2026-10-16 10:04 UTC4
const AREA: Rect = Rect::new(0.0, 0.0, 1000.0, 600.0);
9baaafa4b167atagen2026-10-16 10:04 UTC5
9baaafa4b167atagen2026-10-16 10:04 UTC6
#[derive(Clone, Debug)]
9baaafa4b167atagen2026-10-16 10:04 UTC7
enum Op {
9baaafa4b167atagen2026-10-16 10:04 UTC8
    Add(u8, bool),
9baaafa4b167atagen2026-10-16 10:04 UTC9
    Remove(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC10
    Focus(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC11
    Move(Dir),
21e8e8e5aa3datagen2026-10-20 11:44 UTC12
    Block(Dir, bool),
4444c779eb48atagen2026-10-16 12:07 UTC13
    Merge(Dir),
9baaafa4b167atagen2026-10-16 10:04 UTC14
    View(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC15
    ToggleView(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC16
    Retag(u8, u8),
9baaafa4b167atagen2026-10-16 10:04 UTC17
    Float(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC18
    Expand(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC19
    Flip,
9baaafa4b167atagen2026-10-16 10:04 UTC20
    Resize(bool, i8),
9baaafa4b167atagen2026-10-16 10:04 UTC21
    Scratch(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC22
    Summon,
9baaafa4b167atagen2026-10-16 10:04 UTC23
    Unplug,
9baaafa4b167atagen2026-10-16 10:04 UTC24
    Replug,
9baaafa4b167atagen2026-10-16 10:04 UTC25
}
9baaafa4b167atagen2026-10-16 10:04 UTC26
9baaafa4b167atagen2026-10-16 10:04 UTC27
fn dir() -> impl Strategy<Value = Dir> {
9baaafa4b167atagen2026-10-16 10:04 UTC28
    prop_oneof![
9baaafa4b167atagen2026-10-16 10:04 UTC29
        Just(Dir::Left),
9baaafa4b167atagen2026-10-16 10:04 UTC30
        Just(Dir::Right),
9baaafa4b167atagen2026-10-16 10:04 UTC31
        Just(Dir::Up),
9baaafa4b167atagen2026-10-16 10:04 UTC32
        Just(Dir::Down)
9baaafa4b167atagen2026-10-16 10:04 UTC33
    ]
9baaafa4b167atagen2026-10-16 10:04 UTC34
}
9baaafa4b167atagen2026-10-16 10:04 UTC35
9baaafa4b167atagen2026-10-16 10:04 UTC36
fn op() -> impl Strategy<Value = Op> {
9baaafa4b167atagen2026-10-16 10:04 UTC37
    prop_oneof![
9baaafa4b167atagen2026-10-16 10:04 UTC38
        4 => (0u8..4, prop::bool::weighted(0.15)).prop_map(|(t, f)| Op::Add(t, f)),
9baaafa4b167atagen2026-10-16 10:04 UTC39
        1 => any::<u8>().prop_map(Op::Remove),
9baaafa4b167atagen2026-10-16 10:04 UTC40
        3 => any::<u8>().prop_map(Op::Focus),
9baaafa4b167atagen2026-10-16 10:04 UTC41
        4 => dir().prop_map(Op::Move),
21e8e8e5aa3datagen2026-10-20 11:44 UTC42
        2 => (dir(), any::<bool>()).prop_map(|(d, m)| Op::Block(d, m)),
4444c779eb48atagen2026-10-16 12:07 UTC43
        3 => dir().prop_map(Op::Merge),
9baaafa4b167atagen2026-10-16 10:04 UTC44
        1 => (0u8..4).prop_map(Op::View),
9baaafa4b167atagen2026-10-16 10:04 UTC45
        2 => (0u8..4).prop_map(Op::ToggleView),
9baaafa4b167atagen2026-10-16 10:04 UTC46
        1 => (any::<u8>(), 0u8..4).prop_map(|(w, t)| Op::Retag(w, t)),
9baaafa4b167atagen2026-10-16 10:04 UTC47
        1 => any::<u8>().prop_map(Op::Float),
9baaafa4b167atagen2026-10-16 10:04 UTC48
        1 => (0u8..3).prop_map(Op::Expand),
9baaafa4b167atagen2026-10-16 10:04 UTC49
        1 => Just(Op::Flip),
9baaafa4b167atagen2026-10-16 10:04 UTC50
        1 => (any::<bool>(), any::<i8>()).prop_map(|(a, d)| Op::Resize(a, d)),
9baaafa4b167atagen2026-10-16 10:04 UTC51
        1 => any::<u8>().prop_map(Op::Scratch),
9baaafa4b167atagen2026-10-16 10:04 UTC52
        1 => Just(Op::Summon),
9baaafa4b167atagen2026-10-16 10:04 UTC53
        1 => Just(Op::Unplug),
9baaafa4b167atagen2026-10-16 10:04 UTC54
        1 => Just(Op::Replug),
9baaafa4b167atagen2026-10-16 10:04 UTC55
    ]
9baaafa4b167atagen2026-10-16 10:04 UTC56
}
9baaafa4b167atagen2026-10-16 10:04 UTC57
9baaafa4b167atagen2026-10-16 10:04 UTC58
fn nth(l: &Layout, i: u8) -> Option<WindowId> {
9baaafa4b167atagen2026-10-16 10:04 UTC59
    let ids: Vec<WindowId> = l.windows().map(|(id, _)| id).collect();
9baaafa4b167atagen2026-10-16 10:04 UTC60
    (!ids.is_empty()).then(|| ids[i as usize % ids.len()])
9baaafa4b167atagen2026-10-16 10:04 UTC61
}
9baaafa4b167atagen2026-10-16 10:04 UTC62
9baaafa4b167atagen2026-10-16 10:04 UTC63
fn apply(l: &mut Layout, tags: &[TagId], op: &Op) {
9baaafa4b167atagen2026-10-16 10:04 UTC64
    let active = l.active_output();
9baaafa4b167atagen2026-10-16 10:04 UTC65
    match *op {
9baaafa4b167atagen2026-10-16 10:04 UTC66
        Op::Add(t, f) => {
9baaafa4b167atagen2026-10-16 10:04 UTC67
            let w = l.add_window(
9baaafa4b167atagen2026-10-16 10:04 UTC68
                None,
9baaafa4b167atagen2026-10-16 10:04 UTC69
                Some(tags[t as usize]),
9baaafa4b167atagen2026-10-16 10:04 UTC70
                f.then(|| Rect::new(10.0, 10.0, 200.0, 100.0)),
9baaafa4b167atagen2026-10-16 10:04 UTC71
            );
9baaafa4b167atagen2026-10-16 10:04 UTC72
            if l.visible(w) {
9baaafa4b167atagen2026-10-16 10:04 UTC73
                l.focus(w);
9baaafa4b167atagen2026-10-16 10:04 UTC74
            }
9baaafa4b167atagen2026-10-16 10:04 UTC75
        }
9baaafa4b167atagen2026-10-16 10:04 UTC76
        Op::Remove(i) => nth(l, i).into_iter().for_each(|w| l.remove_window(w)),
9baaafa4b167atagen2026-10-16 10:04 UTC77
        Op::Focus(i) => {
9baaafa4b167atagen2026-10-16 10:04 UTC78
            if let Some(w) = nth(l, i).filter(|&w| l.visible(w)) {
9baaafa4b167atagen2026-10-16 10:04 UTC79
                l.focus(w);
9baaafa4b167atagen2026-10-16 10:04 UTC80
            }
9baaafa4b167atagen2026-10-16 10:04 UTC81
        }
9baaafa4b167atagen2026-10-16 10:04 UTC82
        Op::Move(d) => {
9baaafa4b167atagen2026-10-16 10:04 UTC83
            l.move_window(d);
9baaafa4b167atagen2026-10-16 10:04 UTC84
        }
21e8e8e5aa3datagen2026-10-20 11:44 UTC85
        Op::Block(d, m) => {
21e8e8e5aa3datagen2026-10-20 11:44 UTC86
            l.move_container(d, m);
4444c779eb48atagen2026-10-16 12:07 UTC87
        }
4444c779eb48atagen2026-10-16 12:07 UTC88
        Op::Merge(d) => {
4444c779eb48atagen2026-10-16 12:07 UTC89
            l.merge_window(d);
9baaafa4b167atagen2026-10-16 10:04 UTC90
        }
9baaafa4b167atagen2026-10-16 10:04 UTC91
        Op::View(t) => active.into_iter().for_each(|o| l.view(o, tags[t as usize])),
9baaafa4b167atagen2026-10-16 10:04 UTC92
        Op::ToggleView(t) => active
9baaafa4b167atagen2026-10-16 10:04 UTC93
            .into_iter()
9baaafa4b167atagen2026-10-16 10:04 UTC94
            .for_each(|o| l.toggle_view(o, tags[t as usize])),
9baaafa4b167atagen2026-10-16 10:04 UTC95
        Op::Retag(i, t) => nth(l, i)
9baaafa4b167atagen2026-10-16 10:04 UTC96
            .into_iter()
9baaafa4b167atagen2026-10-16 10:04 UTC97
            .for_each(|w| l.set_tag(w, tags[t as usize])),
9baaafa4b167atagen2026-10-16 10:04 UTC98
        Op::Float(i) => nth(l, i).into_iter().for_each(|w| l.toggle_floating(w)),
9baaafa4b167atagen2026-10-16 10:04 UTC99
        Op::Expand(s) => {
4444c779eb48atagen2026-10-16 12:07 UTC100
            l.expand(
4444c779eb48atagen2026-10-16 12:07 UTC101
                [
4444c779eb48atagen2026-10-16 12:07 UTC102
                    ExpandScope::Window,
4444c779eb48atagen2026-10-16 12:07 UTC103
                    ExpandScope::Container,
4444c779eb48atagen2026-10-16 12:07 UTC104
                    ExpandScope::All,
4444c779eb48atagen2026-10-16 12:07 UTC105
                ][s as usize],
4444c779eb48atagen2026-10-16 12:07 UTC106
            );
9baaafa4b167atagen2026-10-16 10:04 UTC107
        }
9baaafa4b167atagen2026-10-16 10:04 UTC108
        Op::Flip => active.into_iter().for_each(|o| l.flip(o)),
9baaafa4b167atagen2026-10-16 10:04 UTC109
        Op::Resize(h, d) => {
9baaafa4b167atagen2026-10-16 10:04 UTC110
            let axis = if h { Axis::Horizontal } else { Axis::Vertical };
9baaafa4b167atagen2026-10-16 10:04 UTC111
            l.resize(axis, f64::from(d) / 200.0);
9baaafa4b167atagen2026-10-16 10:04 UTC112
        }
9baaafa4b167atagen2026-10-16 10:04 UTC113
        Op::Scratch(i) => nth(l, i).into_iter().for_each(|w| l.scratchpad_add(w, "s")),
9baaafa4b167atagen2026-10-16 10:04 UTC114
        Op::Summon => {
9baaafa4b167atagen2026-10-16 10:04 UTC115
            l.summon("s");
9baaafa4b167atagen2026-10-16 10:04 UTC116
        }
9baaafa4b167atagen2026-10-16 10:04 UTC117
        Op::Unplug => {
9baaafa4b167atagen2026-10-16 10:04 UTC118
            let b = l.outputs().find(|(_, out)| out.name == "b").map(|(o, _)| o);
9baaafa4b167atagen2026-10-16 10:04 UTC119
            if let Some(o) = b {
9baaafa4b167atagen2026-10-16 10:04 UTC120
                l.remove_output(o);
9baaafa4b167atagen2026-10-16 10:04 UTC121
            }
9baaafa4b167atagen2026-10-16 10:04 UTC122
        }
9baaafa4b167atagen2026-10-16 10:04 UTC123
        Op::Replug => {
9baaafa4b167atagen2026-10-16 10:04 UTC124
            if !l.outputs().any(|(_, out)| out.name == "b") {
9baaafa4b167atagen2026-10-16 10:04 UTC125
                l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
9baaafa4b167atagen2026-10-16 10:04 UTC126
            }
9baaafa4b167atagen2026-10-16 10:04 UTC127
        }
9baaafa4b167atagen2026-10-16 10:04 UTC128
    }
9baaafa4b167atagen2026-10-16 10:04 UTC129
}
9baaafa4b167atagen2026-10-16 10:04 UTC130
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC131
/// The area a tiled window gets, zero for floating or unplaced ones.
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC132
fn tiled_area(l: &Layout, w: WindowId) -> f64 {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC133
    let Some(o) = l.window(w).and_then(|x| x.output) else {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC134
        return 0.0;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC135
    };
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC136
    l.arrange(o)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC137
        .tiled
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC138
        .iter()
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC139
        .find(|p| p.window == w && p.visible)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC140
        .map_or(0.0, |p| p.rect.w * p.rect.h)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC141
}
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC142
9baaafa4b167atagen2026-10-16 10:04 UTC143
fn check_geometry(l: &Layout) {
9baaafa4b167atagen2026-10-16 10:04 UTC144
    for (o, out) in l.outputs() {
9baaafa4b167atagen2026-10-16 10:04 UTC145
        let arr = l.arrange(o);
38125b3487d7atagen2026-10-17 05:02 UTC146
        let mut drawn: Vec<WindowId> = arr
38125b3487d7atagen2026-10-17 05:02 UTC147
            .tiled
38125b3487d7atagen2026-10-17 05:02 UTC148
            .iter()
38125b3487d7atagen2026-10-17 05:02 UTC149
            .filter(|p| p.visible)
38125b3487d7atagen2026-10-17 05:02 UTC150
            .map(|p| p.window)
38125b3487d7atagen2026-10-17 05:02 UTC151
            .chain(arr.floating.iter().map(|&(w, _)| w))
38125b3487d7atagen2026-10-17 05:02 UTC152
            .chain(arr.fullscreen)
38125b3487d7atagen2026-10-17 05:02 UTC153
            .collect();
38125b3487d7atagen2026-10-17 05:02 UTC154
        drawn.sort();
38125b3487d7atagen2026-10-17 05:02 UTC155
        drawn.dedup();
38125b3487d7atagen2026-10-17 05:02 UTC156
        let mut visible: Vec<WindowId> = l
38125b3487d7atagen2026-10-17 05:02 UTC157
            .windows()
38125b3487d7atagen2026-10-17 05:02 UTC158
            .filter(|&(w, win)| win.output == Some(o) && l.visible(w))
38125b3487d7atagen2026-10-17 05:02 UTC159
            .map(|(w, _)| w)
38125b3487d7atagen2026-10-17 05:02 UTC160
            .collect();
38125b3487d7atagen2026-10-17 05:02 UTC161
        visible.sort();
38125b3487d7atagen2026-10-17 05:02 UTC162
        assert_eq!(drawn, visible, "arrangement disagrees with visible()");
9baaafa4b167atagen2026-10-16 10:04 UTC163
        let shown: Vec<Rect> = arr
9baaafa4b167atagen2026-10-16 10:04 UTC164
            .tiled
9baaafa4b167atagen2026-10-16 10:04 UTC165
            .iter()
9baaafa4b167atagen2026-10-16 10:04 UTC166
            .filter(|p| p.visible)
9baaafa4b167atagen2026-10-16 10:04 UTC167
            .map(|p| p.rect)
9baaafa4b167atagen2026-10-16 10:04 UTC168
            .collect();
1c8e1b4009f5atagen2026-10-16 23:19 UTC169
        for p in arr.tiled.iter().filter(|p| !p.visible) {
1c8e1b4009f5atagen2026-10-16 23:19 UTC170
            assert!(p.rect.w * p.rect.h <= 1e-6, "hidden {:?} has area", p.rect);
1c8e1b4009f5atagen2026-10-16 23:19 UTC171
        }
9baaafa4b167atagen2026-10-16 10:04 UTC172
        for r in &shown {
9baaafa4b167atagen2026-10-16 10:04 UTC173
            assert!(r.w >= 0.0 && r.h >= 0.0);
9baaafa4b167atagen2026-10-16 10:04 UTC174
            assert!(r.x >= out.area.x - 1e-6 && r.right() <= out.area.right() + 1e-6);
9baaafa4b167atagen2026-10-16 10:04 UTC175
            assert!(r.y >= out.area.y - 1e-6 && r.bottom() <= out.area.bottom() + 1e-6);
9baaafa4b167atagen2026-10-16 10:04 UTC176
        }
9baaafa4b167atagen2026-10-16 10:04 UTC177
        for (i, a) in shown.iter().enumerate() {
9baaafa4b167atagen2026-10-16 10:04 UTC178
            for b in &shown[i + 1..] {
9baaafa4b167atagen2026-10-16 10:04 UTC179
                let ox = a.right().min(b.right()) - a.x.max(b.x);
9baaafa4b167atagen2026-10-16 10:04 UTC180
                let oy = a.bottom().min(b.bottom()) - a.y.max(b.y);
9baaafa4b167atagen2026-10-16 10:04 UTC181
                assert!(ox <= 1e-6 || oy <= 1e-6, "{a:?} overlaps {b:?}");
9baaafa4b167atagen2026-10-16 10:04 UTC182
            }
9baaafa4b167atagen2026-10-16 10:04 UTC183
        }
9baaafa4b167atagen2026-10-16 10:04 UTC184
    }
9baaafa4b167atagen2026-10-16 10:04 UTC185
}
9baaafa4b167atagen2026-10-16 10:04 UTC186
9baaafa4b167atagen2026-10-16 10:04 UTC187
proptest! {
9baaafa4b167atagen2026-10-16 10:04 UTC188
    #![proptest_config(ProptestConfig::with_cases(2000))]
9baaafa4b167atagen2026-10-16 10:04 UTC189
9baaafa4b167atagen2026-10-16 10:04 UTC190
    #[test]
86ef802692f5atagen2026-10-19 07:43 UTC191
    fn random_commands_keep_the_model_sound(
86ef802692f5atagen2026-10-19 07:43 UTC192
        ops in prop::collection::vec(op(), 1..80),
86ef802692f5atagen2026-10-19 07:43 UTC193
        autotile in any::<bool>(),
86ef802692f5atagen2026-10-19 07:43 UTC194
    ) {
86ef802692f5atagen2026-10-19 07:43 UTC195
        let mut l = Layout {
86ef802692f5atagen2026-10-19 07:43 UTC196
            autotile,
86ef802692f5atagen2026-10-19 07:43 UTC197
            ..Layout::default()
86ef802692f5atagen2026-10-19 07:43 UTC198
        };
9baaafa4b167atagen2026-10-16 10:04 UTC199
        l.add_output("a", AREA);
9baaafa4b167atagen2026-10-16 10:04 UTC200
        l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
9baaafa4b167atagen2026-10-16 10:04 UTC201
        let tags = [l.homeless(), l.tag("1"), l.tag("2"), l.tag("3")];
9baaafa4b167atagen2026-10-16 10:04 UTC202
        for op in &ops {
21e8e8e5aa3datagen2026-10-20 11:44 UTC203
            let moving = matches!(op, Op::Move(_) | Op::Merge(_) | Op::Block(..));
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC204
            let shown_before = l.focused().filter(|&w| tiled_area(&l, w) > 1.0);
9baaafa4b167atagen2026-10-16 10:04 UTC205
            apply(&mut l, &tags, op);
9baaafa4b167atagen2026-10-16 10:04 UTC206
            l.check();
9baaafa4b167atagen2026-10-16 10:04 UTC207
            check_geometry(&l);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC208
            if moving && let Some(w) = shown_before {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC209
                prop_assert!(tiled_area(&l, w) > 1.0, "{w:?} vanished after {op:?}");
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC210
            }
9baaafa4b167atagen2026-10-16 10:04 UTC211
        }
9baaafa4b167atagen2026-10-16 10:04 UTC212
    }
38125b3487d7atagen2026-10-17 05:02 UTC213
38125b3487d7atagen2026-10-17 05:02 UTC214
    #[test]
38125b3487d7atagen2026-10-17 05:02 UTC215
    fn replugging_restores_what_unplugging_moved(ops in prop::collection::vec(op(), 1..60)) {
38125b3487d7atagen2026-10-17 05:02 UTC216
        let mut l = Layout::default();
38125b3487d7atagen2026-10-17 05:02 UTC217
        l.add_output("a", AREA);
38125b3487d7atagen2026-10-17 05:02 UTC218
        l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
38125b3487d7atagen2026-10-17 05:02 UTC219
        let tags = [l.homeless(), l.tag("1"), l.tag("2"), l.tag("3")];
38125b3487d7atagen2026-10-17 05:02 UTC220
        for op in &ops {
38125b3487d7atagen2026-10-17 05:02 UTC221
            apply(&mut l, &tags, op);
38125b3487d7atagen2026-10-17 05:02 UTC222
        }
38125b3487d7atagen2026-10-17 05:02 UTC223
        let Some(b) = l.outputs().find(|(_, out)| out.name == "b").map(|(o, _)| o) else {
38125b3487d7atagen2026-10-17 05:02 UTC224
            return Ok(());
38125b3487d7atagen2026-10-17 05:02 UTC225
        };
38125b3487d7atagen2026-10-17 05:02 UTC226
        let before = |l: &Layout, o| {
38125b3487d7atagen2026-10-17 05:02 UTC227
            let out = l.output(o).unwrap();
38125b3487d7atagen2026-10-17 05:02 UTC228
            (out.tree.windows().collect::<Vec<_>>(), out.floating.clone(), out.viewed.clone())
38125b3487d7atagen2026-10-17 05:02 UTC229
        };
38125b3487d7atagen2026-10-17 05:02 UTC230
        let was = before(&l, b);
38125b3487d7atagen2026-10-17 05:02 UTC231
        l.remove_output(b);
38125b3487d7atagen2026-10-17 05:02 UTC232
        let b = l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
38125b3487d7atagen2026-10-17 05:02 UTC233
        l.check();
38125b3487d7atagen2026-10-17 05:02 UTC234
        prop_assert_eq!(before(&l, b), was);
38125b3487d7atagen2026-10-17 05:02 UTC235
    }
9baaafa4b167atagen2026-10-16 10:04 UTC236
}
9baaafa4b167atagen2026-10-16 10:04 UTC237
9baaafa4b167atagen2026-10-16 10:04 UTC238
/// The hidden-slot rules from planner.html, demo 11.
9baaafa4b167atagen2026-10-16 10:04 UTC239
#[test]
9baaafa4b167atagen2026-10-16 10:04 UTC240
fn hidden_windows_keep_their_slots() {
9baaafa4b167atagen2026-10-16 10:04 UTC241
    let (a, b, c, d, e) = (1, 2, 3, 4, 5);
9baaafa4b167atagen2026-10-16 10:04 UTC242
    let mut t: Tree<u32> = Tree::default();
9baaafa4b167atagen2026-10-16 10:04 UTC243
    t.insert_row(a);
9baaafa4b167atagen2026-10-16 10:04 UTC244
    t.insert_after(d, a);
9baaafa4b167atagen2026-10-16 10:04 UTC245
    t.insert_after(b, d);
9baaafa4b167atagen2026-10-16 10:04 UTC246
    t.insert_row(c);
9baaafa4b167atagen2026-10-16 10:04 UTC247
    t.insert_after(e, c);
9baaafa4b167atagen2026-10-16 10:04 UTC248
    t.check();
9baaafa4b167atagen2026-10-16 10:04 UTC249
    let hidden = |w: u32| w != d && w != e;
9baaafa4b167atagen2026-10-16 10:04 UTC250
    let all = |_| true;
9baaafa4b167atagen2026-10-16 10:04 UTC251
    let rect = |t: &Tree<u32>, w| {
9baaafa4b167atagen2026-10-16 10:04 UTC252
        t.arrange(AREA, 0.0, all)
9baaafa4b167atagen2026-10-16 10:04 UTC253
            .into_iter()
9baaafa4b167atagen2026-10-16 10:04 UTC254
            .find(|p| p.0 == w)
9baaafa4b167atagen2026-10-16 10:04 UTC255
            .unwrap()
9baaafa4b167atagen2026-10-16 10:04 UTC256
            .1
9baaafa4b167atagen2026-10-16 10:04 UTC257
    };
9baaafa4b167atagen2026-10-16 10:04 UTC258
9baaafa4b167atagen2026-10-16 10:04 UTC259
    assert!(t.move_window(a, Dir::Right, hidden));
9baaafa4b167atagen2026-10-16 10:04 UTC260
    assert!(rect(&t, b).x < rect(&t, d).x && rect(&t, d).x < rect(&t, a).x);
9baaafa4b167atagen2026-10-16 10:04 UTC261
9baaafa4b167atagen2026-10-16 10:04 UTC262
    assert!(t.move_window(c, Dir::Up, hidden));
9baaafa4b167atagen2026-10-16 10:04 UTC263
    t.check();
9baaafa4b167atagen2026-10-16 10:04 UTC264
    assert_eq!(rect(&t, e).w, AREA.w, "E is demoted to a row of its own");
9baaafa4b167atagen2026-10-16 10:04 UTC265
}
60f00dd4f2beatagen2026-10-16 11:50 UTC266
60f00dd4f2beatagen2026-10-16 11:50 UTC267
/// Moving out of a row toward a lane of hidden windows joins that lane, so
60f00dd4f2beatagen2026-10-16 11:50 UTC268
/// they share it when shown again instead of a new lane opening between.
60f00dd4f2beatagen2026-10-16 11:50 UTC269
#[test]
60f00dd4f2beatagen2026-10-16 11:50 UTC270
fn moving_toward_a_hidden_lane_joins_it() {
60f00dd4f2beatagen2026-10-16 11:50 UTC271
    let (a, b, c, d, e) = (1, 2, 3, 4, 5);
60f00dd4f2beatagen2026-10-16 11:50 UTC272
    let mut t: Tree<u32> = Tree::default();
60f00dd4f2beatagen2026-10-16 11:50 UTC273
    t.insert_row(a);
60f00dd4f2beatagen2026-10-16 11:50 UTC274
    t.insert_after(b, a);
60f00dd4f2beatagen2026-10-16 11:50 UTC275
    t.insert_after(c, b);
60f00dd4f2beatagen2026-10-16 11:50 UTC276
    t.insert_row(d);
60f00dd4f2beatagen2026-10-16 11:50 UTC277
    t.insert_after(e, c);
60f00dd4f2beatagen2026-10-16 11:50 UTC278
    let tag_two = |w: u32| w != a && w != d;
60f00dd4f2beatagen2026-10-16 11:50 UTC279
    assert!(t.move_window(e, Dir::Down, tag_two));
60f00dd4f2beatagen2026-10-16 11:50 UTC280
    t.check();
60f00dd4f2beatagen2026-10-16 11:50 UTC281
    let all = t.arrange(AREA, 0.0, |_| true);
60f00dd4f2beatagen2026-10-16 11:50 UTC282
    let rect = |w| all.iter().find(|p| p.0 == w).unwrap().1;
60f00dd4f2beatagen2026-10-16 11:50 UTC283
    assert_eq!(rect(e).y, rect(d).y);
60f00dd4f2beatagen2026-10-16 11:50 UTC284
    assert!(rect(d).x < rect(e).x);
60f00dd4f2beatagen2026-10-16 11:50 UTC285
    assert!(rect(a).y < rect(d).y);
60f00dd4f2beatagen2026-10-16 11:50 UTC286
}
91957f0ae29eatagen2026-10-17 09:24 UTC287
91957f0ae29eatagen2026-10-17 09:24 UTC288
#[test]
605c44c8c5fcatagen2026-10-17 09:57 UTC289
fn merging_steps_out_of_a_stack_before_joining_another() {
91957f0ae29eatagen2026-10-17 09:24 UTC290
    let (a, b, c) = (1, 2, 3);
605c44c8c5fcatagen2026-10-17 09:57 UTC291
    let stacked = || {
605c44c8c5fcatagen2026-10-17 09:57 UTC292
        let mut t: Tree<u32> = Tree::default();
605c44c8c5fcatagen2026-10-17 09:57 UTC293
        t.insert_row(a);
605c44c8c5fcatagen2026-10-17 09:57 UTC294
        t.insert_after(b, a);
605c44c8c5fcatagen2026-10-17 09:57 UTC295
        t.insert_after(c, b);
605c44c8c5fcatagen2026-10-17 09:57 UTC296
        assert!(t.merge_window(b, Dir::Left, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC297
        t
605c44c8c5fcatagen2026-10-17 09:57 UTC298
    };
605c44c8c5fcatagen2026-10-17 09:57 UTC299
    let rects = |t: &Tree<u32>| {
605c44c8c5fcatagen2026-10-17 09:57 UTC300
        t.check();
605c44c8c5fcatagen2026-10-17 09:57 UTC301
        let all = t.arrange(AREA, 0.0, |_| true);
605c44c8c5fcatagen2026-10-17 09:57 UTC302
        move |w| all.iter().find(|p: &&(u32, Rect)| p.0 == w).unwrap().1
605c44c8c5fcatagen2026-10-17 09:57 UTC303
    };
605c44c8c5fcatagen2026-10-17 09:57 UTC304
605c44c8c5fcatagen2026-10-17 09:57 UTC305
    let mut t = stacked();
605c44c8c5fcatagen2026-10-17 09:57 UTC306
    assert!(t.merge_window(a, Dir::Right, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC307
    let rect = rects(&t);
605c44c8c5fcatagen2026-10-17 09:57 UTC308
    assert!(rect(b).x < rect(a).x && rect(a).x < rect(c).x);
605c44c8c5fcatagen2026-10-17 09:57 UTC309
    assert_eq!(rect(a).h, AREA.h);
605c44c8c5fcatagen2026-10-17 09:57 UTC310
    assert!(t.merge_window(a, Dir::Right, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC311
    let rect = rects(&t);
605c44c8c5fcatagen2026-10-17 09:57 UTC312
    assert_eq!(rect(a).x, rect(c).x);
605c44c8c5fcatagen2026-10-17 09:57 UTC313
605c44c8c5fcatagen2026-10-17 09:57 UTC314
    let mut t = stacked();
91957f0ae29eatagen2026-10-17 09:24 UTC315
    assert!(t.merge_window(a, Dir::Up, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC316
    let rect = rects(&t);
91957f0ae29eatagen2026-10-17 09:24 UTC317
    assert_eq!(rect(a).w, AREA.w);
91957f0ae29eatagen2026-10-17 09:24 UTC318
    assert!(rect(a).bottom() <= rect(b).y && rect(b).y == rect(c).y);
91957f0ae29eatagen2026-10-17 09:24 UTC319
}
54559daf11c3atagen2026-10-17 13:38 UTC320
21e8e8e5aa3datagen2026-10-20 11:44 UTC321
/// An extended window's group moves whole, even into a container running
21e8e8e5aa3datagen2026-10-20 11:44 UTC322
/// its own way, where it would otherwise dissolve and show what it covers.
21e8e8e5aa3datagen2026-10-20 11:44 UTC323
#[test]
21e8e8e5aa3datagen2026-10-20 11:44 UTC324
fn extended_windows_move_their_group() {
21e8e8e5aa3datagen2026-10-20 11:44 UTC325
    let (a, b, c) = (1, 2, 3);
21e8e8e5aa3datagen2026-10-20 11:44 UTC326
    let all = |_| true;
21e8e8e5aa3datagen2026-10-20 11:44 UTC327
    let mut t: Tree<u32> = Tree::default();
21e8e8e5aa3datagen2026-10-20 11:44 UTC328
    t.insert_row(a);
21e8e8e5aa3datagen2026-10-20 11:44 UTC329
    t.insert_after(b, a);
21e8e8e5aa3datagen2026-10-20 11:44 UTC330
    t.insert_after(c, b);
21e8e8e5aa3datagen2026-10-20 11:44 UTC331
    assert!(t.merge_window(b, Dir::Left, all));
21e8e8e5aa3datagen2026-10-20 11:44 UTC332
    assert!(t.toggle_expand(b, 0, 1, all));
21e8e8e5aa3datagen2026-10-20 11:44 UTC333
    let rects = |t: &Tree<u32>| {
21e8e8e5aa3datagen2026-10-20 11:44 UTC334
        t.check();
21e8e8e5aa3datagen2026-10-20 11:44 UTC335
        assert!(t.grouped(b, all) && t.eclipsed(a, all));
21e8e8e5aa3datagen2026-10-20 11:44 UTC336
        let placed = t.arrange(AREA, 0.0, all);
21e8e8e5aa3datagen2026-10-20 11:44 UTC337
        move |w| placed.iter().find(|p: &&(u32, Rect)| p.0 == w).unwrap().1
21e8e8e5aa3datagen2026-10-20 11:44 UTC338
    };
21e8e8e5aa3datagen2026-10-20 11:44 UTC339
21e8e8e5aa3datagen2026-10-20 11:44 UTC340
    assert!(t.move_window(b, Dir::Right, all));
21e8e8e5aa3datagen2026-10-20 11:44 UTC341
    let rect = rects(&t);
21e8e8e5aa3datagen2026-10-20 11:44 UTC342
    assert!(rect(c).x < rect(b).x);
21e8e8e5aa3datagen2026-10-20 11:44 UTC343
21e8e8e5aa3datagen2026-10-20 11:44 UTC344
    assert!(t.move_window(b, Dir::Down, all));
21e8e8e5aa3datagen2026-10-20 11:44 UTC345
    let rect = rects(&t);
21e8e8e5aa3datagen2026-10-20 11:44 UTC346
    assert!(rect(c).bottom() <= rect(b).y && rect(b).w == AREA.w);
21e8e8e5aa3datagen2026-10-20 11:44 UTC347
21e8e8e5aa3datagen2026-10-20 11:44 UTC348
    assert!(t.merge_window(b, Dir::Up, all));
21e8e8e5aa3datagen2026-10-20 11:44 UTC349
    let rect = rects(&t);
21e8e8e5aa3datagen2026-10-20 11:44 UTC350
    assert!(rect(c).right() <= rect(b).x && rect(b).y == rect(c).y);
21e8e8e5aa3datagen2026-10-20 11:44 UTC351
}
21e8e8e5aa3datagen2026-10-20 11:44 UTC352
1c1f70798e98atagen2026-10-22 05:39 UTC353
#[test]
1c1f70798e98atagen2026-10-22 05:39 UTC354
fn retagging_an_extended_window_leaves_hidden_members() {
1c1f70798e98atagen2026-10-22 05:39 UTC355
    let (a, b, c, d) = (1, 2, 3, 4);
1c1f70798e98atagen2026-10-22 05:39 UTC356
    let shown = |w| w != d;
1c1f70798e98atagen2026-10-22 05:39 UTC357
    let mut t: Tree<u32> = Tree::default();
1c1f70798e98atagen2026-10-22 05:39 UTC358
    t.insert_row(a);
1c1f70798e98atagen2026-10-22 05:39 UTC359
    t.insert_after(b, a);
1c1f70798e98atagen2026-10-22 05:39 UTC360
    t.insert_after(c, b);
1c1f70798e98atagen2026-10-22 05:39 UTC361
    t.insert_after(d, c);
1c1f70798e98atagen2026-10-22 05:39 UTC362
    assert!(t.toggle_expand(b, 0, 1, shown));
1c1f70798e98atagen2026-10-22 05:39 UTC363
    let mut group = t.group_windows(b, shown);
1c1f70798e98atagen2026-10-22 05:39 UTC364
    group.sort();
1c1f70798e98atagen2026-10-22 05:39 UTC365
    assert_eq!(group, [a, b, c]);
1c1f70798e98atagen2026-10-22 05:39 UTC366
    assert_eq!(t.group_windows(a, shown), [a]);
1c1f70798e98atagen2026-10-22 05:39 UTC367
}
1c1f70798e98atagen2026-10-22 05:39 UTC368
54559daf11c3atagen2026-10-17 13:38 UTC369
/// A window alone on screen in a container of hidden ones expands among the
54559daf11c3atagen2026-10-17 13:38 UTC370
/// windows it is shown beside, not inside the container nobody can see.
54559daf11c3atagen2026-10-17 13:38 UTC371
#[test]
54559daf11c3atagen2026-10-17 13:38 UTC372
fn expand_skips_containers_of_hidden_windows() {
54559daf11c3atagen2026-10-17 13:38 UTC373
    let (a, b, c) = (1, 2, 3);
54559daf11c3atagen2026-10-17 13:38 UTC374
    let mut t: Tree<u32> = Tree::default();
54559daf11c3atagen2026-10-17 13:38 UTC375
    t.insert_row(a);
54559daf11c3atagen2026-10-17 13:38 UTC376
    t.insert_after(b, a);
54559daf11c3atagen2026-10-17 13:38 UTC377
    t.insert_after(c, b);
54559daf11c3atagen2026-10-17 13:38 UTC378
    assert!(t.merge_window(a, Dir::Right, |_| true));
54559daf11c3atagen2026-10-17 13:38 UTC379
    let shown = |w: u32| w != a;
54559daf11c3atagen2026-10-17 13:38 UTC380
    assert!(t.toggle_expand(b, 0, 1, shown));
54559daf11c3atagen2026-10-17 13:38 UTC381
    assert!(t.maximized(b, shown));
54559daf11c3atagen2026-10-17 13:38 UTC382
    let all = t.arrange(AREA, 0.0, shown);
54559daf11c3atagen2026-10-17 13:38 UTC383
    let rect = |w| all.iter().find(|p| p.0 == w).unwrap().1;
54559daf11c3atagen2026-10-17 13:38 UTC384
    assert_eq!(rect(b).w, AREA.w);
54559daf11c3atagen2026-10-17 13:38 UTC385
}
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC386
e2443dec4dcdatagen2026-10-18 09:58 UTC387
/// Going in and out of a stack repeatedly leaves every window the size it was.
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC388
#[test]
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC389
fn stacking_in_and_out_does_not_drift() {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC390
    for out in [Dir::Up, Dir::Down, Dir::Right] {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC391
        let (a, b, c) = (1, 2, 3);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC392
        let mut t: Tree<u32> = Tree::default();
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC393
        t.insert_row(a);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC394
        t.insert_after(b, a);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC395
        t.insert_after(c, b);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC396
        assert!(t.merge_window(c, Dir::Left, |_| true));
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC397
        assert!(t.merge_window(c, out, |_| true));
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC398
        let once = t.arrange(AREA, 0.0, |_| true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC399
        for _ in 0..8 {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC400
            t.merge_window(c, Dir::Left, |_| true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC401
            t.merge_window(c, out, |_| true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC402
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC403
        t.check();
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC404
        assert_eq!(t.arrange(AREA, 0.0, |_| true), once, "stepping out {out:?}");
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC405
    }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC406
}
9c0e9500c151atagen2026-10-18 09:09 UTC407
e2443dec4dcdatagen2026-10-18 09:58 UTC408
/// Windows of other tags in the rows around a window must not change what
e2443dec4dcdatagen2026-10-18 09:58 UTC409
/// the user sees as it moves through them, nor drift over round trips.
9c0e9500c151atagen2026-10-18 09:09 UTC410
#[test]
9c0e9500c151atagen2026-10-18 09:09 UTC411
fn moving_past_hidden_windows_does_not_drift() {
9c0e9500c151atagen2026-10-18 09:09 UTC412
    let (a, b, h, h2) = (1, 2, 3, 4);
9c0e9500c151atagen2026-10-18 09:09 UTC413
    let mut t: Tree<u32> = Tree::default();
9c0e9500c151atagen2026-10-18 09:09 UTC414
    t.insert_row(h);
9c0e9500c151atagen2026-10-18 09:09 UTC415
    t.insert_row(a);
9c0e9500c151atagen2026-10-18 09:09 UTC416
    t.insert_after(h2, a);
9c0e9500c151atagen2026-10-18 09:09 UTC417
    t.insert_after(b, h2);
9c0e9500c151atagen2026-10-18 09:09 UTC418
    let vis = |w: u32| w != h && w != h2;
00c7b3db8a3fatagen2026-10-18 10:19 UTC419
    let mut once = None;
9c0e9500c151atagen2026-10-18 09:09 UTC420
    for _ in 0..4 {
9c0e9500c151atagen2026-10-18 09:09 UTC421
        for d in [Dir::Up, Dir::Up, Dir::Down] {
e2443dec4dcdatagen2026-10-18 09:58 UTC422
            t.move_window(b, d, vis);
e2443dec4dcdatagen2026-10-18 09:58 UTC423
            let shown = t.arrange(AREA, 0.0, vis);
e2443dec4dcdatagen2026-10-18 09:58 UTC424
            let r = shown.iter().find(|p| p.0 == b).unwrap().1;
e2443dec4dcdatagen2026-10-18 09:58 UTC425
            assert!(
e2443dec4dcdatagen2026-10-18 09:58 UTC426
                (r.w * r.h - AREA.w * AREA.h / 2.0).abs() < 1e-6,
e2443dec4dcdatagen2026-10-18 09:58 UTC427
                "{d:?} left {r:?}"
e2443dec4dcdatagen2026-10-18 09:58 UTC428
            );
9c0e9500c151atagen2026-10-18 09:09 UTC429
        }
9c0e9500c151atagen2026-10-18 09:09 UTC430
        t.check();
00c7b3db8a3fatagen2026-10-18 10:19 UTC431
        let all = t.arrange(AREA, 0.0, |_| true);
00c7b3db8a3fatagen2026-10-18 10:19 UTC432
        assert_eq!(*once.get_or_insert_with(|| all.clone()), all);
9c0e9500c151atagen2026-10-18 09:09 UTC433
    }
9c0e9500c151atagen2026-10-18 09:09 UTC434
}
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC435
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC436
/// Monitors sleeping together unplug one after another, so windows already
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC437
/// moved off the first must still go home to it, not to the second.
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC438
#[test]
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC439
fn windows_return_to_where_they_were_first_evicted_from() {
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC440
    let mut l = Layout::default();
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC441
    let a = l.add_output("a", AREA);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC442
    l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC443
    let c = l.add_output("c", Rect::new(1800.0, 0.0, 800.0, 600.0));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC444
    let w = l.add_window(Some(a), None, None);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC445
    l.remove_output(a);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC446
    let b = l.outputs().find(|(_, out)| out.name == "b").unwrap().0;
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC447
    l.remove_output(b);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC448
    assert_eq!(l.window(w).unwrap().output, Some(c));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC449
    l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC450
    let a = l.add_output("a", AREA);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC451
    l.check();
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC452
    assert_eq!(l.window(w).unwrap().output, Some(a));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC453
    assert!(l.output(a).unwrap().tree.contains(w));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC454
}
00c7b3db8a3fatagen2026-10-18 10:19 UTC455
00c7b3db8a3fatagen2026-10-18 10:19 UTC456
/// A stack showing one window looks like a lone window, so moving toward it
00c7b3db8a3fatagen2026-10-18 10:19 UTC457
/// swaps places instead of joining a stack the user cannot see.
00c7b3db8a3fatagen2026-10-18 10:19 UTC458
#[test]
00c7b3db8a3fatagen2026-10-18 10:19 UTC459
fn moving_toward_a_stack_showing_one_window_swaps() {
00c7b3db8a3fatagen2026-10-18 10:19 UTC460
    let (a, b, h) = (1, 2, 3);
00c7b3db8a3fatagen2026-10-18 10:19 UTC461
    let mut t: Tree<u32> = Tree::default();
00c7b3db8a3fatagen2026-10-18 10:19 UTC462
    t.insert_row(a);
00c7b3db8a3fatagen2026-10-18 10:19 UTC463
    t.insert_after(b, a);
00c7b3db8a3fatagen2026-10-18 10:19 UTC464
    t.insert_after(h, b);
00c7b3db8a3fatagen2026-10-18 10:19 UTC465
    assert!(t.merge_window(h, Dir::Left, |_| true));
00c7b3db8a3fatagen2026-10-18 10:19 UTC466
    let vis = |w: u32| w != h;
00c7b3db8a3fatagen2026-10-18 10:19 UTC467
    assert!(t.move_window(a, Dir::Right, vis));
00c7b3db8a3fatagen2026-10-18 10:19 UTC468
    t.check();
00c7b3db8a3fatagen2026-10-18 10:19 UTC469
    let shown = t.arrange(AREA, 0.0, vis);
00c7b3db8a3fatagen2026-10-18 10:19 UTC470
    let rect = |w| shown.iter().find(|p| p.0 == w).unwrap().1;
00c7b3db8a3fatagen2026-10-18 10:19 UTC471
    assert!(rect(b).x < rect(a).x);
00c7b3db8a3fatagen2026-10-18 10:19 UTC472
    assert_eq!(rect(a).h, AREA.h);
00c7b3db8a3fatagen2026-10-18 10:19 UTC473
}
8f1f743a2f6batagen2026-10-19 03:43 UTC474
ae47743b5745atagen2026-10-19 03:49 UTC475
/// A move either changes what is on screen or changes nothing at all, and a
ae47743b5745atagen2026-10-19 03:49 UTC476
/// window move never rearranges hidden windows among themselves. Steps around
ae47743b5745atagen2026-10-19 03:49 UTC477
/// hidden windows used to succeed invisibly and eat the keypress.
8f1f743a2f6batagen2026-10-19 03:43 UTC478
#[test]
8f1f743a2f6batagen2026-10-19 03:43 UTC479
fn moves_are_seen_or_undone() {
8f1f743a2f6batagen2026-10-19 03:43 UTC480
    let dirs = [Dir::Left, Dir::Right, Dir::Up, Dir::Down];
8f1f743a2f6batagen2026-10-19 03:43 UTC481
    let mut runner = proptest::test_runner::TestRunner::deterministic();
ae47743b5745atagen2026-10-19 03:49 UTC482
    let ops = prop::collection::vec((0u8..4, 0u8..4, 0u8..4, 0u8..32), 1..14);
8f1f743a2f6batagen2026-10-19 03:43 UTC483
    for _ in 0..2000 {
8f1f743a2f6batagen2026-10-19 03:43 UTC484
        let seq = proptest::strategy::ValueTree::current(&ops.new_tree(&mut runner).unwrap());
8f1f743a2f6batagen2026-10-19 03:43 UTC485
        let mut t: Tree<u32> = Tree::default();
8f1f743a2f6batagen2026-10-19 03:43 UTC486
        t.insert_row(0);
8f1f743a2f6batagen2026-10-19 03:43 UTC487
        let mut n = 1;
8f1f743a2f6batagen2026-10-19 03:43 UTC488
        for &(w, d, kind, hide) in &seq {
ae47743b5745atagen2026-10-19 03:49 UTC489
            let vis = |x: u32| hide & (1 << x) == 0;
8f1f743a2f6batagen2026-10-19 03:43 UTC490
            if kind == 0 && n < 5 {
8f1f743a2f6batagen2026-10-19 03:43 UTC491
                t.insert_after(n, u32::from(w) % n);
8f1f743a2f6batagen2026-10-19 03:43 UTC492
                n += 1;
8f1f743a2f6batagen2026-10-19 03:43 UTC493
                continue;
8f1f743a2f6batagen2026-10-19 03:43 UTC494
            }
8f1f743a2f6batagen2026-10-19 03:43 UTC495
            let w = u32::from(w) % n;
8f1f743a2f6batagen2026-10-19 03:43 UTC496
            let shown = |t: &Tree<u32>| {
8f1f743a2f6batagen2026-10-19 03:43 UTC497
                let mut v: Vec<_> = t
8f1f743a2f6batagen2026-10-19 03:43 UTC498
                    .arrange(AREA, 0.0, vis)
8f1f743a2f6batagen2026-10-19 03:43 UTC499
                    .into_iter()
8f1f743a2f6batagen2026-10-19 03:43 UTC500
                    .filter(|p| vis(p.0))
8f1f743a2f6batagen2026-10-19 03:43 UTC501
                    .collect();
8f1f743a2f6batagen2026-10-19 03:43 UTC502
                v.sort_by_key(|p| p.0);
8f1f743a2f6batagen2026-10-19 03:43 UTC503
                v
8f1f743a2f6batagen2026-10-19 03:43 UTC504
            };
8f1f743a2f6batagen2026-10-19 03:43 UTC505
            let before = shown(&t);
ae47743b5745atagen2026-10-19 03:49 UTC506
            let hidden = |t: &Tree<u32>| {
ae47743b5745atagen2026-10-19 03:49 UTC507
                let mut t = t.clone();
ae47743b5745atagen2026-10-19 03:49 UTC508
                t.retain(|x| !vis(x));
ae47743b5745atagen2026-10-19 03:49 UTC509
                t.shape()
ae47743b5745atagen2026-10-19 03:49 UTC510
            };
ae47743b5745atagen2026-10-19 03:49 UTC511
            let rest = hidden(&t);
ae47743b5745atagen2026-10-19 03:49 UTC512
            let d = dirs[usize::from(d)];
ae47743b5745atagen2026-10-19 03:49 UTC513
            let moved = match kind {
ae47743b5745atagen2026-10-19 03:49 UTC514
                1 => t.merge_window(w, d, vis),
ae47743b5745atagen2026-10-19 03:49 UTC515
                2 => t.move_window(w, d, vis),
21e8e8e5aa3datagen2026-10-20 11:44 UTC516
                3 => t.move_container(w, d, false, vis),
ae47743b5745atagen2026-10-19 03:49 UTC517
                _ => continue,
8f1f743a2f6batagen2026-10-19 03:43 UTC518
            };
8f1f743a2f6batagen2026-10-19 03:43 UTC519
            t.check();
8f1f743a2f6batagen2026-10-19 03:43 UTC520
            assert_eq!(moved, shown(&t) != before, "{seq:?}");
ae47743b5745atagen2026-10-19 03:49 UTC521
            if kind != 3 {
ae47743b5745atagen2026-10-19 03:49 UTC522
                assert_eq!(hidden(&t), rest, "{seq:?}");
ae47743b5745atagen2026-10-19 03:49 UTC523
            }
8f1f743a2f6batagen2026-10-19 03:43 UTC524
        }
8f1f743a2f6batagen2026-10-19 03:43 UTC525
    }
8f1f743a2f6batagen2026-10-19 03:43 UTC526
}
b44dde08d7acatagen2026-10-19 07:38 UTC527
b44dde08d7acatagen2026-10-19 07:38 UTC528
/// A fullscreen window hides everything but its own transients, which stay
b44dde08d7acatagen2026-10-19 07:38 UTC529
/// reachable by focus and are not hidden when they map.
b44dde08d7acatagen2026-10-19 07:38 UTC530
#[test]
b44dde08d7acatagen2026-10-19 07:38 UTC531
fn fullscreen_leaves_its_transients_reachable() {
b44dde08d7acatagen2026-10-19 07:38 UTC532
    let mut l = Layout::default();
b44dde08d7acatagen2026-10-19 07:38 UTC533
    let o = l.add_output("a", AREA);
b44dde08d7acatagen2026-10-19 07:38 UTC534
    let beside = l.add_window(None, None, None);
b44dde08d7acatagen2026-10-19 07:38 UTC535
    l.focus(beside);
b44dde08d7acatagen2026-10-19 07:38 UTC536
    let game = l.add_window(None, None, None);
b44dde08d7acatagen2026-10-19 07:38 UTC537
    l.focus(game);
b44dde08d7acatagen2026-10-19 07:38 UTC538
    l.toggle_fullscreen(game);
b44dde08d7acatagen2026-10-19 07:38 UTC539
    let stranger = l.add_window(None, None, None);
b44dde08d7acatagen2026-10-19 07:38 UTC540
    assert!(l.covered(stranger));
b44dde08d7acatagen2026-10-19 07:38 UTC541
    let dialog = l.add_window(None, None, Some(Rect::new(300.0, 200.0, 200.0, 100.0)));
b44dde08d7acatagen2026-10-19 07:38 UTC542
    l.set_parent(dialog, Some(game));
b44dde08d7acatagen2026-10-19 07:38 UTC543
    assert!(!l.covered(dialog));
b44dde08d7acatagen2026-10-19 07:38 UTC544
    l.focus(dialog);
b44dde08d7acatagen2026-10-19 07:38 UTC545
b44dde08d7acatagen2026-10-19 07:38 UTC546
    let arr = l.arrange(o);
b44dde08d7acatagen2026-10-19 07:38 UTC547
    assert_eq!(arr.fullscreen, Some(game));
b44dde08d7acatagen2026-10-19 07:38 UTC548
    assert_eq!(arr.over, vec![dialog]);
b44dde08d7acatagen2026-10-19 07:38 UTC549
    for dir in [Dir::Left, Dir::Right, Dir::Up, Dir::Down] {
b44dde08d7acatagen2026-10-19 07:38 UTC550
        if let Some(w) = l.focus_dir(dir) {
b44dde08d7acatagen2026-10-19 07:38 UTC551
            assert!(w == game || w == dialog, "focus walked to a covered window");
b44dde08d7acatagen2026-10-19 07:38 UTC552
        }
b44dde08d7acatagen2026-10-19 07:38 UTC553
    }
b44dde08d7acatagen2026-10-19 07:38 UTC554
b44dde08d7acatagen2026-10-19 07:38 UTC555
    l.remove_window(game);
b44dde08d7acatagen2026-10-19 07:38 UTC556
    assert!(!l.covered(stranger));
b44dde08d7acatagen2026-10-19 07:38 UTC557
    assert_eq!(l.window(dialog).and_then(|w| w.parent), None);
b44dde08d7acatagen2026-10-19 07:38 UTC558
}
86ef802692f5atagen2026-10-19 07:43 UTC559
86ef802692f5atagen2026-10-19 07:43 UTC560
fn rect_of(t: &Tree<u32>, w: u32, vis: impl Fn(u32) -> bool) -> Rect {
86ef802692f5atagen2026-10-19 07:43 UTC561
    t.arrange(AREA, 0.0, vis)
86ef802692f5atagen2026-10-19 07:43 UTC562
        .into_iter()
86ef802692f5atagen2026-10-19 07:43 UTC563
        .find(|p| p.0 == w)
86ef802692f5atagen2026-10-19 07:43 UTC564
        .unwrap()
86ef802692f5atagen2026-10-19 07:43 UTC565
        .1
86ef802692f5atagen2026-10-19 07:43 UTC566
}
86ef802692f5atagen2026-10-19 07:43 UTC567
86ef802692f5atagen2026-10-19 07:43 UTC568
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC569
fn splitting_a_lone_window_shares_its_space() {
86ef802692f5atagen2026-10-19 07:43 UTC570
    let (a, b, c) = (1, 2, 3);
86ef802692f5atagen2026-10-19 07:43 UTC571
    let mut t: Tree<u32> = Tree::default();
86ef802692f5atagen2026-10-19 07:43 UTC572
    t.insert_row(a);
86ef802692f5atagen2026-10-19 07:43 UTC573
    t.insert_split(b, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC574
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC575
    let (ra, rb) = (rect_of(&t, a, |_| true), rect_of(&t, b, |_| true));
86ef802692f5atagen2026-10-19 07:43 UTC576
    assert_eq!((ra.w, rb.w, ra.h), (AREA.w / 2.0, AREA.w / 2.0, AREA.h));
86ef802692f5atagen2026-10-19 07:43 UTC577
    assert!(ra.x < rb.x);
86ef802692f5atagen2026-10-19 07:43 UTC578
86ef802692f5atagen2026-10-19 07:43 UTC579
    t.insert_split(c, b, Axis::Vertical);
86ef802692f5atagen2026-10-19 07:43 UTC580
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC581
    let (rb, rc) = (rect_of(&t, b, |_| true), rect_of(&t, c, |_| true));
86ef802692f5atagen2026-10-19 07:43 UTC582
    assert_eq!(
86ef802692f5atagen2026-10-19 07:43 UTC583
        (rb.h, rc.h, rb.w),
86ef802692f5atagen2026-10-19 07:43 UTC584
        (AREA.h / 2.0, AREA.h / 2.0, AREA.w / 2.0)
86ef802692f5atagen2026-10-19 07:43 UTC585
    );
86ef802692f5atagen2026-10-19 07:43 UTC586
    assert!(rb.y < rc.y);
86ef802692f5atagen2026-10-19 07:43 UTC587
    assert_eq!(rect_of(&t, a, |_| true), ra, "the sibling is undisturbed");
86ef802692f5atagen2026-10-19 07:43 UTC588
}
86ef802692f5atagen2026-10-19 07:43 UTC589
86ef802692f5atagen2026-10-19 07:43 UTC590
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC591
fn splitting_along_the_parents_axis_appends_a_sibling() {
86ef802692f5atagen2026-10-19 07:43 UTC592
    let (a, b, c, d) = (1, 2, 3, 4);
86ef802692f5atagen2026-10-19 07:43 UTC593
    let mut t: Tree<u32> = Tree::default();
86ef802692f5atagen2026-10-19 07:43 UTC594
    t.insert_row(a);
86ef802692f5atagen2026-10-19 07:43 UTC595
    t.insert_split(b, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC596
    t.insert_split(c, b, Axis::Vertical);
86ef802692f5atagen2026-10-19 07:43 UTC597
    let before = rect_of(&t, a, |_| true);
86ef802692f5atagen2026-10-19 07:43 UTC598
    t.insert_split(d, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC599
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC600
    let all = |_| true;
86ef802692f5atagen2026-10-19 07:43 UTC601
    let (ra, rd, rb) = (
86ef802692f5atagen2026-10-19 07:43 UTC602
        rect_of(&t, a, all),
86ef802692f5atagen2026-10-19 07:43 UTC603
        rect_of(&t, d, all),
86ef802692f5atagen2026-10-19 07:43 UTC604
        rect_of(&t, b, all),
86ef802692f5atagen2026-10-19 07:43 UTC605
    );
86ef802692f5atagen2026-10-19 07:43 UTC606
    assert_eq!((ra.w, rd.w), (before.w / 2.0, before.w / 2.0));
86ef802692f5atagen2026-10-19 07:43 UTC607
    assert!(ra.x < rd.x && rd.x < rb.x);
86ef802692f5atagen2026-10-19 07:43 UTC608
    assert_eq!(rb.w, AREA.w / 2.0, "the neighbour keeps its share");
86ef802692f5atagen2026-10-19 07:43 UTC609
}
86ef802692f5atagen2026-10-19 07:43 UTC610
86ef802692f5atagen2026-10-19 07:43 UTC611
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC612
fn splitting_beside_hidden_windows_leaves_their_slots_alone() {
86ef802692f5atagen2026-10-19 07:43 UTC613
    let (a, b, c, d) = (1, 2, 3, 4);
86ef802692f5atagen2026-10-19 07:43 UTC614
    let mut t: Tree<u32> = Tree::default();
86ef802692f5atagen2026-10-19 07:43 UTC615
    t.insert_row(a);
86ef802692f5atagen2026-10-19 07:43 UTC616
    t.insert_after(b, a);
86ef802692f5atagen2026-10-19 07:43 UTC617
    t.insert_after(c, a);
86ef802692f5atagen2026-10-19 07:43 UTC618
    let tag_one = |w: u32| w == a || w == d;
86ef802692f5atagen2026-10-19 07:43 UTC619
    assert_eq!(rect_of(&t, a, tag_one).h, AREA.h);
86ef802692f5atagen2026-10-19 07:43 UTC620
86ef802692f5atagen2026-10-19 07:43 UTC621
    t.insert_split(d, a, Axis::Vertical);
86ef802692f5atagen2026-10-19 07:43 UTC622
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC623
    let (ra, rd) = (rect_of(&t, a, tag_one), rect_of(&t, d, tag_one));
86ef802692f5atagen2026-10-19 07:43 UTC624
    assert_eq!((ra.h, rd.h, ra.w), (AREA.h / 2.0, AREA.h / 2.0, AREA.w));
86ef802692f5atagen2026-10-19 07:43 UTC625
    assert!(ra.y < rd.y);
86ef802692f5atagen2026-10-19 07:43 UTC626
86ef802692f5atagen2026-10-19 07:43 UTC627
    t.insert_split(d + 1, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC628
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC629
    let shown = |w: u32| w == a || w == d + 1;
86ef802692f5atagen2026-10-19 07:43 UTC630
    let (ra, re) = (rect_of(&t, a, shown), rect_of(&t, d + 1, shown));
86ef802692f5atagen2026-10-19 07:43 UTC631
    assert_eq!((ra.w, re.w), (AREA.w / 2.0, AREA.w / 2.0));
86ef802692f5atagen2026-10-19 07:43 UTC632
    assert!(ra.x < re.x);
86ef802692f5atagen2026-10-19 07:43 UTC633
    assert_eq!(t.windows().count(), 5, "hidden windows stay in the tree");
86ef802692f5atagen2026-10-19 07:43 UTC634
}
86ef802692f5atagen2026-10-19 07:43 UTC635
86ef802692f5atagen2026-10-19 07:43 UTC636
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC637
fn autotile_dwindles_by_the_anchors_shape() {
86ef802692f5atagen2026-10-19 07:43 UTC638
    let mut l = Layout {
86ef802692f5atagen2026-10-19 07:43 UTC639
        autotile: true,
86ef802692f5atagen2026-10-19 07:43 UTC640
        ..Layout::default()
86ef802692f5atagen2026-10-19 07:43 UTC641
    };
86ef802692f5atagen2026-10-19 07:43 UTC642
    let o = l.add_output("a", AREA);
86ef802692f5atagen2026-10-19 07:43 UTC643
    let tag = l.tag("1");
86ef802692f5atagen2026-10-19 07:43 UTC644
    l.view(o, tag);
86ef802692f5atagen2026-10-19 07:43 UTC645
    let mut ids = Vec::new();
86ef802692f5atagen2026-10-19 07:43 UTC646
    for _ in 0..4 {
86ef802692f5atagen2026-10-19 07:43 UTC647
        let w = l.add_window(Some(o), Some(tag), None);
86ef802692f5atagen2026-10-19 07:43 UTC648
        l.focus(w);
86ef802692f5atagen2026-10-19 07:43 UTC649
        ids.push(w);
86ef802692f5atagen2026-10-19 07:43 UTC650
        l.check();
86ef802692f5atagen2026-10-19 07:43 UTC651
    }
86ef802692f5atagen2026-10-19 07:43 UTC652
    let arr = l.arrange(o);
86ef802692f5atagen2026-10-19 07:43 UTC653
    let r = |w| arr.tiled.iter().find(|p| p.window == w).unwrap().rect;
86ef802692f5atagen2026-10-19 07:43 UTC654
    let [a, b, c, d] = [ids[0], ids[1], ids[2], ids[3]];
86ef802692f5atagen2026-10-19 07:43 UTC655
    assert!(
86ef802692f5atagen2026-10-19 07:43 UTC656
        r(a).x < r(b).x && r(a).h > r(b).h * 1.5,
86ef802692f5atagen2026-10-19 07:43 UTC657
        "{a:?} left, {b:?} right"
86ef802692f5atagen2026-10-19 07:43 UTC658
    );
86ef802692f5atagen2026-10-19 07:43 UTC659
    assert!(r(b).y < r(c).y && r(b).x == r(c).x);
86ef802692f5atagen2026-10-19 07:43 UTC660
    assert!(r(c).x < r(d).x && r(c).y == r(d).y);
86ef802692f5atagen2026-10-19 07:43 UTC661
}
9fdf9584d2bdatagen2026-10-19 07:38 UTC662
9fdf9584d2bdatagen2026-10-19 07:38 UTC663
#[test]
9fdf9584d2bdatagen2026-10-19 07:38 UTC664
fn urgency_skips_the_focused_window_and_ends_with_focus() {
9fdf9584d2bdatagen2026-10-19 07:38 UTC665
    let mut l = Layout::default();
9fdf9584d2bdatagen2026-10-19 07:38 UTC666
    let o = l.add_output("a", AREA);
9fdf9584d2bdatagen2026-10-19 07:38 UTC667
    let tag = l.tag("1");
9fdf9584d2bdatagen2026-10-19 07:38 UTC668
    l.view(o, tag);
9fdf9584d2bdatagen2026-10-19 07:38 UTC669
    let a = l.add_window(Some(o), Some(tag), None);
9fdf9584d2bdatagen2026-10-19 07:38 UTC670
    let b = l.add_window(Some(o), Some(tag), None);
9fdf9584d2bdatagen2026-10-19 07:38 UTC671
    l.focus(a);
9fdf9584d2bdatagen2026-10-19 07:38 UTC672
    l.mark_urgent(a);
9fdf9584d2bdatagen2026-10-19 07:38 UTC673
    l.mark_urgent(b);
9fdf9584d2bdatagen2026-10-19 07:38 UTC674
    assert!(!l.window(a).unwrap().urgent);
9fdf9584d2bdatagen2026-10-19 07:38 UTC675
    assert!(l.window(b).unwrap().urgent);
9fdf9584d2bdatagen2026-10-19 07:38 UTC676
    assert!(!l.tags().any(|(_, t)| t.urgent));
9fdf9584d2bdatagen2026-10-19 07:38 UTC677
    l.focus(b);
9fdf9584d2bdatagen2026-10-19 07:38 UTC678
    assert!(!l.window(b).unwrap().urgent);
9fdf9584d2bdatagen2026-10-19 07:38 UTC679
}
b01f4e7ab7e6atagen2026-10-19 07:44 UTC680
b01f4e7ab7e6atagen2026-10-19 07:44 UTC681
#[test]
b01f4e7ab7e6atagen2026-10-19 07:44 UTC682
fn oversized_floats_keep_their_top_edge_and_a_strip() {
b01f4e7ab7e6atagen2026-10-19 07:44 UTC683
    let bounds = Rect::new(0.0, 0.0, 1000.0, 600.0);
b01f4e7ab7e6atagen2026-10-19 07:44 UTC684
    let huge = Rect::new(-5000.0, -300.0, 3000.0, 2000.0);
b01f4e7ab7e6atagen2026-10-19 07:44 UTC685
    let r = clamp_float(huge, bounds);
b01f4e7ab7e6atagen2026-10-19 07:44 UTC686
    assert_eq!((r.x, r.y), (-2936.0, 0.0));
b01f4e7ab7e6atagen2026-10-19 07:44 UTC687
    let r = clamp_float(Rect::new(5000.0, 5000.0, 3000.0, 2000.0), bounds);
b01f4e7ab7e6atagen2026-10-19 07:44 UTC688
    assert_eq!((r.x, r.y), (936.0, 536.0));
b01f4e7ab7e6atagen2026-10-19 07:44 UTC689
    let fits = clamp_float(Rect::new(900.0, 500.0, 300.0, 200.0), bounds);
b01f4e7ab7e6atagen2026-10-19 07:44 UTC690
    assert_eq!((fits.x, fits.y), (700.0, 400.0));
b01f4e7ab7e6atagen2026-10-19 07:44 UTC691
}
4b73e40af1e3atagen2026-10-19 07:44 UTC692
4b73e40af1e3atagen2026-10-19 07:44 UTC693
#[test]
4b73e40af1e3atagen2026-10-19 07:44 UTC694
fn floats_follow_their_output_area_and_panels() {
4b73e40af1e3atagen2026-10-19 07:44 UTC695
    let mut l = Layout::default();
4b73e40af1e3atagen2026-10-19 07:44 UTC696
    let o = l.add_output("a", AREA);
4b73e40af1e3atagen2026-10-19 07:44 UTC697
    let w = l.add_window(Some(o), None, Some(Rect::new(500.0, 300.0, 400.0, 300.0)));
4b73e40af1e3atagen2026-10-19 07:44 UTC698
    l.set_output_area(o, Rect::new(100.0, 50.0, 500.0, 300.0));
4b73e40af1e3atagen2026-10-19 07:44 UTC699
    let r = l.window(w).unwrap().float_rect.unwrap();
4b73e40af1e3atagen2026-10-19 07:44 UTC700
    assert_eq!((r.x, r.y, r.w, r.h), (350.0, 200.0, 200.0, 150.0));
4b73e40af1e3atagen2026-10-19 07:44 UTC701
4b73e40af1e3atagen2026-10-19 07:44 UTC702
    l.set_reserved(
4b73e40af1e3atagen2026-10-19 07:44 UTC703
        o,
4b73e40af1e3atagen2026-10-19 07:44 UTC704
        Insets {
4b73e40af1e3atagen2026-10-19 07:44 UTC705
            top: 100.0,
4b73e40af1e3atagen2026-10-19 07:44 UTC706
            ..Insets::default()
4b73e40af1e3atagen2026-10-19 07:44 UTC707
        },
4b73e40af1e3atagen2026-10-19 07:44 UTC708
    );
4b73e40af1e3atagen2026-10-19 07:44 UTC709
    let r = l.window(w).unwrap().float_rect.unwrap();
4b73e40af1e3atagen2026-10-19 07:44 UTC710
    let usable = l.output(o).unwrap().usable();
4b73e40af1e3atagen2026-10-19 07:44 UTC711
    assert!(r.y >= usable.y && r.bottom() <= usable.bottom());
9c937ba68fa7atagen2026-10-19 08:02 UTC712
    assert_eq!((r.w, r.h), (200.0, 150.0), "a panel never shrinks a float");
4b73e40af1e3atagen2026-10-19 07:44 UTC713
}
4b73e40af1e3atagen2026-10-19 07:44 UTC714
4b73e40af1e3atagen2026-10-19 07:44 UTC715
#[test]
4b73e40af1e3atagen2026-10-19 07:44 UTC716
fn floats_shrink_into_a_smaller_output_they_are_evicted_to() {
4b73e40af1e3atagen2026-10-19 07:44 UTC717
    let mut l = Layout::default();
4b73e40af1e3atagen2026-10-19 07:44 UTC718
    let a = l.add_output("a", AREA);
4b73e40af1e3atagen2026-10-19 07:44 UTC719
    let b = l.add_output("b", Rect::new(1000.0, 0.0, 500.0, 300.0));
4b73e40af1e3atagen2026-10-19 07:44 UTC720
    let w = l.add_window(Some(a), None, Some(Rect::new(800.0, 400.0, 200.0, 200.0)));
4b73e40af1e3atagen2026-10-19 07:44 UTC721
    l.remove_output(a);
4b73e40af1e3atagen2026-10-19 07:44 UTC722
    assert_eq!(l.window(w).unwrap().output, Some(b));
4b73e40af1e3atagen2026-10-19 07:44 UTC723
    let r = l.window(w).unwrap().float_rect.unwrap();
4b73e40af1e3atagen2026-10-19 07:44 UTC724
    let usable = l.output(b).unwrap().usable();
4b73e40af1e3atagen2026-10-19 07:44 UTC725
    assert_eq!((r.w, r.h), (100.0, 100.0));
4b73e40af1e3atagen2026-10-19 07:44 UTC726
    assert!(r.x >= usable.x && r.right() <= usable.right());
4b73e40af1e3atagen2026-10-19 07:44 UTC727
    assert!(r.y >= usable.y && r.bottom() <= usable.bottom());
4b73e40af1e3atagen2026-10-19 07:44 UTC728
}
abe5e42a95d6atagen2026-10-19 07:52 UTC729
abe5e42a95d6atagen2026-10-19 07:52 UTC730
/// Dragging a split moves only the two tiles that meet at it, whichever of
abe5e42a95d6atagen2026-10-19 07:52 UTC731
/// them the drag started from, and stops where one would vanish.
abe5e42a95d6atagen2026-10-19 07:52 UTC732
#[test]
abe5e42a95d6atagen2026-10-19 07:52 UTC733
fn dragging_a_split_moves_just_that_split() {
abe5e42a95d6atagen2026-10-19 07:52 UTC734
    let mut l = Layout::default();
abe5e42a95d6atagen2026-10-19 07:52 UTC735
    let o = l.add_output("a", AREA);
abe5e42a95d6atagen2026-10-19 07:52 UTC736
    let a = l.add_window(Some(o), None, None);
abe5e42a95d6atagen2026-10-19 07:52 UTC737
    let b = l.add_window(Some(o), None, None);
abe5e42a95d6atagen2026-10-19 07:52 UTC738
    let c = l.add_window(Some(o), None, None);
abe5e42a95d6atagen2026-10-19 07:52 UTC739
    let rect = |l: &Layout, w| {
abe5e42a95d6atagen2026-10-19 07:52 UTC740
        l.arrange(o)
abe5e42a95d6atagen2026-10-19 07:52 UTC741
            .tiled
abe5e42a95d6atagen2026-10-19 07:52 UTC742
            .iter()
abe5e42a95d6atagen2026-10-19 07:52 UTC743
            .find(|p| p.window == w)
abe5e42a95d6atagen2026-10-19 07:52 UTC744
            .unwrap()
abe5e42a95d6atagen2026-10-19 07:52 UTC745
            .rect
abe5e42a95d6atagen2026-10-19 07:52 UTC746
    };
abe5e42a95d6atagen2026-10-19 07:52 UTC747
    let before = [rect(&l, a), rect(&l, b), rect(&l, c)];
abe5e42a95d6atagen2026-10-19 07:52 UTC748
    assert!(!l.has_split(a, Dir::Up));
abe5e42a95d6atagen2026-10-19 07:52 UTC749
    assert!(!l.move_split(a, Dir::Up, 50.0));
abe5e42a95d6atagen2026-10-19 07:52 UTC750
    assert!(l.has_split(a, Dir::Down));
abe5e42a95d6atagen2026-10-19 07:52 UTC751
abe5e42a95d6atagen2026-10-19 07:52 UTC752
    assert!(l.move_split(a, Dir::Down, 40.0));
abe5e42a95d6atagen2026-10-19 07:52 UTC753
    let (ra, rb) = (rect(&l, a), rect(&l, b));
abe5e42a95d6atagen2026-10-19 07:52 UTC754
    assert!((ra.h - before[0].h - 40.0).abs() < 1e-6);
abe5e42a95d6atagen2026-10-19 07:52 UTC755
    assert!((rb.h - before[1].h + 40.0).abs() < 1e-6);
abe5e42a95d6atagen2026-10-19 07:52 UTC756
    assert_eq!(rect(&l, c).h, before[2].h);
abe5e42a95d6atagen2026-10-19 07:52 UTC757
abe5e42a95d6atagen2026-10-19 07:52 UTC758
    assert!(l.move_split(b, Dir::Up, -40.0));
abe5e42a95d6atagen2026-10-19 07:52 UTC759
    assert!((rect(&l, a).h - before[0].h).abs() < 1e-6);
abe5e42a95d6atagen2026-10-19 07:52 UTC760
abe5e42a95d6atagen2026-10-19 07:52 UTC761
    l.move_split(a, Dir::Down, 1e6);
abe5e42a95d6atagen2026-10-19 07:52 UTC762
    assert!(rect(&l, b).h > 1.0);
abe5e42a95d6atagen2026-10-19 07:52 UTC763
    l.check();
abe5e42a95d6atagen2026-10-19 07:52 UTC764
}
b001ce7817edatagen2026-10-19 09:23 UTC765
b001ce7817edatagen2026-10-19 09:23 UTC766
#[test]
b001ce7817edatagen2026-10-19 09:23 UTC767
fn scratchpads_stay_centred_whatever_their_rect() {
b001ce7817edatagen2026-10-19 09:23 UTC768
    let mut l = Layout::default();
b001ce7817edatagen2026-10-19 09:23 UTC769
    let o = l.add_output("a", AREA);
b001ce7817edatagen2026-10-19 09:23 UTC770
    let w = l.add_window(Some(o), None, Some(Rect::new(10.0, 20.0, 300.0, 200.0)));
b001ce7817edatagen2026-10-19 09:23 UTC771
    l.scratchpad_add(w, "s");
b001ce7817edatagen2026-10-19 09:23 UTC772
    l.summon("s");
b001ce7817edatagen2026-10-19 09:23 UTC773
    l.set_float_rect(w, Rect::new(0.0, 0.0, 500.0, 5000.0));
b001ce7817edatagen2026-10-19 09:23 UTC774
    let r = l.arrange(o).floating.iter().find(|p| p.0 == w).unwrap().1;
b001ce7817edatagen2026-10-19 09:23 UTC775
    let area = l.output(o).unwrap().usable();
b001ce7817edatagen2026-10-19 09:23 UTC776
    assert_eq!((r.w, r.h), (500.0, area.h));
b001ce7817edatagen2026-10-19 09:23 UTC777
    assert_eq!(r.x + r.w / 2.0, area.x + area.w / 2.0);
b001ce7817edatagen2026-10-19 09:23 UTC778
    assert_eq!(r.y, area.y);
b001ce7817edatagen2026-10-19 09:23 UTC779
}
ecb0c8c1f31datagen2026-10-19 10:15 UTC780
ecb0c8c1f31datagen2026-10-19 10:15 UTC781
/// Focus aimed at a window with a modal dialog lands on the deepest dialog
ecb0c8c1f31datagen2026-10-19 10:15 UTC782
/// from every path, and falls back to the parent once the dialog is gone.
ecb0c8c1f31datagen2026-10-19 10:15 UTC783
#[test]
ecb0c8c1f31datagen2026-10-19 10:15 UTC784
fn modal_dialogs_keep_focus_over_their_parent() {
ecb0c8c1f31datagen2026-10-19 10:15 UTC785
    let mut l = Layout::default();
ecb0c8c1f31datagen2026-10-19 10:15 UTC786
    l.add_output("a", AREA);
ecb0c8c1f31datagen2026-10-19 10:15 UTC787
    let parent = l.add_window(None, None, None);
ecb0c8c1f31datagen2026-10-19 10:15 UTC788
    let other = l.add_window(None, None, None);
ecb0c8c1f31datagen2026-10-19 10:15 UTC789
    l.focus(parent);
ecb0c8c1f31datagen2026-10-19 10:15 UTC790
    let dialog = l.add_window(None, None, Some(Rect::new(300.0, 200.0, 200.0, 100.0)));
ecb0c8c1f31datagen2026-10-19 10:15 UTC791
    let nested = l.add_window(None, None, Some(Rect::new(350.0, 250.0, 100.0, 50.0)));
ecb0c8c1f31datagen2026-10-19 10:15 UTC792
    l.set_parent(dialog, Some(parent));
ecb0c8c1f31datagen2026-10-19 10:15 UTC793
    l.set_parent(nested, Some(dialog));
ecb0c8c1f31datagen2026-10-19 10:15 UTC794
    l.set_modal(dialog, true);
ecb0c8c1f31datagen2026-10-19 10:15 UTC795
    assert_eq!(l.focused(), Some(dialog));
ecb0c8c1f31datagen2026-10-19 10:15 UTC796
    l.set_modal(nested, true);
ecb0c8c1f31datagen2026-10-19 10:15 UTC797
    assert_eq!(l.focused(), Some(nested));
ecb0c8c1f31datagen2026-10-19 10:15 UTC798
ecb0c8c1f31datagen2026-10-19 10:15 UTC799
    l.focus(other);
ecb0c8c1f31datagen2026-10-19 10:15 UTC800
    assert_eq!(l.focused(), Some(other));
ecb0c8c1f31datagen2026-10-19 10:15 UTC801
    assert_eq!(l.focus(parent), Some(nested));
ecb0c8c1f31datagen2026-10-19 10:15 UTC802
    assert_eq!(l.focused(), Some(nested));
ecb0c8c1f31datagen2026-10-19 10:15 UTC803
    assert!(l.is_focused(parent));
ecb0c8c1f31datagen2026-10-19 10:15 UTC804
ecb0c8c1f31datagen2026-10-19 10:15 UTC805
    l.focus(other);
ecb0c8c1f31datagen2026-10-19 10:15 UTC806
    for dir in [Dir::Left, Dir::Right, Dir::Up, Dir::Down] {
ecb0c8c1f31datagen2026-10-19 10:15 UTC807
        l.focus(other);
ecb0c8c1f31datagen2026-10-19 10:15 UTC808
        if let Some(w) = l.focus_dir(dir) {
ecb0c8c1f31datagen2026-10-19 10:15 UTC809
            assert_ne!(w, parent, "directional focus stopped on a blocked parent");
ecb0c8c1f31datagen2026-10-19 10:15 UTC810
        }
ecb0c8c1f31datagen2026-10-19 10:15 UTC811
    }
ecb0c8c1f31datagen2026-10-19 10:15 UTC812
ecb0c8c1f31datagen2026-10-19 10:15 UTC813
    l.focus(parent);
ecb0c8c1f31datagen2026-10-19 10:15 UTC814
    l.remove_window(nested);
ecb0c8c1f31datagen2026-10-19 10:15 UTC815
    assert_eq!(l.focused(), Some(dialog));
ecb0c8c1f31datagen2026-10-19 10:15 UTC816
    l.remove_window(dialog);
ecb0c8c1f31datagen2026-10-19 10:15 UTC817
    assert_eq!(l.focused(), Some(parent));
ecb0c8c1f31datagen2026-10-19 10:15 UTC818
    l.check();
ecb0c8c1f31datagen2026-10-19 10:15 UTC819
}
1c7e3075e57fatagen2026-10-19 10:28 UTC820
1c7e3075e57fatagen2026-10-19 10:28 UTC821
/// A fullscreen window nobody ever focused, as a rule with `focus = false`
1c7e3075e57fatagen2026-10-19 10:28 UTC822
/// makes, still takes the output once its tag is in view.
1c7e3075e57fatagen2026-10-19 10:28 UTC823
#[test]
1c7e3075e57fatagen2026-10-19 10:28 UTC824
fn fullscreen_does_not_need_focus_history() {
1c7e3075e57fatagen2026-10-19 10:28 UTC825
    let mut l = Layout::default();
1c7e3075e57fatagen2026-10-19 10:28 UTC826
    let o = l.add_output("a", AREA);
1c7e3075e57fatagen2026-10-19 10:28 UTC827
    let (main, other) = (l.tag("main"), l.tag("other"));
1c7e3075e57fatagen2026-10-19 10:28 UTC828
    l.view(o, main);
1c7e3075e57fatagen2026-10-19 10:28 UTC829
    let shown = l.add_window(None, Some(main), None);
1c7e3075e57fatagen2026-10-19 10:28 UTC830
    let player = l.add_window(None, Some(main), None);
1c7e3075e57fatagen2026-10-19 10:28 UTC831
    l.toggle_fullscreen(player);
1c7e3075e57fatagen2026-10-19 10:28 UTC832
    assert_eq!(l.arrange(o).fullscreen, Some(player));
1c7e3075e57fatagen2026-10-19 10:28 UTC833
    assert!(l.covered(shown));
1c7e3075e57fatagen2026-10-19 10:28 UTC834
1c7e3075e57fatagen2026-10-19 10:28 UTC835
    let hidden = l.add_window(None, Some(other), None);
1c7e3075e57fatagen2026-10-19 10:28 UTC836
    l.toggle_fullscreen(hidden);
1c7e3075e57fatagen2026-10-19 10:28 UTC837
    assert_eq!(l.arrange(o).fullscreen, Some(player));
1c7e3075e57fatagen2026-10-19 10:28 UTC838
    l.focus(shown);
1c7e3075e57fatagen2026-10-19 10:28 UTC839
    l.view(o, other);
1c7e3075e57fatagen2026-10-19 10:28 UTC840
    assert_eq!(l.arrange(o).fullscreen, Some(hidden));
1c7e3075e57fatagen2026-10-19 10:28 UTC841
}
1c7e3075e57fatagen2026-10-19 10:28 UTC842
1c7e3075e57fatagen2026-10-19 10:28 UTC843
/// A request to focus a covered window flags it instead, but its own
1c7e3075e57fatagen2026-10-19 10:28 UTC844
/// fullscreen window's transients and a summoned scratchpad take the focus.
1c7e3075e57fatagen2026-10-19 10:28 UTC845
#[test]
1c7e3075e57fatagen2026-10-19 10:28 UTC846
fn covered_windows_are_flagged_not_focused() {
1c7e3075e57fatagen2026-10-19 10:28 UTC847
    let mut l = Layout::default();
1c7e3075e57fatagen2026-10-19 10:28 UTC848
    let o = l.add_output("a", AREA);
1c7e3075e57fatagen2026-10-19 10:28 UTC849
    let game = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC850
    l.focus(game);
1c7e3075e57fatagen2026-10-19 10:28 UTC851
    l.toggle_fullscreen(game);
1c7e3075e57fatagen2026-10-19 10:28 UTC852
    let stranger = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC853
    assert!(!l.focus_unless_covered(stranger));
1c7e3075e57fatagen2026-10-19 10:28 UTC854
    assert_eq!(l.focused(), Some(game));
1c7e3075e57fatagen2026-10-19 10:28 UTC855
    assert!(l.window(stranger).unwrap().urgent);
1c7e3075e57fatagen2026-10-19 10:28 UTC856
1c7e3075e57fatagen2026-10-19 10:28 UTC857
    let dialog = l.add_window(None, None, Some(Rect::new(300.0, 200.0, 200.0, 100.0)));
1c7e3075e57fatagen2026-10-19 10:28 UTC858
    l.set_parent(dialog, Some(game));
1c7e3075e57fatagen2026-10-19 10:28 UTC859
    assert!(l.focus_unless_covered(dialog));
1c7e3075e57fatagen2026-10-19 10:28 UTC860
    assert_eq!(l.focused(), Some(dialog));
1c7e3075e57fatagen2026-10-19 10:28 UTC861
1c7e3075e57fatagen2026-10-19 10:28 UTC862
    let pad = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC863
    l.scratchpad_add(pad, "s");
1c7e3075e57fatagen2026-10-19 10:28 UTC864
    assert_eq!(l.summon("s"), Some(pad));
1c7e3075e57fatagen2026-10-19 10:28 UTC865
    assert_eq!(l.focused(), Some(pad));
1c7e3075e57fatagen2026-10-19 10:28 UTC866
    assert!(l.arrange(o).over.contains(&pad));
1c7e3075e57fatagen2026-10-19 10:28 UTC867
    l.check();
1c7e3075e57fatagen2026-10-19 10:28 UTC868
}
1c7e3075e57fatagen2026-10-19 10:28 UTC869
1c7e3075e57fatagen2026-10-19 10:28 UTC870
/// A window its client hid keeps its place, floating state, parent links and
1c7e3075e57fatagen2026-10-19 10:28 UTC871
/// scratchpad, and comes back as it was when shown again.
1c7e3075e57fatagen2026-10-19 10:28 UTC872
#[test]
1c7e3075e57fatagen2026-10-19 10:28 UTC873
fn hidden_windows_come_back_as_they_were() {
1c7e3075e57fatagen2026-10-19 10:28 UTC874
    let mut l = Layout::default();
1c7e3075e57fatagen2026-10-19 10:28 UTC875
    let o = l.add_output("a", AREA);
1c7e3075e57fatagen2026-10-19 10:28 UTC876
    let tag = l.tag("work");
1c7e3075e57fatagen2026-10-19 10:28 UTC877
    l.view(o, tag);
1c7e3075e57fatagen2026-10-19 10:28 UTC878
    let tray = l.add_window(None, Some(tag), Some(Rect::new(40.0, 50.0, 300.0, 200.0)));
1c7e3075e57fatagen2026-10-19 10:28 UTC879
    let child = l.add_window(None, Some(tag), Some(Rect::new(60.0, 70.0, 100.0, 80.0)));
1c7e3075e57fatagen2026-10-19 10:28 UTC880
    let tiled = l.add_window(None, Some(tag), None);
1c7e3075e57fatagen2026-10-19 10:28 UTC881
    l.set_parent(child, Some(tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC882
    l.toggle_sticky(tray);
1c7e3075e57fatagen2026-10-19 10:28 UTC883
    l.focus(tray);
1c7e3075e57fatagen2026-10-19 10:28 UTC884
    l.focus(tiled);
1c7e3075e57fatagen2026-10-19 10:28 UTC885
1c7e3075e57fatagen2026-10-19 10:28 UTC886
    l.set_hidden(tray, true);
1c7e3075e57fatagen2026-10-19 10:28 UTC887
    assert!(!l.visible(tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC888
    assert!(l.arrange(o).floating.iter().all(|f| f.0 != tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC889
    assert!(!l.focus_unless_covered(tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC890
    assert!(!l.window(tray).unwrap().urgent);
1c7e3075e57fatagen2026-10-19 10:28 UTC891
    assert_eq!(l.focused(), Some(tiled));
1c7e3075e57fatagen2026-10-19 10:28 UTC892
    assert_eq!(l.window(child).and_then(|w| w.parent), Some(tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC893
1c7e3075e57fatagen2026-10-19 10:28 UTC894
    l.set_hidden(tray, false);
1c7e3075e57fatagen2026-10-19 10:28 UTC895
    let w = l.window(tray).unwrap();
1c7e3075e57fatagen2026-10-19 10:28 UTC896
    assert!(l.visible(tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC897
    assert!(w.floating && w.sticky && w.tag == tag);
1c7e3075e57fatagen2026-10-19 10:28 UTC898
    assert_eq!(w.float_rect, Some(Rect::new(40.0, 50.0, 300.0, 200.0)));
1c7e3075e57fatagen2026-10-19 10:28 UTC899
    assert!(l.arrange(o).floating.iter().any(|f| f.0 == tray));
1c7e3075e57fatagen2026-10-19 10:28 UTC900
    l.check();
1c7e3075e57fatagen2026-10-19 10:28 UTC901
}
1c7e3075e57fatagen2026-10-19 10:28 UTC902
1c7e3075e57fatagen2026-10-19 10:28 UTC903
/// Summoning skips a scratchpad window that is hidden to a tray.
1c7e3075e57fatagen2026-10-19 10:28 UTC904
#[test]
1c7e3075e57fatagen2026-10-19 10:28 UTC905
fn summoning_skips_hidden_scratchpad_windows() {
1c7e3075e57fatagen2026-10-19 10:28 UTC906
    let mut l = Layout::default();
1c7e3075e57fatagen2026-10-19 10:28 UTC907
    let o = l.add_output("a", AREA);
1c7e3075e57fatagen2026-10-19 10:28 UTC908
    let a = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC909
    let b = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC910
    l.scratchpad_add(a, "s");
1c7e3075e57fatagen2026-10-19 10:28 UTC911
    l.scratchpad_add(b, "s");
1c7e3075e57fatagen2026-10-19 10:28 UTC912
    l.set_hidden(a, true);
1c7e3075e57fatagen2026-10-19 10:28 UTC913
    assert_eq!(l.summon("s"), Some(b));
1c7e3075e57fatagen2026-10-19 10:28 UTC914
    assert!(l.arrange(o).floating.iter().all(|f| f.0 != a));
1c7e3075e57fatagen2026-10-19 10:28 UTC915
    assert_eq!(l.summon("s"), None);
1c7e3075e57fatagen2026-10-19 10:28 UTC916
}
1c7e3075e57fatagen2026-10-19 10:28 UTC917
1c7e3075e57fatagen2026-10-19 10:28 UTC918
/// A modal dialog that takes focus on mapping puts its parent just beneath
1c7e3075e57fatagen2026-10-19 10:28 UTC919
/// it, so closing it does not land on whatever was focused before the parent.
1c7e3075e57fatagen2026-10-19 10:28 UTC920
#[test]
1c7e3075e57fatagen2026-10-19 10:28 UTC921
fn a_modal_brings_its_parent_up_beneath_it() {
1c7e3075e57fatagen2026-10-19 10:28 UTC922
    let mut l = Layout::default();
1c7e3075e57fatagen2026-10-19 10:28 UTC923
    l.add_output("a", AREA);
1c7e3075e57fatagen2026-10-19 10:28 UTC924
    let parent = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC925
    let other = l.add_window(None, None, None);
1c7e3075e57fatagen2026-10-19 10:28 UTC926
    l.focus(parent);
1c7e3075e57fatagen2026-10-19 10:28 UTC927
    l.focus(other);
1c7e3075e57fatagen2026-10-19 10:28 UTC928
    let dialog = l.add_window(None, None, Some(Rect::new(300.0, 200.0, 200.0, 100.0)));
1c7e3075e57fatagen2026-10-19 10:28 UTC929
    l.set_parent(dialog, Some(parent));
1c7e3075e57fatagen2026-10-19 10:28 UTC930
    l.set_modal(dialog, true);
1c7e3075e57fatagen2026-10-19 10:28 UTC931
    l.focus(dialog);
1c7e3075e57fatagen2026-10-19 10:28 UTC932
    l.remove_window(dialog);
1c7e3075e57fatagen2026-10-19 10:28 UTC933
    assert_eq!(l.focused(), Some(parent));
1c7e3075e57fatagen2026-10-19 10:28 UTC934
}
243161cd0662atagen2026-10-19 10:41 UTC935
243161cd0662atagen2026-10-19 10:41 UTC936
/// Floats start inside what the bars leave of their output, whether they came
243161cd0662atagen2026-10-19 10:41 UTC937
/// with a rect hanging off its edge or take the default one.
243161cd0662atagen2026-10-19 10:41 UTC938
#[test]
243161cd0662atagen2026-10-19 10:41 UTC939
fn new_floats_land_inside_the_bars() {
243161cd0662atagen2026-10-19 10:41 UTC940
    let mut l = Layout::default();
243161cd0662atagen2026-10-19 10:41 UTC941
    let o = l.add_output("a", AREA);
243161cd0662atagen2026-10-19 10:41 UTC942
    let bar = Insets {
243161cd0662atagen2026-10-19 10:41 UTC943
        top: 40.0,
243161cd0662atagen2026-10-19 10:41 UTC944
        ..Insets::default()
243161cd0662atagen2026-10-19 10:41 UTC945
    };
243161cd0662atagen2026-10-19 10:41 UTC946
    l.set_reserved(o, bar);
243161cd0662atagen2026-10-19 10:41 UTC947
    let usable = l.output(o).unwrap().usable();
243161cd0662atagen2026-10-19 10:41 UTC948
243161cd0662atagen2026-10-19 10:41 UTC949
    let hanging = l.add_window(None, None, Some(Rect::new(900.0, -30.0, 300.0, 200.0)));
243161cd0662atagen2026-10-19 10:41 UTC950
    let r = l.window(hanging).and_then(|w| w.float_rect).unwrap();
243161cd0662atagen2026-10-19 10:41 UTC951
    assert!(r.x >= usable.x && r.right() <= usable.right() && r.y >= usable.y);
243161cd0662atagen2026-10-19 10:41 UTC952
243161cd0662atagen2026-10-19 10:41 UTC953
    let plain = l.add_window(None, None, None);
243161cd0662atagen2026-10-19 10:41 UTC954
    l.toggle_floating(plain);
243161cd0662atagen2026-10-19 10:41 UTC955
    let r = l.window(plain).and_then(|w| w.float_rect).unwrap();
243161cd0662atagen2026-10-19 10:41 UTC956
    assert_eq!(r.y + r.h / 2.0, usable.y + usable.h / 2.0);
243161cd0662atagen2026-10-19 10:41 UTC957
}