1#![cfg(windows)]
10
11use std::any::Any;
12use std::collections::HashMap;
13use std::sync::{Arc, Mutex, OnceLock};
14
15use lingxia_webview::{WebTag, WebViewError};
16use windows::Win32::Foundation::{HWND, RECT};
17use windows::Win32::Graphics::Gdi::HDC;
18
19type StdResult<T, E = WebViewError> = std::result::Result<T, E>;
20
21pub type HostWindowCreatedHandler = Arc<dyn Fn(isize) + Send + Sync>;
22pub type CloseHandler = Arc<dyn Fn() + Send + Sync>;
23pub type ChromeEventHandler = Arc<dyn Fn(WindowsChromeCommand) + Send + Sync>;
24pub type WebViewVisibilityHandler = Arc<dyn Fn(&WebTag, bool) + Send + Sync>;
25pub type WindowsHostPanelInputHandler = Arc<dyn Fn(WindowsHostPanelKeyEvent) -> bool + Send + Sync>;
26
27static DEFAULT_WINDOW_SIZE: OnceLock<(i32, i32)> = OnceLock::new();
28static BACKEND: OnceLock<Arc<dyn WindowsHostBackend>> = OnceLock::new();
29static CLOSE_HANDLERS: OnceLock<Mutex<HashMap<String, CloseHandler>>> = OnceLock::new();
30static CHROME_HANDLERS: OnceLock<Mutex<HashMap<String, ChromeEventHandler>>> = OnceLock::new();
31static VISIBILITY_HANDLER: OnceLock<Mutex<Option<WebViewVisibilityHandler>>> = OnceLock::new();
32static HOST_WINDOW_CREATED_HANDLERS: OnceLock<Mutex<Vec<HostWindowCreatedHandler>>> =
33 OnceLock::new();
34static HOST_PANEL_INPUT_HANDLERS: OnceLock<Mutex<HashMap<String, WindowsHostPanelInputHandler>>> =
35 OnceLock::new();
36static WINDOW_LAYOUTS: OnceLock<Mutex<HashMap<String, WindowsWindowLayout>>> = OnceLock::new();
37static WINDOWS_CHROME_RENDERER: OnceLock<Mutex<Option<Arc<dyn WindowsChromeRenderer>>>> =
38 OnceLock::new();
39static ASIDE_PANEL_TABS: OnceLock<Mutex<HashMap<String, Vec<WindowsAsidePanelTab>>>> =
40 OnceLock::new();
41static ASIDE_PANEL_EVENT_HANDLER: OnceLock<Mutex<Option<WindowsAsidePanelEventHandler>>> =
42 OnceLock::new();
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct WindowsAsidePanelTab {
47 pub surface_id: String,
48 pub title: String,
49 pub active: bool,
50}
51
52#[derive(Debug, Clone)]
55pub enum WindowsAsidePanelEvent {
56 TabClick {
57 panel_id: String,
58 surface_id: String,
59 },
60 TabClose {
61 panel_id: String,
62 surface_id: String,
63 },
64 Collapse {
66 panel_id: String,
67 },
68 NavBack {
69 panel_id: String,
70 },
71 NavForward {
72 panel_id: String,
73 },
74 NavReload {
75 panel_id: String,
76 },
77}
78
79pub type WindowsAsidePanelEventHandler = Arc<dyn Fn(WindowsAsidePanelEvent) + Send + Sync>;
80
81pub const ASIDE_BROWSER_PANEL_ID: &str = "lx.aside-browser";
83pub const ASIDE_LXAPP_PANEL_ID: &str = "lx.aside-lxapp";
85
86pub fn set_aside_panel_tabs(panel_id: &str, tabs: Vec<WindowsAsidePanelTab>) {
89 let registry = ASIDE_PANEL_TABS.get_or_init(|| Mutex::new(HashMap::new()));
90 if let Ok(mut registry) = registry.lock() {
91 if tabs.is_empty() {
92 registry.remove(panel_id);
93 } else {
94 registry.insert(panel_id.to_string(), tabs);
95 }
96 }
97}
98
99pub fn aside_panel_tabs(panel_id: &str) -> Vec<WindowsAsidePanelTab> {
100 ASIDE_PANEL_TABS
101 .get()
102 .and_then(|registry| registry.lock().ok())
103 .and_then(|registry| registry.get(panel_id).cloned())
104 .unwrap_or_default()
105}
106
107pub fn set_windows_aside_panel_event_handler(handler: WindowsAsidePanelEventHandler) {
108 let slot = ASIDE_PANEL_EVENT_HANDLER.get_or_init(|| Mutex::new(None));
109 if let Ok(mut slot) = slot.lock() {
110 *slot = Some(handler);
111 }
112}
113
114pub fn dispatch_windows_aside_panel_event(event: WindowsAsidePanelEvent) -> bool {
117 let handler = ASIDE_PANEL_EVENT_HANDLER
118 .get()
119 .and_then(|slot| slot.lock().ok())
120 .and_then(|slot| slot.clone());
121 let Some(handler) = handler else {
122 return false;
123 };
124 handler(event);
125 true
126}
127
128fn unsupported_operation<T>(operation: &str) -> StdResult<T> {
129 Err(WebViewError::WebView(format!(
130 "Windows host backend does not support {operation}"
131 )))
132}
133
134pub trait WindowsHostBackend: Send + Sync {
142 fn show_webview_as_panel(
143 &self,
144 _webtag: &WebTag,
145 _title: &str,
146 _panel_id: &str,
147 ) -> StdResult<()> {
148 unsupported_operation("show_webview_as_panel")
149 }
150
151 fn show_webview_as_adaptive_panel(
152 &self,
153 _webtag: &WebTag,
154 _title: &str,
155 _panel_id: &str,
156 _position: WindowsPanelPosition,
157 _preferred_size: Option<i32>,
158 ) -> StdResult<()> {
159 unsupported_operation("show_webview_as_adaptive_panel")
160 }
161
162 fn show_webview_as_overlay_panel(
163 &self,
164 _webtag: &WebTag,
165 _title: &str,
166 _panel_id: &str,
167 _position: WindowsPanelPosition,
168 ) -> StdResult<()> {
169 unsupported_operation("show_webview_as_overlay_panel")
170 }
171
172 fn present_webview_in_active_group(&self, _webtag: &WebTag) -> StdResult<()> {
173 unsupported_operation("present_webview_in_active_group")
174 }
175
176 fn active_host_window_is_device_framed(&self) -> bool {
177 false
178 }
179
180 fn active_host_window_webtag_key(&self) -> Option<String> {
181 None
182 }
183
184 fn present_webview_as_group_main(&self, _webtag: &WebTag, _group_key: String) -> StdResult<()> {
185 unsupported_operation("present_webview_as_group_main")
186 }
187
188 fn present_webview_as_overlay(
189 &self,
190 _webtag: &WebTag,
191 _width: f64,
192 _height: f64,
193 _width_ratio: f64,
194 _height_ratio: f64,
195 _position: u8,
196 ) -> StdResult<()> {
197 unsupported_operation("present_webview_as_overlay")
198 }
199
200 fn configure_webview_surface_interaction(
201 &self,
202 _webtag: &WebTag,
203 _close_button: bool,
204 _dismiss_on_outside: bool,
205 _modal: bool,
206 ) -> StdResult<()> {
207 unsupported_operation("configure_webview_surface_interaction")
208 }
209
210 fn resize_host_window_content(
211 &self,
212 _webtag: &WebTag,
213 _width: i32,
214 _height: i32,
215 ) -> StdResult<()> {
216 unsupported_operation("resize_host_window_content")
217 }
218
219 fn restore_presented_group_main(&self) -> StdResult<()> {
220 unsupported_operation("restore_presented_group_main")
221 }
222
223 fn show_interactive_host_panel(
224 &self,
225 _panel_id: &str,
226 _title: &str,
227 _body: &str,
228 _position: WindowsPanelPosition,
229 ) -> StdResult<()> {
230 unsupported_operation("show_interactive_host_panel")
231 }
232
233 fn hide_host_panel(&self, _panel_id: &str) -> StdResult<()> {
234 unsupported_operation("hide_host_panel")
235 }
236
237 fn hide_exclusive_tray_popover(&self) -> bool {
239 false
240 }
241
242 fn show_exclusive_tray_popover(&self) -> bool {
244 false
245 }
246
247 fn update_host_panel_body(&self, _panel_id: &str, _body: &str) -> StdResult<()> {
248 unsupported_operation("update_host_panel_body")
249 }
250
251 fn set_host_panel_tabs(&self, _panel_id: &str, _tabs: Vec<WindowsHostPanelTab>) -> bool {
252 false
253 }
254
255 fn set_host_panel_maximized(&self, _panel_id: &str, _maximized: bool) -> bool {
256 false
257 }
258
259 fn invalidate_host_panel(&self, _panel_id: &str) -> bool {
260 false
261 }
262
263 fn is_panel_visible(&self, _panel_id: &str) -> bool {
264 false
265 }
266
267 fn find_webview_content_window(&self, _webtag: &WebTag) -> Option<WindowsWebViewContentWindow> {
268 None
269 }
270
271 fn webview_window_snapshot(&self, _webtag: &WebTag) -> StdResult<WindowsWebViewWindowSnapshot> {
272 unsupported_operation("webview_window_snapshot")
273 }
274
275 fn show_webview_window(
276 &self,
277 _webtag: &WebTag,
278 _title: &str,
279 _activate: bool,
280 ) -> StdResult<()> {
281 unsupported_operation("show_webview_window")
282 }
283
284 fn show_webview_window_with_content_size(
285 &self,
286 _webtag: &WebTag,
287 _title: &str,
288 _activate: bool,
289 _width: Option<i32>,
290 _height: Option<i32>,
291 ) -> StdResult<()> {
292 unsupported_operation("show_webview_window_with_content_size")
293 }
294
295 fn show_webview_window_with_chrome(
298 &self,
299 _webtag: &WebTag,
300 _title: &str,
301 _activate: bool,
302 _width: Option<i32>,
303 _height: Option<i32>,
304 _full_chrome: bool,
305 ) -> StdResult<()> {
306 unsupported_operation("show_webview_window_with_chrome")
307 }
308
309 fn navigate_webview_window(
310 &self,
311 _webtag: &WebTag,
312 _title: &str,
313 _activate: bool,
314 _animation: WindowsNavAnimation,
315 ) -> StdResult<()> {
316 unsupported_operation("navigate_webview_window")
317 }
318
319 fn hide_webview_window(&self, _webtag: &WebTag) -> StdResult<()> {
320 unsupported_operation("hide_webview_window")
321 }
322
323 fn request_host_window_layout(&self, _window: WindowsHostWindow) -> bool {
324 false
325 }
326
327 fn active_content_screen_rect(&self) -> Option<WindowsContentRect> {
328 None
329 }
330
331 fn post_to_window_thread(&self, _window: isize, _callback: Box<dyn FnOnce() + Send>) -> bool {
332 false
333 }
334
335 fn sync_webview_window_layout(&self, _webtag: &WebTag) {}
336
337 fn refresh_aside_panel(&self, _panel_id: &str) {}
340}
341
342pub fn refresh_aside_panel(panel_id: &str) {
343 if let Ok(backend) = backend() {
344 backend.refresh_aside_panel(panel_id);
345 }
346}
347
348pub fn set_windows_host_backend(backend: Arc<dyn WindowsHostBackend>) {
349 if BACKEND.set(backend).is_err() {
350 log::warn!("Windows host backend is already installed; ignoring replacement");
351 }
352}
353
354fn backend() -> StdResult<&'static Arc<dyn WindowsHostBackend>> {
355 BACKEND
356 .get()
357 .ok_or_else(|| WebViewError::WebView("Windows host backend is not installed".to_string()))
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub struct WindowsSurfaceCapture {
367 pub x: i32,
369 pub y: i32,
370 pub width: u32,
371 pub height: u32,
372 pub pixels: Vec<u8>,
374}
375
376type SurfaceCaptureProvider = fn(usize) -> Vec<WindowsSurfaceCapture>;
377
378static SURFACE_CAPTURE_PROVIDERS: OnceLock<Mutex<Vec<SurfaceCaptureProvider>>> = OnceLock::new();
379
380fn surface_capture_providers() -> &'static Mutex<Vec<SurfaceCaptureProvider>> {
381 SURFACE_CAPTURE_PROVIDERS.get_or_init(|| Mutex::new(Vec::new()))
382}
383
384pub fn register_surface_capture_provider(provider: SurfaceCaptureProvider) {
386 if let Ok(mut providers) = surface_capture_providers().lock() {
387 providers.push(provider);
388 }
389}
390
391pub fn surface_captures(window_id: usize) -> Vec<WindowsSurfaceCapture> {
393 let providers = match surface_capture_providers().lock() {
394 Ok(providers) => providers.clone(),
395 Err(_) => return Vec::new(),
396 };
397 providers
398 .into_iter()
399 .flat_map(|provider| provider(window_id))
400 .collect()
401}
402
403pub struct WindowsWebViewWindowSnapshot {
404 pub window_id: usize,
405 pub webtag_key: String,
406 pub visible: bool,
407 pub window_left: i32,
408 pub window_top: i32,
409 pub window_width: i32,
410 pub window_height: i32,
411 pub content_left: i32,
412 pub content_top: i32,
413 pub content_width: u32,
414 pub content_height: u32,
415 pub content_corner_radii: [i32; 4],
419}
420
421#[derive(Debug, Clone, Copy, PartialEq)]
422pub struct WindowsWebViewContentWindow {
423 pub window: isize,
424 pub content_left: i32,
425 pub content_top: i32,
426 pub content_width: i32,
427 pub content_height: i32,
428 pub scale: f64,
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
432pub enum WindowsPanelPosition {
433 Left,
434 #[default]
435 Right,
436 Top,
437 Bottom,
438}
439
440#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
446pub enum WindowsNavAnimation {
447 #[default]
448 None,
449 Forward,
450 Backward,
451}
452
453#[derive(Clone, Default)]
454pub struct WindowsWindowLayout {
455 payload: Option<Arc<dyn Any + Send + Sync>>,
456}
457
458impl std::fmt::Debug for WindowsWindowLayout {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 f.debug_struct("WindowsWindowLayout")
461 .field("has_payload", &self.payload.is_some())
462 .finish()
463 }
464}
465
466impl WindowsWindowLayout {
467 pub fn new<T>(payload: T) -> Self
468 where
469 T: Any + Send + Sync + 'static,
470 {
471 Self {
472 payload: Some(Arc::new(payload)),
473 }
474 }
475
476 pub fn empty() -> Self {
477 Self::default()
478 }
479
480 pub fn is_empty(&self) -> bool {
481 self.payload.is_none()
482 }
483
484 pub fn downcast_ref<T>(&self) -> Option<&T>
485 where
486 T: Any + 'static,
487 {
488 self.payload.as_deref()?.downcast_ref::<T>()
489 }
490}
491
492#[derive(Debug, Clone, PartialEq, Eq)]
493pub struct WindowsHostPanelTab {
494 pub id: u64,
495 pub title: String,
496 pub active: bool,
497}
498
499#[derive(Debug, Clone, PartialEq, Eq)]
500pub struct WindowsHostPanelContent {
501 pub title: Option<String>,
502 pub body: Option<String>,
503 pub tabs: Vec<WindowsHostPanelTab>,
504 pub maximized: bool,
505 pub show_maximize: bool,
509}
510
511#[derive(Debug, Clone, PartialEq)]
512pub struct WindowsChromePanel {
513 pub panel_id: String,
514 pub webtag_key: String,
515 pub title: String,
516 pub rect: RECT,
517 pub header_rect: Option<RECT>,
520 pub resize_handle: Option<RECT>,
523 pub host_content: Option<WindowsHostPanelContent>,
524 pub docked: bool,
525 pub overlay: bool,
527}
528
529#[derive(Debug, Clone, PartialEq, Eq)]
530pub struct WindowsChromePanelLayoutInput {
531 pub panel_id: String,
532 pub webtag_key: String,
533 pub position: WindowsPanelPosition,
534 pub requested_size: Option<i32>,
535 pub docked: bool,
536 pub overlay: bool,
538 pub maximized: bool,
539}
540
541#[derive(Debug, Clone, PartialEq)]
542pub struct WindowsChromePanelLayout {
543 pub panel_id: String,
544 pub webtag_key: String,
545 pub rect: RECT,
546 pub header_rect: Option<RECT>,
549 pub resize_handle: Option<RECT>,
550 pub overlay: bool,
552}
553
554#[derive(Debug, Clone, PartialEq)]
555pub struct WindowsChromeAttachedLayout {
556 pub main_region: RECT,
558 pub main: RECT,
560 pub panels: Vec<WindowsChromePanelLayout>,
561}
562
563#[derive(Debug, Clone, PartialEq)]
564pub struct WindowsChromeAttachedState {
565 pub main_region: RECT,
566 pub main: RECT,
567 pub panels: Vec<WindowsChromePanel>,
568}
569
570#[derive(Debug, Clone)]
571pub struct WindowsChromeState {
572 pub hwnd: HWND,
573 pub client: RECT,
574 pub layout: WindowsWindowLayout,
575 pub attached: Option<WindowsChromeAttachedState>,
576 pub frame_button_hover: Option<WindowsFrameButton>,
577 pub frame_button_pressed: Option<WindowsFrameButton>,
578 pub cursor: Option<(i32, i32)>,
581}
582
583#[derive(Debug, Clone, Copy, PartialEq, Eq)]
584pub enum WindowsFrameButton {
585 Minimize,
586 Maximize,
587 Close,
588}
589
590#[derive(Debug, Clone, PartialEq)]
591pub struct WindowsChromeCommand {
592 pub id: String,
593 pub payload: serde_json::Value,
594 pub focus: Option<String>,
595 pub double_click: Option<Box<WindowsChromeCommand>>,
596 pub include_screen_position: bool,
597}
598
599impl WindowsChromeCommand {
600 pub fn new(id: impl Into<String>) -> Self {
601 Self {
602 id: id.into(),
603 payload: serde_json::Value::Null,
604 focus: None,
605 double_click: None,
606 include_screen_position: false,
607 }
608 }
609
610 pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
611 self.payload = payload;
612 self
613 }
614
615 pub fn with_focus(mut self, surface_id: impl Into<String>) -> Self {
616 self.focus = Some(surface_id.into());
617 self
618 }
619
620 pub fn with_double_click(mut self, command: WindowsChromeCommand) -> Self {
621 self.double_click = Some(Box::new(command));
622 self
623 }
624
625 pub fn with_screen_position(mut self) -> Self {
626 self.include_screen_position = true;
627 self
628 }
629}
630
631#[derive(Debug, Clone, PartialEq)]
632pub enum WindowsChromeHit {
633 Caption,
634 FrameButton(WindowsFrameButton),
635 Focusable {
636 id: String,
637 context_menu: Option<WindowsChromeCommand>,
638 click_command: Option<WindowsChromeCommand>,
642 },
643 Command(WindowsChromeCommand),
644 CommandWithContext {
645 command: WindowsChromeCommand,
646 context_menu: WindowsChromeCommand,
647 },
648 Chrome,
649}
650
651pub trait WindowsChromeRenderer: Send + Sync {
652 fn content_rect(&self, client: RECT, layout: &WindowsWindowLayout) -> RECT;
653
654 fn attached_layout(
655 &self,
656 client: RECT,
657 layout: &WindowsWindowLayout,
658 panels: &[WindowsChromePanelLayoutInput],
659 ) -> Option<WindowsChromeAttachedLayout> {
660 let _ = (client, layout, panels);
661 None
662 }
663
664 fn paint(&self, hdc: HDC, state: &WindowsChromeState);
665
666 fn paint_region(&self, hdc: HDC, state: &WindowsChromeState, invalid: RECT) {
667 let _ = invalid;
668 self.paint(hdc, state);
669 }
670
671 fn hit_test(&self, state: &WindowsChromeState, point: (i32, i32)) -> Option<WindowsChromeHit>;
672
673 fn frame_button_rect(
674 &self,
675 state: &WindowsChromeState,
676 button: WindowsFrameButton,
677 ) -> Option<RECT> {
678 let _ = (state, button);
679 None
680 }
681
682 fn hover_rect(&self, state: &WindowsChromeState, point: (i32, i32)) -> Option<RECT> {
686 let _ = (state, point);
687 None
688 }
689
690 fn mouse_wheel(
693 &self,
694 state: &WindowsChromeState,
695 point: (i32, i32),
696 delta: i16,
697 ) -> Option<WindowsChromeCommand> {
698 let _ = (state, point, delta);
699 None
700 }
701}
702
703#[derive(Debug, Clone, Copy, PartialEq, Eq)]
704pub struct WindowsHostPanelKeyEvent {
705 pub vk: u32,
706 pub ctrl: bool,
707 pub shift: bool,
708 pub alt: bool,
709 pub character: Option<char>,
710}
711
712#[derive(Debug, Clone, Copy, PartialEq, Eq)]
713pub struct WindowsHostWindow {
714 pub window: isize,
715}
716
717#[derive(Debug, Clone, Copy, PartialEq, Eq)]
718pub struct WindowsContentRect {
719 pub host_window: isize,
720 pub left: i32,
721 pub top: i32,
722 pub width: i32,
723 pub height: i32,
724 pub dpi: u32,
725}
726
727pub fn set_default_window_size(width: i32, height: i32) {
728 if width > 0 && height > 0 {
729 let _ = DEFAULT_WINDOW_SIZE.set((width, height));
730 }
731}
732
733pub fn default_window_size() -> (i32, i32) {
734 DEFAULT_WINDOW_SIZE.get().copied().unwrap_or((1024, 768))
735}
736
737pub fn set_windows_chrome_renderer(renderer: Arc<dyn WindowsChromeRenderer>) {
738 let slot = WINDOWS_CHROME_RENDERER.get_or_init(|| Mutex::new(None));
739 if let Ok(mut slot) = slot.lock() {
740 *slot = Some(renderer);
741 }
742}
743
744pub fn windows_chrome_renderer() -> Option<Arc<dyn WindowsChromeRenderer>> {
745 WINDOWS_CHROME_RENDERER
746 .get()
747 .and_then(|renderer| renderer.lock().ok())
748 .and_then(|renderer| renderer.clone())
749}
750
751pub fn set_webview_close_handler(webtag: &WebTag, handler: CloseHandler) {
752 let handlers = CLOSE_HANDLERS.get_or_init(|| Mutex::new(HashMap::new()));
753 if let Ok(mut handlers) = handlers.lock() {
754 handlers.insert(webtag.key().to_string(), handler);
755 }
756}
757
758pub fn webview_close_handler(webtag_key: &str) -> Option<CloseHandler> {
759 CLOSE_HANDLERS
760 .get()
761 .and_then(|handlers| handlers.lock().ok())
762 .and_then(|handlers| handlers.get(webtag_key).cloned())
763}
764
765pub fn set_webview_visibility_handler(handler: WebViewVisibilityHandler) {
766 let slot = VISIBILITY_HANDLER.get_or_init(|| Mutex::new(None));
767 if let Ok(mut slot) = slot.lock() {
768 *slot = Some(handler);
769 }
770}
771
772pub fn webview_visibility_handler() -> Option<WebViewVisibilityHandler> {
773 VISIBILITY_HANDLER
774 .get()
775 .and_then(|slot| slot.lock().ok())
776 .and_then(|slot| slot.clone())
777}
778
779pub fn set_webview_chrome_event_handler(webtag: &WebTag, handler: ChromeEventHandler) {
780 let handlers = CHROME_HANDLERS.get_or_init(|| Mutex::new(HashMap::new()));
781 if let Ok(mut handlers) = handlers.lock() {
782 handlers.insert(webtag.key().to_string(), handler);
783 }
784}
785
786pub fn webview_chrome_event_handler(webtag_key: &str) -> Option<ChromeEventHandler> {
787 CHROME_HANDLERS
788 .get()
789 .and_then(|handlers| handlers.lock().ok())
790 .and_then(|handlers| handlers.get(webtag_key).cloned())
791}
792
793pub fn add_host_window_created_handler(handler: HostWindowCreatedHandler) {
794 let handlers = HOST_WINDOW_CREATED_HANDLERS.get_or_init(|| Mutex::new(Vec::new()));
795 if let Ok(mut handlers) = handlers.lock() {
796 handlers.push(handler);
797 }
798}
799
800pub fn host_window_created_handlers() -> Vec<HostWindowCreatedHandler> {
801 HOST_WINDOW_CREATED_HANDLERS
802 .get()
803 .and_then(|state| state.lock().ok())
804 .map(|state| state.clone())
805 .unwrap_or_default()
806}
807
808pub fn set_host_panel_input_handler(panel_id: &str, handler: WindowsHostPanelInputHandler) {
809 let handlers = HOST_PANEL_INPUT_HANDLERS.get_or_init(|| Mutex::new(HashMap::new()));
810 if let Ok(mut handlers) = handlers.lock() {
811 handlers.insert(panel_id.to_string(), handler);
812 }
813}
814
815pub fn clear_host_panel_input_handler(panel_id: &str) {
816 if let Some(handlers) = HOST_PANEL_INPUT_HANDLERS.get()
817 && let Ok(mut handlers) = handlers.lock()
818 {
819 handlers.remove(panel_id);
820 }
821}
822
823pub fn host_panel_input_handler(panel_id: &str) -> Option<WindowsHostPanelInputHandler> {
824 HOST_PANEL_INPUT_HANDLERS
825 .get()
826 .and_then(|handlers| handlers.lock().ok())
827 .and_then(|handlers| handlers.get(panel_id).cloned())
828}
829
830pub fn set_webview_window_layout(webtag: &WebTag, layout: WindowsWindowLayout) -> StdResult<()> {
831 let layouts = WINDOW_LAYOUTS.get_or_init(|| Mutex::new(HashMap::new()));
832 if let Ok(mut layouts) = layouts.lock() {
833 layouts.insert(webtag.key().to_string(), layout);
834 }
835 if let Ok(backend) = backend() {
836 backend.sync_webview_window_layout(webtag);
837 }
838 Ok(())
839}
840
841pub fn current_window_layout(webtag_key: &str) -> WindowsWindowLayout {
842 WINDOW_LAYOUTS
843 .get()
844 .and_then(|layouts| layouts.lock().ok())
845 .and_then(|layouts| layouts.get(webtag_key).cloned())
846 .unwrap_or_default()
847}
848
849pub fn cleanup_webview_state(webtag_key: &str) {
850 if let Some(handlers) = CLOSE_HANDLERS.get()
851 && let Ok(mut handlers) = handlers.lock()
852 {
853 handlers.remove(webtag_key);
854 }
855 if let Some(handlers) = CHROME_HANDLERS.get()
856 && let Ok(mut handlers) = handlers.lock()
857 {
858 handlers.remove(webtag_key);
859 }
860 if let Some(layouts) = WINDOW_LAYOUTS.get()
861 && let Ok(mut layouts) = layouts.lock()
862 {
863 layouts.remove(webtag_key);
864 }
865}
866
867pub fn show_webview_as_panel(webtag: &WebTag, title: &str, panel_id: &str) -> StdResult<()> {
868 backend()?.show_webview_as_panel(webtag, title, panel_id)
869}
870
871pub fn show_webview_as_adaptive_panel(
872 webtag: &WebTag,
873 title: &str,
874 panel_id: &str,
875 position: WindowsPanelPosition,
876 preferred_size: Option<i32>,
877) -> StdResult<()> {
878 backend()?.show_webview_as_adaptive_panel(webtag, title, panel_id, position, preferred_size)
879}
880
881pub fn show_webview_as_overlay_panel(
882 webtag: &WebTag,
883 title: &str,
884 panel_id: &str,
885 position: WindowsPanelPosition,
886) -> StdResult<()> {
887 backend()?.show_webview_as_overlay_panel(webtag, title, panel_id, position)
888}
889
890pub fn present_webview_in_active_group(webtag: &WebTag) -> StdResult<()> {
891 backend()?.present_webview_in_active_group(webtag)
892}
893
894pub fn active_host_window_is_device_framed() -> bool {
895 backend()
896 .map(|backend| backend.active_host_window_is_device_framed())
897 .unwrap_or(false)
898}
899
900pub fn active_host_window_webtag_key() -> Option<String> {
901 backend()
902 .ok()
903 .and_then(|backend| backend.active_host_window_webtag_key())
904}
905
906pub fn present_webview_as_group_main(webtag: &WebTag, group_key: String) -> StdResult<()> {
907 backend()?.present_webview_as_group_main(webtag, group_key)
908}
909
910pub fn present_webview_as_overlay(
911 webtag: &WebTag,
912 width: f64,
913 height: f64,
914 width_ratio: f64,
915 height_ratio: f64,
916 position: u8,
917) -> StdResult<()> {
918 backend()?.present_webview_as_overlay(
919 webtag,
920 width,
921 height,
922 width_ratio,
923 height_ratio,
924 position,
925 )
926}
927
928pub fn configure_webview_surface_interaction(
929 webtag: &WebTag,
930 close_button: bool,
931 dismiss_on_outside: bool,
932 modal: bool,
933) -> StdResult<()> {
934 backend()?.configure_webview_surface_interaction(
935 webtag,
936 close_button,
937 dismiss_on_outside,
938 modal,
939 )
940}
941
942pub fn resize_host_window_content(webtag: &WebTag, width: i32, height: i32) -> StdResult<()> {
943 backend()?.resize_host_window_content(webtag, width, height)
944}
945
946pub fn restore_presented_group_main() -> StdResult<()> {
947 backend()?.restore_presented_group_main()
948}
949
950pub fn show_interactive_host_panel(
951 panel_id: &str,
952 title: &str,
953 body: &str,
954 position: WindowsPanelPosition,
955) -> StdResult<()> {
956 backend()?.show_interactive_host_panel(panel_id, title, body, position)
957}
958
959pub fn hide_host_panel(panel_id: &str) -> StdResult<()> {
960 backend()?.hide_host_panel(panel_id)
961}
962
963pub fn hide_exclusive_tray_popover() -> bool {
964 backend()
965 .map(|backend| backend.hide_exclusive_tray_popover())
966 .unwrap_or(false)
967}
968
969pub fn show_exclusive_tray_popover() -> bool {
970 backend()
971 .map(|backend| backend.show_exclusive_tray_popover())
972 .unwrap_or(false)
973}
974
975pub fn update_host_panel_body(panel_id: &str, body: &str) -> StdResult<()> {
976 backend()?.update_host_panel_body(panel_id, body)
977}
978
979pub fn set_host_panel_tabs(panel_id: &str, tabs: Vec<WindowsHostPanelTab>) -> bool {
980 backend()
981 .map(|backend| backend.set_host_panel_tabs(panel_id, tabs))
982 .unwrap_or(false)
983}
984
985pub fn set_host_panel_maximized(panel_id: &str, maximized: bool) -> bool {
986 backend()
987 .map(|backend| backend.set_host_panel_maximized(panel_id, maximized))
988 .unwrap_or(false)
989}
990
991pub fn invalidate_host_panel(panel_id: &str) -> bool {
992 backend()
993 .map(|backend| backend.invalidate_host_panel(panel_id))
994 .unwrap_or(false)
995}
996
997pub fn is_panel_visible(panel_id: &str) -> bool {
998 backend()
999 .map(|backend| backend.is_panel_visible(panel_id))
1000 .unwrap_or(false)
1001}
1002
1003pub fn find_host_window_for_webview(webtag: &WebTag) -> StdResult<WindowsHostWindow> {
1004 let content = find_webview_content_window(webtag).ok_or_else(|| {
1005 WebViewError::WebView(format!("no window registered for {}", webtag.key()))
1006 })?;
1007 Ok(WindowsHostWindow {
1008 window: content.window,
1009 })
1010}
1011
1012pub fn request_host_window_layout(window: WindowsHostWindow) -> bool {
1013 backend()
1014 .map(|backend| backend.request_host_window_layout(window))
1015 .unwrap_or(false)
1016}
1017
1018pub fn active_content_screen_rect() -> Option<WindowsContentRect> {
1019 backend()
1020 .ok()
1021 .and_then(|backend| backend.active_content_screen_rect())
1022}
1023
1024pub fn find_webview_content_window(webtag: &WebTag) -> Option<WindowsWebViewContentWindow> {
1025 backend()
1026 .ok()
1027 .and_then(|backend| backend.find_webview_content_window(webtag))
1028}
1029
1030pub fn webview_window_snapshot(webtag: &WebTag) -> StdResult<WindowsWebViewWindowSnapshot> {
1031 backend()?.webview_window_snapshot(webtag)
1032}
1033
1034pub fn show_webview_window(webtag: &WebTag, title: &str, activate: bool) -> StdResult<()> {
1035 backend()?.show_webview_window(webtag, title, activate)
1036}
1037
1038pub fn show_webview_window_with_content_size(
1039 webtag: &WebTag,
1040 title: &str,
1041 activate: bool,
1042 width: Option<i32>,
1043 height: Option<i32>,
1044) -> StdResult<()> {
1045 backend()?.show_webview_window_with_content_size(webtag, title, activate, width, height)
1046}
1047
1048pub fn show_webview_window_with_chrome(
1049 webtag: &WebTag,
1050 title: &str,
1051 activate: bool,
1052 width: Option<i32>,
1053 height: Option<i32>,
1054 full_chrome: bool,
1055) -> StdResult<()> {
1056 backend()?.show_webview_window_with_chrome(webtag, title, activate, width, height, full_chrome)
1057}
1058
1059pub fn navigate_webview_window(
1060 webtag: &WebTag,
1061 title: &str,
1062 activate: bool,
1063 animation: WindowsNavAnimation,
1064) -> StdResult<()> {
1065 backend()?.navigate_webview_window(webtag, title, activate, animation)
1066}
1067
1068pub fn hide_webview_window(webtag: &WebTag) -> StdResult<()> {
1069 backend()?.hide_webview_window(webtag)
1070}
1071
1072pub fn post_to_window_thread(window: isize, callback: Box<dyn FnOnce() + Send>) -> bool {
1073 backend()
1074 .map(|backend| backend.post_to_window_thread(window, callback))
1075 .unwrap_or(false)
1076}