Przeglądaj źródła

create driven example for tui renderer

Evan Almloff 2 lat temu
rodzic
commit
71e34452da

+ 21 - 8
packages/native-core/src/dioxus.rs

@@ -2,7 +2,10 @@ use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_hash::{FxHashMap, FxHashSet};
 
 
 use crate::{
 use crate::{
-    node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue},
+    node::{
+        ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
+        TextNode,
+    },
     prelude::NodeImmutable,
     prelude::NodeImmutable,
     real_dom::NodeTypeMut,
     real_dom::NodeTypeMut,
     NodeId, NodeMut, RealDom,
     NodeId, NodeMut, RealDom,
@@ -83,7 +86,10 @@ impl DioxusState {
                     self.stack.push(node_id);
                     self.stack.push(node_id);
                 }
                 }
                 CreateTextNode { value, id } => {
                 CreateTextNode { value, id } => {
-                    let node_data = NodeType::Text(value.to_string());
+                    let node_data = NodeType::Text(TextNode {
+                        listeners: FxHashSet::default(),
+                        text: value.to_string(),
+                    });
                     let node = rdom.create_node(node_data);
                     let node = rdom.create_node(node_data);
                     let node_id = node.id();
                     let node_id = node.id();
                     self.set_element_id(node, id);
                     self.set_element_id(node, id);
@@ -97,7 +103,10 @@ impl DioxusState {
                     if let NodeTypeMut::Text(text) = node.node_type_mut() {
                     if let NodeTypeMut::Text(text) = node.node_type_mut() {
                         *text = value.to_string();
                         *text = value.to_string();
                     } else {
                     } else {
-                        node.set_type(NodeType::Text(value.to_string()));
+                        node.set_type(NodeType::Text(TextNode {
+                            text: value.to_string(),
+                            listeners: FxHashSet::default(),
+                        }));
                     }
                     }
                 }
                 }
                 LoadTemplate { name, index, id } => {
                 LoadTemplate { name, index, id } => {
@@ -153,14 +162,12 @@ impl DioxusState {
                             element.remove_attributes(&OwnedAttributeDiscription {
                             element.remove_attributes(&OwnedAttributeDiscription {
                                 name: name.to_string(),
                                 name: name.to_string(),
                                 namespace: ns.map(|s| s.to_string()),
                                 namespace: ns.map(|s| s.to_string()),
-                                volatile: false,
                             });
                             });
                         } else {
                         } else {
                             element.set_attribute(
                             element.set_attribute(
                                 OwnedAttributeDiscription {
                                 OwnedAttributeDiscription {
                                     name: name.to_string(),
                                     name: name.to_string(),
                                     namespace: ns.map(|s| s.to_string()),
                                     namespace: ns.map(|s| s.to_string()),
-                                    volatile: false,
                                 },
                                 },
                                 OwnedAttributeValue::from(value),
                                 OwnedAttributeValue::from(value),
                             );
                             );
@@ -219,7 +226,6 @@ fn create_template_node(rdom: &mut RealDom, node: &TemplateNode) -> NodeId {
                             OwnedAttributeDiscription {
                             OwnedAttributeDiscription {
                                 namespace: namespace.map(|s| s.to_string()),
                                 namespace: namespace.map(|s| s.to_string()),
                                 name: name.to_string(),
                                 name: name.to_string(),
-                                volatile: false,
                             },
                             },
                             OwnedAttributeValue::Text(value.to_string()),
                             OwnedAttributeValue::Text(value.to_string()),
                         )),
                         )),
@@ -235,9 +241,16 @@ fn create_template_node(rdom: &mut RealDom, node: &TemplateNode) -> NodeId {
             }
             }
             node_id
             node_id
         }
         }
-        TemplateNode::Text { text } => rdom.create_node(NodeType::Text(text.to_string())).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::Dynamic { .. } => rdom.create_node(NodeType::Placeholder).id(),
-        TemplateNode::DynamicText { .. } => rdom.create_node(NodeType::Text(String::new())).id(),
+        TemplateNode::DynamicText { .. } => {
+            rdom.create_node(NodeType::Text(TextNode::default())).id()
+        }
     }
     }
 }
 }
 
 

+ 3 - 5
packages/native-core/src/lib.rs

@@ -35,10 +35,8 @@ pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
 pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
 pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
 pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
 pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
 
 
-pub trait Renderer<V: FromAnyValue + Send + Sync, E> {
+pub trait Renderer<E, V: FromAnyValue + Send + Sync = ()> {
     fn render(&mut self, root: NodeMut<V>);
     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 {})
-    }
+    fn handle_event(&mut self, node: NodeMut<V>, event: &str, value: E, bubbles: bool);
+    fn poll_async(&mut self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
 }
 }

+ 17 - 3
packages/native-core/src/node.rs

@@ -1,7 +1,7 @@
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_hash::{FxHashMap, FxHashSet};
 use std::{any::Any, fmt::Debug};
 use std::{any::Any, fmt::Debug};
 
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Default)]
 pub struct ElementNode<V: FromAnyValue = ()> {
 pub struct ElementNode<V: FromAnyValue = ()> {
     pub tag: String,
     pub tag: String,
     pub namespace: Option<String>,
     pub namespace: Option<String>,
@@ -9,10 +9,25 @@ pub struct ElementNode<V: FromAnyValue = ()> {
     pub listeners: FxHashSet<String>,
     pub listeners: FxHashSet<String>,
 }
 }
 
 
+#[derive(Debug, Clone, Default)]
+pub struct TextNode {
+    pub text: String,
+    pub listeners: FxHashSet<String>,
+}
+
+impl TextNode {
+    pub fn new(text: String) -> Self {
+        Self {
+            text,
+            listeners: Default::default(),
+        }
+    }
+}
+
 /// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
 /// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
 pub enum NodeType<V: FromAnyValue = ()> {
 pub enum NodeType<V: FromAnyValue = ()> {
-    Text(String),
+    Text(TextNode),
     Element(ElementNode<V>),
     Element(ElementNode<V>),
     Placeholder,
     Placeholder,
 }
 }
@@ -21,7 +36,6 @@ pub enum NodeType<V: FromAnyValue = ()> {
 pub struct OwnedAttributeDiscription {
 pub struct OwnedAttributeDiscription {
     pub name: String,
     pub name: String,
     pub namespace: Option<String>,
     pub namespace: Option<String>,
-    pub volatile: bool,
 }
 }
 
 
 /// An attribute on a DOM node, such as `id="my-thing"` or
 /// An attribute on a DOM node, such as `id="my-thing"` or

+ 2 - 1
packages/native-core/src/node_ref.rs

@@ -73,10 +73,11 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
         self.mask
         self.mask
             .text
             .text
             .then_some(match &self.inner {
             .then_some(match &self.inner {
-                NodeType::Text(text) => Some(&**text),
+                NodeType::Text(text) => Some(&text.text),
                 _ => None,
                 _ => None,
             })
             })
             .flatten()
             .flatten()
+            .map(|x| &**x)
     }
     }
 
 
     /// Get the listeners if it is enabled in the mask
     /// Get the listeners if it is enabled in the mask

+ 21 - 4
packages/native-core/src/real_dom.rs

@@ -5,7 +5,7 @@ use std::rc::Rc;
 use std::sync::RwLock;
 use std::sync::RwLock;
 
 
 use crate::node::{
 use crate::node::{
-    ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
+    ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode,
 };
 };
 use crate::node_ref::{NodeMask, NodeMaskBuilder};
 use crate::node_ref::{NodeMask, NodeMaskBuilder};
 use crate::node_watcher::NodeWatcher;
 use crate::node_watcher::NodeWatcher;
@@ -487,6 +487,19 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
 
 
     pub fn remove(&mut self) {
     pub fn remove(&mut self) {
         let id = self.id();
         let id = self.id();
+        if let NodeType::Element(ElementNode { listeners, .. })
+        | NodeType::Text(TextNode { listeners, .. }) =
+            self.dom.get_state_mut_raw::<NodeType<V>>(id).unwrap()
+        {
+            let listeners = std::mem::take(listeners);
+            for event in listeners {
+                self.dom
+                    .nodes_listening
+                    .get_mut(&event)
+                    .unwrap()
+                    .remove(&id);
+            }
+        }
         self.mark_removed();
         self.mark_removed();
         if let Some(parent_id) = self.real_dom_mut().tree.parent_id(id) {
         if let Some(parent_id) = self.real_dom_mut().tree.parent_id(id) {
             self.real_dom_mut()
             self.real_dom_mut()
@@ -519,7 +532,9 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
     pub fn add_event_listener(&mut self, event: &str) {
     pub fn add_event_listener(&mut self, event: &str) {
         let id = self.id();
         let id = self.id();
         let node_type: &mut NodeType<V> = self.dom.tree.get_mut(self.id).unwrap();
         let node_type: &mut NodeType<V> = self.dom.tree.get_mut(self.id).unwrap();
-        if let NodeType::Element(ElementNode { listeners, .. }) = node_type {
+        if let NodeType::Element(ElementNode { listeners, .. })
+        | NodeType::Text(TextNode { listeners, .. }) = node_type
+        {
             self.dom
             self.dom
                 .dirty_nodes
                 .dirty_nodes
                 .mark_dirty(self.id, NodeMaskBuilder::new().with_listeners().build());
                 .mark_dirty(self.id, NodeMaskBuilder::new().with_listeners().build());
@@ -540,7 +555,9 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
     pub fn remove_event_listener(&mut self, event: &str) {
     pub fn remove_event_listener(&mut self, event: &str) {
         let id = self.id();
         let id = self.id();
         let node_type: &mut NodeType<V> = self.dom.tree.get_mut(self.id).unwrap();
         let node_type: &mut NodeType<V> = self.dom.tree.get_mut(self.id).unwrap();
-        if let NodeType::Element(ElementNode { listeners, .. }) = node_type {
+        if let NodeType::Element(ElementNode { listeners, .. })
+        | NodeType::Text(TextNode { listeners, .. }) = node_type
+        {
             self.dom
             self.dom
                 .dirty_nodes
                 .dirty_nodes
                 .mark_dirty(self.id, NodeMaskBuilder::new().with_listeners().build());
                 .mark_dirty(self.id, NodeMaskBuilder::new().with_listeners().build());
@@ -579,7 +596,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
             NodeType::Text(text) => {
             NodeType::Text(text) => {
                 dirty_nodes.mark_dirty(self.id, NodeMaskBuilder::new().with_text().build());
                 dirty_nodes.mark_dirty(self.id, NodeMaskBuilder::new().with_text().build());
 
 
-                NodeTypeMut::Text(text)
+                NodeTypeMut::Text(&mut text.text)
             }
             }
             NodeType::Placeholder => NodeTypeMut::Placeholder,
             NodeType::Placeholder => NodeTypeMut::Placeholder,
         }
         }

+ 1 - 6
packages/native-core/src/tree.rs

@@ -262,12 +262,7 @@ pub struct TreeStateViewEntry<'a, 'b> {
 impl<'a, 'b> AnyMapLike<'a> for TreeStateViewEntry<'a, 'b> {
 impl<'a, 'b> AnyMapLike<'a> for TreeStateViewEntry<'a, 'b> {
     fn get<T: Any + Sync + Send>(self) -> Option<&'a T> {
     fn get<T: Any + Sync + Send>(self) -> Option<&'a T> {
         let slab = self.view.get_slab();
         let slab = self.view.get_slab();
-        dbg!(slab.is_some());
-        slab.and_then(|slab| {
-            let r = slab.get(self.id);
-            dbg!(r.is_some());
-            r
-        })
+        slab.and_then(|slab| slab.get(self.id))
     }
     }
 }
 }
 
 

+ 208 - 0
packages/tui/examples/driven.rs

@@ -0,0 +1,208 @@
+use dioxus_html::EventData;
+use dioxus_native_core::{
+    node::{OwnedAttributeDiscription, OwnedAttributeValue, TextNode},
+    prelude::*,
+    real_dom::{NodeImmutable, NodeTypeMut},
+    NodeId, Renderer,
+};
+use dioxus_tui::{self, render, Config};
+use std::sync::{Arc, RwLock};
+use std::{rc::Rc, sync::Mutex};
+use taffy::Taffy;
+
+struct Test([[usize; 10]; 10]);
+
+impl Renderer<Rc<EventData>> for Test {
+    fn render(&mut self, mut root: dioxus_native_core::NodeMut) {
+        // 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(
+                OwnedAttributeDiscription {
+                    name: "display".into(),
+                    namespace: None,
+                },
+                OwnedAttributeValue::Text("flex".into()),
+            );
+            el.set_attribute(
+                OwnedAttributeDiscription {
+                    name: "flex-direction".into(),
+                    namespace: None,
+                },
+                OwnedAttributeValue::Text("column".into()),
+            );
+            el.set_attribute(
+                OwnedAttributeDiscription {
+                    name: "width".into(),
+                    namespace: None,
+                },
+                OwnedAttributeValue::Text("100%".into()),
+            );
+            el.set_attribute(
+                OwnedAttributeDiscription {
+                    name: "height".into(),
+                    namespace: None,
+                },
+                OwnedAttributeValue::Text("100%".into()),
+            );
+        }
+
+        let root_id = root.id();
+        // Remove old grid. Frameworks should retain the grid and only update the values.
+        let children_ids = root.child_ids().map(|ids| ids.to_vec());
+        let rdom = root.real_dom_mut();
+        if let Some(children) = children_ids {
+            for child in children {
+                rdom.get_mut(child).unwrap().remove();
+            }
+        }
+
+        // create the grid
+        for (x, row) in self.0.iter().copied().enumerate() {
+            let row_node = rdom
+                .create_node(NodeType::Element(ElementNode {
+                    tag: "div".to_string(),
+                    attributes: [
+                        (
+                            OwnedAttributeDiscription {
+                                name: "display".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("flex".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "flex-direction".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("row".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "width".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("100%".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "height".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("100%".into()),
+                        ),
+                    ]
+                    .into_iter()
+                    .collect(),
+                    ..Default::default()
+                }))
+                .id();
+            for (y, count) in row.iter().copied().enumerate() {
+                let node = 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: [
+                        (
+                            OwnedAttributeDiscription {
+                                name: "background-color".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text(format!(
+                                "rgb({}, {}, {})",
+                                count * 10,
+                                0,
+                                (x + y) * 10,
+                            )),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "width".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("100%".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "height".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("100%".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "display".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("flex".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "flex-direction".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("row".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "justify-content".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("center".into()),
+                        ),
+                        (
+                            OwnedAttributeDiscription {
+                                name: "align-items".into(),
+                                namespace: None,
+                            },
+                            OwnedAttributeValue::Text("center".into()),
+                        ),
+                    ]
+                    .into_iter()
+                    .collect(),
+                    ..Default::default()
+                }));
+                button.add_event_listener("click");
+                button.add_event_listener("wheel");
+                button.add_child(node);
+                let button_id = button.id();
+                rdom.get_mut(row_node).unwrap().add_child(button_id);
+            }
+            rdom.get_mut(root_id).unwrap().add_child(row_node);
+        }
+    }
+
+    fn handle_event(
+        &mut self,
+        node: dioxus_native_core::NodeMut<()>,
+        event: &str,
+        value: Rc<EventData>,
+        bubbles: bool,
+    ) {
+        if let Some(parent) = node.parent() {
+            let child_number = parent
+                .child_ids()
+                .unwrap()
+                .iter()
+                .position(|id| *id == node.id())
+                .unwrap();
+            if let Some(parents_parent) = parent.parent() {
+                let parents_child_number = parents_parent
+                    .child_ids()
+                    .unwrap()
+                    .iter()
+                    .position(|id| *id == parent.id())
+                    .unwrap();
+                self.0[parents_child_number][child_number] += 1;
+            }
+        }
+    }
+
+    fn poll_async(&mut self) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + Send>> {
+        Box::pin(async move { tokio::time::sleep(std::time::Duration::from_millis(1000)).await })
+    }
+}
+
+fn main() {
+    render(Config::new(), |_, _, _| Test(Default::default())).unwrap();
+}

+ 0 - 4
packages/tui/src/hooks.rs

@@ -75,7 +75,6 @@ pub struct InnerInputState {
     mouse: Option<MouseData>,
     mouse: Option<MouseData>,
     wheel: Option<WheelData>,
     wheel: Option<WheelData>,
     last_key_pressed: Option<(KeyboardData, Instant)>,
     last_key_pressed: Option<(KeyboardData, Instant)>,
-    screen: Option<(u16, u16)>,
     pub(crate) focus_state: FocusState,
     pub(crate) focus_state: FocusState,
     // subscribers: Vec<Rc<dyn Fn() + 'static>>,
     // subscribers: Vec<Rc<dyn Fn() + 'static>>,
 }
 }
@@ -86,7 +85,6 @@ impl InnerInputState {
             mouse: None,
             mouse: None,
             wheel: None,
             wheel: None,
             last_key_pressed: None,
             last_key_pressed: None,
-            screen: None,
             // subscribers: Vec::new(),
             // subscribers: Vec::new(),
             focus_state: FocusState::create(rdom),
             focus_state: FocusState::create(rdom),
         }
         }
@@ -181,7 +179,6 @@ impl InnerInputState {
         if old_focus != self.focus_state.last_focused_id {
         if old_focus != self.focus_state.last_focused_id {
             // elements with listeners will always have a element id
             // elements with listeners will always have a element id
             if let Some(id) = self.focus_state.last_focused_id {
             if let Some(id) = self.focus_state.last_focused_id {
-                let element = dom.get(id).unwrap();
                 resolved_events.push(Event {
                 resolved_events.push(Event {
                     name: "focus",
                     name: "focus",
                     id,
                     id,
@@ -196,7 +193,6 @@ impl InnerInputState {
                 });
                 });
             }
             }
             if let Some(id) = old_focus {
             if let Some(id) = old_focus {
-                let element = dom.get(id).unwrap();
                 resolved_events.push(Event {
                 resolved_events.push(Event {
                     name: "focusout",
                     name: "focusout",
                     id,
                     id,

+ 2 - 2
packages/tui/src/lib.rs

@@ -68,7 +68,7 @@ impl TuiContext {
     }
     }
 }
 }
 
 
-pub fn render<R: Renderer<(), Rc<EventData>>>(
+pub fn render<R: Renderer<Rc<EventData>>>(
     cfg: Config,
     cfg: Config,
     f: impl FnOnce(&Arc<RwLock<RealDom>>, &Arc<Mutex<Taffy>>, UnboundedSender<InputEvent>) -> R,
     f: impl FnOnce(&Arc<RwLock<RealDom>>, &Arc<Mutex<Taffy>>, UnboundedSender<InputEvent>) -> R,
 ) -> Result<()> {
 ) -> Result<()> {
@@ -271,7 +271,7 @@ pub fn render<R: Renderer<(), Rc<EventData>>>(
 
 
                         for e in evts {
                         for e in evts {
                             let node = rdom.get_mut(e.id).unwrap();
                             let node = rdom.get_mut(e.id).unwrap();
-                            renderer.handle_event(node, e.name, e.data);
+                            renderer.handle_event(node, e.name, e.data, e.bubbles);
                         }
                         }
                     }
                     }
                     let mut rdom = rdom.write().unwrap();
                     let mut rdom = rdom.write().unwrap();

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

@@ -67,7 +67,7 @@ pub(crate) fn render_vnode(
             }
             }
 
 
             let label = Label {
             let label = Label {
-                text,
+                text: &text.text,
                 style: node.get::<StyleModifier>().unwrap().core,
                 style: node.get::<StyleModifier>().unwrap().core,
             };
             };
             let area = Rect::new(x, y, width, height);
             let area = Rect::new(x, y, width, height);