From a264cdde6c74427ef3192f885b85e8e2b53d7d90 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sun, 17 Nov 2024 17:32:36 -0500 Subject: [PATCH] Create `use_layout` hook and refactor `Canvas` (#50) * Create use_layout hook and refactor Canvas * Pass clippy --- crates/actuate-core/src/lib.rs | 7 +++-- src/lib.rs | 6 +---- src/ui/canvas.rs | 49 +++++++--------------------------- src/ui/mod.rs | 44 ++++++++++++++++++++++++++++++ src/ui/window.rs | 1 - src/view.rs | 14 +++++++--- 6 files changed, 69 insertions(+), 52 deletions(-) diff --git a/crates/actuate-core/src/lib.rs b/crates/actuate-core/src/lib.rs index 8045aa5..4d63165 100644 --- a/crates/actuate-core/src/lib.rs +++ b/crates/actuate-core/src/lib.rs @@ -614,14 +614,17 @@ impl fmt::Display for ContextError { /// /// # Panics /// Panics if the context value is not found. -pub fn use_context(cx: &ScopeData) -> Result, ContextError> { +pub fn use_context<'a, T: 'static>(cx: ScopeState<'a>) -> Result<&'a T, ContextError> { let Some(any) = cx.contexts.borrow().values.get(&TypeId::of::()).cloned() else { return Err(ContextError { _marker: PhantomData, }); }; - Ok(any.downcast().unwrap()) + let value: &T = (*any).downcast_ref().unwrap(); + let value: &'a T = unsafe { mem::transmute(value) }; + + Ok(value) } /// Provide a context value of type `T`. diff --git a/src/lib.rs b/src/lib.rs index 160e6c3..e8bef3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,18 +73,14 @@ pub struct WindowContext { is_layout_changed: Cell, canvas_update_fns: RefCell>>, listeners: Rc>>>, - pending_listeners: Rc>>, base_color: Cell, } +#[derive(Data)] struct RenderRoot { content: C, } -unsafe impl Data for RenderRoot { - type Id = RenderRoot; -} - impl Compose for RenderRoot { fn compose(cx: Scope) -> impl Compose { use_provider(&cx, CanvasContext::default); diff --git a/src/ui/canvas.rs b/src/ui/canvas.rs index a7e39fe..59dfd82 100644 --- a/src/ui/canvas.rs +++ b/src/ui/canvas.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, Draw, LayoutContext, WindowContext}; +use crate::{prelude::*, Draw, ListenerFn, WindowContext}; use std::{cell::RefCell, mem, rc::Rc}; use taffy::{Layout, Style}; use vello::{ @@ -6,9 +6,12 @@ use vello::{ Scene, }; +use super::use_layout; + #[derive(Clone, Default)] pub(crate) struct CanvasContext { pub(crate) draws: RefCell>>, + pub(crate) pending_listeners: Rc>>, } type DrawFn<'a> = Box; @@ -31,25 +34,12 @@ impl<'a> Canvas<'a> { impl Compose for Canvas<'_> { fn compose(cx: Scope) -> impl Compose { let canvas_cx = use_context::(&cx).unwrap(); - let layout_cx = use_context::(&cx).unwrap(); let renderer_cx = use_context::(&cx).unwrap(); - let parent_key = layout_cx.parent_id; - let key = *use_ref(&cx, || { - let key = renderer_cx - .taffy - .borrow_mut() - .new_leaf(cx.me().style.clone()) - .unwrap(); - renderer_cx - .taffy - .borrow_mut() - .add_child(parent_key, key) - .unwrap(); - - renderer_cx.is_layout_changed.set(true); - - let listeners = mem::take(&mut *renderer_cx.pending_listeners.borrow_mut()); + let (key, layout) = use_layout(&cx, cx.me().style.clone()); + + use_ref(&cx, || { + let listeners = canvas_cx.pending_listeners.borrow().clone(); renderer_cx.listeners.borrow_mut().insert(key, listeners); let f: Box = Box::new(move || { @@ -60,36 +50,15 @@ impl Compose for Canvas<'_> { let f: Box = unsafe { mem::transmute(f) }; renderer_cx.canvas_update_fns.borrow_mut().insert(key, f); - - key }); // Safety: We must remove `f` here to make the above valid. - - let renderer_cx_handle = renderer_cx.clone(); use_drop(&cx, move || { - renderer_cx_handle - .canvas_update_fns - .borrow_mut() - .remove(&key); - - renderer_cx_handle.taffy.borrow_mut().remove(key).unwrap(); - renderer_cx_handle.listeners.borrow_mut().remove(&key); + renderer_cx.canvas_update_fns.borrow_mut().remove(&key); }); - let last_style = use_ref(&cx, || cx.me().style.clone()); - if cx.me().style != *last_style { - renderer_cx.is_layout_changed.set(true); - renderer_cx - .taffy - .borrow_mut() - .set_style(key, cx.me().style.clone()) - .unwrap(); - } - let scene = use_ref(&cx, || RefCell::new(Scene::new())); - let layout = *renderer_cx.taffy.borrow().layout(key).unwrap(); let mut parent_scene = renderer_cx.scene.borrow_mut(); if cx.is_parent_changed() { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ef5d8f4..f2426f0 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,3 +1,5 @@ +use crate::{prelude::*, LayoutContext, WindowContext}; + pub(crate) mod canvas; pub use self::canvas::Canvas; @@ -9,3 +11,45 @@ pub use self::text::{use_font, Text}; mod window; pub use self::window::Window; + +/// Use a new layout node. +pub fn use_layout(cx: ScopeState, style: Style) -> (NodeId, Layout) { + let layout_cx = use_context::(cx).unwrap(); + let renderer_cx = use_context::(cx).unwrap(); + + let parent_key = layout_cx.parent_id; + let key = *use_ref(cx, || { + let key = renderer_cx + .taffy + .borrow_mut() + .new_leaf(style.clone()) + .unwrap(); + renderer_cx + .taffy + .borrow_mut() + .add_child(parent_key, key) + .unwrap(); + + renderer_cx.is_layout_changed.set(true); + + key + }); + + let last_style = use_ref(cx, || style.clone()); + if style != *last_style { + renderer_cx.is_layout_changed.set(true); + renderer_cx + .taffy + .borrow_mut() + .set_style(key, style.clone()) + .unwrap(); + } + + use_drop(cx, move || { + renderer_cx.taffy.borrow_mut().remove(key).unwrap(); + renderer_cx.listeners.borrow_mut().remove(&key); + }); + + let layout = *renderer_cx.taffy.borrow().layout(key).unwrap(); + (key, layout) +} diff --git a/src/ui/window.rs b/src/ui/window.rs index b60af5b..c7e3d02 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -72,7 +72,6 @@ impl Compose for Window { is_layout_changed: Cell::new(false), canvas_update_fns: RefCell::default(), listeners: Rc::default(), - pending_listeners: Rc::default(), base_color: Cell::new(Color::WHITE), } }); diff --git a/src/view.rs b/src/view.rs index 2b60927..95a1028 100644 --- a/src/view.rs +++ b/src/view.rs @@ -5,7 +5,7 @@ use crate::{ canvas::CanvasContext, text::{IntoFontStack, TextContext}, }, - Event, WindowContext, + Event, }; use parley::FontStack; use std::{cell::RefCell, mem, rc::Rc}; @@ -136,10 +136,10 @@ unsafe impl Data for OnEvent { impl Modify for OnEvent { fn use_state<'a>(&'a self, cx: ScopeState<'a>) { - let renderer_cx = use_context::(cx).unwrap(); + let canvas_cx = use_context::(cx).unwrap(); let state = use_ref(cx, || RefCell::new(self.on_event.borrow_mut().build())); - use_ref(cx, || { + use_provider(cx, || { // Safety: `f` is removed from `canvas_update_fns` on drop. let f: Rc = Rc::new(move |msg| { self.on_event @@ -148,7 +148,13 @@ impl Modify for OnEvent { }); let f: Rc = unsafe { mem::transmute(f) }; - renderer_cx.pending_listeners.borrow_mut().push(f); + let mut pending_listeners = canvas_cx.pending_listeners.borrow().clone(); + pending_listeners.push(f); + + CanvasContext { + draws: canvas_cx.draws.clone(), + pending_listeners: Rc::new(RefCell::new(pending_listeners)), + } }); } }