torin/values/
position.rs

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}