zng_layout/unit/
constraints.rs

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