1use std::fmt;
4
5#[doc(no_inline)]
7pub use zng_unit::*;
8
9mod alignment;
10pub use alignment::*;
11
12mod constraints;
13pub use constraints::*;
14
15mod factor;
16pub use factor::*;
17
18mod grid;
19pub use grid::*;
20
21mod length;
22pub use length::*;
23
24mod line;
25pub use line::*;
26
27mod point;
28pub use point::*;
29
30mod rect;
31pub use rect::*;
32
33mod side_offsets;
34pub use side_offsets::*;
35
36mod size;
37pub use size::*;
38
39mod transform;
40pub use transform::*;
41
42mod vector;
43pub use vector::*;
44
45use crate::context::LayoutMask;
46
47macro_rules! impl_length_comp_conversions {
49 ($(
50 $(#[$docs:meta])*
51 fn from($($n:ident : $N:ident),+) -> $For:ty {
52 $convert:expr
53 }
54 )+) => {
55 $(
56 impl<$($N),+> From<($($N),+)> for $For
57 where
58 $($N: Into<Length>,)+
59 {
60 $(#[$docs])*
61 fn from(($($n),+) : ($($N),+)) -> Self {
62 $convert
63 }
64 }
65
66 impl<$($N),+> zng_var::IntoVar<$For> for ($($N),+)
67 where
68 $($N: Into<Length> + Clone,)+
69 {
70 $(#[$docs])*
71 fn into_var(self) -> zng_var::Var<$For> {
72 zng_var::const_var(self.into())
73 }
74 }
75 )+
76 };
77}
78use impl_length_comp_conversions;
79
80pub trait Layout2d {
84 type Px: Default;
86
87 fn layout(&self) -> Self::Px {
91 self.layout_dft(Default::default())
92 }
93
94 fn layout_dft(&self, default: Self::Px) -> Self::Px;
98
99 fn affect_mask(&self) -> LayoutMask;
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
107pub enum LayoutAxis {
108 X,
110 Y,
112 Z,
114}
115
116pub trait Layout1d {
120 fn layout(&self, axis: LayoutAxis) -> Px {
124 self.layout_dft(axis, Px(0))
125 }
126
127 fn layout_dft(&self, axis: LayoutAxis, default: Px) -> Px;
131
132 fn layout_x(&self) -> Px {
136 self.layout(LayoutAxis::X)
137 }
138
139 fn layout_y(&self) -> Px {
143 self.layout(LayoutAxis::Y)
144 }
145
146 fn layout_z(&self) -> Px {
150 self.layout(LayoutAxis::Z)
151 }
152
153 fn layout_dft_x(&self, default: Px) -> Px {
157 self.layout_dft(LayoutAxis::X, default)
158 }
159
160 fn layout_dft_y(&self, default: Px) -> Px {
164 self.layout_dft(LayoutAxis::Y, default)
165 }
166
167 fn layout_dft_z(&self, default: Px) -> Px {
171 self.layout_dft(LayoutAxis::Z, default)
172 }
173
174 fn layout_f32(&self, axis: LayoutAxis) -> f32 {
178 self.layout_f32_dft(axis, 0.0)
179 }
180
181 fn layout_f32_dft(&self, axis: LayoutAxis, default: f32) -> f32;
185
186 fn layout_f32_x(&self) -> f32 {
190 self.layout_f32(LayoutAxis::X)
191 }
192
193 fn layout_f32_y(&self) -> f32 {
197 self.layout_f32(LayoutAxis::Y)
198 }
199
200 fn layout_f32_z(&self) -> f32 {
204 self.layout_f32(LayoutAxis::Z)
205 }
206
207 fn layout_f32_dft_x(&self, default: f32) -> f32 {
211 self.layout_f32_dft(LayoutAxis::X, default)
212 }
213
214 fn layout_f32_dft_y(&self, default: f32) -> f32 {
218 self.layout_f32_dft(LayoutAxis::Y, default)
219 }
220
221 fn layout_f32_dft_z(&self, default: f32) -> f32 {
225 self.layout_f32_dft(LayoutAxis::Z, default)
226 }
227
228 fn affect_mask(&self) -> LayoutMask;
232}
233
234#[derive(Debug)]
236#[non_exhaustive]
237pub enum ParseFloatCompositeError {
238 Component(std::num::ParseFloatError),
240 MissingComponent,
242 ExtraComponent,
244 UnknownFormat,
246}
247impl fmt::Display for ParseFloatCompositeError {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 match self {
250 ParseFloatCompositeError::Component(e) => write!(f, "error parsing component, {e}"),
251 ParseFloatCompositeError::MissingComponent => write!(f, "missing component"),
252 ParseFloatCompositeError::ExtraComponent => write!(f, "extra component"),
253 ParseFloatCompositeError::UnknownFormat => write!(f, "unknown format"),
254 }
255 }
256}
257impl std::error::Error for ParseFloatCompositeError {
258 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
259 if let ParseFloatCompositeError::Component(e) = self {
260 Some(e)
261 } else {
262 None
263 }
264 }
265}
266impl From<std::num::ParseFloatError> for ParseFloatCompositeError {
267 fn from(value: std::num::ParseFloatError) -> Self {
268 ParseFloatCompositeError::Component(value)
269 }
270}
271
272#[derive(Debug)]
274#[non_exhaustive]
275pub enum ParseCompositeError {
276 FloatComponent(std::num::ParseFloatError),
278 IntComponent(std::num::ParseIntError),
280 MissingComponent,
282 ExtraComponent,
284 UnknownFormat,
286}
287impl fmt::Display for ParseCompositeError {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 match self {
290 ParseCompositeError::FloatComponent(e) => write!(f, "error parsing component, {e}"),
291 ParseCompositeError::IntComponent(e) => write!(f, "error parsing component, {e}"),
292 ParseCompositeError::MissingComponent => write!(f, "missing component"),
293 ParseCompositeError::ExtraComponent => write!(f, "extra component"),
294 ParseCompositeError::UnknownFormat => write!(f, "unknown format"),
295 }
296 }
297}
298impl std::error::Error for ParseCompositeError {
299 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
300 if let ParseCompositeError::FloatComponent(e) = self {
301 Some(e)
302 } else if let ParseCompositeError::IntComponent(e) = self {
303 Some(e)
304 } else {
305 None
306 }
307 }
308}
309impl From<std::num::ParseFloatError> for ParseCompositeError {
310 fn from(value: std::num::ParseFloatError) -> Self {
311 ParseCompositeError::FloatComponent(value)
312 }
313}
314impl From<std::num::ParseIntError> for ParseCompositeError {
315 fn from(value: std::num::ParseIntError) -> Self {
316 ParseCompositeError::IntComponent(value)
317 }
318}
319impl From<ParseFloatCompositeError> for ParseCompositeError {
320 fn from(value: ParseFloatCompositeError) -> Self {
321 match value {
322 ParseFloatCompositeError::Component(e) => ParseCompositeError::FloatComponent(e),
323 ParseFloatCompositeError::MissingComponent => ParseCompositeError::MissingComponent,
324 ParseFloatCompositeError::ExtraComponent => ParseCompositeError::ExtraComponent,
325 ParseFloatCompositeError::UnknownFormat => ParseCompositeError::UnknownFormat,
326 }
327 }
328}
329impl From<ParseIntCompositeError> for ParseCompositeError {
330 fn from(value: ParseIntCompositeError) -> Self {
331 match value {
332 ParseIntCompositeError::Component(e) => ParseCompositeError::IntComponent(e),
333 ParseIntCompositeError::MissingComponent => ParseCompositeError::MissingComponent,
334 ParseIntCompositeError::ExtraComponent => ParseCompositeError::ExtraComponent,
335 ParseIntCompositeError::UnknownFormat => ParseCompositeError::UnknownFormat,
336 _ => unreachable!(),
337 }
338 }
339}
340
341pub(crate) struct LengthCompositeParser<'a> {
342 sep: &'a [char],
343 s: &'a str,
344}
345impl<'a> LengthCompositeParser<'a> {
346 pub(crate) fn new(s: &'a str) -> Result<LengthCompositeParser<'a>, ParseCompositeError> {
347 Self::new_sep(s, &[','])
348 }
349 pub(crate) fn new_sep(s: &'a str, sep: &'a [char]) -> Result<LengthCompositeParser<'a>, ParseCompositeError> {
350 if let Some(s) = s.strip_prefix('(') {
351 if let Some(s) = s.strip_suffix(')') {
352 return Ok(Self { s, sep });
353 } else {
354 return Err(ParseCompositeError::MissingComponent);
355 }
356 }
357 Ok(Self { s, sep })
358 }
359
360 pub(crate) fn next(&mut self) -> Result<Length, ParseCompositeError> {
361 let mut depth = 0;
362 for (ci, c) in self.s.char_indices() {
363 if depth == 0
364 && let Some(sep) = self.sep.iter().find(|s| **s == c)
365 {
366 let l = &self.s[..ci];
367 self.s = &self.s[ci + sep.len_utf8()..];
368 return l.trim().parse();
369 } else if c == '(' {
370 depth += 1;
371 } else if c == ')' {
372 depth -= 1;
373 }
374 }
375 if self.s.is_empty() {
376 Err(ParseCompositeError::MissingComponent)
377 } else {
378 let l = self.s;
379 self.s = "";
380 l.trim().parse()
381 }
382 }
383
384 pub fn has_ended(&self) -> bool {
385 self.s.is_empty()
386 }
387
388 pub(crate) fn expect_last(mut self) -> Result<Length, ParseCompositeError> {
389 let c = self.next()?;
390 if !self.has_ended() {
391 Err(ParseCompositeError::ExtraComponent)
392 } else {
393 Ok(c)
394 }
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use std::f32::consts::{PI, TAU};
401
402 use zng_app_context::{AppId, LocalContext};
403
404 use crate::context::{LAYOUT, LayoutMetrics};
405
406 use super::*;
407
408 #[test]
409 pub fn zero() {
410 all_equal(0.rad(), 0.grad(), 0.deg(), 0.turn());
411 }
412
413 #[test]
414 pub fn half_circle() {
415 all_equal(PI.rad(), 200.grad(), 180.deg(), 0.5.turn())
416 }
417
418 #[test]
419 pub fn full_circle() {
420 all_equal(TAU.rad(), 400.grad(), 360.deg(), 1.turn())
421 }
422
423 #[test]
424 pub fn one_and_a_half_circle() {
425 all_equal((TAU + PI).rad(), 600.grad(), 540.deg(), 1.5.turn())
426 }
427
428 #[test]
429 pub fn modulo_rad() {
430 assert_eq!(PI.rad(), (TAU + PI).rad().modulo());
431 }
432
433 #[test]
434 pub fn modulo_grad() {
435 assert_eq!(200.grad(), 600.grad().modulo());
436 }
437
438 #[test]
439 pub fn modulo_deg() {
440 assert_eq!(180.deg(), 540.deg().modulo());
441 }
442
443 #[test]
444 pub fn modulo_turn() {
445 assert_eq!(0.5.turn(), 1.5.turn().modulo());
446 }
447
448 #[test]
449 pub fn length_expr_same_unit() {
450 let a = Length::from(200);
451 let b = Length::from(300);
452 let c = a + b;
453
454 assert_eq!(c, 500.dip());
455 }
456
457 #[test]
458 pub fn length_expr_diff_units() {
459 let a = Length::from(200);
460 let b = Length::from(10.pct());
461 let c = a + b;
462
463 assert_eq!(c, Length::Expr(Box::new(LengthExpr::Add(200.into(), 10.pct().into()))))
464 }
465
466 #[test]
467 pub fn length_expr_eval() {
468 let _app = LocalContext::start_app(AppId::new_unique());
469
470 let l = (Length::from(200) - 100.pct()).abs();
471 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(600), Px(400)), Px(0));
472 let l = LAYOUT.with_context(metrics, || l.layout_x());
473
474 assert_eq!(l.0, (200i32 - 600i32).abs());
475 }
476
477 #[test]
478 pub fn length_expr_clamp() {
479 let _app = LocalContext::start_app(AppId::new_unique());
480
481 let l = Length::from(100.pct()).clamp(100, 500);
482 assert!(matches!(l, Length::Expr(_)));
483
484 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(200), Px(50)), Px(0));
485 LAYOUT.with_context(metrics, || {
486 let r = l.layout_x();
487 assert_eq!(r.0, 200);
488
489 let r = l.layout_y();
490 assert_eq!(r.0, 100);
491
492 LAYOUT.with_constraints(LAYOUT.constraints().with_new_max_x(Px(550)), || {
493 let r = l.layout_x();
494 assert_eq!(r.0, 500);
495 });
496 });
497 }
498
499 fn all_equal(rad: AngleRadian, grad: AngleGradian, deg: AngleDegree, turn: AngleTurn) {
500 assert_eq!(rad, AngleRadian::from(grad));
501 assert_eq!(rad, AngleRadian::from(deg));
502 assert_eq!(rad, AngleRadian::from(turn));
503
504 assert_eq!(grad, AngleGradian::from(rad));
505 assert_eq!(grad, AngleGradian::from(deg));
506 assert_eq!(grad, AngleGradian::from(turn));
507
508 assert_eq!(deg, AngleDegree::from(rad));
509 assert_eq!(deg, AngleDegree::from(grad));
510 assert_eq!(deg, AngleDegree::from(turn));
511
512 assert_eq!(turn, AngleTurn::from(rad));
513 assert_eq!(turn, AngleTurn::from(grad));
514 assert_eq!(turn, AngleTurn::from(deg));
515 }
516
517 #[test]
518 fn distance_bounds() {
519 assert_eq!(DistanceKey::MAX.distance(), Some(Px::MAX));
520 assert_eq!(DistanceKey::MIN.distance(), Some(Px(0)));
521 }
522
523 #[test]
524 fn orientation_box_above() {
525 let a = PxRect::from_size(PxSize::splat(Px(40)));
526 let mut b = a;
527 b.origin.y = -Px(82);
528 let a = a.to_box2d();
529 let b = b.to_box2d();
530
531 assert!(Orientation2D::Above.box_is(a, b));
532 assert!(!Orientation2D::Below.box_is(a, b));
533 assert!(!Orientation2D::Left.box_is(a, b));
534 assert!(!Orientation2D::Right.box_is(a, b));
535 }
536
537 #[test]
538 fn orientation_box_below() {
539 let a = PxRect::from_size(PxSize::splat(Px(40)));
540 let mut b = a;
541 b.origin.y = Px(42);
542 let a = a.to_box2d();
543 let b = b.to_box2d();
544
545 assert!(!Orientation2D::Above.box_is(a, b));
546 assert!(Orientation2D::Below.box_is(a, b));
547 assert!(!Orientation2D::Left.box_is(a, b));
548 assert!(!Orientation2D::Right.box_is(a, b));
549 }
550
551 #[test]
552 fn orientation_box_left() {
553 let a = PxRect::from_size(PxSize::splat(Px(40)));
554 let mut b = a;
555 b.origin.x = -Px(82);
556 let a = a.to_box2d();
557 let b = b.to_box2d();
558
559 assert!(!Orientation2D::Above.box_is(a, b));
560 assert!(!Orientation2D::Below.box_is(a, b));
561 assert!(Orientation2D::Left.box_is(a, b));
562 assert!(!Orientation2D::Right.box_is(a, b));
563 }
564
565 #[test]
566 fn orientation_box_right() {
567 let a = PxRect::from_size(PxSize::splat(Px(40)));
568 let mut b = a;
569 b.origin.x = Px(42);
570 let a = a.to_box2d();
571 let b = b.to_box2d();
572
573 assert!(!Orientation2D::Above.box_is(a, b));
574 assert!(!Orientation2D::Below.box_is(a, b));
575 assert!(!Orientation2D::Left.box_is(a, b));
576 assert!(Orientation2D::Right.box_is(a, b));
577 }
578
579 #[test]
580 fn length_composite_parser_2() {
581 let mut parser = LengthCompositeParser::new("(10%, 20%)").unwrap();
582 assert_eq!(parser.next().unwrap(), Length::from(10.pct()));
583 assert_eq!(parser.expect_last().unwrap(), Length::from(20.pct()));
584 }
585
586 #[test]
587 fn length_composite_parser_1() {
588 let parser = LengthCompositeParser::new("10px").unwrap();
589 assert_eq!(parser.expect_last().unwrap(), 10.px());
590 }
591}