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