diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index 82415d45f..7720d8256 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -27,7 +27,7 @@ impl SharedArena { /// THIS METHOD IS CURRENTLY UNSAFE /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS - pub fn try_get(&self, idx: ScopeIdx) -> Result<&Scope> { + pub fn try_get(&self, idx: ScopeId) -> Result<&Scope> { let inner = unsafe { &*self.components.get() }; let scope = inner.get(idx); scope.ok_or_else(|| Error::FatalInternal("Scope not found")) @@ -35,7 +35,7 @@ impl SharedArena { /// THIS METHOD IS CURRENTLY UNSAFE /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS - pub fn try_get_mut(&self, idx: ScopeIdx) -> Result<&mut Scope> { + pub fn try_get_mut(&self, idx: ScopeId) -> Result<&mut Scope> { let inner = unsafe { &mut *self.components.get() }; let scope = inner.get_mut(idx); scope.ok_or_else(|| Error::FatalInternal("Scope not found")) @@ -59,7 +59,7 @@ impl SharedArena { pub fn with_scope<'b, O: 'static>( &'b self, - _id: ScopeIdx, + _id: ScopeId, _f: impl FnOnce(&'b mut Scope) -> O, ) -> Result { todo!() @@ -69,13 +69,13 @@ impl SharedArena { // this is useful for merging lifetimes pub fn with_scope_vnode<'b>( &self, - _id: ScopeIdx, + _id: ScopeId, _f: impl FnOnce(&mut Scope) -> &VNode<'b>, ) -> Result<&VNode<'b>> { todo!() } - pub fn try_remove(&self, id: ScopeIdx) -> Result { + pub fn try_remove(&self, id: ScopeId) -> Result { let inner = unsafe { &mut *self.components.get() }; inner .remove(id) diff --git a/packages/core/src/context.rs b/packages/core/src/context.rs index 5e916d80e..faa1df831 100644 --- a/packages/core/src/context.rs +++ b/packages/core/src/context.rs @@ -1,6 +1,5 @@ use crate::innerlude::*; -use bumpalo::Bump; use futures_util::FutureExt; use std::any::Any; @@ -12,7 +11,6 @@ use std::{ cell::RefCell, future::Future, ops::Deref, - pin::Pin, rc::{Rc, Weak}, }; @@ -34,13 +32,20 @@ use std::{ /// } /// } /// ``` -// todo: force lifetime of source into T as a valid lifetime too -// it's definitely possible, just needs some more messing around +/// +/// ## Available Methods: +/// - render +/// - use_hook +/// - use_task +/// - use_suspense +/// - submit_task +/// - children +/// - use_effect +/// pub struct Context<'src, T> { pub props: &'src T, pub scope: &'src Scope, } -// pub type PinnedTask = Pin>>; impl<'src, T> Copy for Context<'src, T> {} impl<'src, T> Clone for Context<'src, T> { @@ -52,9 +57,10 @@ impl<'src, T> Clone for Context<'src, T> { } } +// We currently deref to props, but it might make more sense to deref to Scope? +// This allows for code that takes cx.xyz instead of cx.props.xyz impl<'a, T> Deref for Context<'a, T> { type Target = &'a T; - fn deref(&self) -> &Self::Target { &self.props } @@ -62,14 +68,42 @@ impl<'a, T> Deref for Context<'a, T> { impl<'src, P> Context<'src, P> { /// Access the children elements passed into the component + /// + /// This enables patterns where a component is passed children from its parent. + /// + /// ## Details + /// + /// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions + /// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure + /// on the props that takes Context. + /// + /// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other + /// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's + /// props are valid for the static lifetime. + /// + /// ## Example + /// + /// ```rust + /// const App: FC<()> = |cx| { + /// cx.render(rsx!{ + /// CustomCard { + /// h1 {} + /// p {} + /// } + /// }) + /// } + /// + /// const CustomCard: FC<()> = |cx| { + /// cx.render(rsx!{ + /// div { + /// h1 {"Title card"} + /// {cx.children()} + /// } + /// }) + /// } + /// ``` pub fn children(&self) -> &'src [VNode<'src>] { - // We're re-casting the nodes back out - // They don't really have a static lifetime - unsafe { - let scope = self.scope; - let nodes = scope.child_nodes; - nodes - } + self.scope.child_nodes } /// Create a subscription that schedules a future render for the reference component @@ -227,7 +261,10 @@ Any function prefixed with "use" should not be called conditionally. let ty = TypeId::of::(); while let Some(inner) = scope { - log::debug!("Searching {:#?} for valid shared_context", inner.arena_idx); + log::debug!( + "Searching {:#?} for valid shared_context", + inner.our_arena_idx + ); let shared_contexts = inner.shared_contexts.borrow(); if let Some(shared_cx) = shared_contexts.get(&ty) { @@ -241,7 +278,7 @@ Any function prefixed with "use" should not be called conditionally. hook.par = Some(rc); return Ok(hook.par.as_ref().unwrap()); } else { - match inner.parent { + match inner.parent_idx { Some(parent_id) => { let parent = inner .arena_link @@ -324,7 +361,7 @@ Any function prefixed with "use" should not be called conditionally. let slot = task_dump.clone(); let update = self.schedule_update(); - let originator = self.scope.arena_idx.clone(); + let originator = self.scope.our_arena_idx.clone(); self.submit_task(Box::pin(task_fut.then(move |output| async move { *slot.as_ref().borrow_mut() = Some(output); @@ -411,7 +448,7 @@ impl<'src, P> Context<'src, P> { } }); - let originator = self.scope.arena_idx.clone(); + let originator = self.scope.our_arena_idx.clone(); let task_fut = task_initializer(); let domnode = dom_node_id.clone(); diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 1f857d7ed..5bcfbb41a 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -75,10 +75,10 @@ pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> { pub edits: DomEditor<'real, 'bump>, pub components: &'bump SharedArena, pub task_queue: &'bump TaskQueue, - pub cur_idx: ScopeIdx, - pub diffed: FxHashSet, + pub cur_idx: ScopeId, + pub diffed: FxHashSet, pub event_queue: EventQueue, - pub seen_nodes: FxHashSet, + pub seen_nodes: FxHashSet, } impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom> @@ -89,7 +89,7 @@ where edits: &'real mut Vec>, dom: &'real mut Dom, components: &'bump SharedArena, - cur_idx: ScopeIdx, + cur_idx: ScopeId, event_queue: EventQueue, task_queue: &'bump TaskQueue, ) -> Self { @@ -488,7 +488,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { /// /// Calling this will run the destuctors on all hooks in the tree. /// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered. - fn destroy_scopes(&mut self, old_scope: ScopeIdx) { + fn destroy_scopes(&mut self, old_scope: ScopeId) { let mut nodes_to_delete = vec![old_scope]; let mut scopes_to_explore = vec![old_scope]; diff --git a/packages/core/src/editor.rs b/packages/core/src/editor.rs index 7e6cc457f..3c227c4fe 100644 --- a/packages/core/src/editor.rs +++ b/packages/core/src/editor.rs @@ -1,5 +1,3 @@ -//! Serialization -//! ------------- //! //! //! @@ -7,7 +5,7 @@ //! //! -use crate::{innerlude::ScopeIdx, RealDomNode}; +use crate::{innerlude::ScopeId, RealDomNode}; /// The `DomEditor` provides an imperative interface for the Diffing algorithm to plan out its changes. /// @@ -18,6 +16,7 @@ use crate::{innerlude::ScopeIdx, RealDomNode}; pub struct DomEditor<'real, 'bump> { pub edits: &'real mut Vec>, } + use DomEdit::*; impl<'real, 'bump> DomEditor<'real, 'bump> { pub fn new(edits: &'real mut Vec>) -> Self { @@ -29,35 +28,44 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { let id = root.as_u64(); self.edits.push(PushRoot { id }); } + + #[inline] pub(crate) fn pop(&mut self) { self.edits.push(PopRoot {}); } // Add Nodes to the dom // add m nodes from the stack + #[inline] pub(crate) fn append_children(&mut self, many: u32) { self.edits.push(AppendChildren { many }); } // replace the n-m node on the stack with the m nodes // ends with the last element of the chain on the top of the stack + #[inline] pub(crate) fn replace_with(&mut self, many: u32) { self.edits.push(ReplaceWith { many }); } // Remove Nodesfrom the dom + #[inline] pub(crate) fn remove(&mut self) { self.edits.push(Remove); } + + #[inline] pub(crate) fn remove_all_children(&mut self) { self.edits.push(RemoveAllChildren); } // Create + #[inline] pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) { let id = id.as_u64(); self.edits.push(CreateTextNode { text, id }); } + pub(crate) fn create_element( &mut self, tag: &'static str, @@ -81,25 +89,30 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { pub(crate) fn new_event_listener( &mut self, event: &'static str, - scope: ScopeIdx, + scope: ScopeId, element_id: usize, realnode: RealDomNode, ) { self.edits.push(NewEventListener { scope, - event, - idx: element_id, - node: realnode.as_u64(), + event_name: event, + element_id, + mounted_node_id: realnode.as_u64(), }); } + + #[inline] pub(crate) fn remove_event_listener(&mut self, event: &'static str) { self.edits.push(RemoveEventListener { event }); } // modify + #[inline] pub(crate) fn set_text(&mut self, text: &'bump str) { self.edits.push(SetText { text }); } + + #[inline] pub(crate) fn set_attribute( &mut self, field: &'static str, @@ -108,6 +121,8 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { ) { self.edits.push(SetAttribute { field, value, ns }); } + + #[inline] pub(crate) fn remove_attribute(&mut self, name: &'static str) { self.edits.push(RemoveAttribute { name }); } @@ -151,10 +166,10 @@ pub enum DomEdit<'bump> { id: u64, }, NewEventListener { - event: &'static str, - scope: ScopeIdx, - node: u64, - idx: usize, + event_name: &'static str, + scope: ScopeId, + mounted_node_id: u64, + element_id: usize, }, RemoveEventListener { event: &'static str, diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 85aad57c7..66cc0bf5e 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -4,17 +4,14 @@ //! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might //! be heavy or need to interact through FFI, so the events themselves are designed to be lazy. -use std::{ - cell::{Cell, RefCell}, - rc::Rc, -}; +use std::{cell::Cell, rc::Rc}; -use crate::innerlude::{RealDomNode, ScopeIdx}; +use crate::innerlude::{RealDomNode, ScopeId}; #[derive(Debug)] pub struct EventTrigger { /// The originator of the event trigger - pub originator: ScopeIdx, + pub originator: ScopeId, /// The optional real node associated with the trigger pub real_node_id: Option, @@ -27,7 +24,7 @@ pub struct EventTrigger { } impl EventTrigger { - pub fn new_from_task(originator: ScopeIdx, hook_idx: usize) -> Self { + pub fn new_from_task(originator: ScopeId, hook_idx: usize) -> Self { Self { originator, event: VirtualEvent::AsyncEvent { hook_idx }, @@ -74,7 +71,7 @@ pub enum EventPriority { impl EventTrigger { pub fn new( event: VirtualEvent, - scope: ScopeIdx, + scope: ScopeId, mounted_dom_id: Option, priority: EventPriority, ) -> Self { @@ -184,7 +181,7 @@ pub mod on { Listener { event: stringify!($name), mounted_node: bump.alloc(Cell::new(RealDomNode::empty())), - scope: c.scope_ref.arena_idx, + scope: c.scope_ref.our_arena_idx, callback: bump.alloc(move |evt: VirtualEvent| match evt { VirtualEvent::$wrapper(event) => callback(event), _ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!") diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 8e65250a2..d9241e519 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(non_snake_case, dead_code, unused_must_use, unreachable_code)] +#![allow(non_snake_case)] //! Dioxus Core //! ---------- //! @@ -11,7 +11,7 @@ pub use crate::innerlude::{ format_args_f, html, rsx, DioxusElement, DomEdit, EventTrigger, LazyNodes, NodeFactory, - Properties, RealDom, RealDomNode, ScopeIdx, VNode, VNodeKind, VirtualDom, VirtualEvent, FC, + Properties, RealDom, RealDomNode, ScopeId, VNode, VNodeKind, VirtualDom, VirtualEvent, FC, }; pub mod prelude { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index f3585f63f..b781ad17d 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -5,10 +5,10 @@ use crate::{ events::VirtualEvent, - innerlude::{Context, Properties, RealDomNode, Scope, ScopeIdx, FC}, + innerlude::{Context, Properties, RealDomNode, Scope, ScopeId, FC}, }; use std::{ - cell::{Cell, RefCell}, + cell::Cell, fmt::{Arguments, Debug, Formatter}, marker::PhantomData, rc::Rc, @@ -87,7 +87,7 @@ pub struct Attribute<'a> { pub struct Listener<'bump> { /// The type of event to listen for. pub(crate) event: &'static str, - pub scope: ScopeIdx, + pub scope: ScopeId, pub mounted_node: &'bump Cell, pub(crate) callback: &'bump dyn FnMut(VirtualEvent), } @@ -95,7 +95,7 @@ pub struct Listener<'bump> { /// Virtual Components for custom user-defined components /// Only supports the functional syntax pub struct VComponent<'src> { - pub ass_scope: Cell>, + pub ass_scope: Cell>, pub(crate) caller: Rc VNode>, pub(crate) children: &'src [VNode<'src>], pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>, @@ -328,6 +328,26 @@ impl<'a> NodeFactory<'a> { } } +/// Trait implementations for use in the rsx! and html! macros. +/// +/// ## Details +/// +/// This section provides convenience methods and trait implementations for converting common structs into a format accepted +/// by the macros. +/// +/// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out. +/// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type: +/// ``` +/// impl IntoIterator> +/// ``` +/// +/// As such, all node creation must go through the factory, which is only availble in the component context. +/// These strict requirements make it possible to manage lifetimes and state. +pub trait IntoVNode<'a> { + fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>; +} + +// For the case where a rendered VNode is passed into the rsx! macro through curly braces impl<'a> IntoIterator for VNode<'a> { type Item = VNode<'a>; type IntoIter = std::iter::Once; @@ -335,25 +355,53 @@ impl<'a> IntoIterator for VNode<'a> { std::iter::once(self) } } + +// For the case where a rendered VNode is passed into the rsx! macro through curly braces impl<'a> IntoVNode<'a> for VNode<'a> { fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> { self } } +// For the case where a rendered VNode is by reference passed into the rsx! macro through curly braces +// This behavior is designed for the cx.children method where child nodes are passed by reference. +// +// Designed to support indexing impl<'a> IntoVNode<'a> for &VNode<'a> { fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> { - self.clone() + let kind = match &self.kind { + VNodeKind::Element(element) => VNodeKind::Element(element), + VNodeKind::Text(old) => VNodeKind::Text(VText { + text: old.text, + is_static: old.is_static, + }), + VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment { + children: fragment.children, + is_static: fragment.is_static, + }), + VNodeKind::Component(component) => VNodeKind::Component(component), + + // todo: it doesn't make much sense to pass in suspended nodes + // I think this is right but I'm not too sure. + VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() }, + }; + VNode { + kind, + dom_id: self.dom_id.clone(), + key: self.key.clone(), + } } } -pub trait IntoVNode<'a> { - fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>; -} - -// Wrap the the node-builder closure in a concrete type. -// --- -// This is a bit of a hack to implement the IntoVNode trait for closure types. +/// A concrete type provider for closures that build VNode structures. +/// +/// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over +/// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode. +/// +/// +/// ```rust +/// LazyNodes::new(|f| f.element("div", [], [], [] None)) +/// ``` pub struct LazyNodes<'a, G> where G: FnOnce(NodeFactory<'a>) -> VNode<'a>, @@ -374,11 +422,7 @@ where } } -// Cover the cases where nodes are used by macro. -// Likely used directly. -// --- -// let nodes = rsx!{ ... }; -// rsx! { {nodes } } +// Our blanket impl impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G> where G: FnOnce(NodeFactory<'a>) -> VNode<'a>, @@ -388,7 +432,7 @@ where } } -// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator +// Our blanket impl impl<'a, G> IntoIterator for LazyNodes<'a, G> where G: FnOnce(NodeFactory<'a>) -> VNode<'a>, @@ -400,12 +444,14 @@ where } } +// Conveniently, we also support "null" (nothing) passed in impl IntoVNode<'_> for () { fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> { cx.fragment_from_iter(None as Option) } } +// Conveniently, we also support "None" impl IntoVNode<'_> for Option<()> { fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> { cx.fragment_from_iter(None as Option) @@ -418,30 +464,6 @@ impl Debug for NodeFactory<'_> { } } -// it's okay to clone because vnodes are just references to places into the bump -impl<'a> Clone for VNode<'a> { - fn clone(&self) -> Self { - let kind = match &self.kind { - VNodeKind::Element(element) => VNodeKind::Element(element), - VNodeKind::Text(old) => VNodeKind::Text(VText { - text: old.text, - is_static: old.is_static, - }), - VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment { - children: fragment.children, - is_static: fragment.is_static, - }), - VNodeKind::Component(component) => VNodeKind::Component(component), - VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() }, - }; - VNode { - kind, - dom_id: self.dom_id.clone(), - key: self.key.clone(), - } - } -} - impl Debug for VNode<'_> { fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { match &self.kind { diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 82589db12..981a10114 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -22,53 +22,36 @@ pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>; /// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components. /// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently. pub struct Scope { - // The parent's scope ID - pub parent: Option, + // Book-keeping about the arena + pub(crate) parent_idx: Option, + pub(crate) descendents: RefCell>, + pub(crate) our_arena_idx: ScopeId, + pub(crate) height: u32, - // IDs of children that this scope has created - // This enables us to drop the children and their children when this scope is destroyed - pub(crate) descendents: RefCell>, + // Nodes + // an internal, highly efficient storage of vnodes + pub(crate) frames: ActiveFrame, + pub(crate) child_nodes: &'static [VNode<'static>], + pub(crate) caller: Rc, - pub child_nodes: &'static [VNode<'static>], + // Listeners + pub(crate) listeners: RefCell, *mut dyn FnMut(VirtualEvent))>>, + pub(crate) listener_idx: Cell, + + // State + pub(crate) hooks: HookList, + pub(crate) shared_contexts: RefCell>>, + + // Events + pub(crate) event_channel: Rc, + + // Tasks + pub(crate) task_submitter: TaskSubmitter, + pub(crate) suspended_tasks: Vec<*mut Pin>>>>, // A reference to the list of components. // This lets us traverse the component list whenever we need to access our parent or children. - pub arena_link: SharedArena, - - pub shared_contexts: RefCell>>, - - // Our own ID accessible from the component map - pub arena_idx: ScopeIdx, - - pub height: u32, - - pub event_channel: Rc, - - pub caller: Rc, - - // ========================== - // slightly unsafe stuff - // ========================== - // an internal, highly efficient storage of vnodes - pub frames: ActiveFrame, - - // These hooks are actually references into the hook arena - // These two could be combined with "OwningRef" to remove unsafe usage - // or we could dedicate a tiny bump arena just for them - // could also use ourborous - pub hooks: HookList, - - pub(crate) listener_idx: Cell, - - // Unsafety: - // - is self-refenrential and therefore needs to point into the bump - // Stores references into the listeners attached to the vnodes - // NEEDS TO BE PRIVATE - pub(crate) listeners: RefCell, *mut dyn FnMut(VirtualEvent))>>, - - pub task_submitter: TaskSubmitter, - - pub(crate) suspended_tasks: Vec<*mut Pin>>>>, + pub(crate) arena_link: SharedArena, } pub type FiberTask = Pin>>; @@ -83,8 +66,8 @@ impl Scope { // Therefore, their lifetimes are connected exclusively to the virtual dom pub fn new<'creator_node>( caller: Rc, - arena_idx: ScopeIdx, - parent: Option, + arena_idx: ScopeId, + parent: Option, height: u32, event_channel: EventChannel, arena_link: SharedArena, @@ -102,8 +85,8 @@ impl Scope { Self { child_nodes, caller, - parent, - arena_idx, + parent_idx: parent, + our_arena_idx: arena_idx, height, event_channel, arena_link, @@ -190,7 +173,7 @@ impl Scope { log::debug!( "There are {:?} listeners associated with this scope {:#?}", self.listeners.borrow().len(), - self.arena_idx + self.our_arena_idx ); let listners = self.listeners.borrow_mut(); diff --git a/packages/core/src/tasks.rs b/packages/core/src/tasks.rs index 921f1efc7..cba9c8f59 100644 --- a/packages/core/src/tasks.rs +++ b/packages/core/src/tasks.rs @@ -11,15 +11,13 @@ use std::{ cell::Cell, - pin::Pin, sync::{Arc, RwLock}, - task::{Context, Poll}, }; use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt}; use slotmap::{DefaultKey, SlotMap}; -use crate::innerlude::{EventTrigger, FiberTask, ScopeIdx}; +use crate::innerlude::{EventTrigger, FiberTask, ScopeId}; pub type TaskSubmitter = Arc; @@ -117,11 +115,11 @@ pub struct TaskHandle { pub struct DTask { fut: FiberTask, - originator: ScopeIdx, + originator: ScopeId, dead: Cell, } impl DTask { - pub fn new(fut: FiberTask, originator: ScopeIdx) -> Self { + pub fn new(fut: FiberTask, originator: ScopeId) -> Self { Self { fut, originator, @@ -129,7 +127,7 @@ impl DTask { } } pub fn debug_new(fut: FiberTask) -> Self { - let originator = ScopeIdx::default(); + let originator = ScopeId::default(); Self { fut, originator, diff --git a/packages/core/src/util.rs b/packages/core/src/util.rs index 8e7bf94ea..e91ebab16 100644 --- a/packages/core/src/util.rs +++ b/packages/core/src/util.rs @@ -14,7 +14,7 @@ pub struct EventQueue { } impl EventQueue { - pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc { + pub fn new_channel(&self, height: u32, idx: ScopeId) -> Rc { let inner = self.clone(); let marker = HeightMarker { height, idx }; Rc::new(move || { @@ -35,7 +35,7 @@ impl EventQueue { /// A helper type that lets scopes be ordered by their height #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct HeightMarker { - pub idx: ScopeIdx, + pub idx: ScopeId, pub height: u32, } @@ -59,23 +59,29 @@ impl PartialOrd for HeightMarker { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq)] -pub struct RealDomNode(pub DefaultKey); +pub struct RealDomNode(pub u64); impl RealDomNode { + #[inline] pub fn empty() -> Self { - let data = KeyData::from_ffi(u64::MIN); - let key: DefaultKey = data.into(); - Self(key) + // let data = KeyData::from_ffi(u64::MIN); + // let key: DefaultKey = data.into(); + Self(u64::MIN) } + #[inline] pub fn empty_cell() -> Cell { Cell::new(Self::empty()) } + #[inline] pub fn from_u64(id: u64) -> Self { - let data = KeyData::from_ffi(id); - let key: DefaultKey = data.into(); - Self(key) + // let data = KeyData::from_ffi(id); + // let key: DefaultKey = data.into(); + Self(id) } + + #[inline] pub fn as_u64(&self) -> u64 { - self.0.data().as_ffi() + // self.0.data().as_ffi() + self.0 } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 809daec23..5b68c1438 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -30,10 +30,17 @@ use std::any::TypeId; use std::cell::RefCell; use std::pin::Pin; -pub type ScopeIdx = DefaultKey; +pub type ScopeId = DefaultKey; /// An integrated virtual node system that progresses events and diffs UI trees. /// Differences are converted into patches which a renderer can use to draw the UI. +/// +/// +/// +/// +/// +/// +/// pub struct VirtualDom { /// All mounted components are arena allocated to make additions, removals, and references easy to work with /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena. @@ -44,7 +51,7 @@ pub struct VirtualDom { /// The index of the root component /// Should always be the first (gen=0, id=0) - pub base_scope: ScopeIdx, + pub base_scope: ScopeId, pub triggers: RefCell>, @@ -55,7 +62,7 @@ pub struct VirtualDom { root_props: std::pin::Pin>, - /// Type of the original cx. This is stored as TypeId so VirtualDom does not need to be generic. + /// Type of the original props. This is stored as TypeId so VirtualDom does not need to be generic. /// /// Whenver props need to be updated, an Error will be thrown if the new props do not /// match the props used to create the VirtualDom. diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 412ead50c..6ad49d878 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -130,7 +130,9 @@ impl<'a> TextRenderer<'a> { self.html_render(new_node, f, il + 1)?; } - VNodeKind::Suspended => todo!(), + VNodeKind::Suspended { .. } => { + // we can't do anything with suspended nodes + } } Ok(()) } diff --git a/packages/web/src/new.rs b/packages/web/src/new.rs index 0595a31a2..fb1a3e5e6 100644 --- a/packages/web/src/new.rs +++ b/packages/web/src/new.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; use dioxus_core::{ events::{EventTrigger, VirtualEvent}, - DomEdit, RealDomNode, ScopeIdx, + DomEdit, RealDomNode, ScopeId, }; use fxhash::FxHashMap; use slotmap::{DefaultKey, Key, KeyData}; @@ -85,10 +85,10 @@ impl WebsysDom { DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id), DomEdit::CreatePlaceholder { id } => self.create_placeholder(id), DomEdit::NewEventListener { - event, + event_name: event, scope, - node, - idx, + mounted_node_id: node, + element_id: idx, } => self.new_event_listener(event, scope, idx, node), DomEdit::RemoveEventListener { event } => todo!(), DomEdit::SetText { text } => self.set_text(text), @@ -234,7 +234,7 @@ impl WebsysDom { fn new_event_listener( &mut self, event: &'static str, - scope: ScopeIdx, + scope: ScopeId, _element_id: usize, real_id: u64, ) { @@ -335,7 +335,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom { fn request_available_node(&mut self) -> RealDomNode { let key = self.nodes.insert(None); log::debug!("making new key: {:#?}", key); - RealDomNode(key) + RealDomNode(key.data().as_ffi()) } fn raw_node_as_any(&self) -> &mut dyn std::any::Any { @@ -349,22 +349,24 @@ pub struct Stack { } impl Stack { + #[inline] pub fn with_capacity(cap: usize) -> Self { Stack { list: Vec::with_capacity(cap), } } + #[inline] pub fn push(&mut self, node: Node) { - // debug!("stack-push: {:?}", node); self.list.push(node); } + #[inline] pub fn pop(&mut self) -> Node { - let res = self.list.pop().unwrap(); - res + self.list.pop().unwrap() } + #[inline] pub fn clear(&mut self) { self.list.clear(); } @@ -588,16 +590,11 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { use anyhow::Context; - // for attr in { let attrs = target.attributes(); for x in 0..attrs.length() { let attr = attrs.item(x).unwrap(); log::debug!("attrs include: {:#?}", attr); } - // } - // for attr in target.attributes() { - // log::debug!("attrs include: {:#?}", attr); - // } // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system let val: String = target @@ -613,34 +610,18 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { let real_id = fields .next() - .and_then(|raw_id| { - raw_id - .parse::() - .ok() - .map(|value| KeyData::from_ffi(value)) - .map(|f| RealDomNode(f.into())) - }) + .and_then(|raw_id| raw_id.parse::().ok()) .context("failed to parse real id")?; // Call the trigger log::debug!("decoded scope_id: {}, node_id: {:#?}", gi_id, real_id); - let triggered_scope: ScopeIdx = KeyData::from_ffi(gi_id).into(); + let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into(); log::debug!("Triggered scope is {:#?}", triggered_scope); Ok(EventTrigger::new( virtual_event_from_websys_event(event), triggered_scope, - Some(real_id), + Some(RealDomNode::from_u64(real_id)), dioxus_core::events::EventPriority::High, )) } - -struct ListenerMap {} -impl ListenerMap { - fn get(&self, event: &'static str) -> bool { - false - } -} - -struct JsStringCache {} -impl JsStringCache {} diff --git a/packages/webview/src/dom.rs b/packages/webview/src/dom.rs index 230d98fc2..72aa87d68 100644 --- a/packages/webview/src/dom.rs +++ b/packages/webview/src/dom.rs @@ -1,6 +1,6 @@ //! webview dom -use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeIdx}; +use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeId}; use DomEdit::*; pub struct WebviewRegistry {}