desktop_context.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. use std::cell::RefCell;
  2. use std::rc::Rc;
  3. use std::rc::Weak;
  4. use crate::create_new_window;
  5. use crate::eval::EvalResult;
  6. use crate::events::IpcMessage;
  7. use crate::Config;
  8. use crate::WebviewHandler;
  9. use dioxus_core::ScopeState;
  10. use dioxus_core::VirtualDom;
  11. use serde_json::Value;
  12. use wry::application::event_loop::EventLoopProxy;
  13. use wry::application::event_loop::EventLoopWindowTarget;
  14. #[cfg(target_os = "ios")]
  15. use wry::application::platform::ios::WindowExtIOS;
  16. use wry::application::window::Fullscreen as WryFullscreen;
  17. use wry::application::window::Window;
  18. use wry::application::window::WindowId;
  19. use wry::webview::WebView;
  20. pub type ProxyType = EventLoopProxy<UserWindowEvent>;
  21. /// Get an imperative handle to the current window
  22. pub fn use_window(cx: &ScopeState) -> &DesktopContext {
  23. cx.use_hook(|| cx.consume_context::<DesktopContext>())
  24. .as_ref()
  25. .unwrap()
  26. }
  27. pub(crate) type WebviewQueue = Rc<RefCell<Vec<WebviewHandler>>>;
  28. /// An imperative interface to the current window.
  29. ///
  30. /// To get a handle to the current window, use the [`use_window`] hook.
  31. ///
  32. ///
  33. /// # Example
  34. ///
  35. /// you can use `cx.consume_context::<DesktopContext>` to get this context
  36. ///
  37. /// ```rust, ignore
  38. /// let desktop = cx.consume_context::<DesktopContext>().unwrap();
  39. /// ```
  40. #[derive(Clone)]
  41. pub struct DesktopContext {
  42. /// The wry/tao proxy to the current window
  43. pub webview: Rc<WebView>,
  44. /// The proxy to the event loop
  45. pub proxy: ProxyType,
  46. /// The receiver for eval results since eval is async
  47. pub(super) eval: tokio::sync::broadcast::Sender<Value>,
  48. pub(super) pending_windows: WebviewQueue,
  49. pub(crate) event_loop: EventLoopWindowTarget<UserWindowEvent>,
  50. #[cfg(target_os = "ios")]
  51. pub(crate) views: Rc<RefCell<Vec<*mut objc::runtime::Object>>>,
  52. }
  53. /// A smart pointer to the current window.
  54. impl std::ops::Deref for DesktopContext {
  55. type Target = Window;
  56. fn deref(&self) -> &Self::Target {
  57. self.webview.window()
  58. }
  59. }
  60. impl DesktopContext {
  61. pub(crate) fn new(
  62. webview: Rc<WebView>,
  63. proxy: ProxyType,
  64. event_loop: EventLoopWindowTarget<UserWindowEvent>,
  65. webviews: WebviewQueue,
  66. ) -> Self {
  67. Self {
  68. webview,
  69. proxy,
  70. event_loop,
  71. eval: tokio::sync::broadcast::channel(8).0,
  72. pending_windows: webviews,
  73. #[cfg(target_os = "ios")]
  74. views: Default::default(),
  75. }
  76. }
  77. /// Create a new window using the props and window builder
  78. ///
  79. /// Returns the webview handle for the new window.
  80. ///
  81. /// You can use this to control other windows from the current window.
  82. ///
  83. /// Be careful to not create a cycle of windows, or you might leak memory.
  84. pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak<WebView> {
  85. let window = create_new_window(
  86. cfg,
  87. &self.event_loop,
  88. &self.proxy,
  89. dom,
  90. &self.pending_windows,
  91. );
  92. let id = window.webview.window().id();
  93. self.proxy
  94. .send_event(UserWindowEvent(EventData::NewWindow, id))
  95. .unwrap();
  96. self.proxy
  97. .send_event(UserWindowEvent(EventData::Poll, id))
  98. .unwrap();
  99. let webview = window.webview.clone();
  100. self.pending_windows.borrow_mut().push(window);
  101. Rc::downgrade(&webview)
  102. }
  103. /// trigger the drag-window event
  104. ///
  105. /// Moves the window with the left mouse button until the button is released.
  106. ///
  107. /// you need use it in `onmousedown` event:
  108. /// ```rust, ignore
  109. /// onmousedown: move |_| { desktop.drag_window(); }
  110. /// ```
  111. pub fn drag(&self) {
  112. let window = self.webview.window();
  113. // if the drag_window has any errors, we don't do anything
  114. if window.fullscreen().is_none() {
  115. window.drag_window().unwrap();
  116. }
  117. }
  118. /// Toggle whether the window is maximized or not
  119. pub fn toggle_maximized(&self) {
  120. let window = self.webview.window();
  121. window.set_maximized(!window.is_maximized())
  122. }
  123. /// close window
  124. pub fn close(&self) {
  125. let _ = self
  126. .proxy
  127. .send_event(UserWindowEvent(EventData::CloseWindow, self.id()));
  128. }
  129. /// close window
  130. pub fn close_window(&self, id: WindowId) {
  131. let _ = self
  132. .proxy
  133. .send_event(UserWindowEvent(EventData::CloseWindow, id));
  134. }
  135. /// change window to fullscreen
  136. pub fn set_fullscreen(&self, fullscreen: bool) {
  137. if let Some(handle) = self.webview.window().current_monitor() {
  138. self.webview
  139. .window()
  140. .set_fullscreen(fullscreen.then_some(WryFullscreen::Borderless(Some(handle))));
  141. }
  142. }
  143. /// launch print modal
  144. pub fn print(&self) {
  145. if let Err(e) = self.webview.print() {
  146. log::warn!("Open print modal failed: {e}");
  147. }
  148. }
  149. /// Set the zoom level of the webview
  150. pub fn set_zoom_level(&self, level: f64) {
  151. self.webview.zoom(level);
  152. }
  153. /// opens DevTool window
  154. pub fn devtool(&self) {
  155. #[cfg(debug_assertions)]
  156. self.webview.open_devtools();
  157. #[cfg(not(debug_assertions))]
  158. log::warn!("Devtools are disabled in release builds");
  159. }
  160. /// Evaluate a javascript expression
  161. pub fn eval(&self, code: &str) -> EvalResult {
  162. // Embed the return of the eval in a function so we can send it back to the main thread
  163. let script = format!(
  164. r#"
  165. window.ipc.postMessage(
  166. JSON.stringify({{
  167. "method":"eval_result",
  168. "params": (
  169. function(){{
  170. {}
  171. }}
  172. )()
  173. }})
  174. );
  175. "#,
  176. code
  177. );
  178. if let Err(e) = self.webview.evaluate_script(&script) {
  179. // send an error to the eval receiver
  180. log::warn!("Eval script error: {e}");
  181. }
  182. EvalResult::new(self.eval.clone())
  183. }
  184. /// Push an objc view to the window
  185. #[cfg(target_os = "ios")]
  186. pub fn push_view(&self, view: objc_id::ShareId<objc::runtime::Object>) {
  187. let window = self.webview.window();
  188. unsafe {
  189. use objc::runtime::Object;
  190. use objc::*;
  191. assert!(is_main_thread());
  192. let ui_view = window.ui_view() as *mut Object;
  193. let ui_view_frame: *mut Object = msg_send![ui_view, frame];
  194. let _: () = msg_send![view, setFrame: ui_view_frame];
  195. let _: () = msg_send![view, setAutoresizingMask: 31];
  196. let ui_view_controller = window.ui_view_controller() as *mut Object;
  197. let _: () = msg_send![ui_view_controller, setView: view];
  198. self.views.borrow_mut().push(ui_view);
  199. }
  200. }
  201. /// Pop an objc view from the window
  202. #[cfg(target_os = "ios")]
  203. pub fn pop_view(&self) {
  204. let window = self.webview.window();
  205. unsafe {
  206. use objc::runtime::Object;
  207. use objc::*;
  208. assert!(is_main_thread());
  209. if let Some(view) = self.views.borrow_mut().pop() {
  210. let ui_view_controller = window.ui_view_controller() as *mut Object;
  211. let _: () = msg_send![ui_view_controller, setView: view];
  212. }
  213. }
  214. }
  215. }
  216. #[derive(Debug, Clone)]
  217. pub struct UserWindowEvent(pub EventData, pub WindowId);
  218. #[derive(Debug, Clone)]
  219. pub enum EventData {
  220. Poll,
  221. Ipc(IpcMessage),
  222. NewWindow,
  223. CloseWindow,
  224. }
  225. #[cfg(target_os = "ios")]
  226. fn is_main_thread() -> bool {
  227. use objc::runtime::{Class, BOOL, NO};
  228. use objc::*;
  229. let cls = Class::get("NSThread").unwrap();
  230. let result: BOOL = unsafe { msg_send![cls, isMainThread] };
  231. result != NO
  232. }