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