webview.rs 6.9 KB

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