Skip to main content

gpui_platform/
platform_window.rs

1//! Platform window contract shared by `gpui` and its platform backends.
2
3#[cfg(all(target_os = "linux", feature = "wayland"))]
4use crate::layer_shell;
5use crate::{
6    Decorations, DispatchEventResult, ExternalDragPayload, GpuSpecs, PlatformDisplay,
7    PlatformInput, PlatformInputHandler, PromptButton, PromptLevel, RequestFrameOptions,
8    ResizeEdge, SystemWindowTab, TextInputConfiguration, TextInputStateChange, WindowAppearance,
9    WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
10    WindowInsets,
11};
12use futures::channel::oneshot;
13use gpui_engine::SceneRenderer;
14use gpui_types::{Bounds, Capslock, Modifiers, Pixels, Point, Size};
15use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
16#[cfg(any(test, feature = "test-support", feature = "bench-support"))]
17use std::any::Any;
18use std::rc::Rc;
19
20/// Whether the platform is presenting a window's frames.
21///
22/// This is about presentation, not the window's shown/hidden state: a shown
23/// window that is fully covered by other windows, minimized, on another
24/// Space or virtual desktop, or on a display that is asleep is `Hidden`. A
25/// window only partly covered by other windows is `Visible`.
26///
27/// Each platform reports from a single source, and what that source can see
28/// differs:
29///
30/// * macOS: `NSWindow.occlusionState`. Covers all of the cases above.
31/// * Windows: `WS_VISIBLE` and the minimized state. Windows keeps compositing
32///   covered windows for thumbnails and Alt-Tab and offers no occlusion
33///   notification, so a fully covered window stays `Visible`. Display sleep is
34///   not reported either.
35/// * Wayland: the `xdg_toplevel` `suspended` state (xdg-shell v6). Compositors
36///   that don't support it never report `Hidden`.
37/// * X11: mapped state plus `VisibilityNotify`. Compositing window managers
38///   generally never report a window as fully obscured, so covering is only
39///   detected without compositing.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum WindowVisibility {
42    /// At least part of the window is being presented; frames drawn for it
43    /// will be shown.
44    Visible,
45    /// No part of the window is being presented. The platform will not
46    /// request frames for it until it becomes visible again.
47    Hidden,
48}
49
50impl WindowVisibility {
51    /// Whether frames drawn for the window will be shown.
52    pub fn is_visible(self) -> bool {
53        self == Self::Visible
54    }
55}
56
57/// Callbacks for the accessibility adapter.
58pub struct A11yCallbacks {
59    /// Called when the adapter is activated (a screen reader connects).
60    pub activation: Box<dyn Fn() -> Option<accesskit::TreeUpdate> + Send + 'static>,
61    /// Called when an action is requested by the screen reader.
62    pub action: Box<dyn Fn(accesskit::ActionRequest) + Send + 'static>,
63    /// Called when the adapter is deactivated (screen reader disconnects).
64    pub deactivation: Box<dyn Fn() + Send + 'static>,
65}
66
67#[expect(missing_docs)]
68pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
69    fn bounds(&self) -> Bounds<Pixels>;
70    fn is_maximized(&self) -> bool;
71    fn window_bounds(&self) -> WindowBounds;
72    fn content_size(&self) -> Size<Pixels>;
73    /// Returns the visible viewport in logical pixels relative to the content origin.
74    ///
75    /// This may be smaller or offset when a keyboard or zoom obscures content;
76    /// it must not change the full layout size returned by `content_size`.
77    /// Implementations should return a frame snapshot, not query platform layout here.
78    fn visual_viewport_bounds(&self) -> Bounds<Pixels> {
79        Bounds::new(Point::default(), self.content_size())
80    }
81    /// Registers a callback when visible geometry may have changed.
82    ///
83    /// This requests a frame; backends can sample the new viewport and safe-area
84    /// geometry in `prepare_frame` rather than updating it inside the callback.
85    fn on_visual_viewport_changed(&self, _callback: Box<dyn FnMut()>) {}
86    /// Samples platform geometry before a draw, returning whether view caches must be invalidated.
87    ///
88    /// Geometry getters must remain consistent throughout the ensuing draw.
89    /// Do not invoke callbacks here: GPUI is already updating this window.
90    fn prepare_frame(&self) -> bool {
91        false
92    }
93    fn resize(&mut self, size: Size<Pixels>);
94    fn scale_factor(&self) -> f32;
95    fn appearance(&self) -> WindowAppearance;
96    fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
97    fn mouse_position(&self) -> Point<Pixels>;
98    fn modifiers(&self) -> Modifiers;
99    fn capslock(&self) -> Capslock;
100    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
101    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
102    /// Apply the focused text region's [`TextInputConfiguration`] to the
103    /// platform's text input session (e.g. attributes of the hidden editable
104    /// element on web). Called only when the configuration changes, because
105    /// reconfiguring a live input session can restart the IME connection.
106    fn set_text_input_configuration(&mut self, _configuration: TextInputConfiguration) {}
107    fn prompt(
108        &self,
109        level: PromptLevel,
110        msg: &str,
111        detail: Option<&str>,
112        answers: &[PromptButton],
113    ) -> Option<oneshot::Receiver<usize>>;
114    fn activate(&self);
115    /// Requests that the operating system draw attention to this window.
116    fn request_attention(&self) {}
117    fn is_active(&self) -> bool;
118    /// The current [`WindowVisibility`]. Read once when the window is created;
119    /// afterwards changes arrive through [`Self::on_visibility_change`].
120    fn visibility(&self) -> WindowVisibility;
121    fn is_hovered(&self) -> bool;
122    fn background_appearance(&self) -> WindowBackgroundAppearance;
123    fn set_title(&mut self, title: &str);
124    fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance);
125    fn minimize(&self);
126    fn zoom(&self);
127    fn toggle_fullscreen(&self);
128    fn is_fullscreen(&self) -> bool;
129    fn frame_waker(&self) -> Option<Rc<dyn Fn()>> {
130        None
131    }
132    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>);
133    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>);
134    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
135    /// Registers the callback invoked when [`Self::visibility`] changes. Only
136    /// transitions are reported; the callback runs on the main thread outside
137    /// of any window update.
138    fn on_visibility_change(&self, callback: Box<dyn FnMut(WindowVisibility)>);
139    fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>);
140    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
141    fn on_moved(&self, callback: Box<dyn FnMut()>);
142    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
143    fn on_hit_test_window_control(&self, callback: Box<dyn FnMut() -> Option<WindowControlArea>>);
144    fn on_close(&self, callback: Box<dyn FnOnce()>);
145    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
146    fn on_button_layout_changed(&self, _callback: Box<dyn FnMut()>) {}
147    /// Borrows this window's scene renderer without presenting a frame.
148    ///
149    /// Use this for renderer queries such as reading the sprite atlas or
150    /// capturing an image; backends must not advance their frame loop here.
151    fn with_renderer(&mut self, f: &mut dyn FnMut(&mut dyn SceneRenderer));
152    /// Submits a frame through this window's scene renderer.
153    ///
154    /// `f` encodes and submits the frame and returns whether it was presented.
155    /// Backends that pace themselves on compositor callbacks use that result
156    /// to update their frame loop.
157    fn present(&mut self, f: &mut dyn FnMut(&mut dyn SceneRenderer) -> bool);
158    fn schedule_frame(&self) {}
159    fn is_subpixel_rendering_supported(&self) -> bool;
160
161    // macOS specific methods
162    fn get_title(&self) -> String {
163        String::new()
164    }
165    fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
166        None
167    }
168    fn tab_bar_visible(&self) -> bool {
169        false
170    }
171    fn set_edited(&mut self, _edited: bool) {}
172    fn set_document_path(&self, _path: Option<&std::path::Path>) {}
173    fn toggle_simple_fullscreen(&self) {}
174    fn is_simple_fullscreen(&self) -> bool {
175        false
176    }
177    #[cfg(target_os = "macos")]
178    fn set_traffic_light_position(&self, _position: Point<Pixels>) {}
179    fn show_character_palette(&self) {}
180    fn titlebar_double_click(&self, _is_resizable: bool, _is_minimizable: bool) {}
181    fn on_move_tab_to_new_window(&self, _callback: Box<dyn FnMut()>) {}
182    fn on_merge_all_windows(&self, _callback: Box<dyn FnMut()>) {}
183    fn on_select_previous_tab(&self, _callback: Box<dyn FnMut()>) {}
184    fn on_select_next_tab(&self, _callback: Box<dyn FnMut()>) {}
185    fn on_toggle_tab_bar(&self, _callback: Box<dyn FnMut()>) {}
186    fn merge_all_windows(&self) {}
187    fn move_tab_to_new_window(&self) {}
188    fn toggle_window_tab_overview(&self) {}
189    fn set_tabbing_identifier(&self, _identifier: Option<String>) {}
190
191    fn native_window_state(&self) -> Option<Vec<u8>> {
192        None
193    }
194    fn restore_native_window_state(&self, _state: &[u8]) {}
195
196    #[cfg(target_os = "windows")]
197    fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND;
198
199    // Linux specific methods
200    fn inner_window_bounds(&self) -> WindowBounds {
201        self.window_bounds()
202    }
203    fn request_decorations(&self, _decorations: WindowDecorations) {}
204    fn show_window_menu(&self, _position: Point<Pixels>) {}
205    fn start_window_move(&self) {}
206    fn can_start_external_drag(&self) -> bool {
207        false
208    }
209    fn start_external_drag(&self, _payload: &ExternalDragPayload) -> bool {
210        false
211    }
212    fn start_window_resize(&self, _edge: ResizeEdge) {}
213    fn set_exclusive_zone(&self, _zone: Pixels) {}
214    #[cfg(all(target_os = "linux", feature = "wayland"))]
215    fn set_exclusive_edge(&self, _edge: layer_shell::Anchor) {}
216    fn set_input_region(&self, _region: Option<&[Bounds<Pixels>]>) {}
217    fn window_decorations(&self) -> Decorations {
218        Decorations::Server
219    }
220    fn set_app_id(&mut self, _app_id: &str) {}
221    fn map_window(&mut self) -> anyhow::Result<()> {
222        Ok(())
223    }
224    fn window_controls(&self) -> WindowControls {
225        WindowControls::default()
226    }
227    fn set_client_inset(&self, _inset: Pixels) {}
228    fn gpu_specs(&self) -> Option<GpuSpecs>;
229
230    fn update_ime_position(&self, _bounds: Bounds<Pixels>);
231
232    // Mobile platform methods.
233
234    /// The regions of this window currently obscured or reserved by the
235    /// system. Zero on platforms without such regions.
236    fn insets(&self) -> WindowInsets {
237        WindowInsets::default()
238    }
239
240    /// Registers a callback invoked whenever [`Self::insets`] change.
241    ///
242    /// Contract: fires continuously during animated transitions (Android
243    /// `WindowInsetsAnimation` progress; on iOS the platform interpolates
244    /// the keyboard animation curve on frame ticks) and is exact at rest.
245    fn on_insets_changed(&self, _callback: Box<dyn FnMut(WindowInsets)>) {}
246
247    /// Sets the handler for the system back action (Android back
248    /// button/gesture; no source on iOS or desktop).
249    fn set_back_handler(&self, _callback: Box<dyn FnMut()>) {}
250
251    /// Declares whether the application would currently handle the system
252    /// back action (e.g. navigation depth > 0).
253    fn set_back_enabled(&self, _enabled: bool) {}
254
255    /// Requests that the soft keyboard be shown.
256    fn show_soft_keyboard(&self) {}
257
258    /// Requests that the soft keyboard be hidden.
259    fn hide_soft_keyboard(&self) {}
260
261    /// Inform the operating system that the text input state has changed
262    fn text_input_state_changed(&self, _change: TextInputStateChange) {}
263
264    fn play_system_bell(&self) {}
265
266    /// Initialize the accessibility adapter with callbacks.
267    fn a11y_init(&self, _callbacks: A11yCallbacks) {}
268
269    /// Provide a TreeUpdate to the accessibility adapter.
270    fn a11y_tree_update(&self, _tree_update: accesskit::TreeUpdate) {}
271
272    /// Inform the adapter of updated window bounds.
273    fn a11y_update_window_bounds(&self) {}
274
275    #[cfg(any(test, feature = "test-support", feature = "bench-support"))]
276    fn as_test(&mut self) -> Option<&mut dyn Any> {
277        None
278    }
279}