Skip to main content

lingxia_platform/traits/
screenshot.rs

1use crate::error::PlatformError;
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4
5/// Description of a top-level window belonging to the host app.
6///
7/// Returned by [`AppScreenshot::list_app_windows`]. The `id` is opaque
8/// platform-specific (macOS NSWindow.windowNumber, Windows HWND, etc.) and
9/// is the value to pass back to [`AppScreenshot::take_app_screenshot`] to
10/// target that specific window.
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct WindowInfo {
13    /// Opaque platform-specific identifier (stringified for portability).
14    pub id: String,
15    /// Window title (may be empty if the platform / app does not set one).
16    pub title: String,
17    /// `true` if this window currently has keyboard focus / is "key".
18    pub focused: bool,
19    /// `true` if this window is the app's main window (macOS concept).
20    pub main: bool,
21    /// `true` if the window is currently on-screen / not minimized.
22    pub visible: bool,
23    /// Width in the platform's window-content coordinate unit (points on
24    /// Apple platforms, client pixels on Windows).
25    pub width: u32,
26    /// Height in the platform's window-content coordinate unit.
27    pub height: u32,
28}
29
30/// Capture a PNG snapshot of the host app's window(s).
31///
32/// Conceptually one level above `WebViewController::take_screenshot`: the
33/// WebView API only sees web content, while this captures the entire window
34/// the host renders — host-drawn navigation bars, native overlays, multiple
35/// WebViews composited together, etc.
36#[async_trait]
37pub trait AppScreenshot: Send + Sync {
38    /// Enumerate the app's top-level windows.
39    ///
40    /// Mobile platforms typically return a single "main" entry. Desktop
41    /// platforms return one entry per open NSWindow / HWND.
42    async fn list_app_windows(&self) -> Result<Vec<WindowInfo>, PlatformError> {
43        Err(PlatformError::NotSupported(
44            "list_app_windows is not implemented for this platform".to_string(),
45        ))
46    }
47
48    /// Resolve an optional id using the same default-window policy as capture
49    /// and input, returning the concrete target for automation metadata.
50    async fn resolve_app_window(
51        &self,
52        window_id: Option<&str>,
53    ) -> Result<WindowInfo, PlatformError> {
54        let windows = self.list_app_windows().await?;
55        if let Some(window_id) = window_id {
56            return windows
57                .into_iter()
58                .find(|window| window.id == window_id)
59                .ok_or_else(|| {
60                    PlatformError::InvalidParameter(format!(
61                        "window id does not belong to this app: {window_id}"
62                    ))
63                });
64        }
65        windows
66            .iter()
67            .find(|window| window.focused && window.visible)
68            .or_else(|| windows.iter().find(|window| window.main && window.visible))
69            .or_else(|| windows.iter().find(|window| window.visible))
70            .or_else(|| windows.first())
71            .cloned()
72            .ok_or_else(|| PlatformError::Platform("no app window is available".to_string()))
73    }
74
75    /// Capture and return PNG-encoded bytes of the app's window.
76    ///
77    /// When `window_id` is `None`, the platform picks a sensible default
78    /// (key/focused window on desktop; the sole window on mobile).
79    async fn take_app_screenshot(&self, window_id: Option<&str>) -> Result<Vec<u8>, PlatformError> {
80        let _ = window_id;
81        Err(PlatformError::NotSupported(
82            "app screenshot is not implemented for this platform".to_string(),
83        ))
84    }
85}