egui/data/
input.rs

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