1
0

lib.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. pub mod cfg;
  5. mod controller;
  6. pub mod desktop_context;
  7. pub mod escape;
  8. pub mod events;
  9. mod protocol;
  10. mod user_window_events;
  11. use cfg::DesktopConfig;
  12. use controller::DesktopController;
  13. pub use desktop_context::use_window;
  14. use dioxus_core::*;
  15. use events::parse_ipc_message;
  16. use tao::{
  17. event::{Event, StartCause, WindowEvent},
  18. event_loop::{ControlFlow, EventLoop},
  19. window::Window,
  20. };
  21. pub use wry;
  22. pub use wry::application as tao;
  23. use wry::webview::WebViewBuilder;
  24. use crate::events::trigger_from_serialized;
  25. /// Launch the WebView and run the event loop.
  26. ///
  27. /// This function will start a multithreaded Tokio runtime as well the WebView event loop.
  28. ///
  29. /// ```rust
  30. /// use dioxus::prelude::*;
  31. ///
  32. /// fn main() {
  33. /// dioxus::desktop::launch(app);
  34. /// }
  35. ///
  36. /// fn app(cx: Scope) -> Element {
  37. /// cx.render(rsx!{
  38. /// h1 {"hello world!"}
  39. /// })
  40. /// }
  41. /// ```
  42. pub fn launch(root: Component) {
  43. launch_with_props(root, (), |c| c)
  44. }
  45. /// Launch the WebView and run the event loop, with configuration.
  46. ///
  47. /// This function will start a multithreaded Tokio runtime as well the WebView event loop.
  48. ///
  49. /// You can configure the WebView window with a configuration closure
  50. ///
  51. /// ```rust
  52. /// use dioxus::prelude::*;
  53. ///
  54. /// fn main() {
  55. /// dioxus::desktop::launch_cfg(app, |c| c.with_window(|w| w.with_title("My App")));
  56. /// }
  57. ///
  58. /// fn app(cx: Scope) -> Element {
  59. /// cx.render(rsx!{
  60. /// h1 {"hello world!"}
  61. /// })
  62. /// }
  63. /// ```
  64. pub fn launch_cfg(
  65. root: Component,
  66. config_builder: impl FnOnce(&mut DesktopConfig) -> &mut DesktopConfig,
  67. ) {
  68. launch_with_props(root, (), config_builder)
  69. }
  70. /// Launch the WebView and run the event loop, with configuration and root props.
  71. ///
  72. /// This function will start a multithreaded Tokio runtime as well the WebView event loop.
  73. ///
  74. /// You can configure the WebView window with a configuration closure
  75. ///
  76. /// ```rust
  77. /// use dioxus::prelude::*;
  78. ///
  79. /// fn main() {
  80. /// dioxus::desktop::launch_cfg(app, AppProps { name: "asd" }, |c| c);
  81. /// }
  82. ///
  83. /// struct AppProps {
  84. /// name: &'static str
  85. /// }
  86. ///
  87. /// fn app(cx: Scope<AppProps>) -> Element {
  88. /// cx.render(rsx!{
  89. /// h1 {"hello {cx.props.name}!"}
  90. /// })
  91. /// }
  92. /// ```
  93. pub fn launch_with_props<P: 'static + Send>(
  94. root: Component<P>,
  95. props: P,
  96. builder: impl FnOnce(&mut DesktopConfig) -> &mut DesktopConfig,
  97. ) {
  98. let mut cfg = DesktopConfig::default().with_default_icon();
  99. builder(&mut cfg);
  100. let event_loop = EventLoop::with_user_event();
  101. let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
  102. let proxy = event_loop.create_proxy();
  103. event_loop.run(move |window_event, event_loop, control_flow| {
  104. *control_flow = ControlFlow::Wait;
  105. match window_event {
  106. Event::NewEvents(StartCause::Init) => {
  107. let builder = cfg.window.clone();
  108. let window = builder.build(event_loop).unwrap();
  109. let window_id = window.id();
  110. let (is_ready, sender) = (desktop.is_ready.clone(), desktop.sender.clone());
  111. let proxy = proxy.clone();
  112. let file_handler = cfg.file_drop_handler.take();
  113. let mut webview = WebViewBuilder::new(window)
  114. .unwrap()
  115. .with_transparent(cfg.window.window.transparent)
  116. .with_url("dioxus://index.html/")
  117. .unwrap()
  118. .with_ipc_handler(move |_window: &Window, payload: String| {
  119. parse_ipc_message(&payload)
  120. .map(|message| match message.method() {
  121. "user_event" => {
  122. let event = trigger_from_serialized(message.params());
  123. log::trace!("User event: {:?}", event);
  124. sender.unbounded_send(SchedulerMsg::Event(event)).unwrap();
  125. }
  126. "initialize" => {
  127. is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
  128. let _ = proxy
  129. .send_event(user_window_events::UserWindowEvent::Update);
  130. }
  131. "browser_open" => {
  132. println!("browser_open");
  133. let data = message.params();
  134. log::trace!("Open browser: {:?}", data);
  135. if let Some(temp) = data.as_object() {
  136. if temp.contains_key("href") {
  137. let url = temp.get("href").unwrap().as_str().unwrap();
  138. if let Err(e) = webbrowser::open(url) {
  139. log::error!("Open Browser error: {:?}", e);
  140. }
  141. }
  142. }
  143. }
  144. _ => (),
  145. })
  146. .unwrap_or_else(|| {
  147. log::warn!("invalid IPC message received");
  148. });
  149. })
  150. .with_custom_protocol(String::from("dioxus"), protocol::desktop_handler)
  151. .with_file_drop_handler(move |window, evet| {
  152. file_handler
  153. .as_ref()
  154. .map(|handler| handler(window, evet))
  155. .unwrap_or_default()
  156. });
  157. for (name, handler) in cfg.protocols.drain(..) {
  158. webview = webview.with_custom_protocol(name, handler)
  159. }
  160. if cfg!(debug_assertions) {
  161. // in debug, we are okay with the reload menu showing and dev tool
  162. webview = webview.with_dev_tool(true);
  163. } else {
  164. // in release mode, we don't want to show the dev tool or reload menus
  165. webview = webview.with_initialization_script(
  166. r#"
  167. if (document.addEventListener) {
  168. document.addEventListener('contextmenu', function(e) {
  169. alert("You've tried to open context menu");
  170. e.preventDefault();
  171. }, false);
  172. } else {
  173. document.attachEvent('oncontextmenu', function() {
  174. alert("You've tried to open context menu");
  175. window.event.returnValue = false;
  176. });
  177. }
  178. "#,
  179. )
  180. }
  181. desktop.webviews.insert(window_id, webview.build().unwrap());
  182. }
  183. Event::WindowEvent {
  184. event, window_id, ..
  185. } => match event {
  186. WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
  187. WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
  188. WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
  189. if let Some(view) = desktop.webviews.get_mut(&window_id) {
  190. let _ = view.resize();
  191. }
  192. }
  193. _ => {}
  194. },
  195. Event::UserEvent(user_event) => {
  196. user_window_events::handler(user_event, &mut desktop, control_flow)
  197. }
  198. Event::MainEventsCleared => {}
  199. Event::Resumed => {}
  200. Event::Suspended => {}
  201. Event::LoopDestroyed => {}
  202. Event::RedrawRequested(_id) => {}
  203. _ => {}
  204. }
  205. })
  206. }