Prechádzať zdrojové kódy

feat: add full async suport

Jonathan Kelley 3 rokov pred
rodič
commit
c884f8c1b8
10 zmenil súbory, kde vykonal 177 pridanie a 98 odobranie
  1. 2 0
      Cargo.toml
  2. 0 1
      README.md
  3. 1 1
      examples/frame.rs
  4. 1 1
      examples/list.rs
  5. 1 1
      examples/margin.rs
  6. 1 1
      examples/quadrants.rs
  7. 1 1
      examples/readme.rs
  8. 51 0
      examples/task.rs
  9. 1 1
      examples/text.rs
  10. 118 91
      src/lib.rs

+ 2 - 0
Cargo.toml

@@ -15,4 +15,6 @@ hecs = "0.7.3"
 ctrlc = "3.2.1"
 bumpalo = { version = "3.8.0", features = ["boxed"] }
 stretch2 = { path = "../../Tinkering/stretch2" }
+tokio = { version = "1.15.0", features = ["full"] }
+futures = "0.3.19"
 # stretch2 = { git = "https://github.com/elbaro/stretch2.git" }

+ 0 - 1
README.md

@@ -19,7 +19,6 @@ static App: FC<()> = |cx| {
             justify_content: "center",
             align_items: "center",
 
-
             "Hello world!"
         }
     })

+ 1 - 1
examples/frame.rs

@@ -4,7 +4,7 @@ fn main() {
     let mut dom = VirtualDom::new(app);
     dom.rebuild();
 
-    rink::render_vdom(&dom).unwrap();
+    rink::render_vdom(&mut dom).unwrap();
 }
 
 fn app(cx: Scope) -> Element {

+ 1 - 1
examples/list.rs

@@ -4,7 +4,7 @@ fn main() {
     let mut dom = VirtualDom::new(app);
     dom.rebuild();
 
-    rink::render_vdom(&dom).unwrap();
+    rink::render_vdom(&mut dom).unwrap();
 }
 
 fn app(cx: Scope) -> Element {

+ 1 - 1
examples/margin.rs

@@ -4,7 +4,7 @@ fn main() {
     let mut dom = VirtualDom::new(app);
     dom.rebuild();
 
-    rink::render_vdom(&dom).unwrap();
+    rink::render_vdom(&mut dom).unwrap();
 }
 
 fn app(cx: Scope) -> Element {

+ 1 - 1
examples/quadrants.rs

@@ -4,7 +4,7 @@ fn main() {
     let mut dom = VirtualDom::new(app);
     dom.rebuild();
 
-    rink::render_vdom(&dom).unwrap();
+    rink::render_vdom(&mut dom).unwrap();
 }
 
 fn app(cx: Scope) -> Element {

+ 1 - 1
examples/readme.rs

@@ -4,7 +4,7 @@ fn main() {
     let mut dom = VirtualDom::new(app);
     dom.rebuild();
 
-    rink::render_vdom(&dom).unwrap();
+    rink::render_vdom(&mut dom).unwrap();
 }
 
 fn app(cx: Scope) -> Element {

+ 51 - 0
examples/task.rs

@@ -0,0 +1,51 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&mut dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    let count = use_state(&cx, || 0);
+
+    use_future(&cx, || {
+        let set_count = count.setter();
+        let mut mycount = 0;
+        let update = cx.schedule_update();
+        async move {
+            loop {
+                tokio::time::sleep(std::time::Duration::from_millis(50)).await;
+                mycount += 1;
+                set_count(mycount);
+                update();
+            }
+        }
+    });
+
+    cx.render(rsx! {
+        div { width: "100%",
+            div { width: "50%", height: "5px", background_color: "blue", justify_content: "center", align_items: "center",
+                "Hello {count}!"
+            }
+            div { width: "50%", height: "10px", background_color: "red", justify_content: "center", align_items: "center",
+                "Hello {count}!"
+            }
+        }
+    })
+}
+
+// use_future(&cx, || {
+//         let set_count = count.setter();
+//         let mut mycount = 0;
+//         let update = cx.schedule_update();
+//         async move {
+//             loop {
+//                 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+//                 mycount += 1;
+//                 set_count(mycount);
+//                 update();
+//             }
+//         }
+//     });

+ 1 - 1
examples/text.rs

@@ -4,7 +4,7 @@ fn main() {
     let mut dom = VirtualDom::new(app);
     dom.rebuild();
 
-    rink::render_vdom(&dom).unwrap();
+    rink::render_vdom(&mut dom).unwrap();
 }
 
 fn app(cx: Scope) -> Element {

+ 118 - 91
src/lib.rs

@@ -1,14 +1,15 @@
 use anyhow::Result;
 use crossterm::{
-    event::{self, DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
+    event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
     execute,
     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 };
+use dioxus::core::exports::futures_channel::mpsc::unbounded;
 use dioxus::core::*;
+use futures::{future::Either, pin_mut, StreamExt};
 use std::{
     collections::HashMap,
     io,
-    sync::mpsc,
     time::{Duration, Instant},
 };
 use stretch2::{prelude::Size, Stretch};
@@ -28,22 +29,10 @@ pub struct TuiNode<'a> {
     pub node: &'a VNode<'a>,
 }
 
-pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
-    /*
-    Get the terminal to calcualte the layout from
-    */
-    enable_raw_mode().unwrap();
-    ctrlc::set_handler(move || {
-        disable_raw_mode().unwrap();
-    })
-    .expect("Error setting Ctrl-C handler");
-    let mut stdout = std::io::stdout();
-    execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
-    let backend = CrosstermBackend::new(io::stdout());
-    let mut terminal = Terminal::new(backend).unwrap();
-
+pub fn render_vdom(vdom: &mut VirtualDom) -> Result<()> {
     // Setup input handling
-    let (tx, rx) = mpsc::channel();
+    let (tx, mut rx) = unbounded();
+
     std::thread::spawn(move || {
         let tick_rate = Duration::from_millis(100);
         let mut last_tick = Instant::now();
@@ -53,93 +42,131 @@ pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
                 .checked_sub(last_tick.elapsed())
                 .unwrap_or_else(|| Duration::from_secs(0));
 
-            if event::poll(timeout).unwrap() {
-                if let TermEvent::Key(key) = event::read().unwrap() {
-                    tx.send(InputEvent::UserInput(key)).unwrap();
-                }
+            if crossterm::event::poll(timeout).unwrap() {
+                let evt = crossterm::event::read().unwrap();
+                tx.unbounded_send(InputEvent::UserInput(evt)).unwrap();
             }
 
             if last_tick.elapsed() >= tick_rate {
-                tx.send(InputEvent::Tick).unwrap();
+                tx.unbounded_send(InputEvent::Tick).unwrap();
                 last_tick = Instant::now();
             }
         }
     });
 
-    terminal.clear().unwrap();
-
-    loop {
-        let dims = terminal.size().unwrap();
-        let width = dims.width;
-        let height = dims.height;
-
-        /*
-         -> collect all the nodes with their layout
-         -> solve their layout
-         -> render the nodes in the right place with tui/crosstream
-         -> while rendering, apply styling
-
-         use simd to compare lines for diffing?
-
-        */
-        let mut layout = Stretch::new();
-        let mut nodes = HashMap::new();
-
-        let root_node = vdom.base_scope().root_node();
-        layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
-        /*
-        Compute the layout given th terminal size
-        */
-        let node_id = root_node.try_mounted_id().unwrap();
-        let root_layout = nodes[&node_id].layout;
-        layout.compute_layout(
-            root_layout,
-            Size {
-                width: stretch2::prelude::Number::Defined(width as f32),
-                height: stretch2::prelude::Number::Defined(height as f32),
-            },
-        )?;
-
-        terminal.draw(|frame| {
-            //
-            render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
-            assert!(nodes.is_empty());
-        })?;
-
-        match rx.recv()? {
-            InputEvent::UserInput(event) => match event.code {
-                KeyCode::Char('q') => {
-                    disable_raw_mode()?;
-                    execute!(
-                        terminal.backend_mut(),
-                        LeaveAlternateScreen,
-                        DisableMouseCapture
-                    )?;
-                    terminal.show_cursor()?;
-                    break;
+    tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()?
+        .block_on(async {
+            /*
+            Get the terminal to calcualte the layout from
+            */
+            enable_raw_mode().unwrap();
+            let mut stdout = std::io::stdout();
+            execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
+            let backend = CrosstermBackend::new(io::stdout());
+            let mut terminal = Terminal::new(backend).unwrap();
+
+            terminal.clear().unwrap();
+
+            loop {
+                let dims = terminal.size().unwrap();
+                let width = dims.width;
+                let height = dims.height;
+
+                /*
+                -> collect all the nodes with their layout
+                -> solve their layout
+                -> render the nodes in the right place with tui/crosstream
+                -> while rendering, apply styling
+
+                use simd to compare lines for diffing?
+
+
+                todo: reuse the layout and node objects.
+                our work_with_deadline method can tell us which nodes are dirty.
+                */
+                let mut layout = Stretch::new();
+                let mut nodes = HashMap::new();
+
+                let root_node = vdom.base_scope().root_node();
+                layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
+                /*
+                Compute the layout given th terminal size
+                */
+                let node_id = root_node.try_mounted_id().unwrap();
+                let root_layout = nodes[&node_id].layout;
+                layout.compute_layout(
+                    root_layout,
+                    Size {
+                        width: stretch2::prelude::Number::Defined(width as f32),
+                        height: stretch2::prelude::Number::Defined(height as f32),
+                    },
+                )?;
+
+                terminal.draw(|frame| {
+                    //
+                    render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
+                    assert!(nodes.is_empty());
+                })?;
+
+                use futures::future::{select, Either};
+                {
+                    let wait = vdom.wait_for_work();
+                    pin_mut!(wait);
+
+                    match select(wait, rx.next()).await {
+                        Either::Left((a, b)) => {
+                            // println!("work");
+                        }
+                        Either::Right((evt, o)) => {
+                            // println!("event");
+                            match evt.unwrap() {
+                                InputEvent::UserInput(event) => match event {
+                                    TermEvent::Key(key) => {
+                                        match key.code {
+                                            KeyCode::Char('q') => {
+                                                disable_raw_mode()?;
+                                                execute!(
+                                                    terminal.backend_mut(),
+                                                    LeaveAlternateScreen,
+                                                    DisableMouseCapture
+                                                )?;
+                                                terminal.show_cursor()?;
+                                                break;
+                                            }
+                                            _ => {} // handle event
+                                        }
+                                    }
+                                    TermEvent::Mouse(_) => {}
+                                    TermEvent::Resize(_, _) => {}
+                                },
+                                InputEvent::Tick => {} // tick
+                                InputEvent::Close => {
+                                    break;
+                                }
+                            };
+                        }
+                    }
                 }
-                _ => {} // handle event
-            },
-            InputEvent::Tick => {} // tick
-            InputEvent::Close => {
-                break;
+
+                vdom.work_with_deadline(|| false);
             }
-        };
-    }
-
-    disable_raw_mode()?;
-    execute!(
-        terminal.backend_mut(),
-        LeaveAlternateScreen,
-        DisableMouseCapture
-    )?;
-    terminal.show_cursor()?;
-
-    Ok(())
+
+            disable_raw_mode()?;
+            execute!(
+                terminal.backend_mut(),
+                LeaveAlternateScreen,
+                DisableMouseCapture
+            )?;
+            terminal.show_cursor()?;
+
+            Ok(())
+        })
 }
 
 enum InputEvent {
-    UserInput(KeyEvent),
+    UserInput(TermEvent),
     Close,
     Tick,
 }