diff --git a/Cargo.toml b/Cargo.toml index 9092e99e7..0c7cdfa86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "packages/core-macro", "packages/core", "packages/web", + "packages/webview/client" # "packages/router", # "packages/ssr", # "packages/webview", @@ -28,3 +29,5 @@ members = [ # "packages/virtual-dom-rs", # "packages/virtual-node", ] + + diff --git a/README.md b/README.md index 83bb39720..a746a1c7a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

-# About + Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust. diff --git a/js-host/Cargo.toml b/js-host/Cargo.toml new file mode 100644 index 000000000..14aa743f1 --- /dev/null +++ b/js-host/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "dixous-jshost" +version = "0.0.0" +authors = ["Jonathan Kelley "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] \ No newline at end of file diff --git a/js-host/src/lib.rs b/js-host/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/js-host/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/notes/SOLVEDPROBLEMS.md b/notes/SOLVEDPROBLEMS.md index 8c471eb81..96708e6ca 100644 --- a/notes/SOLVEDPROBLEMS.md +++ b/notes/SOLVEDPROBLEMS.md @@ -306,7 +306,6 @@ unnecessary function runs Does any of this matter? Should we just run any component we see, immediately and imperatively? That will cause checks throughout the whole tree, no matter where the update occurred - https://calendar.perfplanet.com/2013/diff/ Here's how react does it: diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index c22d32eff..643730183 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -259,11 +259,11 @@ impl<'a> DiffMachine<'a> { let id = get_id(); *component.stable_addr.as_ref().borrow_mut() = Some(id); self.change_list.save_known_root(id); - let scope = Rc::downgrade(&component.ass_scope); + let scope = Rc::downgrade(&component.ass_scope); self.lifecycle_events.push_back(LifeCycleEvent::Mount { caller: Rc::downgrade(&component.caller), id, - scope + scope, }); } VNode::Suspended => { @@ -286,20 +286,26 @@ impl<'a> DiffMachine<'a> { } 'outer1: for (_l_idx, new_l) in new.iter().enumerate() { - // unsafe { - // Safety relies on removing `new_l` from the registry manually at - // the end of its lifetime. This happens below in the `'outer2` - // loop, and elsewhere in diffing when removing old dom trees. - // registry.add(new_l); - // } + // go through each new listener + // find its corresponding partner in the old list + // if any characteristics changed, remove and then re-add + + // if nothing changed, then just move on + let event_type = new_l.event; for old_l in old { if new_l.event == old_l.event { + if new_l.id != old_l.id { + self.change_list.remove_event_listener(event_type); + self.change_list + .update_event_listener(event_type, new_l.scope, new_l.id) + } + // if let Some(scope) = self.current_idx { // let cb = CbIdx::from_gi_index(scope, l_idx); - self.change_list - .update_event_listener(event_type, new_l.scope, new_l.id); + // self.change_list + // .update_event_listener(event_type, new_l.scope, new_l.id); // } continue 'outer1; diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index bbc5c3374..7860bfbf1 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -83,7 +83,8 @@ impl Scope { /// This function downcasts the function pointer based on the stored props_type /// /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized) - pub fn run_scope<'bump>(&'bump mut self) -> Result<()> { + pub fn run_scope(&mut self) -> Result<()> { + // pub fn run_scope<'bump>(&'bump mut self) -> Result<()> { let frame = { let frame = self.frames.next(); frame.bump.reset(); @@ -92,7 +93,7 @@ impl Scope { let node_slot = std::rc::Rc::new(RefCell::new(None)); - let ctx: Context<'bump> = Context { + let ctx = Context { arena: &self.hook_arena, hooks: &self.hooks, bump: &frame.bump, @@ -107,7 +108,6 @@ impl Scope { // todo!() // Note that the actual modification of the vnode head element occurs during this call let _: DomTree = (caller.as_ref())(ctx); - // let _: DomTree = (self.raw_caller)(ctx, &self.props); /* SAFETY ALERT @@ -123,7 +123,6 @@ impl Scope { frame.head_node = node_slot .as_ref() - // .deref() .borrow_mut() .take() .expect("Viewing did not happen"); diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 1782fc6a4..1e3755594 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -71,28 +71,16 @@ impl VirtualDom { /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom. pub fn rebuild<'s>(&'s mut self) -> Result> { // Diff from the top - let mut diff_machine = DiffMachine::new(); // partial borrow + let mut diff_machine = DiffMachine::new(); + let very_unsafe_components = &mut self.components as *mut generational_arena::Arena; let mut component = self .components .get_mut(self.base_scope) .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?; - component.run_scope()?; - // let component = &*component; + diff_machine.diff_node(component.old_frame(), component.new_frame()); - // get raw pointer to the arena - let very_unsafe_components = &mut self.components as *mut generational_arena::Arena; - - { - let component = self - .components - .get(self.base_scope) - .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?; - - diff_machine.diff_node(component.old_frame(), component.new_frame()); - } - // let p = &mut self.components; // chew down the the lifecycle events until all dirty nodes are computed while let Some(event) = diff_machine.lifecycle_events.pop_front() { match event { @@ -115,27 +103,19 @@ impl VirtualDom { let real_scope = scope.upgrade().unwrap(); *real_scope.as_ref().borrow_mut() = Some(idx); c.run_scope()?; - diff_machine.change_list.load_known_root(id); - diff_machine.diff_node(c.old_frame(), c.new_frame()); - // diff_machine.change_list.save_known_root(id); } } LifeCycleEvent::PropsChanged { caller, id, scope } => { let idx = scope.upgrade().unwrap().as_ref().borrow().unwrap(); - unsafe { + let p = &mut *(very_unsafe_components); - // todo, hook up the parent/child indexes properly - // let idx = p.insert_with(|f| Scope::new(caller, f, None)); let c = p.get_mut(idx).unwrap(); c.update_caller(caller); c.run_scope()?; - // c.caller = caller; - diff_machine.change_list.load_known_root(id); - diff_machine.diff_node(c.old_frame(), c.new_frame()); } // break 'render; diff --git a/packages/livehost/Cargo.toml b/packages/livehost/Cargo.toml index 919735fd2..1d9b9bb65 100644 --- a/packages/livehost/Cargo.toml +++ b/packages/livehost/Cargo.toml @@ -6,4 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] \ No newline at end of file +[dependencies] +dioxus-core = { path = "../core", version = "0.1.2" } diff --git a/packages/livehost/README.md b/packages/livehost/README.md new file mode 100644 index 000000000..c9b17c12b --- /dev/null +++ b/packages/livehost/README.md @@ -0,0 +1,14 @@ +# Livehost + +Stream events from a server to a client. + +This crate provides drivers for Actix, Warp, and Tide to run coupled frontend and backend. + +This comes in the form of two approaches: + +- tight coupling: frontend and backend are locked together +- loose coupling: hooks subscribe a component to a server using the suspense mechanism + +Tight coupling is basically an implmentation of loose coupling where **all** events move through the backend connection. This coupling option has higher latency but is very simple to deploy. We use this approach for dioxus-webview where latency is minimal (hosted locally) and we want builds to be simple - no need to manually bundle a custom frontend because everything is server rendered. + + diff --git a/packages/webview/src/index.html b/packages/livehost/index.html similarity index 99% rename from packages/webview/src/index.html rename to packages/livehost/index.html index 1fa75f741..258542dbe 100644 --- a/packages/webview/src/index.html +++ b/packages/livehost/index.html @@ -1,3 +1,4 @@ + diff --git a/packages/livehost/src/main.rs b/packages/livehost/src/lib.rs similarity index 100% rename from packages/livehost/src/main.rs rename to packages/livehost/src/lib.rs diff --git a/packages/web/examples/rsxt.rs b/packages/web/examples/rsxt.rs index 80b2ca1fc..7abb29558 100644 --- a/packages/web/examples/rsxt.rs +++ b/packages/web/examples/rsxt.rs @@ -8,7 +8,7 @@ fn main() { console_error_panic_hook::set_once(); wasm_bindgen_futures::spawn_local(async { - let props = ExampleProps { initial_name: "..?", blarg: vec!["abc".to_string(), "abc".to_string()]}; + let props = ExampleProps { initial_name: "..?"}; WebsysRenderer::new_with_props(Example, props) .run() .await @@ -19,13 +19,10 @@ fn main() { #[derive(PartialEq, Props)] struct ExampleProps { initial_name: &'static str, - blarg: Vec } static Example: FC = |ctx, props| { - let (name, set_name) = use_state(&ctx, move || props.initial_name); - - let sub = props.blarg.last().unwrap(); + let (name, set_name) = use_state(&ctx, move || props.initial_name.to_string()); ctx.render(rsx! { div { @@ -39,83 +36,33 @@ static Example: FC = |ctx, props| { "Hello, {name}" } - // CustomButton { name: sub, set_name: Box::new(move || set_name("jack")) } - CustomButton { name: "Jack!", set_name: Box::new(set_name) } - CustomButton { name: "Jill!", set_name: Box::new(set_name) } - CustomButton { name: "Bob!", set_name: Box::new(set_name) } - CustomButton { name: "Bill!", set_name: Box::new(set_name) } - CustomButton { name: "Dill!", set_name: Box::new(set_name) } - CustomButton { name: "Crack!", set_name: Box::new(set_name) } - CustomButton { name: "back!", set_name: Box::new(set_name) } - CustomButton { name: "cheder!", set_name: Box::new(set_name) } - CustomButton { name: "Jack!", set_name: Box::new(set_name) } - CustomButton { name: "Jill!", set_name: Box::new(set_name) } - CustomButton { name: "Bob!", set_name: Box::new(set_name) } - CustomButton { name: "Bill!", set_name: Box::new(set_name) } - CustomButton { name: "Dill!", set_name: Box::new(set_name) } - CustomButton { name: "Crack!", set_name: Box::new(set_name) } - CustomButton { name: "back!", set_name: Box::new(set_name) } - CustomButton { name: "cheder!", set_name: Box::new(set_name) } - CustomButton { name: "Jack!", set_name: Box::new(set_name) } - CustomButton { name: "Jill!", set_name: Box::new(set_name) } - CustomButton { name: "Bob!", set_name: Box::new(set_name) } - CustomButton { name: "Bill!", set_name: Box::new(set_name) } - CustomButton { name: "Dill!", set_name: Box::new(set_name) } - CustomButton { name: "Crack!", set_name: Box::new(set_name) } - CustomButton { name: "back!", set_name: Box::new(set_name) } - CustomButton { name: "cheder!", set_name: Box::new(set_name) } - CustomButton { name: "Jack!", set_name: Box::new(set_name) } - CustomButton { name: "Jill!", set_name: Box::new(set_name) } - CustomButton { name: "Bob!", set_name: Box::new(set_name) } - CustomButton { name: "Bill!", set_name: Box::new(set_name) } - CustomButton { name: "Dill!", set_name: Box::new(set_name) } - CustomButton { name: "Crack!", set_name: Box::new(set_name) } - CustomButton { name: "back!", set_name: Box::new(set_name) } - CustomButton { name: "cheder!", set_name: Box::new(set_name) } - CustomButton { name: "Jack!", set_name: Box::new(set_name) } - CustomButton { name: "Jill!", set_name: Box::new(set_name) } - CustomButton { name: "Bob!", set_name: Box::new(set_name) } - CustomButton { name: "Bill!", set_name: Box::new(set_name) } - CustomButton { name: "Dill!", set_name: Box::new(set_name) } - CustomButton { name: "Crack!", set_name: Box::new(set_name) } - CustomButton { name: "back!", set_name: Box::new(set_name) } - CustomButton { name: "cheder!", set_name: Box::new(set_name) } - CustomButton { name: "Jack!", set_name: Box::new(set_name) } - CustomButton { name: "Jill!", set_name: Box::new(set_name) } - CustomButton { name: "Bob!", set_name: Box::new(set_name) } - CustomButton { name: "Bill!", set_name: Box::new(set_name) } - CustomButton { name: "Dill!", set_name: Box::new(set_name) } - CustomButton { name: "Crack!", set_name: Box::new(set_name) } - CustomButton { name: "back!", set_name: Box::new(set_name) } - CustomButton { name: "cheder!", set_name: Box::new(set_name) } - // CustomButton { name: "Bill!", set_name: Box::new(move || set_name("Bill")) } - // CustomButton { name: "Reset!", set_name: Box::new(move || set_name(props.initial_name)) } - + CustomButton { name: "Jack!", set_name: set_name } + CustomButton { name: "Jill!", set_name: set_name } + CustomButton { name: "Bob!", set_name: set_name } } }) }; #[derive(Props)] struct ButtonProps<'src> { - name: &'static str, - // name: &'src str, - set_name: Box< dyn Fn(&'static str) + 'src> + name: &'src str, + set_name: &'src dyn Fn(String) } + +/// this is an awesome component +fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree { + ctx.render(rsx!{ + button { + class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" + onmouseover: move |evt| (props.set_name)(props.name.to_string()) + "{props.name}" + } + }) +} + + impl PartialEq for ButtonProps<'_> { fn eq(&self, other: &Self) -> bool { false } } - -fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree { - ctx.render(rsx!{ - // div { - button { - class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" - onmouseover: move |_| (props.set_name)(props.name) - "{props.name}" - } - // } - }) -} - diff --git a/packages/webview/Cargo.toml b/packages/webview/Cargo.toml index 1744a07af..a3563da00 100644 --- a/packages/webview/Cargo.toml +++ b/packages/webview/Cargo.toml @@ -21,4 +21,5 @@ thiserror = "1.0.23" log = "0.4.13" fern = { version = "0.6.0", features = ["colored"] } + [build-dependencies] diff --git a/packages/webview/README.md b/packages/webview/README.md index 2cc617541..6adb0ad8d 100644 --- a/packages/webview/README.md +++ b/packages/webview/README.md @@ -36,13 +36,12 @@ dioxus bundle --platform macOS ## Goals -Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-webview is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps. +Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-webview is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps. By bridging the native process, desktop apps can access full multithreading power, peripheral support, hardware access, and native filesystem controls without the hassle of web technologies. Our goal with Dioxus-webview is to make it easy to ship both a web and native application, and quickly see large performance boosts without having to re-write the whole stack. As the dioxus ecosystem grows, we hope to see 3rd parties providing wrappers for storage, offline mode, etc that supports both web and native technologies. - ## Tech Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron. -Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime is needed to +Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime *is* needed - we recommend async-std in tokio mode. diff --git a/packages/webview/client/Cargo.toml b/packages/webview/client/Cargo.toml new file mode 100644 index 000000000..1df42303a --- /dev/null +++ b/packages/webview/client/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "webview-client" +version = "0.1.0" +authors = ["Jonathan Kelley "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-core = {path = "../../core"} diff --git a/packages/webview/client/src/main.rs b/packages/webview/client/src/main.rs new file mode 100644 index 000000000..7a96b2aa0 --- /dev/null +++ b/packages/webview/client/src/main.rs @@ -0,0 +1,61 @@ +use dioxus_core::patch::Edit; +use dioxus_core::prelude::*; + +static SERVER_RENDERED_KEY: &'static str = "abc123"; +#[derive(Debug, PartialEq, Props)] +struct ServerRendered { + name0: String, + name1: String, + name2: String, + name3: String, + name4: String, + name5: String, + name6: String, + name7: String, +} + +fn main() { + // connect to the host server + + let server_rendered = use_server_rendered((111111, 11111, 11), SERVER_RENDERED_KEY, || { + ServerRendered { + name0: "abc".to_string(), + name1: "abc".to_string(), + name2: "abc".to_string(), + name3: "abc".to_string(), + name4: "abc".to_string(), + name5: "abc".to_string(), + name6: "abc".to_string(), + name7: "abc".to_string(), + } + }); + + let handler = dioxus_liveview::new_handler() + .from_directory("abc123") // serve a given directory as the root + .with_context(|| SomeContext {}) // build out a new context for all of the server-rendered components to share + .with_route(SERVER_RENDERED_KEY, |ctx, props: &ServerRendered| { + // + }) + .with_route(SERVER_RENDERED_KEY, |ctx, props| { + // + }) + .with_route(SERVER_RENDERED_KEY, |ctx, props| { + // + }) + .with_route(SERVER_RENDERED_KEY, |ctx, props| { + // + }) + .with_route(SERVER_RENDERED_KEY, |ctx, props| { + // + }) + .with_route(SERVER_RENDERED_KEY, |ctx, props| { + // + }) + // depend on the framework, build a different type of handler + // there are instructions on how to integrate this the various rusty web frameworks in the guide + .build(); + + server.get("abc", handler); +} + +fn use_server_rendered(_p: impl PartialEq, name: &'static str, f: impl Fn() -> F) {}