#![doc = include_str!("../README.md")] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] mod element; mod events; use std::{ any::Any, ops::Deref, rc::Rc, sync::{Arc, RwLock}, }; use dioxus_core::{Component, ElementId, VirtualDom}; use dioxus_html::PlatformEventData; use dioxus_native_core::dioxus::{DioxusState, NodeImmutableDioxusExt}; use dioxus_native_core::prelude::*; use element::{create_mounted_events, find_mount_events}; pub use plasmo::{query::Query, Config, RenderingMode, Size, TuiContext}; use plasmo::{render, Driver}; pub fn launch(app: Component<()>) { launch_cfg(app, Config::default()) } pub fn launch_cfg(app: Component<()>, cfg: Config) { launch_cfg_with_props(app, (), cfg); } pub fn launch_cfg_with_props(app: Component, props: Props, cfg: Config) { dioxus_html::set_event_converter(Box::new(events::SerializedHtmlEventConverter)); render(cfg, |rdom, taffy, event_tx| { let dioxus_state = { let mut rdom = rdom.write().unwrap(); DioxusState::create(&mut rdom) }; let dioxus_state = Rc::new(RwLock::new(dioxus_state)); let mut vdom = VirtualDom::new_with_props(app, props) .with_root_context(TuiContext::new(event_tx)) .with_root_context(Query::new(rdom.clone(), taffy.clone())) .with_root_context(DioxusElementToNodeId { mapping: dioxus_state.clone(), }); let muts = vdom.rebuild(); let mut queued_events = Vec::new(); { let mut rdom = rdom.write().unwrap(); let mut dioxus_state = dioxus_state.write().unwrap(); // Find any mount events let mounted = find_mount_events(&muts); dioxus_state.apply_mutations(&mut rdom, muts); // Send the mount events create_mounted_events( &vdom, &mut queued_events, mounted .iter() .map(|id| (*dbg!(id), dioxus_state.element_to_node_id(*id))), ); } DioxusRenderer { vdom, dioxus_state, queued_events, #[cfg(all(feature = "hot-reload", debug_assertions))] hot_reload_rx: { let (hot_reload_tx, hot_reload_rx) = tokio::sync::mpsc::unbounded_channel::(); dioxus_hot_reload::connect(move |msg| { let _ = hot_reload_tx.send(msg); }); hot_reload_rx }, } }) .unwrap(); } struct DioxusRenderer { vdom: VirtualDom, dioxus_state: Rc>, // Events that are queued up to be sent to the vdom next time the vdom is polled queued_events: Vec<(ElementId, &'static str, Box, bool)>, #[cfg(all(feature = "hot-reload", debug_assertions))] hot_reload_rx: tokio::sync::mpsc::UnboundedReceiver, } impl Driver for DioxusRenderer { fn update(&mut self, rdom: &Arc>) { let muts = self.vdom.render_immediate(); { let mut rdom = rdom.write().unwrap(); { // Find any mount events let mounted = find_mount_events(&muts); let mut dioxus_state = self.dioxus_state.write().unwrap(); dioxus_state.apply_mutations(&mut rdom, muts); // Send the mount events create_mounted_events( &self.vdom, &mut self.queued_events, mounted .iter() .map(|id| (*id, dioxus_state.element_to_node_id(*id))), ); } } } fn handle_event( &mut self, rdom: &Arc>, id: NodeId, event: &str, value: Rc, bubbles: bool, ) { let id = { rdom.read().unwrap().get(id).unwrap().mounted_id() }; if let Some(id) = id { let inner_value = value.deref().clone(); let boxed_event = Box::new(inner_value); let platform_event = PlatformEventData::new(boxed_event); self.vdom .handle_event(event, Rc::new(platform_event), id, bubbles); } } fn poll_async(&mut self) -> std::pin::Pin + '_>> { // Add any queued events for (id, event, value, bubbles) in self.queued_events.drain(..) { let platform_event = PlatformEventData::new(value); self.vdom .handle_event(event, Rc::new(platform_event), id, bubbles); } #[cfg(all(feature = "hot-reload", debug_assertions))] return Box::pin(async { let hot_reload_wait = self.hot_reload_rx.recv(); let mut hot_reload_msg = None; let wait_for_work = self.vdom.wait_for_work(); tokio::select! { Some(msg) = hot_reload_wait => { #[cfg(all(feature = "hot-reload", debug_assertions))] { hot_reload_msg = Some(msg); } #[cfg(not(all(feature = "hot-reload", debug_assertions)))] let () = msg; } _ = wait_for_work => {} } // if we have a new template, replace the old one if let Some(msg) = hot_reload_msg { match msg { dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => { self.vdom.replace_template(template); } dioxus_hot_reload::HotReloadMsg::Shutdown => { std::process::exit(0); } } } }); #[cfg(not(all(feature = "hot-reload", debug_assertions)))] Box::pin(self.vdom.wait_for_work()) } } #[derive(Clone)] pub struct DioxusElementToNodeId { mapping: Rc>, } impl DioxusElementToNodeId { pub fn get_node_id(&self, element_id: ElementId) -> Option { self.mapping .read() .unwrap() .try_element_to_node_id(element_id) } }