Skip to main content

inset_rendering/
binding.rs

1//! Flutter counterpart: `rendering/binding.dart` (`RendererBinding`).
2//!
3//! Semantics and `performReassemble` wait.
4
5use std::rc::Rc;
6
7use inset_embedder::{Offset, ViewId, ViewRef};
8use inset_foundation::{App, Handle, Listener};
9use inset_gestures::{GestureBinding, GestureBindingOverridesObject, HitTestResult, PointerEvent};
10use inset_scheduler::{FrameCallback, FrameTarget, PersistentFrameCallback, SchedulerBinding};
11
12use crate::mouse_tracker::MouseTracker;
13use crate::object::RenderHandle;
14use crate::pipeline_owner::{PipelineManifold, PipelineOwner};
15use crate::view::{RenderView, ViewConfiguration};
16
17/// The object side of Flutter's `WidgetsBinding` overrides of `RendererBinding.drawFrame`:
18/// the widget layer builds before the renderer's frame and finalizes its tree after it.
19/// Implement it on the binding above and register `Rc::new(handle)` with
20/// [`RendererBinding::set_overrides`]; `Handle<T>` is then [`RendererBindingOverrides`].
21pub trait RendererBindingOverridesObject: Sized + 'static {
22    /// Runs at the start of [`RendererBinding::draw_frame`], before layout.
23    fn will_draw_frame(self: Handle<Self>, app: &mut App);
24
25    /// Runs at the end of [`RendererBinding::draw_frame`], after compositing.
26    fn did_draw_frame(self: Handle<Self>, app: &mut App);
27
28    /// Runs at the end of [`RendererBinding::handle_metrics_changed`]: Flutter's
29    /// `WidgetsBinding.handleMetricsChanged` override, which tells its observers.
30    fn did_handle_metrics_changed(self: Handle<Self>, app: &mut App) {
31        let _ = app;
32    }
33}
34
35/// What the binding above this one adds around `drawFrame`; kept erased.
36pub trait RendererBindingOverrides {
37    /// See [`RendererBindingOverridesObject::will_draw_frame`].
38    fn will_draw_frame(&self, app: &mut App);
39
40    /// See [`RendererBindingOverridesObject::did_draw_frame`].
41    fn did_draw_frame(&self, app: &mut App);
42
43    /// See [`RendererBindingOverridesObject::did_handle_metrics_changed`].
44    fn did_handle_metrics_changed(&self, app: &mut App);
45}
46
47impl<T: RendererBindingOverridesObject> RendererBindingOverrides for Handle<T> {
48    fn will_draw_frame(&self, app: &mut App) {
49        T::will_draw_frame(*self, app);
50    }
51
52    fn did_draw_frame(&self, app: &mut App) {
53        T::did_draw_frame(*self, app);
54    }
55
56    fn did_handle_metrics_changed(&self, app: &mut App) {
57        T::did_handle_metrics_changed(*self, app);
58    }
59}
60
61/// Dart's `_BindingPipelineManifold`: the [`PipelineManifold`] the binding attaches its
62/// root [`PipelineOwner`] to. Its `semanticsEnabled` waits with semantics.
63struct BindingPipelineManifold;
64
65impl PipelineManifold for BindingPipelineManifold {
66    /// A frame of every view, for an owner that renders no view of its own.
67    fn request_visual_update(&self, app: &mut App) {
68        SchedulerBinding::ensure_visual_update(app, FrameTarget::EveryView);
69    }
70}
71
72#[derive(Default)]
73pub struct RendererBinding {
74    overrides: Option<Rc<dyn RendererBindingOverrides>>,
75    root_pipeline_owner: Option<Handle<PipelineOwner>>,
76    /// The render view [`init_render_view`](Self::init_render_view) made, which it answers again.
77    test_render_view: Option<RenderHandle<RenderView>>,
78    render_views: Vec<(ViewId, RenderHandle<RenderView>)>,
79    mouse_tracker: Option<Handle<MouseTracker>>,
80    debug_mouse_tracker_update_scheduled: bool,
81    first_frame_deferred_count: u32,
82    first_frame_sent: bool,
83}
84
85impl RendererBinding {
86    /// The current binding, initialized on first access (Flutter's `initInstances`).
87    pub fn instance(app: &mut App) -> Handle<RendererBinding> {
88        let this: Handle<RendererBinding> = app.singleton();
89        if app.get(this).root_pipeline_owner.is_none() {
90            let root_pipeline_owner = this.create_root_pipeline_owner(app);
91            app.get_mut(this).root_pipeline_owner = Some(root_pipeline_owner);
92            root_pipeline_owner.attach(app, Rc::new(BindingPipelineManifold));
93            SchedulerBinding::add_persistent_frame_callback(
94                app,
95                PersistentFrameCallback::new(|app, views, _time_stamp| {
96                    RendererBinding::instance(app).handle_persistent_frame_callback(app, views)
97                }),
98            );
99            this.init_mouse_tracker(app, None);
100            // Flutter's `RendererBinding` overrides `GestureBinding.hitTestInView` and
101            // `dispatchEvent`.
102            GestureBinding::instance(app).set_overrides(app, Rc::new(this));
103        }
104        this
105    }
106
107    /// The object that manages state about currently connected mice, for hover
108    /// notification.
109    pub fn mouse_tracker(self: Handle<Self>, app: &App) -> Handle<MouseTracker> {
110        app.get(self)
111            .mouse_tracker
112            .expect("RendererBinding::instance must run first")
113    }
114
115    /// Creates a [`MouseTracker`] which manages state about currently connected
116    /// mice, for hover notification.
117    ///
118    /// Used by testing framework to reinitialize the mouse tracker between tests.
119    pub fn init_mouse_tracker(
120        self: Handle<Self>,
121        app: &mut App,
122        tracker: Option<Handle<MouseTracker>>,
123    ) {
124        if let Some(previous) = app.get(self).mouse_tracker {
125            previous.dispose(app);
126        }
127        let tracker = tracker.unwrap_or_else(|| {
128            MouseTracker::new(
129                app,
130                Rc::new(move |app: &mut App, position: Offset, view_id: ViewId| {
131                    let mut result = HitTestResult::new();
132                    self.hit_test_in_view(app, &mut result, position, view_id);
133                    result
134                }),
135            )
136        });
137        app.get_mut(self).mouse_tracker = Some(tracker);
138    }
139
140    /// The [`PipelineOwner`] that is the root of the pipeline owner tree.
141    ///
142    /// Typically, this owner has no root render object (a `RenderView` is attached to the
143    /// owners the widget layer adds as children). A test that renders without widgets attaches
144    /// its view's here, through [`init_render_view`](Self::init_render_view).
145    pub fn root_pipeline_owner(self: Handle<Self>, app: &App) -> Handle<PipelineOwner> {
146        app.get(self)
147            .root_pipeline_owner
148            .expect("RendererBinding::instance must run first")
149    }
150
151    /// Creates the [`PipelineOwner`] that serves as the root of the pipeline owner tree.
152    ///
153    /// When a test's render view is its root node ([`init_render_view`](Self::init_render_view)),
154    /// that view is marked to be composited after a paint that had work.
155    fn create_root_pipeline_owner(self: Handle<Self>, app: &mut App) -> Handle<PipelineOwner> {
156        PipelineOwner::new(
157            app,
158            Some(Listener::new(|app| {
159                SchedulerBinding::ensure_visual_update(app, FrameTarget::EveryView);
160            })),
161            Some(Rc::new(move |app: &mut App, is_dirty: bool| {
162                if is_dirty {
163                    let root_node = self.root_pipeline_owner(app).root_node(app);
164                    if let Some(render_view) =
165                        root_node.and_then(|root_node| root_node.downcast::<RenderView>(app))
166                    {
167                        render_view.mark_needs_composite_frame(app);
168                    }
169                }
170            })),
171        )
172    }
173
174    /// The [`RenderView`]s managed by this binding.
175    pub fn render_views(self: Handle<Self>, app: &App) -> Vec<RenderHandle<RenderView>> {
176        app.get(self)
177            .render_views
178            .iter()
179            .map(|(_, view)| *view)
180            .collect()
181    }
182
183    /// Adds a [`RenderView`] to this binding.
184    ///
185    /// The binding will interact with the [`RenderView`] in the following ways:
186    ///
187    /// - setting and updating [`RenderView::configuration`],
188    /// - calling [`RenderView::composite_frame`] when it is time to produce a new frame.
189    pub fn add_render_view(self: Handle<Self>, app: &mut App, view: RenderHandle<RenderView>) {
190        let view_id = view.flutter_view(app).id();
191        debug_assert!(
192            !app.get(self)
193                .render_views
194                .iter()
195                .any(|(id, existing)| *id == view_id || *existing == view)
196        );
197        app.get_mut(self).render_views.push((view_id, view));
198        let configuration = self.create_view_configuration_for(app, view);
199        view.set_configuration(app, configuration);
200        // Ensure the view composites at least once in a non-warm-up frame after
201        // being added, so its contents appear even if no descendants are dirty.
202        view.mark_needs_composite_frame(app);
203    }
204
205    /// Removes a [`RenderView`] previously added with [`add_render_view`](Self::add_render_view).
206    pub fn remove_render_view(self: Handle<Self>, app: &mut App, view: RenderHandle<RenderView>) {
207        let view_id = view.flutter_view(app).id();
208        let views = &mut app.get_mut(self).render_views;
209        debug_assert!(
210            views
211                .iter()
212                .any(|(id, existing)| *id == view_id && *existing == view)
213        );
214        views.retain(|(id, _)| *id != view_id);
215    }
216
217    /// Returns a [`ViewConfiguration`] configured for the provided [`RenderView`] based on
218    /// the current environment.
219    pub fn create_view_configuration_for(
220        self: Handle<Self>,
221        app: &App,
222        render_view: RenderHandle<RenderView>,
223    ) -> ViewConfiguration {
224        ViewConfiguration::from_view(&*render_view.flutter_view(app))
225    }
226
227    /// Marks all views to require compositing, which will force them to re-render
228    /// on their next frame.
229    pub fn handle_mark_all_views_need_render(self: Handle<Self>, app: &mut App) {
230        for view in self.render_views(app) {
231            view.mark_needs_composite_frame(app);
232        }
233    }
234
235    /// Called when the system metrics change.
236    ///
237    /// See `View::metrics`.
238    pub fn handle_metrics_changed(self: Handle<Self>, app: &mut App) {
239        for view in self.render_views(app) {
240            let force_frame = view.child(app).is_some();
241            let configuration = self.create_view_configuration_for(app, view);
242            view.set_configuration(app, configuration);
243            if force_frame {
244                let view_id = view.flutter_view(app).id();
245                SchedulerBinding::schedule_forced_frame(app, FrameTarget::View(view_id));
246            }
247        }
248        if let Some(overrides) = app.get(self).overrides.clone() {
249            overrides.did_handle_metrics_changed(app);
250        }
251    }
252
253    fn handle_persistent_frame_callback(self: Handle<Self>, app: &mut App, views: &[ViewId]) {
254        self.draw_frame(app, views);
255        self.schedule_mouse_tracker_update(app);
256    }
257
258    fn schedule_mouse_tracker_update(self: Handle<Self>, app: &mut App) {
259        debug_assert!(!app.get(self).debug_mouse_tracker_update_scheduled);
260        if cfg!(debug_assertions) {
261            app.get_mut(self).debug_mouse_tracker_update_scheduled = true;
262        }
263        SchedulerBinding::add_post_frame_callback(
264            app,
265            FrameCallback::new(move |app, _duration| {
266                debug_assert!(app.get(self).debug_mouse_tracker_update_scheduled);
267                if cfg!(debug_assertions) {
268                    app.get_mut(self).debug_mouse_tracker_update_scheduled = false;
269                }
270                self.mouse_tracker(app).update_all_devices(app);
271            }),
272            None,
273        );
274    }
275
276    /// Whether frames produced by [`draw_frame`](Self::draw_frame) are sent to the host.
277    ///
278    /// If false the framework will do all the work to produce a frame, but the frame is never
279    /// sent to the host to actually appear on screen.
280    pub fn send_frames_to_engine(self: Handle<Self>, app: &App) -> bool {
281        let data = app.get(self);
282        data.first_frame_sent || data.first_frame_deferred_count == 0
283    }
284
285    /// Tell the framework to not send the first frames to the host until there is a
286    /// corresponding call to [`allow_first_frame`](Self::allow_first_frame).
287    pub fn defer_first_frame(self: Handle<Self>, app: &mut App) {
288        app.get_mut(self).first_frame_deferred_count += 1;
289    }
290
291    /// Called after [`defer_first_frame`](Self::defer_first_frame) to tell the framework that it
292    /// is ok to send the first frame to the host now.
293    pub fn allow_first_frame(self: Handle<Self>, app: &mut App) {
294        debug_assert!(app.get(self).first_frame_deferred_count > 0);
295        app.get_mut(self).first_frame_deferred_count -= 1;
296        if !app.get(self).first_frame_sent {
297            SchedulerBinding::schedule_forced_frame(app, FrameTarget::EveryView);
298        }
299    }
300
301    /// Call this to pretend that no frames have been sent to the host yet.
302    pub fn reset_first_frame_sent(self: Handle<Self>, app: &mut App) {
303        app.get_mut(self).first_frame_sent = false;
304    }
305
306    /// Registers the binding layered above this one, as Flutter's `WidgetsBinding` does by
307    /// overriding `drawFrame`.
308    pub fn set_overrides(
309        self: Handle<Self>,
310        app: &mut App,
311        overrides: Rc<dyn RendererBindingOverrides>,
312    ) {
313        app.get_mut(self).overrides = Some(overrides);
314    }
315
316    /// Pump the rendering pipeline to generate a frame of `views`.
317    ///
318    /// This method is called by `handle_draw_frame`, which itself is called automatically by
319    /// the host when it is time to lay out and paint a frame.
320    ///
321    /// Each frame consists of the following phases: layout and compositing bits for every
322    /// render tree, then paint and compositing for the [`RenderView`] of each of `views`. A view
323    /// paints its own render tree alone, not the views anchored in it, which paint at frames of
324    /// their own; it is composited only when it [needs it](RenderView::needs_composite_frame),
325    /// and otherwise keeps showing its last frame.
326    pub fn draw_frame(self: Handle<Self>, app: &mut App, views: &[ViewId]) {
327        let overrides = app.get(self).overrides.clone();
328        if let Some(overrides) = &overrides {
329            overrides.will_draw_frame(app);
330        }
331        let root_pipeline_owner = self.root_pipeline_owner(app);
332        root_pipeline_owner.flush_layout(app);
333        root_pipeline_owner.flush_compositing_bits(app);
334        let render_views: Vec<RenderHandle<RenderView>> = views
335            .iter()
336            .filter_map(|&view| self.render_view_of(app, view))
337            .collect();
338        for render_view in &render_views {
339            if let Some(owner) = render_view.as_object().owner(app) {
340                owner.flush_paint_without_children(app);
341            }
342        }
343        if self.send_frames_to_engine(app) {
344            for render_view in render_views {
345                if render_view.needs_composite_frame(app) {
346                    render_view.composite_frame(app); // this sends the bits to the GPU
347                }
348                if SchedulerBinding::is_warm_up_frame(app) {
349                    // Warm-up frame does not guarantee that the content is actually
350                    // painted (see [SchedulerBinding::schedule_warm_up_frame]). This is to
351                    // ensure that on the subsequent real frame the view is recomposited.
352                    render_view.mark_needs_composite_frame(app);
353                }
354            }
355            app.get_mut(self).first_frame_sent = true;
356        }
357        if let Some(overrides) = &overrides {
358            overrides.did_draw_frame(app);
359        }
360    }
361
362    /// The [`RenderView`] of `view`, if this binding has one.
363    fn render_view_of(
364        self: Handle<Self>,
365        app: &App,
366        view: ViewId,
367    ) -> Option<RenderHandle<RenderView>> {
368        app.get(self)
369            .render_views
370            .iter()
371            .find(|(id, _)| *id == view)
372            .map(|(_, render_view)| *render_view)
373    }
374
375    /// Flutter's `TestRenderingFlutterBinding.initRenderView`: makes a [`RenderView`] for `view`
376    /// the root of [`root_pipeline_owner`](Self::root_pipeline_owner), registers it, and
377    /// prepares its first frame. Idempotent, as `_ReusableRenderView` is: a later call answers
378    /// the first call's render view.
379    pub fn init_render_view(
380        self: Handle<Self>,
381        app: &mut App,
382        view: ViewRef,
383    ) -> RenderHandle<RenderView> {
384        if let Some(render_view) = app.get(self).test_render_view {
385            return render_view;
386        }
387        let render_view = RenderView::new(app, None, None, view);
388        app.get_mut(self).test_render_view = Some(render_view);
389        self.root_pipeline_owner(app)
390            .set_root_node(app, Some(render_view.as_object()));
391        self.add_render_view(app, render_view);
392        render_view.prepare_initial_frame(app);
393        render_view
394    }
395}
396
397/// Flutter's `RendererBinding.hitTestInView` override: the render view for `view_id` hit-tests
398/// first; `GestureBinding` then adds itself.
399/// Flutter's `RendererBinding` overrides of `GestureBinding`: `hitTestInView` walks the render
400/// trees, `dispatchEvent` updates the mouse tracker before routing.
401impl GestureBindingOverridesObject for RendererBinding {
402    fn hit_test_in_view(
403        self: Handle<Self>,
404        app: &mut App,
405        result: &mut HitTestResult,
406        position: Offset,
407        view_id: ViewId,
408    ) {
409        if let Some(render_view) = self.render_view_of(app, view_id) {
410            render_view.hit_test(app, result, position);
411        }
412    }
413
414    fn will_dispatch_event(
415        self: Handle<Self>,
416        app: &mut App,
417        event: &PointerEvent,
418        hit_test_result: Option<&HitTestResult>,
419    ) {
420        // When the button is pressed, normal hit test uses a cached
421        // result, but MouseTracker requires that the hit test is re-executed to
422        // update the hovering events.
423        let result = match event {
424            PointerEvent::Move(_) => None,
425            _ => hit_test_result,
426        };
427        self.mouse_tracker(app)
428            .update_with_event(app, event, result);
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use std::time::Duration;
435
436    use inset_embedder::{Size, TargetPlatform};
437    use inset_foundation::AppCell;
438    use inset_test::{IMPLICIT_VIEW_ID, TestPlatform, TestView};
439
440    use super::*;
441    use crate::box_::{BoxConstraints, RenderBox};
442    use crate::proxy_box::RenderConstrainedBox;
443
444    /// An [`App`] over one 800x600 view at 2x, with the view and the platform to read back.
445    fn app_with_view() -> (Rc<AppCell>, Rc<TestView>, Rc<TestPlatform>) {
446        let view = Rc::new(TestView::with_pixel_ratio(800.0, 600.0, 2.0));
447        let platform = Rc::new(
448            TestPlatform::new()
449                .on(TargetPlatform::MacOS)
450                .with_view(view.clone()),
451        );
452        (AppCell::with_platform(platform.clone()), view, platform)
453    }
454
455    /// A frame of `views` at `at`, with the microtasks drained after each half.
456    fn pump(app: &mut App, views: &[ViewId], at: Duration) {
457        SchedulerBinding::handle_begin_frame(app, views, Some(at));
458        app.drain_microtasks();
459        SchedulerBinding::handle_draw_frame(app, views);
460        app.drain_microtasks();
461    }
462
463    #[test]
464    fn a_frame_lays_out_paints_and_presents_the_view() {
465        let (cell, view, _platform) = app_with_view();
466        let mut app = cell.borrow_mut();
467        let binding = RendererBinding::instance(&mut app);
468        let render_view = binding.init_render_view(&mut app, view.clone());
469        assert_eq!(
470            render_view.configuration(&app).logical_constraints,
471            BoxConstraints::tight(Size::new(400.0, 300.0))
472        );
473        let child =
474            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(10.0, 10.0)), None);
475        render_view.set_child(&mut app, Some(child.as_box()));
476
477        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
478
479        assert_eq!(render_view.size(&app), Size::new(400.0, 300.0));
480        assert_eq!(child.size(&app), Size::new(400.0, 300.0));
481        assert_eq!(view.presented(), 1);
482        assert!(!child.debug_needs_layout(&app));
483        assert!(!child.as_object().debug_needs_paint(&app));
484    }
485
486    #[test]
487    fn a_dirty_node_requests_a_visual_update() {
488        let (cell, view, platform) = app_with_view();
489        let mut app = cell.borrow_mut();
490        let binding = RendererBinding::instance(&mut app);
491        let render_view = binding.init_render_view(&mut app, view.clone());
492        let child =
493            RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(10.0, 10.0)), None);
494        render_view.set_child(&mut app, Some(child.as_box()));
495        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
496        platform.take_frame_requests();
497
498        child.mark_needs_layout(&mut app);
499        assert_eq!(
500            platform.take_frame_requests(),
501            [IMPLICIT_VIEW_ID],
502            "the root owner's visual update reaches the host"
503        );
504    }
505
506    #[test]
507    fn a_pointer_down_reaches_a_pointer_listener_through_the_gesture_binding() {
508        use inset_gestures::{PointerDownEvent, PointerEvent};
509
510        use crate::proxy_box::{HitTestBehavior, RenderPointerListener};
511
512        let (cell, view, _platform) = app_with_view();
513        let mut app = cell.borrow_mut();
514        let binding = RendererBinding::instance(&mut app);
515        let render_view = binding.init_render_view(&mut app, view.clone());
516        let listener = RenderPointerListener::new(&mut app, HitTestBehavior::Opaque, None);
517        let positions = Rc::new(std::cell::RefCell::new(Vec::new()));
518        let recorded = Rc::clone(&positions);
519        listener.set_on_pointer_down(
520            &mut app,
521            Some(Rc::new(move |_app: &mut App, event: PointerDownEvent| {
522                recorded.borrow_mut().push(event.local_position());
523            })),
524        );
525        render_view.set_child(&mut app, Some(listener.as_box()));
526        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
527
528        GestureBinding::instance(&mut app).handle_pointer_event(
529            &mut app,
530            PointerEvent::Down(PointerDownEvent {
531                position: Offset::new(30.0, 40.0),
532                ..PointerDownEvent::default()
533            }),
534        );
535        assert_eq!(*positions.borrow(), [Offset::new(30.0, 40.0)]);
536    }
537
538    #[test]
539    fn a_mouse_hover_enters_a_mouse_region_and_a_frame_notices_it_moving_away() {
540        use inset_embedder::PointerDeviceKind;
541        use inset_gestures::{
542            PointerAddedEvent, PointerEnterEvent, PointerEvent, PointerExitEvent,
543        };
544        use inset_painting::EdgeInsetsGeometry;
545
546        use crate::proxy_box::RenderMouseRegion;
547        use crate::shifted_box::RenderPadding;
548
549        let (cell, view, _platform) = app_with_view();
550        let mut app = cell.borrow_mut();
551        let binding = RendererBinding::instance(&mut app);
552        let render_view = binding.init_render_view(&mut app, view.clone());
553        let region = RenderMouseRegion::new(&mut app, true, None);
554        let log = Rc::new(std::cell::RefCell::new(Vec::new()));
555        let entered = Rc::clone(&log);
556        region.set_on_enter(
557            &mut app,
558            Some(Rc::new(move |_app: &mut App, event: PointerEnterEvent| {
559                entered
560                    .borrow_mut()
561                    .push(format!("enter {:?}", event.local_position()));
562            })),
563        );
564        let exited = Rc::clone(&log);
565        region.set_on_exit(
566            &mut app,
567            Some(Rc::new(move |_app: &mut App, event: PointerExitEvent| {
568                exited
569                    .borrow_mut()
570                    .push(format!("exit {:?}", event.local_position()));
571            })),
572        );
573        let padding = RenderPadding::new(
574            &mut app,
575            EdgeInsetsGeometry::all(100.0),
576            None,
577            Some(region.as_box()),
578        );
579        render_view.set_child(&mut app, Some(padding.as_box()));
580        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
581
582        GestureBinding::instance(&mut app).handle_pointer_event(
583            &mut app,
584            PointerEvent::Added(PointerAddedEvent {
585                kind: PointerDeviceKind::Mouse,
586                position: Offset::new(150.0, 150.0),
587                ..PointerAddedEvent::default()
588            }),
589        );
590        assert_eq!(*log.borrow(), ["enter Offset(50.0, 50.0)"]);
591        assert!(binding.mouse_tracker(&app).mouse_is_connected(&app));
592
593        // The tree moves under the still mouse; the post-frame update sends the exit.
594        padding.set_padding(&mut app, EdgeInsetsGeometry::only(300.0, 0.0, 0.0, 0.0));
595        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::from_millis(16));
596        assert_eq!(
597            *log.borrow(),
598            ["enter Offset(50.0, 50.0)", "exit Offset(50.0, 50.0)"],
599            "the exit is placed with the transform recorded when the region was entered"
600        );
601    }
602
603    #[test]
604    fn deferred_first_frame_is_not_presented() {
605        let (cell, view, _platform) = app_with_view();
606        let mut app = cell.borrow_mut();
607        let binding = RendererBinding::instance(&mut app);
608        binding.init_render_view(&mut app, view.clone());
609        binding.defer_first_frame(&mut app);
610        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
611        assert_eq!(view.presented(), 0);
612
613        binding.allow_first_frame(&mut app);
614        pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
615        assert_eq!(view.presented(), 1);
616    }
617
618    const FIRST: ViewId = ViewId(1);
619    const SECOND: ViewId = ViewId(2);
620
621    /// An app whose platform has views [`FIRST`] and [`SECOND`], each with a render view under a
622    /// pipeline owner the root owner adopted, which marks its view to be composited after a
623    /// paint that had work, as `RawView`'s does.
624    fn app_with_render_views() -> (Rc<AppCell>, Rc<TestPlatform>, [RenderHandle<RenderView>; 2]) {
625        let platform = Rc::new(
626            TestPlatform::new()
627                .on(TargetPlatform::MacOS)
628                .with_views(&[FIRST, SECOND]),
629        );
630        let cell = AppCell::with_platform(platform.clone());
631        let render_views = {
632            let mut app = cell.borrow_mut();
633            [FIRST, SECOND].map(|view| add_render_view(&mut app, platform.test_view(view)))
634        };
635        (cell, platform, render_views)
636    }
637
638    /// A render view of `view` under a pipeline owner of its own, registered with the binding.
639    fn add_render_view(app: &mut App, view: Rc<TestView>) -> RenderHandle<RenderView> {
640        let binding = RendererBinding::instance(app);
641        let render_view = RenderView::new(app, None, None, view);
642        let owner = PipelineOwner::new(
643            app,
644            None,
645            Some(Rc::new(move |app: &mut App, is_dirty: bool| {
646                if is_dirty {
647                    render_view.mark_needs_composite_frame(app);
648                }
649            })),
650        );
651        owner.set_root_node(app, Some(render_view.as_object()));
652        binding.root_pipeline_owner(app).adopt_child(app, owner);
653        binding.add_render_view(app, render_view);
654        render_view.prepare_initial_frame(app);
655        render_view
656    }
657
658    /// How many frames each of [`FIRST`] and [`SECOND`] was presented.
659    fn presented(platform: &TestPlatform) -> [u32; 2] {
660        [FIRST, SECOND].map(|view| platform.test_view(view).presented())
661    }
662
663    // multi_view_binding_test.dart 'all registered renderviews are asked to composite frame',
664    // with a frame of each view's own where Flutter's one frame is of every view.
665    #[test]
666    fn every_registered_render_view_is_composited_at_its_frame() {
667        let (cell, platform, [first, second]) = app_with_render_views();
668        let mut app = cell.borrow_mut();
669        let binding = RendererBinding::instance(&mut app);
670        assert_eq!(presented(&platform), [0, 0]);
671
672        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
673        assert_eq!(presented(&platform), [1, 1]);
674
675        binding.remove_render_view(&mut app, first);
676        second.as_object().mark_needs_paint(&mut app);
677        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
678        assert_eq!(presented(&platform), [1, 2]);
679
680        binding.remove_render_view(&mut app, second);
681        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
682        assert_eq!(presented(&platform), [1, 2]);
683    }
684
685    // multi_view_binding_test.dart 'render views that are not dirty are not composited'.
686    #[test]
687    fn a_render_view_that_is_not_dirty_is_not_composited() {
688        let (cell, platform, [first, second]) = app_with_render_views();
689        let mut app = cell.borrow_mut();
690        let binding = RendererBinding::instance(&mut app);
691
692        // First frame: both views are composited (they are newly added).
693        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
694        assert_eq!(presented(&platform), [1, 1]);
695
696        // Second frame: neither view is dirty, so neither is composited.
697        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
698        assert_eq!(presented(&platform), [1, 1]);
699
700        // Only the second is marked dirty: only the second is composited.
701        second.as_object().mark_needs_paint(&mut app);
702        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
703        assert_eq!(presented(&platform), [1, 2]);
704
705        binding.remove_render_view(&mut app, first);
706        binding.remove_render_view(&mut app, second);
707    }
708
709    #[test]
710    fn a_frame_paints_and_composites_only_its_own_views() {
711        let (cell, platform, [first, second]) = app_with_render_views();
712        let mut app = cell.borrow_mut();
713        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
714
715        first.as_object().mark_needs_paint(&mut app);
716        second.as_object().mark_needs_paint(&mut app);
717        pump(&mut app, &[SECOND], Duration::ZERO);
718        assert_eq!(presented(&platform), [1, 2]);
719        assert!(
720            first.as_object().debug_needs_paint(&app),
721            "left for its own frame"
722        );
723    }
724
725    #[test]
726    fn a_view_drawn_in_the_warm_up_frame_is_composited_again_at_its_next_frame() {
727        let (cell, platform, _render_views) = app_with_render_views();
728        SchedulerBinding::schedule_warm_up_frame(&mut cell.borrow_mut());
729        cell.elapse(Duration::ZERO);
730        assert_eq!(presented(&platform), [1, 1]);
731
732        let mut app = cell.borrow_mut();
733        pump(&mut app, &[FIRST], Duration::ZERO);
734        assert_eq!(
735            presented(&platform),
736            [2, 1],
737            "a warm-up frame may not reach the screen: the next real frame composites again"
738        );
739        pump(&mut app, &[FIRST], Duration::ZERO);
740        assert_eq!(presented(&platform), [2, 1]);
741    }
742
743    #[test]
744    fn marking_every_view_to_be_rendered_composites_each_at_its_next_frame() {
745        let (cell, platform, _render_views) = app_with_render_views();
746        let mut app = cell.borrow_mut();
747        let binding = RendererBinding::instance(&mut app);
748        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
749
750        binding.handle_mark_all_views_need_render(&mut app);
751        pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
752        assert_eq!(presented(&platform), [2, 2]);
753    }
754}