1use std::sync::Arc;
13
14use inset_embedder::{BlendMode, Offset, Paint, Size, Surface};
15use inset_foundation::App;
16use inset_painting::ClipContext;
17
18use crate::box_::{BoxConstraints, RenderBox, RenderBoxData};
19use crate::layer::{ErasedLayer, SurfaceLayer};
20use crate::object::{RenderHandle, RenderObject, RenderObjectData};
21use crate::painting_context::PaintingContext;
22
23pub struct RenderSurfaceView {
25 render_object: RenderObjectData,
26 render_box: RenderBoxData,
27 surface: Arc<dyn Surface>,
28}
29
30impl RenderSurfaceView {
31 pub fn new(app: &mut App, surface: Arc<dyn Surface>) -> RenderHandle<RenderSurfaceView> {
32 RenderHandle::new_box(
33 app,
34 RenderSurfaceView {
35 render_object: RenderObjectData::new(),
36 render_box: RenderBoxData::new(),
37 surface,
38 },
39 )
40 }
41
42 pub fn surface(self: RenderHandle<Self>, app: &App) -> &Arc<dyn Surface> {
44 &self.get(app).surface
45 }
46
47 pub fn set_surface(self: RenderHandle<Self>, app: &mut App, surface: Arc<dyn Surface>) {
49 if Arc::ptr_eq(&self.get(app).surface, &surface) {
50 return;
51 }
52 self.get_mut(app).surface = surface;
53 self.mark_needs_paint(app);
54 }
55}
56
57impl RenderObject for RenderSurfaceView {
58 crate::render_object_accessors!();
59
60 fn always_needs_compositing(self: RenderHandle<Self>, _app: &App) -> bool {
61 true
62 }
63
64 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
65 let size = self.constraints(app).biggest();
66 self.set_size(app, size);
67 }
68
69 fn paint(
71 self: RenderHandle<Self>,
72 app: &mut App,
73 context: &mut PaintingContext,
74 offset: Offset,
75 ) {
76 let rect = offset & self.size(app);
77 context.canvas().draw_rect(rect, &clearing_paint());
78 let surface = Arc::clone(&self.get(app).surface);
79 let layer = SurfaceLayer::new(app, surface, rect);
80 context.add_layer(app, layer.as_layer());
81 }
82}
83
84impl RenderBox for RenderSurfaceView {
85 crate::render_box_accessors!();
86
87 fn compute_dry_layout(
88 self: RenderHandle<Self>,
89 _app: &mut App,
90 constraints: BoxConstraints,
91 ) -> Size {
92 constraints.biggest()
93 }
94
95 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
97 true
98 }
99}
100
101fn clearing_paint() -> Paint {
104 Paint {
105 blend_mode: BlendMode::Clear,
106 ..Paint::default()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use inset_embedder::test_support::idle_surface;
113 use inset_embedder::{Clip, Matrix4, Rect, SceneBuilder, SurfacePlacement, valo};
114 use inset_foundation::{AppCell, Handle};
115
116 use super::*;
117 use crate::layer::{ContainerLayer, OffsetLayer};
118 use crate::object::AnyRenderObject;
119 use crate::pipeline_owner::PipelineOwner;
120
121 const CHILD_OFFSET: Offset = Offset::new(10.0, 5.0);
123
124 struct Parent {
126 render_object: RenderObjectData,
127 render_box: RenderBoxData,
128 child: RenderHandle<RenderSurfaceView>,
129 painting: ChildPainting,
130 }
131
132 #[derive(Clone, Copy)]
134 enum ChildPainting {
135 AtOffset,
137 HalvedUnderAClip,
139 }
140
141 impl RenderObject for Parent {
142 crate::render_object_accessors!();
143
144 fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
146 true
147 }
148
149 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
150 let child = self.get(app).child;
151 child.layout(app, BoxConstraints::tight(Size::new(30.0, 20.0)), false);
152 self.set_size(app, Size::new(100.0, 100.0));
153 }
154
155 fn visit_children(
156 self: RenderHandle<Self>,
157 app: &App,
158 visitor: &mut dyn FnMut(AnyRenderObject),
159 ) {
160 visitor(self.get(app).child.as_object());
161 }
162
163 fn paint(
164 self: RenderHandle<Self>,
165 app: &mut App,
166 context: &mut PaintingContext,
167 offset: Offset,
168 ) {
169 let child = self.get(app).child.as_object();
170 match self.get(app).painting {
171 ChildPainting::AtOffset => context.paint_child(app, child, offset + CHILD_OFFSET),
172 ChildPainting::HalvedUnderAClip => {
173 let needs_compositing = self.as_object().needs_compositing(app);
174 context.push_clip_rect(
175 app,
176 needs_compositing,
177 offset,
178 Rect::from_ltwh(0.0, 0.0, 100.0, 10.0),
179 |app, context, offset| {
180 context.push_transform(
181 app,
182 needs_compositing,
183 offset + CHILD_OFFSET,
184 Matrix4::scale(0.5, 0.5),
185 |app, context, offset| context.paint_child(app, child, offset),
186 None,
187 );
188 },
189 Clip::HardEdge,
190 None,
191 );
192 }
193 }
194 }
195 }
196
197 impl RenderBox for Parent {
198 crate::render_box_accessors!();
199 }
200
201 fn scene_of_one_frame(
204 app: &mut App,
205 surface: Arc<dyn Surface>,
206 painting: ChildPainting,
207 ) -> (inset_embedder::Scene, Handle<PipelineOwner>) {
208 let owner = PipelineOwner::new(app, None);
209 let child = RenderSurfaceView::new(app, surface);
210 let parent = RenderHandle::new_box(
211 app,
212 Parent {
213 render_object: RenderObjectData::new(),
214 render_box: RenderBoxData::new(),
215 child,
216 painting,
217 },
218 );
219 parent.adopt_child(app, child.as_object());
220 owner.set_root_node(app, Some(parent.as_object()));
221 parent.schedule_initial_layout(app);
222 let root = OffsetLayer::new(app, Offset::ZERO);
223 root.as_layer().attach(app, parent.as_object().id());
224 parent
225 .as_object()
226 .schedule_initial_paint(app, root.as_container_layer());
227 owner.flush_layout(app);
228 owner.flush_compositing_bits(app);
229 owner.flush_paint(app);
230 let scene = root
231 .as_container_layer()
232 .build_scene(app, SceneBuilder::new());
233 (scene, owner)
234 }
235
236 #[test]
237 fn the_rectangle_painted_clear_is_the_rectangle_placed() {
238 let cell = AppCell::new();
239 let mut app = cell.borrow_mut();
240 let surface = idle_surface();
241 let (scene, _owner) =
242 scene_of_one_frame(&mut app, Arc::clone(&surface), ChildPainting::AtOffset);
243 let placed = Rect::from_ltwh(10.0, 5.0, 30.0, 20.0);
244 assert_eq!(
245 scene.surfaces,
246 [SurfacePlacement {
247 surface,
248 rect: placed,
249 scale: 1.0,
250 clip: placed,
251 }]
252 );
253 let ops = inset_embedder::flattened_ops(&scene.picture);
254 let holes: Vec<valo::Rect> = ops
255 .iter()
256 .filter_map(|op| match op {
257 valo::Op::DrawRect { rect, paint, .. } if paint.blend_mode == BlendMode::Clear => {
258 Some(*rect)
259 }
260 _ => None,
261 })
262 .collect();
263 assert_eq!(holes, [valo::Rect::from(placed)], "{ops:?}");
264 }
265
266 #[test]
269 fn a_surface_is_placed_under_the_transform_and_the_clip_above_it() {
270 let cell = AppCell::new();
271 let mut app = cell.borrow_mut();
272 let surface = idle_surface();
273 let (scene, _owner) = scene_of_one_frame(
274 &mut app,
275 Arc::clone(&surface),
276 ChildPainting::HalvedUnderAClip,
277 );
278 assert_eq!(
279 scene.surfaces,
280 [SurfacePlacement {
281 surface,
282 rect: Rect::from_ltwh(10.0, 5.0, 15.0, 10.0),
283 scale: 0.5,
284 clip: Rect::from_ltrb(10.0, 5.0, 25.0, 10.0),
285 }]
286 );
287 }
288}