egui/data/
input.rs

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