Forráskód Böngészése

fix custom attribute values and add example

Evan Almloff 2 éve
szülő
commit
9a16687bdc

+ 1 - 1
packages/native-core-macro/src/lib.rs

@@ -305,7 +305,7 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
     let create_fn = (!has_create_fn).then(|| {
     let create_fn = (!has_create_fn).then(|| {
         quote! {
         quote! {
             fn create<'a>(
             fn create<'a>(
-                node_view: dioxus_native_core::prelude::NodeView<()>,
+                node_view: dioxus_native_core::prelude::NodeView # trait_generics,
                 node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
                 node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
                 parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
                 parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
                 children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
                 children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,

+ 231 - 0
packages/native-core/examples/custom_attr.rs

@@ -0,0 +1,231 @@
+use dioxus_native_core::exports::shipyard::Component;
+use dioxus_native_core::node_ref::*;
+use dioxus_native_core::prelude::*;
+use dioxus_native_core::real_dom::NodeTypeMut;
+use dioxus_native_core_macro::partial_derive_state;
+
+struct FontSize(f64);
+
+// All states need to derive Component
+#[derive(Default, Debug, Copy, Clone, Component)]
+struct Size(f64, f64);
+
+#[derive(Default, Debug, Copy, Clone, Component)]
+struct X;
+
+impl FromAnyValue for X {
+    fn from_any_value(_: &dyn std::any::Any) -> Self {
+        X
+    }
+}
+
+/// Derive some of the boilerplate for the State implementation
+#[partial_derive_state]
+impl State<X> for Size {
+    type ParentDependencies = ();
+
+    // The size of the current node depends on the size of its children
+    type ChildDependencies = (Self,);
+
+    type NodeDependencies = ();
+
+    // Size only cares about the width, height, and text parts of the current node
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+        // Get access to the width and height attributes
+        .with_attrs(AttributeMaskBuilder::Some(&["width", "height"]))
+        // Get access to the text of the node
+        .with_text();
+
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView<X>,
+        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        _parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        context: &SendAnyMap,
+    ) -> bool {
+        let font_size = context.get::<FontSize>().unwrap().0;
+        let mut width;
+        let mut height;
+        if let Some(text) = node_view.text() {
+            // if the node has text, use the text to size our object
+            width = text.len() as f64 * font_size;
+            height = font_size;
+        } else {
+            // otherwise, the size is the maximum size of the children
+            width = children
+                .iter()
+                .map(|(item,)| item.0)
+                .reduce(|accum, item| if accum >= item { accum } else { item })
+                .unwrap_or(0.0);
+
+            height = children
+                .iter()
+                .map(|(item,)| item.1)
+                .reduce(|accum, item| if accum >= item { accum } else { item })
+                .unwrap_or(0.0);
+        }
+        // if the node contains a width or height attribute it overrides the other size
+        for a in node_view.attributes().into_iter().flatten() {
+            match &*a.attribute.name {
+                "width" => width = a.value.as_float().unwrap(),
+                "height" => height = a.value.as_float().unwrap(),
+                // because Size only depends on the width and height, no other attributes will be passed to the member
+                _ => panic!(),
+            }
+        }
+        // to determine what other parts of the dom need to be updated we return a boolean that marks if this member changed
+        let changed = (width != self.0) || (height != self.1);
+        *self = Self(width, height);
+        changed
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Default, Component)]
+struct TextColor {
+    r: u8,
+    g: u8,
+    b: u8,
+}
+
+#[partial_derive_state]
+impl State<X> for TextColor {
+    // TextColor depends on the TextColor part of the parent
+    type ParentDependencies = (Self,);
+
+    type ChildDependencies = ();
+
+    type NodeDependencies = ();
+
+    // TextColor only cares about the color attribute of the current node
+    const NODE_MASK: NodeMaskBuilder<'static> =
+        // Get access to the color attribute
+        NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&["color"]));
+
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView<X>,
+        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _context: &SendAnyMap,
+    ) -> bool {
+        // TextColor only depends on the color tag, so getting the first tag is equivilent to looking through all tags
+        let new = match node_view
+            .attributes()
+            .and_then(|mut attrs| attrs.next())
+            .and_then(|attr| attr.value.as_text())
+        {
+            // if there is a color tag, translate it
+            Some("red") => TextColor { r: 255, g: 0, b: 0 },
+            Some("green") => TextColor { r: 0, g: 255, b: 0 },
+            Some("blue") => TextColor { r: 0, g: 0, b: 255 },
+            Some(color) => panic!("unknown color {color}"),
+            // otherwise check if the node has a parent and inherit that color
+            None => match parent {
+                Some((parent,)) => *parent,
+                None => Self::default(),
+            },
+        };
+        // check if the member has changed
+        let changed = new != *self;
+        *self = new;
+        changed
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Default, Component)]
+struct Border(bool);
+
+#[partial_derive_state]
+impl State<X> for Border {
+    // TextColor depends on the TextColor part of the parent
+    type ParentDependencies = (Self,);
+
+    type ChildDependencies = ();
+
+    type NodeDependencies = ();
+
+    // Border does not depended on any other member in the current node
+    const NODE_MASK: NodeMaskBuilder<'static> =
+        // Get access to the border attribute
+        NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&["border"]));
+
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView<X>,
+        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        _parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _context: &SendAnyMap,
+    ) -> bool {
+        // check if the node contians a border attribute
+        let new = Self(
+            node_view
+                .attributes()
+                .and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
+                .is_some(),
+        );
+        // check if the member has changed
+        let changed = new != *self;
+        *self = new;
+        changed
+    }
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let mut rdom: RealDom<X> = RealDom::new([
+        Border::to_type_erased(),
+        TextColor::to_type_erased(),
+        Size::to_type_erased(),
+    ]);
+
+    let mut count = 0;
+
+    // intial render
+    let text_id = rdom.create_node(format!("Count: {count}")).id();
+    let mut root = rdom.get_mut(rdom.root_id()).unwrap();
+    // set the color to red
+    if let NodeTypeMut::Element(mut element) = root.node_type_mut() {
+        element.set_attribute(("color", "style"), "red".to_string());
+    }
+    root.add_child(text_id);
+
+    let mut ctx = SendAnyMap::new();
+    // set the font size to 3.3
+    ctx.insert(FontSize(3.3));
+    // update the State for nodes in the real_dom tree
+    let _to_rerender = rdom.update_state(ctx);
+
+    // we need to run the vdom in a async runtime
+    tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()?
+        .block_on(async {
+            loop {
+                // update the count
+                count += 1;
+                let mut text = rdom.get_mut(text_id).unwrap();
+                if let NodeTypeMut::Text(mut text) = text.node_type_mut() {
+                    *text = format!("Count: {count}");
+                }
+
+                let mut ctx = SendAnyMap::new();
+                ctx.insert(FontSize(3.3));
+                let _to_rerender = rdom.update_state(ctx);
+
+                // render...
+                rdom.traverse_depth_first(|node| {
+                    let indent = " ".repeat(node.height() as usize);
+                    let color = *node.get::<TextColor>().unwrap();
+                    let size = *node.get::<Size>().unwrap();
+                    let border = *node.get::<Border>().unwrap();
+                    let id = node.id();
+                    println!("{indent}{id:?} {color:?} {size:?} {border:?}");
+                });
+
+                // wait 1 second
+                tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+            }
+        })
+}

+ 6 - 6
packages/native-core/src/node.rs

@@ -62,13 +62,13 @@ pub enum NodeType<V: FromAnyValue = ()> {
     Placeholder,
     Placeholder,
 }
 }
 
 
-impl<S: Into<String>> From<S> for NodeType {
+impl<V: FromAnyValue, S: Into<String>> From<S> for NodeType<V> {
     fn from(text: S) -> Self {
     fn from(text: S) -> Self {
         Self::Text(TextNode::new(text.into()))
         Self::Text(TextNode::new(text.into()))
     }
     }
 }
 }
 
 
-impl From<TextNode> for NodeType {
+impl<V: FromAnyValue> From<TextNode> for NodeType<V> {
     fn from(text: TextNode) -> Self {
     fn from(text: TextNode) -> Self {
         Self::Text(text)
         Self::Text(text)
     }
     }
@@ -136,25 +136,25 @@ pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
     Custom(V),
     Custom(V),
 }
 }
 
 
-impl From<String> for OwnedAttributeValue {
+impl<V: FromAnyValue> From<String> for OwnedAttributeValue<V> {
     fn from(value: String) -> Self {
     fn from(value: String) -> Self {
         Self::Text(value)
         Self::Text(value)
     }
     }
 }
 }
 
 
-impl From<f64> for OwnedAttributeValue {
+impl<V: FromAnyValue> From<f64> for OwnedAttributeValue<V> {
     fn from(value: f64) -> Self {
     fn from(value: f64) -> Self {
         Self::Float(value)
         Self::Float(value)
     }
     }
 }
 }
 
 
-impl From<i64> for OwnedAttributeValue {
+impl<V: FromAnyValue> From<i64> for OwnedAttributeValue<V> {
     fn from(value: i64) -> Self {
     fn from(value: i64) -> Self {
         Self::Int(value)
         Self::Int(value)
     }
     }
 }
 }
 
 
-impl From<bool> for OwnedAttributeValue {
+impl<V: FromAnyValue> From<bool> for OwnedAttributeValue<V> {
     fn from(value: bool) -> Self {
     fn from(value: bool) -> Self {
         Self::Bool(value)
         Self::Bool(value)
     }
     }

+ 1 - 1
packages/native-core/src/passes.rs

@@ -299,7 +299,7 @@ pub trait Dependancy {
 
 
 macro_rules! impl_dependancy {
 macro_rules! impl_dependancy {
     ($($t:ident),*) => {
     ($($t:ident),*) => {
-        impl< $($t: Send + Sync + Component + State),* > Dependancy for ($($t,)*) {
+        impl< $($t: Send + Sync + Component),* > Dependancy for ($($t,)*) {
             type ElementBorrowed<'a> = ($(DependancyView<'a, $t>,)*);
             type ElementBorrowed<'a> = ($(DependancyView<'a, $t>,)*);
 
 
             fn type_ids() -> Box<[TypeId]> {
             fn type_ids() -> Box<[TypeId]> {