window_observer/
window.rs

1use window_getter::Bounds;
2
3use crate::{Error, platform_impl::PlatformWindow};
4
5/// A wrapper around platform-specific window implementations.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Window(pub(crate) PlatformWindow);
8
9impl Window {
10    /// Creates a new `Window` instance from a platform-specific window.
11    pub fn new(platform_window: PlatformWindow) -> Self {
12        Self(platform_window)
13    }
14
15    /// Retrieves the underlying platform-specific window implementation.
16    pub fn inner(&self) -> &PlatformWindow {
17        &self.0
18    }
19
20    /// Retrieves the title of the window.
21    ///
22    /// # Platform-specific
23    /// - **macOS:** It will always return [`Some`] when it is ok.
24    pub fn title(&self) -> Result<Option<String>, Error> {
25        #[cfg(target_os = "macos")]
26        {
27            Ok(Some(self.0.title()?))
28        }
29        #[cfg(target_os = "windows")]
30        {
31            self.0
32                .title()
33                .map_err(|e| Error::PlatformSpecificError(e.into()))
34        }
35    }
36
37    /// Retrieves the size of the window.
38    pub fn size(&self) -> Result<Size, Error> {
39        #[cfg(target_os = "macos")]
40        {
41            Ok(self.0.size()?)
42        }
43        #[cfg(target_os = "windows")]
44        {
45            Ok(self
46                .0
47                .visible_bounds()
48                .map_err(|e| Error::PlatformSpecificError(e.into()))?
49                .into())
50        }
51    }
52
53    /// Retrieves the position of the window.
54    pub fn position(&self) -> Result<Position, Error> {
55        #[cfg(target_os = "macos")]
56        {
57            Ok(self.0.position()?)
58        }
59        #[cfg(target_os = "windows")]
60        {
61            Ok(self
62                .0
63                .visible_bounds()
64                .map_err(|e| Error::PlatformSpecificError(e.into()))?
65                .into())
66        }
67    }
68
69    /// Checks if the window is currently focused.
70    ///
71    /// # Platform-specific
72    /// - **Windows:** It will always return [`Ok`].
73    pub fn is_focused(&self) -> Result<bool, Error> {
74        #[cfg(target_os = "macos")]
75        {
76            Ok(self.0.is_focused()?)
77        }
78        #[cfg(target_os = "windows")]
79        {
80            Ok(self.0.is_foreground())
81        }
82    }
83
84    /// Retrieves the unique identifier of the window.
85    ///
86    /// # Platform-specific
87    /// - **macOS:** It will return a [`CGWindowID`][CGWindowID] which is wrapped by [`window_getter::WindowId`].
88    ///   **Warning:** It uses the private API `_AXUIElementGetWindow` of macOS.
89    /// - **Windows:** It will always return [`Ok`].
90    ///
91    /// [CGWindowID]: https://developer.apple.com/documentation/coregraphics/cgwindowid?language=objc
92    #[cfg(feature = "macos-private-api")]
93    #[cfg_attr(docsrs, doc(cfg(feature = "macos-private-api")))]
94    pub fn id(&self) -> Result<window_getter::WindowId, Error> {
95        #[cfg(target_os = "macos")]
96        {
97            Ok(window_getter::WindowId::new(self.0.id()?))
98        }
99        #[cfg(target_os = "windows")]
100        {
101            Ok(window_getter::WindowId::new(self.0.hwnd()))
102        }
103    }
104
105    /// Retrieves the `Window` implementation by [window-getter-rs][window-getter-rs].
106    ///
107    /// # Panics
108    /// On macOS, if there is no window environment, it will panic.
109    ///
110    /// [window-getter-rs]: https://github.com/tasuren/window-getter-rs
111    ///
112    /// # Platform-specific
113    /// - **Windows:** It will always return `Ok(Some(Window))`.
114    #[cfg(feature = "macos-private-api")]
115    #[cfg_attr(docsrs, doc(cfg(feature = "macos-private-api")))]
116    pub fn create_window_getter_window(&self) -> Result<Option<window_getter::Window>, Error> {
117        #[cfg(target_os = "macos")]
118        {
119            Ok(window_getter::get_window(self.id()?).expect("No window environment found"))
120        }
121        #[cfg(target_os = "windows")]
122        {
123            let window = window_getter::platform_impl::PlatformWindow::new(self.inner().hwnd());
124            Ok(Some(window_getter::Window::new(window)))
125        }
126    }
127}
128
129/// Represents the size of a window.
130#[derive(Debug, Clone, Default, PartialEq)]
131pub struct Size {
132    /// The width of the window.
133    pub width: f64,
134    /// The height of the window.
135    pub height: f64,
136}
137
138/// Represents the position of a window.
139#[derive(Debug, Clone, Copy, Default, PartialEq)]
140pub struct Position {
141    /// The x-coordinate of the window.
142    pub x: f64,
143    /// The y-coordinate of the window.
144    pub y: f64,
145}
146
147impl From<Bounds> for Size {
148    fn from(value: Bounds) -> Self {
149        Size {
150            width: value.width,
151            height: value.height,
152        }
153    }
154}
155
156impl From<Bounds> for Position {
157    fn from(value: Bounds) -> Self {
158        Position {
159            x: value.x,
160            y: value.y,
161        }
162    }
163}