use blitz_shell::{BlitzApplication, View}; use dioxus_core::{provide_context, ScopeId}; use dioxus_history::{History, MemoryHistory}; use std::rc::Rc; use winit::application::ApplicationHandler; use winit::event::{StartCause, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; use winit::window::WindowId; use crate::DioxusNativeWindowRenderer; use crate::{contexts::DioxusNativeDocument, BlitzShellEvent, DioxusDocument, WindowConfig}; /// Dioxus-native specific event type pub enum DioxusNativeEvent { /// A hotreload event, basically telling us to update our templates. #[cfg(all( feature = "hot-reload", debug_assertions, not(target_os = "android"), not(target_os = "ios") ))] DevserverEvent(dioxus_devtools::DevserverMsg), /// Create a new head element from the Link and Title elements /// /// todo(jon): these should probabkly be synchronous somehow CreateHeadElement { window: WindowId, name: String, attributes: Vec<(String, String)>, contents: Option, }, } pub struct DioxusNativeApplication { pending_window: Option>, inner: BlitzApplication, proxy: EventLoopProxy, } impl DioxusNativeApplication { pub fn new( proxy: EventLoopProxy, config: WindowConfig, ) -> Self { Self { pending_window: Some(config), inner: BlitzApplication::new(proxy.clone()), proxy, } } pub fn add_window(&mut self, window_config: WindowConfig) { self.inner.add_window(window_config); } fn handle_blitz_shell_event( &mut self, event_loop: &ActiveEventLoop, event: &DioxusNativeEvent, ) { match event { #[cfg(all( feature = "hot-reload", debug_assertions, not(target_os = "android"), not(target_os = "ios") ))] DioxusNativeEvent::DevserverEvent(event) => match event { dioxus_devtools::DevserverMsg::HotReload(hotreload_message) => { for window in self.inner.windows.values_mut() { let doc = window.downcast_doc_mut::(); // Apply changes to vdom dioxus_devtools::apply_changes(&doc.vdom, hotreload_message); // Reload changed assets for asset_path in &hotreload_message.assets { if let Some(url) = asset_path.to_str() { doc.reload_resource_by_href(url); } } window.poll(); } } dioxus_devtools::DevserverMsg::Shutdown => event_loop.exit(), dioxus_devtools::DevserverMsg::FullReloadStart => {} dioxus_devtools::DevserverMsg::FullReloadFailed => {} dioxus_devtools::DevserverMsg::FullReloadCommand => {} _ => {} }, DioxusNativeEvent::CreateHeadElement { name, attributes, contents, window, } => { if let Some(window) = self.inner.windows.get_mut(window) { let doc = window.downcast_doc_mut::(); doc.create_head_element(name, attributes, contents); window.poll(); } } // Suppress unused variable warning #[cfg(not(all( feature = "hot-reload", debug_assertions, not(target_os = "android"), not(target_os = "ios") )))] #[allow(unreachable_patterns)] _ => { let _ = event_loop; let _ = event; } } } } impl ApplicationHandler for DioxusNativeApplication { fn resumed(&mut self, event_loop: &ActiveEventLoop) { #[cfg(feature = "tracing")] tracing::debug!("Injecting document provider into all windows"); if let Some(config) = self.pending_window.take() { let mut window = View::init(config, event_loop, &self.proxy); let renderer = window.renderer.clone(); let window_id = window.window_id(); let doc = window.downcast_doc_mut::(); doc.vdom.in_scope(ScopeId::ROOT, || { let shared: Rc = Rc::new(DioxusNativeDocument::new(self.proxy.clone(), window_id)); provide_context(shared); }); // Add history let history_provider: Rc = Rc::new(MemoryHistory::default()); doc.vdom .in_scope(ScopeId::ROOT, move || provide_context(history_provider)); // Add renderer doc.vdom .in_scope(ScopeId::ROOT, move || provide_context(renderer)); // Queue rebuild doc.initial_build(); // And then request redraw window.request_redraw(); // todo(jon): we should actually mess with the pending windows instead of passing along the contexts self.inner.windows.insert(window_id, window); } self.inner.resumed(event_loop); } fn suspended(&mut self, event_loop: &ActiveEventLoop) { self.inner.suspended(event_loop); } fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { self.inner.new_events(event_loop, cause); } fn window_event( &mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent, ) { self.inner.window_event(event_loop, window_id, event); } fn user_event(&mut self, event_loop: &ActiveEventLoop, event: BlitzShellEvent) { match event { BlitzShellEvent::Embedder(event) => { if let Some(event) = event.downcast_ref::() { self.handle_blitz_shell_event(event_loop, event); } } event => self.inner.user_event(event_loop, event), } } }