dioxus_native/
dioxus_application.rs1use anyrender_vello::VelloWindowRenderer;
2use blitz_shell::BlitzApplication;
3use dioxus_core::{ScopeId, VirtualDom};
4use dioxus_history::{History, MemoryHistory};
5use std::{collections::HashSet, rc::Rc};
6use winit::application::ApplicationHandler;
7use winit::event::{StartCause, WindowEvent};
8use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
9use winit::window::WindowId;
10
11use crate::{
12 assets::DioxusNativeNetProvider, contexts::DioxusNativeDocument,
13 mutation_writer::MutationWriter, BlitzShellEvent, DioxusDocument, WindowConfig,
14};
15
16pub enum DioxusNativeEvent {
18 #[cfg(all(
20 feature = "hot-reload",
21 debug_assertions,
22 not(target_os = "android"),
23 not(target_os = "ios")
24 ))]
25 DevserverEvent(dioxus_devtools::DevserverMsg),
26
27 CreateHeadElement {
31 window: WindowId,
32 name: String,
33 attributes: Vec<(String, String)>,
34 contents: Option<String>,
35 },
36}
37
38pub struct DioxusNativeApplication {
39 pending_vdom: Option<VirtualDom>,
40 inner: BlitzApplication<VelloWindowRenderer>,
41 proxy: EventLoopProxy<BlitzShellEvent>,
42}
43
44impl DioxusNativeApplication {
45 pub fn new(proxy: EventLoopProxy<BlitzShellEvent>, vdom: VirtualDom) -> Self {
46 Self {
47 pending_vdom: Some(vdom),
48 inner: BlitzApplication::new(proxy.clone()),
49 proxy,
50 }
51 }
52
53 pub fn add_window(&mut self, window_config: WindowConfig<VelloWindowRenderer>) {
54 self.inner.add_window(window_config);
55 }
56
57 fn handle_blitz_shell_event(
58 &mut self,
59 event_loop: &ActiveEventLoop,
60 event: &DioxusNativeEvent,
61 ) {
62 match event {
63 #[cfg(all(
64 feature = "hot-reload",
65 debug_assertions,
66 not(target_os = "android"),
67 not(target_os = "ios")
68 ))]
69 DioxusNativeEvent::DevserverEvent(event) => match event {
70 dioxus_devtools::DevserverMsg::HotReload(hotreload_message) => {
71 for window in self.inner.windows.values_mut() {
72 let doc = window.downcast_doc_mut::<DioxusDocument>();
73 dioxus_devtools::apply_changes(&doc.vdom, hotreload_message);
74 window.poll();
75 }
76 }
77 dioxus_devtools::DevserverMsg::Shutdown => event_loop.exit(),
78 dioxus_devtools::DevserverMsg::FullReloadStart => {}
79 dioxus_devtools::DevserverMsg::FullReloadFailed => {}
80 dioxus_devtools::DevserverMsg::FullReloadCommand => {}
81 _ => {}
82 },
83
84 DioxusNativeEvent::CreateHeadElement {
85 name,
86 attributes,
87 contents,
88 window,
89 } => {
90 if let Some(window) = self.inner.windows.get_mut(window) {
91 let doc = window.downcast_doc_mut::<DioxusDocument>();
92 doc.create_head_element(name, attributes, contents);
93 window.poll();
94 }
95 }
96
97 #[cfg(not(all(
99 feature = "hot-reload",
100 debug_assertions,
101 not(target_os = "android"),
102 not(target_os = "ios")
103 )))]
104 _ => {
105 let _ = event_loop;
106 let _ = event;
107 }
108 }
109 }
110}
111
112impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
113 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
114 tracing::debug!("Injecting document provider into all windows");
115 let vdom = self.pending_vdom.take().unwrap();
116
117 #[cfg(feature = "net")]
118 let net_provider = {
119 let proxy = self.proxy.clone();
120 let net_provider = DioxusNativeNetProvider::shared(proxy);
121 Some(net_provider)
122 };
123
124 #[cfg(not(feature = "net"))]
125 let net_provider = None;
126
127 let doc = DioxusDocument::new(vdom, net_provider);
129 let window = WindowConfig::new(Box::new(doc) as _);
130
131 let old_windows = self.inner.windows.keys().copied().collect::<HashSet<_>>();
133 self.add_window(window);
134 self.inner.resumed(event_loop);
135 let new_windows = self.inner.windows.keys().cloned().collect::<HashSet<_>>();
136
137 for window_id in new_windows.difference(&old_windows) {
139 let window = self.inner.windows.get_mut(window_id).unwrap();
140 let doc = window.downcast_doc_mut::<DioxusDocument>();
141 doc.vdom.in_runtime(|| {
142 let shared: Rc<dyn dioxus_document::Document> =
143 Rc::new(DioxusNativeDocument::new(self.proxy.clone(), *window_id));
144 ScopeId::ROOT.provide_context(shared);
145 });
146
147 let history_provider: Rc<dyn History> = Rc::new(MemoryHistory::default());
149 doc.vdom
150 .in_runtime(|| ScopeId::ROOT.provide_context(history_provider));
151
152 let mut writer = MutationWriter::new(&mut doc.inner, &mut doc.vdom_state);
154 doc.vdom.rebuild(&mut writer);
155 drop(writer);
156
157 window.request_redraw();
159 }
160 }
161
162 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
163 self.inner.suspended(event_loop);
164 }
165
166 fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
167 self.inner.new_events(event_loop, cause);
168 }
169
170 fn window_event(
171 &mut self,
172 event_loop: &ActiveEventLoop,
173 window_id: WindowId,
174 event: WindowEvent,
175 ) {
176 self.inner.window_event(event_loop, window_id, event);
177 }
178
179 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: BlitzShellEvent) {
180 match event {
181 BlitzShellEvent::Embedder(event) => {
182 if let Some(event) = event.downcast_ref::<DioxusNativeEvent>() {
183 self.handle_blitz_shell_event(event_loop, event);
184 }
185 }
186 event => self.inner.user_event(event_loop, event),
187 }
188 }
189}