Skip to main content

egui/data/
input.rs

1//! The input needed by egui.
2
3use epaint::{ColorImage, MarginF32};
4
5use crate::{
6    Key, OrderedViewportIdMap, Theme, ViewportId, ViewportIdMap,
7    emath::{Pos2, Rect, Vec2},
8};
9
10/// What the integrations provides to egui at the start of each frame.
11///
12/// Set the values that make sense, leave the rest at their `Default::default()`.
13///
14/// You can check if `egui` is using the inputs using
15/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`].
16///
17/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left .corner.
18///
19/// Ii "points" can be calculated from native physical pixels
20/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `native_pixels_per_point`;
21#[derive(Clone, Debug, PartialEq)]
22#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
23pub struct RawInput {
24    /// The id of the active viewport.
25    pub viewport_id: ViewportId,
26
27    /// Information about all egui viewports.
28    pub viewports: ViewportIdMap<ViewportInfo>,
29
30    /// The insets used to only render content in a mobile safe area
31    ///
32    /// `None` will be treated as "same as last frame"
33    pub safe_area_insets: Option<SafeAreaInsets>,
34
35    /// Position and size of the area that egui should use, in points.
36    /// Usually you would set this to
37    ///
38    /// `Some(Rect::from_min_size(Default::default(), screen_size_in_points))`.
39    ///
40    /// but you could also constrain egui to some smaller portion of your window if you like.
41    ///
42    /// `None` will be treated as "same as last frame", with the default being a very big area.
43    pub screen_rect: Option<Rect>,
44
45    /// Maximum size of one side of the font texture.
46    ///
47    /// Ask your graphics drivers about this. This corresponds to `GL_MAX_TEXTURE_SIZE`.
48    ///
49    /// The default is a very small (but very portable) 2048.
50    pub max_texture_side: Option<usize>,
51
52    /// Monotonically increasing time, in seconds. Relative to whatever. Used for animations.
53    /// If `None` is provided, egui will assume a time delta of `predicted_dt` (default 1/60 seconds).
54    pub time: Option<f64>,
55
56    /// Should be set to the expected time between frames when painting at vsync speeds.
57    /// The default for this is 1/60.
58    /// Can safely be left at its default value.
59    pub predicted_dt: f32,
60
61    /// Which modifier keys are down at the start of the frame?
62    pub modifiers: Modifiers,
63
64    /// In-order events received this frame.
65    ///
66    /// There is currently no way to know if egui handles a particular event,
67    /// but you can check if egui is using the keyboard with [`crate::Context::wants_keyboard_input`]
68    /// and/or the pointer (mouse/touch) with [`crate::Context::is_using_pointer`].
69    pub events: Vec<Event>,
70
71    /// Dragged files hovering over egui.
72    pub hovered_files: Vec<HoveredFile>,
73
74    /// Dragged files dropped into egui.
75    ///
76    /// Note: when using `eframe` on Windows, this will always be empty if drag-and-drop support has
77    /// been disabled in [`crate::viewport::ViewportBuilder`].
78    pub dropped_files: Vec<DroppedFile>,
79
80    /// The native window has the keyboard focus (i.e. is receiving key presses).
81    ///
82    /// False when the user alt-tab away from the application, for instance.
83    pub focused: bool,
84
85    /// Does the OS use dark or light mode?
86    ///
87    /// `None` means "don't know".
88    pub system_theme: Option<Theme>,
89}
90
91impl Default for RawInput {
92    fn default() -> Self {
93        Self {
94            viewport_id: ViewportId::ROOT,
95            viewports: std::iter::once((ViewportId::ROOT, Default::default())).collect(),
96            screen_rect: None,
97            max_texture_side: None,
98            time: None,
99            predicted_dt: 1.0 / 60.0,
100            modifiers: Modifiers::default(),
101            events: vec![],
102            hovered_files: Default::default(),
103            dropped_files: Default::default(),
104            focused: true, // integrations opt into global focus tracking
105            system_theme: None,
106            safe_area_insets: Default::default(),
107        }
108    }
109}
110
111impl RawInput {
112    /// Info about the active viewport
113    #[inline]
114    pub fn viewport(&self) -> &ViewportInfo {
115        self.viewports.get(&self.viewport_id).expect("Failed to find current viewport in egui RawInput. This is the fault of the egui backend")
116    }
117
118    /// Helper: move volatile (deltas and events), clone the rest.
119    ///
120    /// * [`Self::hovered_files`] is cloned.
121    /// * [`Self::dropped_files`] is moved.
122    pub fn take(&mut self) -> Self {
123        Self {
124            viewport_id: self.viewport_id,
125            viewports: self
126                .viewports
127                .iter_mut()
128                .map(|(id, info)| (*id, info.take()))
129                .collect(),
130            screen_rect: self.screen_rect.take(),
131            safe_area_insets: self.safe_area_insets.take(),
132            max_texture_side: self.max_texture_side.take(),
133            time: self.time,
134            predicted_dt: self.predicted_dt,
135            modifiers: self.modifiers,
136            events: std::mem::take(&mut self.events),
137            hovered_files: self.hovered_files.clone(),
138            dropped_files: std::mem::take(&mut self.dropped_files),
139            focused: self.focused,
140            system_theme: self.system_theme,
141        }
142    }
143
144    /// Add on new input.
145    pub fn append(&mut self, newer: Self) {
146        let Self {
147            viewport_id: viewport_ids,
148            viewports,
149            screen_rect,
150            max_texture_side,
151            time,
152            predicted_dt,
153            modifiers,
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.modifiers = modifiers; // use latest
169        self.events.append(&mut events);
170        self.hovered_files.append(&mut hovered_files);
171        self.dropped_files.append(&mut dropped_files);
172        self.focused = focused;
173        self.system_theme = system_theme;
174        self.safe_area_insets = safe_area;
175    }
176}
177
178/// An input event from the backend into egui, about a specific [viewport](crate::viewport).
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
180#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
181pub enum ViewportEvent {
182    /// The user clicked the close-button on the window, or similar.
183    ///
184    /// If this is the root viewport, the application will exit
185    /// after this frame unless you send a
186    /// [`crate::ViewportCommand::CancelClose`] command.
187    ///
188    /// If this is not the root viewport,
189    /// it is up to the user to hide this viewport the next frame.
190    ///
191    /// This even will wake up both the child and parent viewport.
192    Close,
193}
194
195/// Information about the current viewport, given as input each frame.
196///
197/// `None` means "unknown".
198///
199/// All units are in ui "points", which can be calculated from native physical pixels
200/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `[Self::native_pixels_per_point`];
201#[derive(Clone, Debug, Default, PartialEq)]
202#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
203pub struct ViewportInfo {
204    /// Parent viewport, if known.
205    pub parent: Option<crate::ViewportId>,
206
207    /// Name of the viewport, if known.
208    pub title: Option<String>,
209
210    pub events: Vec<ViewportEvent>,
211
212    /// The OS native pixels-per-point.
213    ///
214    /// This should always be set, if known.
215    ///
216    /// On web this takes browser scaling into account,
217    /// and corresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.
218    pub native_pixels_per_point: Option<f32>,
219
220    /// Current monitor size in egui points.
221    pub monitor_size: Option<Vec2>,
222
223    /// The inner rectangle of the native window, in monitor space and ui points scale.
224    ///
225    /// This is the content rectangle of the viewport.
226    ///
227    /// **`eframe` notes**:
228    ///
229    /// On Android / Wayland, this will always be `None` since getting the
230    /// position of the window is not possible.
231    pub inner_rect: Option<Rect>,
232
233    /// The outer rectangle of the native window, in monitor space and ui points scale.
234    ///
235    /// This is the content rectangle plus decoration chrome.
236    ///
237    /// **`eframe` notes**:
238    ///
239    /// On Android / Wayland, this will always be `None` since getting the
240    /// position of the window is not possible.
241    pub outer_rect: Option<Rect>,
242
243    /// Are we minimized?
244    pub minimized: Option<bool>,
245
246    /// Are we maximized?
247    pub maximized: Option<bool>,
248
249    /// Are we in fullscreen mode?
250    pub fullscreen: Option<bool>,
251
252    /// Is the window focused and able to receive input?
253    ///
254    /// This should be the same as [`RawInput::focused`].
255    pub focused: Option<bool>,
256
257    /// Is the window fully occluded (completely covered) by another window?
258    ///
259    /// Not all platforms support this.
260    /// On platforms that don't, this will be `None` or `Some(false)`.
261    pub occluded: Option<bool>,
262}
263
264impl ViewportInfo {
265    /// Is the window considered visible for rendering purposes?
266    ///
267    /// A window is not visible if it is minimized or occluded.
268    /// When not visible, the UI is not painted and rendering is skipped,
269    /// but application logic may still be executed by some integrations.
270    pub fn visible(&self) -> Option<bool> {
271        match (self.minimized, self.occluded) {
272            (Some(true), _) | (_, Some(true)) => Some(false),
273            (Some(false), Some(false)) => Some(true),
274            (_, None) | (None, _) => None,
275        }
276    }
277
278    /// This viewport has been told to close.
279    ///
280    /// If this is the root viewport, the application will exit
281    /// after this frame unless you send a
282    /// [`crate::ViewportCommand::CancelClose`] command.
283    ///
284    /// If this is not the root viewport,
285    /// it is up to the user to hide this viewport the next frame.
286    pub fn close_requested(&self) -> bool {
287        self.events.contains(&ViewportEvent::Close)
288    }
289
290    /// Helper: move [`Self::events`], clone the other fields.
291    pub fn take(&mut self) -> Self {
292        Self {
293            parent: self.parent,
294            title: self.title.clone(),
295            events: std::mem::take(&mut self.events),
296            native_pixels_per_point: self.native_pixels_per_point,
297            monitor_size: self.monitor_size,
298            inner_rect: self.inner_rect,
299            outer_rect: self.outer_rect,
300            minimized: self.minimized,
301            maximized: self.maximized,
302            fullscreen: self.fullscreen,
303            focused: self.focused,
304            occluded: self.occluded,
305        }
306    }
307
308    pub fn ui(&self, ui: &mut crate::Ui) {
309        let Self {
310            parent,
311            title,
312            events,
313            native_pixels_per_point,
314            monitor_size,
315            inner_rect,
316            outer_rect,
317            minimized,
318            maximized,
319            fullscreen,
320            focused,
321            occluded,
322        } = self;
323
324        crate::Grid::new("viewport_info").show(ui, |ui| {
325            ui.label("Parent:");
326            ui.label(opt_as_str(parent));
327            ui.end_row();
328
329            ui.label("Title:");
330            ui.label(opt_as_str(title));
331            ui.end_row();
332
333            ui.label("Events:");
334            ui.label(format!("{events:?}"));
335            ui.end_row();
336
337            ui.label("Native pixels-per-point:");
338            ui.label(opt_as_str(native_pixels_per_point));
339            ui.end_row();
340
341            ui.label("Monitor size:");
342            ui.label(opt_as_str(monitor_size));
343            ui.end_row();
344
345            ui.label("Inner rect:");
346            ui.label(opt_rect_as_string(inner_rect));
347            ui.end_row();
348
349            ui.label("Outer rect:");
350            ui.label(opt_rect_as_string(outer_rect));
351            ui.end_row();
352
353            ui.label("Minimized:");
354            ui.label(opt_as_str(minimized));
355            ui.end_row();
356
357            ui.label("Maximized:");
358            ui.label(opt_as_str(maximized));
359            ui.end_row();
360
361            ui.label("Fullscreen:");
362            ui.label(opt_as_str(fullscreen));
363            ui.end_row();
364
365            ui.label("Focused:");
366            ui.label(opt_as_str(focused));
367            ui.end_row();
368
369            ui.label("Occluded:");
370            ui.label(opt_as_str(occluded));
371            ui.end_row();
372
373            let visible = self.visible();
374
375            ui.label("Visible:");
376            ui.label(opt_as_str(&visible));
377            ui.end_row();
378
379            fn opt_rect_as_string(v: &Option<Rect>) -> String {
380                v.as_ref().map_or(String::new(), |r| {
381                    format!("Pos: {:?}, size: {:?}", r.min, r.size())
382                })
383            }
384
385            fn opt_as_str<T: std::fmt::Debug>(v: &Option<T>) -> String {
386                v.as_ref().map_or(String::new(), |v| format!("{v:?}"))
387            }
388        });
389    }
390}
391
392/// A file about to be dropped into egui.
393#[derive(Clone, Debug, Default, PartialEq, Eq)]
394#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
395pub struct HoveredFile {
396    /// Set by the `egui-winit` backend.
397    pub path: Option<std::path::PathBuf>,
398
399    /// With the `eframe` web backend, this is set to the mime-type of the file (if available).
400    pub mime: String,
401}
402
403/// A file dropped into egui.
404#[derive(Clone, Debug, Default, PartialEq, Eq)]
405#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
406pub struct DroppedFile {
407    /// Set by the `egui-winit` backend.
408    pub path: Option<std::path::PathBuf>,
409
410    /// Name of the file. Set by the `eframe` web backend.
411    pub name: String,
412
413    /// With the `eframe` web backend, this is set to the mime-type of the file (if available).
414    pub mime: String,
415
416    /// Set by the `eframe` web backend.
417    pub last_modified: Option<std::time::SystemTime>,
418
419    /// Set by the `eframe` web backend.
420    pub bytes: Option<std::sync::Arc<[u8]>>,
421}
422
423/// An input event generated by the integration.
424///
425/// This only covers events that egui cares about.
426#[derive(Clone, Debug, PartialEq)]
427#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
428pub enum Event {
429    /// The integration detected a "copy" event (e.g. Cmd+C).
430    Copy,
431
432    /// The integration detected a "cut" event (e.g. Cmd+X).
433    Cut,
434
435    /// The integration detected a "paste" event (e.g. Cmd+V).
436    Paste(String),
437
438    /// Text input, e.g. via keyboard.
439    ///
440    /// When the user presses enter/return, do not send a [`Text`](Event::Text) (just [`Key::Enter`]).
441    Text(String),
442
443    /// A key was pressed or released.
444    ///
445    /// ## Note for integration authors
446    ///
447    /// Key events that has been processed by IMEs should not be sent to `egui`.
448    Key {
449        /// Most of the time, it's the logical key, heeding the active keymap -- for instance, if the user has Dvorak
450        /// keyboard layout, it will be taken into account.
451        ///
452        /// If it's impossible to determine the logical key on desktop platforms (say, in case of non-Latin letters),
453        /// `key` falls back to the value of the corresponding physical key. This is necessary for proper work of
454        /// standard shortcuts that only respond to Latin-based bindings (such as `Ctrl` + `V`).
455        key: Key,
456
457        /// The physical key, corresponding to the actual position on the keyboard.
458        ///
459        /// This ignores keymaps, so it is not recommended to use this.
460        /// The only thing it makes sense for is things like games,
461        /// where e.g. the physical location of WSAD on QWERTY should always map to movement,
462        /// even if the user is using Dvorak or AZERTY.
463        ///
464        /// `eframe` does not (yet) implement this on web.
465        physical_key: Option<Key>,
466
467        /// Was it pressed or released?
468        pressed: bool,
469
470        /// If this is a `pressed` event, is it a key-repeat?
471        ///
472        /// On many platforms, holding down a key produces many repeated "pressed" events for it, so called key-repeats.
473        /// Sometimes you will want to ignore such events, and this lets you do that.
474        ///
475        /// egui will automatically detect such repeat events and mark them as such here.
476        /// Therefore, if you are writing an egui integration, you do not need to set this (just set it to `false`).
477        repeat: bool,
478
479        /// The state of the modifier keys at the time of the event.
480        modifiers: Modifiers,
481    },
482
483    /// The mouse or touch moved to a new place.
484    PointerMoved(Pos2),
485
486    /// The mouse moved, the units are unspecified.
487    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
488    /// `PointerMoved` and `MouseMoved` can be sent at the same time.
489    /// This event is optional. If the integration can not determine unfiltered motion it should not send this event.
490    MouseMoved(Vec2),
491
492    /// A mouse button was pressed or released (or a touch started or stopped).
493    PointerButton {
494        /// Where is the pointer?
495        pos: Pos2,
496
497        /// What mouse button? For touches, use [`PointerButton::Primary`].
498        button: PointerButton,
499
500        /// Was it the button/touch pressed this frame, or released?
501        pressed: bool,
502
503        /// The state of the modifier keys at the time of the event.
504        modifiers: Modifiers,
505    },
506
507    /// The mouse left the screen, or the last/primary touch input disappeared.
508    ///
509    /// This means there is no longer a cursor on the screen for hovering etc.
510    ///
511    /// On touch-up first send `PointerButton{pressed: false, …}` followed by `PointerLeft`.
512    PointerGone,
513
514    /// Zoom scale factor this frame (e.g. from a pinch gesture).
515    ///
516    /// * `zoom = 1`: no change.
517    /// * `zoom < 1`: pinch together
518    /// * `zoom > 1`: pinch spread
519    ///
520    /// Note that egui also implement zooming by holding `Ctrl` and scrolling the mouse wheel,
521    /// so integration need NOT emit this `Zoom` event in those cases, just [`Self::MouseWheel`].
522    ///
523    /// As a user, check [`crate::InputState::smooth_scroll_delta`] to see if the user did any zooming this frame.
524    Zoom(f32),
525
526    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).
527    Rotate(f32),
528
529    /// IME Event
530    Ime(ImeEvent),
531
532    /// On touch screens, report this *in addition to*
533    /// [`Self::PointerMoved`], [`Self::PointerButton`], [`Self::PointerGone`]
534    Touch {
535        /// Hashed device identifier (if available; may be zero).
536        /// Can be used to separate touches from different devices.
537        device_id: TouchDeviceId,
538
539        /// Unique identifier of a finger/pen. Value is stable from touch down
540        /// to lift-up
541        id: TouchId,
542
543        /// One of: start move end cancel.
544        phase: TouchPhase,
545
546        /// Position of the touch (or where the touch was last detected)
547        pos: Pos2,
548
549        /// Describes how hard the touch device was pressed. May always be `None` if the platform does
550        /// not support pressure sensitivity.
551        /// The value is in the range from 0.0 (no pressure) to 1.0 (maximum pressure).
552        force: Option<f32>,
553    },
554
555    /// A raw mouse wheel event as sent by the backend.
556    ///
557    /// Used for scrolling.
558    MouseWheel {
559        /// The unit of `delta`: points, lines, or pages.
560        unit: MouseWheelUnit,
561
562        /// The direction of the vector indicates how to move the _content_ that is being viewed.
563        /// So if you get positive values, the content being viewed should move to the right and down,
564        /// revealing new things to the left and up.
565        ///
566        /// A positive X-value indicates the content is being moved right,
567        /// as when swiping right on a touch-screen or track-pad with natural scrolling.
568        ///
569        /// A positive Y-value indicates the content is being moved down,
570        /// as when swiping down on a touch-screen or track-pad with natural scrolling.
571        delta: Vec2,
572
573        /// The phase of the scroll, useful for trackpads.
574        ///
575        /// If unknown set this to [`TouchPhase::Move`].
576        phase: TouchPhase,
577
578        /// The state of the modifier keys at the time of the event.
579        modifiers: Modifiers,
580    },
581
582    /// The native window gained or lost focused (e.g. the user clicked alt-tab).
583    WindowFocused(bool),
584
585    /// An assistive technology (e.g. screen reader) requested an action.
586    AccessKitActionRequest(accesskit::ActionRequest),
587
588    /// The reply of a screenshot requested with [`crate::ViewportCommand::Screenshot`].
589    Screenshot {
590        viewport_id: crate::ViewportId,
591
592        /// Whatever was passed to [`crate::ViewportCommand::Screenshot`].
593        user_data: crate::UserData,
594
595        image: std::sync::Arc<ColorImage>,
596    },
597}
598
599/// IME event.
600///
601/// See <https://docs.rs/winit/latest/winit/event/enum.Ime.html>
602#[derive(Clone, Debug, Eq, PartialEq)]
603#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
604pub enum ImeEvent {
605    /// Notifies when the IME was enabled.
606    Enabled,
607
608    /// A new IME candidate is being suggested.
609    Preedit(String),
610
611    /// IME composition ended with this final result.
612    Commit(String),
613
614    /// Notifies when the IME was disabled.
615    Disabled,
616}
617
618/// Mouse button (or similar for touch input)
619#[derive(Clone, Copy, Debug, Eq, PartialEq)]
620#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
621pub enum PointerButton {
622    /// The primary mouse button is usually the left one.
623    Primary = 0,
624
625    /// The secondary mouse button is usually the right one,
626    /// and most often used for context menus or other optional things.
627    Secondary = 1,
628
629    /// The tertiary mouse button is usually the middle mouse button (e.g. clicking the scroll wheel).
630    Middle = 2,
631
632    /// The first extra mouse button on some mice. In web typically corresponds to the Browser back button.
633    Extra1 = 3,
634
635    /// The second extra mouse button on some mice. In web typically corresponds to the Browser forward button.
636    Extra2 = 4,
637}
638
639/// Number of pointer buttons supported by egui, i.e. the number of possible states of [`PointerButton`].
640pub const NUM_POINTER_BUTTONS: usize = 5;
641
642/// State of the modifier keys. These must be fed to egui.
643///
644/// The best way to compare [`Modifiers`] is by using [`Modifiers::matches_logically`] or [`Modifiers::matches_exact`].
645///
646/// To access the [`Modifiers`] you can use the [`crate::Context::input`] function
647///
648/// ```rust
649/// # let ctx = egui::Context::default();
650/// let modifiers = ctx.input(|i| i.modifiers);
651/// ```
652///
653/// NOTE: For cross-platform uses, ALT+SHIFT is a bad combination of modifiers
654/// as on mac that is how you type special characters,
655/// so those key presses are usually not reported to egui.
656#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
657#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
658pub struct Modifiers {
659    /// Either of the alt keys are down (option ⌥ on Mac).
660    pub alt: bool,
661
662    /// Either of the control keys are down.
663    /// When checking for keyboard shortcuts, consider using [`Self::command`] instead.
664    pub ctrl: bool,
665
666    /// Either of the shift keys are down.
667    pub shift: bool,
668
669    /// The Mac ⌘ Command key. Should always be set to `false` on other platforms.
670    pub mac_cmd: bool,
671
672    /// On Windows and Linux, set this to the same value as `ctrl`.
673    /// On Mac, this should be set whenever one of the ⌘ Command keys are down (same as `mac_cmd`).
674    /// This is so that egui can, for instance, select all text by checking for `command + A`
675    /// and it will work on both Mac and Windows.
676    pub command: bool,
677}
678
679impl std::fmt::Debug for Modifiers {
680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
681        if self.is_none() {
682            return write!(f, "Modifiers::NONE");
683        }
684
685        let Self {
686            alt,
687            ctrl,
688            shift,
689            mac_cmd,
690            command,
691        } = *self;
692
693        let mut debug = f.debug_struct("Modifiers");
694        if alt {
695            debug.field("alt", &true);
696        }
697        if ctrl {
698            debug.field("ctrl", &true);
699        }
700        if shift {
701            debug.field("shift", &true);
702        }
703        if mac_cmd {
704            debug.field("mac_cmd", &true);
705        }
706        if command {
707            debug.field("command", &true);
708        }
709        debug.finish()
710    }
711}
712
713impl Modifiers {
714    pub const NONE: Self = Self {
715        alt: false,
716        ctrl: false,
717        shift: false,
718        mac_cmd: false,
719        command: false,
720    };
721
722    pub const ALT: Self = Self {
723        alt: true,
724        ctrl: false,
725        shift: false,
726        mac_cmd: false,
727        command: false,
728    };
729    pub const CTRL: Self = Self {
730        alt: false,
731        ctrl: true,
732        shift: false,
733        mac_cmd: false,
734        command: false,
735    };
736    pub const SHIFT: Self = Self {
737        alt: false,
738        ctrl: false,
739        shift: true,
740        mac_cmd: false,
741        command: false,
742    };
743
744    /// The Mac ⌘ Command key
745    pub const MAC_CMD: Self = Self {
746        alt: false,
747        ctrl: false,
748        shift: false,
749        mac_cmd: true,
750        command: false,
751    };
752
753    /// On Mac: ⌘ Command key, elsewhere: Ctrl key
754    pub const COMMAND: Self = Self {
755        alt: false,
756        ctrl: false,
757        shift: false,
758        mac_cmd: false,
759        command: true,
760    };
761
762    /// ```
763    /// # use egui::Modifiers;
764    /// assert_eq!(
765    ///     Modifiers::CTRL | Modifiers::ALT,
766    ///     Modifiers { ctrl: true, alt: true, ..Default::default() }
767    /// );
768    /// assert_eq!(
769    ///     Modifiers::ALT.plus(Modifiers::CTRL),
770    ///     Modifiers::CTRL.plus(Modifiers::ALT),
771    /// );
772    /// assert_eq!(
773    ///     Modifiers::CTRL | Modifiers::ALT,
774    ///     Modifiers::CTRL.plus(Modifiers::ALT),
775    /// );
776    /// ```
777    #[inline]
778    pub const fn plus(self, rhs: Self) -> Self {
779        Self {
780            alt: self.alt | rhs.alt,
781            ctrl: self.ctrl | rhs.ctrl,
782            shift: self.shift | rhs.shift,
783            mac_cmd: self.mac_cmd | rhs.mac_cmd,
784            command: self.command | rhs.command,
785        }
786    }
787
788    #[inline]
789    pub fn is_none(&self) -> bool {
790        self == &Self::default()
791    }
792
793    #[inline]
794    pub fn any(&self) -> bool {
795        !self.is_none()
796    }
797
798    #[inline]
799    pub fn all(&self) -> bool {
800        self.alt && self.ctrl && self.shift && self.command
801    }
802
803    /// Is shift the only pressed button?
804    #[inline]
805    pub fn shift_only(&self) -> bool {
806        self.shift && !(self.alt || self.command)
807    }
808
809    /// true if only [`Self::ctrl`] or only [`Self::mac_cmd`] is pressed.
810    #[inline]
811    pub fn command_only(&self) -> bool {
812        !self.alt && !self.shift && self.command
813    }
814
815    /// Checks that the `ctrl/cmd` matches, and that the `shift/alt` of the argument is a subset
816    /// of the pressed key (`self`).
817    ///
818    /// This means that if the pattern has not set `shift`, then `self` can have `shift` set or not.
819    ///
820    /// The reason is that many logical keys require `shift` or `alt` on some keyboard layouts.
821    /// For instance, in order to press `+` on an English keyboard, you need to press `shift` and `=`,
822    /// but a Swedish keyboard has dedicated `+` key.
823    /// So if you want to make a [`KeyboardShortcut`] looking for `Cmd` + `+`, it makes sense
824    /// to ignore the shift key.
825    /// Similarly, the `Alt` key is sometimes used to type special characters.
826    ///
827    /// However, if the pattern (the argument) explicitly requires the `shift` or `alt` keys
828    /// to be pressed, then they must be pressed.
829    ///
830    /// # Example:
831    /// ```
832    /// # use egui::Modifiers;
833    /// # let pressed_modifiers = Modifiers::default();
834    /// if pressed_modifiers.matches_logically(Modifiers::ALT | Modifiers::SHIFT) {
835    ///     // Alt and Shift are pressed, but not ctrl/command
836    /// }
837    /// ```
838    ///
839    /// ## Behavior:
840    /// ```
841    /// # use egui::Modifiers;
842    /// assert!(Modifiers::CTRL.matches_logically(Modifiers::CTRL));
843    /// assert!(!Modifiers::CTRL.matches_logically(Modifiers::CTRL | Modifiers::SHIFT));
844    /// assert!((Modifiers::CTRL | Modifiers::SHIFT).matches_logically(Modifiers::CTRL));
845    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_logically(Modifiers::CTRL));
846    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_logically(Modifiers::COMMAND));
847    /// assert!((Modifiers::MAC_CMD | Modifiers::COMMAND).matches_logically(Modifiers::COMMAND));
848    /// assert!(!Modifiers::COMMAND.matches_logically(Modifiers::MAC_CMD));
849    /// ```
850    pub fn matches_logically(&self, pattern: Self) -> bool {
851        if pattern.alt && !self.alt {
852            return false;
853        }
854        if pattern.shift && !self.shift {
855            return false;
856        }
857
858        self.cmd_ctrl_matches(pattern)
859    }
860
861    /// Check for equality but with proper handling of [`Self::command`].
862    ///
863    /// `self` here are the currently pressed modifiers,
864    /// and the argument the pattern we are testing for.
865    ///
866    /// Note that this will require the `shift` and `alt` keys match, even though
867    /// these modifiers are sometimes required to produce some logical keys.
868    /// For instance, to press `+` on an English keyboard, you need to press `shift` and `=`,
869    /// but on a Swedish keyboard you can press the dedicated `+` key.
870    /// Therefore, you often want to use [`Self::matches_logically`] instead.
871    ///
872    /// # Example:
873    /// ```
874    /// # use egui::Modifiers;
875    /// # let pressed_modifiers = Modifiers::default();
876    /// if pressed_modifiers.matches_exact(Modifiers::ALT | Modifiers::SHIFT) {
877    ///     // Alt and Shift are pressed, and nothing else
878    /// }
879    /// ```
880    ///
881    /// ## Behavior:
882    /// ```
883    /// # use egui::Modifiers;
884    /// assert!(Modifiers::CTRL.matches_exact(Modifiers::CTRL));
885    /// assert!(!Modifiers::CTRL.matches_exact(Modifiers::CTRL | Modifiers::SHIFT));
886    /// assert!(!(Modifiers::CTRL | Modifiers::SHIFT).matches_exact(Modifiers::CTRL));
887    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_exact(Modifiers::CTRL));
888    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_exact(Modifiers::COMMAND));
889    /// assert!((Modifiers::MAC_CMD | Modifiers::COMMAND).matches_exact(Modifiers::COMMAND));
890    /// assert!(!Modifiers::COMMAND.matches_exact(Modifiers::MAC_CMD));
891    /// ```
892    pub fn matches_exact(&self, pattern: Self) -> bool {
893        // alt and shift must always match the pattern:
894        if pattern.alt != self.alt || pattern.shift != self.shift {
895            return false;
896        }
897
898        self.cmd_ctrl_matches(pattern)
899    }
900
901    /// Check if any of the modifiers match exactly.
902    ///
903    /// Returns true if the same modifier is pressed in `self` as in `pattern`,
904    /// for at least one modifier.
905    ///
906    /// ## Behavior:
907    /// ```
908    /// # use egui::Modifiers;
909    /// assert!(Modifiers::CTRL.matches_any(Modifiers::CTRL));
910    /// assert!(Modifiers::CTRL.matches_any(Modifiers::CTRL | Modifiers::SHIFT));
911    /// assert!((Modifiers::CTRL | Modifiers::SHIFT).matches_any(Modifiers::CTRL));
912    /// ```
913    pub fn matches_any(&self, pattern: Self) -> bool {
914        if self.alt && pattern.alt {
915            return true;
916        }
917        if self.shift && pattern.shift {
918            return true;
919        }
920        if self.ctrl && pattern.ctrl {
921            return true;
922        }
923        if self.mac_cmd && pattern.mac_cmd {
924            return true;
925        }
926        if (self.mac_cmd || self.command || self.ctrl) && pattern.command {
927            return true;
928        }
929        false
930    }
931
932    /// Checks only cmd/ctrl, not alt/shift.
933    ///
934    /// `self` here are the currently pressed modifiers,
935    /// and the argument the pattern we are testing for.
936    ///
937    /// This takes care to properly handle the difference between
938    /// [`Self::ctrl`], [`Self::command`] and [`Self::mac_cmd`].
939    pub fn cmd_ctrl_matches(&self, pattern: Self) -> bool {
940        if pattern.mac_cmd {
941            // Mac-specific match:
942            if !self.mac_cmd {
943                return false;
944            }
945            if pattern.ctrl != self.ctrl {
946                return false;
947            }
948            return true;
949        }
950
951        if !pattern.ctrl && !pattern.command {
952            // the pattern explicitly doesn't want any ctrl/command:
953            return !self.ctrl && !self.command;
954        }
955
956        // if the pattern is looking for command, then `ctrl` may or may not be set depending on platform.
957        // if the pattern is looking for `ctrl`, then `command` may or may not be set depending on platform.
958
959        if pattern.ctrl && !self.ctrl {
960            return false;
961        }
962        if pattern.command && !self.command {
963            return false;
964        }
965
966        true
967    }
968
969    /// Whether another set of modifiers is contained in this set of modifiers with proper handling of [`Self::command`].
970    ///
971    /// ```
972    /// # use egui::Modifiers;
973    /// assert!(Modifiers::default().contains(Modifiers::default()));
974    /// assert!(Modifiers::CTRL.contains(Modifiers::default()));
975    /// assert!(Modifiers::CTRL.contains(Modifiers::CTRL));
976    /// assert!(Modifiers::CTRL.contains(Modifiers::COMMAND));
977    /// assert!(Modifiers::MAC_CMD.contains(Modifiers::COMMAND));
978    /// assert!(Modifiers::COMMAND.contains(Modifiers::MAC_CMD));
979    /// assert!(Modifiers::COMMAND.contains(Modifiers::CTRL));
980    /// assert!(!(Modifiers::ALT | Modifiers::CTRL).contains(Modifiers::SHIFT));
981    /// assert!((Modifiers::CTRL | Modifiers::SHIFT).contains(Modifiers::CTRL));
982    /// assert!(!Modifiers::CTRL.contains(Modifiers::CTRL | Modifiers::SHIFT));
983    /// ```
984    pub fn contains(&self, query: Self) -> bool {
985        if query == Self::default() {
986            return true;
987        }
988
989        let Self {
990            alt,
991            ctrl,
992            shift,
993            mac_cmd,
994            command,
995        } = *self;
996
997        if alt && query.alt {
998            return self.contains(Self {
999                alt: false,
1000                ..query
1001            });
1002        }
1003        if shift && query.shift {
1004            return self.contains(Self {
1005                shift: false,
1006                ..query
1007            });
1008        }
1009
1010        if (ctrl || command) && (query.ctrl || query.command) {
1011            return self.contains(Self {
1012                command: false,
1013                ctrl: false,
1014                ..query
1015            });
1016        }
1017        if (mac_cmd || command) && (query.mac_cmd || query.command) {
1018            return self.contains(Self {
1019                mac_cmd: false,
1020                command: false,
1021                ..query
1022            });
1023        }
1024
1025        false
1026    }
1027}
1028
1029impl std::ops::BitOr for Modifiers {
1030    type Output = Self;
1031
1032    #[inline]
1033    fn bitor(self, rhs: Self) -> Self {
1034        self.plus(rhs)
1035    }
1036}
1037
1038impl std::ops::BitOrAssign for Modifiers {
1039    #[inline]
1040    fn bitor_assign(&mut self, rhs: Self) {
1041        *self = *self | rhs;
1042    }
1043}
1044
1045impl Modifiers {
1046    pub fn ui(&self, ui: &mut crate::Ui) {
1047        ui.label(ModifierNames::NAMES.format(self, ui.ctx().os().is_mac()));
1048    }
1049}
1050
1051// ----------------------------------------------------------------------------
1052
1053/// Names of different modifier keys.
1054///
1055/// Used to name modifiers.
1056#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1057pub struct ModifierNames<'a> {
1058    pub is_short: bool,
1059
1060    pub alt: &'a str,
1061    pub ctrl: &'a str,
1062    pub shift: &'a str,
1063    pub mac_cmd: &'a str,
1064    pub mac_alt: &'a str,
1065
1066    /// What goes between the names
1067    pub concat: &'a str,
1068}
1069
1070impl ModifierNames<'static> {
1071    /// ⌥ ⌃ ⇧ ⌘ - NOTE: not supported by the default egui font.
1072    pub const SYMBOLS: Self = Self {
1073        is_short: true,
1074        alt: "⌥",
1075        ctrl: "⌃",
1076        shift: "⇧",
1077        mac_cmd: "⌘",
1078        mac_alt: "⌥",
1079        concat: "",
1080    };
1081
1082    /// Alt, Ctrl, Shift, Cmd
1083    pub const NAMES: Self = Self {
1084        is_short: false,
1085        alt: "Alt",
1086        ctrl: "Ctrl",
1087        shift: "Shift",
1088        mac_cmd: "Cmd",
1089        mac_alt: "Option",
1090        concat: "+",
1091    };
1092}
1093
1094impl ModifierNames<'_> {
1095    pub fn format(&self, modifiers: &Modifiers, is_mac: bool) -> String {
1096        let mut s = String::new();
1097
1098        let mut append_if = |modifier_is_active, modifier_name| {
1099            if modifier_is_active {
1100                if !s.is_empty() {
1101                    s += self.concat;
1102                }
1103                s += modifier_name;
1104            }
1105        };
1106
1107        if is_mac {
1108            append_if(modifiers.ctrl, self.ctrl);
1109            append_if(modifiers.shift, self.shift);
1110            append_if(modifiers.alt, self.mac_alt);
1111            append_if(modifiers.mac_cmd || modifiers.command, self.mac_cmd);
1112        } else {
1113            append_if(modifiers.ctrl || modifiers.command, self.ctrl);
1114            append_if(modifiers.alt, self.alt);
1115            append_if(modifiers.shift, self.shift);
1116        }
1117
1118        s
1119    }
1120}
1121
1122// ----------------------------------------------------------------------------
1123
1124/// A keyboard shortcut, e.g. `Ctrl+Alt+W`.
1125///
1126/// Can be used with [`crate::InputState::consume_shortcut`]
1127/// and [`crate::Context::format_shortcut`].
1128#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
1129#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1130pub struct KeyboardShortcut {
1131    pub modifiers: Modifiers,
1132
1133    pub logical_key: Key,
1134}
1135
1136impl KeyboardShortcut {
1137    pub const fn new(modifiers: Modifiers, logical_key: Key) -> Self {
1138        Self {
1139            modifiers,
1140            logical_key,
1141        }
1142    }
1143
1144    pub fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String {
1145        let mut s = names.format(&self.modifiers, is_mac);
1146        if !s.is_empty() {
1147            s += names.concat;
1148        }
1149        if names.is_short {
1150            s += self.logical_key.symbol_or_name();
1151        } else {
1152            s += self.logical_key.name();
1153        }
1154        s
1155    }
1156}
1157
1158#[test]
1159fn format_kb_shortcut() {
1160    let cmd_shift_f = KeyboardShortcut::new(Modifiers::COMMAND | Modifiers::SHIFT, Key::F);
1161    assert_eq!(
1162        cmd_shift_f.format(&ModifierNames::NAMES, false),
1163        "Ctrl+Shift+F"
1164    );
1165    assert_eq!(
1166        cmd_shift_f.format(&ModifierNames::NAMES, true),
1167        "Shift+Cmd+F"
1168    );
1169    assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, false), "⌃⇧F");
1170    assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, true), "⇧⌘F");
1171}
1172
1173// ----------------------------------------------------------------------------
1174
1175impl RawInput {
1176    pub fn ui(&self, ui: &mut crate::Ui) {
1177        let Self {
1178            viewport_id,
1179            viewports,
1180            screen_rect,
1181            max_texture_side,
1182            time,
1183            predicted_dt,
1184            modifiers,
1185            events,
1186            hovered_files,
1187            dropped_files,
1188            focused,
1189            system_theme,
1190            safe_area_insets: safe_area,
1191        } = self;
1192
1193        ui.label(format!("Active viewport: {viewport_id:?}"));
1194        let ordered_viewports = viewports
1195            .iter()
1196            .map(|(id, value)| (*id, value))
1197            .collect::<OrderedViewportIdMap<_>>();
1198        for (id, viewport) in ordered_viewports {
1199            ui.group(|ui| {
1200                ui.label(format!("Viewport {id:?}"));
1201                ui.push_id(id, |ui| {
1202                    viewport.ui(ui);
1203                });
1204            });
1205        }
1206        ui.label(format!("screen_rect: {screen_rect:?} points"));
1207
1208        ui.label(format!("max_texture_side: {max_texture_side:?}"));
1209        if let Some(time) = time {
1210            ui.label(format!("time: {time:.3} s"));
1211        } else {
1212            ui.label("time: None");
1213        }
1214        ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
1215        ui.label(format!("modifiers: {modifiers:#?}"));
1216        ui.label(format!("hovered_files: {}", hovered_files.len()));
1217        ui.label(format!("dropped_files: {}", dropped_files.len()));
1218        ui.label(format!("focused: {focused}"));
1219        ui.label(format!("system_theme: {system_theme:?}"));
1220        ui.label(format!("safe_area: {safe_area:?}"));
1221        ui.scope(|ui| {
1222            ui.set_min_height(150.0);
1223            ui.label(format!("events: {events:#?}"))
1224                .on_hover_text("key presses etc");
1225        });
1226    }
1227}
1228
1229/// this is a `u64` as values of this kind can always be obtained by hashing
1230#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
1231#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1232pub struct TouchDeviceId(pub u64);
1233
1234/// Unique identification of a touch occurrence (finger or pen or …).
1235/// A Touch ID is valid until the finger is lifted.
1236/// A new ID is used for the next touch.
1237#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
1238#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1239pub struct TouchId(pub u64);
1240
1241/// In what phase a touch event is in.
1242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1243#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1244pub enum TouchPhase {
1245    /// User just placed a touch point on the touch surface
1246    Start,
1247
1248    /// User moves a touch point along the surface. This event is also sent when
1249    /// any attributes (position, force, …) of the touch point change.
1250    Move,
1251
1252    /// User lifted the finger or pen from the surface, or slid off the edge of
1253    /// the surface
1254    End,
1255
1256    /// Touch operation has been disrupted by something (various reasons are possible,
1257    /// maybe a pop-up alert or any other kind of interruption which may not have
1258    /// been intended by the user)
1259    Cancel,
1260}
1261
1262/// The unit associated with the numeric value of a mouse wheel event
1263#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1264#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1265pub enum MouseWheelUnit {
1266    /// Number of ui points (logical pixels)
1267    Point,
1268
1269    /// Number of lines
1270    Line,
1271
1272    /// Number of pages
1273    Page,
1274}
1275
1276impl From<u64> for TouchId {
1277    fn from(id: u64) -> Self {
1278        Self(id)
1279    }
1280}
1281
1282impl From<i32> for TouchId {
1283    fn from(id: i32) -> Self {
1284        Self(id as u64)
1285    }
1286}
1287
1288impl From<u32> for TouchId {
1289    fn from(id: u32) -> Self {
1290        Self(id as u64)
1291    }
1292}
1293
1294// ----------------------------------------------------------------------------
1295
1296// TODO(emilk): generalize this to a proper event filter.
1297/// Controls which events that a focused widget will have exclusive access to.
1298///
1299/// Currently this only controls a few special keyboard events,
1300/// but in the future this `struct` should be extended into a full callback thing.
1301///
1302/// Any events not covered by the filter are given to the widget, but are not exclusive.
1303#[derive(Clone, Copy, Debug)]
1304pub struct EventFilter {
1305    /// If `true`, pressing tab will act on the widget,
1306    /// and NOT move focus away from the focused widget.
1307    ///
1308    /// Default: `false`
1309    pub tab: bool,
1310
1311    /// If `true`, pressing horizontal arrows will act on the
1312    /// widget, and NOT move focus away from the focused widget.
1313    ///
1314    /// Default: `false`
1315    pub horizontal_arrows: bool,
1316
1317    /// If `true`, pressing vertical arrows will act on the
1318    /// widget, and NOT move focus away from the focused widget.
1319    ///
1320    /// Default: `false`
1321    pub vertical_arrows: bool,
1322
1323    /// If `true`, pressing escape will act on the widget,
1324    /// and NOT surrender focus from the focused widget.
1325    ///
1326    /// Default: `false`
1327    pub escape: bool,
1328}
1329
1330#[expect(clippy::derivable_impls)] // let's be explicit
1331impl Default for EventFilter {
1332    fn default() -> Self {
1333        Self {
1334            tab: false,
1335            horizontal_arrows: false,
1336            vertical_arrows: false,
1337            escape: false,
1338        }
1339    }
1340}
1341
1342impl EventFilter {
1343    pub fn matches(&self, event: &Event) -> bool {
1344        if let Event::Key { key, .. } = event {
1345            match key {
1346                crate::Key::Tab => self.tab,
1347                crate::Key::ArrowUp | crate::Key::ArrowDown => self.vertical_arrows,
1348                crate::Key::ArrowRight | crate::Key::ArrowLeft => self.horizontal_arrows,
1349                crate::Key::Escape => self.escape,
1350                _ => true,
1351            }
1352        } else {
1353            true
1354        }
1355    }
1356}
1357
1358/// The 'safe area' insets of the screen
1359///
1360/// This represents the area taken up by the status bar, navigation controls, notches,
1361/// or any other items that obscure parts of the screen.
1362#[derive(Debug, PartialEq, Copy, Clone, Default)]
1363#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1364pub struct SafeAreaInsets(pub MarginF32);
1365
1366impl std::ops::Sub<SafeAreaInsets> for Rect {
1367    type Output = Self;
1368
1369    fn sub(self, rhs: SafeAreaInsets) -> Self::Output {
1370        self - rhs.0
1371    }
1372}