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