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#[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 pub fill: bool,
23}
24impl PxConstraints {
25 pub fn new_unbounded() -> Self {
27 PxConstraints {
28 max: Px::MAX,
29 min: Px(0),
30 fill: false,
31 }
32 }
33
34 pub fn new_bounded(max: Px) -> Self {
36 PxConstraints {
37 max,
38 min: Px(0),
39 fill: false,
40 }
41 }
42
43 pub fn new_exact(length: Px) -> Self {
45 PxConstraints {
46 max: length,
47 min: length,
48 fill: true,
49 }
50 }
51
52 pub fn new_fill(length: Px) -> Self {
54 PxConstraints {
55 max: length,
56 min: Px(0),
57 fill: true,
58 }
59 }
60
61 pub fn new_range(min: Px, max: Px) -> Self {
67 assert!(min <= max);
68
69 PxConstraints { max, min, fill: false }
70 }
71
72 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 pub fn with_min(self, min: Px) -> Self {
83 if min > self.min { self.with_new_min(min) } else { self }
84 }
85
86 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 pub fn with_max(self, max: Px) -> Self {
97 if max < self.max { self.with_new_max(max) } else { self }
98 }
99
100 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 pub fn with_exact(self, len: Px) -> Self {
112 self.with_new_exact(self.clamp(len))
113 }
114
115 pub fn with_fill(mut self, fill: bool) -> Self {
117 self.fill = fill;
118 self
119 }
120
121 pub fn with_fill_and(mut self, fill: bool) -> Self {
123 self.fill &= fill;
124 self
125 }
126
127 pub fn with_unbounded(mut self) -> Self {
129 self.max = Px::MAX;
130 self
131 }
132
133 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 pub fn with_more(mut self, add: Px) -> Self {
150 self.max += add;
151 self
152 }
153
154 pub fn is_bounded(self) -> bool {
156 self.max != Px::MAX
157 }
158
159 pub fn is_unbounded(self) -> bool {
161 self.max == Px::MAX
162 }
163
164 pub fn is_exact(self) -> bool {
166 self.max == self.min
167 }
168
169 pub fn is_fill_pref(self) -> bool {
173 self.fill
174 }
175
176 pub fn is_fill_max(self) -> bool {
178 self.fill && !self.is_unbounded()
179 }
180
181 pub fn exact(self) -> Option<Px> {
183 if self.is_exact() { Some(self.max) } else { None }
184 }
185
186 pub fn max(self) -> Option<Px> {
190 if self.max < Px::MAX { Some(self.max) } else { None }
191 }
192
193 pub fn min(self) -> Px {
197 self.min
198 }
199
200 pub fn max_bounded(self) -> Px {
202 if self.max < Px::MAX { self.max } else { self.min }
203 }
204
205 pub fn clamp(self, px: Px) -> Px {
207 self.min.max(px).min(self.max)
208 }
209
210 pub fn fill(self) -> Px {
212 if self.fill && !self.is_unbounded() { self.max } else { self.min }
213 }
214
215 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 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
294pub struct PxConstraints2d {
295 pub x: PxConstraints,
297 pub y: PxConstraints,
299}
300impl PxConstraints2d {
301 pub fn new_unbounded() -> Self {
303 Self {
304 x: PxConstraints::new_unbounded(),
305 y: PxConstraints::new_unbounded(),
306 }
307 }
308
309 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 pub fn new_bounded_size(max: PxSize) -> Self {
319 Self::new_bounded(max.width, max.height)
320 }
321
322 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 pub fn new_exact_size(size: PxSize) -> Self {
334 Self::new_exact(size.width, size.height)
335 }
336
337 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 pub fn new_fill_size(size: PxSize) -> Self {
347 Self::new_fill(size.width, size.height)
348 }
349
350 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 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 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 pub fn with_new_min_size(self, min: PxSize) -> Self {
383 self.with_new_min(min.width, min.height)
384 }
385
386 pub fn with_min_size(self, min: PxSize) -> Self {
389 self.with_min(min.width, min.height)
390 }
391
392 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 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 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 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 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 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 pub fn with_new_max_size(self, max: PxSize) -> Self {
439 self.with_new_max(max.width, max.height)
440 }
441
442 pub fn with_max_size(self, max: PxSize) -> Self {
445 self.with_max(max.width, max.height)
446 }
447
448 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 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 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 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 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 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 pub fn with_new_exact_size(self, size: PxSize) -> Self {
492 self.with_new_exact(size.width, size.height)
493 }
494
495 pub fn with_exact_size(self, size: PxSize) -> Self {
497 self.with_exact(size.width, size.height)
498 }
499
500 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 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 pub fn with_exact_x(mut self, x: Px) -> Self {
515 self.x = self.x.with_exact(x);
516 self
517 }
518
519 pub fn with_exact_y(mut self, y: Px) -> Self {
522 self.y = self.y.with_exact(y);
523 self
524 }
525
526 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 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 pub fn with_fill_vector(self, fill: BoolVector2D) -> Self {
542 self.with_fill(fill.x, fill.y)
543 }
544
545 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 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 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 pub fn with_unbounded_x(mut self) -> Self {
566 self.x = self.x.with_unbounded();
567 self
568 }
569
570 pub fn with_unbounded_y(mut self) -> Self {
572 self.y = self.y.with_unbounded();
573 self
574 }
575
576 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 pub fn with_less_size(self, sub: PxSize) -> Self {
589 self.with_less(sub.width, sub.height)
590 }
591
592 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 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 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 pub fn with_more_size(self, add: PxSize) -> Self {
621 self.with_more(add.width, add.height)
622 }
623
624 pub fn with_x(mut self, x: impl FnOnce(PxConstraints) -> PxConstraints) -> Self {
628 self.x = x(self.x);
629 self
630 }
631
632 pub fn with_y(mut self, y: impl FnOnce(PxConstraints) -> PxConstraints) -> Self {
636 self.y = y(self.y);
637 self
638 }
639
640 pub fn is_bounded(self) -> BoolVector2D {
642 BoolVector2D {
643 x: self.x.is_bounded(),
644 y: self.y.is_bounded(),
645 }
646 }
647
648 pub fn is_unbounded(self) -> BoolVector2D {
650 BoolVector2D {
651 x: self.x.is_unbounded(),
652 y: self.y.is_unbounded(),
653 }
654 }
655
656 pub fn is_exact(self) -> BoolVector2D {
658 BoolVector2D {
659 x: self.x.is_exact(),
660 y: self.y.is_exact(),
661 }
662 }
663
664 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 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 pub fn fixed_size(self) -> Option<PxSize> {
684 Some(PxSize::new(self.x.exact()?, self.y.exact()?))
685 }
686
687 pub fn max_size(self) -> Option<PxSize> {
691 Some(PxSize::new(self.x.max()?, self.y.max()?))
692 }
693
694 pub fn min_size(self) -> PxSize {
698 PxSize::new(self.x.min(), self.y.min())
699 }
700
701 pub fn clamp_size(self, size: PxSize) -> PxSize {
703 PxSize::new(self.x.clamp(size.width), self.y.clamp(size.height))
704 }
705
706 pub fn fill_size(self) -> PxSize {
708 PxSize::new(self.x.fill(), self.y.fill())
709 }
710
711 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 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 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 pub fn max_bounded_size(self) -> PxSize {
728 PxSize::new(self.x.max_bounded(), self.y.max_bounded())
729 }
730
731 pub fn fill_ratio(self, size: PxSize) -> PxSize {
733 if self.x.is_unbounded() {
734 if self.y.is_unbounded() {
735 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 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 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 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 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 fn from(size: PxSize) -> PxConstraints2d {
771 PxConstraints2d::new_exact(size.width, size.height)
772 }
773
774 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}