zng_layout/unit/
constraints.rs

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