Quellcode durchsuchen

make tui agnostic over framework

Evan Almloff vor 2 Jahren
Ursprung
Commit
c805bc25af

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

@@ -241,7 +241,7 @@ fn create_template_node(rdom: &mut RealDom, node: &TemplateNode) -> NodeId {
     }
 }
 
-trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
+pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
     fn mounted_id(&self) -> Option<ElementId> {
         self.get().copied()
     }

+ 11 - 0
packages/native-core/src/lib.rs

@@ -1,9 +1,12 @@
 use std::any::Any;
+use std::future::Future;
 use std::hash::BuildHasherDefault;
+use std::pin::Pin;
 
 pub use node_ref::NodeMask;
 pub use passes::AnyMapLike;
 pub use passes::{Dependancy, Pass, TypeErasedPass};
+use prelude::FromAnyValue;
 pub use real_dom::{NodeMut, NodeRef, RealDom};
 use rustc_hash::FxHasher;
 pub use tree::NodeId;
@@ -31,3 +34,11 @@ pub mod prelude {
 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>;
+
+pub trait Renderer<V: FromAnyValue + Send + Sync, E> {
+    fn render(&mut self, root: NodeMut<V>);
+    fn handle_event(&mut self, node: NodeMut<V>, event: &str, value: E);
+    fn poll_async(&mut self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
+        Box::pin(async {})
+    }
+}

+ 4 - 3
packages/tui/Cargo.toml

@@ -13,8 +13,8 @@ license = "MIT/Apache-2.0"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-dioxus = { path = "../dioxus", version = "^0.3.0" }
-dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] }
+dioxus = { path = "../dioxus", version = "^0.3.0", optional = true }
+dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"], optional = true }
 dioxus-html = { path = "../html", version = "^0.3.0" }
 dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
 dioxus-hot-reload = { path = "../hot-reload", optional = true }
@@ -40,5 +40,6 @@ name = "update"
 harness = false
 
 [features]
-default = ["hot-reload"]
+default = ["hot-reload", "dioxus-bindings"]
 hot-reload = ["dioxus-hot-reload"]
+dioxus-bindings = ["dioxus", "dioxus-core", "dioxus-native-core/dioxus"]

+ 66 - 0
packages/tui/src/dioxus.rs

@@ -0,0 +1,66 @@
+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);
+    let mut rdom = RealDom::new(Box::new([
+        TaffyLayout::to_type_erased(),
+        Focus::to_type_erased(),
+        StyleModifier::to_type_erased(),
+        PreventDefault::to_type_erased(),
+    ]));
+
+    let (handler, state, register_event) = RinkInputHandler::craete(&mut rdom);
+
+    // Setup input handling
+    let (event_tx, event_rx) = unbounded();
+    let event_tx_clone = event_tx.clone();
+    if !cfg.headless {
+        std::thread::spawn(move || {
+            let tick_rate = Duration::from_millis(1000);
+            loop {
+                if crossterm::event::poll(tick_rate).unwrap() {
+                    let evt = crossterm::event::read().unwrap();
+                    if event_tx.unbounded_send(InputEvent::UserInput(evt)).is_err() {
+                        break;
+                    }
+                }
+            }
+        });
+    }
+
+    let cx = dom.base_scope();
+    let rdom = Rc::new(RefCell::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 rdom = rdom.borrow_mut();
+        let mutations = dom.rebuild();
+        rdom.apply_mutations(mutations);
+        let mut any_map = SendAnyMap::new();
+        any_map.insert(taffy.clone());
+        let _ = rdom.update_state(any_map, false);
+    }
+
+    render_vdom(
+        &mut dom,
+        event_rx,
+        handler,
+        cfg,
+        rdom,
+        taffy,
+        register_event,
+    )
+    .unwrap();
+}

+ 19 - 46
packages/tui/src/focus.rs

@@ -2,7 +2,7 @@ use crate::prevent_default::PreventDefault;
 
 use dioxus_native_core::{
     node_ref::{AttributeMaskBuilder, NodeMaskBuilder},
-    real_dom::{NodeImmutable, NodeMutable},
+    real_dom::NodeImmutable,
     utils::{ElementProduced, PersistantElementIter},
     Dependancy, NodeId, Pass, RealDom, SendAnyMap,
 };
@@ -64,7 +64,7 @@ pub(crate) struct Focus {
 }
 
 impl Pass for Focus {
-    const NODE_MASK: NodeMaskBuilder = NodeMaskBuilder::new()
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
         .with_attrs(AttributeMaskBuilder::Some(FOCUS_ATTRIBUTES))
         .with_listeners();
 
@@ -138,7 +138,6 @@ impl Pass for Focus {
 const FOCUS_EVENTS: &[&str] = &["keydown", "keypress", "keyup"];
 const FOCUS_ATTRIBUTES: &[&str] = &["tabindex"];
 
-#[derive(Default)]
 pub(crate) struct FocusState {
     pub(crate) focus_iter: PersistantElementIter,
     pub(crate) last_focused_id: Option<NodeId>,
@@ -147,6 +146,16 @@ pub(crate) struct FocusState {
 }
 
 impl FocusState {
+    pub fn create(rdom: &mut RealDom) -> Self {
+        let mut 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 RealDom, forward: bool) -> bool {
         if let Some(last) = self.last_focused_id {
@@ -242,14 +251,13 @@ impl FocusState {
         }
 
         if let Some(id) = next_focus {
-            let mut node = rdom.get_mut_raw(id).unwrap();
+            let mut node = rdom.get_mut(id).unwrap();
             if !node.get::<Focus>().unwrap().level.focusable() {
                 panic!()
             }
             node.insert(Focused(true));
             if let Some(old) = self.last_focused_id.replace(id) {
-                let mut old = rdom.get_mut_raw(old).unwrap();
-                let focused = old.get_mut::<Focused>().unwrap();
+                let focused = rdom.get_state_mut_raw::<Focused>(old).unwrap();
                 focused.0 = false;
             }
             // reset the position to the currently focused element
@@ -261,49 +269,14 @@ impl FocusState {
         false
     }
 
-    pub(crate) fn prune(&mut self, mutations: &dioxus_core::Mutations, rdom: &RealDom) {
-        fn remove_children(to_prune: &mut [&mut Option<NodeId>], rdom: &RealDom, removed: NodeId) {
-            for opt in to_prune.iter_mut() {
-                if let Some(id) = opt {
-                    if *id == removed {
-                        **opt = None;
-                    }
-                }
-            }
-            let node = rdom.get(removed).unwrap();
-            if let Some(children) = node.child_ids() {
-                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 RealDom, id: NodeId) {
         if let Some(old) = self.last_focused_id.replace(id) {
-            let mut node = rdom.get_mut_raw(old).unwrap();
-            node.get_mut::<Focused>().unwrap().0 = false;
+            let mut focused = rdom.get_state_mut_raw::<Focused>(old).unwrap();
+            *focused = Focused(false);
         }
-        let mut node = rdom.get_mut_raw(id).unwrap();
-        node.insert(Focused(true));
+        let mut focused = rdom.get_state_mut_raw::<Focused>(id).unwrap();
+        *focused = Focused(true);
+        let mut node = rdom.get(id).unwrap();
         self.focus_level = node.get::<Focus>().unwrap().level;
         // reset the position to the currently focused element
         while self.focus_iter.next(rdom).id() != id {}

+ 83 - 89
packages/tui/src/hooks.rs

@@ -1,7 +1,6 @@
 use crossterm::event::{
     Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
 };
-use dioxus_core::*;
 use dioxus_native_core::real_dom::NodeImmutable;
 use dioxus_native_core::{NodeId, NodeRef, RealDom};
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -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},
@@ -28,9 +26,9 @@ use crate::layout::TaffyLayout;
 use crate::{layout_to_screen_space, FocusState};
 
 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,
 }
 
@@ -71,24 +69,6 @@ pub(crate) struct Event {
 
 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 {
@@ -101,14 +81,14 @@ pub struct InnerInputState {
 }
 
 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),
         }
     }
 
@@ -149,7 +129,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
@@ -166,6 +145,7 @@ impl InnerInputState {
 
                 self.last_key_pressed = Some((k.clone(), Instant::now()));
             }
+            _ => {}
         }
     }
 
@@ -202,31 +182,27 @@ impl InnerInputState {
             // elements with listeners will always have a element id
             if let Some(id) = self.focus_state.last_focused_id {
                 let element = dom.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.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"),
+                });
             }
         }
 
@@ -260,29 +236,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: 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 = 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 = current_parent.parent_id().and_then(|id| dom.get(id));
                 }
-                if let Some(id) = node.mounted_id() {
-                    resolved_events.push(Event {
-                        name,
-                        id,
-                        data,
-                        bubbles: event_bubbles(name),
-                    })
-                }
+                resolved_events.push(Event {
+                    name,
+                    id,
+                    data,
+                    bubbles: event_bubbles(name),
+                })
             }
         }
 
@@ -346,7 +320,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,
@@ -370,7 +347,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,
@@ -393,7 +370,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,
@@ -413,7 +393,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,
@@ -434,7 +417,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,
@@ -456,7 +442,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,
@@ -479,7 +468,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,
@@ -503,7 +495,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,
@@ -528,7 +520,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,
@@ -551,7 +546,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,
@@ -571,7 +569,7 @@ impl InnerInputState {
                     let currently_contains = layout_contains_point(node_layout, new_pos);
 
                     if currently_contains && node.get::<Focus>().unwrap().level.focusable() {
-                        focus_id = Some(node.node_data().node_id);
+                        focus_id = Some(node.id());
                     }
                 });
                 if let Some(id) = focus_id {
@@ -612,7 +610,9 @@ 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() -> (
+    pub fn craete(
+        rdom: &mut RealDom,
+    ) -> (
         Self,
         Rc<RefCell<InnerInputState>>,
         impl FnMut(crossterm::event::Event),
@@ -628,7 +628,7 @@ impl RinkInputHandler {
             }
         };
 
-        let state = Rc::new(RefCell::new(InnerInputState::new()));
+        let state = Rc::new(RefCell::new(InnerInputState::create(rdom)));
 
         (
             Self {
@@ -640,10 +640,6 @@ impl RinkInputHandler {
         )
     }
 
-    pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &RealDom) {
-        self.state.borrow_mut().focus_state.prune(mutations, rdom);
-    }
-
     pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut RealDom) -> Vec<Event> {
         let mut resolved_events = Vec::new();
 
@@ -675,14 +671,14 @@ 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 {
@@ -690,14 +686,12 @@ impl RinkInputHandler {
                 for data in &datas {
                     let focused = node.get::<Focused>();
                     if focused.is_some() && focused.unwrap().0 {
-                        if let Some(id) = node.mounted_id() {
-                            resolved_events.push(Event {
-                                name: event,
-                                id,
-                                data: data.clone(),
-                                bubbles: event_bubbles(event),
-                            });
-                        }
+                        resolved_events.push(Event {
+                            name: event,
+                            id: node.id(),
+                            data: data.clone(),
+                            bubbles: event_bubbles(event),
+                        });
                     }
                 }
             }
@@ -779,7 +773,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))

+ 1 - 1
packages/tui/src/layout.rs

@@ -47,7 +47,7 @@ impl Pass for TaffyLayout {
     type ParentDependencies = ();
     type NodeDependencies = ();
 
-    const NODE_MASK: NodeMaskBuilder = NodeMaskBuilder::new()
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
         .with_attrs(AttributeMaskBuilder::Some(SORTED_LAYOUT_ATTRS))
         .with_text();
 

+ 58 - 97
packages/tui/src/lib.rs

@@ -6,24 +6,17 @@ use crossterm::{
     execute,
     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 };
-use dioxus_core::*;
-use dioxus_native_core::{node_ref::NodeMaskBuilder, real_dom::NodeImmutable, Pass};
+use dioxus_html::EventData;
+use dioxus_native_core::{node_ref::NodeMaskBuilder, real_dom::NodeImmutable, Pass, Renderer};
 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, StreamExt};
 use futures_channel::mpsc::unbounded;
 use layout::TaffyLayout;
 use prevent_default::PreventDefault;
-use query::Query;
-use std::rc::Rc;
-use std::{
-    cell::RefCell,
-    sync::{Arc, Mutex},
-};
+use std::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::*};
@@ -41,7 +34,7 @@ mod render;
 mod style;
 mod style_attributes;
 mod widget;
-mod widgets;
+// mod widgets;
 
 pub use config::*;
 pub use hooks::*;
@@ -75,21 +68,21 @@ 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: Renderer<(), Rc<EventData>>>(
+    cfg: Config,
+    f: impl FnOnce(&Arc<RwLock<RealDom>>, &Arc<Mutex<Taffy>>, UnboundedSender<InputEvent>) -> R,
+) -> Result<()> {
+    let mut rdom = RealDom::new(Box::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, state, mut register_event) = RinkInputHandler::craete(&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 || {
@@ -105,51 +98,19 @@ 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(Box::new([
-        TaffyLayout::to_type_erased(),
-        Focus::to_type_erased(),
-        StyleModifier::to_type_erased(),
-        PreventDefault::to_type_erased(),
-    ]))));
+    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 = f(&rdom, &taffy, event_tx_clone);
 
     {
-        let mut rdom = rdom.borrow_mut();
-        let mutations = dom.rebuild();
-        rdom.apply_mutations(mutations);
+        let mut rdom = rdom.write().unwrap();
+        let root_id = rdom.root_id();
+        renderer.render(rdom.get_mut(root_id).unwrap());
         let mut any_map = SendAnyMap::new();
         any_map.insert(taffy.clone());
         let _ = rdom.update_state(any_map, false);
     }
 
-    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<RealDom>>,
-    taffy: Arc<Mutex<Taffy>>,
-    mut register_event: impl FnMut(crossterm::event::Event),
-) -> Result<()> {
     tokio::runtime::Builder::new_current_thread()
         .enable_all()
         .build()?
@@ -225,16 +186,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.get(NodeId(0)).unwrap();
+                            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.write().unwrap();
                         resize(
                             Rect {
                                 x: 0,
@@ -248,13 +209,13 @@ fn render_vdom(
                     }
                 }
 
-                let mut hot_reload_msg = None;
+                // 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();
+                    let wait = renderer.poll_async();
+                    // #[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();
 
                     pin_mut!(wait);
 
@@ -283,40 +244,40 @@ fn render_vdom(
                                 register_event(evt);
                             }
                         },
-                        Some(msg) = hot_reload_wait => {
-                            hot_reload_msg = Some(msg);
-                        }
+                        // Some(msg) = hot_reload_wait => {
+                        //     hot_reload_msg = Some(msg);
+                        // }
                     }
                 }
 
-                // 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) => {
-                            vdom.replace_template(template);
-                        }
-                        dioxus_hot_reload::HotReloadMsg::Shutdown => {
-                            break;
-                        }
-                    }
-                }
+                // // 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) => {
+                //             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 mut rdom = rdom.write().unwrap();
+                        let evts = handler
+                            .get_events(&taffy.lock().expect("taffy lock poisoned"), &mut rdom);
                         updated |= handler.state().focus_state.clean();
+
+                        for e in evts {
+                            let node = rdom.get_mut(e.id).unwrap();
+                            renderer.handle_event(node, e.name, e.data);
+                        }
                     }
-                    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);
+                    let mut rdom = rdom.write().unwrap();
                     // updates the dom's nodes
-                    rdom.apply_mutations(mutations);
+                    let root_id = rdom.root_id();
+                    renderer.render(rdom.get_mut(root_id).unwrap());
                     // update the style and layout
                     let mut any_map = SendAnyMap::new();
                     any_map.insert(taffy.clone());
@@ -346,7 +307,7 @@ fn render_vdom(
 }
 
 #[derive(Debug)]
-enum InputEvent {
+pub enum InputEvent {
     UserInput(TermEvent),
     Close,
 }

+ 1 - 1
packages/tui/src/prelude/mod.rs

@@ -1 +1 @@
-pub use crate::widgets::*;
+// pub use crate::widgets::*;

+ 1 - 1
packages/tui/src/prevent_default.rs

@@ -30,7 +30,7 @@ impl Pass for PreventDefault {
     type ChildDependencies = ();
     type NodeDependencies = ();
 
-    const NODE_MASK: dioxus_native_core::node_ref::NodeMaskBuilder =
+    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",

+ 4 - 5
packages/tui/src/query.rs

@@ -4,8 +4,7 @@ use std::{
     sync::{Arc, Mutex, MutexGuard},
 };
 
-use dioxus_core::ElementId;
-use dioxus_native_core::{real_dom::NodeImmutable, RealDom};
+use dioxus_native_core::{real_dom::NodeImmutable, NodeId, RealDom};
 use taffy::{
     geometry::Point,
     prelude::{Layout, Size},
@@ -52,7 +51,7 @@ pub struct Query {
 }
 
 impl Query {
-    pub fn get(&self, id: ElementId) -> ElementRef {
+    pub fn get(&self, id: NodeId) -> ElementRef {
         ElementRef::new(
             self.rdom.borrow(),
             self.stretch.lock().expect("taffy lock poisoned"),
@@ -64,11 +63,11 @@ impl Query {
 pub struct ElementRef<'a> {
     inner: Ref<'a, RealDom>,
     stretch: MutexGuard<'a, Taffy>,
-    id: ElementId,
+    id: NodeId,
 }
 
 impl<'a> ElementRef<'a> {
-    fn new(inner: Ref<'a, RealDom>, stretch: MutexGuard<'a, Taffy>, id: ElementId) -> Self {
+    fn new(inner: Ref<'a, RealDom>, stretch: MutexGuard<'a, Taffy>, id: NodeId) -> Self {
         Self { inner, stretch, id }
     }
 

+ 1 - 1
packages/tui/src/style_attributes.rs

@@ -51,7 +51,7 @@ impl Pass for StyleModifier {
     type NodeDependencies = ();
 
     // todo: seperate each attribute into it's own class
-    const NODE_MASK: NodeMaskBuilder = NodeMaskBuilder::new()
+    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
         .with_attrs(AttributeMaskBuilder::Some(SORTED_STYLE_ATTRS))
         .with_element();
 

+ 2 - 2
packages/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

@@ -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}"
         }
-    })
+    }
 }

+ 2 - 2
packages/tui/src/widgets/number.rs

@@ -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}",
@@ -205,5 +205,5 @@ pub(crate) fn NumbericInput<'a>(cx: Scope<'a, NumbericInputProps>) -> Element<'a
 
             "{text_after_second_cursor}"
         }
-    })
+    }
 }

+ 2 - 2
packages/tui/src/widgets/password.rs

@@ -114,7 +114,7 @@ pub(crate) fn Password<'a>(cx: Scope<'a, PasswordProps>) -> Element<'a> {
         }
     };
 
-    cx.render(rsx! {
+    render! {
         div {
             width: "{width}",
             height: "{height}",
@@ -187,5 +187,5 @@ pub(crate) fn Password<'a>(cx: Scope<'a, PasswordProps>) -> Element<'a> {
 
             "{text_after_second_cursor}"
         }
-    })
+    }
 }

+ 2 - 2
packages/tui/src/widgets/slider.rs

@@ -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)",
             }
         }
-    })
+    }
 }

+ 2 - 2
packages/tui/src/widgets/textbox.rs

@@ -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}",
@@ -178,5 +178,5 @@ pub(crate) fn TextBox<'a>(cx: Scope<'a, TextBoxProps>) -> Element<'a> {
 
             "{text_after_second_cursor}"
         }
-    })
+    }
 }