From 51dba50c04fdb7b9d39499e4c14aabf8feed5bdb Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 2 Jan 2023 14:40:25 -0600 Subject: [PATCH] use BumpBox and FromAnyValue instead of sync bounds --- packages/core/Cargo.toml | 4 +- packages/core/src/arena.rs | 6 +- packages/core/src/create.rs | 7 +- packages/core/src/diff.rs | 8 +- packages/core/src/lib.rs | 8 +- packages/core/src/mutations.rs | 4 +- packages/core/src/nodes.rs | 205 ++++++++---------- packages/core/src/scopes.rs | 4 +- packages/core/src/virtual_dom.rs | 2 +- packages/core/tests/attr_cleanup.rs | 15 +- packages/core/tests/boolattrs.rs | 3 +- packages/core/tests/kitchen_sink.rs | 2 +- .../native-core-macro/tests/update_state.rs | 1 - packages/native-core/Cargo.toml | 5 - packages/native-core/src/node.rs | 64 +++--- packages/native-core/src/node_ref.rs | 14 +- packages/native-core/src/state.rs | 14 +- packages/native-core/src/tree.rs | 1 - .../src/utils/persistant_iterator.rs | 2 +- packages/web/src/dom.rs | 15 +- 20 files changed, 184 insertions(+), 200 deletions(-) diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 5bf348b33..7158b3970 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -44,6 +44,4 @@ pretty_assertions = "1.3.0" [features] default = [] -serialize = ["serde"] -# Adds a Sync + Send bound on any values in attributes -sync_attributes = [] \ No newline at end of file +serialize = ["serde"] \ No newline at end of file diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index aef8e8b2d..e5440fd85 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -158,10 +158,10 @@ impl VirtualDom { let listener = unsafe { &*listener }; match &listener.value { AttributeValue::Listener(l) => { - _ = l.0.take(); + _ = l.take(); } - AttributeValue::Any(a) => { - _ = a.take(); + AttributeValue::Any(_) => { + todo!() } _ => (), } diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index 30ee00f41..7e5633f12 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -1,5 +1,5 @@ use crate::any_props::AnyProps; -use crate::innerlude::{VComponent, VPlaceholder, VText}; +use crate::innerlude::{BorrowedAttributeValue, VComponent, VPlaceholder, VText}; use crate::mutations::Mutation; use crate::mutations::Mutation::*; use crate::nodes::VNode; @@ -285,7 +285,7 @@ impl<'b> VirtualDom { } } - fn write_attribute(&mut self, attribute: &crate::Attribute, id: ElementId) { + fn write_attribute(&mut self, attribute: &'b crate::Attribute<'b>, id: ElementId) { // Make sure we set the attribute's associated id attribute.mounted_element.set(id); @@ -302,7 +302,8 @@ impl<'b> VirtualDom { } _ => { // Safety: we promise not to re-alias this text later on after committing it to the mutation - let unbounded_value = unsafe { std::mem::transmute(attribute.value.clone()) }; + let value: BorrowedAttributeValue<'b> = (&attribute.value).into(); + let unbounded_value = unsafe { std::mem::transmute(value) }; self.mutations.push(SetAttribute { name: unbounded_name, diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index f055fb650..7227ee6e7 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -1,7 +1,7 @@ use crate::{ any_props::AnyProps, arena::ElementId, - innerlude::{DirtyScope, VComponent, VPlaceholder, VText}, + innerlude::{BorrowedAttributeValue, DirtyScope, VComponent, VPlaceholder, VText}, mutations::Mutation, nodes::RenderReturn, nodes::{DynamicNode, VNode}, @@ -150,10 +150,10 @@ impl<'b> VirtualDom { }; } - fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) { - // todo: add more types of attribute values + fn update_attribute(&mut self, right_attr: &'b Attribute<'b>, left_attr: &'b Attribute) { let name = unsafe { std::mem::transmute(left_attr.name) }; - let value = unsafe { std::mem::transmute(right_attr.value.clone()) }; + let value: BorrowedAttributeValue<'b> = (&right_attr.value).into(); + let value = unsafe { std::mem::transmute(value) }; self.mutations.push(Mutation::SetAttribute { id: left_attr.mounted_element.get(), ns: right_attr.namespace, diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index b6fe456bc..4e26f5a56 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -70,10 +70,10 @@ pub(crate) mod innerlude { } pub use crate::innerlude::{ - fc_to_builder, AnyValueContainer, Attribute, AttributeValue, CapturedError, Component, - DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, - Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, - Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom, + fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, CapturedError, + Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, + Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, + TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom, }; /// The purpose of this module is to alleviate imports of many common types diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index 06116f5dc..b02e89581 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -1,6 +1,6 @@ use rustc_hash::FxHashSet; -use crate::{arena::ElementId, AttributeValue, ScopeId, Template}; +use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template}; /// A container for all the relevant steps to modify the Real DOM /// @@ -208,7 +208,7 @@ pub enum Mutation<'a> { name: &'a str, /// The value of the attribute. - value: AttributeValue<'a>, + value: BorrowedAttributeValue<'a>, /// The ID of the node to set the attribute of. id: ElementId, diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 0c797460a..d17863c20 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -6,9 +6,8 @@ use bumpalo::Bump; use std::{ any::{Any, TypeId}, cell::{Cell, RefCell, UnsafeCell}, - fmt::Arguments, + fmt::{Arguments, Debug}, future::Future, - ops::Deref, }; pub type TemplateId = &'static str; @@ -200,7 +199,7 @@ impl<'a> VNode<'a> { pub(crate) fn clear_listeners(&self) { for attr in self.dynamic_attrs { if let AttributeValue::Listener(l) = &attr.value { - l.0.borrow_mut().take(); + l.borrow_mut().take(); } } } @@ -507,9 +506,6 @@ pub struct Attribute<'a> { /// /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`] /// variant. -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serialize", serde(untagged))] -#[derive(Clone)] pub enum AttributeValue<'a> { /// Text attribute Text(&'a str), @@ -524,107 +520,105 @@ pub enum AttributeValue<'a> { Bool(bool), /// A listener, like "onclick" - Listener(ListenerCb<'a>), + Listener(RefCell>>), /// An arbitrary value that implements PartialEq and is static - Any(RefCell>), + Any(BumpBox<'a, dyn AnyValue>), /// A "none" value, resulting in the removal of an attribute from the dom None, } -pub type ListenerCbInner<'a> = RefCell) + 'a>>>; -pub struct ListenerCb<'a>(pub ListenerCbInner<'a>); +pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event) + 'a>; -impl Clone for ListenerCb<'_> { - fn clone(&self) -> Self { - panic!("ListenerCb cannot be cloned") +/// 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(&'a dyn AnyValue), + + /// A "none" value, resulting in the removal of an attribute from the dom + None, +} + +impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> { + fn from(value: &'a AttributeValue<'a>) -> 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) => BorrowedAttributeValue::Any(&**value), + AttributeValue::None => BorrowedAttributeValue::None, + } } } -#[cfg(feature = "serialize")] -impl<'a> serde::Serialize for ListenerCb<'a> { - fn serialize(&self, _: S) -> Result - where - S: serde::Serializer, - { - panic!("ListenerCb cannot be serialized") +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"), + } } } -#[cfg(feature = "serialize")] -impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> { - fn deserialize(_: D) -> Result - where - D: serde::Deserializer<'de>, - { - panic!("ListenerCb cannot be deserialized") - } -} - -#[derive(Clone)] -#[cfg(not(feature = "sync_attributes"))] -/// A boxed value that implements PartialEq, and Any -pub struct AnyValueContainer(pub std::rc::Rc); - -#[derive(Clone)] -#[cfg(feature = "sync_attributes")] -/// A boxed value that implements PartialEq, Any, Sync, and Send -pub struct AnyValueContainer(pub std::sync::Arc); - -impl PartialEq for AnyValueContainer { +impl PartialEq for BorrowedAttributeValue<'_> { fn eq(&self, other: &Self) -> bool { - self.0.any_cmp(other.0.as_ref()) - } -} - -impl AnyValueContainer { - /// Create a new AnyValueContainer containing the specified data. - pub fn new(value: T) -> Self { - #[cfg(feature = "sync_attributes")] - return Self(std::sync::Arc::new(value)); - #[cfg(not(feature = "sync_attributes"))] - return Self(std::rc::Rc::new(value)); - } - - /// Returns a reference to the inner value. - pub fn downcast_ref(&self) -> Option<&T> { - self.0.deref().as_any().downcast_ref() - } - - /// Checks if the type of the inner value is 'T'. - pub fn is(&self) -> bool { - self.0.deref().as_any().is::() - } -} - -#[test] -fn test_any_value_rc() { - let a = AnyValueContainer::new(1i32); - assert_eq!(a.downcast_ref::(), Some(&1i32)); - assert_eq!(a.downcast_ref::(), None); - assert!(a.is::()); - assert!(!a.is::()); -} - -#[cfg(feature = "serialize")] -impl serde::Serialize for AnyValueContainer { - fn serialize(&self, _: S) -> Result - where - S: serde::Serializer, - { - panic!("AnyValueBox cannot be serialized") + 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")] -impl<'de> serde::Deserialize<'de> for AnyValueContainer { - fn deserialize(_: D) -> Result - where - D: serde::Deserializer<'de>, - { - panic!("AnyValueBox cannot be deserialized") - } +fn serialize_any_value(_: &&dyn AnyValue, _: S) -> Result +where + S: serde::Serializer, +{ + panic!("Any cannot be serialized") +} + +#[cfg(feature = "serialize")] +fn deserialize_any_value<'de, 'a, D>(_: D) -> Result<&'a dyn AnyValue, D::Error> +where + D: serde::Deserializer<'de>, +{ + panic!("Any cannot be deserialized") } impl<'a> std::fmt::Debug for AttributeValue<'a> { @@ -649,41 +643,13 @@ impl<'a> PartialEq for AttributeValue<'a> { (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)) => l0 == r0, + (Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0), _ => false, } } } -// Some renderers need attributes to be sync and send. The rest of the attributes are already sync and send, but there is no way to force Any values to be sync and send on the renderer side. -// The sync_attributes flag restricts any valuse to be sync and send. #[doc(hidden)] -#[cfg(feature = "sync_attributes")] -pub trait AnyValue: Sync + Send + 'static { - fn any_cmp(&self, other: &dyn AnyValue) -> bool; - fn as_any(&self) -> &dyn Any; - fn type_id(&self) -> TypeId { - self.as_any().type_id() - } -} - -#[cfg(feature = "sync_attributes")] -impl AnyValue for T { - fn any_cmp(&self, other: &dyn AnyValue) -> bool { - if let Some(other) = other.as_any().downcast_ref() { - self == other - } else { - false - } - } - - fn as_any(&self) -> &dyn Any { - self - } -} - -#[doc(hidden)] -#[cfg(not(feature = "sync_attributes"))] pub trait AnyValue: 'static { fn any_cmp(&self, other: &dyn AnyValue) -> bool; fn as_any(&self) -> &dyn Any; @@ -692,7 +658,6 @@ pub trait AnyValue: 'static { } } -#[cfg(not(feature = "sync_attributes"))] impl AnyValue for T { fn any_cmp(&self, other: &dyn AnyValue) -> bool { if let Some(other) = other.as_any().downcast_ref() { @@ -903,8 +868,8 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> { } } -impl<'a> IntoAttributeValue<'a> for AnyValueContainer { +impl<'a> IntoAttributeValue<'a> for BumpBox<'a, dyn AnyValue> { fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { - AttributeValue::Any(RefCell::new(Some(self))) + AttributeValue::Any(self) } } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 20be78cce..d07b52704 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -4,7 +4,7 @@ use crate::{ arena::ElementId, bump_frame::BumpFrame, innerlude::{DynamicNode, EventHandler, VComponent, VText}, - innerlude::{ErrorBoundary, ListenerCb, Scheduler, SchedulerMsg}, + innerlude::{ErrorBoundary, Scheduler, SchedulerMsg}, lazynodes::LazyNodes, nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn}, Attribute, AttributeValue, Element, Event, Properties, TaskId, @@ -510,7 +510,7 @@ impl<'src> ScopeState { })) }; - AttributeValue::Listener(ListenerCb(RefCell::new(Some(boxed)))) + AttributeValue::Listener(RefCell::new(Some(boxed))) } /// Inject an error into the nearest error boundary and quit rendering diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 56363788f..043e7fafb 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -384,7 +384,7 @@ impl VirtualDom { // We check the bubble state between each call to see if the event has been stopped from bubbling for listener in listeners.drain(..).rev() { if let AttributeValue::Listener(listener) = listener { - if let Some(cb) = listener.0.borrow_mut().as_deref_mut() { + if let Some(cb) = listener.borrow_mut().as_deref_mut() { cb(uievent.clone()); } diff --git a/packages/core/tests/attr_cleanup.rs b/packages/core/tests/attr_cleanup.rs index fb67d0527..2e3cf5d17 100644 --- a/packages/core/tests/attr_cleanup.rs +++ b/packages/core/tests/attr_cleanup.rs @@ -5,7 +5,7 @@ use bumpalo::Bump; use dioxus::core::{ElementId, Mutation::*}; use dioxus::prelude::*; -use dioxus_core::AttributeValue; +use dioxus_core::{AttributeValue, BorrowedAttributeValue}; #[test] fn attrs_cycle() { @@ -42,11 +42,16 @@ fn attrs_cycle() { AssignId { path: &[0,], id: ElementId(3,) }, SetAttribute { name: "class", - value: "1".into_value(&bump), + value: (&*bump.alloc("1".into_value(&bump))).into(), + id: ElementId(3,), + ns: None + }, + SetAttribute { + name: "id", + value: (&*bump.alloc("1".into_value(&bump))).into(), id: ElementId(3,), ns: None }, - SetAttribute { name: "id", value: "1".into_value(&bump), id: ElementId(3,), ns: None }, ReplaceWith { id: ElementId(1,), m: 1 }, ] ); @@ -68,13 +73,13 @@ fn attrs_cycle() { AssignId { path: &[0], id: ElementId(3) }, SetAttribute { name: "class", - value: AttributeValue::Text("3"), + value: BorrowedAttributeValue::Text("3"), id: ElementId(3), ns: None }, SetAttribute { name: "id", - value: AttributeValue::Text("3"), + value: BorrowedAttributeValue::Text("3"), id: ElementId(3), ns: None }, diff --git a/packages/core/tests/boolattrs.rs b/packages/core/tests/boolattrs.rs index c2ad70333..0d02bf8af 100644 --- a/packages/core/tests/boolattrs.rs +++ b/packages/core/tests/boolattrs.rs @@ -6,13 +6,14 @@ use dioxus::prelude::*; fn bool_test() { let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false }))); let bump = Bump::new(); + assert_eq!( app.rebuild().santize().edits, [ LoadTemplate { name: "template", index: 0, id: ElementId(1) }, SetAttribute { name: "hidden", - value: false.into_value(&bump), + value: (&*bump.alloc(false.into_value(&bump))).into(), id: ElementId(1,), ns: None }, diff --git a/packages/core/tests/kitchen_sink.rs b/packages/core/tests/kitchen_sink.rs index 22ed1f26d..191d1da7c 100644 --- a/packages/core/tests/kitchen_sink.rs +++ b/packages/core/tests/kitchen_sink.rs @@ -36,7 +36,7 @@ fn dual_stream() { LoadTemplate { name: "template", index: 0, id: ElementId(1) }, SetAttribute { name: "class", - value: "123".into_value(&bump), + value: (&*bump.alloc("123".into_value(&bump))).into(), id: ElementId(1), ns: None, }, diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs index e2f04c72c..fc36c42ed 100644 --- a/packages/native-core-macro/tests/update_state.rs +++ b/packages/native-core-macro/tests/update_state.rs @@ -43,7 +43,6 @@ impl ParentDepState for ParentDepCallCounter { const NODE_MASK: NodeMask = NodeMask::ALL; fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool { self.0 += 1; - println!("ParentDepCallCounter::reduce on {:?}\n{}", _node, self.0); true } } diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 5cc25b61c..9958b8a02 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -27,8 +27,3 @@ dashmap = "5.4.0" [dev-dependencies] rand = "0.8.5" dioxus = { path = "../dioxus", version = "^0.2.1" } - -[features] -default = ["parallel"] -# This makes core only accept sync and send any values for attributes to allow for parallel resolution of passes. -parallel = ["dioxus-core/sync_attributes"] \ No newline at end of file diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index 99777dc27..3b91cee45 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -1,44 +1,44 @@ use crate::{state::State, tree::NodeId}; -use dioxus_core::{AnyValueContainer, AttributeValue, ElementId}; +use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId}; use rustc_hash::{FxHashMap, FxHashSet}; use std::fmt::Debug; /// The node is stored client side and stores only basic data about the node. #[derive(Debug, Clone)] -pub struct Node { +pub struct Node { /// The transformed state of the node. pub state: S, /// The raw data for the node - pub node_data: NodeData, + pub node_data: NodeData, } #[derive(Debug, Clone)] -pub struct NodeData { +pub struct NodeData { /// The id of the node pub node_id: NodeId, /// The id of the node in the vdom. pub element_id: Option, /// Additional inforation specific to the node type - pub node_type: NodeType, + pub node_type: NodeType, } /// A type of node with data specific to the node type. The types are a subset of the [VNode] types. #[derive(Debug, Clone)] -pub enum NodeType { +pub enum NodeType { Text { text: String, }, Element { tag: String, namespace: Option, - attributes: FxHashMap, + attributes: FxHashMap>, listeners: FxHashSet, }, Placeholder, } -impl Node { - pub(crate) fn new(node_type: NodeType) -> Self { +impl Node { + pub(crate) fn new(node_type: NodeType) -> Self { Node { state: S::default(), node_data: NodeData { @@ -65,52 +65,59 @@ pub struct OwnedAttributeDiscription { /// An attribute on a DOM node, such as `id="my-thing"` or /// `href="https://example.com"`. #[derive(Clone, Copy, Debug)] -pub struct OwnedAttributeView<'a> { +pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> { /// The discription of the attribute. pub attribute: &'a OwnedAttributeDiscription, /// The value of the attribute. - pub value: &'a OwnedAttributeValue, + pub value: &'a OwnedAttributeValue, } #[derive(Clone)] -pub enum OwnedAttributeValue { +pub enum OwnedAttributeValue { Text(String), Float(f64), Int(i64), Bool(bool), - Any(AnyValueContainer), + Custom(V), None, } -impl Debug for OwnedAttributeValue { +pub trait FromAnyValue { + fn from_any_value(value: &dyn AnyValue) -> Self; +} + +impl FromAnyValue for () { + fn from_any_value(_: &dyn AnyValue) -> Self {} +} + +impl Debug for OwnedAttributeValue { 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").finish(), + Self::Custom(_) => f.debug_tuple("Any").finish(), Self::None => write!(f, "None"), } } } -impl From> for OwnedAttributeValue { - fn from(value: AttributeValue<'_>) -> Self { +impl From> for OwnedAttributeValue { + fn from(value: BorrowedAttributeValue<'_>) -> Self { match value { - AttributeValue::Text(text) => Self::Text(text.to_string()), - AttributeValue::Float(float) => Self::Float(float), - AttributeValue::Int(int) => Self::Int(int), - AttributeValue::Bool(bool) => Self::Bool(bool), - AttributeValue::Any(any) => Self::Any(any.borrow().as_ref().unwrap().clone()), - AttributeValue::None => Self::None, - _ => Self::None, + BorrowedAttributeValue::Text(text) => Self::Text(text.to_string()), + BorrowedAttributeValue::Float(float) => Self::Float(float), + BorrowedAttributeValue::Int(int) => Self::Int(int), + BorrowedAttributeValue::Bool(bool) => Self::Bool(bool), + BorrowedAttributeValue::Any(any) => Self::Custom(V::from_any_value(any)), + BorrowedAttributeValue::None => Self::None, } } } -impl OwnedAttributeValue { +impl OwnedAttributeValue { pub fn as_text(&self) -> Option<&str> { match self { OwnedAttributeValue::Text(text) => Some(text), @@ -145,4 +152,11 @@ impl OwnedAttributeValue { _ => None, } } + + pub fn as_custom(&self) -> Option<&V> { + match self { + OwnedAttributeValue::Custom(custom) => Some(custom), + _ => None, + } + } } diff --git a/packages/native-core/src/node_ref.rs b/packages/native-core/src/node_ref.rs index d178c2d18..8d4523efc 100644 --- a/packages/native-core/src/node_ref.rs +++ b/packages/native-core/src/node_ref.rs @@ -1,21 +1,21 @@ use dioxus_core::ElementId; use crate::{ - node::{NodeData, NodeType, OwnedAttributeView}, + node::{FromAnyValue, NodeData, NodeType, OwnedAttributeView}, state::union_ordered_iter, RealNodeId, }; /// A view into a [VNode] with limited access. #[derive(Debug)] -pub struct NodeView<'a> { - inner: &'a NodeData, +pub struct NodeView<'a, V: FromAnyValue = ()> { + inner: &'a NodeData, mask: NodeMask, } -impl<'a> NodeView<'a> { +impl<'a, V: FromAnyValue> NodeView<'a, V> { /// Create a new NodeView from a VNode, and mask. - pub fn new(node: &'a NodeData, view: NodeMask) -> Self { + pub fn new(node: &'a NodeData, view: NodeMask) -> Self { Self { inner: node, mask: view, @@ -55,7 +55,9 @@ impl<'a> NodeView<'a> { } /// Get any attributes that are enabled in the mask - pub fn attributes<'b>(&'b self) -> Option> + 'b> { + pub fn attributes<'b>( + &'b self, + ) -> Option> + 'b> { match &self.inner.node_type { NodeType::Element { attributes, .. } => Some( attributes diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index acdffa483..2e8eed085 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use crate::node::Node; +use crate::node::{FromAnyValue, Node}; use crate::node_ref::{NodeMask, NodeView}; use crate::passes::{resolve_passes, resolve_passes_single_threaded, AnyPass, DirtyNodeStates}; use crate::tree::TreeView; @@ -76,7 +76,7 @@ pub(crate) fn union_ordered_iter<'a>( /// } /// } /// ``` -pub trait ChildDepState { +pub trait ChildDepState { /// The context is passed to the [ChildDepState::reduce] when it is resolved. type Ctx; /// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree. @@ -87,7 +87,7 @@ pub trait ChildDepState { /// Resolve the state current node's state from the state of the children, the state of the node, and some external context. fn reduce<'a>( &mut self, - node: NodeView<'a>, + node: NodeView<'a, V>, children: impl Iterator::ElementBorrowed<'a>>, ctx: &Self::Ctx, ) -> bool @@ -138,7 +138,7 @@ pub trait ChildDepState { /// } /// } /// ``` -pub trait ParentDepState { +pub trait ParentDepState { /// The context is passed to the [ParentDepState::reduce] when it is resolved. type Ctx; /// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree. @@ -149,7 +149,7 @@ pub trait ParentDepState { /// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context. fn reduce<'a>( &mut self, - node: NodeView<'a>, + node: NodeView<'a, V>, parent: Option<::ElementBorrowed<'a>>, ctx: &Self::Ctx, ) -> bool; @@ -193,7 +193,7 @@ pub trait ParentDepState { /// } /// } /// ``` -pub trait NodeDepState { +pub trait NodeDepState { /// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState]. type DepState: ElementBorrowable; /// The state passed to [NodeDepState::reduce] when it is resolved. @@ -203,7 +203,7 @@ pub trait NodeDepState { /// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context. fn reduce<'a>( &mut self, - node: NodeView<'a>, + node: NodeView<'a, V>, node_state: ::ElementBorrowed<'a>, ctx: &Self::Ctx, ) -> bool; diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index a10cf1714..760afe87b 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -305,7 +305,6 @@ impl TreeView for Tree { fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> { // Safety: No node will appear as a child twice if let Some(children_ids) = self.children_ids(id) { - println!("parent_child_mut: {:?}\n{:?}", id, children_ids); debug_assert!(!children_ids.iter().any(|child_id| *child_id == id)); let mut borrowed = unsafe { let as_vec = children_ids.to_vec(); diff --git a/packages/native-core/src/utils/persistant_iterator.rs b/packages/native-core/src/utils/persistant_iterator.rs index 75bcf9064..7a34b90ad 100644 --- a/packages/native-core/src/utils/persistant_iterator.rs +++ b/packages/native-core/src/utils/persistant_iterator.rs @@ -229,7 +229,7 @@ impl PersistantElementIter { #[derive(Default, Clone, Debug)] struct Empty {} impl State for Empty { - const PASSES: &'static [crate::AnyPass>] = &[]; + const PASSES: &'static [crate::AnyPass>] = &[]; const MASKS: &'static [crate::NodeMask] = &[]; } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 887f70518..427adc742 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -7,7 +7,9 @@ //! - tests to ensure dyn_into works for various event types. //! - Partial delegation?> -use dioxus_core::{ElementId, Mutation, Template, TemplateAttribute, TemplateNode}; +use dioxus_core::{ + BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, +}; use dioxus_html::{event_bubbles, CompositionData, FormData}; use dioxus_interpreter_js::{save_template, Channel}; use futures_channel::mpsc; @@ -176,21 +178,24 @@ impl WebsysDom { id, ns, } => match value { - dioxus_core::AttributeValue::Text(txt) => { + BorrowedAttributeValue::Text(txt) => { i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default()) } - dioxus_core::AttributeValue::Float(f) => { + BorrowedAttributeValue::Float(f) => { i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default()) } - dioxus_core::AttributeValue::Int(n) => { + BorrowedAttributeValue::Int(n) => { i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default()) } - dioxus_core::AttributeValue::Bool(b) => i.set_attribute( + BorrowedAttributeValue::Bool(b) => i.set_attribute( id.0 as u32, name, if *b { "true" } else { "false" }, ns.unwrap_or_default(), ), + BorrowedAttributeValue::None => { + i.remove_attribute(id.0 as u32, name, ns.unwrap_or_default()) + } _ => unreachable!(), }, SetText { value, id } => i.set_text(id.0 as u32, value),