lib.rs 98 KB

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