zng_layout/unit/
constraints.rs

1use std::fmt;
2
3use zng_var::{animation::Transitionable, impl_from_and_into_var};
4
5use super::{FactorUnits, Px, PxSize, euclid};
6
7pub use euclid::BoolVector2D;
8
9/// Pixel length constraints.
10///
11/// These constraints can express lower and upper bounds, unbounded upper and preference of *fill* length.
12///
13/// See also the [`PxConstraints2d`].
14#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
15pub struct PxConstraints {
16    #[serde(with = "serde_constraints_max")]
17    max: Px,
18    min: Px,
19
20    /// Fill preference, when this is `true` and the constraints have a maximum bound the fill length is the maximum bounds,
21    /// otherwise the fill length is the minimum bounds.
22    pub fill: bool,
23}
24impl PxConstraints {
25    /// New unbounded constrain.
26    pub fn new_unbounded() -> Self {
27        PxConstraints {
28            max: Px::MAX,
29            min: Px(0),
30            fill: false,
31        }
32    }
33
34    /// New bounded between zero and `max` with no fill.
35    pub fn new_bounded(max: Px) -> Self {
36        PxConstraints {
37            max,
38            min: Px(0),
39            fill: false,
40        }
41    }
42
43    /// New bounded to only allow the `length` and fill.
44    pub fn new_exact(length: Px) -> Self {
45        PxConstraints {
46            max: length,
47            min: length,
48            fill: true,
49        }
50    }
51
52    /// New bounded to fill the `length`.
53    pub fn new_fill(length: Px) -> Self {
54        PxConstraints {
55            max: length,
56            min: Px(0),
57            fill: true,
58        }
59    }
60
61    /// New bounded to a inclusive range.
62    ///
63    /// # Panics
64    ///
65    /// Panics if `min` is not <= `max`.
66    pub fn new_range(min: Px, max: Px) -> Self {
67        assert!(min <= max);
68
69        PxConstraints { max, min, fill: false }
70    }
71
72    /// Returns a copy of the current constraints that has `min` as the lower bound and max adjusted to be >= `min`.
73    pub fn with_new_min(mut self, min: Px) -> Self {
74        self.min = min;
75        self.max = self.max.max(self.min);
76        self
77    }
78
79    /// Returns a copy [`with_new_min`] if `min` is greater then the current minimum.
80    ///
81    /// [`with_new_min`]: Self::with_new_min
82    pub fn with_min(self, min: Px) -> Self {
83        if min > self.min { self.with_new_min(min) } else { self }
84    }
85
86    /// Returns a copy of the current constraints that has `max` as the upper bound and min adjusted to be <= `max`.
87    pub fn with_new_max(mut self, max: Px) -> Self {
88        self.max = max;
89        self.min = self.min.min(self.max);
90        self
91    }
92
93    /// Returns a copy [`with_new_max`] if `max` is less then the current maximum or the current maximum is unbounded.
94    ///
95    /// [`with_new_max`]: Self::with_new_max
96    pub fn with_max(self, max: Px) -> Self {
97        if max < self.max { self.with_new_max(max) } else { self }
98    }
99
100    /// Returns a copy of the current constraints that has max and min set to `len` and fill enabled.
101    pub fn with_new_exact(mut self, len: Px) -> Self {
102        self.max = len;
103        self.min = len;
104        self.fill = true;
105        self
106    }
107
108    /// Returns a copy [`with_new_exact`] if the new length clamped by the current constraints.
109    ///
110    /// [`with_new_exact`]: Self::with_new_exact
111    pub fn with_exact(self, len: Px) -> Self {
112        self.with_new_exact(self.clamp(len))
113    }
114
115    /// Returns a copy of the current constraints that sets the `fill` preference.
116    pub fn with_fill(mut self, fill: bool) -> Self {
117        self.fill = fill;
118        self
119    }
120
121    /// Returns a copy of the current constraints that sets the fill preference to `self.fill && fill`.
122    pub fn with_fill_and(mut self, fill: bool) -> Self {
123        self.fill &= fill;
124        self
125    }
126
127    /// Returns a copy of the current constraints without upper bound.
128    pub fn with_unbounded(mut self) -> Self {
129        self.max = Px::MAX;
130        self
131    }
132
133    /// Returns a copy of the current constraints with `sub` subtracted from the min and max bounds.
134    ///
135    /// The subtraction is saturating, does not subtract max if unbounded.
136    pub fn with_less(mut self, sub: Px) -> Self {
137        if self.max < Px::MAX {
138            self.max -= sub;
139            self.max = self.max.max(Px(0));
140        }
141        self.min -= sub;
142        self.min = self.min.max(Px(0));
143        self
144    }
145
146    /// Returns a copy of the current constraints with `add` added to the maximum bounds.
147    ///
148    /// Does a saturation addition, this can potentially unbound the constraints if [`Px::MAX`] is reached.
149    pub fn with_more(mut self, add: Px) -> Self {
150        self.max += add;
151        self
152    }
153
154    /// Gets if the constraints have an upper bound.
155    pub fn is_bounded(self) -> bool {
156        self.max != Px::MAX
157    }
158
159    /// Gets if the constraints have no upper bound.
160    pub fn is_unbounded(self) -> bool {
161        self.max == Px::MAX
162    }
163
164    /// Gets if the constraints only allow one length.
165    pub fn is_exact(self) -> bool {
166        self.max == self.min
167    }
168
169    /// Gets if the context prefers the maximum length over the minimum.
170    ///
171    /// Note that if the constraints are unbounded there is not maximum length, in this case the fill length is the minimum.
172    pub fn is_fill_pref(self) -> bool {
173        self.fill
174    }
175
176    /// Gets if the context prefers the maximum length and there is a maximum length.
177    pub fn is_fill_max(self) -> bool {
178        self.fill && !self.is_unbounded()
179    }
180
181    /// Gets the fixed length if the constraints only allow one length.
182    pub fn exact(self) -> Option<Px> {
183        if self.is_exact() { Some(self.max) } else { None }
184    }
185
186    /// Gets the maximum allowed length, or `None` if is unbounded.
187    ///
188    /// The maximum is inclusive.
189    pub fn max(self) -> Option<Px> {
190        if self.max < Px::MAX { Some(self.max) } else { None }
191    }
192
193    /// Gets the minimum allowed length.
194    //
195    /// The minimum is inclusive.
196    pub fn min(self) -> Px {
197        self.min
198    }
199
200    /// Gets the maximum length if it is bounded, or the minimum if not.
201    pub fn max_bounded(self) -> Px {
202        if self.max < Px::MAX { self.max } else { self.min }
203    }
204
205    /// Clamp the `px` by min and max.
206    pub fn clamp(self, px: Px) -> Px {
207        self.min.max(px).min(self.max)
208    }
209
210    /// Gets the fill length, if fill is `true` this is the maximum length, otherwise it is the minimum length.
211    pub fn fill(self) -> Px {
212        if self.fill && !self.is_unbounded() { self.max } else { self.min }
213    }
214
215    /// Gets the maximum if fill is preferred and max is bounded, or `length` clamped by the constraints.
216    pub fn fill_or(self, length: Px) -> Px {
217        if self.fill && !self.is_unbounded() {
218            self.max
219        } else {
220            self.clamp(length)
221        }
222    }
223
224    /// Gets the max size if is fill and has max bounds, or gets the exact size if min equals max.
225    pub fn fill_or_exact(self) -> Option<Px> {
226        if self.is_fill_max() || self.is_exact() {
227            Some(self.max)
228        } else {
229            None
230        }
231    }
232
233    /// Gets the maximum length if bounded or `length` clamped by the constraints.
234    pub fn max_or(self, length: Px) -> Px {
235        if self.is_unbounded() { self.clamp(length) } else { self.max }
236    }
237}
238impl_from_and_into_var! {
239    /// New exact.
240    fn from(length: Px) -> PxConstraints {
241        PxConstraints::new_exact(length)
242    }
243}
244impl fmt::Debug for PxConstraints {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        if f.alternate() {
247            f.debug_struct("PxConstraints")
248                .field("max", &self.max())
249                .field("min", &self.min)
250                .field("fill", &self.fill)
251                .finish()
252        } else if self.is_exact() {
253            write!(f, "exact({})", self.min)
254        } else if self.is_unbounded() {
255            write!(f, "min({})", self.min)
256        } else if self.fill {
257            write!(f, "fill({}, {})", self.min, self.max)
258        } else {
259            write!(f, "range({}, {})", self.min, self.max)
260        }
261    }
262}
263impl Default for PxConstraints {
264    fn default() -> Self {
265        Self::new_unbounded()
266    }
267}
268mod serde_constraints_max {
269    use super::Px;
270    use serde::*;
271    pub fn serialize<S: Serializer>(max: &Px, serializer: S) -> Result<S::Ok, S::Error> {
272        if serializer.is_human_readable() {
273            let px = if *max == Px::MAX { None } else { Some(*max) };
274            px.serialize(serializer)
275        } else {
276            max.serialize(serializer)
277        }
278    }
279
280    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Px, D::Error> {
281        if deserializer.is_human_readable() {
282            Ok(Option::<Px>::deserialize(deserializer)?.unwrap_or(Px::MAX))
283        } else {
284            Px::deserialize(deserializer)
285        }
286    }
287}
288
289/// Pixel *size* constraints.
290///
291/// These constraints can express lower and upper bounds, unbounded upper and preference of *fill* length for
292/// both the ***x*** and ***y*** axis.
293#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
294pub struct PxConstraints2d {
295    /// Constraints of lengths in the *x* or *width* dimension.
296    pub x: PxConstraints,
297    /// Constraints of lengths in the *y* or *height* dimension.
298    pub y: PxConstraints,
299}
300impl PxConstraints2d {
301    /// New unbounded constrain.
302    pub fn new_unbounded() -> Self {
303        Self {
304            x: PxConstraints::new_unbounded(),
305            y: PxConstraints::new_unbounded(),
306        }
307    }
308
309    /// New bounded between zero and `max_y`, `max_y` with no fill.
310    pub fn new_bounded(max_x: Px, max_y: Px) -> Self {
311        Self {
312            x: PxConstraints::new_bounded(max_x),
313            y: PxConstraints::new_bounded(max_y),
314        }
315    }
316
317    /// New bounded between zero and `max` with no fill.
318    pub fn new_bounded_size(max: PxSize) -> Self {
319        Self::new_bounded(max.width, max.height)
320    }
321
322    /// New bounded to only allow the *size* and fill.
323    ///
324    /// The type [`PxSize`] can also be converted into fixed constraints.
325    pub fn new_exact(x: Px, y: Px) -> Self {
326        Self {
327            x: PxConstraints::new_exact(x),
328            y: PxConstraints::new_exact(y),
329        }
330    }
331
332    /// New bounded to only allow the `size` and fill.
333    pub fn new_exact_size(size: PxSize) -> Self {
334        Self::new_exact(size.width, size.height)
335    }
336
337    /// New bounded to fill the maximum `x` and `y`.
338    pub fn new_fill(x: Px, y: Px) -> Self {
339        Self {
340            x: PxConstraints::new_fill(x),
341            y: PxConstraints::new_fill(y),
342        }
343    }
344
345    /// New bounded to fill the maximum `size`.
346    pub fn new_fill_size(size: PxSize) -> Self {
347        Self::new_fill(size.width, size.height)
348    }
349
350    /// New bounded to a inclusive range.
351    ///
352    /// A tuple of two [`PxSize`] values can also be converted to these constraints.
353    ///
354    /// # Panics
355    ///
356    /// Panics if min is greater then max.
357    pub fn new_range(min_x: Px, max_x: Px, min_y: Px, max_y: Px) -> Self {
358        Self {
359            x: PxConstraints::new_range(min_x, max_x),
360            y: PxConstraints::new_range(min_y, max_y),
361        }
362    }
363
364    /// Returns a copy of the current constraints that has `min_x` and `min_y` as the lower
365    /// bound and max adjusted to be >= min in both axis.
366    pub fn with_new_min(mut self, min_x: Px, min_y: Px) -> Self {
367        self.x = self.x.with_new_min(min_x);
368        self.y = self.y.with_new_min(min_y);
369        self
370    }
371
372    /// Returns a copy of the current constraints that has `min_x` and `min_y` as the lower
373    /// bound and max adjusted to be >= min in both axis, if the new min is greater then the current min.
374    pub fn with_min(mut self, min_x: Px, min_y: Px) -> Self {
375        self.x = self.x.with_min(min_x);
376        self.y = self.y.with_min(min_y);
377        self
378    }
379
380    /// Returns a copy of the current constraints that has `min` as the lower
381    /// bound and max adjusted to be >= min in both axis.
382    pub fn with_new_min_size(self, min: PxSize) -> Self {
383        self.with_new_min(min.width, min.height)
384    }
385
386    /// Returns a copy of the current constraints that has `min` as the lower
387    /// bound and max adjusted to be >= min in both axis, if the new min is greater then the current min.
388    pub fn with_min_size(self, min: PxSize) -> Self {
389        self.with_min(min.width, min.height)
390    }
391
392    /// Returns a copy of the current constraints that has `min_x` as the lower
393    /// bound and max adjusted to be >= min in the **x** axis.
394    pub fn with_new_min_x(mut self, min_x: Px) -> Self {
395        self.x = self.x.with_new_min(min_x);
396        self
397    }
398
399    /// Returns a copy of the current constraints that has `min_y` as the lower
400    /// bound and max adjusted to be >= min in the **y** axis.
401    pub fn with_new_min_y(mut self, min_y: Px) -> Self {
402        self.y = self.y.with_new_min(min_y);
403        self
404    }
405
406    /// Returns a copy of the current constraints that has `min_x` as the lower
407    /// bound and max adjusted to be >= min in the **x** axis if the new min is greater then the current min.
408    pub fn with_min_x(mut self, min_x: Px) -> Self {
409        self.x = self.x.with_min(min_x);
410        self
411    }
412
413    /// Returns a copy of the current constraints that has `min_y` as the lower
414    /// bound and max adjusted to be >= min in the **y** axis if the new min is greater then the current min.
415    pub fn with_min_y(mut self, min_y: Px) -> Self {
416        self.y = self.y.with_min(min_y);
417        self
418    }
419
420    /// Returns a copy of the current constraints that has `max_x` and `max_y` as the upper
421    /// bound and min adjusted to be <= max in both axis.
422    pub fn with_new_max(mut self, max_x: Px, max_y: Px) -> Self {
423        self.x = self.x.with_new_max(max_x);
424        self.y = self.y.with_new_max(max_y);
425        self
426    }
427
428    /// Returns a copy of the current constraints that has `max_x` and `max_y` as the upper
429    /// bound and min adjusted to be <= max in both axis if the new max if less then the current max.
430    pub fn with_max(mut self, max_x: Px, max_y: Px) -> Self {
431        self.x = self.x.with_max(max_x);
432        self.y = self.y.with_max(max_y);
433        self
434    }
435
436    /// Returns a copy of the current constraints that has `max` as the upper
437    /// bound and min adjusted to be <= max in both axis.
438    pub fn with_new_max_size(self, max: PxSize) -> Self {
439        self.with_new_max(max.width, max.height)
440    }
441
442    /// Returns a copy of the current constraints that has `max` as the upper
443    /// bound and min adjusted to be <= max in both axis if the new max if less then the current max.
444    pub fn with_max_size(self, max: PxSize) -> Self {
445        self.with_max(max.width, max.height)
446    }
447
448    /// Returns a copy of the current constraints that has `min_x` as the lower
449    /// bound and max adjusted to be << max in the **x** axis.
450    pub fn with_new_max_x(mut self, max_x: Px) -> Self {
451        self.x = self.x.with_new_max(max_x);
452        self
453    }
454
455    /// Returns a copy of the current constraints that has `max_y` as the lower
456    /// bound and min adjusted to be <= max in the **y** axis.
457    pub fn with_new_max_y(mut self, max_y: Px) -> Self {
458        self.y = self.y.with_new_max(max_y);
459        self
460    }
461
462    /// Returns a copy of the current constraints that has `min_x` as the lower
463    /// bound and max adjusted to be << max in the **x** axis if the new max if less then the current max.
464    pub fn with_max_x(mut self, max_x: Px) -> Self {
465        self.x = self.x.with_max(max_x);
466        self
467    }
468
469    /// Returns a copy of the current constraints that has `max_y` as the lower
470    /// bound and min adjusted to be <= max in the **y** axis if the new max if less then the current max.
471    pub fn with_max_y(mut self, max_y: Px) -> Self {
472        self.y = self.y.with_max(max_y);
473        self
474    }
475
476    /// Returns a copy with min and max bounds set to `x` and `y`.
477    pub fn with_new_exact(mut self, x: Px, y: Px) -> Self {
478        self.x = self.x.with_new_exact(x);
479        self.y = self.y.with_new_exact(y);
480        self
481    }
482
483    /// Returns a copy with min and max bounds set to `x` and `y` clamped by the current constraints.
484    pub fn with_exact(mut self, x: Px, y: Px) -> Self {
485        self.x = self.x.with_exact(x);
486        self.y = self.y.with_exact(y);
487        self
488    }
489
490    /// Returns a copy with min and max bounds set to `size`.
491    pub fn with_new_exact_size(self, size: PxSize) -> Self {
492        self.with_new_exact(size.width, size.height)
493    }
494
495    /// Returns a copy with min and max bounds set to `size` clamped by the current constraints.
496    pub fn with_exact_size(self, size: PxSize) -> Self {
497        self.with_exact(size.width, size.height)
498    }
499
500    /// Returns a copy of the current constraints with the **x** maximum and minimum set to `x`.
501    pub fn with_new_exact_x(mut self, x: Px) -> Self {
502        self.x = self.x.with_new_exact(x);
503        self
504    }
505
506    /// Returns a copy of the current constraints with the **y** maximum and minimum set to `y`.
507    pub fn with_new_exact_y(mut self, y: Px) -> Self {
508        self.y = self.y.with_new_exact(y);
509        self
510    }
511
512    /// Returns a copy of the current constraints with the **x** maximum and minimum set to `x`
513    /// clamped by the current constraints.
514    pub fn with_exact_x(mut self, x: Px) -> Self {
515        self.x = self.x.with_exact(x);
516        self
517    }
518
519    /// Returns a copy of the current constraints with the **y** maximum and minimum set to `y`
520    /// clamped by the current constraints.
521    pub fn with_exact_y(mut self, y: Px) -> Self {
522        self.y = self.y.with_exact(y);
523        self
524    }
525
526    /// Returns a copy of the current constraints that sets the `fill_x` and `fill_y` preference.
527    pub fn with_fill(mut self, fill_x: bool, fill_y: bool) -> Self {
528        self.x = self.x.with_fill(fill_x);
529        self.y = self.y.with_fill(fill_y);
530        self
531    }
532
533    /// Returns a copy of the current constraints that sets the fill preference to *current && fill*.
534    pub fn with_fill_and(mut self, fill_x: bool, fill_y: bool) -> Self {
535        self.x = self.x.with_fill_and(fill_x);
536        self.y = self.y.with_fill_and(fill_y);
537        self
538    }
539
540    /// Returns a copy of the current constraints that sets the `fill` preference
541    pub fn with_fill_vector(self, fill: BoolVector2D) -> Self {
542        self.with_fill(fill.x, fill.y)
543    }
544
545    /// Returns a copy of the current constraints that sets the `fill_x` preference.
546    pub fn with_fill_x(mut self, fill_x: bool) -> Self {
547        self.x = self.x.with_fill(fill_x);
548        self
549    }
550
551    /// Returns a copy of the current constraints that sets the `fill_y` preference.
552    pub fn with_fill_y(mut self, fill_y: bool) -> Self {
553        self.y = self.y.with_fill(fill_y);
554        self
555    }
556
557    /// Returns a copy of the current constraints without upper bound in both axis.
558    pub fn with_unbounded(mut self) -> Self {
559        self.x = self.x.with_unbounded();
560        self.y = self.y.with_unbounded();
561        self
562    }
563
564    /// Returns a copy of the current constraints without a upper bound in the **x** axis.
565    pub fn with_unbounded_x(mut self) -> Self {
566        self.x = self.x.with_unbounded();
567        self
568    }
569
570    /// Returns a copy of the current constraints without a upper bound in the **y** axis.
571    pub fn with_unbounded_y(mut self) -> Self {
572        self.y = self.y.with_unbounded();
573        self
574    }
575
576    /// Returns a copy of the current constraints with `sub_x` and `sub_y` subtracted from the min and max bounds.
577    ///
578    /// The subtraction is saturating, does not subtract max if unbounded.
579    pub fn with_less(mut self, sub_x: Px, sub_y: Px) -> Self {
580        self.x = self.x.with_less(sub_x);
581        self.y = self.y.with_less(sub_y);
582        self
583    }
584
585    /// Returns a copy of the current constraints with `sub` subtracted from the min and max bounds.
586    ///
587    /// The subtraction is saturating, does not subtract max if unbounded.
588    pub fn with_less_size(self, sub: PxSize) -> Self {
589        self.with_less(sub.width, sub.height)
590    }
591
592    /// Returns a copy of the current constraints with `sub_x` subtracted from the min and max bounds of the **x** axis.
593    ///
594    /// The subtraction is saturating, does not subtract max if unbounded.
595    pub fn with_less_x(mut self, sub_x: Px) -> Self {
596        self.x = self.x.with_less(sub_x);
597        self
598    }
599
600    /// Returns a copy of the current constraints with `sub_y` subtracted from the min and max bounds of the **y** axis.
601    ///
602    /// The subtraction is saturating, does not subtract max if unbounded.
603    pub fn with_less_y(mut self, sub_y: Px) -> Self {
604        self.y = self.y.with_less(sub_y);
605        self
606    }
607
608    /// Returns a copy of the current constraints with `add_x` and `add_y` added to the maximum bounds.
609    ///
610    /// Does a saturation addition, this can potentially unbound the constraints if [`Px::MAX`] is reached.
611    pub fn with_more(mut self, add_x: Px, add_y: Px) -> Self {
612        self.x = self.x.with_more(add_x);
613        self.y = self.y.with_more(add_y);
614        self
615    }
616
617    /// Returns a copy of the current constraints with `add` added to the maximum bounds.
618    ///
619    /// Does a saturation addition, this can potentially unbound the constraints if [`Px::MAX`] is reached.
620    pub fn with_more_size(self, add: PxSize) -> Self {
621        self.with_more(add.width, add.height)
622    }
623
624    /// Returns a copy of the current constraints with [`x`] modified by the closure.
625    ///
626    /// [`x`]: Self::x
627    pub fn with_x(mut self, x: impl FnOnce(PxConstraints) -> PxConstraints) -> Self {
628        self.x = x(self.x);
629        self
630    }
631
632    /// Returns a copy of the current constraints with [`y`] modified by the closure.
633    ///
634    /// [`y`]: Self::y
635    pub fn with_y(mut self, y: impl FnOnce(PxConstraints) -> PxConstraints) -> Self {
636        self.y = y(self.y);
637        self
638    }
639
640    /// Gets if the constraints have an upper bound.
641    pub fn is_bounded(self) -> BoolVector2D {
642        BoolVector2D {
643            x: self.x.is_bounded(),
644            y: self.y.is_bounded(),
645        }
646    }
647
648    /// Gets if the constraints have no upper bound.
649    pub fn is_unbounded(self) -> BoolVector2D {
650        BoolVector2D {
651            x: self.x.is_unbounded(),
652            y: self.y.is_unbounded(),
653        }
654    }
655
656    /// Gets if the constraints only allow one length.
657    pub fn is_exact(self) -> BoolVector2D {
658        BoolVector2D {
659            x: self.x.is_exact(),
660            y: self.y.is_exact(),
661        }
662    }
663
664    /// Gets if the context prefers the maximum length over the minimum.
665    ///
666    /// Note that if the constraints are unbounded there is not maximum length, in this case the fill length is the minimum.
667    pub fn is_fill_pref(self) -> BoolVector2D {
668        BoolVector2D {
669            x: self.x.is_fill_pref(),
670            y: self.y.is_fill_pref(),
671        }
672    }
673
674    /// Gets if the context prefers the maximum length over the minimum and there is a maximum length.
675    pub fn is_fill_max(self) -> BoolVector2D {
676        BoolVector2D {
677            x: self.x.is_fill_max(),
678            y: self.y.is_fill_max(),
679        }
680    }
681
682    /// Gets the fixed size if the constraints only allow one length in both axis.
683    pub fn fixed_size(self) -> Option<PxSize> {
684        Some(PxSize::new(self.x.exact()?, self.y.exact()?))
685    }
686
687    /// Gets the maximum allowed size, or `None` if is unbounded in any of the axis.
688    ///
689    /// The maximum is inclusive.
690    pub fn max_size(self) -> Option<PxSize> {
691        Some(PxSize::new(self.x.max()?, self.y.max()?))
692    }
693
694    /// Gets the minimum allowed size.
695    //
696    /// The minimum is inclusive.
697    pub fn min_size(self) -> PxSize {
698        PxSize::new(self.x.min(), self.y.min())
699    }
700
701    /// Clamp the `size` by min and max.
702    pub fn clamp_size(self, size: PxSize) -> PxSize {
703        PxSize::new(self.x.clamp(size.width), self.y.clamp(size.height))
704    }
705
706    /// Gets the fill size, if fill is `true` this is the maximum length, otherwise it is the minimum length.
707    pub fn fill_size(self) -> PxSize {
708        PxSize::new(self.x.fill(), self.y.fill())
709    }
710
711    /// Gets the maximum if fill is preferred and max is bounded, or `size` clamped by the constraints.
712    pub fn fill_size_or(self, size: PxSize) -> PxSize {
713        PxSize::new(self.x.fill_or(size.width), self.y.fill_or(size.height))
714    }
715
716    /// Gets the max size if is fill and has max bounds, or gets the exact size if min equals max.
717    pub fn fill_or_exact(self) -> Option<PxSize> {
718        Some(PxSize::new(self.x.fill_or_exact()?, self.y.fill_or_exact()?))
719    }
720
721    /// Gets the maximum size if bounded, or the `size` clamped by constraints.
722    pub fn max_size_or(self, size: PxSize) -> PxSize {
723        PxSize::new(self.x.max_or(size.width), self.y.max_or(size.height))
724    }
725
726    /// Gets the maximum size if bounded, or the minimum if not.
727    pub fn max_bounded_size(self) -> PxSize {
728        PxSize::new(self.x.max_bounded(), self.y.max_bounded())
729    }
730
731    /// Gets the maximum fill size that preserves the `size` ratio.
732    pub fn fill_ratio(self, size: PxSize) -> PxSize {
733        if self.x.is_unbounded() {
734            if self.y.is_unbounded() {
735                // cover min
736                let container = size.max(self.min_size()).to_f32();
737                let content = size.to_f32();
738                let scale = (container.width / content.width).max(container.height / content.height).fct();
739                size * scale
740            } else {
741                // expand height
742                let height = self.y.fill_or(size.height.max(self.y.min));
743                let scale = (height.0 as f32 / size.height.0 as f32).fct();
744                PxSize::new(size.width * scale, height)
745            }
746        } else if self.y.is_unbounded() {
747            // expand width
748            let width = self.x.fill_or(size.width.max(self.x.min));
749            let scale = (width.0 as f32 / size.width.0 as f32).fct();
750            PxSize::new(width, size.height * scale)
751        } else if self.x.is_fill_pref() || self.y.is_fill_pref() {
752            // contain max & clamp min
753            let container = self.fill_size_or(size).to_f32();
754            let content = size.to_f32();
755            let scale = (container.width / content.width).min(container.height / content.height).fct();
756
757            (size * scale).max(self.min_size())
758        } else {
759            // cover min & clamp max
760            let container = self.min_size().to_f32();
761            let content = size.to_f32();
762            let scale = (container.width / content.width).max(container.height / content.height).fct();
763
764            (size * scale).min(PxSize::new(self.x.max, self.y.max))
765        }
766    }
767}
768impl_from_and_into_var! {
769    /// New exact.
770    fn from(size: PxSize) -> PxConstraints2d {
771        PxConstraints2d::new_exact(size.width, size.height)
772    }
773
774    /// New range, the minimum and maximum is computed.
775    fn from((a, b): (PxSize, PxSize)) -> PxConstraints2d {
776        PxConstraints2d {
777            x: if a.width > b.width {
778                PxConstraints::new_range(b.width, a.width)
779            } else {
780                PxConstraints::new_range(a.width, b.width)
781            },
782            y: if a.height > b.height {
783                PxConstraints::new_range(b.height, a.height)
784            } else {
785                PxConstraints::new_range(a.height, b.height)
786            },
787        }
788    }
789}
790impl Default for PxConstraints2d {
791    fn default() -> Self {
792        Self::new_unbounded()
793    }
794}
795
796#[cfg(test)]
797mod tests {
798    use super::*;
799
800    #[test]
801    fn fill_ratio_unbounded_no_min() {
802        let constraints = PxConstraints2d::new_unbounded();
803
804        let size = PxSize::new(Px(400), Px(200));
805        let filled = constraints.fill_ratio(size);
806
807        assert_eq!(size, filled)
808    }
809
810    #[test]
811    fn fill_ratio_unbounded_with_min_x() {
812        let constraints = PxConstraints2d::new_unbounded().with_min_x(Px(800));
813
814        let size = PxSize::new(Px(400), Px(200));
815        let filled = constraints.fill_ratio(size);
816
817        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
818    }
819
820    #[test]
821    fn fill_ratio_unbounded_with_min_y() {
822        let constraints = PxConstraints2d::new_unbounded().with_min_y(Px(400));
823
824        let size = PxSize::new(Px(400), Px(200));
825        let filled = constraints.fill_ratio(size);
826
827        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
828    }
829
830    #[test]
831    fn fill_ratio_bounded_x() {
832        let constraints = PxConstraints2d::new_fill(Px(800), Px::MAX);
833
834        let size = PxSize::new(Px(400), Px(200));
835        let filled = constraints.fill_ratio(size);
836
837        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
838    }
839
840    #[test]
841    fn fill_ratio_bounded_y() {
842        let constraints = PxConstraints2d::new_fill(Px::MAX, Px(400));
843
844        let size = PxSize::new(Px(400), Px(200));
845        let filled = constraints.fill_ratio(size);
846
847        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
848    }
849
850    #[test]
851    fn fill_ratio_bounded1() {
852        let constraints = PxConstraints2d::new_fill(Px(800), Px(400));
853
854        let size = PxSize::new(Px(400), Px(200));
855        let filled = constraints.fill_ratio(size);
856
857        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
858    }
859
860    #[test]
861    fn fill_ratio_bounded2() {
862        let constraints = PxConstraints2d::new_fill(Px(400), Px(400));
863
864        let size = PxSize::new(Px(400), Px(200));
865        let filled = constraints.fill_ratio(size);
866
867        assert_eq!(filled, PxSize::new(Px(400), Px(200)))
868    }
869
870    #[test]
871    fn fill_ratio_exact() {
872        let constraints = PxConstraints2d::new_exact(Px(123), Px(321));
873
874        let size = PxSize::new(Px(400), Px(200));
875        let filled = constraints.fill_ratio(size);
876
877        assert_eq!(filled, PxSize::new(Px(123), Px(321)))
878    }
879
880    #[test]
881    fn fill_ratio_no_fill_bounded_with_min_x() {
882        let constraints = PxConstraints2d::new_bounded(Px(1000), Px(1000)).with_min_x(Px(800));
883
884        let size = PxSize::new(Px(400), Px(200));
885        let filled = constraints.fill_ratio(size);
886
887        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
888    }
889
890    #[test]
891    fn fill_ratio_no_fill_bounded_with_min_y() {
892        let constraints = PxConstraints2d::new_bounded(Px(1000), Px(1000)).with_min_y(Px(400));
893
894        let size = PxSize::new(Px(400), Px(200));
895        let filled = constraints.fill_ratio(size);
896
897        assert_eq!(filled, PxSize::new(Px(800), Px(400)))
898    }
899}