1use std::sync::Arc;
14
15use inset_embedder::{CompositionLayer, Offset, Size};
16use inset_foundation::App;
17
18use crate::box_::{BoxConstraints, RenderBox, RenderBoxData};
19use crate::layer::{EmbeddedLayer, ErasedLayer};
20use crate::object::{RenderHandle, RenderObject, RenderObjectData};
21use crate::painting_context::PaintingContext;
22
23pub struct RenderCompositionLayerView {
25 render_object: RenderObjectData,
26 render_box: RenderBoxData,
27 composition_layer: Arc<dyn CompositionLayer>,
28}
29
30impl RenderCompositionLayerView {
31 pub fn new(
32 app: &mut App,
33 composition_layer: Arc<dyn CompositionLayer>,
34 ) -> RenderHandle<RenderCompositionLayerView> {
35 RenderHandle::new_box(
36 app,
37 RenderCompositionLayerView {
38 render_object: RenderObjectData::new(),
39 render_box: RenderBoxData::new(),
40 composition_layer,
41 },
42 )
43 }
44
45 pub fn composition_layer(self: RenderHandle<Self>, app: &App) -> &Arc<dyn CompositionLayer> {
47 &self.get(app).composition_layer
48 }
49
50 pub fn set_composition_layer(
52 self: RenderHandle<Self>,
53 app: &mut App,
54 composition_layer: Arc<dyn CompositionLayer>,
55 ) {
56 if Arc::ptr_eq(&self.get(app).composition_layer, &composition_layer) {
57 return;
58 }
59 self.get_mut(app).composition_layer = composition_layer;
60 self.mark_needs_paint(app);
61 }
62}
63
64impl RenderObject for RenderCompositionLayerView {
65 crate::render_object_accessors!();
66
67 fn always_needs_compositing(self: RenderHandle<Self>, _app: &App) -> bool {
68 true
69 }
70
71 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
72 let size = self.constraints(app).biggest();
73 self.set_size(app, size);
74 }
75
76 fn paint(
79 self: RenderHandle<Self>,
80 app: &mut App,
81 context: &mut PaintingContext,
82 offset: Offset,
83 ) {
84 let composition_layer = Arc::clone(&self.get(app).composition_layer);
85 let layer = EmbeddedLayer::new(app, composition_layer, offset & self.size(app));
86 context.add_layer(app, layer.as_layer());
87 }
88}
89
90impl RenderBox for RenderCompositionLayerView {
91 crate::render_box_accessors!();
92
93 fn compute_dry_layout(
94 self: RenderHandle<Self>,
95 _app: &mut App,
96 constraints: BoxConstraints,
97 ) -> Size {
98 constraints.biggest()
99 }
100
101 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
104 true
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use inset_embedder::test_support::idle_layer;
111 use inset_embedder::{Clip, Matrix4, Mutator, Part, Rect, SceneBuilder};
112 use inset_foundation::{AppCell, Handle};
113
114 use super::*;
115 use crate::layer::{ContainerLayer, OffsetLayer};
116 use crate::object::AnyRenderObject;
117 use crate::pipeline_owner::PipelineOwner;
118
119 const CHILD_OFFSET: Offset = Offset::new(10.0, 5.0);
121
122 struct Parent {
124 render_object: RenderObjectData,
125 render_box: RenderBoxData,
126 child: RenderHandle<RenderCompositionLayerView>,
127 painting: ChildPainting,
128 }
129
130 #[derive(Clone, Copy)]
132 enum ChildPainting {
133 AtOffset,
135 HalvedUnderAClip,
137 }
138
139 impl RenderObject for Parent {
140 crate::render_object_accessors!();
141
142 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
144 true
145 }
146
147 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
148 let child = self.get(app).child;
149 child.layout(app, BoxConstraints::tight(Size::new(30.0, 20.0)), false);
150 self.set_size(app, Size::new(100.0, 100.0));
151 }
152
153 fn visit_children(
154 self: RenderHandle<Self>,
155 app: &App,
156 visitor: &mut dyn FnMut(AnyRenderObject),
157 ) {
158 visitor(self.get(app).child.as_object());
159 }
160
161 fn paint(
162 self: RenderHandle<Self>,
163 app: &mut App,
164 context: &mut PaintingContext,
165 offset: Offset,
166 ) {
167 let child = self.get(app).child.as_object();
168 match self.get(app).painting {
169 ChildPainting::AtOffset => context.paint_child(app, child, offset + CHILD_OFFSET),
170 ChildPainting::HalvedUnderAClip => {
171 let needs_compositing = self.as_object().needs_compositing(app);
172 context.push_clip_rect(
173 app,
174 needs_compositing,
175 offset,
176 Rect::from_ltwh(0.0, 0.0, 100.0, 10.0),
177 |app, context, offset| {
178 context.push_transform(
179 app,
180 needs_compositing,
181 offset + CHILD_OFFSET,
182 Matrix4::scale(0.5, 0.5),
183 |app, context, offset| context.paint_child(app, child, offset),
184 None,
185 );
186 },
187 Clip::HardEdge,
188 None,
189 );
190 }
191 }
192 }
193 }
194
195 impl RenderBox for Parent {
196 crate::render_box_accessors!();
197 }
198
199 fn scene_of_one_frame(
202 app: &mut App,
203 composition_layer: Arc<dyn CompositionLayer>,
204 painting: ChildPainting,
205 ) -> (inset_embedder::Scene, Handle<PipelineOwner>) {
206 let owner = PipelineOwner::new(app, None, None);
207 let child = RenderCompositionLayerView::new(app, composition_layer);
208 let parent = RenderHandle::new_box(
209 app,
210 Parent {
211 render_object: RenderObjectData::new(),
212 render_box: RenderBoxData::new(),
213 child,
214 painting,
215 },
216 );
217 parent.adopt_child(app, child.as_object());
218 owner.set_root_node(app, Some(parent.as_object()));
219 parent.schedule_initial_layout(app);
220 let root = OffsetLayer::new(app, Offset::ZERO);
221 root.as_layer().attach(app, parent.as_object().id());
222 parent
223 .as_object()
224 .schedule_initial_paint(app, root.as_container_layer());
225 owner.flush_layout(app);
226 owner.flush_compositing_bits(app);
227 owner.flush_paint(app);
228 let scene = root
229 .as_container_layer()
230 .build_scene(app, SceneBuilder::new());
231 (scene, owner)
232 }
233
234 #[test]
235 fn the_box_is_placed_and_nothing_is_painted() {
236 let cell = AppCell::new();
237 let mut app = cell.borrow_mut();
238 let composition_layer = idle_layer();
239 let (scene, _owner) = scene_of_one_frame(
240 &mut app,
241 Arc::clone(&composition_layer),
242 ChildPainting::AtOffset,
243 );
244 let slices = inset_embedder::slices(&scene, |_| None);
245 let [Part::CompositionLayer { layer, placement }] = slices.parts() else {
246 panic!("the composition layer alone: {:?}", slices.parts());
247 };
248 assert!(Arc::ptr_eq(layer, &composition_layer));
249 let placed = Rect::from_ltwh(10.0, 5.0, 30.0, 20.0);
250 assert_eq!(placement.bounds(), placed);
251 assert_eq!(placement.visible_bounds(), placed);
252 assert!(!slices.paints());
253 }
254
255 #[test]
258 fn a_composition_layer_is_placed_under_the_transform_and_the_clip_above_it() {
259 let cell = AppCell::new();
260 let mut app = cell.borrow_mut();
261 let (scene, _owner) =
262 scene_of_one_frame(&mut app, idle_layer(), ChildPainting::HalvedUnderAClip);
263 let slices = inset_embedder::slices(&scene, |_| None);
264 let (_, placement) = slices.composition_layers().next().expect("placed");
265 assert!(
266 placement
267 .mutators
268 .contains(&Mutator::ClipRect(Rect::from_ltwh(0.0, 0.0, 100.0, 10.0)))
269 );
270 assert_eq!(placement.scale(), 0.5);
271 assert_eq!(placement.bounds(), Rect::from_ltwh(10.0, 5.0, 15.0, 10.0));
272 assert_eq!(
273 placement.visible_bounds(),
274 Rect::from_ltrb(10.0, 5.0, 25.0, 10.0)
275 );
276 }
277}