dioxus_native/
dioxus_application.rs

1use blitz_shell::{BlitzApplication, View};
2use dioxus_core::ScopeId;
3use dioxus_history::{History, MemoryHistory};
4use std::rc::Rc;
5use winit::application::ApplicationHandler;
6use winit::event::{StartCause, WindowEvent};
7use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
8use winit::window::WindowId;
9
10use crate::DioxusNativeWindowRenderer;
11use crate::{
12    contexts::DioxusNativeDocument, mutation_writer::MutationWriter, BlitzShellEvent,
13    DioxusDocument, WindowConfig,
14};
15
16/// Dioxus-native specific event type
17pub enum DioxusNativeEvent {
18    /// A hotreload event, basically telling us to update our templates.
19    #[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    /// Create a new head element from the Link and Title elements
28    ///
29    /// todo(jon): these should probabkly be synchronous somehow
30    CreateHeadElement {
31        window: WindowId,
32        name: String,
33        attributes: Vec<(String, String)>,
34        contents: Option<String>,
35    },
36}
37
38pub struct DioxusNativeApplication {
39    pending_window: Option<WindowConfig<DioxusNativeWindowRenderer>>,
40    inner: BlitzApplication<DioxusNativeWindowRenderer>,
41    proxy: EventLoopProxy<BlitzShellEvent>,
42}
43
44impl DioxusNativeApplication {
45    pub fn new(
46        proxy: EventLoopProxy<BlitzShellEvent>,
47        config: WindowConfig<DioxusNativeWindowRenderer>,
48    ) -> Self {
49        Self {
50            pending_window: Some(config),
51            inner: BlitzApplication::new(proxy.clone()),
52            proxy,
53        }
54    }
55
56    pub fn add_window(&mut self, window_config: WindowConfig<DioxusNativeWindowRenderer>) {
57        self.inner.add_window(window_config);
58    }
59
60    fn handle_blitz_shell_event(
61        &mut self,
62        event_loop: &ActiveEventLoop,
63        event: &DioxusNativeEvent,
64    ) {
65        match event {
66            #[cfg(all(
67                feature = "hot-reload",
68                debug_assertions,
69                not(target_os = "android"),
70                not(target_os = "ios")
71            ))]
72            DioxusNativeEvent::DevserverEvent(event) => match event {
73                dioxus_devtools::DevserverMsg::HotReload(hotreload_message) => {
74                    for window in self.inner.windows.values_mut() {
75                        let doc = window.downcast_doc_mut::<DioxusDocument>();
76                        dioxus_devtools::apply_changes(&doc.vdom, hotreload_message);
77                        window.poll();
78                    }
79                }
80                dioxus_devtools::DevserverMsg::Shutdown => event_loop.exit(),
81                dioxus_devtools::DevserverMsg::FullReloadStart => {}
82                dioxus_devtools::DevserverMsg::FullReloadFailed => {}
83                dioxus_devtools::DevserverMsg::FullReloadCommand => {}
84                _ => {}
85            },
86
87            DioxusNativeEvent::CreateHeadElement {
88                name,
89                attributes,
90                contents,
91                window,
92            } => {
93                if let Some(window) = self.inner.windows.get_mut(window) {
94                    let doc = window.downcast_doc_mut::<DioxusDocument>();
95                    doc.create_head_element(name, attributes, contents);
96                    window.poll();
97                }
98            }
99
100            // Suppress unused variable warning
101            #[cfg(not(all(
102                feature = "hot-reload",
103                debug_assertions,
104                not(target_os = "android"),
105                not(target_os = "ios")
106            )))]
107            _ => {
108                let _ = event_loop;
109                let _ = event;
110            }
111        }
112    }
113}
114
115impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
116    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
117        tracing::debug!("Injecting document provider into all windows");
118
119        if let Some(config) = self.pending_window.take() {
120            let mut window = View::init(config, event_loop, &self.proxy);
121            let renderer = window.renderer.clone();
122            let window_id = window.window_id();
123            let doc = window.downcast_doc_mut::<DioxusDocument>();
124
125            doc.vdom.in_runtime(|| {
126                let shared: Rc<dyn dioxus_document::Document> =
127                    Rc::new(DioxusNativeDocument::new(self.proxy.clone(), window_id));
128                ScopeId::ROOT.provide_context(shared);
129            });
130
131            // Add history
132            let history_provider: Rc<dyn History> = Rc::new(MemoryHistory::default());
133            doc.vdom
134                .in_runtime(move || ScopeId::ROOT.provide_context(history_provider));
135
136            // Add renderer
137            doc.vdom
138                .in_runtime(move || ScopeId::ROOT.provide_context(renderer));
139
140            // Queue rebuild
141            let mut writer = MutationWriter::new(&mut doc.inner, &mut doc.vdom_state);
142            doc.vdom.rebuild(&mut writer);
143            drop(writer);
144
145            // And then request redraw
146            window.request_redraw();
147
148            // todo(jon): we should actually mess with the pending windows instead of passing along the contexts
149            self.inner.windows.insert(window_id, window);
150        }
151
152        self.inner.resumed(event_loop);
153    }
154
155    fn suspended(&mut self, event_loop: &ActiveEventLoop) {
156        self.inner.suspended(event_loop);
157    }
158
159    fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
160        self.inner.new_events(event_loop, cause);
161    }
162
163    fn window_event(
164        &mut self,
165        event_loop: &ActiveEventLoop,
166        window_id: WindowId,
167        event: WindowEvent,
168    ) {
169        self.inner.window_event(event_loop, window_id, event);
170    }
171
172    fn user_event(&mut self, event_loop: &ActiveEventLoop, event: BlitzShellEvent) {
173        match event {
174            BlitzShellEvent::Embedder(event) => {
175                if let Some(event) = event.downcast_ref::<DioxusNativeEvent>() {
176                    self.handle_blitz_shell_event(event_loop, event);
177                }
178            }
179            event => self.inner.user_event(event_loop, event),
180        }
181    }
182}