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