reverie_engine_opengl/gui/
layout.rs

1use std::fmt::Debug;
2
3/// 長方形の領域
4#[derive(Debug, PartialEq, Eq)]
5pub struct Rect<T, U>
6where
7    T: PartialEq,
8    U: PartialEq,
9{
10    origin_x: T,
11    origin_y: T,
12    width: U,
13    height: U,
14}
15
16/// 長方形の領域のうち、レイアウトの基準とする部分
17#[derive(Debug, PartialEq, Eq)]
18pub enum Origin {
19    TopLeft,
20    Top,
21    TopRight,
22    Left,
23    Center,
24    Right,
25    BottomLeft,
26    Bottom,
27    BottomRight,
28}
29
30impl Origin {
31    pub const fn x_diff(&self, width: u32) -> i32 {
32        use Origin::*;
33        match *self {
34            TopLeft | Left | BottomLeft => 0,
35            Top | Center | Bottom => width as i32 / 2,
36            TopRight | Right | BottomRight => width as i32,
37        }
38    }
39
40    pub const fn y_diff(&self, height: u32) -> i32 {
41        use Origin::*;
42        match *self {
43            TopLeft | Top | TopRight => 0,
44            Left | Center | Right => height as i32 / 2,
45            BottomLeft | Bottom | BottomRight => height as i32,
46        }
47    }
48}
49
50#[derive(Debug)]
51pub enum Position<T> {
52    Positive(T),
53    Center(T),
54    Negative(T),
55}
56
57impl Position<i32> {
58    pub const fn actual_value(&self, max: i32) -> i32 {
59        match *self {
60            Self::Positive(distance) => distance,
61            Self::Center(distance) => max / 2_i32 + distance,
62            Self::Negative(distance) => max - distance,
63        }
64    }
65}
66
67impl<T, U> Rect<T, U>
68where
69    T: PartialEq,
70    U: PartialEq,
71{
72    pub const fn new(origin_x: T, origin_y: T, width: U, height: U) -> Self {
73        Self {
74            origin_x,
75            origin_y,
76            width,
77            height,
78        }
79    }
80
81    pub const fn origin_x(&self) -> &T {
82        &self.origin_x
83    }
84
85    pub const fn origin_y(&self) -> &T {
86        &self.origin_y
87    }
88
89    pub const fn width(&self) -> &U {
90        &self.width
91    }
92
93    pub const fn height(&self) -> &U {
94        &self.height
95    }
96}
97
98impl Rect<i32, u32> {
99    pub const fn new_const(origin_x: i32, origin_y: i32, width: u32, height: u32) -> Self {
100        Self {
101            origin_x,
102            origin_y,
103            width,
104            height,
105        }
106    }
107
108    pub fn new_in_rect(
109        outer: &Self,
110        origin: &Origin,
111        position_x: &Position<i32>,
112        position_y: &Position<i32>,
113        inner_width: u32,
114        inner_height: u32,
115    ) -> Self {
116        let x = outer.origin_x() + position_x.actual_value(*outer.width() as i32)
117            - origin.x_diff(inner_width);
118        let y = outer.origin_y() + position_y.actual_value(*outer.height() as i32)
119            - origin.y_diff(inner_height);
120        Self {
121            origin_x: x,
122            origin_y: y,
123            width: inner_width,
124            height: inner_height,
125        }
126    }
127
128    pub fn new_biggest_in_rect(outer: &Self, inner_width: u32, inner_height: u32) -> Self {
129        let outer_aspect: f32 = *outer.width() as f32 / *outer.height() as f32;
130        let inner_aspect: f32 = inner_width as f32 / inner_height as f32;
131
132        let (big_inner_width, big_inner_height) = if outer_aspect >= inner_aspect
133        /* 外枠のRectの方が横に長い */
134        {
135            let width = (*outer.height() as f32 * inner_aspect) as u32;
136            let height = *outer.height();
137            (width, height)
138        } else
139        /* 外枠のRectの方が縦に長い */
140        {
141            let width = *outer.width();
142            let height = (*outer.width() as f32 / inner_aspect) as u32;
143            (width, height)
144        };
145
146        Self::new_in_rect(
147            outer,
148            &Origin::Center,
149            &Position::Center(0),
150            &Position::Center(0),
151            big_inner_width,
152            big_inner_height,
153        )
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    #[test]
162    fn origin_x_diff() {
163        assert_eq!(Origin::TopLeft.x_diff(10), 0);
164        assert_eq!(Origin::Center.x_diff(10), 5);
165        assert_eq!(Origin::BottomRight.x_diff(10), 10);
166    }
167
168    #[test]
169    fn origin_y_diff() {
170        assert_eq!(Origin::TopLeft.y_diff(10), 0);
171        assert_eq!(Origin::Center.y_diff(10), 5);
172        assert_eq!(Origin::BottomRight.y_diff(10), 10);
173    }
174
175    #[test]
176    fn position_positive() {
177        assert_eq!(Position::Positive(10).actual_value(100), 10);
178        assert_eq!(Position::Positive(0).actual_value(100), 0);
179    }
180
181    #[test]
182    fn position_center() {
183        assert_eq!(Position::Center(10).actual_value(100), 60);
184        assert_eq!(Position::Center(-10).actual_value(100), 40);
185        assert_eq!(Position::Center(0).actual_value(100), 50);
186    }
187
188    #[test]
189    fn position_negative() {
190        assert_eq!(Position::Negative(10).actual_value(100), 90);
191        assert_eq!(Position::Negative(0).actual_value(100), 100);
192    }
193
194    #[test]
195    fn new_rect_in_rect_topleft() {
196        let outer = Rect::<i32, u32>::new(0, 0, 400, 300);
197        let rect = Rect::new_in_rect(
198            &outer,
199            &Origin::TopLeft,
200            &Position::Positive(10),
201            &Position::Positive(20),
202            100,
203            100,
204        );
205
206        assert_eq!(rect, Rect::new(10, 20, 100, 100));
207    }
208
209    #[test]
210    fn new_rect_in_rect_center() {
211        let outer = Rect::<i32, u32>::new(0, 0, 400, 300);
212        let rect = Rect::new_in_rect(
213            &outer,
214            &Origin::Center,
215            &Position::Center(0),
216            &Position::Center(0),
217            100,
218            100,
219        );
220
221        assert_eq!(rect, Rect::new(150, 100, 100, 100));
222    }
223}