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

Port widgets to the new system

Evan Almloff 2 éve
szülő
commit
efa7305794

+ 17 - 16
packages/native-core/src/custom_element.rs

@@ -9,7 +9,7 @@ use shipyard::Component;
 use crate::{
     node::{FromAnyValue, NodeType},
     node_ref::AttributeMask,
-    prelude::{NodeImmutable, NodeMut, RealDom},
+    prelude::{NodeImmutable, NodeMut},
     tree::TreeMut,
     NodeId,
 };
@@ -27,11 +27,15 @@ impl<V: FromAnyValue + Send + Sync> Default for CustomElementRegistry<V> {
 }
 
 impl<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
-    pub fn register<W: CustomElement<V>>(&mut self) {
+    pub fn register<F, U>(&mut self)
+    where
+        F: CustomElementFactory<U, V>,
+        U: CustomElementUpdater<V>,
+    {
         self.builders.insert(
-            (W::NAME, W::NAMESPACE),
+            (F::NAME, F::NAMESPACE),
             CustomElementBuilder {
-                create: |dom| Box::new(W::create(dom)),
+                create: |node| Box::new(F::create(node)),
             },
         );
     }
@@ -44,10 +48,7 @@ impl<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
         };
         if let Some((tag, ns)) = element_tag {
             if let Some(builder) = self.builders.get(&(tag.as_str(), ns.as_deref())) {
-                let boxed_widget = {
-                    let dom = node.real_dom_mut();
-                    (builder.create)(dom)
-                };
+                let boxed_widget = { (builder.create)(node.reborrow()) };
 
                 let shadow_roots = boxed_widget.roots();
 
@@ -69,7 +70,7 @@ impl<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
 }
 
 struct CustomElementBuilder<V: FromAnyValue + Send + Sync> {
-    create: fn(&mut RealDom<V>) -> Box<dyn CustomElementUpdater<V>>,
+    create: fn(NodeMut<V>) -> Box<dyn CustomElementUpdater<V>>,
 }
 
 /// A controlled element that renders to a shadow DOM
@@ -81,7 +82,7 @@ pub trait CustomElement<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'stat
     const NAMESPACE: Option<&'static str> = None;
 
     /// Create a new widget *without mounting* it.
-    fn create(dom: &mut RealDom<V>) -> Self;
+    fn create(node: NodeMut<V>) -> Self;
 
     /// The root node of the widget. This must be static once the element is created.
     fn roots(&self) -> Vec<NodeId>;
@@ -96,7 +97,7 @@ pub trait CustomElement<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'stat
 }
 
 /// A factory for creating widgets
-trait ElementFactory<W: CustomElementUpdater<V>, V: FromAnyValue + Send + Sync = ()>:
+pub trait CustomElementFactory<W: CustomElementUpdater<V>, V: FromAnyValue + Send + Sync = ()>:
     Send + Sync + 'static
 {
     /// The tag the widget is registered under.
@@ -106,21 +107,21 @@ trait ElementFactory<W: CustomElementUpdater<V>, V: FromAnyValue + Send + Sync =
     const NAMESPACE: Option<&'static str> = None;
 
     /// Create a new widget.
-    fn create(dom: &mut RealDom<V>) -> W;
+    fn create(dom: NodeMut<V>) -> W;
 }
 
-impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> ElementFactory<W, V> for W {
+impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> CustomElementFactory<W, V> for W {
     const NAME: &'static str = W::NAME;
 
     const NAMESPACE: Option<&'static str> = W::NAMESPACE;
 
-    fn create(dom: &mut RealDom<V>) -> Self {
-        Self::create(dom)
+    fn create(node: NodeMut<V>) -> Self {
+        Self::create(node)
     }
 }
 
 /// A trait for updating widgets
-trait CustomElementUpdater<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
+pub trait CustomElementUpdater<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
     /// Called when the attributes of the widget are changed.
     fn attributes_changed(&mut self, light_root: NodeMut<V>, attributes: &AttributeMask);
 

+ 22 - 2
packages/native-core/src/real_dom.rs

@@ -10,7 +10,10 @@ use std::collections::VecDeque;
 use std::ops::{Deref, DerefMut};
 use std::sync::{Arc, RwLock};
 
-use crate::custom_element::{CustomElement, CustomElementManager, CustomElementRegistry};
+use crate::custom_element::{
+    CustomElement, CustomElementFactory, CustomElementManager, CustomElementRegistry,
+    CustomElementUpdater,
+};
 use crate::node::{
     ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode,
 };
@@ -478,7 +481,16 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
 
     /// Registers a new custom element.
     pub fn register_custom_element<E: CustomElement<V>>(&mut self) {
-        self.custom_elements.write().unwrap().register::<E>()
+        self.register_custom_element_factory::<E, E>()
+    }
+
+    /// Registers a new custom element with a custom factory.
+    pub fn register_custom_element_factory<F, U>(&mut self)
+    where
+        F: CustomElementFactory<U, V>,
+        U: CustomElementUpdater<V>,
+    {
+        self.custom_elements.write().unwrap().register::<F, U>()
     }
 }
 
@@ -678,6 +690,14 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable<V> for NodeMut<'a, V> {
 }
 
 impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
+    /// Reborrow the node mutably
+    pub fn reborrow(&mut self) -> NodeMut<'_, V> {
+        NodeMut {
+            id: self.id,
+            dom: self.dom,
+        }
+    }
+
     /// Get the real dom this node was created in mutably
     #[inline(always)]
     pub fn real_dom_mut(&mut self) -> &mut RealDom<V> {

+ 4 - 2
packages/native-core/tests/custom_element.rs

@@ -179,7 +179,8 @@ struct CustomElementWithSlot {
 impl CustomElement for CustomElementWithSlot {
     const NAME: &'static str = "customelementslot";
 
-    fn create(dom: &mut RealDom<()>) -> Self {
+    fn create(mut node: NodeMut<()>) -> Self {
+        let dom = node.real_dom_mut();
         let child = dom.create_node(ElementNode {
             tag: "div".into(),
             namespace: None,
@@ -227,7 +228,8 @@ struct CustomElementWithNoSlot {
 impl CustomElement for CustomElementWithNoSlot {
     const NAME: &'static str = "customelementnoslot";
 
-    fn create(dom: &mut RealDom<()>) -> Self {
+    fn create(mut node: NodeMut<()>) -> Self {
+        let dom = node.real_dom_mut();
         let root = dom.create_node(ElementNode {
             tag: "div".into(),
             namespace: None,

+ 1 - 1
packages/rink/examples/widgets.rs

@@ -77,7 +77,7 @@ impl Driver for Counter {
         _: bool,
     ) {
         match event_type {
-            "oninput" => {
+            "input" => {
                 // when the button is clicked, increment the counter
                 if let EventData::Form(input_event) = &*event {
                     if let Ok(value) = input_event.value.parse::<f64>() {

+ 2 - 2
packages/rink/src/focus.rs

@@ -183,7 +183,7 @@ impl FocusState {
 
                 if forward {
                     // find the closest focusable element after the current level
-                    rdom.traverse_depth_first(|n| {
+                    rdom.traverse_depth_first(true, |n| {
                         let node_level = n.get::<Focus>().unwrap().level;
                         if node_level != *focus_level
                             && node_level.focusable()
@@ -200,7 +200,7 @@ impl FocusState {
                     });
                 } else {
                     // find the closest focusable element before the current level
-                    rdom.traverse_depth_first(|n| {
+                    rdom.traverse_depth_first(true, |n| {
                         let node_level = n.get::<Focus>().unwrap().level;
                         if node_level != *focus_level
                             && node_level.focusable()

+ 12 - 30
packages/rink/src/hooks.rs

@@ -25,7 +25,7 @@ use taffy::{prelude::Layout, Taffy};
 
 use crate::focus::{Focus, Focused};
 use crate::layout::TaffyLayout;
-use crate::{layout_to_screen_space, FocusState};
+use crate::{get_abs_layout, layout_to_screen_space, FocusState};
 
 #[derive(Debug, Clone, PartialEq)]
 pub struct Event {
@@ -323,7 +323,7 @@ impl InnerInputState {
                 if old_pos != Some(new_pos) {
                     let mut will_bubble = FxHashSet::default();
                     for node in dom.get_listening_sorted("mousemove") {
-                        let node_layout = get_abs_layout(node, dom, layout);
+                        let node_layout = get_abs_layout(node, layout);
                         let previously_contained = old_pos
                             .filter(|pos| layout_contains_point(&node_layout, *pos))
                             .is_some();
@@ -347,7 +347,7 @@ impl InnerInputState {
                 // mouseenter
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mouseenter") {
-                    let node_layout = get_abs_layout(node, dom, layout);
+                    let node_layout = get_abs_layout(node, layout);
                     let previously_contained = old_pos
                         .filter(|pos| layout_contains_point(&node_layout, *pos))
                         .is_some();
@@ -370,7 +370,7 @@ impl InnerInputState {
                 // mouseover
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mouseover") {
-                    let node_layout = get_abs_layout(node, dom, layout);
+                    let node_layout = get_abs_layout(node, layout);
                     let previously_contained = old_pos
                         .filter(|pos| layout_contains_point(&node_layout, *pos))
                         .is_some();
@@ -393,7 +393,7 @@ impl InnerInputState {
             if was_pressed {
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mousedown") {
-                    let node_layout = get_abs_layout(node, dom, layout);
+                    let node_layout = get_abs_layout(node, layout);
                     let currently_contains = layout_contains_point(&node_layout, new_pos);
 
                     if currently_contains {
@@ -414,7 +414,7 @@ impl InnerInputState {
                 if was_released {
                     let mut will_bubble = FxHashSet::default();
                     for node in dom.get_listening_sorted("mouseup") {
-                        let node_layout = get_abs_layout(node, dom, layout);
+                        let node_layout = get_abs_layout(node, layout);
                         let currently_contains = layout_contains_point(&node_layout, new_pos);
 
                         if currently_contains {
@@ -436,7 +436,7 @@ impl InnerInputState {
                 if mouse_data.trigger_button() == Some(DioxusMouseButton::Primary) && was_released {
                     let mut will_bubble = FxHashSet::default();
                     for node in dom.get_listening_sorted("click") {
-                        let node_layout = get_abs_layout(node, dom, layout);
+                        let node_layout = get_abs_layout(node, layout);
                         let currently_contains = layout_contains_point(&node_layout, new_pos);
 
                         if currently_contains {
@@ -459,7 +459,7 @@ impl InnerInputState {
                 {
                     let mut will_bubble = FxHashSet::default();
                     for node in dom.get_listening_sorted("contextmenu") {
-                        let node_layout = get_abs_layout(node, dom, layout);
+                        let node_layout = get_abs_layout(node, layout);
                         let currently_contains = layout_contains_point(&node_layout, new_pos);
 
                         if currently_contains {
@@ -482,7 +482,7 @@ impl InnerInputState {
                     if was_scrolled {
                         let mut will_bubble = FxHashSet::default();
                         for node in dom.get_listening_sorted("wheel") {
-                            let node_layout = get_abs_layout(node, dom, layout);
+                            let node_layout = get_abs_layout(node, layout);
 
                             let currently_contains = layout_contains_point(&node_layout, new_pos);
 
@@ -505,7 +505,7 @@ impl InnerInputState {
                 // mouseleave
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mouseleave") {
-                    let node_layout = get_abs_layout(node, dom, layout);
+                    let node_layout = get_abs_layout(node, layout);
                     let previously_contained = old_pos
                         .filter(|pos| layout_contains_point(&node_layout, *pos))
                         .is_some();
@@ -528,7 +528,7 @@ impl InnerInputState {
                 // mouseout
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mouseout") {
-                    let node_layout = get_abs_layout(node, dom, layout);
+                    let node_layout = get_abs_layout(node, layout);
                     let previously_contained = old_pos
                         .filter(|pos| layout_contains_point(&node_layout, *pos))
                         .is_some();
@@ -550,7 +550,7 @@ impl InnerInputState {
             // update focus
             if was_released {
                 let mut focus_id = None;
-                dom.traverse_depth_first(|node| {
+                dom.traverse_depth_first(true, |node| {
                     let node_layout = layout
                         .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
                         .unwrap();
@@ -572,24 +572,6 @@ impl InnerInputState {
     // }
 }
 
-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) = current.parent_id() {
-        let parent = dom.get(parent).unwrap();
-        current = parent;
-        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;
-    }
-    node_layout
-}
-
 pub struct RinkInputHandler {
     state: Rc<RefCell<InnerInputState>>,
     queued_events: Rc<RefCell<Vec<EventCore>>>,

+ 3 - 6
packages/rink/src/layout.rs

@@ -25,12 +25,6 @@ impl<T> PossiblyUninitalized<T> {
             _ => panic!("uninitalized"),
         }
     }
-    pub fn ok(self) -> Option<T> {
-        match self {
-            Self::Initialized(i) => Some(i),
-            _ => None,
-        }
-    }
 }
 impl<T> Default for PossiblyUninitalized<T> {
     fn default() -> Self {
@@ -54,6 +48,9 @@ impl State for TaffyLayout {
         .with_attrs(AttributeMaskBuilder::Some(SORTED_LAYOUT_ATTRS))
         .with_text();
 
+    // The layout state should be effected by the shadow dom
+    const TRAVERSE_SHADOW_DOM: bool = true;
+
     fn update<'a>(
         &mut self,
         node_view: NodeView,

+ 41 - 10
packages/rink/src/lib.rs

@@ -6,7 +6,7 @@ use crossterm::{
     execute,
     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 };
-use dioxus_native_core::prelude::*;
+use dioxus_native_core::{prelude::*, tree::TreeRef};
 use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, SendAnyMap};
 use focus::FocusState;
 use futures::{channel::mpsc::UnboundedSender, pin_mut, Future, StreamExt};
@@ -262,7 +262,7 @@ pub fn render<R: Driver>(
                             }
                         },
                         Some(evt) = event_reciever.next() => {
-                            event_recieved=Some(evt);
+                            event_recieved = Some(evt);
                         }
                     }
                 }
@@ -342,19 +342,50 @@ pub trait Driver {
 /// Before sending the event to drivers, we need to bubble it up the tree to any widgets that are listening
 fn bubble_event_to_widgets(rdom: &mut RealDom, event: &Event) {
     let id = event.id;
-    let mut node = Some(rdom.get_mut(id).unwrap());
+    let mut node = Some(id);
+
+    while let Some(node_id) = node {
+        let parent_id = {
+            let tree = rdom.tree_ref();
+            tree.parent_id_advanced(node_id, true)
+        };
 
-    while let Some(mut node_mut) = node {
-        let parent_id = node_mut.parent_id();
-        if let Some(mut widget) = node_mut
-            .get_mut::<RinkWidgetTraitObject>()
-            .map(|w| w.clone())
         {
-            widget.handle_event(event, &mut node_mut)
+            // println!("@ bubbling event to node {:?}", node_id);
+            let mut node_mut = rdom.get_mut(node_id).unwrap();
+            if let Some(mut widget) = node_mut
+                .get_mut::<RinkWidgetTraitObject>()
+                .map(|w| w.clone())
+            {
+                widget.handle_event(event, node_mut)
+            }
         }
+
         if !event.bubbles {
+            // println!("event does not bubble");
             break;
         }
-        node = parent_id.map(|id| rdom.get_mut(id).unwrap());
+        node = parent_id;
+    }
+}
+
+pub(crate) fn get_abs_layout(node: NodeRef, taffy: &Taffy) -> Layout {
+    let mut node_layout = *taffy
+        .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
+        .unwrap();
+    let mut current = node;
+
+    let dom = node.real_dom();
+    let tree = dom.tree_ref();
+
+    while let Some(parent) = tree.parent_id_advanced(current.id(), true) {
+        let parent = dom.get(parent).unwrap();
+        current = parent;
+        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;
     }
+    node_layout
 }

+ 3 - 16
packages/rink/src/query.rs

@@ -8,7 +8,7 @@ use taffy::{
     Taffy,
 };
 
-use crate::{layout::TaffyLayout, layout_to_screen_space};
+use crate::{get_abs_layout, 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.
@@ -95,21 +95,8 @@ impl<'a> ElementRef<'a> {
 }
 
 pub(crate) fn get_layout(node: NodeRef, stretch: &Taffy) -> Option<Layout> {
-    let layout = stretch
-        .layout(node.get::<TaffyLayout>().unwrap().node.ok()?)
-        .ok()?;
-
-    let mut current_node_id = node.parent_id();
-    let mut pos = layout.location;
-    let rdom = node.real_dom();
-    while let Some(node) = current_node_id.and_then(|id| rdom.get(id)) {
-        let current_layout = stretch
-            .layout(node.get::<TaffyLayout>().unwrap().node.ok()?)
-            .ok()?;
-        pos.x += current_layout.location.x;
-        pos.y += current_layout.location.y;
-        current_node_id = node.parent_id();
-    }
+    let layout = get_abs_layout(node, stretch);
+    let pos = layout.location;
 
     Some(Layout {
         order: layout.order,

+ 5 - 2
packages/rink/src/render.rs

@@ -1,4 +1,4 @@
-use dioxus_native_core::prelude::*;
+use dioxus_native_core::{prelude::*, tree::TreeRef};
 use std::io::Stdout;
 use taffy::{
     geometry::Point,
@@ -83,7 +83,10 @@ pub(crate) fn render_vnode(
                 frame.render_widget(WidgetWithContext::new(node, cfg), area);
             }
 
-            for c in node.children() {
+            let node_id = node.id();
+            let rdom = node.real_dom();
+            for child_id in rdom.tree_ref().children_ids_advanced(node_id, true) {
+                let c = rdom.get(child_id).unwrap();
                 render_vnode(frame, layout, c, cfg, location);
             }
         }

+ 22 - 20
packages/rink/src/widgets/button.rs

@@ -2,14 +2,14 @@ use std::collections::HashMap;
 
 use dioxus_html::input_data::keyboard_types::Key;
 use dioxus_native_core::{
+    custom_element::CustomElement,
     node::OwnedAttributeDiscription,
     node_ref::AttributeMask,
     prelude::NodeType,
-    real_dom::{ElementNodeMut, NodeImmutable, NodeTypeMut, RealDom},
-    utils::widget_watcher::Widget,
+    real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
     NodeId,
 };
-use shipyard::UniqueViewMut;
+use shipyard::UniqueView;
 
 use crate::FormData;
 
@@ -17,7 +17,6 @@ use super::{RinkWidget, WidgetContext};
 
 #[derive(Debug, Default)]
 pub(crate) struct Button {
-    div_id: NodeId,
     text_id: NodeId,
     value: String,
 }
@@ -88,14 +87,14 @@ impl Button {
         }
     }
 
-    fn switch(&mut self, ctx: &mut WidgetContext) {
+    fn switch(&mut self, ctx: &mut WidgetContext, node: NodeMut) {
         let data = FormData {
             value: self.value.to_string(),
             values: HashMap::new(),
             files: None,
         };
         ctx.send(crate::Event {
-            id: self.div_id,
+            id: node.id(),
             name: "input",
             data: crate::EventData::Form(data),
             bubbles: true,
@@ -103,10 +102,14 @@ impl Button {
     }
 }
 
-impl Widget for Button {
+impl CustomElement for Button {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<NodeId> {
+        vec![self.text_id]
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         let node_type = root.node_type();
         let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
 
@@ -127,11 +130,8 @@ impl Widget for Button {
 
         root.add_event_listener("keydown");
         root.add_event_listener("click");
-        let div_id = root.id();
-        root.add_child(text_id);
 
         Self {
-            div_id,
             text_id,
             value: value.unwrap_or_default(),
         }
@@ -139,7 +139,7 @@ impl Widget for Button {
 
     fn attributes_changed(
         &mut self,
-        mut root: dioxus_native_core::real_dom::NodeMut<()>,
+        mut root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match attributes {
@@ -175,16 +175,18 @@ impl RinkWidget for Button {
     fn handle_event(
         &mut self,
         event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
+        mut node: dioxus_native_core::real_dom::NodeMut,
     ) {
-        let mut ctx: UniqueViewMut<WidgetContext> = node
-            .real_dom_mut()
-            .raw_world_mut()
-            .borrow()
-            .expect("expected widget context");
+        let mut ctx: WidgetContext = {
+            node.real_dom_mut()
+                .raw_world_mut()
+                .borrow::<UniqueView<WidgetContext>>()
+                .expect("expected widget context")
+                .clone()
+        };
 
         match event.name {
-            "click" => self.switch(&mut ctx),
+            "click" => self.switch(&mut ctx, node),
             "keydown" => {
                 if let crate::EventData::Keyboard(data) = &event.data {
                     if !data.is_auto_repeating()
@@ -194,7 +196,7 @@ impl RinkWidget for Button {
                             _ => false,
                         }
                     {
-                        self.switch(&mut ctx);
+                        self.switch(&mut ctx, node);
                     }
                 }
             }

+ 13 - 14
packages/rink/src/widgets/checkbox.rs

@@ -2,11 +2,11 @@ use std::collections::HashMap;
 
 use dioxus_html::input_data::keyboard_types::Key;
 use dioxus_native_core::{
+    custom_element::CustomElement,
     node::OwnedAttributeDiscription,
     node_ref::AttributeMask,
     prelude::NodeType,
     real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut},
-    utils::widget_watcher::Widget,
     NodeId,
 };
 use shipyard::UniqueView;
@@ -91,7 +91,7 @@ impl CheckBox {
             == "true";
     }
 
-    fn write_value(&self, root: &mut NodeMut) {
+    fn write_value(&self, mut root: NodeMut) {
         let single_char = {
             let node_type = root.node_type_mut();
             let NodeTypeMut::Element( el) = node_type else { panic!("input must be an element") };
@@ -117,7 +117,7 @@ impl CheckBox {
         }
     }
 
-    fn switch(&mut self, node: &mut NodeMut) {
+    fn switch(&mut self, mut node: NodeMut) {
         let new_state = !self.checked;
 
         let data = FormData {
@@ -147,10 +147,14 @@ impl CheckBox {
     }
 }
 
-impl Widget for CheckBox {
+impl CustomElement for CheckBox {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<NodeId> {
+        vec![self.text_id]
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         let node_type = root.node_type();
         let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
 
@@ -172,7 +176,6 @@ impl Widget for CheckBox {
         root.add_event_listener("click");
         root.add_event_listener("keydown");
         let div_id = root.id();
-        root.add_child(text_id);
 
         let myself = Self {
             div_id,
@@ -187,7 +190,7 @@ impl Widget for CheckBox {
 
     fn attributes_changed(
         &mut self,
-        mut root: dioxus_native_core::real_dom::NodeMut<()>,
+        mut root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match attributes {
@@ -199,7 +202,7 @@ impl Widget for CheckBox {
                     self.update_size_attr(&mut el);
                     self.update_checked_attr(&el);
                 }
-                self.write_value(&mut root);
+                self.write_value(root);
             }
             AttributeMask::Some(attrs) => {
                 {
@@ -216,7 +219,7 @@ impl Widget for CheckBox {
                     }
                 }
                 if attrs.contains("checked") {
-                    self.write_value(&mut root);
+                    self.write_value(root);
                 }
             }
         }
@@ -224,11 +227,7 @@ impl Widget for CheckBox {
 }
 
 impl RinkWidget for CheckBox {
-    fn handle_event(
-        &mut self,
-        event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
-    ) {
+    fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) {
         match event.name {
             "click" => self.switch(node),
             "keydown" => {

+ 32 - 11
packages/rink/src/widgets/input.rs

@@ -1,6 +1,6 @@
 use dioxus_native_core::{
-    node::OwnedAttributeDiscription, prelude::NodeType, real_dom::NodeImmutable,
-    utils::widget_watcher::Widget,
+    custom_element::CustomElement, node::OwnedAttributeDiscription, prelude::NodeType,
+    real_dom::NodeImmutable,
 };
 
 use super::{
@@ -18,10 +18,32 @@ pub(crate) enum Input {
     Slider(Slider),
 }
 
-impl Widget for Input {
+impl CustomElement for Input {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<dioxus_native_core::NodeId> {
+        match self {
+            Input::Button(button) => button.roots(),
+            Input::CheckBox(checkbox) => checkbox.roots(),
+            Input::TextBox(textbox) => textbox.roots(),
+            Input::Password(password) => password.roots(),
+            Input::Number(number) => number.roots(),
+            Input::Slider(slider) => slider.roots(),
+        }
+    }
+
+    fn slot(&self) -> Option<dioxus_native_core::NodeId> {
+        match self {
+            Input::Button(button) => button.slot(),
+            Input::CheckBox(checkbox) => checkbox.slot(),
+            Input::TextBox(textbox) => textbox.slot(),
+            Input::Password(password) => password.slot(),
+            Input::Number(number) => number.slot(),
+            Input::Slider(slider) => slider.slot(),
+        }
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         {
             // currently widgets are not allowed to have children
             let children = root.child_ids();
@@ -42,7 +64,10 @@ impl Widget for Input {
                 namespace: None,
             })
             .and_then(|value| value.as_text());
-        match input_type {
+        match input_type
+            .map(|type_| type_.trim().to_lowercase())
+            .as_deref()
+        {
             Some("button") => {
                 drop(node_type);
                 Input::Button(Button::create(root))
@@ -76,7 +101,7 @@ impl Widget for Input {
 
     fn attributes_changed(
         &mut self,
-        root: dioxus_native_core::real_dom::NodeMut<()>,
+        root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match self {
@@ -103,11 +128,7 @@ impl Widget for Input {
 }
 
 impl RinkWidget for Input {
-    fn handle_event(
-        &mut self,
-        event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
-    ) {
+    fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) {
         match self {
             Input::Button(button) => {
                 button.handle_event(event, node);

+ 56 - 24
packages/rink/src/widgets/mod.rs

@@ -9,8 +9,8 @@ mod textbox;
 use std::sync::{Arc, RwLock};
 
 use dioxus_native_core::{
-    real_dom::RealDom,
-    utils::widget_watcher::{Widget, WidgetFactory, WidgetUpdater, WidgetWatcher},
+    custom_element::{CustomElement, CustomElementUpdater},
+    real_dom::{NodeMut, RealDom},
 };
 use futures_channel::mpsc::UnboundedSender;
 use shipyard::{Component, Unique};
@@ -21,42 +21,64 @@ pub(crate) fn register_widgets(rdom: &mut RealDom, sender: UnboundedSender<Event
     // inject the widget context
     rdom.raw_world().add_unique(WidgetContext { sender });
 
-    // create the widget watcher
-    let mut widget_watcher = WidgetWatcher::default();
-
-    widget_watcher
-        .register_widget::<RinkWidgetTraitObjectFactory<input::Input>, RinkWidgetTraitObject>();
-
-    widget_watcher.attach(rdom);
+    rdom.register_custom_element::<RinkWidgetWrapper<input::Input>>();
 }
 
-trait RinkWidget: Sync + Send + Widget + 'static {
-    fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut);
+trait RinkWidget: Sync + Send + CustomElement + 'static {
+    fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut);
 }
 
-pub trait RinkWidgetResponder: WidgetUpdater {
-    fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut);
+pub trait RinkWidgetResponder: CustomElementUpdater {
+    fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut);
 }
 
 impl<W: RinkWidget> RinkWidgetResponder for W {
-    fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut) {
+    fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut) {
         RinkWidget::handle_event(self, event, node)
     }
 }
 
-struct RinkWidgetTraitObjectFactory<W: RinkWidget> {
+struct RinkWidgetWrapper<W: RinkWidget> {
+    inner: RinkWidgetTraitObject,
     _marker: std::marker::PhantomData<W>,
 }
 
-impl<W: RinkWidget> WidgetFactory<RinkWidgetTraitObject> for RinkWidgetTraitObjectFactory<W> {
+impl<W: RinkWidget> CustomElement for RinkWidgetWrapper<W> {
     const NAME: &'static str = W::NAME;
 
-    fn create(node: &mut dioxus_native_core::real_dom::NodeMut) -> RinkWidgetTraitObject {
+    const NAMESPACE: Option<&'static str> = W::NAMESPACE;
+
+    fn create(mut node: NodeMut) -> Self {
         let myself = RinkWidgetTraitObject {
-            widget: Arc::new(RwLock::new(W::create(node))),
+            widget: Arc::new(RwLock::new(W::create(node.reborrow()))),
         };
+
+        // Insert the widget as an arbitrary data node so that it can be recognized when bubbling events
         node.insert(myself.clone());
-        myself
+
+        RinkWidgetWrapper {
+            inner: myself,
+            _marker: std::marker::PhantomData,
+        }
+    }
+
+    fn attributes_changed(
+        &mut self,
+        root: dioxus_native_core::real_dom::NodeMut,
+        attributes: &dioxus_native_core::node_ref::AttributeMask,
+    ) {
+        let mut widget = self.inner.widget.write().unwrap();
+        widget.attributes_changed(root, attributes);
+    }
+
+    fn roots(&self) -> Vec<dioxus_native_core::NodeId> {
+        let widget = self.inner.widget.read().unwrap();
+        widget.roots()
+    }
+
+    fn slot(&self) -> Option<dioxus_native_core::NodeId> {
+        let widget = self.inner.widget.read().unwrap();
+        widget.slot()
     }
 }
 
@@ -65,25 +87,35 @@ pub(crate) struct RinkWidgetTraitObject {
     widget: Arc<RwLock<dyn RinkWidgetResponder + Send + Sync>>,
 }
 
-impl WidgetUpdater for RinkWidgetTraitObject {
+impl CustomElementUpdater for RinkWidgetTraitObject {
     fn attributes_changed(
         &mut self,
-        root: dioxus_native_core::real_dom::NodeMut,
+        light_root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         let mut widget = self.widget.write().unwrap();
-        widget.attributes_changed(root, attributes);
+        widget.attributes_changed(light_root, attributes);
+    }
+
+    fn roots(&self) -> Vec<dioxus_native_core::NodeId> {
+        let widget = self.widget.read().unwrap();
+        widget.roots()
+    }
+
+    fn slot(&self) -> Option<dioxus_native_core::NodeId> {
+        let widget = self.widget.read().unwrap();
+        widget.slot()
     }
 }
 
 impl RinkWidgetResponder for RinkWidgetTraitObject {
-    fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut) {
+    fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut) {
         let mut widget = self.widget.write().unwrap();
         widget.handle_event(event, node);
     }
 }
 
-#[derive(Unique)]
+#[derive(Unique, Clone)]
 pub(crate) struct WidgetContext {
     sender: UnboundedSender<Event>,
 }

+ 13 - 23
packages/rink/src/widgets/number.rs

@@ -3,14 +3,12 @@ use std::{collections::HashMap, io::stdout};
 use crossterm::{cursor::MoveTo, execute};
 use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
 use dioxus_native_core::{
+    custom_element::CustomElement,
     node::OwnedAttributeDiscription,
     node_ref::AttributeMask,
     prelude::{ElementNode, NodeType},
     real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
-    utils::{
-        cursor::{Cursor, Pos},
-        widget_watcher::Widget,
-    },
+    utils::cursor::{Cursor, Pos},
     NodeId,
 };
 use shipyard::UniqueView;
@@ -169,7 +167,7 @@ impl Number {
         self.text = (num - 1.0).to_string();
     }
 
-    fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) {
+    fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) {
         let key = data.key();
         let is_text = match key.clone() {
             Key::ArrowLeft | Key::ArrowRight | Key::Backspace => true,
@@ -235,14 +233,11 @@ impl Number {
         }
     }
 
-    fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) {
         if self.dragging {
             let id = root.id();
             let offset = data.element_coordinates();
             let mut new = Pos::new(offset.x as usize, offset.y as usize);
-            if self.border {
-                new.col = new.col.saturating_sub(1);
-            }
             // textboxs are only one line tall
             new.row = 0;
 
@@ -254,12 +249,9 @@ impl Number {
         }
     }
 
-    fn handle_mousedown(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) {
         let offset = data.element_coordinates();
         let mut new = Pos::new(offset.x as usize, offset.y as usize);
-        if self.border {
-            new.col = new.col.saturating_sub(1);
-        }
 
         // textboxs are only one line tall
         new.row = 0;
@@ -297,10 +289,14 @@ impl Number {
     }
 }
 
-impl Widget for Number {
+impl CustomElement for Number {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<NodeId> {
+        vec![self.div_wrapper]
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         let node_type = root.node_type();
         let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
 
@@ -364,8 +360,6 @@ impl Widget for Number {
         root.add_event_listener("keydown");
         root.add_event_listener("focusout");
 
-        root.add_child(div_wrapper_id);
-
         Self {
             pre_cursor_text: pre_text_id,
             highlighted_text: highlighted_text_id,
@@ -379,7 +373,7 @@ impl Widget for Number {
 
     fn attributes_changed(
         &mut self,
-        mut root: dioxus_native_core::real_dom::NodeMut<()>,
+        mut root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match attributes {
@@ -418,11 +412,7 @@ impl Widget for Number {
 }
 
 impl RinkWidget for Number {
-    fn handle_event(
-        &mut self,
-        event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
-    ) {
+    fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) {
         match event.name {
             "keydown" => {
                 if let EventData::Keyboard(data) = &event.data {

+ 13 - 23
packages/rink/src/widgets/password.rs

@@ -3,14 +3,12 @@ use std::{collections::HashMap, io::stdout};
 use crossterm::{cursor::MoveTo, execute};
 use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
 use dioxus_native_core::{
+    custom_element::CustomElement,
     node::OwnedAttributeDiscription,
     node_ref::AttributeMask,
     prelude::{ElementNode, NodeType},
     real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
-    utils::{
-        cursor::{Cursor, Pos},
-        widget_watcher::Widget,
-    },
+    utils::cursor::{Cursor, Pos},
     NodeId,
 };
 use shipyard::UniqueView;
@@ -159,7 +157,7 @@ impl Password {
         }
     }
 
-    fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) {
+    fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) {
         let key = data.key();
         let modifiers = data.modifiers();
         let code = data.code();
@@ -203,14 +201,11 @@ impl Password {
         }
     }
 
-    fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) {
         if self.dragging {
             let id = root.id();
             let offset = data.element_coordinates();
             let mut new = Pos::new(offset.x as usize, offset.y as usize);
-            if self.border {
-                new.col = new.col.saturating_sub(1);
-            }
             // textboxs are only one line tall
             new.row = 0;
 
@@ -222,12 +217,9 @@ impl Password {
         }
     }
 
-    fn handle_mousedown(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) {
         let offset = data.element_coordinates();
         let mut new = Pos::new(offset.x as usize, offset.y as usize);
-        if self.border {
-            new.col = new.col.saturating_sub(1);
-        }
 
         // textboxs are only one line tall
         new.row = 0;
@@ -265,10 +257,14 @@ impl Password {
     }
 }
 
-impl Widget for Password {
+impl CustomElement for Password {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<NodeId> {
+        vec![self.div_wrapper]
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         let node_type = root.node_type();
         let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
 
@@ -332,8 +328,6 @@ impl Widget for Password {
         root.add_event_listener("keydown");
         root.add_event_listener("focusout");
 
-        root.add_child(div_wrapper_id);
-
         Self {
             pre_cursor_text: pre_text_id,
             highlighted_text: highlighted_text_id,
@@ -347,7 +341,7 @@ impl Widget for Password {
 
     fn attributes_changed(
         &mut self,
-        mut root: dioxus_native_core::real_dom::NodeMut<()>,
+        mut root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match attributes {
@@ -386,11 +380,7 @@ impl Widget for Password {
 }
 
 impl RinkWidget for Password {
-    fn handle_event(
-        &mut self,
-        event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
-    ) {
+    fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) {
         match event.name {
             "keydown" => {
                 if let EventData::Keyboard(data) = &event.data {

+ 13 - 15
packages/rink/src/widgets/slider.rs

@@ -2,11 +2,11 @@ use std::collections::HashMap;
 
 use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
 use dioxus_native_core::{
+    custom_element::CustomElement,
     node::{OwnedAttributeDiscription, OwnedAttributeValue},
     node_ref::AttributeMask,
     prelude::{ElementNode, NodeType},
     real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
-    utils::widget_watcher::Widget,
     NodeId,
 };
 use shipyard::UniqueView;
@@ -205,7 +205,7 @@ impl Slider {
         }
     }
 
-    fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) {
+    fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) {
         let key = data.key();
 
         let step = self.step();
@@ -227,7 +227,7 @@ impl Slider {
         self.write_value(rdom, id);
     }
 
-    fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) {
         if !data.held_buttons().is_empty() {
             let id = root.id();
             let rdom = root.real_dom_mut();
@@ -250,10 +250,14 @@ impl Slider {
     }
 }
 
-impl Widget for Slider {
+impl CustomElement for Slider {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<NodeId> {
+        vec![self.div_wrapper]
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         let node_type = root.node_type();
         let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
 
@@ -364,12 +368,10 @@ impl Widget for Slider {
         div_wrapper.add_child(cursor_span_id);
         div_wrapper.add_child(post_cursor_div_id);
 
-        div_wrapper.add_event_listener("mousemove");
-        div_wrapper.add_event_listener("mousedown");
+        root.add_event_listener("mousemove");
+        root.add_event_listener("mousedown");
         root.add_event_listener("keydown");
 
-        root.add_child(div_wrapper_id);
-
         Self {
             pre_cursor_div: pre_cursor_div_id,
             post_cursor_div: post_cursor_div_id,
@@ -381,7 +383,7 @@ impl Widget for Slider {
 
     fn attributes_changed(
         &mut self,
-        mut root: dioxus_native_core::real_dom::NodeMut<()>,
+        mut root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match attributes {
@@ -428,11 +430,7 @@ impl Widget for Slider {
 }
 
 impl RinkWidget for Slider {
-    fn handle_event(
-        &mut self,
-        event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
-    ) {
+    fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) {
         match event.name {
             "keydown" => {
                 if let EventData::Keyboard(data) = &event.data {

+ 14 - 23
packages/rink/src/widgets/textbox.rs

@@ -3,14 +3,12 @@ use std::{collections::HashMap, io::stdout};
 use crossterm::{cursor::MoveTo, execute};
 use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
 use dioxus_native_core::{
+    custom_element::CustomElement,
     node::OwnedAttributeDiscription,
     node_ref::AttributeMask,
     prelude::{ElementNode, NodeType},
     real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
-    utils::{
-        cursor::{Cursor, Pos},
-        widget_watcher::Widget,
-    },
+    utils::cursor::{Cursor, Pos},
     NodeId,
 };
 use shipyard::UniqueView;
@@ -159,7 +157,7 @@ impl TextBox {
         }
     }
 
-    fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) {
+    fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) {
         let key = data.key();
         let modifiers = data.modifiers();
         let code = data.code();
@@ -203,14 +201,12 @@ impl TextBox {
         }
     }
 
-    fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) {
         if self.dragging {
             let id = root.id();
             let offset = data.element_coordinates();
             let mut new = Pos::new(offset.x as usize, offset.y as usize);
-            if self.border {
-                new.col = new.col.saturating_sub(1);
-            }
+
             // textboxs are only one line tall
             new.row = 0;
 
@@ -222,12 +218,9 @@ impl TextBox {
         }
     }
 
-    fn handle_mousedown(&mut self, root: &mut NodeMut, data: &MouseData) {
+    fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) {
         let offset = data.element_coordinates();
         let mut new = Pos::new(offset.x as usize, offset.y as usize);
-        if self.border {
-            new.col = new.col.saturating_sub(1);
-        }
 
         // textboxs are only one line tall
         new.row = 0;
@@ -265,10 +258,14 @@ impl TextBox {
     }
 }
 
-impl Widget for TextBox {
+impl CustomElement for TextBox {
     const NAME: &'static str = "input";
 
-    fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
+    fn roots(&self) -> Vec<NodeId> {
+        vec![self.div_wrapper]
+    }
+
+    fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
         let node_type = root.node_type();
         let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
 
@@ -332,8 +329,6 @@ impl Widget for TextBox {
         root.add_event_listener("keydown");
         root.add_event_listener("focusout");
 
-        root.add_child(div_wrapper_id);
-
         Self {
             pre_cursor_text: pre_text_id,
             highlighted_text: highlighted_text_id,
@@ -347,7 +342,7 @@ impl Widget for TextBox {
 
     fn attributes_changed(
         &mut self,
-        mut root: dioxus_native_core::real_dom::NodeMut<()>,
+        mut root: dioxus_native_core::real_dom::NodeMut,
         attributes: &dioxus_native_core::node_ref::AttributeMask,
     ) {
         match attributes {
@@ -386,11 +381,7 @@ impl Widget for TextBox {
 }
 
 impl RinkWidget for TextBox {
-    fn handle_event(
-        &mut self,
-        event: &crate::Event,
-        node: &mut dioxus_native_core::real_dom::NodeMut,
-    ) {
+    fn handle_event(&mut self, event: &crate::Event, node: NodeMut) {
         match event.name {
             "keydown" => {
                 if let EventData::Keyboard(data) = &event.data {