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))]
23#[derive(PartialEq, Clone, Debug)]
24pub enum Position {
25 Stacked(Box<PositionSides>),
26
27 Absolute(Box<PositionSides>),
28 Global(Box<PositionSides>),
29}
30
31impl Default for Position {
32 fn default() -> Self {
33 Self::new_stacked()
34 }
35}
36
37impl Position {
38 pub fn new_absolute() -> Self {
39 Self::Absolute(Box::new(PositionSides {
40 top: None,
41 right: None,
42 bottom: None,
43 left: None,
44 }))
45 }
46
47 pub fn new_global() -> Self {
48 Self::Global(Box::new(PositionSides {
49 top: None,
50 right: None,
51 bottom: None,
52 left: None,
53 }))
54 }
55
56 pub fn new_stacked() -> Self {
57 Self::Stacked(Box::new(PositionSides {
58 top: None,
59 right: None,
60 bottom: None,
61 left: None,
62 }))
63 }
64
65 #[must_use]
66 pub fn top(mut self, value: f32) -> Self {
67 self.position_mut().top = Some(value);
68 self
69 }
70
71 #[must_use]
72 pub fn right(mut self, value: f32) -> Self {
73 self.position_mut().right = Some(value);
74 self
75 }
76
77 #[must_use]
78 pub fn bottom(mut self, value: f32) -> Self {
79 self.position_mut().bottom = Some(value);
80 self
81 }
82
83 #[must_use]
84 pub fn left(mut self, value: f32) -> Self {
85 self.position_mut().left = Some(value);
86 self
87 }
88
89 fn position_mut(&mut self) -> &mut PositionSides {
90 match self {
91 Self::Absolute(position) | Self::Global(position) | Self::Stacked(position) => position,
92 }
93 }
94
95 pub fn is_stacked(&self) -> bool {
96 matches!(self, Self::Stacked { .. })
97 }
98
99 pub fn is_absolute(&self) -> bool {
100 matches!(self, Self::Absolute { .. })
101 }
102
103 pub fn is_global(&self) -> bool {
104 matches!(self, Self::Global { .. })
105 }
106
107 pub(crate) fn get_origin(
108 &self,
109 available_parent_area: &AreaOf<Available>,
110 parent_area: &AreaOf<Parent>,
111 area_size: Size2D,
112 root_area: &Area,
113 ) -> Point2D {
114 match self {
115 Self::Stacked(_) => available_parent_area.origin.cast_unit(),
116 Self::Absolute(absolute_position) => {
117 let PositionSides {
118 top,
119 right,
120 bottom,
121 left,
122 } = &**absolute_position;
123 let y = {
124 let mut y = parent_area.min_y();
125 if let Some(top) = top {
126 y += top;
127 } else if let Some(bottom) = bottom {
128 y = parent_area.max_y() - bottom - area_size.height;
129 }
130 y
131 };
132 let x = {
133 let mut x = parent_area.min_x();
134 if let Some(left) = left {
135 x += left;
136 } else if let Some(right) = right {
137 x = parent_area.max_x() - right - area_size.width;
138 }
139 x
140 };
141 Point2D::new(x, y)
142 }
143 Self::Global(global_position) => {
144 let PositionSides {
145 top,
146 right,
147 bottom,
148 left,
149 } = &**global_position;
150 let y = {
151 let mut y = 0.;
152 if let Some(top) = top {
153 y = *top;
154 } else if let Some(bottom) = bottom {
155 y = root_area.max_y() - bottom - area_size.height;
156 }
157 y
158 };
159 let x = {
160 let mut x = 0.;
161 if let Some(left) = left {
162 x = *left;
163 } else if let Some(right) = right {
164 x = root_area.max_x() - right - area_size.width;
165 }
166 x
167 };
168 Point2D::new(x, y)
169 }
170 }
171 }
172}
173
174impl Scaled for Position {
175 fn scale(&mut self, scale_factor: f32) {
176 match self {
177 Self::Absolute(position) | Self::Global(position) => {
178 if let Some(top) = &mut position.top {
179 *top *= scale_factor;
180 }
181 if let Some(right) = &mut position.right {
182 *right *= scale_factor;
183 }
184 if let Some(bottom) = &mut position.bottom {
185 *bottom *= scale_factor;
186 }
187 if let Some(left) = &mut position.left {
188 *left *= scale_factor;
189 }
190 }
191 Self::Stacked(_) => {}
192 }
193 }
194}
195
196impl Position {
197 pub fn pretty(&self) -> String {
198 match self {
199 Self::Stacked(_) => "stacked".to_string(),
200 Self::Absolute(positions) | Self::Global(positions) => format!(
201 "{}, {}, {}, {}",
202 positions.top.unwrap_or_default(),
203 positions.right.unwrap_or_default(),
204 positions.bottom.unwrap_or_default(),
205 positions.left.unwrap_or_default()
206 ),
207 }
208 }
209}