Skip to main content

egui/data/input/
viewport_info.rs

1use crate::emath::{Rect, Vec2};
2
3/// An input event from the backend into egui, about a specific [viewport](crate::viewport).
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
6pub enum ViewportEvent {
7    /// The user clicked the close-button on the window, or similar.
8    ///
9    /// If this is the root viewport, the application will exit
10    /// after this frame unless you send a
11    /// [`crate::ViewportCommand::CancelClose`] command.
12    ///
13    /// If this is not the root viewport,
14    /// it is up to the user to hide this viewport the next frame.
15    ///
16    /// This even will wake up both the child and parent viewport.
17    Close,
18}
19
20/// Information about the current viewport, given as input each frame.
21///
22/// `None` means "unknown".
23///
24/// All units are in ui "points", which can be calculated from native physical pixels
25/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `[Self::native_pixels_per_point`];
26#[derive(Clone, Debug, Default, PartialEq)]
27#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
28pub struct ViewportInfo {
29    /// Parent viewport, if known.
30    pub parent: Option<crate::ViewportId>,
31
32    /// Name of the viewport, if known.
33    pub title: Option<String>,
34
35    pub events: Vec<ViewportEvent>,
36
37    /// The OS native pixels-per-point.
38    ///
39    /// This should always be set, if known.
40    ///
41    /// On web this takes browser scaling into account,
42    /// and corresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.
43    pub native_pixels_per_point: Option<f32>,
44
45    /// Current monitor size in egui points.
46    pub monitor_size: Option<Vec2>,
47
48    /// The inner rectangle of the native window, in monitor space and ui points scale.
49    ///
50    /// This is the content rectangle of the viewport.
51    ///
52    /// **`eframe` notes**:
53    ///
54    /// On Android / Wayland, this will always be `None` since getting the
55    /// position of the window is not possible.
56    pub inner_rect: Option<Rect>,
57
58    /// The outer rectangle of the native window, in monitor space and ui points scale.
59    ///
60    /// This is the content rectangle plus decoration chrome.
61    ///
62    /// **`eframe` notes**:
63    ///
64    /// On Android / Wayland, this will always be `None` since getting the
65    /// position of the window is not possible.
66    pub outer_rect: Option<Rect>,
67
68    /// Are we minimized?
69    pub minimized: Option<bool>,
70
71    /// Are we maximized?
72    pub maximized: Option<bool>,
73
74    /// Are we in fullscreen mode?
75    pub fullscreen: Option<bool>,
76
77    /// Is the window focused and able to receive input?
78    ///
79    /// This should be the same as [`RawInput::focused`](crate::RawInput::focused).
80    pub focused: Option<bool>,
81
82    /// Is the window fully occluded (completely covered) by another window?
83    ///
84    /// Not all platforms support this.
85    /// On platforms that don't, this will be `None` or `Some(false)`.
86    pub occluded: Option<bool>,
87}
88
89impl ViewportInfo {
90    /// Is the window considered visible for rendering purposes?
91    ///
92    /// A window is not visible if it is minimized or occluded.
93    /// When not visible, the UI is not painted and rendering is skipped,
94    /// but application logic may still be executed by some integrations.
95    pub fn visible(&self) -> Option<bool> {
96        match (self.minimized, self.occluded) {
97            (Some(true), _) | (_, Some(true)) => Some(false),
98            (Some(false), Some(false)) => Some(true),
99            (_, None) | (None, _) => None,
100        }
101    }
102
103    /// This viewport has been told to close.
104    ///
105    /// If this is the root viewport, the application will exit
106    /// after this frame unless you send a
107    /// [`crate::ViewportCommand::CancelClose`] command.
108    ///
109    /// If this is not the root viewport,
110    /// it is up to the user to hide this viewport the next frame.
111    pub fn close_requested(&self) -> bool {
112        self.events.contains(&ViewportEvent::Close)
113    }
114
115    /// Helper: move [`Self::events`], clone the other fields.
116    pub fn take(&mut self) -> Self {
117        Self {
118            parent: self.parent,
119            title: self.title.clone(),
120            events: core::mem::take(&mut self.events),
121            native_pixels_per_point: self.native_pixels_per_point,
122            monitor_size: self.monitor_size,
123            inner_rect: self.inner_rect,
124            outer_rect: self.outer_rect,
125            minimized: self.minimized,
126            maximized: self.maximized,
127            fullscreen: self.fullscreen,
128            focused: self.focused,
129            occluded: self.occluded,
130        }
131    }
132
133    pub fn ui(&self, ui: &mut crate::Ui) {
134        let Self {
135            parent,
136            title,
137            events,
138            native_pixels_per_point,
139            monitor_size,
140            inner_rect,
141            outer_rect,
142            minimized,
143            maximized,
144            fullscreen,
145            focused,
146            occluded,
147        } = self;
148
149        crate::Grid::new("viewport_info").show(ui, |ui| {
150            ui.label("Parent:");
151            ui.label(opt_as_str(parent));
152            ui.end_row();
153
154            ui.label("Title:");
155            ui.label(opt_as_str(title));
156            ui.end_row();
157
158            ui.label("Events:");
159            ui.label(format!("{events:?}"));
160            ui.end_row();
161
162            ui.label("Native pixels-per-point:");
163            ui.label(opt_as_str(native_pixels_per_point));
164            ui.end_row();
165
166            ui.label("Monitor size:");
167            ui.label(opt_as_str(monitor_size));
168            ui.end_row();
169
170            ui.label("Inner rect:");
171            ui.label(opt_rect_as_string(inner_rect));
172            ui.end_row();
173
174            ui.label("Outer rect:");
175            ui.label(opt_rect_as_string(outer_rect));
176            ui.end_row();
177
178            ui.label("Minimized:");
179            ui.label(opt_as_str(minimized));
180            ui.end_row();
181
182            ui.label("Maximized:");
183            ui.label(opt_as_str(maximized));
184            ui.end_row();
185
186            ui.label("Fullscreen:");
187            ui.label(opt_as_str(fullscreen));
188            ui.end_row();
189
190            ui.label("Focused:");
191            ui.label(opt_as_str(focused));
192            ui.end_row();
193
194            ui.label("Occluded:");
195            ui.label(opt_as_str(occluded));
196            ui.end_row();
197
198            let visible = self.visible();
199
200            ui.label("Visible:");
201            ui.label(opt_as_str(&visible));
202            ui.end_row();
203
204            #[expect(clippy::ref_option)]
205            fn opt_rect_as_string(v: &Option<Rect>) -> String {
206                v.as_ref().map_or(String::new(), |r| {
207                    format!("Pos: {:?}, size: {:?}", r.min, r.size())
208                })
209            }
210
211            #[expect(clippy::ref_option)]
212            fn opt_as_str<T: core::fmt::Debug>(v: &Option<T>) -> String {
213                v.as_ref().map_or(String::new(), |v| format!("{v:?}"))
214            }
215        });
216    }
217}