webview.rs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. use crate::desktop_context::{EditQueue, EventData};
  2. use crate::protocol::{self, AssetHandlerRegistry};
  3. use crate::{desktop_context::UserWindowEvent, Config};
  4. use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget};
  5. pub use wry;
  6. pub use wry::application as tao;
  7. use wry::application::window::Window;
  8. use wry::webview::{WebContext, WebView, WebViewBuilder};
  9. pub(crate) fn build(
  10. cfg: &mut Config,
  11. event_loop: &EventLoopWindowTarget<UserWindowEvent>,
  12. proxy: EventLoopProxy<UserWindowEvent>,
  13. ) -> (WebView, WebContext, AssetHandlerRegistry, EditQueue) {
  14. let builder = cfg.window.clone();
  15. let window = builder.with_visible(false).build(event_loop).unwrap();
  16. let file_handler = cfg.file_drop_handler.take();
  17. let custom_head = cfg.custom_head.clone();
  18. let index_file = cfg.custom_index.clone();
  19. let root_name = cfg.root_name.clone();
  20. let assets_head = {
  21. #[cfg(all(
  22. debug_assertions,
  23. any(
  24. target_os = "windows",
  25. target_os = "macos",
  26. target_os = "linux",
  27. target_os = "dragonfly",
  28. target_os = "freebsd",
  29. target_os = "netbsd",
  30. target_os = "openbsd"
  31. )
  32. ))]
  33. {
  34. None
  35. }
  36. #[cfg(not(all(
  37. debug_assertions,
  38. any(
  39. target_os = "windows",
  40. target_os = "macos",
  41. target_os = "linux",
  42. target_os = "dragonfly",
  43. target_os = "freebsd",
  44. target_os = "netbsd",
  45. target_os = "openbsd"
  46. )
  47. )))]
  48. {
  49. let head = crate::protocol::get_asset_root_or_default();
  50. let head = head.join("dist/__assets_head.html");
  51. match std::fs::read_to_string(&head) {
  52. Ok(s) => Some(s),
  53. Err(err) => {
  54. tracing::error!("Failed to read {head:?}: {err}");
  55. None
  56. }
  57. }
  58. }
  59. };
  60. // TODO: restore the menu bar with muda: https://github.com/tauri-apps/muda/blob/dev/examples/wry.rs
  61. // if cfg.enable_default_menu_bar {
  62. // builder = builder.with_menu(build_default_menu_bar());
  63. // }
  64. // We assume that if the icon is None in cfg, then the user just didnt set it
  65. if cfg.window.window.window_icon.is_none() {
  66. window.set_window_icon(Some(
  67. tao::window::Icon::from_rgba(
  68. include_bytes!("./assets/default_icon.bin").to_vec(),
  69. 460,
  70. 460,
  71. )
  72. .expect("image parse failed"),
  73. ));
  74. }
  75. let mut web_context = WebContext::new(cfg.data_dir.clone());
  76. let edit_queue = EditQueue::default();
  77. let headless = !cfg.window.window.visible;
  78. let asset_handlers = AssetHandlerRegistry::new();
  79. let asset_handlers_ref = asset_handlers.clone();
  80. let mut webview = WebViewBuilder::new(window)
  81. .unwrap()
  82. .with_transparent(cfg.window.window.transparent)
  83. .with_url("dioxus://index.html/")
  84. .unwrap()
  85. .with_ipc_handler(move |window: &Window, payload: String| {
  86. // defer the event to the main thread
  87. if let Ok(message) = serde_json::from_str(&payload) {
  88. _ = proxy.send_event(UserWindowEvent(EventData::Ipc(message), window.id()));
  89. }
  90. })
  91. .with_asynchronous_custom_protocol(String::from("dioxus"), {
  92. let edit_queue = edit_queue.clone();
  93. move |request, responder| {
  94. let custom_head = custom_head.clone();
  95. let index_file = index_file.clone();
  96. let assets_head = assets_head.clone();
  97. let root_name = root_name.clone();
  98. let asset_handlers_ref = asset_handlers_ref.clone();
  99. let edit_queue = edit_queue.clone();
  100. tokio::spawn(async move {
  101. protocol::desktop_handler(
  102. request,
  103. custom_head.clone(),
  104. index_file.clone(),
  105. assets_head.clone(),
  106. &root_name,
  107. &asset_handlers_ref,
  108. &edit_queue,
  109. headless,
  110. responder,
  111. )
  112. .await;
  113. });
  114. }
  115. })
  116. .with_file_drop_handler(move |window, evet| {
  117. file_handler
  118. .as_ref()
  119. .map(|handler| handler(window, evet))
  120. .unwrap_or_default()
  121. })
  122. .with_web_context(&mut web_context);
  123. #[cfg(windows)]
  124. {
  125. // Windows has a platform specific settings to disable the browser shortcut keys
  126. use wry::webview::WebViewBuilderExtWindows;
  127. webview = webview.with_browser_accelerator_keys(false);
  128. }
  129. if let Some(color) = cfg.background_color {
  130. webview = webview.with_background_color(color);
  131. }
  132. // These are commented out because wry is currently broken in wry
  133. // let mut web_context = WebContext::new(cfg.data_dir.clone());
  134. // .with_web_context(&mut web_context);
  135. for (name, handler) in cfg.protocols.drain(..) {
  136. webview = webview.with_custom_protocol(name, move |r| handler(r))
  137. }
  138. if cfg.disable_context_menu {
  139. // in release mode, we don't want to show the dev tool or reload menus
  140. webview = webview.with_initialization_script(
  141. r#"
  142. if (document.addEventListener) {
  143. document.addEventListener('contextmenu', function(e) {
  144. e.preventDefault();
  145. }, false);
  146. } else {
  147. document.attachEvent('oncontextmenu', function() {
  148. window.event.returnValue = false;
  149. });
  150. }
  151. "#,
  152. )
  153. } else {
  154. // in debug, we are okay with the reload menu showing and dev tool
  155. webview = webview.with_devtools(true);
  156. }
  157. (
  158. webview.build().unwrap(),
  159. web_context,
  160. asset_handlers,
  161. edit_queue,
  162. )
  163. }
  164. // /// Builds a standard menu bar depending on the users platform. It may be used as a starting point
  165. // /// to further customize the menu bar and pass it to a [`WindowBuilder`](tao::window::WindowBuilder).
  166. // /// > Note: The default menu bar enables macOS shortcuts like cut/copy/paste.
  167. // /// > The menu bar differs per platform because of constraints introduced
  168. // /// > by [`MenuItem`](tao::menu::MenuItem).
  169. // pub fn build_default_menu_bar() -> MenuBar {
  170. // let mut menu_bar = MenuBar::new();
  171. // // since it is uncommon on windows to have an "application menu"
  172. // // we add a "window" menu to be more consistent across platforms with the standard menu
  173. // let mut window_menu = MenuBar::new();
  174. // #[cfg(target_os = "macos")]
  175. // {
  176. // window_menu.add_native_item(MenuItem::EnterFullScreen);
  177. // window_menu.add_native_item(MenuItem::Zoom);
  178. // window_menu.add_native_item(MenuItem::Separator);
  179. // }
  180. // window_menu.add_native_item(MenuItem::Hide);
  181. // #[cfg(target_os = "macos")]
  182. // {
  183. // window_menu.add_native_item(MenuItem::HideOthers);
  184. // window_menu.add_native_item(MenuItem::ShowAll);
  185. // }
  186. // window_menu.add_native_item(MenuItem::Minimize);
  187. // window_menu.add_native_item(MenuItem::CloseWindow);
  188. // window_menu.add_native_item(MenuItem::Separator);
  189. // window_menu.add_native_item(MenuItem::Quit);
  190. // menu_bar.add_submenu("Window", true, window_menu);
  191. // // since tao supports none of the below items on linux we should only add them on macos/windows
  192. // #[cfg(not(target_os = "linux"))]
  193. // {
  194. // let mut edit_menu = MenuBar::new();
  195. // #[cfg(target_os = "macos")]
  196. // {
  197. // edit_menu.add_native_item(MenuItem::Undo);
  198. // edit_menu.add_native_item(MenuItem::Redo);
  199. // edit_menu.add_native_item(MenuItem::Separator);
  200. // }
  201. // edit_menu.add_native_item(MenuItem::Cut);
  202. // edit_menu.add_native_item(MenuItem::Copy);
  203. // edit_menu.add_native_item(MenuItem::Paste);
  204. // #[cfg(target_os = "macos")]
  205. // {
  206. // edit_menu.add_native_item(MenuItem::Separator);
  207. // edit_menu.add_native_item(MenuItem::SelectAll);
  208. // }
  209. // menu_bar.add_submenu("Edit", true, edit_menu);
  210. // }
  211. // menu_bar
  212. // }