1use std::any::{Any, TypeId};
7use std::collections::HashMap;
8use std::fmt::{self, Debug, Display};
9use std::hash::{Hash, Hasher};
10
11use std::cell::Cell;
12use std::ops::{Add, Deref, DerefMut};
13
14use inset_embedder::{
15 Matrix4, Offset, Rect, Size, TextBaseline, ViewConstraints, clamp_double, lerp_double,
16};
17use inset_foundation::{App, Handle, HandleId};
18use inset_gestures::{HitTestEntry, HitTestResult, HitTestTarget, PointerEvent};
19use inset_painting::EdgeInsetsGeometry;
20use inset_painting::transform_point;
21
22use crate::object::{
23 AnyRenderObject, Constraints, ContainerParentDataMixin, ContainerRenderObjectMixin, ParentData,
24 RenderHandle, RenderObject, RenderObjectVTable, create, debug_checking_intrinsics, resolve,
25};
26use crate::painting_context::PaintingContext;
27use crate::pipeline_owner::PipelineOwner;
28
29thread_local! {
30 static DEBUG_DOING_BASELINE: Cell<bool> = const { Cell::new(false) };
31}
32
33#[derive(Clone, Copy)]
41pub struct BoxConstraints {
42 pub min_width: f64,
44 pub max_width: f64,
48 pub min_height: f64,
50 pub max_height: f64,
54}
55
56impl BoxConstraints {
57 pub const fn new() -> BoxConstraints {
59 BoxConstraints {
60 min_width: 0.0,
61 max_width: f64::INFINITY,
62 min_height: 0.0,
63 max_height: f64::INFINITY,
64 }
65 }
66
67 pub const fn min_width(mut self, value: f64) -> BoxConstraints {
69 self.min_width = value;
70 self
71 }
72
73 pub const fn max_width(mut self, value: f64) -> BoxConstraints {
75 self.max_width = value;
76 self
77 }
78
79 pub const fn min_height(mut self, value: f64) -> BoxConstraints {
81 self.min_height = value;
82 self
83 }
84
85 pub const fn max_height(mut self, value: f64) -> BoxConstraints {
87 self.max_height = value;
88 self
89 }
90
91 pub fn tight(size: Size) -> BoxConstraints {
93 BoxConstraints {
94 min_width: size.width(),
95 max_width: size.width(),
96 min_height: size.height(),
97 max_height: size.height(),
98 }
99 }
100
101 pub const fn tight_for(width: Option<f64>, height: Option<f64>) -> BoxConstraints {
103 BoxConstraints {
104 min_width: match width {
105 Some(width) => width,
106 None => 0.0,
107 },
108 max_width: match width {
109 Some(width) => width,
110 None => f64::INFINITY,
111 },
112 min_height: match height {
113 Some(height) => height,
114 None => 0.0,
115 },
116 max_height: match height {
117 Some(height) => height,
118 None => f64::INFINITY,
119 },
120 }
121 }
122
123 pub const fn tight_for_finite(width: f64, height: f64) -> BoxConstraints {
126 BoxConstraints {
127 min_width: if width != f64::INFINITY { width } else { 0.0 },
128 max_width: if width != f64::INFINITY {
129 width
130 } else {
131 f64::INFINITY
132 },
133 min_height: if height != f64::INFINITY { height } else { 0.0 },
134 max_height: if height != f64::INFINITY {
135 height
136 } else {
137 f64::INFINITY
138 },
139 }
140 }
141
142 pub fn loose(size: Size) -> BoxConstraints {
144 BoxConstraints {
145 min_width: 0.0,
146 max_width: size.width(),
147 min_height: 0.0,
148 max_height: size.height(),
149 }
150 }
151
152 pub const fn expand(width: Option<f64>, height: Option<f64>) -> BoxConstraints {
154 BoxConstraints {
155 min_width: match width {
156 Some(width) => width,
157 None => f64::INFINITY,
158 },
159 max_width: match width {
160 Some(width) => width,
161 None => f64::INFINITY,
162 },
163 min_height: match height {
164 Some(height) => height,
165 None => f64::INFINITY,
166 },
167 max_height: match height {
168 Some(height) => height,
169 None => f64::INFINITY,
170 },
171 }
172 }
173
174 pub const fn from_view_constraints(constraints: ViewConstraints) -> BoxConstraints {
176 BoxConstraints {
177 min_width: constraints.min_width,
178 max_width: constraints.max_width,
179 min_height: constraints.min_height,
180 max_height: constraints.max_height,
181 }
182 }
183
184 pub const fn copy_with(self) -> BoxConstraints {
186 self
187 }
188
189 pub fn deflate(self, edges: EdgeInsetsGeometry) -> BoxConstraints {
191 debug_assert!(self.debug_assert_is_valid(false));
192 let horizontal = edges.horizontal();
193 let vertical = edges.vertical();
194 let deflated_min_width = (self.min_width - horizontal).max(0.0);
195 let deflated_min_height = (self.min_height - vertical).max(0.0);
196 BoxConstraints {
197 min_width: deflated_min_width,
198 max_width: (self.max_width - horizontal).max(deflated_min_width),
199 min_height: deflated_min_height,
200 max_height: (self.max_height - vertical).max(deflated_min_height),
201 }
202 }
203
204 pub fn loosen(self) -> BoxConstraints {
206 debug_assert!(self.debug_assert_is_valid(false));
207 BoxConstraints {
208 min_width: 0.0,
209 max_width: self.max_width,
210 min_height: 0.0,
211 max_height: self.max_height,
212 }
213 }
214
215 pub fn enforce(self, constraints: BoxConstraints) -> BoxConstraints {
218 BoxConstraints {
219 min_width: clamp_double(self.min_width, constraints.min_width, constraints.max_width),
220 max_width: clamp_double(self.max_width, constraints.min_width, constraints.max_width),
221 min_height: clamp_double(
222 self.min_height,
223 constraints.min_height,
224 constraints.max_height,
225 ),
226 max_height: clamp_double(
227 self.max_height,
228 constraints.min_height,
229 constraints.max_height,
230 ),
231 }
232 }
233
234 pub fn tighten(self, width: Option<f64>, height: Option<f64>) -> BoxConstraints {
238 BoxConstraints {
239 min_width: match width {
240 Some(width) => clamp_double(width, self.min_width, self.max_width),
241 None => self.min_width,
242 },
243 max_width: match width {
244 Some(width) => clamp_double(width, self.min_width, self.max_width),
245 None => self.max_width,
246 },
247 min_height: match height {
248 Some(height) => clamp_double(height, self.min_height, self.max_height),
249 None => self.min_height,
250 },
251 max_height: match height {
252 Some(height) => clamp_double(height, self.min_height, self.max_height),
253 None => self.max_height,
254 },
255 }
256 }
257
258 pub const fn flipped(self) -> BoxConstraints {
260 BoxConstraints {
261 min_width: self.min_height,
262 max_width: self.max_height,
263 min_height: self.min_width,
264 max_height: self.max_width,
265 }
266 }
267
268 pub const fn width_constraints(self) -> BoxConstraints {
271 BoxConstraints {
272 min_width: self.min_width,
273 max_width: self.max_width,
274 min_height: 0.0,
275 max_height: f64::INFINITY,
276 }
277 }
278
279 pub const fn height_constraints(self) -> BoxConstraints {
282 BoxConstraints {
283 min_width: 0.0,
284 max_width: f64::INFINITY,
285 min_height: self.min_height,
286 max_height: self.max_height,
287 }
288 }
289
290 pub fn constrain_width(self, width: f64) -> f64 {
293 debug_assert!(self.debug_assert_is_valid(false));
294 clamp_double(width, self.min_width, self.max_width)
295 }
296
297 pub fn constrain_height(self, height: f64) -> f64 {
300 debug_assert!(self.debug_assert_is_valid(false));
301 clamp_double(height, self.min_height, self.max_height)
302 }
303
304 pub fn constrain(self, size: Size) -> Size {
307 Size::new(
308 self.constrain_width(size.width()),
309 self.constrain_height(size.height()),
310 )
311 }
312
313 pub fn constrain_dimensions(self, width: f64, height: f64) -> Size {
316 Size::new(self.constrain_width(width), self.constrain_height(height))
317 }
318
319 pub fn constrain_size_and_attempt_to_preserve_aspect_ratio(self, size: Size) -> Size {
327 if self.is_tight() {
328 return self.smallest();
329 }
330 if size.is_empty() {
331 return self.constrain(size);
332 }
333 let mut width = size.width();
334 let mut height = size.height();
335 let aspect_ratio = width / height;
336 if width > self.max_width {
337 width = self.max_width;
338 height = width / aspect_ratio;
339 }
340 if height > self.max_height {
341 height = self.max_height;
342 width = height * aspect_ratio;
343 }
344 if width < self.min_width {
345 width = self.min_width;
346 height = width / aspect_ratio;
347 }
348 if height < self.min_height {
349 height = self.min_height;
350 width = height * aspect_ratio;
351 }
352 Size::new(self.constrain_width(width), self.constrain_height(height))
353 }
354
355 pub fn biggest(self) -> Size {
357 Size::new(
358 self.constrain_width(f64::INFINITY),
359 self.constrain_height(f64::INFINITY),
360 )
361 }
362
363 pub fn smallest(self) -> Size {
365 Size::new(self.constrain_width(0.0), self.constrain_height(0.0))
366 }
367
368 pub fn has_tight_width(self) -> bool {
370 self.min_width >= self.max_width
371 }
372
373 pub fn has_tight_height(self) -> bool {
375 self.min_height >= self.max_height
376 }
377
378 pub fn has_bounded_width(self) -> bool {
380 self.max_width < f64::INFINITY
381 }
382
383 pub fn has_bounded_height(self) -> bool {
385 self.max_height < f64::INFINITY
386 }
387
388 pub fn has_infinite_width(self) -> bool {
390 self.min_width >= f64::INFINITY
391 }
392
393 pub fn has_infinite_height(self) -> bool {
395 self.min_height >= f64::INFINITY
396 }
397
398 pub fn is_satisfied_by(self, size: Size) -> bool {
400 debug_assert!(self.debug_assert_is_valid(false));
401 self.min_width <= size.width()
402 && size.width() <= self.max_width
403 && self.min_height <= size.height()
404 && size.height() <= self.max_height
405 }
406
407 pub fn truncating_div(self, factor: f64) -> BoxConstraints {
410 BoxConstraints {
411 min_width: (self.min_width / factor).trunc(),
412 max_width: (self.max_width / factor).trunc(),
413 min_height: (self.min_height / factor).trunc(),
414 max_height: (self.max_height / factor).trunc(),
415 }
416 }
417
418 pub fn lerp(
423 a: Option<BoxConstraints>,
424 b: Option<BoxConstraints>,
425 t: f64,
426 ) -> Option<BoxConstraints> {
427 if a == b {
428 return a;
429 }
430 let Some(a) = a else {
431 return Some((b.unwrap() * t).normalize());
432 };
433 let Some(b) = b else {
434 return Some((a * (1.0 - t)).normalize());
435 };
436 debug_assert!(a.debug_assert_is_valid(false));
437 debug_assert!(b.debug_assert_is_valid(false));
438 debug_assert!(
439 (a.min_width.is_finite() && b.min_width.is_finite())
440 || (a.min_width == f64::INFINITY && b.min_width == f64::INFINITY),
441 "Cannot interpolate between finite constraints and unbounded constraints."
442 );
443 debug_assert!(
444 (a.max_width.is_finite() && b.max_width.is_finite())
445 || (a.max_width == f64::INFINITY && b.max_width == f64::INFINITY),
446 "Cannot interpolate between finite constraints and unbounded constraints."
447 );
448 debug_assert!(
449 (a.min_height.is_finite() && b.min_height.is_finite())
450 || (a.min_height == f64::INFINITY && b.min_height == f64::INFINITY),
451 "Cannot interpolate between finite constraints and unbounded constraints."
452 );
453 debug_assert!(
454 (a.max_height.is_finite() && b.max_height.is_finite())
455 || (a.max_height == f64::INFINITY && b.max_height == f64::INFINITY),
456 "Cannot interpolate between finite constraints and unbounded constraints."
457 );
458 Some(
459 BoxConstraints {
460 min_width: if a.min_width.is_finite() {
461 lerp_double(Some(a.min_width), Some(b.min_width), t).unwrap()
462 } else {
463 f64::INFINITY
464 },
465 max_width: if a.max_width.is_finite() {
466 lerp_double(Some(a.max_width), Some(b.max_width), t).unwrap()
467 } else {
468 f64::INFINITY
469 },
470 min_height: if a.min_height.is_finite() {
471 lerp_double(Some(a.min_height), Some(b.min_height), t).unwrap()
472 } else {
473 f64::INFINITY
474 },
475 max_height: if a.max_height.is_finite() {
476 lerp_double(Some(a.max_height), Some(b.max_height), t).unwrap()
477 } else {
478 f64::INFINITY
479 },
480 }
481 .normalize(),
482 )
483 }
484
485 pub fn normalize(self) -> BoxConstraints {
487 if self.is_normalized() {
488 return self;
489 }
490 let min_width = if self.min_width >= 0.0 {
491 self.min_width
492 } else {
493 0.0
494 };
495 let min_height = if self.min_height >= 0.0 {
496 self.min_height
497 } else {
498 0.0
499 };
500 BoxConstraints {
501 min_width,
502 max_width: if min_width > self.max_width {
503 min_width
504 } else {
505 self.max_width
506 },
507 min_height,
508 max_height: if min_height > self.max_height {
509 min_height
510 } else {
511 self.max_height
512 },
513 }
514 }
515}
516
517impl Default for BoxConstraints {
518 fn default() -> BoxConstraints {
519 BoxConstraints::new()
520 }
521}
522
523impl Constraints for BoxConstraints {
524 fn is_tight(&self) -> bool {
525 self.has_tight_width() && self.has_tight_height()
526 }
527
528 fn is_normalized(&self) -> bool {
529 self.min_width >= 0.0
530 && self.min_width <= self.max_width
531 && self.min_height >= 0.0
532 && self.min_height <= self.max_height
533 }
534
535 fn debug_assert_is_valid(&self, is_applied_constraint: bool) -> bool {
536 if cfg!(debug_assertions) {
537 assert!(
538 !self.min_width.is_nan()
539 && !self.max_width.is_nan()
540 && !self.min_height.is_nan()
541 && !self.max_height.is_nan(),
542 "BoxConstraints has a NaN value."
543 );
544 assert!(
545 self.min_width >= 0.0,
546 "BoxConstraints has a negative minimum width."
547 );
548 assert!(
549 self.min_height >= 0.0,
550 "BoxConstraints has a negative minimum height."
551 );
552 assert!(
553 self.max_width >= self.min_width,
554 "BoxConstraints has non-normalized width constraints."
555 );
556 assert!(
557 self.max_height >= self.min_height,
558 "BoxConstraints has non-normalized height constraints."
559 );
560 if is_applied_constraint {
561 assert!(
562 self.min_width.is_finite(),
563 "BoxConstraints forces an infinite width."
564 );
565 assert!(
566 self.min_height.is_finite(),
567 "BoxConstraints forces an infinite height."
568 );
569 }
570 debug_assert!(self.is_normalized());
571 }
572 self.is_normalized()
573 }
574}
575
576impl std::ops::Mul<f64> for BoxConstraints {
577 type Output = BoxConstraints;
578
579 fn mul(self, factor: f64) -> BoxConstraints {
580 BoxConstraints {
581 min_width: self.min_width * factor,
582 max_width: self.max_width * factor,
583 min_height: self.min_height * factor,
584 max_height: self.max_height * factor,
585 }
586 }
587}
588
589impl std::ops::Div<f64> for BoxConstraints {
590 type Output = BoxConstraints;
591
592 fn div(self, factor: f64) -> BoxConstraints {
593 BoxConstraints {
594 min_width: self.min_width / factor,
595 max_width: self.max_width / factor,
596 min_height: self.min_height / factor,
597 max_height: self.max_height / factor,
598 }
599 }
600}
601
602impl std::ops::Rem<f64> for BoxConstraints {
603 type Output = BoxConstraints;
604
605 fn rem(self, value: f64) -> BoxConstraints {
606 BoxConstraints {
607 min_width: self.min_width % value,
608 max_width: self.max_width % value,
609 min_height: self.min_height % value,
610 max_height: self.max_height % value,
611 }
612 }
613}
614
615impl PartialEq for BoxConstraints {
616 fn eq(&self, other: &BoxConstraints) -> bool {
617 debug_assert!(self.debug_assert_is_valid(false));
618 debug_assert!(other.debug_assert_is_valid(false));
619 self.min_width == other.min_width
620 && self.max_width == other.max_width
621 && self.min_height == other.min_height
622 && self.max_height == other.max_height
623 }
624}
625
626impl Eq for BoxConstraints {}
627
628impl Hash for BoxConstraints {
629 fn hash<H: Hasher>(&self, state: &mut H) {
630 debug_assert!(self.debug_assert_is_valid(false));
631 self.min_width.to_bits().hash(state);
632 self.max_width.to_bits().hash(state);
633 self.min_height.to_bits().hash(state);
634 self.max_height.to_bits().hash(state);
635 }
636}
637
638impl Display for BoxConstraints {
639 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640 let annotation = if self.is_normalized() {
641 ""
642 } else {
643 "; NOT NORMALIZED"
644 };
645 if self.min_width == f64::INFINITY && self.min_height == f64::INFINITY {
646 return write!(f, "BoxConstraints(biggest{annotation})");
647 }
648 if self.min_width == 0.0
649 && self.max_width == f64::INFINITY
650 && self.min_height == 0.0
651 && self.max_height == f64::INFINITY
652 {
653 return write!(f, "BoxConstraints(unconstrained{annotation})");
654 }
655 fn describe(min: f64, max: f64, dim: &str) -> String {
656 if min == max {
657 format!("{dim}={min:.1}")
658 } else {
659 format!("{min:.1}<={dim}<={max:.1}")
660 }
661 }
662 write!(
663 f,
664 "BoxConstraints({}, {}{annotation})",
665 describe(self.min_width, self.max_width, "w"),
666 describe(self.min_height, self.max_height, "h")
667 )
668 }
669}
670
671impl Debug for BoxConstraints {
672 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673 Display::fmt(self, f)
674 }
675}
676
677pub type BoxHitTest<'r> = dyn FnOnce(&mut BoxHitTestResult<'r>, Offset) -> bool;
682
683pub struct BoxHitTestResult<'a>(&'a mut HitTestResult);
688
689impl<'a> BoxHitTestResult<'a> {
690 pub fn wrap(result: &'a mut HitTestResult) -> BoxHitTestResult<'a> {
692 BoxHitTestResult(result)
693 }
694
695 pub fn add_with_paint_transform(
710 &mut self,
711 transform: Option<Matrix4>,
712 position: Offset,
713 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
714 ) -> bool {
715 let transform = match transform {
716 Some(transform) => match transform.invert() {
717 Some(inverted) => Some(inverted),
718 None => return false,
719 },
720 None => None,
721 };
722 self.add_with_raw_transform(transform, position, hit_test)
723 }
724
725 pub fn add_with_paint_offset(
730 &mut self,
731 offset: Option<Offset>,
732 position: Offset,
733 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
734 ) -> bool {
735 let transformed_position = match offset {
736 Some(offset) => position - offset,
737 None => position,
738 };
739 if let Some(offset) = offset {
740 self.0.push_offset(Offset::ZERO - offset);
741 }
742 let is_hit = hit_test(self, transformed_position);
743 if offset.is_some() {
744 self.0.pop_transform();
745 }
746 is_hit
747 }
748
749 pub fn add_with_raw_transform(
756 &mut self,
757 transform: Option<Matrix4>,
758 position: Offset,
759 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
760 ) -> bool {
761 let transformed_position = match transform {
762 Some(transform) => transform_point(&transform, position),
763 None => position,
764 };
765 if let Some(transform) = transform {
766 self.0.push_transform(transform);
767 }
768 let is_hit = hit_test(self, transformed_position);
769 if transform.is_some() {
770 self.0.pop_transform();
771 }
772 is_hit
773 }
774
775 pub fn add_with_out_of_band_position(
780 &mut self,
781 paint_offset: Option<Offset>,
782 paint_transform: Option<Matrix4>,
783 raw_transform: Option<Matrix4>,
784 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>) -> bool,
785 ) -> bool {
786 debug_assert_eq!(
787 [
788 paint_offset.is_some(),
789 paint_transform.is_some(),
790 raw_transform.is_some()
791 ]
792 .iter()
793 .filter(|given| **given)
794 .count(),
795 1,
796 "Exactly one transform or offset argument must be provided."
797 );
798 if let Some(paint_offset) = paint_offset {
799 self.0.push_offset(Offset::ZERO - paint_offset);
800 } else if let Some(raw_transform) = raw_transform {
801 self.0.push_transform(raw_transform);
802 } else {
803 let paint_transform = paint_transform
804 .expect("checked")
805 .invert()
806 .expect("paint_transform must be invertible.");
807 self.0.push_transform(paint_transform);
808 }
809 let is_hit = hit_test(self);
810 self.0.pop_transform();
811 is_hit
812 }
813}
814
815impl Deref for BoxHitTestResult<'_> {
816 type Target = HitTestResult;
817
818 fn deref(&self) -> &HitTestResult {
819 self.0
820 }
821}
822
823impl DerefMut for BoxHitTestResult<'_> {
824 fn deref_mut(&mut self) -> &mut HitTestResult {
825 self.0
826 }
827}
828
829#[derive(Clone, Copy, Debug)]
835pub struct BoxHitTestEntry {
836 target: AnyRenderBox,
837 local_position: Offset,
838}
839
840impl BoxHitTestEntry {
841 pub fn new(target: AnyRenderBox, local_position: Offset) -> BoxHitTestEntry {
843 BoxHitTestEntry {
844 target,
845 local_position,
846 }
847 }
848
849 pub fn target(&self) -> AnyRenderBox {
851 self.target
852 }
853
854 pub fn local_position(&self) -> Offset {
856 self.local_position
857 }
858}
859
860impl HitTestTarget for BoxHitTestEntry {
861 fn handle_event(&self, app: &mut App, event: &PointerEvent, _entry: &HitTestEntry) {
862 self.target.handle_event(app, event, self);
863 }
864
865 fn retained_handle(&self) -> Option<HandleId> {
866 Some(self.target.id)
867 }
868}
869
870impl From<BoxHitTestEntry> for HitTestEntry {
871 fn from(entry: BoxHitTestEntry) -> HitTestEntry {
872 HitTestEntry::new(entry)
873 }
874}
875
876#[derive(Clone, Copy, Debug)]
878pub struct BoxParentData {
879 pub offset: Offset,
881}
882
883impl BoxParentData {
884 pub const fn new() -> BoxParentData {
886 BoxParentData {
887 offset: Offset::ZERO,
888 }
889 }
890}
891
892impl Default for BoxParentData {
893 fn default() -> BoxParentData {
894 BoxParentData::new()
895 }
896}
897
898impl ParentData for BoxParentData {
899 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
900 (id == TypeId::of::<BoxParentData>()).then_some(self)
901 }
902
903 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
904 (id == TypeId::of::<BoxParentData>()).then_some(self)
905 }
906}
907
908impl fmt::Display for BoxParentData {
909 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910 write!(f, "offset={:?}", self.offset)
911 }
912}
913
914pub trait ContainerBoxParentData: ContainerParentDataMixin<ChildType = AnyRenderBox> {
920 fn box_parent_data(&self) -> &BoxParentData;
925
926 fn box_parent_data_mut(&mut self) -> &mut BoxParentData;
928
929 fn offset(&self) -> Offset {
931 self.box_parent_data().offset
932 }
933
934 fn set_offset(&mut self, value: Offset) {
936 self.box_parent_data_mut().offset = value;
937 }
938}
939
940#[derive(Clone, Copy, PartialEq, Debug)]
942pub struct BaselineOffset(pub Option<f64>);
943
944impl BaselineOffset {
945 pub const NO_BASELINE: BaselineOffset = BaselineOffset(None);
951
952 pub fn offset(self) -> Option<f64> {
954 self.0
955 }
956
957 pub fn min_of(self, other: BaselineOffset) -> BaselineOffset {
964 match (self.0, other.0) {
965 (Some(lhs), Some(rhs)) => {
966 if lhs >= rhs {
967 other
968 } else {
969 self
970 }
971 }
972 (Some(_), None) => self,
973 (None, _) => other,
974 }
975 }
976}
977
978impl Add<f64> for BaselineOffset {
979 type Output = BaselineOffset;
980
981 fn add(self, offset: f64) -> BaselineOffset {
984 BaselineOffset(self.0.map(|value| value + offset))
985 }
986}
987
988#[derive(Clone, Copy, Debug)]
993pub enum DryLayoutFailure<'a> {
994 Reason(&'a str),
996
997 Error(&'a str),
999}
1000
1001#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1004enum IntrinsicDimension {
1005 MinWidth,
1006 MaxWidth,
1007 MinHeight,
1008 MaxHeight,
1009}
1010
1011#[derive(Clone, Copy, PartialEq, Eq, Hash)]
1013struct DoubleKey(u64);
1014
1015impl DoubleKey {
1016 fn new(value: f64) -> DoubleKey {
1017 DoubleKey(value.to_bits())
1018 }
1019}
1020
1021#[derive(Default)]
1028struct LayoutCacheStorage {
1029 cached_intrinsic_dimensions: HashMap<(IntrinsicDimension, DoubleKey), f64>,
1030 cached_dry_layout_sizes: HashMap<BoxConstraints, Size>,
1031 cached_alphabetic_baseline: HashMap<BoxConstraints, BaselineOffset>,
1032 cached_ideo_baseline: HashMap<BoxConstraints, BaselineOffset>,
1033}
1034
1035impl LayoutCacheStorage {
1036 fn clear(&mut self) -> bool {
1038 let has_cache = !self.cached_dry_layout_sizes.is_empty()
1039 || !self.cached_intrinsic_dimensions.is_empty()
1040 || !self.cached_alphabetic_baseline.is_empty()
1041 || !self.cached_ideo_baseline.is_empty();
1042 if has_cache {
1043 self.cached_dry_layout_sizes.clear();
1044 self.cached_intrinsic_dimensions.clear();
1045 self.cached_alphabetic_baseline.clear();
1046 self.cached_ideo_baseline.clear();
1047 }
1048 has_cache
1049 }
1050
1051 fn baselines(
1052 &mut self,
1053 baseline: TextBaseline,
1054 ) -> &mut HashMap<BoxConstraints, BaselineOffset> {
1055 match baseline {
1056 TextBaseline::Alphabetic => &mut self.cached_alphabetic_baseline,
1057 TextBaseline::Ideographic => &mut self.cached_ideo_baseline,
1058 }
1059 }
1060}
1061
1062pub struct RenderBoxData {
1064 pub(crate) size: Option<Size>,
1065 pub(crate) constraints: Option<BoxConstraints>,
1066 layout_cache: LayoutCacheStorage,
1067 debug_computing_this_dry_layout: bool,
1068 debug_computing_this_dry_baseline: bool,
1069}
1070
1071impl RenderBoxData {
1072 pub fn new() -> RenderBoxData {
1074 RenderBoxData {
1075 size: None,
1076 constraints: None,
1077 layout_cache: LayoutCacheStorage::default(),
1078 debug_computing_this_dry_layout: false,
1079 debug_computing_this_dry_baseline: false,
1080 }
1081 }
1082}
1083
1084impl Default for RenderBoxData {
1085 fn default() -> RenderBoxData {
1086 RenderBoxData::new()
1087 }
1088}
1089
1090#[macro_export]
1092macro_rules! render_box_accessors {
1093 () => {
1094 fn render_box_data(
1095 self: $crate::RenderHandle<Self>,
1096 app: &::inset_foundation::App,
1097 ) -> &$crate::RenderBoxData {
1098 &self.get(app).render_box
1099 }
1100 fn render_box_data_mut(
1101 self: $crate::RenderHandle<Self>,
1102 app: &mut ::inset_foundation::App,
1103 ) -> &mut $crate::RenderBoxData {
1104 &mut self.get_mut(app).render_box
1105 }
1106 };
1107}
1108
1109fn box_parent_data_of(app: &App, child: AnyRenderObject) -> &BoxParentData {
1115 child
1116 .parent_data(app)
1117 .and_then(|parent_data| parent_data.part::<BoxParentData>())
1118 .expect("parent data is not a BoxParentData")
1119}
1120
1121pub trait RenderBoxContainerDefaultsMixin:
1129 ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType: ContainerBoxParentData>
1130 + RenderBox
1131{
1132 fn default_compute_distance_to_first_actual_baseline(
1137 self: RenderHandle<Self>,
1138 app: &mut App,
1139 baseline: TextBaseline,
1140 ) -> Option<f64> {
1141 debug_assert!(!self.as_object().debug_needs_layout(app));
1142 let mut child = self.first_child(app);
1143 while let Some(current) = child {
1144 let child_parent_data = current
1145 .as_object()
1146 .parent_data_of::<Self::ParentDataType>(app);
1147 let (offset, next_sibling) =
1148 (child_parent_data.offset(), child_parent_data.next_sibling());
1149 if let Some(result) = current.get_distance_to_actual_baseline(app, baseline) {
1150 return Some(result + offset.dy());
1151 }
1152 child = next_sibling;
1153 }
1154 None
1155 }
1156
1157 fn default_compute_distance_to_highest_actual_baseline(
1162 self: RenderHandle<Self>,
1163 app: &mut App,
1164 baseline: TextBaseline,
1165 ) -> Option<f64> {
1166 debug_assert!(!self.as_object().debug_needs_layout(app));
1167 let mut min_baseline = BaselineOffset::NO_BASELINE;
1168 let mut child = self.first_child(app);
1169 while let Some(current) = child {
1170 let child_parent_data = current
1171 .as_object()
1172 .parent_data_of::<Self::ParentDataType>(app);
1173 let (offset, next_sibling) =
1174 (child_parent_data.offset(), child_parent_data.next_sibling());
1175 let candidate = BaselineOffset(current.get_distance_to_actual_baseline(app, baseline))
1176 + offset.dy();
1177 min_baseline = min_baseline.min_of(candidate);
1178 child = next_sibling;
1179 }
1180 min_baseline.offset()
1181 }
1182
1183 fn default_hit_test_children(
1193 self: RenderHandle<Self>,
1194 app: &mut App,
1195 result: &mut BoxHitTestResult<'_>,
1196 position: Offset,
1197 ) -> bool {
1198 let mut child = self.last_child(app);
1199 while let Some(current) = child {
1200 let child_parent_data = current
1202 .as_object()
1203 .parent_data_of::<Self::ParentDataType>(app);
1204 let (offset, previous_sibling) = (
1205 child_parent_data.offset(),
1206 child_parent_data.previous_sibling(),
1207 );
1208 let is_hit =
1209 result.add_with_paint_offset(Some(offset), position, |result, transformed| {
1210 debug_assert_eq!(transformed, position - offset);
1211 current.hit_test(app, result, transformed)
1212 });
1213 if is_hit {
1214 return true;
1215 }
1216 child = previous_sibling;
1217 }
1218 false
1219 }
1220
1221 fn default_paint(
1228 self: RenderHandle<Self>,
1229 app: &mut App,
1230 context: &mut PaintingContext,
1231 offset: Offset,
1232 ) {
1233 let mut child = self.first_child(app);
1234 while let Some(current) = child {
1235 let child_parent_data = current
1236 .as_object()
1237 .parent_data_of::<Self::ParentDataType>(app);
1238 let (child_offset, next_sibling) =
1239 (child_parent_data.offset(), child_parent_data.next_sibling());
1240 context.paint_child(app, current.as_object(), child_offset + offset);
1241 child = next_sibling;
1242 }
1243 }
1244}
1245
1246pub trait RenderBox: RenderObject {
1252 fn render_box_data(self: RenderHandle<Self>, app: &App) -> &RenderBoxData;
1254
1255 fn render_box_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderBoxData;
1257
1258 fn constraints(self: RenderHandle<Self>, app: &App) -> BoxConstraints {
1264 self.render_box_data(app).constraints.unwrap_or_else(|| {
1265 panic!("A RenderObject does not have any constraints before it has been laid out.")
1266 })
1267 }
1268
1269 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1273 if !child.parent_data_is::<BoxParentData>(app) {
1274 child.set_parent_data(app, BoxParentData::new());
1275 }
1276 }
1277
1278 fn get_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1280 self.as_box().get_min_intrinsic_width(app, height)
1281 }
1282
1283 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1328 let _ = (self, app, height);
1329 0.0
1330 }
1331
1332 fn get_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1334 self.as_box().get_max_intrinsic_width(app, height)
1335 }
1336
1337 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1358 let _ = (self, app, height);
1359 0.0
1360 }
1361
1362 fn get_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1364 self.as_box().get_min_intrinsic_height(app, width)
1365 }
1366
1367 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1384 let _ = (self, app, width);
1385 0.0
1386 }
1387
1388 fn get_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1390 self.as_box().get_max_intrinsic_height(app, width)
1391 }
1392
1393 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1410 let _ = (self, app, width);
1411 0.0
1412 }
1413
1414 fn get_dry_layout(
1416 self: RenderHandle<Self>,
1417 app: &mut App,
1418 constraints: BoxConstraints,
1419 ) -> Size {
1420 self.as_box().get_dry_layout(app, constraints)
1421 }
1422
1423 fn compute_dry_layout(
1448 self: RenderHandle<Self>,
1449 app: &mut App,
1450 constraints: BoxConstraints,
1451 ) -> Size {
1452 let _ = (app, constraints);
1453 self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1454 "It does not implement RenderBox::compute_dry_layout.",
1455 ));
1456 Size::ZERO
1457 }
1458
1459 fn get_dry_baseline(
1461 self: RenderHandle<Self>,
1462 app: &mut App,
1463 constraints: BoxConstraints,
1464 baseline: TextBaseline,
1465 ) -> Option<f64> {
1466 self.as_box().get_dry_baseline(app, constraints, baseline)
1467 }
1468
1469 fn compute_dry_baseline(
1493 self: RenderHandle<Self>,
1494 app: &mut App,
1495 constraints: BoxConstraints,
1496 baseline: TextBaseline,
1497 ) -> Option<f64> {
1498 let _ = (app, constraints, baseline);
1499 self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1500 "It does not implement RenderBox::compute_dry_baseline.",
1501 ));
1502 None
1503 }
1504
1505 fn debug_cannot_compute_dry_layout(self: RenderHandle<Self>, failure: DryLayoutFailure<'_>) {
1513 let _ = self;
1514 if !cfg!(debug_assertions) || debug_checking_intrinsics() {
1515 return;
1516 }
1517 match failure {
1518 DryLayoutFailure::Reason(reason) => panic!(
1519 "The {} class does not support dry layout. {reason}",
1520 std::any::type_name::<Self>()
1521 ),
1522 DryLayoutFailure::Error(error) => panic!("{error}"),
1523 }
1524 }
1525
1526 fn get_distance_to_baseline(
1528 self: RenderHandle<Self>,
1529 app: &mut App,
1530 baseline: TextBaseline,
1531 only_real: bool,
1532 ) -> Option<f64> {
1533 self.as_box()
1534 .get_distance_to_baseline(app, baseline, only_real)
1535 }
1536
1537 fn get_distance_to_actual_baseline(
1539 self: RenderHandle<Self>,
1540 app: &mut App,
1541 baseline: TextBaseline,
1542 ) -> Option<f64> {
1543 self.as_box().get_distance_to_actual_baseline(app, baseline)
1544 }
1545
1546 fn compute_distance_to_actual_baseline(
1566 self: RenderHandle<Self>,
1567 app: &mut App,
1568 baseline: TextBaseline,
1569 ) -> Option<f64> {
1570 let _ = (self, app, baseline);
1571 debug_assert!(
1572 DEBUG_DOING_BASELINE.get(),
1573 "Please see the documentation for compute_distance_to_actual_baseline for the \
1574 required calling conventions of this method."
1575 );
1576 None
1577 }
1578
1579 fn mark_needs_layout(self: RenderHandle<Self>, app: &mut App) {
1584 if self.render_box_data_mut(app).layout_cache.clear() && self.parent(app).is_some() {
1597 self.as_object().mark_parent_needs_layout(app);
1598 return;
1599 }
1600 crate::object::RenderObjectBase::mark_needs_layout(self, app);
1601 }
1602
1603 fn perform_resize(self: RenderHandle<Self>, app: &mut App) {
1610 RenderBoxBase::perform_resize(self, app);
1611 }
1612
1613 fn size(self: RenderHandle<Self>, app: &App) -> Size {
1615 self.as_box().size(app)
1616 }
1617
1618 fn apply_paint_transform(
1628 self: RenderHandle<Self>,
1629 app: &App,
1630 child: AnyRenderObject,
1631 transform: &mut Matrix4,
1632 ) {
1633 debug_assert!(child.parent(app).map(AnyRenderObject::id) == Some(self.id()));
1634 let offset = box_parent_data_of(app, child).offset;
1637 crate::object::translate(transform, offset.dx(), offset.dy());
1638 }
1639
1640 fn paint_bounds(self: RenderHandle<Self>, app: &App) -> Rect {
1642 Offset::ZERO & self.size(app)
1643 }
1644
1645 fn has_size(self: RenderHandle<Self>, app: &App) -> bool {
1647 self.render_box_data(app).size.is_some()
1648 }
1649
1650 fn handle_event(
1656 self: RenderHandle<Self>,
1657 app: &mut App,
1658 event: &PointerEvent,
1659 entry: &BoxHitTestEntry,
1660 ) {
1661 let _ = (self, app, event, entry);
1662 }
1663
1664 fn hit_test(
1679 self: RenderHandle<Self>,
1680 app: &mut App,
1681 result: &mut BoxHitTestResult<'_>,
1682 position: Offset,
1683 ) -> bool {
1684 RenderBoxBase::hit_test(self, app, result, position)
1685 }
1686
1687 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1692 let _ = self;
1693 false
1694 }
1695
1696 fn hit_test_children(
1701 self: RenderHandle<Self>,
1702 app: &mut App,
1703 result: &mut BoxHitTestResult<'_>,
1704 position: Offset,
1705 ) -> bool {
1706 let _ = (self, app, result, position);
1707 false
1708 }
1709
1710 fn set_size(self: RenderHandle<Self>, app: &mut App, size: Size) {
1713 self.as_box().set_size(app, size)
1714 }
1715
1716 fn layout(
1718 self: RenderHandle<Self>,
1719 app: &mut App,
1720 constraints: BoxConstraints,
1721 parent_uses_size: bool,
1722 ) {
1723 self.as_box().layout(app, constraints, parent_uses_size)
1724 }
1725
1726 fn as_box(self: RenderHandle<Self>) -> AnyRenderBox {
1728 AnyRenderBox {
1729 id: self.id(),
1730 vtable: const { &RenderBoxVTable::of::<Self>() },
1731 }
1732 }
1733
1734 fn as_object(self: RenderHandle<Self>) -> AnyRenderObject {
1736 self.as_box().as_object()
1737 }
1738
1739 fn adopt_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1741 self.as_object().adopt_child(app, child)
1742 }
1743
1744 fn drop_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1746 self.as_object().drop_child(app, child)
1747 }
1748
1749 fn mark_needs_paint(self: RenderHandle<Self>, app: &mut App) {
1751 self.as_object().mark_needs_paint(app)
1752 }
1753
1754 fn mark_needs_composited_layer_update(self: RenderHandle<Self>, app: &mut App) {
1756 self.as_object().mark_needs_composited_layer_update(app)
1757 }
1758
1759 fn mark_needs_compositing_bits_update(self: RenderHandle<Self>, app: &mut App) {
1761 self.as_object().mark_needs_compositing_bits_update(app)
1762 }
1763
1764 fn schedule_initial_layout(self: RenderHandle<Self>, app: &mut App) {
1766 self.as_object().schedule_initial_layout(app)
1767 }
1768
1769 fn parent(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderObject> {
1771 self.as_object().parent(app)
1772 }
1773
1774 fn owner(self: RenderHandle<Self>, app: &App) -> Option<Handle<PipelineOwner>> {
1776 self.as_object().owner(app)
1777 }
1778
1779 fn attached(self: RenderHandle<Self>, app: &App) -> bool {
1781 self.as_object().attached(app)
1782 }
1783
1784 fn debug_needs_layout(self: RenderHandle<Self>, app: &App) -> bool {
1786 self.as_object().debug_needs_layout(app)
1787 }
1788}
1789
1790pub(crate) trait RenderBoxBase: RenderBox {
1796 fn perform_resize(self: RenderHandle<Self>, app: &mut App) {
1798 let constraints = RenderBox::constraints(self, app);
1800 let size = RenderBox::compute_dry_layout(self, app, constraints);
1801 debug_assert!(size.width().is_finite() && size.height().is_finite());
1802 self.set_size(app, size);
1803 }
1804
1805 fn hit_test(
1807 self: RenderHandle<Self>,
1808 app: &mut App,
1809 result: &mut BoxHitTestResult<'_>,
1810 position: Offset,
1811 ) -> bool {
1812 debug_assert!(
1813 self.has_size(app),
1814 "Cannot hit test a render box that has never been laid out: {self:?}"
1815 );
1816 if self.size(app).contains(position)
1817 && (RenderBox::hit_test_children(self, app, result, position)
1818 || RenderBox::hit_test_self(self, app, position))
1819 {
1820 result.add(BoxHitTestEntry::new(self.as_box(), position).into());
1821 return true;
1822 }
1823 false
1824 }
1825}
1826
1827impl<T: RenderBox> RenderBoxBase for T {}
1828
1829pub(crate) struct RenderBoxVTable {
1831 pub object: RenderObjectVTable,
1832 pub box_data: fn(&App, HandleId) -> &RenderBoxData,
1833 pub box_data_mut: fn(&mut App, HandleId) -> &mut RenderBoxData,
1834 pub hit_test: fn(&mut App, HandleId, &mut BoxHitTestResult<'_>, Offset) -> bool,
1835 pub handle_event: fn(&mut App, HandleId, &PointerEvent, &BoxHitTestEntry),
1836 pub compute_min_intrinsic_width: fn(&mut App, HandleId, f64) -> f64,
1837 pub compute_max_intrinsic_width: fn(&mut App, HandleId, f64) -> f64,
1838 pub compute_min_intrinsic_height: fn(&mut App, HandleId, f64) -> f64,
1839 pub compute_max_intrinsic_height: fn(&mut App, HandleId, f64) -> f64,
1840 pub compute_dry_layout: fn(&mut App, HandleId, BoxConstraints) -> Size,
1841 pub compute_dry_baseline: fn(&mut App, HandleId, BoxConstraints, TextBaseline) -> Option<f64>,
1842 pub compute_distance_to_actual_baseline: fn(&mut App, HandleId, TextBaseline) -> Option<f64>,
1843}
1844
1845impl RenderBoxVTable {
1846 const fn of<T: RenderBox>() -> RenderBoxVTable {
1847 RenderBoxVTable {
1848 object: RenderObjectVTable::of::<T>(
1849 |app, id, child| <T as RenderBox>::setup_parent_data(resolve(id), app, child),
1850 |app, id| T::paint_bounds(resolve(id), app),
1851 |app, id, child, transform| {
1852 <T as RenderBox>::apply_paint_transform(resolve(id), app, child, transform)
1853 },
1854 |app, id| <T as RenderBox>::perform_resize(resolve(id), app),
1855 |app, id| <T as RenderBox>::mark_needs_layout(resolve(id), app),
1856 Some(|| const { &RenderBoxVTable::of::<T>() }),
1857 None,
1858 ),
1859 box_data: |app, id| T::render_box_data(resolve(id), app),
1860 box_data_mut: |app, id| T::render_box_data_mut(resolve(id), app),
1861 hit_test: |app, id, result, position| T::hit_test(resolve(id), app, result, position),
1862 handle_event: |app, id, event, entry| T::handle_event(resolve(id), app, event, entry),
1863 compute_min_intrinsic_width: |app, id, height| {
1864 T::compute_min_intrinsic_width(resolve(id), app, height)
1865 },
1866 compute_max_intrinsic_width: |app, id, height| {
1867 T::compute_max_intrinsic_width(resolve(id), app, height)
1868 },
1869 compute_min_intrinsic_height: |app, id, width| {
1870 T::compute_min_intrinsic_height(resolve(id), app, width)
1871 },
1872 compute_max_intrinsic_height: |app, id, width| {
1873 T::compute_max_intrinsic_height(resolve(id), app, width)
1874 },
1875 compute_dry_layout: |app, id, constraints| {
1876 T::compute_dry_layout(resolve(id), app, constraints)
1877 },
1878 compute_dry_baseline: |app, id, constraints, baseline| {
1879 T::compute_dry_baseline(resolve(id), app, constraints, baseline)
1880 },
1881 compute_distance_to_actual_baseline: |app, id, baseline| {
1882 T::compute_distance_to_actual_baseline(resolve(id), app, baseline)
1883 },
1884 }
1885 }
1886}
1887
1888impl<T: RenderBox> RenderHandle<T> {
1889 pub fn new_box(app: &mut App, object: T) -> RenderHandle<T> {
1891 let this = create(app, object);
1892 let data = this.render_object_data_mut(app);
1893 data.object_vtable = Some(&const { RenderBoxVTable::of::<T>() }.object);
1894 let is_repaint_boundary = this.is_repaint_boundary(app);
1898 let always_needs_compositing = this.always_needs_compositing(app);
1899 let data = this.render_object_data_mut(app);
1900 data.was_repaint_boundary = is_repaint_boundary;
1901 data.needs_compositing = is_repaint_boundary || always_needs_compositing;
1902 this
1903 }
1904}
1905
1906struct DebugDoingBaseline;
1908
1909impl DebugDoingBaseline {
1910 fn enter() -> DebugDoingBaseline {
1911 DEBUG_DOING_BASELINE.set(cfg!(debug_assertions));
1912 DebugDoingBaseline
1913 }
1914}
1915
1916impl Drop for DebugDoingBaseline {
1917 fn drop(&mut self) {
1918 DEBUG_DOING_BASELINE.set(false);
1919 }
1920}
1921
1922fn should_cache_intrinsics(this: AnyRenderBox, app: &App) -> bool {
1924 debug_assert!(debug_checking_intrinsics() || !this.as_object().debug_doing_this_resize(app));
1926 !cfg!(debug_assertions) || !debug_checking_intrinsics()
1928}
1929
1930fn memoize_intrinsic(
1932 this: AnyRenderBox,
1933 app: &mut App,
1934 dimension: IntrinsicDimension,
1935 input: f64,
1936 computer: fn(&mut App, HandleId, f64) -> f64,
1937) -> f64 {
1938 if !should_cache_intrinsics(this, app) {
1939 return computer(app, this.id, input);
1940 }
1941 let key = (dimension, DoubleKey::new(input));
1942 if let Some(cached) = this.layout_cache(app).cached_intrinsic_dimensions.get(&key) {
1943 return *cached;
1944 }
1945 let result = computer(app, this.id, input);
1946 this.layout_cache_mut(app)
1947 .cached_intrinsic_dimensions
1948 .insert(key, result);
1949 result
1950}
1951
1952fn memoize_baseline(
1954 this: AnyRenderBox,
1955 app: &mut App,
1956 constraints: BoxConstraints,
1957 baseline: TextBaseline,
1958 computer: impl FnOnce(AnyRenderBox, &mut App) -> BaselineOffset,
1959) -> BaselineOffset {
1960 if !should_cache_intrinsics(this, app) {
1961 return computer(this, app);
1962 }
1963 if let Some(cached) = this
1964 .layout_cache_mut(app)
1965 .baselines(baseline)
1966 .get(&constraints)
1967 {
1968 return *cached;
1969 }
1970 let result = computer(this, app);
1971 this.layout_cache_mut(app)
1972 .baselines(baseline)
1973 .insert(constraints, result);
1974 result
1975}
1976
1977#[derive(Clone, Copy)]
1979pub struct AnyRenderBox {
1980 id: HandleId,
1981 vtable: &'static RenderBoxVTable,
1982}
1983
1984impl PartialEq for AnyRenderBox {
1985 fn eq(&self, other: &AnyRenderBox) -> bool {
1986 self.id == other.id
1987 }
1988}
1989
1990impl Eq for AnyRenderBox {}
1991
1992impl std::hash::Hash for AnyRenderBox {
1993 fn hash<H: Hasher>(&self, state: &mut H) {
1994 self.id.hash(state);
1995 }
1996}
1997
1998impl Debug for AnyRenderBox {
1999 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2000 write!(f, "AnyRenderBox({:?})", self.id)
2001 }
2002}
2003
2004impl AnyRenderBox {
2005 pub(crate) fn from_vtable(id: HandleId, vtable: &'static RenderBoxVTable) -> AnyRenderBox {
2006 AnyRenderBox { id, vtable }
2007 }
2008
2009 pub fn has_size(self, app: &App) -> bool {
2012 self.box_data(app).size.is_some()
2013 }
2014
2015 pub fn as_object(self) -> AnyRenderObject {
2016 AnyRenderObject::from_vtable(self.id, &self.vtable.object)
2017 }
2018
2019 pub fn hit_test(
2021 self,
2022 app: &mut App,
2023 result: &mut BoxHitTestResult<'_>,
2024 position: Offset,
2025 ) -> bool {
2026 (self.vtable.hit_test)(app, self.id, result, position)
2027 }
2028
2029 pub fn handle_event(self, app: &mut App, event: &PointerEvent, entry: &BoxHitTestEntry) {
2031 (self.vtable.handle_event)(app, self.id, event, entry)
2032 }
2033
2034 fn box_data(self, app: &App) -> &RenderBoxData {
2035 (self.vtable.box_data)(app, self.id)
2036 }
2037
2038 fn box_data_mut(self, app: &mut App) -> &mut RenderBoxData {
2039 (self.vtable.box_data_mut)(app, self.id)
2040 }
2041
2042 pub fn layout(self, app: &mut App, constraints: BoxConstraints, parent_uses_size: bool) {
2044 debug_assert!(
2045 !app.is_disposed(self.id),
2046 "layout on a disposed render object"
2047 );
2048 debug_assert!(constraints.debug_assert_is_valid(true));
2049 let same = self.box_data(app).constraints == Some(constraints);
2050 self.as_object()
2051 .run_layout(app, parent_uses_size, constraints.is_tight(), same, |app| {
2052 self.box_data_mut(app).constraints = Some(constraints)
2053 });
2054 }
2055
2056 pub fn size(self, app: &App) -> Size {
2062 self.box_data(app)
2063 .size
2064 .unwrap_or_else(|| panic!("RenderBox was not laid out: {self:?}"))
2065 }
2066
2067 pub fn set_size(self, app: &mut App, size: Size) {
2069 self.box_data_mut(app).size = Some(size);
2070 }
2071
2072 pub fn constraints(self, app: &App) -> BoxConstraints {
2078 self.box_data(app).constraints.unwrap_or_else(|| {
2079 panic!("A RenderObject does not have any constraints before it has been laid out.")
2080 })
2081 }
2082
2083 pub fn get_min_intrinsic_width(self, app: &mut App, height: f64) -> f64 {
2099 debug_assert!(
2100 height >= 0.0,
2101 "The height argument to get_min_intrinsic_width was negative. If you perform \
2102 computations on another height before passing it to get_min_intrinsic_width, \
2103 consider using f64::max or clamp_double to force the value into the valid range."
2104 );
2105 memoize_intrinsic(
2106 self,
2107 app,
2108 IntrinsicDimension::MinWidth,
2109 height,
2110 self.vtable.compute_min_intrinsic_width,
2111 )
2112 }
2113
2114 pub fn get_max_intrinsic_width(self, app: &mut App, height: f64) -> f64 {
2121 debug_assert!(
2122 height >= 0.0,
2123 "The height argument to get_max_intrinsic_width was negative."
2124 );
2125 memoize_intrinsic(
2126 self,
2127 app,
2128 IntrinsicDimension::MaxWidth,
2129 height,
2130 self.vtable.compute_max_intrinsic_width,
2131 )
2132 }
2133
2134 pub fn get_min_intrinsic_height(self, app: &mut App, width: f64) -> f64 {
2140 debug_assert!(
2141 width >= 0.0,
2142 "The width argument to get_min_intrinsic_height was negative."
2143 );
2144 memoize_intrinsic(
2145 self,
2146 app,
2147 IntrinsicDimension::MinHeight,
2148 width,
2149 self.vtable.compute_min_intrinsic_height,
2150 )
2151 }
2152
2153 pub fn get_max_intrinsic_height(self, app: &mut App, width: f64) -> f64 {
2160 debug_assert!(
2161 width >= 0.0,
2162 "The width argument to get_max_intrinsic_height was negative."
2163 );
2164 memoize_intrinsic(
2165 self,
2166 app,
2167 IntrinsicDimension::MaxHeight,
2168 width,
2169 self.vtable.compute_max_intrinsic_height,
2170 )
2171 }
2172
2173 pub fn get_dry_layout(self, app: &mut App, constraints: BoxConstraints) -> Size {
2191 if !should_cache_intrinsics(self, app) {
2192 return self.compute_dry_layout(app, constraints);
2193 }
2194 if let Some(cached) = self
2195 .layout_cache(app)
2196 .cached_dry_layout_sizes
2197 .get(&constraints)
2198 {
2199 return *cached;
2200 }
2201 let result = self.compute_dry_layout(app, constraints);
2202 self.layout_cache_mut(app)
2203 .cached_dry_layout_sizes
2204 .insert(constraints, result);
2205 result
2206 }
2207
2208 fn compute_dry_layout(self, app: &mut App, constraints: BoxConstraints) -> Size {
2210 if cfg!(debug_assertions) {
2211 assert!(!self.box_data(app).debug_computing_this_dry_layout);
2212 self.box_data_mut(app).debug_computing_this_dry_layout = true;
2213 }
2214 let result = (self.vtable.compute_dry_layout)(app, self.id, constraints);
2215 if cfg!(debug_assertions) {
2216 self.box_data_mut(app).debug_computing_this_dry_layout = false;
2217 }
2218 result
2219 }
2220
2221 pub fn get_dry_baseline(
2237 self,
2238 app: &mut App,
2239 constraints: BoxConstraints,
2240 baseline: TextBaseline,
2241 ) -> Option<f64> {
2242 let baseline_offset = memoize_baseline(self, app, constraints, baseline, |this, app| {
2243 this.compute_dry_baseline(app, constraints, baseline)
2244 })
2245 .offset();
2246 if cfg!(debug_assertions) && !debug_checking_intrinsics() {
2251 assert_eq!(
2252 baseline_offset,
2253 (self.vtable.compute_dry_baseline)(app, self.id, constraints, baseline)
2254 );
2255 }
2256 baseline_offset
2257 }
2258
2259 fn compute_dry_baseline(
2261 self,
2262 app: &mut App,
2263 constraints: BoxConstraints,
2264 baseline: TextBaseline,
2265 ) -> BaselineOffset {
2266 if cfg!(debug_assertions) {
2267 assert!(!self.box_data(app).debug_computing_this_dry_baseline);
2268 self.box_data_mut(app).debug_computing_this_dry_baseline = true;
2269 }
2270 let result = BaselineOffset((self.vtable.compute_dry_baseline)(
2271 app,
2272 self.id,
2273 constraints,
2274 baseline,
2275 ));
2276 if cfg!(debug_assertions) {
2277 self.box_data_mut(app).debug_computing_this_dry_baseline = false;
2278 }
2279 result
2280 }
2281
2282 pub fn get_distance_to_baseline(
2298 self,
2299 app: &mut App,
2300 baseline: TextBaseline,
2301 only_real: bool,
2302 ) -> Option<f64> {
2303 debug_assert!(
2304 !DEBUG_DOING_BASELINE.get(),
2305 "Please see the documentation for compute_distance_to_actual_baseline for the \
2306 required calling conventions of this method."
2307 );
2308 debug_assert!(!self.as_object().debug_needs_layout(app) || debug_checking_intrinsics());
2309 let result = {
2310 let _doing_baseline = DebugDoingBaseline::enter();
2311 self.get_distance_to_actual_baseline(app, baseline)
2312 };
2313 match result {
2314 None if !only_real => Some(self.size(app).height()),
2315 result => result,
2316 }
2317 }
2318
2319 pub fn get_distance_to_actual_baseline(
2325 self,
2326 app: &mut App,
2327 baseline: TextBaseline,
2328 ) -> Option<f64> {
2329 debug_assert!(
2330 DEBUG_DOING_BASELINE.get() || !cfg!(debug_assertions),
2331 "Please see the documentation for compute_distance_to_actual_baseline for the \
2332 required calling conventions of this method."
2333 );
2334 let constraints = self
2335 .box_data(app)
2336 .constraints
2337 .expect("a RenderBox has constraints once it has been laid out");
2338 memoize_baseline(self, app, constraints, baseline, |this, app| {
2339 BaselineOffset((this.vtable.compute_distance_to_actual_baseline)(
2340 app, this.id, baseline,
2341 ))
2342 })
2343 .offset()
2344 }
2345
2346 fn layout_cache(self, app: &App) -> &LayoutCacheStorage {
2347 &self.box_data(app).layout_cache
2348 }
2349
2350 fn layout_cache_mut(self, app: &mut App) -> &mut LayoutCacheStorage {
2351 &mut self.box_data_mut(app).layout_cache
2352 }
2353
2354 pub fn box_parent_data(self, app: &App) -> &BoxParentData {
2359 box_parent_data_of(app, self.as_object())
2360 }
2361
2362 pub fn global_to_local(
2377 self,
2378 app: &App,
2379 point: Offset,
2380 ancestor: Option<AnyRenderObject>,
2381 ) -> Offset {
2382 let transform = self.as_object().get_transform_to(app, ancestor);
2397 let Some(transform) = transform.invert() else {
2398 return Offset::ZERO;
2399 };
2400
2401 let n = [0.0, 0.0, 1.0];
2404 let i = crate::object::perspective_transform(&transform, [0.0, 0.0, 0.0]);
2405 let d = subtract(
2406 crate::object::perspective_transform(&transform, [0.0, 0.0, 1.0]),
2407 i,
2408 );
2409 let s = crate::object::perspective_transform(&transform, [point.dx(), point.dy(), 0.0]);
2410 let p = subtract(s, scale(d, dot(n, s) / dot(n, d)));
2412 Offset::new(p[0], p[1])
2413 }
2414
2415 pub fn local_to_global(
2426 self,
2427 app: &App,
2428 point: Offset,
2429 ancestor: Option<AnyRenderObject>,
2430 ) -> Offset {
2431 inset_painting::transform_point(&self.as_object().get_transform_to(app, ancestor), point)
2432 }
2433
2434 pub fn parent_data_of_mut<P: crate::object::ParentData + 'static>(
2436 self,
2437 app: &mut App,
2438 ) -> &mut P {
2439 self.as_object().parent_data_of_mut(app)
2440 }
2441
2442 pub fn parent_data_is<P: crate::object::ParentData + 'static>(self, app: &App) -> bool {
2444 self.as_object().parent_data_is::<P>(app)
2445 }
2446}
2447
2448impl crate::object::ErasedRenderObject for AnyRenderBox {
2449 fn as_object(self) -> AnyRenderObject {
2450 AnyRenderBox::as_object(self)
2451 }
2452
2453 fn from_object(object: AnyRenderObject) -> AnyRenderBox {
2454 object
2455 .as_box()
2456 .expect("the render object is not a RenderBox")
2457 }
2458}
2459
2460impl From<AnyRenderBox> for AnyRenderObject {
2461 fn from(box_: AnyRenderBox) -> AnyRenderObject {
2462 box_.as_object()
2463 }
2464}
2465
2466fn subtract(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
2467 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
2468}
2469
2470fn scale(a: [f64; 3], factor: f64) -> [f64; 3] {
2471 [a[0] * factor, a[1] * factor, a[2] * factor]
2472}
2473
2474fn dot(a: [f64; 3], b: [f64; 3]) -> f64 {
2475 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
2476}
2477
2478#[cfg(test)]
2479mod tests {
2480 use super::*;
2481
2482 #[test]
2483 fn to_string_marks_biggest_unconstrained_and_tight() {
2484 assert!(
2485 BoxConstraints::expand(None, None)
2486 .to_string()
2487 .contains("biggest")
2488 );
2489 assert!(BoxConstraints::new().to_string().contains("unconstrained"));
2490 assert!(
2491 BoxConstraints::tight_for(Some(50.0), None)
2492 .to_string()
2493 .contains("w=50")
2494 );
2495 }
2496
2497 #[test]
2498 fn copy_with_replaces_fields() {
2499 let constraints = BoxConstraints::new()
2500 .min_width(3.0)
2501 .max_width(7.0)
2502 .min_height(11.0)
2503 .max_height(17.0);
2504 let copy = constraints.copy_with();
2505 assert_eq!(copy, constraints);
2506 let copy = constraints
2507 .copy_with()
2508 .min_width(13.0)
2509 .max_width(17.0)
2510 .min_height(111.0)
2511 .max_height(117.0);
2512 assert_eq!(copy.min_width, 13.0);
2513 assert_eq!(copy.max_width, 17.0);
2514 assert_eq!(copy.min_height, 111.0);
2515 assert_eq!(copy.max_height, 117.0);
2516 assert_ne!(copy, constraints);
2517 let mut copy_hasher = std::collections::hash_map::DefaultHasher::new();
2518 let mut original_hasher = std::collections::hash_map::DefaultHasher::new();
2519 copy.hash(&mut copy_hasher);
2520 constraints.hash(&mut original_hasher);
2521 assert_ne!(copy_hasher.finish(), original_hasher.finish());
2522 }
2523
2524 #[test]
2525 fn operators() {
2526 let constraints = BoxConstraints::new()
2527 .min_width(3.0)
2528 .max_width(7.0)
2529 .min_height(11.0)
2530 .max_height(17.0);
2531 let copy = constraints * 2.0;
2532 assert_eq!(copy.min_width, 6.0);
2533 assert_eq!(copy.max_width, 14.0);
2534 assert_eq!(copy.min_height, 22.0);
2535 assert_eq!(copy.max_height, 34.0);
2536 assert_eq!(copy / 2.0, constraints);
2537 let copy = constraints.truncating_div(2.0);
2538 assert_eq!(copy.min_width, 1.0);
2539 assert_eq!(copy.max_width, 3.0);
2540 assert_eq!(copy.min_height, 5.0);
2541 assert_eq!(copy.max_height, 8.0);
2542 let copy = constraints % 3.0;
2543 assert_eq!(copy.min_width, 0.0);
2544 assert_eq!(copy.max_width, 1.0);
2545 assert_eq!(copy.min_height, 2.0);
2546 assert_eq!(copy.max_height, 2.0);
2547 }
2548
2549 #[test]
2550 fn lerp_from_null_and_between() {
2551 assert!(BoxConstraints::lerp(None, None, 0.5).is_none());
2552 let constraints = BoxConstraints::new()
2553 .min_width(3.0)
2554 .max_width(7.0)
2555 .min_height(11.0)
2556 .max_height(17.0);
2557 let copy = BoxConstraints::lerp(None, Some(constraints), 0.5).unwrap();
2558 assert!((copy.min_width - 1.5).abs() < 1e-9);
2559 assert!((copy.max_width - 3.5).abs() < 1e-9);
2560 assert!((copy.min_height - 5.5).abs() < 1e-9);
2561 assert!((copy.max_height - 8.5).abs() < 1e-9);
2562 let copy = BoxConstraints::lerp(Some(constraints), None, 0.5).unwrap();
2563 assert!((copy.min_width - 1.5).abs() < 1e-9);
2564 let copy = BoxConstraints::lerp(
2565 Some(
2566 BoxConstraints::new()
2567 .min_width(13.0)
2568 .max_width(17.0)
2569 .min_height(111.0)
2570 .max_height(117.0),
2571 ),
2572 Some(constraints),
2573 0.2,
2574 )
2575 .unwrap();
2576 assert!((copy.min_width - 11.0).abs() < 1e-9);
2577 assert!((copy.max_width - 15.0).abs() < 1e-9);
2578 assert!((copy.min_height - 91.0).abs() < 1e-9);
2579 assert!((copy.max_height - 97.0).abs() < 1e-9);
2580 }
2581
2582 #[test]
2583 fn lerp_unbounded_axes() {
2584 let constraints1 = BoxConstraints::new()
2585 .min_width(f64::INFINITY)
2586 .min_height(10.0)
2587 .max_height(20.0);
2588 let constraints2 = BoxConstraints::new()
2589 .min_width(f64::INFINITY)
2590 .min_height(20.0)
2591 .max_height(30.0);
2592 let constraints3 = BoxConstraints::new()
2593 .min_width(f64::INFINITY)
2594 .min_height(15.0)
2595 .max_height(25.0);
2596 assert_eq!(
2597 BoxConstraints::lerp(Some(constraints1), Some(constraints2), 0.5),
2598 Some(constraints3)
2599 );
2600 }
2601
2602 #[test]
2603 fn lerp_overshoot_is_normalized() {
2604 assert_eq!(
2605 BoxConstraints::lerp(
2606 Some(BoxConstraints::tight_for(None, Some(25.0))),
2607 Some(BoxConstraints::tight_for(None, Some(0.0))),
2608 1.2,
2609 ),
2610 Some(BoxConstraints::tight_for(None, Some(0.0)))
2611 );
2612 let lerped = BoxConstraints::lerp(
2613 Some(BoxConstraints::tight(Size::new(10.0, 10.0))),
2614 Some(BoxConstraints::new().max_width(100.0).max_height(100.0)),
2615 -0.1,
2616 )
2617 .unwrap();
2618 assert!(lerped.is_normalized());
2619 assert_eq!(lerped, BoxConstraints::tight(Size::new(11.0, 11.0)));
2620 }
2621
2622 #[test]
2623 fn normalize_raises_inverted_max() {
2624 let constraints = BoxConstraints::new()
2625 .min_width(3.0)
2626 .max_width(2.0)
2627 .min_height(11.0)
2628 .max_height(18.0);
2629 let copy = constraints.normalize();
2630 assert_eq!(copy.min_width, 3.0);
2631 assert_eq!(copy.max_width, 3.0);
2632 assert_eq!(copy.min_height, 11.0);
2633 assert_eq!(copy.max_height, 18.0);
2634 }
2635
2636 #[test]
2637 fn from_view_constraints() {
2638 let unconstrained = BoxConstraints::from_view_constraints(ViewConstraints::default());
2639 assert_eq!(unconstrained, BoxConstraints::new());
2640 let constraints =
2641 BoxConstraints::from_view_constraints(ViewConstraints::new(1.0, 2.0, 3.0, 4.0));
2642 assert_eq!(
2643 constraints,
2644 BoxConstraints::new()
2645 .min_width(1.0)
2646 .max_width(2.0)
2647 .min_height(3.0)
2648 .max_height(4.0)
2649 );
2650 }
2651
2652 #[test]
2653 fn constrain_size_and_attempt_to_preserve_aspect_ratio_empty_size() {
2654 let constraints = BoxConstraints::new()
2655 .min_width(10.0)
2656 .max_width(20.0)
2657 .min_height(10.0)
2658 .max_height(20.0);
2659 let constrained =
2660 constraints.constrain_size_and_attempt_to_preserve_aspect_ratio(Size::new(15.0, 0.0));
2661 assert_eq!(constrained, Size::new(15.0, 10.0));
2662 }
2663}
2664
2665#[cfg(test)]
2666mod hit_test_tests {
2667 use super::*;
2668
2669 #[derive(Debug)]
2670 struct DummyHitTestTarget;
2671
2672 impl HitTestTarget for DummyHitTestTarget {
2673 fn handle_event(&self, _app: &mut App, _event: &PointerEvent, _entry: &HitTestEntry) {}
2674 }
2675
2676 #[test]
2678 fn wrapping_shares_the_path() {
2679 let transform = Matrix4::translation(40.0, 150.0);
2680 let mut wrapped = HitTestResult::new();
2681 wrapped.push_transform(transform);
2682 wrapped.add(HitTestEntry::new(DummyHitTestTarget));
2683 assert_eq!(wrapped.path().len(), 1);
2684 assert_eq!(wrapped.path()[0].transform(), Some(transform));
2685
2686 let mut wrapping = BoxHitTestResult::wrap(&mut wrapped);
2687 wrapping.add(HitTestEntry::new(DummyHitTestTarget));
2688 assert_eq!(wrapping.path().len(), 2);
2689 assert_eq!(wrapping.path()[1].transform(), Some(transform));
2690
2691 wrapped.add(HitTestEntry::new(DummyHitTestTarget));
2692 assert_eq!(wrapped.path().len(), 3);
2693 assert_eq!(wrapped.path()[2].transform(), Some(transform));
2694 }
2695
2696 #[test]
2698 fn add_with_paint_transform() {
2699 let mut base = HitTestResult::new();
2700 let mut result = BoxHitTestResult::wrap(&mut base);
2701 let mut positions = Vec::new();
2702
2703 let is_hit = result.add_with_paint_transform(None, Offset::ZERO, |_, position| {
2704 positions.push(position);
2705 true
2706 });
2707 assert!(is_hit);
2708 assert_eq!(positions, [Offset::ZERO]);
2709 positions.clear();
2710
2711 let is_hit = result.add_with_paint_transform(
2712 Some(Matrix4::translation(20.0, 30.0)),
2713 Offset::ZERO,
2714 |_, position| {
2715 positions.push(position);
2716 true
2717 },
2718 );
2719 assert!(is_hit);
2720 assert_eq!(positions, [Offset::new(-20.0, -30.0)]);
2721 positions.clear();
2722
2723 let position = Offset::new(3.0, 4.0);
2724 let is_hit = result.add_with_paint_transform(None, position, |_, position| {
2725 positions.push(position);
2726 false
2727 });
2728 assert!(!is_hit);
2729 assert_eq!(positions, [position]);
2730 positions.clear();
2731
2732 let is_hit = result.add_with_paint_transform(
2733 Some(Matrix4::translation(20.0, 30.0)),
2734 position,
2735 |_, position| {
2736 positions.push(position);
2737 true
2738 },
2739 );
2740 assert!(is_hit);
2741 assert_eq!(positions, [position - Offset::new(20.0, 30.0)]);
2742 positions.clear();
2743
2744 let is_hit = result.add_with_paint_transform(
2746 Some(Matrix4::scale(0.0, 0.0)),
2747 position,
2748 |_, position| {
2749 positions.push(position);
2750 true
2751 },
2752 );
2753 assert!(!is_hit);
2754 assert!(positions.is_empty());
2755 }
2756
2757 #[test]
2759 fn add_with_paint_offset() {
2760 let mut base = HitTestResult::new();
2761 let mut result = BoxHitTestResult::wrap(&mut base);
2762 let mut positions = Vec::new();
2763
2764 let is_hit = result.add_with_paint_offset(None, Offset::ZERO, |_, position| {
2765 positions.push(position);
2766 true
2767 });
2768 assert!(is_hit);
2769 assert_eq!(positions, [Offset::ZERO]);
2770 positions.clear();
2771
2772 let is_hit = result.add_with_paint_offset(
2773 Some(Offset::new(55.0, 32.0)),
2774 Offset::ZERO,
2775 |_, position| {
2776 positions.push(position);
2777 true
2778 },
2779 );
2780 assert!(is_hit);
2781 assert_eq!(positions, [Offset::new(-55.0, -32.0)]);
2782 positions.clear();
2783
2784 let position = Offset::new(3.0, 4.0);
2785 let is_hit = result.add_with_paint_offset(
2786 Some(Offset::new(55.0, 32.0)),
2787 position,
2788 |result, position| {
2789 result.add(HitTestEntry::new(DummyHitTestTarget));
2790 positions.push(position);
2791 true
2792 },
2793 );
2794 assert!(is_hit);
2795 assert_eq!(positions, [position - Offset::new(55.0, 32.0)]);
2796 assert_eq!(
2797 result.path()[0].transform(),
2798 Some(Matrix4::translation(-55.0, -32.0)),
2799 "the entry records the transform to the child"
2800 );
2801 }
2802
2803 #[test]
2805 fn add_with_raw_transform() {
2806 let mut base = HitTestResult::new();
2807 let mut result = BoxHitTestResult::wrap(&mut base);
2808 let mut positions = Vec::new();
2809
2810 let is_hit = result.add_with_raw_transform(
2811 Some(Matrix4::translation(20.0, 30.0)),
2812 Offset::ZERO,
2813 |_, position| {
2814 positions.push(position);
2815 true
2816 },
2817 );
2818 assert!(is_hit);
2819 assert_eq!(positions, [Offset::new(20.0, 30.0)]);
2820 positions.clear();
2821
2822 let position = Offset::new(3.0, 4.0);
2823 let is_hit =
2824 result.add_with_raw_transform(Some(Matrix4::IDENTITY), position, |_, position| {
2825 positions.push(position);
2826 true
2827 });
2828 assert!(is_hit);
2829 assert_eq!(positions, [position]);
2830 }
2831}
2832
2833#[cfg(test)]
2834mod intrinsics_tests {
2835 use inset_foundation::AppCell;
2836 use std::cell::Cell;
2837 use std::rc::Rc;
2838
2839 use super::*;
2840 use crate::object::RenderObjectData;
2841
2842 struct CountingBox {
2845 render_object: RenderObjectData,
2846 render_box: RenderBoxData,
2847 computations: Rc<Cell<u32>>,
2848 }
2849
2850 impl RenderObject for CountingBox {
2851 crate::render_object_accessors!();
2852
2853 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2854 let size = self.constraints(app).smallest();
2855 self.set_size(app, size);
2856 }
2857 }
2858
2859 impl RenderBox for CountingBox {
2860 crate::render_box_accessors!();
2861
2862 fn compute_min_intrinsic_width(
2863 self: RenderHandle<Self>,
2864 app: &mut App,
2865 height: f64,
2866 ) -> f64 {
2867 let computations = self.get(app).computations.clone();
2868 computations.set(computations.get() + 1);
2869 100.0 + height
2870 }
2871 }
2872
2873 #[test]
2876 fn the_intrinsic_cache_is_reused_and_cleared_by_mark_needs_layout() {
2877 let cell = AppCell::new();
2878 let mut app = cell.borrow_mut();
2879 let computations = Rc::new(Cell::new(0));
2880 let box_ = RenderHandle::new_box(
2881 &mut app,
2882 CountingBox {
2883 render_object: RenderObjectData::new(),
2884 render_box: RenderBoxData::new(),
2885 computations: computations.clone(),
2886 },
2887 );
2888
2889 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2890 assert_eq!(computations.get(), 1);
2891
2892 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2893 assert_eq!(computations.get(), 1, "the memoized value is reused");
2894
2895 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 10.0), 110.0);
2896 assert_eq!(computations.get(), 2, "another height is another entry");
2897
2898 box_.mark_needs_layout(&mut app);
2899 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2900 assert_eq!(computations.get(), 3, "mark_needs_layout cleared the cache");
2901 }
2902
2903 #[test]
2906 fn clearing_a_cached_intrinsic_marks_the_parent() {
2907 let cell = AppCell::new();
2908 let mut app = cell.borrow_mut();
2909 let computations = Rc::new(Cell::new(0));
2910 let child = RenderHandle::new_box(
2911 &mut app,
2912 CountingBox {
2913 render_object: RenderObjectData::new(),
2914 render_box: RenderBoxData::new(),
2915 computations,
2916 },
2917 );
2918 let parent = crate::proxy_box::RenderConstrainedBox::new(
2919 &mut app,
2920 BoxConstraints::new(),
2921 Some(child.as_box()),
2922 );
2923 parent.layout(&mut app, BoxConstraints::new().max_width(50.0), false);
2924 assert!(!parent.debug_needs_layout(&app));
2925
2926 child.as_box().get_min_intrinsic_width(&mut app, 0.0);
2927 child.mark_needs_layout(&mut app);
2928 assert!(
2929 parent.debug_needs_layout(&app),
2930 "the parent read an intrinsic, so it has to lay out again"
2931 );
2932 }
2933}