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 update_host_panel_body(&self, _panel_id: &str, _body: &str) -> StdResult<()> {
238 unsupported_operation("update_host_panel_body")
239 }
240
241 fn set_host_panel_tabs(&self, _panel_id: &str, _tabs: Vec<WindowsHostPanelTab>) -> bool {
242 false
243 }
244
245 fn set_host_panel_maximized(&self, _panel_id: &str, _maximized: bool) -> bool {
246 false
247 }
248
249 fn invalidate_host_panel(&self, _panel_id: &str) -> bool {
250 false
251 }
252
253 fn is_panel_visible(&self, _panel_id: &str) -> bool {
254 false
255 }
256
257 fn find_webview_content_window(&self, _webtag: &WebTag) -> Option<WindowsWebViewContentWindow> {
258 None
259 }
260
261 fn webview_window_snapshot(&self, _webtag: &WebTag) -> StdResult<WindowsWebViewWindowSnapshot> {
262 unsupported_operation("webview_window_snapshot")
263 }
264
265 fn show_webview_window(
266 &self,
267 _webtag: &WebTag,
268 _title: &str,
269 _activate: bool,
270 ) -> StdResult<()> {
271 unsupported_operation("show_webview_window")
272 }
273
274 fn show_webview_window_with_content_size(
275 &self,
276 _webtag: &WebTag,
277 _title: &str,
278 _activate: bool,
279 _width: Option<i32>,
280 _height: Option<i32>,
281 ) -> StdResult<()> {
282 unsupported_operation("show_webview_window_with_content_size")
283 }
284
285 fn show_webview_window_with_chrome(
288 &self,
289 _webtag: &WebTag,
290 _title: &str,
291 _activate: bool,
292 _width: Option<i32>,
293 _height: Option<i32>,
294 _full_chrome: bool,
295 ) -> StdResult<()> {
296 unsupported_operation("show_webview_window_with_chrome")
297 }
298
299 fn navigate_webview_window(
300 &self,
301 _webtag: &WebTag,
302 _title: &str,
303 _activate: bool,
304 _animation: WindowsNavAnimation,
305 ) -> StdResult<()> {
306 unsupported_operation("navigate_webview_window")
307 }
308
309 fn hide_webview_window(&self, _webtag: &WebTag) -> StdResult<()> {
310 unsupported_operation("hide_webview_window")
311 }
312
313 fn request_host_window_layout(&self, _window: WindowsHostWindow) -> bool {
314 false
315 }
316
317 fn active_content_screen_rect(&self) -> Option<WindowsContentRect> {
318 None
319 }
320
321 fn post_to_window_thread(&self, _window: isize, _callback: Box<dyn FnOnce() + Send>) -> bool {
322 false
323 }
324
325 fn sync_webview_window_layout(&self, _webtag: &WebTag) {}
326
327 fn refresh_aside_panel(&self, _panel_id: &str) {}
330}
331
332pub fn refresh_aside_panel(panel_id: &str) {
333 if let Ok(backend) = backend() {
334 backend.refresh_aside_panel(panel_id);
335 }
336}
337
338pub fn set_windows_host_backend(backend: Arc<dyn WindowsHostBackend>) {
339 if BACKEND.set(backend).is_err() {
340 log::warn!("Windows host backend is already installed; ignoring replacement");
341 }
342}
343
344fn backend() -> StdResult<&'static Arc<dyn WindowsHostBackend>> {
345 BACKEND
346 .get()
347 .ok_or_else(|| WebViewError::WebView("Windows host backend is not installed".to_string()))
348}
349
350#[derive(Debug, Clone, PartialEq, Eq)]
351pub struct WindowsSurfaceCapture {
357 pub x: i32,
359 pub y: i32,
360 pub width: u32,
361 pub height: u32,
362 pub pixels: Vec<u8>,
364}
365
366type SurfaceCaptureProvider = fn(usize) -> Vec<WindowsSurfaceCapture>;
367
368static SURFACE_CAPTURE_PROVIDERS: OnceLock<Mutex<Vec<SurfaceCaptureProvider>>> = OnceLock::new();
369
370fn surface_capture_providers() -> &'static Mutex<Vec<SurfaceCaptureProvider>> {
371 SURFACE_CAPTURE_PROVIDERS.get_or_init(|| Mutex::new(Vec::new()))
372}
373
374pub fn register_surface_capture_provider(provider: SurfaceCaptureProvider) {
376 if let Ok(mut providers) = surface_capture_providers().lock() {
377 providers.push(provider);
378 }
379}
380
381pub fn surface_captures(window_id: usize) -> Vec<WindowsSurfaceCapture> {
383 let providers = match surface_capture_providers().lock() {
384 Ok(providers) => providers.clone(),
385 Err(_) => return Vec::new(),
386 };
387 providers
388 .into_iter()
389 .flat_map(|provider| provider(window_id))
390 .collect()
391}
392
393pub struct WindowsWebViewWindowSnapshot {
394 pub window_id: usize,
395 pub webtag_key: String,
396 pub visible: bool,
397 pub window_left: i32,
398 pub window_top: i32,
399 pub window_width: i32,
400 pub window_height: i32,
401 pub content_left: i32,
402 pub content_top: i32,
403 pub content_width: u32,
404 pub content_height: u32,
405 pub content_corner_radii: [i32; 4],
409}
410
411#[derive(Debug, Clone, Copy, PartialEq)]
412pub struct WindowsWebViewContentWindow {
413 pub window: isize,
414 pub content_left: i32,
415 pub content_top: i32,
416 pub content_width: i32,
417 pub content_height: i32,
418 pub scale: f64,
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
422pub enum WindowsPanelPosition {
423 Left,
424 #[default]
425 Right,
426 Top,
427 Bottom,
428}
429
430#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
436pub enum WindowsNavAnimation {
437 #[default]
438 None,
439 Forward,
440 Backward,
441}
442
443#[derive(Clone, Default)]
444pub struct WindowsWindowLayout {
445 payload: Option<Arc<dyn Any + Send + Sync>>,
446}
447
448impl std::fmt::Debug for WindowsWindowLayout {
449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
450 f.debug_struct("WindowsWindowLayout")
451 .field("has_payload", &self.payload.is_some())
452 .finish()
453 }
454}
455
456impl WindowsWindowLayout {
457 pub fn new<T>(payload: T) -> Self
458 where
459 T: Any + Send + Sync + 'static,
460 {
461 Self {
462 payload: Some(Arc::new(payload)),
463 }
464 }
465
466 pub fn empty() -> Self {
467 Self::default()
468 }
469
470 pub fn is_empty(&self) -> bool {
471 self.payload.is_none()
472 }
473
474 pub fn downcast_ref<T>(&self) -> Option<&T>
475 where
476 T: Any + 'static,
477 {
478 self.payload.as_deref()?.downcast_ref::<T>()
479 }
480}
481
482#[derive(Debug, Clone, PartialEq, Eq)]
483pub struct WindowsHostPanelTab {
484 pub id: u64,
485 pub title: String,
486 pub active: bool,
487}
488
489#[derive(Debug, Clone, PartialEq, Eq)]
490pub struct WindowsHostPanelContent {
491 pub title: Option<String>,
492 pub body: Option<String>,
493 pub tabs: Vec<WindowsHostPanelTab>,
494 pub maximized: bool,
495 pub show_maximize: bool,
499}
500
501#[derive(Debug, Clone, PartialEq)]
502pub struct WindowsChromePanel {
503 pub panel_id: String,
504 pub webtag_key: String,
505 pub title: String,
506 pub rect: RECT,
507 pub header_rect: Option<RECT>,
510 pub resize_handle: Option<RECT>,
513 pub host_content: Option<WindowsHostPanelContent>,
514 pub docked: bool,
515 pub overlay: bool,
517}
518
519#[derive(Debug, Clone, PartialEq, Eq)]
520pub struct WindowsChromePanelLayoutInput {
521 pub panel_id: String,
522 pub webtag_key: String,
523 pub position: WindowsPanelPosition,
524 pub requested_size: Option<i32>,
525 pub docked: bool,
526 pub overlay: bool,
528 pub maximized: bool,
529}
530
531#[derive(Debug, Clone, PartialEq)]
532pub struct WindowsChromePanelLayout {
533 pub panel_id: String,
534 pub webtag_key: String,
535 pub rect: RECT,
536 pub header_rect: Option<RECT>,
539 pub resize_handle: Option<RECT>,
540 pub overlay: bool,
542}
543
544#[derive(Debug, Clone, PartialEq)]
545pub struct WindowsChromeAttachedLayout {
546 pub main_region: RECT,
548 pub main: RECT,
550 pub panels: Vec<WindowsChromePanelLayout>,
551}
552
553#[derive(Debug, Clone, PartialEq)]
554pub struct WindowsChromeAttachedState {
555 pub main_region: RECT,
556 pub main: RECT,
557 pub panels: Vec<WindowsChromePanel>,
558}
559
560#[derive(Debug, Clone)]
561pub struct WindowsChromeState {
562 pub hwnd: HWND,
563 pub client: RECT,
564 pub layout: WindowsWindowLayout,
565 pub attached: Option<WindowsChromeAttachedState>,
566 pub frame_button_hover: Option<WindowsFrameButton>,
567 pub frame_button_pressed: Option<WindowsFrameButton>,
568 pub cursor: Option<(i32, i32)>,
571}
572
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub enum WindowsFrameButton {
575 Minimize,
576 Maximize,
577 Close,
578}
579
580#[derive(Debug, Clone, PartialEq)]
581pub struct WindowsChromeCommand {
582 pub id: String,
583 pub payload: serde_json::Value,
584 pub focus: Option<String>,
585 pub double_click: Option<Box<WindowsChromeCommand>>,
586 pub include_screen_position: bool,
587}
588
589impl WindowsChromeCommand {
590 pub fn new(id: impl Into<String>) -> Self {
591 Self {
592 id: id.into(),
593 payload: serde_json::Value::Null,
594 focus: None,
595 double_click: None,
596 include_screen_position: false,
597 }
598 }
599
600 pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
601 self.payload = payload;
602 self
603 }
604
605 pub fn with_focus(mut self, surface_id: impl Into<String>) -> Self {
606 self.focus = Some(surface_id.into());
607 self
608 }
609
610 pub fn with_double_click(mut self, command: WindowsChromeCommand) -> Self {
611 self.double_click = Some(Box::new(command));
612 self
613 }
614
615 pub fn with_screen_position(mut self) -> Self {
616 self.include_screen_position = true;
617 self
618 }
619}
620
621#[derive(Debug, Clone, PartialEq)]
622pub enum WindowsChromeHit {
623 Caption,
624 FrameButton(WindowsFrameButton),
625 Focusable {
626 id: String,
627 context_menu: Option<WindowsChromeCommand>,
628 click_command: Option<WindowsChromeCommand>,
632 },
633 Command(WindowsChromeCommand),
634 CommandWithContext {
635 command: WindowsChromeCommand,
636 context_menu: WindowsChromeCommand,
637 },
638 Chrome,
639}
640
641pub trait WindowsChromeRenderer: Send + Sync {
642 fn content_rect(&self, client: RECT, layout: &WindowsWindowLayout) -> RECT;
643
644 fn attached_layout(
645 &self,
646 client: RECT,
647 layout: &WindowsWindowLayout,
648 panels: &[WindowsChromePanelLayoutInput],
649 ) -> Option<WindowsChromeAttachedLayout> {
650 let _ = (client, layout, panels);
651 None
652 }
653
654 fn paint(&self, hdc: HDC, state: &WindowsChromeState);
655
656 fn paint_region(&self, hdc: HDC, state: &WindowsChromeState, invalid: RECT) {
657 let _ = invalid;
658 self.paint(hdc, state);
659 }
660
661 fn hit_test(&self, state: &WindowsChromeState, point: (i32, i32)) -> Option<WindowsChromeHit>;
662
663 fn frame_button_rect(
664 &self,
665 state: &WindowsChromeState,
666 button: WindowsFrameButton,
667 ) -> Option<RECT> {
668 let _ = (state, button);
669 None
670 }
671
672 fn hover_rect(&self, state: &WindowsChromeState, point: (i32, i32)) -> Option<RECT> {
676 let _ = (state, point);
677 None
678 }
679
680 fn mouse_wheel(
683 &self,
684 state: &WindowsChromeState,
685 point: (i32, i32),
686 delta: i16,
687 ) -> Option<WindowsChromeCommand> {
688 let _ = (state, point, delta);
689 None
690 }
691}
692
693#[derive(Debug, Clone, Copy, PartialEq, Eq)]
694pub struct WindowsHostPanelKeyEvent {
695 pub vk: u32,
696 pub ctrl: bool,
697 pub shift: bool,
698 pub alt: bool,
699 pub character: Option<char>,
700}
701
702#[derive(Debug, Clone, Copy, PartialEq, Eq)]
703pub struct WindowsHostWindow {
704 pub window: isize,
705}
706
707#[derive(Debug, Clone, Copy, PartialEq, Eq)]
708pub struct WindowsContentRect {
709 pub host_window: isize,
710 pub left: i32,
711 pub top: i32,
712 pub width: i32,
713 pub height: i32,
714 pub dpi: u32,
715}
716
717pub fn set_default_window_size(width: i32, height: i32) {
718 if width > 0 && height > 0 {
719 let _ = DEFAULT_WINDOW_SIZE.set((width, height));
720 }
721}
722
723pub fn default_window_size() -> (i32, i32) {
724 DEFAULT_WINDOW_SIZE.get().copied().unwrap_or((1024, 768))
725}
726
727pub fn set_windows_chrome_renderer(renderer: Arc<dyn WindowsChromeRenderer>) {
728 let slot = WINDOWS_CHROME_RENDERER.get_or_init(|| Mutex::new(None));
729 if let Ok(mut slot) = slot.lock() {
730 *slot = Some(renderer);
731 }
732}
733
734pub fn windows_chrome_renderer() -> Option<Arc<dyn WindowsChromeRenderer>> {
735 WINDOWS_CHROME_RENDERER
736 .get()
737 .and_then(|renderer| renderer.lock().ok())
738 .and_then(|renderer| renderer.clone())
739}
740
741pub fn set_webview_close_handler(webtag: &WebTag, handler: CloseHandler) {
742 let handlers = CLOSE_HANDLERS.get_or_init(|| Mutex::new(HashMap::new()));
743 if let Ok(mut handlers) = handlers.lock() {
744 handlers.insert(webtag.key().to_string(), handler);
745 }
746}
747
748pub fn webview_close_handler(webtag_key: &str) -> Option<CloseHandler> {
749 CLOSE_HANDLERS
750 .get()
751 .and_then(|handlers| handlers.lock().ok())
752 .and_then(|handlers| handlers.get(webtag_key).cloned())
753}
754
755pub fn set_webview_visibility_handler(handler: WebViewVisibilityHandler) {
756 let slot = VISIBILITY_HANDLER.get_or_init(|| Mutex::new(None));
757 if let Ok(mut slot) = slot.lock() {
758 *slot = Some(handler);
759 }
760}
761
762pub fn webview_visibility_handler() -> Option<WebViewVisibilityHandler> {
763 VISIBILITY_HANDLER
764 .get()
765 .and_then(|slot| slot.lock().ok())
766 .and_then(|slot| slot.clone())
767}
768
769pub fn set_webview_chrome_event_handler(webtag: &WebTag, handler: ChromeEventHandler) {
770 let handlers = CHROME_HANDLERS.get_or_init(|| Mutex::new(HashMap::new()));
771 if let Ok(mut handlers) = handlers.lock() {
772 handlers.insert(webtag.key().to_string(), handler);
773 }
774}
775
776pub fn webview_chrome_event_handler(webtag_key: &str) -> Option<ChromeEventHandler> {
777 CHROME_HANDLERS
778 .get()
779 .and_then(|handlers| handlers.lock().ok())
780 .and_then(|handlers| handlers.get(webtag_key).cloned())
781}
782
783pub fn add_host_window_created_handler(handler: HostWindowCreatedHandler) {
784 let handlers = HOST_WINDOW_CREATED_HANDLERS.get_or_init(|| Mutex::new(Vec::new()));
785 if let Ok(mut handlers) = handlers.lock() {
786 handlers.push(handler);
787 }
788}
789
790pub fn host_window_created_handlers() -> Vec<HostWindowCreatedHandler> {
791 HOST_WINDOW_CREATED_HANDLERS
792 .get()
793 .and_then(|state| state.lock().ok())
794 .map(|state| state.clone())
795 .unwrap_or_default()
796}
797
798pub fn set_host_panel_input_handler(panel_id: &str, handler: WindowsHostPanelInputHandler) {
799 let handlers = HOST_PANEL_INPUT_HANDLERS.get_or_init(|| Mutex::new(HashMap::new()));
800 if let Ok(mut handlers) = handlers.lock() {
801 handlers.insert(panel_id.to_string(), handler);
802 }
803}
804
805pub fn clear_host_panel_input_handler(panel_id: &str) {
806 if let Some(handlers) = HOST_PANEL_INPUT_HANDLERS.get()
807 && let Ok(mut handlers) = handlers.lock()
808 {
809 handlers.remove(panel_id);
810 }
811}
812
813pub fn host_panel_input_handler(panel_id: &str) -> Option<WindowsHostPanelInputHandler> {
814 HOST_PANEL_INPUT_HANDLERS
815 .get()
816 .and_then(|handlers| handlers.lock().ok())
817 .and_then(|handlers| handlers.get(panel_id).cloned())
818}
819
820pub fn set_webview_window_layout(webtag: &WebTag, layout: WindowsWindowLayout) -> StdResult<()> {
821 let layouts = WINDOW_LAYOUTS.get_or_init(|| Mutex::new(HashMap::new()));
822 if let Ok(mut layouts) = layouts.lock() {
823 layouts.insert(webtag.key().to_string(), layout);
824 }
825 if let Ok(backend) = backend() {
826 backend.sync_webview_window_layout(webtag);
827 }
828 Ok(())
829}
830
831pub fn current_window_layout(webtag_key: &str) -> WindowsWindowLayout {
832 WINDOW_LAYOUTS
833 .get()
834 .and_then(|layouts| layouts.lock().ok())
835 .and_then(|layouts| layouts.get(webtag_key).cloned())
836 .unwrap_or_default()
837}
838
839pub fn cleanup_webview_state(webtag_key: &str) {
840 if let Some(handlers) = CLOSE_HANDLERS.get()
841 && let Ok(mut handlers) = handlers.lock()
842 {
843 handlers.remove(webtag_key);
844 }
845 if let Some(handlers) = CHROME_HANDLERS.get()
846 && let Ok(mut handlers) = handlers.lock()
847 {
848 handlers.remove(webtag_key);
849 }
850 if let Some(layouts) = WINDOW_LAYOUTS.get()
851 && let Ok(mut layouts) = layouts.lock()
852 {
853 layouts.remove(webtag_key);
854 }
855}
856
857pub fn show_webview_as_panel(webtag: &WebTag, title: &str, panel_id: &str) -> StdResult<()> {
858 backend()?.show_webview_as_panel(webtag, title, panel_id)
859}
860
861pub fn show_webview_as_adaptive_panel(
862 webtag: &WebTag,
863 title: &str,
864 panel_id: &str,
865 position: WindowsPanelPosition,
866 preferred_size: Option<i32>,
867) -> StdResult<()> {
868 backend()?.show_webview_as_adaptive_panel(webtag, title, panel_id, position, preferred_size)
869}
870
871pub fn show_webview_as_overlay_panel(
872 webtag: &WebTag,
873 title: &str,
874 panel_id: &str,
875 position: WindowsPanelPosition,
876) -> StdResult<()> {
877 backend()?.show_webview_as_overlay_panel(webtag, title, panel_id, position)
878}
879
880pub fn present_webview_in_active_group(webtag: &WebTag) -> StdResult<()> {
881 backend()?.present_webview_in_active_group(webtag)
882}
883
884pub fn active_host_window_is_device_framed() -> bool {
885 backend()
886 .map(|backend| backend.active_host_window_is_device_framed())
887 .unwrap_or(false)
888}
889
890pub fn active_host_window_webtag_key() -> Option<String> {
891 backend()
892 .ok()
893 .and_then(|backend| backend.active_host_window_webtag_key())
894}
895
896pub fn present_webview_as_group_main(webtag: &WebTag, group_key: String) -> StdResult<()> {
897 backend()?.present_webview_as_group_main(webtag, group_key)
898}
899
900pub fn present_webview_as_overlay(
901 webtag: &WebTag,
902 width: f64,
903 height: f64,
904 width_ratio: f64,
905 height_ratio: f64,
906 position: u8,
907) -> StdResult<()> {
908 backend()?.present_webview_as_overlay(
909 webtag,
910 width,
911 height,
912 width_ratio,
913 height_ratio,
914 position,
915 )
916}
917
918pub fn configure_webview_surface_interaction(
919 webtag: &WebTag,
920 close_button: bool,
921 dismiss_on_outside: bool,
922 modal: bool,
923) -> StdResult<()> {
924 backend()?.configure_webview_surface_interaction(
925 webtag,
926 close_button,
927 dismiss_on_outside,
928 modal,
929 )
930}
931
932pub fn resize_host_window_content(webtag: &WebTag, width: i32, height: i32) -> StdResult<()> {
933 backend()?.resize_host_window_content(webtag, width, height)
934}
935
936pub fn restore_presented_group_main() -> StdResult<()> {
937 backend()?.restore_presented_group_main()
938}
939
940pub fn show_interactive_host_panel(
941 panel_id: &str,
942 title: &str,
943 body: &str,
944 position: WindowsPanelPosition,
945) -> StdResult<()> {
946 backend()?.show_interactive_host_panel(panel_id, title, body, position)
947}
948
949pub fn hide_host_panel(panel_id: &str) -> StdResult<()> {
950 backend()?.hide_host_panel(panel_id)
951}
952
953pub fn update_host_panel_body(panel_id: &str, body: &str) -> StdResult<()> {
954 backend()?.update_host_panel_body(panel_id, body)
955}
956
957pub fn set_host_panel_tabs(panel_id: &str, tabs: Vec<WindowsHostPanelTab>) -> bool {
958 backend()
959 .map(|backend| backend.set_host_panel_tabs(panel_id, tabs))
960 .unwrap_or(false)
961}
962
963pub fn set_host_panel_maximized(panel_id: &str, maximized: bool) -> bool {
964 backend()
965 .map(|backend| backend.set_host_panel_maximized(panel_id, maximized))
966 .unwrap_or(false)
967}
968
969pub fn invalidate_host_panel(panel_id: &str) -> bool {
970 backend()
971 .map(|backend| backend.invalidate_host_panel(panel_id))
972 .unwrap_or(false)
973}
974
975pub fn is_panel_visible(panel_id: &str) -> bool {
976 backend()
977 .map(|backend| backend.is_panel_visible(panel_id))
978 .unwrap_or(false)
979}
980
981pub fn find_host_window_for_webview(webtag: &WebTag) -> StdResult<WindowsHostWindow> {
982 let content = find_webview_content_window(webtag).ok_or_else(|| {
983 WebViewError::WebView(format!("no window registered for {}", webtag.key()))
984 })?;
985 Ok(WindowsHostWindow {
986 window: content.window,
987 })
988}
989
990pub fn request_host_window_layout(window: WindowsHostWindow) -> bool {
991 backend()
992 .map(|backend| backend.request_host_window_layout(window))
993 .unwrap_or(false)
994}
995
996pub fn active_content_screen_rect() -> Option<WindowsContentRect> {
997 backend()
998 .ok()
999 .and_then(|backend| backend.active_content_screen_rect())
1000}
1001
1002pub fn find_webview_content_window(webtag: &WebTag) -> Option<WindowsWebViewContentWindow> {
1003 backend()
1004 .ok()
1005 .and_then(|backend| backend.find_webview_content_window(webtag))
1006}
1007
1008pub fn webview_window_snapshot(webtag: &WebTag) -> StdResult<WindowsWebViewWindowSnapshot> {
1009 backend()?.webview_window_snapshot(webtag)
1010}
1011
1012pub fn show_webview_window(webtag: &WebTag, title: &str, activate: bool) -> StdResult<()> {
1013 backend()?.show_webview_window(webtag, title, activate)
1014}
1015
1016pub fn show_webview_window_with_content_size(
1017 webtag: &WebTag,
1018 title: &str,
1019 activate: bool,
1020 width: Option<i32>,
1021 height: Option<i32>,
1022) -> StdResult<()> {
1023 backend()?.show_webview_window_with_content_size(webtag, title, activate, width, height)
1024}
1025
1026pub fn show_webview_window_with_chrome(
1027 webtag: &WebTag,
1028 title: &str,
1029 activate: bool,
1030 width: Option<i32>,
1031 height: Option<i32>,
1032 full_chrome: bool,
1033) -> StdResult<()> {
1034 backend()?.show_webview_window_with_chrome(webtag, title, activate, width, height, full_chrome)
1035}
1036
1037pub fn navigate_webview_window(
1038 webtag: &WebTag,
1039 title: &str,
1040 activate: bool,
1041 animation: WindowsNavAnimation,
1042) -> StdResult<()> {
1043 backend()?.navigate_webview_window(webtag, title, activate, animation)
1044}
1045
1046pub fn hide_webview_window(webtag: &WebTag) -> StdResult<()> {
1047 backend()?.hide_webview_window(webtag)
1048}
1049
1050pub fn post_to_window_thread(window: isize, callback: Box<dyn FnOnce() + Send>) -> bool {
1051 backend()
1052 .map(|backend| backend.post_to_window_thread(window, callback))
1053 .unwrap_or(false)
1054}