123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- use crate::{
- app::SharedContext,
- assets::AssetHandlerRegistry,
- edits::EditQueue,
- ipc::{EventData, UserWindowEvent},
- query::QueryEngine,
- shortcut::{HotKey, ShortcutId, ShortcutRegistryError},
- webview::WebviewInstance,
- AssetRequest, Config,
- };
- use dioxus_core::{
- prelude::{current_scope_id, ScopeId},
- use_hook, VirtualDom,
- };
- use dioxus_interpreter_js::MutationState;
- use slab::Slab;
- use std::{cell::RefCell, fmt::Debug, rc::Rc, rc::Weak};
- use tao::{
- event::Event,
- event_loop::EventLoopWindowTarget,
- window::{Fullscreen as WryFullscreen, Window, WindowId},
- };
- use wry::{RequestAsyncResponder, WebView};
- #[cfg(target_os = "ios")]
- use tao::platform::ios::WindowExtIOS;
- /// Get an imperative handle to the current window without using a hook
- ///
- /// ## Panics
- ///
- /// This function will panic if it is called outside of the context of a Dioxus App.
- pub fn window() -> DesktopContext {
- dioxus_core::prelude::consume_context()
- }
- /// A handle to the [`DesktopService`] that can be passed around.
- pub type DesktopContext = Rc<DesktopService>;
- /// An imperative interface to the current window.
- ///
- /// To get a handle to the current window, use the [`use_window`] hook.
- ///
- ///
- /// # Example
- ///
- /// you can use `cx.consume_context::<DesktopContext>` to get this context
- ///
- /// ```rust, ignore
- /// let desktop = cx.consume_context::<DesktopContext>().unwrap();
- /// ```
- pub struct DesktopService {
- /// The wry/tao proxy to the current window
- pub webview: WebView,
- /// The tao window itself
- pub window: Window,
- pub(crate) shared: Rc<SharedContext>,
- /// The receiver for queries about the current window
- pub(super) query: QueryEngine,
- pub(crate) edit_queue: EditQueue,
- pub(crate) mutation_state: RefCell<MutationState>,
- pub(crate) asset_handlers: AssetHandlerRegistry,
- #[cfg(target_os = "ios")]
- pub(crate) views: Rc<RefCell<Vec<*mut objc::runtime::Object>>>,
- }
- /// A smart pointer to the current window.
- impl std::ops::Deref for DesktopService {
- type Target = Window;
- fn deref(&self) -> &Self::Target {
- &self.window
- }
- }
- impl DesktopService {
- pub(crate) fn new(
- webview: WebView,
- window: Window,
- shared: Rc<SharedContext>,
- edit_queue: EditQueue,
- asset_handlers: AssetHandlerRegistry,
- ) -> Self {
- Self {
- window,
- webview,
- shared,
- edit_queue,
- mutation_state: Default::default(),
- asset_handlers,
- query: Default::default(),
- #[cfg(target_os = "ios")]
- views: Default::default(),
- }
- }
- /// Send a list of mutations to the webview
- pub(crate) fn send_edits(&self) {
- let mut mutations = self.mutation_state.borrow_mut();
- let serialized_edits = mutations.export_memory();
- self.edit_queue.add_edits(serialized_edits);
- }
- /// Create a new window using the props and window builder
- ///
- /// Returns the webview handle for the new window.
- ///
- /// You can use this to control other windows from the current window.
- ///
- /// Be careful to not create a cycle of windows, or you might leak memory.
- pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak<DesktopService> {
- let window = WebviewInstance::new(cfg, dom, self.shared.clone());
- let cx = window.dom.in_runtime(|| {
- ScopeId::ROOT
- .consume_context::<Rc<DesktopService>>()
- .unwrap()
- });
- self.shared
- .proxy
- .send_event(UserWindowEvent(EventData::NewWindow, cx.id()))
- .unwrap();
- self.shared
- .proxy
- .send_event(UserWindowEvent(EventData::Poll, cx.id()))
- .unwrap();
- self.shared.pending_webviews.borrow_mut().push(window);
- Rc::downgrade(&cx)
- }
- /// trigger the drag-window event
- ///
- /// Moves the window with the left mouse button until the button is released.
- ///
- /// you need use it in `onmousedown` event:
- /// ```rust, ignore
- /// onmousedown: move |_| { desktop.drag_window(); }
- /// ```
- pub fn drag(&self) {
- if self.window.fullscreen().is_none() {
- _ = self.window.drag_window();
- }
- }
- /// Toggle whether the window is maximized or not
- pub fn toggle_maximized(&self) {
- self.window.set_maximized(!self.window.is_maximized())
- }
- /// Close this window
- pub fn close(&self) {
- let _ = self
- .shared
- .proxy
- .send_event(UserWindowEvent(EventData::CloseWindow, self.id()));
- }
- /// Close a particular window, given its ID
- pub fn close_window(&self, id: WindowId) {
- let _ = self
- .shared
- .proxy
- .send_event(UserWindowEvent(EventData::CloseWindow, id));
- }
- /// change window to fullscreen
- pub fn set_fullscreen(&self, fullscreen: bool) {
- if let Some(handle) = &self.window.current_monitor() {
- self.window.set_fullscreen(
- fullscreen.then_some(WryFullscreen::Borderless(Some(handle.clone()))),
- );
- }
- }
- /// launch print modal
- pub fn print(&self) {
- if let Err(e) = self.webview.print() {
- tracing::warn!("Open print modal failed: {e}");
- }
- }
- /// Set the zoom level of the webview
- pub fn set_zoom_level(&self, level: f64) {
- self.webview.zoom(level);
- }
- /// opens DevTool window
- pub fn devtool(&self) {
- #[cfg(debug_assertions)]
- self.webview.open_devtools();
- #[cfg(not(debug_assertions))]
- tracing::warn!("Devtools are disabled in release builds");
- }
- /// Create a wry event handler that listens for wry events.
- /// This event handler is scoped to the currently active window and will only recieve events that are either global or related to the current window.
- ///
- /// The id this function returns can be used to remove the event handler with [`DesktopContext::remove_wry_event_handler`]
- pub fn create_wry_event_handler(
- &self,
- handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
- ) -> WryEventHandlerId {
- self.shared.event_handlers.add(self.window.id(), handler)
- }
- /// Remove a wry event handler created with [`DesktopContext::create_wry_event_handler`]
- pub fn remove_wry_event_handler(&self, id: WryEventHandlerId) {
- self.shared.event_handlers.remove(id)
- }
- /// Create a global shortcut
- ///
- /// Linux: Only works on x11. See [this issue](https://github.com/tauri-apps/tao/issues/331) for more information.
- pub fn create_shortcut(
- &self,
- hotkey: HotKey,
- callback: impl FnMut() + 'static,
- ) -> Result<ShortcutId, ShortcutRegistryError> {
- self.shared
- .shortcut_manager
- .add_shortcut(hotkey, Box::new(callback))
- }
- /// Remove a global shortcut
- pub fn remove_shortcut(&self, id: ShortcutId) {
- self.shared.shortcut_manager.remove_shortcut(id)
- }
- /// Remove all global shortcuts
- pub fn remove_all_shortcuts(&self) {
- self.shared.shortcut_manager.remove_all()
- }
- /// Provide a callback to handle asset loading yourself.
- /// If the ScopeId isn't provided, defaults to a global handler.
- /// Note that the handler is namespaced by name, not ScopeId.
- ///
- /// When the component is dropped, the handler is removed.
- ///
- /// See [`use_asset_handle`](crate::use_asset_handle) for a convenient hook.
- pub fn register_asset_handler(
- &self,
- name: String,
- f: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
- scope: Option<ScopeId>,
- ) {
- self.asset_handlers.register_handler(
- name,
- f,
- scope.unwrap_or(current_scope_id().unwrap_or(ScopeId(0))),
- )
- }
- /// Removes an asset handler by its identifier.
- ///
- /// Returns `None` if the handler did not exist.
- pub fn remove_asset_handler(&self, name: &str) -> Option<()> {
- self.asset_handlers.remove_handler(name).map(|_| ())
- }
- /// Push an objc view to the window
- #[cfg(target_os = "ios")]
- pub fn push_view(&self, view: objc_id::ShareId<objc::runtime::Object>) {
- let window = &self.window;
- unsafe {
- use objc::runtime::Object;
- use objc::*;
- assert!(is_main_thread());
- let ui_view = window.ui_view() as *mut Object;
- let ui_view_frame: *mut Object = msg_send![ui_view, frame];
- let _: () = msg_send![view, setFrame: ui_view_frame];
- let _: () = msg_send![view, setAutoresizingMask: 31];
- let ui_view_controller = window.ui_view_controller() as *mut Object;
- let _: () = msg_send![ui_view_controller, setView: view];
- self.views.borrow_mut().push(ui_view);
- }
- }
- /// Pop an objc view from the window
- #[cfg(target_os = "ios")]
- pub fn pop_view(&self) {
- let window = &self.window;
- unsafe {
- use objc::runtime::Object;
- use objc::*;
- assert!(is_main_thread());
- if let Some(view) = self.views.borrow_mut().pop() {
- let ui_view_controller = window.ui_view_controller() as *mut Object;
- let _: () = msg_send![ui_view_controller, setView: view];
- }
- }
- }
- }
- #[cfg(target_os = "ios")]
- fn is_main_thread() -> bool {
- use objc::runtime::{Class, BOOL, NO};
- use objc::*;
- let cls = Class::get("NSThread").unwrap();
- let result: BOOL = unsafe { msg_send![cls, isMainThread] };
- result != NO
- }
- /// The unique identifier of a window event handler. This can be used to later remove the handler.
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
- pub struct WryEventHandlerId(usize);
- #[derive(Clone, Default)]
- pub(crate) struct WindowEventHandlers {
- handlers: Rc<RefCell<Slab<WryWindowEventHandlerInner>>>,
- }
- impl WindowEventHandlers {
- pub(crate) fn add(
- &self,
- window_id: WindowId,
- handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
- ) -> WryEventHandlerId {
- WryEventHandlerId(
- self.handlers
- .borrow_mut()
- .insert(WryWindowEventHandlerInner {
- window_id,
- handler: Box::new(handler),
- }),
- )
- }
- pub(crate) fn remove(&self, id: WryEventHandlerId) {
- self.handlers.borrow_mut().try_remove(id.0);
- }
- pub(crate) fn apply_event(
- &self,
- event: &Event<UserWindowEvent>,
- target: &EventLoopWindowTarget<UserWindowEvent>,
- ) {
- for (_, handler) in self.handlers.borrow_mut().iter_mut() {
- handler.apply_event(event, target);
- }
- }
- }
- struct WryWindowEventHandlerInner {
- window_id: WindowId,
- handler: WryEventHandlerCallback,
- }
- type WryEventHandlerCallback =
- Box<dyn FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static>;
- impl WryWindowEventHandlerInner {
- fn apply_event(
- &mut self,
- event: &Event<UserWindowEvent>,
- target: &EventLoopWindowTarget<UserWindowEvent>,
- ) {
- // if this event does not apply to the window this listener cares about, return
- if let Event::WindowEvent { window_id, .. } = event {
- if *window_id != self.window_id {
- return;
- }
- }
- (self.handler)(event, target)
- }
- }
- /// Get a closure that executes any JavaScript in the WebView context.
- pub fn use_wry_event_handler(
- handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
- ) -> WryEventHandler {
- use_hook(move || {
- let desktop = window();
- let id = desktop.create_wry_event_handler(handler);
- WryEventHandler {
- handlers: desktop.shared.event_handlers.clone(),
- id,
- }
- })
- }
- /// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events.
- ///
- /// This will automatically be removed when the component is unmounted.
- #[derive(Clone)]
- pub struct WryEventHandler {
- pub(crate) handlers: WindowEventHandlers,
- /// The unique identifier of the event handler.
- pub id: WryEventHandlerId,
- }
- impl WryEventHandler {
- /// Remove the event handler.
- pub fn remove(&self) {
- self.handlers.remove(self.id);
- }
- }
- impl Drop for WryEventHandler {
- fn drop(&mut self) {
- self.handlers.remove(self.id);
- }
- }
|