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#[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 pub fn new_unbounded() -> Self {
38 PxConstraints {
39 max: Px::MAX,
40 min: Px(0),
41 flags: PxConstraintsFlags::empty(),
42 }
43 }
44
45 pub fn new_bounded(max: Px) -> Self {
47 PxConstraints {
48 max,
49 min: Px(0),
50 flags: PxConstraintsFlags::empty(),
51 }
52 }
53
54 pub fn new_exact(length: Px) -> Self {
56 PxConstraints {
57 max: length,
58 min: length,
59 flags: PxConstraintsFlags::FILL,
60 }
61 }
62
63 pub fn new_fill(length: Px) -> Self {
65 PxConstraints {
66 max: length,
67 min: Px(0),
68 flags: PxConstraintsFlags::FILL,
69 }
70 }
71
72 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 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 pub fn with_min(self, min: Px) -> Self {
98 if min > self.min { self.with_new_min(min) } else { self }
99 }
100
101 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 pub fn with_max(self, max: Px) -> Self {
112 if max < self.max { self.with_new_max(max) } else { self }
113 }
114
115 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 pub fn with_exact(self, len: Px) -> Self {
127 self.with_new_exact(self.clamp(len))
128 }
129
130 pub fn with_fill(mut self, fill: bool) -> Self {
134 self.flags.set(PxConstraintsFlags::FILL, fill);
135 self
136 }
137
138 pub fn with_inner(mut self, inner: bool) -> Self {
142 self.flags.set(PxConstraintsFlags::INNER, inner);
143 self
144 }
145
146 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 pub fn with_unbounded(mut self) -> Self {
155 self.max = Px::MAX;
156 self
157 }
158
159 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 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 pub fn is_bounded(self) -> bool {
182 self.max != Px::MAX
183 }
184
185 pub fn is_unbounded(self) -> bool {
187 self.max == Px::MAX
188 }
189
190 pub fn is_exact(self) -> bool {
192 self.max == self.min
193 }
194
195 pub fn is_fill(self) -> bool {
199 self.flags.contains(PxConstraintsFlags::FILL)
200 }
201
202 pub fn is_fill_max(self) -> bool {
204 self.is_fill() && !self.is_unbounded()
205 }
206
207 pub fn is_inner(self) -> bool {
215 self.flags.contains(PxConstraintsFlags::INNER)
216 }
217
218 pub fn exact(self) -> Option<Px> {
220 if self.is_exact() { Some(self.max) } else { None }
221 }
222
223 pub fn max(self) -> Option<Px> {
227 if self.max < Px::MAX { Some(self.max) } else { None }
228 }
229
230 pub fn min(self) -> Px {
234 self.min
235 }
236
237 pub fn max_bounded(self) -> Px {
239 if self.max < Px::MAX { self.max } else { self.min }
240 }
241
242 pub fn clamp(self, px: Px) -> Px {
244 self.min.max(px).min(self.max)
245 }
246
247 pub fn fill(self) -> Px {
251 if self.is_fill_max() { self.max } else { self.min }
252 }
253
254 pub fn fill_or(self, length: Px) -> Px {
256 if self.is_fill_max() { self.max } else { self.clamp(length) }
257 }
258
259 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 pub fn max_or(self, length: Px) -> Px {
270 if self.is_unbounded() { self.clamp(length) } else { self.max }
271 }
272
273 pub fn inner(self) -> Self {
281 if self.is_inner() { PxConstraints::new_unbounded() } else { self }
282 }
283}
284impl_from_and_into_var! {
285 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
324pub struct PxConstraints2d {
325 pub x: PxConstraints,
327 pub y: PxConstraints,
329}
330impl PxConstraints2d {
331 pub fn new_unbounded() -> Self {
333 Self {
334 x: PxConstraints::new_unbounded(),
335 y: PxConstraints::new_unbounded(),
336 }
337 }
338
339 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 pub fn new_bounded_size(max: PxSize) -> Self {
349 Self::new_bounded(max.width, max.height)
350 }
351
352 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 pub fn new_exact_size(size: PxSize) -> Self {
364 Self::new_exact(size.width, size.height)
365 }
366
367 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 pub fn new_fill_size(size: PxSize) -> Self {
377 Self::new_fill(size.width, size.height)
378 }
379
380 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 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 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 pub fn with_new_min_size(self, min: PxSize) -> Self {
413 self.with_new_min(min.width, min.height)
414 }
415
416 pub fn with_min_size(self, min: PxSize) -> Self {
419 self.with_min(min.width, min.height)
420 }
421
422 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 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 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 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 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 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 pub fn with_new_max_size(self, max: PxSize) -> Self {
469 self.with_new_max(max.width, max.height)
470 }
471
472 pub fn with_max_size(self, max: PxSize) -> Self {
475 self.with_max(max.width, max.height)
476 }
477
478 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 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 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 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 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 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 pub fn with_new_exact_size(self, size: PxSize) -> Self {
522 self.with_new_exact(size.width, size.height)
523 }
524
525 pub fn with_exact_size(self, size: PxSize) -> Self {
527 self.with_exact(size.width, size.height)
528 }
529
530 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 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 pub fn with_exact_x(mut self, x: Px) -> Self {
545 self.x = self.x.with_exact(x);
546 self
547 }
548
549 pub fn with_exact_y(mut self, y: Px) -> Self {
552 self.y = self.y.with_exact(y);
553 self
554 }
555
556 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 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 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 pub fn with_fill_vector(self, fill: BoolVector2D) -> Self {
581 self.with_fill(fill.x, fill.y)
582 }
583
584 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 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 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 pub fn with_unbounded_x(mut self) -> Self {
605 self.x = self.x.with_unbounded();
606 self
607 }
608
609 pub fn with_unbounded_y(mut self) -> Self {
611 self.y = self.y.with_unbounded();
612 self
613 }
614
615 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 pub fn with_less_size(self, sub: PxSize) -> Self {
628 self.with_less(sub.width, sub.height)
629 }
630
631 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 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 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 pub fn with_more_size(self, add: PxSize) -> Self {
660 self.with_more(add.width, add.height)
661 }
662
663 pub fn with_x(mut self, x: impl FnOnce(PxConstraints) -> PxConstraints) -> Self {
667 self.x = x(self.x);
668 self
669 }
670
671 pub fn with_y(mut self, y: impl FnOnce(PxConstraints) -> PxConstraints) -> Self {
675 self.y = y(self.y);
676 self
677 }
678
679 pub fn is_bounded(self) -> BoolVector2D {
681 BoolVector2D {
682 x: self.x.is_bounded(),
683 y: self.y.is_bounded(),
684 }
685 }
686
687 pub fn is_unbounded(self) -> BoolVector2D {
689 BoolVector2D {
690 x: self.x.is_unbounded(),
691 y: self.y.is_unbounded(),
692 }
693 }
694
695 pub fn is_exact(self) -> BoolVector2D {
697 BoolVector2D {
698 x: self.x.is_exact(),
699 y: self.y.is_exact(),
700 }
701 }
702
703 pub fn is_fill(self) -> BoolVector2D {
707 BoolVector2D {
708 x: self.x.is_fill(),
709 y: self.y.is_fill(),
710 }
711 }
712
713 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 pub fn is_inner(self) -> BoolVector2D {
729 BoolVector2D {
730 x: self.x.is_inner(),
731 y: self.y.is_inner(),
732 }
733 }
734
735 pub fn fixed_size(self) -> Option<PxSize> {
737 Some(PxSize::new(self.x.exact()?, self.y.exact()?))
738 }
739
740 pub fn max_size(self) -> Option<PxSize> {
744 Some(PxSize::new(self.x.max()?, self.y.max()?))
745 }
746
747 pub fn min_size(self) -> PxSize {
751 PxSize::new(self.x.min(), self.y.min())
752 }
753
754 pub fn clamp_size(self, size: PxSize) -> PxSize {
756 PxSize::new(self.x.clamp(size.width), self.y.clamp(size.height))
757 }
758
759 pub fn fill_size(self) -> PxSize {
763 PxSize::new(self.x.fill(), self.y.fill())
764 }
765
766 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 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 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 pub fn max_bounded_size(self) -> PxSize {
783 PxSize::new(self.x.max_bounded(), self.y.max_bounded())
784 }
785
786 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 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 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 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 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 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 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 fn from(size: PxSize) -> PxConstraints2d {
844 PxConstraints2d::new_exact(size.width, size.height)
845 }
846
847 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}