1use std::sync::Arc;
4
5use inset_embedder::{Canvas, Clip, Matrix4, Offset, Path, PathBuilder, RRect, Rect};
6use inset_foundation::{App, Handle};
7use inset_painting::{ClipContext, inverse_transform_rect};
8
9use crate::layer::{
10 AnyContainerLayer, AnyLayer, ClipPathLayer, ClipRRectLayer, ClipRectLayer, CompositionCallback,
11 ContainerLayer, ErasedLayer, LayerHandle, OffsetLayerMixin, OpacityLayer, PictureLayer,
12 TransformLayer,
13};
14use crate::object::AnyRenderObject;
15
16pub struct PaintingContext {
32 container_layer: AnyContainerLayer,
33 pub estimated_bounds: Rect,
41 canvas: Option<Canvas>,
42 is_complex_hint: bool,
43 will_change_hint: bool,
44}
45
46impl PaintingContext {
47 pub fn new(container_layer: AnyContainerLayer, estimated_bounds: Rect) -> PaintingContext {
52 PaintingContext {
53 container_layer,
54 estimated_bounds,
55 canvas: None,
56 is_complex_hint: false,
57 will_change_hint: false,
58 }
59 }
60
61 pub fn repaint_composited_child(app: &mut App, child: AnyRenderObject) {
68 debug_assert!(child.needs_paint(app));
69 Self::repaint_composited_child_inner(app, child, false, None);
70 }
71
72 fn repaint_composited_child_inner(
73 app: &mut App,
74 child: AnyRenderObject,
75 debug_also_painted_parent: bool,
76 child_context: Option<PaintingContext>,
77 ) {
78 debug_assert!(child.is_repaint_boundary(app));
79 let child_layer = if let Some(child_layer) =
80 child.layer(app).and_then(|layer| layer.as_offset_layer())
81 {
82 debug_assert!(debug_also_painted_parent || child_layer.as_layer().attached(app));
83 let debug_old_offset = if cfg!(debug_assertions) {
84 Some(child_layer.offset(app))
85 } else {
86 None
87 };
88 child_layer.as_container_layer().remove_all_children(app);
89 let updated_layer = child.update_composited_layer(app, Some(child_layer));
90 debug_assert_eq!(
91 updated_layer, child_layer,
92 "{child:?} created a new layer instance {updated_layer:?} instead of reusing the \
93 existing layer {child_layer:?}. See the documentation of RenderObject.updateCompositedLayer \
94 for more information on how to correctly implement this method."
95 );
96 debug_assert_eq!(debug_old_offset, Some(updated_layer.offset(app)));
97 child_layer
98 } else {
99 debug_assert!(debug_also_painted_parent);
100 debug_assert!(child.layer(app).is_none());
101
102 let layer = child.update_composited_layer(app, None);
107 LayerHandle::set_layer(
108 app,
109 |app| &mut child.data_mut(app).layer,
110 Some(layer.as_container_layer()),
111 );
112 layer
113 };
114 child.set_needs_composited_layer_update(app, false);
115 debug_assert_eq!(Some(child_layer.as_container_layer()), child.layer(app));
116 debug_assert!(
117 child
118 .layer(app)
119 .is_some_and(|layer| layer.as_offset_layer().is_some())
120 );
121
122 let mut child_context = child_context.unwrap_or_else(|| {
123 PaintingContext::new(child_layer.as_container_layer(), child.paint_bounds(app))
124 });
125 child.paint_with_context(app, &mut child_context, Offset::ZERO);
126
127 debug_assert_eq!(Some(child_layer.as_container_layer()), child.layer(app));
130 child_context.stop_recording_if_needed(app);
131 }
132
133 pub fn update_layer_properties(app: &mut App, child: AnyRenderObject) {
140 debug_assert!(child.is_repaint_boundary(app) && child.was_repaint_boundary(app));
141 debug_assert!(!child.needs_paint(app));
142 debug_assert!(child.layer(app).is_some());
143
144 let child_layer = child
145 .layer(app)
146 .expect("checked")
147 .as_offset_layer()
148 .expect("a repaint boundary's layer is an OffsetLayer");
149 let debug_old_offset = if cfg!(debug_assertions) {
150 Some(child_layer.offset(app))
151 } else {
152 None
153 };
154 let updated_layer = child.update_composited_layer(app, Some(child_layer));
155 debug_assert_eq!(
156 updated_layer, child_layer,
157 "{child:?} created a new layer instance {updated_layer:?} instead of reusing the \
158 existing layer {child_layer:?}. See the documentation of RenderObject.updateCompositedLayer \
159 for more information on how to correctly implement this method."
160 );
161 debug_assert_eq!(debug_old_offset, Some(updated_layer.offset(app)));
162 child.set_needs_composited_layer_update(app, false);
163 }
164
165 pub fn paint_child(&mut self, app: &mut App, child: AnyRenderObject, offset: Offset) {
171 if child.is_repaint_boundary(app) {
172 self.stop_recording_if_needed(app);
173 self.composite_child(app, child, offset);
174 } else if child.was_repaint_boundary(app) {
175 debug_assert!(
176 child
177 .layer(app)
178 .is_some_and(|layer| layer.as_offset_layer().is_some())
179 );
180 LayerHandle::set_layer(app, |app| &mut child.data_mut(app).layer, None);
181 child.paint_with_context(app, self, offset);
182 } else {
183 child.paint_with_context(app, self, offset);
184 }
185 }
186
187 fn composite_child(&mut self, app: &mut App, child: AnyRenderObject, offset: Offset) {
188 debug_assert!(!self.is_recording());
189 debug_assert!(child.is_repaint_boundary(app));
190
191 if child.needs_paint(app) || !child.was_repaint_boundary(app) {
192 Self::repaint_composited_child_inner(app, child, true, None);
193 } else if child.needs_composited_layer_update(app) {
194 Self::update_layer_properties(app, child);
195 }
196 debug_assert!(
197 child
198 .layer(app)
199 .is_some_and(|layer| layer.as_offset_layer().is_some())
200 );
201 let child_offset_layer = child
202 .layer(app)
203 .expect("checked")
204 .as_offset_layer()
205 .expect("a repaint boundary's layer is an OffsetLayer");
206 child_offset_layer.set_offset(app, offset);
207 self.append_layer(app, child_offset_layer.as_layer());
208 }
209
210 fn append_layer(&mut self, app: &mut App, layer: AnyLayer) {
217 debug_assert!(!self.is_recording());
218 layer.remove(app);
219 self.container_layer.append(app, layer);
220 }
221
222 fn is_recording(&self) -> bool {
223 self.canvas.is_some()
224 }
225
226 fn start_recording(&mut self) {
227 debug_assert!(!self.is_recording());
228 self.canvas = Some(Canvas::new());
229 self.is_complex_hint = false;
230 self.will_change_hint = false;
231 }
232
233 pub fn add_composition_callback(
236 &self,
237 app: &mut App,
238 callback: CompositionCallback,
239 ) -> Box<dyn FnOnce(&mut App)> {
240 self.container_layer
241 .as_layer()
242 .add_composition_callback(app, callback)
243 }
244
245 pub fn stop_recording_if_needed(&mut self, app: &mut App) {
256 if !self.is_recording() {
257 return;
258 }
259 let canvas = self.canvas.take().expect("recording");
260 let picture = PictureLayer::new(app, self.estimated_bounds);
261 picture.set_picture(app, Some(Arc::new(canvas.build())));
262 picture.set_is_complex_hint(app, self.is_complex_hint);
263 picture.set_will_change_hint(app, self.will_change_hint);
264 self.is_complex_hint = false;
265 self.will_change_hint = false;
266 self.container_layer.append(app, picture.as_layer());
267 }
268
269 pub fn set_is_complex_hint(&mut self) {
272 if self.canvas.is_none() {
273 self.start_recording();
274 }
275 self.is_complex_hint = true;
276 }
277
278 pub fn set_will_change_hint(&mut self) {
280 if self.canvas.is_none() {
281 self.start_recording();
282 }
283 self.will_change_hint = true;
284 }
285
286 pub fn add_layer(&mut self, app: &mut App, layer: AnyLayer) {
291 self.stop_recording_if_needed(app);
292 self.append_layer(app, layer);
293 }
294
295 pub fn push_layer(
316 &mut self,
317 app: &mut App,
318 child_layer: AnyContainerLayer,
319 painter: impl FnOnce(&mut App, &mut PaintingContext, Offset),
320 offset: Offset,
321 child_paint_bounds: Option<Rect>,
322 ) {
323 if child_layer.has_children(app) {
326 child_layer.remove_all_children(app);
327 }
328 self.stop_recording_if_needed(app);
329 self.append_layer(app, child_layer.as_layer());
330 let mut child_context = self.create_child_context(
331 child_layer,
332 child_paint_bounds.unwrap_or(self.estimated_bounds),
333 );
334
335 painter(app, &mut child_context, offset);
336 child_context.stop_recording_if_needed(app);
337 }
338
339 fn create_child_context(
343 &self,
344 child_layer: AnyContainerLayer,
345 bounds: Rect,
346 ) -> PaintingContext {
347 PaintingContext::new(child_layer, bounds)
348 }
349
350 #[allow(clippy::too_many_arguments)]
369 pub fn push_clip_rect(
370 &mut self,
371 app: &mut App,
372 needs_compositing: bool,
373 offset: Offset,
374 clip_rect: Rect,
375 painter: impl FnOnce(&mut App, &mut PaintingContext, Offset),
376 clip_behavior: Clip,
377 old_layer: Option<Handle<ClipRectLayer>>,
378 ) -> Option<Handle<ClipRectLayer>> {
379 if clip_behavior == Clip::None {
380 painter(app, self, offset);
381 return None;
382 }
383 let offset_clip_rect = clip_rect.shift(offset);
384 if needs_compositing {
385 let layer = old_layer.unwrap_or_else(|| ClipRectLayer::new(app));
386 layer.set_clip_rect(app, Some(offset_clip_rect));
387 layer.set_clip_behavior(app, clip_behavior);
388 self.push_layer(
389 app,
390 layer.as_container_layer(),
391 painter,
392 offset,
393 Some(offset_clip_rect),
394 );
395 Some(layer)
396 } else {
397 self.clip_rect_and_paint(
398 offset_clip_rect,
399 clip_behavior,
400 offset_clip_rect,
401 |context| painter(app, context, offset),
402 );
403 None
404 }
405 }
406
407 #[allow(clippy::too_many_arguments)]
409 pub fn push_clip_rrect(
410 &mut self,
411 app: &mut App,
412 needs_compositing: bool,
413 offset: Offset,
414 bounds: Rect,
415 clip_rrect: RRect,
416 painter: impl FnOnce(&mut App, &mut PaintingContext, Offset),
417 clip_behavior: Clip,
418 old_layer: Option<Handle<ClipRRectLayer>>,
419 ) -> Option<Handle<ClipRRectLayer>> {
420 if clip_behavior == Clip::None {
421 painter(app, self, offset);
422 return None;
423 }
424 let offset_bounds = bounds.shift(offset);
425 let offset_clip_rrect = clip_rrect.shift(offset);
426 if needs_compositing {
427 let layer = old_layer.unwrap_or_else(|| ClipRRectLayer::new(app));
428 layer.set_clip_rrect(app, Some(offset_clip_rrect));
429 layer.set_clip_behavior(app, clip_behavior);
430 self.push_layer(
431 app,
432 layer.as_container_layer(),
433 painter,
434 offset,
435 Some(offset_bounds),
436 );
437 Some(layer)
438 } else {
439 self.clip_rrect_and_paint(offset_clip_rrect, clip_behavior, offset_bounds, |context| {
440 painter(app, context, offset)
441 });
442 None
443 }
444 }
445
446 #[allow(clippy::too_many_arguments)]
448 pub fn push_clip_path(
449 &mut self,
450 app: &mut App,
451 needs_compositing: bool,
452 offset: Offset,
453 bounds: Rect,
454 clip_path: Arc<Path>,
455 painter: impl FnOnce(&mut App, &mut PaintingContext, Offset),
456 clip_behavior: Clip,
457 old_layer: Option<Handle<ClipPathLayer>>,
458 ) -> Option<Handle<ClipPathLayer>> {
459 if clip_behavior == Clip::None {
460 painter(app, self, offset);
461 return None;
462 }
463 let offset_bounds = bounds.shift(offset);
464 let offset_clip_path = shift_path(&clip_path, offset);
465 if needs_compositing {
466 let layer = old_layer.unwrap_or_else(|| ClipPathLayer::new(app));
467 layer.set_clip_path(app, Some(offset_clip_path));
468 layer.set_clip_behavior(app, clip_behavior);
469 self.push_layer(
470 app,
471 layer.as_container_layer(),
472 painter,
473 offset,
474 Some(offset_bounds),
475 );
476 Some(layer)
477 } else {
478 self.clip_path_and_paint(&offset_clip_path, clip_behavior, offset_bounds, |context| {
479 painter(app, context, offset)
480 });
481 None
482 }
483 }
484
485 pub fn push_transform(
494 &mut self,
495 app: &mut App,
496 needs_compositing: bool,
497 offset: Offset,
498 transform: Matrix4,
499 painter: impl FnOnce(&mut App, &mut PaintingContext, Offset),
500 old_layer: Option<Handle<TransformLayer>>,
501 ) -> Option<Handle<TransformLayer>> {
502 let (dx, dy) = (offset.dx() as f32, offset.dy() as f32);
503 let effective_transform = Matrix4::translation(dx, dy)
504 .then(&transform)
505 .then(&Matrix4::translation(-dx, -dy));
506 if needs_compositing {
507 let layer = old_layer.unwrap_or_else(|| TransformLayer::new(app));
508 layer.set_transform(app, effective_transform);
509 let child_paint_bounds =
510 inverse_transform_rect(effective_transform, self.estimated_bounds);
511 self.push_layer(
512 app,
513 layer.as_container_layer(),
514 painter,
515 offset,
516 Some(child_paint_bounds),
517 );
518 Some(layer)
519 } else {
520 self.canvas().save();
521 self.canvas().concat(&effective_transform);
522 painter(app, self, offset);
523 self.canvas().restore();
524 None
525 }
526 }
527
528 pub fn push_opacity(
537 &mut self,
538 app: &mut App,
539 offset: Offset,
540 alpha: i32,
541 painter: impl FnOnce(&mut App, &mut PaintingContext, Offset),
542 old_layer: Option<Handle<OpacityLayer>>,
543 ) -> Handle<OpacityLayer> {
544 let layer = old_layer.unwrap_or_else(|| OpacityLayer::new(app));
545 layer.set_alpha(app, alpha);
546 layer.set_offset(app, offset);
547 self.push_layer(app, layer.as_container_layer(), painter, Offset::ZERO, None);
548 layer
549 }
550}
551
552fn shift_path(path: &Arc<Path>, offset: Offset) -> Arc<Path> {
553 if offset == Offset::ZERO {
554 return Arc::clone(path);
555 }
556 let mut builder = PathBuilder::new();
557 builder.append(
558 path,
559 &Matrix4::translation(offset.dx() as f32, offset.dy() as f32),
560 );
561 builder.build()
562}
563
564impl ClipContext for PaintingContext {
565 fn canvas(&mut self) -> &mut Canvas {
571 if self.canvas.is_none() {
572 self.start_recording();
573 }
574 self.canvas.as_mut().expect("recording")
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use inset_foundation::AppCell;
581 use std::cell::Cell;
582 use std::rc::Rc;
583
584 use inset_embedder::{Color, Effect, EngineLayer, Layer, Paint, SceneBuilder, Size};
585
586 use super::*;
587 use crate::box_::{AnyRenderBox, BoxConstraints, RenderBox, RenderBoxData};
588 use crate::layer::{AnyOffsetLayer, ContainerLayer, ErasedLayer, OffsetLayer, OpacityLayer};
589 use crate::object::{RenderHandle, RenderObject, RenderObjectData};
590 use inset_foundation::Handle;
591
592 use crate::pipeline_owner::PipelineOwner;
593
594 struct Leaf {
597 render_object: RenderObjectData,
598 render_box: RenderBoxData,
599 repaint_boundary: bool,
600 alpha: i32,
601 paints: Rc<Cell<u32>>,
602 }
603
604 impl RenderObject for Leaf {
605 crate::render_object_accessors!();
606
607 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
608 let size = self.constraints(app).smallest();
609 self.set_size(app, size);
610 }
611
612 fn is_repaint_boundary(self: RenderHandle<Self>, app: &App) -> bool {
613 self.get(app).repaint_boundary
614 }
615
616 fn update_composited_layer(
617 self: RenderHandle<Self>,
618 app: &mut App,
619 old_layer: Option<AnyOffsetLayer>,
620 ) -> AnyOffsetLayer {
621 debug_assert!(self.is_repaint_boundary(app));
622 let layer = match old_layer {
623 Some(old) => app
624 .handle::<OpacityLayer>(old.id())
625 .expect("Leaf reuses an OpacityLayer"),
626 None => OpacityLayer::new(app),
627 };
628 layer.set_alpha(app, self.get(app).alpha);
629 layer.as_offset_layer()
630 }
631
632 fn paint(
633 self: RenderHandle<Self>,
634 app: &mut App,
635 context: &mut PaintingContext,
636 offset: Offset,
637 ) {
638 let paints = Rc::clone(&self.get(app).paints);
639 paints.set(paints.get() + 1);
640 let paint = Paint {
641 color: Color::from_argb(255, 0, 0, 0).into(),
642 ..Paint::default()
643 };
644 context.canvas().draw_rect(offset & self.size(app), &paint);
645 }
646 }
647
648 impl RenderBox for Leaf {
649 crate::render_box_accessors!();
650
651 fn paint_bounds(self: RenderHandle<Self>, app: &App) -> Rect {
653 self.render_box_data(app)
654 .size
655 .map_or(Rect::ZERO, |size| Offset::ZERO & size)
656 }
657 }
658
659 struct Parent {
661 render_object: RenderObjectData,
662 render_box: RenderBoxData,
663 child: Option<AnyRenderBox>,
664 paints_child: bool,
665 paints: Rc<Cell<u32>>,
666 }
667
668 impl RenderObject for Parent {
669 crate::render_object_accessors!();
670
671 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
672 if let Some(child) = self.get(app).child {
673 child.layout(app, BoxConstraints::tight(Size::new(50.0, 50.0)), false);
674 }
675 self.set_size(app, Size::new(100.0, 100.0));
676 }
677
678 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
679 true
680 }
681
682 fn visit_children(
683 self: RenderHandle<Self>,
684 app: &App,
685 visitor: &mut dyn FnMut(AnyRenderObject),
686 ) {
687 if let Some(child) = self.get(app).child {
688 visitor(child.as_object());
689 }
690 }
691
692 fn paint(
693 self: RenderHandle<Self>,
694 app: &mut App,
695 context: &mut PaintingContext,
696 offset: Offset,
697 ) {
698 let paints = Rc::clone(&self.get(app).paints);
699 paints.set(paints.get() + 1);
700 if let Some(child) = self.get(app).child
701 && self.get(app).paints_child
702 {
703 context.paint_child(app, child.as_object(), offset);
704 }
705 }
706 }
707
708 impl RenderBox for Parent {
709 crate::render_box_accessors!();
710 }
711
712 struct Tree {
713 owner: Handle<PipelineOwner>,
714 parent: RenderHandle<Parent>,
715 leaf: RenderHandle<Leaf>,
716 parent_paints: Rc<Cell<u32>>,
717 leaf_paints: Rc<Cell<u32>>,
718 }
719
720 fn tree(app: &mut App, leaf_is_boundary: bool) -> Tree {
721 let owner = PipelineOwner::new(app, None, None);
722 let leaf_paints = Rc::new(Cell::new(0));
723 let parent_paints = Rc::new(Cell::new(0));
724 let leaf = RenderHandle::new_box(
725 app,
726 Leaf {
727 render_object: RenderObjectData::new(),
728 render_box: RenderBoxData::new(),
729 repaint_boundary: leaf_is_boundary,
730 alpha: 255,
731 paints: Rc::clone(&leaf_paints),
732 },
733 );
734 let parent = RenderHandle::new_box(
735 app,
736 Parent {
737 render_object: RenderObjectData::new(),
738 render_box: RenderBoxData::new(),
739 child: Some(leaf.as_box()),
740 paints_child: true,
741 paints: Rc::clone(&parent_paints),
742 },
743 );
744 parent.adopt_child(app, leaf.as_object());
745 owner.set_root_node(app, Some(parent.as_object()));
746 parent.schedule_initial_layout(app);
747 schedule_root_paint(app, parent.as_object());
748 Tree {
749 owner,
750 parent,
751 leaf,
752 parent_paints,
753 leaf_paints,
754 }
755 }
756
757 fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
758 let root = OffsetLayer::new(app, Offset::ZERO);
759 root.as_layer().attach(app, node.id());
760 node.schedule_initial_paint(app, root.as_container_layer());
761 }
762
763 fn frame(app: &mut App, owner: Handle<PipelineOwner>) {
764 owner.flush_layout(app);
765 owner.flush_compositing_bits(app);
766 owner.flush_paint(app);
767 }
768
769 fn paints(tree: &Tree) -> (u32, u32) {
770 (tree.parent_paints.get(), tree.leaf_paints.get())
771 }
772
773 fn leaf_layer_attached(app: &App, tree: &Tree) -> bool {
774 tree.leaf
775 .as_object()
776 .debug_layer(app)
777 .expect("leaf has a layer")
778 .as_layer()
779 .attached(app)
780 }
781
782 #[test]
784 fn nodes_needing_paint_updated_with_paint_changes() {
785 let cell = AppCell::new();
786 let mut app = cell.borrow_mut();
787 let owner = PipelineOwner::new(&mut app, None, None);
788 let node = RenderHandle::new_box(
789 &mut app,
790 Leaf {
791 render_object: RenderObjectData::new(),
792 render_box: RenderBoxData::new(),
793 repaint_boundary: true,
794 alpha: 255,
795 paints: Rc::new(Cell::new(0)),
796 },
797 );
798 node.as_object().attach(&mut app, owner);
799 assert!(owner.nodes_needing_paint(&app).is_empty());
800
801 node.mark_needs_paint(&mut app);
802 let root = OpacityLayer::new(&mut app);
803 root.as_layer().attach(&mut app, node.as_object().id());
804 node.as_object()
805 .schedule_initial_paint(&mut app, root.as_container_layer());
806 assert!(owner.nodes_needing_paint(&app).contains(&node.as_object()));
807
808 owner.flush_compositing_bits(&mut app);
809 owner.flush_paint(&mut app);
810 assert!(owner.nodes_needing_paint(&app).is_empty());
811 }
812
813 #[test]
814 fn child_boundary_repaints_without_its_parent() {
815 let cell = AppCell::new();
816 let mut app = cell.borrow_mut();
817 let tree = tree(&mut app, true);
818 frame(&mut app, tree.owner);
819 assert_eq!(paints(&tree), (1, 1));
820 assert!(leaf_layer_attached(&app, &tree));
821 let parent_layer = tree.parent.as_object().debug_layer(&app).expect("parent");
822 let leaf_layer = tree.leaf.as_object().debug_layer(&app).expect("leaf");
823 assert_eq!(leaf_layer.as_layer().parent(&app), Some(parent_layer));
824
825 tree.leaf.mark_needs_paint(&mut app);
826 assert_eq!(
827 tree.owner.nodes_needing_paint(&app),
828 vec![tree.leaf.as_object()]
829 );
830 frame(&mut app, tree.owner);
831 assert_eq!(paints(&tree), (1, 2));
832 let parent_layer = tree.parent.as_object().debug_layer(&app).expect("parent");
833 let leaf_layer = tree.leaf.as_object().debug_layer(&app).expect("leaf");
834 assert_eq!(leaf_layer.as_layer().parent(&app), Some(parent_layer));
835 }
836
837 #[test]
838 fn composited_layer_update_does_not_repaint() {
839 let cell = AppCell::new();
840 let mut app = cell.borrow_mut();
841 let tree = tree(&mut app, true);
842 frame(&mut app, tree.owner);
843
844 tree.leaf.get_mut(&mut app).alpha = 128;
845 tree.leaf.mark_needs_composited_layer_update(&mut app);
846 assert!(
847 tree.leaf
848 .as_object()
849 .debug_needs_composited_layer_update(&app)
850 );
851 frame(&mut app, tree.owner);
852 assert_eq!(paints(&tree), (1, 1));
853 let layer = tree.leaf.as_object().debug_layer(&app).expect("layer");
854 let opacity = app
855 .handle::<OpacityLayer>(layer.id())
856 .expect("OpacityLayer");
857 assert_eq!(opacity.alpha(&app), Some(128));
858 }
859
860 #[test]
862 fn unpainted_boundary_is_detached_and_skipped() {
863 let cell = AppCell::new();
864 let mut app = cell.borrow_mut();
865 let tree = tree(&mut app, true);
866 frame(&mut app, tree.owner);
867 assert!(leaf_layer_attached(&app, &tree));
868
869 tree.parent.get_mut(&mut app).paints_child = false;
870 tree.parent.mark_needs_paint(&mut app);
871 frame(&mut app, tree.owner);
872 assert_eq!(paints(&tree), (2, 1));
873 assert!(!leaf_layer_attached(&app, &tree));
874
875 tree.leaf.mark_needs_paint(&mut app);
876 frame(&mut app, tree.owner);
877 assert_eq!(
878 paints(&tree),
879 (2, 1),
880 "a detached boundary is not repainted"
881 );
882 assert!(tree.leaf.as_object().debug_needs_paint(&app));
883
884 tree.parent.get_mut(&mut app).paints_child = true;
885 tree.parent.mark_needs_paint(&mut app);
886 frame(&mut app, tree.owner);
887 assert_eq!(paints(&tree), (3, 2));
888 assert!(leaf_layer_attached(&app, &tree));
889 }
890
891 #[test]
892 fn non_boundary_child_paints_into_the_parent_picture() {
893 let cell = AppCell::new();
894 let mut app = cell.borrow_mut();
895 let tree = tree(&mut app, false);
896 frame(&mut app, tree.owner);
897 assert_eq!(paints(&tree), (1, 1));
898 assert!(tree.leaf.as_object().debug_layer(&app).is_none());
899
900 tree.leaf.mark_needs_paint(&mut app);
901 assert_eq!(
902 tree.owner.nodes_needing_paint(&app),
903 vec![tree.parent.as_object()]
904 );
905 frame(&mut app, tree.owner);
906 assert_eq!(paints(&tree), (2, 2));
907 }
908
909 #[test]
910 fn composing_the_root_draws_the_child_boundary_through_its_layer() {
911 let cell = AppCell::new();
912 let mut app = cell.borrow_mut();
913 let tree = tree(&mut app, true);
914 tree.leaf.get_mut(&mut app).alpha = 128;
915 frame(&mut app, tree.owner);
916
917 let layer = tree.parent.as_object().layer(&app).expect("painted");
918 let scene = layer.build_scene(&mut app, SceneBuilder::new());
919 let parent = only_container(&scene.root.children);
920 let child = only_container(&parent.children);
921 assert!(
922 matches!(child.effect, Effect::Opacity { alpha: 128, .. }),
923 "the child's OpacityLayer: {:?}",
924 child.effect
925 );
926 assert!(
927 matches!(child.children.as_slice(), [Layer::DisplayList(_)]),
928 "the child's picture: {:?}",
929 child.children
930 );
931 }
932
933 fn only_container(children: &[Layer]) -> &EngineLayer {
935 let mut containers = children.iter().filter_map(|child| match child {
936 Layer::Container(container) => Some(container),
937 _ => None,
938 });
939 let container = containers.next().expect("a container");
940 assert!(containers.next().is_none(), "one container: {children:?}");
941 container
942 }
943}