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