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

feat: omg what a dumb mistake

Jonathan Kelley 3 жил өмнө
parent
commit
f782e14211

+ 1 - 1
Cargo.toml

@@ -19,7 +19,7 @@ dioxus-mobile = { path = "./packages/mobile", optional = true }
 
 [features]
 # core
-default = ["core", "ssr"]
+default = ["core", "ssr", "desktop"]
 core = ["macro", "hooks", "html"]
 macro = ["dioxus-core-macro"]
 hooks = ["dioxus-hooks"]

+ 71 - 0
examples/coroutine.rs

@@ -0,0 +1,71 @@
+//! Example: Coroutines!
+//! --------------------
+//!
+//! Coroutines are an awesome way to write concurrent code. Dioxus heavily leverages coroutines to make sense of complex
+//! ongoing asynchronous tasks. The async scheduler of Dioxus supports both single-threaded and multi-threaded coroutines,
+//! so you can drop in code to run across multiple threads without blocking the main thread.
+//!
+//! Dioxus cannot simply abstract away the threading model for the web, unfortunately. If you want to use "web threads"
+//! you either need to limit support for Chrome, or you need to use a Web Workers and message passing. This is easy enough
+//! to do in your own code, and doesn't require 1st-party support from Dioxus itself.
+//!
+//! UseState and friends work fine with coroutines, but coroutines might be easier to use with the Dirac global state
+//! management API. This lets you easily drive global state from a coroutine without having to subscribe to the state.
+//!
+//! For now, this example shows how to use coroutines used with use_state.
+//!
+//!
+//! ## What is a Couroutine?
+//!
+//! A coroutine is a function that can be paused and resumed. It can be paused internally through "await" or externally
+//! using the `TaskHandle` API. Within a coroutine, you may execute asynchonous code, that modifies values captured when
+//! the coroutine was initiated. `use_state` always returns the same setter, so you don't need to worry about
+
+fn main() {
+    dioxus::desktop::launch(App, |c| c);
+}
+
+use dioxus::prelude::*;
+
+static App: FC<()> = |cx| {
+    let p1 = use_state(cx, || 0);
+    let p2 = use_state(cx, || 0);
+
+    let (mut p1_async, mut p2_async) = (p1.for_async(), p2.for_async());
+    let (p1_handle, _) = cx.use_task(|| async move {
+        loop {
+            *p1_async.get_mut() += 1;
+            async_std::task::sleep(std::time::Duration::from_millis(75)).await;
+        }
+    });
+    let (p2_handle, _) = cx.use_task(|| async move {
+        loop {
+            *p2_async.get_mut() += 1;
+            async_std::task::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    });
+
+    cx.render(rsx! {
+        div { style: { width: "400px", height: "400px", position: "relative", background: "yellow" }
+            button { "reset", onclick: move |_| {} }
+            Horsey { pos: *p1, "horsey 1" }
+            Horsey { pos: *p2, "horsey 2" }
+        }
+    })
+};
+
+#[derive(PartialEq, Props)]
+struct HorseyProps {
+    pos: i32,
+}
+
+static Horsey: FC<HorseyProps> = |cx| {
+    cx.render(rsx! {
+    div {
+        button { "pause" }
+        div {
+            {cx.children()}
+        }
+      }
+    })
+};

+ 113 - 0
examples/file_explorer.rs

@@ -0,0 +1,113 @@
+//! Example: File Explorer
+//! -------------------------
+//!
+//! This is a fun little desktop application that lets you explore the file system.
+//!
+//! This example is interesting because it's mixing filesystem operations and GUI, which is typically hard for UI to do.
+
+use dioxus::desktop::wry::application::dpi::LogicalSize;
+use dioxus::prelude::*;
+use std::fs::{self, DirEntry};
+
+fn main() {
+    env_logger::init();
+    dioxus::desktop::launch(App, |c| {
+        c.with_resizable(false)
+            .with_inner_size(LogicalSize::new(800.0, 400.0))
+    })
+    .unwrap();
+}
+
+static App: FC<()> = |cx| {
+    let files = use_state(cx, || Files::new());
+
+    let file_list = files.path_names.iter().enumerate().map(|(dir_id, path)| {
+        rsx! (
+            li { a {"{path}", onclick: move |_| files.get_mut().enter_dir(dir_id), href: "#"} }
+        )
+    });
+
+    let err_disp = files.err.as_ref().map(|err| {
+        rsx! {
+            div {
+                code {"{err}"}
+                button {"x", onclick: move |_| files.get_mut().clear_err() }
+            }
+        }
+    });
+
+    let cur = files.current();
+    cx.render(rsx! {
+        div {
+            h1 {"Files: "}
+            h3 {"Cur dir: {cur}"}
+            button { "go up", onclick: move |_| files.get_mut().go_up() }
+            ol { {file_list} }
+            {err_disp}
+        }
+    })
+};
+
+// right now, this gets cloned every time. It might be a bit better to use im_rc's collections instead
+#[derive(Clone)]
+struct Files {
+    path_stack: Vec<String>,
+    path_names: Vec<String>,
+    err: Option<String>,
+}
+
+impl Files {
+    fn new() -> Self {
+        let mut files = Self {
+            path_stack: vec!["./".to_string()],
+            path_names: vec![],
+            err: None,
+        };
+
+        files.reload_path_list();
+
+        files
+    }
+
+    fn reload_path_list(&mut self) {
+        let cur_path = self.path_stack.last().unwrap();
+        let paths = match fs::read_dir(cur_path) {
+            Ok(e) => e,
+            Err(err) => {
+                let err = format!("An error occured: {:?}", err);
+                self.err = Some(err);
+                self.path_stack.pop();
+                return;
+            }
+        };
+
+        // clear the current state
+        self.clear_err();
+        self.path_names.clear();
+
+        for path in paths {
+            self.path_names
+                .push(path.unwrap().path().display().to_string());
+        }
+    }
+
+    fn go_up(&mut self) {
+        if self.path_stack.len() > 1 {
+            self.path_stack.pop();
+        }
+        self.reload_path_list();
+    }
+
+    fn enter_dir(&mut self, dir_id: usize) {
+        let path = &self.path_names[dir_id];
+        self.path_stack.push(path.clone());
+        self.reload_path_list();
+    }
+
+    fn current(&self) -> &str {
+        self.path_stack.last().unwrap()
+    }
+    fn clear_err(&mut self) {
+        self.err = None;
+    }
+}

+ 7 - 2
examples/model.rs

@@ -17,12 +17,17 @@
 
 use dioxus::events::on::*;
 use dioxus::prelude::*;
+use dioxus_desktop::wry::application::dpi::LogicalSize;
 
 const STYLE: &str = include_str!("./assets/calculator.css");
 fn main() {
     env_logger::init();
-    dioxus::desktop::launch(App, |cfg| cfg.with_title("Calculator Demo"))
-        .expect("failed to launch dioxus app");
+    dioxus::desktop::launch(App, |cfg| {
+        cfg.with_title("Calculator Demo")
+            .with_resizable(false)
+            .with_inner_size(LogicalSize::new(320.0, 530.0))
+    })
+    .expect("failed to launch dioxus app");
 }
 
 static App: FC<()> = |cx| {

+ 9 - 1
packages/core/src/bumpframe.rs

@@ -12,7 +12,7 @@ pub struct ActiveFrame {
 
 pub struct BumpFrame {
     pub bump: Bump,
-    pub head_node: VNode<'static>,
+    pub(crate) head_node: VNode<'static>,
 
     // used internally for debugging
     name: &'static str,
@@ -36,6 +36,14 @@ impl ActiveFrame {
         }
     }
 
+    pub unsafe fn reset_wip_frame(&mut self) {
+        self.wip_frame_mut().bump.reset()
+    }
+
+    pub fn update_head_node<'a>(&mut self, node: VNode<'a>) {
+        self.wip_frame_mut().head_node = unsafe { std::mem::transmute(node) };
+    }
+
     /// The "work in progress frame" represents the frame that is currently being worked on.
     pub fn wip_frame(&self) -> &BumpFrame {
         match self.generation.get() & 1 == 0 {

+ 4 - 7
packages/core/src/context.rs

@@ -148,10 +148,10 @@ impl<'src, P> Context<'src, P> {
         lazy_nodes: LazyNodes<'src, F>,
     ) -> DomTree<'src> {
         let scope_ref = self.scope;
-        let listener_id = &scope_ref.listener_idx;
+        // let listener_id = &scope_ref.listener_idx;
         Some(lazy_nodes.into_vnode(NodeFactory {
             scope: scope_ref,
-            listener_id,
+            // listener_id,
         }))
     }
 
@@ -505,10 +505,7 @@ impl<'src> SuspendedContext<'src> {
         lazy_nodes: LazyNodes<'src, F>,
     ) -> DomTree<'src> {
         let scope_ref = self.inner.scope;
-        let listener_id = &scope_ref.listener_idx;
-        Some(lazy_nodes.into_vnode(NodeFactory {
-            scope: scope_ref,
-            listener_id,
-        }))
+
+        Some(lazy_nodes.into_vnode(NodeFactory { scope: scope_ref }))
     }
 }

+ 30 - 63
packages/core/src/diff.rs

@@ -55,7 +55,7 @@ use crate::{arena::SharedResources, innerlude::*};
 use fxhash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
 
-use std::{any::Any, borrow::Borrow};
+use std::{any::Any, borrow::Borrow, cmp::Ordering};
 
 /// Instead of having handles directly over nodes, Dioxus uses simple u32 as node IDs.
 /// The expectation is that the underlying renderer will mainain their Nodes in vec where the ids are the index. This allows
@@ -206,13 +206,12 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
                     scope.update_children(ScopeChildren(new.children));
 
                     // React doesn't automatically memoize, but we do.
-                    let should_render = true;
-                    // let should_render = match old.comparator {
-                    //     Some(comparator) => comparator(new),
-                    //     None => true,
-                    // };
+                    let are_the_same = match old.comparator {
+                        Some(comparator) => comparator(new),
+                        None => false,
+                    };
 
-                    if should_render {
+                    if !are_the_same {
                         scope.run_scope().unwrap();
                         self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
                     } else {
@@ -915,7 +914,7 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     //     [... parent]
     //
     // When this function returns, the change list stack is in the same state.
-    pub fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
+    fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
         // debug_assert!(self.edits.traversal_is_committed());
         log::debug!("REMOVING CHILDREN");
         for _child in old {
@@ -1208,67 +1207,35 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     // the change list stack is in the same state when this function returns.
     fn diff_non_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
         // Handled these cases in `diff_children` before calling this function.
+        //
         // debug_assert!(!new.is_empty());
         // debug_assert!(!old.is_empty());
 
-        //     [... parent]
-        // self.edits.go_down();
-        // self.edits.push_root()
-        //     [... parent child]
-
-        // todo!()
         for (_i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
-            // [... parent prev_child]
-            // self.edits.go_to_sibling(i);
-            // [... parent this_child]
-
-            // let did = old_child.get_mounted_id(self.components).unwrap();
-            // if did.0 == 0 {
-            //     log::debug!("Root is bad: {:#?}", old_child);
-            // }
-            // self.edits.push_root(did);
-            // dbg!("dffing child", new_child, old_child);
             self.diff_node(old_child, new_child);
-
-            // let old_id = old_child.get_mounted_id(self.components).unwrap();
-            // let new_id = new_child.get_mounted_id(self.components).unwrap();
-
-            // log::debug!(
-            //     "pushed root. {:?}, {:?}",
-            //     old_child.get_mounted_id(self.components).unwrap(),
-            //     new_child.get_mounted_id(self.components).unwrap()
-            // );
-            // if old_id != new_id {
-            //     log::debug!("Mismatch: {:?}", new_child);
-            // }
         }
 
-        // match old.len().cmp(&new.len()) {
-        //     // old.len > new.len -> removing some nodes
-        //     Ordering::Greater => {
-        //         // [... parent prev_child]
-        //         self.edits.go_to_sibling(new.len());
-        //         // [... parent first_child_to_remove]
-        //         // self.edits.commit_traversal();
-        //         // support::remove_self_and_next_siblings(state, &old[new.len()..]);
-        //         self.remove_self_and_next_siblings(&old[new.len()..]);
-        //         // [... parent]
-        //     }
-        //     // old.len < new.len -> adding some nodes
-        //     Ordering::Less => {
-        //         // [... parent last_child]
-        //         self.edits.go_up();
-        //         // [... parent]
-        //         // self.edits.commit_traversal();
-        //         self.create_and_append_children(&new[old.len()..]);
-        //     }
-        //     // old.len == new.len -> no nodes added/removed, but πerhaps changed
-        //     Ordering::Equal => {
-        //         // [... parent child]
-        //         self.edits.go_up();
-        //         // [... parent]
-        //     }
-        // }
+        match old.len().cmp(&new.len()) {
+            // old.len > new.len -> removing some nodes
+            Ordering::Greater => {
+                for item in &old[new.len()..] {
+                    for i in RealChildIterator::new(item, self.vdom) {
+                        self.edits.push_root(i.element_id().unwrap());
+                        self.edits.remove();
+                    }
+                }
+            }
+            // old.len < new.len -> adding some nodes
+            Ordering::Less => {
+                // [... parent last_child]
+                // self.edits.go_up();
+                // [... parent]
+                // self.edits.commit_traversal();
+                self.create_and_append_children(&new[old.len()..]);
+            }
+            // old.len == new.len -> no nodes added/removed, but πerhaps changed
+            Ordering::Equal => {}
+        }
     }
 
     // ======================
@@ -1284,7 +1251,7 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     // After the function returns, the child is no longer on the change list stack:
     //
     //     [... parent]
-    pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
+    fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
         // debug_assert!(self.edits.traversal_is_committed());
         for child in old {
             if let VNodeKind::Component(_vcomp) = child.kind {

+ 4 - 34
packages/core/src/nodes.rs

@@ -144,14 +144,12 @@ pub struct VComponent<'src> {
 #[derive(Copy, Clone)]
 pub struct NodeFactory<'a> {
     pub scope: &'a Scope,
-    pub listener_id: &'a Cell<usize>,
 }
 
 impl<'a> NodeFactory<'a> {
     #[inline]
     pub fn bump(&self) -> &'a bumpalo::Bump {
         &self.scope.frames.wip_frame().bump
-        // &self.scope.cur_frame().bump
     }
 
     pub fn unstable_place_holder() -> VNode<'static> {
@@ -346,6 +344,9 @@ impl<'a> NodeFactory<'a> {
                 if user_fc == other.user_fc {
                     let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
                     let props_memoized = unsafe { props.memoize(&real_other) };
+
+                    // It's only okay to memoize if there are no children and the props can be memoized
+                    // Implementing memoize is unsafe and done automatically with the props trait
                     match (props_memoized, children.len() == 0) {
                         (true, true) => true,
                         _ => false,
@@ -395,7 +396,7 @@ impl<'a> NodeFactory<'a> {
         unsafe { std::mem::transmute::<_, Captured<'static>>(caller) }
     }
 
-    pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a> + 'a) -> VNode<'a> {
+    pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
         let children = node_iter.into_vnode_list(self);
 
         VNode {
@@ -501,37 +502,6 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
     }
 }
 
-// For the case where a rendered VNode is by reference passed into the rsx! macro through curly braces
-// This behavior is designed for the cx.children method where child nodes are passed by reference.
-//
-// Designed to support indexing
-impl<'a> IntoVNode<'a> for &VNode<'a> {
-    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
-        todo!()
-        // let kind = match &self.kind {
-        //     VNodeKind::Element(element) => VNodeKind::Element(element),
-        //     VNodeKind::Text(old) => VNodeKind::Text(VText {
-        //         text: old.text,
-        //         is_static: old.is_static,
-        //     }),
-        //     VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
-        //         children: fragment.children,
-        //         is_static: fragment.is_static,
-        //     }),
-        //     VNodeKind::Component(component) => VNodeKind::Component(component),
-
-        //     // todo: it doesn't make much sense to pass in suspended nodes
-        //     // I think this is right but I'm not too sure.
-        //     VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() },
-        // };
-        // VNode {
-        //     kind,
-        //     dom_id: self.dom_id.clone(),
-        //     key: self.key.clone(),
-        // }
-    }
-}
-
 /// A concrete type provider for closures that build VNode structures.
 ///
 /// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over

+ 17 - 18
packages/core/src/scope.rs

@@ -34,7 +34,6 @@ pub struct Scope {
 
     // Listeners
     pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
-    pub(crate) listener_idx: Cell<usize>,
 
     // State
     pub(crate) hooks: HookList,
@@ -72,9 +71,7 @@ impl Scope {
         vdom: SharedResources,
     ) -> Self {
         let child_nodes = unsafe { child_nodes.extend_lifetime() };
-        // let child_nodes = unsafe { std::mem::transmute(child_nodes) };
 
-        // dbg!(child_nodes);
         Self {
             child_nodes,
             caller,
@@ -82,7 +79,6 @@ impl Scope {
             our_arena_idx: arena_idx,
             height,
             vdom,
-            listener_idx: Default::default(),
             frames: ActiveFrame::new(),
             hooks: Default::default(),
             shared_contexts: Default::default(),
@@ -114,24 +110,27 @@ impl Scope {
 
         // make sure we call the drop implementation on all the listeners
         // this is important to not leak memory
-        for li in self.listeners.borrow_mut().drain(..) {
-            log::debug!("dropping listener");
-            let d = unsafe { &*li };
-            let mut r = d.callback.borrow_mut();
-            let p = r.take().unwrap();
-            std::mem::drop(p);
-            // d.callback.borrow_mut();
+        for listener in self
+            .listeners
+            .borrow_mut()
+            .drain(..)
+            .map(|li| unsafe { &*li })
+        {
+            let mut cb = listener.callback.borrow_mut();
+            match cb.take() {
+                Some(val) => std::mem::drop(val),
+                None => log::info!("no callback to drop. component must be broken"),
+            };
         }
 
-        // self.listeners.borrow_mut().clear();
-
+        // Safety:
+        // - We dropped the listeners, so no more &mut T can be used while these are held
+        // - All children nodes that rely on &mut T are replaced with a new reference
         unsafe { self.hooks.reset() };
 
-        self.listener_idx.set(0);
-
-        // This is a very dangerous operation
-        let next_frame = self.frames.wip_frame_mut();
-        next_frame.bump.reset();
+        // Safety:
+        // - We've dropped all references to the wip bump frame
+        unsafe { self.frames.reset_wip_frame() };
 
         // Cast the caller ptr from static to one with our own reference
         let c3: &WrappedCaller = self.caller.as_ref();

+ 1 - 1
packages/desktop/Cargo.toml

@@ -11,7 +11,7 @@ license = "MIT/Apache-2.0"
 [dependencies]
 # web-view = { git = "https://github.com/Boscop/web-view" }
 dioxus-core = { path = "../core", version = "0.1.2", features = ["serialize"] }
-anyhow = "1.0.38"
+anyhow = "1.0"
 argh = "0.1.4"
 serde = "1.0.120"
 serde_json = "1.0.61"

+ 1 - 1
packages/hooks/src/usestate.rs

@@ -52,7 +52,7 @@ use std::{
 pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
     cx: Context<'a, P>,
     initial_state_fn: F,
-) -> UseState<T> {
+) -> UseState<'a, T> {
     cx.use_hook(
         move |_| UseStateInner {
             current_val: initial_state_fn(),

+ 1 - 1
packages/mobile/Cargo.toml

@@ -7,7 +7,7 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-anyhow = "1.0.42"
+anyhow = "1.0"
 # cacao = { git = "https://github.com/ryanmcgrath/cacao" }
 dioxus-core = { path = "../core", version = "0.1.2" }
 log = "0.4.14"

+ 1 - 0
packages/mobile/src/lib.rs

@@ -185,6 +185,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
         });
     }
 }
+// brad johnson go chat
 
 fn main() {
     init_logging();

+ 1 - 1
packages/ssr/Cargo.toml

@@ -17,7 +17,7 @@ tide-websockets = "*"
 thiserror = "1.0.23"
 log = "0.4.13"
 fern = { version = "0.6.0", features = ["colored"] }
-anyhow = "1.0.38"
+anyhow = "1.0"
 argh = "0.1.4"
 serde = "1.0.120"
 serde_json = "1.0.61"

+ 1 - 1
packages/web/Cargo.toml

@@ -22,7 +22,7 @@ generational-arena = "0.2.8"
 wasm-bindgen-test = "0.3.21"
 once_cell = "1.8"
 async-channel = "1.6.1"
-anyhow = "1.0.41"
+anyhow = "1.0"
 
 futures-util = "0.3.15"