diff --git a/packages/core/src/changelist.rs b/packages/core/src/changelist.rs index 9f4d374b6..3bb936cef 100644 --- a/packages/core/src/changelist.rs +++ b/packages/core/src/changelist.rs @@ -64,9 +64,9 @@ pub enum Edit<'d> { /// We can go back and forth between the two via methods on GI #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct CbIdx { - gi_id: usize, - gi_gen: u64, - listener_idx: usize, + pub gi_id: usize, + pub gi_gen: u64, + pub listener_idx: usize, } impl CbIdx { diff --git a/packages/core/src/context.rs b/packages/core/src/context.rs index 1feeb2b61..68154b55d 100644 --- a/packages/core/src/context.rs +++ b/packages/core/src/context.rs @@ -102,6 +102,7 @@ pub mod hooks { use super::*; + #[derive(Debug)] pub struct Hook(pub Box); impl Hook { diff --git a/packages/core/src/dodriodiff.rs b/packages/core/src/dodriodiff.rs index c91a7c018..749a577b0 100644 --- a/packages/core/src/dodriodiff.rs +++ b/packages/core/src/dodriodiff.rs @@ -68,7 +68,7 @@ pub struct DiffMachine<'a> { impl<'a> DiffMachine<'a> { pub fn new(bump: &'a Bump) -> Self { - log::debug!("starting diff machine"); + // log::debug!("starting diff machine"); Self { change_list: EditMachine::new(bump), immediate_queue: Vec::new(), @@ -88,7 +88,8 @@ impl<'a> DiffMachine<'a> { new: &VNode<'a>, scope: Option, ) { - log::debug!("Diffing nodes"); + log::debug!("old {:#?}", old); + log::debug!("new {:#?}", new); // Set it while diffing // Reset it when finished diffing @@ -131,7 +132,7 @@ impl<'a> DiffMachine<'a> { // compare elements // if different, schedule different types of update (VNode::Element(eold), VNode::Element(enew)) => { - log::debug!("elements are different"); + // log::debug!("elements are different"); // If the element type is completely different, the element needs to be re-rendered completely if enew.tag_name != eold.tag_name || enew.namespace != eold.namespace { self.change_list.commit_traversal(); @@ -894,7 +895,7 @@ impl<'a> DiffMachine<'a> { children, namespace, }) => { - log::info!("Creating {:#?}", node); + // log::info!("Creating {:#?}", node); if let Some(namespace) = namespace { self.change_list.create_element_ns(tag_name, namespace); } else { diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 50718868e..32547ddd1 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -6,16 +6,29 @@ //! The goal here is to provide a consistent event interface across all renderer types use generational_arena::Index; +use crate::innerlude::CbIdx; + #[derive(Debug)] pub struct EventTrigger { pub component_id: Index, - pub listener_id: u32, + pub listener_id: usize, pub event: VirtualEvent, } impl EventTrigger { - pub fn new() -> Self { - todo!() + pub fn new(event: VirtualEvent, cb: CbIdx) -> Self { + let CbIdx { + gi_id, + gi_gen, + listener_idx, + } = cb; + + let component_id = Index::from_raw_parts(gi_id, gi_gen); + Self { + component_id, + listener_id: listener_idx, + event, + } } } diff --git a/packages/core/src/hooks.rs b/packages/core/src/hooks.rs index d75131301..5236c45c0 100644 --- a/packages/core/src/hooks.rs +++ b/packages/core/src/hooks.rs @@ -51,6 +51,7 @@ mod use_state_def { caller: Box::new(|_| println!("setter called!")), }, move |hook| { + log::debug!("Use_state set called"); let inner = hook.new_val.clone(); let scheduled_update = ctx.schedule_update(); diff --git a/packages/core/src/nodebuilder.rs b/packages/core/src/nodebuilder.rs index 8ca9686bd..e6b023e08 100644 --- a/packages/core/src/nodebuilder.rs +++ b/packages/core/src/nodebuilder.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use crate::{ + events::VirtualEvent, innerlude::VComponent, nodes::{Attribute, Listener, NodeKey, VNode}, prelude::VElement, @@ -338,7 +339,7 @@ where /// .finish(); /// ``` #[inline] - pub fn on(mut self, event: &'static str, callback: impl Fn(()) + 'a) -> Self { + pub fn on(mut self, event: &'static str, callback: impl Fn(VirtualEvent) + 'a) -> Self { self.listeners.push(Listener { event, callback: self.bump.alloc(callback), @@ -1081,7 +1082,7 @@ pub fn on<'a, 'b>( // pub fn on<'a, 'b, F: 'static>( bump: &'a Bump, event: &'static str, - callback: impl Fn(()) + 'a, + callback: impl Fn(VirtualEvent) + 'a, ) -> Listener<'a> { Listener { event, diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index fdf88d4eb..1e6cd21c0 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -94,6 +94,8 @@ mod vnode { } mod velement { + use crate::events::VirtualEvent; + use super::*; use std::fmt::Debug; @@ -188,7 +190,7 @@ mod velement { // pub(crate) _i: &'bump str, // #[serde(skip_serializing, skip_deserializing, default="")] // /// The callback to invoke when the event happens. - pub(crate) callback: &'bump (dyn Fn(())), + pub(crate) callback: &'bump (dyn Fn(VirtualEvent)), } impl Debug for Listener<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -197,7 +199,7 @@ mod velement { .finish() } } - pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(())); + pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(VirtualEvent)); union CallbackFatPtr<'a> { callback: ListenerCallback<'a>, parts: (u32, u32), diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 6749316bb..b4b84868e 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -36,8 +36,10 @@ pub struct Scope { pub frames: ActiveFrame, - // IE Which listeners need to be woken up? - pub listeners: Vec>, + // List of listeners generated when CTX is evaluated + pub listeners: Vec<*const dyn Fn(crate::events::VirtualEvent)>, + // pub listeners: Vec<*const dyn Fn(crate::events::VirtualEvent)>, + // pub listeners: Vec>, // lying, cheating reference >:( pub props: Box, @@ -55,9 +57,8 @@ impl Scope { // Capture the caller let caller = f as *const (); - let listeners: Vec> = vec![Box::new(|_| { - log::info!("Base listener called"); - })]; + let listeners = vec![]; + // let listeners: Vec> = vec![]; let old_frame = BumpFrame { bump: Bump::new(), @@ -95,6 +96,7 @@ impl Scope { let frame = { let frame = self.frames.next(); frame.bump.reset(); + log::debug!("Rednering into frame {:?}", frame as *const _); frame }; @@ -145,6 +147,20 @@ impl Scope { .borrow_mut() .take() .expect("Viewing did not happen"); + + // todo: + // make this so we dont have to iterate through the vnodes to get its listener + let mut listeners = vec![]; + retrieve_listeners(&frame.head_node, &mut listeners); + self.listeners = listeners + .into_iter() + .map(|f| { + let g = f.callback; + g as *const _ + }) + .collect(); + + // consume the listeners from the head_node into a list of boxed ref listeners } } @@ -159,6 +175,18 @@ impl Scope { } } +fn retrieve_listeners(node: &VNode<'static>, listeners: &mut Vec<&Listener>) { + if let VNode::Element(el) = *node { + for listener in el.listeners { + // let g = listener as *const Listener; + listeners.push(listener); + } + for child in el.children { + retrieve_listeners(child, listeners); + } + } +} + // todo, do better with the active frame stuff // somehow build this vnode with a lifetime tied to self // This root node has "static" lifetime, but it's really not static. @@ -184,7 +212,7 @@ impl ActiveFrame { } fn current_head_node<'b>(&'b self) -> &'b VNode<'b> { - let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 { + let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 { true => &self.frames[0], false => &self.frames[1], }; @@ -196,7 +224,7 @@ impl ActiveFrame { } fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> { - let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 { + let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 { true => &self.frames[0], false => &self.frames[1], }; @@ -211,11 +239,22 @@ impl ActiveFrame { fn next(&mut self) -> &mut BumpFrame { self.idx.fetch_add(1, Ordering::Relaxed); let cur = self.idx.borrow().load(Ordering::Relaxed); - match cur % 1 { - 1 => &mut self.frames[1], - 0 => &mut self.frames[0], - _ => unreachable!("mod cannot by non-zero"), + log::debug!("Next frame! {}", cur); + + if cur % 2 == 0 { + log::debug!("Chosing frame 0"); + &mut self.frames[0] + } else { + log::debug!("Chosing frame 1"); + &mut self.frames[1] } + // match cur % 1 { + // 0 => { + // } + // 1 => { + // } + // _ => unreachable!("mod cannot by non-zero"), + // } } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index c5224e02e..4d896ed1f 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -140,29 +140,63 @@ impl VirtualDom { let component = self .components - .get(component_id) + .get_mut(component_id) .expect("Component should exist if an event was triggered"); - let listener = component - .listeners - .get(listener_id as usize) - .expect("Listener should exist if it was triggered") - .as_ref(); + log::debug!("list: {}", component.listeners.len()); + + let listener = unsafe { + component + .listeners + .get(listener_id as usize) + .expect("Listener should exist if it was triggered") + .as_ref() + } + .unwrap(); // Run the callback with the user event listener(source); + // Reset and then build a new diff machine + // The previous edit list cannot be around while &mut is held + // Make sure variance doesnt break this + self.diff_bump.reset(); + let mut diff_machine = DiffMachine::new(&self.diff_bump); + + // this is still a WIP + // we'll need to re-fecth all the scopes that were changed and build the diff machine + // fetch the component again + // let component = self + // .components + // .get_mut(self.base_scope) + // .expect("Root should always exist"); + + component.run::<()>(); + + diff_machine.diff_node( + component.old_frame(), + component.new_frame(), + Some(self.base_scope), + ); + // diff_machine.diff_node( + // component.old_frame(), + // component.new_frame(), + // Some(self.base_scope), + // ); + + Ok(diff_machine.consume()) + // Err(crate::error::Error::NoEvent) // Mark dirty components. Descend from the highest node until all dirty nodes are updated. - let mut affected_components = Vec::new(); + // let mut affected_components = Vec::new(); - while let Some(event) = self.pop_event() { - if let Some(component_idx) = event.index() { - affected_components.push(component_idx); - } - self.process_lifecycle(event)?; - } + // while let Some(event) = self.pop_event() { + // if let Some(component_idx) = event.index() { + // affected_components.push(component_idx); + // } + // self.process_lifecycle(event)?; + // } - todo!() + // todo!() } /// Using mutable access to the Virtual Dom, progress a given lifecycle event diff --git a/packages/web/examples/basic.rs b/packages/web/examples/basic.rs index ffd17ce5d..fc4e127af 100644 --- a/packages/web/examples/basic.rs +++ b/packages/web/examples/basic.rs @@ -6,10 +6,6 @@ use dioxus_web::*; fn main() { // Setup logging wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - // wasm_logger::init(wasm_logger::Config::with_prefix( - // log::Level::Debug, - // "dioxus_core", - // )); console_error_panic_hook::set_once(); // Run the app diff --git a/packages/web/examples/jackjill.rs b/packages/web/examples/jackjill.rs index 5933c4cf8..e5484fea5 100644 --- a/packages/web/examples/jackjill.rs +++ b/packages/web/examples/jackjill.rs @@ -1,13 +1,10 @@ -use dioxus::prelude::bumpalo; -use dioxus::prelude::format_args_f; use dioxus::prelude::*; use dioxus_core as dioxus; -use dioxus_core::prelude::html; use dioxus_web::WebsysRenderer; fn main() { - pretty_env_logger::init(); - log::info!("Hello!"); + wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); + console_error_panic_hook::set_once(); wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example)) } @@ -15,11 +12,55 @@ fn main() { static Example: FC<()> = |ctx, props| { let (name, set_name) = use_state(&ctx, || "...?"); + log::debug!("Running component...."); + ctx.view(html! { -
-

"Hello, {name}"

- - -
- }) + //
+ //

"Hello, {name}"

+ // + // + //
+
+
+
+ // Tagline + + "Dioxus Example: Jack and Jill" + + + // Header +

+ "Hello, {name}" +

+ + // Control buttons +
+ + + +
+
+
+
+ }) }; + +//
+//

"Hello, {name}"

+// +// +//
diff --git a/packages/web/src/interpreter.rs b/packages/web/src/interpreter.rs index bc0b227fd..44f56aeec 100644 --- a/packages/web/src/interpreter.rs +++ b/packages/web/src/interpreter.rs @@ -1,6 +1,9 @@ -use std::fmt::Debug; +use std::{borrow::Borrow, fmt::Debug, sync::Arc}; -use dioxus_core::{changelist::Edit, events::EventTrigger}; +use dioxus_core::{ + changelist::{CbIdx, Edit}, + events::{EventTrigger, MouseEvent, VirtualEvent}, +}; use fxhash::FxHashMap; use log::debug; use wasm_bindgen::{closure::Closure, JsCast}; @@ -9,10 +12,13 @@ use web_sys::{window, Document, Element, Event, HtmlInputElement, HtmlOptionElem #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct CacheId(u32); -struct RootCallback(Box); +#[derive(Clone)] +struct RootCallback(Arc); impl std::fmt::Debug for RootCallback { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() + Ok(()) + // a no-op for now + // todo!() } } @@ -37,15 +43,18 @@ pub(crate) struct EventDelegater { callback_id: usize, // map of listener types to number of those listeners - listeners: FxHashMap<&'static str, (usize, Closure)>, + listeners: FxHashMap)>, // Map of callback_id to component index and listener id callback_map: FxHashMap, + + trigger: RootCallback, } impl EventDelegater { - pub fn new(root: Element) -> Self { + pub fn new(root: Element, trigger: impl Fn(EventTrigger) + 'static) -> Self { Self { + trigger: RootCallback(Arc::new(trigger)), root, callback_id: 0, listeners: FxHashMap::default(), @@ -53,12 +62,55 @@ impl EventDelegater { } } - pub fn add_listener( - &mut self, - event: &'static str, - gi: generational_arena::Index, - listener_id: usize, - ) { + pub fn add_listener(&mut self, event: &str, cb: CbIdx) { + if let Some(entry) = self.listeners.get_mut(event) { + entry.0 += 1; + } else { + let trigger = self.trigger.clone(); + let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| { + log::debug!("Handling event!"); + + let target = event + .target() + .expect("missing target") + .dyn_into::() + .expect("not a valid element"); + + let typ = event.type_(); + + let gi_id: usize = target + .get_attribute(&format!("dioxus-giid-{}", typ)) + .and_then(|v| v.parse().ok()) + .unwrap_or_default(); + + let gi_gen: u64 = target + .get_attribute(&format!("dioxus-gigen-{}", typ)) + .and_then(|v| v.parse().ok()) + .unwrap_or_default(); + + let li_idx: usize = target + .get_attribute(&format!("dioxus-lidx-{}", typ)) + .and_then(|v| v.parse().ok()) + .unwrap_or_default(); + + // Call the trigger + trigger.0.as_ref()(EventTrigger::new( + virtual_event_from_websys_event(event), + CbIdx { + gi_gen, + gi_id, + listener_idx: li_idx, + }, + )); + }) as Box); + + self.root + .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref()) + .unwrap(); + + // Increment the listeners + self.listeners.insert(event.into(), (1, handler)); + } } } @@ -106,14 +158,14 @@ impl Stack { } impl PatchMachine { - pub fn new(root: Element, event_callback: impl Fn(EventTrigger)) -> Self { + pub fn new(root: Element, event_callback: impl Fn(EventTrigger) + 'static) -> Self { let document = window() .expect("must have access to the window") .document() .expect("must have access to the Document"); // attach all listeners to the container element - let events = EventDelegater::new(root.clone()); + let events = EventDelegater::new(root.clone(), event_callback); Self { root, @@ -146,31 +198,6 @@ impl PatchMachine { // self.templates.get(&id) } - /// On any listener wakeup, find the listener that generated the event from th e attribute - // pub fn init_events_trampoline(&mut self, _trampoline: ()) { - pub fn init_events_trampoline(&mut self, event_channel: impl Fn(EventTrigger)) { - // self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| { - // let target = event - // .target() - // .expect("missing target") - // .dyn_into::() - // .expect("not a valid element"); - // let typ = event.type_(); - // let a: u32 = target - // .get_attribute(&format!("dioxus-a-{}", typ)) - // .and_then(|v| v.parse().ok()) - // .unwrap_or_default(); - - // let b: u32 = target - // .get_attribute(&format!("dioxus-b-{}", typ)) - // .and_then(|v| v.parse().ok()) - // .unwrap_or_default(); - - // // get a and b from the target - // trampoline(event.clone(), a, b); - // }) as Box)); - } - pub fn handle_edit(&mut self, edit: &Edit) { match *edit { // 0 @@ -319,11 +346,7 @@ impl PatchMachine { } // 11 - Edit::NewEventListener { - event_type, - idx: a, - b, - } => { + Edit::NewEventListener { event_type, idx } => { // attach the correct attributes to the element // these will be used by accessing the event's target // This ensures we only ever have one handler attached to the root, but decide @@ -341,24 +364,33 @@ impl PatchMachine { // ) // .unwrap(); - debug!("adding attributes: {}, {}", a, b); - el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string()) + // debug!("adding attributes: {}, {}", a, b); + + let CbIdx { + gi_id, + gi_gen, + listener_idx: lidx, + } = idx; + + el.set_attribute(&format!("dioxus-giid-{}", event_type), &gi_id.to_string()) .unwrap(); - el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string()) + el.set_attribute(&format!("dioxus-gigen-{}", event_type), &gi_gen.to_string()) + .unwrap(); + el.set_attribute(&format!("dioxus-lidx-{}", event_type), &lidx.to_string()) .unwrap(); - self.events.add_listener(event_type, gi, listener_id) + self.events.add_listener(event_type, idx); } // 12 - Edit::UpdateEventListener { event_type, a, b } => { + Edit::UpdateEventListener { event_type, idx } => { // update our internal mapping, and then modify the attribute if let Some(el) = self.stack.top().dyn_ref::() { - el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string()) - .unwrap(); - el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string()) - .unwrap(); + // el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string()) + // .unwrap(); + // el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string()) + // .unwrap(); } } @@ -468,3 +500,10 @@ impl PatchMachine { // // self.templates.contains_key(&id) // } } + +fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { + match event.type_().as_str() { + "click" => VirtualEvent::MouseEvent(MouseEvent {}), + _ => VirtualEvent::OtherEvent, + } +} diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 7c4399788..6caad18e6 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -59,12 +59,19 @@ impl WebsysRenderer { } pub async fn run(&mut self) -> dioxus_core::error::Result<()> { - let (sender, mut receiver) = mpsc::unbounded::(); + let (mut sender, mut receiver) = mpsc::unbounded::(); let body_element = prepare_websys_dom(); - let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), |_| {}); + + let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), move |ev| { + log::debug!("Event trigger! {:#?}", ev); + let mut c = sender.clone(); + wasm_bindgen_futures::spawn_local(async move { + c.send(ev).await.unwrap(); + }); + }); let root_node = body_element.first_child().unwrap(); - patch_machine.stack.push(root_node); + patch_machine.stack.push(root_node.clone()); // todo: initialize the event registry properly on the root @@ -72,17 +79,32 @@ impl WebsysRenderer { log::debug!("patching with {:?}", edit); patch_machine.handle_edit(edit); }); + log::debug!("patch stack size {:?}", patch_machine.stack); // Event loop waits for the receiver to finish up // TODO! Connect the sender to the virtual dom's suspense system // Suspense is basically an external event that can force renders to specific nodes while let Some(event) = receiver.next().await { + log::debug!("patch stack size before {:#?}", patch_machine.stack); + // patch_machine.reset(); + // patch_machine.stack.push(root_node.clone()); self.internal_dom .progress_with_event(event)? .iter() .for_each(|edit| { + log::debug!("edit stream {:?}", edit); patch_machine.handle_edit(edit); }); + + log::debug!("patch stack size after {:#?}", patch_machine.stack); + patch_machine.reset(); + // our root node reference gets invalidated + // not sure why + // for now, just select the first child again. + // eventually, we'll just make our own root element instead of using body + // or just use body directly IDEK + let root_node = body_element.first_child().unwrap(); + patch_machine.stack.push(root_node.clone()); } Ok(()) // should actually never return from this, should be an error, rustc just cant see it @@ -146,13 +168,14 @@ mod tests { log::info!("Hello!"); wasm_bindgen_futures::spawn_local(WebsysRenderer::start(|ctx, _| { - ctx.view(html! { -
- "Hello world" - - -
- }) + todo!() + // ctx.view(html! { + //
+ // "Hello world" + // + // + //
+ // }) })) } }