hyperchad_transformer/layout/
mod.rs1use std::sync::atomic::AtomicU16;
8
9use crate::Container;
10
11pub mod calc;
13pub mod font;
15
16pub static EPSILON: f32 = 0.001;
20static SCROLLBAR_SIZE: AtomicU16 = AtomicU16::new(16);
21
22#[macro_export]
35macro_rules! float_eq {
36 ($a:expr, $b:expr $(,)?) => {{ ($a - $b).abs() < $crate::layout::EPSILON }};
37}
38
39#[macro_export]
51macro_rules! float_lt {
52 ($a:expr, $b:expr $(,)?) => {{ $a - $b <= -$crate::layout::EPSILON }};
53}
54
55#[macro_export]
67macro_rules! float_lte {
68 ($a:expr, $b:expr $(,)?) => {{ $a - $b < $crate::layout::EPSILON }};
69}
70
71#[macro_export]
83macro_rules! float_gt {
84 ($a:expr, $b:expr $(,)?) => {{ $a - $b >= $crate::layout::EPSILON }};
85}
86
87#[macro_export]
99macro_rules! float_gte {
100 ($a:expr, $b:expr $(,)?) => {{ $a - $b > -$crate::layout::EPSILON }};
101}
102
103#[macro_export]
113macro_rules! min_float {
114 ($a:expr, $b:expr $(,)?) => {{ if $a <= $b { $a } else { $b } }};
115}
116
117#[macro_export]
127macro_rules! max_float {
128 ($a:expr, $b:expr $(,)?) => {{ if $a > $b { $a } else { $b } }};
129}
130
131#[must_use]
133pub fn get_scrollbar_size() -> u16 {
134 SCROLLBAR_SIZE.load(std::sync::atomic::Ordering::SeqCst)
135}
136
137pub fn set_scrollbar_size(size: u16) {
139 SCROLLBAR_SIZE.store(size, std::sync::atomic::Ordering::SeqCst);
140}
141
142pub trait Calc {
144 fn calc(&self, container: &mut Container) -> bool;
148}
149
150#[derive(Clone, Copy, Default)]
152pub struct Rect {
153 pub x: f32,
155 pub y: f32,
157 pub width: f32,
159 pub height: f32,
161}
162
163#[allow(clippy::trivially_copy_pass_by_ref)]
164#[inline]
165#[must_use]
166pub(crate) fn order_float(a: &f32, b: &f32) -> std::cmp::Ordering {
167 if a > b {
168 std::cmp::Ordering::Greater
169 } else if a < b {
170 std::cmp::Ordering::Less
171 } else {
172 std::cmp::Ordering::Equal
173 }
174}
175
176pub fn increase_opt(opt: &mut Option<f32>, value: f32) -> f32 {
180 if let Some(existing) = *opt {
181 opt.replace(existing + value);
182 existing + value
183 } else {
184 opt.replace(value);
185 value
186 }
187}
188
189pub fn set_value<T: PartialEq + Copy>(opt: &mut Option<T>, value: T) -> Option<T> {
193 if let Some(existing) = *opt {
194 if existing != value {
195 *opt = Some(value);
196 return *opt;
197 }
198 } else {
199 *opt = Some(value);
200 return *opt;
201 }
202
203 None
204}
205
206pub fn set_float(opt: &mut Option<f32>, value: f32) -> Option<f32> {
211 if let Some(existing) = *opt {
212 if !float_eq!(existing, value) {
213 *opt = Some(value);
214 return *opt;
215 }
216 } else {
217 *opt = Some(value);
218 return *opt;
219 }
220
221 None
222}
223
224#[cfg(test)]
225mod tests {
226 use serial_test::serial;
227
228 use super::*;
229
230 #[test_log::test]
231 #[serial(scrollbar_size)]
232 fn get_and_set_scrollbar_size_updates_global_value() {
233 let original = get_scrollbar_size();
234
235 set_scrollbar_size(42);
236 assert_eq!(get_scrollbar_size(), 42);
237
238 set_scrollbar_size(100);
239 assert_eq!(get_scrollbar_size(), 100);
240
241 set_scrollbar_size(original);
243 }
244
245 #[test_log::test]
246 fn order_float_returns_greater_when_a_greater_than_b() {
247 assert_eq!(order_float(&2.0, &1.0), std::cmp::Ordering::Greater);
248 assert_eq!(order_float(&100.5, &100.4), std::cmp::Ordering::Greater);
249 }
250
251 #[test_log::test]
252 fn order_float_returns_less_when_a_less_than_b() {
253 assert_eq!(order_float(&1.0, &2.0), std::cmp::Ordering::Less);
254 assert_eq!(order_float(&-5.0, &0.0), std::cmp::Ordering::Less);
255 }
256
257 #[test_log::test]
258 fn order_float_returns_equal_when_values_equal() {
259 assert_eq!(order_float(&1.0, &1.0), std::cmp::Ordering::Equal);
260 assert_eq!(order_float(&0.0, &0.0), std::cmp::Ordering::Equal);
261 }
262
263 #[test_log::test]
264 fn increase_opt_sets_value_when_none() {
265 let mut opt: Option<f32> = None;
266 let result = increase_opt(&mut opt, 10.0);
267
268 assert!((result - 10.0).abs() < f32::EPSILON);
269 assert!((opt.unwrap() - 10.0).abs() < f32::EPSILON);
270 }
271
272 #[test_log::test]
273 fn increase_opt_adds_to_existing_value() {
274 let mut opt: Option<f32> = Some(5.0);
275 let result = increase_opt(&mut opt, 10.0);
276
277 assert!((result - 15.0).abs() < f32::EPSILON);
278 assert!((opt.unwrap() - 15.0).abs() < f32::EPSILON);
279 }
280
281 #[test_log::test]
282 fn increase_opt_handles_negative_values() {
283 let mut opt: Option<f32> = Some(10.0);
284 let result = increase_opt(&mut opt, -3.0);
285
286 assert!((result - 7.0).abs() < f32::EPSILON);
287 assert!((opt.unwrap() - 7.0).abs() < f32::EPSILON);
288 }
289
290 #[test_log::test]
291 fn increase_opt_accumulates_multiple_calls() {
292 let mut opt: Option<f32> = None;
293
294 increase_opt(&mut opt, 5.0);
295 increase_opt(&mut opt, 3.0);
296 let result = increase_opt(&mut opt, 2.0);
297
298 assert!((result - 10.0).abs() < f32::EPSILON);
299 assert!((opt.unwrap() - 10.0).abs() < f32::EPSILON);
300 }
301
302 #[test_log::test]
303 fn set_value_sets_when_none_and_returns_new_value() {
304 let mut opt: Option<i32> = None;
305 let result = set_value(&mut opt, 42);
306
307 assert_eq!(result, Some(42));
308 assert_eq!(opt, Some(42));
309 }
310
311 #[test_log::test]
312 fn set_value_updates_when_different_value() {
313 let mut opt: Option<i32> = Some(10);
314 let result = set_value(&mut opt, 42);
315
316 assert_eq!(result, Some(42));
317 assert_eq!(opt, Some(42));
318 }
319
320 #[test_log::test]
321 fn set_value_returns_none_when_same_value() {
322 let mut opt: Option<i32> = Some(42);
323 let result = set_value(&mut opt, 42);
324
325 assert_eq!(result, None);
326 assert_eq!(opt, Some(42));
327 }
328
329 #[test_log::test]
330 fn set_float_sets_when_none_and_returns_new_value() {
331 let mut opt: Option<f32> = None;
332 let result = set_float(&mut opt, 7.25);
333
334 assert!(result.is_some());
335 assert!((result.unwrap() - 7.25).abs() < f32::EPSILON);
336 assert!((opt.unwrap() - 7.25).abs() < f32::EPSILON);
337 }
338
339 #[test_log::test]
340 fn set_float_updates_when_significantly_different() {
341 let mut opt: Option<f32> = Some(1.0);
342 let result = set_float(&mut opt, 2.0);
343
344 assert!(result.is_some());
345 assert!((result.unwrap() - 2.0).abs() < f32::EPSILON);
346 assert!((opt.unwrap() - 2.0).abs() < f32::EPSILON);
347 }
348
349 #[test_log::test]
350 fn set_float_returns_none_when_within_epsilon() {
351 let mut opt: Option<f32> = Some(1.0);
352 let result = set_float(&mut opt, 1.0005);
354
355 assert!(result.is_none());
356 assert!((opt.unwrap() - 1.0).abs() < f32::EPSILON);
358 }
359
360 #[test_log::test]
361 fn set_float_updates_when_just_outside_epsilon() {
362 let mut opt: Option<f32> = Some(1.0);
363 let result = set_float(&mut opt, 1.002);
365
366 assert!(result.is_some());
367 assert!((opt.unwrap() - 1.002).abs() < f32::EPSILON);
368 }
369}