1use core::fmt;
4
5use crate::{
6 impl_option, impl_vec, impl_vec_clone, impl_vec_debug, impl_vec_mut, impl_vec_partialeq,
7 impl_vec_partialord,
8};
9
10#[derive(Copy, Default, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
12#[repr(C)]
13pub struct LayoutPoint {
14 pub x: isize,
15 pub y: isize,
16}
17
18impl fmt::Debug for LayoutPoint {
19 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20 write!(f, "{}", self)
21 }
22}
23impl fmt::Display for LayoutPoint {
24 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25 write!(f, "({}, {})", self.x, self.y)
26 }
27}
28
29impl LayoutPoint {
30 #[inline(always)]
31 pub const fn new(x: isize, y: isize) -> Self {
32 Self { x, y }
33 }
34 #[inline(always)]
35 pub const fn zero() -> Self {
36 Self::new(0, 0)
37 }
38}
39
40impl_option!(
41 LayoutPoint,
42 OptionLayoutPoint,
43 [Debug, Copy, Clone, PartialEq, PartialOrd]
44);
45
46#[derive(Copy, Default, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
48#[repr(C)]
49pub struct LayoutSize {
50 pub width: isize,
51 pub height: isize,
52}
53
54impl fmt::Debug for LayoutSize {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 write!(f, "{}", self)
57 }
58}
59impl fmt::Display for LayoutSize {
60 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61 write!(f, "{}x{}", self.width, self.height)
62 }
63}
64
65impl LayoutSize {
66 #[inline(always)]
67 pub const fn new(width: isize, height: isize) -> Self {
68 Self { width, height }
69 }
70 #[inline(always)]
71 pub const fn zero() -> Self {
72 Self::new(0, 0)
73 }
74 #[inline]
75 pub fn round(width: f32, height: f32) -> Self {
76 Self {
77 width: libm::roundf(width) as isize,
78 height: libm::roundf(height) as isize,
79 }
80 }
81}
82
83impl_option!(
84 LayoutSize,
85 OptionLayoutSize,
86 [Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash]
87);
88
89#[derive(Copy, Clone, PartialEq, PartialOrd)]
91#[repr(C)]
92pub struct LayoutRect {
93 pub origin: LayoutPoint,
94 pub size: LayoutSize,
95}
96
97impl_option!(
98 LayoutRect,
99 OptionLayoutRect,
100 [Debug, Copy, Clone, PartialEq, PartialOrd]
101);
102impl_vec!(LayoutRect, LayoutRectVec, LayoutRectVecDestructor, LayoutRectVecDestructorType, LayoutRectVecSlice, OptionLayoutRect);
103impl_vec_clone!(LayoutRect, LayoutRectVec, LayoutRectVecDestructor);
104impl_vec_debug!(LayoutRect, LayoutRectVec);
105impl_vec_mut!(LayoutRect, LayoutRectVec);
106impl_vec_partialeq!(LayoutRect, LayoutRectVec);
107impl_vec_partialord!(LayoutRect, LayoutRectVec);
108
109impl fmt::Debug for LayoutRect {
110 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 write!(f, "{}", self)
112 }
113}
114impl fmt::Display for LayoutRect {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "{} @ {}", self.size, self.origin)
117 }
118}
119
120impl LayoutRect {
121 #[inline(always)]
122 pub const fn new(origin: LayoutPoint, size: LayoutSize) -> Self {
123 Self { origin, size }
124 }
125 #[inline(always)]
126 pub const fn zero() -> Self {
127 Self::new(LayoutPoint::zero(), LayoutSize::zero())
128 }
129 #[inline(always)]
130 pub const fn max_x(&self) -> isize {
131 self.origin.x + self.size.width
132 }
133 #[inline(always)]
134 pub const fn min_x(&self) -> isize {
135 self.origin.x
136 }
137 #[inline(always)]
138 pub const fn max_y(&self) -> isize {
139 self.origin.y + self.size.height
140 }
141 #[inline(always)]
142 pub const fn min_y(&self) -> isize {
143 self.origin.y
144 }
145 #[inline(always)]
146 pub const fn width(&self) -> isize {
147 self.max_x() - self.min_x()
148 }
149 #[inline(always)]
150 pub const fn height(&self) -> isize {
151 self.max_y() - self.min_y()
152 }
153
154 pub const fn contains(&self, other: &LayoutPoint) -> bool {
155 self.min_x() <= other.x
156 && other.x < self.max_x()
157 && self.min_y() <= other.y
158 && other.y < self.max_y()
159 }
160
161 pub fn contains_f32(&self, other_x: f32, other_y: f32) -> bool {
162 self.min_x() as f32 <= other_x
163 && other_x < self.max_x() as f32
164 && self.min_y() as f32 <= other_y
165 && other_y < self.max_y() as f32
166 }
167
168 #[inline]
172 pub const fn hit_test(&self, other: &LayoutPoint) -> Option<LayoutPoint> {
173 let dx_left_edge = other.x - self.min_x();
174 let dx_right_edge = self.max_x() - other.x;
175 let dy_top_edge = other.y - self.min_y();
176 let dy_bottom_edge = self.max_y() - other.y;
177 if dx_left_edge > 0 && dx_right_edge > 0 && dy_top_edge > 0 && dy_bottom_edge > 0 {
178 Some(LayoutPoint::new(dx_left_edge, dy_top_edge))
179 } else {
180 None
181 }
182 }
183
184 #[inline]
186 pub fn union<I: Iterator<Item = Self>>(mut rects: I) -> Option<Self> {
187 let first = rects.next()?;
188
189 let mut max_width = first.size.width;
190 let mut max_height = first.size.height;
191 let mut min_x = first.origin.x;
192 let mut min_y = first.origin.y;
193
194 while let Some(Self {
195 origin: LayoutPoint { x, y },
196 size: LayoutSize { width, height },
197 }) = rects.next()
198 {
199 let cur_lower_right_x = x + width;
200 let cur_lower_right_y = y + height;
201 max_width = max_width.max(cur_lower_right_x - min_x);
202 max_height = max_height.max(cur_lower_right_y - min_y);
203 min_x = min_x.min(x);
204 min_y = min_y.min(y);
205 }
206
207 Some(Self {
208 origin: LayoutPoint { x: min_x, y: min_y },
209 size: LayoutSize {
210 width: max_width,
211 height: max_height,
212 },
213 })
214 }
215
216 #[inline]
218 pub fn get_scroll_rect<I: Iterator<Item = Self>>(&self, children: I) -> Option<Self> {
219 let children_union = Self::union(children)?;
220 Self::union([*self, children_union].iter().map(|r| *r))
221 }
222
223 #[inline(always)]
225 pub const fn contains_rect(&self, b: &LayoutRect) -> bool {
226 let a = self;
227
228 let a_x = a.origin.x;
229 let a_y = a.origin.y;
230 let a_width = a.size.width;
231 let a_height = a.size.height;
232
233 let b_x = b.origin.x;
234 let b_y = b.origin.y;
235 let b_width = b.size.width;
236 let b_height = b.size.height;
237
238 b_x >= a_x
239 && b_y >= a_y
240 && b_x + b_width <= a_x + a_width
241 && b_y + b_height <= a_y + a_height
242 }
243}