1use inset_embedder::{Matrix4, Offset, TextDirection};
6use inset_foundation::App;
7use inset_painting::{Axis, AxisDirection, EdgeInsets, EdgeInsetsGeometry};
8
9use crate::object::{
10 AnyRenderObject, RenderHandle, RenderObject, RenderObjectData, RenderObjectWithChildData,
11 RenderObjectWithChildMixin,
12};
13use crate::painting_context::PaintingContext;
14use crate::sliver::{
15 AnyRenderSliver, RenderSliver, RenderSliverData, SliverGeometry, SliverHitTestResult,
16 SliverPhysicalParentData, apply_growth_direction_to_axis_direction, calculate_cache_offset,
17 calculate_paint_offset,
18};
19
20pub trait RenderSliverEdgeInsetsPadding:
25 RenderSliver + RenderObjectWithChildMixin<ChildType = AnyRenderSliver>
26{
27 fn resolved_padding(self: RenderHandle<Self>, app: &App) -> Option<EdgeInsets>;
34
35 fn before_padding(self: RenderHandle<Self>, app: &App) -> f64 {
40 let padding = self
41 .resolved_padding(app)
42 .expect("the padding is resolved before layout");
43 let constraints = self.constraints(app);
44 match apply_growth_direction_to_axis_direction(
45 constraints.axis_direction,
46 constraints.growth_direction,
47 ) {
48 AxisDirection::Up => padding.bottom,
49 AxisDirection::Right => padding.left,
50 AxisDirection::Down => padding.top,
51 AxisDirection::Left => padding.right,
52 }
53 }
54
55 fn after_padding(self: RenderHandle<Self>, app: &App) -> f64 {
59 let padding = self
60 .resolved_padding(app)
61 .expect("the padding is resolved before layout");
62 let constraints = self.constraints(app);
63 match apply_growth_direction_to_axis_direction(
64 constraints.axis_direction,
65 constraints.growth_direction,
66 ) {
67 AxisDirection::Up => padding.top,
68 AxisDirection::Right => padding.right,
69 AxisDirection::Down => padding.bottom,
70 AxisDirection::Left => padding.left,
71 }
72 }
73
74 fn main_axis_padding(self: RenderHandle<Self>, app: &App) -> f64 {
79 let padding = self
80 .resolved_padding(app)
81 .expect("the padding is resolved before layout");
82 match self.constraints(app).axis() {
84 Axis::Horizontal => padding.horizontal(),
85 Axis::Vertical => padding.vertical(),
86 }
87 }
88
89 fn cross_axis_padding(self: RenderHandle<Self>, app: &App) -> f64 {
94 let padding = self
95 .resolved_padding(app)
96 .expect("the padding is resolved before layout");
97 match self.constraints(app).axis() {
98 Axis::Horizontal => padding.vertical(),
99 Axis::Vertical => padding.horizontal(),
100 }
101 }
102
103 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
105 let _ = self;
106 if !child.parent_data_is::<SliverPhysicalParentData>(app) {
107 child.set_parent_data(app, SliverPhysicalParentData::new());
108 }
109 }
110
111 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
113 let constraints = self.constraints(app);
114 let paint_offset = |from: f64, to: f64| calculate_paint_offset(constraints, from, to);
115 let cache_offset = |from: f64, to: f64| calculate_cache_offset(constraints, from, to);
116
117 let resolved_padding = self
118 .resolved_padding(app)
119 .expect("the padding is resolved before layout");
120 let before_padding = self.before_padding(app);
121 let after_padding = self.after_padding(app);
122 let main_axis_padding = self.main_axis_padding(app);
123 let cross_axis_padding = self.cross_axis_padding(app);
124 let Some(child) = self.child(app) else {
125 let paint_extent = paint_offset(0.0, main_axis_padding);
126 let cache_extent = cache_offset(0.0, main_axis_padding);
127 self.set_geometry(
128 app,
129 SliverGeometry::new()
130 .scroll_extent(main_axis_padding)
131 .paint_extent(paint_extent.min(constraints.remaining_paint_extent))
132 .max_paint_extent(main_axis_padding)
133 .cache_extent(cache_extent),
134 );
135 return;
136 };
137 let before_padding_paint_extent = paint_offset(0.0, before_padding);
138 let mut overlap = constraints.overlap;
139 if overlap > 0.0 {
140 overlap = (constraints.overlap - before_padding_paint_extent).max(0.0);
141 }
142 let child_constraints = constraints
143 .copy_with()
144 .scroll_offset((constraints.scroll_offset - before_padding).max(0.0))
145 .cache_origin((constraints.cache_origin + before_padding).min(0.0))
146 .overlap(overlap)
147 .remaining_paint_extent(
148 constraints.remaining_paint_extent - paint_offset(0.0, before_padding),
149 )
150 .remaining_cache_extent(
151 constraints.remaining_cache_extent - cache_offset(0.0, before_padding),
152 )
153 .cross_axis_extent((constraints.cross_axis_extent - cross_axis_padding).max(0.0))
154 .preceding_scroll_extent(before_padding + constraints.preceding_scroll_extent);
155 child.layout(app, child_constraints, true);
156
157 let child_layout_geometry = child.geometry(app);
158 if let Some(correction) = child_layout_geometry.scroll_offset_correction {
159 self.set_geometry(
160 app,
161 SliverGeometry::new().scroll_offset_correction(correction),
162 );
163 return;
164 }
165 let scroll_extent = child_layout_geometry.scroll_extent;
166 let before_padding_cache_extent = cache_offset(0.0, before_padding);
167 let after_padding_cache_extent = cache_offset(
168 before_padding + scroll_extent,
169 main_axis_padding + scroll_extent,
170 );
171 let after_padding_paint_extent = paint_offset(
172 before_padding + scroll_extent,
173 main_axis_padding + scroll_extent,
174 );
175 let main_axis_padding_cache_extent =
176 before_padding_cache_extent + after_padding_cache_extent;
177 let main_axis_padding_paint_extent =
178 before_padding_paint_extent + after_padding_paint_extent;
179 let paint_extent = (before_padding_paint_extent
180 + child_layout_geometry
181 .paint_extent
182 .max(child_layout_geometry.layout_extent + after_padding_paint_extent))
183 .min(constraints.remaining_paint_extent);
184 self.set_geometry(
185 app,
186 SliverGeometry::new()
187 .paint_origin(child_layout_geometry.paint_origin)
188 .scroll_extent(main_axis_padding + scroll_extent)
189 .paint_extent(paint_extent)
190 .layout_extent(
191 (main_axis_padding_paint_extent + child_layout_geometry.layout_extent)
192 .min(paint_extent),
193 )
194 .cache_extent(
195 (main_axis_padding_cache_extent + child_layout_geometry.cache_extent)
196 .min(constraints.remaining_cache_extent),
197 )
198 .max_paint_extent(main_axis_padding + child_layout_geometry.max_paint_extent)
199 .hit_test_extent(
200 (main_axis_padding_paint_extent + child_layout_geometry.paint_extent)
201 .max(before_padding_paint_extent + child_layout_geometry.hit_test_extent),
202 )
203 .has_visual_overflow(child_layout_geometry.has_visual_overflow),
204 );
205 let calculated_offset = match apply_growth_direction_to_axis_direction(
206 constraints.axis_direction,
207 constraints.growth_direction,
208 ) {
209 AxisDirection::Up => paint_offset(
210 resolved_padding.bottom + scroll_extent,
211 resolved_padding.vertical() + scroll_extent,
212 ),
213 AxisDirection::Left => paint_offset(
214 resolved_padding.right + scroll_extent,
215 resolved_padding.horizontal() + scroll_extent,
216 ),
217 AxisDirection::Right => paint_offset(0.0, resolved_padding.left),
218 AxisDirection::Down => paint_offset(0.0, resolved_padding.top),
219 };
220 let child_paint_offset = match constraints.axis() {
221 Axis::Horizontal => Offset::new(calculated_offset, resolved_padding.top),
222 Axis::Vertical => Offset::new(resolved_padding.left, calculated_offset),
223 };
224 child
225 .as_object()
226 .parent_data_of_mut::<SliverPhysicalParentData>(app)
227 .paint_offset = child_paint_offset;
228 debug_assert_eq!(before_padding, self.before_padding(app));
229 debug_assert_eq!(after_padding, self.after_padding(app));
230 debug_assert_eq!(main_axis_padding, self.main_axis_padding(app));
231 debug_assert_eq!(cross_axis_padding, self.cross_axis_padding(app));
232 }
233
234 fn hit_test_children(
236 self: RenderHandle<Self>,
237 app: &mut App,
238 result: &mut SliverHitTestResult<'_>,
239 main_axis_position: f64,
240 cross_axis_position: f64,
241 ) -> bool {
242 let Some(child) = self.child(app) else {
243 return false;
244 };
245 if child.geometry(app).hit_test_extent <= 0.0 {
246 return false;
247 }
248 let paint_offset = child
249 .as_object()
250 .parent_data_of::<SliverPhysicalParentData>(app)
251 .paint_offset;
252 let main_axis_offset =
253 RenderSliverEdgeInsetsPadding::child_main_axis_position(self, app, child.as_object());
254 let cross_axis_offset =
255 RenderSliverEdgeInsetsPadding::child_cross_axis_position(self, app, child.as_object());
256 result.add_with_axis_offset(
257 Some(paint_offset),
258 main_axis_offset,
259 cross_axis_offset,
260 main_axis_position,
261 cross_axis_position,
262 |result, main, cross| child.hit_test(app, result, main, cross),
263 )
264 }
265
266 fn child_main_axis_position(
268 self: RenderHandle<Self>,
269 app: &App,
270 child: AnyRenderObject,
271 ) -> f64 {
272 debug_assert_eq!(Some(child), self.child(app).map(AnyRenderSliver::as_object));
273 let constraints = self.constraints(app);
274 let before_padding = self.before_padding(app);
275 self.calculate_paint_offset(app, constraints, 0.0, before_padding)
276 }
277
278 fn child_cross_axis_position(
280 self: RenderHandle<Self>,
281 app: &App,
282 child: AnyRenderObject,
283 ) -> f64 {
284 debug_assert_eq!(Some(child), self.child(app).map(AnyRenderSliver::as_object));
285 let padding = self
286 .resolved_padding(app)
287 .expect("the padding is resolved before layout");
288 match self.constraints(app).axis() {
289 Axis::Horizontal => padding.top,
290 Axis::Vertical => padding.left,
291 }
292 }
293
294 fn child_scroll_offset(
296 self: RenderHandle<Self>,
297 app: &App,
298 child: AnyRenderObject,
299 ) -> Option<f64> {
300 debug_assert_eq!(child.parent(app), Some(self.as_object()));
301 Some(self.before_padding(app))
302 }
303
304 fn apply_paint_transform(
306 self: RenderHandle<Self>,
307 app: &App,
308 child: AnyRenderObject,
309 transform: &mut Matrix4,
310 ) {
311 debug_assert_eq!(Some(child), self.child(app).map(AnyRenderSliver::as_object));
312 child
313 .parent_data_of::<SliverPhysicalParentData>(app)
314 .apply_paint_transform(transform);
315 }
316
317 fn paint(
319 self: RenderHandle<Self>,
320 app: &mut App,
321 context: &mut PaintingContext,
322 offset: Offset,
323 ) {
324 let Some(child) = self.child(app) else {
325 return;
326 };
327 if !child.geometry(app).visible {
328 return;
329 }
330 let paint_offset = child
331 .as_object()
332 .parent_data_of::<SliverPhysicalParentData>(app)
333 .paint_offset;
334 context.paint_child(app, child.as_object(), offset + paint_offset);
335 }
336}
337
338pub struct RenderSliverPadding {
343 render_object: RenderObjectData,
344 render_sliver: RenderSliverData,
345 child: RenderObjectWithChildData<AnyRenderSliver>,
346 padding: EdgeInsetsGeometry,
347 text_direction: Option<TextDirection>,
348 resolved_padding: Option<EdgeInsets>,
349}
350
351impl RenderSliverPadding {
352 pub fn new(
356 app: &mut App,
357 padding: EdgeInsetsGeometry,
358 text_direction: Option<TextDirection>,
359 child: Option<AnyRenderSliver>,
360 ) -> RenderHandle<RenderSliverPadding> {
361 debug_assert!(padding.is_non_negative());
362 let this = RenderHandle::new_sliver(
363 app,
364 RenderSliverPadding {
365 render_object: RenderObjectData::new(),
366 render_sliver: RenderSliverData::new(),
367 child: RenderObjectWithChildData::new(),
368 padding,
369 text_direction,
370 resolved_padding: None,
371 },
372 );
373 this.set_child(app, child);
374 this
375 }
376
377 fn resolve(self: RenderHandle<Self>, app: &mut App) {
379 if self.get(app).resolved_padding.is_some() {
380 return;
381 }
382 let this = self.get(app);
383 let resolved = this.padding.resolve(this.text_direction);
384 debug_assert!(resolved.is_non_negative());
385 self.get_mut(app).resolved_padding = Some(resolved);
386 }
387
388 fn mark_needs_resolution(self: RenderHandle<Self>, app: &mut App) {
390 self.get_mut(app).resolved_padding = None;
391 self.mark_needs_layout(app);
392 }
393
394 pub fn padding(self: RenderHandle<Self>, app: &App) -> EdgeInsetsGeometry {
399 self.get(app).padding
400 }
401
402 pub fn set_padding(self: RenderHandle<Self>, app: &mut App, value: EdgeInsetsGeometry) {
404 debug_assert!(value.is_non_negative());
405 if self.get(app).padding == value {
406 return;
407 }
408 self.get_mut(app).padding = value;
409 self.mark_needs_resolution(app);
410 }
411
412 pub fn text_direction(self: RenderHandle<Self>, app: &App) -> Option<TextDirection> {
417 self.get(app).text_direction
418 }
419
420 pub fn set_text_direction(
422 self: RenderHandle<Self>,
423 app: &mut App,
424 value: Option<TextDirection>,
425 ) {
426 if self.get(app).text_direction == value {
427 return;
428 }
429 self.get_mut(app).text_direction = value;
430 self.mark_needs_resolution(app);
431 }
432}
433
434impl RenderObjectWithChildMixin for RenderSliverPadding {
435 type ChildType = AnyRenderSliver;
436
437 fn child_data(
438 self: RenderHandle<Self>,
439 app: &App,
440 ) -> &RenderObjectWithChildData<AnyRenderSliver> {
441 &self.get(app).child
442 }
443
444 fn child_data_mut(
445 self: RenderHandle<Self>,
446 app: &mut App,
447 ) -> &mut RenderObjectWithChildData<AnyRenderSliver> {
448 &mut self.get_mut(app).child
449 }
450}
451
452impl RenderSliverEdgeInsetsPadding for RenderSliverPadding {
453 fn resolved_padding(self: RenderHandle<Self>, app: &App) -> Option<EdgeInsets> {
454 self.get(app).resolved_padding
455 }
456}
457
458impl RenderObject for RenderSliverPadding {
459 crate::render_object_accessors!();
460
461 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
462 RenderSliverEdgeInsetsPadding::setup_parent_data(self, app, child)
463 }
464
465 fn visit_children(
466 self: RenderHandle<Self>,
467 app: &App,
468 visitor: &mut dyn FnMut(AnyRenderObject),
469 ) {
470 if let Some(child) = self.child(app) {
471 visitor(child.as_object());
472 }
473 }
474
475 fn apply_paint_transform(
476 self: RenderHandle<Self>,
477 app: &App,
478 child: AnyRenderObject,
479 transform: &mut Matrix4,
480 ) {
481 RenderSliverEdgeInsetsPadding::apply_paint_transform(self, app, child, transform)
482 }
483
484 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
485 self.resolve(app);
486 RenderSliverEdgeInsetsPadding::perform_layout(self, app)
487 }
488
489 fn paint(
490 self: RenderHandle<Self>,
491 app: &mut App,
492 context: &mut PaintingContext,
493 offset: Offset,
494 ) {
495 RenderSliverEdgeInsetsPadding::paint(self, app, context, offset)
496 }
497}
498
499impl RenderSliver for RenderSliverPadding {
500 crate::render_sliver_accessors!();
501
502 fn hit_test_children(
503 self: RenderHandle<Self>,
504 app: &mut App,
505 result: &mut SliverHitTestResult<'_>,
506 main_axis_position: f64,
507 cross_axis_position: f64,
508 ) -> bool {
509 RenderSliverEdgeInsetsPadding::hit_test_children(
510 self,
511 app,
512 result,
513 main_axis_position,
514 cross_axis_position,
515 )
516 }
517
518 fn child_main_axis_position(
519 self: RenderHandle<Self>,
520 app: &App,
521 child: AnyRenderObject,
522 ) -> f64 {
523 RenderSliverEdgeInsetsPadding::child_main_axis_position(self, app, child)
524 }
525
526 fn child_cross_axis_position(
527 self: RenderHandle<Self>,
528 app: &App,
529 child: AnyRenderObject,
530 ) -> f64 {
531 RenderSliverEdgeInsetsPadding::child_cross_axis_position(self, app, child)
532 }
533
534 fn child_scroll_offset(
535 self: RenderHandle<Self>,
536 app: &App,
537 child: AnyRenderObject,
538 ) -> Option<f64> {
539 RenderSliverEdgeInsetsPadding::child_scroll_offset(self, app, child)
540 }
541}
542
543#[cfg(test)]
544mod tests {
545 use inset_embedder::Size;
546 use inset_foundation::AppCell;
547 use inset_foundation::Handle;
548 use inset_gestures::HitTestResult;
549
550 use super::*;
551 use crate::box_::{AnyRenderBox, BoxConstraints, RenderBox};
552 use crate::pipeline_owner::PipelineOwner;
553 use crate::sliver::{RenderSliverToBoxAdapter, SliverPhysicalParentData};
554 use crate::sliver_multi_box_adaptor::test_support::hit_testable_box;
555 use crate::viewport::RenderViewport;
556 use crate::viewport_offset::{FixedViewportOffset, ViewportOffset};
557
558 fn lay_out(
560 app: &mut App,
561 sliver: AnyRenderSliver,
562 size: Size,
563 scroll_offset: f64,
564 ) -> (Handle<PipelineOwner>, RenderHandle<RenderViewport>) {
565 let offset = FixedViewportOffset::new(app, scroll_offset);
566 let viewport = RenderViewport::new(
567 app,
568 AxisDirection::Right,
569 offset.as_viewport_offset(),
570 Some(vec![sliver]),
571 None,
572 );
573 let owner = PipelineOwner::new(app, None, None);
574 owner.set_root_node(app, Some(viewport.as_object()));
575 viewport
576 .as_box()
577 .layout(app, BoxConstraints::tight(size), false);
578 (owner, viewport)
579 }
580
581 fn padded_box(
582 app: &mut App,
583 padding: EdgeInsetsGeometry,
584 height: f64,
585 ) -> (RenderHandle<RenderSliverPadding>, AnyRenderBox) {
586 let child_box = hit_testable_box(app, height);
587 let adapter = RenderSliverToBoxAdapter::new(app, Some(child_box));
588 let padding = RenderSliverPadding::new(
589 app,
590 padding,
591 Some(TextDirection::Ltr),
592 Some(adapter.as_sliver()),
593 );
594 (padding, child_box)
595 }
596
597 #[test]
600 fn padding_insets_the_child_and_grows_the_scroll_extent() {
601 let cell = AppCell::new();
602 let mut app = cell.borrow_mut();
603 let (padding, child_box) = padded_box(
604 &mut app,
605 EdgeInsetsGeometry::from_ltrb(10.0, 20.0, 30.0, 40.0),
606 100.0,
607 );
608 lay_out(&mut app, padding.as_sliver(), Size::new(200.0, 400.0), 0.0);
609
610 let geometry = padding.geometry(&app);
611 assert_eq!(geometry.scroll_extent, 160.0);
612 assert_eq!(geometry.paint_extent, 160.0);
613 assert_eq!(child_box.size(&app), Size::new(160.0, 100.0));
615 assert_eq!(padding.before_padding(&app), 20.0);
616 assert_eq!(padding.after_padding(&app), 40.0);
617 assert_eq!(padding.main_axis_padding(&app), 60.0);
618 assert_eq!(padding.cross_axis_padding(&app), 40.0);
619 let child = padding.child(&app).expect("has a child");
620 assert_eq!(
621 child
622 .as_object()
623 .parent_data_of::<SliverPhysicalParentData>(&app)
624 .paint_offset,
625 Offset::new(10.0, 20.0)
626 );
627 assert_eq!(
628 RenderSliver::child_scroll_offset(padding, &app, child.as_object()),
629 Some(20.0)
630 );
631 }
632
633 #[test]
635 fn scrolling_consumes_the_leading_padding_first() {
636 let cell = AppCell::new();
637 let mut app = cell.borrow_mut();
638 let (padding, _) = padded_box(
639 &mut app,
640 EdgeInsetsGeometry::from_ltrb(0.0, 20.0, 0.0, 0.0),
641 100.0,
642 );
643 lay_out(&mut app, padding.as_sliver(), Size::new(200.0, 400.0), 10.0);
644
645 let child = padding.child(&app).expect("has a child");
646 assert_eq!(child.constraints(&app).scroll_offset, 0.0);
648 assert_eq!(
649 RenderSliverEdgeInsetsPadding::child_main_axis_position(
650 padding,
651 &app,
652 child.as_object()
653 ),
654 10.0
655 );
656 assert_eq!(padding.geometry(&app).scroll_extent, 120.0);
657 }
658
659 #[test]
661 fn a_childless_padding_is_the_padding_alone() {
662 let cell = AppCell::new();
663 let mut app = cell.borrow_mut();
664 let padding = RenderSliverPadding::new(
665 &mut app,
666 EdgeInsetsGeometry::all(15.0),
667 Some(TextDirection::Ltr),
668 None,
669 );
670 lay_out(&mut app, padding.as_sliver(), Size::new(200.0, 400.0), 0.0);
671
672 let geometry = padding.geometry(&app);
673 assert_eq!(geometry.scroll_extent, 30.0);
674 assert_eq!(geometry.paint_extent, 30.0);
675 assert_eq!(geometry.max_paint_extent, 30.0);
676 }
677
678 #[test]
680 fn padding_hit_tests_its_child_at_the_inset_position() {
681 let cell = AppCell::new();
682 let mut app = cell.borrow_mut();
683 let (padding, _) = padded_box(
684 &mut app,
685 EdgeInsetsGeometry::from_ltrb(10.0, 20.0, 30.0, 40.0),
686 100.0,
687 );
688 lay_out(&mut app, padding.as_sliver(), Size::new(200.0, 400.0), 0.0);
689
690 let mut inside = HitTestResult::new();
691 let hit_inside = padding.hit_test(
692 &mut app,
693 &mut SliverHitTestResult::wrap(&mut inside),
694 25.0,
695 20.0,
696 );
697 let mut in_padding = HitTestResult::new();
698 let hit_in_padding = padding.hit_test(
699 &mut app,
700 &mut SliverHitTestResult::wrap(&mut in_padding),
701 5.0,
702 20.0,
703 );
704
705 let child = padding.child(&app).expect("has a child");
706 assert_eq!(
708 RenderSliver::child_cross_axis_position(padding, &app, child.as_object()),
709 10.0
710 );
711 assert!(hit_inside);
713 assert_eq!(inside.path().len(), 3);
714 assert!(!hit_in_padding);
715 }
716
717 #[test]
719 fn setting_the_padding_re_resolves_it() {
720 let cell = AppCell::new();
721 let mut app = cell.borrow_mut();
722 let (padding, _) = padded_box(&mut app, EdgeInsetsGeometry::all(10.0), 100.0);
723 lay_out(&mut app, padding.as_sliver(), Size::new(200.0, 400.0), 0.0);
724 assert_eq!(padding.resolved_padding(&app), Some(EdgeInsets::all(10.0)));
725
726 padding.set_padding(&mut app, EdgeInsetsGeometry::all(25.0));
727
728 assert_eq!(padding.resolved_padding(&app), None);
729 assert!(padding.as_object().debug_needs_layout(&app));
730 }
731}