mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
remove a lot of unsafe
This commit is contained in:
parent
c70e2bfcb6
commit
0c76770da0
12 changed files with 201 additions and 363 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::{nodes::RenderReturn, Element};
|
||||
use std::{ops::Deref, panic::AssertUnwindSafe};
|
||||
use std::{any::Any, ops::Deref, panic::AssertUnwindSafe};
|
||||
|
||||
/// A boxed version of AnyProps that can be cloned
|
||||
pub(crate) struct BoxedAnyProps {
|
||||
|
@ -7,7 +7,7 @@ pub(crate) struct BoxedAnyProps {
|
|||
}
|
||||
|
||||
impl BoxedAnyProps {
|
||||
fn new(inner: impl AnyProps + 'static) -> Self {
|
||||
pub fn new(inner: impl AnyProps + 'static) -> Self {
|
||||
Self {
|
||||
inner: Box::new(inner),
|
||||
}
|
||||
|
@ -33,7 +33,8 @@ impl Clone for BoxedAnyProps {
|
|||
/// A trait that essentially allows VComponentProps to be used generically
|
||||
pub(crate) trait AnyProps {
|
||||
fn render<'a>(&'a self) -> RenderReturn;
|
||||
fn memoize(&self, other: &dyn AnyProps) -> bool;
|
||||
fn memoize(&self, other: &dyn Any) -> bool;
|
||||
fn props(&self) -> &dyn Any;
|
||||
fn duplicate(&self) -> Box<dyn AnyProps>;
|
||||
}
|
||||
|
||||
|
@ -41,25 +42,35 @@ pub(crate) struct VProps<P> {
|
|||
pub render_fn: fn(P) -> Element,
|
||||
pub memo: fn(&P, &P) -> bool,
|
||||
pub props: P,
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
impl<P> VProps<P> {
|
||||
pub(crate) fn new(render_fn: fn(P) -> Element, memo: fn(&P, &P) -> bool, props: P) -> Self {
|
||||
pub(crate) fn new(
|
||||
render_fn: fn(P) -> Element,
|
||||
memo: fn(&P, &P) -> bool,
|
||||
props: P,
|
||||
name: &'static str,
|
||||
) -> Self {
|
||||
Self {
|
||||
render_fn,
|
||||
memo,
|
||||
props,
|
||||
name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Clone> AnyProps for VProps<P> {
|
||||
// Safety:
|
||||
// this will downcast the other ptr as our swallowed type!
|
||||
// you *must* make this check *before* calling this method
|
||||
// if your functions are not the same, then you will downcast a pointer into a different type (UB)
|
||||
fn memoize(&self, other: &dyn AnyProps) -> bool {
|
||||
(self.memo)(self, other)
|
||||
fn memoize(&self, other: &dyn Any) -> bool {
|
||||
match other.downcast_ref::<Self>() {
|
||||
Some(other) => (self.memo)(&self.props, &other.props),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn props(&self) -> &dyn Any {
|
||||
&self.props
|
||||
}
|
||||
|
||||
fn render(&self) -> RenderReturn {
|
||||
|
@ -72,7 +83,7 @@ impl<P: Clone> AnyProps for VProps<P> {
|
|||
Ok(Some(e)) => RenderReturn::Ready(e),
|
||||
Ok(None) => RenderReturn::default(),
|
||||
Err(err) => {
|
||||
let component_name = cx.name();
|
||||
let component_name = self.name;
|
||||
tracing::error!("Error while rendering component `{component_name}`: {err:?}");
|
||||
RenderReturn::default()
|
||||
}
|
||||
|
@ -84,6 +95,7 @@ impl<P: Clone> AnyProps for VProps<P> {
|
|||
render_fn: self.render_fn,
|
||||
memo: self.memo,
|
||||
props: self.props.clone(),
|
||||
name: self.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use crate::{
|
||||
innerlude::DirtyScope, nodes::RenderReturn, nodes::VNode, virtual_dom::VirtualDom, DynamicNode,
|
||||
ScopeId,
|
||||
|
@ -21,16 +19,16 @@ pub struct ElementId(pub usize);
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct VNodeId(pub usize);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ElementRef {
|
||||
// the pathway of the real element inside the template
|
||||
pub(crate) path: ElementPath,
|
||||
|
||||
// The actual template
|
||||
pub(crate) template: VNodeId,
|
||||
|
||||
// The scope the element belongs to
|
||||
// the scope that this element belongs to
|
||||
pub(crate) scope: ScopeId,
|
||||
|
||||
// The actual element
|
||||
pub(crate) element: VNode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -43,14 +41,6 @@ impl VirtualDom {
|
|||
ElementId(self.elements.insert(None))
|
||||
}
|
||||
|
||||
pub(crate) fn next_vnode_ref(&mut self, vnode: &VNode) -> VNodeId {
|
||||
let new_id = VNodeId(self.element_refs.insert(Some(unsafe {
|
||||
std::mem::transmute::<NonNull<VNode>, _>(vnode.into())
|
||||
})));
|
||||
|
||||
new_id
|
||||
}
|
||||
|
||||
pub(crate) fn reclaim(&mut self, el: ElementId) {
|
||||
self.try_reclaim(el)
|
||||
.unwrap_or_else(|| panic!("cannot reclaim {:?}", el));
|
||||
|
@ -67,11 +57,6 @@ impl VirtualDom {
|
|||
self.elements.try_remove(el.0).map(|_| ())
|
||||
}
|
||||
|
||||
pub(crate) fn set_template(&mut self, id: VNodeId, vnode: &VNode) {
|
||||
self.element_refs[id.0] =
|
||||
Some(unsafe { std::mem::transmute::<NonNull<VNode>, _>(vnode.into()) });
|
||||
}
|
||||
|
||||
// Drop a scope and all its children
|
||||
//
|
||||
// Note: This will not remove any ids from the arena
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use crate::any_props::AnyProps;
|
||||
use crate::innerlude::{
|
||||
BorrowedAttributeValue, ElementPath, ElementRef, VComponent, VPlaceholder, VText,
|
||||
};
|
||||
use crate::innerlude::{ElementPath, ElementRef, VComponent, VPlaceholder, VText};
|
||||
use crate::mutations::Mutation;
|
||||
use crate::mutations::Mutation::*;
|
||||
use crate::nodes::VNode;
|
||||
|
@ -96,9 +93,6 @@ impl VirtualDom {
|
|||
nodes_mut.resize(len, ElementId::default());
|
||||
};
|
||||
|
||||
// Set this node id
|
||||
node.stable_id.set(Some(self.next_vnode_ref(node)));
|
||||
|
||||
// The best renderers will have templates prehydrated and registered
|
||||
// Just in case, let's create the template using instructions anyways
|
||||
self.register_template(node.template.get());
|
||||
|
@ -190,7 +184,7 @@ impl VirtualDom {
|
|||
path: ElementPath {
|
||||
path: template.template.get().node_paths[idx],
|
||||
},
|
||||
template: template.stable_id().unwrap(),
|
||||
element: template.clone(),
|
||||
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
|
||||
};
|
||||
self.create_dynamic_node(template_ref, node)
|
||||
|
@ -200,10 +194,10 @@ impl VirtualDom {
|
|||
path: ElementPath {
|
||||
path: template.template.get().node_paths[idx],
|
||||
},
|
||||
template: template.stable_id().unwrap(),
|
||||
element: template.clone(),
|
||||
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
|
||||
};
|
||||
parent.set(Some(template_ref));
|
||||
*parent.borrow_mut() = Some(template_ref);
|
||||
let id = self.set_slot(id);
|
||||
self.mutations.push(CreatePlaceholder { id });
|
||||
1
|
||||
|
@ -217,12 +211,7 @@ impl VirtualDom {
|
|||
}
|
||||
|
||||
fn create_static_text(&mut self, value: &str, id: ElementId) {
|
||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||
let unbounded_text: &str = unsafe { std::mem::transmute(value) };
|
||||
self.mutations.push(CreateTextNode {
|
||||
value: unbounded_text,
|
||||
id,
|
||||
});
|
||||
self.mutations.push(CreateTextNode { value, id });
|
||||
}
|
||||
|
||||
/// We write all the descendent data for this element
|
||||
|
@ -289,7 +278,7 @@ impl VirtualDom {
|
|||
path: ElementPath {
|
||||
path: template.template.get().node_paths[idx],
|
||||
},
|
||||
template: template.stable_id().unwrap(),
|
||||
element: template.clone(),
|
||||
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
|
||||
};
|
||||
let m = self.create_dynamic_node(boundary_ref, &template.dynamic_nodes[idx]);
|
||||
|
@ -336,14 +325,14 @@ impl VirtualDom {
|
|||
attribute.mounted_element.set(id);
|
||||
|
||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||
let unbounded_name: &str = unsafe { std::mem::transmute(attribute.name) };
|
||||
let unbounded_name: &str = &attribute.name;
|
||||
|
||||
match &attribute.value {
|
||||
AttributeValue::Listener(_) => {
|
||||
let path = &template.template.get().attr_paths[idx];
|
||||
let element_ref = ElementRef {
|
||||
path: ElementPath { path },
|
||||
template: template.stable_id().unwrap(),
|
||||
element: template.clone(),
|
||||
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
|
||||
};
|
||||
self.elements[id.0] = Some(element_ref);
|
||||
|
@ -354,9 +343,7 @@ impl VirtualDom {
|
|||
})
|
||||
}
|
||||
_ => {
|
||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||
let value: BorrowedAttributeValue = (&attribute.value).into();
|
||||
let unbounded_value = unsafe { std::mem::transmute(value) };
|
||||
let unbounded_value = &attribute.value;
|
||||
|
||||
self.mutations.push(SetAttribute {
|
||||
name: unbounded_name,
|
||||
|
@ -516,7 +503,7 @@ impl VirtualDom {
|
|||
placeholder.id.set(Some(id));
|
||||
|
||||
// Assign the placeholder's parent
|
||||
placeholder.parent.set(Some(parent));
|
||||
*placeholder.parent.borrow_mut() = Some(parent);
|
||||
|
||||
// Assign the ID to the existing node in the template
|
||||
self.mutations.push(AssignId {
|
||||
|
@ -540,7 +527,11 @@ impl VirtualDom {
|
|||
|
||||
component.scope.set(Some(scope));
|
||||
|
||||
match self.run_scope(scope) {
|
||||
let new = self.run_scope(scope);
|
||||
|
||||
self.scopes[scope.0].last_rendered_node = Some(new.clone());
|
||||
|
||||
match &new {
|
||||
// Create the component's root element
|
||||
Ready(t) => {
|
||||
self.assign_boundary_ref(parent, t);
|
||||
|
@ -550,22 +541,20 @@ impl VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
/// Load a scope from a vcomponent. If the props don't exist, that means the component is currently "live"
|
||||
/// Load a scope from a vcomponent. If the scope id doesn't exist, that means the component is currently "live"
|
||||
fn load_scope_from_vcomponent(&mut self, component: &VComponent) -> ScopeId {
|
||||
component
|
||||
.props
|
||||
.map(|props| {
|
||||
let unbounded_props: Box<dyn AnyProps> = unsafe { std::mem::transmute(props) };
|
||||
self.new_scope(unbounded_props, component.name).context().id
|
||||
})
|
||||
.unwrap_or_else(|| component.scope.get().unwrap())
|
||||
component.scope.get().unwrap_or_else(|| {
|
||||
self.new_scope(component.props.clone(), component.name)
|
||||
.context()
|
||||
.id
|
||||
})
|
||||
}
|
||||
|
||||
fn mount_aborted(&mut self, placeholder: &VPlaceholder, parent: Option<ElementRef>) -> usize {
|
||||
let id = self.next_element();
|
||||
self.mutations.push(Mutation::CreatePlaceholder { id });
|
||||
placeholder.id.set(Some(id));
|
||||
placeholder.parent.set(parent);
|
||||
*placeholder.parent.borrow_mut() = parent;
|
||||
|
||||
1
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use crate::{
|
||||
any_props::AnyProps,
|
||||
arena::ElementId,
|
||||
innerlude::{
|
||||
BorrowedAttributeValue, DirtyScope, ElementPath, ElementRef, VComponent, VPlaceholder,
|
||||
VText,
|
||||
},
|
||||
innerlude::{DirtyScope, ElementPath, ElementRef, VComponent, VPlaceholder, VText},
|
||||
mutations::Mutation,
|
||||
nodes::RenderReturn,
|
||||
nodes::{DynamicNode, VNode},
|
||||
|
@ -17,52 +16,43 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||
use DynamicNode::*;
|
||||
|
||||
impl VirtualDom {
|
||||
pub(super) fn diff_scope(&mut self, scope: ScopeId) {
|
||||
pub(super) fn diff_scope(&mut self, scope: ScopeId, new_nodes: RenderReturn) {
|
||||
self.runtime.scope_stack.borrow_mut().push(scope);
|
||||
let scope_state = &mut self.get_scope(scope).unwrap();
|
||||
unsafe {
|
||||
// Load the old and new bump arenas
|
||||
let old = scope_state
|
||||
.previous_frame()
|
||||
.try_load_node()
|
||||
.expect("Call rebuild before diffing");
|
||||
let scope_state = &mut self.scopes[scope.0];
|
||||
// Load the old and new bump arenas
|
||||
let old = std::mem::replace(&mut scope_state.last_rendered_node, Some(new_nodes)).unwrap();
|
||||
let new = scope_state.last_rendered_node.as_ref().unwrap();
|
||||
|
||||
let new = scope_state
|
||||
.current_frame()
|
||||
.try_load_node()
|
||||
.expect("Call rebuild before diffing");
|
||||
use RenderReturn::{Aborted, Ready};
|
||||
|
||||
use RenderReturn::{Aborted, Ready};
|
||||
match (&old, new) {
|
||||
// Normal pathway
|
||||
(Ready(l), Ready(r)) => self.diff_node(l, r),
|
||||
|
||||
match (old, new) {
|
||||
// Normal pathway
|
||||
(Ready(l), Ready(r)) => self.diff_node(l, r),
|
||||
// Unwind the mutations if need be
|
||||
(Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p),
|
||||
|
||||
// Unwind the mutations if need be
|
||||
(Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p),
|
||||
// Just move over the placeholder
|
||||
(Aborted(l), Aborted(r)) => {
|
||||
r.id.set(l.id.get());
|
||||
*r.parent.borrow_mut() = l.parent.borrow().clone();
|
||||
}
|
||||
|
||||
// Just move over the placeholder
|
||||
(Aborted(l), Aborted(r)) => {
|
||||
r.id.set(l.id.get());
|
||||
r.parent.set(l.parent.get())
|
||||
}
|
||||
|
||||
// Placeholder becomes something
|
||||
// We should also clear the error now
|
||||
(Aborted(l), Ready(r)) => self.replace_placeholder(
|
||||
l,
|
||||
[r],
|
||||
l.parent.get().expect("root node should not be none"),
|
||||
),
|
||||
};
|
||||
}
|
||||
// Placeholder becomes something
|
||||
// We should also clear the error now
|
||||
(Aborted(l), Ready(r)) => self.replace_placeholder(
|
||||
l,
|
||||
[r],
|
||||
l.parent.borrow().expect("root node should not be none"),
|
||||
),
|
||||
};
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
}
|
||||
|
||||
fn diff_ok_to_err(&mut self, l: &VNode, p: &VPlaceholder) {
|
||||
let id = self.next_element();
|
||||
p.id.set(Some(id));
|
||||
p.parent.set(l.parent.get());
|
||||
*p.parent.borrow_mut() = l.parent.borrow().clone();
|
||||
self.mutations.push(Mutation::CreatePlaceholder { id });
|
||||
|
||||
let pre_edits = self.mutations.edits.len();
|
||||
|
@ -101,13 +91,7 @@ impl VirtualDom {
|
|||
|
||||
// Copy over the parent
|
||||
{
|
||||
right_template.parent.set(left_template.parent.get());
|
||||
}
|
||||
|
||||
// Update the bubble id pointer
|
||||
right_template.stable_id.set(left_template.stable_id.get());
|
||||
if let Some(bubble_id) = right_template.stable_id.get() {
|
||||
self.set_template(bubble_id, right_template);
|
||||
*right_template.parent.borrow_mut() = left_template.parent.borrow().clone();
|
||||
}
|
||||
|
||||
// If the templates are the same, we don't need to do anything, nor do we want to
|
||||
|
@ -145,7 +129,7 @@ impl VirtualDom {
|
|||
.enumerate()
|
||||
.for_each(|(dyn_node_idx, (left_node, right_node))| {
|
||||
let current_ref = ElementRef {
|
||||
template: right_template.stable_id().unwrap(),
|
||||
element: right_template.clone(),
|
||||
path: ElementPath {
|
||||
path: left_template.template.get().node_paths[dyn_node_idx],
|
||||
},
|
||||
|
@ -175,19 +159,18 @@ impl VirtualDom {
|
|||
(Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right, parent),
|
||||
(Placeholder(left), Placeholder(right)) => {
|
||||
right.id.set(left.id.get());
|
||||
right.parent.set(left.parent.get());
|
||||
*right.parent.borrow_mut() = left.parent.borrow().clone();
|
||||
},
|
||||
(Component(left), Component(right)) => self.diff_vcomponent(left, right, Some(parent)),
|
||||
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, *right, parent),
|
||||
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, right, parent),
|
||||
(Fragment(left), Placeholder(right)) => self.node_to_placeholder(left, right, parent),
|
||||
_ => todo!("This is an usual custom case for dynamic nodes. We don't know how to handle it yet."),
|
||||
};
|
||||
}
|
||||
|
||||
fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
|
||||
let name = unsafe { std::mem::transmute(left_attr.name) };
|
||||
let value: BorrowedAttributeValue = (&right_attr.value).into();
|
||||
let value = unsafe { std::mem::transmute(value) };
|
||||
let name = &left_attr.name;
|
||||
let value = &right_attr.value;
|
||||
self.mutations.push(Mutation::SetAttribute {
|
||||
id: left_attr.mounted_element.get(),
|
||||
ns: right_attr.namespace,
|
||||
|
@ -218,13 +201,13 @@ impl VirtualDom {
|
|||
|
||||
// copy out the box for both
|
||||
let old_scope = &self.scopes[scope_id.0];
|
||||
let old = old_scope.props.as_ref();
|
||||
let new: &dyn AnyProps = right.props.as_ref();
|
||||
let old = old_scope.props.deref();
|
||||
let new: &dyn AnyProps = right.props.deref();
|
||||
|
||||
// If the props are static, then we try to memoize by setting the new with the old
|
||||
// The target scopestate still has the reference to the old props, so there's no need to update anything
|
||||
// This also implicitly drops the new props since they're not used
|
||||
if old.memoize(new) {
|
||||
if old.memoize(new.props()) {
|
||||
tracing::trace!(
|
||||
"Memoized props for component {:#?} ({})",
|
||||
scope_id,
|
||||
|
@ -234,11 +217,11 @@ impl VirtualDom {
|
|||
}
|
||||
|
||||
// First, move over the props from the old to the new, dropping old props in the process
|
||||
self.scopes[scope_id.0].props = new;
|
||||
self.scopes[scope_id.0].props = right.props.clone();
|
||||
|
||||
// Now run the component and diff it
|
||||
self.run_scope(scope_id);
|
||||
self.diff_scope(scope_id);
|
||||
let new = self.run_scope(scope_id);
|
||||
self.diff_scope(scope_id, new);
|
||||
|
||||
self.dirty_scopes.remove(&DirtyScope {
|
||||
height: self.runtime.get_context(scope_id).unwrap().height,
|
||||
|
@ -325,8 +308,10 @@ impl VirtualDom {
|
|||
|
||||
right.id.set(Some(id));
|
||||
if left.value != right.value {
|
||||
let value = unsafe { std::mem::transmute(right.value) };
|
||||
self.mutations.push(Mutation::SetText { id, value });
|
||||
self.mutations.push(Mutation::SetText {
|
||||
id,
|
||||
value: &right.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -824,7 +809,7 @@ impl VirtualDom {
|
|||
let placeholder = self.next_element();
|
||||
|
||||
r.id.set(Some(placeholder));
|
||||
r.parent.set(Some(parent));
|
||||
r.parent.borrow_mut().replace(parent);
|
||||
|
||||
self.mutations
|
||||
.push(Mutation::CreatePlaceholder { id: placeholder });
|
||||
|
@ -862,16 +847,6 @@ impl VirtualDom {
|
|||
// Clean up the roots, assuming we need to generate mutations for these
|
||||
// This is done last in order to preserve Node ID reclaim order (reclaim in reverse order of claim)
|
||||
self.reclaim_roots(node, gen_muts);
|
||||
|
||||
// Clean up the vnode id
|
||||
self.reclaim_vnode_id(node);
|
||||
}
|
||||
|
||||
fn reclaim_vnode_id(&mut self, node: &VNode) {
|
||||
// Clean up the vnode id
|
||||
if let Some(id) = node.stable_id() {
|
||||
self.element_refs.remove(id.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn reclaim_roots(&mut self, node: &VNode, gen_muts: bool) {
|
||||
|
@ -976,10 +951,6 @@ impl VirtualDom {
|
|||
RenderReturn::Aborted(placeholder) => self.remove_placeholder(placeholder, gen_muts),
|
||||
};
|
||||
|
||||
// Restore the props back to the vcomponent in case it gets rendered again
|
||||
let props = self.scopes[scope.0].props.take();
|
||||
*comp.props.borrow_mut() = unsafe { std::mem::transmute(props) };
|
||||
|
||||
// Now drop all the resouces
|
||||
self.drop_scope(scope, false);
|
||||
}
|
||||
|
@ -1019,7 +990,7 @@ impl VirtualDom {
|
|||
pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &VNode) {
|
||||
if let Some(parent) = parent {
|
||||
// assign the parent of the child
|
||||
child.parent.set(Some(parent));
|
||||
child.parent.borrow_mut().replace(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::{
|
||||
scope_context::{consume_context, current_scope_id, schedule_update_any},
|
||||
Element, IntoDynNode, Properties, ScopeId, ScopeState, Template, TemplateAttribute,
|
||||
TemplateNode, VNode,
|
||||
Element, IntoDynNode, Properties, ScopeId, Template, TemplateAttribute, TemplateNode, VNode,
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
|
@ -14,8 +13,9 @@ use std::{
|
|||
};
|
||||
|
||||
/// Provide an error boundary to catch errors from child components
|
||||
pub fn use_error_boundary(cx: &ScopeState) -> &ErrorBoundary {
|
||||
cx.use_hook(|| cx.provide_context(ErrorBoundary::new()))
|
||||
pub fn use_error_boundary() -> ErrorBoundary {
|
||||
// use_hook(|| cx.provide_context(ErrorBoundary::new()))
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// A boundary that will capture any errors from child components
|
||||
|
@ -262,10 +262,11 @@ impl<T> Throw for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ErrorHandler(Box<dyn Fn(CapturedError) -> Element>);
|
||||
#[derive(Clone)]
|
||||
pub struct ErrorHandler(Rc<dyn Fn(CapturedError) -> Element>);
|
||||
impl<F: Fn(CapturedError) -> Element> From<F> for ErrorHandler {
|
||||
fn from(value: F) -> Self {
|
||||
Self(Box::new(value))
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
fn default_handler(error: CapturedError) -> Element {
|
||||
|
@ -284,15 +285,13 @@ fn default_handler(error: CapturedError) -> Element {
|
|||
node_paths: &[&[0u8, 0u8]],
|
||||
attr_paths: &[],
|
||||
};
|
||||
Some(VNode {
|
||||
parent: Default::default(),
|
||||
stable_id: Default::default(),
|
||||
key: None,
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: Vec::with_capacity(1usize).into(),
|
||||
dynamic_nodes: vec![error.to_string().into_dyn_node()],
|
||||
dynamic_attrs: Default::default(),
|
||||
})
|
||||
Some(VNode::new(
|
||||
None,
|
||||
TEMPLATE,
|
||||
Vec::with_capacity(1usize),
|
||||
vec![error.to_string().into_dyn_node()],
|
||||
Default::default(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -417,7 +416,7 @@ impl<
|
|||
::core::default::Default::default()
|
||||
});
|
||||
let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
|
||||
ErrorHandler(Box::new(default_handler))
|
||||
ErrorHandler(Rc::new(default_handler))
|
||||
});
|
||||
ErrorBoundaryProps {
|
||||
children,
|
||||
|
@ -448,27 +447,24 @@ impl<
|
|||
/// They are similar to `try/catch` in JavaScript, but they only catch errors in the tree below them.
|
||||
/// Error boundaries are quick to implement, but it can be useful to individually handle errors in your components to provide a better user experience when you know that an error is likely to occur.
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub fn ErrorBoundary(cx: ErrorBoundaryProps) -> Element {
|
||||
let error_boundary = use_error_boundary(cx);
|
||||
pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
|
||||
let error_boundary = use_error_boundary();
|
||||
match error_boundary.take_error() {
|
||||
Some(error) => cx.render((cx.props.handle_error.0)(error)),
|
||||
Some(error) => (props.handle_error.0)(error),
|
||||
None => Some({
|
||||
let __cx = cx;
|
||||
static TEMPLATE: Template = Template {
|
||||
name: "examples/error_handle.rs:81:17:2342",
|
||||
roots: &[TemplateNode::Dynamic { id: 0usize }],
|
||||
node_paths: &[&[0u8]],
|
||||
attr_paths: &[],
|
||||
};
|
||||
VNode {
|
||||
parent: Default::default(),
|
||||
stable_id: Default::default(),
|
||||
key: None,
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: Vec::with_capacity(1usize).into(),
|
||||
dynamic_nodes: vec![(&cx.props.children).into_dyn_node()],
|
||||
dynamic_attrs: __cx.bump().alloc([]),
|
||||
}
|
||||
VNode::new(
|
||||
None,
|
||||
TEMPLATE,
|
||||
Vec::with_capacity(1usize),
|
||||
vec![(props.children).into_dyn_node()],
|
||||
Default::default(),
|
||||
)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,16 +27,7 @@ use crate::innerlude::*;
|
|||
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub fn Fragment(cx: FragmentProps) -> Element {
|
||||
let children = cx.0.as_ref()?;
|
||||
Some(VNode {
|
||||
key: children.key,
|
||||
parent: children.parent.clone(),
|
||||
stable_id: children.stable_id.clone(),
|
||||
template: children.template.clone(),
|
||||
root_ids: children.root_ids.clone(),
|
||||
dynamic_nodes: children.dynamic_nodes,
|
||||
dynamic_attrs: children.dynamic_attrs,
|
||||
})
|
||||
cx.0.clone()
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
|
|
|
@ -73,10 +73,10 @@ pub(crate) mod innerlude {
|
|||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue,
|
||||
CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode,
|
||||
Mutation, Mutations, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template,
|
||||
TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom,
|
||||
fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeValue, CapturedError,
|
||||
Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, Mutation, Mutations,
|
||||
Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template, TemplateAttribute,
|
||||
TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template};
|
||||
use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
|
||||
|
||||
/// A container for all the relevant steps to modify the Real DOM
|
||||
///
|
||||
|
@ -13,7 +13,6 @@ use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Templa
|
|||
/// Templates, however, apply to all subtrees, not just target subtree.
|
||||
///
|
||||
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[derive(Debug, Default)]
|
||||
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
|
||||
pub struct Mutations<'a> {
|
||||
|
@ -56,11 +55,6 @@ impl<'a> Mutations<'a> {
|
|||
/// of the Dioxus VirtualDom.
|
||||
///
|
||||
/// These edits can be serialized and sent over the network or through any interface
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(tag = "type")
|
||||
)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Mutation<'a> {
|
||||
/// Add these m children to the target element
|
||||
|
@ -195,7 +189,7 @@ pub enum Mutation<'a> {
|
|||
name: &'a str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: BorrowedAttributeValue<'a>,
|
||||
value: &'a AttributeValue,
|
||||
|
||||
/// The ID of the node to set the attribute of.
|
||||
id: ElementId,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use crate::innerlude::{ElementRef, VNodeId};
|
||||
use crate::{any_props::AnyProps, arena::ElementId, Element, Event, ScopeId, ScopeState};
|
||||
use crate::any_props::BoxedAnyProps;
|
||||
use crate::innerlude::ElementRef;
|
||||
use crate::{arena::ElementId, Element, Event, ScopeId};
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
|
@ -15,6 +18,7 @@ pub type TemplateId = &'static str;
|
|||
///
|
||||
/// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
|
||||
/// you might need to handle the case where there's no node immediately ready.
|
||||
#[derive(Clone)]
|
||||
pub enum RenderReturn {
|
||||
/// A currently-available element
|
||||
Ready(VNode),
|
||||
|
@ -36,26 +40,23 @@ impl Default for RenderReturn {
|
|||
///
|
||||
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
|
||||
/// static parts of the template.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VNode {
|
||||
#[derive(Debug)]
|
||||
pub struct VNodeInner {
|
||||
/// The key given to the root of this template.
|
||||
///
|
||||
/// In fragments, this is the key of the first child. In other cases, it is the key of the root.
|
||||
pub key: Option<String>,
|
||||
|
||||
/// When rendered, this template will be linked to its parent manually
|
||||
pub(crate) parent: Cell<Option<ElementRef>>,
|
||||
|
||||
/// The bubble id assigned to the child that we need to update and drop when diffing happens
|
||||
pub(crate) stable_id: Cell<Option<VNodeId>>,
|
||||
|
||||
/// The static nodes and static descriptor of the template
|
||||
pub template: Cell<Template<'static>>,
|
||||
pub(crate) parent: RefCell<Option<ElementRef>>,
|
||||
|
||||
/// The IDs for the roots of this template - to be used when moving the template around and removing it from
|
||||
/// the actual Dom
|
||||
pub root_ids: RefCell<Vec<ElementId>>,
|
||||
|
||||
/// The static nodes and static descriptor of the template
|
||||
pub template: Cell<Template<'static>>,
|
||||
|
||||
/// The dynamic parts of the template
|
||||
pub dynamic_nodes: Vec<DynamicNode>,
|
||||
|
||||
|
@ -63,13 +64,33 @@ pub struct VNode {
|
|||
pub dynamic_attrs: Vec<Attribute>,
|
||||
}
|
||||
|
||||
/// A reference to a template along with any context needed to hydrate it
|
||||
///
|
||||
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
|
||||
/// static parts of the template.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VNode(Rc<VNodeInner>);
|
||||
|
||||
impl PartialEq for VNode {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for VNode {
|
||||
type Target = VNodeInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl VNode {
|
||||
/// Create a template with no nodes that will be skipped over during diffing
|
||||
pub fn empty() -> Element {
|
||||
Some(VNode {
|
||||
Some(Self(Rc::new(VNodeInner {
|
||||
key: None,
|
||||
parent: Default::default(),
|
||||
stable_id: Default::default(),
|
||||
root_ids: Default::default(),
|
||||
dynamic_nodes: Vec::new(),
|
||||
dynamic_attrs: Vec::new(),
|
||||
|
@ -79,7 +100,7 @@ impl VNode {
|
|||
node_paths: &[],
|
||||
attr_paths: &[],
|
||||
}),
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a new VNode
|
||||
|
@ -90,20 +111,14 @@ impl VNode {
|
|||
dynamic_nodes: Vec<DynamicNode>,
|
||||
dynamic_attrs: Vec<Attribute>,
|
||||
) -> Self {
|
||||
Self {
|
||||
Self(Rc::new(VNodeInner {
|
||||
key,
|
||||
parent: Cell::new(None),
|
||||
stable_id: Cell::new(None),
|
||||
parent: Default::default(),
|
||||
template: Cell::new(template),
|
||||
root_ids: RefCell::new(root_ids),
|
||||
dynamic_nodes,
|
||||
dynamic_attrs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the stable id of this node used for bubbling events
|
||||
pub(crate) fn stable_id(&self) -> Option<VNodeId> {
|
||||
self.stable_id.get()
|
||||
}))
|
||||
}
|
||||
|
||||
/// Load a dynamic root at the given index
|
||||
|
@ -293,7 +308,7 @@ pub enum TemplateNode {
|
|||
/// A node created at runtime
|
||||
///
|
||||
/// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum DynamicNode {
|
||||
/// A component node
|
||||
///
|
||||
|
@ -355,7 +370,6 @@ impl<'a> std::fmt::Debug for VComponent {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("VComponent")
|
||||
.field("name", &self.name)
|
||||
.field("static_props", &self.static_props)
|
||||
.field("scope", &self.scope)
|
||||
.finish()
|
||||
}
|
||||
|
@ -387,12 +401,12 @@ impl<'a> VText {
|
|||
}
|
||||
|
||||
/// A placeholder node, used by suspense and fragments
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct VPlaceholder {
|
||||
/// The ID of this node in the real DOM
|
||||
pub(crate) id: Cell<Option<ElementId>>,
|
||||
/// The parent of this node
|
||||
pub(crate) parent: Cell<Option<ElementRef>>,
|
||||
pub(crate) parent: RefCell<Option<ElementRef>>,
|
||||
}
|
||||
|
||||
impl VPlaceholder {
|
||||
|
@ -436,7 +450,7 @@ pub enum TemplateAttribute {
|
|||
}
|
||||
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Attribute {
|
||||
/// The name of the attribute.
|
||||
pub name: &'static str,
|
||||
|
@ -483,7 +497,6 @@ impl Attribute {
|
|||
///
|
||||
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
||||
/// variant.
|
||||
#[derive(Clone)]
|
||||
pub enum AttributeValue {
|
||||
/// Text attribute
|
||||
Text(String),
|
||||
|
@ -498,7 +511,7 @@ pub enum AttributeValue {
|
|||
Bool(bool),
|
||||
|
||||
/// A listener, like "onclick"
|
||||
Listener(ListenerCb),
|
||||
Listener(RefCell<ListenerCb>),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
Any(Box<dyn AnyValue>),
|
||||
|
@ -509,85 +522,6 @@ pub enum AttributeValue {
|
|||
|
||||
pub type ListenerCb = Box<dyn FnMut(Event<dyn Any>)>;
|
||||
|
||||
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
|
||||
///
|
||||
/// These varients are used to communicate what the value of an attribute is that needs to be updated
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serialize", serde(untagged))]
|
||||
pub enum BorrowedAttributeValue<'a> {
|
||||
/// Text attribute
|
||||
Text(&'a str),
|
||||
|
||||
/// A float
|
||||
Float(f64),
|
||||
|
||||
/// Signed integer
|
||||
Int(i64),
|
||||
|
||||
/// Boolean
|
||||
Bool(bool),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
serde(
|
||||
deserialize_with = "deserialize_any_value",
|
||||
serialize_with = "serialize_any_value"
|
||||
)
|
||||
)]
|
||||
Any(std::cell::Ref<'a, dyn AnyValue>),
|
||||
|
||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||
None,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a AttributeValue> for BorrowedAttributeValue<'a> {
|
||||
fn from(value: &'a AttributeValue) -> Self {
|
||||
match value {
|
||||
AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
|
||||
AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
|
||||
AttributeValue::Int(value) => BorrowedAttributeValue::Int(*value),
|
||||
AttributeValue::Bool(value) => BorrowedAttributeValue::Bool(*value),
|
||||
AttributeValue::Listener(_) => {
|
||||
panic!("A listener cannot be turned into a borrowed value")
|
||||
}
|
||||
AttributeValue::Any(value) => {
|
||||
let value = value.borrow();
|
||||
BorrowedAttributeValue::Any(std::cell::Ref::map(value, |value| {
|
||||
&**value.as_ref().unwrap()
|
||||
}))
|
||||
}
|
||||
AttributeValue::None => BorrowedAttributeValue::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BorrowedAttributeValue<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
|
||||
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
||||
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
||||
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||
Self::Any(_) => f.debug_tuple("Any").field(&"...").finish(),
|
||||
Self::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BorrowedAttributeValue<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
|
||||
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
|
||||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn serialize_any_value<S>(_: &std::cell::Ref<'_, dyn AnyValue>, _: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -626,11 +560,7 @@ impl PartialEq for AttributeValue {
|
|||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
(Self::Listener(_), Self::Listener(_)) => true,
|
||||
(Self::Any(l0), Self::Any(r0)) => {
|
||||
let l0 = l0.borrow();
|
||||
let r0 = r0.borrow();
|
||||
l0.as_ref().unwrap().any_cmp(&**r0.as_ref().unwrap())
|
||||
}
|
||||
(Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -820,7 +750,7 @@ impl<'a> IntoAttributeValue for Arguments<'_> {
|
|||
|
||||
impl IntoAttributeValue for Box<dyn AnyValue> {
|
||||
fn into_value(self) -> AttributeValue {
|
||||
AttributeValue::Any(RefCell::new(Some(self)))
|
||||
AttributeValue::Any(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
any_props::AnyProps,
|
||||
any_props::{AnyProps, BoxedAnyProps},
|
||||
innerlude::DirtyScope,
|
||||
nodes::RenderReturn,
|
||||
scope_context::ScopeContext,
|
||||
|
@ -8,11 +8,7 @@ use crate::{
|
|||
};
|
||||
|
||||
impl VirtualDom {
|
||||
pub(super) fn new_scope(
|
||||
&mut self,
|
||||
props: Box<dyn AnyProps>,
|
||||
name: &'static str,
|
||||
) -> &ScopeState {
|
||||
pub(super) fn new_scope(&mut self, props: BoxedAnyProps, name: &'static str) -> &ScopeState {
|
||||
let parent_id = self.runtime.current_scope_id();
|
||||
let height = parent_id
|
||||
.and_then(|parent_id| self.get_scope(parent_id).map(|f| f.context().height + 1))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::{
|
||||
any_props::AnyProps,
|
||||
any_props::VProps,
|
||||
any_props::{BoxedAnyProps, VProps},
|
||||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
runtime::Runtime,
|
||||
|
@ -49,7 +48,7 @@ pub struct ScopeState {
|
|||
|
||||
pub(crate) last_rendered_node: Option<RenderReturn>,
|
||||
|
||||
pub(crate) props: Box<dyn AnyProps>,
|
||||
pub(crate) props: BoxedAnyProps,
|
||||
}
|
||||
|
||||
impl Drop for ScopeState {
|
||||
|
@ -300,16 +299,12 @@ impl<'src> ScopeState {
|
|||
// The properties must be valid until the next bump frame
|
||||
P: Properties,
|
||||
{
|
||||
let vcomp = VProps::new(component, P::memoize, props);
|
||||
|
||||
// cast off the lifetime of the render return
|
||||
let as_dyn: Box<dyn AnyProps> = Box::new(vcomp);
|
||||
let extended: Box<dyn AnyProps> = unsafe { std::mem::transmute(as_dyn) };
|
||||
let vcomp = VProps::new(component, P::memoize, props, fn_name);
|
||||
|
||||
DynamicNode::Component(VComponent {
|
||||
name: fn_name,
|
||||
render_fn: component as *const (),
|
||||
props: extended,
|
||||
props: BoxedAnyProps::new(vcomp),
|
||||
scope: Default::default(),
|
||||
})
|
||||
}
|
||||
|
@ -332,14 +327,14 @@ impl<'src> ScopeState {
|
|||
&'src self,
|
||||
mut callback: impl FnMut(Event<T>) + 'src,
|
||||
) -> AttributeValue {
|
||||
AttributeValue::Listener(Box::new(move |event: Event<dyn Any>| {
|
||||
AttributeValue::Listener(RefCell::new(Box::new(move |event: Event<dyn Any>| {
|
||||
if let Ok(data) = event.data.downcast::<T>() {
|
||||
callback(Event {
|
||||
propagates: event.propagates,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
|
||||
|
||||
use crate::{
|
||||
any_props::VProps,
|
||||
any_props::{BoxedAnyProps, VProps},
|
||||
arena::ElementId,
|
||||
innerlude::{DirtyScope, ElementRef, ErrorBoundary, Mutations, Scheduler, SchedulerMsg},
|
||||
mutations::Mutation,
|
||||
|
@ -11,14 +11,12 @@ use crate::{
|
|||
nodes::{Template, TemplateId},
|
||||
runtime::{Runtime, RuntimeGuard},
|
||||
scopes::{ScopeId, ScopeState},
|
||||
AttributeValue, Element, Event, VNode,
|
||||
AttributeValue, Element, Event,
|
||||
};
|
||||
use futures_util::{pin_mut, StreamExt};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use slab::Slab;
|
||||
use std::{
|
||||
any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc,
|
||||
};
|
||||
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc, sync::Arc};
|
||||
|
||||
/// A virtual node system that progresses user events and diffs UI trees.
|
||||
///
|
||||
|
@ -187,9 +185,6 @@ pub struct VirtualDom {
|
|||
// Maps a template path to a map of byteindexes to templates
|
||||
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
|
||||
|
||||
// Every element is actually a dual reference - one to the template and the other to the dynamic node in that template
|
||||
pub(crate) element_refs: Slab<Option<NonNull<VNode>>>,
|
||||
|
||||
// The element ids that are used in the renderer
|
||||
pub(crate) elements: Slab<Option<ElementRef>>,
|
||||
|
||||
|
@ -268,13 +263,12 @@ impl VirtualDom {
|
|||
dirty_scopes: Default::default(),
|
||||
templates: Default::default(),
|
||||
elements: Default::default(),
|
||||
element_refs: Default::default(),
|
||||
mutations: Mutations::default(),
|
||||
suspended_scopes: Default::default(),
|
||||
};
|
||||
|
||||
let root = dom.new_scope(
|
||||
Box::new(VProps::new(root, |_, _| unreachable!(), root_props)),
|
||||
BoxedAnyProps::new(VProps::new(root, |_, _| unreachable!(), root_props, "root")),
|
||||
"app",
|
||||
);
|
||||
|
||||
|
@ -363,14 +357,10 @@ impl VirtualDom {
|
|||
| <-- no, broke early
|
||||
*/
|
||||
let parent_path = match self.elements.get(element.0) {
|
||||
Some(Some(el)) => el,
|
||||
Some(Some(el)) => el.clone(),
|
||||
_ => return,
|
||||
};
|
||||
let mut parent_node = self
|
||||
.element_refs
|
||||
.get(parent_path.template.0)
|
||||
.cloned()
|
||||
.map(|el| (*parent_path, el));
|
||||
let mut parent_node = Some(parent_path);
|
||||
let mut listeners = vec![];
|
||||
|
||||
// We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
|
||||
|
@ -382,13 +372,12 @@ impl VirtualDom {
|
|||
// If the event bubbles, we traverse through the tree until we find the target element.
|
||||
if bubbles {
|
||||
// Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent.
|
||||
while let Some((path, el_ref)) = parent_node {
|
||||
// safety: we maintain references of all vnodes in the element slab
|
||||
let template = unsafe { el_ref.unwrap().as_ref() };
|
||||
let node_template = template.template.get();
|
||||
while let Some(path) = parent_node {
|
||||
let el_ref = &path.element;
|
||||
let node_template = el_ref.template.get();
|
||||
let target_path = path.path;
|
||||
|
||||
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
|
||||
for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
|
@ -413,9 +402,7 @@ impl VirtualDom {
|
|||
let origin = path.scope;
|
||||
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
self.runtime.rendering.set(false);
|
||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
cb(uievent.clone());
|
||||
}
|
||||
(listener.borrow_mut())(uievent.clone());
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
|
@ -425,22 +412,16 @@ impl VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
parent_node = template.parent.get().and_then(|element_ref| {
|
||||
self.element_refs
|
||||
.get(element_ref.template.0)
|
||||
.cloned()
|
||||
.map(|el| (element_ref, el))
|
||||
});
|
||||
parent_node = el_ref.parent.borrow().clone();
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we just call the listener on the target element
|
||||
if let Some((path, el_ref)) = parent_node {
|
||||
// safety: we maintain references of all vnodes in the element slab
|
||||
let template = unsafe { el_ref.unwrap().as_ref() };
|
||||
let node_template = template.template.get();
|
||||
if let Some(path) = parent_node {
|
||||
let el_ref = &path.element;
|
||||
let node_template = el_ref.template.get();
|
||||
let target_path = path.path;
|
||||
|
||||
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
|
||||
for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
|
@ -450,9 +431,7 @@ impl VirtualDom {
|
|||
let origin = path.scope;
|
||||
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
self.runtime.rendering.set(false);
|
||||
if let Some(cb) = listener.as_deref_mut() {
|
||||
cb(uievent.clone());
|
||||
}
|
||||
(listener.borrow_mut())(uievent.clone());
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
|
@ -570,7 +549,7 @@ impl VirtualDom {
|
|||
match unsafe { self.run_scope(ScopeId::ROOT) } {
|
||||
// Rebuilding implies we append the created elements to the root
|
||||
RenderReturn::Ready(node) => {
|
||||
let m = self.create_scope(ScopeId::ROOT, node);
|
||||
let m = self.create_scope(ScopeId::ROOT, &node);
|
||||
self.mutations.edits.push(Mutation::AppendChildren {
|
||||
id: ElementId(0),
|
||||
m,
|
||||
|
@ -645,8 +624,8 @@ impl VirtualDom {
|
|||
{
|
||||
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
||||
// Run the scope and get the mutations
|
||||
self.run_scope(dirty.id);
|
||||
self.diff_scope(dirty.id);
|
||||
let new_nodes = self.run_scope(dirty.id);
|
||||
self.diff_scope(dirty.id, new_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue