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