Skip to main content

sequoia_dock/
drop.rs

1use sequoia_core::{Rect, Vec2};
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4#[non_exhaustive]
5pub enum DockDrop {
6    Left,
7    Right,
8    Top,
9    Bottom,
10
11    #[default]
12    Tab,
13
14    Float,
15}
16
17impl DockDrop {
18    pub const fn is_side(self) -> bool {
19        matches!(self, Self::Left | Self::Right | Self::Top | Self::Bottom)
20    }
21
22    pub const fn is_tab(self) -> bool {
23        matches!(self, Self::Tab)
24    }
25
26    pub const fn is_float(self) -> bool {
27        matches!(self, Self::Float)
28    }
29}
30
31pub const EDGE_FRACTION: f32 = 0.22;
32
33pub fn drop_from_fraction(fraction: Vec2, edge: f32) -> DockDrop {
34    let e = if edge.is_finite() {
35        edge.clamp(0.0, 0.45)
36    } else {
37        EDGE_FRACTION
38    };
39    let fx = fraction.x.clamp(0.0, 1.0);
40    let fy = fraction.y.clamp(0.0, 1.0);
41
42    let dist_left = fx;
43    let dist_right = 1.0 - fx;
44    let dist_top = fy;
45    let dist_bottom = 1.0 - fy;
46    let min_h = dist_left.min(dist_right);
47    let min_v = dist_top.min(dist_bottom);
48    if min_h < e || min_v < e {
49        if min_h < min_v {
50            if dist_left < dist_right {
51                DockDrop::Left
52            } else {
53                DockDrop::Right
54            }
55        } else if dist_top < dist_bottom {
56            DockDrop::Top
57        } else {
58            DockDrop::Bottom
59        }
60    } else {
61        DockDrop::Tab
62    }
63}
64
65pub fn drop_from_fraction_or_float(fraction: Vec2, edge: f32, force_float: bool) -> DockDrop {
66    if force_float {
67        DockDrop::Float
68    } else {
69        drop_from_fraction(fraction, edge)
70    }
71}
72
73pub fn preview_rect(host: Rect, drop: DockDrop, frac: f32) -> Rect {
74    use sequoia_layout::{Dock, dock_preview_rect};
75    match drop {
76        DockDrop::Left => dock_preview_rect(host, Dock::Left, frac),
77        DockDrop::Right => dock_preview_rect(host, Dock::Right, frac),
78        DockDrop::Top => dock_preview_rect(host, Dock::Top, frac),
79        DockDrop::Bottom => dock_preview_rect(host, Dock::Bottom, frac),
80        DockDrop::Tab => host,
81        DockDrop::Float => {
82            let w = host.size.width.max(0.0);
83            let h = host.size.height.max(0.0);
84            let pw = w * 0.6;
85            let ph = h * 0.6;
86            Rect::from_xywh(
87                host.origin.x + (w - pw) * 0.5,
88                host.origin.y + (h - ph) * 0.5,
89                pw,
90                ph,
91            )
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    fn v(x: f32, y: f32) -> Vec2 {
101        Vec2::new(x, y)
102    }
103
104    #[test]
105    fn edges_win_over_center() {
106        assert_eq!(
107            drop_from_fraction(v(0.05, 0.5), EDGE_FRACTION),
108            DockDrop::Left
109        );
110        assert_eq!(
111            drop_from_fraction(v(0.95, 0.5), EDGE_FRACTION),
112            DockDrop::Right
113        );
114        assert_eq!(
115            drop_from_fraction(v(0.5, 0.05), EDGE_FRACTION),
116            DockDrop::Top
117        );
118        assert_eq!(
119            drop_from_fraction(v(0.5, 0.95), EDGE_FRACTION),
120            DockDrop::Bottom
121        );
122        assert_eq!(
123            drop_from_fraction(v(0.5, 0.5), EDGE_FRACTION),
124            DockDrop::Tab
125        );
126    }
127
128    #[test]
129    fn bad_edge_falls_back() {
130        assert_eq!(drop_from_fraction(v(0.5, 0.5), f32::NAN), DockDrop::Tab);
131        assert_eq!(drop_from_fraction(v(0.05, 0.5), -1.0), DockDrop::Tab);
132    }
133
134    #[test]
135    fn force_float_wins() {
136        assert_eq!(
137            drop_from_fraction_or_float(v(0.05, 0.5), EDGE_FRACTION, true),
138            DockDrop::Float
139        );
140        assert_eq!(
141            drop_from_fraction_or_float(v(0.05, 0.5), EDGE_FRACTION, false),
142            DockDrop::Left
143        );
144    }
145
146    #[test]
147    fn preview_splits_host() {
148        let host = Rect::from_xywh(0.0, 0.0, 400.0, 300.0);
149        let l = preview_rect(host, DockDrop::Left, 0.5);
150        assert_eq!((l.size.width, l.size.height), (200.0, 300.0));
151        let b = preview_rect(host, DockDrop::Bottom, 0.25);
152        assert!((b.size.height - 75.0).abs() < 0.01);
153        let t = preview_rect(host, DockDrop::Tab, 0.5);
154        assert_eq!(t, host);
155        let f = preview_rect(host, DockDrop::Float, 0.5);
156        assert!(f.size.width < host.size.width && f.size.height < host.size.height);
157    }
158
159    #[test]
160    fn preview_frac_clamps() {
161        let host = Rect::from_xywh(0.0, 0.0, 400.0, 300.0);
162        let l = preview_rect(host, DockDrop::Left, f32::NAN);
163        assert!((l.size.width - 200.0).abs() < 0.01);
164        let l2 = preview_rect(host, DockDrop::Left, 99.0);
165        assert!((l2.size.width - 400.0 * 0.85).abs() < 0.01);
166    }
167}