egui/data/
input.rs

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