Skip to main content

inset_embedder/
window.rs

1//! Asking for views, and the windows around them. Not a Flutter file.
2//!
3//! An app asks for a view in a window configured as a [`WindowConfig`]
4//! (`Platform::request_view`), and the view arrives later, as every view does, carrying the
5//! request's [`RequestId`], as iOS hands back the activity of `requestSceneSessionActivation` with
6//! the connecting scene. On a desktop the host makes the window; where the platform gives the app
7//! views and makes none, on a phone and when embedded, a request is refused. A view in a window
8//! the app may manage, one it asked for or one the platform gave, has a [`HostWindow`]
9//! (`View::window`): the configuration every window wants up front, whether the window is
10//! minimized or occluded, which the host reports with the window's moves
11//! (`EmbedderClient::window_changed`), the native handle for anything beyond it, and the window's
12//! closing, which is how a view closes.
13//!
14//! Flutter's windowing API (`widgets/_window.dart`, experimental) builds window archetypes
15//! over the engine's views, each controller making its window within its constructor.
16
17use std::fmt;
18use std::rc::Rc;
19use std::time::Duration;
20
21pub use raw_window_handle::RawWindowHandle;
22
23use crate::{Rect, ViewRef};
24
25/// How a window is made. Sizes and positions are logical, in the host's screen space with
26/// the origin at the top left of the primary screen.
27///
28/// Every option names what the window is for, not how a system does it: the host maps each
29/// onto what its system has, and one its system lacks it leaves at the system's default.
30/// What no option expresses is set through the window's
31/// [`native_handle`](HostWindow::native_handle).
32#[derive(Clone, Debug, PartialEq)]
33pub struct WindowConfig {
34    pub title: String,
35    pub size: [f64; 2],
36    /// Where the window opens; the host chooses when `None`.
37    pub position: Option<[f64; 2]>,
38    /// The system's title bar and border.
39    pub decorations: bool,
40    pub resizable: bool,
41    pub level: WindowLevel,
42    /// Whether showing or clicking the window makes it the focused one. A host may not be
43    /// able to refuse activation.
44    pub activating: bool,
45    /// Whether the window is on every virtual desktop: macOS Spaces, X11 workspaces.
46    pub all_desktops: bool,
47    pub background: WindowBackground,
48    /// Whether the window casts the system's shadow.
49    pub shadow: bool,
50    pub visible: bool,
51}
52
53impl Default for WindowConfig {
54    fn default() -> WindowConfig {
55        WindowConfig {
56            title: String::new(),
57            size: [900.0, 600.0],
58            position: None,
59            decorations: true,
60            resizable: true,
61            level: WindowLevel::Normal,
62            activating: true,
63            all_desktops: false,
64            background: WindowBackground::Solid,
65            shadow: true,
66            visible: true,
67        }
68    }
69}
70
71#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
72pub enum WindowLevel {
73    #[default]
74    Normal,
75    /// Above ordinary windows, as a palette or a panel sits.
76    AlwaysOnTop,
77    /// Below ordinary windows, as a desktop widget sits.
78    AlwaysOnBottom,
79}
80
81/// What the window shows behind the app's painting. Anything but `Solid` lets what is
82/// behind the window show where the app paints nothing.
83#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
84pub enum WindowBackground {
85    #[default]
86    Solid,
87    Transparent,
88    /// The system's blur of what lies behind the window (macOS vibrancy).
89    Blurred,
90    /// The system's glass material (macOS 26's Liquid Glass); the blur where the system
91    /// has none.
92    Glass,
93}
94
95impl WindowBackground {
96    /// Whether what is behind the window can show through it.
97    pub fn sees_through(self) -> bool {
98        self != WindowBackground::Solid
99    }
100}
101
102/// The number of a request for a view, which the view carries when it arrives
103/// ([`View::request_id`](crate::View::request_id)).
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
105pub struct RequestId(pub u64);
106
107/// Why a request for a view got no view.
108#[derive(Clone, Debug, PartialEq, Eq)]
109pub enum ViewError {
110    /// This platform gives the app its views and makes none on request.
111    Unsupported,
112    Failed(String),
113}
114
115impl fmt::Display for ViewError {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match self {
118            ViewError::Unsupported => f.write_str("the platform makes no views on request"),
119            ViewError::Failed(reason) => {
120                write!(f, "the platform could not make the view: {reason}")
121            }
122        }
123    }
124}
125
126impl std::error::Error for ViewError {}
127
128pub type WindowRef = Rc<dyn HostWindow>;
129
130/// The window around a view the host puts on screen in a window the app may manage.
131pub trait HostWindow: 'static {
132    fn view(&self) -> ViewRef;
133
134    /// The window's logical frame in screen space.
135    fn frame(&self) -> Rect;
136
137    /// Moves and sizes the window, over `animate` when the host can animate a frame.
138    fn set_frame(&self, frame: Rect, animate: Option<Duration>);
139
140    fn set_title(&self, title: &str);
141
142    fn show(&self);
143
144    fn hide(&self);
145
146    /// Flutter `WindowController.isMinimized`; macOS `isMiniaturized`, Windows `IsIconic`.
147    /// `false` where the system does not tell a client, as Wayland does not.
148    fn is_minimized(&self) -> bool;
149
150    /// Covered all over, or otherwise not shown: macOS's occlusion state without its visible bit,
151    /// winit's `Occluded`. Not in Flutter's controller; Inset's, after the platforms. `false`
152    /// where the system does not tell a window, as Windows and Wayland do not.
153    fn is_occluded(&self) -> bool;
154
155    /// Closes the window; its view goes away with it. The user's asking to close it only reports
156    /// it ([`EmbedderClient::view_close_requested`](crate::EmbedderClient::view_close_requested));
157    /// this closes it.
158    fn close(&self);
159
160    /// The native window, for configuration this interface does not carry.
161    fn native_handle(&self) -> Option<RawWindowHandle>;
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167
168    #[test]
169    fn a_default_window_is_an_ordinary_visible_one() {
170        let config = WindowConfig::default();
171        assert!(config.decorations && config.visible && config.activating);
172        assert_eq!(config.level, WindowLevel::Normal);
173        assert_eq!(config.background, WindowBackground::Solid);
174    }
175
176    #[test]
177    fn every_background_but_solid_sees_through() {
178        assert!(!WindowBackground::Solid.sees_through());
179        assert!(WindowBackground::Transparent.sees_through());
180        assert!(WindowBackground::Blurred.sees_through());
181        assert!(WindowBackground::Glass.sees_through());
182    }
183}