use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::mpsc;
use kozan_core::Document;
use kozan_core::compositor::layer_tree::LayerTree;
use kozan_core::paint::DisplayList;
use kozan_core::scroll::ScrollOffsets;
use kozan_core::scroll::ScrollTree;
use kozan_core::widget::FrameWidget;
use kozan_scheduler::WakeSender;
use crate::host::PlatformHost;
use crate::id::WindowId;
use crate::pipeline::render_loop::RenderEvent;
pub(crate) type StagedFuture = Pin<Box<dyn Future<Output = ()> + 'static>>;
pub(crate) type FrameCallback = Box<dyn FnMut(kozan_scheduler::FrameInfo) -> bool + 'static>;
pub struct FrameOutput {
pub display_list: Arc<DisplayList>,
pub layer_tree: LayerTree,
pub scroll_tree: ScrollTree,
}
pub struct ViewContext {
frame: FrameWidget,
wake_sender: WakeSender,
host: Arc<dyn PlatformHost>,
window_id: WindowId,
render_sender: mpsc::Sender<RenderEvent>,
staged_futures: Rc<RefCell<Vec<StagedFuture>>>,
staged_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
last_fps: Rc<std::cell::Cell<f64>>,
}
impl ViewContext {
pub(crate) fn new(
frame: FrameWidget,
wake_sender: WakeSender,
host: Arc<dyn PlatformHost>,
window_id: WindowId,
render_sender: mpsc::Sender<RenderEvent>,
) -> Self {
Self {
frame,
wake_sender,
host,
window_id,
render_sender,
staged_futures: Rc::new(RefCell::new(Vec::new())),
staged_frame_callbacks: Rc::new(RefCell::new(Vec::new())),
last_fps: Rc::new(std::cell::Cell::new(0.0)),
}
}
#[inline]
pub fn document(&self) -> &Document {
self.frame.document()
}
pub fn register_font(
&self,
data: impl Into<kozan_core::layout::inline::font_system::FontBlob>,
) -> Vec<String> {
self.frame.font_system().register_font(data)
}
pub fn spawn(&self, future: impl Future<Output = ()> + 'static) {
self.staged_futures.borrow_mut().push(Box::pin(future));
}
pub(crate) fn take_staged_futures(&self) -> Vec<StagedFuture> {
self.staged_futures.borrow_mut().drain(..).collect()
}
pub(crate) fn take_staged_frame_callbacks(&self) -> Vec<FrameCallback> {
std::mem::take(&mut *self.staged_frame_callbacks.borrow_mut())
}
#[inline]
pub fn wake_sender(&self) -> WakeSender {
self.wake_sender.clone()
}
pub fn request_redraw(&self) {
self.host.request_redraw(self.window_id);
}
pub fn set_title(&self, title: &str) {
self.host.set_title(self.window_id, title);
}
pub fn close_window(&self) {
self.host.close_window(self.window_id);
}
#[inline]
pub fn window_id(&self) -> WindowId {
self.window_id
}
#[inline]
pub fn fps(&self) -> f64 {
self.last_fps.get()
}
#[inline]
pub fn fps_cell(&self) -> Rc<std::cell::Cell<f64>> {
Rc::clone(&self.last_fps)
}
pub fn request_frame(
&self,
callback: impl FnMut(kozan_scheduler::FrameInfo) -> bool + 'static,
) {
self.staged_frame_callbacks
.borrow_mut()
.push(Box::new(callback));
}
pub(crate) fn set_last_fps(&self, fps: f64) {
self.last_fps.set(fps);
}
pub(crate) fn last_frame_timing(&self) -> kozan_primitives::timing::FrameTiming {
self.frame.last_timing()
}
pub(crate) fn document_needs_frame(&self) -> bool {
self.frame.document().needs_visual_update()
}
pub(crate) fn apply_scroll_sync(&mut self, offsets: ScrollOffsets) {
self.frame.apply_compositor_scroll(&offsets);
}
pub(crate) fn update_lifecycle_and_commit(&mut self) {
self.frame.update_lifecycle();
let dl = self.frame.last_display_list();
let layer_tree = self.frame.take_layer_tree();
if let (Some(dl), Some(tree)) = (dl, layer_tree) {
let (scroll_tree, _scroll_offsets) = self.frame.scroll_state_snapshot();
let _ = self.render_sender.send(RenderEvent::Commit(FrameOutput {
display_list: dl,
layer_tree: tree,
scroll_tree,
}));
}
}
pub(crate) fn on_input(&mut self, input: kozan_core::InputEvent) -> bool {
self.frame.handle_input(input)
}
pub(crate) fn invalidate_style(&mut self) {
self.frame.mark_needs_update();
}
pub(crate) fn on_resize(&mut self, width: u32, height: u32) {
self.frame.resize(width, height);
}
pub(crate) fn on_scale_factor_changed(&mut self, factor: f64) {
self.frame.set_scale_factor(factor);
}
}