egui/data/
input.rs

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