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::{
19 HitTestEntry, HitTestResult, HitTestTarget, PointerEvent, remove_perspective_transform,
20};
21use inset_painting::EdgeInsetsGeometry;
22use inset_painting::transform_point;
23
24use crate::object::{
25 AnyRenderObject, Constraints, ContainerParentDataMixin, ContainerRenderObjectMixin, ParentData,
26 RenderHandle, RenderObject, RenderObjectVTable, create, debug_checking_intrinsics, resolve,
27};
28use crate::painting_context::PaintingContext;
29use crate::pipeline_owner::PipelineOwner;
30
31thread_local! {
32 static DEBUG_DOING_BASELINE: Cell<bool> = const { Cell::new(false) };
33}
34
35#[derive(Clone, Copy)]
43pub struct BoxConstraints {
44 pub min_width: f64,
46 pub max_width: f64,
50 pub min_height: f64,
52 pub max_height: f64,
56}
57
58impl BoxConstraints {
59 pub const fn new() -> BoxConstraints {
61 BoxConstraints {
62 min_width: 0.0,
63 max_width: f64::INFINITY,
64 min_height: 0.0,
65 max_height: f64::INFINITY,
66 }
67 }
68
69 pub const fn min_width(mut self, value: f64) -> BoxConstraints {
71 self.min_width = value;
72 self
73 }
74
75 pub const fn max_width(mut self, value: f64) -> BoxConstraints {
77 self.max_width = value;
78 self
79 }
80
81 pub const fn min_height(mut self, value: f64) -> BoxConstraints {
83 self.min_height = value;
84 self
85 }
86
87 pub const fn max_height(mut self, value: f64) -> BoxConstraints {
89 self.max_height = value;
90 self
91 }
92
93 pub fn tight(size: Size) -> BoxConstraints {
95 BoxConstraints {
96 min_width: size.width(),
97 max_width: size.width(),
98 min_height: size.height(),
99 max_height: size.height(),
100 }
101 }
102
103 pub const fn tight_for(width: Option<f64>, height: Option<f64>) -> BoxConstraints {
105 BoxConstraints {
106 min_width: match width {
107 Some(width) => width,
108 None => 0.0,
109 },
110 max_width: match width {
111 Some(width) => width,
112 None => f64::INFINITY,
113 },
114 min_height: match height {
115 Some(height) => height,
116 None => 0.0,
117 },
118 max_height: match height {
119 Some(height) => height,
120 None => f64::INFINITY,
121 },
122 }
123 }
124
125 pub const fn tight_for_finite(width: f64, height: f64) -> BoxConstraints {
128 BoxConstraints {
129 min_width: if width != f64::INFINITY { width } else { 0.0 },
130 max_width: if width != f64::INFINITY {
131 width
132 } else {
133 f64::INFINITY
134 },
135 min_height: if height != f64::INFINITY { height } else { 0.0 },
136 max_height: if height != f64::INFINITY {
137 height
138 } else {
139 f64::INFINITY
140 },
141 }
142 }
143
144 pub fn loose(size: Size) -> BoxConstraints {
146 BoxConstraints {
147 min_width: 0.0,
148 max_width: size.width(),
149 min_height: 0.0,
150 max_height: size.height(),
151 }
152 }
153
154 pub const fn expand(width: Option<f64>, height: Option<f64>) -> BoxConstraints {
156 BoxConstraints {
157 min_width: match width {
158 Some(width) => width,
159 None => f64::INFINITY,
160 },
161 max_width: match width {
162 Some(width) => width,
163 None => f64::INFINITY,
164 },
165 min_height: match height {
166 Some(height) => height,
167 None => f64::INFINITY,
168 },
169 max_height: match height {
170 Some(height) => height,
171 None => f64::INFINITY,
172 },
173 }
174 }
175
176 pub const fn from_view_constraints(constraints: ViewConstraints) -> BoxConstraints {
178 BoxConstraints {
179 min_width: constraints.min_width,
180 max_width: constraints.max_width,
181 min_height: constraints.min_height,
182 max_height: constraints.max_height,
183 }
184 }
185
186 pub const fn copy_with(self) -> BoxConstraints {
188 self
189 }
190
191 pub fn deflate(self, edges: EdgeInsetsGeometry) -> BoxConstraints {
193 debug_assert!(self.debug_assert_is_valid(false));
194 let horizontal = edges.horizontal();
195 let vertical = edges.vertical();
196 let deflated_min_width = (self.min_width - horizontal).max(0.0);
197 let deflated_min_height = (self.min_height - vertical).max(0.0);
198 BoxConstraints {
199 min_width: deflated_min_width,
200 max_width: (self.max_width - horizontal).max(deflated_min_width),
201 min_height: deflated_min_height,
202 max_height: (self.max_height - vertical).max(deflated_min_height),
203 }
204 }
205
206 pub fn loosen(self) -> BoxConstraints {
208 debug_assert!(self.debug_assert_is_valid(false));
209 BoxConstraints {
210 min_width: 0.0,
211 max_width: self.max_width,
212 min_height: 0.0,
213 max_height: self.max_height,
214 }
215 }
216
217 pub fn enforce(self, constraints: BoxConstraints) -> BoxConstraints {
220 BoxConstraints {
221 min_width: clamp_double(self.min_width, constraints.min_width, constraints.max_width),
222 max_width: clamp_double(self.max_width, constraints.min_width, constraints.max_width),
223 min_height: clamp_double(
224 self.min_height,
225 constraints.min_height,
226 constraints.max_height,
227 ),
228 max_height: clamp_double(
229 self.max_height,
230 constraints.min_height,
231 constraints.max_height,
232 ),
233 }
234 }
235
236 pub fn tighten(self, width: Option<f64>, height: Option<f64>) -> BoxConstraints {
240 BoxConstraints {
241 min_width: match width {
242 Some(width) => clamp_double(width, self.min_width, self.max_width),
243 None => self.min_width,
244 },
245 max_width: match width {
246 Some(width) => clamp_double(width, self.min_width, self.max_width),
247 None => self.max_width,
248 },
249 min_height: match height {
250 Some(height) => clamp_double(height, self.min_height, self.max_height),
251 None => self.min_height,
252 },
253 max_height: match height {
254 Some(height) => clamp_double(height, self.min_height, self.max_height),
255 None => self.max_height,
256 },
257 }
258 }
259
260 pub const fn flipped(self) -> BoxConstraints {
262 BoxConstraints {
263 min_width: self.min_height,
264 max_width: self.max_height,
265 min_height: self.min_width,
266 max_height: self.max_width,
267 }
268 }
269
270 pub const fn width_constraints(self) -> BoxConstraints {
273 BoxConstraints {
274 min_width: self.min_width,
275 max_width: self.max_width,
276 min_height: 0.0,
277 max_height: f64::INFINITY,
278 }
279 }
280
281 pub const fn height_constraints(self) -> BoxConstraints {
284 BoxConstraints {
285 min_width: 0.0,
286 max_width: f64::INFINITY,
287 min_height: self.min_height,
288 max_height: self.max_height,
289 }
290 }
291
292 pub fn constrain_width(self, width: f64) -> f64 {
295 debug_assert!(self.debug_assert_is_valid(false));
296 clamp_double(width, self.min_width, self.max_width)
297 }
298
299 pub fn constrain_height(self, height: f64) -> f64 {
302 debug_assert!(self.debug_assert_is_valid(false));
303 clamp_double(height, self.min_height, self.max_height)
304 }
305
306 pub fn constrain(self, size: Size) -> Size {
309 Size::new(
310 self.constrain_width(size.width()),
311 self.constrain_height(size.height()),
312 )
313 }
314
315 pub fn constrain_dimensions(self, width: f64, height: f64) -> Size {
318 Size::new(self.constrain_width(width), self.constrain_height(height))
319 }
320
321 pub fn constrain_size_and_attempt_to_preserve_aspect_ratio(self, size: Size) -> Size {
329 if self.is_tight() {
330 return self.smallest();
331 }
332 if size.is_empty() {
333 return self.constrain(size);
334 }
335 let mut width = size.width();
336 let mut height = size.height();
337 let aspect_ratio = width / height;
338 if width > self.max_width {
339 width = self.max_width;
340 height = width / aspect_ratio;
341 }
342 if height > self.max_height {
343 height = self.max_height;
344 width = height * aspect_ratio;
345 }
346 if width < self.min_width {
347 width = self.min_width;
348 height = width / aspect_ratio;
349 }
350 if height < self.min_height {
351 height = self.min_height;
352 width = height * aspect_ratio;
353 }
354 Size::new(self.constrain_width(width), self.constrain_height(height))
355 }
356
357 pub fn biggest(self) -> Size {
359 Size::new(
360 self.constrain_width(f64::INFINITY),
361 self.constrain_height(f64::INFINITY),
362 )
363 }
364
365 pub fn smallest(self) -> Size {
367 Size::new(self.constrain_width(0.0), self.constrain_height(0.0))
368 }
369
370 pub fn has_tight_width(self) -> bool {
372 self.min_width >= self.max_width
373 }
374
375 pub fn has_tight_height(self) -> bool {
377 self.min_height >= self.max_height
378 }
379
380 pub fn has_bounded_width(self) -> bool {
382 self.max_width < f64::INFINITY
383 }
384
385 pub fn has_bounded_height(self) -> bool {
387 self.max_height < f64::INFINITY
388 }
389
390 pub fn has_infinite_width(self) -> bool {
392 self.min_width >= f64::INFINITY
393 }
394
395 pub fn has_infinite_height(self) -> bool {
397 self.min_height >= f64::INFINITY
398 }
399
400 pub fn is_satisfied_by(self, size: Size) -> bool {
402 debug_assert!(self.debug_assert_is_valid(false));
403 self.min_width <= size.width()
404 && size.width() <= self.max_width
405 && self.min_height <= size.height()
406 && size.height() <= self.max_height
407 }
408
409 pub fn truncating_div(self, factor: f64) -> BoxConstraints {
412 BoxConstraints {
413 min_width: (self.min_width / factor).trunc(),
414 max_width: (self.max_width / factor).trunc(),
415 min_height: (self.min_height / factor).trunc(),
416 max_height: (self.max_height / factor).trunc(),
417 }
418 }
419
420 pub fn lerp(
425 a: Option<BoxConstraints>,
426 b: Option<BoxConstraints>,
427 t: f64,
428 ) -> Option<BoxConstraints> {
429 if a == b {
430 return a;
431 }
432 let Some(a) = a else {
433 return Some((b.unwrap() * t).normalize());
434 };
435 let Some(b) = b else {
436 return Some((a * (1.0 - t)).normalize());
437 };
438 debug_assert!(a.debug_assert_is_valid(false));
439 debug_assert!(b.debug_assert_is_valid(false));
440 debug_assert!(
441 (a.min_width.is_finite() && b.min_width.is_finite())
442 || (a.min_width == f64::INFINITY && b.min_width == f64::INFINITY),
443 "Cannot interpolate between finite constraints and unbounded constraints."
444 );
445 debug_assert!(
446 (a.max_width.is_finite() && b.max_width.is_finite())
447 || (a.max_width == f64::INFINITY && b.max_width == f64::INFINITY),
448 "Cannot interpolate between finite constraints and unbounded constraints."
449 );
450 debug_assert!(
451 (a.min_height.is_finite() && b.min_height.is_finite())
452 || (a.min_height == f64::INFINITY && b.min_height == f64::INFINITY),
453 "Cannot interpolate between finite constraints and unbounded constraints."
454 );
455 debug_assert!(
456 (a.max_height.is_finite() && b.max_height.is_finite())
457 || (a.max_height == f64::INFINITY && b.max_height == f64::INFINITY),
458 "Cannot interpolate between finite constraints and unbounded constraints."
459 );
460 Some(
461 BoxConstraints {
462 min_width: if a.min_width.is_finite() {
463 lerp_double(Some(a.min_width), Some(b.min_width), t).unwrap()
464 } else {
465 f64::INFINITY
466 },
467 max_width: if a.max_width.is_finite() {
468 lerp_double(Some(a.max_width), Some(b.max_width), t).unwrap()
469 } else {
470 f64::INFINITY
471 },
472 min_height: if a.min_height.is_finite() {
473 lerp_double(Some(a.min_height), Some(b.min_height), t).unwrap()
474 } else {
475 f64::INFINITY
476 },
477 max_height: if a.max_height.is_finite() {
478 lerp_double(Some(a.max_height), Some(b.max_height), t).unwrap()
479 } else {
480 f64::INFINITY
481 },
482 }
483 .normalize(),
484 )
485 }
486
487 pub fn normalize(self) -> BoxConstraints {
489 if self.is_normalized() {
490 return self;
491 }
492 let min_width = if self.min_width >= 0.0 {
493 self.min_width
494 } else {
495 0.0
496 };
497 let min_height = if self.min_height >= 0.0 {
498 self.min_height
499 } else {
500 0.0
501 };
502 BoxConstraints {
503 min_width,
504 max_width: if min_width > self.max_width {
505 min_width
506 } else {
507 self.max_width
508 },
509 min_height,
510 max_height: if min_height > self.max_height {
511 min_height
512 } else {
513 self.max_height
514 },
515 }
516 }
517}
518
519impl Default for BoxConstraints {
520 fn default() -> BoxConstraints {
521 BoxConstraints::new()
522 }
523}
524
525impl Constraints for BoxConstraints {
526 fn is_tight(&self) -> bool {
527 self.has_tight_width() && self.has_tight_height()
528 }
529
530 fn is_normalized(&self) -> bool {
531 self.min_width >= 0.0
532 && self.min_width <= self.max_width
533 && self.min_height >= 0.0
534 && self.min_height <= self.max_height
535 }
536
537 fn debug_assert_is_valid(&self, is_applied_constraint: bool) -> bool {
538 if cfg!(debug_assertions) {
539 assert!(
540 !self.min_width.is_nan()
541 && !self.max_width.is_nan()
542 && !self.min_height.is_nan()
543 && !self.max_height.is_nan(),
544 "BoxConstraints has a NaN value."
545 );
546 assert!(
547 self.min_width >= 0.0,
548 "BoxConstraints has a negative minimum width."
549 );
550 assert!(
551 self.min_height >= 0.0,
552 "BoxConstraints has a negative minimum height."
553 );
554 assert!(
555 self.max_width >= self.min_width,
556 "BoxConstraints has non-normalized width constraints."
557 );
558 assert!(
559 self.max_height >= self.min_height,
560 "BoxConstraints has non-normalized height constraints."
561 );
562 if is_applied_constraint {
563 assert!(
564 self.min_width.is_finite(),
565 "BoxConstraints forces an infinite width."
566 );
567 assert!(
568 self.min_height.is_finite(),
569 "BoxConstraints forces an infinite height."
570 );
571 }
572 debug_assert!(self.is_normalized());
573 }
574 self.is_normalized()
575 }
576}
577
578impl std::ops::Mul<f64> for BoxConstraints {
579 type Output = BoxConstraints;
580
581 fn mul(self, factor: f64) -> BoxConstraints {
582 BoxConstraints {
583 min_width: self.min_width * factor,
584 max_width: self.max_width * factor,
585 min_height: self.min_height * factor,
586 max_height: self.max_height * factor,
587 }
588 }
589}
590
591impl std::ops::Div<f64> for BoxConstraints {
592 type Output = BoxConstraints;
593
594 fn div(self, factor: f64) -> BoxConstraints {
595 BoxConstraints {
596 min_width: self.min_width / factor,
597 max_width: self.max_width / factor,
598 min_height: self.min_height / factor,
599 max_height: self.max_height / factor,
600 }
601 }
602}
603
604impl std::ops::Rem<f64> for BoxConstraints {
605 type Output = BoxConstraints;
606
607 fn rem(self, value: f64) -> BoxConstraints {
608 BoxConstraints {
609 min_width: self.min_width % value,
610 max_width: self.max_width % value,
611 min_height: self.min_height % value,
612 max_height: self.max_height % value,
613 }
614 }
615}
616
617impl PartialEq for BoxConstraints {
618 fn eq(&self, other: &BoxConstraints) -> bool {
619 debug_assert!(self.debug_assert_is_valid(false));
620 debug_assert!(other.debug_assert_is_valid(false));
621 self.min_width == other.min_width
622 && self.max_width == other.max_width
623 && self.min_height == other.min_height
624 && self.max_height == other.max_height
625 }
626}
627
628impl Eq for BoxConstraints {}
629
630impl Hash for BoxConstraints {
631 fn hash<H: Hasher>(&self, state: &mut H) {
632 debug_assert!(self.debug_assert_is_valid(false));
633 self.min_width.to_bits().hash(state);
634 self.max_width.to_bits().hash(state);
635 self.min_height.to_bits().hash(state);
636 self.max_height.to_bits().hash(state);
637 }
638}
639
640impl Display for BoxConstraints {
641 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642 let annotation = if self.is_normalized() {
643 ""
644 } else {
645 "; NOT NORMALIZED"
646 };
647 if self.min_width == f64::INFINITY && self.min_height == f64::INFINITY {
648 return write!(f, "BoxConstraints(biggest{annotation})");
649 }
650 if self.min_width == 0.0
651 && self.max_width == f64::INFINITY
652 && self.min_height == 0.0
653 && self.max_height == f64::INFINITY
654 {
655 return write!(f, "BoxConstraints(unconstrained{annotation})");
656 }
657 fn describe(min: f64, max: f64, dim: &str) -> String {
658 if min == max {
659 format!("{dim}={min:.1}")
660 } else {
661 format!("{min:.1}<={dim}<={max:.1}")
662 }
663 }
664 write!(
665 f,
666 "BoxConstraints({}, {}{annotation})",
667 describe(self.min_width, self.max_width, "w"),
668 describe(self.min_height, self.max_height, "h")
669 )
670 }
671}
672
673impl Debug for BoxConstraints {
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675 Display::fmt(self, f)
676 }
677}
678
679pub type BoxHitTest<'r> = dyn FnOnce(&mut BoxHitTestResult<'r>, Offset) -> bool;
684
685pub struct BoxHitTestResult<'a>(&'a mut HitTestResult);
690
691impl<'a> BoxHitTestResult<'a> {
692 pub fn wrap(result: &'a mut HitTestResult) -> BoxHitTestResult<'a> {
694 BoxHitTestResult(result)
695 }
696
697 pub fn add_with_paint_transform(
712 &mut self,
713 transform: Option<Matrix4>,
714 position: Offset,
715 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
716 ) -> bool {
717 let transform = match transform {
718 Some(transform) => match remove_perspective_transform(transform).invert() {
719 Some(inverted) => Some(inverted),
720 None => return false,
722 },
723 None => None,
724 };
725 self.add_with_raw_transform(transform, position, hit_test)
726 }
727
728 pub fn add_with_paint_offset(
733 &mut self,
734 offset: Option<Offset>,
735 position: Offset,
736 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
737 ) -> bool {
738 let transformed_position = match offset {
739 Some(offset) => position - offset,
740 None => position,
741 };
742 if let Some(offset) = offset {
743 self.0.push_offset(Offset::ZERO - offset);
744 }
745 let is_hit = hit_test(self, transformed_position);
746 if offset.is_some() {
747 self.0.pop_transform();
748 }
749 is_hit
750 }
751
752 pub fn add_with_raw_transform(
759 &mut self,
760 transform: Option<Matrix4>,
761 position: Offset,
762 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>, Offset) -> bool,
763 ) -> bool {
764 let transformed_position = match transform {
765 Some(transform) => transform_point(&transform, position),
766 None => position,
767 };
768 if let Some(transform) = transform {
769 self.0.push_transform(transform);
770 }
771 let is_hit = hit_test(self, transformed_position);
772 if transform.is_some() {
773 self.0.pop_transform();
774 }
775 is_hit
776 }
777
778 pub fn add_with_out_of_band_position(
783 &mut self,
784 paint_offset: Option<Offset>,
785 paint_transform: Option<Matrix4>,
786 raw_transform: Option<Matrix4>,
787 hit_test: impl FnOnce(&mut BoxHitTestResult<'_>) -> bool,
788 ) -> bool {
789 debug_assert_eq!(
790 [
791 paint_offset.is_some(),
792 paint_transform.is_some(),
793 raw_transform.is_some()
794 ]
795 .iter()
796 .filter(|given| **given)
797 .count(),
798 1,
799 "Exactly one transform or offset argument must be provided."
800 );
801 if let Some(paint_offset) = paint_offset {
802 self.0.push_offset(Offset::ZERO - paint_offset);
803 } else if let Some(raw_transform) = raw_transform {
804 self.0.push_transform(raw_transform);
805 } else {
806 let paint_transform = paint_transform
807 .expect("checked")
808 .invert()
809 .expect("paint_transform must be invertible.");
810 self.0.push_transform(paint_transform);
811 }
812 let is_hit = hit_test(self);
813 self.0.pop_transform();
814 is_hit
815 }
816}
817
818impl Deref for BoxHitTestResult<'_> {
819 type Target = HitTestResult;
820
821 fn deref(&self) -> &HitTestResult {
822 self.0
823 }
824}
825
826impl DerefMut for BoxHitTestResult<'_> {
827 fn deref_mut(&mut self) -> &mut HitTestResult {
828 self.0
829 }
830}
831
832#[derive(Clone, Copy, Debug)]
838pub struct BoxHitTestEntry {
839 target: AnyRenderBox,
840 local_position: Offset,
841}
842
843impl BoxHitTestEntry {
844 pub fn new(target: AnyRenderBox, local_position: Offset) -> BoxHitTestEntry {
846 BoxHitTestEntry {
847 target,
848 local_position,
849 }
850 }
851
852 pub fn target(&self) -> AnyRenderBox {
854 self.target
855 }
856
857 pub fn local_position(&self) -> Offset {
859 self.local_position
860 }
861}
862
863impl HitTestTarget for BoxHitTestEntry {
864 fn handle_event(&self, app: &mut App, event: &PointerEvent, _entry: &HitTestEntry) {
865 self.target.handle_event(app, event, self);
866 }
867
868 fn retained_handle(&self) -> Option<HandleId> {
869 Some(self.target.id)
870 }
871}
872
873impl From<BoxHitTestEntry> for HitTestEntry {
874 fn from(entry: BoxHitTestEntry) -> HitTestEntry {
875 HitTestEntry::new(entry)
876 }
877}
878
879#[derive(Clone, Copy, Debug)]
881pub struct BoxParentData {
882 pub offset: Offset,
884}
885
886impl BoxParentData {
887 pub const fn new() -> BoxParentData {
889 BoxParentData {
890 offset: Offset::ZERO,
891 }
892 }
893}
894
895impl Default for BoxParentData {
896 fn default() -> BoxParentData {
897 BoxParentData::new()
898 }
899}
900
901impl ParentData for BoxParentData {
902 fn provide(&self, id: TypeId) -> Option<&dyn Any> {
903 (id == TypeId::of::<BoxParentData>()).then_some(self)
904 }
905
906 fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
907 (id == TypeId::of::<BoxParentData>()).then_some(self)
908 }
909}
910
911impl fmt::Display for BoxParentData {
912 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
913 write!(f, "offset={:?}", self.offset)
914 }
915}
916
917pub trait ContainerBoxParentData: ContainerParentDataMixin<ChildType = AnyRenderBox> {
923 fn box_parent_data(&self) -> &BoxParentData;
928
929 fn box_parent_data_mut(&mut self) -> &mut BoxParentData;
931
932 fn offset(&self) -> Offset {
934 self.box_parent_data().offset
935 }
936
937 fn set_offset(&mut self, value: Offset) {
939 self.box_parent_data_mut().offset = value;
940 }
941}
942
943#[derive(Clone, Copy, PartialEq, Debug)]
945pub struct BaselineOffset(pub Option<f64>);
946
947impl BaselineOffset {
948 pub const NO_BASELINE: BaselineOffset = BaselineOffset(None);
954
955 pub fn offset(self) -> Option<f64> {
957 self.0
958 }
959
960 pub fn min_of(self, other: BaselineOffset) -> BaselineOffset {
967 match (self.0, other.0) {
968 (Some(lhs), Some(rhs)) => {
969 if lhs >= rhs {
970 other
971 } else {
972 self
973 }
974 }
975 (Some(_), None) => self,
976 (None, _) => other,
977 }
978 }
979}
980
981impl Add<f64> for BaselineOffset {
982 type Output = BaselineOffset;
983
984 fn add(self, offset: f64) -> BaselineOffset {
987 BaselineOffset(self.0.map(|value| value + offset))
988 }
989}
990
991#[derive(Clone, Copy, Debug)]
996pub enum DryLayoutFailure<'a> {
997 Reason(&'a str),
999
1000 Error(&'a str),
1002}
1003
1004#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1007enum IntrinsicDimension {
1008 MinWidth,
1009 MaxWidth,
1010 MinHeight,
1011 MaxHeight,
1012}
1013
1014#[derive(Clone, Copy, PartialEq, Eq, Hash)]
1016struct DoubleKey(u64);
1017
1018impl DoubleKey {
1019 fn new(value: f64) -> DoubleKey {
1020 DoubleKey(value.to_bits())
1021 }
1022}
1023
1024#[derive(Default)]
1031struct LayoutCacheStorage {
1032 cached_intrinsic_dimensions: HashMap<(IntrinsicDimension, DoubleKey), f64>,
1033 cached_dry_layout_sizes: HashMap<BoxConstraints, Size>,
1034 cached_alphabetic_baseline: HashMap<BoxConstraints, BaselineOffset>,
1035 cached_ideo_baseline: HashMap<BoxConstraints, BaselineOffset>,
1036}
1037
1038impl LayoutCacheStorage {
1039 fn clear(&mut self) -> bool {
1041 let has_cache = !self.cached_dry_layout_sizes.is_empty()
1042 || !self.cached_intrinsic_dimensions.is_empty()
1043 || !self.cached_alphabetic_baseline.is_empty()
1044 || !self.cached_ideo_baseline.is_empty();
1045 if has_cache {
1046 self.cached_dry_layout_sizes.clear();
1047 self.cached_intrinsic_dimensions.clear();
1048 self.cached_alphabetic_baseline.clear();
1049 self.cached_ideo_baseline.clear();
1050 }
1051 has_cache
1052 }
1053
1054 fn baselines(
1055 &mut self,
1056 baseline: TextBaseline,
1057 ) -> &mut HashMap<BoxConstraints, BaselineOffset> {
1058 match baseline {
1059 TextBaseline::Alphabetic => &mut self.cached_alphabetic_baseline,
1060 TextBaseline::Ideographic => &mut self.cached_ideo_baseline,
1061 }
1062 }
1063}
1064
1065pub struct RenderBoxData {
1067 pub(crate) size: Option<Size>,
1068 pub(crate) constraints: Option<BoxConstraints>,
1069 layout_cache: LayoutCacheStorage,
1070 debug_computing_this_dry_layout: bool,
1071 debug_computing_this_dry_baseline: bool,
1072}
1073
1074impl RenderBoxData {
1075 pub fn new() -> RenderBoxData {
1077 RenderBoxData {
1078 size: None,
1079 constraints: None,
1080 layout_cache: LayoutCacheStorage::default(),
1081 debug_computing_this_dry_layout: false,
1082 debug_computing_this_dry_baseline: false,
1083 }
1084 }
1085}
1086
1087impl Default for RenderBoxData {
1088 fn default() -> RenderBoxData {
1089 RenderBoxData::new()
1090 }
1091}
1092
1093#[macro_export]
1095macro_rules! render_box_accessors {
1096 () => {
1097 fn render_box_data(
1098 self: $crate::RenderHandle<Self>,
1099 app: &::inset_foundation::App,
1100 ) -> &$crate::RenderBoxData {
1101 &self.get(app).render_box
1102 }
1103 fn render_box_data_mut(
1104 self: $crate::RenderHandle<Self>,
1105 app: &mut ::inset_foundation::App,
1106 ) -> &mut $crate::RenderBoxData {
1107 &mut self.get_mut(app).render_box
1108 }
1109 };
1110}
1111
1112fn box_parent_data_of(app: &App, child: AnyRenderObject) -> &BoxParentData {
1118 child
1119 .parent_data(app)
1120 .and_then(|parent_data| parent_data.part::<BoxParentData>())
1121 .expect("parent data is not a BoxParentData")
1122}
1123
1124pub trait RenderBoxContainerDefaultsMixin:
1132 ContainerRenderObjectMixin<ChildType = AnyRenderBox, ParentDataType: ContainerBoxParentData>
1133 + RenderBox
1134{
1135 fn default_compute_distance_to_first_actual_baseline(
1140 self: RenderHandle<Self>,
1141 app: &mut App,
1142 baseline: TextBaseline,
1143 ) -> Option<f64> {
1144 debug_assert!(!self.as_object().debug_needs_layout(app));
1145 let mut child = self.first_child(app);
1146 while let Some(current) = child {
1147 let child_parent_data = current
1148 .as_object()
1149 .parent_data_of::<Self::ParentDataType>(app);
1150 let (offset, next_sibling) =
1151 (child_parent_data.offset(), child_parent_data.next_sibling());
1152 if let Some(result) = current.get_distance_to_actual_baseline(app, baseline) {
1153 return Some(result + offset.dy());
1154 }
1155 child = next_sibling;
1156 }
1157 None
1158 }
1159
1160 fn default_compute_distance_to_highest_actual_baseline(
1165 self: RenderHandle<Self>,
1166 app: &mut App,
1167 baseline: TextBaseline,
1168 ) -> Option<f64> {
1169 debug_assert!(!self.as_object().debug_needs_layout(app));
1170 let mut min_baseline = BaselineOffset::NO_BASELINE;
1171 let mut child = self.first_child(app);
1172 while let Some(current) = child {
1173 let child_parent_data = current
1174 .as_object()
1175 .parent_data_of::<Self::ParentDataType>(app);
1176 let (offset, next_sibling) =
1177 (child_parent_data.offset(), child_parent_data.next_sibling());
1178 let candidate = BaselineOffset(current.get_distance_to_actual_baseline(app, baseline))
1179 + offset.dy();
1180 min_baseline = min_baseline.min_of(candidate);
1181 child = next_sibling;
1182 }
1183 min_baseline.offset()
1184 }
1185
1186 fn default_hit_test_children(
1196 self: RenderHandle<Self>,
1197 app: &mut App,
1198 result: &mut BoxHitTestResult<'_>,
1199 position: Offset,
1200 ) -> bool {
1201 let mut child = self.last_child(app);
1202 while let Some(current) = child {
1203 let child_parent_data = current
1205 .as_object()
1206 .parent_data_of::<Self::ParentDataType>(app);
1207 let (offset, previous_sibling) = (
1208 child_parent_data.offset(),
1209 child_parent_data.previous_sibling(),
1210 );
1211 let is_hit =
1212 result.add_with_paint_offset(Some(offset), position, |result, transformed| {
1213 debug_assert_eq!(transformed, position - offset);
1214 current.hit_test(app, result, transformed)
1215 });
1216 if is_hit {
1217 return true;
1218 }
1219 child = previous_sibling;
1220 }
1221 false
1222 }
1223
1224 fn default_paint(
1231 self: RenderHandle<Self>,
1232 app: &mut App,
1233 context: &mut PaintingContext,
1234 offset: Offset,
1235 ) {
1236 let mut child = self.first_child(app);
1237 while let Some(current) = child {
1238 let child_parent_data = current
1239 .as_object()
1240 .parent_data_of::<Self::ParentDataType>(app);
1241 let (child_offset, next_sibling) =
1242 (child_parent_data.offset(), child_parent_data.next_sibling());
1243 context.paint_child(app, current.as_object(), child_offset + offset);
1244 child = next_sibling;
1245 }
1246 }
1247}
1248
1249pub trait RenderBox: RenderObject {
1255 fn render_box_data(self: RenderHandle<Self>, app: &App) -> &RenderBoxData;
1257
1258 fn render_box_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderBoxData;
1260
1261 fn constraints(self: RenderHandle<Self>, app: &App) -> BoxConstraints {
1267 self.render_box_data(app).constraints.unwrap_or_else(|| {
1268 panic!("A RenderObject does not have any constraints before it has been laid out.")
1269 })
1270 }
1271
1272 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1276 if !child.parent_data_is::<BoxParentData>(app) {
1277 child.set_parent_data(app, BoxParentData::new());
1278 }
1279 }
1280
1281 fn get_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1283 self.as_box().get_min_intrinsic_width(app, height)
1284 }
1285
1286 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1331 let _ = (self, app, height);
1332 0.0
1333 }
1334
1335 fn get_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1337 self.as_box().get_max_intrinsic_width(app, height)
1338 }
1339
1340 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
1361 let _ = (self, app, height);
1362 0.0
1363 }
1364
1365 fn get_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1367 self.as_box().get_min_intrinsic_height(app, width)
1368 }
1369
1370 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1387 let _ = (self, app, width);
1388 0.0
1389 }
1390
1391 fn get_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1393 self.as_box().get_max_intrinsic_height(app, width)
1394 }
1395
1396 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1413 let _ = (self, app, width);
1414 0.0
1415 }
1416
1417 fn get_dry_layout(
1419 self: RenderHandle<Self>,
1420 app: &mut App,
1421 constraints: BoxConstraints,
1422 ) -> Size {
1423 self.as_box().get_dry_layout(app, constraints)
1424 }
1425
1426 fn compute_dry_layout(
1451 self: RenderHandle<Self>,
1452 app: &mut App,
1453 constraints: BoxConstraints,
1454 ) -> Size {
1455 let _ = (app, constraints);
1456 self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1457 "It does not implement RenderBox::compute_dry_layout.",
1458 ));
1459 Size::ZERO
1460 }
1461
1462 fn get_dry_baseline(
1464 self: RenderHandle<Self>,
1465 app: &mut App,
1466 constraints: BoxConstraints,
1467 baseline: TextBaseline,
1468 ) -> Option<f64> {
1469 self.as_box().get_dry_baseline(app, constraints, baseline)
1470 }
1471
1472 fn compute_dry_baseline(
1496 self: RenderHandle<Self>,
1497 app: &mut App,
1498 constraints: BoxConstraints,
1499 baseline: TextBaseline,
1500 ) -> Option<f64> {
1501 let _ = (app, constraints, baseline);
1502 self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1503 "It does not implement RenderBox::compute_dry_baseline.",
1504 ));
1505 None
1506 }
1507
1508 fn debug_cannot_compute_dry_layout(self: RenderHandle<Self>, failure: DryLayoutFailure<'_>) {
1516 let _ = self;
1517 if !cfg!(debug_assertions) || debug_checking_intrinsics() {
1518 return;
1519 }
1520 match failure {
1521 DryLayoutFailure::Reason(reason) => panic!(
1522 "The {} class does not support dry layout. {reason}",
1523 std::any::type_name::<Self>()
1524 ),
1525 DryLayoutFailure::Error(error) => panic!("{error}"),
1526 }
1527 }
1528
1529 fn get_distance_to_baseline(
1531 self: RenderHandle<Self>,
1532 app: &mut App,
1533 baseline: TextBaseline,
1534 only_real: bool,
1535 ) -> Option<f64> {
1536 self.as_box()
1537 .get_distance_to_baseline(app, baseline, only_real)
1538 }
1539
1540 fn get_distance_to_actual_baseline(
1542 self: RenderHandle<Self>,
1543 app: &mut App,
1544 baseline: TextBaseline,
1545 ) -> Option<f64> {
1546 self.as_box().get_distance_to_actual_baseline(app, baseline)
1547 }
1548
1549 fn compute_distance_to_actual_baseline(
1569 self: RenderHandle<Self>,
1570 app: &mut App,
1571 baseline: TextBaseline,
1572 ) -> Option<f64> {
1573 let _ = (self, app, baseline);
1574 debug_assert!(
1575 DEBUG_DOING_BASELINE.get(),
1576 "Please see the documentation for compute_distance_to_actual_baseline for the \
1577 required calling conventions of this method."
1578 );
1579 None
1580 }
1581
1582 fn mark_needs_layout(self: RenderHandle<Self>, app: &mut App) {
1587 if self.render_box_data_mut(app).layout_cache.clear() && self.parent(app).is_some() {
1600 self.as_object().mark_parent_needs_layout(app);
1601 return;
1602 }
1603 crate::object::RenderObjectBase::mark_needs_layout(self, app);
1604 }
1605
1606 fn perform_resize(self: RenderHandle<Self>, app: &mut App) {
1613 RenderBoxBase::perform_resize(self, app);
1614 }
1615
1616 fn size(self: RenderHandle<Self>, app: &App) -> Size {
1618 self.as_box().size(app)
1619 }
1620
1621 fn apply_paint_transform(
1631 self: RenderHandle<Self>,
1632 app: &App,
1633 child: AnyRenderObject,
1634 transform: &mut Matrix4,
1635 ) {
1636 debug_assert!(child.parent(app).map(AnyRenderObject::id) == Some(self.id()));
1637 let offset = box_parent_data_of(app, child).offset;
1640 crate::object::translate(transform, offset.dx(), offset.dy());
1641 }
1642
1643 fn paint_bounds(self: RenderHandle<Self>, app: &App) -> Rect {
1645 Offset::ZERO & self.size(app)
1646 }
1647
1648 fn has_size(self: RenderHandle<Self>, app: &App) -> bool {
1650 self.render_box_data(app).size.is_some()
1651 }
1652
1653 fn handle_event(
1659 self: RenderHandle<Self>,
1660 app: &mut App,
1661 event: &PointerEvent,
1662 entry: &BoxHitTestEntry,
1663 ) {
1664 let _ = (self, app, event, entry);
1665 }
1666
1667 fn hit_test(
1682 self: RenderHandle<Self>,
1683 app: &mut App,
1684 result: &mut BoxHitTestResult<'_>,
1685 position: Offset,
1686 ) -> bool {
1687 RenderBoxBase::hit_test(self, app, result, position)
1688 }
1689
1690 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
1695 let _ = self;
1696 false
1697 }
1698
1699 fn hit_test_children(
1704 self: RenderHandle<Self>,
1705 app: &mut App,
1706 result: &mut BoxHitTestResult<'_>,
1707 position: Offset,
1708 ) -> bool {
1709 let _ = (self, app, result, position);
1710 false
1711 }
1712
1713 fn set_size(self: RenderHandle<Self>, app: &mut App, size: Size) {
1716 self.as_box().set_size(app, size)
1717 }
1718
1719 fn layout(
1721 self: RenderHandle<Self>,
1722 app: &mut App,
1723 constraints: BoxConstraints,
1724 parent_uses_size: bool,
1725 ) {
1726 self.as_box().layout(app, constraints, parent_uses_size)
1727 }
1728
1729 fn as_box(self: RenderHandle<Self>) -> AnyRenderBox {
1731 AnyRenderBox {
1732 id: self.id(),
1733 vtable: const { &RenderBoxVTable::of::<Self>() },
1734 }
1735 }
1736
1737 fn as_object(self: RenderHandle<Self>) -> AnyRenderObject {
1739 self.as_box().as_object()
1740 }
1741
1742 fn adopt_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1744 self.as_object().adopt_child(app, child)
1745 }
1746
1747 fn drop_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1749 self.as_object().drop_child(app, child)
1750 }
1751
1752 fn mark_needs_paint(self: RenderHandle<Self>, app: &mut App) {
1754 self.as_object().mark_needs_paint(app)
1755 }
1756
1757 fn mark_needs_composited_layer_update(self: RenderHandle<Self>, app: &mut App) {
1759 self.as_object().mark_needs_composited_layer_update(app)
1760 }
1761
1762 fn mark_needs_compositing_bits_update(self: RenderHandle<Self>, app: &mut App) {
1764 self.as_object().mark_needs_compositing_bits_update(app)
1765 }
1766
1767 fn schedule_initial_layout(self: RenderHandle<Self>, app: &mut App) {
1769 self.as_object().schedule_initial_layout(app)
1770 }
1771
1772 fn parent(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderObject> {
1774 self.as_object().parent(app)
1775 }
1776
1777 fn owner(self: RenderHandle<Self>, app: &App) -> Option<Handle<PipelineOwner>> {
1779 self.as_object().owner(app)
1780 }
1781
1782 fn attached(self: RenderHandle<Self>, app: &App) -> bool {
1784 self.as_object().attached(app)
1785 }
1786
1787 fn debug_needs_layout(self: RenderHandle<Self>, app: &App) -> bool {
1789 self.as_object().debug_needs_layout(app)
1790 }
1791}
1792
1793pub(crate) trait RenderBoxBase: RenderBox {
1799 fn perform_resize(self: RenderHandle<Self>, app: &mut App) {
1801 let constraints = RenderBox::constraints(self, app);
1803 let size = RenderBox::compute_dry_layout(self, app, constraints);
1804 debug_assert!(size.width().is_finite() && size.height().is_finite());
1805 self.set_size(app, size);
1806 }
1807
1808 fn hit_test(
1810 self: RenderHandle<Self>,
1811 app: &mut App,
1812 result: &mut BoxHitTestResult<'_>,
1813 position: Offset,
1814 ) -> bool {
1815 debug_assert!(
1816 self.has_size(app),
1817 "Cannot hit test a render box that has never been laid out: {self:?}"
1818 );
1819 if self.size(app).contains(position)
1820 && (RenderBox::hit_test_children(self, app, result, position)
1821 || RenderBox::hit_test_self(self, app, position))
1822 {
1823 result.add(BoxHitTestEntry::new(self.as_box(), position).into());
1824 return true;
1825 }
1826 false
1827 }
1828}
1829
1830impl<T: RenderBox> RenderBoxBase for T {}
1831
1832pub(crate) struct RenderBoxVTable {
1834 pub object: RenderObjectVTable,
1835 pub box_data: fn(&App, HandleId) -> &RenderBoxData,
1836 pub box_data_mut: fn(&mut App, HandleId) -> &mut RenderBoxData,
1837 pub hit_test: fn(&mut App, HandleId, &mut BoxHitTestResult<'_>, Offset) -> bool,
1838 pub handle_event: fn(&mut App, HandleId, &PointerEvent, &BoxHitTestEntry),
1839 pub compute_min_intrinsic_width: fn(&mut App, HandleId, f64) -> f64,
1840 pub compute_max_intrinsic_width: fn(&mut App, HandleId, f64) -> f64,
1841 pub compute_min_intrinsic_height: fn(&mut App, HandleId, f64) -> f64,
1842 pub compute_max_intrinsic_height: fn(&mut App, HandleId, f64) -> f64,
1843 pub compute_dry_layout: fn(&mut App, HandleId, BoxConstraints) -> Size,
1844 pub compute_dry_baseline: fn(&mut App, HandleId, BoxConstraints, TextBaseline) -> Option<f64>,
1845 pub compute_distance_to_actual_baseline: fn(&mut App, HandleId, TextBaseline) -> Option<f64>,
1846}
1847
1848impl RenderBoxVTable {
1849 const fn of<T: RenderBox>() -> RenderBoxVTable {
1850 RenderBoxVTable {
1851 object: RenderObjectVTable::of::<T>(
1852 |app, id, child| <T as RenderBox>::setup_parent_data(resolve(id), app, child),
1853 |app, id| T::paint_bounds(resolve(id), app),
1854 |app, id, child, transform| {
1855 <T as RenderBox>::apply_paint_transform(resolve(id), app, child, transform)
1856 },
1857 |app, id| <T as RenderBox>::perform_resize(resolve(id), app),
1858 |app, id| <T as RenderBox>::mark_needs_layout(resolve(id), app),
1859 Some(|| const { &RenderBoxVTable::of::<T>() }),
1860 None,
1861 ),
1862 box_data: |app, id| T::render_box_data(resolve(id), app),
1863 box_data_mut: |app, id| T::render_box_data_mut(resolve(id), app),
1864 hit_test: |app, id, result, position| T::hit_test(resolve(id), app, result, position),
1865 handle_event: |app, id, event, entry| T::handle_event(resolve(id), app, event, entry),
1866 compute_min_intrinsic_width: |app, id, height| {
1867 T::compute_min_intrinsic_width(resolve(id), app, height)
1868 },
1869 compute_max_intrinsic_width: |app, id, height| {
1870 T::compute_max_intrinsic_width(resolve(id), app, height)
1871 },
1872 compute_min_intrinsic_height: |app, id, width| {
1873 T::compute_min_intrinsic_height(resolve(id), app, width)
1874 },
1875 compute_max_intrinsic_height: |app, id, width| {
1876 T::compute_max_intrinsic_height(resolve(id), app, width)
1877 },
1878 compute_dry_layout: |app, id, constraints| {
1879 T::compute_dry_layout(resolve(id), app, constraints)
1880 },
1881 compute_dry_baseline: |app, id, constraints, baseline| {
1882 T::compute_dry_baseline(resolve(id), app, constraints, baseline)
1883 },
1884 compute_distance_to_actual_baseline: |app, id, baseline| {
1885 T::compute_distance_to_actual_baseline(resolve(id), app, baseline)
1886 },
1887 }
1888 }
1889}
1890
1891impl<T: RenderBox> RenderHandle<T> {
1892 pub fn new_box(app: &mut App, object: T) -> RenderHandle<T> {
1894 let this = create(app, object);
1895 let data = this.render_object_data_mut(app);
1896 data.object_vtable = Some(&const { RenderBoxVTable::of::<T>() }.object);
1897 let is_repaint_boundary = this.is_repaint_boundary(app);
1901 let always_needs_compositing = this.always_needs_compositing(app);
1902 let data = this.render_object_data_mut(app);
1903 data.was_repaint_boundary = is_repaint_boundary;
1904 data.needs_compositing = is_repaint_boundary || always_needs_compositing;
1905 this
1906 }
1907}
1908
1909struct DebugDoingBaseline;
1911
1912impl DebugDoingBaseline {
1913 fn enter() -> DebugDoingBaseline {
1914 DEBUG_DOING_BASELINE.set(cfg!(debug_assertions));
1915 DebugDoingBaseline
1916 }
1917}
1918
1919impl Drop for DebugDoingBaseline {
1920 fn drop(&mut self) {
1921 DEBUG_DOING_BASELINE.set(false);
1922 }
1923}
1924
1925fn should_cache_intrinsics(this: AnyRenderBox, app: &App) -> bool {
1927 debug_assert!(debug_checking_intrinsics() || !this.as_object().debug_doing_this_resize(app));
1929 !cfg!(debug_assertions) || !debug_checking_intrinsics()
1931}
1932
1933fn memoize_intrinsic(
1935 this: AnyRenderBox,
1936 app: &mut App,
1937 dimension: IntrinsicDimension,
1938 input: f64,
1939 computer: fn(&mut App, HandleId, f64) -> f64,
1940) -> f64 {
1941 if !should_cache_intrinsics(this, app) {
1942 return computer(app, this.id, input);
1943 }
1944 let key = (dimension, DoubleKey::new(input));
1945 if let Some(cached) = this.layout_cache(app).cached_intrinsic_dimensions.get(&key) {
1946 return *cached;
1947 }
1948 let result = computer(app, this.id, input);
1949 this.layout_cache_mut(app)
1950 .cached_intrinsic_dimensions
1951 .insert(key, result);
1952 result
1953}
1954
1955fn memoize_baseline(
1957 this: AnyRenderBox,
1958 app: &mut App,
1959 constraints: BoxConstraints,
1960 baseline: TextBaseline,
1961 computer: impl FnOnce(AnyRenderBox, &mut App) -> BaselineOffset,
1962) -> BaselineOffset {
1963 if !should_cache_intrinsics(this, app) {
1964 return computer(this, app);
1965 }
1966 if let Some(cached) = this
1967 .layout_cache_mut(app)
1968 .baselines(baseline)
1969 .get(&constraints)
1970 {
1971 return *cached;
1972 }
1973 let result = computer(this, app);
1974 this.layout_cache_mut(app)
1975 .baselines(baseline)
1976 .insert(constraints, result);
1977 result
1978}
1979
1980#[derive(Clone, Copy)]
1982pub struct AnyRenderBox {
1983 id: HandleId,
1984 vtable: &'static RenderBoxVTable,
1985}
1986
1987impl PartialEq for AnyRenderBox {
1988 fn eq(&self, other: &AnyRenderBox) -> bool {
1989 self.id == other.id
1990 }
1991}
1992
1993impl Eq for AnyRenderBox {}
1994
1995impl std::hash::Hash for AnyRenderBox {
1996 fn hash<H: Hasher>(&self, state: &mut H) {
1997 self.id.hash(state);
1998 }
1999}
2000
2001impl Debug for AnyRenderBox {
2002 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2003 write!(f, "AnyRenderBox({:?})", self.id)
2004 }
2005}
2006
2007impl AnyRenderBox {
2008 pub(crate) fn from_vtable(id: HandleId, vtable: &'static RenderBoxVTable) -> AnyRenderBox {
2009 AnyRenderBox { id, vtable }
2010 }
2011
2012 pub fn has_size(self, app: &App) -> bool {
2015 self.box_data(app).size.is_some()
2016 }
2017
2018 pub fn as_object(self) -> AnyRenderObject {
2019 AnyRenderObject::from_vtable(self.id, &self.vtable.object)
2020 }
2021
2022 pub fn hit_test(
2024 self,
2025 app: &mut App,
2026 result: &mut BoxHitTestResult<'_>,
2027 position: Offset,
2028 ) -> bool {
2029 (self.vtable.hit_test)(app, self.id, result, position)
2030 }
2031
2032 pub fn handle_event(self, app: &mut App, event: &PointerEvent, entry: &BoxHitTestEntry) {
2034 (self.vtable.handle_event)(app, self.id, event, entry)
2035 }
2036
2037 fn box_data(self, app: &App) -> &RenderBoxData {
2038 (self.vtable.box_data)(app, self.id)
2039 }
2040
2041 fn box_data_mut(self, app: &mut App) -> &mut RenderBoxData {
2042 (self.vtable.box_data_mut)(app, self.id)
2043 }
2044
2045 pub fn layout(self, app: &mut App, constraints: BoxConstraints, parent_uses_size: bool) {
2047 debug_assert!(
2048 !app.is_disposed(self.id),
2049 "layout on a disposed render object"
2050 );
2051 debug_assert!(constraints.debug_assert_is_valid(true));
2052 let same = self.box_data(app).constraints == Some(constraints);
2053 self.as_object()
2054 .run_layout(app, parent_uses_size, constraints.is_tight(), same, |app| {
2055 self.box_data_mut(app).constraints = Some(constraints)
2056 });
2057 }
2058
2059 pub fn size(self, app: &App) -> Size {
2065 self.box_data(app)
2066 .size
2067 .unwrap_or_else(|| panic!("RenderBox was not laid out: {self:?}"))
2068 }
2069
2070 pub fn set_size(self, app: &mut App, size: Size) {
2072 self.box_data_mut(app).size = Some(size);
2073 }
2074
2075 pub fn constraints(self, app: &App) -> BoxConstraints {
2081 self.box_data(app).constraints.unwrap_or_else(|| {
2082 panic!("A RenderObject does not have any constraints before it has been laid out.")
2083 })
2084 }
2085
2086 pub fn get_min_intrinsic_width(self, app: &mut App, height: f64) -> f64 {
2102 debug_assert!(
2103 height >= 0.0,
2104 "The height argument to get_min_intrinsic_width was negative. If you perform \
2105 computations on another height before passing it to get_min_intrinsic_width, \
2106 consider using f64::max or clamp_double to force the value into the valid range."
2107 );
2108 memoize_intrinsic(
2109 self,
2110 app,
2111 IntrinsicDimension::MinWidth,
2112 height,
2113 self.vtable.compute_min_intrinsic_width,
2114 )
2115 }
2116
2117 pub fn get_max_intrinsic_width(self, app: &mut App, height: f64) -> f64 {
2124 debug_assert!(
2125 height >= 0.0,
2126 "The height argument to get_max_intrinsic_width was negative."
2127 );
2128 memoize_intrinsic(
2129 self,
2130 app,
2131 IntrinsicDimension::MaxWidth,
2132 height,
2133 self.vtable.compute_max_intrinsic_width,
2134 )
2135 }
2136
2137 pub fn get_min_intrinsic_height(self, app: &mut App, width: f64) -> f64 {
2143 debug_assert!(
2144 width >= 0.0,
2145 "The width argument to get_min_intrinsic_height was negative."
2146 );
2147 memoize_intrinsic(
2148 self,
2149 app,
2150 IntrinsicDimension::MinHeight,
2151 width,
2152 self.vtable.compute_min_intrinsic_height,
2153 )
2154 }
2155
2156 pub fn get_max_intrinsic_height(self, app: &mut App, width: f64) -> f64 {
2163 debug_assert!(
2164 width >= 0.0,
2165 "The width argument to get_max_intrinsic_height was negative."
2166 );
2167 memoize_intrinsic(
2168 self,
2169 app,
2170 IntrinsicDimension::MaxHeight,
2171 width,
2172 self.vtable.compute_max_intrinsic_height,
2173 )
2174 }
2175
2176 pub fn get_dry_layout(self, app: &mut App, constraints: BoxConstraints) -> Size {
2194 if !should_cache_intrinsics(self, app) {
2195 return self.compute_dry_layout(app, constraints);
2196 }
2197 if let Some(cached) = self
2198 .layout_cache(app)
2199 .cached_dry_layout_sizes
2200 .get(&constraints)
2201 {
2202 return *cached;
2203 }
2204 let result = self.compute_dry_layout(app, constraints);
2205 self.layout_cache_mut(app)
2206 .cached_dry_layout_sizes
2207 .insert(constraints, result);
2208 result
2209 }
2210
2211 fn compute_dry_layout(self, app: &mut App, constraints: BoxConstraints) -> Size {
2213 if cfg!(debug_assertions) {
2214 assert!(!self.box_data(app).debug_computing_this_dry_layout);
2215 self.box_data_mut(app).debug_computing_this_dry_layout = true;
2216 }
2217 let result = (self.vtable.compute_dry_layout)(app, self.id, constraints);
2218 if cfg!(debug_assertions) {
2219 self.box_data_mut(app).debug_computing_this_dry_layout = false;
2220 }
2221 result
2222 }
2223
2224 pub fn get_dry_baseline(
2240 self,
2241 app: &mut App,
2242 constraints: BoxConstraints,
2243 baseline: TextBaseline,
2244 ) -> Option<f64> {
2245 let baseline_offset = memoize_baseline(self, app, constraints, baseline, |this, app| {
2246 this.compute_dry_baseline(app, constraints, baseline)
2247 })
2248 .offset();
2249 if cfg!(debug_assertions) && !debug_checking_intrinsics() {
2254 assert_eq!(
2255 baseline_offset,
2256 (self.vtable.compute_dry_baseline)(app, self.id, constraints, baseline)
2257 );
2258 }
2259 baseline_offset
2260 }
2261
2262 fn compute_dry_baseline(
2264 self,
2265 app: &mut App,
2266 constraints: BoxConstraints,
2267 baseline: TextBaseline,
2268 ) -> BaselineOffset {
2269 if cfg!(debug_assertions) {
2270 assert!(!self.box_data(app).debug_computing_this_dry_baseline);
2271 self.box_data_mut(app).debug_computing_this_dry_baseline = true;
2272 }
2273 let result = BaselineOffset((self.vtable.compute_dry_baseline)(
2274 app,
2275 self.id,
2276 constraints,
2277 baseline,
2278 ));
2279 if cfg!(debug_assertions) {
2280 self.box_data_mut(app).debug_computing_this_dry_baseline = false;
2281 }
2282 result
2283 }
2284
2285 pub fn get_distance_to_baseline(
2301 self,
2302 app: &mut App,
2303 baseline: TextBaseline,
2304 only_real: bool,
2305 ) -> Option<f64> {
2306 debug_assert!(
2307 !DEBUG_DOING_BASELINE.get(),
2308 "Please see the documentation for compute_distance_to_actual_baseline for the \
2309 required calling conventions of this method."
2310 );
2311 debug_assert!(!self.as_object().debug_needs_layout(app) || debug_checking_intrinsics());
2312 let result = {
2313 let _doing_baseline = DebugDoingBaseline::enter();
2314 self.get_distance_to_actual_baseline(app, baseline)
2315 };
2316 match result {
2317 None if !only_real => Some(self.size(app).height()),
2318 result => result,
2319 }
2320 }
2321
2322 pub fn get_distance_to_actual_baseline(
2328 self,
2329 app: &mut App,
2330 baseline: TextBaseline,
2331 ) -> Option<f64> {
2332 debug_assert!(
2333 DEBUG_DOING_BASELINE.get() || !cfg!(debug_assertions),
2334 "Please see the documentation for compute_distance_to_actual_baseline for the \
2335 required calling conventions of this method."
2336 );
2337 let constraints = self
2338 .box_data(app)
2339 .constraints
2340 .expect("a RenderBox has constraints once it has been laid out");
2341 memoize_baseline(self, app, constraints, baseline, |this, app| {
2342 BaselineOffset((this.vtable.compute_distance_to_actual_baseline)(
2343 app, this.id, baseline,
2344 ))
2345 })
2346 .offset()
2347 }
2348
2349 fn layout_cache(self, app: &App) -> &LayoutCacheStorage {
2350 &self.box_data(app).layout_cache
2351 }
2352
2353 fn layout_cache_mut(self, app: &mut App) -> &mut LayoutCacheStorage {
2354 &mut self.box_data_mut(app).layout_cache
2355 }
2356
2357 pub fn box_parent_data(self, app: &App) -> &BoxParentData {
2362 box_parent_data_of(app, self.as_object())
2363 }
2364
2365 pub fn global_to_local(
2380 self,
2381 app: &App,
2382 point: Offset,
2383 ancestor: Option<AnyRenderObject>,
2384 ) -> Offset {
2385 let transform = self.as_object().get_transform_to(app, ancestor);
2400 let Some(transform) = transform.invert() else {
2401 return Offset::ZERO;
2402 };
2403
2404 let n = [0.0, 0.0, 1.0];
2407 let i = crate::object::perspective_transform(&transform, [0.0, 0.0, 0.0]);
2408 let d = subtract(
2409 crate::object::perspective_transform(&transform, [0.0, 0.0, 1.0]),
2410 i,
2411 );
2412 let s = crate::object::perspective_transform(&transform, [point.dx(), point.dy(), 0.0]);
2413 let p = subtract(s, scale(d, dot(n, s) / dot(n, d)));
2415 Offset::new(p[0], p[1])
2416 }
2417
2418 pub fn local_to_global(
2429 self,
2430 app: &App,
2431 point: Offset,
2432 ancestor: Option<AnyRenderObject>,
2433 ) -> Offset {
2434 inset_painting::transform_point(&self.as_object().get_transform_to(app, ancestor), point)
2435 }
2436
2437 pub fn parent_data_of_mut<P: crate::object::ParentData + 'static>(
2439 self,
2440 app: &mut App,
2441 ) -> &mut P {
2442 self.as_object().parent_data_of_mut(app)
2443 }
2444
2445 pub fn parent_data_is<P: crate::object::ParentData + 'static>(self, app: &App) -> bool {
2447 self.as_object().parent_data_is::<P>(app)
2448 }
2449}
2450
2451impl crate::object::ErasedRenderObject for AnyRenderBox {
2452 fn as_object(self) -> AnyRenderObject {
2453 AnyRenderBox::as_object(self)
2454 }
2455
2456 fn from_object(object: AnyRenderObject) -> AnyRenderBox {
2457 object
2458 .as_box()
2459 .expect("the render object is not a RenderBox")
2460 }
2461}
2462
2463impl From<AnyRenderBox> for AnyRenderObject {
2464 fn from(box_: AnyRenderBox) -> AnyRenderObject {
2465 box_.as_object()
2466 }
2467}
2468
2469fn subtract(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
2470 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
2471}
2472
2473fn scale(a: [f64; 3], factor: f64) -> [f64; 3] {
2474 [a[0] * factor, a[1] * factor, a[2] * factor]
2475}
2476
2477fn dot(a: [f64; 3], b: [f64; 3]) -> f64 {
2478 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
2479}
2480
2481#[cfg(test)]
2482mod tests {
2483 use super::*;
2484
2485 #[test]
2486 fn to_string_marks_biggest_unconstrained_and_tight() {
2487 assert!(
2488 BoxConstraints::expand(None, None)
2489 .to_string()
2490 .contains("biggest")
2491 );
2492 assert!(BoxConstraints::new().to_string().contains("unconstrained"));
2493 assert!(
2494 BoxConstraints::tight_for(Some(50.0), None)
2495 .to_string()
2496 .contains("w=50")
2497 );
2498 }
2499
2500 #[test]
2501 fn copy_with_replaces_fields() {
2502 let constraints = BoxConstraints::new()
2503 .min_width(3.0)
2504 .max_width(7.0)
2505 .min_height(11.0)
2506 .max_height(17.0);
2507 let copy = constraints.copy_with();
2508 assert_eq!(copy, constraints);
2509 let copy = constraints
2510 .copy_with()
2511 .min_width(13.0)
2512 .max_width(17.0)
2513 .min_height(111.0)
2514 .max_height(117.0);
2515 assert_eq!(copy.min_width, 13.0);
2516 assert_eq!(copy.max_width, 17.0);
2517 assert_eq!(copy.min_height, 111.0);
2518 assert_eq!(copy.max_height, 117.0);
2519 assert_ne!(copy, constraints);
2520 let mut copy_hasher = std::collections::hash_map::DefaultHasher::new();
2521 let mut original_hasher = std::collections::hash_map::DefaultHasher::new();
2522 copy.hash(&mut copy_hasher);
2523 constraints.hash(&mut original_hasher);
2524 assert_ne!(copy_hasher.finish(), original_hasher.finish());
2525 }
2526
2527 #[test]
2528 fn operators() {
2529 let constraints = BoxConstraints::new()
2530 .min_width(3.0)
2531 .max_width(7.0)
2532 .min_height(11.0)
2533 .max_height(17.0);
2534 let copy = constraints * 2.0;
2535 assert_eq!(copy.min_width, 6.0);
2536 assert_eq!(copy.max_width, 14.0);
2537 assert_eq!(copy.min_height, 22.0);
2538 assert_eq!(copy.max_height, 34.0);
2539 assert_eq!(copy / 2.0, constraints);
2540 let copy = constraints.truncating_div(2.0);
2541 assert_eq!(copy.min_width, 1.0);
2542 assert_eq!(copy.max_width, 3.0);
2543 assert_eq!(copy.min_height, 5.0);
2544 assert_eq!(copy.max_height, 8.0);
2545 let copy = constraints % 3.0;
2546 assert_eq!(copy.min_width, 0.0);
2547 assert_eq!(copy.max_width, 1.0);
2548 assert_eq!(copy.min_height, 2.0);
2549 assert_eq!(copy.max_height, 2.0);
2550 }
2551
2552 #[test]
2553 fn lerp_from_null_and_between() {
2554 assert!(BoxConstraints::lerp(None, None, 0.5).is_none());
2555 let constraints = BoxConstraints::new()
2556 .min_width(3.0)
2557 .max_width(7.0)
2558 .min_height(11.0)
2559 .max_height(17.0);
2560 let copy = BoxConstraints::lerp(None, Some(constraints), 0.5).unwrap();
2561 assert!((copy.min_width - 1.5).abs() < 1e-9);
2562 assert!((copy.max_width - 3.5).abs() < 1e-9);
2563 assert!((copy.min_height - 5.5).abs() < 1e-9);
2564 assert!((copy.max_height - 8.5).abs() < 1e-9);
2565 let copy = BoxConstraints::lerp(Some(constraints), None, 0.5).unwrap();
2566 assert!((copy.min_width - 1.5).abs() < 1e-9);
2567 let copy = BoxConstraints::lerp(
2568 Some(
2569 BoxConstraints::new()
2570 .min_width(13.0)
2571 .max_width(17.0)
2572 .min_height(111.0)
2573 .max_height(117.0),
2574 ),
2575 Some(constraints),
2576 0.2,
2577 )
2578 .unwrap();
2579 assert!((copy.min_width - 11.0).abs() < 1e-9);
2580 assert!((copy.max_width - 15.0).abs() < 1e-9);
2581 assert!((copy.min_height - 91.0).abs() < 1e-9);
2582 assert!((copy.max_height - 97.0).abs() < 1e-9);
2583 }
2584
2585 #[test]
2586 fn lerp_unbounded_axes() {
2587 let constraints1 = BoxConstraints::new()
2588 .min_width(f64::INFINITY)
2589 .min_height(10.0)
2590 .max_height(20.0);
2591 let constraints2 = BoxConstraints::new()
2592 .min_width(f64::INFINITY)
2593 .min_height(20.0)
2594 .max_height(30.0);
2595 let constraints3 = BoxConstraints::new()
2596 .min_width(f64::INFINITY)
2597 .min_height(15.0)
2598 .max_height(25.0);
2599 assert_eq!(
2600 BoxConstraints::lerp(Some(constraints1), Some(constraints2), 0.5),
2601 Some(constraints3)
2602 );
2603 }
2604
2605 #[test]
2606 fn lerp_overshoot_is_normalized() {
2607 assert_eq!(
2608 BoxConstraints::lerp(
2609 Some(BoxConstraints::tight_for(None, Some(25.0))),
2610 Some(BoxConstraints::tight_for(None, Some(0.0))),
2611 1.2,
2612 ),
2613 Some(BoxConstraints::tight_for(None, Some(0.0)))
2614 );
2615 let lerped = BoxConstraints::lerp(
2616 Some(BoxConstraints::tight(Size::new(10.0, 10.0))),
2617 Some(BoxConstraints::new().max_width(100.0).max_height(100.0)),
2618 -0.1,
2619 )
2620 .unwrap();
2621 assert!(lerped.is_normalized());
2622 assert_eq!(lerped, BoxConstraints::tight(Size::new(11.0, 11.0)));
2623 }
2624
2625 #[test]
2626 fn normalize_raises_inverted_max() {
2627 let constraints = BoxConstraints::new()
2628 .min_width(3.0)
2629 .max_width(2.0)
2630 .min_height(11.0)
2631 .max_height(18.0);
2632 let copy = constraints.normalize();
2633 assert_eq!(copy.min_width, 3.0);
2634 assert_eq!(copy.max_width, 3.0);
2635 assert_eq!(copy.min_height, 11.0);
2636 assert_eq!(copy.max_height, 18.0);
2637 }
2638
2639 #[test]
2640 fn from_view_constraints() {
2641 let unconstrained = BoxConstraints::from_view_constraints(ViewConstraints::default());
2642 assert_eq!(unconstrained, BoxConstraints::new());
2643 let constraints =
2644 BoxConstraints::from_view_constraints(ViewConstraints::new(1.0, 2.0, 3.0, 4.0));
2645 assert_eq!(
2646 constraints,
2647 BoxConstraints::new()
2648 .min_width(1.0)
2649 .max_width(2.0)
2650 .min_height(3.0)
2651 .max_height(4.0)
2652 );
2653 }
2654
2655 #[test]
2656 fn constrain_size_and_attempt_to_preserve_aspect_ratio_empty_size() {
2657 let constraints = BoxConstraints::new()
2658 .min_width(10.0)
2659 .max_width(20.0)
2660 .min_height(10.0)
2661 .max_height(20.0);
2662 let constrained =
2663 constraints.constrain_size_and_attempt_to_preserve_aspect_ratio(Size::new(15.0, 0.0));
2664 assert_eq!(constrained, Size::new(15.0, 10.0));
2665 }
2666}
2667
2668#[cfg(test)]
2669mod hit_test_tests {
2670 use super::*;
2671
2672 #[derive(Debug)]
2673 struct DummyHitTestTarget;
2674
2675 impl HitTestTarget for DummyHitTestTarget {
2676 fn handle_event(&self, _app: &mut App, _event: &PointerEvent, _entry: &HitTestEntry) {}
2677 }
2678
2679 #[test]
2681 fn wrapping_shares_the_path() {
2682 let transform = Matrix4::translation(40.0, 150.0);
2683 let mut wrapped = HitTestResult::new();
2684 wrapped.push_transform(transform);
2685 wrapped.add(HitTestEntry::new(DummyHitTestTarget));
2686 assert_eq!(wrapped.path().len(), 1);
2687 assert_eq!(wrapped.path()[0].transform(), Some(transform));
2688
2689 let mut wrapping = BoxHitTestResult::wrap(&mut wrapped);
2690 wrapping.add(HitTestEntry::new(DummyHitTestTarget));
2691 assert_eq!(wrapping.path().len(), 2);
2692 assert_eq!(wrapping.path()[1].transform(), Some(transform));
2693
2694 wrapped.add(HitTestEntry::new(DummyHitTestTarget));
2695 assert_eq!(wrapped.path().len(), 3);
2696 assert_eq!(wrapped.path()[2].transform(), Some(transform));
2697 }
2698
2699 #[test]
2701 fn add_with_paint_transform() {
2702 let mut base = HitTestResult::new();
2703 let mut result = BoxHitTestResult::wrap(&mut base);
2704 let mut positions = Vec::new();
2705
2706 let is_hit = result.add_with_paint_transform(None, Offset::ZERO, |_, position| {
2707 positions.push(position);
2708 true
2709 });
2710 assert!(is_hit);
2711 assert_eq!(positions, [Offset::ZERO]);
2712 positions.clear();
2713
2714 let is_hit = result.add_with_paint_transform(
2715 Some(Matrix4::translation(20.0, 30.0)),
2716 Offset::ZERO,
2717 |_, position| {
2718 positions.push(position);
2719 true
2720 },
2721 );
2722 assert!(is_hit);
2723 assert_eq!(positions, [Offset::new(-20.0, -30.0)]);
2724 positions.clear();
2725
2726 let position = Offset::new(3.0, 4.0);
2727 let is_hit = result.add_with_paint_transform(None, position, |_, position| {
2728 positions.push(position);
2729 false
2730 });
2731 assert!(!is_hit);
2732 assert_eq!(positions, [position]);
2733 positions.clear();
2734
2735 let is_hit =
2736 result.add_with_paint_transform(Some(Matrix4::IDENTITY), position, |_, position| {
2737 positions.push(position);
2738 true
2739 });
2740 assert!(is_hit);
2741 assert_eq!(positions, [position]);
2742 positions.clear();
2743
2744 let is_hit = result.add_with_paint_transform(
2745 Some(Matrix4::translation(20.0, 30.0)),
2746 position,
2747 |_, position| {
2748 positions.push(position);
2749 true
2750 },
2751 );
2752 assert!(is_hit);
2753 assert_eq!(positions, [position - Offset::new(20.0, 30.0)]);
2754 positions.clear();
2755
2756 let is_hit = result.add_with_paint_transform(
2757 Some(force_to_point(position)), position,
2759 |_, position| {
2760 positions.push(position);
2761 true
2762 },
2763 );
2764 assert!(!is_hit);
2765 assert!(positions.is_empty());
2766 }
2767
2768 fn force_to_point(offset: Offset) -> Matrix4 {
2770 let mut storage = [0.0; 16];
2771 storage[10] = 1.0;
2772 storage[12] = offset.dx() as f32;
2773 storage[13] = offset.dy() as f32;
2774 storage[15] = 1.0;
2775 Matrix4::from_flutter_array(&storage)
2776 }
2777
2778 #[test]
2783 fn a_hit_test_through_a_perspective_transform_lands_where_flutters_does() {
2784 let angle = 0.5_f64;
2785 let (cos, sin) = (angle.cos(), angle.sin());
2786 let perspective = 0.001;
2787 let mut storage = Matrix4::IDENTITY.to_flutter_array();
2788 storage[0] = cos as f32;
2789 storage[2] = -sin as f32;
2790 storage[8] = sin as f32;
2791 storage[10] = cos as f32;
2792 storage[3] = (-perspective * sin) as f32;
2794 storage[11] = (perspective * cos) as f32;
2795 let transform = Matrix4::from_flutter_array(&storage);
2796
2797 let mut base = HitTestResult::new();
2798 let mut result = BoxHitTestResult::wrap(&mut base);
2799 let position = Offset::new(100.0, 50.0);
2800 let mut landed = None;
2801 let is_hit = result.add_with_paint_transform(Some(transform), position, |result, local| {
2802 result.add(HitTestEntry::new(DummyHitTestTarget));
2803 landed = Some(local);
2804 true
2805 });
2806 assert!(is_hit);
2807
2808 let w = 1.0 + perspective * sin * position.dx() / cos;
2811 let expected = Offset::new(position.dx() / cos / w, position.dy() / w);
2812 let landed = landed.expect("the child was hit-tested");
2813 assert!(
2814 (landed.dx() - expected.dx()).abs() < 1e-3
2815 && (landed.dy() - expected.dy()).abs() < 1e-3,
2816 "{landed:?} != {expected:?}"
2817 );
2818 }
2819
2820 #[test]
2822 fn add_with_paint_offset() {
2823 let mut base = HitTestResult::new();
2824 let mut result = BoxHitTestResult::wrap(&mut base);
2825 let mut positions = Vec::new();
2826
2827 let is_hit = result.add_with_paint_offset(None, Offset::ZERO, |_, position| {
2828 positions.push(position);
2829 true
2830 });
2831 assert!(is_hit);
2832 assert_eq!(positions, [Offset::ZERO]);
2833 positions.clear();
2834
2835 let is_hit = result.add_with_paint_offset(
2836 Some(Offset::new(55.0, 32.0)),
2837 Offset::ZERO,
2838 |_, position| {
2839 positions.push(position);
2840 true
2841 },
2842 );
2843 assert!(is_hit);
2844 assert_eq!(positions, [Offset::new(-55.0, -32.0)]);
2845 positions.clear();
2846
2847 let position = Offset::new(3.0, 4.0);
2848 let is_hit = result.add_with_paint_offset(
2849 Some(Offset::new(55.0, 32.0)),
2850 position,
2851 |result, position| {
2852 result.add(HitTestEntry::new(DummyHitTestTarget));
2853 positions.push(position);
2854 true
2855 },
2856 );
2857 assert!(is_hit);
2858 assert_eq!(positions, [position - Offset::new(55.0, 32.0)]);
2859 assert_eq!(
2860 result.path()[0].transform(),
2861 Some(Matrix4::translation(-55.0, -32.0)),
2862 "the entry records the transform to the child"
2863 );
2864 }
2865
2866 #[test]
2868 fn add_with_raw_transform() {
2869 let mut base = HitTestResult::new();
2870 let mut result = BoxHitTestResult::wrap(&mut base);
2871 let mut positions = Vec::new();
2872
2873 let is_hit = result.add_with_raw_transform(
2874 Some(Matrix4::translation(20.0, 30.0)),
2875 Offset::ZERO,
2876 |_, position| {
2877 positions.push(position);
2878 true
2879 },
2880 );
2881 assert!(is_hit);
2882 assert_eq!(positions, [Offset::new(20.0, 30.0)]);
2883 positions.clear();
2884
2885 let position = Offset::new(3.0, 4.0);
2886 let is_hit =
2887 result.add_with_raw_transform(Some(Matrix4::IDENTITY), position, |_, position| {
2888 positions.push(position);
2889 true
2890 });
2891 assert!(is_hit);
2892 assert_eq!(positions, [position]);
2893 }
2894}
2895
2896#[cfg(test)]
2897mod intrinsics_tests {
2898 use inset_foundation::AppCell;
2899 use std::cell::Cell;
2900 use std::rc::Rc;
2901
2902 use super::*;
2903 use crate::object::RenderObjectData;
2904
2905 struct CountingBox {
2908 render_object: RenderObjectData,
2909 render_box: RenderBoxData,
2910 computations: Rc<Cell<u32>>,
2911 }
2912
2913 impl RenderObject for CountingBox {
2914 crate::render_object_accessors!();
2915
2916 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
2917 let size = self.constraints(app).smallest();
2918 self.set_size(app, size);
2919 }
2920 }
2921
2922 impl RenderBox for CountingBox {
2923 crate::render_box_accessors!();
2924
2925 fn compute_min_intrinsic_width(
2926 self: RenderHandle<Self>,
2927 app: &mut App,
2928 height: f64,
2929 ) -> f64 {
2930 let computations = self.get(app).computations.clone();
2931 computations.set(computations.get() + 1);
2932 100.0 + height
2933 }
2934 }
2935
2936 #[test]
2939 fn the_intrinsic_cache_is_reused_and_cleared_by_mark_needs_layout() {
2940 let cell = AppCell::new();
2941 let mut app = cell.borrow_mut();
2942 let computations = Rc::new(Cell::new(0));
2943 let box_ = RenderHandle::new_box(
2944 &mut app,
2945 CountingBox {
2946 render_object: RenderObjectData::new(),
2947 render_box: RenderBoxData::new(),
2948 computations: computations.clone(),
2949 },
2950 );
2951
2952 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2953 assert_eq!(computations.get(), 1);
2954
2955 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2956 assert_eq!(computations.get(), 1, "the memoized value is reused");
2957
2958 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 10.0), 110.0);
2959 assert_eq!(computations.get(), 2, "another height is another entry");
2960
2961 box_.mark_needs_layout(&mut app);
2962 assert_eq!(box_.as_box().get_min_intrinsic_width(&mut app, 0.0), 100.0);
2963 assert_eq!(computations.get(), 3, "mark_needs_layout cleared the cache");
2964 }
2965
2966 #[test]
2969 fn clearing_a_cached_intrinsic_marks_the_parent() {
2970 let cell = AppCell::new();
2971 let mut app = cell.borrow_mut();
2972 let computations = Rc::new(Cell::new(0));
2973 let child = RenderHandle::new_box(
2974 &mut app,
2975 CountingBox {
2976 render_object: RenderObjectData::new(),
2977 render_box: RenderBoxData::new(),
2978 computations,
2979 },
2980 );
2981 let parent = crate::proxy_box::RenderConstrainedBox::new(
2982 &mut app,
2983 BoxConstraints::new(),
2984 Some(child.as_box()),
2985 );
2986 parent.layout(&mut app, BoxConstraints::new().max_width(50.0), false);
2987 assert!(!parent.debug_needs_layout(&app));
2988
2989 child.as_box().get_min_intrinsic_width(&mut app, 0.0);
2990 child.mark_needs_layout(&mut app);
2991 assert!(
2992 parent.debug_needs_layout(&app),
2993 "the parent read an intrinsic, so it has to lay out again"
2994 );
2995 }
2996}