1use crate::{
2 prelude::{
3 Area,
4 AreaOf,
5 Available,
6 Parent,
7 Point2D,
8 Size2D,
9 },
10 scaled::Scaled,
11};
12
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Default, PartialEq, Clone, Debug)]
15pub struct PositionSides {
16 pub top: Option<f32>,
17 pub right: Option<f32>,
18 pub bottom: Option<f32>,
19 pub left: Option<f32>,
20}
21
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35#[derive(PartialEq, Clone, Debug)]
36pub enum Position {
37 Stacked(Box<PositionSides>),
39
40 Absolute(Box<PositionSides>),
42 Global(Box<PositionSides>),
44}
45
46impl Default for Position {
47 fn default() -> Self {
48 Self::new_stacked()
49 }
50}
51
52impl Position {
53 pub fn new_absolute() -> Self {
55 Self::Absolute(Box::new(PositionSides {
56 top: None,
57 right: None,
58 bottom: None,
59 left: None,
60 }))
61 }
62
63 pub fn new_global() -> Self {
65 Self::Global(Box::new(PositionSides {
66 top: None,
67 right: None,
68 bottom: None,
69 left: None,
70 }))
71 }
72
73 pub fn new_stacked() -> Self {
75 Self::Stacked(Box::new(PositionSides {
76 top: None,
77 right: None,
78 bottom: None,
79 left: None,
80 }))
81 }
82
83 #[must_use]
85 pub fn top(mut self, value: f32) -> Self {
86 self.position_mut().top = Some(value);
87 self
88 }
89
90 #[must_use]
92 pub fn right(mut self, value: f32) -> Self {
93 self.position_mut().right = Some(value);
94 self
95 }
96
97 #[must_use]
99 pub fn bottom(mut self, value: f32) -> Self {
100 self.position_mut().bottom = Some(value);
101 self
102 }
103
104 #[must_use]
106 pub fn left(mut self, value: f32) -> Self {
107 self.position_mut().left = Some(value);
108 self
109 }
110
111 fn position_mut(&mut self) -> &mut PositionSides {
112 match self {
113 Self::Absolute(position) | Self::Global(position) | Self::Stacked(position) => position,
114 }
115 }
116
117 pub fn is_stacked(&self) -> bool {
118 matches!(self, Self::Stacked { .. })
119 }
120
121 pub fn is_absolute(&self) -> bool {
122 matches!(self, Self::Absolute { .. })
123 }
124
125 pub fn is_global(&self) -> bool {
126 matches!(self, Self::Global { .. })
127 }
128
129 pub(crate) fn get_origin(
130 &self,
131 available_parent_area: &AreaOf<Available>,
132 parent_area: &AreaOf<Parent>,
133 area_size: Size2D,
134 root_area: &Area,
135 ) -> Point2D {
136 match self {
137 Self::Stacked(_) => available_parent_area.origin.cast_unit(),
138 Self::Absolute(absolute_position) => {
139 let PositionSides {
140 top,
141 right,
142 bottom,
143 left,
144 } = &**absolute_position;
145 let y = {
146 let mut y = parent_area.min_y();
147 if let Some(top) = top {
148 y += top;
149 } else if let Some(bottom) = bottom {
150 y = parent_area.max_y() - bottom - area_size.height;
151 }
152 y
153 };
154 let x = {
155 let mut x = parent_area.min_x();
156 if let Some(left) = left {
157 x += left;
158 } else if let Some(right) = right {
159 x = parent_area.max_x() - right - area_size.width;
160 }
161 x
162 };
163 Point2D::new(x, y)
164 }
165 Self::Global(global_position) => {
166 let PositionSides {
167 top,
168 right,
169 bottom,
170 left,
171 } = &**global_position;
172 let y = {
173 let mut y = 0.;
174 if let Some(top) = top {
175 y = *top;
176 } else if let Some(bottom) = bottom {
177 y = root_area.max_y() - bottom - area_size.height;
178 }
179 y
180 };
181 let x = {
182 let mut x = 0.;
183 if let Some(left) = left {
184 x = *left;
185 } else if let Some(right) = right {
186 x = root_area.max_x() - right - area_size.width;
187 }
188 x
189 };
190 Point2D::new(x, y)
191 }
192 }
193 }
194}
195
196impl Scaled for Position {
197 fn scale(&mut self, scale_factor: f32) {
198 match self {
199 Self::Absolute(position) | Self::Global(position) => {
200 if let Some(top) = &mut position.top {
201 *top *= scale_factor;
202 }
203 if let Some(right) = &mut position.right {
204 *right *= scale_factor;
205 }
206 if let Some(bottom) = &mut position.bottom {
207 *bottom *= scale_factor;
208 }
209 if let Some(left) = &mut position.left {
210 *left *= scale_factor;
211 }
212 }
213 Self::Stacked(_) => {}
214 }
215 }
216}
217
218impl Position {
219 pub fn pretty(&self) -> String {
220 match self {
221 Self::Stacked(_) => "stacked".to_string(),
222 Self::Absolute(positions) | Self::Global(positions) => format!(
223 "{}, {}, {}, {}",
224 positions.top.unwrap_or_default(),
225 positions.right.unwrap_or_default(),
226 positions.bottom.unwrap_or_default(),
227 positions.left.unwrap_or_default()
228 ),
229 }
230 }
231}