fix custom attribute values and add example

This commit is contained in:
Evan Almloff committed 2023-03-18 07:16:53 -05:00
1 parent a974fb3a8a
commit 9a16687bdc
4 files changed
+239 -8

No files matched your search

+1 -1
View File
@@ -305,7 +305,7 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
let create_fn = (!has_create_fn).then(|| {
quote! {
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>,
parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
@@ -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
View File
@@ -62,13 +62,13 @@ pub enum NodeType<V: FromAnyValue = ()> {
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 {
Self::Text(TextNode::new(text.into()))
}
}
impl From<TextNode> for NodeType {
impl<V: FromAnyValue> From<TextNode> for NodeType<V> {
fn from(text: TextNode) -> Self {
Self::Text(text)
}
@@ -136,25 +136,25 @@ pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
Custom(V),
}
impl From<String> for OwnedAttributeValue {
impl<V: FromAnyValue> From<String> for OwnedAttributeValue<V> {
fn from(value: String) -> Self {
Self::Text(value)
}
}
impl From<f64> for OwnedAttributeValue {
impl<V: FromAnyValue> From<f64> for OwnedAttributeValue<V> {
fn from(value: f64) -> Self {
Self::Float(value)
}
}
impl From<i64> for OwnedAttributeValue {
impl<V: FromAnyValue> From<i64> for OwnedAttributeValue<V> {
fn from(value: i64) -> Self {
Self::Int(value)
}
}
impl From<bool> for OwnedAttributeValue {
impl<V: FromAnyValue> From<bool> for OwnedAttributeValue<V> {
fn from(value: bool) -> Self {
Self::Bool(value)
}
+1 -1
View File
@@ -299,7 +299,7 @@ pub trait Dependancy {
macro_rules! impl_dependancy {
($($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>,)*);
fn type_ids() -> Box<[TypeId]> {