Skip to main content

egui/data/input/
raw_input.rs

1use crate::{OrderedViewportIdMap, Theme, ViewportId, ViewportIdMap, emath::Rect};
2
3use super::{DroppedFileHandle, Event, HoveredFile, SafeAreaInsets, ViewportInfo};
4
5/// What the integrations provides to egui at the start of each frame.
6///
7/// Set the values that make sense, leave the rest at their `Default::default()`.
8///
9/// You can check if `egui` is using the inputs using
10/// [`crate::Context::egui_wants_pointer_input`] and [`crate::Context::egui_wants_keyboard_input`].
11///
12/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left .corner.
13///
14/// Ii "points" can be calculated from native physical pixels
15/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `native_pixels_per_point`;
16#[derive(Clone, Debug)]
17#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
18pub struct RawInput {
19    /// The id of the active viewport.
20    pub viewport_id: ViewportId,
21
22    /// Information about all egui viewports.
23    pub viewports: ViewportIdMap<ViewportInfo>,
24
25    /// The insets used to only render content in a mobile safe area
26    ///
27    /// `None` will be treated as "same as last frame"
28    pub safe_area_insets: Option<SafeAreaInsets>,
29
30    /// Position and size of the area that egui should use, in points.
31    /// Usually you would set this to
32    ///
33    /// `Some(Rect::from_min_size(Default::default(), screen_size_in_points))`.
34    ///
35    /// but you could also constrain egui to some smaller portion of your window if you like.
36    ///
37    /// `None` will be treated as "same as last frame", with the default being a very big area.
38    pub screen_rect: Option<Rect>,
39
40    /// Maximum size of one side of the font texture.
41    ///
42    /// Ask your graphics drivers about this. This corresponds to `GL_MAX_TEXTURE_SIZE`.
43    ///
44    /// The default is a very small (but very portable) 2048.
45    pub max_texture_side: Option<usize>,
46
47    /// Monotonically increasing time, in seconds. Relative to whatever. Used for animations.
48    /// If `None` is provided, egui will assume a time delta of `predicted_dt` (default 1/60 seconds).
49    pub time: Option<f64>,
50
51    /// Should be set to the expected time between frames when painting at vsync speeds.
52    /// The default for this is 1/60.
53    /// Can safely be left at its default value.
54    pub predicted_dt: f32,
55
56    /// In-order events received this frame.
57    ///
58    /// There is currently no way to know if egui handles a particular event,
59    /// but you can check if egui is using the keyboard with [`crate::Context::egui_wants_keyboard_input`]
60    /// and/or the pointer (mouse/touch) with [`crate::Context::egui_is_using_pointer`].
61    pub events: Vec<Event>,
62
63    /// Dragged files hovering over egui.
64    pub hovered_files: Vec<HoveredFile>,
65
66    /// Dragged files dropped into egui.
67    ///
68    /// egui never reads the file contents.
69    #[cfg_attr(
70        not(target_arch = "wasm32"),
71        doc = "Call [`crate::DroppedFile::bytes`] to read a dropped file."
72    )]
73    #[cfg_attr(
74        target_arch = "wasm32",
75        doc = "Call [`crate::DroppedFile::bytes_async`] to read a dropped file."
76    )]
77    ///
78    /// Note: when using `eframe` on Windows, this will always be empty if drag-and-drop support has
79    /// been disabled in [`crate::viewport::ViewportBuilder`].
80    #[cfg_attr(feature = "serde", serde(skip))]
81    pub dropped_files: Vec<DroppedFileHandle>,
82
83    /// The native window has the keyboard focus (i.e. is receiving key presses).
84    ///
85    /// False when the user alt-tab away from the application, for instance.
86    pub focused: bool,
87
88    /// Does the OS use dark or light mode?
89    ///
90    /// `None` means "don't know".
91    pub system_theme: Option<Theme>,
92}
93
94impl Default for RawInput {
95    fn default() -> Self {
96        Self {
97            viewport_id: ViewportId::ROOT,
98            viewports: core::iter::once((ViewportId::ROOT, Default::default())).collect(),
99            screen_rect: None,
100            max_texture_side: None,
101            time: None,
102            predicted_dt: 1.0 / 60.0,
103            events: vec![],
104            hovered_files: Default::default(),
105            dropped_files: Default::default(),
106            focused: true, // integrations opt into global focus tracking
107            system_theme: None,
108            safe_area_insets: Default::default(),
109        }
110    }
111}
112
113impl RawInput {
114    /// Info about the active viewport
115    #[inline]
116    pub fn viewport(&self) -> &ViewportInfo {
117        self.viewports.get(&self.viewport_id).expect("Failed to find current viewport in egui RawInput. This is the fault of the egui backend")
118    }
119
120    /// Helper: move volatile (deltas and events), clone the rest.
121    ///
122    /// * [`Self::hovered_files`] is cloned.
123    /// * [`Self::dropped_files`] is moved.
124    pub fn take(&mut self) -> Self {
125        Self {
126            viewport_id: self.viewport_id,
127            viewports: self
128                .viewports
129                .iter_mut()
130                .map(|(id, info)| (*id, info.take()))
131                .collect(),
132            screen_rect: self.screen_rect.take(),
133            safe_area_insets: self.safe_area_insets.take(),
134            max_texture_side: self.max_texture_side.take(),
135            time: self.time,
136            predicted_dt: self.predicted_dt,
137            events: core::mem::take(&mut self.events),
138            hovered_files: self.hovered_files.clone(),
139            dropped_files: core::mem::take(&mut self.dropped_files),
140            focused: self.focused,
141            system_theme: self.system_theme,
142        }
143    }
144
145    /// Add on new input.
146    pub fn append(&mut self, newer: Self) {
147        let Self {
148            viewport_id: viewport_ids,
149            viewports,
150            screen_rect,
151            max_texture_side,
152            time,
153            predicted_dt,
154            mut events,
155            mut hovered_files,
156            mut dropped_files,
157            focused,
158            system_theme,
159            safe_area_insets: safe_area,
160        } = newer;
161
162        self.viewport_id = viewport_ids;
163        self.viewports = viewports;
164        self.screen_rect = screen_rect.or(self.screen_rect);
165        self.max_texture_side = max_texture_side.or(self.max_texture_side);
166        self.time = time; // use latest time
167        self.predicted_dt = predicted_dt; // use latest dt
168        self.events.append(&mut events);
169        self.hovered_files.append(&mut hovered_files);
170        self.dropped_files.append(&mut dropped_files);
171        self.focused = focused;
172        self.system_theme = system_theme;
173        self.safe_area_insets = safe_area;
174    }
175}
176
177impl RawInput {
178    pub fn ui(&self, ui: &mut crate::Ui) {
179        let Self {
180            viewport_id,
181            viewports,
182            screen_rect,
183            max_texture_side,
184            time,
185            predicted_dt,
186            events,
187            hovered_files,
188            dropped_files,
189            focused,
190            system_theme,
191            safe_area_insets: safe_area,
192        } = self;
193
194        ui.label(format!("Active viewport: {viewport_id:?}"));
195        let ordered_viewports = viewports
196            .iter()
197            .map(|(id, value)| (*id, value))
198            .collect::<OrderedViewportIdMap<_>>();
199        for (id, viewport) in ordered_viewports {
200            ui.group(|ui| {
201                ui.label(format!("Viewport {id:?}"));
202                ui.push_id(id, |ui| {
203                    viewport.ui(ui);
204                });
205            });
206        }
207        ui.label(format!("screen_rect: {screen_rect:?} points"));
208
209        ui.label(format!("max_texture_side: {max_texture_side:?}"));
210        if let Some(time) = time {
211            ui.label(format!("time: {time:.3} s"));
212        } else {
213            ui.label("time: None");
214        }
215        ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
216        ui.label(format!("hovered_files: {}", hovered_files.len()));
217        ui.label(format!("dropped_files: {}", dropped_files.len()));
218        ui.label(format!("focused: {focused}"));
219        ui.label(format!("system_theme: {system_theme:?}"));
220        ui.label(format!("safe_area: {safe_area:?}"));
221        ui.scope(|ui| {
222            ui.set_min_height(150.0);
223            ui.label(format!("events: {events:#?}"))
224                .on_hover_text("key presses etc");
225        });
226    }
227}