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}