use std::rc::Rc;
use inset_embedder::{Offset, ViewId};
use inset_foundation::{App, Handle, Listener};
use inset_gestures::{GestureBinding, GestureBindingOverridesObject, HitTestResult, PointerEvent};
use inset_scheduler::{FrameCallback, SchedulerBinding};
use crate::mouse_tracker::MouseTracker;
use crate::object::RenderHandle;
use crate::pipeline_owner::{PipelineManifold, PipelineOwner};
use crate::view::{RenderView, ViewConfiguration};
pub trait RendererBindingOverridesObject: Sized + 'static {
fn will_draw_frame(self: Handle<Self>, app: &mut App);
fn did_draw_frame(self: Handle<Self>, app: &mut App);
fn did_handle_metrics_changed(self: Handle<Self>, app: &mut App) {
let _ = app;
}
}
pub trait RendererBindingOverrides {
fn will_draw_frame(&self, app: &mut App);
fn did_draw_frame(&self, app: &mut App);
fn did_handle_metrics_changed(&self, app: &mut App);
}
impl<T: RendererBindingOverridesObject> RendererBindingOverrides for Handle<T> {
fn will_draw_frame(&self, app: &mut App) {
T::will_draw_frame(*self, app);
}
fn did_draw_frame(&self, app: &mut App) {
T::did_draw_frame(*self, app);
}
fn did_handle_metrics_changed(&self, app: &mut App) {
T::did_handle_metrics_changed(*self, app);
}
}
struct BindingPipelineManifold;
impl PipelineManifold for BindingPipelineManifold {
fn request_visual_update(&self, app: &mut App) {
SchedulerBinding::ensure_visual_update(app);
}
}
#[derive(Default)]
pub struct RendererBinding {
overrides: Option<Rc<dyn RendererBindingOverrides>>,
root_pipeline_owner: Option<Handle<PipelineOwner>>,
pipeline_owner: Option<Handle<PipelineOwner>>,
render_view: Option<RenderHandle<RenderView>>,
render_view_initial_frame_prepared: bool,
render_views: Vec<(ViewId, RenderHandle<RenderView>)>,
mouse_tracker: Option<Handle<MouseTracker>>,
debug_mouse_tracker_update_scheduled: bool,
first_frame_deferred_count: u32,
first_frame_sent: bool,
}
impl RendererBinding {
pub fn instance(app: &mut App) -> Handle<RendererBinding> {
let this: Handle<RendererBinding> = app.singleton();
if app.get(this).root_pipeline_owner.is_none() {
let root_pipeline_owner = this.create_root_pipeline_owner(app);
app.get_mut(this).root_pipeline_owner = Some(root_pipeline_owner);
root_pipeline_owner.attach(app, Rc::new(BindingPipelineManifold));
SchedulerBinding::add_persistent_frame_callback(
app,
FrameCallback::new(|app, _time_stamp| {
RendererBinding::instance(app).handle_persistent_frame_callback(app)
}),
);
this.init_mouse_tracker(app, None);
GestureBinding::instance(app).set_overrides(app, Rc::new(this));
}
this
}
pub fn mouse_tracker(self: Handle<Self>, app: &App) -> Handle<MouseTracker> {
app.get(self)
.mouse_tracker
.expect("RendererBinding::instance must run first")
}
pub fn init_mouse_tracker(
self: Handle<Self>,
app: &mut App,
tracker: Option<Handle<MouseTracker>>,
) {
if let Some(previous) = app.get(self).mouse_tracker {
previous.dispose(app);
}
let tracker = tracker.unwrap_or_else(|| {
MouseTracker::new(
app,
Rc::new(move |app: &mut App, position: Offset, view_id: ViewId| {
let mut result = HitTestResult::new();
self.hit_test_in_view(app, &mut result, position, view_id);
result
}),
)
});
app.get_mut(self).mouse_tracker = Some(tracker);
}
pub fn root_pipeline_owner(self: Handle<Self>, app: &App) -> Handle<PipelineOwner> {
app.get(self)
.root_pipeline_owner
.expect("RendererBinding::instance must run first")
}
fn create_root_pipeline_owner(self: Handle<Self>, app: &mut App) -> Handle<PipelineOwner> {
PipelineOwner::new(
app,
Some(Listener::new(|app| {
SchedulerBinding::ensure_visual_update(app);
})),
)
}
pub fn pipeline_owner(self: Handle<Self>, app: &mut App) -> Handle<PipelineOwner> {
if let Some(owner) = app.get(self).pipeline_owner {
return owner;
}
let owner = PipelineOwner::new(app, None);
app.get_mut(self).pipeline_owner = Some(owner);
owner
}
pub fn render_view(self: Handle<Self>, app: &mut App) -> RenderHandle<RenderView> {
if let Some(view) = app.get(self).render_view {
return view;
}
let implicit_view = app
.platform()
.implicit_view()
.expect("the platform has no implicit view");
let view = RenderView::new(app, None, None, implicit_view);
app.get_mut(self).render_view = Some(view);
view
}
pub fn render_views(self: Handle<Self>, app: &App) -> Vec<RenderHandle<RenderView>> {
app.get(self)
.render_views
.iter()
.map(|(_, view)| *view)
.collect()
}
pub fn add_render_view(self: Handle<Self>, app: &mut App, view: RenderHandle<RenderView>) {
let view_id = view.flutter_view(app).id();
debug_assert!(
!app.get(self)
.render_views
.iter()
.any(|(id, existing)| *id == view_id || *existing == view)
);
app.get_mut(self).render_views.push((view_id, view));
let configuration = self.create_view_configuration_for(app, view);
view.set_configuration(app, configuration);
}
pub fn remove_render_view(self: Handle<Self>, app: &mut App, view: RenderHandle<RenderView>) {
let view_id = view.flutter_view(app).id();
let views = &mut app.get_mut(self).render_views;
debug_assert!(
views
.iter()
.any(|(id, existing)| *id == view_id && *existing == view)
);
views.retain(|(id, _)| *id != view_id);
}
pub fn create_view_configuration_for(
self: Handle<Self>,
app: &App,
render_view: RenderHandle<RenderView>,
) -> ViewConfiguration {
ViewConfiguration::from_view(&*render_view.flutter_view(app))
}
pub fn handle_metrics_changed(self: Handle<Self>, app: &mut App) {
let mut force_frame = false;
for view in self.render_views(app) {
force_frame = force_frame || view.child(app).is_some();
let configuration = self.create_view_configuration_for(app, view);
view.set_configuration(app, configuration);
}
if force_frame {
SchedulerBinding::schedule_forced_frame(app);
}
if let Some(overrides) = app.get(self).overrides.clone() {
overrides.did_handle_metrics_changed(app);
}
}
fn handle_persistent_frame_callback(self: Handle<Self>, app: &mut App) {
self.draw_frame(app);
self.schedule_mouse_tracker_update(app);
}
fn schedule_mouse_tracker_update(self: Handle<Self>, app: &mut App) {
debug_assert!(!app.get(self).debug_mouse_tracker_update_scheduled);
if cfg!(debug_assertions) {
app.get_mut(self).debug_mouse_tracker_update_scheduled = true;
}
SchedulerBinding::add_post_frame_callback(
app,
FrameCallback::new(move |app, _duration| {
debug_assert!(app.get(self).debug_mouse_tracker_update_scheduled);
if cfg!(debug_assertions) {
app.get_mut(self).debug_mouse_tracker_update_scheduled = false;
}
self.mouse_tracker(app).update_all_devices(app);
}),
);
}
pub fn send_frames_to_engine(self: Handle<Self>, app: &App) -> bool {
let data = app.get(self);
data.first_frame_sent || data.first_frame_deferred_count == 0
}
pub fn defer_first_frame(self: Handle<Self>, app: &mut App) {
app.get_mut(self).first_frame_deferred_count += 1;
}
pub fn allow_first_frame(self: Handle<Self>, app: &mut App) {
debug_assert!(app.get(self).first_frame_deferred_count > 0);
app.get_mut(self).first_frame_deferred_count -= 1;
if !app.get(self).first_frame_sent {
SchedulerBinding::schedule_forced_frame(app);
}
}
pub fn reset_first_frame_sent(self: Handle<Self>, app: &mut App) {
app.get_mut(self).first_frame_sent = false;
}
pub fn set_overrides(
self: Handle<Self>,
app: &mut App,
overrides: Rc<dyn RendererBindingOverrides>,
) {
app.get_mut(self).overrides = Some(overrides);
}
pub fn draw_frame(self: Handle<Self>, app: &mut App) {
let overrides = app.get(self).overrides.clone();
if let Some(overrides) = &overrides {
overrides.will_draw_frame(app);
}
let root_pipeline_owner = self.root_pipeline_owner(app);
root_pipeline_owner.flush_layout(app);
root_pipeline_owner.flush_compositing_bits(app);
root_pipeline_owner.flush_paint(app);
if self.send_frames_to_engine(app) {
for render_view in self.render_views(app) {
render_view.composite_frame(app); }
app.get_mut(self).first_frame_sent = true;
}
if let Some(overrides) = &overrides {
overrides.did_draw_frame(app);
}
}
pub fn init_render_view(self: Handle<Self>, app: &mut App) -> RenderHandle<RenderView> {
let render_view = self.render_view(app);
if app.get(self).render_view_initial_frame_prepared {
return render_view;
}
self.root_pipeline_owner(app)
.set_root_node(app, Some(render_view.as_object()));
self.add_render_view(app, render_view);
render_view.prepare_initial_frame(app);
app.get_mut(self).render_view_initial_frame_prepared = true;
render_view
}
}
impl GestureBindingOverridesObject for RendererBinding {
fn hit_test(self: Handle<Self>, app: &mut App, result: &mut HitTestResult, position: Offset) {
let Some(view) = app.platform().implicit_view() else {
return;
};
self.hit_test_in_view(app, result, position, view.id());
}
fn hit_test_in_view(
self: Handle<Self>,
app: &mut App,
result: &mut HitTestResult,
position: Offset,
view_id: ViewId,
) {
let render_view = app
.get(self)
.render_views
.iter()
.find(|(id, _)| *id == view_id)
.map(|(_, view)| *view);
if let Some(render_view) = render_view {
render_view.hit_test(app, result, position);
}
}
fn will_dispatch_event(
self: Handle<Self>,
app: &mut App,
event: &PointerEvent,
hit_test_result: Option<&HitTestResult>,
) {
let result = match event {
PointerEvent::Move(_) => None,
_ => hit_test_result,
};
self.mouse_tracker(app)
.update_with_event(app, event, result);
}
}
#[cfg(test)]
mod tests {
use inset_foundation::AppCell;
use std::cell::Cell;
use std::time::Duration;
use inset_embedder::{
Picture, Platform, PlatformRef, Size, TargetPlatform, View, ViewMetrics, ViewRef,
};
use super::*;
use crate::box_::{BoxConstraints, RenderBox};
use crate::proxy_box::RenderConstrainedBox;
struct TestView {
presented: Rc<Cell<u32>>,
}
impl View for TestView {
fn id(&self) -> ViewId {
ViewId(0)
}
fn metrics(&self) -> ViewMetrics {
ViewMetrics {
physical_size: [800.0, 600.0],
physical_constraints: inset_embedder::ViewConstraints::tight(800.0, 600.0),
device_pixel_ratio: 2.0,
..ViewMetrics::default()
}
}
fn present(&self, _picture: &Picture) {
self.presented.set(self.presented.get() + 1);
}
}
struct TestPlatform {
view: ViewRef,
frames: Rc<Cell<u32>>,
}
impl Platform for TestPlatform {
fn target_platform(&self) -> TargetPlatform {
TargetPlatform::MacOS
}
fn request_frame(&self) {
self.frames.set(self.frames.get() + 1);
}
fn now(&self) -> std::time::Instant {
std::time::Instant::now()
}
fn wake_at(&self, _deadline: std::time::Instant) {}
fn views(&self) -> Vec<ViewRef> {
vec![Rc::clone(&self.view)]
}
fn view(&self, id: ViewId) -> Option<ViewRef> {
(self.view.id() == id).then(|| Rc::clone(&self.view))
}
fn implicit_view(&self) -> Option<ViewRef> {
Some(Rc::clone(&self.view))
}
}
fn app_with_view() -> (Rc<AppCell>, Rc<Cell<u32>>, Rc<Cell<u32>>) {
let presented = Rc::new(Cell::new(0));
let frames = Rc::new(Cell::new(0));
let platform: PlatformRef = Rc::new(TestPlatform {
view: Rc::new(TestView {
presented: Rc::clone(&presented),
}),
frames: Rc::clone(&frames),
});
(AppCell::with_platform(platform), presented, frames)
}
#[test]
fn a_frame_lays_out_paints_and_presents_the_implicit_view() {
let (cell, presented, _frames) = app_with_view();
let mut app = cell.borrow_mut();
let binding = RendererBinding::instance(&mut app);
let render_view = binding.init_render_view(&mut app);
assert_eq!(
render_view.configuration(&app).logical_constraints,
BoxConstraints::tight(Size::new(400.0, 300.0))
);
let child =
RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(10.0, 10.0)), None);
render_view.set_child(&mut app, Some(child.as_box()));
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::ZERO));
SchedulerBinding::handle_draw_frame(&mut app);
assert_eq!(render_view.size(&app), Size::new(400.0, 300.0));
assert_eq!(child.size(&app), Size::new(400.0, 300.0));
assert_eq!(presented.get(), 1);
assert!(!child.debug_needs_layout(&app));
assert!(!child.as_object().debug_needs_paint(&app));
}
#[test]
fn a_dirty_node_requests_a_visual_update() {
let (cell, _presented, frames) = app_with_view();
let mut app = cell.borrow_mut();
let binding = RendererBinding::instance(&mut app);
let render_view = binding.init_render_view(&mut app);
let child =
RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(10.0, 10.0)), None);
render_view.set_child(&mut app, Some(child.as_box()));
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::ZERO));
SchedulerBinding::handle_draw_frame(&mut app);
let before = frames.get();
child.mark_needs_layout(&mut app);
assert!(
frames.get() > before,
"the root owner's visual update reaches the host"
);
}
#[test]
fn a_pointer_down_reaches_a_pointer_listener_through_the_gesture_binding() {
use inset_gestures::{PointerDownEvent, PointerEvent};
use crate::proxy_box::{HitTestBehavior, RenderPointerListener};
let (cell, _presented, _frames) = app_with_view();
let mut app = cell.borrow_mut();
let binding = RendererBinding::instance(&mut app);
let render_view = binding.init_render_view(&mut app);
let listener = RenderPointerListener::new(&mut app, HitTestBehavior::Opaque, None);
let positions = Rc::new(std::cell::RefCell::new(Vec::new()));
let recorded = Rc::clone(&positions);
listener.set_on_pointer_down(
&mut app,
Some(Rc::new(move |_app: &mut App, event: PointerDownEvent| {
recorded.borrow_mut().push(event.local_position());
})),
);
render_view.set_child(&mut app, Some(listener.as_box()));
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::ZERO));
SchedulerBinding::handle_draw_frame(&mut app);
GestureBinding::instance(&mut app).handle_pointer_event(
&mut app,
PointerEvent::Down(PointerDownEvent {
position: Offset::new(30.0, 40.0),
..PointerDownEvent::default()
}),
);
assert_eq!(*positions.borrow(), [Offset::new(30.0, 40.0)]);
}
#[test]
fn a_mouse_hover_enters_a_mouse_region_and_a_frame_notices_it_moving_away() {
use inset_embedder::PointerDeviceKind;
use inset_gestures::{
PointerAddedEvent, PointerEnterEvent, PointerEvent, PointerExitEvent,
};
use inset_painting::EdgeInsetsGeometry;
use crate::proxy_box::RenderMouseRegion;
use crate::shifted_box::RenderPadding;
let (cell, _presented, _frames) = app_with_view();
let mut app = cell.borrow_mut();
let binding = RendererBinding::instance(&mut app);
let render_view = binding.init_render_view(&mut app);
let region = RenderMouseRegion::new(&mut app, true, None);
let log = Rc::new(std::cell::RefCell::new(Vec::new()));
let entered = Rc::clone(&log);
region.set_on_enter(
&mut app,
Some(Rc::new(move |_app: &mut App, event: PointerEnterEvent| {
entered
.borrow_mut()
.push(format!("enter {:?}", event.local_position()));
})),
);
let exited = Rc::clone(&log);
region.set_on_exit(
&mut app,
Some(Rc::new(move |_app: &mut App, event: PointerExitEvent| {
exited
.borrow_mut()
.push(format!("exit {:?}", event.local_position()));
})),
);
let padding = RenderPadding::new(
&mut app,
EdgeInsetsGeometry::all(100.0),
None,
Some(region.as_box()),
);
render_view.set_child(&mut app, Some(padding.as_box()));
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::ZERO));
SchedulerBinding::handle_draw_frame(&mut app);
GestureBinding::instance(&mut app).handle_pointer_event(
&mut app,
PointerEvent::Added(PointerAddedEvent {
kind: PointerDeviceKind::Mouse,
position: Offset::new(150.0, 150.0),
..PointerAddedEvent::default()
}),
);
assert_eq!(*log.borrow(), ["enter Offset(50.0, 50.0)"]);
assert!(binding.mouse_tracker(&app).mouse_is_connected(&app));
padding.set_padding(&mut app, EdgeInsetsGeometry::only(300.0, 0.0, 0.0, 0.0));
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::from_millis(16)));
SchedulerBinding::handle_draw_frame(&mut app);
assert_eq!(
*log.borrow(),
["enter Offset(50.0, 50.0)", "exit Offset(50.0, 50.0)"],
"the exit is placed with the transform recorded when the region was entered"
);
}
#[test]
fn deferred_first_frame_is_not_presented() {
let (cell, presented, _frames) = app_with_view();
let mut app = cell.borrow_mut();
let binding = RendererBinding::instance(&mut app);
binding.init_render_view(&mut app);
binding.defer_first_frame(&mut app);
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::ZERO));
SchedulerBinding::handle_draw_frame(&mut app);
assert_eq!(presented.get(), 0);
binding.allow_first_frame(&mut app);
SchedulerBinding::handle_begin_frame(&mut app, Some(Duration::ZERO));
SchedulerBinding::handle_draw_frame(&mut app);
assert_eq!(presented.get(), 1);
}
}