1 use crate::Vec2;
2 use crate::XY;
3 use std::cmp::min;
4 
5 /// Location of the view on screen
6 pub type Position = XY<Offset>;
7 
8 impl Position {
9     /// Returns a position centered on both axis.
center() -> Self10     pub fn center() -> Self {
11         Position::new(Offset::Center, Offset::Center)
12     }
13 
14     /// Returns a position absolute on both axis.
absolute<T: Into<Vec2>>(offset: T) -> Self15     pub fn absolute<T: Into<Vec2>>(offset: T) -> Self {
16         let offset = offset.into();
17         Position::new(Offset::Absolute(offset.x), Offset::Absolute(offset.y))
18     }
19 
20     /// Returns a position relative to the parent on both axis.
parent<T: Into<XY<isize>>>(offset: T) -> Self21     pub fn parent<T: Into<XY<isize>>>(offset: T) -> Self {
22         let offset = offset.into();
23         Position::new(Offset::Parent(offset.x), Offset::Parent(offset.y))
24     }
25 
26     /// Computes the offset required to draw a view.
27     ///
28     /// When drawing a view with `size` in a container with `available`,
29     /// and a parent with the absolute coordinates `parent`, drawing the
30     /// child with its top-left corner at the returned coordinates will
31     /// position him appropriately.
compute_offset<S, A, P>( &self, size: S, available: A, parent: P, ) -> Vec2 where S: Into<Vec2>, A: Into<Vec2>, P: Into<Vec2>,32     pub fn compute_offset<S, A, P>(
33         &self,
34         size: S,
35         available: A,
36         parent: P,
37     ) -> Vec2
38     where
39         S: Into<Vec2>,
40         A: Into<Vec2>,
41         P: Into<Vec2>,
42     {
43         let available = available.into();
44         let size = size.into();
45         let parent = parent.into();
46 
47         Vec2::new(
48             self.x.compute_offset(size.x, available.x, parent.x),
49             self.y.compute_offset(size.y, available.y, parent.y),
50         )
51     }
52 }
53 
54 /// Single-dimensional offset policy.
55 #[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
56 pub enum Offset {
57     /// In the center of the screen
58     Center,
59     /// Place top-left corner at the given absolute coordinates
60     Absolute(usize),
61 
62     /// Offset from the previous layer's top-left corner.
63     ///
64     /// If this is the first layer, behaves like `Absolute`.
65     Parent(isize),
66 }
67 
68 impl Offset {
69     /// Computes a single-dimension offset requred to draw a view.
compute_offset( &self, size: usize, available: usize, parent: usize, ) -> usize70     pub fn compute_offset(
71         &self,
72         size: usize,
73         available: usize,
74         parent: usize,
75     ) -> usize {
76         if size > available {
77             0
78         } else {
79             match *self {
80                 Offset::Center => (available - size) / 2,
81                 Offset::Absolute(offset) => min(offset, available - size),
82                 Offset::Parent(offset) => {
83                     min((parent as isize + offset) as usize, available - size)
84                 }
85             }
86         }
87     }
88 }
89 
90 #[cfg(test)]
91 mod tests {
92 
93     use super::Position;
94     use crate::Vec2;
95 
96     #[test]
test_center()97     fn test_center() {
98         let c = Position::center();
99         assert_eq!(Vec2::new(2, 1), c.compute_offset((1, 1), (5, 3), (0, 0)));
100         assert_eq!(Vec2::new(2, 0), c.compute_offset((1, 3), (5, 3), (0, 0)));
101         assert_eq!(Vec2::new(1, 1), c.compute_offset((3, 1), (5, 3), (0, 0)));
102         assert_eq!(Vec2::new(0, 1), c.compute_offset((5, 1), (5, 3), (0, 0)));
103         assert_eq!(Vec2::new(0, 0), c.compute_offset((5, 3), (5, 3), (0, 0)));
104         assert_eq!(Vec2::new(0, 0), c.compute_offset((5, 3), (3, 1), (0, 0)));
105     }
106 }
107