dioxus_desktop/
launch.rs

1use crate::Config;
2use crate::{
3    app::App,
4    ipc::{IpcMethod, UserWindowEvent},
5};
6use dioxus_core::*;
7use dioxus_document::eval;
8use std::any::Any;
9use tao::event::{Event, StartCause, WindowEvent};
10
11/// Launch the WebView and run the event loop, with configuration and root props.
12///
13/// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
14/// and is equivalent to calling launch_with_props with the tokio feature disabled.
15pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config: Config) -> ! {
16    let mut custom_event_handler = desktop_config.custom_event_handler.take();
17    let (event_loop, mut app) = App::new(desktop_config, virtual_dom);
18
19    event_loop.run(move |window_event, event_loop, control_flow| {
20        // Set the control flow and check if any events need to be handled in the app itself
21        app.tick(&window_event);
22
23        if let Some(ref mut f) = custom_event_handler {
24            f(&window_event, event_loop)
25        }
26
27        match window_event {
28            Event::NewEvents(StartCause::Init) => app.handle_start_cause_init(),
29            Event::LoopDestroyed => app.handle_loop_destroyed(),
30            Event::WindowEvent {
31                event, window_id, ..
32            } => match event {
33                WindowEvent::CloseRequested => app.handle_close_requested(window_id),
34                WindowEvent::Destroyed { .. } => app.window_destroyed(window_id),
35                WindowEvent::Resized(new_size) => app.resize_window(window_id, new_size),
36                _ => {}
37            },
38
39            Event::UserEvent(event) => match event {
40                UserWindowEvent::Poll(id) => app.poll_vdom(id),
41                UserWindowEvent::NewWindow => app.handle_new_window(),
42                UserWindowEvent::CloseWindow(id) => app.handle_close_requested(id),
43                UserWindowEvent::Shutdown => app.control_flow = tao::event_loop::ControlFlow::Exit,
44
45                #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
46                UserWindowEvent::GlobalHotKeyEvent(evnt) => app.handle_global_hotkey(evnt),
47
48                #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
49                UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt),
50
51                #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
52                UserWindowEvent::TrayMenuEvent(evnt) => app.handle_tray_menu_event(evnt),
53
54                #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
55                UserWindowEvent::TrayIconEvent(evnt) => app.handle_tray_icon_event(evnt),
56
57                #[cfg(all(feature = "devtools", debug_assertions))]
58                UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
59
60                // Windows-only drag-n-drop fix events. We need to call the interpreter drag-n-drop code.
61                UserWindowEvent::WindowsDragDrop(id) => {
62                    if let Some(webview) = app.webviews.get(&id) {
63                        webview.dom.in_scope(ScopeId::ROOT, || {
64                            eval("window.interpreter.handleWindowsDragDrop();");
65                        });
66                    }
67                }
68                UserWindowEvent::WindowsDragLeave(id) => {
69                    if let Some(webview) = app.webviews.get(&id) {
70                        webview.dom.in_scope(ScopeId::ROOT, || {
71                            eval("window.interpreter.handleWindowsDragLeave();");
72                        });
73                    }
74                }
75                UserWindowEvent::WindowsDragOver(id, x_pos, y_pos) => {
76                    if let Some(webview) = app.webviews.get(&id) {
77                        webview.dom.in_scope(ScopeId::ROOT, || {
78                            let e = eval(
79                                r#"
80                                    const xPos = await dioxus.recv();
81                                    const yPos = await dioxus.recv();
82                                    window.interpreter.handleWindowsDragOver(xPos, yPos)
83                                    "#,
84                            );
85
86                            _ = e.send(x_pos);
87                            _ = e.send(y_pos);
88                        });
89                    }
90                }
91
92                UserWindowEvent::Ipc { id, msg } => match msg.method() {
93                    IpcMethod::Initialize => app.handle_initialize_msg(id),
94                    IpcMethod::UserEvent => {}
95                    IpcMethod::Query => app.handle_query_msg(msg, id),
96                    IpcMethod::BrowserOpen => app.handle_browser_open(msg),
97                    IpcMethod::Other(_) => {}
98                },
99            },
100            _ => {}
101        }
102
103        *control_flow = app.control_flow;
104    })
105}
106
107/// Launches the WebView and runs the event loop, with configuration and root props.
108pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
109    #[cfg(feature = "tokio_runtime")]
110    {
111        if let std::result::Result::Ok(handle) = tokio::runtime::Handle::try_current() {
112            assert_ne!(
113                handle.runtime_flavor(),
114                tokio::runtime::RuntimeFlavor::CurrentThread,
115                "The tokio current-thread runtime does not work with dioxus event handling"
116            );
117            launch_virtual_dom_blocking(virtual_dom, desktop_config);
118        } else {
119            tokio::runtime::Builder::new_multi_thread()
120                .enable_all()
121                .build()
122                .unwrap()
123                .block_on(tokio::task::unconstrained(async move {
124                    launch_virtual_dom_blocking(virtual_dom, desktop_config)
125                }));
126
127            unreachable!("The desktop launch function will never exit")
128        }
129    }
130
131    #[cfg(not(feature = "tokio_runtime"))]
132    {
133        launch_virtual_dom_blocking(virtual_dom, desktop_config);
134    }
135}
136
137/// Launches the WebView and runs the event loop, with configuration and root props.
138pub fn launch(
139    root: fn() -> Element,
140    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
141    platform_config: Vec<Box<dyn Any>>,
142) -> ! {
143    let mut virtual_dom = VirtualDom::new(root);
144
145    for context in contexts {
146        virtual_dom.insert_any_root_context(context());
147    }
148
149    let platform_config = *platform_config
150        .into_iter()
151        .find_map(|cfg| cfg.downcast::<Config>().ok())
152        .unwrap_or_default();
153
154    launch_virtual_dom(virtual_dom, platform_config)
155}