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}