diff --git a/examples/hydration.rs b/examples/hydration.rs index dc9188f5a..0f083d933 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::prelude::*; use dioxus::ssr; fn main() { - let mut vdom = VirtualDom::launch_in_place(App); + let vdom = VirtualDom::new(App); let content = ssr::render_vdom(&vdom, |f| f.pre_render(true)); dioxus::desktop::launch(App, |c| c.with_prerendered(content)).unwrap(); diff --git a/examples/reference/tostring.rs b/examples/reference/tostring.rs index 8827932f2..02b1daf81 100644 --- a/examples/reference/tostring.rs +++ b/examples/reference/tostring.rs @@ -6,7 +6,7 @@ pub static Example: FC<()> = |cx| { // Currently, SSR is only supported for whole VirtualDOMs // This is an easy/low hanging fruit to improve upon let mut dom = VirtualDom::new(SomeApp); - dom.rebuild_in_place().unwrap(); + dom.rebuild(); ssr::render_vdom(&dom, |c| c) }); diff --git a/examples/web_tick.rs b/examples/web_tick.rs index 8d58acfec..e82f131ec 100644 --- a/examples/web_tick.rs +++ b/examples/web_tick.rs @@ -13,32 +13,13 @@ use dioxus::prelude::*; fn main() { - // Setup logging - // wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - // console_error_panic_hook::set_once(); - for adj in ADJECTIVES { - wasm_bindgen::intern(adj); - } - for col in COLOURS { - wasm_bindgen::intern(col); - } - for no in NOUNS { - wasm_bindgen::intern(no); - } - wasm_bindgen::intern("col-md-1"); - wasm_bindgen::intern("col-md-6"); - wasm_bindgen::intern("glyphicon glyphicon-remove remove"); - wasm_bindgen::intern("remove"); - wasm_bindgen::intern("dioxus"); - wasm_bindgen::intern("lbl"); - wasm_bindgen::intern("true"); + #[cfg(target_arch = "wasm32")] + intern_strings(); dioxus::web::launch(App, |c| c); } static App: FC<()> = |cx| { - // let mut count = use_state(cx, || 0); - let mut rng = SmallRng::from_entropy(); let rows = (0..1_000).map(|f| { let label = Label::new(&mut rng); @@ -49,6 +30,7 @@ static App: FC<()> = |cx| { } } }); + cx.render(rsx! { table { tbody { @@ -56,15 +38,6 @@ static App: FC<()> = |cx| { } } }) - // cx.render(rsx! { - // div { - // // h1 { "Hifive counter: {count}" } - // // {cx.children()} - // // button { onclick: move |_| count += 1, "Up high!" } - // // button { onclick: move |_| count -= 1, "Down low!" } - // {(0..1000).map(|i| rsx!{ div { "Count: {count}" } })} - // } - // }) }; #[derive(PartialEq, Props)] @@ -141,3 +114,23 @@ static NOUNS: &[&str] = &[ "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", "keyboard", ]; + +#[cfg(target_arch = "wasm32")] +fn intern_strings() { + for adj in ADJECTIVES { + wasm_bindgen::intern(adj); + } + for col in COLOURS { + wasm_bindgen::intern(col); + } + for no in NOUNS { + wasm_bindgen::intern(no); + } + wasm_bindgen::intern("col-md-1"); + wasm_bindgen::intern("col-md-6"); + wasm_bindgen::intern("glyphicon glyphicon-remove remove"); + wasm_bindgen::intern("remove"); + wasm_bindgen::intern("dioxus"); + wasm_bindgen::intern("lbl"); + wasm_bindgen::intern("true"); +} diff --git a/packages/core/examples/vdom_usage.rs b/packages/core/examples/vdom_usage.rs index 027452104..4dde570d8 100644 --- a/packages/core/examples/vdom_usage.rs +++ b/packages/core/examples/vdom_usage.rs @@ -12,8 +12,4 @@ async fn main() { let mut deadline = async_std::task::sleep(Duration::from_millis(50)); let fut = dom.run_with_deadline(deadline); - - if let Some(mutations) = fut.await { - // - } } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index d8e423c9f..7c6ef2751 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -106,7 +106,6 @@ use DomEdit::*; /// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's /// stack machines all the way down! pub struct DiffMachine<'bump> { - pub channel: EventChannel, pub vdom: &'bump ResourcePool, pub mutations: Mutations<'bump>, pub stack: DiffStack<'bump>, @@ -117,7 +116,6 @@ pub struct DiffMachine<'bump> { /// in regular diff machine, the &'bump reference is a stack borrow, but the /// bump lifetimes are heap borrows. pub struct SavedDiffWork<'bump> { - pub channel: EventChannel, pub mutations: Mutations<'bump>, pub stack: DiffStack<'bump>, pub seen_scopes: FxHashSet, @@ -131,7 +129,6 @@ impl<'a> SavedDiffWork<'a> { let extended: SavedDiffWork<'b> = std::mem::transmute(self); DiffMachine { vdom, - channel: extended.channel, mutations: extended.mutations, stack: extended.stack, seen_scopes: extended.seen_scopes, @@ -140,13 +137,8 @@ impl<'a> SavedDiffWork<'a> { } impl<'bump> DiffMachine<'bump> { - pub(crate) fn new( - edits: Mutations<'bump>, - shared: &'bump mut ResourcePool, - channel: EventChannel, - ) -> Self { + pub(crate) fn new(edits: Mutations<'bump>, shared: &'bump mut ResourcePool) -> Self { Self { - channel, stack: DiffStack::new(), mutations: edits, vdom: shared, @@ -156,7 +148,6 @@ impl<'bump> DiffMachine<'bump> { pub fn save(self) -> SavedDiffWork<'bump> { SavedDiffWork { - channel: self.channel, mutations: self.mutations, stack: self.stack, seen_scopes: self.seen_scopes, @@ -208,12 +199,10 @@ impl<'bump> DiffMachine<'bump> { } DiffInstruction::PrepareMoveNode { node } => { - log::debug!("Preparing to move node: {:?}", node); - todo!(); - // for el in RealChildIterator::new(node, self.vdom) { - // self.mutations.push_root(el.direct_id()); - // self.stack.add_child_count(1); - // } + for el in RealChildIterator::new(node, self.vdom) { + self.mutations.push_root(el.direct_id()); + self.stack.add_child_count(1); + } } }; @@ -237,12 +226,11 @@ impl<'bump> DiffMachine<'bump> { } MountType::Replace { old } => { - todo!() - // let mut iter = RealChildIterator::new(old, self.vdom); - // let first = iter.next().unwrap(); - // self.mutations - // .replace_with(first.direct_id(), nodes_created as u32); - // self.remove_nodes(iter); + let mut iter = RealChildIterator::new(old, self.vdom); + let first = iter.next().unwrap(); + self.mutations + .replace_with(first.direct_id(), nodes_created as u32); + self.remove_nodes(iter); } MountType::ReplaceByElementId { el: old } => { @@ -343,7 +331,7 @@ impl<'bump> DiffMachine<'bump> { let parent_idx = self.stack.current_scope().unwrap(); - let shared = self.channel.clone(); + let shared = self.vdom.channel.clone(); // Insert a new scope into our component list let parent_scope = self.vdom.get_scope(parent_idx).unwrap(); let new_idx = self.vdom.insert_scope_with_key(|new_idx| { diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 454975a7d..a52c24f53 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -15,11 +15,6 @@ pub struct EventTrigger { /// The type of event pub event: SyntheticEvent, - - /// Is consistency important for this event? - /// UI events are the only events where consistency is important. - /// All else may be batched. - pub discrete: bool, } pub enum SyntheticEvent { diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 0850181ed..910ba3ea6 100644 --- a/packages/core/src/scheduler.rs +++ b/packages/core/src/scheduler.rs @@ -133,8 +133,6 @@ pub struct Scheduler { pub heuristics: HeuristicsEngine, - pub channel: EventChannel, - pub receiver: UnboundedReceiver, // Garbage stored @@ -166,11 +164,6 @@ impl Scheduler { let components = Rc::new(UnsafeCell::new(Slab::with_capacity(100))); let raw_elements = Rc::new(UnsafeCell::new(Slab::with_capacity(2000))); - let pool = ResourcePool { - components: components.clone(), - raw_elements, - }; - let heuristics = HeuristicsEngine::new(); let (sender, receiver) = futures_channel::mpsc::unbounded::(); @@ -214,9 +207,14 @@ impl Scheduler { }, }; + let pool = ResourcePool { + components: components.clone(), + raw_elements, + channel, + }; + Self { pool, - channel, receiver, async_tasks: FuturesUnordered::new(), @@ -253,7 +251,7 @@ impl Scheduler { } // Converts UI events into dirty scopes with various priorities - pub fn consume_pending_events(&mut self) -> Result<()> { + pub fn consume_pending_events(&mut self) { // while let Some(trigger) = self.ui_events.pop_back() { // match &trigger.event { // SyntheticEvent::ClipboardEvent(_) @@ -287,8 +285,6 @@ impl Scheduler { // } // } // } - - Ok(()) } // nothing to do, no events on channels, no work @@ -363,8 +359,6 @@ impl Scheduler { /// Uses some fairly complex logic to schedule what work should be produced. /// /// Returns a list of successful mutations. - /// - /// pub async fn work_with_deadline<'a>( &'a mut self, mut deadline: Pin>>, @@ -392,10 +386,6 @@ impl Scheduler { */ let mut committed_mutations = Vec::>::new(); - // TODO: - // the scheduler uses a bunch of different receivers to mimic a "topic" queue system. The futures-channel implementation - // doesn't really have a concept of a "topic" queue, so there's a lot of noise in the hand-rolled scheduler. We should - // explore abstracting the scheduler into a topic-queue channel system - similar to Kafka or something similar. loop { // Internalize any pending work since the last time we ran self.manually_poll_events(); @@ -411,7 +401,7 @@ impl Scheduler { } // Create work from the pending event queue - self.consume_pending_events().unwrap(); + self.consume_pending_events(); // Work through the current subtree, and commit the results when it finishes // When the deadline expires, give back the work @@ -443,8 +433,8 @@ impl Scheduler { pin_mut!(fut); use futures_util::future::{select, Either}; match select(fut, &mut deadline).await { - Either::Left((work, _other)) => true, - Either::Right((deadline, _other)) => false, + Either::Left((_, _other)) => true, + Either::Right((_, _other)) => false, } }; @@ -461,6 +451,7 @@ impl Scheduler { committed_mutations.push(new_mutations); } + self.save_work(saved); if !completed { @@ -670,6 +661,8 @@ pub struct ResourcePool { In the future, we could actually store a pointer to the VNode instead of nil to provide O(1) lookup for VNodes... */ pub raw_elements: Rc>>, + + pub channel: EventChannel, } impl ResourcePool { @@ -711,8 +704,8 @@ impl ResourcePool { } pub fn reserve_node(&self) -> ElementId { - todo!("reserving wip until it's fast enough again") - // ElementId(self.raw_elements.insert(())) + let els = unsafe { &mut *self.raw_elements.get() }; + ElementId(els.insert(())) } /// return the id, freeing the space of the original node diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 2222a7335..40bfd43ec 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -164,7 +164,7 @@ impl Scope { "clean up your garabge please" ); - todo!("arch changes"); + // todo!("arch changes"); // // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we // // run the hooks (which hold an &mut Referrence) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 3d7d6d58c..6c75899ba 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -116,7 +116,7 @@ impl VirtualDom { None, 0, ScopeChildren(&[]), - scheduler.channel.clone(), + scheduler.pool.channel.clone(), ) }); @@ -158,29 +158,31 @@ impl VirtualDom { /// the diff and creating nodes can be expensive, so we provide this method to avoid blocking the main thread. This /// method can be useful when needing to perform some crucial periodic tasks. pub async fn rebuild_async<'s>(&'s mut self) -> Mutations<'s> { - todo!() - // let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.scheduler); + let mut shared = self.scheduler.pool.clone(); + let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared); - // let cur_component = self - // .scheduler - // .get_scope_mut(self.base_scope) - // .expect("The base scope should never be moved"); + let cur_component = self + .scheduler + .pool + .get_scope_mut(self.base_scope) + .expect("The base scope should never be moved"); - // // // We run the component. If it succeeds, then we can diff it and add the changes to the dom. - // if cur_component.run_scope().is_ok() { - // diff_machine - // .stack - // .create_node(cur_component.frames.fin_head(), MountType::Append); - // diff_machine.work().await; - // } else { - // // todo: should this be a hard error? - // log::warn!( - // "Component failed to run succesfully during rebuild. - // This does not result in a failed rebuild, but indicates a logic failure within your app." - // ); - // } + // // We run the component. If it succeeds, then we can diff it and add the changes to the dom. + if cur_component.run_scope().is_ok() { + diff_machine + .stack + .create_node(cur_component.frames.fin_head(), MountType::Append); + diff_machine.stack.scope_stack.push(self.base_scope); + diff_machine.work().await; + } else { + // todo: should this be a hard error? + log::warn!( + "Component failed to run succesfully during rebuild. + This does not result in a failed rebuild, but indicates a logic failure within your app." + ); + } - // diff_machine.mutations + unsafe { std::mem::transmute(diff_machine.mutations) } } pub fn diff_sync<'s>(&'s mut self) -> Mutations<'s> { @@ -194,7 +196,7 @@ impl VirtualDom { } pub async fn diff_async<'s>(&'s mut self) -> Mutations<'s> { - let mut diff_machine = DiffMachine::new(Mutations::new(), todo!(), todo!()); + let mut diff_machine = DiffMachine::new(Mutations::new(), todo!()); let cur_component = self .scheduler @@ -273,7 +275,7 @@ impl VirtualDom { } pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender { - self.scheduler.channel.sender.clone() + self.scheduler.pool.channel.sender.clone() } pub fn has_work(&self) -> bool { diff --git a/packages/core/tests/eventsystem.rs b/packages/core/tests/eventsystem.rs index 2b90ca256..db57118b4 100644 --- a/packages/core/tests/eventsystem.rs +++ b/packages/core/tests/eventsystem.rs @@ -17,6 +17,6 @@ async fn event_queue_works() { let edits = dom.rebuild(); async_std::task::spawn_local(async move { - let mutations = dom.run_unbounded().await; + // let mutations = dom.run_unbounded().await; }); } diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index 6d4c8ddd5..ee26d2fbc 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -24,8 +24,11 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> EventTrigger { let event = SyntheticEvent::MouseEvent(MouseEvent(Rc::new(WebviewMouseEvent))); let scope = ScopeId(data.scope as usize); let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize)); - let priority = EventPriority::High; - EventTrigger::new(event, scope, mounted_dom_id, priority) + EventTrigger { + event, + scope, + mounted_dom_id, + } } #[derive(Debug)] diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index cb907cdc6..5a62bfe76 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}; use dioxus_core::{ events::{on::GenericEventInner, EventTrigger, SyntheticEvent}, mutations::NodeRefMutation, + scheduler::SchedulerMsg, DomEdit, ElementId, ScopeId, }; use fxhash::FxHashMap; @@ -24,7 +25,7 @@ pub struct WebsysDom { root: Element, - sender_callback: Rc, + sender_callback: Rc, // map of listener types to number of those listeners // This is roughly a delegater @@ -39,7 +40,7 @@ pub struct WebsysDom { last_node_was_text: bool, } impl WebsysDom { - pub fn new(root: Element, cfg: WebConfig, sender_callback: Rc) -> Self { + pub fn new(root: Element, cfg: WebConfig, sender_callback: Rc) -> Self { let document = load_document(); let mut nodes = NodeSlab::new(2000); @@ -277,7 +278,7 @@ impl WebsysDom { // "Result" cannot be received from JS // Instead, we just build and immediately execute a closure that returns result match decode_trigger(event) { - Ok(synthetic_event) => trigger.as_ref()(synthetic_event), + Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::UiEvent(synthetic_event)), Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e), }; }) as Box); @@ -560,12 +561,11 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { let triggered_scope = gi_id; // let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into(); log::debug!("Triggered scope is {:#?}", triggered_scope); - Ok(EventTrigger::new( - virtual_event_from_websys_event(event.clone()), - ScopeId(triggered_scope as usize), - Some(ElementId(real_id as usize)), - dioxus_core::events::EventPriority::High, - )) + Ok(EventTrigger { + event: virtual_event_from_websys_event(event.clone()), + mounted_dom_id: Some(ElementId(real_id as usize)), + scope: ScopeId(triggered_scope as usize), + }) } pub fn prepare_websys_dom() -> Element { diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index e8180ef85..b22481676 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -97,8 +97,7 @@ where F: FnOnce(WebConfig) -> WebConfig, { let config = config(WebConfig::default()); - let fut = run_with_props(root, root_props, config); - wasm_bindgen_futures::spawn_local(fut); + wasm_bindgen_futures::spawn_local(run_with_props(root, root_props, config)); } /// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering. /// See DioxusErrors for more information on how these errors could occour. @@ -143,9 +142,8 @@ pub async fn run_with_props(root: FC, root_props: T, let deadline = work_loop.wait_for_idle_time().await; - if let Some(mut mutations) = dom.run_with_deadline(deadline).await { - work_loop.wait_for_raf().await; - websys_dom.process_edits(&mut mutations.edits); - } + let mut mutations = dom.run_with_deadline(deadline).await; + work_loop.wait_for_raf().await; + websys_dom.process_edits(&mut mutations[0].edits); } }