1
0
Эх сурвалжийг харах

Merge pull request #730 from Demonthos/simplify-native-core

Simplify native core state with ECS and make native-core work with other frameworks
Jon Kelley 2 жил өмнө
parent
commit
83613abbc1
86 өөрчлөгдсөн 5849 нэмэгдсэн , 4741 устгасан
  1. 2 1
      Cargo.toml
  2. 4 1
      docs/guide/Cargo.toml
  3. 311 0
      docs/guide/examples/custom_renderer.rs
  4. 26 203
      docs/guide/src/en/custom_renderer/index.md
  5. 0 0
      packages/dioxus-tui/.gitignore
  6. 0 0
      packages/dioxus-tui/.vscode/spellright.dict
  7. 2 7
      packages/dioxus-tui/Cargo.toml
  8. 9 9
      packages/dioxus-tui/README.md
  9. 165 0
      packages/dioxus-tui/benches/update.rs
  10. 0 0
      packages/dioxus-tui/examples/all_events.rs
  11. 0 0
      packages/dioxus-tui/examples/border.rs
  12. 0 0
      packages/dioxus-tui/examples/buttons.rs
  13. 0 0
      packages/dioxus-tui/examples/color_test.rs
  14. 4 2
      packages/dioxus-tui/examples/colorpicker.rs
  15. 0 0
      packages/dioxus-tui/examples/components.rs
  16. 0 0
      packages/dioxus-tui/examples/example.png
  17. 0 0
      packages/dioxus-tui/examples/flex.rs
  18. 0 0
      packages/dioxus-tui/examples/hover.rs
  19. 0 0
      packages/dioxus-tui/examples/list.rs
  20. 0 0
      packages/dioxus-tui/examples/margin.rs
  21. 0 0
      packages/dioxus-tui/examples/quadrants.rs
  22. 0 0
      packages/dioxus-tui/examples/readme.rs
  23. 5 19
      packages/dioxus-tui/examples/stress.rs
  24. 0 0
      packages/dioxus-tui/examples/task.rs
  25. 0 0
      packages/dioxus-tui/examples/text.rs
  26. 0 1
      packages/dioxus-tui/examples/widgets.rs
  27. 143 0
      packages/dioxus-tui/src/lib.rs
  28. 1 0
      packages/dioxus-tui/src/prelude/mod.rs
  29. 2 2
      packages/dioxus-tui/src/widgets/button.rs
  30. 2 2
      packages/dioxus-tui/src/widgets/checkbox.rs
  31. 0 0
      packages/dioxus-tui/src/widgets/input.rs
  32. 22 0
      packages/dioxus-tui/src/widgets/mod.rs
  33. 4 4
      packages/dioxus-tui/src/widgets/number.rs
  34. 6 4
      packages/dioxus-tui/src/widgets/password.rs
  35. 3 3
      packages/dioxus-tui/src/widgets/slider.rs
  36. 4 4
      packages/dioxus-tui/src/widgets/textbox.rs
  37. 0 0
      packages/dioxus-tui/test.html
  38. 0 0
      packages/dioxus-tui/tests/events.rs
  39. 348 525
      packages/native-core-macro/src/lib.rs
  40. 0 29
      packages/native-core-macro/src/sorted_slice.rs
  41. 0 244
      packages/native-core-macro/tests/called_minimally_on_build.rs
  42. 0 421
      packages/native-core-macro/tests/update_state.rs
  43. 16 6
      packages/native-core/Cargo.toml
  44. 1 1
      packages/native-core/README.md
  45. 231 0
      packages/native-core/examples/custom_attr.rs
  46. 222 0
      packages/native-core/examples/simple.rs
  47. 249 0
      packages/native-core/examples/simple_dioxus.rs
  48. 296 0
      packages/native-core/src/dioxus.rs
  49. 4 0
      packages/native-core/src/layout_attributes.rs
  50. 33 35
      packages/native-core/src/lib.rs
  51. 160 54
      packages/native-core/src/node.rs
  52. 128 128
      packages/native-core/src/node_ref.rs
  53. 17 0
      packages/native-core/src/node_watcher.rs
  54. 243 826
      packages/native-core/src/passes.rs
  55. 952 404
      packages/native-core/src/real_dom.rs
  56. 0 305
      packages/native-core/src/state.rs
  57. 215 561
      packages/native-core/src/tree.rs
  58. 43 71
      packages/native-core/src/utils/cursor.rs
  59. 4 0
      packages/native-core/src/utils/mod.rs
  60. 219 263
      packages/native-core/src/utils/persistant_iterator.rs
  61. 237 0
      packages/native-core/tests/called_minimally_on_build.rs
  62. 51 31
      packages/native-core/tests/fuzzing.rs
  63. 50 31
      packages/native-core/tests/miri_native.rs
  64. 440 0
      packages/native-core/tests/passes.rs
  65. 2 0
      packages/rink/.gitignore
  66. 2 0
      packages/rink/.vscode/spellright.dict
  67. 39 0
      packages/rink/Cargo.toml
  68. 74 0
      packages/rink/README.md
  69. 104 0
      packages/rink/examples/counter.rs
  70. BIN
      packages/rink/examples/example.png
  71. 173 0
      packages/rink/examples/grid.rs
  72. 0 0
      packages/rink/src/config.rs
  73. 81 78
      packages/rink/src/focus.rs
  74. 108 151
      packages/rink/src/hooks.rs
  75. 41 30
      packages/rink/src/layout.rs
  76. 90 121
      packages/rink/src/lib.rs
  77. 2 0
      packages/rink/src/prelude/mod.rs
  78. 86 0
      packages/rink/src/prevent_default.rs
  79. 29 15
      packages/rink/src/query.rs
  80. 30 25
      packages/rink/src/render.rs
  81. 0 0
      packages/rink/src/style.rs
  82. 41 17
      packages/rink/src/style_attributes.rs
  83. 0 0
      packages/rink/src/widget.rs
  84. 73 0
      packages/rink/test.html
  85. 0 89
      packages/tui/src/node.rs
  86. 0 18
      packages/tui/src/widgets/mod.rs

+ 2 - 1
Cargo.toml

@@ -15,7 +15,8 @@ members = [
     "packages/liveview",
     "packages/autofmt",
     "packages/rsx",
-    "packages/tui",
+    "packages/dioxus-tui",
+    "packages/rink",
     "packages/native-core",
     "packages/native-core-macro",
     "packages/rsx-rosetta",

+ 4 - 1
docs/guide/Cargo.toml

@@ -11,10 +11,13 @@ dioxus = { path = "../../packages/dioxus" }
 dioxus-desktop = { path = "../../packages/desktop" }
 dioxus-web = { path = "../../packages/web" }
 dioxus-ssr = { path = "../../packages/ssr" }
+dioxus-native-core = { path = "../../packages/native-core" }
+dioxus-native-core-macro = { path = "../../packages/native-core-macro" }
 dioxus-router = { path = "../../packages/router" }
 dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] }
-dioxus-tui = { path = "../../packages/tui" }
+dioxus-tui = { path = "../../packages/dioxus-tui" }
 fermi = { path = "../../packages/fermi" }
+shipyard = "0.6.2"
 
 
 # dioxus = { path = "../../packages/dioxus", features = ["desktop", "web", "ssr", "router", "fermi", "tui"] }

+ 311 - 0
docs/guide/examples/custom_renderer.rs

@@ -0,0 +1,311 @@
+use dioxus::html::input_data::keyboard_types::{Code, Key, Modifiers};
+use dioxus::prelude::*;
+use dioxus_native_core::exports::shipyard::Component;
+use dioxus_native_core::node_ref::*;
+use dioxus_native_core::prelude::*;
+use dioxus_native_core::utils::cursor::{Cursor, Pos};
+use dioxus_native_core_macro::partial_derive_state;
+
+// ANCHOR: state_impl
+struct FontSize(f64);
+
+// All states need to derive Component
+#[derive(Default, Debug, Copy, Clone, Component)]
+struct Size(f64, f64);
+
+/// Derive some of the boilerplate for the State implementation
+#[partial_derive_state]
+impl State 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<()>,
+        _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 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<()>,
+        _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 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<()>,
+        _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
+    }
+}
+// ANCHOR_END: state_impl
+
+// ANCHOR: rendering
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    fn app(cx: Scope) -> Element {
+        let count = use_state(cx, || 0);
+
+        use_future(cx, (count,), |(count,)| async move {
+            loop {
+                tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+                count.set(*count + 1);
+            }
+        });
+
+        cx.render(rsx! {
+            div{
+                color: "red",
+                "{count}"
+            }
+        })
+    }
+
+    // create the vdom, the real_dom, and the binding layer between them
+    let mut vdom = VirtualDom::new(app);
+    let mut rdom: RealDom = RealDom::new([
+        Border::to_type_erased(),
+        TextColor::to_type_erased(),
+        Size::to_type_erased(),
+    ]);
+    let mut dioxus_intigration_state = DioxusState::create(&mut rdom);
+
+    let mutations = vdom.rebuild();
+    // update the structure of the real_dom tree
+    dioxus_intigration_state.apply_mutations(&mut rdom, mutations);
+    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 {
+                // wait for the vdom to update
+                vdom.wait_for_work().await;
+
+                // get the mutations from the vdom
+                let mutations = vdom.render_immediate();
+
+                // update the structure of the real_dom tree
+                dioxus_intigration_state.apply_mutations(&mut rdom, mutations);
+
+                // update the state of the real_dom tree
+                let mut ctx = SendAnyMap::new();
+                // set the font size to 3.3
+                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();
+                    let node = node.node_type();
+                    let node_type = &*node;
+                    println!("{indent}{id:?} {color:?} {size:?} {border:?} {node_type:?}");
+                });
+            }
+        })
+}
+// ANCHOR_END: rendering
+
+// ANCHOR: derive_state
+// All states must derive Component (https://docs.rs/shipyard/latest/shipyard/derive.Component.html)
+// They also must implement Default or provide a custom implementation of create in the State trait
+#[derive(Default, Component)]
+struct MyState;
+
+/// Derive some of the boilerplate for the State implementation
+#[partial_derive_state]
+impl State for MyState {
+    // The states of the parent nodes this state depends on
+    type ParentDependencies = ();
+
+    // The states of the child nodes this state depends on
+    type ChildDependencies = (Self,);
+
+    // The states of the current node this state depends on
+    type NodeDependencies = ();
+
+    // The parts of the current text, element, or placeholder node in the tree that this state depends on
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+    // How to update the state of the current node based on the state of the parent nodes, child nodes, and the current node
+    // Returns true if the node was updated and false if the node was not updated
+    fn update<'a>(
+        &mut self,
+        // The view of the current node limited to the parts this state depends on
+        _node_view: NodeView<()>,
+        // The state of the current node that this state depends on
+        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        // The state of the parent nodes that this state depends on
+        _parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        // The state of the child nodes that this state depends on
+        _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        // The context of the current node used to pass global state into the tree
+        _context: &SendAnyMap,
+    ) -> bool {
+        todo!()
+    }
+
+    // partial_derive_state will generate a default implementation of all the other methods
+}
+// ANCHOR_END: derive_state
+
+#[allow(unused)]
+// ANCHOR: cursor
+fn text_editing() {
+    let mut cursor = Cursor::default();
+    let mut text = String::new();
+
+    // handle keyboard input with a max text length of 10
+    cursor.handle_input(
+        &Code::ArrowRight,
+        &Key::ArrowRight,
+        &Modifiers::empty(),
+        &mut text,
+        10,
+    );
+
+    // mannually select text between characters 0-5 on the first line (this could be from dragging with a mouse)
+    cursor.start = Pos::new(0, 0);
+    cursor.end = Some(Pos::new(5, 0));
+
+    // delete the selected text and move the cursor to the start of the selection
+    cursor.delete_selection(&mut text);
+}
+// ANCHOR_END: cursor

+ 26 - 203
docs/guide/src/en/custom_renderer/index.md

@@ -99,13 +99,14 @@ Template {
     attr_paths: &'a [&'a [u8]],
 }
 ```
+
 > For more detailed docs about the struture of templates see the [Template api docs](https://docs.rs/dioxus-core/latest/dioxus_core/prelude/struct.Template.html)
 
 This template will be sent to the renderer in the [list of templates](https://docs.rs/dioxus-core/latest/dioxus_core/struct.Mutations.html#structfield.templates) supplied with the mutations the first time it is used. Any time the renderer encounters a [LoadTemplate](https://docs.rs/dioxus-core/latest/dioxus_core/enum.Mutation.html#variant.LoadTemplate) mutation after this, it should clone the template and store it in the given id.
 
-For dynamic nodes and dynamic text nodes, a placeholder node should be created and inserted into the UI so that the node can be navigated to later.
+For dynamic nodes and dynamic text nodes, a placeholder node should be created and inserted into the UI so that the node can be modified later.
 
-In HTML renderers, this template could look like:
+In HTML renderers, this template could look like this:
 
 ```html
 <h1>""</h1>
@@ -206,7 +207,9 @@ nodes: [
     "count: 0",
 ]
 ```
+
 Over time, our stack looked like this:
+
 ```rust
 [Root]
 [Root, <h1>""</h1>]
@@ -218,15 +221,15 @@ Conveniently, this approach completely separates the Virtual DOM and the Real DO
 
 Dioxus is also really fast. Because Dioxus splits the diff and patch phase, it's able to make all the edits to the RealDOM in a very short amount of time (less than a single frame) making rendering very snappy. It also allows Dioxus to cancel large diffing operations if higher priority work comes in while it's diffing.
 
-This little demo serves to show exactly how a Renderer would need to process an edit stream to build UIs.
+This little demo serves to show exactly how a Renderer would need to process a mutation stream to build UIs.
 
 ## Event loop
 
-Like most GUIs, Dioxus relies on an event loop to progress the VirtualDOM. The VirtualDOM itself can produce events as well, so it's important that your custom renderer can handle those too.
+Like most GUIs, Dioxus relies on an event loop to progress the VirtualDOM. The VirtualDOM itself can produce events as well, so it's important for your custom renderer can handle those too.
 
 The code for the WebSys implementation is straightforward, so we'll add it here to demonstrate how simple an event loop is:
 
-```rust
+```rust, ignore
 pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
     // Push the body element onto the WebsysDom's stack machine
     let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom());
@@ -254,9 +257,9 @@ pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
 }
 ```
 
-It's important that you decode the real events from your event system into Dioxus' synthetic event system (synthetic meaning abstracted). This simply means matching your event type and creating a Dioxus `UserEvent` type. Right now, the VirtualEvent system is modeled almost entirely around the HTML spec, but we are interested in slimming it down.
+It's important to decode what the real events are for your event system into Dioxus' synthetic event system (synthetic meaning abstracted). This simply means matching your event type and creating a Dioxus `UserEvent` type. Right now, the virtual event system is modeled almost entirely around the HTML spec, but we are interested in slimming it down.
 
-```rust
+```rust, ignore
 fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
     match event.type_().as_str() {
         "keydown" => {
@@ -294,16 +297,17 @@ For more examples and information on how to create custom namespaces, see the [`
 
 # Native Core
 
-If you are creating a renderer in rust, the [native-core](https://github.com/DioxusLabs/dioxus/tree/master/packages/native-core) crate provides some utilities to implement a renderer. It provides an abstraction over Mutations and Templates and contains helpers that can handle the layout, and text editing for you.
+If you are creating a renderer in rust, the [native-core](https://github.com/DioxusLabs/dioxus/tree/master/packages/native-core) crate provides some utilities to implement a renderer. It provides an abstraction over Mutations and Templates and contains helpers that can handle the layout and text editing for you.
 
 ## The RealDom
 
-The `RealDom` is a higher-level abstraction over updating the Dom. It updates with `Mutations` and provides a way to incrementally update the state of nodes based on attributes or other states that change.
+The `RealDom` is a higher-level abstraction over updating the Dom. It uses an entity component system to manage the state of nodes. This system allows you to modify insert and modify arbitrary components on nodes. On top of this, the RealDom provides a way to manage a tree of nodes, and the State trait provides a way to automatically add and update these components when the tree is modified. It also provides a way to apply `Mutations` to the RealDom.
 
 ### Example
 
 Let's build a toy renderer with borders, size, and text color.
 Before we start let's take a look at an example element we can render:
+
 ```rust
 cx.render(rsx!{
     div{
@@ -372,188 +376,24 @@ In the following diagram arrows represent dataflow:
 [//]: # "        end"
 [//]: # "    end"
 
-To help in building a Dom, native-core provides four traits: State, ChildDepState, ParentDepState, NodeDepState, and a RealDom struct. The ChildDepState, ParentDepState, and NodeDepState provide a way to describe how some information in a node relates to that of its relatives. By providing how to build a single node from its relations, native-core will derive a way to update the state of all nodes for you with ```#[derive(State)]```. Once you have a state you can provide it as a generic to RealDom. RealDom provides all of the methods to interact and update your new dom.
+To help in building a Dom, native-core provides the State trait and a RealDom struct. The State trait provides a way to describe how states in a node depend on other states in its relatives. By describing how to update a single node from its relations, native-core will derive a way to update the states of all nodes for you. Once you have a state you can provide it as a generic to RealDom. RealDom provides all of the methods to interact and update your new dom.
 
-```rust
+Native Core cannot create all of the required methods for the State trait, but it can derive some of them. To implement the State trait, you must implement the following methods and let the `#[partial_derive_state]` macro handle the rest:
 
-use dioxus_native_core::node_ref::*;
-use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
-use dioxus_native_core_macro::{sorted_str_slice, State};
-
-#[derive(Default, Copy, Clone)]
-struct Size(f64, f64);
-// Size only depends on the current node and its children, so it implements ChildDepState
-impl ChildDepState for Size {
-    // Size accepts a font size context
-    type Ctx = f64;
-    // Size depends on the Size part of each child
-    type DepState = (Self,);
-    // Size only cares about the width, height, and text parts of the current node
-    const NODE_MASK: NodeMask =
-        NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!([
-            "width", "height"
-        ])))
-        .with_text();
-    fn reduce<'a>(
-        &mut self,
-        node: NodeView,
-        children: impl Iterator<Item = (&'a Self,)>,
-        ctx: &Self::Ctx,
-    ) -> bool
-    where
-        Self::DepState: 'a,
-    {
-        let mut width;
-        let mut height;
-        if let Some(text) = node.text() {
-            // if the node has text, use the text to size our object
-            width = text.len() as f64 * ctx;
-            height = *ctx;
-        } else {
-            // otherwise, the size is the maximum size of the children
-            width = children
-                .by_ref()
-                .map(|(item,)| item.0)
-                .reduce(|accum, item| if accum >= item { accum } else { item })
-                .unwrap_or(0.0);
-
-            height = children
-                .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.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)]
-struct TextColor {
-    r: u8,
-    g: u8,
-    b: u8,
-}
-// TextColor only depends on the current node and its parent, so it implements ParentDepState
-impl ParentDepState for TextColor {
-    type Ctx = ();
-    // TextColor depends on the TextColor part of the parent
-    type DepState = (Self,);
-    // TextColor only cares about the color attribute of the current node
-    const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(&["color"]));
-    fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
-        // TextColor only depends on the color tag, so getting the first tag is equivilent to looking through all tags
-        let new = match node
-            .attributes()
-            .and_then(|attrs| attrs.next())
-            .map(|attr| attr.attribute.name.as_str())
-        {
-            // 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(_) => panic!("unknown 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
-    }
-}
+```rust, ignore
+{{#include ../../../examples/custom_renderer.rs:derive_state}}
+```
 
-#[derive(Debug, Clone, PartialEq, Default)]
-struct Border(bool);
-// TextColor only depends on the current node, so it implements NodeDepState
-impl NodeDepState for Border {
-    type Ctx = ();
-    type DepState = ();
-
-    // Border does not depended on any other member in the current node
-    const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(&["border"]));
-    fn reduce(&mut self, node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
-        // check if the node contians a border attribute
-        let new = Self(
-            node.attributes()
-                .and_then(|attrs| attrs.next().map(|a| a.attribute.name == "border"))
-                .is_some(),
-        );
-        // check if the member has changed
-        let changed = new != *self;
-        *self = new;
-        changed
-    }
-}
+Lets take a look at how to implement the State trait for a simple renderer.
 
-// State provides a derive macro, but anotations on the members are needed in the form #[dep_type(dep_member, CtxType)]
-#[derive(State, Default, Clone)]
-struct ToyState {
-    // the color member of it's parent and no context
-    #[parent_dep_state(color)]
-    color: TextColor,
-    // depends on the node, and no context
-    #[node_dep_state()]
-    border: Border,
-    // depends on the layout_width member of children and f32 context (for text size)
-    #[child_dep_state(size, f32)]
-    size: Size,
-}
+```rust
+{{#include ../../../examples/custom_renderer.rs:state_impl}}
 ```
 
-Now that we have our state, we can put it to use in our dom. We can update the dom with update_state to update the structure of the dom (adding, removing, and changing properties of nodes) and then apply_mutations to update the ToyState for each of the nodes that changed.
+Now that we have our state, we can put it to use in our RealDom. We can update the RealDom with apply_mutations to update the structure of the dom (adding, removing, and changing properties of nodes) and then update_state to update the States for each of the nodes that changed.
+
 ```rust
-fn main(){
-    fn app(cx: Scope) -> Element {
-        cx.render(rsx!{
-            div{
-                color: "red",
-                "hello world"
-            }
-        })
-    }
-    let vdom = VirtualDom::new(app);
-    let rdom: RealDom<ToyState> = RealDom::new();
-
-    let mutations = dom.rebuild();
-    // update the structure of the real_dom tree
-    let to_update = rdom.apply_mutations(vec![mutations]);
-    let mut ctx = AnyMap::new();
-    // set the font size to 3.3
-    ctx.insert(3.3f64);
-    // update the ToyState for nodes in the real_dom tree
-    let _to_rerender = rdom.update_state(&dom, to_update, ctx).unwrap();
-
-    // we need to run the vdom in a async runtime
-    tokio::runtime::Builder::new_current_thread()
-        .enable_all()
-        .build()?
-        .block_on(async {
-            loop{
-                let wait = vdom.wait_for_work();
-                let mutations = vdom.work_with_deadline(|| false);
-                let to_update = rdom.apply_mutations(mutations);
-                let mut ctx = AnyMap::new();
-                ctx.insert(3.3f64);
-                let _to_rerender = rdom.update_state(vdom, to_update, ctx).unwrap();
-
-                // render...
-            }
-        })
-}
+{{#include ../../../examples/custom_renderer.rs:rendering}}
 ```
 
 ## Layout
@@ -565,26 +405,9 @@ For most platforms, the layout of the Elements will stay the same. The [layout_a
 To make it easier to implement text editing in rust renderers, `native-core` also contains a renderer-agnostic cursor system. The cursor can handle text editing, selection, and movement with common keyboard shortcuts integrated.
 
 ```rust
-let mut cursor = Cursor::default();
-let mut text = String::new();
-
-let keyboard_data = dioxus_html::KeyboardData::new(
-    dioxus_html::input_data::keyboard_types::Key::ArrowRight,
-    dioxus_html::input_data::keyboard_types::Code::ArrowRight,
-    dioxus_html::input_data::keyboard_types::Location::Standard,
-    false,
-    Modifiers::empty(),
-);
-// handle keyboard input with a max text length of 10
-cursor.handle_input(&keyboard_data, &mut text, 10);
-
-// mannually select text between characters 0-5 on the first line (this could be from dragging with a mouse)
-cursor.start = Pos::new(0, 0);
-cursor.end = Some(Pos::new(5, 0));
-
-// delete the selected text and move the cursor to the start of the selection
-cursor.delete_selection(&mut text);
+{{#include ../../../examples/custom_renderer.rs:cursor}}
 ```
 
 ## Conclusion
+
 That should be it! You should have nearly all the knowledge required on how to implement your renderer. We're super interested in seeing Dioxus apps brought to custom desktop renderers, mobile renderers, video game UI, and even augmented reality! If you're interested in contributing to any of these projects, don't be afraid to reach out or join the [community](https://discord.gg/XgGxMSkvUM).

+ 0 - 0
packages/tui/.gitignore → packages/dioxus-tui/.gitignore


+ 0 - 0
packages/tui/.vscode/spellright.dict → packages/dioxus-tui/.vscode/spellright.dict


+ 2 - 7
packages/tui/Cargo.toml → packages/dioxus-tui/Cargo.toml

@@ -16,20 +16,15 @@ license = "MIT/Apache-2.0"
 dioxus = { path = "../dioxus", version = "^0.3.0" }
 dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] }
 dioxus-html = { path = "../html", version = "^0.3.0" }
-dioxus-native-core = { path = "../native-core", version = "^0.3.0" }
+dioxus-native-core = { path = "../native-core", version = "^0.2.0", features = ["dioxus"] }
 dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.3.0" }
 dioxus-hot-reload = { path = "../hot-reload", optional = true }
+rink = { path = "../rink" }
 
-tui = "0.17.0"
 crossterm = "0.23.0"
-anyhow = "1.0.42"
 tokio = { version = "1.15.0", features = ["full"] }
 futures = "0.3.19"
 taffy = "0.2.1"
-smallvec = "1.6"
-rustc-hash = "1.1.0"
-anymap = "1.0.0-beta.2"
-futures-channel = "0.3.25"
 
 [dev-dependencies]
 dioxus = { path = "../dioxus" }

+ 9 - 9
packages/tui/README.md → packages/dioxus-tui/README.md

@@ -1,5 +1,5 @@
 <div align="center">
-  <h1>Rink</h1>
+  <h1>Dioxus TUI</h1>
   <p>
     <strong>Beautiful terminal user interfaces in Rust with <a href="https://dioxuslabs.com/">Dioxus </a>.</strong>
   </p>
@@ -37,7 +37,6 @@
   </a>
 </div>
 
-
 <br/>
 
 Leverage React-like patterns, CSS, HTML, and Rust to build beautiful, portable, terminal user interfaces with Dioxus.
@@ -63,33 +62,34 @@ fn app(cx: Scope) -> Element {
 
 You can use Html-like semantics with inline styles, tree hierarchy, components, and more in your [`text-based user interface (TUI)`](https://en.wikipedia.org/wiki/Text-based_user_interface) application.
 
-Rink is essentially a port of [Ink](https://github.com/vadimdemedes/ink) but for [`Rust`](https://www.rust-lang.org/) and [`Dioxus`](https://dioxuslabs.com/). Rink doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.
+Dioxus TUI is essentially a port of [Ink](https://github.com/vadimdemedes/ink) but for [`Rust`](https://www.rust-lang.org/) and [`Dioxus`](https://dioxuslabs.com/). Dioxus TUI doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.
 
 ## Limitations
 
 - **Subset of Html**
-Terminals can only render a subset of HTML. We support as much as we can.
+  Terminals can only render a subset of HTML. We support as much as we can.
 - **Particular frontend design**
-Terminals and browsers are and look different. Therefore, the same design might not be the best to cover both renderers.
-
+  Terminals and browsers are and look different. Therefore, the same design might not be the best to cover both renderers.
 
 ## Status
 
-**WARNING: Rink is currently under construction!**
+**WARNING: Dioxus TUI is currently under construction!**
 
 Rendering a VirtualDom works fine, but the ecosystem of hooks is not yet ready. Additionally, some bugs in the flexbox implementation might be quirky at times.
 
 ## Features
 
-Rink features:
+Dioxus TUI features:
+
 - [x] Flexbox-based layout system
 - [ ] CSS selectors
 - [x] inline CSS support
 - [x] Built-in focusing system
+
 * [x] Widgets<sup>1</sup>
 * [ ] Support for events, hooks, and callbacks<sup>2</sup>
 * [ ] Html tags<sup>3</sup>
 
 <sup>1</sup> Currently only a subset of the input element is implemented as a component (not an element). The `Input` component supports sliders, text, numbers, passwords, buttons, and checkboxes.
 <sup>2</sup> Basic keyboard, mouse, and focus events are implemented.
-<sup>3</sup> Currently, most HTML tags don't translate into any meaning inside of Dioxus TUI. So an `input` *element* won't mean anything nor does it have any additional functionality.
+<sup>3</sup> Currently, most HTML tags don't translate into any meaning inside of Dioxus TUI. So an `input` _element_ won't mean anything nor does it have any additional functionality.

+ 165 - 0
packages/dioxus-tui/benches/update.rs

@@ -0,0 +1,165 @@
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
+use dioxus::prelude::*;
+use dioxus_tui::{Config, TuiContext};
+
+criterion_group!(mbenches, tui_update);
+criterion_main!(mbenches);
+
+/// This benchmarks the cache performance of the TUI for small edits by changing one box at a time.
+fn tui_update(c: &mut Criterion) {
+    {
+        let mut group = c.benchmark_group("Update boxes");
+
+        for size in 1..=20usize {
+            let parameter_string = format!("{}", (size).pow(2));
+            group.bench_with_input(
+                BenchmarkId::new("size", parameter_string),
+                &size,
+                |b, size| {
+                    b.iter(|| {
+                        dioxus_tui::launch_cfg_with_props(
+                            app,
+                            GridProps {
+                                size: *size,
+                                update_count: 1,
+                            },
+                            Config::default().with_headless(),
+                        )
+                    })
+                },
+            );
+        }
+    }
+
+    {
+        let mut group = c.benchmark_group("Update many boxes");
+
+        for update_count in 1..=20usize {
+            let update_count = update_count * 20;
+            let parameter_string = update_count.to_string();
+            group.bench_with_input(
+                BenchmarkId::new("update count", parameter_string),
+                &update_count,
+                |b, update_count| {
+                    b.iter(|| {
+                        dioxus_tui::launch_cfg_with_props(
+                            app,
+                            GridProps {
+                                size: 20,
+                                update_count: *update_count,
+                            },
+                            Config::default().with_headless(),
+                        )
+                    })
+                },
+            );
+        }
+    }
+}
+
+#[derive(Props, PartialEq)]
+struct BoxProps {
+    x: usize,
+    y: usize,
+    hue: f32,
+    alpha: f32,
+}
+#[allow(non_snake_case)]
+fn Box(cx: Scope<BoxProps>) -> Element {
+    let count = use_state(cx, || 0);
+
+    let x = cx.props.x * 2;
+    let y = cx.props.y * 2;
+    let hue = cx.props.hue;
+    let display_hue = cx.props.hue as u32 / 10;
+    let count = count.get();
+    let alpha = cx.props.alpha + (count % 100) as f32;
+
+    cx.render(rsx! {
+        div {
+            left: "{x}%",
+            top: "{y}%",
+            width: "100%",
+            height: "100%",
+            background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
+            align_items: "center",
+            p{"{display_hue:03}"}
+        }
+    })
+}
+
+#[derive(Props, PartialEq)]
+struct GridProps {
+    size: usize,
+    update_count: usize,
+}
+#[allow(non_snake_case)]
+fn Grid(cx: Scope<GridProps>) -> Element {
+    let size = cx.props.size;
+    let count = use_state(cx, || 0);
+    let counts = use_ref(cx, || vec![0; size * size]);
+
+    let ctx: TuiContext = cx.consume_context().unwrap();
+    if *count.get() + cx.props.update_count >= (size * size) {
+        ctx.quit();
+    } else {
+        for _ in 0..cx.props.update_count {
+            counts.with_mut(|c| {
+                let i = *count.current();
+                c[i] += 1;
+                c[i] %= 360;
+            });
+            count.with_mut(|i| {
+                *i += 1;
+                *i %= size * size;
+            });
+        }
+    }
+
+    render! {
+        div{
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+            (0..size).map(|x|
+                    {
+                    rsx! {
+                        div{
+                            width: "100%",
+                            height: "100%",
+                            flex_direction: "row",
+                            (0..size).map(|y|
+                                {
+                                    let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32;
+                                    let key = format!("{}-{}", x, y);
+                                    rsx! {
+                                        Box{
+                                            x: x,
+                                            y: y,
+                                            alpha: 100.0,
+                                            hue: alpha,
+                                            key: "{key}",
+                                        }
+                                    }
+                                }
+                            )
+                        }
+                    }
+                }
+            )
+        }
+    }
+}
+
+fn app(cx: Scope<GridProps>) -> Element {
+    cx.render(rsx! {
+        div{
+            width: "100%",
+            height: "100%",
+            Grid{
+                size: cx.props.size,
+                update_count: cx.props.update_count,
+            }
+        }
+    })
+}

+ 0 - 0
packages/tui/examples/all_events.rs → packages/dioxus-tui/examples/all_events.rs


+ 0 - 0
packages/tui/examples/border.rs → packages/dioxus-tui/examples/border.rs


+ 0 - 0
packages/tui/examples/buttons.rs → packages/dioxus-tui/examples/buttons.rs


+ 0 - 0
packages/tui/examples/color_test.rs → packages/dioxus-tui/examples/color_test.rs


+ 4 - 2
packages/tui/examples/colorpicker.rs → packages/dioxus-tui/examples/colorpicker.rs

@@ -1,6 +1,7 @@
 use dioxus::core::RenderReturn;
 use dioxus::prelude::*;
-use dioxus_tui::query::Query;
+use dioxus_tui::DioxusElementToNodeId;
+use dioxus_tui::Query;
 use dioxus_tui::Size;
 
 fn main() {
@@ -11,6 +12,7 @@ fn app(cx: Scope) -> Element {
     let hue = use_state(cx, || 0.0);
     let brightness = use_state(cx, || 0.0);
     let tui_query: Query = cx.consume_context().unwrap();
+    let mapping: DioxusElementToNodeId = cx.consume_context().unwrap();
     // disable templates so that every node has an id and can be queried
     cx.render(rsx! {
         div{
@@ -19,7 +21,7 @@ fn app(cx: Scope) -> Element {
             onmousemove: move |evt| {
                 if let RenderReturn::Ready(node) = cx.root_node() {
                     if let Some(id) = node.root_ids.get(0){
-                        let node = tui_query.get(id);
+                        let node = tui_query.get(mapping.get_node_id(id).unwrap());
                         let Size{width, height} = node.size().unwrap();
                         let pos = evt.inner().element_coordinates();
                         hue.set((pos.x as f32/width as f32)*255.0);

+ 0 - 0
packages/tui/examples/components.rs → packages/dioxus-tui/examples/components.rs


+ 0 - 0
packages/tui/examples/example.png → packages/dioxus-tui/examples/example.png


+ 0 - 0
packages/tui/examples/flex.rs → packages/dioxus-tui/examples/flex.rs


+ 0 - 0
packages/tui/examples/hover.rs → packages/dioxus-tui/examples/hover.rs


+ 0 - 0
packages/tui/examples/list.rs → packages/dioxus-tui/examples/list.rs


+ 0 - 0
packages/tui/examples/margin.rs → packages/dioxus-tui/examples/margin.rs


+ 0 - 0
packages/tui/examples/quadrants.rs → packages/dioxus-tui/examples/quadrants.rs


+ 0 - 0
packages/tui/examples/readme.rs → packages/dioxus-tui/examples/readme.rs


+ 5 - 19
packages/tui/benches/update.rs → packages/dioxus-tui/examples/stress.rs

@@ -1,26 +1,12 @@
-use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
 use dioxus::prelude::*;
 use dioxus_tui::{Config, TuiContext};
 
-criterion_group!(mbenches, tui_update);
-criterion_main!(mbenches);
-
 /// This benchmarks the cache performance of the TUI for small edits by changing one box at a time.
-fn tui_update(c: &mut Criterion) {
-    let mut group = c.benchmark_group("Update boxes");
-
-    // We can also use loops to define multiple benchmarks, even over multiple dimensions.
-    for size in 1..=8usize {
-        let parameter_string = format!("{}", (3 * size).pow(2));
-        group.bench_with_input(
-            BenchmarkId::new("size", parameter_string),
-            &size,
-            |b, size| {
-                b.iter(|| {
-                    dioxus_tui::launch_cfg_with_props(app, *size, Config::default().with_headless())
-                })
-            },
-        );
+fn main() {
+    for size in 1..=20usize {
+        for _ in 0..10 {
+            dioxus_tui::launch_cfg_with_props(app, size, Config::default().with_headless())
+        }
     }
 }
 

+ 0 - 0
packages/tui/examples/task.rs → packages/dioxus-tui/examples/task.rs


+ 0 - 0
packages/tui/examples/text.rs → packages/dioxus-tui/examples/text.rs


+ 0 - 1
packages/tui/examples/widgets.rs → packages/dioxus-tui/examples/widgets.rs

@@ -1,7 +1,6 @@
 use dioxus::prelude::*;
 use dioxus_html::FormData;
 use dioxus_tui::prelude::*;
-use dioxus_tui::Config;
 
 fn main() {
     dioxus_tui::launch_cfg(app, Config::new());

+ 143 - 0
packages/dioxus-tui/src/lib.rs

@@ -0,0 +1,143 @@
+pub mod prelude;
+pub mod widgets;
+
+use std::{
+    ops::Deref,
+    rc::Rc,
+    sync::{Arc, RwLock},
+};
+
+use dioxus_core::{Component, ElementId, VirtualDom};
+use dioxus_html::EventData;
+use dioxus_native_core::dioxus::{DioxusState, NodeImmutableDioxusExt};
+use dioxus_native_core::prelude::*;
+
+pub use rink::{query::Query, Config, RenderingMode, Size, TuiContext};
+use rink::{render, Driver};
+
+pub fn launch(app: Component<()>) {
+    launch_cfg(app, Config::default())
+}
+
+pub fn launch_cfg(app: Component<()>, cfg: Config) {
+    launch_cfg_with_props(app, (), cfg);
+}
+
+pub fn launch_cfg_with_props<Props: 'static>(app: Component<Props>, props: Props, cfg: Config) {
+    render(cfg, |rdom, taffy, event_tx| {
+        let dioxus_state = {
+            let mut rdom = rdom.write().unwrap();
+            DioxusState::create(&mut rdom)
+        };
+        let dioxus_state = Rc::new(RwLock::new(dioxus_state));
+        let mut vdom = VirtualDom::new_with_props(app, props)
+            .with_root_context(TuiContext::new(event_tx))
+            .with_root_context(Query::new(rdom.clone(), taffy.clone()))
+            .with_root_context(DioxusElementToNodeId {
+                mapping: dioxus_state.clone(),
+            });
+        let muts = vdom.rebuild();
+        let mut rdom = rdom.write().unwrap();
+        dioxus_state
+            .write()
+            .unwrap()
+            .apply_mutations(&mut rdom, muts);
+        DioxusRenderer {
+            vdom,
+            dioxus_state,
+            #[cfg(all(feature = "hot-reload", debug_assertions))]
+            hot_reload_rx: {
+                let (hot_reload_tx, hot_reload_rx) =
+                    tokio::sync::mpsc::unbounded_channel::<dioxus_hot_reload::HotReloadMsg>();
+                dioxus_hot_reload::connect(move |msg| {
+                    let _ = hot_reload_tx.send(msg);
+                });
+                hot_reload_rx
+            },
+        }
+    })
+    .unwrap();
+}
+
+struct DioxusRenderer {
+    vdom: VirtualDom,
+    dioxus_state: Rc<RwLock<DioxusState>>,
+    #[cfg(all(feature = "hot-reload", debug_assertions))]
+    hot_reload_rx: tokio::sync::mpsc::UnboundedReceiver<dioxus_hot_reload::HotReloadMsg>,
+}
+
+impl Driver for DioxusRenderer {
+    fn update(&mut self, rdom: &Arc<RwLock<RealDom>>) {
+        let muts = self.vdom.render_immediate();
+        {
+            let mut rdom = rdom.write().unwrap();
+            self.dioxus_state
+                .write()
+                .unwrap()
+                .apply_mutations(&mut rdom, muts);
+        }
+    }
+
+    fn handle_event(
+        &mut self,
+        rdom: &Arc<RwLock<RealDom>>,
+        id: NodeId,
+        event: &str,
+        value: Rc<EventData>,
+        bubbles: bool,
+    ) {
+        let id = { rdom.read().unwrap().get(id).unwrap().mounted_id() };
+        if let Some(id) = id {
+            self.vdom
+                .handle_event(event, value.deref().clone().into_any(), id, bubbles);
+        }
+    }
+
+    fn poll_async(&mut self) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + '_>> {
+        #[cfg(all(feature = "hot-reload", debug_assertions))]
+        return Box::pin(async {
+            let hot_reload_wait = self.hot_reload_rx.recv();
+            let mut hot_reload_msg = None;
+            let wait_for_work = self.vdom.wait_for_work();
+            tokio::select! {
+                Some(msg) = hot_reload_wait => {
+                    #[cfg(all(feature = "hot-reload", debug_assertions))]
+                    {
+                        hot_reload_msg = Some(msg);
+                    }
+                    #[cfg(not(all(feature = "hot-reload", debug_assertions)))]
+                    let () = msg;
+                }
+                _ = wait_for_work => {}
+            }
+            // if we have a new template, replace the old one
+            if let Some(msg) = hot_reload_msg {
+                match msg {
+                    dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => {
+                        self.vdom.replace_template(template);
+                    }
+                    dioxus_hot_reload::HotReloadMsg::Shutdown => {
+                        std::process::exit(0);
+                    }
+                }
+            }
+        });
+
+        #[cfg(not(all(feature = "hot-reload", debug_assertions)))]
+        Box::pin(self.vdom.wait_for_work())
+    }
+}
+
+#[derive(Clone)]
+pub struct DioxusElementToNodeId {
+    mapping: Rc<RwLock<DioxusState>>,
+}
+
+impl DioxusElementToNodeId {
+    pub fn get_node_id(&self, element_id: ElementId) -> Option<NodeId> {
+        self.mapping
+            .read()
+            .unwrap()
+            .try_element_to_node_id(element_id)
+    }
+}

+ 1 - 0
packages/tui/src/prelude/mod.rs → packages/dioxus-tui/src/prelude/mod.rs

@@ -1 +1,2 @@
 pub use crate::widgets::*;
+pub use rink::Config;

+ 2 - 2
packages/tui/src/widgets/button.rs → packages/dioxus-tui/src/widgets/button.rs

@@ -37,7 +37,7 @@ pub(crate) fn Button<'a>(cx: Scope<'a, ButtonProps>) -> Element<'a> {
         }
         state.set(new_state);
     };
-    cx.render(rsx! {
+    render! {
         div{
             width: "{width}",
             height: "{height}",
@@ -55,5 +55,5 @@ pub(crate) fn Button<'a>(cx: Scope<'a, ButtonProps>) -> Element<'a> {
             },
             "{text}"
         }
-    })
+    }
 }

+ 2 - 2
packages/tui/src/widgets/checkbox.rs → packages/dioxus-tui/src/widgets/checkbox.rs

@@ -61,7 +61,7 @@ pub(crate) fn CheckBox<'a>(cx: Scope<'a, CheckBoxProps>) -> Element<'a> {
         }
         state.set(new_state);
     };
-    cx.render(rsx! {
+    render! {
         div {
             width: "{width}",
             height: "{height}",
@@ -78,5 +78,5 @@ pub(crate) fn CheckBox<'a>(cx: Scope<'a, CheckBoxProps>) -> Element<'a> {
             },
             "{text}"
         }
-    })
+    }
 }

+ 0 - 0
packages/tui/src/widgets/input.rs → packages/dioxus-tui/src/widgets/input.rs


+ 22 - 0
packages/dioxus-tui/src/widgets/mod.rs

@@ -0,0 +1,22 @@
+mod button;
+mod checkbox;
+mod input;
+mod number;
+mod password;
+mod slider;
+mod textbox;
+
+use dioxus_core::{RenderReturn, Scope};
+use dioxus_native_core::NodeId;
+pub use input::*;
+
+use crate::DioxusElementToNodeId;
+
+pub(crate) fn get_root_id<T>(cx: Scope<T>) -> Option<NodeId> {
+    if let RenderReturn::Ready(sync) = cx.root_node() {
+        let mapping: DioxusElementToNodeId = cx.consume_context()?;
+        mapping.get_node_id(sync.root_ids.get(0)?)
+    } else {
+        None
+    }
+}

+ 4 - 4
packages/tui/src/widgets/number.rs → packages/dioxus-tui/src/widgets/number.rs

@@ -1,11 +1,11 @@
 use crate::widgets::get_root_id;
-use crate::Query;
 use crossterm::{cursor::MoveTo, execute};
 use dioxus::prelude::*;
 use dioxus_elements::input_data::keyboard_types::Key;
 use dioxus_html as dioxus_elements;
 use dioxus_html::FormData;
 use dioxus_native_core::utils::cursor::{Cursor, Pos};
+use rink::Query;
 use std::{collections::HashMap, io::stdout};
 use taffy::geometry::Point;
 
@@ -99,7 +99,7 @@ pub(crate) fn NumbericInput<'a>(cx: Scope<'a, NumbericInputProps>) -> Element<'a
         update(text.clone());
     };
 
-    cx.render(rsx! {
+    render! {
         div{
             width: "{width}",
             height: "{height}",
@@ -113,7 +113,7 @@ pub(crate) fn NumbericInput<'a>(cx: Scope<'a, NumbericInputProps>) -> Element<'a
                 };
                 if is_text{
                     let mut text = text_ref.write();
-                    cursor.write().handle_input(&k, &mut *text, max_len);
+                    cursor.write().handle_input(&k.code(), &k.key(), &k.modifiers(), &mut *text, max_len);
                     update(text.clone());
 
                     let node = tui_query.get(get_root_id(cx).unwrap());
@@ -205,5 +205,5 @@ pub(crate) fn NumbericInput<'a>(cx: Scope<'a, NumbericInputProps>) -> Element<'a
 
             "{text_after_second_cursor}"
         }
-    })
+    }
 }

+ 6 - 4
packages/tui/src/widgets/password.rs → packages/dioxus-tui/src/widgets/password.rs

@@ -1,11 +1,11 @@
 use crate::widgets::get_root_id;
-use crate::Query;
 use crossterm::{cursor::*, execute};
 use dioxus::prelude::*;
 use dioxus_elements::input_data::keyboard_types::Key;
 use dioxus_html as dioxus_elements;
 use dioxus_html::FormData;
 use dioxus_native_core::utils::cursor::{Cursor, Pos};
+use rink::Query;
 use std::{collections::HashMap, io::stdout};
 use taffy::geometry::Point;
 
@@ -88,7 +88,9 @@ pub(crate) fn Password<'a>(cx: Scope<'a, PasswordProps>) -> Element<'a> {
             return;
         }
         let mut text = text_ref.write();
-        cursor.write().handle_input(&k, &mut *text, max_len);
+        cursor
+            .write()
+            .handle_input(&k.code(), &k.key(), &k.modifiers(), &mut *text, max_len);
         if let Some(input_handler) = &cx.props.raw_oninput {
             input_handler.call(FormData {
                 value: text.clone(),
@@ -114,7 +116,7 @@ pub(crate) fn Password<'a>(cx: Scope<'a, PasswordProps>) -> Element<'a> {
         }
     };
 
-    cx.render(rsx! {
+    render! {
         div {
             width: "{width}",
             height: "{height}",
@@ -187,5 +189,5 @@ pub(crate) fn Password<'a>(cx: Scope<'a, PasswordProps>) -> Element<'a> {
 
             "{text_after_second_cursor}"
         }
-    })
+    }
 }

+ 3 - 3
packages/tui/src/widgets/slider.rs → packages/dioxus-tui/src/widgets/slider.rs

@@ -1,11 +1,11 @@
 use std::collections::HashMap;
 
 use crate::widgets::get_root_id;
-use crate::Query;
 use dioxus::prelude::*;
 use dioxus_elements::input_data::keyboard_types::Key;
 use dioxus_html as dioxus_elements;
 use dioxus_html::FormData;
+use rink::Query;
 
 #[derive(Props)]
 pub(crate) struct SliderProps<'a> {
@@ -62,7 +62,7 @@ pub(crate) fn Slider<'a>(cx: Scope<'a, SliderProps>) -> Element<'a> {
         }
     };
 
-    cx.render(rsx! {
+    render! {
         div{
             width: "{width}",
             height: "{height}",
@@ -103,5 +103,5 @@ pub(crate) fn Slider<'a>(cx: Scope<'a, SliderProps>) -> Element<'a> {
                 background_color: "rgba(10,10,10,0.5)",
             }
         }
-    })
+    }
 }

+ 4 - 4
packages/tui/src/widgets/textbox.rs → packages/dioxus-tui/src/widgets/textbox.rs

@@ -1,11 +1,11 @@
 use crate::widgets::get_root_id;
-use crate::Query;
 use crossterm::{cursor::*, execute};
 use dioxus::prelude::*;
 use dioxus_elements::input_data::keyboard_types::Key;
 use dioxus_html as dioxus_elements;
 use dioxus_html::FormData;
 use dioxus_native_core::utils::cursor::{Cursor, Pos};
+use rink::Query;
 use std::{collections::HashMap, io::stdout};
 use taffy::geometry::Point;
 
@@ -79,7 +79,7 @@ pub(crate) fn TextBox<'a>(cx: Scope<'a, TextBoxProps>) -> Element<'a> {
         "solid"
     };
 
-    cx.render(rsx! {
+    render! {
         div{
             width: "{width}",
             height: "{height}",
@@ -90,7 +90,7 @@ pub(crate) fn TextBox<'a>(cx: Scope<'a, TextBoxProps>) -> Element<'a> {
                     return;
                 }
                 let mut text = text_ref.write();
-                cursor.write().handle_input(&k, &mut *text, max_len);
+                cursor.write().handle_input(&k.code(), &k.key(), &k.modifiers(), &mut *text, max_len);
                 if let Some(input_handler) = &cx.props.raw_oninput{
                     input_handler.call(FormData{
                         value: text.clone(),
@@ -178,5 +178,5 @@ pub(crate) fn TextBox<'a>(cx: Scope<'a, TextBoxProps>) -> Element<'a> {
 
             "{text_after_second_cursor}"
         }
-    })
+    }
 }

+ 0 - 0
packages/tui/test.html → packages/dioxus-tui/test.html


+ 0 - 0
packages/tui/tests/events.rs → packages/dioxus-tui/tests/events.rs


+ 348 - 525
packages/native-core-macro/src/lib.rs

@@ -1,563 +1,386 @@
 extern crate proc_macro;
 
-mod sorted_slice;
+use std::collections::HashSet;
 
 use proc_macro::TokenStream;
-use quote::{quote, ToTokens, __private::Span};
-use sorted_slice::StrSlice;
-use syn::parenthesized;
-use syn::parse::ParseBuffer;
-use syn::punctuated::Punctuated;
-use syn::{
-    self,
-    parse::{Parse, ParseStream, Result},
-    parse_macro_input, parse_quote, Error, Field, Ident, Token, Type,
-};
+use quote::{format_ident, quote};
+use syn::{parse_macro_input, ItemImpl, Type, TypePath, TypeTuple};
 
-/// Sorts a slice of string literals at compile time.
-#[proc_macro]
-pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
-    let slice: StrSlice = parse_macro_input!(input as StrSlice);
-    let strings = slice.map.values();
-    quote!([#(#strings, )*]).into()
-}
-
-#[derive(PartialEq, Debug, Clone)]
-enum DependencyKind {
-    Node,
-    Child,
-    Parent,
-}
+/// A helper attribute for deriving `State` for a struct.
+#[proc_macro_attribute]
+pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
+    let impl_block: syn::ItemImpl = parse_macro_input!(input as syn::ItemImpl);
 
-/// Derive's the state from any elements that have a node_dep_state, child_dep_state, parent_dep_state, or state attribute.
-///
-/// # Declaring elements
-/// Each of the attributes require specifying the members of the struct it depends on to allow the macro to find the optimal resultion order.
-/// These dependencies should match the types declared in the trait the member implements.
-///
-/// # The node_dep_state attribute
-/// The node_dep_state attribute declares a member that implements the NodeDepState trait.
-/// ```rust, ignore
-/// #[derive(State)]
-/// struct MyStruct {
-///     // MyDependency implements ChildDepState<()>
-///     #[node_dep_state()]
-///     my_dependency_1: MyDependency,
-///     // MyDependency2 implements ChildDepState<(MyDependency,)>
-///     #[node_dep_state(my_dependency_1)]
-///     my_dependency_2: MyDependency2,
-/// }
-/// // or
-/// #[derive(State)]
-/// struct MyStruct {
-///     // MyDependnancy implements NodeDepState<()>
-///     #[node_dep_state()]
-///     my_dependency_1: MyDependency,
-///     // MyDependency2 implements NodeDepState<()>
-///     #[node_dep_state()]
-///     my_dependency_2: MyDependency2,
-///     // MyDependency3 implements NodeDepState<(MyDependency, MyDependency2)> with Ctx = f32
-///     #[node_dep_state((my_dependency_1, my_dependency_2), f32)]
-///     my_dependency_3: MyDependency2,
-/// }
-/// ```
-/// # The child_dep_state attribute
-/// The child_dep_state attribute declares a member that implements the ChildDepState trait.
-/// ```rust, ignore
-/// #[derive(State)]
-/// struct MyStruct {
-///     // MyDependnacy implements ChildDepState with DepState = Self
-///     #[child_dep_state(my_dependency_1)]
-///     my_dependency_1: MyDependency,
-/// }
-/// // or
-/// #[derive(State)]
-/// struct MyStruct {
-///     // MyDependnacy implements ChildDepState with DepState = Self
-///     #[child_dep_state(my_dependency_1)]
-///     my_dependency_1: MyDependency,
-///     // MyDependnacy2 implements ChildDepState with DepState = MyDependency and Ctx = f32
-///     #[child_dep_state(my_dependency_1, f32)]
-///     my_dependency_2: MyDependency2,
-/// }
-/// ```
-/// # The parent_dep_state attribute
-/// The parent_dep_state attribute declares a member that implements the ParentDepState trait.
-/// The parent_dep_state attribute can be called in the forms:
-/// ```rust, ignore
-/// #[derive(State)]
-/// struct MyStruct {
-///     // MyDependnacy implements ParentDepState with DepState = Self
-///     #[parent_dep_state(my_dependency_1)]
-///     my_dependency_1: MyDependency,
-/// }
-/// // or
-/// #[derive(State)]
-/// struct MyStruct {
-///     // MyDependnacy implements ParentDepState with DepState = Self
-///     #[parent_dep_state(my_dependency_1)]
-///     my_dependency_1: MyDependency,
-///     // MyDependnacy2 implements ParentDepState with DepState = MyDependency and Ctx = f32
-///     #[parent_dep_state(my_dependency_1, f32)]
-///     my_dependency_2: MyDependency2,
-/// }
-/// ```
-///
-/// # Combining dependancies
-/// The node_dep_state, parent_dep_state, and child_dep_state attributes can be combined to allow for more complex dependancies.
-/// For example if we wanted to combine the font that is passed from the parent to the child and the layout of the size children to find the size of the current node we could do:
-/// ```rust, ignore
-/// #[derive(State)]
-/// struct MyStruct {
-///     // ChildrenSize implements ChildDepState with DepState = Size
-///     #[child_dep_state(size)]
-///     children_size: ChildrenSize,
-///     // FontSize implements ParentDepState with DepState = Self
-///     #[parent_dep_state(font_size)]
-///     font_size: FontSize,
-///     // Size implements NodeDepState<(ChildrenSize, FontSize)>
-///     #[parent_dep_state((children_size, font_size))]
-///     size: Size,
-/// }
-/// ```
-///
-/// # The state attribute
-/// The state macro declares a member that implements the State trait. This allows you to organize your state into multiple isolated components.
-/// Unlike the other attributes, the state attribute does not accept any arguments, because a nested state cannot depend on any other part of the state.
-///
-/// # Custom values
-///
-/// If your state has a custom value type you can specify it with the state attribute.
-///
-/// ```rust, ignore
-/// #[derive(State)]
-/// #[state(custom_value = MyCustomType)]
-/// struct MyStruct {
-///     // ...
-/// }
-#[proc_macro_derive(
-    State,
-    attributes(node_dep_state, child_dep_state, parent_dep_state, state)
-)]
-pub fn state_macro_derive(input: TokenStream) -> TokenStream {
-    let ast = syn::parse(input).unwrap();
-    impl_derive_macro(&ast)
-}
+    let has_create_fn = impl_block
+        .items
+        .iter()
+        .any(|item| matches!(item, syn::ImplItem::Method(method) if method.sig.ident == "create"));
 
-fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
-    let custom_type = ast
-        .attrs
+    let parent_dependencies = impl_block
+        .items
         .iter()
-        .find(|a| a.path.is_ident("state"))
-        .and_then(|attr| {
-            // parse custom_type = "MyType"
-            let assignment = attr.parse_args::<syn::Expr>().unwrap();
-            if let syn::Expr::Assign(assign) = assignment {
-                let (left, right) = (&*assign.left, &*assign.right);
-                if let syn::Expr::Path(e) = left {
-                    let path = &e.path;
-                    if let Some(ident) = path.get_ident() {
-                        if ident == "custom_value" {
-                            return match right {
-                                syn::Expr::Path(e) => {
-                                    let path = &e.path;
-                                    Some(quote! {#path})
-                                }
-                                _ => None,
-                            };
-                        }
-                    }
-                }
+        .find_map(|item| {
+            if let syn::ImplItem::Type(syn::ImplItemType { ident, ty, .. }) = item {
+                (ident == "ParentDependencies").then_some(ty)
+            } else {
+                None
             }
-            None
         })
-        .unwrap_or(quote! {()});
-    let type_name = &ast.ident;
-    let fields: Vec<_> = match &ast.data {
-        syn::Data::Struct(data) => match &data.fields {
-            syn::Fields::Named(e) => &e.named,
-            syn::Fields::Unnamed(_) => todo!("unnamed fields"),
-            syn::Fields::Unit => todo!("unit structs"),
-        }
+        .expect("ParentDependencies must be defined");
+    let child_dependencies = impl_block
+        .items
+        .iter()
+        .find_map(|item| {
+            if let syn::ImplItem::Type(syn::ImplItemType { ident, ty, .. }) = item {
+                (ident == "ChildDependencies").then_some(ty)
+            } else {
+                None
+            }
+        })
+        .expect("ChildDependencies must be defined");
+    let node_dependencies = impl_block
+        .items
         .iter()
-        .collect(),
-        _ => unimplemented!(),
+        .find_map(|item| {
+            if let syn::ImplItem::Type(syn::ImplItemType { ident, ty, .. }) = item {
+                (ident == "NodeDependencies").then_some(ty)
+            } else {
+                None
+            }
+        })
+        .expect("NodeDependencies must be defined");
+
+    let this_type = &impl_block.self_ty;
+    let this_type = extract_type_path(this_type)
+        .unwrap_or_else(|| panic!("Self must be a type path, found {}", quote!(#this_type)));
+
+    let mut combined_dependencies = HashSet::new();
+
+    let self_path: TypePath = syn::parse_quote!(Self);
+
+    let parent_dependencies = match extract_tuple(parent_dependencies) {
+        Some(tuple) => {
+            let mut parent_dependencies = Vec::new();
+            for type_ in &tuple.elems {
+                let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
+                    panic!(
+                        "ParentDependencies must be a tuple of type paths, found {}",
+                        quote!(#type_)
+                    )
+                });
+                if type_ == self_path {
+                    type_ = this_type.clone();
+                }
+                combined_dependencies.insert(type_.clone());
+                parent_dependencies.push(type_);
+            }
+            parent_dependencies
+        }
+        _ => panic!(
+            "ParentDependencies must be a tuple, found {}",
+            quote!(#parent_dependencies)
+        ),
     };
-    let strct = Struct::new(type_name.clone(), &fields);
-    match StateStruct::parse(&fields, &strct) {
-        Ok(state_strct) => {
-            let passes = state_strct.state_members.iter().map(|m| {
-                let unit = &m.mem.unit_type;
-                match m.dep_kind {
-                    DependencyKind::Node => quote! {dioxus_native_core::AnyPass::Node(&#unit)},
-                    DependencyKind::Child => quote! {dioxus_native_core::AnyPass::Upward(&#unit)},
-                    DependencyKind::Parent => {
-                        quote! {dioxus_native_core::AnyPass::Downward(&#unit)}
-                    }
+    let child_dependencies = match extract_tuple(child_dependencies) {
+        Some(tuple) => {
+            let mut child_dependencies = Vec::new();
+            for type_ in &tuple.elems {
+                let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
+                    panic!(
+                        "ChildDependencies must be a tuple of type paths, found {}",
+                        quote!(#type_)
+                    )
+                });
+                if type_ == self_path {
+                    type_ = this_type.clone();
                 }
-            });
-            let member_types = state_strct.state_members.iter().map(|m| &m.mem.ty);
-            let impl_members = state_strct
-                .state_members
-                .iter()
-                .map(|m| m.impl_pass(state_strct.ty, &custom_type));
-
-            let gen = quote! {
-                #(#impl_members)*
-                impl State<#custom_type> for #type_name {
-                    const PASSES: &'static [dioxus_native_core::AnyPass<dioxus_native_core::node::Node<Self, #custom_type>>] = &[
-                        #(#passes),*
-                    ];
-                    const MASKS: &'static [dioxus_native_core::NodeMask] = &[#(#member_types::NODE_MASK),*];
+                combined_dependencies.insert(type_.clone());
+                child_dependencies.push(type_);
+            }
+            child_dependencies
+        }
+        _ => panic!(
+            "ChildDependencies must be a tuple, found {}",
+            quote!(#child_dependencies)
+        ),
+    };
+    let node_dependencies = match extract_tuple(node_dependencies) {
+        Some(tuple) => {
+            let mut node_dependencies = Vec::new();
+            for type_ in &tuple.elems {
+                let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
+                    panic!(
+                        "NodeDependencies must be a tuple of type paths, found {}",
+                        quote!(#type_)
+                    )
+                });
+                if type_ == self_path {
+                    type_ = this_type.clone();
                 }
-            };
-            gen.into()
+                combined_dependencies.insert(type_.clone());
+                node_dependencies.push(type_);
+            }
+            node_dependencies
         }
-        Err(e) => e.into_compile_error().into(),
-    }
-}
+        _ => panic!(
+            "NodeDependencies must be a tuple, found {}",
+            quote!(#node_dependencies)
+        ),
+    };
+    combined_dependencies.insert(this_type.clone());
 
-struct Struct {
-    name: Ident,
-    members: Vec<Member>,
-}
+    let combined_dependencies: Vec<_> = combined_dependencies.into_iter().collect();
+    let parent_dependancies_idxes: Vec<_> = parent_dependencies
+        .iter()
+        .filter_map(|ident| combined_dependencies.iter().position(|i| i == ident))
+        .collect();
+    let child_dependencies_idxes: Vec<_> = child_dependencies
+        .iter()
+        .filter_map(|ident| combined_dependencies.iter().position(|i| i == ident))
+        .collect();
+    let node_dependencies_idxes: Vec<_> = node_dependencies
+        .iter()
+        .filter_map(|ident| combined_dependencies.iter().position(|i| i == ident))
+        .collect();
+    let this_type_idx = combined_dependencies
+        .iter()
+        .enumerate()
+        .find_map(|(i, ident)| (this_type == *ident).then_some(i))
+        .unwrap();
+    let this_view = format_ident!("__data{}", this_type_idx);
 
-impl Struct {
-    fn new(name: Ident, fields: &[&Field]) -> Self {
-        let members = fields
-            .iter()
-            .enumerate()
-            .filter_map(|(i, f)| Member::parse(&name, f, i as u64))
-            .collect();
-        Self { name, members }
-    }
-}
+    let combined_dependencies_quote = combined_dependencies.iter().map(|ident| {
+        if ident == &this_type {
+            quote! {shipyard::ViewMut<#ident>}
+        } else {
+            quote! {shipyard::View<#ident>}
+        }
+    });
+    let combined_dependencies_quote = quote!((#(#combined_dependencies_quote,)*));
 
-struct StateStruct<'a> {
-    state_members: Vec<StateMember<'a>>,
-    #[allow(unused)]
-    child_states: Vec<&'a Member>,
-    ty: &'a Ident,
-}
+    let ItemImpl {
+        attrs,
+        defaultness,
+        unsafety,
+        impl_token,
+        generics,
+        trait_,
+        self_ty,
+        items,
+        ..
+    } = impl_block;
+    let for_ = trait_.as_ref().map(|t| t.2);
+    let trait_ = trait_.map(|t| t.1);
 
-impl<'a> StateStruct<'a> {
-    /// Parse the state structure, and find a resolution order that will allow us to update the state for each node in after the state(s) it depends on have been resolved.
-    fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
-        let mut parse_err = Ok(());
-        let mut state_members: Vec<_> = strct
-            .members
-            .iter()
-            .zip(fields.iter())
-            .filter_map(|(m, f)| match StateMember::parse(f, m, strct) {
-                Ok(m) => m,
-                Err(err) => {
-                    parse_err = Err(err);
-                    None
-                }
-            })
-            .collect();
-        parse_err?;
-        for i in 0..state_members.len() {
-            let deps: Vec<_> = state_members[i].dep_mems.iter().map(|m| m.id).collect();
-            for dep in deps {
-                state_members[dep as usize].dependant_mems.push(i as u64);
+    let split_views: Vec<_> = (0..combined_dependencies.len())
+        .map(|i| {
+            let ident = format_ident!("__data{}", i);
+            if i == this_type_idx {
+                quote! {mut #ident}
+            } else {
+                quote! {#ident}
+            }
+        })
+        .collect();
+
+    let node_view = node_dependencies_idxes
+        .iter()
+        .map(|i| format_ident!("__data{}", i))
+        .collect::<Vec<_>>();
+    let get_node_view = {
+        if node_dependencies.is_empty() {
+            quote! {
+                let raw_node = ();
+            }
+        } else {
+            let temps = (0..node_dependencies.len())
+                .map(|i| format_ident!("__temp{}", i))
+                .collect::<Vec<_>>();
+            quote! {
+                let raw_node: (#(*const #node_dependencies,)*) = {
+                    let (#(#temps,)*) = (#(&#node_view,)*).get(id).unwrap_or_else(|err| panic!("Failed to get node view {:?}", err));
+                    (#(#temps as *const _,)*)
+                };
+            }
+        }
+    };
+    let deref_node_view = {
+        if node_dependencies.is_empty() {
+            quote! {
+                let node = raw_node;
+            }
+        } else {
+            let indexes = (0..node_dependencies.len()).map(syn::Index::from);
+            quote! {
+                let node = unsafe { (#(dioxus_native_core::prelude::DependancyView::new(&*raw_node.#indexes),)*) };
             }
         }
+    };
 
-        let child_states = strct
-            .members
-            .iter()
-            .zip(fields.iter())
-            .filter(|(_, f)| {
-                f.attrs.iter().any(|a| {
-                    a.path
-                        .get_ident()
-                        .filter(|i| i.to_string().as_str() == "state")
-                        .is_some()
-                })
-            })
-            .map(|(m, _)| m);
+    let parent_view = parent_dependancies_idxes
+        .iter()
+        .map(|i| format_ident!("__data{}", i))
+        .collect::<Vec<_>>();
+    let get_parent_view = {
+        if parent_dependencies.is_empty() {
+            quote! {
+                let raw_parent = tree.parent_id(id).map(|_| ());
+            }
+        } else {
+            let temps = (0..parent_dependencies.len())
+                .map(|i| format_ident!("__temp{}", i))
+                .collect::<Vec<_>>();
+            quote! {
+                let raw_parent = tree.parent_id(id).and_then(|parent_id| {
+                    let raw_parent: Option<(#(*const #parent_dependencies,)*)> = (#(&#parent_view,)*).get(parent_id).ok().map(|c| {
+                        let (#(#temps,)*) = c;
+                        (#(#temps as *const _,)*)
+                    });
+                    raw_parent
+                });
+            }
+        }
+    };
+    let deref_parent_view = {
+        if parent_dependencies.is_empty() {
+            quote! {
+                let parent = raw_parent;
+            }
+        } else {
+            let indexes = (0..parent_dependencies.len()).map(syn::Index::from);
+            quote! {
+                let parent = unsafe { raw_parent.map(|raw_parent| (#(dioxus_native_core::prelude::DependancyView::new(&*raw_parent.#indexes),)*)) };
+            }
+        }
+    };
 
-        // members need to be sorted so that members are updated after the members they depend on
-        Ok(Self {
-            ty: &strct.name,
-            state_members,
-            child_states: child_states.collect(),
-        })
-    }
-}
+    let child_view = child_dependencies_idxes
+        .iter()
+        .map(|i| format_ident!("__data{}", i))
+        .collect::<Vec<_>>();
+    let get_child_view = {
+        if child_dependencies.is_empty() {
+            quote! {
+                let raw_children: Vec<_> = tree.children_ids(id).into_iter().map(|_| ()).collect();
+            }
+        } else {
+            let temps = (0..child_dependencies.len())
+                .map(|i| format_ident!("__temp{}", i))
+                .collect::<Vec<_>>();
+            quote! {
+                let raw_children: Vec<_> = tree.children_ids(id).into_iter().filter_map(|id| {
+                    let raw_children: Option<(#(*const #child_dependencies,)*)> = (#(&#child_view,)*).get(id).ok().map(|c| {
+                        let (#(#temps,)*) = c;
+                        (#(#temps as *const _,)*)
+                    });
+                    raw_children
+                }).collect();
+            }
+        }
+    };
+    let deref_child_view = {
+        if child_dependencies.is_empty() {
+            quote! {
+                let children = raw_children;
+            }
+        } else {
+            let indexes = (0..child_dependencies.len()).map(syn::Index::from);
+            quote! {
+                let children = unsafe { raw_children.iter().map(|raw_children| (#(dioxus_native_core::prelude::DependancyView::new(&*raw_children.#indexes),)*)).collect::<Vec<_>>() };
+            }
+        }
+    };
 
-fn try_parenthesized(input: ParseStream) -> Result<ParseBuffer> {
-    let inside;
-    parenthesized!(inside in input);
-    Ok(inside)
-}
+    let trait_generics = trait_
+        .as_ref()
+        .unwrap()
+        .segments
+        .last()
+        .unwrap()
+        .arguments
+        .clone();
 
-struct Dependency {
-    ctx_ty: Option<Type>,
-    deps: Vec<Ident>,
-}
+    // if a create function is defined, we don't generate one
+    // otherwise we generate a default one that uses the update function and the default constructor
+    let create_fn = (!has_create_fn).then(|| {
+        quote! {
+            fn create<'a>(
+                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>>,
+                context: &dioxus_native_core::prelude::SendAnyMap,
+            ) -> Self {
+                let mut myself = Self::default();
+                myself.update(node_view, node, parent, children, context);
+                myself
+            }
+        }
+    });
 
-impl Parse for Dependency {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let deps: Option<Punctuated<Ident, Token![,]>> = {
-            try_parenthesized(input)
-                .ok()
-                .and_then(|inside| inside.parse_terminated(Ident::parse).ok())
-        };
-        let deps: Vec<_> = deps
-            .map(|deps| deps.into_iter().collect())
-            .or_else(|| {
-                input
-                    .parse::<Ident>()
-                    .ok()
-                    .filter(|i: &Ident| i != "NONE")
-                    .map(|i| vec![i])
-            })
-            .unwrap_or_default();
-        let comma: Option<Token![,]> = input.parse().ok();
-        let ctx_ty = input.parse().ok();
-        Ok(Self {
-            ctx_ty: comma.and(ctx_ty),
-            deps,
-        })
-    }
-}
+    quote!(
+        #(#attrs)*
+        #defaultness #unsafety #impl_token #generics #trait_ #for_ #self_ty {
+            #create_fn
 
-/// The type of the member and the ident of the member
-#[derive(PartialEq, Debug)]
-struct Member {
-    id: u64,
-    ty: Type,
-    unit_type: Ident,
-    ident: Ident,
-}
+            #(#items)*
 
-impl Member {
-    fn parse(parent: &Ident, field: &Field, id: u64) -> Option<Self> {
-        Some(Self {
-            id,
-            ty: field.ty.clone(),
-            unit_type: Ident::new(
-                ("_Unit".to_string()
-                    + parent.to_token_stream().to_string().as_str()
-                    + field.ty.to_token_stream().to_string().as_str())
-                .as_str(),
-                Span::call_site(),
-            ),
-            ident: field.ident.as_ref()?.clone(),
-        })
-    }
-}
+            fn workload_system(type_id: std::any::TypeId, dependants: dioxus_native_core::exports::FxHashSet<std::any::TypeId>, pass_direction: dioxus_native_core::prelude::PassDirection) -> dioxus_native_core::exports::shipyard::WorkloadSystem {
+                use dioxus_native_core::exports::shipyard::{IntoWorkloadSystem, Get, AddComponent};
+                use dioxus_native_core::tree::TreeRef;
+                use dioxus_native_core::prelude::{NodeType, NodeView};
 
-#[derive(Debug, Clone)]
-struct StateMember<'a> {
-    mem: &'a Member,
-    // the kind of dependncies this state has
-    dep_kind: DependencyKind,
-    // the depenancy and if it is satified
-    dep_mems: Vec<&'a Member>,
-    // any members that depend on this member
-    dependant_mems: Vec<u64>,
-    // the context this state requires
-    ctx_ty: Option<Type>,
-}
+                let node_mask = Self::NODE_MASK.build();
 
-impl<'a> StateMember<'a> {
-    fn parse(
-        field: &Field,
-        mem: &'a Member,
-        parent: &'a Struct,
-    ) -> Result<Option<StateMember<'a>>> {
-        let mut err = Ok(());
-        let member = field.attrs.iter().find_map(|a| {
-            let dep_kind = a
-                .path
-                .get_ident()
-                .and_then(|i| match i.to_string().as_str() {
-                    "node_dep_state" => Some(DependencyKind::Node),
-                    "child_dep_state" => Some(DependencyKind::Child),
-                    "parent_dep_state" => Some(DependencyKind::Parent),
-                    _ => None,
-                })?;
-            match a.parse_args::<Dependency>() {
-                Ok(dependency) => {
-                    let dep_mems = dependency
-                        .deps
-                        .iter()
-                        .filter_map(|name| {
-                            if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
-                                Some(found)
-                            } else {
-                                err = Err(Error::new(
-                                    name.span(),
-                                    format!("{} not found in {}", name, parent.name),
-                                ));
-                                None
-                            }
-                        })
-                        .collect();
-                    Some(Self {
-                        mem,
-                        dep_kind,
-                        dep_mems,
-                        dependant_mems: Vec::new(),
-                        ctx_ty: dependency.ctx_ty,
-                    })
-                }
-                Err(e) => {
-                    err = Err(e);
-                    None
-                }
-            }
-        });
-        err?;
-        Ok(member)
-    }
+                (move |data: #combined_dependencies_quote, run_view: dioxus_native_core::prelude::RunPassView #trait_generics| {
+                    let (#(#split_views,)*) = data;
+                    let tree = run_view.tree.clone();
+                    let node_types = run_view.node_type.clone();
+                    dioxus_native_core::prelude::run_pass(type_id, dependants.clone(), pass_direction, run_view, |id, context| {
+                        let node_data: &NodeType<_> = node_types.get(id).unwrap_or_else(|err| panic!("Failed to get node type {:?}", err));
+                        // get all of the states from the tree view
+                        // Safety: No node has itself as a parent or child.
+                        let raw_myself: Option<*mut Self> = (&mut #this_view).get(id).ok().map(|c| c as *mut _);
+                        #get_node_view
+                        #get_parent_view
+                        #get_child_view
 
-    /// generate code to call the resolve function for the state. This does not handle checking if resolving the state is necessary, or marking the states that depend on this state as dirty.
-    fn impl_pass(
-        &self,
-        parent_type: &Ident,
-        custom_type: impl ToTokens,
-    ) -> quote::__private::TokenStream {
-        let ident = &self.mem.ident;
-        let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
-            if ctx_ty == &parse_quote!(()) {
-                quote! {&()}
-            } else {
-                let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
-                quote! {ctx.get().expect(#msg)}
-            }
-        } else {
-            quote! {&()}
-        };
+                        let myself: Option<&mut Self> = unsafe { raw_myself.map(|val| &mut *val) };
+                        #deref_node_view
+                        #deref_parent_view
+                        #deref_child_view
 
-        let ty = &self.mem.ty;
-        let unit_type = &self.mem.unit_type;
-        let node_view =
-            quote!(dioxus_native_core::node_ref::NodeView::new(&node.node_data, #ty::NODE_MASK));
-        let dep_idents = self.dep_mems.iter().map(|m| &m.ident);
-        let impl_specific = match self.dep_kind {
-            DependencyKind::Node => {
-                quote! {
-                    impl dioxus_native_core::NodePass<dioxus_native_core::node::Node<#parent_type, #custom_type>> for #unit_type {
-                        fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type, #custom_type>, ctx: &dioxus_native_core::SendAnyMap) -> bool {
-                            node.state.#ident.reduce(#node_view, (#(&node.state.#dep_idents,)*), #get_ctx)
-                        }
-                    }
-                }
-            }
-            DependencyKind::Child => {
-                let update = if self.dep_mems.iter().any(|m| m.id == self.mem.id) {
-                    quote! {
-                        if update {
-                            dioxus_native_core::PassReturn{
-                                progress: true,
-                                mark_dirty: true,
-                            }
-                        } else {
-                            dioxus_native_core::PassReturn{
-                                progress: false,
-                                mark_dirty: false,
-                            }
+                        let view = NodeView::new(id, node_data, &node_mask);
+                        if let Some(myself) = myself {
+                            myself
+                                .update(view, node, parent, children, context)
                         }
-                    }
-                } else {
-                    quote! {
-                        if update {
-                            dioxus_native_core::PassReturn{
-                                progress: false,
-                                mark_dirty: true,
-                            }
-                        } else {
-                            dioxus_native_core::PassReturn{
-                                progress: false,
-                                mark_dirty: false,
-                            }
-                        }
-                    }
-                };
-                quote!(
-                    impl dioxus_native_core::UpwardPass<dioxus_native_core::node::Node<#parent_type, #custom_type>> for #unit_type{
-                        fn pass<'a>(
-                            &self,
-                            node: &mut dioxus_native_core::node::Node<#parent_type, #custom_type>,
-                            children: &mut dyn Iterator<Item = &'a mut dioxus_native_core::node::Node<#parent_type, #custom_type>>,
-                            ctx: &dioxus_native_core::SendAnyMap,
-                        ) -> dioxus_native_core::PassReturn {
-                            let update = node.state.#ident.reduce(#node_view, children.map(|c| (#(&c.state.#dep_idents,)*)), #get_ctx);
-                            #update
-                        }
-                    }
-                )
-            }
-            DependencyKind::Parent => {
-                let update = if self.dep_mems.iter().any(|m| m.id == self.mem.id) {
-                    quote! {
-                        if update {
-                            dioxus_native_core::PassReturn{
-                                progress: true,
-                                mark_dirty: true,
-                            }
-                        } else {
-                            dioxus_native_core::PassReturn{
-                                progress: false,
-                                mark_dirty: false,
-                            }
+                        else {
+                            (&mut #this_view).add_component_unchecked(
+                                id,
+                                Self::create(view, node, parent, children, context));
+                            true
                         }
-                    }
-                } else {
-                    quote! {
-                        if update {
-                            dioxus_native_core::PassReturn{
-                                progress: false,
-                                mark_dirty: true,
-                            }
-                        } else {
-                            dioxus_native_core::PassReturn{
-                                progress: false,
-                                mark_dirty: false,
-                            }
-                        }
-                    }
-                };
-                quote!(
-                    impl dioxus_native_core::DownwardPass<dioxus_native_core::node::Node<#parent_type, #custom_type>> for #unit_type {
-                        fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type, #custom_type>, parent: Option<&mut dioxus_native_core::node::Node<#parent_type, #custom_type>>, ctx: &dioxus_native_core::SendAnyMap) -> dioxus_native_core::PassReturn{
-                            let update = node.state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.state.#dep_idents,)*)), #get_ctx);
-                            #update
-                        }
-                    }
-                )
-            }
-        };
-        let pass_id = self.mem.id;
-        let depenancies = self.dep_mems.iter().map(|m| m.id);
-        let dependants = &self.dependant_mems;
-        let mask = self
-            .dep_mems
-            .iter()
-            .map(|m| 1u64 << m.id)
-            .fold(1 << self.mem.id, |a, b| a | b);
-        quote! {
-            #[derive(Clone, Copy)]
-            struct #unit_type;
-            #impl_specific
-            impl dioxus_native_core::Pass for #unit_type {
-                fn pass_id(&self) -> dioxus_native_core::PassId {
-                    dioxus_native_core::PassId(#pass_id)
-                }
-                fn dependancies(&self) -> &'static [dioxus_native_core::PassId] {
-                    &[#(dioxus_native_core::PassId(#depenancies)),*]
-                }
-                fn dependants(&self) -> &'static [dioxus_native_core::PassId] {
-                    &[#(dioxus_native_core::PassId(#dependants)),*]
-                }
-                fn mask(&self) -> dioxus_native_core::MemberMask {
-                    dioxus_native_core::MemberMask(#mask)
-                }
+                    })
+                }).into_workload_system().unwrap()
             }
         }
+    )
+    .into()
+}
+
+fn extract_tuple(ty: &Type) -> Option<TypeTuple> {
+    match ty {
+        Type::Tuple(tuple) => Some(tuple.clone()),
+        Type::Group(group) => extract_tuple(&group.elem),
+        _ => None,
+    }
+}
+
+fn extract_type_path(ty: &Type) -> Option<TypePath> {
+    match ty {
+        Type::Path(path) => Some(path.clone()),
+        Type::Group(group) => extract_type_path(&group.elem),
+        _ => None,
     }
 }

+ 0 - 29
packages/native-core-macro/src/sorted_slice.rs

@@ -1,29 +0,0 @@
-extern crate proc_macro;
-
-use std::collections::BTreeMap;
-
-use syn::{
-    self, bracketed,
-    parse::{Parse, ParseStream, Result},
-    LitStr, Token,
-};
-
-pub struct StrSlice {
-    pub map: BTreeMap<String, LitStr>,
-}
-
-impl Parse for StrSlice {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let content;
-        bracketed!(content in input);
-        let mut map = BTreeMap::new();
-        while let Ok(s) = content.parse::<LitStr>() {
-            map.insert(s.value(), s);
-            #[allow(unused_must_use)]
-            {
-                content.parse::<Token![,]>();
-            }
-        }
-        Ok(StrSlice { map })
-    }
-}

+ 0 - 244
packages/native-core-macro/tests/called_minimally_on_build.rs

@@ -1,244 +0,0 @@
-use dioxus::prelude::*;
-use dioxus_native_core::node_ref::*;
-use dioxus_native_core::real_dom::*;
-use dioxus_native_core::state::{
-    ChildDepState, ElementBorrowable, NodeDepState, ParentDepState, State,
-};
-use dioxus_native_core::tree::*;
-use dioxus_native_core::SendAnyMap;
-use dioxus_native_core_macro::State;
-
-macro_rules! dep {
-    ( child( $name:ty, $dep:ty ) ) => {
-        impl ChildDepState for $name {
-            type Ctx = ();
-            type DepState = $dep;
-            const NODE_MASK: NodeMask = NodeMask::ALL;
-            fn reduce<'a>(
-                &mut self,
-                _: NodeView,
-                _: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
-                _: &Self::Ctx,
-            ) -> bool
-            where
-                Self::DepState: 'a,
-            {
-                self.0 += 1;
-                true
-            }
-        }
-    };
-
-    ( parent( $name:ty, $dep:ty ) ) => {
-        impl ParentDepState for $name {
-            type Ctx = ();
-            type DepState = $dep;
-            const NODE_MASK: NodeMask = NodeMask::ALL;
-            fn reduce(
-                &mut self,
-                _: NodeView,
-                _: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'_>>,
-                _: &Self::Ctx,
-            ) -> bool {
-                self.0 += 1;
-                true
-            }
-        }
-    };
-
-    ( node( $name:ty, $dep:ty ) ) => {
-        impl NodeDepState for $name {
-            type Ctx = ();
-            type DepState = $dep;
-            const NODE_MASK: NodeMask = NodeMask::ALL;
-            fn reduce(
-                &mut self,
-                _: NodeView,
-                _: <Self::DepState as ElementBorrowable>::ElementBorrowed<'_>,
-                _: &Self::Ctx,
-            ) -> bool {
-                self.0 += 1;
-                true
-            }
-        }
-    };
-}
-
-macro_rules! test_state{
-    ( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => {
-        #[test]
-        fn state_reduce_initally_called_minimally() {
-            #[allow(non_snake_case)]
-            fn Base(cx: Scope) -> Element {
-                render!{
-                    div {
-                        div{
-                            div{
-                                p{}
-                            }
-                            p{
-                                "hello"
-                            }
-                            div{
-                                h1{}
-                            }
-                            p{
-                                "world"
-                            }
-                        }
-                    }
-                }
-            }
-
-            let mut vdom = VirtualDom::new(Base);
-
-            let mutations = vdom.rebuild();
-
-            let mut dom: RealDom<$s> = RealDom::new();
-
-            let (nodes_updated, _) = dom.apply_mutations(mutations);
-            let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
-
-            dom.traverse_depth_first(|n| {
-                $(
-                    assert_eq!(n.state.$child.0, 1);
-                )*
-                $(
-                    assert_eq!(n.state.$node.0, 1);
-                )*
-                $(
-                    assert_eq!(n.state.$parent.0, 1);
-                )*
-            });
-        }
-    }
-}
-
-mod node_depends_on_child_and_parent {
-    use super::*;
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Node(i32);
-    dep!(node(Node, (Child, Parent)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Child(i32);
-    dep!(child(Child, (Child,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Parent(i32);
-    dep!(parent(Parent, (Parent,)));
-
-    #[derive(Debug, Clone, Default, State)]
-    struct StateTester {
-        #[node_dep_state((child, parent))]
-        node: Node,
-        #[child_dep_state(child)]
-        child: Child,
-        #[parent_dep_state(parent)]
-        parent: Parent,
-    }
-
-    test_state!(StateTester, child: (child), node: (node), parent: (parent));
-}
-
-mod child_depends_on_node_that_depends_on_parent {
-    use super::*;
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Node(i32);
-    dep!(node(Node, (Parent,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Child(i32);
-    dep!(child(Child, (Node,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Parent(i32);
-    dep!(parent(Parent, (Parent,)));
-
-    #[derive(Debug, Clone, Default, State)]
-    struct StateTester {
-        #[node_dep_state(parent)]
-        node: Node,
-        #[child_dep_state(node)]
-        child: Child,
-        #[parent_dep_state(parent)]
-        parent: Parent,
-    }
-
-    test_state!(StateTester, child: (child), node: (node), parent: (parent));
-}
-
-mod parent_depends_on_node_that_depends_on_child {
-    use super::*;
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Node(i32);
-    dep!(node(Node, (Child,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Child(i32);
-    dep!(child(Child, (Child,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Parent(i32);
-    dep!(parent(Parent, (Node,)));
-
-    #[derive(Debug, Clone, Default, State)]
-    struct StateTester {
-        #[node_dep_state(child)]
-        node: Node,
-        #[child_dep_state(child)]
-        child: Child,
-        #[parent_dep_state(node)]
-        parent: Parent,
-    }
-
-    test_state!(StateTester, child: (child), node: (node), parent: (parent));
-}
-
-mod node_depends_on_other_node_state {
-    use super::*;
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Node1(i32);
-    dep!(node(Node1, (Node2,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Node2(i32);
-    dep!(node(Node2, ()));
-
-    #[derive(Debug, Clone, Default, State)]
-    struct StateTester {
-        #[node_dep_state((node2))]
-        node1: Node1,
-        #[node_dep_state()]
-        node2: Node2,
-    }
-
-    test_state!(StateTester, child: (), node: (node1, node2), parent: ());
-}
-
-mod node_child_and_parent_state_depends_on_self {
-    use super::*;
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Node(i32);
-    dep!(node(Node, ()));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Child(i32);
-    dep!(child(Child, (Child,)));
-
-    #[derive(Debug, Clone, Default, PartialEq)]
-    struct Parent(i32);
-    dep!(parent(Parent, (Parent,)));
-
-    #[derive(Debug, Clone, Default, State)]
-    struct StateTester {
-        #[node_dep_state()]
-        node: Node,
-        #[child_dep_state(child)]
-        child: Child,
-        #[parent_dep_state(parent)]
-        parent: Parent,
-    }
-
-    test_state!(StateTester, child: (child), node: (node), parent: (parent));
-}

+ 0 - 421
packages/native-core-macro/tests/update_state.rs

@@ -1,421 +0,0 @@
-use dioxus::prelude::*;
-use dioxus_native_core::real_dom::*;
-use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
-use dioxus_native_core::tree::TreeView;
-use dioxus_native_core::{node_ref::*, NodeId, SendAnyMap};
-use dioxus_native_core_macro::State;
-
-#[derive(Debug, Clone, Default, State)]
-struct CallCounterState {
-    #[child_dep_state(child_counter)]
-    child_counter: ChildDepCallCounter,
-    #[parent_dep_state(parent_counter)]
-    parent_counter: ParentDepCallCounter,
-    #[node_dep_state()]
-    node_counter: NodeDepCallCounter,
-}
-
-#[derive(Debug, Clone, Default)]
-struct ChildDepCallCounter(u32);
-impl ChildDepState for ChildDepCallCounter {
-    type Ctx = ();
-    type DepState = (Self,);
-    const NODE_MASK: NodeMask = NodeMask::ALL;
-    fn reduce<'a>(
-        &mut self,
-        _: NodeView,
-        _: impl Iterator<Item = (&'a Self,)>,
-        _: &Self::Ctx,
-    ) -> bool
-    where
-        Self::DepState: 'a,
-    {
-        self.0 += 1;
-        true
-    }
-}
-
-#[derive(Debug, Clone, Default)]
-struct ParentDepCallCounter(u32);
-impl ParentDepState for ParentDepCallCounter {
-    type Ctx = ();
-    type DepState = (Self,);
-    const NODE_MASK: NodeMask = NodeMask::ALL;
-    fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
-        self.0 += 1;
-        true
-    }
-}
-
-#[derive(Debug, Clone, Default)]
-struct NodeDepCallCounter(u32);
-impl NodeDepState for NodeDepCallCounter {
-    type Ctx = ();
-    type DepState = ();
-    const NODE_MASK: NodeMask = NodeMask::ALL;
-    fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
-        self.0 += 1;
-        true
-    }
-}
-
-#[allow(clippy::vec_box)]
-#[derive(Debug, Clone, PartialEq, Default)]
-struct BubbledUpStateTester(Option<String>, Vec<Box<BubbledUpStateTester>>);
-impl ChildDepState for BubbledUpStateTester {
-    type Ctx = u32;
-    type DepState = (Self,);
-    const NODE_MASK: NodeMask = NodeMask::new().with_tag();
-    fn reduce<'a>(
-        &mut self,
-        node: NodeView,
-        children: impl Iterator<Item = (&'a Self,)>,
-        ctx: &Self::Ctx,
-    ) -> bool
-    where
-        Self::DepState: 'a,
-    {
-        assert_eq!(*ctx, 42);
-        *self = BubbledUpStateTester(
-            node.tag().map(|s| s.to_string()),
-            children
-                .into_iter()
-                .map(|(c,)| Box::new(c.clone()))
-                .collect(),
-        );
-        true
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Default)]
-struct PushedDownStateTester(Option<String>, Option<Box<PushedDownStateTester>>);
-impl ParentDepState for PushedDownStateTester {
-    type Ctx = u32;
-    type DepState = (Self,);
-    const NODE_MASK: NodeMask = NodeMask::new().with_tag();
-    fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, ctx: &Self::Ctx) -> bool {
-        assert_eq!(*ctx, 42);
-        *self = PushedDownStateTester(
-            node.tag().map(|s| s.to_string()),
-            parent.map(|(c,)| Box::new(c.clone())),
-        );
-        true
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Default)]
-struct NodeStateTester(Option<String>, Vec<(String, String)>);
-impl NodeDepState for NodeStateTester {
-    type Ctx = u32;
-    type DepState = ();
-    const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
-    fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool {
-        assert_eq!(*ctx, 42);
-        *self = NodeStateTester(
-            node.tag().map(|s| s.to_string()),
-            node.attributes()
-                .map(|iter| {
-                    iter.map(|a| {
-                        (
-                            a.attribute.name.to_string(),
-                            a.value.as_text().unwrap().to_string(),
-                        )
-                    })
-                    .collect()
-                })
-                .unwrap_or_default(),
-        );
-        true
-    }
-}
-
-#[derive(State, Clone, Default, Debug)]
-struct StateTester {
-    #[child_dep_state(bubbled, u32)]
-    bubbled: BubbledUpStateTester,
-    #[parent_dep_state(pushed, u32)]
-    pushed: PushedDownStateTester,
-    #[node_dep_state(NONE, u32)]
-    node: NodeStateTester,
-}
-
-#[test]
-fn state_initial() {
-    #[allow(non_snake_case)]
-    fn Base(cx: Scope) -> Element {
-        render! {
-            div {
-                p{
-                    color: "red"
-                }
-                h1{}
-            }
-        }
-    }
-
-    let mut vdom = VirtualDom::new(Base);
-
-    let mutations = vdom.rebuild();
-
-    let mut dom: RealDom<StateTester> = RealDom::new();
-
-    let (nodes_updated, _) = dom.apply_mutations(mutations);
-    let mut ctx = SendAnyMap::new();
-    ctx.insert(42u32);
-    let _to_rerender = dom.update_state(nodes_updated, ctx);
-
-    let root_div_id = dom.children_ids(NodeId(0)).unwrap()[0];
-    let root_div = &dom.get(root_div_id).unwrap();
-    assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
-    assert_eq!(
-        root_div.state.bubbled.1,
-        vec![
-            Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())),
-            Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new()))
-        ]
-    );
-    assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
-    assert_eq!(
-        root_div.state.pushed.1,
-        Some(Box::new(PushedDownStateTester(
-            Some("Root".to_string()),
-            None
-        )))
-    );
-    assert_eq!(root_div.state.node.0, Some("div".to_string()));
-    assert_eq!(root_div.state.node.1, vec![]);
-
-    let child_p_id = dom.children_ids(root_div_id).unwrap()[0];
-    let child_p = &dom[child_p_id];
-    assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
-    assert_eq!(child_p.state.bubbled.1, Vec::new());
-    assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
-    assert_eq!(
-        child_p.state.pushed.1,
-        Some(Box::new(PushedDownStateTester(
-            Some("div".to_string()),
-            Some(Box::new(PushedDownStateTester(
-                Some("Root".to_string()),
-                None
-            )))
-        )))
-    );
-    assert_eq!(child_p.state.node.0, Some("p".to_string()));
-    assert_eq!(
-        child_p.state.node.1,
-        vec![("color".to_string(), "red".to_string())]
-    );
-
-    let child_h1_id = dom.children_ids(root_div_id).unwrap()[1];
-    let child_h1 = &dom[child_h1_id];
-    assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
-    assert_eq!(child_h1.state.bubbled.1, Vec::new());
-    assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
-    assert_eq!(
-        child_h1.state.pushed.1,
-        Some(Box::new(PushedDownStateTester(
-            Some("div".to_string()),
-            Some(Box::new(PushedDownStateTester(
-                Some("Root".to_string()),
-                None
-            )))
-        )))
-    );
-    assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
-    assert_eq!(child_h1.state.node.1, vec![]);
-}
-
-#[test]
-fn state_reduce_parent_called_minimally_on_update() {
-    #[allow(non_snake_case)]
-    fn Base(cx: Scope) -> Element {
-        let width = if cx.generation() == 0 { "100%" } else { "99%" };
-        cx.render(rsx! {
-            div {
-                width: "{width}",
-                div{
-                    div{
-                        p{}
-                    }
-                    p{
-                        "hello"
-                    }
-                    div{
-                        h1{}
-                    }
-                    p{
-                        "world"
-                    }
-                }
-            }
-        })
-    }
-
-    let mut vdom = VirtualDom::new(Base);
-
-    let mut dom: RealDom<CallCounterState> = RealDom::new();
-
-    let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild());
-    let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
-    vdom.mark_dirty(ScopeId(0));
-    let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate());
-    let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
-
-    let mut is_root = true;
-    dom.traverse_depth_first(|n| {
-        if is_root {
-            is_root = false;
-            assert_eq!(n.state.parent_counter.0, 1);
-        } else {
-            assert_eq!(n.state.parent_counter.0, 2);
-        }
-    });
-}
-
-#[test]
-fn state_reduce_child_called_minimally_on_update() {
-    #[allow(non_snake_case)]
-    fn Base(cx: Scope) -> Element {
-        let width = if cx.generation() == 0 { "100%" } else { "99%" };
-        cx.render(rsx! {
-            // updated: 2
-            div {
-                // updated: 2
-                div{
-                    // updated: 2
-                    div{
-                        // updated: 2
-                        p{
-                            width: "{width}",
-                        }
-                    }
-                    // updated: 1
-                    p{
-                        // updated: 1
-                        "hello"
-                    }
-                    // updated: 1
-                    div{
-                        // updated: 1
-                        h1{}
-                    }
-                    // updated: 1
-                    p{
-                        // updated: 1
-                        "world"
-                    }
-                }
-            }
-        })
-    }
-
-    let mut vdom = VirtualDom::new(Base);
-
-    let mut dom: RealDom<CallCounterState> = RealDom::new();
-
-    let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild());
-    let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
-    vdom.mark_dirty(ScopeId(0));
-    let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate());
-    let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
-
-    let mut traverse_count = 0;
-    dom.traverse_depth_first(|n| {
-        assert_eq!(n.state.child_counter.0, {
-            if traverse_count > 4 {
-                1
-            } else {
-                2
-            }
-        });
-        traverse_count += 1;
-    });
-}
-
-#[derive(Debug, Clone, Default, State)]
-struct UnorderedDependanciesState {
-    #[node_dep_state(c)]
-    b: BDepCallCounter,
-    #[node_dep_state()]
-    c: CDepCallCounter,
-    #[node_dep_state(b)]
-    a: ADepCallCounter,
-}
-
-#[derive(Debug, Clone, Default, PartialEq)]
-struct ADepCallCounter(usize, BDepCallCounter);
-impl NodeDepState for ADepCallCounter {
-    type Ctx = ();
-    type DepState = (BDepCallCounter,);
-    const NODE_MASK: NodeMask = NodeMask::NONE;
-    fn reduce(
-        &mut self,
-        _node: NodeView,
-        (sibling,): (&BDepCallCounter,),
-        _ctx: &Self::Ctx,
-    ) -> bool {
-        self.0 += 1;
-        self.1 = sibling.clone();
-        true
-    }
-}
-
-#[derive(Debug, Clone, Default, PartialEq)]
-struct BDepCallCounter(usize, CDepCallCounter);
-impl NodeDepState for BDepCallCounter {
-    type Ctx = ();
-    type DepState = (CDepCallCounter,);
-    const NODE_MASK: NodeMask = NodeMask::NONE;
-    fn reduce(
-        &mut self,
-        _node: NodeView,
-        (sibling,): (&CDepCallCounter,),
-        _ctx: &Self::Ctx,
-    ) -> bool {
-        self.0 += 1;
-        self.1 = sibling.clone();
-        true
-    }
-}
-
-#[derive(Debug, Clone, Default, PartialEq)]
-struct CDepCallCounter(usize);
-impl NodeDepState for CDepCallCounter {
-    type Ctx = ();
-    type DepState = ();
-    const NODE_MASK: NodeMask = NodeMask::ALL;
-    fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
-        self.0 += 1;
-        true
-    }
-}
-
-#[test]
-fn dependancies_order_independant() {
-    #[allow(non_snake_case)]
-    fn Base(cx: Scope) -> Element {
-        render!(div {
-            width: "100%",
-            p{
-                "hello"
-            }
-        })
-    }
-
-    let mut vdom = VirtualDom::new(Base);
-
-    let mut dom: RealDom<UnorderedDependanciesState> = RealDom::new();
-
-    let mutations = vdom.rebuild();
-    let (nodes_updated, _) = dom.apply_mutations(mutations);
-    let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
-
-    let c = CDepCallCounter(1);
-    let b = BDepCallCounter(1, c.clone());
-    let a = ADepCallCounter(1, b.clone());
-    dom.traverse_depth_first(|n| {
-        assert_eq!(&n.state.a, &a);
-        assert_eq!(&n.state.b, &b);
-        assert_eq!(&n.state.c, &c);
-    });
-}

+ 16 - 6
packages/native-core/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "dioxus-native-core"
-version = "0.3.0"
+version = "0.2.0"
 edition = "2021"
 license = "MIT/Apache-2.0"
 repository = "https://github.com/DioxusLabs/dioxus/"
@@ -11,24 +11,34 @@ keywords = ["dom", "ui", "gui", "react"]
 
 
 [dependencies]
-dioxus-core = { path = "../core", version = "^0.3.0" }
-dioxus-html = { path = "../html", version = "^0.3.0" }
-dioxus-core-macro = { path = "../core-macro", version = "^0.3.0" }
+dioxus-core = { path = "../core", version = "^0.3.0", optional = true }
 
+keyboard-types = "0.6.2"
 taffy = "0.2.1"
 smallvec = "1.6"
 rustc-hash = "1.1.0"
 anymap = "1.0.0-beta.2"
 slab = "0.4"
-parking_lot = "0.12.1"
+parking_lot = { version = "0.12.1", features = ["send_guard"] }
 crossbeam-deque = "0.8.2"
 dashmap = "5.4.0"
+hashbrown = { version = "0.13.2", features = ["raw"] }
 
 # for parsing attributes
 lightningcss = "1.0.0-alpha.39"
 
+rayon = "1.6.1"
+shipyard = { version = "0.6.2", features = ["proc", "std"], default-features = false }
+shipyard_hierarchy = "0.6.0"
+
 [dev-dependencies]
 rand = "0.8.5"
 dioxus = { path = "../dioxus", version = "^0.3.0" }
+tokio = { version = "*", features = ["full"] }
+dioxus-native-core = { path = ".", features = ["dioxus"] }
 dioxus-native-core-macro = { path = "../native-core-macro" }
-tokio = { version = "1.0", features = ["full"] }
+
+[features]
+default = []
+dioxus = ["dioxus-core"]
+parallel = ["shipyard/parallel"]

+ 1 - 1
packages/native-core/README.md

@@ -18,7 +18,7 @@
 [discord-url]: https://discord.gg/XgGxMSkvUM
 
 [Website](https://dioxuslabs.com) |
-[Guides](https://dioxuslabs.com/docs/0.3/guide/en/) |
+[Guides](https://dioxuslabs.com/guide/) |
 [API Docs](https://docs.rs/dioxus-native-core/latest/dioxus_native_core) |
 [Chat](https://discord.gg/XgGxMSkvUM)
 

+ 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;
+            }
+        })
+}

+ 222 - 0
packages/native-core/examples/simple.rs

@@ -0,0 +1,222 @@
+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 some of the boilerplate for the State implementation
+#[partial_derive_state]
+impl State 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<()>,
+        _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 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<()>,
+        _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 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<()>,
+        _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 = 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;
+            }
+        })
+}

+ 249 - 0
packages/native-core/examples/simple_dioxus.rs

@@ -0,0 +1,249 @@
+#![allow(non_snake_case)]
+use dioxus::prelude::*;
+use dioxus_native_core::exports::shipyard::Component;
+use dioxus_native_core::node_ref::*;
+use dioxus_native_core::prelude::*;
+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 some of the boilerplate for the State implementation
+#[partial_derive_state]
+impl State 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<()>,
+        _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 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<()>,
+        _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 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<()>,
+        _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>> {
+    fn app(cx: Scope) -> Element {
+        let count = use_state(cx, || 0);
+
+        use_future(cx, (count,), |(count,)| async move {
+            loop {
+                tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+                count.set(*count + 1);
+            }
+        });
+
+        cx.render(rsx! {
+            div{
+                color: "red",
+                "{count}",
+                Comp {}
+            }
+        })
+    }
+
+    fn Comp(cx: Scope) -> Element {
+        cx.render(rsx! {
+            div{
+                border: "",
+                "hello world"
+            }
+        })
+    }
+
+    // create the vdom, the real_dom, and the binding layer between them
+    let mut vdom = VirtualDom::new(app);
+    let mut rdom: RealDom = RealDom::new([
+        Border::to_type_erased(),
+        TextColor::to_type_erased(),
+        Size::to_type_erased(),
+    ]);
+    let mut dioxus_intigration_state = DioxusState::create(&mut rdom);
+
+    let mutations = vdom.rebuild();
+    // update the structure of the real_dom tree
+    dioxus_intigration_state.apply_mutations(&mut rdom, mutations);
+    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 {
+                // wait for the vdom to update
+                vdom.wait_for_work().await;
+
+                // get the mutations from the vdom
+                let mutations = vdom.render_immediate();
+
+                // update the structure of the real_dom tree
+                dioxus_intigration_state.apply_mutations(&mut rdom, mutations);
+
+                // update the state of the real_dom tree
+                let mut ctx = SendAnyMap::new();
+                // set the font size to 3.3
+                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();
+                    let node = node.node_type();
+                    let node_type = &*node;
+                    println!("{indent}{id:?} {color:?} {size:?} {border:?} {node_type:?}");
+                });
+            }
+        })
+}

+ 296 - 0
packages/native-core/src/dioxus.rs

@@ -0,0 +1,296 @@
+//! Integration between Dioxus and the RealDom
+
+use crate::tree::TreeMut;
+use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
+use rustc_hash::{FxHashMap, FxHashSet};
+use shipyard::Component;
+
+use crate::{
+    node::{
+        ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
+        TextNode,
+    },
+    prelude::*,
+    real_dom::NodeTypeMut,
+    NodeId,
+};
+
+#[derive(Component)]
+struct ElementIdComponent(ElementId);
+
+/// The state of the Dioxus integration with the RealDom
+pub struct DioxusState {
+    templates: FxHashMap<String, Vec<NodeId>>,
+    stack: Vec<NodeId>,
+    node_id_mapping: Vec<Option<NodeId>>,
+}
+
+impl DioxusState {
+    /// Initialize the DioxusState in the RealDom
+    pub fn create<V: FromAnyValue + Send + Sync>(rdom: &mut RealDom<V>) -> Self {
+        let root_id = rdom.root_id();
+        let mut root = rdom.get_mut(root_id).unwrap();
+        root.insert(ElementIdComponent(ElementId(0)));
+        Self {
+            templates: FxHashMap::default(),
+            stack: vec![root_id],
+            node_id_mapping: vec![Some(root_id)],
+        }
+    }
+
+    /// Convert an ElementId to a NodeId
+    pub fn element_to_node_id(&self, element_id: ElementId) -> NodeId {
+        self.try_element_to_node_id(element_id).unwrap()
+    }
+
+    /// Attempt to convert an ElementId to a NodeId. This will return None if the ElementId is not in the RealDom.
+    pub fn try_element_to_node_id(&self, element_id: ElementId) -> Option<NodeId> {
+        self.node_id_mapping.get(element_id.0).copied().flatten()
+    }
+
+    fn set_element_id<V: FromAnyValue + Send + Sync>(
+        &mut self,
+        mut node: NodeMut<V>,
+        element_id: ElementId,
+    ) {
+        let node_id = node.id();
+        node.insert(ElementIdComponent(element_id));
+        if self.node_id_mapping.len() <= element_id.0 {
+            self.node_id_mapping.resize(element_id.0 + 1, None);
+        }
+        self.node_id_mapping[element_id.0] = Some(node_id);
+    }
+
+    fn load_child<V: FromAnyValue + Send + Sync>(&self, rdom: &RealDom<V>, path: &[u8]) -> NodeId {
+        let mut current = rdom.get(*self.stack.last().unwrap()).unwrap();
+        for i in path {
+            let new_id = current.child_ids()[*i as usize];
+            current = rdom.get(new_id).unwrap();
+        }
+        current.id()
+    }
+
+    /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state.
+    pub fn apply_mutations<V: FromAnyValue + Send + Sync>(
+        &mut self,
+        rdom: &mut RealDom<V>,
+        mutations: Mutations,
+    ) {
+        for template in mutations.templates {
+            let mut template_root_ids = Vec::new();
+            for root in template.roots {
+                let id = create_template_node(rdom, root);
+                template_root_ids.push(id);
+            }
+            self.templates
+                .insert(template.name.to_string(), template_root_ids);
+        }
+
+        for e in mutations.edits {
+            use dioxus_core::Mutation::*;
+            match e {
+                AppendChildren { id, m } => {
+                    let children = self.stack.split_off(self.stack.len() - m);
+                    let parent = self.element_to_node_id(id);
+                    for child in children {
+                        rdom.get_mut(parent).unwrap().add_child(child);
+                    }
+                }
+                AssignId { path, id } => {
+                    let node_id = self.load_child(rdom, path);
+                    self.set_element_id(rdom.get_mut(node_id).unwrap(), id);
+                }
+                CreatePlaceholder { id } => {
+                    let node = NodeType::Placeholder;
+                    let node = rdom.create_node(node);
+                    let node_id = node.id();
+                    self.set_element_id(node, id);
+                    self.stack.push(node_id);
+                }
+                CreateTextNode { value, id } => {
+                    let node_data = NodeType::Text(TextNode {
+                        listeners: FxHashSet::default(),
+                        text: value.to_string(),
+                    });
+                    let node = rdom.create_node(node_data);
+                    let node_id = node.id();
+                    self.set_element_id(node, id);
+                    self.stack.push(node_id);
+                }
+                HydrateText { path, value, id } => {
+                    let node_id = self.load_child(rdom, path);
+                    let node = rdom.get_mut(node_id).unwrap();
+                    self.set_element_id(node, id);
+                    let mut node = rdom.get_mut(node_id).unwrap();
+                    let node_type_mut = node.node_type_mut();
+                    if let NodeTypeMut::Text(mut text) = node_type_mut {
+                        *text.text_mut() = value.to_string();
+                    } else {
+                        drop(node_type_mut);
+                        node.set_type(NodeType::Text(TextNode {
+                            text: value.to_string(),
+                            listeners: FxHashSet::default(),
+                        }));
+                    }
+                }
+                LoadTemplate { name, index, id } => {
+                    let template_id = self.templates[name][index];
+                    let clone_id = rdom.get_mut(template_id).unwrap().clone_node();
+                    let clone = rdom.get_mut(clone_id).unwrap();
+                    self.set_element_id(clone, id);
+                    self.stack.push(clone_id);
+                }
+                ReplaceWith { id, m } => {
+                    let new_nodes = self.stack.split_off(self.stack.len() - m);
+                    let old_node_id = self.element_to_node_id(id);
+                    for new in new_nodes {
+                        let mut node = rdom.get_mut(new).unwrap();
+                        node.insert_before(old_node_id);
+                    }
+                    rdom.get_mut(old_node_id).unwrap().remove();
+                }
+                ReplacePlaceholder { path, m } => {
+                    let new_nodes = self.stack.split_off(self.stack.len() - m);
+                    let old_node_id = self.load_child(rdom, path);
+                    for new in new_nodes {
+                        let mut node = rdom.get_mut(new).unwrap();
+                        node.insert_before(old_node_id);
+                    }
+                    rdom.get_mut(old_node_id).unwrap().remove();
+                }
+                InsertAfter { id, m } => {
+                    let new_nodes = self.stack.split_off(self.stack.len() - m);
+                    let old_node_id = self.element_to_node_id(id);
+                    for new in new_nodes.into_iter().rev() {
+                        let mut node = rdom.get_mut(new).unwrap();
+                        node.insert_after(old_node_id);
+                    }
+                }
+                InsertBefore { id, m } => {
+                    let new_nodes = self.stack.split_off(self.stack.len() - m);
+                    let old_node_id = self.element_to_node_id(id);
+                    for new in new_nodes {
+                        rdom.tree_mut().insert_before(old_node_id, new);
+                    }
+                }
+                SetAttribute {
+                    name,
+                    value,
+                    id,
+                    ns,
+                } => {
+                    let node_id = self.element_to_node_id(id);
+                    let mut node = rdom.get_mut(node_id).unwrap();
+                    let mut node_type_mut = node.node_type_mut();
+                    if let NodeTypeMut::Element(element) = &mut node_type_mut {
+                        if let BorrowedAttributeValue::None = &value {
+                            element.remove_attribute(&OwnedAttributeDiscription {
+                                name: name.to_string(),
+                                namespace: ns.map(|s| s.to_string()),
+                            });
+                        } else {
+                            element.set_attribute(
+                                OwnedAttributeDiscription {
+                                    name: name.to_string(),
+                                    namespace: ns.map(|s| s.to_string()),
+                                },
+                                OwnedAttributeValue::from(value),
+                            );
+                        }
+                    }
+                }
+                SetText { value, id } => {
+                    let node_id = self.element_to_node_id(id);
+                    let mut node = rdom.get_mut(node_id).unwrap();
+                    let node_type_mut = node.node_type_mut();
+                    if let NodeTypeMut::Text(mut text) = node_type_mut {
+                        *text.text_mut() = value.to_string();
+                    }
+                }
+                NewEventListener { name, id } => {
+                    let node_id = self.element_to_node_id(id);
+                    let mut node = rdom.get_mut(node_id).unwrap();
+                    node.add_event_listener(name);
+                }
+                RemoveEventListener { id, name } => {
+                    let node_id = self.element_to_node_id(id);
+                    let mut node = rdom.get_mut(node_id).unwrap();
+                    node.remove_event_listener(name);
+                }
+                Remove { id } => {
+                    let node_id = self.element_to_node_id(id);
+                    rdom.get_mut(node_id).unwrap().remove();
+                }
+                PushRoot { id } => {
+                    let node_id = self.element_to_node_id(id);
+                    self.stack.push(node_id);
+                }
+            }
+        }
+    }
+}
+
+fn create_template_node<V: FromAnyValue + Send + Sync>(
+    rdom: &mut RealDom<V>,
+    node: &TemplateNode,
+) -> NodeId {
+    match node {
+        TemplateNode::Element {
+            tag,
+            namespace,
+            attrs,
+            children,
+        } => {
+            let node = NodeType::Element(ElementNode {
+                tag: tag.to_string(),
+                namespace: namespace.map(|s| s.to_string()),
+                attributes: attrs
+                    .iter()
+                    .filter_map(|attr| match attr {
+                        dioxus_core::TemplateAttribute::Static {
+                            name,
+                            value,
+                            namespace,
+                        } => Some((
+                            OwnedAttributeDiscription {
+                                namespace: namespace.map(|s| s.to_string()),
+                                name: name.to_string(),
+                            },
+                            OwnedAttributeValue::Text(value.to_string()),
+                        )),
+                        dioxus_core::TemplateAttribute::Dynamic { .. } => None,
+                    })
+                    .collect(),
+                listeners: FxHashSet::default(),
+            });
+            let node_id = rdom.create_node(node).id();
+            for child in *children {
+                let child_id = create_template_node(rdom, child);
+                rdom.get_mut(node_id).unwrap().add_child(child_id);
+            }
+            node_id
+        }
+        TemplateNode::Text { text } => rdom
+            .create_node(NodeType::Text(TextNode {
+                text: text.to_string(),
+                ..Default::default()
+            }))
+            .id(),
+        TemplateNode::Dynamic { .. } => rdom.create_node(NodeType::Placeholder).id(),
+        TemplateNode::DynamicText { .. } => {
+            rdom.create_node(NodeType::Text(TextNode::default())).id()
+        }
+    }
+}
+
+/// A trait that extends the `NodeImmutable` trait with methods that are useful for dioxus.
+pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
+    /// Returns the id of the element that this node is mounted to.
+    /// Not all nodes are mounted to an element, only nodes with dynamic content that have been renderered will have an id.
+    fn mounted_id(&self) -> Option<ElementId> {
+        let id = self.get::<ElementIdComponent>();
+        id.map(|id| id.0)
+    }
+}
+
+impl<T: NodeImmutable<V>, V: FromAnyValue + Send + Sync> NodeImmutableDioxusExt<V> for T {}

+ 4 - 0
packages/native-core/src/layout_attributes.rs

@@ -1,3 +1,5 @@
+//! Utility functions for applying layout attributes to taffy layout
+
 /*
 - [ ] pub display: Display, ----> taffy doesnt support all display types
 - [x] pub position_type: PositionType,  --> taffy doesnt support everything
@@ -46,12 +48,14 @@ use taffy::{
     style::{FlexDirection, PositionType},
 };
 
+/// Default values for layout attributes
 #[derive(Default)]
 pub struct LayoutConfigeration {
     /// the default border widths to use
     pub border_widths: BorderWidths,
 }
 
+/// Default border widths
 pub struct BorderWidths {
     /// the default border width to use for thin borders
     pub thin: f32,

+ 33 - 35
packages/native-core/src/lib.rs

@@ -1,51 +1,49 @@
+#![doc = include_str!("../README.md")]
+#![warn(missing_docs)]
+
 use std::any::Any;
 use std::hash::BuildHasherDefault;
 
-pub use node_ref::NodeMask;
-pub use passes::{
-    AnyPass, DownwardPass, MemberMask, NodePass, Pass, PassId, PassReturn, UpwardPass,
-};
+use node_ref::NodeMask;
 use rustc_hash::FxHasher;
-pub use tree::NodeId;
 
+#[cfg(feature = "dioxus")]
+pub mod dioxus;
 pub mod layout_attributes;
 pub mod node;
 pub mod node_ref;
-pub mod passes;
+pub mod node_watcher;
+mod passes;
 pub mod real_dom;
-pub mod state;
 pub mod tree;
 pub mod utils;
+pub use shipyard::EntityId as NodeId;
 
-/// A id for a node that lives in the real dom.
-pub type RealNodeId = NodeId;
-pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
-pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
-pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
-
-/// Used in derived state macros
-#[derive(Eq, PartialEq)]
-#[doc(hidden)]
-pub struct HeightOrdering {
-    pub height: u16,
-    pub id: RealNodeId,
+pub mod exports {
+    //! Important dependencies that are used by the rest of the library
+    //! Feel free to just add the dependencies in your own Crates.toml
+    // exported for the macro
+    #[doc(hidden)]
+    pub use rustc_hash::FxHashSet;
+    pub use shipyard;
 }
 
-impl HeightOrdering {
-    pub fn new(height: u16, id: RealNodeId) -> Self {
-        HeightOrdering { height, id }
-    }
+/// A prelude of commonly used items
+pub mod prelude {
+    #[cfg(feature = "dioxus")]
+    pub use crate::dioxus::*;
+    pub use crate::node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView, TextNode};
+    pub use crate::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView};
+    pub use crate::passes::{run_pass, PassDirection, RunPassView, TypeErasedState};
+    pub use crate::passes::{Dependancy, DependancyView, State};
+    pub use crate::real_dom::{NodeImmutable, NodeMut, NodeRef, RealDom};
+    pub use crate::NodeId;
+    pub use crate::SendAnyMap;
 }
 
-// not the ordering after height is just for deduplication it can be any ordering as long as it is consistent
-impl Ord for HeightOrdering {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.height.cmp(&other.height).then(self.id.cmp(&other.id))
-    }
-}
-
-impl PartialOrd for HeightOrdering {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
+/// A map that can be sent between threads
+pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
+/// A set that can be sent between threads
+pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
+/// A map of types that can be sent between threads
+pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;

+ 160 - 54
packages/native-core/src/node.rs

@@ -1,65 +1,113 @@
-use crate::{state::State, tree::NodeId};
-use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId};
+//! Items related to Nodes in the RealDom
+
 use rustc_hash::{FxHashMap, FxHashSet};
-use std::fmt::Debug;
+use shipyard::Component;
+use std::{
+    any::Any,
+    fmt::{Debug, Display},
+};
+
+/// A element node in the RealDom
+#[derive(Debug, Clone, Default)]
+pub struct ElementNode<V: FromAnyValue = ()> {
+    /// The [tag](https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName) of the element
+    pub tag: String,
+    /// The [namespace](https://developer.mozilla.org/en-US/docs/Web/API/Element/namespaceURI) of the element
+    pub namespace: Option<String>,
+    /// The attributes of the element
+    pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
+    /// The events the element is listening for
+    pub listeners: FxHashSet<String>,
+}
 
-/// The node is stored client side and stores only basic data about the node.
-#[derive(Debug, Clone)]
-pub struct Node<S: State<V>, V: FromAnyValue + 'static = ()> {
-    /// The transformed state of the node.
-    pub state: S,
-    /// The raw data for the node
-    pub node_data: NodeData<V>,
+impl ElementNode {
+    /// Create a new element node
+    pub fn new(tag: impl Into<String>, namespace: impl Into<Option<String>>) -> Self {
+        Self {
+            tag: tag.into(),
+            namespace: namespace.into(),
+            attributes: Default::default(),
+            listeners: Default::default(),
+        }
+    }
 }
 
-#[derive(Debug, Clone)]
-pub struct NodeData<V: FromAnyValue = ()> {
-    /// The id of the node
-    pub node_id: NodeId,
-    /// The id of the node in the vdom.
-    pub element_id: Option<ElementId>,
-    /// Additional inforation specific to the node type
-    pub node_type: NodeType<V>,
+/// A text node in the RealDom
+#[derive(Debug, Clone, Default)]
+pub struct TextNode {
+    /// The text of the node
+    pub text: String,
+    /// The events the node is listening for
+    pub listeners: FxHashSet<String>,
 }
 
-/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
-#[derive(Debug, Clone)]
+impl TextNode {
+    /// Create a new text node
+    pub fn new(text: String) -> Self {
+        Self {
+            text,
+            listeners: Default::default(),
+        }
+    }
+}
+
+/// A type of node with data specific to the node type.
+#[derive(Debug, Clone, Component)]
 pub enum NodeType<V: FromAnyValue = ()> {
-    Text {
-        text: String,
-    },
-    Element {
-        tag: String,
-        namespace: Option<String>,
-        attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
-        listeners: FxHashSet<String>,
-    },
+    /// A text node
+    Text(TextNode),
+    /// An element node
+    Element(ElementNode<V>),
+    /// A placeholder node. This can be used as a cheaper placeholder for a node that will be created later
     Placeholder,
 }
 
-impl<S: State<V>, V: FromAnyValue> Node<S, V> {
-    pub(crate) fn new(node_type: NodeType<V>) -> Self {
-        Node {
-            state: S::default(),
-            node_data: NodeData {
-                element_id: None,
-                node_type,
-                node_id: NodeId(0),
-            },
-        }
+impl<V: FromAnyValue, S: Into<String>> From<S> for NodeType<V> {
+    fn from(text: S) -> Self {
+        Self::Text(TextNode::new(text.into()))
     }
+}
+
+impl<V: FromAnyValue> From<TextNode> for NodeType<V> {
+    fn from(text: TextNode) -> Self {
+        Self::Text(text)
+    }
+}
 
-    /// get the mounted id of the node
-    pub fn mounted_id(&self) -> Option<ElementId> {
-        self.node_data.element_id
+impl<V: FromAnyValue> From<ElementNode<V>> for NodeType<V> {
+    fn from(element: ElementNode<V>) -> Self {
+        Self::Element(element)
     }
 }
 
+/// A discription of an attribute on a DOM node, such as `id` or `href`.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct OwnedAttributeDiscription {
+    /// The name of the attribute.
     pub name: String,
+    /// The namespace of the attribute used to identify what kind of attribute it is.
+    ///
+    /// For renderers that use HTML, this can be used to identify if the attribute is a style attribute.
+    /// Instead of parsing the style attribute every time a style is changed, you can set an attribute with the `style` namespace.
     pub namespace: Option<String>,
-    pub volatile: bool,
+}
+
+impl From<String> for OwnedAttributeDiscription {
+    fn from(name: String) -> Self {
+        Self {
+            name,
+            namespace: None,
+        }
+    }
+}
+
+impl<S: Into<String>, N: Into<String>> From<(S, N)> for OwnedAttributeDiscription {
+    fn from(name: (S, N)) -> Self {
+        Self {
+            name: name.0.into(),
+            namespace: Some(name.1.into()),
+        }
+    }
 }
 
 /// An attribute on a DOM node, such as `id="my-thing"` or
@@ -73,21 +121,59 @@ pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
     pub value: &'a OwnedAttributeValue<V>,
 }
 
+/// The value of an attribute on a DOM node. This contains non-text values to allow users to skip parsing attribute values in some cases.
 #[derive(Clone)]
 pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
+    /// A string value. This is the most common type of attribute.
     Text(String),
+    /// A floating point value.
     Float(f64),
+    /// An integer value.
     Int(i64),
+    /// A boolean value.
     Bool(bool),
+    /// A custom value specific to the renderer
     Custom(V),
 }
 
-pub trait FromAnyValue: Clone {
-    fn from_any_value(value: &dyn AnyValue) -> Self;
+impl<V: FromAnyValue> From<String> for OwnedAttributeValue<V> {
+    fn from(value: String) -> Self {
+        Self::Text(value)
+    }
+}
+
+impl<V: FromAnyValue> From<f64> for OwnedAttributeValue<V> {
+    fn from(value: f64) -> Self {
+        Self::Float(value)
+    }
+}
+
+impl<V: FromAnyValue> From<i64> for OwnedAttributeValue<V> {
+    fn from(value: i64) -> Self {
+        Self::Int(value)
+    }
+}
+
+impl<V: FromAnyValue> From<bool> for OwnedAttributeValue<V> {
+    fn from(value: bool) -> Self {
+        Self::Bool(value)
+    }
+}
+
+impl<V: FromAnyValue> From<V> for OwnedAttributeValue<V> {
+    fn from(value: V) -> Self {
+        Self::Custom(value)
+    }
+}
+
+/// Something that can be converted from a borrowed [Any] value.
+pub trait FromAnyValue: Clone + 'static {
+    /// Convert from an [Any] value.
+    fn from_any_value(value: &dyn Any) -> Self;
 }
 
 impl FromAnyValue for () {
-    fn from_any_value(_: &dyn AnyValue) -> Self {}
+    fn from_any_value(_: &dyn Any) -> Self {}
 }
 
 impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
@@ -102,20 +188,34 @@ impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
     }
 }
 
-impl<V: FromAnyValue> From<BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
-    fn from(value: BorrowedAttributeValue<'_>) -> Self {
+impl<V: FromAnyValue> Display for OwnedAttributeValue<V> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Text(arg0) => f.write_str(arg0),
+            Self::Float(arg0) => f.write_str(&arg0.to_string()),
+            Self::Int(arg0) => f.write_str(&arg0.to_string()),
+            Self::Bool(arg0) => f.write_str(&arg0.to_string()),
+            Self::Custom(_) => f.write_str("custom"),
+        }
+    }
+}
+
+#[cfg(feature = "dioxus")]
+impl<V: FromAnyValue> From<dioxus_core::BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
+    fn from(value: dioxus_core::BorrowedAttributeValue<'_>) -> Self {
         match value {
-            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 => panic!("None attribute values result in removing the attribute, not converting it to a None value.")
+            dioxus_core::BorrowedAttributeValue::Text(text) => Self::Text(text.to_string()),
+            dioxus_core::BorrowedAttributeValue::Float(float) => Self::Float(float),
+            dioxus_core::BorrowedAttributeValue::Int(int) => Self::Int(int),
+            dioxus_core::BorrowedAttributeValue::Bool(bool) => Self::Bool(bool),
+            dioxus_core::BorrowedAttributeValue::Any(any) => Self::Custom(V::from_any_value(any.as_any())),
+            dioxus_core::BorrowedAttributeValue::None => panic!("None attribute values result in removing the attribute, not converting it to a None value.")
         }
     }
 }
 
 impl<V: FromAnyValue> OwnedAttributeValue<V> {
+    /// Attempt to convert the attribute value to a string.
     pub fn as_text(&self) -> Option<&str> {
         match self {
             OwnedAttributeValue::Text(text) => Some(text),
@@ -123,20 +223,25 @@ impl<V: FromAnyValue> OwnedAttributeValue<V> {
         }
     }
 
+    /// Attempt to convert the attribute value to a float.
     pub fn as_float(&self) -> Option<f64> {
         match self {
             OwnedAttributeValue::Float(float) => Some(*float),
+            OwnedAttributeValue::Int(int) => Some(*int as f64),
             _ => None,
         }
     }
 
+    /// Attempt to convert the attribute value to an integer.
     pub fn as_int(&self) -> Option<i64> {
         match self {
+            OwnedAttributeValue::Float(float) => Some(*float as i64),
             OwnedAttributeValue::Int(int) => Some(*int),
             _ => None,
         }
     }
 
+    /// Attempt to convert the attribute value to a boolean.
     pub fn as_bool(&self) -> Option<bool> {
         match self {
             OwnedAttributeValue::Bool(bool) => Some(*bool),
@@ -144,6 +249,7 @@ impl<V: FromAnyValue> OwnedAttributeValue<V> {
         }
     }
 
+    /// Attempt to convert the attribute value to a custom value.
     pub fn as_custom(&self) -> Option<&V> {
         match self {
             OwnedAttributeValue::Custom(custom) => Some(custom),

+ 128 - 128
packages/native-core/src/node_ref.rs

@@ -1,43 +1,41 @@
-use dioxus_core::ElementId;
+//! Utilities that provide limited access to nodes
+
+use rustc_hash::FxHashSet;
 
 use crate::{
-    node::{FromAnyValue, NodeData, NodeType, OwnedAttributeView},
-    state::union_ordered_iter,
-    RealNodeId,
+    node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView},
+    NodeId,
 };
 
-/// A view into a [VNode] with limited access.
+/// A view into a [NodeType] with a mask that determines what is visible.
 #[derive(Debug)]
 pub struct NodeView<'a, V: FromAnyValue = ()> {
-    inner: &'a NodeData<V>,
-    mask: NodeMask,
+    id: NodeId,
+    inner: &'a NodeType<V>,
+    mask: &'a NodeMask,
 }
 
 impl<'a, V: FromAnyValue> NodeView<'a, V> {
     /// Create a new NodeView from a VNode, and mask.
-    pub fn new(node: &'a NodeData<V>, view: NodeMask) -> Self {
+    pub fn new(id: NodeId, node: &'a NodeType<V>, view: &'a NodeMask) -> Self {
         Self {
             inner: node,
             mask: view,
+            id,
         }
     }
 
-    /// Get the id of the node
-    pub fn id(&self) -> Option<ElementId> {
-        self.inner.element_id
-    }
-
     /// Get the node id of the node
-    pub fn node_id(&self) -> RealNodeId {
-        self.inner.node_id
+    pub fn node_id(&self) -> NodeId {
+        self.id
     }
 
     /// Get the tag of the node if the tag is enabled in the mask
     pub fn tag(&self) -> Option<&'a str> {
         self.mask
             .tag
-            .then_some(match &self.inner.node_type {
-                NodeType::Element { tag, .. } => Some(&**tag),
+            .then_some(match &self.inner {
+                NodeType::Element(ElementNode { tag, .. }) => Some(&**tag),
                 _ => None,
             })
             .flatten()
@@ -47,8 +45,8 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
     pub fn namespace(&self) -> Option<&'a str> {
         self.mask
             .namespace
-            .then_some(match &self.inner.node_type {
-                NodeType::Element { namespace, .. } => namespace.as_deref(),
+            .then_some(match &self.inner {
+                NodeType::Element(ElementNode { namespace, .. }) => namespace.as_deref(),
                 _ => None,
             })
             .flatten()
@@ -58,8 +56,8 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
     pub fn attributes<'b>(
         &'b self,
     ) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
-        match &self.inner.node_type {
-            NodeType::Element { attributes, .. } => Some(
+        match &self.inner {
+            NodeType::Element(ElementNode { attributes, .. }) => Some(
                 attributes
                     .iter()
                     .filter(move |(attr, _)| self.mask.attritutes.contains_attribute(&attr.name))
@@ -76,18 +74,21 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
     pub fn text(&self) -> Option<&str> {
         self.mask
             .text
-            .then_some(match &self.inner.node_type {
-                NodeType::Text { text } => Some(&**text),
+            .then_some(match &self.inner {
+                NodeType::Text(text) => Some(&text.text),
                 _ => None,
             })
             .flatten()
+            .map(|x| &**x)
     }
 
     /// Get the listeners if it is enabled in the mask
     pub fn listeners(&self) -> Option<impl Iterator<Item = &'a str> + '_> {
         if self.mask.listeners {
-            match &self.inner.node_type {
-                NodeType::Element { listeners, .. } => Some(listeners.iter().map(|l| &**l)),
+            match &self.inner {
+                NodeType::Element(ElementNode { listeners, .. }) => {
+                    Some(listeners.iter().map(|l| &**l))
+                }
                 _ => None,
             }
         } else {
@@ -99,125 +100,54 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
 /// A mask that contains a list of attributes that are visible.
 #[derive(PartialEq, Eq, Clone, Debug)]
 pub enum AttributeMask {
+    /// All attributes are visible
     All,
-    /// A list of attribute names that are visible, this list must be sorted
-    Dynamic(Vec<String>),
-    /// A list of attribute names that are visible, this list must be sorted
-    Static(&'static [&'static str]),
+    /// Only the given attributes are visible
+    Some(FxHashSet<Box<str>>),
 }
 
 impl AttributeMask {
-    /// A empty attribute mask
-    pub const NONE: Self = Self::Static(&[]);
-
+    /// Check if the given attribute is visible
     fn contains_attribute(&self, attr: &str) -> bool {
         match self {
             AttributeMask::All => true,
-            AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(),
-            AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
+            AttributeMask::Some(attrs) => attrs.contains(attr),
         }
     }
 
     /// Create a new dynamic attribute mask with a single attribute
     pub fn single(new: &str) -> Self {
-        Self::Dynamic(vec![new.to_string()])
-    }
-
-    /// Ensure the attribute list is sorted.
-    pub fn verify(&self) {
-        match &self {
-            AttributeMask::Static(attrs) => debug_assert!(
-                attrs.windows(2).all(|w| w[0] < w[1]),
-                "attritutes must be increasing"
-            ),
-            AttributeMask::Dynamic(attrs) => debug_assert!(
-                attrs.windows(2).all(|w| w[0] < w[1]),
-                "attritutes must be increasing"
-            ),
-            _ => (),
-        }
+        let mut set = FxHashSet::default();
+        set.insert(new.into());
+        Self::Some(set)
     }
 
     /// Combine two attribute masks
     pub fn union(&self, other: &Self) -> Self {
-        let new = match (self, other) {
-            (AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => {
-                AttributeMask::Dynamic(union_ordered_iter(
-                    s.iter().map(|s| s.as_str()),
-                    o.iter().map(|s| s.as_str()),
-                    s.len() + o.len(),
-                ))
-            }
-            (AttributeMask::Static(s), AttributeMask::Dynamic(o)) => {
-                AttributeMask::Dynamic(union_ordered_iter(
-                    s.iter().copied(),
-                    o.iter().map(|s| s.as_str()),
-                    s.len() + o.len(),
-                ))
-            }
-            (AttributeMask::Dynamic(s), AttributeMask::Static(o)) => {
-                AttributeMask::Dynamic(union_ordered_iter(
-                    s.iter().map(|s| s.as_str()),
-                    o.iter().copied(),
-                    s.len() + o.len(),
-                ))
+        match (self, other) {
+            (AttributeMask::Some(s), AttributeMask::Some(o)) => {
+                AttributeMask::Some(s.union(o).cloned().collect())
             }
-            (AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
-                union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
-            ),
             _ => AttributeMask::All,
-        };
-        new.verify();
-        new
+        }
     }
 
     /// Check if two attribute masks overlap
     fn overlaps(&self, other: &Self) -> bool {
-        fn overlaps_iter<'a>(
-            self_iter: impl Iterator<Item = &'a str>,
-            mut other_iter: impl Iterator<Item = &'a str>,
-        ) -> bool {
-            if let Some(mut other_attr) = other_iter.next() {
-                for self_attr in self_iter {
-                    while other_attr < self_attr {
-                        if let Some(attr) = other_iter.next() {
-                            other_attr = attr;
-                        } else {
-                            return false;
-                        }
-                    }
-                    if other_attr == self_attr {
-                        return true;
-                    }
-                }
-            }
-            false
-        }
         match (self, other) {
-            (AttributeMask::All, AttributeMask::All) => true,
-            (AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
-            (AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
-            (AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
-            (AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
-            (AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
-                overlaps_iter(v1.iter().map(|s| s.as_str()), v2.iter().map(|s| s.as_str()))
-            }
-            (AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
-                overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
-            }
-            (AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
-                overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
-            }
-            (AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
-                overlaps_iter(s1.iter().copied(), s2.iter().copied())
+            (AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
+            (AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
+            (AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
+                !attrs1.is_disjoint(attrs2)
             }
+            _ => true,
         }
     }
 }
 
 impl Default for AttributeMask {
     fn default() -> Self {
-        AttributeMask::Static(&[])
+        AttributeMask::Some(FxHashSet::default())
     }
 }
 
@@ -232,14 +162,6 @@ pub struct NodeMask {
 }
 
 impl NodeMask {
-    /// A node mask with no parts visible.
-    pub const NONE: Self = Self::new();
-    /// A node mask with every part visible.
-    pub const ALL: Self = Self::new_with_attrs(AttributeMask::All)
-        .with_text()
-        .with_element()
-        .with_listeners();
-
     /// Check if two masks overlap
     pub fn overlaps(&self, other: &Self) -> bool {
         (self.tag && other.tag)
@@ -260,10 +182,71 @@ impl NodeMask {
         }
     }
 
-    /// Create a new node mask with the given attributes
-    pub const fn new_with_attrs(attritutes: AttributeMask) -> Self {
+    /// Allow the mask to view the given attributes
+    pub fn add_attributes(&mut self, attributes: AttributeMask) {
+        self.attritutes = self.attritutes.union(&attributes);
+    }
+
+    /// Set the mask to view the tag
+    pub fn set_tag(&mut self) {
+        self.tag = true;
+    }
+
+    /// Set the mask to view the namespace
+    pub fn set_namespace(&mut self) {
+        self.namespace = true;
+    }
+
+    /// Set the mask to view the text
+    pub fn set_text(&mut self) {
+        self.text = true;
+    }
+
+    /// Set the mask to view the listeners
+    pub fn set_listeners(&mut self) {
+        self.listeners = true;
+    }
+}
+
+/// A builder for a mask that controls what attributes are visible.
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum AttributeMaskBuilder<'a> {
+    /// All attributes are visible
+    All,
+    /// Only the given attributes are visible
+    Some(&'a [&'a str]),
+}
+
+impl Default for AttributeMaskBuilder<'_> {
+    fn default() -> Self {
+        AttributeMaskBuilder::Some(&[])
+    }
+}
+
+/// A mask that limits what parts of a node a dependency can see.
+#[derive(Default, PartialEq, Eq, Clone, Debug)]
+pub struct NodeMaskBuilder<'a> {
+    attritutes: AttributeMaskBuilder<'a>,
+    tag: bool,
+    namespace: bool,
+    text: bool,
+    listeners: bool,
+}
+
+impl<'a> NodeMaskBuilder<'a> {
+    /// A node mask with no parts visible.
+    pub const NONE: Self = Self::new();
+    /// A node mask with every part visible.
+    pub const ALL: Self = Self::new()
+        .with_attrs(AttributeMaskBuilder::All)
+        .with_text()
+        .with_element()
+        .with_listeners();
+
+    /// Create a empty node mask
+    pub const fn new() -> Self {
         Self {
-            attritutes,
+            attritutes: AttributeMaskBuilder::Some(&[]),
             tag: false,
             namespace: false,
             text: false,
@@ -271,9 +254,10 @@ impl NodeMask {
         }
     }
 
-    /// Create a empty node mask
-    pub const fn new() -> Self {
-        Self::new_with_attrs(AttributeMask::NONE)
+    /// Allow the mask to view the given attributes
+    pub const fn with_attrs(mut self, attritutes: AttributeMaskBuilder<'a>) -> Self {
+        self.attritutes = attritutes;
+        self
     }
 
     /// Allow the mask to view the tag
@@ -304,4 +288,20 @@ impl NodeMask {
         self.listeners = true;
         self
     }
+
+    /// Build the mask
+    pub fn build(self) -> NodeMask {
+        NodeMask {
+            attritutes: match self.attritutes {
+                AttributeMaskBuilder::All => AttributeMask::All,
+                AttributeMaskBuilder::Some(attrs) => {
+                    AttributeMask::Some(attrs.iter().map(|s| (*s).into()).collect())
+                }
+            },
+            tag: self.tag,
+            namespace: self.namespace,
+            text: self.text,
+            listeners: self.listeners,
+        }
+    }
 }

+ 17 - 0
packages/native-core/src/node_watcher.rs

@@ -0,0 +1,17 @@
+//! Helpers for watching for changes in the DOM tree.
+
+use crate::{node::FromAnyValue, prelude::*};
+
+/// A trait for watching for changes in the DOM tree.
+pub trait NodeWatcher<V: FromAnyValue + Send + Sync> {
+    /// Called after a node is added to the tree.
+    fn on_node_added(&self, _node: NodeMut<V>) {}
+    /// Called before a node is removed from the tree.
+    fn on_node_removed(&self, _node: NodeMut<V>) {}
+    /// Called after a node is moved to a new parent.
+    fn on_node_moved(&self, _node: NodeMut<V>) {}
+    // /// Called after the text content of a node is changed.
+    // fn on_text_changed(&self, _node: NodeMut<V>) {}
+    // /// Called after an attribute of an element is changed.
+    // fn on_attribute_changed(&self, _node: NodeMut<V>, attribute: &str) {}
+}

+ 243 - 826
packages/native-core/src/passes.rs

@@ -1,911 +1,328 @@
-use crate::tree::{NodeId, TreeView};
-use crate::{FxDashSet, SendAnyMap};
+use parking_lot::RwLock;
 use rustc_hash::{FxHashMap, FxHashSet};
+use shipyard::{Borrow, BorrowInfo, Component, Unique, UniqueView, View, WorkloadSystem};
+use std::any::{Any, TypeId};
 use std::collections::BTreeMap;
-use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
+use std::marker::PhantomData;
+use std::ops::Deref;
 use std::sync::Arc;
 
+use crate::node::{FromAnyValue, NodeType};
+use crate::node_ref::{NodeMaskBuilder, NodeView};
+use crate::real_dom::{DirtyNodesResult, SendAnyMapWrapper};
+use crate::tree::{TreeRef, TreeRefView};
+use crate::SendAnyMap;
+use crate::{NodeId, NodeMask};
+
 #[derive(Default)]
 struct DirtyNodes {
-    passes_dirty: Vec<u64>,
+    nodes_dirty: Vec<NodeId>,
 }
 
 impl DirtyNodes {
-    fn add_node(&mut self, node_id: NodeId) {
-        let node_id = node_id.0;
-        let index = node_id / 64;
-        let bit = node_id % 64;
-        let encoded = 1 << bit;
-        if let Some(passes) = self.passes_dirty.get_mut(index) {
-            *passes |= encoded;
-        } else {
-            self.passes_dirty.resize(index + 1, 0);
-            self.passes_dirty[index] |= encoded;
-        }
+    pub fn add_node(&mut self, node_id: NodeId) {
+        self.nodes_dirty.push(node_id);
     }
 
-    fn is_empty(&self) -> bool {
-        self.passes_dirty.iter().all(|dirty| *dirty == 0)
+    pub fn is_empty(&self) -> bool {
+        self.nodes_dirty.is_empty()
     }
 
-    fn pop(&mut self) -> Option<NodeId> {
-        let index = self.passes_dirty.iter().position(|dirty| *dirty != 0)?;
-        let passes = self.passes_dirty[index];
-        let node_id = passes.trailing_zeros();
-        let encoded = 1 << node_id;
-        self.passes_dirty[index] &= !encoded;
-        Some(NodeId((index * 64) + node_id as usize))
+    pub fn pop(&mut self) -> Option<NodeId> {
+        self.nodes_dirty.pop()
     }
 }
 
-#[derive(Default)]
+#[derive(Clone, Unique)]
 pub struct DirtyNodeStates {
-    dirty: BTreeMap<u16, FxHashMap<PassId, DirtyNodes>>,
+    dirty: Arc<FxHashMap<TypeId, RwLock<BTreeMap<u16, DirtyNodes>>>>,
 }
 
 impl DirtyNodeStates {
-    pub fn insert(&mut self, pass_id: PassId, node_id: NodeId, height: u16) {
-        if let Some(dirty) = self.dirty.get_mut(&height) {
-            if let Some(entry) = dirty.get_mut(&pass_id) {
+    pub fn with_passes(passes: impl Iterator<Item = TypeId>) -> Self {
+        Self {
+            dirty: Arc::new(
+                passes
+                    .map(|pass| (pass, RwLock::new(BTreeMap::new())))
+                    .collect(),
+            ),
+        }
+    }
+
+    pub fn insert(&self, pass_id: TypeId, node_id: NodeId, height: u16) {
+        if let Some(btree) = self.dirty.get(&pass_id) {
+            let mut write = btree.write();
+            if let Some(entry) = write.get_mut(&height) {
                 entry.add_node(node_id);
             } else {
                 let mut entry = DirtyNodes::default();
                 entry.add_node(node_id);
-                dirty.insert(pass_id, entry);
+                write.insert(height, entry);
             }
-        } else {
-            let mut entry = DirtyNodes::default();
-            entry.add_node(node_id);
-            let mut hm = FxHashMap::default();
-            hm.insert(pass_id, entry);
-            self.dirty.insert(height, hm);
         }
     }
 
-    fn pop_front(&mut self, pass_id: PassId) -> Option<(u16, NodeId)> {
-        let (&height, values) = self
-            .dirty
-            .iter_mut()
-            .find(|(_, values)| values.contains_key(&pass_id))?;
-        let dirty = values.get_mut(&pass_id)?;
-        let node_id = dirty.pop()?;
-        if dirty.is_empty() {
-            values.remove(&pass_id);
-        }
-        if values.is_empty() {
-            self.dirty.remove(&height);
+    fn pop_front(&self, pass_id: TypeId) -> Option<(u16, NodeId)> {
+        let mut values = self.dirty.get(&pass_id)?.write();
+        let mut value = values.first_entry()?;
+        let height = *value.key();
+        let ids = value.get_mut();
+        let id = ids.pop()?;
+        if ids.is_empty() {
+            value.remove_entry();
         }
 
-        Some((height, node_id))
+        Some((height, id))
     }
 
-    fn pop_back(&mut self, pass_id: PassId) -> Option<(u16, NodeId)> {
-        let (&height, values) = self
-            .dirty
-            .iter_mut()
-            .rev()
-            .find(|(_, values)| values.contains_key(&pass_id))?;
-        let dirty = values.get_mut(&pass_id)?;
-        let node_id = dirty.pop()?;
-        if dirty.is_empty() {
-            values.remove(&pass_id);
+    fn pop_back(&self, pass_id: TypeId) -> Option<(u16, NodeId)> {
+        let mut values = self.dirty.get(&pass_id)?.write();
+        let mut value = values.last_entry()?;
+        let height = *value.key();
+        let ids = value.get_mut();
+        let id = ids.pop()?;
+        if ids.is_empty() {
+            value.remove_entry();
         }
-        if values.is_empty() {
-            self.dirty.remove(&height);
-        }
-
-        Some((height, node_id))
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
-pub struct PassId(pub u64);
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
-pub struct MemberMask(pub u64);
 
-impl MemberMask {
-    pub fn overlaps(&self, other: Self) -> bool {
-        (*self & other).0 != 0
+        Some((height, id))
     }
 }
 
-impl BitAndAssign for MemberMask {
-    fn bitand_assign(&mut self, rhs: Self) {
-        self.0 &= rhs.0;
-    }
-}
-
-impl BitAnd for MemberMask {
-    type Output = Self;
-
-    fn bitand(self, rhs: Self) -> Self::Output {
-        MemberMask(self.0 & rhs.0)
-    }
-}
-
-impl BitOrAssign for MemberMask {
-    fn bitor_assign(&mut self, rhs: Self) {
-        self.0 |= rhs.0;
+/// A state that is automatically inserted in a node with dependencies.
+pub trait State<V: FromAnyValue + Send + Sync = ()>: Any + Send + Sync {
+    /// This is a tuple of (T: State, ..) of states read from the parent required to update this state
+    type ParentDependencies: Dependancy;
+    /// This is a tuple of (T: State, ..) of states read from the children required to update this state
+    type ChildDependencies: Dependancy;
+    /// This is a tuple of (T: State, ..) of states read from the node required to update this state
+    type NodeDependencies: Dependancy;
+    /// This is a mask of what aspects of the node are required to update this state
+    const NODE_MASK: NodeMaskBuilder<'static>;
+
+    /// Update this state in a node, returns if the state was updated
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView<V>,
+        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;
+
+    /// Create a new instance of this state
+    fn create<'a>(
+        node_view: NodeView<V>,
+        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,
+    ) -> Self;
+
+    /// Create a workload system for this state
+    fn workload_system(
+        type_id: TypeId,
+        dependants: FxHashSet<TypeId>,
+        pass_direction: PassDirection,
+    ) -> WorkloadSystem;
+
+    /// Converts to a type erased version of the trait
+    fn to_type_erased() -> TypeErasedState<V>
+    where
+        Self: Sized,
+    {
+        let node_mask = Self::NODE_MASK.build();
+        TypeErasedState {
+            this_type_id: TypeId::of::<Self>(),
+            combined_dependancy_type_ids: all_dependanices::<V, Self>().iter().copied().collect(),
+            parent_dependant: !Self::ParentDependencies::type_ids().is_empty(),
+            child_dependant: !Self::ChildDependencies::type_ids().is_empty(),
+            dependants: FxHashSet::default(),
+            mask: node_mask,
+            pass_direction: pass_direction::<V, Self>(),
+            workload: Self::workload_system,
+            phantom: PhantomData,
+        }
     }
 }
 
-impl BitOr for MemberMask {
-    type Output = Self;
-
-    fn bitor(self, rhs: Self) -> Self::Output {
-        Self(self.0 | rhs.0)
+fn pass_direction<V: FromAnyValue + Send + Sync, S: State<V>>() -> PassDirection {
+    if S::ChildDependencies::type_ids()
+        .iter()
+        .any(|type_id| *type_id == TypeId::of::<S>())
+    {
+        PassDirection::ChildToParent
+    } else if S::ParentDependencies::type_ids()
+        .iter()
+        .any(|type_id| *type_id == TypeId::of::<S>())
+    {
+        PassDirection::ParentToChild
+    } else {
+        PassDirection::AnyOrder
     }
 }
 
-pub struct PassReturn {
-    pub progress: bool,
-    pub mark_dirty: bool,
+fn all_dependanices<V: FromAnyValue + Send + Sync, S: State<V>>() -> Box<[TypeId]> {
+    let mut dependencies = S::ParentDependencies::type_ids().to_vec();
+    dependencies.extend(S::ChildDependencies::type_ids().iter());
+    dependencies.extend(S::NodeDependencies::type_ids().iter());
+    dependencies.into_boxed_slice()
 }
 
-pub trait Pass {
-    fn pass_id(&self) -> PassId;
-    fn dependancies(&self) -> &'static [PassId];
-    fn dependants(&self) -> &'static [PassId];
-    fn mask(&self) -> MemberMask;
+#[doc(hidden)]
+#[derive(Borrow, BorrowInfo)]
+pub struct RunPassView<'a, V: FromAnyValue + Send + Sync = ()> {
+    pub tree: TreeRefView<'a>,
+    pub node_type: View<'a, NodeType<V>>,
+    dirty_nodes_result: UniqueView<'a, DirtyNodesResult>,
+    node_states: UniqueView<'a, DirtyNodeStates>,
+    any_map: UniqueView<'a, SendAnyMapWrapper>,
 }
 
-pub trait UpwardPass<T>: Pass {
-    fn pass(
-        &self,
-        node: &mut T,
-        children: &mut dyn Iterator<Item = &mut T>,
-        ctx: &SendAnyMap,
-    ) -> PassReturn;
-}
-
-fn resolve_upward_pass<T, P: UpwardPass<T> + ?Sized>(
-    tree: &mut impl TreeView<T>,
-    pass: &P,
-    dirty_states: &mut DirtyNodeStates,
-    nodes_updated: &FxDashSet<NodeId>,
-    ctx: &SendAnyMap,
+// This is used by the macro
+/// Updates the given pass, marking any nodes that were changed
+#[doc(hidden)]
+pub fn run_pass<V: FromAnyValue + Send + Sync>(
+    type_id: TypeId,
+    dependants: FxHashSet<TypeId>,
+    pass_direction: PassDirection,
+    view: RunPassView<V>,
+    mut update_node: impl FnMut(NodeId, &SendAnyMap) -> bool,
 ) {
-    let pass_id = pass.pass_id();
-    while let Some((height, id)) = dirty_states.pop_back(pass_id) {
-        let (node, mut children) = tree.parent_child_mut(id).unwrap();
-        let result = pass.pass(node, &mut children, ctx);
-        drop(children);
-        if result.progress || result.mark_dirty {
-            nodes_updated.insert(id);
-            if let Some(id) = tree.parent_id(id) {
-                if result.mark_dirty {
-                    for dependant in pass.dependants() {
-                        dirty_states.insert(*dependant, id, height - 1);
+    let RunPassView {
+        tree,
+        dirty_nodes_result: nodes_updated,
+        node_states: dirty,
+        any_map: ctx,
+        ..
+    } = view;
+    let ctx = ctx.as_ref();
+    match pass_direction {
+        PassDirection::ParentToChild => {
+            while let Some((height, id)) = dirty.pop_front(type_id) {
+                if (update_node)(id, ctx) {
+                    nodes_updated.insert(id);
+                    for id in tree.children_ids(id) {
+                        for dependant in &dependants {
+                            dirty.insert(*dependant, id, height + 1);
+                        }
                     }
                 }
-                if result.progress && height > 0 {
-                    dirty_states.insert(pass_id, id, height - 1);
-                }
             }
         }
-    }
-}
-
-pub trait DownwardPass<T>: Pass {
-    fn pass(&self, node: &mut T, parent: Option<&mut T>, ctx: &SendAnyMap) -> PassReturn;
-}
-
-fn resolve_downward_pass<T, P: DownwardPass<T> + ?Sized>(
-    tree: &mut impl TreeView<T>,
-    pass: &P,
-    dirty_states: &mut DirtyNodeStates,
-    nodes_updated: &FxDashSet<NodeId>,
-    ctx: &SendAnyMap,
-) {
-    let pass_id = pass.pass_id();
-    while let Some((height, id)) = dirty_states.pop_front(pass_id) {
-        let (node, parent) = tree.node_parent_mut(id).unwrap();
-        let result = pass.pass(node, parent, ctx);
-        if result.mark_dirty {
-            nodes_updated.insert(id);
-        }
-        if result.mark_dirty || result.progress {
-            for id in tree.children_ids(id).unwrap() {
-                if result.mark_dirty {
-                    for dependant in pass.dependants() {
-                        dirty_states.insert(*dependant, *id, height + 1);
+        PassDirection::ChildToParent => {
+            while let Some((height, id)) = dirty.pop_back(type_id) {
+                if (update_node)(id, ctx) {
+                    nodes_updated.insert(id);
+                    if let Some(id) = tree.parent_id(id) {
+                        for dependant in &dependants {
+                            dirty.insert(*dependant, id, height - 1);
+                        }
                     }
                 }
-                if result.progress {
-                    dirty_states.insert(pass_id, *id, height + 1);
-                }
-            }
-        }
-    }
-}
-
-pub trait NodePass<T>: Pass {
-    fn pass(&self, node: &mut T, ctx: &SendAnyMap) -> bool;
-}
-
-fn resolve_node_pass<T, P: NodePass<T> + ?Sized>(
-    tree: &mut impl TreeView<T>,
-    pass: &P,
-    dirty_states: &mut DirtyNodeStates,
-    nodes_updated: &FxDashSet<NodeId>,
-    ctx: &SendAnyMap,
-) {
-    let pass_id = pass.pass_id();
-    while let Some((height, id)) = dirty_states.pop_back(pass_id) {
-        let node = tree.get_mut(id).unwrap();
-        if pass.pass(node, ctx) {
-            nodes_updated.insert(id);
-            for dependant in pass.dependants() {
-                dirty_states.insert(*dependant, id, height);
             }
         }
-    }
-}
-
-pub enum AnyPass<T: 'static> {
-    Upward(&'static (dyn UpwardPass<T> + Send + Sync + 'static)),
-    Downward(&'static (dyn DownwardPass<T> + Send + Sync + 'static)),
-    Node(&'static (dyn NodePass<T> + Send + Sync + 'static)),
-}
-
-impl<T> AnyPass<T> {
-    pub fn pass_id(&self) -> PassId {
-        match self {
-            Self::Upward(pass) => pass.pass_id(),
-            Self::Downward(pass) => pass.pass_id(),
-            Self::Node(pass) => pass.pass_id(),
-        }
-    }
-
-    pub fn dependancies(&self) -> &'static [PassId] {
-        match self {
-            Self::Upward(pass) => pass.dependancies(),
-            Self::Downward(pass) => pass.dependancies(),
-            Self::Node(pass) => pass.dependancies(),
-        }
-    }
-
-    fn resolve(
-        &self,
-        tree: &mut impl TreeView<T>,
-        dirty_states: &mut DirtyNodeStates,
-        nodes_updated: &FxDashSet<NodeId>,
-        ctx: &SendAnyMap,
-    ) {
-        match self {
-            Self::Downward(pass) => {
-                resolve_downward_pass(tree, *pass, dirty_states, nodes_updated, ctx)
-            }
-            Self::Upward(pass) => {
-                resolve_upward_pass(tree, *pass, dirty_states, nodes_updated, ctx)
+        PassDirection::AnyOrder => {
+            while let Some((height, id)) = dirty.pop_back(type_id) {
+                if (update_node)(id, ctx) {
+                    nodes_updated.insert(id);
+                    for dependant in &dependants {
+                        dirty.insert(*dependant, id, height);
+                    }
+                }
             }
-            Self::Node(pass) => resolve_node_pass(tree, *pass, dirty_states, nodes_updated, ctx),
         }
     }
 }
 
-pub fn resolve_passes<T, Tr: TreeView<T> + Sync + Send>(
-    tree: &mut Tr,
-    dirty_nodes: DirtyNodeStates,
-    passes: Vec<&AnyPass<T>>,
-    ctx: SendAnyMap,
-) -> FxDashSet<NodeId> {
-    resolve_passes_single_threaded(tree, dirty_nodes, passes, ctx)
-    // TODO: multithreadeding has some safety issues currently that need to be resolved before it can be used
-    // let dirty_states = Arc::new(dirty_nodes);
-    // let mut resolved_passes: FxHashSet<PassId> = FxHashSet::default();
-    // let mut resolving = Vec::new();
-    // let nodes_updated = Arc::new(FxDashSet::default());
-    // let ctx = Arc::new(ctx);
-    // while !passes.is_empty() {
-    //     let mut currently_borrowed = MemberMask::default();
-    //     std::thread::scope(|s| {
-    //         let mut i = 0;
-    //         while i < passes.len() {
-    //             let pass = &passes[i];
-    //             let pass_id = pass.pass_id();
-    //             let pass_mask = pass.mask();
-    //             if pass
-    //                 .dependancies()
-    //                 .iter()
-    //                 .all(|d| resolved_passes.contains(d) || *d == pass_id)
-    //                 && !pass_mask.overlaps(currently_borrowed)
-    //             {
-    //                 let pass = passes.remove(i);
-    //                 resolving.push(pass_id);
-    //                 currently_borrowed |= pass_mask;
-    //                 let dirty_states = dirty_states.clone();
-    //                 let nodes_updated = nodes_updated.clone();
-    //                 let ctx = ctx.clone();
-    //                 let mut dirty = DirtyNodes::default();
-    //                 // dirty_states.all_dirty(pass_id, &mut dirty, tree);
-    //                 // this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
-    //                 let tree_mut_unbounded = unsafe { &mut *(tree as *mut Tr) };
-    //                 s.spawn(move || {
-    //                     pass.resolve(
-    //                         tree_mut_unbounded,
-    //                         dirty,
-    //                         &dirty_states,
-    //                         &nodes_updated,
-    //                         &ctx,
-    //                     );
-    //                 });
-    //             } else {
-    //                 i += 1;
-    //             }
-    //         }
-    //         // all passes are resolved at the end of the scope
-    //     });
-    //     resolved_passes.extend(resolving.iter().copied());
-    //     resolving.clear()
-    // }
-    // std::sync::Arc::try_unwrap(nodes_updated).unwrap()
+/// A type erased version of [`State`] that can be added to the [`crate::prelude::RealDom`] with [`crate::prelude::RealDom::new`]
+pub struct TypeErasedState<V: FromAnyValue + Send = ()> {
+    pub(crate) this_type_id: TypeId,
+    pub(crate) parent_dependant: bool,
+    pub(crate) child_dependant: bool,
+    pub(crate) combined_dependancy_type_ids: FxHashSet<TypeId>,
+    pub(crate) dependants: FxHashSet<TypeId>,
+    pub(crate) mask: NodeMask,
+    pub(crate) workload: fn(TypeId, FxHashSet<TypeId>, PassDirection) -> WorkloadSystem,
+    pub(crate) pass_direction: PassDirection,
+    phantom: PhantomData<V>,
 }
 
-pub fn resolve_passes_single_threaded<T, Tr: TreeView<T>>(
-    tree: &mut Tr,
-    dirty_nodes: DirtyNodeStates,
-    mut passes: Vec<&AnyPass<T>>,
-    ctx: SendAnyMap,
-) -> FxDashSet<NodeId> {
-    let mut dirty_states = dirty_nodes;
-    let mut resolved_passes: FxHashSet<PassId> = FxHashSet::default();
-    let nodes_updated = Arc::new(FxDashSet::default());
-    let ctx = Arc::new(ctx);
-    while !passes.is_empty() {
-        for (i, pass) in passes.iter().enumerate() {
-            let pass_id = pass.pass_id();
-            if pass
-                .dependancies()
-                .iter()
-                .all(|d| resolved_passes.contains(d) || *d == pass_id)
-            {
-                let pass = passes.remove(i);
-                let nodes_updated = nodes_updated.clone();
-                let ctx = ctx.clone();
-                pass.resolve(tree, &mut dirty_states, &nodes_updated, &ctx);
-                resolved_passes.insert(pass_id);
-                break;
-            }
-        }
+impl<V: FromAnyValue + Send> TypeErasedState<V> {
+    pub(crate) fn create_workload(&self) -> WorkloadSystem {
+        (self.workload)(
+            self.this_type_id,
+            self.dependants.clone(),
+            self.pass_direction,
+        )
     }
-    std::sync::Arc::try_unwrap(nodes_updated).unwrap()
 }
 
-#[test]
-fn node_pass() {
-    use crate::tree::{Tree, TreeLike};
-    let mut tree = Tree::new(0);
-
-    struct AddPass;
-    impl Pass for AddPass {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-
-    impl NodePass<i32> for AddPass {
-        fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool {
-            *node += 1;
-            true
-        }
-    }
-
-    let add_pass = AnyPass::Node(&AddPass);
-    let passes = vec![&add_pass];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(0), tree.root(), 0);
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    assert_eq!(tree.get(tree.root()).unwrap(), &1);
+/// The direction that a pass should be run in
+#[derive(Debug, Clone, Copy)]
+pub enum PassDirection {
+    /// The pass should be run from the root to the leaves
+    ParentToChild,
+    /// The pass should be run from the leaves to the root
+    ChildToParent,
+    /// The pass can be run in any order
+    AnyOrder,
 }
 
-#[test]
-fn dependant_node_pass() {
-    use crate::tree::{Tree, TreeLike};
-    let mut tree = Tree::new(0);
+/// A trait that is implemented for all the dependancies of a [`State`]
+pub trait Dependancy {
+    /// A tuple with all the elements of the dependancy as [`DependancyView`]
+    type ElementBorrowed<'a>;
 
-    struct AddPass;
-    impl Pass for AddPass {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[PassId(1)]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-
-    impl NodePass<i32> for AddPass {
-        fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool {
-            *node += 1;
-            true
-        }
-    }
-
-    struct SubtractPass;
-
-    impl Pass for SubtractPass {
-        fn pass_id(&self) -> PassId {
-            PassId(1)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[PassId(0)]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl NodePass<i32> for SubtractPass {
-        fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool {
-            *node -= 1;
-            true
-        }
+    /// Returns a list of all the [`TypeId`]s of the elements in the dependancy
+    fn type_ids() -> Box<[TypeId]> {
+        Box::new([])
     }
-
-    let add_pass = AnyPass::Node(&AddPass);
-    let subtract_pass = AnyPass::Node(&SubtractPass);
-    let passes = vec![&add_pass, &subtract_pass];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(1), tree.root(), 0);
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    assert_eq!(*tree.get(tree.root()).unwrap(), 0);
 }
 
-#[test]
-fn independant_node_pass() {
-    use crate::tree::{Tree, TreeLike};
-    let mut tree = Tree::new((0, 0));
+macro_rules! impl_dependancy {
+    ($($t:ident),*) => {
+        impl< $($t: Send + Sync + Component),* > Dependancy for ($($t,)*) {
+            type ElementBorrowed<'a> = ($(DependancyView<'a, $t>,)*);
 
-    struct AddPass1;
-    impl Pass for AddPass1 {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-
-    impl NodePass<(i32, i32)> for AddPass1 {
-        fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool {
-            node.0 += 1;
-            true
-        }
-    }
-
-    struct AddPass2;
-    impl Pass for AddPass2 {
-        fn pass_id(&self) -> PassId {
-            PassId(1)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(1)
-        }
-    }
-
-    impl NodePass<(i32, i32)> for AddPass2 {
-        fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool {
-            node.1 += 1;
-            true
-        }
-    }
-
-    let add_pass1 = AnyPass::Node(&AddPass1);
-    let add_pass2 = AnyPass::Node(&AddPass2);
-    let passes = vec![&add_pass1, &add_pass2];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(0), tree.root(), 0);
-    dirty_nodes.insert(PassId(1), tree.root(), 0);
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    assert_eq!(tree.get(tree.root()).unwrap(), &(1, 1));
-}
-
-#[test]
-fn down_pass() {
-    use crate::tree::{Tree, TreeLike};
-    let mut tree = Tree::new(1);
-    let parent = tree.root();
-    let child1 = tree.create_node(1);
-    tree.add_child(parent, child1);
-    let grandchild1 = tree.create_node(1);
-    tree.add_child(child1, grandchild1);
-    let child2 = tree.create_node(1);
-    tree.add_child(parent, child2);
-    let grandchild2 = tree.create_node(1);
-    tree.add_child(child2, grandchild2);
-
-    struct AddPass;
-
-    impl Pass for AddPass {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl DownwardPass<i32> for AddPass {
-        fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn {
-            if let Some(parent) = parent {
-                *node += *parent;
-            }
-            PassReturn {
-                progress: true,
-                mark_dirty: true,
+            fn type_ids() -> Box<[TypeId]> {
+                Box::new([$(TypeId::of::<$t>()),*])
             }
         }
-    }
-
-    let add_pass = AnyPass::Downward(&AddPass);
-    let passes = vec![&add_pass];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(0), tree.root(), 0);
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    assert_eq!(tree.get(tree.root()).unwrap(), &1);
-    assert_eq!(tree.get(child1).unwrap(), &2);
-    assert_eq!(tree.get(grandchild1).unwrap(), &3);
-    assert_eq!(tree.get(child2).unwrap(), &2);
-    assert_eq!(tree.get(grandchild2).unwrap(), &3);
+    };
 }
 
-#[test]
-fn dependant_down_pass() {
-    use crate::tree::{Tree, TreeLike};
-    // 0
-    let mut tree = Tree::new(1);
-    let parent = tree.root();
-    // 1
-    let child1 = tree.create_node(1);
-    tree.add_child(parent, child1);
-    // 2
-    let grandchild1 = tree.create_node(1);
-    tree.add_child(child1, grandchild1);
-    // 3
-    let child2 = tree.create_node(1);
-    tree.add_child(parent, child2);
-    // 4
-    let grandchild2 = tree.create_node(1);
-    tree.add_child(child2, grandchild2);
-
-    struct AddPass;
-    impl Pass for AddPass {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[PassId(1)]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl DownwardPass<i32> for AddPass {
-        fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn {
-            if let Some(parent) = parent {
-                *node += *parent;
-            } else {
-            }
-            PassReturn {
-                progress: true,
-                mark_dirty: true,
-            }
-        }
-    }
-
-    struct SubtractPass;
-    impl Pass for SubtractPass {
-        fn pass_id(&self) -> PassId {
-            PassId(1)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[PassId(0)]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl DownwardPass<i32> for SubtractPass {
-        fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn {
-            if let Some(parent) = parent {
-                *node -= *parent;
-            } else {
-            }
-            PassReturn {
-                progress: true,
-                mark_dirty: true,
-            }
-        }
-    }
-
-    let add_pass = AnyPass::Downward(&AddPass);
-    let subtract_pass = AnyPass::Downward(&SubtractPass);
-    let passes = vec![&add_pass, &subtract_pass];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(1), tree.root(), 0);
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    // Tree before:
-    // 1=\
-    //   1=\
-    //     1
-    //   1=\
-    //     1
-    // Tree after subtract:
-    // 1=\
-    //   0=\
-    //     1
-    //   0=\
-    //     1
-    // Tree after add:
-    // 1=\
-    //   1=\
-    //     2
-    //   1=\
-    //     2
-    assert_eq!(tree.get(tree.root()).unwrap(), &1);
-    assert_eq!(tree.get(child1).unwrap(), &1);
-    assert_eq!(tree.get(grandchild1).unwrap(), &2);
-    assert_eq!(tree.get(child2).unwrap(), &1);
-    assert_eq!(tree.get(grandchild2).unwrap(), &2);
+// TODO: track what components are actually read to update subscriptions
+// making this a wrapper makes it possible to implement that optimization without a breaking change
+/// A immutable view of a [`State`]
+pub struct DependancyView<'a, T> {
+    inner: &'a T,
 }
 
-#[test]
-fn up_pass() {
-    use crate::tree::{Tree, TreeLike};
-    // Tree before:
-    // 0=\
-    //   0=\
-    //     1
-    //   0=\
-    //     1
-    // Tree after:
-    // 2=\
-    //   1=\
-    //     1
-    //   1=\
-    //     1
-    let mut tree = Tree::new(0);
-    let parent = tree.root();
-    let child1 = tree.create_node(0);
-    tree.add_child(parent, child1);
-    let grandchild1 = tree.create_node(1);
-    tree.add_child(child1, grandchild1);
-    let child2 = tree.create_node(0);
-    tree.add_child(parent, child2);
-    let grandchild2 = tree.create_node(1);
-    tree.add_child(child2, grandchild2);
-
-    struct AddPass;
-    impl Pass for AddPass {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl UpwardPass<i32> for AddPass {
-        fn pass(
-            &self,
-            node: &mut i32,
-            children: &mut dyn Iterator<Item = &mut i32>,
-            _: &SendAnyMap,
-        ) -> PassReturn {
-            *node += children.map(|i| *i).sum::<i32>();
-            PassReturn {
-                progress: true,
-                mark_dirty: true,
-            }
-        }
+impl<'a, T> DependancyView<'a, T> {
+    // This should only be used in the macro. This is not a public API or stable
+    #[doc(hidden)]
+    pub fn new(inner: &'a T) -> Self {
+        Self { inner }
     }
-
-    let add_pass = AnyPass::Upward(&AddPass);
-    let passes = vec![&add_pass];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(0), grandchild1, tree.height(grandchild1).unwrap());
-    dirty_nodes.insert(PassId(0), grandchild2, tree.height(grandchild2).unwrap());
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    assert_eq!(tree.get(tree.root()).unwrap(), &2);
-    assert_eq!(tree.get(child1).unwrap(), &1);
-    assert_eq!(tree.get(grandchild1).unwrap(), &1);
-    assert_eq!(tree.get(child2).unwrap(), &1);
-    assert_eq!(tree.get(grandchild2).unwrap(), &1);
 }
 
-#[test]
-fn dependant_up_pass() {
-    use crate::tree::{Tree, TreeLike};
-    // 0
-    let mut tree = Tree::new(0);
-    let parent = tree.root();
-    // 1
-    let child1 = tree.create_node(0);
-    tree.add_child(parent, child1);
-    // 2
-    let grandchild1 = tree.create_node(1);
-    tree.add_child(child1, grandchild1);
-    // 3
-    let child2 = tree.create_node(0);
-    tree.add_child(parent, child2);
-    // 4
-    let grandchild2 = tree.create_node(1);
-    tree.add_child(child2, grandchild2);
-
-    struct AddPass;
-    impl Pass for AddPass {
-        fn pass_id(&self) -> PassId {
-            PassId(0)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[PassId(1)]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl UpwardPass<i32> for AddPass {
-        fn pass(
-            &self,
-            node: &mut i32,
-            children: &mut dyn Iterator<Item = &mut i32>,
-            _: &SendAnyMap,
-        ) -> PassReturn {
-            *node += children.map(|i| *i).sum::<i32>();
-            PassReturn {
-                progress: true,
-                mark_dirty: true,
-            }
-        }
-    }
-
-    struct SubtractPass;
-    impl Pass for SubtractPass {
-        fn pass_id(&self) -> PassId {
-            PassId(1)
-        }
-
-        fn dependancies(&self) -> &'static [PassId] {
-            &[]
-        }
-
-        fn dependants(&self) -> &'static [PassId] {
-            &[PassId(0)]
-        }
+impl<'a, T> Deref for DependancyView<'a, T> {
+    type Target = T;
 
-        fn mask(&self) -> MemberMask {
-            MemberMask(0)
-        }
-    }
-    impl UpwardPass<i32> for SubtractPass {
-        fn pass(
-            &self,
-            node: &mut i32,
-            children: &mut dyn Iterator<Item = &mut i32>,
-            _: &SendAnyMap,
-        ) -> PassReturn {
-            *node -= children.map(|i| *i).sum::<i32>();
-            PassReturn {
-                progress: true,
-                mark_dirty: true,
-            }
-        }
+    fn deref(&self) -> &Self::Target {
+        self.inner
     }
-
-    let add_pass = AnyPass::Upward(&AddPass);
-    let subtract_pass = AnyPass::Upward(&SubtractPass);
-    let passes = vec![&add_pass, &subtract_pass];
-    let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
-    dirty_nodes.insert(PassId(1), grandchild1, tree.height(grandchild1).unwrap());
-    dirty_nodes.insert(PassId(1), grandchild2, tree.height(grandchild2).unwrap());
-    resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
-
-    // Tree before:
-    // 0=\
-    //   0=\
-    //     1
-    //   0=\
-    //     1
-    // Tree after subtract:
-    // 2=\
-    //   -1=\
-    //      1
-    //   -1=\
-    //      1
-    // Tree after add:
-    // 2=\
-    //   0=\
-    //     1
-    //   0=\
-    //     1
-    assert_eq!(tree.get(tree.root()).unwrap(), &2);
-    assert_eq!(tree.get(child1).unwrap(), &0);
-    assert_eq!(tree.get(grandchild1).unwrap(), &1);
-    assert_eq!(tree.get(child2).unwrap(), &0);
-    assert_eq!(tree.get(grandchild2).unwrap(), &1);
 }
+
+impl_dependancy!();
+impl_dependancy!(A);
+impl_dependancy!(A, B);
+impl_dependancy!(A, B, C);
+impl_dependancy!(A, B, C, D);
+impl_dependancy!(A, B, C, D, E);
+impl_dependancy!(A, B, C, D, E, F);
+impl_dependancy!(A, B, C, D, E, F, G);
+impl_dependancy!(A, B, C, D, E, F, G, H);
+impl_dependancy!(A, B, C, D, E, F, G, H, I);
+impl_dependancy!(A, B, C, D, E, F, G, H, I, J);

+ 952 - 404
packages/native-core/src/real_dom.rs

@@ -1,482 +1,1030 @@
-use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
+//! A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
+
 use rustc_hash::{FxHashMap, FxHashSet};
-use std::fmt::Debug;
-use std::ops::{Deref, DerefMut, Index, IndexMut};
-
-use crate::node::{FromAnyValue, Node, NodeType, OwnedAttributeDiscription, OwnedAttributeValue};
-use crate::node_ref::{AttributeMask, NodeMask};
-use crate::passes::DirtyNodeStates;
-use crate::state::State;
-use crate::tree::{NodeId, Tree, TreeLike, TreeView};
-use crate::{FxDashSet, RealNodeId, SendAnyMap};
-
-fn mark_dirty(
-    node_id: NodeId,
-    mask: NodeMask,
-    nodes_updated: &mut FxHashMap<RealNodeId, NodeMask>,
-) {
-    if let Some(node) = nodes_updated.get_mut(&node_id) {
-        *node = node.union(&mask);
-    } else {
-        nodes_updated.insert(node_id, mask);
+use shipyard::error::GetStorage;
+use shipyard::track::Untracked;
+use shipyard::{Component, Get, IntoBorrow, ScheduledWorkload, Unique, View, ViewMut, Workload};
+use shipyard::{SystemModificator, World};
+use std::any::TypeId;
+use std::collections::VecDeque;
+use std::ops::{Deref, DerefMut};
+use std::sync::{Arc, RwLock};
+
+use crate::node::{
+    ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode,
+};
+use crate::node_ref::{NodeMask, NodeMaskBuilder};
+use crate::node_watcher::NodeWatcher;
+use crate::passes::{DirtyNodeStates, TypeErasedState};
+use crate::prelude::AttributeMaskBuilder;
+use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
+use crate::NodeId;
+use crate::{FxDashSet, SendAnyMap};
+
+/// The context passes can receive when they are executed
+#[derive(Unique)]
+pub(crate) struct SendAnyMapWrapper(SendAnyMap);
+
+impl Deref for SendAnyMapWrapper {
+    type Target = SendAnyMap;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+/// The nodes that were changed when updating the state of the RealDom
+#[derive(Unique, Default)]
+pub(crate) struct DirtyNodesResult(FxDashSet<NodeId>);
+
+impl Deref for DirtyNodesResult {
+    type Target = FxDashSet<NodeId>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
     }
 }
 
+/// The nodes that have been marked as dirty in the RealDom
+pub(crate) struct NodesDirty<V: FromAnyValue + Send + Sync> {
+    passes_updated: FxHashMap<NodeId, FxHashSet<TypeId>>,
+    nodes_updated: FxHashMap<NodeId, NodeMask>,
+    pub(crate) passes: Box<[TypeErasedState<V>]>,
+}
+
+impl<V: FromAnyValue + Send + Sync> NodesDirty<V> {
+    /// Mark a node as dirty
+    fn mark_dirty(&mut self, node_id: NodeId, mask: NodeMask) {
+        self.passes_updated.entry(node_id).or_default().extend(
+            self.passes
+                .iter()
+                .filter_map(|x| x.mask.overlaps(&mask).then_some(x.this_type_id)),
+        );
+        let nodes_updated = &mut self.nodes_updated;
+        if let Some(node) = nodes_updated.get_mut(&node_id) {
+            *node = node.union(&mask);
+        } else {
+            nodes_updated.insert(node_id, mask);
+        }
+    }
+
+    /// Mark a node as added or removed from the tree
+    fn mark_parent_added_or_removed(&mut self, node_id: NodeId) {
+        let hm = self.passes_updated.entry(node_id).or_default();
+        for pass in &*self.passes {
+            if pass.parent_dependant {
+                hm.insert(pass.this_type_id);
+            }
+        }
+    }
+
+    /// Mark a node as having a child added or removed
+    fn mark_child_changed(&mut self, node_id: NodeId) {
+        let hm = self.passes_updated.entry(node_id).or_default();
+        for pass in &*self.passes {
+            if pass.child_dependant {
+                hm.insert(pass.this_type_id);
+            }
+        }
+    }
+}
+
+type NodeWatchers<V> = Arc<RwLock<Vec<Box<dyn NodeWatcher<V> + Send + Sync>>>>;
+
 /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
 /// The render state passes from parent to children and or accumulates state from children to parents.
-/// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
+/// To get started:
+/// 1) Implement [crate::passes::State] for each part of your state that you want to compute incrementally
+/// 2) Create a RealDom [RealDom::new], passing in each state you created
+/// 3) Update the state of the RealDom by adding and modifying nodes
+/// 4) Call [RealDom::update_state] to update the state of incrementally computed values on each node
 ///
-/// # Custom values
+/// # Custom attribute values
 /// To allow custom values to be passed into attributes implement FromAnyValue on a type that can represent your custom value and specify the V generic to be that type. If you have many different custom values, it can be useful to use a enum type to represent the varients.
-#[derive(Debug)]
-pub struct RealDom<S: State<V>, V: FromAnyValue + 'static = ()> {
-    pub tree: Tree<Node<S, V>>,
-    /// a map from element id to real node id
-    node_id_mapping: Vec<Option<RealNodeId>>,
-    nodes_listening: FxHashMap<String, FxHashSet<RealNodeId>>,
-    stack: Vec<RealNodeId>,
-    templates: FxHashMap<String, Vec<RealNodeId>>,
-    root_initialized: bool,
-}
-
-impl<S: State<V>, V: FromAnyValue> Default for RealDom<S, V> {
-    fn default() -> Self {
-        Self::new()
-    }
+pub struct RealDom<V: FromAnyValue + Send + Sync = ()> {
+    pub(crate) world: World,
+    nodes_listening: FxHashMap<String, FxHashSet<NodeId>>,
+    pub(crate) dirty_nodes: NodesDirty<V>,
+    node_watchers: NodeWatchers<V>,
+    workload: ScheduledWorkload,
+    root_id: NodeId,
+    phantom: std::marker::PhantomData<V>,
 }
 
-impl<S: State<V>, V: FromAnyValue> RealDom<S, V> {
-    pub fn new() -> RealDom<S, V> {
-        let mut root = Node::new(NodeType::Element {
+impl<V: FromAnyValue + Send + Sync> RealDom<V> {
+    /// Create a new RealDom with the given states that will be inserted and updated when needed
+    pub fn new(tracked_states: impl Into<Box<[TypeErasedState<V>]>>) -> RealDom<V> {
+        let mut tracked_states = tracked_states.into();
+        // resolve dependants for each pass
+        for i in 1..tracked_states.len() {
+            let (before, after) = tracked_states.split_at_mut(i);
+            let (current, before) = before.split_last_mut().unwrap();
+            for pass in before.iter_mut().chain(after.iter_mut()) {
+                if current
+                    .combined_dependancy_type_ids
+                    .contains(&pass.this_type_id)
+                {
+                    pass.dependants.insert(current.this_type_id);
+                }
+            }
+        }
+        let workload = construct_workload(&mut tracked_states);
+        let (workload, _) = workload.build().unwrap();
+        let mut world = World::new();
+        let root_node: NodeType<V> = NodeType::Element(ElementNode {
             tag: "Root".to_string(),
             namespace: Some("Root".to_string()),
             attributes: FxHashMap::default(),
             listeners: FxHashSet::default(),
         });
-        root.node_data.element_id = Some(ElementId(0));
-        let mut tree = Tree::new(root);
-        let root_id = tree.root();
-        tree.get_mut(root_id).unwrap().node_data.node_id = root_id;
+        let root_id = world.add_entity(root_node);
+        {
+            let mut tree: TreeMutView = world.borrow().unwrap();
+            tree.create_node(root_id);
+        }
+
+        let mut passes_updated = FxHashMap::default();
+        let mut nodes_updated = FxHashMap::default();
+
+        passes_updated.insert(
+            root_id,
+            tracked_states.iter().map(|x| x.this_type_id).collect(),
+        );
+        nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build());
 
         RealDom {
-            tree,
-            node_id_mapping: vec![Some(root_id)],
+            world,
             nodes_listening: FxHashMap::default(),
-            stack: vec![root_id],
-            templates: FxHashMap::default(),
-            root_initialized: false,
-        }
-    }
-
-    pub fn element_to_node_id(&self, element_id: ElementId) -> RealNodeId {
-        self.node_id_mapping.get(element_id.0).unwrap().unwrap()
-    }
-
-    fn set_element_id(&mut self, node_id: NodeId, element_id: ElementId) {
-        let node = self.tree.get_mut(node_id).unwrap();
-        let node_id = node.node_data.node_id;
-        node.node_data.element_id = Some(element_id);
-        if self.node_id_mapping.len() <= element_id.0 {
-            self.node_id_mapping.resize(element_id.0 + 1, None);
-        }
-        self.node_id_mapping[element_id.0] = Some(node_id);
-    }
-
-    fn load_child(&self, path: &[u8]) -> RealNodeId {
-        let mut current = *self.stack.last().unwrap();
-        for i in path {
-            current = self.tree.children_ids(current).unwrap()[*i as usize];
-        }
-        current
-    }
-
-    fn create_node(&mut self, node: Node<S, V>) -> RealNodeId {
-        let node_id = self.tree.create_node(node);
-        let node = self.tree.get_mut(node_id).unwrap();
-        node.node_data.node_id = node_id;
-        node_id
-    }
-
-    fn add_child(&mut self, node_id: RealNodeId, child_id: RealNodeId) {
-        self.tree.add_child(node_id, child_id);
-    }
-
-    fn create_template_node(&mut self, node: &TemplateNode) -> RealNodeId {
-        match node {
-            TemplateNode::Element {
-                tag,
-                namespace,
-                attrs,
-                children,
-            } => {
-                let node = Node::new(NodeType::Element {
-                    tag: tag.to_string(),
-                    namespace: namespace.map(|s| s.to_string()),
-                    attributes: attrs
-                        .iter()
-                        .filter_map(|attr| match attr {
-                            dioxus_core::TemplateAttribute::Static {
-                                name,
-                                value,
-                                namespace,
-                            } => Some((
-                                OwnedAttributeDiscription {
-                                    namespace: namespace.map(|s| s.to_string()),
-                                    name: name.to_string(),
-                                    volatile: false,
-                                },
-                                OwnedAttributeValue::Text(value.to_string()),
-                            )),
-                            dioxus_core::TemplateAttribute::Dynamic { .. } => None,
-                        })
-                        .collect(),
-                    listeners: FxHashSet::default(),
-                });
-                let node_id = self.create_node(node);
-                for child in *children {
-                    let child_id = self.create_template_node(child);
-                    self.add_child(node_id, child_id);
+            dirty_nodes: NodesDirty {
+                passes_updated,
+                nodes_updated,
+                passes: tracked_states,
+            },
+            node_watchers: Default::default(),
+            workload,
+            root_id,
+            phantom: std::marker::PhantomData,
+        }
+    }
+
+    /// Get a reference to the tree.
+    pub fn tree_ref(&self) -> TreeRefView {
+        self.world.borrow().unwrap()
+    }
+
+    /// Get a mutable reference to the tree.
+    pub fn tree_mut(&self) -> TreeMutView {
+        self.world.borrow().unwrap()
+    }
+
+    /// Create a new node of the given type in the dom and return a mutable reference to it.
+    pub fn create_node(&mut self, node: impl Into<NodeType<V>>) -> NodeMut<'_, V> {
+        let id = self.world.add_entity(node.into());
+        self.tree_mut().create_node(id);
+        self.dirty_nodes
+            .passes_updated
+            .entry(id)
+            .or_default()
+            .extend(self.dirty_nodes.passes.iter().map(|x| x.this_type_id));
+        self.dirty_nodes
+            .mark_dirty(id, NodeMaskBuilder::ALL.build());
+        let watchers = self.node_watchers.clone();
+        for watcher in &*watchers.read().unwrap() {
+            watcher.on_node_added(NodeMut::new(id, self));
+        }
+        NodeMut::new(id, self)
+    }
+
+    /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
+    /// This can be useful to avoid creating duplicate events.
+    pub fn get_listening_sorted(&self, event: &str) -> Vec<NodeRef<V>> {
+        if let Some(nodes) = self.nodes_listening.get(event) {
+            let mut listening: Vec<_> = nodes
+                .iter()
+                .map(|id| (*id, self.tree_ref().height(*id).unwrap()))
+                .collect();
+            listening.sort_by(|(_, h1), (_, h2)| h1.cmp(h2).reverse());
+            listening
+                .into_iter()
+                .map(|(id, _)| NodeRef { id, dom: self })
+                .collect()
+        } else {
+            Vec::new()
+        }
+    }
+
+    /// Returns the id of the root node.
+    pub fn root_id(&self) -> NodeId {
+        self.root_id
+    }
+
+    /// Check if a node exists in the dom.
+    pub fn contains(&self, id: NodeId) -> bool {
+        self.tree_ref().contains(id)
+    }
+
+    /// Get a reference to a node.
+    pub fn get(&self, id: NodeId) -> Option<NodeRef<'_, V>> {
+        self.contains(id).then_some(NodeRef { id, dom: self })
+    }
+
+    /// Get a mutable reference to a node.
+    pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
+        let contains = self.contains(id);
+        contains.then(|| NodeMut::new(id, self))
+    }
+
+    /// Borrow a component from the world without updating the dirty nodes.
+    fn borrow_raw<'a, B: IntoBorrow>(&'a self) -> Result<B, GetStorage>
+    where
+        B::Borrow: shipyard::Borrow<'a, View = B>,
+    {
+        self.world.borrow()
+    }
+
+    /// Borrow a component from the world without updating the dirty nodes.
+    fn borrow_node_type_mut(&self) -> Result<ViewMut<NodeType<V>>, GetStorage> {
+        self.world.borrow()
+    }
+
+    /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
+    pub fn update_state(
+        &mut self,
+        ctx: SendAnyMap,
+    ) -> (FxDashSet<NodeId>, FxHashMap<NodeId, NodeMask>) {
+        let passes = std::mem::take(&mut self.dirty_nodes.passes_updated);
+        let nodes_updated = std::mem::take(&mut self.dirty_nodes.nodes_updated);
+        let dirty_nodes =
+            DirtyNodeStates::with_passes(self.dirty_nodes.passes.iter().map(|p| p.this_type_id));
+        let tree = self.tree_ref();
+        for (node_id, passes) in passes {
+            // remove any nodes that were created and then removed in the same mutations from the dirty nodes list
+            if let Some(height) = tree.height(node_id) {
+                for pass in passes {
+                    dirty_nodes.insert(pass, node_id, height);
                 }
-                node_id
             }
-            TemplateNode::Text { text } => self.create_node(Node::new(NodeType::Text {
-                text: text.to_string(),
-            })),
-            TemplateNode::Dynamic { .. } => self.create_node(Node::new(NodeType::Placeholder)),
-            TemplateNode::DynamicText { .. } => self.create_node(Node::new(NodeType::Text {
-                text: String::new(),
-            })),
         }
+
+        let _ = self.world.remove_unique::<DirtyNodeStates>();
+        let _ = self.world.remove_unique::<SendAnyMapWrapper>();
+        self.world.add_unique(dirty_nodes);
+        self.world.add_unique(SendAnyMapWrapper(ctx));
+        self.world.add_unique(DirtyNodesResult::default());
+
+        self.workload.run_with_world(&self.world).unwrap();
+
+        let dirty = self.world.remove_unique::<DirtyNodesResult>().unwrap();
+
+        (dirty.0, nodes_updated)
     }
 
-    /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state.
-    pub fn apply_mutations(
-        &mut self,
-        mutations: Mutations,
-    ) -> (DirtyNodeStates, FxHashMap<RealNodeId, NodeMask>) {
-        let mut nodes_updated: FxHashMap<RealNodeId, NodeMask> = FxHashMap::default();
-        for template in mutations.templates {
-            let mut template_root_ids = Vec::new();
-            for root in template.roots {
-                let id = self.create_template_node(root);
-                template_root_ids.push(id);
+    /// Traverses the dom in a depth first manner, calling the provided function on each node.
+    pub fn traverse_depth_first(&self, mut f: impl FnMut(NodeRef<V>)) {
+        let mut stack = vec![self.root_id()];
+        let tree = self.tree_ref();
+        while let Some(id) = stack.pop() {
+            if let Some(node) = self.get(id) {
+                f(node);
+                let children = tree.children_ids(id);
+                stack.extend(children.iter().copied().rev());
             }
-            self.templates
-                .insert(template.name.to_string(), template_root_ids);
-        }
-        if !self.root_initialized {
-            self.root_initialized = true;
-            let root_id = self.tree.root();
-            nodes_updated.insert(root_id, NodeMask::ALL);
-        }
-        for e in mutations.edits {
-            use dioxus_core::Mutation::*;
-            match e {
-                AppendChildren { id, m } => {
-                    let children = self.stack.split_off(self.stack.len() - m);
-                    let parent = self.element_to_node_id(id);
-                    for child in children {
-                        self.add_child(parent, child);
-                        mark_dirty(child, NodeMask::ALL, &mut nodes_updated);
-                    }
-                }
-                AssignId { path, id } => {
-                    self.set_element_id(self.load_child(path), id);
-                }
-                CreatePlaceholder { id } => {
-                    let node = Node::new(NodeType::Placeholder);
-                    let node_id = self.create_node(node);
-                    self.set_element_id(node_id, id);
-                    self.stack.push(node_id);
-                    mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated);
-                }
-                CreateTextNode { value, id } => {
-                    let node = Node::new(NodeType::Text {
-                        text: value.to_string(),
-                    });
-                    let node_id = self.create_node(node);
-                    self.set_element_id(node_id, id);
-                    self.stack.push(node_id);
-                    mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
-                }
-                HydrateText { path, value, id } => {
-                    let node_id = self.load_child(path);
-                    self.set_element_id(node_id, id);
-                    let node = self.tree.get_mut(node_id).unwrap();
-                    if let NodeType::Text { text } = &mut node.node_data.node_type {
-                        *text = value.to_string();
-                    } else {
-                        node.node_data.node_type = NodeType::Text {
-                            text: value.to_string(),
-                        };
-                    }
-
-                    mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
-                }
-                LoadTemplate { name, index, id } => {
-                    let template_id = self.templates[name][index];
-                    let clone_id = self.clone_node(template_id, &mut nodes_updated);
-                    self.set_element_id(clone_id, id);
-                    self.stack.push(clone_id);
-                }
-                ReplaceWith { id, m } => {
-                    let new_nodes = self.stack.split_off(self.stack.len() - m);
-                    let old_node_id = self.element_to_node_id(id);
-                    for new in new_nodes {
-                        self.tree.insert_before(old_node_id, new);
-                        mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
-                    }
-                    self.remove(old_node_id, &mut nodes_updated);
-                }
-                ReplacePlaceholder { path, m } => {
-                    let new_nodes = self.stack.split_off(self.stack.len() - m);
-                    let old_node_id = self.load_child(path);
-                    for new in new_nodes {
-                        self.tree.insert_before(old_node_id, new);
-                        mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
-                    }
-                    self.remove(old_node_id, &mut nodes_updated);
-                }
-                InsertAfter { id, m } => {
-                    let new_nodes = self.stack.split_off(self.stack.len() - m);
-                    let old_node_id = self.element_to_node_id(id);
-                    for new in new_nodes.into_iter().rev() {
-                        self.tree.insert_after(old_node_id, new);
-                        mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
-                    }
-                }
-                InsertBefore { id, m } => {
-                    let new_nodes = self.stack.split_off(self.stack.len() - m);
-                    let old_node_id = self.element_to_node_id(id);
-                    for new in new_nodes {
-                        self.tree.insert_before(old_node_id, new);
-                        mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
-                    }
-                }
-                SetAttribute {
-                    name,
-                    value,
-                    id,
-                    ns,
-                } => {
-                    let node_id = self.element_to_node_id(id);
-                    let node = self.tree.get_mut(node_id).unwrap();
-                    if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type {
-                        if let BorrowedAttributeValue::None = &value {
-                            attributes.remove(&OwnedAttributeDiscription {
-                                name: name.to_string(),
-                                namespace: ns.map(|s| s.to_string()),
-                                volatile: false,
-                            });
-                            mark_dirty(
-                                node_id,
-                                NodeMask::new_with_attrs(AttributeMask::single(name)),
-                                &mut nodes_updated,
-                            );
-                        } else {
-                            attributes.insert(
-                                OwnedAttributeDiscription {
-                                    name: name.to_string(),
-                                    namespace: ns.map(|s| s.to_string()),
-                                    volatile: false,
-                                },
-                                OwnedAttributeValue::from(value),
-                            );
-                            mark_dirty(
-                                node_id,
-                                NodeMask::new_with_attrs(AttributeMask::single(name)),
-                                &mut nodes_updated,
-                            );
-                        }
-                    }
-                }
-                SetText { value, id } => {
-                    let node_id = self.element_to_node_id(id);
-                    let node = self.tree.get_mut(node_id).unwrap();
-                    if let NodeType::Text { text } = &mut node.node_data.node_type {
-                        *text = value.to_string();
-                    }
-                    mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
-                }
-                NewEventListener { name, id } => {
-                    let node_id = self.element_to_node_id(id);
-                    let node = self.tree.get_mut(node_id).unwrap();
-                    if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type {
-                        match self.nodes_listening.get_mut(name) {
-                            Some(hs) => {
-                                hs.insert(node_id);
-                            }
-                            None => {
-                                let mut hs = FxHashSet::default();
-                                hs.insert(node_id);
-                                self.nodes_listening.insert(name.to_string(), hs);
-                            }
-                        }
-                        listeners.insert(name.to_string());
-                    }
-                }
-                RemoveEventListener { id, name } => {
-                    let node_id = self.element_to_node_id(id);
-                    let node = self.tree.get_mut(node_id).unwrap();
-                    if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type {
-                        listeners.remove(name);
-                    }
-                    self.nodes_listening.get_mut(name).unwrap().remove(&node_id);
-                }
-                Remove { id } => {
-                    let node_id = self.element_to_node_id(id);
-                    self.remove(node_id, &mut nodes_updated);
-                }
-                PushRoot { id } => {
-                    let node_id = self.element_to_node_id(id);
-                    self.stack.push(node_id);
+        }
+    }
+
+    /// Traverses the dom in a breadth first manner, calling the provided function on each node.
+    pub fn traverse_breadth_first(&self, mut f: impl FnMut(NodeRef<V>)) {
+        let mut queue = VecDeque::new();
+        queue.push_back(self.root_id());
+        let tree = self.tree_ref();
+        while let Some(id) = queue.pop_front() {
+            if let Some(node) = self.get(id) {
+                f(node);
+                let children = tree.children_ids(id);
+                for id in children {
+                    queue.push_back(id);
                 }
             }
         }
+    }
 
-        let mut dirty_nodes = DirtyNodeStates::default();
-        for (&n, mask) in &nodes_updated {
-            // remove any nodes that were created and then removed in the same mutations from the dirty nodes list
-            if let Some(height) = self.tree.height(n) {
-                for (m, p) in S::MASKS.iter().zip(S::PASSES.iter()) {
-                    if mask.overlaps(m) {
-                        dirty_nodes.insert(p.pass_id(), n, height);
-                    }
+    /// Traverses the dom in a depth first manner mutably, calling the provided function on each node.
+    pub fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(NodeMut<V>)) {
+        let mut stack = vec![self.root_id()];
+        while let Some(id) = stack.pop() {
+            let tree = self.tree_ref();
+            let mut children = tree.children_ids(id);
+            drop(tree);
+            children.reverse();
+            if let Some(node) = self.get_mut(id) {
+                let node = node;
+                f(node);
+                stack.extend(children.iter());
+            }
+        }
+    }
+
+    /// Traverses the dom in a breadth first manner mutably, calling the provided function on each node.
+    pub fn traverse_breadth_first_mut(&mut self, mut f: impl FnMut(NodeMut<V>)) {
+        let mut queue = VecDeque::new();
+        queue.push_back(self.root_id());
+        while let Some(id) = queue.pop_front() {
+            let tree = self.tree_ref();
+            let children = tree.children_ids(id);
+            drop(tree);
+            if let Some(node) = self.get_mut(id) {
+                f(node);
+                for id in children {
+                    queue.push_back(id);
                 }
             }
         }
+    }
 
-        (dirty_nodes, nodes_updated)
+    /// Adds a [`NodeWatcher`] to the dom. Node watchers are called whenever a node is created or removed.
+    pub fn add_node_watcher(&mut self, watcher: impl NodeWatcher<V> + 'static + Send + Sync) {
+        self.node_watchers.write().unwrap().push(Box::new(watcher));
     }
 
-    /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
-    pub fn update_state_single_threaded(
-        &mut self,
-        nodes_updated: DirtyNodeStates,
-        ctx: SendAnyMap,
-    ) -> FxDashSet<RealNodeId> {
-        S::update_single_threaded(nodes_updated, &mut self.tree, ctx)
+    /// Returns a reference to the underlying world. Any changes made to the world will not update the reactive system.
+    pub fn raw_world(&self) -> &World {
+        &self.world
     }
 
-    /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
-    /// This can be useful to avoid creating duplicate events.
-    pub fn get_listening_sorted(&self, event: &str) -> Vec<&Node<S, V>> {
-        if let Some(nodes) = self.nodes_listening.get(event) {
-            let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
-            listening.sort_by(|n1, n2| {
-                (self.tree.height(n1.node_data.node_id))
-                    .cmp(&self.tree.height(n2.node_data.node_id))
-                    .reverse()
-            });
-            listening
+    /// Returns a mutable reference to the underlying world. Any changes made to the world will not update the reactive system.
+    pub fn raw_world_mut(&mut self) -> &mut World {
+        &mut self.world
+    }
+}
+
+/// A reference to a tracked component in a node.
+pub struct ViewEntry<'a, V: Component + Send + Sync> {
+    view: View<'a, V>,
+    id: NodeId,
+}
+
+impl<'a, V: Component + Send + Sync> ViewEntry<'a, V> {
+    fn new(view: View<'a, V>, id: NodeId) -> Self {
+        Self { view, id }
+    }
+}
+
+impl<'a, V: Component + Send + Sync> Deref for ViewEntry<'a, V> {
+    type Target = V;
+
+    fn deref(&self) -> &Self::Target {
+        &self.view[self.id]
+    }
+}
+
+/// A mutable reference to a tracked component in a node.
+pub struct ViewEntryMut<'a, V: Component<Tracking = Untracked> + Send + Sync> {
+    view: ViewMut<'a, V, Untracked>,
+    id: NodeId,
+}
+
+impl<'a, V: Component<Tracking = Untracked> + Send + Sync> ViewEntryMut<'a, V> {
+    fn new(view: ViewMut<'a, V, Untracked>, id: NodeId) -> Self {
+        Self { view, id }
+    }
+}
+
+impl<'a, V: Component<Tracking = Untracked> + Send + Sync> Deref for ViewEntryMut<'a, V> {
+    type Target = V;
+
+    fn deref(&self) -> &Self::Target {
+        self.view.get(self.id).unwrap()
+    }
+}
+
+impl<'a, V: Component<Tracking = Untracked> + Send + Sync> DerefMut for ViewEntryMut<'a, V> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        (&mut self.view).get(self.id).unwrap()
+    }
+}
+
+/// A immutable view of a node
+pub trait NodeImmutable<V: FromAnyValue + Send + Sync = ()>: Sized {
+    /// Get the real dom this node was created in
+    fn real_dom(&self) -> &RealDom<V>;
+
+    /// Get the id of the current node
+    fn id(&self) -> NodeId;
+
+    /// Get the type of the current node
+    #[inline]
+    fn node_type(&self) -> ViewEntry<NodeType<V>> {
+        self.get().unwrap()
+    }
+
+    /// Get a component from the current node
+    #[inline]
+    fn get<'a, T: Component + Sync + Send>(&'a self) -> Option<ViewEntry<'a, T>> {
+        // self.real_dom().tree.get(self.id())
+        let view: View<'a, T> = self.real_dom().borrow_raw().ok()?;
+        view.contains(self.id())
+            .then(|| ViewEntry::new(view, self.id()))
+    }
+
+    /// Get the ids of the children of the current node
+    #[inline]
+    fn child_ids(&self) -> Vec<NodeId> {
+        self.real_dom().tree_ref().children_ids(self.id())
+    }
+
+    /// Get the children of the current node
+    #[inline]
+    fn children(&self) -> Vec<NodeRef<V>> {
+        self.child_ids()
+            .iter()
+            .map(|id| NodeRef {
+                id: *id,
+                dom: self.real_dom(),
+            })
+            .collect()
+    }
+
+    /// Get the id of the parent of the current node
+    #[inline]
+    fn parent_id(&self) -> Option<NodeId> {
+        self.real_dom().tree_ref().parent_id(self.id())
+    }
+
+    /// Get the parent of the current node
+    #[inline]
+    fn parent(&self) -> Option<NodeRef<V>> {
+        self.parent_id().map(|id| NodeRef {
+            id,
+            dom: self.real_dom(),
+        })
+    }
+
+    /// Get the node after the current node
+    #[inline]
+    fn next(&self) -> Option<NodeRef<V>> {
+        let parent = self.parent_id()?;
+        let children = self.real_dom().tree_ref().children_ids(parent);
+        let index = children.iter().position(|id| *id == self.id())?;
+        if index + 1 < children.len() {
+            Some(NodeRef {
+                id: children[index + 1],
+                dom: self.real_dom(),
+            })
         } else {
-            Vec::new()
+            None
         }
     }
 
-    /// Return the number of nodes in the dom.
-    pub fn size(&self) -> usize {
-        // The dom has a root node, ignore it.
-        self.tree.size() - 1
+    /// Get the node before the current node
+    #[inline]
+    fn prev(&self) -> Option<NodeRef<V>> {
+        let parent = self.parent_id()?;
+        let children = self.real_dom().tree_ref().children_ids(parent);
+        let index = children.iter().position(|id| *id == self.id())?;
+        if index > 0 {
+            Some(NodeRef {
+                id: children[index - 1],
+                dom: self.real_dom(),
+            })
+        } else {
+            None
+        }
     }
 
-    /// Returns the id of the root node.
-    pub fn root_id(&self) -> NodeId {
-        self.tree.root()
+    /// Get the height of the current node in the tree (the number of nodes between the current node and the root)
+    #[inline]
+    fn height(&self) -> u16 {
+        self.real_dom().tree_ref().height(self.id()).unwrap()
     }
+}
 
-    fn clone_node(
+/// An immutable reference to a node in a RealDom
+pub struct NodeRef<'a, V: FromAnyValue + Send + Sync = ()> {
+    id: NodeId,
+    dom: &'a RealDom<V>,
+}
+
+impl<'a, V: FromAnyValue + Send + Sync> Clone for NodeRef<'a, V> {
+    fn clone(&self) -> Self {
+        Self {
+            id: self.id,
+            dom: self.dom,
+        }
+    }
+}
+
+impl<'a, V: FromAnyValue + Send + Sync> Copy for NodeRef<'a, V> {}
+
+impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable<V> for NodeRef<'a, V> {
+    #[inline(always)]
+    fn real_dom(&self) -> &RealDom<V> {
+        self.dom
+    }
+
+    #[inline(always)]
+    fn id(&self) -> NodeId {
+        self.id
+    }
+}
+
+/// A mutable refrence to a node in the RealDom that tracks what States need to be updated
+pub struct NodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
+    id: NodeId,
+    dom: &'a mut RealDom<V>,
+}
+
+impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
+    /// Create a new mutable refrence to a node in a RealDom
+    pub fn new(id: NodeId, dom: &'a mut RealDom<V>) -> Self {
+        Self { id, dom }
+    }
+}
+
+impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable<V> for NodeMut<'a, V> {
+    #[inline(always)]
+    fn real_dom(&self) -> &RealDom<V> {
+        self.dom
+    }
+
+    #[inline(always)]
+    fn id(&self) -> NodeId {
+        self.id
+    }
+}
+
+impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
+    /// Get the real dom this node was created in mutably
+    #[inline(always)]
+    pub fn real_dom_mut(&mut self) -> &mut RealDom<V> {
+        self.dom
+    }
+
+    /// Get the parent of this node mutably
+    #[inline]
+    pub fn parent_mut(&mut self) -> Option<NodeMut<V>> {
+        self.parent_id().map(|id| NodeMut { id, dom: self.dom })
+    }
+
+    /// Get a component from the current node mutably
+    #[inline]
+    pub fn get_mut<T: Component<Tracking = Untracked> + Sync + Send>(
         &mut self,
-        node_id: NodeId,
-        nodes_updated: &mut FxHashMap<RealNodeId, NodeMask>,
-    ) -> RealNodeId {
-        let node = self.tree.get(node_id).unwrap();
-        let new_node = node.clone();
-        let new_id = self.create_node(new_node);
-        mark_dirty(new_id, NodeMask::ALL, nodes_updated);
-        let self_ptr = self as *mut Self;
-        for child in self.tree.children_ids(node_id).unwrap() {
-            unsafe {
-                // this is safe because no node has itself as a child
-                let self_mut = &mut *self_ptr;
-                let child_id = self_mut.clone_node(*child, nodes_updated);
-                self_mut.add_child(new_id, child_id);
-            }
+    ) -> Option<ViewEntryMut<T>> {
+        // mark the node state as dirty
+        self.dom
+            .dirty_nodes
+            .passes_updated
+            .entry(self.id)
+            .or_default()
+            .insert(TypeId::of::<T>());
+        let view_mut: ViewMut<T> = self.dom.borrow_raw().ok()?;
+        view_mut
+            .contains(self.id)
+            .then_some(ViewEntryMut::new(view_mut, self.id))
+    }
+
+    /// Insert a custom component into this node
+    ///
+    /// Note: Components that implement State and are added when the RealDom is created will automatically be created
+    #[inline]
+    pub fn insert<T: Component + Sync + Send>(&mut self, value: T) {
+        // mark the node state as dirty
+        self.dom
+            .dirty_nodes
+            .passes_updated
+            .entry(self.id)
+            .or_default()
+            .insert(TypeId::of::<T>());
+        self.dom.world.add_component(self.id, value);
+    }
+
+    /// Get the next node
+    #[inline]
+    pub fn next_mut(self) -> Option<NodeMut<'a, V>> {
+        let parent = self.parent_id()?;
+        let children = self.dom.tree_mut().children_ids(parent);
+        let index = children.iter().position(|id| *id == self.id)?;
+        if index + 1 < children.len() {
+            Some(NodeMut::new(children[index + 1], self.dom))
+        } else {
+            None
         }
-        new_id
     }
 
-    fn remove(&mut self, node_id: NodeId, nodes_updated: &mut FxHashMap<RealNodeId, NodeMask>) {
-        let node = self.tree.get(node_id).unwrap();
-        if let NodeType::Element { listeners, .. } = &node.node_data.node_type {
-            for name in listeners.iter() {
-                self.nodes_listening.get_mut(name).unwrap().remove(&node_id);
+    /// Get the previous node
+    #[inline]
+    pub fn prev_mut(self) -> Option<NodeMut<'a, V>> {
+        let parent = self.parent_id()?;
+        let children = self.dom.tree_ref().children_ids(parent);
+        let index = children.iter().position(|id| *id == self.id)?;
+        if index > 0 {
+            Some(NodeMut::new(children[index - 1], self.dom))
+        } else {
+            None
+        }
+    }
+
+    /// Add the given node to the end of this nodes children
+    #[inline]
+    pub fn add_child(&mut self, child: NodeId) {
+        self.dom.dirty_nodes.mark_child_changed(self.id);
+        self.dom.dirty_nodes.mark_parent_added_or_removed(child);
+        self.dom.tree_mut().add_child(self.id, child);
+        NodeMut::new(child, self.dom).mark_moved();
+    }
+
+    /// Insert this node after the given node
+    #[inline]
+    pub fn insert_after(&mut self, old: NodeId) {
+        let id = self.id();
+        let parent_id = { self.dom.tree_ref().parent_id(old) };
+        if let Some(parent_id) = parent_id {
+            self.dom.dirty_nodes.mark_child_changed(parent_id);
+            self.dom.dirty_nodes.mark_parent_added_or_removed(id);
+        }
+        self.dom.tree_mut().insert_after(old, id);
+        self.mark_moved();
+    }
+
+    /// Insert this node before the given node
+    #[inline]
+    pub fn insert_before(&mut self, old: NodeId) {
+        let id = self.id();
+        let parent_id = { self.dom.tree_ref().parent_id(old) };
+        if let Some(parent_id) = parent_id {
+            self.dom.dirty_nodes.mark_child_changed(parent_id);
+            self.dom.dirty_nodes.mark_parent_added_or_removed(id);
+        }
+        self.dom.tree_mut().insert_before(old, id);
+        self.mark_moved();
+    }
+
+    /// Remove this node from the RealDom
+    #[inline]
+    pub fn remove(&mut self) {
+        let id = self.id();
+        {
+            let RealDom {
+                world,
+                nodes_listening,
+                ..
+            } = &mut self.dom;
+            let mut view: ViewMut<NodeType<V>> = world.borrow().unwrap();
+            if let NodeType::Element(ElementNode { listeners, .. })
+            | NodeType::Text(TextNode { listeners, .. }) = (&mut view).get(id).unwrap()
+            {
+                let listeners = std::mem::take(listeners);
+                for event in listeners {
+                    nodes_listening.get_mut(&event).unwrap().remove(&id);
+                }
             }
         }
-        if let Some(children) = self.tree.children_ids(node_id) {
-            let children = children.to_vec();
-            for child in children {
-                self.remove(child, nodes_updated);
+        self.mark_removed();
+        let parent_id = { self.dom.tree_ref().parent_id(id) };
+        if let Some(parent_id) = parent_id {
+            self.real_dom_mut()
+                .dirty_nodes
+                .mark_child_changed(parent_id);
+        }
+        let children_ids = self.child_ids();
+        let children_ids_vec = children_ids.to_vec();
+        for child in children_ids_vec {
+            self.dom.get_mut(child).unwrap().remove();
+        }
+        self.dom.tree_mut().remove(id);
+        self.real_dom_mut().raw_world_mut().delete_entity(id);
+    }
+
+    /// Replace this node with a different node
+    #[inline]
+    pub fn replace(mut self, new: NodeId) {
+        self.mark_removed();
+        if let Some(parent_id) = self.parent_id() {
+            self.real_dom_mut()
+                .dirty_nodes
+                .mark_child_changed(parent_id);
+            self.real_dom_mut()
+                .dirty_nodes
+                .mark_parent_added_or_removed(new);
+        }
+        let id = self.id();
+        self.dom.tree_mut().replace(id, new);
+    }
+
+    /// Add an event listener
+    #[inline]
+    pub fn add_event_listener(&mut self, event: &str) {
+        let id = self.id();
+        let RealDom {
+            world,
+            dirty_nodes,
+            nodes_listening,
+            ..
+        } = &mut self.dom;
+        let mut view: ViewMut<NodeType<V>> = world.borrow().unwrap();
+        let node_type: &mut NodeType<V> = (&mut view).get(self.id).unwrap();
+        if let NodeType::Element(ElementNode { listeners, .. })
+        | NodeType::Text(TextNode { listeners, .. }) = node_type
+        {
+            dirty_nodes.mark_dirty(self.id, NodeMaskBuilder::new().with_listeners().build());
+            listeners.insert(event.to_string());
+            match nodes_listening.get_mut(event) {
+                Some(hs) => {
+                    hs.insert(id);
+                }
+                None => {
+                    let mut hs = FxHashSet::default();
+                    hs.insert(id);
+                    nodes_listening.insert(event.to_string(), hs);
+                }
             }
         }
-        self.tree.remove(node_id);
-        mark_dirty(node_id, NodeMask::ALL, nodes_updated);
+    }
+
+    /// Remove an event listener
+    #[inline]
+    pub fn remove_event_listener(&mut self, event: &str) {
+        let id = self.id();
+        let RealDom {
+            world,
+            dirty_nodes,
+            nodes_listening,
+            ..
+        } = &mut self.dom;
+        let mut view: ViewMut<NodeType<V>> = world.borrow().unwrap();
+        let node_type: &mut NodeType<V> = (&mut view).get(self.id).unwrap();
+        if let NodeType::Element(ElementNode { listeners, .. })
+        | NodeType::Text(TextNode { listeners, .. }) = node_type
+        {
+            dirty_nodes.mark_dirty(self.id, NodeMaskBuilder::new().with_listeners().build());
+            listeners.remove(event);
+
+            nodes_listening.get_mut(event).unwrap().remove(&id);
+        }
+    }
+
+    /// mark that this node was removed for the incremental system
+    fn mark_removed(&mut self) {
+        let watchers = self.dom.node_watchers.clone();
+        for watcher in &*watchers.read().unwrap() {
+            watcher.on_node_removed(NodeMut::new(self.id(), self.dom));
+        }
+    }
+
+    /// mark that this node was moved for the incremental system
+    fn mark_moved(&mut self) {
+        let watchers = self.dom.node_watchers.clone();
+        for watcher in &*watchers.read().unwrap() {
+            watcher.on_node_moved(NodeMut::new(self.id(), self.dom));
+        }
+    }
+
+    /// Get a mutable reference to the type of the current node
+    pub fn node_type_mut(&mut self) -> NodeTypeMut<'_, V> {
+        let id = self.id();
+        let RealDom {
+            world, dirty_nodes, ..
+        } = &mut self.dom;
+        let view: ViewMut<NodeType<V>> = world.borrow().unwrap();
+        let node_type = ViewEntryMut::new(view, id);
+        match &*node_type {
+            NodeType::Element(_) => NodeTypeMut::Element(ElementNodeMut {
+                id,
+                element: node_type,
+                dirty_nodes,
+            }),
+            NodeType::Text(_) => NodeTypeMut::Text(TextNodeMut {
+                id,
+                text: node_type,
+                dirty_nodes,
+            }),
+            NodeType::Placeholder => NodeTypeMut::Placeholder,
+        }
+    }
+
+    /// Set the type of the current node
+    pub fn set_type(&mut self, new: NodeType<V>) {
+        {
+            let mut view: ViewMut<NodeType<V>> = self.dom.borrow_node_type_mut().unwrap();
+            *(&mut view).get(self.id).unwrap() = new;
+        }
+        self.dom
+            .dirty_nodes
+            .mark_dirty(self.id, NodeMaskBuilder::ALL.build())
+    }
+
+    /// Clone a node and it's children and returns the id of the new node.
+    /// This is more effecient than creating the node from scratch because it can pre-allocate the memory required.
+    #[inline]
+    pub fn clone_node(&mut self) -> NodeId {
+        let new_node = self.node_type().clone();
+        let rdom = self.real_dom_mut();
+        let new_id = rdom.create_node(new_node).id();
+
+        let children = self.child_ids();
+        let children = children.to_vec();
+        let rdom = self.real_dom_mut();
+        for child in children {
+            let child_id = rdom.get_mut(child).unwrap().clone_node();
+            rdom.get_mut(new_id).unwrap().add_child(child_id);
+        }
+        new_id
     }
 }
 
-impl<S: State<V> + Sync, V: FromAnyValue> RealDom<S, V>
-where
-    Tree<Node<S, V>>: Sync + Send,
-{
-    /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
-    /// This will resolve the state in parallel
-    pub fn update_state(
-        &mut self,
-        nodes_updated: DirtyNodeStates,
-        ctx: SendAnyMap,
-    ) -> FxDashSet<RealNodeId> {
-        S::update(nodes_updated, &mut self.tree, ctx)
+/// A mutable refrence to the type of a node in the RealDom
+pub enum NodeTypeMut<'a, V: FromAnyValue + Send + Sync = ()> {
+    /// An element node
+    Element(ElementNodeMut<'a, V>),
+    /// A text node
+    Text(TextNodeMut<'a, V>),
+    /// A placeholder node
+    Placeholder,
+}
+
+/// A mutable refrence to a text node in the RealDom
+pub struct TextNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
+    id: NodeId,
+    text: ViewEntryMut<'a, NodeType<V>>,
+    dirty_nodes: &'a mut NodesDirty<V>,
+}
+
+impl<V: FromAnyValue + Send + Sync> TextNodeMut<'_, V> {
+    /// Get the underlying test of the node
+    pub fn text(&self) -> &str {
+        match &*self.text {
+            NodeType::Text(text) => &text.text,
+            _ => unreachable!(),
+        }
+    }
+
+    /// Get the underlying text mutably
+    pub fn text_mut(&mut self) -> &mut String {
+        self.dirty_nodes
+            .mark_dirty(self.id, NodeMaskBuilder::new().with_text().build());
+        match &mut *self.text {
+            NodeType::Text(text) => &mut text.text,
+            _ => unreachable!(),
+        }
     }
 }
 
-impl<S: State<V>, V: FromAnyValue> Deref for RealDom<S, V> {
-    type Target = Tree<Node<S, V>>;
+impl<V: FromAnyValue + Send + Sync> Deref for TextNodeMut<'_, V> {
+    type Target = String;
 
     fn deref(&self) -> &Self::Target {
-        &self.tree
+        match &*self.text {
+            NodeType::Text(text) => &text.text,
+            _ => unreachable!(),
+        }
     }
 }
 
-impl<S: State<V>, V: FromAnyValue> DerefMut for RealDom<S, V> {
+impl<V: FromAnyValue + Send + Sync> DerefMut for TextNodeMut<'_, V> {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.tree
+        self.text_mut()
     }
 }
 
-impl<S: State<V>, V: FromAnyValue> Index<ElementId> for RealDom<S, V> {
-    type Output = Node<S, V>;
+/// A mutable refrence to a text Element node in the RealDom
+pub struct ElementNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
+    id: NodeId,
+    element: ViewEntryMut<'a, NodeType<V>>,
+    dirty_nodes: &'a mut NodesDirty<V>,
+}
 
-    fn index(&self, id: ElementId) -> &Self::Output {
-        self.tree.get(self.element_to_node_id(id)).unwrap()
+impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
+    /// Get the current element
+    fn element(&self) -> &ElementNode<V> {
+        match &*self.element {
+            NodeType::Element(element) => element,
+            _ => unreachable!(),
+        }
+    }
+
+    /// Get the current element mutably (does not mark anything as dirty)
+    fn element_mut(&mut self) -> &mut ElementNode<V> {
+        match &mut *self.element {
+            NodeType::Element(element) => element,
+            _ => unreachable!(),
+        }
     }
-}
 
-impl<S: State<V>, V: FromAnyValue> Index<RealNodeId> for RealDom<S, V> {
-    type Output = Node<S, V>;
+    /// Get the tag of the element
+    pub fn tag(&self) -> &str {
+        &self.element().tag
+    }
 
-    fn index(&self, idx: RealNodeId) -> &Self::Output {
-        self.tree.get(idx).unwrap()
+    /// Get a mutable reference to the tag of the element
+    pub fn tag_mut(&mut self) -> &mut String {
+        self.dirty_nodes
+            .mark_dirty(self.id, NodeMaskBuilder::new().with_tag().build());
+        &mut self.element_mut().tag
+    }
+
+    /// Get a reference to the namespace the element is in
+    pub fn namespace(&self) -> Option<&str> {
+        self.element().namespace.as_deref()
+    }
+
+    /// Get a mutable reference to the namespace the element is in
+    pub fn namespace_mut(&mut self) -> &mut Option<String> {
+        self.dirty_nodes
+            .mark_dirty(self.id, NodeMaskBuilder::new().with_namespace().build());
+        &mut self.element_mut().namespace
+    }
+
+    /// Get a reference to all of the attributes currently set on the element
+    pub fn attributes(&self) -> &FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>> {
+        &self.element().attributes
     }
-}
 
-impl<S: State<V>, V: FromAnyValue> IndexMut<ElementId> for RealDom<S, V> {
-    fn index_mut(&mut self, id: ElementId) -> &mut Self::Output {
-        self.tree.get_mut(self.element_to_node_id(id)).unwrap()
+    /// Set an attribute in the element
+    pub fn set_attribute(
+        &mut self,
+        name: impl Into<OwnedAttributeDiscription>,
+        value: impl Into<OwnedAttributeValue<V>>,
+    ) -> Option<OwnedAttributeValue<V>> {
+        let name = name.into();
+        let value = value.into();
+        self.dirty_nodes.mark_dirty(
+            self.id,
+            NodeMaskBuilder::new()
+                .with_attrs(AttributeMaskBuilder::Some(&[&name.name]))
+                .build(),
+        );
+        self.element_mut().attributes.insert(name, value)
+    }
+
+    /// Remove an attribute from the element
+    pub fn remove_attribute(
+        &mut self,
+        name: &OwnedAttributeDiscription,
+    ) -> Option<OwnedAttributeValue<V>> {
+        self.dirty_nodes.mark_dirty(
+            self.id,
+            NodeMaskBuilder::new()
+                .with_attrs(AttributeMaskBuilder::Some(&[&name.name]))
+                .build(),
+        );
+        self.element_mut().attributes.remove(name)
+    }
+
+    /// Get an attribute of the element
+    pub fn get_attribute_mut(
+        &mut self,
+        name: &OwnedAttributeDiscription,
+    ) -> Option<&mut OwnedAttributeValue<V>> {
+        self.dirty_nodes.mark_dirty(
+            self.id,
+            NodeMaskBuilder::new()
+                .with_attrs(AttributeMaskBuilder::Some(&[&name.name]))
+                .build(),
+        );
+        self.element_mut().attributes.get_mut(name)
+    }
+
+    /// Get the set of all events the element is listening to
+    pub fn listeners(&self) -> &FxHashSet<String> {
+        &self.element().listeners
     }
 }
 
-impl<S: State<V>, V: FromAnyValue> IndexMut<RealNodeId> for RealDom<S, V> {
-    fn index_mut(&mut self, idx: RealNodeId) -> &mut Self::Output {
-        self.tree.get_mut(idx).unwrap()
+// Create a workload from all of the passes. This orders the passes so that each pass will only run at most once.
+fn construct_workload<V: FromAnyValue + Send + Sync>(
+    passes: &mut [TypeErasedState<V>],
+) -> Workload {
+    let mut workload = Workload::new("Main Workload");
+    // Assign a unique index to keep track of each pass
+    let mut unresloved_workloads = passes
+        .iter_mut()
+        .enumerate()
+        .map(|(i, pass)| {
+            let workload = Some(pass.create_workload());
+            (i, pass, workload)
+        })
+        .collect::<Vec<_>>();
+    // set all the labels
+    for (id, _, workload) in &mut unresloved_workloads {
+        *workload = Some(workload.take().unwrap().tag(id.to_string()));
+    }
+    // mark any dependancies
+    for i in 0..unresloved_workloads.len() {
+        let (_, pass, _) = &unresloved_workloads[i];
+        for ty_id in pass.combined_dependancy_type_ids.clone() {
+            let &(dependancy_id, _, _) = unresloved_workloads
+                .iter()
+                .find(|(_, pass, _)| pass.this_type_id == ty_id)
+                .unwrap();
+            let (_, _, workload) = &mut unresloved_workloads[i];
+            *workload = workload
+                .take()
+                .map(|workload| workload.after_all(dependancy_id.to_string()));
+        }
+    }
+    // Add all of the passes
+    for (_, _, mut workload_system) in unresloved_workloads {
+        workload = workload.with_system(workload_system.take().unwrap());
     }
+    workload
 }

+ 0 - 305
packages/native-core/src/state.rs

@@ -1,305 +0,0 @@
-use std::cmp::Ordering;
-
-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;
-use crate::{FxDashSet, RealNodeId, SendAnyMap};
-
-/// Join two sorted iterators
-pub(crate) fn union_ordered_iter<'a>(
-    s_iter: impl Iterator<Item = &'a str>,
-    o_iter: impl Iterator<Item = &'a str>,
-    new_len_guess: usize,
-) -> Vec<String> {
-    let mut s_peekable = s_iter.peekable();
-    let mut o_peekable = o_iter.peekable();
-    let mut v = Vec::with_capacity(new_len_guess);
-    while let Some(s_i) = s_peekable.peek() {
-        while let Some(o_i) = o_peekable.peek() {
-            match o_i.cmp(s_i) {
-                Ordering::Greater => {
-                    break;
-                }
-                Ordering::Less => {
-                    v.push(o_peekable.next().unwrap().to_string());
-                }
-                Ordering::Equal => {
-                    o_peekable.next();
-                    break;
-                }
-            }
-        }
-        v.push(s_peekable.next().unwrap().to_string());
-    }
-    for o_i in o_peekable {
-        v.push(o_i.to_string());
-    }
-    for w in v.windows(2) {
-        debug_assert!(w[1] > w[0]);
-    }
-    v
-}
-
-/// This state is derived from children. For example a node's size could be derived from the size of children.
-/// Called when the current node's node properties are modified, a child's [ChildDepState] is modified or a child is removed.
-/// Called at most once per update.
-/// ```rust
-/// # use dioxus_native_core::node_ref::NodeView;
-/// # use dioxus_native_core::state::ChildDepState;
-/// #[derive(Clone, Copy, PartialEq, Default)]
-/// struct Layout {
-///     width: u32,
-///     height: u32,
-/// }
-///
-/// impl ChildDepState for Layout {
-///     type Ctx = ();
-///     // The layout depends on the layout of the children.
-///     type DepState = (Layout,);
-///     fn reduce<'a>(
-///         &mut self,
-///         _node: NodeView,
-///         children: impl Iterator<Item = (&'a Self,)>,
-///         _ctx: &Self::Ctx,
-///     ) -> bool
-///     where
-///         Self::DepState: 'a{
-///         /// Children are layed out form left to right. The width of the parent is the sum of the widths and the max of the heights.
-///         let new = children.map(|(&c,)|c).reduce(|c1, c2| Layout{
-///             width: c1.width + c2.width,
-///             height: c1.height.max(c2.height)
-///         }).unwrap_or_default();
-///         let changed = new != *self;
-///         *self = new;
-///         changed
-///     }
-/// }
-/// ```
-pub trait ChildDepState<V: FromAnyValue = ()> {
-    /// 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.
-    /// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState] or [NodeDepState].
-    type DepState: ElementBorrowable;
-    /// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated.
-    const NODE_MASK: NodeMask = NodeMask::NONE;
-    /// 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, V>,
-        children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
-        ctx: &Self::Ctx,
-    ) -> bool
-    where
-        Self::DepState: 'a;
-}
-
-/// This state that is passed down to children. For example text properties (`<b>` `<i>` `<u>`) would be passed to children.
-/// Called when the current node's node properties are modified or a parrent's [ParentDepState] is modified.
-/// Called at most once per update.
-/// ```rust
-/// use dioxus_native_core::node_ref::{NodeMask, AttributeMask, NodeView};
-/// use dioxus_native_core::state::*;
-///
-/// #[derive(Clone, Copy, PartialEq)]
-/// struct FontSize(usize);
-///
-/// impl ParentDepState for FontSize {
-///     type Ctx = ();
-///     // The font size depends on the font size of the parent element.
-///     type DepState = (Self,);
-///     const NODE_MASK: NodeMask =
-///         NodeMask::new_with_attrs(AttributeMask::Static(&[
-///             "font-size"
-///         ]));
-///     fn reduce<'a>(
-///         &mut self,
-///         node: NodeView,
-///         parent: Option<(&'a Self,)>,
-///         ctx: &Self::Ctx,
-///     ) -> bool{
-///         let old = *self;
-///         // If the font size was set on the parent, it is passed down to the current element
-///         if let Some((parent,)) = parent {
-///             *self = *parent;
-///         }
-///         // If the current node overrides the font size, use that size insead.
-///         for attr in node.attributes().unwrap() {
-///             match attr.attribute.name.as_str() {
-///                 "font-size" => {
-///                     self.0 = attr.value.as_text().unwrap().parse().unwrap();
-///                 }
-///                 // font-size is the only attribute we specified in the mask, so it is the only one we can see
-///                 _ => unreachable!(),
-///             }
-///         }
-///         old != *self
-///     }
-/// }
-/// ```
-pub trait ParentDepState<V: FromAnyValue = ()> {
-    /// 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.
-    /// Depstate must be a tuple containing any number of borrowed elements that are either [ParentDepState] or [NodeDepState].
-    type DepState: ElementBorrowable;
-    /// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated.
-    const NODE_MASK: NodeMask = NodeMask::NONE;
-    /// 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, V>,
-        parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
-        ctx: &Self::Ctx,
-    ) -> bool;
-}
-
-/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
-/// Called when the current node's node properties are modified or a one of its dependanices are modified.
-/// Called at most once per update.
-/// NodeDepState is the only state that can accept multiple dependancies, but only from the current node.
-/// ```rust
-/// use dioxus_native_core::node_ref::{NodeMask, AttributeMask, NodeView};
-/// use dioxus_native_core::state::*;
-///
-/// #[derive(Clone, Copy, PartialEq)]
-/// struct TabIndex(usize);
-///
-/// impl NodeDepState for TabIndex {
-///     type Ctx = ();
-///     type DepState = ();
-///     const NODE_MASK: NodeMask =
-///         NodeMask::new_with_attrs(AttributeMask::Static(&[
-///             "tabindex"
-///         ]));
-///     fn reduce(
-///         &mut self,
-///         node: NodeView,
-///         siblings: (),
-///         ctx: &(),
-///     ) -> bool {
-///         let old = self.clone();
-///         for attr in node.attributes().unwrap() {
-///             match attr.attribute.name.as_str() {
-///                 "tabindex" => {
-///                     self.0 = attr.value.as_text().unwrap().parse().unwrap();
-///                 }
-///                 // tabindex is the only attribute we specified in the mask, so it is the only one we can see
-///                 _ => unreachable!(),
-///             }
-///         }
-///         old != *self
-///     }
-/// }
-/// ```
-pub trait NodeDepState<V: FromAnyValue = ()> {
-    /// 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.
-    type Ctx;
-    /// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated.
-    const NODE_MASK: NodeMask = NodeMask::NONE;
-    /// 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, V>,
-        node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
-        ctx: &Self::Ctx,
-    ) -> bool;
-}
-
-/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
-pub trait State<V: FromAnyValue + 'static>: Default + Clone + 'static {
-    #[doc(hidden)]
-    const PASSES: &'static [AnyPass<Node<Self, V>>];
-    #[doc(hidden)]
-    const MASKS: &'static [NodeMask];
-
-    #[doc(hidden)]
-    fn update<T: TreeView<Node<Self, V>> + Sync + Send>(
-        dirty: DirtyNodeStates,
-        tree: &mut T,
-        ctx: SendAnyMap,
-    ) -> FxDashSet<RealNodeId> {
-        let passes = Self::PASSES.iter().collect();
-        resolve_passes(tree, dirty, passes, ctx)
-    }
-
-    #[doc(hidden)]
-    fn update_single_threaded<T: TreeView<Node<Self, V>>>(
-        dirty: DirtyNodeStates,
-        tree: &mut T,
-        ctx: SendAnyMap,
-    ) -> FxDashSet<RealNodeId> {
-        let passes = Self::PASSES.iter().collect();
-        resolve_passes_single_threaded(tree, dirty, passes, ctx)
-    }
-}
-
-impl ChildDepState for () {
-    type Ctx = ();
-    type DepState = ();
-    fn reduce<'a>(&mut self, _: NodeView<'a>, _: impl Iterator<Item = ()>, _: &Self::Ctx) -> bool
-    where
-        Self::DepState: 'a,
-    {
-        false
-    }
-}
-
-impl ParentDepState for () {
-    type Ctx = ();
-    type DepState = ();
-    fn reduce(&mut self, _: NodeView, _: Option<()>, _: &Self::Ctx) -> bool {
-        false
-    }
-}
-
-impl NodeDepState for () {
-    type DepState = ();
-    type Ctx = ();
-    fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
-        false
-    }
-}
-
-pub trait ElementBorrowable {
-    type ElementBorrowed<'a>
-    where
-        Self: 'a;
-
-    fn borrow_elements(&self) -> Self::ElementBorrowed<'_>;
-}
-
-macro_rules! impl_element_borrowable {
-    ($($t:ident),*) => {
-        impl< $($t),* > ElementBorrowable for ($($t,)*) {
-            type ElementBorrowed<'a> = ($(&'a $t,)*) where Self: 'a;
-
-            #[allow(clippy::unused_unit, non_snake_case)]
-            fn borrow_elements<'a>(&'a self) -> Self::ElementBorrowed<'a> {
-                let ($($t,)*) = self;
-                ($(&$t,)*)
-            }
-        }
-    };
-}
-
-impl_element_borrowable!();
-impl_element_borrowable!(A);
-impl_element_borrowable!(A, B);
-impl_element_borrowable!(A, B, C);
-impl_element_borrowable!(A, B, C, D);
-impl_element_borrowable!(A, B, C, D, E);
-impl_element_borrowable!(A, B, C, D, E, F);
-impl_element_borrowable!(A, B, C, D, E, F, G);
-impl_element_borrowable!(A, B, C, D, E, F, G, H);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
-impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);

+ 215 - 561
packages/native-core/src/tree.rs

@@ -1,669 +1,323 @@
-use std::collections::VecDeque;
-use std::marker::PhantomData;
+//! A tree of nodes intigated with shipyard
 
-#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
-pub struct NodeId(pub usize);
+use crate::NodeId;
+use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut};
+use std::fmt::Debug;
 
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct Node<T> {
-    value: T,
+/// A node in a tree.
+#[derive(PartialEq, Eq, Clone, Debug, Component)]
+pub struct Node {
     parent: Option<NodeId>,
     children: Vec<NodeId>,
     height: u16,
 }
 
-#[derive(Debug)]
-pub struct Tree<T> {
-    nodes: Slab<Node<T>>,
-    root: NodeId,
-}
-
-impl<T> Tree<T> {
-    fn try_remove(&mut self, id: NodeId) -> Option<Node<T>> {
-        self.nodes.try_remove(id.0).map(|node| {
-            if let Some(parent) = node.parent {
-                self.nodes
-                    .get_mut(parent.0)
-                    .unwrap()
-                    .children
-                    .retain(|child| child != &id);
-            }
-            for child in &node.children {
-                self.remove_recursive(*child);
-            }
-            node
-        })
-    }
-
-    fn remove_recursive(&mut self, node: NodeId) {
-        let node = self.nodes.remove(node.0);
-        for child in node.children {
-            self.remove_recursive(child);
-        }
-    }
-
-    fn set_height(&mut self, node: NodeId, height: u16) {
-        let self_mut = self as *mut Self;
-        let node = self.nodes.get_mut(node.0).unwrap();
-        node.height = height;
-        unsafe {
-            // Safety: No node has itself as a child
-            for child in &node.children {
-                (*self_mut).set_height(*child, height + 1);
-            }
-        }
-    }
-}
-
-pub trait TreeView<T>: Sized {
-    type Iterator<'a>: Iterator<Item = &'a T>
-    where
-        T: 'a,
-        Self: 'a;
-    type IteratorMut<'a>: Iterator<Item = &'a mut T>
-    where
-        T: 'a,
-        Self: 'a;
-
-    fn root(&self) -> NodeId;
-
-    fn contains(&self, id: NodeId) -> bool {
-        self.get(id).is_some()
-    }
-
-    fn get(&self, id: NodeId) -> Option<&T>;
-
-    fn get_unchecked(&self, id: NodeId) -> &T {
-        unsafe { self.get(id).unwrap_unchecked() }
-    }
-
-    fn get_mut(&mut self, id: NodeId) -> Option<&mut T>;
-
-    fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T {
-        unsafe { self.get_mut(id).unwrap_unchecked() }
-    }
-
-    fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>>;
-
-    fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>>;
-
-    fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)>;
-
-    fn children_ids(&self, id: NodeId) -> Option<&[NodeId]>;
-
-    fn parent(&self, id: NodeId) -> Option<&T>;
-
-    fn parent_mut(&mut self, id: NodeId) -> Option<&mut T>;
-
-    fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)>;
+/// A view of a tree.
+pub type TreeRefView<'a> = View<'a, Node>;
+/// A mutable view of a tree.
+pub type TreeMutView<'a> = (EntitiesViewMut<'a>, ViewMut<'a, Node>);
 
+/// A immutable view of a tree.
+pub trait TreeRef {
+    /// The parent id of the node.
     fn parent_id(&self, id: NodeId) -> Option<NodeId>;
-
+    /// The children ids of the node.
+    fn children_ids(&self, id: NodeId) -> Vec<NodeId>;
+    /// The height of the node.
     fn height(&self, id: NodeId) -> Option<u16>;
-
-    fn size(&self) -> usize;
-
-    fn traverse_depth_first(&self, mut f: impl FnMut(&T)) {
-        let mut stack = vec![self.root()];
-        while let Some(id) = stack.pop() {
-            if let Some(node) = self.get(id) {
-                f(node);
-                if let Some(children) = self.children_ids(id) {
-                    stack.extend(children.iter().copied().rev());
-                }
-            }
-        }
-    }
-
-    fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(&mut T)) {
-        let mut stack = vec![self.root()];
-        while let Some(id) = stack.pop() {
-            if let Some(node) = self.get_mut(id) {
-                f(node);
-                if let Some(children) = self.children_ids(id) {
-                    stack.extend(children.iter().copied().rev());
-                }
-            }
-        }
-    }
-
-    fn traverse_breadth_first(&self, mut f: impl FnMut(&T)) {
-        let mut queue = VecDeque::new();
-        queue.push_back(self.root());
-        while let Some(id) = queue.pop_front() {
-            if let Some(node) = self.get(id) {
-                f(node);
-                if let Some(children) = self.children_ids(id) {
-                    for id in children {
-                        queue.push_back(*id);
-                    }
-                }
-            }
-        }
-    }
-
-    fn traverse_breadth_first_mut(&mut self, mut f: impl FnMut(&mut T)) {
-        let mut queue = VecDeque::new();
-        queue.push_back(self.root());
-        while let Some(id) = queue.pop_front() {
-            if let Some(node) = self.get_mut(id) {
-                f(node);
-                if let Some(children) = self.children_ids(id) {
-                    for id in children {
-                        queue.push_back(*id);
-                    }
-                }
-            }
-        }
-    }
-}
-
-pub trait TreeLike<T>: TreeView<T> {
-    fn new(root: T) -> Self;
-
-    fn create_node(&mut self, value: T) -> NodeId;
-
-    fn add_child(&mut self, parent: NodeId, child: NodeId);
-
-    fn remove(&mut self, id: NodeId) -> Option<T>;
-
-    fn remove_all_children(&mut self, id: NodeId) -> Vec<T>;
-
-    fn replace(&mut self, old: NodeId, new: NodeId);
-
-    fn insert_before(&mut self, id: NodeId, new: NodeId);
-
-    fn insert_after(&mut self, id: NodeId, new: NodeId);
-}
-
-pub struct ChildNodeIterator<'a, T> {
-    nodes: &'a Slab<Node<T>>,
-    children_ids: Vec<NodeId>,
-    index: usize,
-    node_type: PhantomData<T>,
-}
-
-impl<'a, T: 'a> Iterator for ChildNodeIterator<'a, T> {
-    type Item = &'a T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.children_ids.get(self.index).map(|id| {
-            self.index += 1;
-            &self.nodes.get(id.0).unwrap().value
-        })
-    }
-}
-
-pub struct ChildNodeIteratorMut<'a, T> {
-    nodes: Vec<&'a mut Node<T>>,
+    /// Returns true if the node exists.
+    fn contains(&self, id: NodeId) -> bool;
 }
 
-impl<'a, T: 'a> Iterator for ChildNodeIteratorMut<'a, T> {
-    type Item = &'a mut T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.nodes.pop().map(|node| &mut node.value)
-    }
+/// A mutable view of a tree.
+pub trait TreeMut: TreeRef {
+    /// Removes the node and all of its children.
+    fn remove(&mut self, id: NodeId);
+    /// Removes the node and all of its children.
+    fn remove_single(&mut self, id: NodeId);
+    /// Adds a new node to the tree.
+    fn create_node(&mut self, id: NodeId);
+    /// Adds a child to the node.
+    fn add_child(&mut self, parent: NodeId, new: NodeId);
+    /// Replaces the node with a new node.
+    fn replace(&mut self, old_id: NodeId, new_id: NodeId);
+    /// Inserts a node before another node.
+    fn insert_before(&mut self, old_id: NodeId, new_id: NodeId);
+    /// Inserts a node after another node.
+    fn insert_after(&mut self, old_id: NodeId, new_id: NodeId);
 }
 
-impl<T> TreeView<T> for Tree<T> {
-    type Iterator<'a> = ChildNodeIterator<'a, T> where T: 'a;
-    type IteratorMut<'a> = ChildNodeIteratorMut<'a, T> where T: 'a;
-
-    fn root(&self) -> NodeId {
-        self.root
+impl<'a> TreeRef for TreeRefView<'a> {
+    fn parent_id(&self, id: NodeId) -> Option<NodeId> {
+        self.get(id).ok()?.parent
     }
 
-    fn get(&self, id: NodeId) -> Option<&T> {
-        self.nodes.get(id.0).map(|node| &node.value)
+    fn children_ids(&self, id: NodeId) -> Vec<NodeId> {
+        self.get(id)
+            .map(|node| node.children.clone())
+            .unwrap_or_default()
     }
 
-    fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
-        self.nodes.get_mut(id.0).map(|node| &mut node.value)
+    fn height(&self, id: NodeId) -> Option<u16> {
+        Some(self.get(id).ok()?.height)
     }
 
-    fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
-        self.children_ids(id).map(|children_ids| ChildNodeIterator {
-            nodes: &self.nodes,
-            children_ids: children_ids.to_vec(),
-            index: 0,
-            node_type: PhantomData,
-        })
+    fn contains(&self, id: NodeId) -> bool {
+        self.get(id).is_ok()
     }
+}
 
-    fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
-        // Safety: No node has itself as a parent.
-        if let Some(children_ids) = self.children_ids(id) {
-            let children_ids = children_ids.to_vec();
-            Some(ChildNodeIteratorMut {
-                nodes: unsafe {
-                    self.nodes
-                        .get_many_mut_unchecked(children_ids.into_iter().rev().map(|id| id.0))
-                        .unwrap()
-                },
-            })
-        } else {
-            None
+impl<'a> TreeMut for TreeMutView<'a> {
+    fn remove(&mut self, id: NodeId) {
+        fn recurse(tree: &mut TreeMutView<'_>, id: NodeId) {
+            let children = tree.children_ids(id);
+            for child in children {
+                recurse(tree, child);
+            }
         }
-    }
-
-    fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
-        self.nodes.get(id.0).map(|node| node.children.as_slice())
-    }
-
-    fn parent(&self, id: NodeId) -> Option<&T> {
-        self.nodes
-            .get(id.0)
-            .and_then(|node| node.parent.map(|id| self.nodes.get(id.0).unwrap()))
-            .map(|node| &node.value)
-    }
-
-    fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> {
-        let self_ptr = self as *mut Self;
-        unsafe {
-            // Safety: No node has itself as a parent.
-            self.nodes
-                .get_mut(id.0)
-                .and_then(move |node| {
-                    node.parent
-                        .map(move |id| (*self_ptr).nodes.get_mut(id.0).unwrap())
-                })
-                .map(|node| &mut node.value)
+        {
+            let mut node_data_mut = &mut self.1;
+            if let Some(parent) = node_data_mut.get(id).unwrap().parent {
+                let parent = (&mut node_data_mut).get(parent).unwrap();
+                parent.children.retain(|&child| child != id);
+            }
         }
-    }
-
-    fn parent_id(&self, id: NodeId) -> Option<NodeId> {
-        self.nodes.get(id.0).and_then(|node| node.parent)
-    }
-
-    fn height(&self, id: NodeId) -> Option<u16> {
-        self.nodes.get(id.0).map(|n| n.height)
-    }
 
-    fn get_unchecked(&self, id: NodeId) -> &T {
-        unsafe { &self.nodes.get_unchecked(id.0).value }
+        recurse(self, id);
     }
 
-    fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T {
-        unsafe { &mut self.nodes.get_unchecked_mut(id.0).value }
-    }
-
-    fn size(&self) -> usize {
-        self.nodes.len()
-    }
-
-    fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> {
-        if let Some(parent_id) = self.parent_id(id) {
-            self.nodes
-                .get2_mut(id.0, parent_id.0)
-                .map(|(node, parent)| (&mut node.value, Some(&mut parent.value)))
-        } else {
-            self.nodes.get_mut(id.0).map(|node| (&mut node.value, None))
-        }
-    }
-
-    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) {
-            debug_assert!(!children_ids.iter().any(|child_id| *child_id == id));
-            let mut borrowed = unsafe {
-                let as_vec = children_ids.to_vec();
-                self.nodes
-                    .get_many_mut_unchecked(
-                        as_vec
-                            .into_iter()
-                            .rev()
-                            .map(|id| id.0)
-                            .chain(std::iter::once(id.0)),
-                    )
-                    .unwrap()
-            };
-            let node = &mut borrowed.pop().unwrap().value;
-            Some((node, ChildNodeIteratorMut { nodes: borrowed }))
-        } else {
-            None
+    fn remove_single(&mut self, id: NodeId) {
+        {
+            let mut node_data_mut = &mut self.1;
+            if let Some(parent) = node_data_mut.get(id).unwrap().parent {
+                let parent = (&mut node_data_mut).get(parent).unwrap();
+                parent.children.retain(|&child| child != id);
+            }
         }
     }
-}
-
-impl<T> TreeLike<T> for Tree<T> {
-    fn new(root: T) -> Self {
-        let mut nodes = Slab::default();
-        let root = NodeId(nodes.insert(Node {
-            value: root,
-            parent: None,
-            children: Vec::new(),
-            height: 0,
-        }));
-        Self { nodes, root }
-    }
 
-    fn create_node(&mut self, value: T) -> NodeId {
-        NodeId(self.nodes.insert(Node {
-            value,
-            parent: None,
-            children: Vec::new(),
-            height: 0,
-        }))
+    fn create_node(&mut self, id: NodeId) {
+        let (entities, node_data_mut) = self;
+        entities.add_component(
+            id,
+            node_data_mut,
+            Node {
+                parent: None,
+                children: Vec::new(),
+                height: 0,
+            },
+        );
     }
 
     fn add_child(&mut self, parent: NodeId, new: NodeId) {
-        self.nodes.get_mut(new.0).unwrap().parent = Some(parent);
-        let parent = self.nodes.get_mut(parent.0).unwrap();
-        parent.children.push(new);
-        let height = parent.height + 1;
-        self.set_height(new, height);
-    }
-
-    fn remove(&mut self, id: NodeId) -> Option<T> {
-        self.try_remove(id).map(|node| node.value)
-    }
-
-    fn remove_all_children(&mut self, id: NodeId) -> Vec<T> {
-        let mut children = Vec::new();
-        let self_mut = self as *mut Self;
-        for child in self.children_ids(id).unwrap() {
-            unsafe {
-                // Safety: No node has itself as a child
-                children.push((*self_mut).remove(*child).unwrap());
-            }
+        let height;
+        {
+            let mut node_state = &mut self.1;
+            (&mut node_state).get(new).unwrap().parent = Some(parent);
+            let parent = (&mut node_state).get(parent).unwrap();
+            parent.children.push(new);
+            height = parent.height + 1;
         }
-        children
+        set_height(self, new, height);
     }
 
     fn replace(&mut self, old_id: NodeId, new_id: NodeId) {
-        // remove the old node
-        let old = self
-            .try_remove(old_id)
-            .expect("tried to replace a node that doesn't exist");
-        // update the parent's link to the child
-        if let Some(parent_id) = old.parent {
-            let parent = self.nodes.get_mut(parent_id.0).unwrap();
-            for id in &mut parent.children {
-                if *id == old_id {
-                    *id = new_id;
+        {
+            let mut node_state = &mut self.1;
+            // update the parent's link to the child
+            if let Some(parent_id) = node_state.get(old_id).unwrap().parent {
+                let parent = (&mut node_state).get(parent_id).unwrap();
+                for id in &mut parent.children {
+                    if *id == old_id {
+                        *id = new_id;
+                        break;
+                    }
                 }
+                let height = parent.height + 1;
+                set_height(self, new_id, height);
             }
-            let height = parent.height + 1;
-            self.set_height(new_id, height);
         }
+        // remove the old node
+        self.remove(old_id);
     }
 
-    fn insert_before(&mut self, id: NodeId, new: NodeId) {
-        let node = self.nodes.get(id.0).unwrap();
-        let parent_id = node.parent.expect("tried to insert before root");
-        self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id);
-        let parent = self.nodes.get_mut(parent_id.0).unwrap();
+    fn insert_before(&mut self, old_id: NodeId, new_id: NodeId) {
+        let mut node_state = &mut self.1;
+        let old_node = node_state.get(old_id).unwrap();
+        let parent_id = old_node.parent.expect("tried to insert before root");
+        (&mut node_state).get(new_id).unwrap().parent = Some(parent_id);
+        let parent = (&mut node_state).get(parent_id).unwrap();
         let index = parent
             .children
             .iter()
-            .position(|child| child == &id)
+            .position(|child| *child == old_id)
             .unwrap();
-        parent.children.insert(index, new);
+        parent.children.insert(index, new_id);
         let height = parent.height + 1;
-        self.set_height(new, height);
+        set_height(self, new_id, height);
     }
 
-    fn insert_after(&mut self, id: NodeId, new: NodeId) {
-        let node = self.nodes.get(id.0).unwrap();
-        let parent_id = node.parent.expect("tried to insert before root");
-        self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id);
-        let parent = self.nodes.get_mut(parent_id.0).unwrap();
+    fn insert_after(&mut self, old_id: NodeId, new_id: NodeId) {
+        let mut node_state = &mut self.1;
+        let old_node = node_state.get(old_id).unwrap();
+        let parent_id = old_node.parent.expect("tried to insert before root");
+        (&mut node_state).get(new_id).unwrap().parent = Some(parent_id);
+        let parent = (&mut node_state).get(parent_id).unwrap();
         let index = parent
             .children
             .iter()
-            .position(|child| child == &id)
+            .position(|child| *child == old_id)
             .unwrap();
-        parent.children.insert(index + 1, new);
+        parent.children.insert(index + 1, new_id);
         let height = parent.height + 1;
-        self.set_height(new, height);
+        set_height(self, new_id, height);
+    }
+}
+
+/// Sets the height of a node and updates the height of all its children
+fn set_height(tree: &mut TreeMutView<'_>, node: NodeId, height: u16) {
+    let children = {
+        let mut node_data_mut = &mut tree.1;
+        let mut node = (&mut node_data_mut).get(node).unwrap();
+        node.height = height;
+        node.children.clone()
+    };
+    for child in children {
+        set_height(tree, child, height + 1);
+    }
+}
+
+impl<'a> TreeRef for TreeMutView<'a> {
+    fn parent_id(&self, id: NodeId) -> Option<NodeId> {
+        let node_data = &self.1;
+        node_data.get(id).unwrap().parent
+    }
+
+    fn children_ids(&self, id: NodeId) -> Vec<NodeId> {
+        let node_data = &self.1;
+        node_data
+            .get(id)
+            .map(|node| node.children.clone())
+            .unwrap_or_default()
+    }
+
+    fn height(&self, id: NodeId) -> Option<u16> {
+        let node_data = &self.1;
+        node_data.get(id).map(|node| node.height).ok()
+    }
+
+    fn contains(&self, id: NodeId) -> bool {
+        self.1.get(id).is_ok()
     }
 }
+
 #[test]
 fn creation() {
-    let mut tree = Tree::new(1);
-    let parent = tree.root();
-    let child = tree.create_node(0);
-    tree.add_child(parent, child);
+    use shipyard::World;
+    #[derive(Component)]
+    struct Num(i32);
 
-    println!("Tree: {tree:#?}");
-    assert_eq!(tree.size(), 2);
-    assert_eq!(tree.height(parent), Some(0));
-    assert_eq!(tree.height(child), Some(1));
-    assert_eq!(*tree.get(parent).unwrap(), 1);
-    assert_eq!(*tree.get(child).unwrap(), 0);
-    assert_eq!(tree.parent_id(parent), None);
-    assert_eq!(tree.parent_id(child).unwrap(), parent);
-    assert_eq!(tree.children_ids(parent).unwrap(), &[child]);
+    let mut world = World::new();
+    let parent_id = world.add_entity(Num(1i32));
+    let child_id = world.add_entity(Num(0i32));
+
+    let mut tree = world.borrow::<TreeMutView>().unwrap();
+
+    tree.create_node(parent_id);
+    tree.create_node(child_id);
+
+    tree.add_child(parent_id, child_id);
+
+    assert_eq!(tree.height(parent_id), Some(0));
+    assert_eq!(tree.height(child_id), Some(1));
+    assert_eq!(tree.parent_id(parent_id), None);
+    assert_eq!(tree.parent_id(child_id).unwrap(), parent_id);
+    assert_eq!(tree.children_ids(parent_id), &[child_id]);
 }
 
 #[test]
 fn insertion() {
-    let mut tree = Tree::new(0);
-    let parent = tree.root();
-    let child = tree.create_node(2);
+    use shipyard::World;
+    #[derive(Component)]
+    struct Num(i32);
+
+    let mut world = World::new();
+    let parent = world.add_entity(Num(0));
+    let child = world.add_entity(Num(2));
+    let before = world.add_entity(Num(1));
+    let after = world.add_entity(Num(3));
+
+    let mut tree = world.borrow::<TreeMutView>().unwrap();
+
+    tree.create_node(parent);
+    tree.create_node(child);
+    tree.create_node(before);
+    tree.create_node(after);
+
     tree.add_child(parent, child);
-    let before = tree.create_node(1);
     tree.insert_before(child, before);
-    let after = tree.create_node(3);
     tree.insert_after(child, after);
 
-    println!("Tree: {tree:#?}");
-    assert_eq!(tree.size(), 4);
     assert_eq!(tree.height(parent), Some(0));
     assert_eq!(tree.height(child), Some(1));
     assert_eq!(tree.height(before), Some(1));
     assert_eq!(tree.height(after), Some(1));
-    assert_eq!(*tree.get(parent).unwrap(), 0);
-    assert_eq!(*tree.get(before).unwrap(), 1);
-    assert_eq!(*tree.get(child).unwrap(), 2);
-    assert_eq!(*tree.get(after).unwrap(), 3);
     assert_eq!(tree.parent_id(before).unwrap(), parent);
     assert_eq!(tree.parent_id(child).unwrap(), parent);
     assert_eq!(tree.parent_id(after).unwrap(), parent);
-    assert_eq!(tree.children_ids(parent).unwrap(), &[before, child, after]);
+    assert_eq!(tree.children_ids(parent), &[before, child, after]);
 }
 
 #[test]
 fn deletion() {
-    let mut tree = Tree::new(0);
-    let parent = tree.root();
-    let child = tree.create_node(2);
+    use shipyard::World;
+    #[derive(Component)]
+    struct Num(i32);
+
+    let mut world = World::new();
+    let parent = world.add_entity(Num(0));
+    let child = world.add_entity(Num(2));
+    let before = world.add_entity(Num(1));
+    let after = world.add_entity(Num(3));
+
+    let mut tree = world.borrow::<TreeMutView>().unwrap();
+
+    tree.create_node(parent);
+    tree.create_node(child);
+    tree.create_node(before);
+    tree.create_node(after);
+
     tree.add_child(parent, child);
-    let before = tree.create_node(1);
     tree.insert_before(child, before);
-    let after = tree.create_node(3);
     tree.insert_after(child, after);
 
-    println!("Tree: {tree:#?}");
-    assert_eq!(tree.size(), 4);
     assert_eq!(tree.height(parent), Some(0));
     assert_eq!(tree.height(child), Some(1));
     assert_eq!(tree.height(before), Some(1));
     assert_eq!(tree.height(after), Some(1));
-    assert_eq!(*tree.get(parent).unwrap(), 0);
-    assert_eq!(*tree.get(before).unwrap(), 1);
-    assert_eq!(*tree.get(child).unwrap(), 2);
-    assert_eq!(*tree.get(after).unwrap(), 3);
     assert_eq!(tree.parent_id(before).unwrap(), parent);
     assert_eq!(tree.parent_id(child).unwrap(), parent);
     assert_eq!(tree.parent_id(after).unwrap(), parent);
-    assert_eq!(tree.children_ids(parent).unwrap(), &[before, child, after]);
+    assert_eq!(tree.children_ids(parent), &[before, child, after]);
 
     tree.remove(child);
 
-    println!("Tree: {tree:#?}");
-    assert_eq!(tree.size(), 3);
     assert_eq!(tree.height(parent), Some(0));
     assert_eq!(tree.height(before), Some(1));
     assert_eq!(tree.height(after), Some(1));
-    assert_eq!(*tree.get(parent).unwrap(), 0);
-    assert_eq!(*tree.get(before).unwrap(), 1);
-    assert_eq!(tree.get(child), None);
-    assert_eq!(*tree.get(after).unwrap(), 3);
     assert_eq!(tree.parent_id(before).unwrap(), parent);
     assert_eq!(tree.parent_id(after).unwrap(), parent);
-    assert_eq!(tree.children_ids(parent).unwrap(), &[before, after]);
+    assert_eq!(tree.children_ids(parent), &[before, after]);
 
     tree.remove(before);
 
-    println!("Tree: {tree:#?}");
-    assert_eq!(tree.size(), 2);
     assert_eq!(tree.height(parent), Some(0));
     assert_eq!(tree.height(after), Some(1));
-    assert_eq!(*tree.get(parent).unwrap(), 0);
-    assert_eq!(tree.get(before), None);
-    assert_eq!(*tree.get(after).unwrap(), 3);
     assert_eq!(tree.parent_id(after).unwrap(), parent);
-    assert_eq!(tree.children_ids(parent).unwrap(), &[after]);
+    assert_eq!(tree.children_ids(parent), &[after]);
 
     tree.remove(after);
 
-    println!("Tree: {tree:#?}");
-    assert_eq!(tree.size(), 1);
     assert_eq!(tree.height(parent), Some(0));
-    assert_eq!(*tree.get(parent).unwrap(), 0);
-    assert_eq!(tree.get(after), None);
-    assert_eq!(tree.children_ids(parent).unwrap(), &[]);
-}
-
-#[test]
-fn traverse_depth_first() {
-    let mut tree = Tree::new(0);
-    let parent = tree.root();
-    let child1 = tree.create_node(1);
-    tree.add_child(parent, child1);
-    let grandchild1 = tree.create_node(2);
-    tree.add_child(child1, grandchild1);
-    let child2 = tree.create_node(3);
-    tree.add_child(parent, child2);
-    let grandchild2 = tree.create_node(4);
-    tree.add_child(child2, grandchild2);
-
-    let mut node_count = 0;
-    tree.traverse_depth_first(move |node| {
-        assert_eq!(*node, node_count);
-        node_count += 1;
-    });
-}
-
-#[test]
-fn get_node_children_mut() {
-    let mut tree = Tree::new(0);
-    let parent = tree.root();
-    let child1 = tree.create_node(1);
-    tree.add_child(parent, child1);
-    let child2 = tree.create_node(2);
-    tree.add_child(parent, child2);
-    let child3 = tree.create_node(3);
-    tree.add_child(parent, child3);
-
-    let (parent, children) = tree.parent_child_mut(parent).unwrap();
-    for (i, child) in children.enumerate() {
-        assert_eq!(*child, i + 1);
-    }
-    println!("Parent: {parent:#?}");
-}
-
-#[test]
-fn get_many_mut_unchecked() {
-    let mut slab = Slab::new();
-    let parent = slab.insert(0);
-    let child = slab.insert(1);
-    let grandchild = slab.insert(2);
-
-    let all =
-        unsafe { slab.get_many_mut_unchecked([parent, child, grandchild].into_iter()) }.unwrap();
-    println!("All: {all:#?}");
-}
-
-#[derive(Debug)]
-struct Slab<T> {
-    data: Vec<Option<T>>,
-    free: VecDeque<usize>,
-}
-
-impl<T> Default for Slab<T> {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl<T> Slab<T> {
-    fn new() -> Self {
-        Self {
-            data: Vec::new(),
-            free: VecDeque::new(),
-        }
-    }
-
-    fn get(&self, id: usize) -> Option<&T> {
-        self.data.get(id).and_then(|x| x.as_ref())
-    }
-
-    unsafe fn get_unchecked(&self, id: usize) -> &T {
-        self.data.get_unchecked(id).as_ref().unwrap()
-    }
-
-    fn get_mut(&mut self, id: usize) -> Option<&mut T> {
-        self.data.get_mut(id).and_then(|x| x.as_mut())
-    }
-
-    unsafe fn get_unchecked_mut(&mut self, id: usize) -> &mut T {
-        self.data.get_unchecked_mut(id).as_mut().unwrap()
-    }
-
-    fn get2_mut(&mut self, id1: usize, id2: usize) -> Option<(&mut T, &mut T)> {
-        assert!(id1 != id2);
-        let ptr = self.data.as_mut_ptr();
-        let first = unsafe { &mut *ptr.add(id1) };
-        let second = unsafe { &mut *ptr.add(id2) };
-        if let (Some(first), Some(second)) = (first, second) {
-            Some((first, second))
-        } else {
-            None
-        }
-    }
-
-    unsafe fn get_many_mut_unchecked(
-        &mut self,
-        ids: impl Iterator<Item = usize>,
-    ) -> Option<Vec<&mut T>> {
-        let ptr = self.data.as_mut_ptr();
-        let mut result = Vec::new();
-        for id in ids {
-            let item = unsafe { &mut *ptr.add(id) };
-            if let Some(item) = item {
-                result.push(item);
-            } else {
-                return None;
-            }
-        }
-        Some(result)
-    }
-
-    fn insert(&mut self, value: T) -> usize {
-        if let Some(id) = self.free.pop_front() {
-            self.data[id] = Some(value);
-            id
-        } else {
-            self.data.push(Some(value));
-            self.data.len() - 1
-        }
-    }
-
-    fn try_remove(&mut self, id: usize) -> Option<T> {
-        self.data.get_mut(id).and_then(|x| {
-            self.free.push_back(id);
-            x.take()
-        })
-    }
-
-    fn remove(&mut self, id: usize) -> T {
-        self.try_remove(id).unwrap()
-    }
-
-    fn len(&self) -> usize {
-        self.data.len() - self.free.len()
-    }
+    assert_eq!(tree.children_ids(parent), &[]);
 }

+ 43 - 71
packages/native-core/src/utils/cursor.rs

@@ -1,6 +1,8 @@
+//! A cursor implementation that can be used to navigate and edit text.
+
 use std::{cmp::Ordering, ops::Range};
 
-use dioxus_html::input_data::keyboard_types::{Code, Key, Modifiers};
+use keyboard_types::{Code, Key, Modifiers};
 
 /// This contains the information about the text that is used by the cursor to handle navigation.
 pub trait Text {
@@ -221,26 +223,28 @@ impl Cursor {
     /// Handle moving the cursor with the given key.
     pub fn handle_input<T: Text + ?Sized>(
         &mut self,
-        data: &dioxus_html::KeyboardData,
+        code: &Code,
+        key: &Key,
+        modifiers: &Modifiers,
         text: &mut impl TextEditable<T>,
         max_text_length: usize,
     ) {
         use Code::*;
-        match data.code() {
+        match code {
             ArrowUp => {
                 self.move_cursor(
                     |c| c.up(text.as_ref()),
-                    data.modifiers().contains(Modifiers::SHIFT),
+                    modifiers.contains(Modifiers::SHIFT),
                 );
             }
             ArrowDown => {
                 self.move_cursor(
                     |c| c.down(text.as_ref()),
-                    data.modifiers().contains(Modifiers::SHIFT),
+                    modifiers.contains(Modifiers::SHIFT),
                 );
             }
             ArrowRight => {
-                if data.modifiers().contains(Modifiers::CONTROL) {
+                if modifiers.contains(Modifiers::CONTROL) {
                     self.move_cursor(
                         |c| {
                             let mut change = 1;
@@ -255,17 +259,17 @@ impl Cursor {
                             }
                             c.move_col(change as i32, text.as_ref());
                         },
-                        data.modifiers().contains(Modifiers::SHIFT),
+                        modifiers.contains(Modifiers::SHIFT),
                     );
                 } else {
                     self.move_cursor(
                         |c| c.right(text.as_ref()),
-                        data.modifiers().contains(Modifiers::SHIFT),
+                        modifiers.contains(Modifiers::SHIFT),
                     );
                 }
             }
             ArrowLeft => {
-                if data.modifiers().contains(Modifiers::CONTROL) {
+                if modifiers.contains(Modifiers::CONTROL) {
                     self.move_cursor(
                         |c| {
                             let mut change = -1;
@@ -279,23 +283,23 @@ impl Cursor {
                             }
                             c.move_col(change, text.as_ref());
                         },
-                        data.modifiers().contains(Modifiers::SHIFT),
+                        modifiers.contains(Modifiers::SHIFT),
                     );
                 } else {
                     self.move_cursor(
                         |c| c.left(text.as_ref()),
-                        data.modifiers().contains(Modifiers::SHIFT),
+                        modifiers.contains(Modifiers::SHIFT),
                     );
                 }
             }
             End => {
                 self.move_cursor(
                     |c| c.col = c.len_line(text.as_ref()),
-                    data.modifiers().contains(Modifiers::SHIFT),
+                    modifiers.contains(Modifiers::SHIFT),
                 );
             }
             Home => {
-                self.move_cursor(|c| c.col = 0, data.modifiers().contains(Modifiers::SHIFT));
+                self.move_cursor(|c| c.col = 0, modifiers.contains(Modifiers::SHIFT));
             }
             Backspace => {
                 self.start.realize_col(text.as_ref());
@@ -305,7 +309,7 @@ impl Cursor {
                 } else if start_idx > 0 {
                     self.start.left(text.as_ref());
                     text.delete_range(start_idx - 1..start_idx);
-                    if data.modifiers().contains(Modifiers::CONTROL) {
+                    if modifiers.contains(Modifiers::CONTROL) {
                         start_idx = self.start.idx(text.as_ref());
                         while start_idx > 0
                             && text
@@ -340,7 +344,7 @@ impl Cursor {
             }
             _ => {
                 self.start.realize_col(text.as_ref());
-                if let Key::Character(character) = data.key() {
+                if let Key::Character(character) = key {
                     if text.as_ref().length() + 1 - self.selection_len(text.as_ref())
                         <= max_text_length
                     {
@@ -460,13 +464,9 @@ fn cursor_input() {
 
     for _ in 0..5 {
         cursor.handle_input(
-            &dioxus_html::KeyboardData::new(
-                dioxus_html::input_data::keyboard_types::Key::ArrowRight,
-                dioxus_html::input_data::keyboard_types::Code::ArrowRight,
-                dioxus_html::input_data::keyboard_types::Location::Standard,
-                false,
-                Modifiers::empty(),
-            ),
+            &keyboard_types::Code::ArrowRight,
+            &keyboard_types::Key::ArrowRight,
+            &Modifiers::empty(),
             &mut text,
             10,
         );
@@ -474,13 +474,9 @@ fn cursor_input() {
 
     for _ in 0..5 {
         cursor.handle_input(
-            &dioxus_html::KeyboardData::new(
-                dioxus_html::input_data::keyboard_types::Key::Backspace,
-                dioxus_html::input_data::keyboard_types::Code::Backspace,
-                dioxus_html::input_data::keyboard_types::Location::Standard,
-                false,
-                Modifiers::empty(),
-            ),
+            &keyboard_types::Code::Backspace,
+            &keyboard_types::Key::Backspace,
+            &Modifiers::empty(),
             &mut text,
             10,
         );
@@ -491,61 +487,41 @@ fn cursor_input() {
     let goal_text = "hello world\nhi";
     let max_width = goal_text.len();
     cursor.handle_input(
-        &dioxus_html::KeyboardData::new(
-            dioxus_html::input_data::keyboard_types::Key::Character("h".to_string()),
-            dioxus_html::input_data::keyboard_types::Code::KeyH,
-            dioxus_html::input_data::keyboard_types::Location::Standard,
-            false,
-            Modifiers::empty(),
-        ),
+        &keyboard_types::Code::KeyH,
+        &keyboard_types::Key::Character("h".to_string()),
+        &Modifiers::empty(),
         &mut text,
         max_width,
     );
 
     cursor.handle_input(
-        &dioxus_html::KeyboardData::new(
-            dioxus_html::input_data::keyboard_types::Key::Character("e".to_string()),
-            dioxus_html::input_data::keyboard_types::Code::KeyE,
-            dioxus_html::input_data::keyboard_types::Location::Standard,
-            false,
-            Modifiers::empty(),
-        ),
+        &keyboard_types::Code::KeyE,
+        &keyboard_types::Key::Character("e".to_string()),
+        &Modifiers::empty(),
         &mut text,
         max_width,
     );
 
     cursor.handle_input(
-        &dioxus_html::KeyboardData::new(
-            dioxus_html::input_data::keyboard_types::Key::Character("l".to_string()),
-            dioxus_html::input_data::keyboard_types::Code::KeyL,
-            dioxus_html::input_data::keyboard_types::Location::Standard,
-            false,
-            Modifiers::empty(),
-        ),
+        &keyboard_types::Code::KeyL,
+        &keyboard_types::Key::Character("l".to_string()),
+        &Modifiers::empty(),
         &mut text,
         max_width,
     );
 
     cursor.handle_input(
-        &dioxus_html::KeyboardData::new(
-            dioxus_html::input_data::keyboard_types::Key::Character("l".to_string()),
-            dioxus_html::input_data::keyboard_types::Code::KeyL,
-            dioxus_html::input_data::keyboard_types::Location::Standard,
-            false,
-            Modifiers::empty(),
-        ),
+        &keyboard_types::Code::KeyL,
+        &keyboard_types::Key::Character("l".to_string()),
+        &Modifiers::empty(),
         &mut text,
         max_width,
     );
 
     cursor.handle_input(
-        &dioxus_html::KeyboardData::new(
-            dioxus_html::input_data::keyboard_types::Key::Character("o".to_string()),
-            dioxus_html::input_data::keyboard_types::Code::KeyO,
-            dioxus_html::input_data::keyboard_types::Location::Standard,
-            false,
-            Modifiers::empty(),
-        ),
+        &keyboard_types::Code::KeyO,
+        &keyboard_types::Key::Character("o".to_string()),
+        &Modifiers::empty(),
         &mut text,
         max_width,
     );
@@ -553,13 +529,9 @@ fn cursor_input() {
     // these should be ignored
     for _ in 0..10 {
         cursor.handle_input(
-            &dioxus_html::KeyboardData::new(
-                dioxus_html::input_data::keyboard_types::Key::Character("o".to_string()),
-                dioxus_html::input_data::keyboard_types::Code::KeyO,
-                dioxus_html::input_data::keyboard_types::Location::Standard,
-                false,
-                Modifiers::empty(),
-            ),
+            &keyboard_types::Code::KeyO,
+            &keyboard_types::Key::Character("o".to_string()),
+            &Modifiers::empty(),
             &mut text,
             max_width,
         );

+ 4 - 0
packages/native-core/src/utils/mod.rs

@@ -1,3 +1,7 @@
+//! # Utilities for renders using the RealDOM
+//!
+//! This includes an iterator that can be used to iterate over the children of a node that persists changes in the struture of the DOM, and a cursor for text editing.
+
 mod persistant_iterator;
 pub use persistant_iterator::*;
 pub mod cursor;

+ 219 - 263
packages/native-core/src/utils/persistant_iterator.rs

@@ -1,252 +1,204 @@
+use smallvec::SmallVec;
+
 use crate::{
-    node::{FromAnyValue, NodeType},
-    real_dom::RealDom,
-    state::State,
-    tree::TreeView,
-    NodeId, RealNodeId,
+    node::FromAnyValue,
+    node_watcher::NodeWatcher,
+    prelude::{NodeMut, NodeRef},
+    real_dom::{NodeImmutable, RealDom},
+    NodeId,
+};
+use std::{
+    fmt::Debug,
+    sync::{Arc, Mutex},
 };
-use dioxus_core::{Mutation, Mutations};
-use std::fmt::Debug;
 
-#[derive(Debug)]
-pub enum ElementProduced {
-    /// The iterator produced an element by progressing to the next node in a depth first order.
-    Progressed(RealNodeId),
+/// The element produced by the iterator
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct ElementProduced {
+    id: NodeId,
+    movement: IteratorMovement,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+/// The method by which the iterator produced an element
+pub enum IteratorMovement {
+    /// The iterator produced an element by progressing to the next node
+    Progressed,
     /// The iterator reached the end of the tree and looped back to the root
-    Looped(RealNodeId),
+    Looped,
 }
+
 impl ElementProduced {
-    pub fn id(&self) -> RealNodeId {
-        match self {
-            ElementProduced::Progressed(id) => *id,
-            ElementProduced::Looped(id) => *id,
+    /// Get the id of the element produced
+    pub fn id(&self) -> NodeId {
+        self.id
+    }
+
+    /// The movement the iterator made to produce the element
+    pub fn movement(&self) -> &IteratorMovement {
+        &self.movement
+    }
+
+    fn looped(id: NodeId) -> Self {
+        Self {
+            id,
+            movement: IteratorMovement::Looped,
+        }
+    }
+
+    fn progressed(id: NodeId) -> Self {
+        Self {
+            id,
+            movement: IteratorMovement::Progressed,
         }
     }
 }
 
-#[derive(Debug)]
-enum NodePosition {
-    AtNode,
-    InChild(usize),
+struct PersistantElementIterUpdater<V> {
+    stack: Arc<Mutex<smallvec::SmallVec<[NodeId; 5]>>>,
+    phantom: std::marker::PhantomData<V>,
 }
 
-impl NodePosition {
-    fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self {
-        match self {
-            Self::AtNode => Self::AtNode,
-            Self::InChild(i) => Self::InChild(f(*i)),
+impl<V: FromAnyValue + Sync + Send> NodeWatcher<V> for PersistantElementIterUpdater<V> {
+    fn on_node_moved(&self, node: NodeMut<V>) {
+        // if any element is moved, update its parents in the stack
+        let mut stack = self.stack.lock().unwrap();
+        let moved = node.id();
+        let rdom = node.real_dom();
+        if let Some(r) = stack.iter().position(|el_id| *el_id == moved) {
+            let back = &stack[r..];
+            let mut new = SmallVec::new();
+            let mut parent = node.parent_id();
+            while let Some(p) = parent.and_then(|id| rdom.get(id)) {
+                new.push(p.id());
+                parent = p.parent_id();
+            }
+            new.extend(back.iter().copied());
+            *stack = new;
         }
     }
 
-    fn get_or_insert(&mut self, child_idx: usize) -> usize {
-        match self {
-            Self::AtNode => {
-                *self = Self::InChild(child_idx);
-                child_idx
-            }
-            Self::InChild(i) => *i,
+    fn on_node_removed(&self, node: NodeMut<V>) {
+        // if any element is removed in the chain, remove it and its children from the stack
+        let mut stack = self.stack.lock().unwrap();
+        let removed = node.id();
+        if let Some(r) = stack.iter().position(|el_id| *el_id == removed) {
+            stack.truncate(r);
         }
     }
 }
 
-/// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom].
+/// Focus systems need a iterator that can persist through changes in the [crate::prelude::RealDom]
 /// This iterator traverses the tree depth first.
-/// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]).
+/// You can iterate through it with [PersistantElementIter::next] and [PersistantElementIter::prev].
 /// The iterator loops around when it reaches the end or the beginning.
 pub struct PersistantElementIter {
-    // stack of elements and fragments
-    stack: smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
-}
-
-impl Default for PersistantElementIter {
-    fn default() -> Self {
-        PersistantElementIter {
-            stack: smallvec::smallvec![(NodeId(0), NodePosition::AtNode)],
-        }
-    }
+    // stack of elements and fragments, the last element is the last element that was yielded
+    stack: Arc<Mutex<smallvec::SmallVec<[NodeId; 5]>>>,
 }
 
 impl PersistantElementIter {
-    pub fn new() -> Self {
-        Self::default()
-    }
+    /// Create a new iterator in the RealDom
+    pub fn create<V: FromAnyValue + Send + Sync>(rdom: &mut RealDom<V>) -> Self {
+        let inner = Arc::new(Mutex::new(smallvec::smallvec![rdom.root_id()]));
 
-    /// remove stale element refreneces
-    /// returns true if the focused element is removed
-    pub fn prune<S: State<V>, V: FromAnyValue>(
-        &mut self,
-        mutations: &Mutations,
-        rdom: &RealDom<S, V>,
-    ) -> bool {
-        let mut changed = false;
-        let ids_removed: Vec<_> = mutations
-            .edits
-            .iter()
-            .filter_map(|m| {
-                // nodes within templates will never be removed
-                match m {
-                    Mutation::Remove { id } => Some(rdom.element_to_node_id(*id)),
-                    Mutation::ReplaceWith { id, .. } => Some(rdom.element_to_node_id(*id)),
-                    _ => None,
-                }
-            })
-            .collect();
-        // if any element is removed in the chain, remove it and its children from the stack
-        if let Some(r) = self
-            .stack
-            .iter()
-            .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id))
-        {
-            self.stack.truncate(r);
-            changed = true;
-        }
-        // if a child is removed or inserted before or at the current element, update the child index
-        for (el_id, child_idx) in self.stack.iter_mut() {
-            if let NodePosition::InChild(child_idx) = child_idx {
-                if let Some(children) = &rdom.tree.children_ids(*el_id) {
-                    for m in &mutations.edits {
-                        match m {
-                            Mutation::Remove { id } => {
-                                let id = rdom.element_to_node_id(*id);
-                                if children.iter().take(*child_idx + 1).any(|c| *c == id) {
-                                    *child_idx -= 1;
-                                }
-                            }
-                            Mutation::InsertBefore { id, m } => {
-                                let id = rdom.element_to_node_id(*id);
-                                if children.iter().take(*child_idx + 1).any(|c| *c == id) {
-                                    *child_idx += *m;
-                                }
-                            }
-                            Mutation::InsertAfter { id, m } => {
-                                let id = rdom.element_to_node_id(*id);
-                                if children.iter().take(*child_idx).any(|c| *c == id) {
-                                    *child_idx += *m;
-                                }
-                            }
-                            _ => (),
-                        }
-                    }
-                }
-            }
-        }
-        changed
+        rdom.add_node_watcher(PersistantElementIterUpdater {
+            stack: inner.clone(),
+            phantom: std::marker::PhantomData,
+        });
+
+        PersistantElementIter { stack: inner }
     }
 
     /// get the next element
-    pub fn next<S: State<V>, V: FromAnyValue>(&mut self, rdom: &RealDom<S, V>) -> ElementProduced {
-        if self.stack.is_empty() {
-            let id = NodeId(0);
-            let new = (id, NodePosition::AtNode);
-            self.stack.push(new);
-            ElementProduced::Looped(id)
+    pub fn next<V: FromAnyValue + Send + Sync>(&mut self, rdom: &RealDom<V>) -> ElementProduced {
+        let mut stack = self.stack.lock().unwrap();
+        if stack.is_empty() {
+            let id = rdom.root_id();
+            let new = id;
+            stack.push(new);
+            ElementProduced::looped(id)
         } else {
-            let (last, old_child_idx) = self.stack.last_mut().unwrap();
-            let node = &rdom[*last];
-            match &node.node_data.node_type {
-                NodeType::Element { .. } => {
-                    let children = rdom.tree.children_ids(*last).unwrap();
-                    *old_child_idx = old_child_idx.map(|i| i + 1);
-                    // if we have children, go to the next child
-                    let child_idx = old_child_idx.get_or_insert(0);
-                    if child_idx >= children.len() {
-                        self.pop();
-                        self.next(rdom)
-                    } else {
-                        let id = children[child_idx];
-                        if let NodeType::Element { .. } = &rdom[id].node_data.node_type {
-                            self.stack.push((id, NodePosition::AtNode));
+            let mut look_in_children = true;
+            loop {
+                if let Some(current) = stack.last().and_then(|last| rdom.get(*last)) {
+                    // if the current element has children, add the first child to the stack and return it
+                    if look_in_children {
+                        if let Some(first) = current.children().first() {
+                            let new = first.id();
+                            stack.push(new);
+                            return ElementProduced::progressed(new);
                         }
-                        ElementProduced::Progressed(id)
                     }
+                    stack.pop();
+                    if let Some(new) = current.next() {
+                        // the next element exists, add it to the stack and return it
+                        let new = new.id();
+                        stack.push(new);
+                        return ElementProduced::progressed(new);
+                    }
+                    // otherwise, continue the loop and go to the parent
+                } else {
+                    // if there is no parent, loop back to the root
+                    let new = rdom.root_id();
+                    stack.clear();
+                    stack.push(new);
+                    return ElementProduced::looped(new);
                 }
-
-                NodeType::Text { .. } | NodeType::Placeholder { .. } => {
-                    // we are at a leaf, so we are done
-                    ElementProduced::Progressed(self.pop())
-                }
+                look_in_children = false;
             }
         }
     }
 
     /// get the previous element
-    pub fn prev<S: State<V>, V: FromAnyValue>(&mut self, rdom: &RealDom<S, V>) -> ElementProduced {
+    pub fn prev<V: FromAnyValue + Send + Sync>(&mut self, rdom: &RealDom<V>) -> ElementProduced {
         // recursively add the last child element to the stack
-        fn push_back<S: State<V>, V: FromAnyValue>(
-            stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
-            new_node: RealNodeId,
-            rdom: &RealDom<S, V>,
-        ) -> RealNodeId {
-            match &rdom[new_node].node_data.node_type {
-                NodeType::Element { .. } => {
-                    let children = rdom.tree.children_ids(new_node).unwrap();
-                    if children.is_empty() {
-                        new_node
-                    } else {
-                        stack.push((new_node, NodePosition::InChild(children.len() - 1)));
-                        push_back(stack, *children.last().unwrap(), rdom)
-                    }
-                }
-                _ => new_node,
+        fn push_back<V: FromAnyValue + Send + Sync>(
+            stack: &mut smallvec::SmallVec<[NodeId; 5]>,
+            node: NodeRef<V>,
+        ) -> NodeId {
+            stack.push(node.id());
+            if let Some(last) = node.children().last() {
+                push_back(stack, *last)
+            } else {
+                node.id()
             }
         }
-        if self.stack.is_empty() {
-            let new_node = NodeId(0);
-            ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom))
-        } else {
-            let (last, old_child_idx) = self.stack.last_mut().unwrap();
-            let node = &rdom[*last];
-            match &node.node_data.node_type {
-                NodeType::Element { .. } => {
-                    let children = rdom.tree.children_ids(*last).unwrap();
-                    // if we have children, go to the next child
-                    if let NodePosition::InChild(0) = old_child_idx {
-                        ElementProduced::Progressed(self.pop())
-                    } else {
-                        *old_child_idx = old_child_idx.map(|i| i - 1);
-                        if let NodePosition::InChild(child_idx) = old_child_idx {
-                            if *child_idx >= children.len() || children.is_empty() {
-                                self.pop();
-                                self.prev(rdom)
-                            } else {
-                                let new_node = children[*child_idx];
-                                ElementProduced::Progressed(push_back(
-                                    &mut self.stack,
-                                    new_node,
-                                    rdom,
-                                ))
-                            }
-                        } else {
-                            self.pop();
-                            self.prev(rdom)
-                        }
-                    }
-                }
-
-                NodeType::Text { .. } | NodeType::Placeholder { .. } => {
-                    // we are at a leaf, so we are done
-                    ElementProduced::Progressed(self.pop())
-                }
+        let mut stack = self.stack.lock().unwrap();
+        if stack.is_empty() {
+            let id = rdom.root_id();
+            let last = push_back(&mut stack, rdom.get(id).unwrap());
+            ElementProduced::looped(last)
+        } else if let Some(current) = stack.pop().and_then(|last| rdom.get(last)) {
+            if let Some(new) = current.prev() {
+                // the next element exists, add it to the stack and return it
+                let new = push_back(&mut stack, new);
+                ElementProduced::progressed(new)
             }
+            // otherwise, yeild the parent
+            else if let Some(parent) = stack.last() {
+                // if there is a parent, return it
+                ElementProduced::progressed(*parent)
+            } else {
+                // if there is no parent, loop back to the root
+                let id = rdom.root_id();
+                let last = push_back(&mut stack, rdom.get(id).unwrap());
+                ElementProduced::looped(last)
+            }
+        } else {
+            // if there is no parent, loop back to the root
+            let id = rdom.root_id();
+            let last = push_back(&mut stack, rdom.get(id).unwrap());
+            ElementProduced::looped(last)
         }
     }
-
-    fn pop(&mut self) -> RealNodeId {
-        self.stack.pop().unwrap().0
-    }
-}
-
-#[derive(Default, Clone, Debug)]
-struct Empty {}
-impl State<()> for Empty {
-    const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
-
-    const MASKS: &'static [crate::NodeMask] = &[];
 }
 
 #[test]
 #[allow(unused_variables)]
 fn traverse() {
+    use crate::dioxus::DioxusState;
+    use crate::prelude::*;
     use dioxus::prelude::*;
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
@@ -266,82 +218,85 @@ fn traverse() {
     let mut vdom = VirtualDom::new(Base);
     let mutations = vdom.rebuild();
 
-    let mut rdom: RealDom<Empty> = RealDom::new();
+    let mut rdom: RealDom = RealDom::new([]);
 
-    let _to_update = rdom.apply_mutations(mutations);
+    let mut iter = PersistantElementIter::create(&mut rdom);
+    let mut dioxus_state = DioxusState::create(&mut rdom);
+    dioxus_state.apply_mutations(&mut rdom, mutations);
 
-    let mut iter = PersistantElementIter::new();
     let div_tag = "div".to_string();
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: div_tag, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: div_tag, .. })
     ));
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: div_tag, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: div_tag, .. })
     ));
     let text1 = "hello".to_string();
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text1, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text1)
     ));
     let p_tag = "p".to_string();
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: p_tag, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: p_tag, .. })
     ));
     let text2 = "world".to_string();
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text2, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text2)
     ));
     let text3 = "hello world".to_string();
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text3, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text3)
     ));
     assert!(matches!(
-        &rdom[iter.next(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: div_tag, .. }
+        &*rdom.get(iter.next(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: div_tag, .. })
     ));
 
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text3, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text3)
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text2, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text2)
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: p_tag, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: p_tag, .. })
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text1, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text1)
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: div_tag, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: div_tag, .. })
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: div_tag, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: div_tag, .. })
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Element { tag: div_tag, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: div_tag, .. })
     ));
     assert!(matches!(
-        &rdom[iter.prev(&rdom).id()].node_data.node_type,
-        NodeType::Text { text: text3, .. }
+        &*rdom.get(iter.prev(&rdom).id()).unwrap().node_type(),
+        NodeType::Text(text3)
     ));
 }
 
 #[test]
 #[allow(unused_variables)]
 fn persist_removes() {
+    use crate::dioxus::DioxusState;
+    use crate::prelude::*;
     use dioxus::prelude::*;
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
@@ -365,16 +320,17 @@ fn persist_removes() {
     }
     let mut vdom = VirtualDom::new(Base);
 
-    let mut rdom: RealDom<Empty> = RealDom::new();
+    let mut rdom: RealDom = RealDom::new([]);
 
     let build = vdom.rebuild();
     println!("{build:#?}");
-    let _to_update = rdom.apply_mutations(build);
-
     // this will end on the node that is removed
-    let mut iter1 = PersistantElementIter::new();
+    let mut iter1 = PersistantElementIter::create(&mut rdom);
     // this will end on the after node that is removed
-    let mut iter2 = PersistantElementIter::new();
+    let mut iter2 = PersistantElementIter::create(&mut rdom);
+    let mut dioxus_state = DioxusState::create(&mut rdom);
+    dioxus_state.apply_mutations(&mut rdom, build);
+
     // root
     iter1.next(&rdom).id();
     iter2.next(&rdom).id();
@@ -401,29 +357,27 @@ fn persist_removes() {
     vdom.mark_dirty(ScopeId(0));
     let update = vdom.render_immediate();
     println!("{update:#?}");
-    iter1.prune(&update, &rdom);
-    iter2.prune(&update, &rdom);
-    let _to_update = rdom.apply_mutations(update);
+    dioxus_state.apply_mutations(&mut rdom, update);
 
     let root_tag = "Root".to_string();
     let idx = iter1.next(&rdom).id();
-    dbg!(&rdom[idx].node_data.node_type);
     assert!(matches!(
-        &rdom[idx].node_data.node_type,
-        NodeType::Element { tag: root_tag, .. }
+        &*rdom.get(idx).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: root_tag, .. })
     ));
 
     let idx = iter2.next(&rdom).id();
-    dbg!(&rdom[idx].node_data.node_type);
     assert!(matches!(
-        &rdom[idx].node_data.node_type,
-        NodeType::Element { tag: root_tag, .. }
+        &*rdom.get(idx).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: root_tag, .. })
     ));
 }
 
 #[test]
 #[allow(unused_variables)]
 fn persist_instertions_before() {
+    use crate::dioxus::DioxusState;
+    use crate::prelude::*;
     use dioxus::prelude::*;
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
@@ -447,12 +401,13 @@ fn persist_instertions_before() {
     }
     let mut vdom = VirtualDom::new(Base);
 
-    let mut rdom: RealDom<Empty> = RealDom::new();
+    let mut rdom: RealDom = RealDom::new([]);
+    let mut dioxus_state = DioxusState::create(&mut rdom);
 
     let build = vdom.rebuild();
-    let _to_update = rdom.apply_mutations(build);
+    dioxus_state.apply_mutations(&mut rdom, build);
 
-    let mut iter = PersistantElementIter::new();
+    let mut iter = PersistantElementIter::create(&mut rdom);
     // div
     iter.next(&rdom).id();
     // p
@@ -466,20 +421,21 @@ fn persist_instertions_before() {
 
     vdom.mark_dirty(ScopeId(0));
     let update = vdom.render_immediate();
-    iter.prune(&update, &rdom);
-    let _to_update = rdom.apply_mutations(update);
+    dioxus_state.apply_mutations(&mut rdom, update);
 
     let p_tag = "div".to_string();
     let idx = iter.next(&rdom).id();
     assert!(matches!(
-        &rdom[idx].node_data.node_type,
-        NodeType::Element { tag: p_tag, .. }
+        &*rdom.get(idx).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: p_tag, .. })
     ));
 }
 
 #[test]
 #[allow(unused_variables)]
 fn persist_instertions_after() {
+    use crate::dioxus::DioxusState;
+    use crate::prelude::*;
     use dioxus::prelude::*;
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
@@ -503,12 +459,13 @@ fn persist_instertions_after() {
     }
     let mut vdom = VirtualDom::new(Base);
 
-    let mut rdom: RealDom<Empty> = RealDom::new();
+    let mut rdom: RealDom = RealDom::new([]);
+    let mut iter = PersistantElementIter::create(&mut rdom);
+    let mut dioxus_state = DioxusState::create(&mut rdom);
 
     let build = vdom.rebuild();
-    let _to_update = rdom.apply_mutations(build);
+    dioxus_state.apply_mutations(&mut rdom, build);
 
-    let mut iter = PersistantElementIter::new();
     // div
     iter.next(&rdom).id();
     // p
@@ -521,19 +478,18 @@ fn persist_instertions_after() {
     iter.next(&rdom).id();
 
     let update = vdom.rebuild();
-    iter.prune(&update, &rdom);
-    let _to_update = rdom.apply_mutations(update);
+    dioxus_state.apply_mutations(&mut rdom, update);
 
     let p_tag = "p".to_string();
     let idx = iter.next(&rdom).id();
     assert!(matches!(
-        &rdom[idx].node_data.node_type,
-        NodeType::Element { tag: p_tag, .. }
+        &*rdom.get(idx).unwrap().node_type(),
+        NodeType::Element(ElementNode { tag: p_tag, .. })
     ));
     let text = "hello world".to_string();
     let idx = iter.next(&rdom).id();
     assert!(matches!(
-        &rdom[idx].node_data.node_type,
-        NodeType::Text { text, .. }
+        &*rdom.get(idx).unwrap().node_type(),
+        NodeType::Text(text)
     ));
 }

+ 237 - 0
packages/native-core/tests/called_minimally_on_build.rs

@@ -0,0 +1,237 @@
+use dioxus::prelude::*;
+use dioxus_native_core::prelude::*;
+use dioxus_native_core_macro::partial_derive_state;
+use shipyard::Component;
+
+macro_rules! dep {
+    ( child( $name:ty, $dep:ty ) ) => {
+        #[partial_derive_state]
+        impl State for $name {
+            type ParentDependencies = ();
+            type ChildDependencies = $dep;
+            type NodeDependencies = ();
+
+            const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::ALL;
+
+            fn update<'a>(
+                &mut self,
+                _: NodeView,
+                _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+                _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+                _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+                _: &SendAnyMap,
+            ) -> bool {
+                self.0 += 1;
+                true
+            }
+
+            fn create<'a>(
+                node_view: NodeView<()>,
+                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,
+            ) -> Self {
+                let mut myself = Self::default();
+                myself.update(node_view, node, parent, children, context);
+                myself
+            }
+        }
+    };
+
+    ( parent( $name:ty, $dep:ty ) ) => {
+        #[partial_derive_state]
+        impl State for $name {
+            type ParentDependencies = $dep;
+            type ChildDependencies = ();
+            type NodeDependencies = ();
+
+            const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::ALL;
+
+            fn update<'a>(
+                &mut self,
+                _: NodeView,
+                _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+                _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+                _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+                _: &SendAnyMap,
+            ) -> bool {
+                self.0 += 1;
+                true
+            }
+
+            fn create<'a>(
+                node_view: NodeView<()>,
+                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,
+            ) -> Self {
+                let mut myself = Self::default();
+                myself.update(node_view, node, parent, children, context);
+                myself
+            }
+        }
+    };
+
+    ( node( $name:ty, $dep:ty ) ) => {
+        #[partial_derive_state]
+        impl State for $name {
+            type ParentDependencies = $dep;
+            type ChildDependencies = ();
+            type NodeDependencies = ();
+
+            const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::ALL;
+
+            fn update<'a>(
+                &mut self,
+                _: NodeView,
+                _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+                _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+                _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+                _: &SendAnyMap,
+            ) -> bool {
+                self.0 += 1;
+                true
+            }
+
+            fn create<'a>(
+                node_view: NodeView<()>,
+                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,
+            ) -> Self {
+                let mut myself = Self::default();
+                myself.update(node_view, node, parent, children, context);
+                myself
+            }
+        }
+    };
+}
+
+macro_rules! test_state{
+    ( state: ( $( $state:ty ),* ) ) => {
+        #[test]
+        fn state_reduce_initally_called_minimally() {
+            #[allow(non_snake_case)]
+            fn Base(cx: Scope) -> Element {
+                render!{
+                    div {
+                        div{
+                            div{
+                                p{}
+                            }
+                            p{
+                                "hello"
+                            }
+                            div{
+                                h1{}
+                            }
+                            p{
+                                "world"
+                            }
+                        }
+                    }
+                }
+            }
+
+            let mut vdom = VirtualDom::new(Base);
+
+            let mutations = vdom.rebuild();
+
+            let mut dom: RealDom = RealDom::new([$( <$state>::to_type_erased() ),*]);
+            let mut dioxus_state = DioxusState::create(&mut dom);
+
+            dioxus_state.apply_mutations(&mut dom, mutations);
+            dom.update_state(SendAnyMap::new());
+
+            dom.traverse_depth_first(|n| {
+                $(
+                    assert_eq!(n.get::<$state>().unwrap().0, 1);
+                )*
+            });
+        }
+    }
+}
+
+mod node_depends_on_child_and_parent {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Node(i32);
+    dep!(node(Node, (Child, Parent)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Child(i32);
+    dep!(child(Child, (Child,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Parent(i32);
+    dep!(parent(Parent, (Parent,)));
+
+    test_state!(state: (Child, Node, Parent));
+}
+
+mod child_depends_on_node_that_depends_on_parent {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Node(i32);
+    dep!(node(Node, (Parent,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Child(i32);
+    dep!(child(Child, (Node,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Parent(i32);
+    dep!(parent(Parent, (Parent,)));
+
+    test_state!(state: (Child, Node, Parent));
+}
+
+mod parent_depends_on_node_that_depends_on_child {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Node(i32);
+    dep!(node(Node, (Child,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Child(i32);
+    dep!(child(Child, (Child,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Parent(i32);
+    dep!(parent(Parent, (Node,)));
+
+    test_state!(state: (Child, Node, Parent));
+}
+
+mod node_depends_on_other_node_state {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Node1(i32);
+    dep!(node(Node1, (Node2,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Node2(i32);
+    dep!(node(Node2, ()));
+
+    test_state!(state: (Node1, Node2));
+}
+
+mod node_child_and_parent_state_depends_on_self {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Node(i32);
+    dep!(node(Node, ()));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Child(i32);
+    dep!(child(Child, (Child,)));
+
+    #[derive(Debug, Clone, Default, PartialEq, Component)]
+    struct Parent(i32);
+    dep!(parent(Parent, (Parent,)));
+
+    test_state!(state: (Child, Node, Parent));
+}

+ 51 - 31
packages/native-core/tests/fuzzing.rs

@@ -1,12 +1,8 @@
 use dioxus::prelude::Props;
 use dioxus_core::*;
-use dioxus_native_core::{
-    node_ref::{AttributeMask, NodeView},
-    real_dom::RealDom,
-    state::{ParentDepState, State},
-    NodeMask, SendAnyMap,
-};
-use dioxus_native_core_macro::{sorted_str_slice, State};
+use dioxus_native_core::prelude::*;
+use dioxus_native_core_macro::partial_derive_state;
+use shipyard::Component;
 use std::cell::Cell;
 
 fn random_ns() -> Option<&'static str> {
@@ -232,7 +228,7 @@ fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute {
 
 static mut TEMPLATE_COUNT: usize = 0;
 
-#[derive(PartialEq, Props)]
+#[derive(PartialEq, Props, Component)]
 struct DepthProps {
     depth: usize,
     root: bool,
@@ -294,26 +290,48 @@ fn create_random_element(cx: Scope<DepthProps>) -> Element {
     node
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Default)]
-pub struct BlablaState {}
+#[derive(Debug, Clone, PartialEq, Eq, Default, Component)]
+pub struct BlablaState {
+    count: usize,
+}
 
-/// Font style are inherited by default if not specified otherwise by some of the supported attributes.
-impl ParentDepState for BlablaState {
-    type Ctx = ();
-    type DepState = (Self,);
+#[partial_derive_state]
+impl State for BlablaState {
+    type ParentDependencies = (Self,);
+    type ChildDependencies = ();
+    type NodeDependencies = ();
 
-    const NODE_MASK: NodeMask =
-        NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!(["blabla",])));
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+        .with_attrs(AttributeMaskBuilder::Some(&["blabla"]))
+        .with_element();
 
-    fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
-        false
+    fn update<'a>(
+        &mut self,
+        _: NodeView,
+        _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: &SendAnyMap,
+    ) -> bool {
+        if let Some((parent,)) = parent {
+            if parent.count != 0 {
+                self.count += 1;
+            }
+        }
+        true
     }
-}
 
-#[derive(Clone, State, Default, Debug)]
-pub struct NodeState {
-    #[parent_dep_state(blabla)]
-    blabla: BlablaState,
+    fn create<'a>(
+        node_view: NodeView<()>,
+        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,
+    ) -> Self {
+        let mut myself = Self::default();
+        myself.update(node_view, node, parent, children, context);
+        myself
+    }
 }
 
 // test for panics when creating random nodes and templates
@@ -328,11 +346,12 @@ fn create() {
             },
         );
         let mutations = vdom.rebuild();
-        let mut rdom: RealDom<NodeState> = RealDom::new();
-        let (to_update, _diff) = rdom.apply_mutations(mutations);
+        let mut rdom: RealDom = RealDom::new([BlablaState::to_type_erased()]);
+        let mut dioxus_state = DioxusState::create(&mut rdom);
+        dioxus_state.apply_mutations(&mut rdom, mutations);
 
         let ctx = SendAnyMap::new();
-        rdom.update_state(to_update, ctx);
+        rdom.update_state(ctx);
     }
 }
 
@@ -349,17 +368,18 @@ fn diff() {
             },
         );
         let mutations = vdom.rebuild();
-        let mut rdom: RealDom<NodeState> = RealDom::new();
-        let (to_update, _diff) = rdom.apply_mutations(mutations);
+        let mut rdom: RealDom = RealDom::new([BlablaState::to_type_erased()]);
+        let mut dioxus_state = DioxusState::create(&mut rdom);
+        dioxus_state.apply_mutations(&mut rdom, mutations);
 
         let ctx = SendAnyMap::new();
-        rdom.update_state(to_update, ctx);
+        rdom.update_state(ctx);
         for _ in 0..10 {
             let mutations = vdom.render_immediate();
-            let (to_update, _diff) = rdom.apply_mutations(mutations);
+            dioxus_state.apply_mutations(&mut rdom, mutations);
 
             let ctx = SendAnyMap::new();
-            rdom.update_state(to_update, ctx);
+            rdom.update_state(ctx);
         }
     }
 }

+ 50 - 31
packages/native-core/tests/miri_native.rs

@@ -1,34 +1,51 @@
 use dioxus::prelude::*;
-use dioxus_native_core::{
-    node_ref::{AttributeMask, NodeView},
-    real_dom::RealDom,
-    state::{ParentDepState, State},
-    NodeMask, SendAnyMap,
-};
-use dioxus_native_core_macro::{sorted_str_slice, State};
-use std::sync::{Arc, Mutex};
+use dioxus_native_core::prelude::*;
+use dioxus_native_core_macro::partial_derive_state;
+use shipyard::Component;
 use tokio::time::sleep;
 
-#[derive(Debug, Clone, PartialEq, Eq, Default)]
-pub struct BlablaState {}
-
-/// Font style are inherited by default if not specified otherwise by some of the supported attributes.
-impl ParentDepState for BlablaState {
-    type Ctx = ();
-    type DepState = (Self,);
-
-    const NODE_MASK: NodeMask =
-        NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!(["blabla",])));
+#[derive(Debug, Clone, PartialEq, Eq, Default, Component)]
+pub struct BlablaState {
+    count: usize,
+}
 
-    fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
-        false
+#[partial_derive_state]
+impl State for BlablaState {
+    type ParentDependencies = (Self,);
+    type ChildDependencies = ();
+    type NodeDependencies = ();
+
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+        .with_attrs(AttributeMaskBuilder::Some(&["blabla"]))
+        .with_element();
+
+    fn update<'a>(
+        &mut self,
+        _: NodeView,
+        _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: &SendAnyMap,
+    ) -> bool {
+        if let Some((parent,)) = parent {
+            if parent.count != 0 {
+                self.count += 1;
+            }
+        }
+        true
     }
-}
 
-#[derive(Clone, State, Default, Debug)]
-pub struct NodeState {
-    #[parent_dep_state(blabla)]
-    blabla: BlablaState,
+    fn create<'a>(
+        node_view: NodeView<()>,
+        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,
+    ) -> Self {
+        let mut myself = Self::default();
+        myself.update(node_view, node, parent, children, context);
+        myself
+    }
 }
 
 mod dioxus_elements {
@@ -76,6 +93,7 @@ mod dioxus_elements {
 
 #[test]
 fn native_core_is_okay() {
+    use std::sync::{Arc, Mutex};
     use std::time::Duration;
 
     fn app(cx: Scope) -> Element {
@@ -113,23 +131,24 @@ fn native_core_is_okay() {
         .unwrap();
 
     rt.block_on(async {
-        let rdom = Arc::new(Mutex::new(RealDom::<NodeState>::new()));
+        let rdom = Arc::new(Mutex::new(RealDom::new([BlablaState::to_type_erased()])));
+        let mut dioxus_state = DioxusState::create(&mut rdom.lock().unwrap());
         let mut dom = VirtualDom::new(app);
 
-        let muts = dom.rebuild();
-        let (to_update, _diff) = rdom.lock().unwrap().apply_mutations(muts);
+        let mutations = dom.rebuild();
+        dioxus_state.apply_mutations(&mut rdom.lock().unwrap(), mutations);
 
         let ctx = SendAnyMap::new();
-        rdom.lock().unwrap().update_state(to_update, ctx);
+        rdom.lock().unwrap().update_state(ctx);
 
         for _ in 0..10 {
             dom.wait_for_work().await;
 
             let mutations = dom.render_immediate();
-            let (to_update, _diff) = rdom.lock().unwrap().apply_mutations(mutations);
+            dioxus_state.apply_mutations(&mut rdom.lock().unwrap(), mutations);
 
             let ctx = SendAnyMap::new();
-            rdom.lock().unwrap().update_state(to_update, ctx);
+            rdom.lock().unwrap().update_state(ctx);
         }
     });
 }

+ 440 - 0
packages/native-core/tests/passes.rs

@@ -0,0 +1,440 @@
+use dioxus_native_core::node::NodeType;
+use dioxus_native_core::prelude::*;
+use dioxus_native_core_macro::partial_derive_state;
+use rustc_hash::{FxHashMap, FxHashSet};
+use shipyard::Component;
+
+fn create_blank_element() -> NodeType {
+    NodeType::Element(ElementNode {
+        tag: "div".to_owned(),
+        namespace: None,
+        attributes: FxHashMap::default(),
+        listeners: FxHashSet::default(),
+    })
+}
+
+#[test]
+fn node_pass() {
+    #[derive(Debug, Default, Clone, PartialEq, Component)]
+    struct Number(i32);
+
+    #[partial_derive_state]
+    impl State for Number {
+        type ChildDependencies = ();
+        type NodeDependencies = ();
+        type ParentDependencies = ();
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            self.0 += 1;
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self::default();
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    let mut tree: RealDom = RealDom::new([Number::to_type_erased()]);
+    tree.update_state(SendAnyMap::new());
+
+    assert_eq!(
+        tree.get(tree.root_id()).unwrap().get().as_deref(),
+        Some(&Number(1))
+    );
+
+    // mark the node as dirty
+    tree.get_mut(tree.root_id()).unwrap().get_mut::<Number>();
+
+    tree.update_state(SendAnyMap::new());
+    assert_eq!(
+        tree.get(tree.root_id()).unwrap().get().as_deref(),
+        Some(&Number(2))
+    );
+}
+
+#[test]
+fn dependant_node_pass() {
+    #[derive(Debug, Default, Clone, PartialEq, Component)]
+    struct AddNumber(i32);
+
+    #[partial_derive_state]
+    impl State for AddNumber {
+        type ChildDependencies = ();
+        type NodeDependencies = (SubtractNumber,);
+        type ParentDependencies = ();
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            self.0 += 1;
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self::default();
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    #[derive(Debug, Default, Clone, PartialEq, Component)]
+    struct SubtractNumber(i32);
+
+    #[partial_derive_state]
+    impl State for SubtractNumber {
+        type ChildDependencies = ();
+        type NodeDependencies = ();
+        type ParentDependencies = ();
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            self.0 -= 1;
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self::default();
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    let mut tree: RealDom = RealDom::new([
+        AddNumber::to_type_erased(),
+        SubtractNumber::to_type_erased(),
+    ]);
+    tree.update_state(SendAnyMap::new());
+
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(1)));
+    assert_eq!(root.get().as_deref(), Some(&SubtractNumber(-1)));
+
+    // mark the subtract state as dirty, it should update the add state
+    tree.get_mut(tree.root_id())
+        .unwrap()
+        .get_mut::<SubtractNumber>();
+
+    tree.update_state(SendAnyMap::new());
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(2)));
+    assert_eq!(root.get().as_deref(), Some(&SubtractNumber(-2)));
+
+    // mark the add state as dirty, it should ~not~ update the subtract state
+    tree.get_mut(tree.root_id()).unwrap().get_mut::<AddNumber>();
+
+    tree.update_state(SendAnyMap::new());
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(3)));
+    assert_eq!(root.get().as_deref(), Some(&SubtractNumber(-2)));
+}
+
+#[test]
+fn independant_node_pass() {
+    #[derive(Debug, Default, Clone, PartialEq, Component)]
+    struct AddNumber(i32);
+
+    #[partial_derive_state]
+    impl State for AddNumber {
+        type ChildDependencies = ();
+        type NodeDependencies = ();
+        type ParentDependencies = ();
+
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            self.0 += 1;
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self::default();
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    #[derive(Debug, Default, Clone, PartialEq, Component)]
+    struct SubtractNumber(i32);
+
+    #[partial_derive_state]
+    impl State for SubtractNumber {
+        type ChildDependencies = ();
+        type NodeDependencies = ();
+        type ParentDependencies = ();
+
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            self.0 -= 1;
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self::default();
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    let mut tree: RealDom = RealDom::new([
+        AddNumber::to_type_erased(),
+        SubtractNumber::to_type_erased(),
+    ]);
+    tree.update_state(SendAnyMap::new());
+
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(1)));
+    assert_eq!(root.get().as_deref(), Some(&SubtractNumber(-1)));
+
+    // mark the subtract state as dirty, it should ~not~ update the add state
+    tree.get_mut(tree.root_id())
+        .unwrap()
+        .get_mut::<SubtractNumber>();
+
+    tree.update_state(SendAnyMap::new());
+
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(1)));
+    assert_eq!(root.get().as_deref(), Some(&SubtractNumber(-2)));
+
+    // mark the add state as dirty, it should ~not~ update the subtract state
+    tree.get_mut(tree.root_id()).unwrap().get_mut::<AddNumber>();
+
+    tree.update_state(SendAnyMap::new());
+
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(2)));
+    assert_eq!(root.get().as_deref(), Some(&SubtractNumber(-2)));
+}
+
+#[test]
+fn down_pass() {
+    #[derive(Debug, Clone, PartialEq, Component)]
+    struct AddNumber(i32);
+
+    impl Default for AddNumber {
+        fn default() -> Self {
+            Self(1)
+        }
+    }
+
+    #[partial_derive_state]
+    impl State for AddNumber {
+        type ChildDependencies = ();
+        type NodeDependencies = ();
+        type ParentDependencies = (AddNumber,);
+
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            if let Some((parent,)) = parent {
+                self.0 += parent.0;
+            }
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self::default();
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    let mut tree: RealDom = RealDom::new([AddNumber::to_type_erased()]);
+    let grandchild1 = tree.create_node(create_blank_element());
+    let grandchild1 = grandchild1.id();
+    let mut child1 = tree.create_node(create_blank_element());
+    child1.add_child(grandchild1);
+    let child1 = child1.id();
+    let grandchild2 = tree.create_node(create_blank_element());
+    let grandchild2 = grandchild2.id();
+    let mut child2 = tree.create_node(create_blank_element());
+    child2.add_child(grandchild2);
+    let child2 = child2.id();
+    let mut parent = tree.get_mut(tree.root_id()).unwrap();
+    parent.add_child(child1);
+    parent.add_child(child2);
+
+    tree.update_state(SendAnyMap::new());
+
+    let root = tree.get(tree.root_id()).unwrap();
+    dbg!(root.id());
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(1)));
+
+    let child1 = tree.get(child1).unwrap();
+    dbg!(child1.id());
+    assert_eq!(child1.get().as_deref(), Some(&AddNumber(2)));
+
+    let grandchild1 = tree.get(grandchild1).unwrap();
+    assert_eq!(grandchild1.get().as_deref(), Some(&AddNumber(3)));
+
+    let child2 = tree.get(child2).unwrap();
+    assert_eq!(child2.get().as_deref(), Some(&AddNumber(2)));
+
+    let grandchild2 = tree.get(grandchild2).unwrap();
+    assert_eq!(grandchild2.get().as_deref(), Some(&AddNumber(3)));
+}
+
+#[test]
+fn up_pass() {
+    // Tree before:
+    // 1=\
+    //   1=\
+    //     1
+    //   1=\
+    //     1
+    // Tree after:
+    // 4=\
+    //   2=\
+    //     1
+    //   2=\
+    //     1
+
+    #[derive(Debug, Clone, PartialEq, Component)]
+    struct AddNumber(i32);
+
+    #[partial_derive_state]
+    impl State for AddNumber {
+        type ChildDependencies = (AddNumber,);
+        type NodeDependencies = ();
+        type ParentDependencies = ();
+
+        const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new();
+
+        fn update<'a>(
+            &mut self,
+            _: NodeView,
+            _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+            _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+            children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+            _: &SendAnyMap,
+        ) -> bool {
+            self.0 += children.iter().map(|(i,)| i.0).sum::<i32>();
+            true
+        }
+
+        fn create<'a>(
+            node_view: NodeView<()>,
+            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,
+        ) -> Self {
+            let mut myself = Self(1);
+            myself.update(node_view, node, parent, children, context);
+            myself
+        }
+    }
+
+    let mut tree: RealDom = RealDom::new([AddNumber::to_type_erased()]);
+    let grandchild1 = tree.create_node(create_blank_element());
+    let grandchild1 = grandchild1.id();
+    let mut child1 = tree.create_node(create_blank_element());
+    child1.add_child(grandchild1);
+    let child1 = child1.id();
+    let grandchild2 = tree.create_node(create_blank_element());
+    let grandchild2 = grandchild2.id();
+    let mut child2 = tree.create_node(create_blank_element());
+    child2.add_child(grandchild2);
+    let child2 = child2.id();
+    let mut parent = tree.get_mut(tree.root_id()).unwrap();
+    parent.add_child(child1);
+    parent.add_child(child2);
+
+    tree.update_state(SendAnyMap::new());
+
+    let root = tree.get(tree.root_id()).unwrap();
+    assert_eq!(root.get().as_deref(), Some(&AddNumber(5)));
+
+    let child1 = tree.get(child1).unwrap();
+    assert_eq!(child1.get().as_deref(), Some(&AddNumber(2)));
+
+    let grandchild1 = tree.get(grandchild1).unwrap();
+    assert_eq!(grandchild1.get().as_deref(), Some(&AddNumber(1)));
+
+    let child2 = tree.get(child2).unwrap();
+    assert_eq!(child2.get().as_deref(), Some(&AddNumber(2)));
+
+    let grandchild2 = tree.get(grandchild2).unwrap();
+    assert_eq!(grandchild2.get().as_deref(), Some(&AddNumber(1)));
+}

+ 2 - 0
packages/rink/.gitignore

@@ -0,0 +1,2 @@
+/target
+Cargo.lock

+ 2 - 0
packages/rink/.vscode/spellright.dict

@@ -0,0 +1,2 @@
+esque
+Tui

+ 39 - 0
packages/rink/Cargo.toml

@@ -0,0 +1,39 @@
+[package]
+name = "rink"
+version = "0.2.2"
+authors = ["Jonathan Kelley, @dementhos"]
+edition = "2021"
+description = "TUI-based renderer for Dioxus"
+repository = "https://github.com/DioxusLabs/dioxus/"
+homepage = "https://dioxuslabs.com"
+documentation = "https://dioxuslabs.com"
+keywords = ["dom", "ui", "gui", "react", "terminal"]
+license = "MIT/Apache-2.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+dioxus-html = { path = "../html", version = "^0.3.0" }
+dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
+dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.3.0" }
+
+tui = "0.17.0"
+crossterm = "0.23.0"
+anyhow = "1.0.42"
+tokio = { version = "1.15.0", features = ["full"] }
+futures = "0.3.19"
+taffy = "0.2.1"
+smallvec = "1.6"
+rustc-hash = "1.1.0"
+anymap = "1.0.0-beta.2"
+futures-channel = "0.3.25"
+shipyard = { version = "0.6.2", features = ["proc", "std"], default-features = false }
+once_cell = "1.17.1"
+
+[dev-dependencies]
+tokio = { version = "1" }
+criterion = "0.3.5"
+
+[features]
+default = []
+parallel = ["shipyard/parallel"]

+ 74 - 0
packages/rink/README.md

@@ -0,0 +1,74 @@
+<div align="center">
+  <h1>Rink</h1>
+  <p>
+    <strong>A beautiful terminal user interfaces library in Rust.</strong>
+  </p>
+</div>
+
+<div align="center">
+  <!-- Crates version -->
+  <a href="https://crates.io/crates/rink">
+    <img src="https://img.shields.io/crates/v/rink.svg?style=flat-square"
+    alt="Crates.io version" />
+  </a>
+  <!-- Downloads -->
+  <a href="https://crates.io/crates/rink">
+    <img src="https://img.shields.io/crates/d/rink.svg?style=flat-square"
+      alt="Download" />
+  </a>
+  <!-- docs -->
+  <a href="https://docs.rs/rink">
+    <img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
+      alt="docs.rs docs" />
+  </a>
+  <!-- CI -->
+  <a href="https://github.com/jkelleyrtp/rink/actions">
+    <img src="https://github.com/dioxuslabs/rink/actions/workflows/main.yml/badge.svg"
+      alt="CI status" />
+  </a>
+  <!-- Discord -->
+  <a href="https://discord.gg/XgGxMSkvUM">
+    <img src="https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square" alt="Discord Link" />
+  </a>
+</div>
+
+<br/>
+
+Leverage CSS, HTML, and Rust to build beautiful, portable, terminal user interfaces. Rink is the cross-framework library that powers [`Dioxus-TUI`](https://github.com/DioxusLabs/dioxus/tree/master/packages/dioxus-tui)
+
+![demo app](examples/example.png)
+
+## Background
+
+You can use Html-like semantics with inline styles, tree hierarchy, components, and more in your [`text-based user interface (TUI)`](https://en.wikipedia.org/wiki/Text-based_user_interface) application.
+
+Rink is essentially a port of [Ink](https://github.com/vadimdemedes/ink) but for [`Rust`](https://www.rust-lang.org/). Rink doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.
+
+## Limitations
+
+- **Subset of Html**
+  Terminals can only render a subset of HTML. We support as much as we can.
+- **Particular frontend design**
+  Terminals and browsers are and look different. Therefore, the same design might not be the best to cover both renderers.
+
+## Status
+
+**WARNING: Rink is currently under construction!**
+
+Rendering a Dom works fine, but the ecosystem of widgets is not ready yet. Additionally, some bugs in the flexbox implementation might be quirky at times.
+
+## Features
+
+Rink features:
+
+- [x] Flexbox-based layout system
+- [ ] CSS selectors
+- [x] inline CSS support
+- [x] Built-in focusing system
+
+* [ ] Widgets
+* [ ] Support for events, hooks, and callbacks<sup>1</sup>
+* [ ] Html tags<sup>2</sup>
+
+<sup>1</sup> Basic keyboard, mouse, and focus events are implemented.
+<sup>2</sup> Currently, most HTML tags don't translate into any meaning inside of Rink. So an `input` _element_ won't mean anything nor does it have any additional functionality.

+ 104 - 0
packages/rink/examples/counter.rs

@@ -0,0 +1,104 @@
+use dioxus_html::EventData;
+use dioxus_native_core::{
+    node::TextNode,
+    prelude::*,
+    real_dom::{NodeImmutable, NodeTypeMut},
+    NodeId,
+};
+use rink::{render, Config, Driver};
+use std::rc::Rc;
+use std::sync::{Arc, RwLock};
+
+#[derive(Default)]
+struct Counter {
+    count: usize,
+    counter_id: NodeId,
+    button_id: NodeId,
+}
+
+impl Counter {
+    fn create(mut root: NodeMut) -> Self {
+        let mut myself = Self::default();
+
+        let root_id = root.id();
+        let rdom = root.real_dom_mut();
+
+        // create the counter
+        let count = myself.count;
+        myself.counter_id = rdom
+            .create_node(NodeType::Text(TextNode::new(count.to_string())))
+            .id();
+        let mut button = rdom.create_node(NodeType::Element(ElementNode {
+            tag: "div".to_string(),
+            attributes: [
+                ("display".to_string().into(), "flex".to_string().into()),
+                (
+                    ("background-color", "style").into(),
+                    format!("rgb({}, {}, {})", count * 10, 0, 0,).into(),
+                ),
+                (("width", "style").into(), "100%".to_string().into()),
+                (("height", "style").into(), "100%".to_string().into()),
+                (("flex-direction", "style").into(), "row".to_string().into()),
+                (
+                    ("justify-content", "style").into(),
+                    "center".to_string().into(),
+                ),
+                (("align-items", "style").into(), "center".to_string().into()),
+            ]
+            .into_iter()
+            .collect(),
+            ..Default::default()
+        }));
+        button.add_event_listener("click");
+        button.add_event_listener("wheel");
+        button.add_child(myself.counter_id);
+        myself.button_id = button.id();
+        rdom.get_mut(root_id).unwrap().add_child(myself.button_id);
+
+        myself
+    }
+}
+
+impl Driver for Counter {
+    fn update(&mut self, rdom: &Arc<RwLock<RealDom>>) {
+        // update the counter
+        let mut rdom = rdom.write().unwrap();
+        let mut node = rdom.get_mut(self.button_id).unwrap();
+        if let NodeTypeMut::Element(mut el) = node.node_type_mut() {
+            el.set_attribute(
+                ("background-color", "style"),
+                format!("rgb({}, {}, {})", self.count * 10, 0, 0,),
+            );
+        }
+        let mut text = rdom.get_mut(self.counter_id).unwrap();
+        let type_mut = text.node_type_mut();
+        if let NodeTypeMut::Text(mut text) = type_mut {
+            *text = self.count.to_string();
+        }
+    }
+
+    fn handle_event(
+        &mut self,
+        _: &Arc<RwLock<RealDom>>,
+        _: NodeId,
+        _: &str,
+        _: Rc<EventData>,
+        _: bool,
+    ) {
+        // when a click or wheel event is fired, increment the counter
+        self.count += 1;
+    }
+
+    fn poll_async(&mut self) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + '_>> {
+        Box::pin(async move { tokio::time::sleep(std::time::Duration::from_millis(1000)).await })
+    }
+}
+
+fn main() {
+    render(Config::new(), |rdom, _, _| {
+        let mut rdom = rdom.write().unwrap();
+        let root = rdom.root_id();
+        Counter::create(rdom.get_mut(root).unwrap())
+    })
+    .unwrap();
+}

BIN
packages/rink/examples/example.png


+ 173 - 0
packages/rink/examples/grid.rs

@@ -0,0 +1,173 @@
+use dioxus_html::EventData;
+use dioxus_native_core::{
+    node::TextNode,
+    prelude::*,
+    real_dom::{NodeImmutable, NodeTypeMut},
+    NodeId,
+};
+use rink::{render, Config, Driver};
+use rustc_hash::FxHashSet;
+use std::rc::Rc;
+use std::sync::{Arc, RwLock};
+
+const SIZE: usize = 20;
+
+#[derive(Default, Clone, Copy)]
+struct Node {
+    container_id: Option<NodeId>,
+    text_id: Option<NodeId>,
+    count: usize,
+}
+
+struct Test {
+    node_states: [[Node; SIZE]; SIZE],
+    dirty: FxHashSet<(usize, usize)>,
+}
+
+impl Default for Test {
+    fn default() -> Self {
+        Self {
+            node_states: [[Node {
+                container_id: None,
+                text_id: None,
+                count: 0,
+            }; SIZE]; SIZE],
+            dirty: FxHashSet::default(),
+        }
+    }
+}
+
+impl Test {
+    fn create(mut root: NodeMut) -> Self {
+        let mut myself = Self::default();
+
+        // Set the root node to be a flexbox with a column direction.
+        if let NodeTypeMut::Element(mut el) = root.node_type_mut() {
+            el.set_attribute("display".to_string(), "flex".to_string());
+            el.set_attribute(("flex-direction", "style"), "column".to_string());
+            el.set_attribute(("width", "style"), "100%".to_string());
+            el.set_attribute(("height", "style"), "100%".to_string());
+        }
+
+        let root_id = root.id();
+        let rdom = root.real_dom_mut();
+
+        // create the grid
+        for (x, row) in myself.node_states.iter_mut().enumerate() {
+            let row_node = rdom
+                .create_node(NodeType::Element(ElementNode {
+                    tag: "div".to_string(),
+                    attributes: [
+                        ("display".to_string().into(), "flex".to_string().into()),
+                        (("flex-direction", "style").into(), "row".to_string().into()),
+                        (("width", "style").into(), "100%".to_string().into()),
+                        (("height", "style").into(), "100%".to_string().into()),
+                    ]
+                    .into_iter()
+                    .collect(),
+                    ..Default::default()
+                }))
+                .id();
+            for (y, node) in row.iter_mut().enumerate() {
+                let count = node.count;
+                let id = rdom
+                    .create_node(NodeType::Text(TextNode::new(count.to_string())))
+                    .id();
+                let mut button = rdom.create_node(NodeType::Element(ElementNode {
+                    tag: "div".to_string(),
+                    attributes: [
+                        ("display".to_string().into(), "flex".to_string().into()),
+                        (
+                            ("background-color", "style").into(),
+                            format!("rgb({}, {}, {})", count * 10, 0, (x + y),).into(),
+                        ),
+                        (("width", "style").into(), "100%".to_string().into()),
+                        (("height", "style").into(), "100%".to_string().into()),
+                        (("flex-direction", "style").into(), "row".to_string().into()),
+                        (
+                            ("justify-content", "style").into(),
+                            "center".to_string().into(),
+                        ),
+                        (("align-items", "style").into(), "center".to_string().into()),
+                    ]
+                    .into_iter()
+                    .collect(),
+                    ..Default::default()
+                }));
+                button.add_event_listener("click");
+                button.add_event_listener("wheel");
+                button.add_child(id);
+                let button_id = button.id();
+                rdom.get_mut(row_node).unwrap().add_child(button_id);
+                node.container_id = Some(button_id);
+                node.text_id = Some(id);
+            }
+            rdom.get_mut(root_id).unwrap().add_child(row_node);
+        }
+        myself
+    }
+}
+
+impl Driver for Test {
+    fn update(&mut self, rdom: &Arc<RwLock<RealDom>>) {
+        let mut rdom = rdom.write().unwrap();
+        for (x, y) in self.dirty.drain() {
+            let node = self.node_states[x][y];
+            let node_id = node.container_id.unwrap();
+            let mut container = rdom.get_mut(node_id).unwrap();
+            if let NodeTypeMut::Element(mut el) = container.node_type_mut() {
+                el.set_attribute(
+                    ("background-color", "style"),
+                    format!("rgb({}, {}, {})", node.count * 10, 0, (x + y),),
+                );
+            }
+            let text_id = node.text_id.unwrap();
+            let mut text = rdom.get_mut(text_id).unwrap();
+            let type_mut = text.node_type_mut();
+            if let NodeTypeMut::Text(mut text) = type_mut {
+                *text = node.count.to_string();
+            }
+        }
+    }
+
+    fn handle_event(
+        &mut self,
+        rdom: &Arc<RwLock<RealDom>>,
+        id: NodeId,
+        _: &str,
+        _: Rc<EventData>,
+        _: bool,
+    ) {
+        let rdom = rdom.read().unwrap();
+        let node = rdom.get(id).unwrap();
+        if let Some(parent) = node.parent() {
+            let child_number = parent
+                .child_ids()
+                .iter()
+                .position(|id| *id == node.id())
+                .unwrap();
+            if let Some(parents_parent) = parent.parent() {
+                let parents_child_number = parents_parent
+                    .child_ids()
+                    .iter()
+                    .position(|id| *id == parent.id())
+                    .unwrap();
+                self.node_states[parents_child_number][child_number].count += 1;
+                self.dirty.insert((parents_child_number, child_number));
+            }
+        }
+    }
+
+    fn poll_async(&mut self) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + '_>> {
+        Box::pin(async move { tokio::time::sleep(std::time::Duration::from_millis(1000)).await })
+    }
+}
+
+fn main() {
+    render(Config::new(), |rdom, _, _| {
+        let mut rdom = rdom.write().unwrap();
+        let root = rdom.root_id();
+        Test::create(rdom.get_mut(root).unwrap())
+    })
+    .unwrap();
+}

+ 0 - 0
packages/tui/src/config.rs → packages/rink/src/config.rs


+ 81 - 78
packages/tui/src/focus.rs → packages/rink/src/focus.rs

@@ -1,18 +1,23 @@
-use crate::{node::PreventDefault, TuiDom};
+use crate::prevent_default::PreventDefault;
 
 use dioxus_native_core::{
-    tree::TreeView,
-    utils::{ElementProduced, PersistantElementIter},
-    RealNodeId,
+    node_ref::{AttributeMaskBuilder, NodeMaskBuilder},
+    prelude::*,
+    real_dom::NodeImmutable,
+    utils::{IteratorMovement, PersistantElementIter},
 };
-use dioxus_native_core_macro::sorted_str_slice;
+use dioxus_native_core_macro::partial_derive_state;
+use once_cell::sync::Lazy;
+use rustc_hash::FxHashSet;
+use shipyard::Component;
+use shipyard::{Get, ViewMut};
 
 use std::{cmp::Ordering, num::NonZeroU16};
 
-use dioxus_native_core::{
-    node_ref::{AttributeMask, NodeMask, NodeView},
-    state::NodeDepState,
-};
+use dioxus_native_core::node_ref::NodeView;
+
+#[derive(Component)]
+pub struct Focused(pub bool);
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
 pub(crate) enum FocusLevel {
@@ -54,20 +59,31 @@ impl Ord for FocusLevel {
     }
 }
 
-#[derive(Clone, PartialEq, Debug, Default)]
+#[derive(Clone, PartialEq, Debug, Default, Component)]
 pub(crate) struct Focus {
     pub level: FocusLevel,
 }
 
-impl NodeDepState for Focus {
-    type DepState = ();
-    type Ctx = ();
-    const NODE_MASK: NodeMask =
-        NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
+#[partial_derive_state]
+impl State for Focus {
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+        .with_attrs(AttributeMaskBuilder::Some(FOCUS_ATTRIBUTES))
+        .with_listeners();
 
-    fn reduce(&mut self, node: NodeView<'_>, _sibling: (), _: &Self::Ctx) -> bool {
+    type ParentDependencies = ();
+    type ChildDependencies = ();
+    type NodeDependencies = ();
+
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView,
+        _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: &SendAnyMap,
+    ) -> bool {
         let new = Focus {
-            level: if let Some(a) = node
+            level: if let Some(a) = node_view
                 .attributes()
                 .and_then(|mut a| a.find(|a| a.attribute.name == "tabindex"))
             {
@@ -86,12 +102,10 @@ impl NodeDepState for Focus {
                 } else {
                     FocusLevel::Unfocusable
                 }
-            } else if node
+            } else if node_view
                 .listeners()
                 .and_then(|mut listeners| {
-                    listeners
-                        .any(|l| FOCUS_EVENTS.binary_search(&l).is_ok())
-                        .then_some(())
+                    listeners.any(|l| FOCUS_EVENTS.contains(&l)).then_some(())
                 })
                 .is_some()
             {
@@ -107,24 +121,48 @@ impl NodeDepState for Focus {
             false
         }
     }
+
+    fn create<'a>(
+        node_view: NodeView<()>,
+        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,
+    ) -> Self {
+        let mut myself = Self::default();
+        myself.update(node_view, node, parent, children, context);
+        myself
+    }
 }
 
-const FOCUS_EVENTS: &[&str] = &sorted_str_slice!(["keydown", "keypress", "keyup"]);
-const FOCUS_ATTRIBUTES: &[&str] = &sorted_str_slice!(["tabindex"]);
+static FOCUS_EVENTS: Lazy<FxHashSet<&str>> =
+    Lazy::new(|| ["keydown", "keypress", "keyup"].into_iter().collect());
+const FOCUS_ATTRIBUTES: &[&str] = &["tabindex"];
 
-#[derive(Default)]
 pub(crate) struct FocusState {
     pub(crate) focus_iter: PersistantElementIter,
-    pub(crate) last_focused_id: Option<RealNodeId>,
+    pub(crate) last_focused_id: Option<NodeId>,
     pub(crate) focus_level: FocusLevel,
     pub(crate) dirty: bool,
 }
 
 impl FocusState {
+    pub fn create(rdom: &mut RealDom) -> Self {
+        let focus_iter = PersistantElementIter::create(rdom);
+        Self {
+            focus_iter,
+            last_focused_id: Default::default(),
+            focus_level: Default::default(),
+            dirty: Default::default(),
+        }
+    }
+
     /// Returns true if the focus has changed.
-    pub fn progress(&mut self, rdom: &mut TuiDom, forward: bool) -> bool {
+    pub fn progress(&mut self, rdom: &mut RealDom, forward: bool) -> bool {
         if let Some(last) = self.last_focused_id {
-            if rdom[last].state.prevent_default == PreventDefault::KeyDown {
+            if rdom.get(last).unwrap().get::<PreventDefault>().map(|p| *p)
+                == Some(PreventDefault::KeyDown)
+            {
                 return false;
             }
         }
@@ -140,13 +178,13 @@ impl FocusState {
                 self.focus_iter.prev(rdom)
             };
             let new_id = new.id();
-            if let ElementProduced::Looped(_) = new {
+            if let IteratorMovement::Looped = new.movement() {
                 let mut closest_level = None;
 
                 if forward {
                     // find the closest focusable element after the current level
                     rdom.traverse_depth_first(|n| {
-                        let node_level = n.state.focus.level;
+                        let node_level = n.get::<Focus>().unwrap().level;
                         if node_level != *focus_level
                             && node_level.focusable()
                             && node_level > *focus_level
@@ -163,7 +201,7 @@ impl FocusState {
                 } else {
                     // find the closest focusable element before the current level
                     rdom.traverse_depth_first(|n| {
-                        let node_level = n.state.focus.level;
+                        let node_level = n.get::<Focus>().unwrap().level;
                         if node_level != *focus_level
                             && node_level.focusable()
                             && node_level < *focus_level
@@ -200,7 +238,7 @@ impl FocusState {
                 loop_marker_id = Some(new_id);
             }
 
-            let current_level = rdom[new_id].state.focus.level;
+            let current_level = rdom.get(new_id).unwrap().get::<Focus>().unwrap().level;
             let after_previous_focused = if forward {
                 current_level >= *focus_level
             } else {
@@ -214,12 +252,15 @@ impl FocusState {
         }
 
         if let Some(id) = next_focus {
-            if !rdom[id].state.focus.level.focusable() {
+            let mut node = rdom.get_mut(id).unwrap();
+            if !node.get::<Focus>().unwrap().level.focusable() {
                 panic!()
             }
-            rdom[id].state.focused = true;
+            node.insert(Focused(true));
             if let Some(old) = self.last_focused_id.replace(id) {
-                rdom[old].state.focused = false;
+                let mut focused_borrow: ViewMut<Focused> = rdom.raw_world().borrow().unwrap();
+                let focused = (&mut focused_borrow).get(old).unwrap();
+                focused.0 = false;
             }
             // reset the position to the currently focused element
             while self.focus_iter.next(rdom).id() != id {}
@@ -230,52 +271,14 @@ impl FocusState {
         false
     }
 
-    pub(crate) fn prune(&mut self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) {
-        fn remove_children(
-            to_prune: &mut [&mut Option<RealNodeId>],
-            rdom: &TuiDom,
-            removed: RealNodeId,
-        ) {
-            for opt in to_prune.iter_mut() {
-                if let Some(id) = opt {
-                    if *id == removed {
-                        **opt = None;
-                    }
-                }
-            }
-            if let Some(children) = &rdom.children_ids(removed) {
-                for child in *children {
-                    remove_children(to_prune, rdom, *child);
-                }
-            }
-        }
-        if self.focus_iter.prune(mutations, rdom) {
-            self.dirty = true;
-        }
-        for m in &mutations.edits {
-            match m {
-                dioxus_core::Mutation::ReplaceWith { id, .. } => remove_children(
-                    &mut [&mut self.last_focused_id],
-                    rdom,
-                    rdom.element_to_node_id(*id),
-                ),
-                dioxus_core::Mutation::Remove { id } => remove_children(
-                    &mut [&mut self.last_focused_id],
-                    rdom,
-                    rdom.element_to_node_id(*id),
-                ),
-                _ => (),
-            }
-        }
-    }
-
-    pub(crate) fn set_focus(&mut self, rdom: &mut TuiDom, id: RealNodeId) {
+    pub(crate) fn set_focus(&mut self, rdom: &mut RealDom, id: NodeId) {
         if let Some(old) = self.last_focused_id.replace(id) {
-            rdom[old].state.focused = false;
+            let mut node = rdom.get_mut(old).unwrap();
+            node.insert(Focused(false));
         }
-        let state = &mut rdom[id].state;
-        state.focused = true;
-        self.focus_level = state.focus.level;
+        let mut node = rdom.get_mut(id).unwrap();
+        node.insert(Focused(true));
+        self.focus_level = node.get::<Focus>().unwrap().level;
         // reset the position to the currently focused element
         while self.focus_iter.next(rdom).id() != id {}
         self.dirty = true;

+ 108 - 151
packages/tui/src/hooks.rs → packages/rink/src/hooks.rs

@@ -1,9 +1,8 @@
 use crossterm::event::{
     Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
 };
-use dioxus_core::*;
-use dioxus_native_core::tree::TreeView;
-use dioxus_native_core::NodeId;
+use dioxus_native_core::prelude::*;
+use dioxus_native_core::real_dom::NodeImmutable;
 use rustc_hash::{FxHashMap, FxHashSet};
 
 use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
@@ -13,9 +12,8 @@ use dioxus_html::geometry::{
 use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
 use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
 use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
-use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData};
+use dioxus_html::{event_bubbles, EventData, FocusData, KeyboardData, MouseData, WheelData};
 use std::{
-    any::Any,
     cell::{RefCell, RefMut},
     rc::Rc,
     time::{Duration, Instant},
@@ -23,91 +21,37 @@ use std::{
 use taffy::geometry::{Point, Size};
 use taffy::{prelude::Layout, Taffy};
 
+use crate::focus::{Focus, Focused};
+use crate::layout::TaffyLayout;
 use crate::{layout_to_screen_space, FocusState};
-use crate::{TuiDom, TuiNode};
 
 pub(crate) struct Event {
-    pub id: ElementId,
+    pub id: NodeId,
     pub name: &'static str,
-    pub data: Rc<dyn Any>,
+    pub data: Rc<EventData>,
     pub bubbles: bool,
 }
 
-// a wrapper around the input state for easier access
-// todo: fix loop
-// pub struct InputState(Rc<Rc<RefCell<InnerInputState>>>);
-// impl InputState {
-//     pub fn get(cx: &ScopeState) -> InputState {
-//         let inner = cx
-//             .consume_context::<Rc<RefCell<InnerInputState>>>()
-//             .expect("Rink InputState can only be used in Rink apps!");
-//         (**inner).borrow_mut().subscribe(cx.schedule_update());
-//         InputState(inner)
-//     }
-
-//     pub fn mouse(&self) -> Option<MouseData> {
-//         let data = (**self.0).borrow();
-//         mouse.as_ref().map(|m| m.clone())
-//     }
-
-//     pub fn wheel(&self) -> Option<WheelData> {
-//         let data = (**self.0).borrow();
-//         wheel.as_ref().map(|w| w.clone())
-//     }
-
-//     pub fn screen(&self) -> Option<(u16, u16)> {
-//         let data = (**self.0).borrow();
-//         screen.as_ref().map(|m| m.clone())
-//     }
-
-//     pub fn last_key_pressed(&self) -> Option<KeyboardData> {
-//         let data = (**self.0).borrow();
-//         last_key_pressed
-//             .as_ref()
-//             .map(|k| &k.0.clone())
-//     }
-// }
-
 type EventCore = (&'static str, EventData);
 
-#[derive(Debug)]
-enum EventData {
-    Mouse(MouseData),
-    Wheel(WheelData),
-    Screen((u16, u16)),
-    Keyboard(KeyboardData),
-}
-impl EventData {
-    fn into_any(self) -> Rc<dyn Any + Send + Sync> {
-        match self {
-            Self::Mouse(m) => Rc::new(m),
-            Self::Wheel(w) => Rc::new(w),
-            Self::Screen(s) => Rc::new(s),
-            Self::Keyboard(k) => Rc::new(k),
-        }
-    }
-}
-
 const MAX_REPEAT_TIME: Duration = Duration::from_millis(100);
 
 pub struct InnerInputState {
     mouse: Option<MouseData>,
     wheel: Option<WheelData>,
     last_key_pressed: Option<(KeyboardData, Instant)>,
-    screen: Option<(u16, u16)>,
     pub(crate) focus_state: FocusState,
     // subscribers: Vec<Rc<dyn Fn() + 'static>>,
 }
 
 impl InnerInputState {
-    fn new() -> Self {
+    fn create(rdom: &mut RealDom) -> Self {
         Self {
             mouse: None,
             wheel: None,
             last_key_pressed: None,
-            screen: None,
             // subscribers: Vec::new(),
-            focus_state: FocusState::default(),
+            focus_state: FocusState::create(rdom),
         }
     }
 
@@ -148,7 +92,6 @@ impl InnerInputState {
                 *m = new_mouse_data;
             }
             EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
-            EventData::Screen(ref s) => self.screen = Some(*s),
             EventData::Keyboard(ref mut k) => {
                 let is_repeating = self
                     .last_key_pressed
@@ -165,6 +108,7 @@ impl InnerInputState {
 
                 self.last_key_pressed = Some((k.clone(), Instant::now()));
             }
+            _ => {}
         }
     }
 
@@ -173,7 +117,7 @@ impl InnerInputState {
         evts: &mut Vec<EventCore>,
         resolved_events: &mut Vec<Event>,
         layout: &Taffy,
-        dom: &mut TuiDom,
+        dom: &mut RealDom,
     ) {
         let previous_mouse = self.mouse.clone();
 
@@ -200,32 +144,26 @@ impl InnerInputState {
         if old_focus != self.focus_state.last_focused_id {
             // elements with listeners will always have a element id
             if let Some(id) = self.focus_state.last_focused_id {
-                let element = dom.tree.get(id).unwrap();
-                if let Some(id) = element.node_data.element_id {
-                    resolved_events.push(Event {
-                        name: "focus",
-                        id,
-                        data: Rc::new(FocusData {}),
-                        bubbles: event_bubbles("focus"),
-                    });
-                    resolved_events.push(Event {
-                        name: "focusin",
-                        id,
-                        data: Rc::new(FocusData {}),
-                        bubbles: event_bubbles("focusin"),
-                    });
-                }
+                resolved_events.push(Event {
+                    name: "focus",
+                    id,
+                    data: Rc::new(EventData::Focus(FocusData {})),
+                    bubbles: event_bubbles("focus"),
+                });
+                resolved_events.push(Event {
+                    name: "focusin",
+                    id,
+                    data: Rc::new(EventData::Focus(FocusData {})),
+                    bubbles: event_bubbles("focusin"),
+                });
             }
             if let Some(id) = old_focus {
-                let element = dom.tree.get(id).unwrap();
-                if let Some(id) = element.node_data.element_id {
-                    resolved_events.push(Event {
-                        name: "focusout",
-                        id,
-                        data: Rc::new(FocusData {}),
-                        bubbles: event_bubbles("focusout"),
-                    });
-                }
+                resolved_events.push(Event {
+                    name: "focusout",
+                    id,
+                    data: Rc::new(EventData::Focus(FocusData {})),
+                    bubbles: event_bubbles("focusout"),
+                });
             }
         }
 
@@ -239,7 +177,7 @@ impl InnerInputState {
         previous_mouse: Option<MouseData>,
         resolved_events: &mut Vec<Event>,
         layout: &Taffy,
-        dom: &mut TuiDom,
+        dom: &mut RealDom,
     ) {
         fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
             let Point { x, y } = layout.location;
@@ -259,29 +197,27 @@ impl InnerInputState {
 
         fn try_create_event(
             name: &'static str,
-            data: Rc<dyn Any>,
+            data: Rc<EventData>,
             will_bubble: &mut FxHashSet<NodeId>,
             resolved_events: &mut Vec<Event>,
-            node: &TuiNode,
-            dom: &TuiDom,
+            node: NodeRef,
+            dom: &RealDom,
         ) {
             // only trigger event if the event was not triggered already by a child
-            let id = node.node_data.node_id;
+            let id = node.id();
             if will_bubble.insert(id) {
-                let mut parent = dom.parent(id);
+                let mut parent = Some(node);
                 while let Some(current_parent) = parent {
-                    let parent_id = current_parent.node_data.node_id;
+                    let parent_id = current_parent.id();
                     will_bubble.insert(parent_id);
-                    parent = dom.parent(parent_id);
-                }
-                if let Some(id) = node.mounted_id() {
-                    resolved_events.push(Event {
-                        name,
-                        id,
-                        data,
-                        bubbles: event_bubbles(name),
-                    })
+                    parent = current_parent.parent_id().and_then(|id| dom.get(id));
                 }
+                resolved_events.push(Event {
+                    name,
+                    id,
+                    data,
+                    bubbles: event_bubbles(name),
+                })
             }
         }
 
@@ -345,7 +281,10 @@ impl InnerInputState {
                         if currently_contains && previously_contained {
                             try_create_event(
                                 "mousemove",
-                                Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                                Rc::new(EventData::Mouse(prepare_mouse_data(
+                                    mouse_data,
+                                    &node_layout,
+                                ))),
                                 &mut will_bubble,
                                 resolved_events,
                                 node,
@@ -369,7 +308,7 @@ impl InnerInputState {
                     if currently_contains && !previously_contained {
                         try_create_event(
                             "mouseenter",
-                            Rc::new(mouse_data.clone()),
+                            Rc::new(dioxus_html::EventData::Mouse(mouse_data.clone())),
                             &mut will_bubble,
                             resolved_events,
                             node,
@@ -392,7 +331,10 @@ impl InnerInputState {
                     if currently_contains && !previously_contained {
                         try_create_event(
                             "mouseover",
-                            Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                            Rc::new(EventData::Mouse(prepare_mouse_data(
+                                mouse_data,
+                                &node_layout,
+                            ))),
                             &mut will_bubble,
                             resolved_events,
                             node,
@@ -412,7 +354,10 @@ impl InnerInputState {
                     if currently_contains {
                         try_create_event(
                             "mousedown",
-                            Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                            Rc::new(EventData::Mouse(prepare_mouse_data(
+                                mouse_data,
+                                &node_layout,
+                            ))),
                             &mut will_bubble,
                             resolved_events,
                             node,
@@ -433,7 +378,10 @@ impl InnerInputState {
                         if currently_contains {
                             try_create_event(
                                 "mouseup",
-                                Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                                Rc::new(EventData::Mouse(prepare_mouse_data(
+                                    mouse_data,
+                                    &node_layout,
+                                ))),
                                 &mut will_bubble,
                                 resolved_events,
                                 node,
@@ -455,7 +403,10 @@ impl InnerInputState {
                         if currently_contains {
                             try_create_event(
                                 "click",
-                                Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                                Rc::new(EventData::Mouse(prepare_mouse_data(
+                                    mouse_data,
+                                    &node_layout,
+                                ))),
                                 &mut will_bubble,
                                 resolved_events,
                                 node,
@@ -478,7 +429,10 @@ impl InnerInputState {
                         if currently_contains {
                             try_create_event(
                                 "contextmenu",
-                                Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                                Rc::new(EventData::Mouse(prepare_mouse_data(
+                                    mouse_data,
+                                    &node_layout,
+                                ))),
                                 &mut will_bubble,
                                 resolved_events,
                                 node,
@@ -502,7 +456,7 @@ impl InnerInputState {
                             if currently_contains {
                                 try_create_event(
                                     "wheel",
-                                    Rc::new(w.clone()),
+                                    Rc::new(EventData::Wheel(w.clone())),
                                     &mut will_bubble,
                                     resolved_events,
                                     node,
@@ -527,7 +481,10 @@ impl InnerInputState {
                     if !currently_contains && previously_contained {
                         try_create_event(
                             "mouseleave",
-                            Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                            Rc::new(EventData::Mouse(prepare_mouse_data(
+                                mouse_data,
+                                &node_layout,
+                            ))),
                             &mut will_bubble,
                             resolved_events,
                             node,
@@ -550,7 +507,10 @@ impl InnerInputState {
                     if !currently_contains && previously_contained {
                         try_create_event(
                             "mouseout",
-                            Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
+                            Rc::new(EventData::Mouse(prepare_mouse_data(
+                                mouse_data,
+                                &node_layout,
+                            ))),
                             &mut will_bubble,
                             resolved_events,
                             node,
@@ -564,11 +524,13 @@ impl InnerInputState {
             if was_released {
                 let mut focus_id = None;
                 dom.traverse_depth_first(|node| {
-                    let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
+                    let node_layout = layout
+                        .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
+                        .unwrap();
                     let currently_contains = layout_contains_point(node_layout, new_pos);
 
-                    if currently_contains && node.state.focus.level.focusable() {
-                        focus_id = Some(node.node_data.node_id);
+                    if currently_contains && node.get::<Focus>().unwrap().level.focusable() {
+                        focus_id = Some(node.id());
                     }
                 });
                 if let Some(id) = focus_id {
@@ -583,13 +545,18 @@ impl InnerInputState {
     // }
 }
 
-fn get_abs_layout(node: &TuiNode, dom: &TuiDom, taffy: &Taffy) -> Layout {
-    let mut node_layout = *taffy.layout(node.state.layout.node.unwrap()).unwrap();
+fn get_abs_layout(node: NodeRef, dom: &RealDom, taffy: &Taffy) -> Layout {
+    let mut node_layout = *taffy
+        .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
+        .unwrap();
     let mut current = node;
 
-    while let Some(parent) = dom.parent(current.node_data.node_id) {
+    while let Some(parent) = current.parent_id() {
+        let parent = dom.get(parent).unwrap();
         current = parent;
-        let parent_layout = taffy.layout(parent.state.layout.node.unwrap()).unwrap();
+        let parent_layout = taffy
+            .layout(parent.get::<TaffyLayout>().unwrap().node.unwrap())
+            .unwrap();
         node_layout.location.x += parent_layout.location.x;
         node_layout.location.y += parent_layout.location.y;
     }
@@ -604,11 +571,7 @@ pub struct RinkInputHandler {
 impl RinkInputHandler {
     /// global context that handles events
     /// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
-    pub fn new() -> (
-        Self,
-        Rc<RefCell<InnerInputState>>,
-        impl FnMut(crossterm::event::Event),
-    ) {
+    pub fn create(rdom: &mut RealDom) -> (Self, impl FnMut(crossterm::event::Event)) {
         let queued_events = Rc::new(RefCell::new(Vec::new()));
         let queued_events2 = Rc::downgrade(&queued_events);
 
@@ -620,23 +583,18 @@ impl RinkInputHandler {
             }
         };
 
-        let state = Rc::new(RefCell::new(InnerInputState::new()));
+        let state = Rc::new(RefCell::new(InnerInputState::create(rdom)));
 
         (
             Self {
-                state: state.clone(),
+                state,
                 queued_events,
             },
-            state,
             regester_event,
         )
     }
 
-    pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) {
-        self.state.borrow_mut().focus_state.prune(mutations, rdom);
-    }
-
-    pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut TuiDom) -> Vec<Event> {
+    pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut RealDom) -> Vec<Event> {
         let mut resolved_events = Vec::new();
 
         (*self.state).borrow_mut().update(
@@ -667,28 +625,27 @@ impl RinkInputHandler {
                 ]
                 .contains(&e.0)
             })
-            .map(|evt| (evt.0, evt.1.into_any()));
+            .map(|evt| (evt.0, evt.1));
 
-        let mut hm: FxHashMap<&'static str, Vec<Rc<dyn Any + Send + Sync>>> = FxHashMap::default();
+        let mut hm: FxHashMap<&'static str, Vec<Rc<EventData>>> = FxHashMap::default();
         for (event, data) in events {
             if let Some(v) = hm.get_mut(event) {
-                v.push(data);
+                v.push(Rc::new(data));
             } else {
-                hm.insert(event, vec![data]);
+                hm.insert(event, vec![Rc::new(data)]);
             }
         }
         for (event, datas) in hm {
             for node in dom.get_listening_sorted(event) {
                 for data in &datas {
-                    if node.state.focused {
-                        if let Some(id) = node.mounted_id() {
-                            resolved_events.push(Event {
-                                name: event,
-                                id,
-                                data: data.clone(),
-                                bubbles: event_bubbles(event),
-                            });
-                        }
+                    let focused = node.get::<Focused>();
+                    if focused.is_some() && focused.unwrap().0 {
+                        resolved_events.push(Event {
+                            name: event,
+                            id: node.id(),
+                            data: data.clone(),
+                            bubbles: event_bubbles(event),
+                        });
                     }
                 }
             }
@@ -770,7 +727,7 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
                 MouseEventKind::ScrollUp => ("wheel", get_wheel_data(true)),
             }
         }
-        TermEvent::Resize(x, y) => ("resize", EventData::Screen((x, y))),
+        _ => return None,
     };
 
     Some((name, data))

+ 41 - 30
packages/tui/src/layout.rs → packages/rink/src/layout.rs

@@ -1,12 +1,13 @@
 use std::sync::{Arc, Mutex};
 
+use dioxus_native_core::exports::shipyard::Component;
 use dioxus_native_core::layout_attributes::{
     apply_layout_attributes_cfg, BorderWidths, LayoutConfigeration,
 };
 use dioxus_native_core::node::OwnedAttributeView;
-use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView};
-use dioxus_native_core::state::ChildDepState;
-use dioxus_native_core_macro::sorted_str_slice;
+use dioxus_native_core::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView};
+use dioxus_native_core::prelude::*;
+use dioxus_native_core_macro::partial_derive_state;
 use taffy::prelude::*;
 
 use crate::{screen_to_layout_space, unit_to_layout_space};
@@ -16,6 +17,7 @@ pub(crate) enum PossiblyUninitalized<T> {
     Uninitalized,
     Initialized(T),
 }
+
 impl<T> PossiblyUninitalized<T> {
     pub fn unwrap(self) -> T {
         match self {
@@ -36,35 +38,35 @@ impl<T> Default for PossiblyUninitalized<T> {
     }
 }
 
-#[derive(Clone, PartialEq, Default, Debug)]
+#[derive(Clone, PartialEq, Default, Debug, Component)]
 pub(crate) struct TaffyLayout {
     pub style: Style,
     pub node: PossiblyUninitalized<Node>,
 }
 
-impl ChildDepState for TaffyLayout {
-    type Ctx = Arc<Mutex<Taffy>>;
-    type DepState = (Self,);
-    // use tag to force this to be called when a node is built
-    const NODE_MASK: NodeMask =
-        NodeMask::new_with_attrs(AttributeMask::Static(SORTED_LAYOUT_ATTRS))
-            .with_text()
-            .with_tag();
+#[partial_derive_state]
+impl State for TaffyLayout {
+    type ChildDependencies = (Self,);
+    type ParentDependencies = ();
+    type NodeDependencies = ();
+
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+        .with_attrs(AttributeMaskBuilder::Some(SORTED_LAYOUT_ATTRS))
+        .with_text();
 
-    /// Setup the layout
-    fn reduce<'a>(
+    fn update<'a>(
         &mut self,
-        node: NodeView,
-        children: impl Iterator<Item = (&'a Self,)>,
-        ctx: &Self::Ctx,
-    ) -> bool
-    where
-        Self::DepState: 'a,
-    {
+        node_view: NodeView,
+        _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        ctx: &SendAnyMap,
+    ) -> bool {
         let mut changed = false;
-        let mut taffy = ctx.lock().expect("poisoned taffy");
+        let taffy: &Arc<Mutex<Taffy>> = ctx.get().unwrap();
+        let mut taffy = taffy.lock().expect("poisoned taffy");
         let mut style = Style::default();
-        if let Some(text) = node.text() {
+        if let Some(text) = node_view.text() {
             let char_len = text.chars().count();
 
             style = Style {
@@ -87,14 +89,11 @@ impl ChildDepState for TaffyLayout {
             }
         } else {
             // gather up all the styles from the attribute list
-            if let Some(attributes) = node.attributes() {
+            if let Some(attributes) = node_view.attributes() {
                 for OwnedAttributeView {
                     attribute, value, ..
                 } in attributes
                 {
-                    assert!(SORTED_LAYOUT_ATTRS
-                        .binary_search(&attribute.name.as_ref())
-                        .is_ok());
                     if let Some(text) = value.as_text() {
                         apply_layout_attributes_cfg(
                             &attribute.name,
@@ -191,10 +190,22 @@ impl ChildDepState for TaffyLayout {
         }
         changed
     }
+
+    fn create<'a>(
+        node_view: NodeView<()>,
+        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,
+    ) -> Self {
+        let mut myself = Self::default();
+        myself.update(node_view, node, parent, children, context);
+        myself
+    }
 }
 
 // these are the attributes in layout_attiributes in native-core
-const SORTED_LAYOUT_ATTRS: &[&str] = &sorted_str_slice!([
+const SORTED_LAYOUT_ATTRS: &[&str] = &[
     "align-content",
     "align-items",
     "align-self",
@@ -346,5 +357,5 @@ const SORTED_LAYOUT_ATTRS: &[&str] = &sorted_str_slice!([
     "word-break",
     "word-spacing",
     "word-wrap",
-    "z-index"
-]);
+    "z-index",
+];

+ 90 - 121
packages/tui/src/lib.rs → packages/rink/src/lib.rs

@@ -1,3 +1,4 @@
+use crate::focus::Focus;
 use anyhow::Result;
 use crossterm::{
     cursor::{MoveTo, RestorePosition, SavePosition, Show},
@@ -5,21 +6,21 @@ use crossterm::{
     execute,
     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 };
-use dioxus_core::*;
-use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, NodeMask, SendAnyMap};
+use dioxus_html::EventData;
+use dioxus_native_core::prelude::*;
+use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, SendAnyMap};
 use focus::FocusState;
-use futures::{
-    channel::mpsc::{UnboundedReceiver, UnboundedSender},
-    pin_mut, StreamExt,
-};
+use futures::{channel::mpsc::UnboundedSender, pin_mut, Future, StreamExt};
 use futures_channel::mpsc::unbounded;
-use query::Query;
-use std::rc::Rc;
+use layout::TaffyLayout;
+use prevent_default::PreventDefault;
+use std::{io, time::Duration};
 use std::{
-    cell::RefCell,
+    pin::Pin,
     sync::{Arc, Mutex},
 };
-use std::{io, time::Duration};
+use std::{rc::Rc, sync::RwLock};
+use style_attributes::StyleModifier;
 use taffy::Taffy;
 pub use taffy::{geometry::Point, prelude::*};
 use tokio::select;
@@ -29,18 +30,17 @@ mod config;
 mod focus;
 mod hooks;
 mod layout;
-mod node;
 pub mod prelude;
+mod prevent_default;
 pub mod query;
 mod render;
 mod style;
 mod style_attributes;
 mod widget;
-mod widgets;
 
 pub use config::*;
 pub use hooks::*;
-pub(crate) use node::*;
+pub use query::Query;
 
 // the layout space has a multiplier of 10 to minimize rounding errors
 pub(crate) fn screen_to_layout_space(screen: u16) -> f32 {
@@ -59,7 +59,12 @@ pub(crate) fn layout_to_screen_space(layout: f32) -> f32 {
 pub struct TuiContext {
     tx: UnboundedSender<InputEvent>,
 }
+
 impl TuiContext {
+    pub fn new(tx: UnboundedSender<InputEvent>) -> Self {
+        Self { tx }
+    }
+
     pub fn quit(&self) {
         self.tx.unbounded_send(InputEvent::Close).unwrap();
     }
@@ -71,21 +76,25 @@ impl TuiContext {
     }
 }
 
-pub fn launch(app: Component<()>) {
-    launch_cfg(app, Config::default())
-}
-
-pub fn launch_cfg(app: Component<()>, cfg: Config) {
-    launch_cfg_with_props(app, (), cfg);
-}
-
-pub fn launch_cfg_with_props<Props: 'static>(app: Component<Props>, props: Props, cfg: Config) {
-    let mut dom = VirtualDom::new_with_props(app, props);
+pub fn render<R: Driver>(
+    cfg: Config,
+    create_renderer: impl FnOnce(
+        &Arc<RwLock<RealDom>>,
+        &Arc<Mutex<Taffy>>,
+        UnboundedSender<InputEvent>,
+    ) -> R,
+) -> Result<()> {
+    let mut rdom = RealDom::new([
+        TaffyLayout::to_type_erased(),
+        Focus::to_type_erased(),
+        StyleModifier::to_type_erased(),
+        PreventDefault::to_type_erased(),
+    ]);
 
-    let (handler, state, register_event) = RinkInputHandler::new();
+    let (handler, mut register_event) = RinkInputHandler::create(&mut rdom);
 
     // Setup input handling
-    let (event_tx, event_rx) = unbounded();
+    let (event_tx, mut event_reciever) = unbounded();
     let event_tx_clone = event_tx.clone();
     if !cfg.headless {
         std::thread::spawn(move || {
@@ -101,59 +110,22 @@ pub fn launch_cfg_with_props<Props: 'static>(app: Component<Props>, props: Props
         });
     }
 
-    let cx = dom.base_scope();
-    let rdom = Rc::new(RefCell::new(RealDom::new()));
+    let rdom = Arc::new(RwLock::new(rdom));
     let taffy = Arc::new(Mutex::new(Taffy::new()));
-    cx.provide_context(state);
-    cx.provide_context(TuiContext { tx: event_tx_clone });
-    cx.provide_context(Query {
-        rdom: rdom.clone(),
-        stretch: taffy.clone(),
-    });
+    let mut renderer = create_renderer(&rdom, &taffy, event_tx_clone);
 
     {
-        let mut rdom = rdom.borrow_mut();
-        let mutations = dom.rebuild();
-        let (to_update, _) = rdom.apply_mutations(mutations);
+        renderer.update(&rdom);
         let mut any_map = SendAnyMap::new();
         any_map.insert(taffy.clone());
-        let _to_rerender = rdom.update_state(to_update, any_map);
+        let mut rdom = rdom.write().unwrap();
+        let _ = rdom.update_state(any_map);
     }
 
-    render_vdom(
-        &mut dom,
-        event_rx,
-        handler,
-        cfg,
-        rdom,
-        taffy,
-        register_event,
-    )
-    .unwrap();
-}
-
-fn render_vdom(
-    vdom: &mut VirtualDom,
-    mut event_reciever: UnboundedReceiver<InputEvent>,
-    handler: RinkInputHandler,
-    cfg: Config,
-    rdom: Rc<RefCell<TuiDom>>,
-    taffy: Arc<Mutex<Taffy>>,
-    mut register_event: impl FnMut(crossterm::event::Event),
-) -> Result<()> {
     tokio::runtime::Builder::new_current_thread()
         .enable_all()
         .build()?
         .block_on(async {
-            #[cfg(all(feature = "hot-reload", debug_assertions))]
-            let mut hot_reload_rx = {
-                let (hot_reload_tx, hot_reload_rx) =
-                    tokio::sync::mpsc::unbounded_channel::<dioxus_hot_reload::HotReloadMsg>();
-                dioxus_hot_reload::connect(move |msg| {
-                    let _ = hot_reload_tx.send(msg);
-                });
-                hot_reload_rx
-            };
             let mut terminal = (!cfg.headless).then(|| {
                 enable_raw_mode().unwrap();
                 let mut stdout = std::io::stdout();
@@ -172,7 +144,7 @@ fn render_vdom(
             }
 
             let mut to_rerender = FxDashSet::default();
-            to_rerender.insert(NodeId(0));
+            to_rerender.insert(rdom.read().unwrap().root_id());
             let mut updated = true;
 
             loop {
@@ -187,19 +159,27 @@ fn render_vdom(
 
                 if !to_rerender.is_empty() || updated {
                     updated = false;
-                    fn resize(dims: Rect, taffy: &mut Taffy, rdom: &TuiDom) {
+                    fn resize(dims: Rect, taffy: &mut Taffy, rdom: &RealDom) {
                         let width = screen_to_layout_space(dims.width);
                         let height = screen_to_layout_space(dims.height);
-                        let root_node = rdom[NodeId(0)].state.layout.node.unwrap();
+                        let root_node = rdom
+                            .get(rdom.root_id())
+                            .unwrap()
+                            .get::<TaffyLayout>()
+                            .unwrap()
+                            .node
+                            .unwrap();
 
                         // the root node fills the entire area
-
                         let mut style = *taffy.style(root_node).unwrap();
-                        style.size = Size {
+                        let new_size = Size {
                             width: Dimension::Points(width),
                             height: Dimension::Points(height),
                         };
-                        taffy.set_style(root_node, style).unwrap();
+                        if style.size != new_size {
+                            style.size = new_size;
+                            taffy.set_style(root_node, style).unwrap();
+                        }
 
                         let size = Size {
                             width: AvailableSpace::Definite(width),
@@ -210,16 +190,16 @@ fn render_vdom(
                     if let Some(terminal) = &mut terminal {
                         execute!(terminal.backend_mut(), SavePosition).unwrap();
                         terminal.draw(|frame| {
-                            let rdom = rdom.borrow();
+                            let rdom = rdom.write().unwrap();
                             let mut taffy = taffy.lock().expect("taffy lock poisoned");
                             // size is guaranteed to not change when rendering
                             resize(frame.size(), &mut taffy, &rdom);
-                            let root = &rdom[NodeId(0)];
-                            render::render_vnode(frame, &taffy, &rdom, root, cfg, Point::ZERO);
+                            let root = rdom.get(rdom.root_id()).unwrap();
+                            render::render_vnode(frame, &taffy, root, cfg, Point::ZERO);
                         })?;
                         execute!(terminal.backend_mut(), RestorePosition, Show).unwrap();
                     } else {
-                        let rdom = rdom.borrow();
+                        let rdom = rdom.read().unwrap();
                         resize(
                             Rect {
                                 x: 0,
@@ -233,14 +213,8 @@ fn render_vdom(
                     }
                 }
 
-                #[cfg(all(feature = "hot-reload", debug_assertions))]
-                let mut hot_reload_msg = None;
                 {
-                    let wait = vdom.wait_for_work();
-                    #[cfg(all(feature = "hot-reload", debug_assertions))]
-                    let hot_reload_wait = hot_reload_rx.recv();
-                    #[cfg(not(all(feature = "hot-reload", debug_assertions)))]
-                    let hot_reload_wait: std::future::Pending<Option<()>> = std::future::pending();
+                    let wait = renderer.poll_async();
 
                     pin_mut!(wait);
 
@@ -253,8 +227,8 @@ fn render_vdom(
                                 InputEvent::UserInput(event) => match event {
                                     TermEvent::Key(key) => {
                                         if matches!(key.code, KeyCode::Char('C' | 'c'))
-                                            && key.modifiers.contains(KeyModifiers::CONTROL)
-                                            && cfg.ctrl_c_quit
+                                        && key.modifiers.contains(KeyModifiers::CONTROL)
+                                        && cfg.ctrl_c_quit
                                         {
                                             break;
                                         }
@@ -269,52 +243,34 @@ fn render_vdom(
                                 register_event(evt);
                             }
                         },
-                        Some(msg) = hot_reload_wait => {
-                            #[cfg(all(feature = "hot-reload", debug_assertions))]
-                            {
-                                hot_reload_msg = Some(msg);
-                            }
-                            #[cfg(not(all(feature = "hot-reload", debug_assertions)))]
-                            let () = msg;
-                        }
-                    }
-                }
-
-                // if we have a new template, replace the old one
-                #[cfg(all(feature = "hot-reload", debug_assertions))]
-                if let Some(msg) = hot_reload_msg {
-                    match msg {
-                        dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => {
-                            vdom.replace_template(template);
-                        }
-                        dioxus_hot_reload::HotReloadMsg::Shutdown => {
-                            break;
-                        }
                     }
                 }
 
                 {
-                    let evts = {
-                        let mut rdom = rdom.borrow_mut();
-                        handler.get_events(&taffy.lock().expect("taffy lock poisoned"), &mut rdom)
-                    };
                     {
+                        let evts = {
+                            handler.get_events(
+                                &taffy.lock().expect("taffy lock poisoned"),
+                                &mut rdom.write().unwrap(),
+                            )
+                        };
                         updated |= handler.state().focus_state.clean();
+
+                        for e in evts {
+                            renderer.handle_event(&rdom, e.id, e.name, e.data, e.bubbles);
+                        }
                     }
-                    for e in evts {
-                        vdom.handle_event(e.name, e.data, e.id, e.bubbles)
-                    }
-                    let mut rdom = rdom.borrow_mut();
-                    let mutations = vdom.render_immediate();
-                    handler.prune(&mutations, &rdom);
                     // updates the dom's nodes
-                    let (to_update, dirty) = rdom.apply_mutations(mutations);
+                    renderer.update(&rdom);
                     // update the style and layout
+                    let mut rdom = rdom.write().unwrap();
                     let mut any_map = SendAnyMap::new();
                     any_map.insert(taffy.clone());
-                    to_rerender = rdom.update_state(to_update, any_map);
+                    let (new_to_rerender, dirty) = rdom.update_state(any_map);
+                    to_rerender = new_to_rerender;
+                    let text_mask = NodeMaskBuilder::new().with_text().build();
                     for (id, mask) in dirty {
-                        if mask.overlaps(&NodeMask::new().with_text()) {
+                        if mask.overlaps(&text_mask) {
                             to_rerender.insert(id);
                         }
                     }
@@ -336,7 +292,20 @@ fn render_vdom(
 }
 
 #[derive(Debug)]
-enum InputEvent {
+pub enum InputEvent {
     UserInput(TermEvent),
     Close,
 }
+
+pub trait Driver {
+    fn update(&mut self, rdom: &Arc<RwLock<RealDom>>);
+    fn handle_event(
+        &mut self,
+        rdom: &Arc<RwLock<RealDom>>,
+        id: NodeId,
+        event: &str,
+        value: Rc<EventData>,
+        bubbles: bool,
+    );
+    fn poll_async(&mut self) -> Pin<Box<dyn Future<Output = ()> + '_>>;
+}

+ 2 - 0
packages/rink/src/prelude/mod.rs

@@ -0,0 +1,2 @@
+#[cfg(feature = "dioxus-bindings")]
+pub use crate::widgets::*;

+ 86 - 0
packages/rink/src/prevent_default.rs

@@ -0,0 +1,86 @@
+use dioxus_native_core::prelude::*;
+use dioxus_native_core_macro::partial_derive_state;
+use shipyard::Component;
+
+#[derive(PartialEq, Debug, Clone, Copy, Component, Default)]
+pub(crate) enum PreventDefault {
+    Focus,
+    KeyPress,
+    KeyRelease,
+    KeyDown,
+    KeyUp,
+    MouseDown,
+    Click,
+    MouseEnter,
+    MouseLeave,
+    MouseOut,
+    #[default]
+    Unknown,
+    MouseOver,
+    ContextMenu,
+    Wheel,
+    MouseUp,
+}
+
+#[partial_derive_state]
+impl State for PreventDefault {
+    type ParentDependencies = ();
+    type ChildDependencies = ();
+    type NodeDependencies = ();
+
+    const NODE_MASK: dioxus_native_core::node_ref::NodeMaskBuilder<'static> =
+        dioxus_native_core::node_ref::NodeMaskBuilder::new()
+            .with_attrs(dioxus_native_core::node_ref::AttributeMaskBuilder::Some(&[
+                "dioxus-prevent-default",
+            ]))
+            .with_listeners();
+
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView,
+        _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: &SendAnyMap,
+    ) -> bool {
+        let new = match node_view.attributes().and_then(|mut attrs| {
+            attrs
+                .find(|a| a.attribute.name == "dioxus-prevent-default")
+                .and_then(|a| a.value.as_text())
+        }) {
+            Some("onfocus") => PreventDefault::Focus,
+            Some("onkeypress") => PreventDefault::KeyPress,
+            Some("onkeyrelease") => PreventDefault::KeyRelease,
+            Some("onkeydown") => PreventDefault::KeyDown,
+            Some("onkeyup") => PreventDefault::KeyUp,
+            Some("onclick") => PreventDefault::Click,
+            Some("onmousedown") => PreventDefault::MouseDown,
+            Some("onmouseup") => PreventDefault::MouseUp,
+            Some("onmouseenter") => PreventDefault::MouseEnter,
+            Some("onmouseover") => PreventDefault::MouseOver,
+            Some("onmouseleave") => PreventDefault::MouseLeave,
+            Some("onmouseout") => PreventDefault::MouseOut,
+            Some("onwheel") => PreventDefault::Wheel,
+            Some("oncontextmenu") => PreventDefault::ContextMenu,
+            _ => return false,
+        };
+        if new == *self {
+            false
+        } else {
+            *self = new;
+            true
+        }
+    }
+
+    fn create<'a>(
+        node_view: NodeView<()>,
+        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,
+    ) -> Self {
+        let mut myself = Self::default();
+        myself.update(node_view, node, parent, children, context);
+        myself
+    }
+}

+ 29 - 15
packages/tui/src/query.rs → packages/rink/src/query.rs

@@ -1,17 +1,13 @@
-use std::{
-    cell::{Ref, RefCell},
-    rc::Rc,
-    sync::{Arc, Mutex, MutexGuard},
-};
+use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
 
-use dioxus_core::ElementId;
+use dioxus_native_core::prelude::*;
 use taffy::{
     geometry::Point,
     prelude::{Layout, Size},
     Taffy,
 };
 
-use crate::{layout_to_screen_space, TuiDom};
+use crate::{layout::TaffyLayout, layout_to_screen_space};
 
 /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
 /// Provided as a root context for all tui applictions.
@@ -46,28 +42,38 @@ use crate::{layout_to_screen_space, TuiDom};
 /// ```
 #[derive(Clone)]
 pub struct Query {
-    pub(crate) rdom: Rc<RefCell<TuiDom>>,
+    pub(crate) rdom: Arc<RwLock<RealDom>>,
     pub(crate) stretch: Arc<Mutex<Taffy>>,
 }
 
 impl Query {
-    pub fn get(&self, id: ElementId) -> ElementRef {
+    pub fn new(rdom: Arc<RwLock<RealDom>>, stretch: Arc<Mutex<Taffy>>) -> Self {
+        Self { rdom, stretch }
+    }
+
+    pub fn get(&self, id: NodeId) -> ElementRef {
+        let rdom = self.rdom.read();
+        let stretch = self.stretch.lock();
         ElementRef::new(
-            self.rdom.borrow(),
-            self.stretch.lock().expect("taffy lock poisoned"),
+            rdom.expect("rdom lock poisoned"),
+            stretch.expect("taffy lock poisoned"),
             id,
         )
     }
 }
 
 pub struct ElementRef<'a> {
-    inner: Ref<'a, TuiDom>,
+    inner: RwLockReadGuard<'a, RealDom>,
     stretch: MutexGuard<'a, Taffy>,
-    id: ElementId,
+    id: NodeId,
 }
 
 impl<'a> ElementRef<'a> {
-    fn new(inner: Ref<'a, TuiDom>, stretch: MutexGuard<'a, Taffy>, id: ElementId) -> Self {
+    fn new(
+        inner: RwLockReadGuard<'a, RealDom>,
+        stretch: MutexGuard<'a, Taffy>,
+        id: NodeId,
+    ) -> Self {
         Self { inner, stretch, id }
     }
 
@@ -85,7 +91,15 @@ impl<'a> ElementRef<'a> {
     pub fn layout(&self) -> Option<Layout> {
         let layout = self
             .stretch
-            .layout(self.inner[self.id].state.layout.node.ok()?)
+            .layout(
+                self.inner
+                    .get(self.id)
+                    .unwrap()
+                    .get::<TaffyLayout>()
+                    .unwrap()
+                    .node
+                    .ok()?,
+            )
             .ok();
         layout.map(|layout| Layout {
             order: layout.order,

+ 30 - 25
packages/tui/src/render.rs → packages/rink/src/render.rs

@@ -1,4 +1,4 @@
-use dioxus_native_core::tree::TreeView;
+use dioxus_native_core::prelude::*;
 use std::io::Stdout;
 use taffy::{
     geometry::Point,
@@ -8,11 +8,13 @@ use taffy::{
 use tui::{backend::CrosstermBackend, layout::Rect, style::Color};
 
 use crate::{
+    focus::Focused,
+    layout::TaffyLayout,
     layout_to_screen_space,
     style::{RinkColor, RinkStyle},
-    style_attributes::{BorderEdge, BorderStyle},
+    style_attributes::{BorderEdge, BorderStyle, StyleModifier},
     widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
-    Config, TuiDom, TuiNode,
+    Config,
 };
 
 const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
@@ -20,20 +22,19 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
 pub(crate) fn render_vnode(
     frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
     layout: &Taffy,
-    rdom: &TuiDom,
-    node: &TuiNode,
+    node: NodeRef,
     cfg: Config,
     parent_location: Point<f32>,
 ) {
-    use dioxus_native_core::node::NodeType;
-
-    if let NodeType::Placeholder = &node.node_data.node_type {
+    if let NodeType::Placeholder = &*node.node_type() {
         return;
     }
 
     let Layout {
         mut location, size, ..
-    } = layout.layout(node.state.layout.node.unwrap()).unwrap();
+    } = layout
+        .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
+        .unwrap();
     location.x += parent_location.x;
     location.y += parent_location.y;
 
@@ -44,8 +45,8 @@ pub(crate) fn render_vnode(
     let width = layout_to_screen_space(fx + width).round() as u16 - x;
     let height = layout_to_screen_space(fy + height).round() as u16 - y;
 
-    match &node.node_data.node_type {
-        NodeType::Text { text } => {
+    match &*node.node_type() {
+        NodeType::Text(text) => {
             #[derive(Default)]
             struct Label<'a> {
                 text: &'a str,
@@ -64,8 +65,8 @@ pub(crate) fn render_vnode(
             }
 
             let label = Label {
-                text,
-                style: node.state.style.core,
+                text: &text.text,
+                style: node.get::<StyleModifier>().unwrap().core,
             };
             let area = Rect::new(x, y, width, height);
 
@@ -82,15 +83,15 @@ pub(crate) fn render_vnode(
                 frame.render_widget(WidgetWithContext::new(node, cfg), area);
             }
 
-            for c in rdom.children_ids(node.node_data.node_id).unwrap() {
-                render_vnode(frame, layout, rdom, &rdom[*c], cfg, location);
+            for c in node.children() {
+                render_vnode(frame, layout, c, cfg, location);
             }
         }
         NodeType::Placeholder => unreachable!(),
     }
 }
 
-impl RinkWidget for &TuiNode {
+impl RinkWidget for NodeRef<'_> {
     fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
         use tui::symbols::line::*;
 
@@ -266,18 +267,22 @@ impl RinkWidget for &TuiNode {
         for x in area.left()..area.right() {
             for y in area.top()..area.bottom() {
                 let mut new_cell = RinkCell::default();
-                if let Some(c) = self.state.style.core.bg {
+                if let Some(c) = self.get::<StyleModifier>().unwrap().core.bg {
                     new_cell.bg = c;
                 }
-                if self.state.focused {
-                    new_cell.bg.alpha = 100;
-                    new_cell.bg.color = new_cell.bg.blend(Color::White);
+                if let Some(focused) = self.get::<Focused>() {
+                    if focused.0 {
+                        new_cell.bg.alpha = 100;
+                        new_cell.bg.color = new_cell.bg.blend(Color::White);
+                    }
                 }
                 buf.set(x, y, new_cell);
             }
         }
 
-        let borders = &self.state.style.modifier.borders;
+        let style = self.get::<StyleModifier>().unwrap();
+
+        let borders = &style.modifier.borders;
 
         let last_edge = &borders.left;
         let current_edge = &borders.top;
@@ -294,7 +299,7 @@ impl RinkWidget for &TuiNode {
                 (last_r * RADIUS_MULTIPLIER[0]) as u16,
                 (last_r * RADIUS_MULTIPLIER[1]) as u16,
             ];
-            let color = current_edge.color.or(self.state.style.core.fg);
+            let color = current_edge.color.or(style.core.fg);
             let mut new_cell = RinkCell::default();
             if let Some(c) = color {
                 new_cell.fg = c;
@@ -329,7 +334,7 @@ impl RinkWidget for &TuiNode {
                 (last_r * RADIUS_MULTIPLIER[0]) as u16,
                 (last_r * RADIUS_MULTIPLIER[1]) as u16,
             ];
-            let color = current_edge.color.or(self.state.style.core.fg);
+            let color = current_edge.color.or(style.core.fg);
             let mut new_cell = RinkCell::default();
             if let Some(c) = color {
                 new_cell.fg = c;
@@ -364,7 +369,7 @@ impl RinkWidget for &TuiNode {
                 (last_r * RADIUS_MULTIPLIER[0]) as u16,
                 (last_r * RADIUS_MULTIPLIER[1]) as u16,
             ];
-            let color = current_edge.color.or(self.state.style.core.fg);
+            let color = current_edge.color.or(style.core.fg);
             let mut new_cell = RinkCell::default();
             if let Some(c) = color {
                 new_cell.fg = c;
@@ -399,7 +404,7 @@ impl RinkWidget for &TuiNode {
                 (last_r * RADIUS_MULTIPLIER[0]) as u16,
                 (last_r * RADIUS_MULTIPLIER[1]) as u16,
             ];
-            let color = current_edge.color.or(self.state.style.core.fg);
+            let color = current_edge.color.or(style.core.fg);
             let mut new_cell = RinkCell::default();
             if let Some(c) = color {
                 new_cell.fg = c;

+ 0 - 0
packages/tui/src/style.rs → packages/rink/src/style.rs


+ 41 - 17
packages/tui/src/style_attributes.rs → packages/rink/src/style_attributes.rs

@@ -32,36 +32,48 @@
 use dioxus_native_core::{
     layout_attributes::parse_value,
     node::OwnedAttributeView,
-    node_ref::{AttributeMask, NodeMask, NodeView},
-    state::ParentDepState,
+    node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView},
+    prelude::*,
 };
-use dioxus_native_core_macro::sorted_str_slice;
+use dioxus_native_core_macro::partial_derive_state;
+use shipyard::Component;
 use taffy::prelude::*;
 
 use crate::style::{RinkColor, RinkStyle};
 
-#[derive(Default, Clone, PartialEq, Debug)]
+#[derive(Default, Clone, PartialEq, Debug, Component)]
 pub struct StyleModifier {
     pub core: RinkStyle,
     pub modifier: TuiModifier,
 }
 
-impl ParentDepState for StyleModifier {
-    type Ctx = ();
-    type DepState = (Self,);
-    // todo: seperate each attribute into it's own class
-    const NODE_MASK: NodeMask =
-        NodeMask::new_with_attrs(AttributeMask::Static(SORTED_STYLE_ATTRS)).with_element();
+#[partial_derive_state]
+impl State for StyleModifier {
+    type ParentDependencies = (Self,);
+    type ChildDependencies = ();
+    type NodeDependencies = ();
 
-    fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, _: &Self::Ctx) -> bool {
+    // todo: seperate each attribute into it's own class
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+        .with_attrs(AttributeMaskBuilder::Some(SORTED_STYLE_ATTRS))
+        .with_element();
+
+    fn update<'a>(
+        &mut self,
+        node_view: NodeView,
+        _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
+        parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
+        _: &SendAnyMap,
+    ) -> bool {
         let mut new = StyleModifier::default();
         if parent.is_some() {
             new.core.fg = None;
         }
 
         // handle text modifier elements
-        if node.namespace().is_none() {
-            if let Some(tag) = node.tag() {
+        if node_view.namespace().is_none() {
+            if let Some(tag) = node_view.tag() {
                 match tag {
                     "b" => apply_style_attributes("font-weight", "bold", &mut new),
                     "strong" => apply_style_attributes("font-weight", "bold", &mut new),
@@ -79,7 +91,7 @@ impl ParentDepState for StyleModifier {
         }
 
         // gather up all the styles from the attribute list
-        if let Some(attrs) = node.attributes() {
+        if let Some(attrs) = node_view.attributes() {
             for OwnedAttributeView {
                 attribute, value, ..
             } in attrs
@@ -103,6 +115,18 @@ impl ParentDepState for StyleModifier {
             false
         }
     }
+
+    fn create<'a>(
+        node_view: NodeView<()>,
+        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,
+    ) -> Self {
+        let mut myself = Self::default();
+        myself.update(node_view, node, parent, children, context);
+        myself
+    }
 }
 
 #[derive(Default, Clone, PartialEq, Debug)]
@@ -598,7 +622,7 @@ fn apply_transition(_name: &str, _value: &str, _style: &mut StyleModifier) {
     todo!()
 }
 
-const SORTED_STYLE_ATTRS: &[&str] = &sorted_str_slice!([
+const SORTED_STYLE_ATTRS: &[&str] = &[
     "animation",
     "animation-delay",
     "animation-direction",
@@ -799,5 +823,5 @@ const SORTED_STYLE_ATTRS: &[&str] = &sorted_str_slice!([
     "text-justify",
     "text-overflow",
     "text-shadow",
-    "text-transform"
-]);
+    "text-transform",
+];

+ 0 - 0
packages/tui/src/widget.rs → packages/rink/src/widget.rs


+ 73 - 0
packages/rink/test.html

@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>Test</title>
+    <style>
+        html,
+        body {
+            height: 100%;
+        }
+        
+        .container {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            flex-direction: column;
+            background-color: black;
+            /* justify-content: center;
+            align-items: center; */
+            /* margin: auto; */
+        }
+        
+        .smaller {
+            height: 70%;
+            width: 70%;
+            background-color: green;
+            /* justify-content: center; */
+            /* align-items: center; */
+        }
+        
+        .superinner {
+            height: 100%;
+            width: 100%;
+            /* display: flex; */
+            /*  */
+            margin-top: 20px;
+            margin-bottom: 20px;
+            margin-left: 20px;
+            margin-right: 20px;
+            /*  */
+            background-color: red;
+            justify-content: center;
+            align-items: center;
+            flex-direction: column;
+            /* margin: 20px; */
+            /* margin: 20px; */
+        }
+    </style>
+</head>
+
+<body>
+    <div class="container">
+        <div class="smaller">
+            <div class="superinner">
+                <h1>Hello World</h1>
+                <p>This is a test</p>
+            </div>
+        </div>
+    </div>
+    <!-- <div class="container">
+        <div class="smaller">
+            hello world
+            <div style="color: green; margin: 40px;">
+                goodbye
+                <div style="color:red;">
+                    asdasdasd
+                </div>
+            </div>
+        </div>
+    </div> -->
+</body>
+
+</html>

+ 0 - 89
packages/tui/src/node.rs

@@ -1,89 +0,0 @@
-use crate::focus::Focus;
-use crate::layout::TaffyLayout;
-use crate::style_attributes::StyleModifier;
-use dioxus_native_core::{real_dom::RealDom, state::*};
-use dioxus_native_core_macro::{sorted_str_slice, State};
-
-pub(crate) type TuiDom = RealDom<NodeState>;
-pub(crate) type TuiNode = dioxus_native_core::node::Node<NodeState>;
-
-#[derive(Debug, Clone, State, Default)]
-pub(crate) struct NodeState {
-    #[child_dep_state(layout, Mutex<Stretch>)]
-    pub layout: TaffyLayout,
-    #[parent_dep_state(style)]
-    pub style: StyleModifier,
-    #[node_dep_state()]
-    pub prevent_default: PreventDefault,
-    #[node_dep_state()]
-    pub focus: Focus,
-    pub focused: bool,
-}
-
-#[derive(PartialEq, Debug, Clone, Default)]
-pub(crate) enum PreventDefault {
-    Focus,
-    KeyPress,
-    KeyRelease,
-    KeyDown,
-    KeyUp,
-    MouseDown,
-    Click,
-    MouseEnter,
-    MouseLeave,
-    MouseOut,
-    #[default]
-    Unknown,
-    MouseOver,
-    ContextMenu,
-    Wheel,
-    MouseUp,
-}
-
-impl NodeDepState for PreventDefault {
-    type DepState = ();
-    type Ctx = ();
-
-    const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
-        dioxus_native_core::node_ref::NodeMask::new_with_attrs(
-            dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([
-                "dioxus-prevent-default"
-            ])),
-        )
-        .with_listeners();
-
-    fn reduce(
-        &mut self,
-        node: dioxus_native_core::node_ref::NodeView,
-        _sibling: (),
-        _ctx: &Self::Ctx,
-    ) -> bool {
-        let new = match node.attributes().and_then(|mut attrs| {
-            attrs
-                .find(|a| a.attribute.name == "dioxus-prevent-default")
-                .and_then(|a| a.value.as_text())
-        }) {
-            Some("onfocus") => PreventDefault::Focus,
-            Some("onkeypress") => PreventDefault::KeyPress,
-            Some("onkeyrelease") => PreventDefault::KeyRelease,
-            Some("onkeydown") => PreventDefault::KeyDown,
-            Some("onkeyup") => PreventDefault::KeyUp,
-            Some("onclick") => PreventDefault::Click,
-            Some("onmousedown") => PreventDefault::MouseDown,
-            Some("onmouseup") => PreventDefault::MouseUp,
-            Some("onmouseenter") => PreventDefault::MouseEnter,
-            Some("onmouseover") => PreventDefault::MouseOver,
-            Some("onmouseleave") => PreventDefault::MouseLeave,
-            Some("onmouseout") => PreventDefault::MouseOut,
-            Some("onwheel") => PreventDefault::Wheel,
-            Some("oncontextmenu") => PreventDefault::ContextMenu,
-            _ => return false,
-        };
-        if new == *self {
-            false
-        } else {
-            *self = new;
-            true
-        }
-    }
-}

+ 0 - 18
packages/tui/src/widgets/mod.rs

@@ -1,18 +0,0 @@
-mod button;
-mod checkbox;
-mod input;
-mod number;
-mod password;
-mod slider;
-mod textbox;
-
-use dioxus_core::{ElementId, RenderReturn, Scope};
-pub use input::*;
-
-pub(crate) fn get_root_id<T>(cx: Scope<T>) -> Option<ElementId> {
-    if let RenderReturn::Ready(sync) = cx.root_node() {
-        sync.root_ids.get(0)
-    } else {
-        None
-    }
-}