lib.rs 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343
  1. // Copyright 2019-2022 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! The [`wry`] Tauri [`Runtime`].
  5. use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle};
  6. use tauri_runtime::{
  7. http::{header::CONTENT_TYPE, Request as HttpRequest, RequestParts, Response as HttpResponse},
  8. menu::{AboutMetadata, CustomMenuItem, Menu, MenuEntry, MenuHash, MenuId, MenuItem, MenuUpdate},
  9. monitor::Monitor,
  10. webview::{WebviewIpcHandler, WindowBuilder, WindowBuilderBase},
  11. window::{
  12. dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
  13. CursorIcon, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent,
  14. },
  15. DeviceEventFilter, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result,
  16. RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType, UserEvent,
  17. };
  18. use tauri_runtime::window::MenuEvent;
  19. #[cfg(all(desktop, feature = "system-tray"))]
  20. use tauri_runtime::{SystemTray, SystemTrayEvent};
  21. #[cfg(windows)]
  22. use webview2_com::FocusChangedEventHandler;
  23. #[cfg(windows)]
  24. use windows::Win32::{Foundation::HWND, System::WinRT::EventRegistrationToken};
  25. #[cfg(target_os = "macos")]
  26. use wry::application::platform::macos::EventLoopWindowTargetExtMacOS;
  27. #[cfg(target_os = "macos")]
  28. use wry::application::platform::macos::WindowBuilderExtMacOS;
  29. #[cfg(target_os = "linux")]
  30. use wry::application::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
  31. #[cfg(windows)]
  32. use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
  33. #[cfg(windows)]
  34. use wry::webview::WebViewBuilderExtWindows;
  35. #[cfg(target_os = "macos")]
  36. use tauri_utils::TitleBarStyle;
  37. use tauri_utils::{config::WindowConfig, debug_eprintln, Theme};
  38. use uuid::Uuid;
  39. use wry::{
  40. application::{
  41. dpi::{
  42. LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize,
  43. PhysicalPosition as WryPhysicalPosition, PhysicalSize as WryPhysicalSize,
  44. Position as WryPosition, Size as WrySize,
  45. },
  46. event::{Event, StartCause, WindowEvent as WryWindowEvent},
  47. event_loop::{
  48. ControlFlow, DeviceEventFilter as WryDeviceEventFilter, EventLoop,
  49. EventLoopProxy as WryEventLoopProxy, EventLoopWindowTarget,
  50. },
  51. menu::{
  52. AboutMetadata as WryAboutMetadata, CustomMenuItem as WryCustomMenuItem, MenuBar,
  53. MenuId as WryMenuId, MenuItem as WryMenuItem, MenuItemAttributes as WryMenuItemAttributes,
  54. MenuType,
  55. },
  56. monitor::MonitorHandle,
  57. window::{
  58. CursorIcon as WryCursorIcon, Fullscreen, Icon as WryWindowIcon, Theme as WryTheme,
  59. UserAttentionType as WryUserAttentionType,
  60. },
  61. },
  62. http::{Request as WryRequest, Response as WryResponse},
  63. webview::{FileDropEvent as WryFileDropEvent, Url, WebContext, WebView, WebViewBuilder},
  64. };
  65. pub use wry;
  66. pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId};
  67. #[cfg(windows)]
  68. use wry::webview::WebviewExtWindows;
  69. #[cfg(target_os = "android")]
  70. use wry::webview::{
  71. prelude::{dispatch, find_class},
  72. WebViewBuilderExtAndroid, WebviewExtAndroid,
  73. };
  74. #[cfg(target_os = "macos")]
  75. use tauri_runtime::{menu::NativeImage, ActivationPolicy};
  76. #[cfg(target_os = "macos")]
  77. pub use wry::application::platform::macos::{
  78. ActivationPolicy as WryActivationPolicy, CustomMenuItemExtMacOS, EventLoopExtMacOS,
  79. NativeImage as WryNativeImage, WindowExtMacOS,
  80. };
  81. use std::{
  82. borrow::Cow,
  83. cell::RefCell,
  84. collections::{
  85. hash_map::Entry::{Occupied, Vacant},
  86. HashMap, HashSet,
  87. },
  88. fmt,
  89. ops::Deref,
  90. path::PathBuf,
  91. sync::{
  92. mpsc::{channel, Sender},
  93. Arc, Mutex, Weak,
  94. },
  95. thread::{current as current_thread, ThreadId},
  96. };
  97. pub type WebviewId = u64;
  98. type IpcHandler = dyn Fn(&Window, String) + 'static;
  99. type FileDropHandler = dyn Fn(&Window, WryFileDropEvent) -> bool + 'static;
  100. #[cfg(all(desktop, feature = "system-tray"))]
  101. pub use tauri_runtime::TrayId;
  102. mod webview;
  103. pub use webview::Webview;
  104. #[cfg(all(desktop, feature = "system-tray"))]
  105. mod system_tray;
  106. #[cfg(all(desktop, feature = "system-tray"))]
  107. use system_tray::*;
  108. #[cfg(all(desktop, feature = "global-shortcut"))]
  109. mod global_shortcut;
  110. #[cfg(all(desktop, feature = "global-shortcut"))]
  111. use global_shortcut::*;
  112. #[cfg(feature = "clipboard")]
  113. mod clipboard;
  114. #[cfg(feature = "clipboard")]
  115. use clipboard::*;
  116. pub type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
  117. // window
  118. pub type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
  119. pub type WindowEventListeners = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;
  120. // menu
  121. pub type MenuEventHandler = Box<dyn Fn(&MenuEvent) + Send>;
  122. pub type WindowMenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
  123. #[derive(Debug, Clone, Default)]
  124. pub struct WebviewIdStore(Arc<Mutex<HashMap<WindowId, WebviewId>>>);
  125. impl WebviewIdStore {
  126. pub fn insert(&self, w: WindowId, id: WebviewId) {
  127. self.0.lock().unwrap().insert(w, id);
  128. }
  129. fn get(&self, w: &WindowId) -> Option<WebviewId> {
  130. self.0.lock().unwrap().get(w).copied()
  131. }
  132. }
  133. #[macro_export]
  134. macro_rules! getter {
  135. ($self: ident, $rx: expr, $message: expr) => {{
  136. $crate::send_user_message(&$self.context, $message)?;
  137. $rx
  138. .recv()
  139. .map_err(|_| $crate::Error::FailedToReceiveMessage)
  140. }};
  141. }
  142. macro_rules! window_getter {
  143. ($self: ident, $message: expr) => {{
  144. let (tx, rx) = channel();
  145. getter!($self, rx, Message::Window($self.window_id, $message(tx)))
  146. }};
  147. }
  148. pub(crate) fn send_user_message<T: UserEvent>(
  149. context: &Context<T>,
  150. message: Message<T>,
  151. ) -> Result<()> {
  152. if current_thread().id() == context.main_thread_id {
  153. handle_user_message(
  154. &context.main_thread.window_target,
  155. message,
  156. UserMessageContext {
  157. webview_id_map: context.webview_id_map.clone(),
  158. #[cfg(all(desktop, feature = "global-shortcut"))]
  159. global_shortcut_manager: context.main_thread.global_shortcut_manager.clone(),
  160. #[cfg(feature = "clipboard")]
  161. clipboard_manager: context.main_thread.clipboard_manager.clone(),
  162. windows: context.main_thread.windows.clone(),
  163. #[cfg(all(desktop, feature = "system-tray"))]
  164. system_tray_manager: context.main_thread.system_tray_manager.clone(),
  165. },
  166. &context.main_thread.web_context,
  167. );
  168. Ok(())
  169. } else {
  170. context
  171. .proxy
  172. .send_event(message)
  173. .map_err(|_| Error::FailedToSendMessage)
  174. }
  175. }
  176. #[derive(Clone)]
  177. pub struct Context<T: UserEvent> {
  178. pub webview_id_map: WebviewIdStore,
  179. main_thread_id: ThreadId,
  180. pub proxy: WryEventLoopProxy<Message<T>>,
  181. main_thread: DispatcherMainThreadContext<T>,
  182. }
  183. impl<T: UserEvent> Context<T> {
  184. pub fn run_threaded<R, F>(&self, f: F) -> R
  185. where
  186. F: FnOnce(Option<&DispatcherMainThreadContext<T>>) -> R,
  187. {
  188. f(if current_thread().id() == self.main_thread_id {
  189. Some(&self.main_thread)
  190. } else {
  191. None
  192. })
  193. }
  194. }
  195. impl<T: UserEvent> Context<T> {
  196. fn create_webview(&self, pending: PendingWindow<T, Wry<T>>) -> Result<DetachedWindow<T, Wry<T>>> {
  197. let label = pending.label.clone();
  198. let menu_ids = pending.menu_ids.clone();
  199. let js_event_listeners = pending.js_event_listeners.clone();
  200. let context = self.clone();
  201. let window_id = rand::random();
  202. send_user_message(
  203. self,
  204. Message::CreateWebview(
  205. window_id,
  206. Box::new(move |event_loop, web_context| {
  207. create_webview(window_id, event_loop, web_context, context, pending)
  208. }),
  209. ),
  210. )?;
  211. let dispatcher = WryDispatcher {
  212. window_id,
  213. context: self.clone(),
  214. };
  215. Ok(DetachedWindow {
  216. label,
  217. dispatcher,
  218. menu_ids,
  219. js_event_listeners,
  220. })
  221. }
  222. }
  223. #[derive(Debug, Clone)]
  224. pub struct DispatcherMainThreadContext<T: UserEvent> {
  225. pub window_target: EventLoopWindowTarget<Message<T>>,
  226. pub web_context: WebContextStore,
  227. #[cfg(all(desktop, feature = "global-shortcut"))]
  228. pub global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  229. #[cfg(feature = "clipboard")]
  230. pub clipboard_manager: Arc<Mutex<Clipboard>>,
  231. pub windows: Arc<RefCell<HashMap<WebviewId, WindowWrapper>>>,
  232. #[cfg(all(desktop, feature = "system-tray"))]
  233. system_tray_manager: SystemTrayManager,
  234. }
  235. // SAFETY: we ensure this type is only used on the main thread.
  236. #[allow(clippy::non_send_fields_in_send_ty)]
  237. unsafe impl<T: UserEvent> Send for DispatcherMainThreadContext<T> {}
  238. // SAFETY: we ensure this type is only used on the main thread.
  239. #[allow(clippy::non_send_fields_in_send_ty)]
  240. unsafe impl<T: UserEvent> Sync for DispatcherMainThreadContext<T> {}
  241. impl<T: UserEvent> fmt::Debug for Context<T> {
  242. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  243. f.debug_struct("Context")
  244. .field("main_thread_id", &self.main_thread_id)
  245. .field("proxy", &self.proxy)
  246. .field("main_thread", &self.main_thread)
  247. .finish()
  248. }
  249. }
  250. struct HttpRequestWrapper(HttpRequest);
  251. impl From<&WryRequest<Vec<u8>>> for HttpRequestWrapper {
  252. fn from(req: &WryRequest<Vec<u8>>) -> Self {
  253. let parts = RequestParts {
  254. uri: req.uri().to_string(),
  255. method: req.method().clone(),
  256. headers: req.headers().clone(),
  257. };
  258. Self(HttpRequest::new_internal(parts, req.body().clone()))
  259. }
  260. }
  261. // response
  262. struct HttpResponseWrapper(WryResponse<Cow<'static, [u8]>>);
  263. impl From<HttpResponse> for HttpResponseWrapper {
  264. fn from(response: HttpResponse) -> Self {
  265. let (parts, body) = response.into_parts();
  266. let mut res_builder = WryResponse::builder()
  267. .status(parts.status)
  268. .version(parts.version);
  269. if let Some(mime) = parts.mimetype {
  270. res_builder = res_builder.header(CONTENT_TYPE, mime);
  271. }
  272. for (name, val) in parts.headers.iter() {
  273. res_builder = res_builder.header(name, val);
  274. }
  275. let res = res_builder.body(body).unwrap();
  276. Self(res)
  277. }
  278. }
  279. pub struct MenuItemAttributesWrapper<'a>(pub WryMenuItemAttributes<'a>);
  280. impl<'a> From<&'a CustomMenuItem> for MenuItemAttributesWrapper<'a> {
  281. fn from(item: &'a CustomMenuItem) -> Self {
  282. let mut attributes = WryMenuItemAttributes::new(&item.title)
  283. .with_enabled(item.enabled)
  284. .with_selected(item.selected)
  285. .with_id(WryMenuId(item.id));
  286. if let Some(accelerator) = item.keyboard_accelerator.as_ref() {
  287. attributes = attributes.with_accelerators(&accelerator.parse().expect("invalid accelerator"));
  288. }
  289. Self(attributes)
  290. }
  291. }
  292. pub struct AboutMetadataWrapper(pub WryAboutMetadata);
  293. impl From<AboutMetadata> for AboutMetadataWrapper {
  294. fn from(metadata: AboutMetadata) -> Self {
  295. Self(WryAboutMetadata {
  296. version: metadata.version,
  297. authors: metadata.authors,
  298. comments: metadata.comments,
  299. copyright: metadata.copyright,
  300. license: metadata.license,
  301. website: metadata.website,
  302. website_label: metadata.website_label,
  303. })
  304. }
  305. }
  306. pub struct MenuItemWrapper(pub WryMenuItem);
  307. impl From<MenuItem> for MenuItemWrapper {
  308. fn from(item: MenuItem) -> Self {
  309. match item {
  310. MenuItem::About(name, metadata) => Self(WryMenuItem::About(
  311. name,
  312. AboutMetadataWrapper::from(metadata).0,
  313. )),
  314. MenuItem::Hide => Self(WryMenuItem::Hide),
  315. MenuItem::Services => Self(WryMenuItem::Services),
  316. MenuItem::HideOthers => Self(WryMenuItem::HideOthers),
  317. MenuItem::ShowAll => Self(WryMenuItem::ShowAll),
  318. MenuItem::CloseWindow => Self(WryMenuItem::CloseWindow),
  319. MenuItem::Quit => Self(WryMenuItem::Quit),
  320. MenuItem::Copy => Self(WryMenuItem::Copy),
  321. MenuItem::Cut => Self(WryMenuItem::Cut),
  322. MenuItem::Undo => Self(WryMenuItem::Undo),
  323. MenuItem::Redo => Self(WryMenuItem::Redo),
  324. MenuItem::SelectAll => Self(WryMenuItem::SelectAll),
  325. MenuItem::Paste => Self(WryMenuItem::Paste),
  326. MenuItem::EnterFullScreen => Self(WryMenuItem::EnterFullScreen),
  327. MenuItem::Minimize => Self(WryMenuItem::Minimize),
  328. MenuItem::Zoom => Self(WryMenuItem::Zoom),
  329. MenuItem::Separator => Self(WryMenuItem::Separator),
  330. _ => unimplemented!(),
  331. }
  332. }
  333. }
  334. pub struct DeviceEventFilterWrapper(pub WryDeviceEventFilter);
  335. impl From<DeviceEventFilter> for DeviceEventFilterWrapper {
  336. fn from(item: DeviceEventFilter) -> Self {
  337. match item {
  338. DeviceEventFilter::Always => Self(WryDeviceEventFilter::Always),
  339. DeviceEventFilter::Never => Self(WryDeviceEventFilter::Never),
  340. DeviceEventFilter::Unfocused => Self(WryDeviceEventFilter::Unfocused),
  341. }
  342. }
  343. }
  344. #[cfg(target_os = "macos")]
  345. pub struct NativeImageWrapper(pub WryNativeImage);
  346. #[cfg(target_os = "macos")]
  347. impl std::fmt::Debug for NativeImageWrapper {
  348. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  349. f.debug_struct("NativeImageWrapper").finish()
  350. }
  351. }
  352. #[cfg(target_os = "macos")]
  353. impl From<NativeImage> for NativeImageWrapper {
  354. fn from(image: NativeImage) -> NativeImageWrapper {
  355. let wry_image = match image {
  356. NativeImage::Add => WryNativeImage::Add,
  357. NativeImage::Advanced => WryNativeImage::Advanced,
  358. NativeImage::Bluetooth => WryNativeImage::Bluetooth,
  359. NativeImage::Bookmarks => WryNativeImage::Bookmarks,
  360. NativeImage::Caution => WryNativeImage::Caution,
  361. NativeImage::ColorPanel => WryNativeImage::ColorPanel,
  362. NativeImage::ColumnView => WryNativeImage::ColumnView,
  363. NativeImage::Computer => WryNativeImage::Computer,
  364. NativeImage::EnterFullScreen => WryNativeImage::EnterFullScreen,
  365. NativeImage::Everyone => WryNativeImage::Everyone,
  366. NativeImage::ExitFullScreen => WryNativeImage::ExitFullScreen,
  367. NativeImage::FlowView => WryNativeImage::FlowView,
  368. NativeImage::Folder => WryNativeImage::Folder,
  369. NativeImage::FolderBurnable => WryNativeImage::FolderBurnable,
  370. NativeImage::FolderSmart => WryNativeImage::FolderSmart,
  371. NativeImage::FollowLinkFreestanding => WryNativeImage::FollowLinkFreestanding,
  372. NativeImage::FontPanel => WryNativeImage::FontPanel,
  373. NativeImage::GoLeft => WryNativeImage::GoLeft,
  374. NativeImage::GoRight => WryNativeImage::GoRight,
  375. NativeImage::Home => WryNativeImage::Home,
  376. NativeImage::IChatTheater => WryNativeImage::IChatTheater,
  377. NativeImage::IconView => WryNativeImage::IconView,
  378. NativeImage::Info => WryNativeImage::Info,
  379. NativeImage::InvalidDataFreestanding => WryNativeImage::InvalidDataFreestanding,
  380. NativeImage::LeftFacingTriangle => WryNativeImage::LeftFacingTriangle,
  381. NativeImage::ListView => WryNativeImage::ListView,
  382. NativeImage::LockLocked => WryNativeImage::LockLocked,
  383. NativeImage::LockUnlocked => WryNativeImage::LockUnlocked,
  384. NativeImage::MenuMixedState => WryNativeImage::MenuMixedState,
  385. NativeImage::MenuOnState => WryNativeImage::MenuOnState,
  386. NativeImage::MobileMe => WryNativeImage::MobileMe,
  387. NativeImage::MultipleDocuments => WryNativeImage::MultipleDocuments,
  388. NativeImage::Network => WryNativeImage::Network,
  389. NativeImage::Path => WryNativeImage::Path,
  390. NativeImage::PreferencesGeneral => WryNativeImage::PreferencesGeneral,
  391. NativeImage::QuickLook => WryNativeImage::QuickLook,
  392. NativeImage::RefreshFreestanding => WryNativeImage::RefreshFreestanding,
  393. NativeImage::Refresh => WryNativeImage::Refresh,
  394. NativeImage::Remove => WryNativeImage::Remove,
  395. NativeImage::RevealFreestanding => WryNativeImage::RevealFreestanding,
  396. NativeImage::RightFacingTriangle => WryNativeImage::RightFacingTriangle,
  397. NativeImage::Share => WryNativeImage::Share,
  398. NativeImage::Slideshow => WryNativeImage::Slideshow,
  399. NativeImage::SmartBadge => WryNativeImage::SmartBadge,
  400. NativeImage::StatusAvailable => WryNativeImage::StatusAvailable,
  401. NativeImage::StatusNone => WryNativeImage::StatusNone,
  402. NativeImage::StatusPartiallyAvailable => WryNativeImage::StatusPartiallyAvailable,
  403. NativeImage::StatusUnavailable => WryNativeImage::StatusUnavailable,
  404. NativeImage::StopProgressFreestanding => WryNativeImage::StopProgressFreestanding,
  405. NativeImage::StopProgress => WryNativeImage::StopProgress,
  406. NativeImage::TrashEmpty => WryNativeImage::TrashEmpty,
  407. NativeImage::TrashFull => WryNativeImage::TrashFull,
  408. NativeImage::User => WryNativeImage::User,
  409. NativeImage::UserAccounts => WryNativeImage::UserAccounts,
  410. NativeImage::UserGroup => WryNativeImage::UserGroup,
  411. NativeImage::UserGuest => WryNativeImage::UserGuest,
  412. };
  413. Self(wry_image)
  414. }
  415. }
  416. /// Wrapper around a [`wry::application::window::Icon`] that can be created from an [`Icon`].
  417. pub struct WryIcon(pub WryWindowIcon);
  418. fn icon_err<E: std::error::Error + Send + Sync + 'static>(e: E) -> Error {
  419. Error::InvalidIcon(Box::new(e))
  420. }
  421. impl TryFrom<Icon> for WryIcon {
  422. type Error = Error;
  423. fn try_from(icon: Icon) -> std::result::Result<Self, Self::Error> {
  424. WryWindowIcon::from_rgba(icon.rgba, icon.width, icon.height)
  425. .map(Self)
  426. .map_err(icon_err)
  427. }
  428. }
  429. pub struct WindowEventWrapper(pub Option<WindowEvent>);
  430. impl WindowEventWrapper {
  431. fn parse(webview: &Option<WindowHandle>, event: &WryWindowEvent<'_>) -> Self {
  432. match event {
  433. // resized event from tao doesn't include a reliable size on macOS
  434. // because wry replaces the NSView
  435. WryWindowEvent::Resized(_) => {
  436. if let Some(webview) = webview {
  437. Self(Some(WindowEvent::Resized(
  438. PhysicalSizeWrapper(webview.inner_size()).into(),
  439. )))
  440. } else {
  441. Self(None)
  442. }
  443. }
  444. e => e.into(),
  445. }
  446. }
  447. }
  448. pub fn map_theme(theme: &WryTheme) -> Theme {
  449. match theme {
  450. WryTheme::Light => Theme::Light,
  451. WryTheme::Dark => Theme::Dark,
  452. _ => Theme::Light,
  453. }
  454. }
  455. impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
  456. fn from(event: &WryWindowEvent<'a>) -> Self {
  457. let event = match event {
  458. WryWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
  459. WryWindowEvent::Moved(position) => {
  460. WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
  461. }
  462. WryWindowEvent::Destroyed => WindowEvent::Destroyed,
  463. WryWindowEvent::ScaleFactorChanged {
  464. scale_factor,
  465. new_inner_size,
  466. } => WindowEvent::ScaleFactorChanged {
  467. scale_factor: *scale_factor,
  468. new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
  469. },
  470. #[cfg(any(target_os = "linux", target_os = "macos"))]
  471. WryWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
  472. WryWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
  473. _ => return Self(None),
  474. };
  475. Self(Some(event))
  476. }
  477. }
  478. impl From<&WebviewEvent> for WindowEventWrapper {
  479. fn from(event: &WebviewEvent) -> Self {
  480. let event = match event {
  481. WebviewEvent::Focused(focused) => WindowEvent::Focused(*focused),
  482. };
  483. Self(Some(event))
  484. }
  485. }
  486. pub struct MonitorHandleWrapper(pub MonitorHandle);
  487. impl From<MonitorHandleWrapper> for Monitor {
  488. fn from(monitor: MonitorHandleWrapper) -> Monitor {
  489. Self {
  490. name: monitor.0.name(),
  491. position: PhysicalPositionWrapper(monitor.0.position()).into(),
  492. size: PhysicalSizeWrapper(monitor.0.size()).into(),
  493. scale_factor: monitor.0.scale_factor(),
  494. }
  495. }
  496. }
  497. pub struct PhysicalPositionWrapper<T>(pub WryPhysicalPosition<T>);
  498. impl<T> From<PhysicalPositionWrapper<T>> for PhysicalPosition<T> {
  499. fn from(position: PhysicalPositionWrapper<T>) -> Self {
  500. Self {
  501. x: position.0.x,
  502. y: position.0.y,
  503. }
  504. }
  505. }
  506. impl<T> From<PhysicalPosition<T>> for PhysicalPositionWrapper<T> {
  507. fn from(position: PhysicalPosition<T>) -> Self {
  508. Self(WryPhysicalPosition {
  509. x: position.x,
  510. y: position.y,
  511. })
  512. }
  513. }
  514. struct LogicalPositionWrapper<T>(WryLogicalPosition<T>);
  515. impl<T> From<LogicalPosition<T>> for LogicalPositionWrapper<T> {
  516. fn from(position: LogicalPosition<T>) -> Self {
  517. Self(WryLogicalPosition {
  518. x: position.x,
  519. y: position.y,
  520. })
  521. }
  522. }
  523. pub struct PhysicalSizeWrapper<T>(pub WryPhysicalSize<T>);
  524. impl<T> From<PhysicalSizeWrapper<T>> for PhysicalSize<T> {
  525. fn from(size: PhysicalSizeWrapper<T>) -> Self {
  526. Self {
  527. width: size.0.width,
  528. height: size.0.height,
  529. }
  530. }
  531. }
  532. impl<T> From<PhysicalSize<T>> for PhysicalSizeWrapper<T> {
  533. fn from(size: PhysicalSize<T>) -> Self {
  534. Self(WryPhysicalSize {
  535. width: size.width,
  536. height: size.height,
  537. })
  538. }
  539. }
  540. struct LogicalSizeWrapper<T>(WryLogicalSize<T>);
  541. impl<T> From<LogicalSize<T>> for LogicalSizeWrapper<T> {
  542. fn from(size: LogicalSize<T>) -> Self {
  543. Self(WryLogicalSize {
  544. width: size.width,
  545. height: size.height,
  546. })
  547. }
  548. }
  549. pub struct SizeWrapper(pub WrySize);
  550. impl From<Size> for SizeWrapper {
  551. fn from(size: Size) -> Self {
  552. match size {
  553. Size::Logical(s) => Self(WrySize::Logical(LogicalSizeWrapper::from(s).0)),
  554. Size::Physical(s) => Self(WrySize::Physical(PhysicalSizeWrapper::from(s).0)),
  555. }
  556. }
  557. }
  558. pub struct PositionWrapper(pub WryPosition);
  559. impl From<Position> for PositionWrapper {
  560. fn from(position: Position) -> Self {
  561. match position {
  562. Position::Logical(s) => Self(WryPosition::Logical(LogicalPositionWrapper::from(s).0)),
  563. Position::Physical(s) => Self(WryPosition::Physical(PhysicalPositionWrapper::from(s).0)),
  564. }
  565. }
  566. }
  567. #[derive(Debug, Clone)]
  568. pub struct UserAttentionTypeWrapper(pub WryUserAttentionType);
  569. impl From<UserAttentionType> for UserAttentionTypeWrapper {
  570. fn from(request_type: UserAttentionType) -> Self {
  571. let o = match request_type {
  572. UserAttentionType::Critical => WryUserAttentionType::Critical,
  573. UserAttentionType::Informational => WryUserAttentionType::Informational,
  574. };
  575. Self(o)
  576. }
  577. }
  578. #[derive(Debug)]
  579. pub struct CursorIconWrapper(pub WryCursorIcon);
  580. impl From<CursorIcon> for CursorIconWrapper {
  581. fn from(icon: CursorIcon) -> Self {
  582. use CursorIcon::*;
  583. let i = match icon {
  584. Default => WryCursorIcon::Default,
  585. Crosshair => WryCursorIcon::Crosshair,
  586. Hand => WryCursorIcon::Hand,
  587. Arrow => WryCursorIcon::Arrow,
  588. Move => WryCursorIcon::Move,
  589. Text => WryCursorIcon::Text,
  590. Wait => WryCursorIcon::Wait,
  591. Help => WryCursorIcon::Help,
  592. Progress => WryCursorIcon::Progress,
  593. NotAllowed => WryCursorIcon::NotAllowed,
  594. ContextMenu => WryCursorIcon::ContextMenu,
  595. Cell => WryCursorIcon::Cell,
  596. VerticalText => WryCursorIcon::VerticalText,
  597. Alias => WryCursorIcon::Alias,
  598. Copy => WryCursorIcon::Copy,
  599. NoDrop => WryCursorIcon::NoDrop,
  600. Grab => WryCursorIcon::Grab,
  601. Grabbing => WryCursorIcon::Grabbing,
  602. AllScroll => WryCursorIcon::AllScroll,
  603. ZoomIn => WryCursorIcon::ZoomIn,
  604. ZoomOut => WryCursorIcon::ZoomOut,
  605. EResize => WryCursorIcon::EResize,
  606. NResize => WryCursorIcon::NResize,
  607. NeResize => WryCursorIcon::NeResize,
  608. NwResize => WryCursorIcon::NwResize,
  609. SResize => WryCursorIcon::SResize,
  610. SeResize => WryCursorIcon::SeResize,
  611. SwResize => WryCursorIcon::SwResize,
  612. WResize => WryCursorIcon::WResize,
  613. EwResize => WryCursorIcon::EwResize,
  614. NsResize => WryCursorIcon::NsResize,
  615. NeswResize => WryCursorIcon::NeswResize,
  616. NwseResize => WryCursorIcon::NwseResize,
  617. ColResize => WryCursorIcon::ColResize,
  618. RowResize => WryCursorIcon::RowResize,
  619. _ => WryCursorIcon::Default,
  620. };
  621. Self(i)
  622. }
  623. }
  624. #[derive(Debug, Clone, Default)]
  625. pub struct WindowBuilderWrapper {
  626. inner: WryWindowBuilder,
  627. center: bool,
  628. #[cfg(target_os = "macos")]
  629. tabbing_identifier: Option<String>,
  630. menu: Option<Menu>,
  631. }
  632. // SAFETY: this type is `Send` since `menu_items` are read only here
  633. #[allow(clippy::non_send_fields_in_send_ty)]
  634. unsafe impl Send for WindowBuilderWrapper {}
  635. impl WindowBuilderBase for WindowBuilderWrapper {}
  636. impl WindowBuilder for WindowBuilderWrapper {
  637. fn new() -> Self {
  638. Self::default().focused(true)
  639. }
  640. fn with_config(config: WindowConfig) -> Self {
  641. let mut window = WindowBuilderWrapper::new();
  642. #[cfg(target_os = "macos")]
  643. {
  644. window = window
  645. .hidden_title(config.hidden_title)
  646. .title_bar_style(config.title_bar_style);
  647. if let Some(identifier) = &config.tabbing_identifier {
  648. window = window.tabbing_identifier(identifier);
  649. }
  650. }
  651. #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
  652. {
  653. window = window.transparent(config.transparent);
  654. }
  655. #[cfg(all(
  656. target_os = "macos",
  657. not(feature = "macos-private-api"),
  658. debug_assertions
  659. ))]
  660. if config.transparent {
  661. eprintln!(
  662. "The window is set to be transparent but the `macos-private-api` is not enabled.
  663. This can be enabled via the `tauri.macOSPrivateApi` configuration property <https://tauri.app/docs/api/config#tauri.macOSPrivateApi>
  664. ");
  665. }
  666. #[cfg(target_os = "linux")]
  667. {
  668. // Mouse event is disabled on Linux since sudden event bursts could block event loop.
  669. window.inner = window.inner.with_cursor_moved_event(false);
  670. }
  671. #[cfg(desktop)]
  672. {
  673. window = window
  674. .title(config.title.to_string())
  675. .inner_size(config.width, config.height)
  676. .visible(config.visible)
  677. .resizable(config.resizable)
  678. .fullscreen(config.fullscreen)
  679. .decorations(config.decorations)
  680. .maximized(config.maximized)
  681. .always_on_top(config.always_on_top)
  682. .content_protected(config.content_protected)
  683. .skip_taskbar(config.skip_taskbar)
  684. .theme(config.theme)
  685. .shadow(config.shadow);
  686. if let (Some(min_width), Some(min_height)) = (config.min_width, config.min_height) {
  687. window = window.min_inner_size(min_width, min_height);
  688. }
  689. if let (Some(max_width), Some(max_height)) = (config.max_width, config.max_height) {
  690. window = window.max_inner_size(max_width, max_height);
  691. }
  692. if let (Some(x), Some(y)) = (config.x, config.y) {
  693. window = window.position(x, y);
  694. }
  695. if config.center {
  696. window = window.center();
  697. }
  698. }
  699. window
  700. }
  701. fn menu(mut self, menu: Menu) -> Self {
  702. self.menu.replace(menu);
  703. self
  704. }
  705. fn center(mut self) -> Self {
  706. self.center = true;
  707. self
  708. }
  709. fn position(mut self, x: f64, y: f64) -> Self {
  710. self.inner = self.inner.with_position(WryLogicalPosition::new(x, y));
  711. self
  712. }
  713. fn inner_size(mut self, width: f64, height: f64) -> Self {
  714. self.inner = self
  715. .inner
  716. .with_inner_size(WryLogicalSize::new(width, height));
  717. self
  718. }
  719. fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
  720. self.inner = self
  721. .inner
  722. .with_min_inner_size(WryLogicalSize::new(min_width, min_height));
  723. self
  724. }
  725. fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
  726. self.inner = self
  727. .inner
  728. .with_max_inner_size(WryLogicalSize::new(max_width, max_height));
  729. self
  730. }
  731. fn resizable(mut self, resizable: bool) -> Self {
  732. self.inner = self.inner.with_resizable(resizable);
  733. self
  734. }
  735. fn title<S: Into<String>>(mut self, title: S) -> Self {
  736. self.inner = self.inner.with_title(title.into());
  737. self
  738. }
  739. fn fullscreen(mut self, fullscreen: bool) -> Self {
  740. self.inner = if fullscreen {
  741. self
  742. .inner
  743. .with_fullscreen(Some(Fullscreen::Borderless(None)))
  744. } else {
  745. self.inner.with_fullscreen(None)
  746. };
  747. self
  748. }
  749. fn focused(mut self, focused: bool) -> Self {
  750. self.inner = self.inner.with_focused(focused);
  751. self
  752. }
  753. fn maximized(mut self, maximized: bool) -> Self {
  754. self.inner = self.inner.with_maximized(maximized);
  755. self
  756. }
  757. fn visible(mut self, visible: bool) -> Self {
  758. self.inner = self.inner.with_visible(visible);
  759. self
  760. }
  761. #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
  762. fn transparent(mut self, transparent: bool) -> Self {
  763. self.inner = self.inner.with_transparent(transparent);
  764. self
  765. }
  766. fn decorations(mut self, decorations: bool) -> Self {
  767. self.inner = self.inner.with_decorations(decorations);
  768. self
  769. }
  770. fn always_on_top(mut self, always_on_top: bool) -> Self {
  771. self.inner = self.inner.with_always_on_top(always_on_top);
  772. self
  773. }
  774. fn content_protected(mut self, protected: bool) -> Self {
  775. self.inner = self.inner.with_content_protection(protected);
  776. self
  777. }
  778. fn shadow(#[allow(unused_mut)] mut self, _enable: bool) -> Self {
  779. #[cfg(windows)]
  780. {
  781. self.inner = self.inner.with_undecorated_shadow(_enable);
  782. }
  783. #[cfg(target_os = "macos")]
  784. {
  785. self.inner = self.inner.with_has_shadow(_enable);
  786. }
  787. self
  788. }
  789. #[cfg(windows)]
  790. fn parent_window(mut self, parent: HWND) -> Self {
  791. self.inner = self.inner.with_parent_window(parent);
  792. self
  793. }
  794. #[cfg(target_os = "macos")]
  795. fn parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
  796. self.inner = self.inner.with_parent_window(parent);
  797. self
  798. }
  799. #[cfg(windows)]
  800. fn owner_window(mut self, owner: HWND) -> Self {
  801. self.inner = self.inner.with_owner_window(owner);
  802. self
  803. }
  804. #[cfg(target_os = "macos")]
  805. fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
  806. match style {
  807. TitleBarStyle::Visible => {
  808. self.inner = self.inner.with_titlebar_transparent(false);
  809. // Fixes rendering issue when resizing window with devtools open (https://github.com/tauri-apps/tauri/issues/3914)
  810. self.inner = self.inner.with_fullsize_content_view(true);
  811. }
  812. TitleBarStyle::Transparent => {
  813. self.inner = self.inner.with_titlebar_transparent(true);
  814. self.inner = self.inner.with_fullsize_content_view(false);
  815. }
  816. TitleBarStyle::Overlay => {
  817. self.inner = self.inner.with_titlebar_transparent(true);
  818. self.inner = self.inner.with_fullsize_content_view(true);
  819. }
  820. }
  821. self
  822. }
  823. #[cfg(target_os = "macos")]
  824. fn hidden_title(mut self, hidden: bool) -> Self {
  825. self.inner = self.inner.with_title_hidden(hidden);
  826. self
  827. }
  828. #[cfg(target_os = "macos")]
  829. fn tabbing_identifier(mut self, identifier: &str) -> Self {
  830. self.inner = self.inner.with_tabbing_identifier(identifier);
  831. self.tabbing_identifier.replace(identifier.into());
  832. self
  833. }
  834. fn icon(mut self, icon: Icon) -> Result<Self> {
  835. self.inner = self
  836. .inner
  837. .with_window_icon(Some(WryIcon::try_from(icon)?.0));
  838. Ok(self)
  839. }
  840. #[cfg(any(windows, target_os = "linux"))]
  841. fn skip_taskbar(mut self, skip: bool) -> Self {
  842. self.inner = self.inner.with_skip_taskbar(skip);
  843. self
  844. }
  845. #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
  846. fn skip_taskbar(self, _skip: bool) -> Self {
  847. self
  848. }
  849. #[allow(unused_variables, unused_mut)]
  850. fn theme(mut self, theme: Option<Theme>) -> Self {
  851. self.inner = self.inner.with_theme(if let Some(t) = theme {
  852. match t {
  853. Theme::Dark => Some(WryTheme::Dark),
  854. _ => Some(WryTheme::Light),
  855. }
  856. } else {
  857. None
  858. });
  859. self
  860. }
  861. fn has_icon(&self) -> bool {
  862. self.inner.window.window_icon.is_some()
  863. }
  864. fn get_menu(&self) -> Option<&Menu> {
  865. self.menu.as_ref()
  866. }
  867. }
  868. pub struct FileDropEventWrapper(WryFileDropEvent);
  869. // on Linux, the paths are percent-encoded
  870. #[cfg(any(
  871. target_os = "linux",
  872. target_os = "dragonfly",
  873. target_os = "freebsd",
  874. target_os = "netbsd",
  875. target_os = "openbsd"
  876. ))]
  877. fn decode_path(path: PathBuf) -> PathBuf {
  878. percent_encoding::percent_decode(path.display().to_string().as_bytes())
  879. .decode_utf8_lossy()
  880. .into_owned()
  881. .into()
  882. }
  883. // on Windows and macOS, we do not need to decode the path
  884. #[cfg(not(any(
  885. target_os = "linux",
  886. target_os = "dragonfly",
  887. target_os = "freebsd",
  888. target_os = "netbsd",
  889. target_os = "openbsd"
  890. )))]
  891. fn decode_path(path: PathBuf) -> PathBuf {
  892. path
  893. }
  894. impl From<FileDropEventWrapper> for FileDropEvent {
  895. fn from(event: FileDropEventWrapper) -> Self {
  896. match event.0 {
  897. WryFileDropEvent::Hovered { paths, position: _ } => {
  898. FileDropEvent::Hovered(paths.into_iter().map(decode_path).collect())
  899. }
  900. WryFileDropEvent::Dropped { paths, position: _ } => {
  901. FileDropEvent::Dropped(paths.into_iter().map(decode_path).collect())
  902. }
  903. // default to cancelled
  904. // FIXME(maybe): Add `FileDropEvent::Unknown` event?
  905. _ => FileDropEvent::Cancelled,
  906. }
  907. }
  908. }
  909. #[cfg(any(
  910. target_os = "linux",
  911. target_os = "dragonfly",
  912. target_os = "freebsd",
  913. target_os = "netbsd",
  914. target_os = "openbsd"
  915. ))]
  916. pub struct GtkWindow(pub gtk::ApplicationWindow);
  917. #[cfg(any(
  918. target_os = "linux",
  919. target_os = "dragonfly",
  920. target_os = "freebsd",
  921. target_os = "netbsd",
  922. target_os = "openbsd"
  923. ))]
  924. #[allow(clippy::non_send_fields_in_send_ty)]
  925. unsafe impl Send for GtkWindow {}
  926. pub struct RawWindowHandle(pub raw_window_handle::RawWindowHandle);
  927. unsafe impl Send for RawWindowHandle {}
  928. #[cfg(target_os = "macos")]
  929. #[derive(Debug, Clone)]
  930. pub enum ApplicationMessage {
  931. Show,
  932. Hide,
  933. }
  934. pub enum WindowMessage {
  935. WithWebview(Box<dyn FnOnce(Webview) + Send>),
  936. AddEventListener(Uuid, Box<dyn Fn(&WindowEvent) + Send>),
  937. AddMenuEventListener(Uuid, Box<dyn Fn(&MenuEvent) + Send>),
  938. // Devtools
  939. #[cfg(any(debug_assertions, feature = "devtools"))]
  940. OpenDevTools,
  941. #[cfg(any(debug_assertions, feature = "devtools"))]
  942. CloseDevTools,
  943. #[cfg(any(debug_assertions, feature = "devtools"))]
  944. IsDevToolsOpen(Sender<bool>),
  945. // Getters
  946. Url(Sender<Url>),
  947. ScaleFactor(Sender<f64>),
  948. InnerPosition(Sender<Result<PhysicalPosition<i32>>>),
  949. OuterPosition(Sender<Result<PhysicalPosition<i32>>>),
  950. InnerSize(Sender<PhysicalSize<u32>>),
  951. OuterSize(Sender<PhysicalSize<u32>>),
  952. IsFullscreen(Sender<bool>),
  953. IsMinimized(Sender<bool>),
  954. IsMaximized(Sender<bool>),
  955. IsDecorated(Sender<bool>),
  956. IsResizable(Sender<bool>),
  957. IsVisible(Sender<bool>),
  958. Title(Sender<String>),
  959. IsMenuVisible(Sender<bool>),
  960. CurrentMonitor(Sender<Option<MonitorHandle>>),
  961. PrimaryMonitor(Sender<Option<MonitorHandle>>),
  962. AvailableMonitors(Sender<Vec<MonitorHandle>>),
  963. #[cfg(any(
  964. target_os = "linux",
  965. target_os = "dragonfly",
  966. target_os = "freebsd",
  967. target_os = "netbsd",
  968. target_os = "openbsd"
  969. ))]
  970. GtkWindow(Sender<GtkWindow>),
  971. RawWindowHandle(Sender<RawWindowHandle>),
  972. Theme(Sender<Theme>),
  973. // Setters
  974. Center,
  975. RequestUserAttention(Option<UserAttentionTypeWrapper>),
  976. SetResizable(bool),
  977. SetTitle(String),
  978. Maximize,
  979. Unmaximize,
  980. Minimize,
  981. Unminimize,
  982. ShowMenu,
  983. HideMenu,
  984. Show,
  985. Hide,
  986. Close,
  987. SetDecorations(bool),
  988. SetShadow(bool),
  989. SetAlwaysOnTop(bool),
  990. SetContentProtected(bool),
  991. SetSize(Size),
  992. SetMinSize(Option<Size>),
  993. SetMaxSize(Option<Size>),
  994. SetPosition(Position),
  995. SetFullscreen(bool),
  996. SetFocus,
  997. SetIcon(WryWindowIcon),
  998. SetSkipTaskbar(bool),
  999. SetCursorGrab(bool),
  1000. SetCursorVisible(bool),
  1001. SetCursorIcon(CursorIcon),
  1002. SetCursorPosition(Position),
  1003. SetIgnoreCursorEvents(bool),
  1004. DragWindow,
  1005. UpdateMenuItem(u16, MenuUpdate),
  1006. RequestRedraw,
  1007. }
  1008. #[derive(Debug, Clone)]
  1009. pub enum WebviewMessage {
  1010. EvaluateScript(String),
  1011. #[allow(dead_code)]
  1012. WebviewEvent(WebviewEvent),
  1013. Print,
  1014. }
  1015. #[allow(dead_code)]
  1016. #[derive(Debug, Clone)]
  1017. pub enum WebviewEvent {
  1018. Focused(bool),
  1019. }
  1020. #[cfg(all(desktop, feature = "system-tray"))]
  1021. #[derive(Debug, Clone)]
  1022. pub enum TrayMessage {
  1023. UpdateItem(u16, MenuUpdate),
  1024. UpdateMenu(SystemTrayMenu),
  1025. UpdateIcon(Icon),
  1026. #[cfg(target_os = "macos")]
  1027. UpdateIconAsTemplate(bool),
  1028. #[cfg(target_os = "macos")]
  1029. UpdateTitle(String),
  1030. UpdateTooltip(String),
  1031. Create(SystemTray, Sender<Result<()>>),
  1032. Destroy(Sender<Result<()>>),
  1033. }
  1034. pub type CreateWebviewClosure<T> = Box<
  1035. dyn FnOnce(&EventLoopWindowTarget<Message<T>>, &WebContextStore) -> Result<WindowWrapper> + Send,
  1036. >;
  1037. pub enum Message<T: 'static> {
  1038. Task(Box<dyn FnOnce() + Send>),
  1039. #[cfg(target_os = "macos")]
  1040. Application(ApplicationMessage),
  1041. Window(WebviewId, WindowMessage),
  1042. Webview(WebviewId, WebviewMessage),
  1043. #[cfg(all(desktop, feature = "system-tray"))]
  1044. Tray(TrayId, TrayMessage),
  1045. CreateWebview(WebviewId, CreateWebviewClosure<T>),
  1046. CreateWindow(
  1047. WebviewId,
  1048. Box<dyn FnOnce() -> (String, WryWindowBuilder) + Send>,
  1049. Sender<Result<Weak<Window>>>,
  1050. ),
  1051. #[cfg(all(desktop, feature = "global-shortcut"))]
  1052. GlobalShortcut(GlobalShortcutMessage),
  1053. #[cfg(feature = "clipboard")]
  1054. Clipboard(ClipboardMessage),
  1055. UserEvent(T),
  1056. }
  1057. impl<T: UserEvent> Clone for Message<T> {
  1058. fn clone(&self) -> Self {
  1059. match self {
  1060. Self::Webview(i, m) => Self::Webview(*i, m.clone()),
  1061. #[cfg(all(desktop, feature = "system-tray"))]
  1062. Self::Tray(i, m) => Self::Tray(*i, m.clone()),
  1063. #[cfg(all(desktop, feature = "global-shortcut"))]
  1064. Self::GlobalShortcut(m) => Self::GlobalShortcut(m.clone()),
  1065. #[cfg(feature = "clipboard")]
  1066. Self::Clipboard(m) => Self::Clipboard(m.clone()),
  1067. Self::UserEvent(t) => Self::UserEvent(t.clone()),
  1068. _ => unimplemented!(),
  1069. }
  1070. }
  1071. }
  1072. /// The Tauri [`Dispatch`] for [`Wry`].
  1073. #[derive(Debug, Clone)]
  1074. pub struct WryDispatcher<T: UserEvent> {
  1075. window_id: WebviewId,
  1076. context: Context<T>,
  1077. }
  1078. // SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`.
  1079. #[allow(clippy::non_send_fields_in_send_ty)]
  1080. unsafe impl<T: UserEvent> Sync for WryDispatcher<T> {}
  1081. impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
  1082. type Runtime = Wry<T>;
  1083. type WindowBuilder = WindowBuilderWrapper;
  1084. fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
  1085. send_user_message(&self.context, Message::Task(Box::new(f)))
  1086. }
  1087. fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> Uuid {
  1088. let id = Uuid::new_v4();
  1089. let _ = self.context.proxy.send_event(Message::Window(
  1090. self.window_id,
  1091. WindowMessage::AddEventListener(id, Box::new(f)),
  1092. ));
  1093. id
  1094. }
  1095. fn on_menu_event<F: Fn(&MenuEvent) + Send + 'static>(&self, f: F) -> Uuid {
  1096. let id = Uuid::new_v4();
  1097. let _ = self.context.proxy.send_event(Message::Window(
  1098. self.window_id,
  1099. WindowMessage::AddMenuEventListener(id, Box::new(f)),
  1100. ));
  1101. id
  1102. }
  1103. fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
  1104. send_user_message(
  1105. &self.context,
  1106. Message::Window(
  1107. self.window_id,
  1108. WindowMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))),
  1109. ),
  1110. )
  1111. }
  1112. #[cfg(any(debug_assertions, feature = "devtools"))]
  1113. fn open_devtools(&self) {
  1114. let _ = send_user_message(
  1115. &self.context,
  1116. Message::Window(self.window_id, WindowMessage::OpenDevTools),
  1117. );
  1118. }
  1119. #[cfg(any(debug_assertions, feature = "devtools"))]
  1120. fn close_devtools(&self) {
  1121. let _ = send_user_message(
  1122. &self.context,
  1123. Message::Window(self.window_id, WindowMessage::CloseDevTools),
  1124. );
  1125. }
  1126. /// Gets the devtools window's current open state.
  1127. #[cfg(any(debug_assertions, feature = "devtools"))]
  1128. fn is_devtools_open(&self) -> Result<bool> {
  1129. window_getter!(self, WindowMessage::IsDevToolsOpen)
  1130. }
  1131. // Getters
  1132. fn url(&self) -> Result<Url> {
  1133. window_getter!(self, WindowMessage::Url)
  1134. }
  1135. fn scale_factor(&self) -> Result<f64> {
  1136. window_getter!(self, WindowMessage::ScaleFactor)
  1137. }
  1138. fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
  1139. window_getter!(self, WindowMessage::InnerPosition)?
  1140. }
  1141. fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
  1142. window_getter!(self, WindowMessage::OuterPosition)?
  1143. }
  1144. fn inner_size(&self) -> Result<PhysicalSize<u32>> {
  1145. window_getter!(self, WindowMessage::InnerSize)
  1146. }
  1147. fn outer_size(&self) -> Result<PhysicalSize<u32>> {
  1148. window_getter!(self, WindowMessage::OuterSize)
  1149. }
  1150. fn is_fullscreen(&self) -> Result<bool> {
  1151. window_getter!(self, WindowMessage::IsFullscreen)
  1152. }
  1153. fn is_minimized(&self) -> Result<bool> {
  1154. window_getter!(self, WindowMessage::IsMinimized)
  1155. }
  1156. fn is_maximized(&self) -> Result<bool> {
  1157. window_getter!(self, WindowMessage::IsMaximized)
  1158. }
  1159. /// Gets the window’s current decoration state.
  1160. fn is_decorated(&self) -> Result<bool> {
  1161. window_getter!(self, WindowMessage::IsDecorated)
  1162. }
  1163. /// Gets the window’s current resizable state.
  1164. fn is_resizable(&self) -> Result<bool> {
  1165. window_getter!(self, WindowMessage::IsResizable)
  1166. }
  1167. fn is_visible(&self) -> Result<bool> {
  1168. window_getter!(self, WindowMessage::IsVisible)
  1169. }
  1170. fn title(&self) -> Result<String> {
  1171. window_getter!(self, WindowMessage::Title)
  1172. }
  1173. fn is_menu_visible(&self) -> Result<bool> {
  1174. window_getter!(self, WindowMessage::IsMenuVisible)
  1175. }
  1176. fn current_monitor(&self) -> Result<Option<Monitor>> {
  1177. Ok(window_getter!(self, WindowMessage::CurrentMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
  1178. }
  1179. fn primary_monitor(&self) -> Result<Option<Monitor>> {
  1180. Ok(window_getter!(self, WindowMessage::PrimaryMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
  1181. }
  1182. fn available_monitors(&self) -> Result<Vec<Monitor>> {
  1183. Ok(
  1184. window_getter!(self, WindowMessage::AvailableMonitors)?
  1185. .into_iter()
  1186. .map(|m| MonitorHandleWrapper(m).into())
  1187. .collect(),
  1188. )
  1189. }
  1190. fn theme(&self) -> Result<Theme> {
  1191. window_getter!(self, WindowMessage::Theme)
  1192. }
  1193. /// Returns the `ApplicationWindow` from gtk crate that is used by this window.
  1194. #[cfg(any(
  1195. target_os = "linux",
  1196. target_os = "dragonfly",
  1197. target_os = "freebsd",
  1198. target_os = "netbsd",
  1199. target_os = "openbsd"
  1200. ))]
  1201. fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
  1202. window_getter!(self, WindowMessage::GtkWindow).map(|w| w.0)
  1203. }
  1204. fn raw_window_handle(&self) -> Result<raw_window_handle::RawWindowHandle> {
  1205. window_getter!(self, WindowMessage::RawWindowHandle).map(|w| w.0)
  1206. }
  1207. // Setters
  1208. fn center(&self) -> Result<()> {
  1209. send_user_message(
  1210. &self.context,
  1211. Message::Window(self.window_id, WindowMessage::Center),
  1212. )
  1213. }
  1214. fn print(&self) -> Result<()> {
  1215. send_user_message(
  1216. &self.context,
  1217. Message::Webview(self.window_id, WebviewMessage::Print),
  1218. )
  1219. }
  1220. fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
  1221. send_user_message(
  1222. &self.context,
  1223. Message::Window(
  1224. self.window_id,
  1225. WindowMessage::RequestUserAttention(request_type.map(Into::into)),
  1226. ),
  1227. )
  1228. }
  1229. // Creates a window by dispatching a message to the event loop.
  1230. // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
  1231. fn create_window(
  1232. &mut self,
  1233. pending: PendingWindow<T, Self::Runtime>,
  1234. ) -> Result<DetachedWindow<T, Self::Runtime>> {
  1235. self.context.create_webview(pending)
  1236. }
  1237. fn set_resizable(&self, resizable: bool) -> Result<()> {
  1238. send_user_message(
  1239. &self.context,
  1240. Message::Window(self.window_id, WindowMessage::SetResizable(resizable)),
  1241. )
  1242. }
  1243. fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
  1244. send_user_message(
  1245. &self.context,
  1246. Message::Window(self.window_id, WindowMessage::SetTitle(title.into())),
  1247. )
  1248. }
  1249. fn maximize(&self) -> Result<()> {
  1250. send_user_message(
  1251. &self.context,
  1252. Message::Window(self.window_id, WindowMessage::Maximize),
  1253. )
  1254. }
  1255. fn unmaximize(&self) -> Result<()> {
  1256. send_user_message(
  1257. &self.context,
  1258. Message::Window(self.window_id, WindowMessage::Unmaximize),
  1259. )
  1260. }
  1261. fn minimize(&self) -> Result<()> {
  1262. send_user_message(
  1263. &self.context,
  1264. Message::Window(self.window_id, WindowMessage::Minimize),
  1265. )
  1266. }
  1267. fn unminimize(&self) -> Result<()> {
  1268. send_user_message(
  1269. &self.context,
  1270. Message::Window(self.window_id, WindowMessage::Unminimize),
  1271. )
  1272. }
  1273. fn show_menu(&self) -> Result<()> {
  1274. send_user_message(
  1275. &self.context,
  1276. Message::Window(self.window_id, WindowMessage::ShowMenu),
  1277. )
  1278. }
  1279. fn hide_menu(&self) -> Result<()> {
  1280. send_user_message(
  1281. &self.context,
  1282. Message::Window(self.window_id, WindowMessage::HideMenu),
  1283. )
  1284. }
  1285. fn show(&self) -> Result<()> {
  1286. send_user_message(
  1287. &self.context,
  1288. Message::Window(self.window_id, WindowMessage::Show),
  1289. )
  1290. }
  1291. fn hide(&self) -> Result<()> {
  1292. send_user_message(
  1293. &self.context,
  1294. Message::Window(self.window_id, WindowMessage::Hide),
  1295. )
  1296. }
  1297. fn close(&self) -> Result<()> {
  1298. // NOTE: close cannot use the `send_user_message` function because it accesses the event loop callback
  1299. self
  1300. .context
  1301. .proxy
  1302. .send_event(Message::Window(self.window_id, WindowMessage::Close))
  1303. .map_err(|_| Error::FailedToSendMessage)
  1304. }
  1305. fn set_decorations(&self, decorations: bool) -> Result<()> {
  1306. send_user_message(
  1307. &self.context,
  1308. Message::Window(self.window_id, WindowMessage::SetDecorations(decorations)),
  1309. )
  1310. }
  1311. fn set_shadow(&self, enable: bool) -> Result<()> {
  1312. send_user_message(
  1313. &self.context,
  1314. Message::Window(self.window_id, WindowMessage::SetShadow(enable)),
  1315. )
  1316. }
  1317. fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
  1318. send_user_message(
  1319. &self.context,
  1320. Message::Window(self.window_id, WindowMessage::SetAlwaysOnTop(always_on_top)),
  1321. )
  1322. }
  1323. fn set_content_protected(&self, protected: bool) -> Result<()> {
  1324. send_user_message(
  1325. &self.context,
  1326. Message::Window(
  1327. self.window_id,
  1328. WindowMessage::SetContentProtected(protected),
  1329. ),
  1330. )
  1331. }
  1332. fn set_size(&self, size: Size) -> Result<()> {
  1333. send_user_message(
  1334. &self.context,
  1335. Message::Window(self.window_id, WindowMessage::SetSize(size)),
  1336. )
  1337. }
  1338. fn set_min_size(&self, size: Option<Size>) -> Result<()> {
  1339. send_user_message(
  1340. &self.context,
  1341. Message::Window(self.window_id, WindowMessage::SetMinSize(size)),
  1342. )
  1343. }
  1344. fn set_max_size(&self, size: Option<Size>) -> Result<()> {
  1345. send_user_message(
  1346. &self.context,
  1347. Message::Window(self.window_id, WindowMessage::SetMaxSize(size)),
  1348. )
  1349. }
  1350. fn set_position(&self, position: Position) -> Result<()> {
  1351. send_user_message(
  1352. &self.context,
  1353. Message::Window(self.window_id, WindowMessage::SetPosition(position)),
  1354. )
  1355. }
  1356. fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
  1357. send_user_message(
  1358. &self.context,
  1359. Message::Window(self.window_id, WindowMessage::SetFullscreen(fullscreen)),
  1360. )
  1361. }
  1362. fn set_focus(&self) -> Result<()> {
  1363. send_user_message(
  1364. &self.context,
  1365. Message::Window(self.window_id, WindowMessage::SetFocus),
  1366. )
  1367. }
  1368. fn set_icon(&self, icon: Icon) -> Result<()> {
  1369. send_user_message(
  1370. &self.context,
  1371. Message::Window(
  1372. self.window_id,
  1373. WindowMessage::SetIcon(WryIcon::try_from(icon)?.0),
  1374. ),
  1375. )
  1376. }
  1377. fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
  1378. send_user_message(
  1379. &self.context,
  1380. Message::Window(self.window_id, WindowMessage::SetSkipTaskbar(skip)),
  1381. )
  1382. }
  1383. fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
  1384. send_user_message(
  1385. &self.context,
  1386. Message::Window(self.window_id, WindowMessage::SetCursorGrab(grab)),
  1387. )
  1388. }
  1389. fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
  1390. send_user_message(
  1391. &self.context,
  1392. Message::Window(self.window_id, WindowMessage::SetCursorVisible(visible)),
  1393. )
  1394. }
  1395. fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
  1396. send_user_message(
  1397. &self.context,
  1398. Message::Window(self.window_id, WindowMessage::SetCursorIcon(icon)),
  1399. )
  1400. }
  1401. fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
  1402. send_user_message(
  1403. &self.context,
  1404. Message::Window(
  1405. self.window_id,
  1406. WindowMessage::SetCursorPosition(position.into()),
  1407. ),
  1408. )
  1409. }
  1410. fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
  1411. send_user_message(
  1412. &self.context,
  1413. Message::Window(self.window_id, WindowMessage::SetIgnoreCursorEvents(ignore)),
  1414. )
  1415. }
  1416. fn start_dragging(&self) -> Result<()> {
  1417. send_user_message(
  1418. &self.context,
  1419. Message::Window(self.window_id, WindowMessage::DragWindow),
  1420. )
  1421. }
  1422. fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
  1423. send_user_message(
  1424. &self.context,
  1425. Message::Webview(
  1426. self.window_id,
  1427. WebviewMessage::EvaluateScript(script.into()),
  1428. ),
  1429. )
  1430. }
  1431. fn update_menu_item(&self, id: u16, update: MenuUpdate) -> Result<()> {
  1432. send_user_message(
  1433. &self.context,
  1434. Message::Window(self.window_id, WindowMessage::UpdateMenuItem(id, update)),
  1435. )
  1436. }
  1437. }
  1438. #[derive(Clone)]
  1439. enum WindowHandle {
  1440. Webview {
  1441. inner: Arc<WebView>,
  1442. context_store: WebContextStore,
  1443. // the key of the WebContext if it's not shared
  1444. context_key: Option<PathBuf>,
  1445. },
  1446. Window(Arc<Window>),
  1447. }
  1448. impl Drop for WindowHandle {
  1449. fn drop(&mut self) {
  1450. if let Self::Webview {
  1451. inner,
  1452. context_store,
  1453. context_key,
  1454. } = self
  1455. {
  1456. if Arc::get_mut(inner).is_some() {
  1457. context_store.lock().unwrap().remove(context_key);
  1458. }
  1459. }
  1460. }
  1461. }
  1462. impl fmt::Debug for WindowHandle {
  1463. fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
  1464. Ok(())
  1465. }
  1466. }
  1467. impl Deref for WindowHandle {
  1468. type Target = Window;
  1469. #[inline(always)]
  1470. fn deref(&self) -> &Window {
  1471. match self {
  1472. Self::Webview { inner, .. } => inner.window(),
  1473. Self::Window(w) => w,
  1474. }
  1475. }
  1476. }
  1477. impl WindowHandle {
  1478. fn inner_size(&self) -> WryPhysicalSize<u32> {
  1479. match self {
  1480. WindowHandle::Window(w) => w.inner_size(),
  1481. WindowHandle::Webview { inner, .. } => inner.inner_size(),
  1482. }
  1483. }
  1484. }
  1485. pub struct WindowWrapper {
  1486. label: String,
  1487. inner: Option<WindowHandle>,
  1488. menu_items: Option<HashMap<u16, WryCustomMenuItem>>,
  1489. window_event_listeners: WindowEventListeners,
  1490. menu_event_listeners: WindowMenuEventListeners,
  1491. }
  1492. impl fmt::Debug for WindowWrapper {
  1493. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  1494. f.debug_struct("WindowWrapper")
  1495. .field("label", &self.label)
  1496. .field("inner", &self.inner)
  1497. .field("menu_items", &self.menu_items)
  1498. .finish()
  1499. }
  1500. }
  1501. #[derive(Debug, Clone)]
  1502. pub struct EventProxy<T: UserEvent>(WryEventLoopProxy<Message<T>>);
  1503. #[cfg(target_os = "ios")]
  1504. #[allow(clippy::non_send_fields_in_send_ty)]
  1505. unsafe impl<T: UserEvent> Sync for EventProxy<T> {}
  1506. impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
  1507. fn send_event(&self, event: T) -> Result<()> {
  1508. self
  1509. .0
  1510. .send_event(Message::UserEvent(event))
  1511. .map_err(|_| Error::EventLoopClosed)
  1512. }
  1513. }
  1514. pub trait PluginBuilder<T: UserEvent> {
  1515. type Plugin: Plugin<T>;
  1516. fn build(self, context: Context<T>) -> Self::Plugin;
  1517. }
  1518. pub trait Plugin<T: UserEvent> {
  1519. fn on_event(
  1520. &mut self,
  1521. event: &Event<Message<T>>,
  1522. event_loop: &EventLoopWindowTarget<Message<T>>,
  1523. proxy: &WryEventLoopProxy<Message<T>>,
  1524. control_flow: &mut ControlFlow,
  1525. context: EventLoopIterationContext<'_, T>,
  1526. web_context: &WebContextStore,
  1527. ) -> bool;
  1528. }
  1529. /// A Tauri [`Runtime`] wrapper around wry.
  1530. pub struct Wry<T: UserEvent> {
  1531. context: Context<T>,
  1532. plugins: Vec<Box<dyn Plugin<T>>>,
  1533. #[cfg(all(desktop, feature = "global-shortcut"))]
  1534. global_shortcut_manager_handle: GlobalShortcutManagerHandle<T>,
  1535. #[cfg(feature = "clipboard")]
  1536. clipboard_manager_handle: ClipboardManagerWrapper<T>,
  1537. event_loop: EventLoop<Message<T>>,
  1538. }
  1539. impl<T: UserEvent> fmt::Debug for Wry<T> {
  1540. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  1541. let mut d = f.debug_struct("Wry");
  1542. d.field("main_thread_id", &self.context.main_thread_id)
  1543. .field("event_loop", &self.event_loop)
  1544. .field("windows", &self.context.main_thread.windows)
  1545. .field("web_context", &self.context.main_thread.web_context);
  1546. #[cfg(all(desktop, feature = "system-tray"))]
  1547. d.field(
  1548. "system_tray_manager",
  1549. &self.context.main_thread.system_tray_manager,
  1550. );
  1551. #[cfg(all(desktop, feature = "global-shortcut"))]
  1552. #[cfg(feature = "global-shortcut")]
  1553. d.field(
  1554. "global_shortcut_manager",
  1555. &self.context.main_thread.global_shortcut_manager,
  1556. )
  1557. .field(
  1558. "global_shortcut_manager_handle",
  1559. &self.global_shortcut_manager_handle,
  1560. );
  1561. #[cfg(feature = "clipboard")]
  1562. d.field(
  1563. "clipboard_manager",
  1564. &self.context.main_thread.clipboard_manager,
  1565. )
  1566. .field("clipboard_manager_handle", &self.clipboard_manager_handle);
  1567. d.finish()
  1568. }
  1569. }
  1570. /// A handle to the Wry runtime.
  1571. #[derive(Debug, Clone)]
  1572. pub struct WryHandle<T: UserEvent> {
  1573. context: Context<T>,
  1574. }
  1575. // SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`.
  1576. #[allow(clippy::non_send_fields_in_send_ty)]
  1577. unsafe impl<T: UserEvent> Sync for WryHandle<T> {}
  1578. impl<T: UserEvent> WryHandle<T> {
  1579. /// Creates a new tao window using a callback, and returns its window id.
  1580. pub fn create_tao_window<F: FnOnce() -> (String, WryWindowBuilder) + Send + 'static>(
  1581. &self,
  1582. f: F,
  1583. ) -> Result<Weak<Window>> {
  1584. let (tx, rx) = channel();
  1585. send_user_message(
  1586. &self.context,
  1587. Message::CreateWindow(rand::random(), Box::new(f), tx),
  1588. )?;
  1589. rx.recv().unwrap()
  1590. }
  1591. /// Gets the [`WebviewId'] associated with the given [`WindowId`].
  1592. pub fn window_id(&self, window_id: WindowId) -> WebviewId {
  1593. *self
  1594. .context
  1595. .webview_id_map
  1596. .0
  1597. .lock()
  1598. .unwrap()
  1599. .get(&window_id)
  1600. .unwrap()
  1601. }
  1602. /// Send a message to the event loop.
  1603. pub fn send_event(&self, message: Message<T>) -> Result<()> {
  1604. self
  1605. .context
  1606. .proxy
  1607. .send_event(message)
  1608. .map_err(|_| Error::FailedToSendMessage)?;
  1609. Ok(())
  1610. }
  1611. }
  1612. impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
  1613. type Runtime = Wry<T>;
  1614. fn create_proxy(&self) -> EventProxy<T> {
  1615. EventProxy(self.context.proxy.clone())
  1616. }
  1617. // Creates a window by dispatching a message to the event loop.
  1618. // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
  1619. fn create_window(
  1620. &self,
  1621. pending: PendingWindow<T, Self::Runtime>,
  1622. ) -> Result<DetachedWindow<T, Self::Runtime>> {
  1623. self.context.create_webview(pending)
  1624. }
  1625. fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
  1626. send_user_message(&self.context, Message::Task(Box::new(f)))
  1627. }
  1628. #[cfg(all(desktop, feature = "system-tray"))]
  1629. fn system_tray(
  1630. &self,
  1631. system_tray: SystemTray,
  1632. ) -> Result<<Self::Runtime as Runtime<T>>::TrayHandler> {
  1633. let id = system_tray.id;
  1634. let (tx, rx) = channel();
  1635. send_user_message(
  1636. &self.context,
  1637. Message::Tray(id, TrayMessage::Create(system_tray, tx)),
  1638. )?;
  1639. rx.recv().unwrap()?;
  1640. Ok(SystemTrayHandle {
  1641. context: self.context.clone(),
  1642. id,
  1643. proxy: self.context.proxy.clone(),
  1644. })
  1645. }
  1646. fn raw_display_handle(&self) -> RawDisplayHandle {
  1647. self.context.main_thread.window_target.raw_display_handle()
  1648. }
  1649. #[cfg(target_os = "macos")]
  1650. fn show(&self) -> tauri_runtime::Result<()> {
  1651. send_user_message(
  1652. &self.context,
  1653. Message::Application(ApplicationMessage::Show),
  1654. )
  1655. }
  1656. #[cfg(target_os = "macos")]
  1657. fn hide(&self) -> tauri_runtime::Result<()> {
  1658. send_user_message(
  1659. &self.context,
  1660. Message::Application(ApplicationMessage::Hide),
  1661. )
  1662. }
  1663. #[cfg(target_os = "android")]
  1664. fn find_class<'a>(
  1665. &'a self,
  1666. env: jni::JNIEnv<'a>,
  1667. activity: jni::objects::JObject<'a>,
  1668. name: impl Into<String>,
  1669. ) -> std::result::Result<jni::objects::JClass<'a>, jni::errors::Error> {
  1670. find_class(env, activity, name.into())
  1671. }
  1672. #[cfg(target_os = "android")]
  1673. fn run_on_android_context<F>(&self, f: F)
  1674. where
  1675. F: FnOnce(jni::JNIEnv<'_>, jni::objects::JObject<'_>, jni::objects::JObject<'_>)
  1676. + Send
  1677. + 'static,
  1678. {
  1679. dispatch(f)
  1680. }
  1681. }
  1682. impl<T: UserEvent> Wry<T> {
  1683. fn init(event_loop: EventLoop<Message<T>>) -> Result<Self> {
  1684. let main_thread_id = current_thread().id();
  1685. let web_context = WebContextStore::default();
  1686. #[cfg(all(desktop, feature = "global-shortcut"))]
  1687. let global_shortcut_manager = Arc::new(Mutex::new(WryShortcutManager::new(&event_loop)));
  1688. #[cfg(feature = "clipboard")]
  1689. let clipboard_manager = Arc::new(Mutex::new(Clipboard::new()));
  1690. let windows = Arc::new(RefCell::new(HashMap::default()));
  1691. let webview_id_map = WebviewIdStore::default();
  1692. #[cfg(all(desktop, feature = "system-tray"))]
  1693. let system_tray_manager = Default::default();
  1694. let context = Context {
  1695. webview_id_map,
  1696. main_thread_id,
  1697. proxy: event_loop.create_proxy(),
  1698. main_thread: DispatcherMainThreadContext {
  1699. window_target: event_loop.deref().clone(),
  1700. web_context,
  1701. #[cfg(all(desktop, feature = "global-shortcut"))]
  1702. global_shortcut_manager,
  1703. #[cfg(feature = "clipboard")]
  1704. clipboard_manager,
  1705. windows,
  1706. #[cfg(all(desktop, feature = "system-tray"))]
  1707. system_tray_manager,
  1708. },
  1709. };
  1710. #[cfg(all(desktop, feature = "global-shortcut"))]
  1711. let global_shortcut_manager_handle = GlobalShortcutManagerHandle {
  1712. context: context.clone(),
  1713. shortcuts: Default::default(),
  1714. listeners: Default::default(),
  1715. };
  1716. #[cfg(feature = "clipboard")]
  1717. #[allow(clippy::redundant_clone)]
  1718. let clipboard_manager_handle = ClipboardManagerWrapper {
  1719. context: context.clone(),
  1720. };
  1721. Ok(Self {
  1722. context,
  1723. plugins: Default::default(),
  1724. #[cfg(all(desktop, feature = "global-shortcut"))]
  1725. global_shortcut_manager_handle,
  1726. #[cfg(feature = "clipboard")]
  1727. clipboard_manager_handle,
  1728. event_loop,
  1729. })
  1730. }
  1731. pub fn plugin<P: PluginBuilder<T> + 'static>(&mut self, plugin: P) {
  1732. self
  1733. .plugins
  1734. .push(Box::new(plugin.build(self.context.clone())));
  1735. }
  1736. }
  1737. impl<T: UserEvent> Runtime<T> for Wry<T> {
  1738. type Dispatcher = WryDispatcher<T>;
  1739. type Handle = WryHandle<T>;
  1740. #[cfg(all(desktop, feature = "global-shortcut"))]
  1741. type GlobalShortcutManager = GlobalShortcutManagerHandle<T>;
  1742. #[cfg(feature = "clipboard")]
  1743. type ClipboardManager = ClipboardManagerWrapper<T>;
  1744. #[cfg(all(desktop, feature = "system-tray"))]
  1745. type TrayHandler = SystemTrayHandle<T>;
  1746. type EventLoopProxy = EventProxy<T>;
  1747. fn new() -> Result<Self> {
  1748. let event_loop = EventLoop::<Message<T>>::with_user_event();
  1749. Self::init(event_loop)
  1750. }
  1751. #[cfg(any(windows, target_os = "linux"))]
  1752. fn new_any_thread() -> Result<Self> {
  1753. #[cfg(target_os = "linux")]
  1754. use wry::application::platform::unix::EventLoopExtUnix;
  1755. #[cfg(windows)]
  1756. use wry::application::platform::windows::EventLoopExtWindows;
  1757. let event_loop = EventLoop::<Message<T>>::new_any_thread();
  1758. Self::init(event_loop)
  1759. }
  1760. fn create_proxy(&self) -> EventProxy<T> {
  1761. EventProxy(self.event_loop.create_proxy())
  1762. }
  1763. fn handle(&self) -> Self::Handle {
  1764. WryHandle {
  1765. context: self.context.clone(),
  1766. }
  1767. }
  1768. #[cfg(all(desktop, feature = "global-shortcut"))]
  1769. fn global_shortcut_manager(&self) -> Self::GlobalShortcutManager {
  1770. self.global_shortcut_manager_handle.clone()
  1771. }
  1772. #[cfg(feature = "clipboard")]
  1773. fn clipboard_manager(&self) -> Self::ClipboardManager {
  1774. self.clipboard_manager_handle.clone()
  1775. }
  1776. fn create_window(&self, pending: PendingWindow<T, Self>) -> Result<DetachedWindow<T, Self>> {
  1777. let label = pending.label.clone();
  1778. let menu_ids = pending.menu_ids.clone();
  1779. let js_event_listeners = pending.js_event_listeners.clone();
  1780. let window_id = rand::random();
  1781. let webview = create_webview(
  1782. window_id,
  1783. &self.event_loop,
  1784. &self.context.main_thread.web_context,
  1785. self.context.clone(),
  1786. pending,
  1787. )?;
  1788. let dispatcher = WryDispatcher {
  1789. window_id,
  1790. context: self.context.clone(),
  1791. };
  1792. self
  1793. .context
  1794. .main_thread
  1795. .windows
  1796. .borrow_mut()
  1797. .insert(window_id, webview);
  1798. Ok(DetachedWindow {
  1799. label,
  1800. dispatcher,
  1801. menu_ids,
  1802. js_event_listeners,
  1803. })
  1804. }
  1805. #[cfg(all(desktop, feature = "system-tray"))]
  1806. fn system_tray(&self, mut system_tray: SystemTray) -> Result<Self::TrayHandler> {
  1807. let id = system_tray.id;
  1808. let mut listeners = Vec::new();
  1809. if let Some(l) = system_tray.on_event.take() {
  1810. listeners.push(Arc::new(l));
  1811. }
  1812. let (tray, items) = create_tray(WryTrayId(id), system_tray, &self.event_loop)?;
  1813. self
  1814. .context
  1815. .main_thread
  1816. .system_tray_manager
  1817. .trays
  1818. .lock()
  1819. .unwrap()
  1820. .insert(
  1821. id,
  1822. TrayContext {
  1823. tray: Arc::new(Mutex::new(Some(tray))),
  1824. listeners: Arc::new(Mutex::new(listeners)),
  1825. items: Arc::new(Mutex::new(items)),
  1826. },
  1827. );
  1828. Ok(SystemTrayHandle {
  1829. context: self.context.clone(),
  1830. id,
  1831. proxy: self.event_loop.create_proxy(),
  1832. })
  1833. }
  1834. #[cfg(all(desktop, feature = "system-tray"))]
  1835. fn on_system_tray_event<F: Fn(TrayId, &SystemTrayEvent) + Send + 'static>(&mut self, f: F) {
  1836. self
  1837. .context
  1838. .main_thread
  1839. .system_tray_manager
  1840. .global_listeners
  1841. .lock()
  1842. .unwrap()
  1843. .push(Arc::new(Box::new(f)));
  1844. }
  1845. #[cfg(target_os = "macos")]
  1846. fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
  1847. self
  1848. .event_loop
  1849. .set_activation_policy(match activation_policy {
  1850. ActivationPolicy::Regular => WryActivationPolicy::Regular,
  1851. ActivationPolicy::Accessory => WryActivationPolicy::Accessory,
  1852. ActivationPolicy::Prohibited => WryActivationPolicy::Prohibited,
  1853. _ => unimplemented!(),
  1854. });
  1855. }
  1856. #[cfg(target_os = "macos")]
  1857. fn show(&self) {
  1858. self.event_loop.show_application();
  1859. }
  1860. #[cfg(target_os = "macos")]
  1861. fn hide(&self) {
  1862. self.event_loop.hide_application();
  1863. }
  1864. fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
  1865. self
  1866. .event_loop
  1867. .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
  1868. }
  1869. #[cfg(desktop)]
  1870. fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, mut callback: F) -> RunIteration {
  1871. use wry::application::platform::run_return::EventLoopExtRunReturn;
  1872. let windows = self.context.main_thread.windows.clone();
  1873. let webview_id_map = self.context.webview_id_map.clone();
  1874. let web_context = &self.context.main_thread.web_context;
  1875. let plugins = &mut self.plugins;
  1876. #[cfg(all(desktop, feature = "system-tray"))]
  1877. let system_tray_manager = self.context.main_thread.system_tray_manager.clone();
  1878. #[cfg(all(desktop, feature = "global-shortcut"))]
  1879. let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone();
  1880. #[cfg(all(desktop, feature = "global-shortcut"))]
  1881. let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone();
  1882. #[cfg(feature = "clipboard")]
  1883. let clipboard_manager = self.context.main_thread.clipboard_manager.clone();
  1884. let mut iteration = RunIteration::default();
  1885. let proxy = self.event_loop.create_proxy();
  1886. self
  1887. .event_loop
  1888. .run_return(|event, event_loop, control_flow| {
  1889. *control_flow = ControlFlow::Wait;
  1890. if let Event::MainEventsCleared = &event {
  1891. *control_flow = ControlFlow::Exit;
  1892. }
  1893. for p in plugins.iter_mut() {
  1894. let prevent_default = p.on_event(
  1895. &event,
  1896. event_loop,
  1897. &proxy,
  1898. control_flow,
  1899. EventLoopIterationContext {
  1900. callback: &mut callback,
  1901. webview_id_map: webview_id_map.clone(),
  1902. windows: windows.clone(),
  1903. #[cfg(all(desktop, feature = "global-shortcut"))]
  1904. global_shortcut_manager: global_shortcut_manager.clone(),
  1905. #[cfg(all(desktop, feature = "global-shortcut"))]
  1906. global_shortcut_manager_handle: &global_shortcut_manager_handle,
  1907. #[cfg(feature = "clipboard")]
  1908. clipboard_manager: clipboard_manager.clone(),
  1909. #[cfg(all(desktop, feature = "system-tray"))]
  1910. system_tray_manager: system_tray_manager.clone(),
  1911. },
  1912. web_context,
  1913. );
  1914. if prevent_default {
  1915. return;
  1916. }
  1917. }
  1918. iteration = handle_event_loop(
  1919. event,
  1920. event_loop,
  1921. control_flow,
  1922. EventLoopIterationContext {
  1923. callback: &mut callback,
  1924. windows: windows.clone(),
  1925. webview_id_map: webview_id_map.clone(),
  1926. #[cfg(all(desktop, feature = "global-shortcut"))]
  1927. global_shortcut_manager: global_shortcut_manager.clone(),
  1928. #[cfg(all(desktop, feature = "global-shortcut"))]
  1929. global_shortcut_manager_handle: &global_shortcut_manager_handle,
  1930. #[cfg(feature = "clipboard")]
  1931. clipboard_manager: clipboard_manager.clone(),
  1932. #[cfg(all(desktop, feature = "system-tray"))]
  1933. system_tray_manager: system_tray_manager.clone(),
  1934. },
  1935. web_context,
  1936. );
  1937. });
  1938. iteration
  1939. }
  1940. fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
  1941. let windows = self.context.main_thread.windows.clone();
  1942. let webview_id_map = self.context.webview_id_map.clone();
  1943. let web_context = self.context.main_thread.web_context;
  1944. let mut plugins = self.plugins;
  1945. #[cfg(all(desktop, feature = "system-tray"))]
  1946. let system_tray_manager = self.context.main_thread.system_tray_manager;
  1947. #[cfg(all(desktop, feature = "global-shortcut"))]
  1948. let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone();
  1949. #[cfg(all(desktop, feature = "global-shortcut"))]
  1950. let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone();
  1951. #[cfg(feature = "clipboard")]
  1952. let clipboard_manager = self.context.main_thread.clipboard_manager.clone();
  1953. let proxy = self.event_loop.create_proxy();
  1954. self.event_loop.run(move |event, event_loop, control_flow| {
  1955. for p in &mut plugins {
  1956. let prevent_default = p.on_event(
  1957. &event,
  1958. event_loop,
  1959. &proxy,
  1960. control_flow,
  1961. EventLoopIterationContext {
  1962. callback: &mut callback,
  1963. webview_id_map: webview_id_map.clone(),
  1964. windows: windows.clone(),
  1965. #[cfg(all(desktop, feature = "global-shortcut"))]
  1966. global_shortcut_manager: global_shortcut_manager.clone(),
  1967. #[cfg(all(desktop, feature = "global-shortcut"))]
  1968. global_shortcut_manager_handle: &global_shortcut_manager_handle,
  1969. #[cfg(feature = "clipboard")]
  1970. clipboard_manager: clipboard_manager.clone(),
  1971. #[cfg(all(desktop, feature = "system-tray"))]
  1972. system_tray_manager: system_tray_manager.clone(),
  1973. },
  1974. &web_context,
  1975. );
  1976. if prevent_default {
  1977. return;
  1978. }
  1979. }
  1980. handle_event_loop(
  1981. event,
  1982. event_loop,
  1983. control_flow,
  1984. EventLoopIterationContext {
  1985. callback: &mut callback,
  1986. webview_id_map: webview_id_map.clone(),
  1987. windows: windows.clone(),
  1988. #[cfg(all(desktop, feature = "global-shortcut"))]
  1989. global_shortcut_manager: global_shortcut_manager.clone(),
  1990. #[cfg(all(desktop, feature = "global-shortcut"))]
  1991. global_shortcut_manager_handle: &global_shortcut_manager_handle,
  1992. #[cfg(feature = "clipboard")]
  1993. clipboard_manager: clipboard_manager.clone(),
  1994. #[cfg(all(desktop, feature = "system-tray"))]
  1995. system_tray_manager: system_tray_manager.clone(),
  1996. },
  1997. &web_context,
  1998. );
  1999. })
  2000. }
  2001. }
  2002. pub struct EventLoopIterationContext<'a, T: UserEvent> {
  2003. pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
  2004. pub webview_id_map: WebviewIdStore,
  2005. pub windows: Arc<RefCell<HashMap<WebviewId, WindowWrapper>>>,
  2006. #[cfg(all(desktop, feature = "global-shortcut"))]
  2007. pub global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  2008. #[cfg(all(desktop, feature = "global-shortcut"))]
  2009. pub global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
  2010. #[cfg(feature = "clipboard")]
  2011. pub clipboard_manager: Arc<Mutex<Clipboard>>,
  2012. #[cfg(all(desktop, feature = "system-tray"))]
  2013. pub system_tray_manager: SystemTrayManager,
  2014. }
  2015. struct UserMessageContext {
  2016. windows: Arc<RefCell<HashMap<WebviewId, WindowWrapper>>>,
  2017. webview_id_map: WebviewIdStore,
  2018. #[cfg(all(desktop, feature = "global-shortcut"))]
  2019. global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  2020. #[cfg(feature = "clipboard")]
  2021. clipboard_manager: Arc<Mutex<Clipboard>>,
  2022. #[cfg(all(desktop, feature = "system-tray"))]
  2023. system_tray_manager: SystemTrayManager,
  2024. }
  2025. fn handle_user_message<T: UserEvent>(
  2026. event_loop: &EventLoopWindowTarget<Message<T>>,
  2027. message: Message<T>,
  2028. context: UserMessageContext,
  2029. web_context: &WebContextStore,
  2030. ) -> RunIteration {
  2031. let UserMessageContext {
  2032. webview_id_map,
  2033. #[cfg(all(desktop, feature = "global-shortcut"))]
  2034. global_shortcut_manager,
  2035. #[cfg(feature = "clipboard")]
  2036. clipboard_manager,
  2037. windows,
  2038. #[cfg(all(desktop, feature = "system-tray"))]
  2039. system_tray_manager,
  2040. } = context;
  2041. match message {
  2042. Message::Task(task) => task(),
  2043. #[cfg(target_os = "macos")]
  2044. Message::Application(application_message) => match application_message {
  2045. ApplicationMessage::Show => {
  2046. event_loop.show_application();
  2047. }
  2048. ApplicationMessage::Hide => {
  2049. event_loop.hide_application();
  2050. }
  2051. },
  2052. Message::Window(id, window_message) => {
  2053. if let WindowMessage::UpdateMenuItem(item_id, update) = window_message {
  2054. if let Some(menu_items) = windows.borrow_mut().get_mut(&id).map(|w| &mut w.menu_items) {
  2055. if let Some(menu_items) = menu_items.as_mut() {
  2056. let item = menu_items.get_mut(&item_id).expect("menu item not found");
  2057. match update {
  2058. MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
  2059. MenuUpdate::SetTitle(title) => item.set_title(&title),
  2060. MenuUpdate::SetSelected(selected) => item.set_selected(selected),
  2061. #[cfg(target_os = "macos")]
  2062. MenuUpdate::SetNativeImage(image) => {
  2063. item.set_native_image(NativeImageWrapper::from(image).0)
  2064. }
  2065. }
  2066. }
  2067. }
  2068. } else {
  2069. let w = windows.borrow().get(&id).map(|w| {
  2070. (
  2071. w.inner.clone(),
  2072. w.window_event_listeners.clone(),
  2073. w.menu_event_listeners.clone(),
  2074. )
  2075. });
  2076. if let Some((Some(window), window_event_listeners, menu_event_listeners)) = w {
  2077. match window_message {
  2078. WindowMessage::WithWebview(f) => {
  2079. if let WindowHandle::Webview { inner: w, .. } = &window {
  2080. #[cfg(any(
  2081. target_os = "linux",
  2082. target_os = "dragonfly",
  2083. target_os = "freebsd",
  2084. target_os = "netbsd",
  2085. target_os = "openbsd"
  2086. ))]
  2087. {
  2088. use wry::webview::WebviewExtUnix;
  2089. f(w.webview());
  2090. }
  2091. #[cfg(target_os = "macos")]
  2092. {
  2093. use wry::webview::WebviewExtMacOS;
  2094. f(Webview {
  2095. webview: w.webview(),
  2096. manager: w.manager(),
  2097. ns_window: w.ns_window(),
  2098. });
  2099. }
  2100. #[cfg(target_os = "ios")]
  2101. {
  2102. use wry::webview::WebviewExtIOS;
  2103. f(Webview {
  2104. webview: w.webview(),
  2105. manager: w.manager(),
  2106. });
  2107. }
  2108. #[cfg(windows)]
  2109. {
  2110. f(Webview {
  2111. controller: w.controller(),
  2112. });
  2113. }
  2114. #[cfg(target_os = "android")]
  2115. {
  2116. f(w.handle())
  2117. }
  2118. }
  2119. }
  2120. WindowMessage::AddEventListener(id, listener) => {
  2121. window_event_listeners.lock().unwrap().insert(id, listener);
  2122. }
  2123. WindowMessage::AddMenuEventListener(id, listener) => {
  2124. menu_event_listeners.lock().unwrap().insert(id, listener);
  2125. }
  2126. #[cfg(any(debug_assertions, feature = "devtools"))]
  2127. WindowMessage::OpenDevTools => {
  2128. if let WindowHandle::Webview { inner: w, .. } = &window {
  2129. w.open_devtools();
  2130. }
  2131. }
  2132. #[cfg(any(debug_assertions, feature = "devtools"))]
  2133. WindowMessage::CloseDevTools => {
  2134. if let WindowHandle::Webview { inner: w, .. } = &window {
  2135. w.close_devtools();
  2136. }
  2137. }
  2138. #[cfg(any(debug_assertions, feature = "devtools"))]
  2139. WindowMessage::IsDevToolsOpen(tx) => {
  2140. if let WindowHandle::Webview { inner: w, .. } = &window {
  2141. tx.send(w.is_devtools_open()).unwrap();
  2142. } else {
  2143. tx.send(false).unwrap();
  2144. }
  2145. }
  2146. // Getters
  2147. WindowMessage::Url(tx) => {
  2148. if let WindowHandle::Webview { inner: w, .. } = &window {
  2149. tx.send(w.url()).unwrap();
  2150. }
  2151. }
  2152. WindowMessage::ScaleFactor(tx) => tx.send(window.scale_factor()).unwrap(),
  2153. WindowMessage::InnerPosition(tx) => tx
  2154. .send(
  2155. window
  2156. .inner_position()
  2157. .map(|p| PhysicalPositionWrapper(p).into())
  2158. .map_err(|_| Error::FailedToSendMessage),
  2159. )
  2160. .unwrap(),
  2161. WindowMessage::OuterPosition(tx) => tx
  2162. .send(
  2163. window
  2164. .outer_position()
  2165. .map(|p| PhysicalPositionWrapper(p).into())
  2166. .map_err(|_| Error::FailedToSendMessage),
  2167. )
  2168. .unwrap(),
  2169. WindowMessage::InnerSize(tx) => tx
  2170. .send(PhysicalSizeWrapper(window.inner_size()).into())
  2171. .unwrap(),
  2172. WindowMessage::OuterSize(tx) => tx
  2173. .send(PhysicalSizeWrapper(window.outer_size()).into())
  2174. .unwrap(),
  2175. WindowMessage::IsFullscreen(tx) => tx.send(window.fullscreen().is_some()).unwrap(),
  2176. WindowMessage::IsMinimized(tx) => tx.send(window.is_minimized()).unwrap(),
  2177. WindowMessage::IsMaximized(tx) => tx.send(window.is_maximized()).unwrap(),
  2178. WindowMessage::IsDecorated(tx) => tx.send(window.is_decorated()).unwrap(),
  2179. WindowMessage::IsResizable(tx) => tx.send(window.is_resizable()).unwrap(),
  2180. WindowMessage::IsVisible(tx) => tx.send(window.is_visible()).unwrap(),
  2181. WindowMessage::Title(tx) => tx.send(window.title()).unwrap(),
  2182. WindowMessage::IsMenuVisible(tx) => tx.send(window.is_menu_visible()).unwrap(),
  2183. WindowMessage::CurrentMonitor(tx) => tx.send(window.current_monitor()).unwrap(),
  2184. WindowMessage::PrimaryMonitor(tx) => tx.send(window.primary_monitor()).unwrap(),
  2185. WindowMessage::AvailableMonitors(tx) => {
  2186. tx.send(window.available_monitors().collect()).unwrap()
  2187. }
  2188. #[cfg(any(
  2189. target_os = "linux",
  2190. target_os = "dragonfly",
  2191. target_os = "freebsd",
  2192. target_os = "netbsd",
  2193. target_os = "openbsd"
  2194. ))]
  2195. WindowMessage::GtkWindow(tx) => {
  2196. tx.send(GtkWindow(window.gtk_window().clone())).unwrap()
  2197. }
  2198. WindowMessage::RawWindowHandle(tx) => tx
  2199. .send(RawWindowHandle(window.raw_window_handle()))
  2200. .unwrap(),
  2201. WindowMessage::Theme(tx) => {
  2202. tx.send(map_theme(&window.theme())).unwrap();
  2203. }
  2204. // Setters
  2205. WindowMessage::Center => {
  2206. let _ = center_window(&window, window.inner_size());
  2207. }
  2208. WindowMessage::RequestUserAttention(request_type) => {
  2209. window.request_user_attention(request_type.map(|r| r.0));
  2210. }
  2211. WindowMessage::SetResizable(resizable) => window.set_resizable(resizable),
  2212. WindowMessage::SetTitle(title) => window.set_title(&title),
  2213. WindowMessage::Maximize => window.set_maximized(true),
  2214. WindowMessage::Unmaximize => window.set_maximized(false),
  2215. WindowMessage::Minimize => window.set_minimized(true),
  2216. WindowMessage::Unminimize => window.set_minimized(false),
  2217. WindowMessage::ShowMenu => window.show_menu(),
  2218. WindowMessage::HideMenu => window.hide_menu(),
  2219. WindowMessage::Show => window.set_visible(true),
  2220. WindowMessage::Hide => window.set_visible(false),
  2221. WindowMessage::Close => {
  2222. panic!("cannot handle `WindowMessage::Close` on the main thread")
  2223. }
  2224. WindowMessage::SetDecorations(decorations) => window.set_decorations(decorations),
  2225. WindowMessage::SetShadow(_enable) => {
  2226. #[cfg(windows)]
  2227. window.set_undecorated_shadow(_enable);
  2228. #[cfg(target_os = "macos")]
  2229. window.set_has_shadow(_enable);
  2230. }
  2231. WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top),
  2232. WindowMessage::SetContentProtected(protected) => {
  2233. window.set_content_protection(protected)
  2234. }
  2235. WindowMessage::SetSize(size) => {
  2236. window.set_inner_size(SizeWrapper::from(size).0);
  2237. }
  2238. WindowMessage::SetMinSize(size) => {
  2239. window.set_min_inner_size(size.map(|s| SizeWrapper::from(s).0));
  2240. }
  2241. WindowMessage::SetMaxSize(size) => {
  2242. window.set_max_inner_size(size.map(|s| SizeWrapper::from(s).0));
  2243. }
  2244. WindowMessage::SetPosition(position) => {
  2245. window.set_outer_position(PositionWrapper::from(position).0)
  2246. }
  2247. WindowMessage::SetFullscreen(fullscreen) => {
  2248. if fullscreen {
  2249. window.set_fullscreen(Some(Fullscreen::Borderless(None)))
  2250. } else {
  2251. window.set_fullscreen(None)
  2252. }
  2253. }
  2254. WindowMessage::SetFocus => {
  2255. window.set_focus();
  2256. }
  2257. WindowMessage::SetIcon(icon) => {
  2258. window.set_window_icon(Some(icon));
  2259. }
  2260. #[allow(unused_variables)]
  2261. WindowMessage::SetSkipTaskbar(skip) => {
  2262. #[cfg(any(windows, target_os = "linux"))]
  2263. window.set_skip_taskbar(skip);
  2264. }
  2265. WindowMessage::SetCursorGrab(grab) => {
  2266. let _ = window.set_cursor_grab(grab);
  2267. }
  2268. WindowMessage::SetCursorVisible(visible) => {
  2269. window.set_cursor_visible(visible);
  2270. }
  2271. WindowMessage::SetCursorIcon(icon) => {
  2272. window.set_cursor_icon(CursorIconWrapper::from(icon).0);
  2273. }
  2274. WindowMessage::SetCursorPosition(position) => {
  2275. let _ = window.set_cursor_position(PositionWrapper::from(position).0);
  2276. }
  2277. WindowMessage::SetIgnoreCursorEvents(ignore) => {
  2278. let _ = window.set_ignore_cursor_events(ignore);
  2279. }
  2280. WindowMessage::DragWindow => {
  2281. let _ = window.drag_window();
  2282. }
  2283. WindowMessage::UpdateMenuItem(_id, _update) => {
  2284. // already handled
  2285. }
  2286. WindowMessage::RequestRedraw => {
  2287. window.request_redraw();
  2288. }
  2289. }
  2290. }
  2291. }
  2292. }
  2293. Message::Webview(id, webview_message) => match webview_message {
  2294. WebviewMessage::EvaluateScript(script) => {
  2295. if let Some(WindowHandle::Webview { inner: webview, .. }) =
  2296. windows.borrow().get(&id).and_then(|w| w.inner.as_ref())
  2297. {
  2298. if let Err(e) = webview.evaluate_script(&script) {
  2299. debug_eprintln!("{}", e);
  2300. }
  2301. }
  2302. }
  2303. WebviewMessage::Print => {
  2304. if let Some(WindowHandle::Webview { inner: webview, .. }) =
  2305. windows.borrow().get(&id).and_then(|w| w.inner.as_ref())
  2306. {
  2307. let _ = webview.print();
  2308. }
  2309. }
  2310. WebviewMessage::WebviewEvent(event) => {
  2311. let window_event_listeners = windows
  2312. .borrow()
  2313. .get(&id)
  2314. .map(|w| w.window_event_listeners.clone());
  2315. if let Some(window_event_listeners) = window_event_listeners {
  2316. if let Some(event) = WindowEventWrapper::from(&event).0 {
  2317. let listeners = window_event_listeners.lock().unwrap();
  2318. let handlers = listeners.values();
  2319. for handler in handlers {
  2320. handler(&event);
  2321. }
  2322. }
  2323. }
  2324. }
  2325. },
  2326. Message::CreateWebview(window_id, handler) => match handler(event_loop, web_context) {
  2327. Ok(webview) => {
  2328. windows.borrow_mut().insert(window_id, webview);
  2329. }
  2330. Err(e) => {
  2331. debug_eprintln!("{}", e);
  2332. }
  2333. },
  2334. Message::CreateWindow(window_id, handler, sender) => {
  2335. let (label, builder) = handler();
  2336. if let Ok(window) = builder.build(event_loop) {
  2337. webview_id_map.insert(window.id(), window_id);
  2338. let w = Arc::new(window);
  2339. windows.borrow_mut().insert(
  2340. window_id,
  2341. WindowWrapper {
  2342. label,
  2343. inner: Some(WindowHandle::Window(w.clone())),
  2344. menu_items: Default::default(),
  2345. window_event_listeners: Default::default(),
  2346. menu_event_listeners: Default::default(),
  2347. },
  2348. );
  2349. sender.send(Ok(Arc::downgrade(&w))).unwrap();
  2350. } else {
  2351. sender.send(Err(Error::CreateWindow)).unwrap();
  2352. }
  2353. }
  2354. #[cfg(all(desktop, feature = "system-tray"))]
  2355. Message::Tray(tray_id, tray_message) => {
  2356. let mut trays = system_tray_manager.trays.lock().unwrap();
  2357. if let TrayMessage::Create(tray, tx) = tray_message {
  2358. match create_tray(WryTrayId(tray_id), tray, event_loop) {
  2359. Ok((tray, items)) => {
  2360. trays.insert(
  2361. tray_id,
  2362. TrayContext {
  2363. tray: Arc::new(Mutex::new(Some(tray))),
  2364. listeners: Default::default(),
  2365. items: Arc::new(Mutex::new(items)),
  2366. },
  2367. );
  2368. tx.send(Ok(())).unwrap();
  2369. }
  2370. Err(e) => {
  2371. tx.send(Err(e)).unwrap();
  2372. }
  2373. }
  2374. } else if let Some(tray_context) = trays.get(&tray_id) {
  2375. match tray_message {
  2376. TrayMessage::UpdateItem(menu_id, update) => {
  2377. let mut tray = tray_context.items.as_ref().lock().unwrap();
  2378. let item = tray.get_mut(&menu_id).expect("menu item not found");
  2379. match update {
  2380. MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
  2381. MenuUpdate::SetTitle(title) => item.set_title(&title),
  2382. MenuUpdate::SetSelected(selected) => item.set_selected(selected),
  2383. #[cfg(target_os = "macos")]
  2384. MenuUpdate::SetNativeImage(image) => {
  2385. item.set_native_image(NativeImageWrapper::from(image).0)
  2386. }
  2387. }
  2388. }
  2389. TrayMessage::UpdateMenu(menu) => {
  2390. if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
  2391. let mut items = HashMap::new();
  2392. tray.set_menu(&to_wry_context_menu(&mut items, menu));
  2393. *tray_context.items.lock().unwrap() = items;
  2394. }
  2395. }
  2396. TrayMessage::UpdateIcon(icon) => {
  2397. if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
  2398. if let Ok(icon) = TrayIcon::try_from(icon) {
  2399. tray.set_icon(icon.0);
  2400. }
  2401. }
  2402. }
  2403. #[cfg(target_os = "macos")]
  2404. TrayMessage::UpdateIconAsTemplate(is_template) => {
  2405. if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
  2406. tray.set_icon_as_template(is_template);
  2407. }
  2408. }
  2409. #[cfg(target_os = "macos")]
  2410. TrayMessage::UpdateTitle(title) => {
  2411. if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
  2412. tray.set_title(&title);
  2413. }
  2414. }
  2415. TrayMessage::UpdateTooltip(tooltip) => {
  2416. if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
  2417. tray.set_tooltip(&tooltip);
  2418. }
  2419. }
  2420. TrayMessage::Create(_tray, _tx) => {
  2421. // already handled
  2422. }
  2423. TrayMessage::Destroy(tx) => {
  2424. *tray_context.tray.lock().unwrap() = None;
  2425. tray_context.listeners.lock().unwrap().clear();
  2426. tray_context.items.lock().unwrap().clear();
  2427. tx.send(Ok(())).unwrap();
  2428. }
  2429. }
  2430. }
  2431. }
  2432. #[cfg(all(desktop, feature = "global-shortcut"))]
  2433. Message::GlobalShortcut(message) => {
  2434. handle_global_shortcut_message(message, &global_shortcut_manager)
  2435. }
  2436. #[cfg(feature = "clipboard")]
  2437. Message::Clipboard(message) => handle_clipboard_message(message, &clipboard_manager),
  2438. Message::UserEvent(_) => (),
  2439. }
  2440. let it = RunIteration {
  2441. window_count: windows.borrow().len(),
  2442. };
  2443. it
  2444. }
  2445. fn handle_event_loop<T: UserEvent>(
  2446. event: Event<'_, Message<T>>,
  2447. event_loop: &EventLoopWindowTarget<Message<T>>,
  2448. control_flow: &mut ControlFlow,
  2449. context: EventLoopIterationContext<'_, T>,
  2450. web_context: &WebContextStore,
  2451. ) -> RunIteration {
  2452. let EventLoopIterationContext {
  2453. callback,
  2454. webview_id_map,
  2455. windows,
  2456. #[cfg(all(desktop, feature = "global-shortcut"))]
  2457. global_shortcut_manager,
  2458. #[cfg(all(desktop, feature = "global-shortcut"))]
  2459. global_shortcut_manager_handle,
  2460. #[cfg(feature = "clipboard")]
  2461. clipboard_manager,
  2462. #[cfg(all(desktop, feature = "system-tray"))]
  2463. system_tray_manager,
  2464. } = context;
  2465. if *control_flow != ControlFlow::Exit {
  2466. *control_flow = ControlFlow::Wait;
  2467. }
  2468. match event {
  2469. Event::NewEvents(StartCause::Init) => {
  2470. callback(RunEvent::Ready);
  2471. }
  2472. Event::NewEvents(StartCause::Poll) => {
  2473. callback(RunEvent::Resumed);
  2474. }
  2475. Event::MainEventsCleared => {
  2476. callback(RunEvent::MainEventsCleared);
  2477. }
  2478. Event::LoopDestroyed => {
  2479. callback(RunEvent::Exit);
  2480. }
  2481. #[cfg(all(desktop, feature = "global-shortcut"))]
  2482. Event::GlobalShortcutEvent(accelerator_id) => {
  2483. for (id, handler) in &*global_shortcut_manager_handle.listeners.lock().unwrap() {
  2484. if accelerator_id == *id {
  2485. handler();
  2486. }
  2487. }
  2488. }
  2489. Event::MenuEvent {
  2490. window_id,
  2491. menu_id,
  2492. origin: MenuType::MenuBar,
  2493. ..
  2494. } => {
  2495. #[allow(unused_mut)]
  2496. let mut window_id = window_id.unwrap(); // always Some on MenuBar event
  2497. #[cfg(target_os = "macos")]
  2498. {
  2499. // safety: we're only checking to see if the window_id is 0
  2500. // which is the value sent by macOS when the window is minimized (NSApplication::sharedApplication::mainWindow is null)
  2501. if window_id == unsafe { WindowId::dummy() } {
  2502. window_id = *webview_id_map.0.lock().unwrap().keys().next().unwrap();
  2503. }
  2504. }
  2505. let event = MenuEvent {
  2506. menu_item_id: menu_id.0,
  2507. };
  2508. let window_menu_event_listeners = {
  2509. // on macOS the window id might be the inspector window if it is detached
  2510. let window_id = if let Some(window_id) = webview_id_map.get(&window_id) {
  2511. window_id
  2512. } else {
  2513. *webview_id_map.0.lock().unwrap().values().next().unwrap()
  2514. };
  2515. windows
  2516. .borrow()
  2517. .get(&window_id)
  2518. .unwrap()
  2519. .menu_event_listeners
  2520. .clone()
  2521. };
  2522. let listeners = window_menu_event_listeners.lock().unwrap();
  2523. let handlers = listeners.values();
  2524. for handler in handlers {
  2525. handler(&event);
  2526. }
  2527. }
  2528. #[cfg(all(desktop, feature = "system-tray"))]
  2529. Event::MenuEvent {
  2530. window_id: _,
  2531. menu_id,
  2532. origin: MenuType::ContextMenu,
  2533. ..
  2534. } => {
  2535. let event = SystemTrayEvent::MenuItemClick(menu_id.0);
  2536. let trays = system_tray_manager.trays.lock().unwrap();
  2537. let trays_iter = trays.iter();
  2538. let (mut listeners, mut tray_id) = (None, 0);
  2539. for (id, tray_context) in trays_iter {
  2540. let has_menu = {
  2541. let items = tray_context.items.lock().unwrap();
  2542. items.contains_key(&menu_id.0)
  2543. };
  2544. if has_menu {
  2545. listeners.replace(tray_context.listeners.lock().unwrap().clone());
  2546. tray_id = *id;
  2547. break;
  2548. }
  2549. }
  2550. drop(trays);
  2551. if let Some(listeners) = listeners {
  2552. let handlers = listeners.iter();
  2553. for handler in handlers {
  2554. handler(&event);
  2555. }
  2556. let global_listeners = system_tray_manager.global_listeners.lock().unwrap();
  2557. let global_listeners_iter = global_listeners.iter();
  2558. for global_listener in global_listeners_iter {
  2559. global_listener(tray_id, &event);
  2560. }
  2561. }
  2562. }
  2563. #[cfg(all(desktop, feature = "system-tray"))]
  2564. Event::TrayEvent {
  2565. id,
  2566. bounds,
  2567. event,
  2568. position: _cursor_position,
  2569. ..
  2570. } => {
  2571. let (position, size) = (
  2572. PhysicalPositionWrapper(bounds.position).into(),
  2573. PhysicalSizeWrapper(bounds.size).into(),
  2574. );
  2575. let event = match event {
  2576. TrayEvent::RightClick => SystemTrayEvent::RightClick { position, size },
  2577. TrayEvent::DoubleClick => SystemTrayEvent::DoubleClick { position, size },
  2578. // default to left click
  2579. _ => SystemTrayEvent::LeftClick { position, size },
  2580. };
  2581. let trays = system_tray_manager.trays.lock().unwrap();
  2582. if let Some(tray_context) = trays.get(&id.0) {
  2583. let listeners = tray_context.listeners.lock().unwrap();
  2584. let iter = listeners.iter();
  2585. for handler in iter {
  2586. handler(&event);
  2587. }
  2588. }
  2589. let global_listeners = system_tray_manager.global_listeners.lock().unwrap();
  2590. let global_listeners_iter = global_listeners.iter();
  2591. for global_listener in global_listeners_iter {
  2592. global_listener(id.0, &event);
  2593. }
  2594. }
  2595. Event::WindowEvent {
  2596. event, window_id, ..
  2597. } => {
  2598. if let Some(window_id) = webview_id_map.get(&window_id) {
  2599. {
  2600. let windows_ref = windows.borrow();
  2601. if let Some(window) = windows_ref.get(&window_id) {
  2602. if let Some(event) = WindowEventWrapper::parse(&window.inner, &event).0 {
  2603. let label = window.label.clone();
  2604. let window_event_listeners = window.window_event_listeners.clone();
  2605. drop(windows_ref);
  2606. callback(RunEvent::WindowEvent {
  2607. label,
  2608. event: event.clone(),
  2609. });
  2610. let listeners = window_event_listeners.lock().unwrap();
  2611. let handlers = listeners.values();
  2612. for handler in handlers {
  2613. handler(&event);
  2614. }
  2615. }
  2616. }
  2617. }
  2618. match event {
  2619. #[cfg(windows)]
  2620. WryWindowEvent::ThemeChanged(theme) => {
  2621. if let Some(window) = windows.borrow().get(&window_id) {
  2622. if let Some(WindowHandle::Webview { inner, .. }) = &window.inner {
  2623. let theme = match theme {
  2624. WryTheme::Dark => wry::webview::Theme::Dark,
  2625. WryTheme::Light => wry::webview::Theme::Light,
  2626. _ => wry::webview::Theme::Light,
  2627. };
  2628. inner.set_theme(theme);
  2629. }
  2630. }
  2631. }
  2632. WryWindowEvent::CloseRequested => {
  2633. on_close_requested(callback, window_id, windows.clone());
  2634. }
  2635. WryWindowEvent::Destroyed => {
  2636. let removed = windows.borrow_mut().remove(&window_id).is_some();
  2637. if removed {
  2638. let is_empty = windows.borrow().is_empty();
  2639. if is_empty {
  2640. let (tx, rx) = channel();
  2641. callback(RunEvent::ExitRequested { tx });
  2642. let recv = rx.try_recv();
  2643. let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
  2644. if !should_prevent {
  2645. *control_flow = ControlFlow::Exit;
  2646. }
  2647. }
  2648. }
  2649. }
  2650. _ => {}
  2651. }
  2652. }
  2653. }
  2654. Event::UserEvent(message) => match message {
  2655. Message::Window(id, WindowMessage::Close) => {
  2656. on_window_close(id, windows.clone());
  2657. }
  2658. Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
  2659. message => {
  2660. return handle_user_message(
  2661. event_loop,
  2662. message,
  2663. UserMessageContext {
  2664. webview_id_map,
  2665. #[cfg(all(desktop, feature = "global-shortcut"))]
  2666. global_shortcut_manager,
  2667. #[cfg(feature = "clipboard")]
  2668. clipboard_manager,
  2669. windows,
  2670. #[cfg(all(desktop, feature = "system-tray"))]
  2671. system_tray_manager,
  2672. },
  2673. web_context,
  2674. );
  2675. }
  2676. },
  2677. _ => (),
  2678. }
  2679. let it = RunIteration {
  2680. window_count: windows.borrow().len(),
  2681. };
  2682. it
  2683. }
  2684. fn on_close_requested<'a, T: UserEvent>(
  2685. callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
  2686. window_id: WebviewId,
  2687. windows: Arc<RefCell<HashMap<WebviewId, WindowWrapper>>>,
  2688. ) {
  2689. let (tx, rx) = channel();
  2690. let windows_ref = windows.borrow();
  2691. if let Some(w) = windows_ref.get(&window_id) {
  2692. let label = w.label.clone();
  2693. let window_event_listeners = w.window_event_listeners.clone();
  2694. drop(windows_ref);
  2695. let listeners = window_event_listeners.lock().unwrap();
  2696. let handlers = listeners.values();
  2697. for handler in handlers {
  2698. handler(&WindowEvent::CloseRequested {
  2699. signal_tx: tx.clone(),
  2700. });
  2701. }
  2702. callback(RunEvent::WindowEvent {
  2703. label,
  2704. event: WindowEvent::CloseRequested { signal_tx: tx },
  2705. });
  2706. if let Ok(true) = rx.try_recv() {
  2707. } else {
  2708. on_window_close(window_id, windows);
  2709. }
  2710. }
  2711. }
  2712. fn on_window_close(window_id: WebviewId, windows: Arc<RefCell<HashMap<WebviewId, WindowWrapper>>>) {
  2713. if let Some(mut window_wrapper) = windows.borrow_mut().get_mut(&window_id) {
  2714. window_wrapper.inner = None;
  2715. }
  2716. }
  2717. pub fn center_window(window: &Window, window_size: WryPhysicalSize<u32>) -> Result<()> {
  2718. if let Some(monitor) = window.current_monitor() {
  2719. let screen_size = monitor.size();
  2720. let monitor_pos = monitor.position();
  2721. let x = (screen_size.width as i32 - window_size.width as i32) / 2;
  2722. let y = (screen_size.height as i32 - window_size.height as i32) / 2;
  2723. window.set_outer_position(WryPhysicalPosition::new(
  2724. monitor_pos.x + x,
  2725. monitor_pos.y + y,
  2726. ));
  2727. Ok(())
  2728. } else {
  2729. Err(Error::FailedToGetMonitor)
  2730. }
  2731. }
  2732. fn to_wry_menu(
  2733. custom_menu_items: &mut HashMap<MenuHash, WryCustomMenuItem>,
  2734. menu: Menu,
  2735. ) -> MenuBar {
  2736. let mut wry_menu = MenuBar::new();
  2737. for item in menu.items {
  2738. match item {
  2739. MenuEntry::CustomItem(c) => {
  2740. let mut attributes = MenuItemAttributesWrapper::from(&c).0;
  2741. attributes = attributes.with_id(WryMenuId(c.id));
  2742. #[allow(unused_mut)]
  2743. let mut item = wry_menu.add_item(attributes);
  2744. #[cfg(target_os = "macos")]
  2745. if let Some(native_image) = c.native_image {
  2746. item.set_native_image(NativeImageWrapper::from(native_image).0);
  2747. }
  2748. custom_menu_items.insert(c.id, item);
  2749. }
  2750. MenuEntry::NativeItem(i) => {
  2751. wry_menu.add_native_item(MenuItemWrapper::from(i).0);
  2752. }
  2753. MenuEntry::Submenu(submenu) => {
  2754. wry_menu.add_submenu(
  2755. &submenu.title,
  2756. submenu.enabled,
  2757. to_wry_menu(custom_menu_items, submenu.inner),
  2758. );
  2759. }
  2760. }
  2761. }
  2762. wry_menu
  2763. }
  2764. fn create_webview<T: UserEvent>(
  2765. window_id: WebviewId,
  2766. event_loop: &EventLoopWindowTarget<Message<T>>,
  2767. web_context_store: &WebContextStore,
  2768. context: Context<T>,
  2769. pending: PendingWindow<T, Wry<T>>,
  2770. ) -> Result<WindowWrapper> {
  2771. #[allow(unused_mut)]
  2772. let PendingWindow {
  2773. webview_attributes,
  2774. uri_scheme_protocols,
  2775. mut window_builder,
  2776. ipc_handler,
  2777. label,
  2778. url,
  2779. menu_ids,
  2780. js_event_listeners,
  2781. #[cfg(target_os = "android")]
  2782. on_webview_created,
  2783. ..
  2784. } = pending;
  2785. let webview_id_map = context.webview_id_map.clone();
  2786. #[cfg(windows)]
  2787. let proxy = context.proxy.clone();
  2788. let window_event_listeners = WindowEventListeners::default();
  2789. #[cfg(windows)]
  2790. {
  2791. window_builder.inner = window_builder
  2792. .inner
  2793. .with_drag_and_drop(webview_attributes.file_drop_handler_enabled);
  2794. }
  2795. #[cfg(windows)]
  2796. let window_theme = window_builder.inner.window.preferred_theme;
  2797. #[cfg(target_os = "macos")]
  2798. {
  2799. if window_builder.tabbing_identifier.is_none()
  2800. || window_builder.inner.window.transparent
  2801. || !window_builder.inner.window.decorations
  2802. {
  2803. window_builder.inner = window_builder.inner.with_automatic_window_tabbing(false);
  2804. }
  2805. }
  2806. let is_window_transparent = window_builder.inner.window.transparent;
  2807. let menu_items = if let Some(menu) = window_builder.menu {
  2808. let mut menu_items = HashMap::new();
  2809. let menu = to_wry_menu(&mut menu_items, menu);
  2810. window_builder.inner = window_builder.inner.with_menu(menu);
  2811. Some(menu_items)
  2812. } else {
  2813. None
  2814. };
  2815. let window = window_builder.inner.build(event_loop).unwrap();
  2816. webview_id_map.insert(window.id(), window_id);
  2817. if window_builder.center {
  2818. let _ = center_window(&window, window.inner_size());
  2819. }
  2820. let mut webview_builder = WebViewBuilder::new(window)
  2821. .map_err(|e| Error::CreateWebview(Box::new(e)))?
  2822. .with_url(&url)
  2823. .unwrap() // safe to unwrap because we validate the URL beforehand
  2824. .with_transparent(is_window_transparent)
  2825. .with_accept_first_mouse(webview_attributes.accept_first_mouse);
  2826. if webview_attributes.file_drop_handler_enabled {
  2827. webview_builder = webview_builder
  2828. .with_file_drop_handler(create_file_drop_handler(window_event_listeners.clone()));
  2829. }
  2830. if let Some(navigation_handler) = pending.navigation_handler {
  2831. webview_builder = webview_builder.with_navigation_handler(move |url| {
  2832. Url::parse(&url).map(&navigation_handler).unwrap_or(true)
  2833. });
  2834. }
  2835. if let Some(user_agent) = webview_attributes.user_agent {
  2836. webview_builder = webview_builder.with_user_agent(&user_agent);
  2837. }
  2838. #[cfg(windows)]
  2839. if let Some(additional_browser_args) = webview_attributes.additional_browser_args {
  2840. webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args);
  2841. }
  2842. #[cfg(windows)]
  2843. if let Some(theme) = window_theme {
  2844. webview_builder = webview_builder.with_theme(match theme {
  2845. WryTheme::Dark => wry::webview::Theme::Dark,
  2846. WryTheme::Light => wry::webview::Theme::Light,
  2847. _ => wry::webview::Theme::Light,
  2848. });
  2849. }
  2850. if let Some(handler) = ipc_handler {
  2851. webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
  2852. context,
  2853. label.clone(),
  2854. menu_ids,
  2855. js_event_listeners,
  2856. handler,
  2857. ));
  2858. }
  2859. for (scheme, protocol) in uri_scheme_protocols {
  2860. webview_builder = webview_builder.with_custom_protocol(scheme, move |wry_request| {
  2861. protocol(&HttpRequestWrapper::from(wry_request).0)
  2862. .map(|tauri_response| HttpResponseWrapper::from(tauri_response).0)
  2863. .map_err(|_| wry::Error::InitScriptError)
  2864. });
  2865. }
  2866. for script in webview_attributes.initialization_scripts {
  2867. webview_builder = webview_builder.with_initialization_script(&script);
  2868. }
  2869. let mut web_context = web_context_store.lock().expect("poisoned WebContext store");
  2870. let is_first_context = web_context.is_empty();
  2871. let automation_enabled = std::env::var("TAURI_AUTOMATION").as_deref() == Ok("true");
  2872. let web_context_key = // force a unique WebContext when automation is false;
  2873. // the context must be stored on the HashMap because it must outlive the WebView on macOS
  2874. if automation_enabled {
  2875. webview_attributes.data_directory.clone()
  2876. } else {
  2877. // random unique key
  2878. Some(Uuid::new_v4().as_hyphenated().to_string().into())
  2879. };
  2880. let entry = web_context.entry(web_context_key.clone());
  2881. let web_context = match entry {
  2882. Occupied(occupied) => occupied.into_mut(),
  2883. Vacant(vacant) => {
  2884. let mut web_context = WebContext::new(webview_attributes.data_directory);
  2885. web_context.set_allows_automation(if automation_enabled {
  2886. is_first_context
  2887. } else {
  2888. false
  2889. });
  2890. vacant.insert(web_context)
  2891. }
  2892. };
  2893. if webview_attributes.clipboard {
  2894. webview_builder.webview.clipboard = true;
  2895. }
  2896. #[cfg(any(debug_assertions, feature = "devtools"))]
  2897. {
  2898. webview_builder = webview_builder.with_devtools(true);
  2899. }
  2900. #[cfg(target_os = "android")]
  2901. {
  2902. if let Some(on_webview_created) = on_webview_created {
  2903. webview_builder = webview_builder.on_webview_created(move |ctx| {
  2904. on_webview_created(tauri_runtime::window::CreationContext {
  2905. env: ctx.env,
  2906. activity: ctx.activity,
  2907. webview: ctx.webview,
  2908. })
  2909. });
  2910. }
  2911. }
  2912. let webview = webview_builder
  2913. .with_web_context(web_context)
  2914. .build()
  2915. .map_err(|e| Error::CreateWebview(Box::new(e)))?;
  2916. #[cfg(windows)]
  2917. {
  2918. let controller = webview.controller();
  2919. let proxy_ = proxy.clone();
  2920. let mut token = EventRegistrationToken::default();
  2921. unsafe {
  2922. controller.add_GotFocus(
  2923. &FocusChangedEventHandler::create(Box::new(move |_, _| {
  2924. let _ = proxy_.send_event(Message::Webview(
  2925. window_id,
  2926. WebviewMessage::WebviewEvent(WebviewEvent::Focused(true)),
  2927. ));
  2928. Ok(())
  2929. })),
  2930. &mut token,
  2931. )
  2932. }
  2933. .unwrap();
  2934. unsafe {
  2935. controller.add_LostFocus(
  2936. &FocusChangedEventHandler::create(Box::new(move |_, _| {
  2937. let _ = proxy.send_event(Message::Webview(
  2938. window_id,
  2939. WebviewMessage::WebviewEvent(WebviewEvent::Focused(false)),
  2940. ));
  2941. Ok(())
  2942. })),
  2943. &mut token,
  2944. )
  2945. }
  2946. .unwrap();
  2947. }
  2948. Ok(WindowWrapper {
  2949. label,
  2950. inner: Some(WindowHandle::Webview {
  2951. inner: Arc::new(webview),
  2952. context_store: web_context_store.clone(),
  2953. context_key: if automation_enabled {
  2954. None
  2955. } else {
  2956. web_context_key
  2957. },
  2958. }),
  2959. menu_items,
  2960. window_event_listeners,
  2961. menu_event_listeners: Default::default(),
  2962. })
  2963. }
  2964. /// Create a wry ipc handler from a tauri ipc handler.
  2965. fn create_ipc_handler<T: UserEvent>(
  2966. context: Context<T>,
  2967. label: String,
  2968. menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
  2969. js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
  2970. handler: WebviewIpcHandler<T, Wry<T>>,
  2971. ) -> Box<IpcHandler> {
  2972. Box::new(move |window, request| {
  2973. let window_id = context.webview_id_map.get(&window.id()).unwrap();
  2974. handler(
  2975. DetachedWindow {
  2976. dispatcher: WryDispatcher {
  2977. window_id,
  2978. context: context.clone(),
  2979. },
  2980. label: label.clone(),
  2981. menu_ids: menu_ids.clone(),
  2982. js_event_listeners: js_event_listeners.clone(),
  2983. },
  2984. request,
  2985. );
  2986. })
  2987. }
  2988. /// Create a wry file drop handler.
  2989. fn create_file_drop_handler(window_event_listeners: WindowEventListeners) -> Box<FileDropHandler> {
  2990. Box::new(move |_window, event| {
  2991. let event: FileDropEvent = FileDropEventWrapper(event).into();
  2992. let window_event = WindowEvent::FileDrop(event);
  2993. let listeners_map = window_event_listeners.lock().unwrap();
  2994. let has_listener = !listeners_map.is_empty();
  2995. let handlers = listeners_map.values();
  2996. for listener in handlers {
  2997. listener(&window_event);
  2998. }
  2999. // block the default OS action on file drop if we had a listener
  3000. has_listener
  3001. })
  3002. }