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}