lib.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #![doc = include_str!("../README.md")]
  2. #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
  3. #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
  4. mod element;
  5. mod events;
  6. use std::{
  7. any::Any,
  8. ops::Deref,
  9. rc::Rc,
  10. sync::{Arc, RwLock},
  11. };
  12. use dioxus_core::{Element, ElementId, ScopeId, VirtualDom};
  13. use dioxus_html::PlatformEventData;
  14. use dioxus_native_core::dioxus::{DioxusState, NodeImmutableDioxusExt};
  15. use dioxus_native_core::prelude::*;
  16. use element::DioxusTUIMutationWriter;
  17. pub use plasmo::{query::Query, Config, RenderingMode, Size, TuiContext};
  18. use plasmo::{render, Driver};
  19. pub mod launch {
  20. use super::*;
  21. pub type Config = super::Config;
  22. /// Launches the WebView and runs the event loop, with configuration and root props.
  23. pub fn launch(
  24. root: fn() -> Element,
  25. contexts: Vec<Box<dyn Fn() -> Box<dyn Any>>>,
  26. platform_config: Config,
  27. ) {
  28. let mut virtual_dom = VirtualDom::new(root);
  29. for context in contexts {
  30. virtual_dom.insert_any_root_context(context());
  31. }
  32. launch_vdom_cfg(virtual_dom, platform_config)
  33. }
  34. }
  35. pub fn launch(app: fn() -> Element) {
  36. launch_cfg(app, Config::default())
  37. }
  38. pub fn launch_cfg(app: fn() -> Element, cfg: Config) {
  39. launch_vdom_cfg(VirtualDom::new(app), cfg)
  40. }
  41. pub fn launch_cfg_with_props<P: Clone + 'static>(app: fn(P) -> Element, props: P, cfg: Config) {
  42. launch_vdom_cfg(VirtualDom::new_with_props(app, props), cfg)
  43. }
  44. pub fn launch_vdom_cfg(vdom: VirtualDom, cfg: Config) {
  45. dioxus_html::set_event_converter(Box::new(events::SerializedHtmlEventConverter));
  46. render(cfg, |rdom, taffy, event_tx| {
  47. let dioxus_state = {
  48. let mut rdom = rdom.write().unwrap();
  49. DioxusState::create(&mut rdom)
  50. };
  51. let dioxus_state = Rc::new(RwLock::new(dioxus_state));
  52. let vdom = vdom
  53. .with_root_context(TuiContext::new(event_tx))
  54. .with_root_context(Query::new(rdom.clone(), taffy.clone()))
  55. .with_root_context(DioxusElementToNodeId {
  56. mapping: dioxus_state.clone(),
  57. });
  58. let queued_events = Vec::new();
  59. let mut myself = DioxusRenderer {
  60. vdom,
  61. dioxus_state,
  62. queued_events,
  63. #[cfg(all(feature = "hot-reload", debug_assertions))]
  64. hot_reload_rx: {
  65. let (hot_reload_tx, hot_reload_rx) =
  66. tokio::sync::mpsc::unbounded_channel::<dioxus_hot_reload::HotReloadMsg>();
  67. dioxus_hot_reload::connect(move |msg| {
  68. let _ = hot_reload_tx.send(msg);
  69. });
  70. hot_reload_rx
  71. },
  72. };
  73. {
  74. let mut rdom = rdom.write().unwrap();
  75. let mut dioxus_state = myself.dioxus_state.write().unwrap();
  76. let mut writer = DioxusTUIMutationWriter {
  77. query: myself
  78. .vdom
  79. .in_runtime(|| ScopeId::ROOT.consume_context().unwrap()),
  80. events: &mut myself.queued_events,
  81. native_core_writer: dioxus_state.create_mutation_writer(&mut rdom),
  82. };
  83. // Find any mount events
  84. myself.vdom.rebuild(&mut writer);
  85. }
  86. myself
  87. })
  88. .unwrap();
  89. }
  90. struct DioxusRenderer {
  91. vdom: VirtualDom,
  92. dioxus_state: Rc<RwLock<DioxusState>>,
  93. // Events that are queued up to be sent to the vdom next time the vdom is polled
  94. queued_events: Vec<(ElementId, &'static str, Box<dyn Any>, bool)>,
  95. #[cfg(all(feature = "hot-reload", debug_assertions))]
  96. hot_reload_rx: tokio::sync::mpsc::UnboundedReceiver<dioxus_hot_reload::HotReloadMsg>,
  97. }
  98. impl Driver for DioxusRenderer {
  99. fn update(&mut self, rdom: &Arc<RwLock<RealDom>>) {
  100. let mut rdom = rdom.write().unwrap();
  101. let mut dioxus_state = self.dioxus_state.write().unwrap();
  102. let mut writer = DioxusTUIMutationWriter {
  103. query: self
  104. .vdom
  105. .in_runtime(|| ScopeId::ROOT.consume_context().unwrap()),
  106. events: &mut self.queued_events,
  107. native_core_writer: dioxus_state.create_mutation_writer(&mut rdom),
  108. };
  109. // Find any mount events
  110. self.vdom.render_immediate(&mut writer);
  111. }
  112. fn handle_event(
  113. &mut self,
  114. rdom: &Arc<RwLock<RealDom>>,
  115. id: NodeId,
  116. event: &str,
  117. value: Rc<plasmo::EventData>,
  118. bubbles: bool,
  119. ) {
  120. let id = { rdom.read().unwrap().get(id).unwrap().mounted_id() };
  121. if let Some(id) = id {
  122. let inner_value = value.deref().clone();
  123. let boxed_event = Box::new(inner_value);
  124. let platform_event = PlatformEventData::new(boxed_event);
  125. self.vdom
  126. .handle_event(event, Rc::new(platform_event), id, bubbles);
  127. }
  128. }
  129. fn poll_async(&mut self) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + '_>> {
  130. // Add any queued events
  131. for (id, event, value, bubbles) in self.queued_events.drain(..) {
  132. let platform_event = PlatformEventData::new(value);
  133. self.vdom
  134. .handle_event(event, Rc::new(platform_event), id, bubbles);
  135. }
  136. #[cfg(all(feature = "hot-reload", debug_assertions))]
  137. return Box::pin(async {
  138. let hot_reload_wait = self.hot_reload_rx.recv();
  139. let mut hot_reload_msg = None;
  140. let wait_for_work = self.vdom.wait_for_work();
  141. tokio::select! {
  142. Some(msg) = hot_reload_wait => {
  143. #[cfg(all(feature = "hot-reload", debug_assertions))]
  144. {
  145. hot_reload_msg = Some(msg);
  146. }
  147. #[cfg(not(all(feature = "hot-reload", debug_assertions)))]
  148. let () = msg;
  149. }
  150. _ = wait_for_work => {}
  151. }
  152. // if we have a new template, replace the old one
  153. if let Some(msg) = hot_reload_msg {
  154. match msg {
  155. dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => {
  156. self.vdom.replace_template(template);
  157. }
  158. dioxus_hot_reload::HotReloadMsg::Shutdown => {
  159. std::process::exit(0);
  160. }
  161. }
  162. }
  163. });
  164. #[cfg(not(all(feature = "hot-reload", debug_assertions)))]
  165. Box::pin(self.vdom.wait_for_work())
  166. }
  167. }
  168. #[derive(Clone)]
  169. pub struct DioxusElementToNodeId {
  170. mapping: Rc<RwLock<DioxusState>>,
  171. }
  172. impl DioxusElementToNodeId {
  173. pub fn get_node_id(&self, element_id: ElementId) -> Option<NodeId> {
  174. self.mapping
  175. .read()
  176. .unwrap()
  177. .try_element_to_node_id(element_id)
  178. }
  179. }