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 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}