Skip to main content

egui/input_state/
mod.rs

1mod touch_state;
2mod wheel_state;
3
4use crate::{
5    SafeAreaInsets,
6    emath::{NumExt as _, Pos2, Rect, Vec2, vec2},
7    util::History,
8};
9use crate::{
10    data::input::{
11        Event, EventFilter, KeyboardShortcut, Modifiers, NUM_POINTER_BUTTONS, PointerButton,
12        RawInput, TouchDeviceId, ViewportInfo,
13    },
14    input_state::wheel_state::WheelState,
15};
16use core::time::Duration;
17use std::collections::{BTreeMap, HashSet};
18
19pub use crate::Key;
20pub use touch_state::MultiTouchInfo;
21use touch_state::TouchState;
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
25pub enum SurrenderFocusOn {
26    /// Surrender focus if the user _presses_ somewhere outside the focused widget.
27    Presses,
28
29    /// Surrender focus if the user _clicks_ somewhere outside the focused widget.
30    #[default]
31    Clicks,
32
33    /// Never surrender focus.
34    Never,
35}
36
37impl SurrenderFocusOn {
38    pub fn ui(&mut self, ui: &mut crate::Ui) {
39        ui.horizontal(|ui| {
40            ui.selectable_value(self, Self::Presses, "Presses")
41                .on_hover_text(
42                    "Surrender focus if the user presses somewhere outside the focused widget.",
43                );
44            ui.selectable_value(self, Self::Clicks, "Clicks")
45                .on_hover_text(
46                    "Surrender focus if the user clicks somewhere outside the focused widget.",
47                );
48            ui.selectable_value(self, Self::Never, "Never")
49                .on_hover_text("Never surrender focus.");
50        });
51    }
52}
53
54/// Options for input state handling.
55#[derive(Clone, Copy, Debug, PartialEq)]
56#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
57pub struct InputOptions {
58    /// Multiplier for the scroll speed when reported in [`crate::MouseWheelUnit::Line`]s.
59    pub line_scroll_speed: f32,
60
61    /// Controls the speed at which we zoom in when doing ctrl/cmd + scroll.
62    pub scroll_zoom_speed: f32,
63
64    /// After a pointer-down event, if the pointer moves more than this, it won't become a click.
65    pub max_click_dist: f32,
66
67    /// If the pointer is down for longer than this it will no longer register as a click.
68    ///
69    /// If a touch is held for this many seconds while still, then it will register as a
70    /// "long-touch" which is equivalent to a secondary click.
71    ///
72    /// This is to support "press and hold for context menu" on touch screens.
73    pub max_click_duration: f64,
74
75    /// The new pointer press must come within this many seconds from previous pointer release
76    /// for double click (or when this value is doubled, triple click) to count.
77    pub max_double_click_delay: f64,
78
79    /// When this modifier is down, all scroll events are treated as zoom events.
80    ///
81    /// The default is CTRL/CMD, and it is STRONGLY recommended to NOT change this.
82    pub zoom_modifier: Modifiers,
83
84    /// When this modifier is down, all scroll events are treated as horizontal scrolls,
85    /// and when combined with [`Self::zoom_modifier`] it will result in zooming
86    /// on only the horizontal axis.
87    ///
88    /// The default is SHIFT, and it is STRONGLY recommended to NOT change this.
89    pub horizontal_scroll_modifier: Modifiers,
90
91    /// When this modifier is down, all scroll events are treated as vertical scrolls,
92    /// and when combined with [`Self::zoom_modifier`] it will result in zooming
93    /// on only the vertical axis.
94    pub vertical_scroll_modifier: Modifiers,
95
96    /// When should we surrender focus from the focused widget?
97    pub surrender_focus_on: SurrenderFocusOn,
98}
99
100impl Default for InputOptions {
101    fn default() -> Self {
102        // TODO(emilk): figure out why these constants need to be different on web and on native (winit).
103        let is_web = cfg!(target_arch = "wasm32");
104        let line_scroll_speed = if is_web {
105            8.0
106        } else {
107            40.0 // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461
108        };
109
110        Self {
111            line_scroll_speed,
112            scroll_zoom_speed: 1.0 / 200.0,
113            max_click_dist: 6.0,
114            max_click_duration: 0.8,
115            max_double_click_delay: 0.3,
116            zoom_modifier: Modifiers::COMMAND,
117            horizontal_scroll_modifier: Modifiers::SHIFT,
118            vertical_scroll_modifier: Modifiers::ALT,
119            surrender_focus_on: SurrenderFocusOn::default(),
120        }
121    }
122}
123
124impl InputOptions {
125    /// Show the options in the ui.
126    pub fn ui(&mut self, ui: &mut crate::Ui) {
127        let Self {
128            line_scroll_speed,
129            scroll_zoom_speed,
130            max_click_dist,
131            max_click_duration,
132            max_double_click_delay,
133            zoom_modifier,
134            horizontal_scroll_modifier,
135            vertical_scroll_modifier,
136            surrender_focus_on,
137        } = self;
138        crate::Grid::new("InputOptions")
139            .num_columns(2)
140            .striped(true)
141            .show(ui, |ui| {
142                ui.label("Line scroll speed");
143                ui.add(crate::DragValue::new(line_scroll_speed).range(0.0..=f32::INFINITY))
144                    .on_hover_text(
145                        "How many lines to scroll with each tick of the mouse wheel",
146                    );
147                ui.end_row();
148
149                ui.label("Scroll zoom speed");
150                ui.add(
151                    crate::DragValue::new(scroll_zoom_speed)
152                        .range(0.0..=f32::INFINITY)
153                        .speed(0.001),
154                )
155                .on_hover_text("How fast to zoom with ctrl/cmd + scroll");
156                ui.end_row();
157
158                ui.label("Max click distance");
159                ui.add(crate::DragValue::new(max_click_dist).range(0.0..=f32::INFINITY))
160                    .on_hover_text(
161                        "If the pointer moves more than this, it won't become a click",
162                    );
163                ui.end_row();
164
165                ui.label("Max click duration");
166                ui.add(
167                    crate::DragValue::new(max_click_duration)
168                        .range(0.1..=f64::INFINITY)
169                        .speed(0.1),
170                    )
171                    .on_hover_text(
172                        "If the pointer is down for longer than this it will no longer register as a click",
173                    );
174                ui.end_row();
175
176                ui.label("Max double click delay");
177                ui.add(
178                    crate::DragValue::new(max_double_click_delay)
179                        .range(0.01..=f64::INFINITY)
180                        .speed(0.1),
181                )
182                .on_hover_text("Max time interval for double click to count");
183                ui.end_row();
184
185                ui.label("zoom_modifier");
186                zoom_modifier.ui(ui);
187                ui.end_row();
188
189                ui.label("horizontal_scroll_modifier");
190                horizontal_scroll_modifier.ui(ui);
191                ui.end_row();
192
193                ui.label("vertical_scroll_modifier");
194                vertical_scroll_modifier.ui(ui);
195                ui.end_row();
196
197                ui.label("surrender_focus_on");
198                surrender_focus_on.ui(ui);
199                ui.end_row();
200
201            });
202    }
203}
204
205/// Input state that egui updates each frame.
206///
207/// You can access this with [`crate::Context::input`].
208///
209/// You can check if `egui` is using the inputs using
210/// [`crate::Context::egui_wants_pointer_input`] and [`crate::Context::egui_wants_keyboard_input`].
211#[derive(Clone, Debug)]
212#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
213pub struct InputState {
214    /// The raw input we got this frame from the backend.
215    pub raw: RawInput,
216
217    /// State of the mouse or simple touch gestures which can be mapped to mouse operations.
218    pub pointer: PointerState,
219
220    /// State of touches, except those covered by `PointerState` (like clicks and drags).
221    /// (We keep a separate [`TouchState`] for each encountered touch device.)
222    touch_states: BTreeMap<TouchDeviceId, TouchState>,
223
224    // ----------------------------------------------
225    // Scrolling:
226    #[cfg_attr(feature = "serde", serde(skip))]
227    wheel: WheelState,
228
229    /// How many points the user scrolled, smoothed over a few frames.
230    ///
231    /// The delta dictates how the _content_ should move.
232    ///
233    /// A positive X-value indicates the content is being moved right,
234    /// as when swiping right on a touch-screen or track-pad with natural scrolling.
235    ///
236    /// A positive Y-value indicates the content is being moved down,
237    /// as when swiping down on a touch-screen or track-pad with natural scrolling.
238    ///
239    /// [`crate::ScrollArea`] will both read and write to this field, so that
240    /// at the end of the frame this will be zero if a scroll-area consumed the delta.
241    pub smooth_scroll_delta: Vec2,
242
243    /// Zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
244    ///
245    /// * `zoom = 1`: no change.
246    /// * `zoom < 1`: pinch together
247    /// * `zoom > 1`: pinch spread
248    zoom_factor_delta: f32,
249
250    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).
251    rotation_radians: f32,
252
253    // ----------------------------------------------
254    /// Position and size of the egui area.
255    ///
256    /// This is including the area that may be covered by the `safe_area_insets`.
257    viewport_rect: Rect,
258
259    /// The safe area insets, subtracted from the `viewport_rect` in [`Self::content_rect`].
260    safe_area_insets: SafeAreaInsets,
261
262    /// Also known as device pixel ratio, > 1 for high resolution screens.
263    pub pixels_per_point: f32,
264
265    /// Maximum size of one side of a texture.
266    ///
267    /// This depends on the backend.
268    pub max_texture_side: usize,
269
270    /// Time in seconds. Relative to whatever. Used for animation.
271    pub time: f64,
272
273    /// Time since last frame, in seconds.
274    ///
275    /// This can be very unstable in reactive mode (when we don't paint each frame).
276    /// For animations it is therefore better to use [`Self::stable_dt`].
277    pub unstable_dt: f32,
278
279    /// Estimated time until next frame (provided we repaint right away).
280    ///
281    /// Used for animations to get instant feedback (avoid frame delay).
282    /// Should be set to the expected time between frames when painting at vsync speeds.
283    ///
284    /// On most integrations this has a fixed value of `1.0 / 60.0`, so it is not a very accurate estimate.
285    pub predicted_dt: f32,
286
287    /// Time since last frame (in seconds), but gracefully handles the first frame after sleeping in reactive mode.
288    ///
289    /// In reactive mode (available in e.g. `eframe`), `egui` only updates when there is new input
290    /// or something is animating.
291    /// This can lead to large gaps of time (sleep), leading to large [`Self::unstable_dt`].
292    ///
293    /// If `egui` requested a repaint the previous frame, then `egui` will use
294    /// `stable_dt = unstable_dt;`, but if `egui` did not not request a repaint last frame,
295    /// then `egui` will assume `unstable_dt` is too large, and will use
296    /// `stable_dt = predicted_dt;`.
297    ///
298    /// This means that for the first frame after a sleep,
299    /// `stable_dt` will be a prediction of the delta-time until the next frame,
300    /// and in all other situations this will be an accurate measurement of time passed
301    /// since the previous frame.
302    ///
303    /// Note that a frame can still stall for various reasons, so `stable_dt` can
304    /// still be unusually large in some situations.
305    ///
306    /// When animating something, it is recommended that you use something like
307    /// `stable_dt.min(0.1)` - this will give you smooth animations when the framerate is good
308    /// (even in reactive mode), but will avoid large jumps when framerate is bad,
309    /// and will effectively slow down the animation when FPS drops below 10.
310    pub stable_dt: f32,
311
312    /// The native window has the keyboard focus (i.e. is receiving key presses).
313    ///
314    /// False when the user alt-tab away from the application, for instance.
315    pub focused: bool,
316
317    /// Which modifier keys are down at the start of the frame?
318    pub modifiers: Modifiers,
319
320    /// The keys that are currently being held down.
321    ///
322    /// Keys released this frame are NOT considered down.
323    pub keys_down: HashSet<Key>,
324
325    /// In-order events received this frame
326    pub events: Vec<Event>,
327
328    /// Input state management configuration.
329    ///
330    /// This gets copied from `egui::Options` at the start of each frame for convenience.
331    options: InputOptions,
332}
333
334impl Default for InputState {
335    fn default() -> Self {
336        Self {
337            raw: Default::default(),
338            pointer: Default::default(),
339            touch_states: Default::default(),
340
341            wheel: Default::default(),
342            smooth_scroll_delta: Vec2::ZERO,
343            zoom_factor_delta: 1.0,
344            rotation_radians: 0.0,
345
346            viewport_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
347            safe_area_insets: Default::default(),
348            pixels_per_point: 1.0,
349            max_texture_side: 2048,
350            time: 0.0,
351            unstable_dt: 1.0 / 60.0,
352            predicted_dt: 1.0 / 60.0,
353            stable_dt: 1.0 / 60.0,
354            focused: false,
355            modifiers: Default::default(),
356            keys_down: Default::default(),
357            events: Default::default(),
358            options: Default::default(),
359        }
360    }
361}
362
363impl InputState {
364    #[must_use]
365    pub fn begin_pass(
366        mut self,
367        mut new: RawInput,
368        requested_immediate_repaint_prev_frame: bool,
369        pixels_per_point: f32,
370        options: InputOptions,
371    ) -> Self {
372        profiling::function_scope!();
373
374        let time = new.time.unwrap_or(self.time + new.predicted_dt as f64);
375        let unstable_dt = (time - self.time) as f32;
376
377        let stable_dt = if requested_immediate_repaint_prev_frame {
378            // we should have had a repaint straight away,
379            // so this should be trustable.
380            unstable_dt
381        } else {
382            new.predicted_dt
383        };
384
385        let safe_area_insets = new.safe_area_insets.unwrap_or(self.safe_area_insets);
386        let viewport_rect = new.screen_rect.unwrap_or(self.viewport_rect);
387        self.create_touch_states_for_new_devices(&new.events);
388        for touch_state in self.touch_states.values_mut() {
389            touch_state.begin_pass(time, &new, self.pointer.interact_pos);
390        }
391        let pointer = self.pointer.begin_pass(time, &new, options);
392
393        let mut keys_down = self.keys_down;
394        let mut modifiers = self.modifiers;
395        let mut zoom_factor_delta = 1.0; // TODO(emilk): smoothing for zoom factor
396        let mut rotation_radians = 0.0;
397
398        self.wheel.smooth_wheel_delta = Vec2::ZERO;
399
400        for event in &mut new.events {
401            match event {
402                Event::Key {
403                    key,
404                    pressed,
405                    repeat,
406                    ..
407                } => {
408                    if *pressed {
409                        let first_press = keys_down.insert(*key);
410                        *repeat = !first_press;
411                    } else {
412                        keys_down.remove(key);
413                    }
414                }
415                Event::MouseWheel {
416                    unit,
417                    delta,
418                    phase,
419                    modifiers,
420                } => {
421                    self.wheel.on_wheel_event(
422                        viewport_rect,
423                        &options,
424                        time,
425                        *unit,
426                        *delta,
427                        *phase,
428                        *modifiers,
429                    );
430                }
431                Event::ModifiersChanged(new_modifiers) => {
432                    modifiers = *new_modifiers;
433                }
434                Event::Zoom(factor) => {
435                    zoom_factor_delta *= *factor;
436                }
437                Event::Rotate(radians) => {
438                    rotation_radians += *radians;
439                }
440                Event::WindowFocused(false) => {
441                    // Example: pressing `Cmd+S` brings up a save-dialog (e.g. using rfd),
442                    // but we get no key-up event for the `S` key (in winit).
443                    // This leads to `S` being mistakenly marked as down when we switch back to the app.
444                    // So we take the safe route and just clear all the keys and modifiers when
445                    // the app loses focus.
446                    keys_down.clear();
447                    modifiers = Modifiers::default();
448                }
449                _ => {}
450            }
451        }
452
453        let mut smooth_scroll_delta = Vec2::ZERO;
454
455        {
456            let dt = stable_dt.at_most(0.1);
457            self.wheel.after_events(time, dt);
458
459            let is_zoom = self.wheel.modifiers.matches_any(options.zoom_modifier);
460
461            if is_zoom {
462                zoom_factor_delta *= (options.scroll_zoom_speed
463                    * (self.wheel.smooth_wheel_delta.x + self.wheel.smooth_wheel_delta.y))
464                    .exp();
465            } else {
466                smooth_scroll_delta = self.wheel.smooth_wheel_delta;
467            }
468        }
469
470        Self {
471            pointer,
472            touch_states: self.touch_states,
473
474            wheel: self.wheel,
475            smooth_scroll_delta,
476            zoom_factor_delta,
477            rotation_radians,
478
479            viewport_rect,
480            safe_area_insets,
481            pixels_per_point,
482            max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
483            time,
484            unstable_dt,
485            predicted_dt: new.predicted_dt,
486            stable_dt,
487            focused: new.focused,
488            modifiers,
489            keys_down,
490            events: new.events.clone(), // TODO(emilk): remove clone() and use raw.events
491            raw: new,
492            options,
493        }
494    }
495
496    /// Info about the active viewport
497    #[inline]
498    pub fn viewport(&self) -> &ViewportInfo {
499        self.raw.viewport()
500    }
501
502    /// Returns the region of the screen that is safe for content rendering
503    ///
504    /// Returns the `viewport_rect` with the `safe_area_insets` removed.
505    ///
506    /// If you want to render behind e.g. the dynamic island on iOS, use [`Self::viewport_rect`].
507    ///
508    /// See also [`RawInput::safe_area_insets`].
509    #[inline(always)]
510    pub fn content_rect(&self) -> Rect {
511        self.viewport_rect - self.safe_area_insets
512    }
513
514    /// Returns the full area available to egui, including parts that might be partially covered,
515    /// for example, by the OS status bar or notches (see [`Self::safe_area_insets`]).
516    ///
517    /// Usually you want to use [`Self::content_rect`] instead.
518    ///
519    /// This rectangle includes e.g. the dynamic island on iOS.
520    /// If you want to only render _below_ the that (not behind), then you should use
521    /// [`Self::content_rect`] instead.
522    ///
523    /// See also [`RawInput::safe_area_insets`].
524    pub fn viewport_rect(&self) -> Rect {
525        self.viewport_rect
526    }
527
528    /// Get the safe area insets.
529    ///
530    /// This represents the area of the screen covered by status bars, navigation controls, notches,
531    /// or other items that obscure part of the screen.
532    ///
533    /// See [`Self::content_rect`] to get the `viewport_rect` with the safe area insets removed.
534    pub fn safe_area_insets(&self) -> SafeAreaInsets {
535        self.safe_area_insets
536    }
537
538    /// How many points the user scrolled, smoothed over a few frames.
539    ///
540    /// The delta dictates how the _content_ should move.
541    ///
542    /// A positive X-value indicates the content is being moved right,
543    /// as when swiping right on a touch-screen or track-pad with natural scrolling.
544    ///
545    /// A positive Y-value indicates the content is being moved down,
546    /// as when swiping down on a touch-screen or track-pad with natural scrolling.
547    ///
548    /// [`crate::ScrollArea`] will both read and write to this field, so that
549    /// at the end of the frame this will be zero if a scroll-area consumed the delta.
550    pub fn smooth_scroll_delta(&self) -> Vec2 {
551        self.smooth_scroll_delta
552    }
553
554    /// Uniform zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
555    /// * `zoom = 1`: no change
556    /// * `zoom < 1`: pinch together
557    /// * `zoom > 1`: pinch spread
558    ///
559    /// If your application supports non-proportional zooming,
560    /// then you probably want to use [`Self::zoom_delta_2d`] instead.
561    #[inline(always)]
562    pub fn zoom_delta(&self) -> f32 {
563        // If a multi touch gesture is detected, it measures the exact and linear proportions of
564        // the distances of the finger tips. It is therefore potentially more accurate than
565        // `zoom_factor_delta` which is based on the `ctrl-scroll` event which, in turn, may be
566        // synthesized from an original touch gesture.
567        self.multi_touch()
568            .map_or(self.zoom_factor_delta, |touch| touch.zoom_delta)
569    }
570
571    /// 2D non-proportional zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
572    ///
573    /// For multitouch devices the user can do a horizontal or vertical pinch gesture.
574    /// In these cases a non-proportional zoom factor is a available.
575    /// In other cases, this reverts to `Vec2::splat(self.zoom_delta())`.
576    ///
577    /// For horizontal pinches, this will return `[z, 1]`,
578    /// for vertical pinches this will return `[1, z]`,
579    /// and otherwise this will return `[z, z]`,
580    /// where `z` is the zoom factor:
581    /// * `zoom = 1`: no change
582    /// * `zoom < 1`: pinch together
583    /// * `zoom > 1`: pinch spread
584    #[inline(always)]
585    pub fn zoom_delta_2d(&self) -> Vec2 {
586        // If a multi touch gesture is detected, it measures the exact and linear proportions of
587        // the distances of the finger tips.  It is therefore potentially more accurate than
588        // `zoom_factor_delta` which is based on the `ctrl-scroll` event which, in turn, may be
589        // synthesized from an original touch gesture.
590        if let Some(multi_touch) = self.multi_touch() {
591            multi_touch.zoom_delta_2d
592        } else {
593            let mut zoom = Vec2::splat(self.zoom_factor_delta);
594
595            let is_horizontal = self
596                .modifiers
597                .matches_any(self.options.horizontal_scroll_modifier);
598            let is_vertical = self
599                .modifiers
600                .matches_any(self.options.vertical_scroll_modifier);
601
602            if is_horizontal && !is_vertical {
603                // Horizontal-only zooming.
604                zoom.y = 1.0;
605            }
606            if !is_horizontal && is_vertical {
607                // Vertical-only zooming.
608                zoom.x = 1.0;
609            }
610
611            zoom
612        }
613    }
614
615    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).
616    #[inline(always)]
617    pub fn rotation_delta(&self) -> f32 {
618        self.multi_touch()
619            .map_or(self.rotation_radians, |touch| touch.rotation_delta)
620    }
621
622    /// Panning translation in pixels this frame (e.g. from scrolling or a pan gesture)
623    ///
624    /// The delta indicates how the **content** should move.
625    ///
626    /// A positive X-value indicates the content is being moved right, as when swiping right on a touch-screen or track-pad with natural scrolling.
627    ///
628    /// A positive Y-value indicates the content is being moved down, as when swiping down on a touch-screen or track-pad with natural scrolling.
629    #[inline(always)]
630    pub fn translation_delta(&self) -> Vec2 {
631        self.multi_touch().map_or_else(
632            || self.smooth_scroll_delta(),
633            |touch| touch.translation_delta,
634        )
635    }
636
637    /// True if there is an active scroll action that might scroll more when using [`Self::smooth_scroll_delta`].
638    pub fn is_scrolling(&self) -> bool {
639        self.wheel.is_scrolling()
640    }
641
642    /// How long has it been (in seconds) since the last scroll event?
643    #[inline(always)]
644    pub fn time_since_last_scroll(&self) -> f32 {
645        (self.time - self.wheel.last_wheel_event) as f32
646    }
647
648    /// The [`crate::Context`] will call this at the beginning of each frame to see if we need a repaint.
649    ///
650    /// Returns how long to wait for a repaint.
651    ///
652    /// NOTE: It's important to call this immediately after [`Self::begin_pass`] since calls to
653    /// [`Self::consume_key`] will remove events from the vec, meaning those key presses wouldn't
654    /// cause a repaint.
655    pub(crate) fn wants_repaint_after(&self) -> Option<Duration> {
656        if self.pointer.wants_repaint()
657            || self.wheel.unprocessed_wheel_delta.abs().max_elem() > 0.2
658            || !self.events.is_empty()
659            || !self.raw.hovered_files.is_empty()
660            || !self.raw.dropped_files.is_empty()
661        {
662            // Immediate repaint
663            return Some(Duration::ZERO);
664        }
665
666        if self.any_touches() && !self.pointer.is_decidedly_dragging() {
667            // We need to wake up and check for press-and-hold for the context menu.
668            if let Some(press_start_time) = self.pointer.press_start_time {
669                let press_duration = self.time - press_start_time;
670                if self.options.max_click_duration.is_finite()
671                    && press_duration < self.options.max_click_duration
672                {
673                    let secs_until_menu = self.options.max_click_duration - press_duration;
674                    return Some(Duration::from_secs_f64(secs_until_menu));
675                }
676            }
677        }
678
679        None
680    }
681
682    /// Count presses of a key. If non-zero, the presses are consumed, so that this will only return non-zero once.
683    ///
684    /// Includes key-repeat events.
685    ///
686    /// This uses [`Modifiers::matches_logically`] to match modifiers,
687    /// meaning extra Shift and Alt modifiers are ignored.
688    /// Therefore, you should match most specific shortcuts first,
689    /// i.e. check for `Cmd-Shift-S` ("Save as…") before `Cmd-S` ("Save"),
690    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!
691    pub fn count_and_consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> usize {
692        let mut count = 0usize;
693
694        self.events.retain(|event| {
695            let is_match = matches!(
696                event,
697                Event::Key {
698                    key: ev_key,
699                    modifiers: ev_mods,
700                    pressed: true,
701                    ..
702                } if *ev_key == logical_key && ev_mods.matches_logically(modifiers)
703            );
704
705            count += is_match as usize;
706
707            !is_match
708        });
709
710        count
711    }
712
713    /// Check for a key press. If found, `true` is returned and the key pressed is consumed, so that this will only return `true` once.
714    ///
715    /// Includes key-repeat events.
716    ///
717    /// This uses [`Modifiers::matches_logically`] to match modifiers,
718    /// meaning extra Shift and Alt modifiers are ignored.
719    /// Therefore, you should match most specific shortcuts first,
720    /// i.e. check for `Cmd-Shift-S` ("Save as…") before `Cmd-S` ("Save"),
721    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!
722    pub fn consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> bool {
723        self.count_and_consume_key(modifiers, logical_key) > 0
724    }
725
726    /// Check if the given shortcut has been pressed.
727    ///
728    /// If so, `true` is returned and the key pressed is consumed, so that this will only return `true` once.
729    ///
730    /// This uses [`Modifiers::matches_logically`] to match modifiers,
731    /// meaning extra Shift and Alt modifiers are ignored.
732    /// Therefore, you should match most specific shortcuts first,
733    /// i.e. check for `Cmd-Shift-S` ("Save as…") before `Cmd-S` ("Save"),
734    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!
735    pub fn consume_shortcut(&mut self, shortcut: &KeyboardShortcut) -> bool {
736        let KeyboardShortcut {
737            modifiers,
738            logical_key,
739        } = *shortcut;
740        self.consume_key(modifiers, logical_key)
741    }
742
743    /// Was the given key pressed this frame?
744    ///
745    /// Includes key-repeat events.
746    pub fn key_pressed(&self, desired_key: Key) -> bool {
747        self.num_presses(desired_key) > 0
748    }
749
750    /// How many times was the given key pressed this frame?
751    ///
752    /// Includes key-repeat events.
753    pub fn num_presses(&self, desired_key: Key) -> usize {
754        self.events
755            .iter()
756            .filter(|event| {
757                matches!(
758                    event,
759                    Event::Key { key, pressed: true, .. }
760                    if *key == desired_key
761                )
762            })
763            .count()
764    }
765
766    /// Is the given key currently held down?
767    ///
768    /// Keys released this frame are NOT considered down.
769    pub fn key_down(&self, desired_key: Key) -> bool {
770        self.keys_down.contains(&desired_key)
771    }
772
773    /// Was the given key released this frame?
774    pub fn key_released(&self, desired_key: Key) -> bool {
775        self.events.iter().any(|event| {
776            matches!(
777                event,
778                Event::Key {
779                    key,
780                    pressed: false,
781                    ..
782                } if *key == desired_key
783            )
784        })
785    }
786
787    /// Also known as device pixel ratio, > 1 for high resolution screens.
788    #[inline(always)]
789    pub fn pixels_per_point(&self) -> f32 {
790        self.pixels_per_point
791    }
792
793    /// Size of a physical pixel in logical gui coordinates (points).
794    #[inline(always)]
795    pub fn physical_pixel_size(&self) -> f32 {
796        1.0 / self.pixels_per_point()
797    }
798
799    /// How imprecise do we expect the mouse/touch input to be?
800    /// Returns imprecision in points.
801    #[inline(always)]
802    pub fn aim_radius(&self) -> f32 {
803        // TODO(emilk): multiply by ~3 for touch inputs because fingers are fat
804        self.physical_pixel_size()
805    }
806
807    /// Returns details about the currently ongoing multi-touch gesture, if any. Note that this
808    /// method returns `None` for single-touch gestures (click, drag, …).
809    ///
810    /// ```
811    /// # use egui::emath::Rot2;
812    /// # egui::__run_test_ui(|ui| {
813    /// let mut zoom = 1.0; // no zoom
814    /// let mut rotation = 0.0; // no rotation
815    /// let multi_touch = ui.input(|i| i.multi_touch());
816    /// if let Some(multi_touch) = multi_touch {
817    ///     zoom *= multi_touch.zoom_delta;
818    ///     rotation += multi_touch.rotation_delta;
819    /// }
820    /// let transform = zoom * Rot2::from_angle(rotation);
821    /// # });
822    /// ```
823    ///
824    /// By far not all touch devices are supported, and the details depend on the `egui`
825    /// integration backend you are using. `eframe` web supports multi touch for most mobile
826    /// devices, but not for a `Trackpad` on `MacOS`, for example. The backend has to be able to
827    /// capture native touch events, but many browsers seem to pass such events only for touch
828    /// _screens_, but not touch _pads._
829    ///
830    /// Refer to [`MultiTouchInfo`] for details about the touch information available.
831    ///
832    /// Consider using `zoom_delta()` instead of `MultiTouchInfo::zoom_delta` as the former
833    /// delivers a synthetic zoom factor based on ctrl-scroll events, as a fallback.
834    pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
835        // In case of multiple touch devices simply pick the touch_state of the first active device
836        self.touch_states.values().find_map(|t| t.info())
837    }
838
839    /// True if there currently are any fingers touching egui.
840    pub fn any_touches(&self) -> bool {
841        self.touch_states.values().any(|t| t.any_touches())
842    }
843
844    /// True if we have ever received a touch event.
845    pub fn has_touch_screen(&self) -> bool {
846        !self.touch_states.is_empty()
847    }
848
849    /// Scans `events` for device IDs of touch devices we have not seen before,
850    /// and creates a new [`TouchState`] for each such device.
851    fn create_touch_states_for_new_devices(&mut self, events: &[Event]) {
852        for event in events {
853            if let Event::Touch { device_id, .. } = event {
854                self.touch_states
855                    .entry(*device_id)
856                    .or_insert_with(|| TouchState::new(*device_id));
857            }
858        }
859    }
860
861    pub fn accesskit_action_requests(
862        &self,
863        id: crate::Id,
864        action: accesskit::Action,
865    ) -> impl Iterator<Item = &accesskit::ActionRequest> {
866        let accesskit_id = id.accesskit_id();
867        self.events.iter().filter_map(move |event| {
868            if let Event::AccessKitActionRequest(request) = event
869                && request.target_node == accesskit_id
870                && request.target_tree == accesskit::TreeId::ROOT
871                && request.action == action
872            {
873                return Some(request);
874            }
875            None
876        })
877    }
878
879    pub fn consume_accesskit_action_requests(
880        &mut self,
881        id: crate::Id,
882        mut consume: impl FnMut(&accesskit::ActionRequest) -> bool,
883    ) {
884        let accesskit_id = id.accesskit_id();
885        self.events.retain(|event| {
886            if let Event::AccessKitActionRequest(request) = event
887                && request.target_node == accesskit_id
888                && request.target_tree == accesskit::TreeId::ROOT
889            {
890                return !consume(request);
891            }
892            true
893        });
894    }
895
896    pub fn has_accesskit_action_request(&self, id: crate::Id, action: accesskit::Action) -> bool {
897        self.accesskit_action_requests(id, action).next().is_some()
898    }
899
900    pub fn num_accesskit_action_requests(&self, id: crate::Id, action: accesskit::Action) -> usize {
901        self.accesskit_action_requests(id, action).count()
902    }
903
904    /// Get all events that matches the given filter.
905    pub fn filtered_events(&self, filter: &EventFilter) -> Vec<Event> {
906        self.events
907            .iter()
908            .filter(|event| filter.matches(event))
909            .cloned()
910            .collect()
911    }
912
913    /// A long press is something we detect on touch screens
914    /// to trigger a secondary click (context menu).
915    ///
916    /// Returns `true` only on one frame.
917    pub(crate) fn is_long_touch(&self) -> bool {
918        self.any_touches() && self.pointer.is_long_press()
919    }
920}
921
922// ----------------------------------------------------------------------------
923
924/// A pointer (mouse or touch) click.
925#[derive(Clone, Debug, PartialEq)]
926#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
927pub(crate) struct Click {
928    pub pos: Pos2,
929
930    /// 1 or 2 (double-click) or 3 (triple-click)
931    pub count: u32,
932
933    /// Allows you to check for e.g. shift-click
934    pub modifiers: Modifiers,
935}
936
937impl Click {
938    pub fn is_double(&self) -> bool {
939        self.count == 2
940    }
941
942    pub fn is_triple(&self) -> bool {
943        self.count == 3
944    }
945}
946
947#[derive(Clone, Debug, PartialEq)]
948#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
949pub(crate) enum PointerEvent {
950    Moved(Pos2),
951    Pressed {
952        position: Pos2,
953        button: PointerButton,
954    },
955    Released {
956        click: Option<Click>,
957        button: PointerButton,
958    },
959}
960
961impl PointerEvent {
962    pub fn is_press(&self) -> bool {
963        matches!(self, Self::Pressed { .. })
964    }
965
966    pub fn is_release(&self) -> bool {
967        matches!(self, Self::Released { .. })
968    }
969
970    pub fn is_click(&self) -> bool {
971        matches!(self, Self::Released { click: Some(_), .. })
972    }
973}
974
975/// Mouse or touch state.
976///
977/// To access the methods of [`PointerState`] you can use the [`crate::Context::input`] function
978///
979/// ```rust
980/// # let ctx = egui::Context::default();
981/// let latest_pos = ctx.input(|i| i.pointer.latest_pos());
982/// let is_pointer_down = ctx.input(|i| i.pointer.any_down());
983/// ```
984///
985#[derive(Clone, Debug)]
986#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
987pub struct PointerState {
988    /// Latest known time
989    time: f64,
990
991    // Consider a finger tapping a touch screen.
992    // What position should we report?
993    // The location of the touch, or `None`, because the finger is gone?
994    //
995    // For some cases we want the first: e.g. to check for interaction.
996    // For showing tooltips, we want the latter (no tooltips, since there are no fingers).
997    /// Latest reported pointer position.
998    /// When tapping a touch screen, this will be `None`.
999    latest_pos: Option<Pos2>,
1000
1001    /// Latest position of the mouse, but ignoring any [`Event::PointerGone`]
1002    /// if there were interactions this frame.
1003    /// When tapping a touch screen, this will be the location of the touch.
1004    interact_pos: Option<Pos2>,
1005
1006    /// How much the pointer moved compared to last frame, in points.
1007    delta: Vec2,
1008
1009    /// How much the mouse moved since the last frame, in unspecified units.
1010    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
1011    /// May be unavailable on some integrations.
1012    motion: Option<Vec2>,
1013
1014    /// Current velocity of pointer.
1015    velocity: Vec2,
1016
1017    /// Current direction of pointer.
1018    direction: Vec2,
1019
1020    /// Recent movement of the pointer.
1021    /// Used for calculating velocity of pointer.
1022    pos_history: History<Pos2>,
1023
1024    /// Buttons currently down, excluding those released this frame.
1025    down: [bool; NUM_POINTER_BUTTONS],
1026
1027    /// Where did the current click/drag originate?
1028    /// `None` if no mouse button is down.
1029    press_origin: Option<Pos2>,
1030
1031    /// When did the current click/drag originate?
1032    /// `None` if no mouse button is down.
1033    press_start_time: Option<f64>,
1034
1035    /// Set to `true` if the pointer has moved too much (since being pressed)
1036    /// for it to be registered as a click.
1037    pub(crate) has_moved_too_much_for_a_click: bool,
1038
1039    /// Did [`Self::is_decidedly_dragging`] go from `false` to `true` this frame?
1040    ///
1041    /// This could also be the trigger point for a long-touch.
1042    pub(crate) started_decidedly_dragging: bool,
1043
1044    /// Where did the last click originate?
1045    /// `None` if no mouse click occurred.
1046    last_click_pos: Option<Pos2>,
1047
1048    /// When did the pointer get click last?
1049    /// Used to check for double-clicks.
1050    last_click_time: f64,
1051
1052    /// When did the pointer get click two clicks ago?
1053    /// Used to check for triple-clicks.
1054    last_last_click_time: f64,
1055
1056    /// When was the pointer last moved?
1057    /// Used for things like showing hover ui/tooltip with a delay.
1058    last_move_time: f64,
1059
1060    /// All button events that occurred this frame
1061    pub(crate) pointer_events: Vec<PointerEvent>,
1062
1063    /// Input state management configuration.
1064    ///
1065    /// This gets copied from `egui::Options` at the start of each frame for convenience.
1066    options: InputOptions,
1067}
1068
1069impl Default for PointerState {
1070    fn default() -> Self {
1071        Self {
1072            time: -f64::INFINITY,
1073            latest_pos: None,
1074            interact_pos: None,
1075            delta: Vec2::ZERO,
1076            motion: None,
1077            velocity: Vec2::ZERO,
1078            direction: Vec2::ZERO,
1079            pos_history: History::new(2..1000, 0.1),
1080            down: Default::default(),
1081            press_origin: None,
1082            press_start_time: None,
1083            has_moved_too_much_for_a_click: false,
1084            started_decidedly_dragging: false,
1085            last_click_pos: None,
1086            last_click_time: f64::NEG_INFINITY,
1087            last_last_click_time: f64::NEG_INFINITY,
1088            last_move_time: f64::NEG_INFINITY,
1089            pointer_events: vec![],
1090            options: Default::default(),
1091        }
1092    }
1093}
1094
1095impl PointerState {
1096    #[must_use]
1097    pub(crate) fn begin_pass(mut self, time: f64, new: &RawInput, options: InputOptions) -> Self {
1098        let was_decidedly_dragging = self.is_decidedly_dragging();
1099
1100        self.time = time;
1101        self.options = options;
1102
1103        self.pointer_events.clear();
1104
1105        let old_pos = self.latest_pos;
1106        self.interact_pos = self.latest_pos;
1107        if self.motion.is_some() {
1108            self.motion = Some(Vec2::ZERO);
1109        }
1110
1111        let mut clear_history_after_velocity_calculation = false;
1112        for event in &new.events {
1113            match event {
1114                Event::PointerMoved(pos) => {
1115                    let pos = *pos;
1116
1117                    self.latest_pos = Some(pos);
1118                    self.interact_pos = Some(pos);
1119
1120                    if let Some(press_origin) = self.press_origin {
1121                        self.has_moved_too_much_for_a_click |=
1122                            press_origin.distance(pos) > self.options.max_click_dist;
1123                    }
1124
1125                    self.last_move_time = time;
1126                    self.pointer_events.push(PointerEvent::Moved(pos));
1127                }
1128                Event::PointerButton {
1129                    pos,
1130                    button,
1131                    pressed,
1132                    modifiers,
1133                } => {
1134                    let pos = *pos;
1135                    let button = *button;
1136                    let pressed = *pressed;
1137                    let modifiers = *modifiers;
1138
1139                    self.latest_pos = Some(pos);
1140                    self.interact_pos = Some(pos);
1141
1142                    if pressed {
1143                        // Start of a drag: we want to track the velocity for during the drag
1144                        // and ignore any incoming movement
1145                        self.pos_history.clear();
1146                    }
1147
1148                    if pressed {
1149                        self.press_origin = Some(pos);
1150                        self.press_start_time = Some(time);
1151                        self.has_moved_too_much_for_a_click = false;
1152                        self.pointer_events.push(PointerEvent::Pressed {
1153                            position: pos,
1154                            button,
1155                        });
1156                    } else {
1157                        // Released
1158                        let clicked = self.could_any_button_be_click();
1159
1160                        let click = if clicked {
1161                            let click_dist_sq = self
1162                                .last_click_pos
1163                                .map_or(0.0, |last_pos| last_pos.distance_sq(pos));
1164
1165                            let double_click = (time - self.last_click_time)
1166                                < self.options.max_double_click_delay
1167                                && click_dist_sq
1168                                    < self.options.max_click_dist * self.options.max_click_dist;
1169                            let triple_click = (time - self.last_last_click_time)
1170                                < (self.options.max_double_click_delay * 2.0)
1171                                && click_dist_sq
1172                                    < self.options.max_click_dist * self.options.max_click_dist;
1173                            let count = if triple_click {
1174                                3
1175                            } else if double_click {
1176                                2
1177                            } else {
1178                                1
1179                            };
1180
1181                            self.last_last_click_time = self.last_click_time;
1182                            self.last_click_time = time;
1183                            self.last_click_pos = Some(pos);
1184
1185                            Some(Click {
1186                                pos,
1187                                count,
1188                                modifiers,
1189                            })
1190                        } else {
1191                            None
1192                        };
1193
1194                        self.pointer_events
1195                            .push(PointerEvent::Released { click, button });
1196
1197                        self.press_origin = None;
1198                        self.press_start_time = None;
1199                    }
1200
1201                    self.down[button as usize] = pressed; // must be done after the above call to `could_any_button_be_click`
1202                }
1203                Event::PointerGone => {
1204                    self.latest_pos = None;
1205                    // When dragging a slider and the mouse leaves the viewport, we still want the drag to work,
1206                    // so we don't treat this as a `PointerEvent::Released`.
1207                    // NOTE: we do NOT clear `self.interact_pos` here. It will be cleared next frame.
1208
1209                    // Delay the clearing until after the final velocity calculation, so we can
1210                    // get the final velocity when `drag_stopped` is true.
1211                    clear_history_after_velocity_calculation = true;
1212                }
1213                Event::MouseMoved(delta) => *self.motion.get_or_insert(Vec2::ZERO) += *delta,
1214                _ => {}
1215            }
1216        }
1217
1218        self.delta = if let (Some(old_pos), Some(new_pos)) = (old_pos, self.latest_pos) {
1219            new_pos - old_pos
1220        } else {
1221            Vec2::ZERO
1222        };
1223
1224        if let Some(pos) = self.latest_pos {
1225            self.pos_history.add(time, pos);
1226        } else {
1227            // we do not clear the `pos_history` here, because it is exactly when a finger has
1228            // released from the touch screen that we may want to assign a velocity to whatever
1229            // the user tried to throw.
1230        }
1231
1232        self.pos_history.flush(time);
1233
1234        self.velocity = if self.pos_history.len() >= 3 && self.pos_history.duration() > 0.01 {
1235            self.pos_history.velocity().unwrap_or_default()
1236        } else {
1237            Vec2::default()
1238        };
1239        if self.velocity != Vec2::ZERO {
1240            self.last_move_time = time;
1241        }
1242        if clear_history_after_velocity_calculation {
1243            self.pos_history.clear();
1244        }
1245
1246        self.direction = self.pos_history.velocity().unwrap_or_default().normalized();
1247
1248        self.started_decidedly_dragging = self.is_decidedly_dragging() && !was_decidedly_dragging;
1249
1250        self
1251    }
1252
1253    fn wants_repaint(&self) -> bool {
1254        !self.pointer_events.is_empty() || self.delta != Vec2::ZERO
1255    }
1256
1257    /// How much the pointer moved compared to last frame, in points.
1258    #[inline(always)]
1259    pub fn delta(&self) -> Vec2 {
1260        self.delta
1261    }
1262
1263    /// How much the mouse moved since the last frame, in unspecified units.
1264    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
1265    /// May be unavailable on some integrations.
1266    #[inline(always)]
1267    pub fn motion(&self) -> Option<Vec2> {
1268        self.motion
1269    }
1270
1271    /// Current velocity of pointer.
1272    ///
1273    /// This is smoothed over a few frames,
1274    /// but can be ZERO when frame-rate is bad.
1275    #[inline(always)]
1276    pub fn velocity(&self) -> Vec2 {
1277        self.velocity
1278    }
1279
1280    /// Current direction of the pointer.
1281    ///
1282    /// This is less sensitive to bad framerate than [`Self::velocity`].
1283    #[inline(always)]
1284    pub fn direction(&self) -> Vec2 {
1285        self.direction
1286    }
1287
1288    /// Where did the current click/drag originate?
1289    /// `None` if no mouse button is down.
1290    #[inline(always)]
1291    pub fn press_origin(&self) -> Option<Pos2> {
1292        self.press_origin
1293    }
1294
1295    /// How far has the pointer moved since the start of the drag (if any)?
1296    pub fn total_drag_delta(&self) -> Option<Vec2> {
1297        Some(self.latest_pos? - self.press_origin?)
1298    }
1299
1300    /// When did the current click/drag originate?
1301    /// `None` if no mouse button is down.
1302    #[inline(always)]
1303    pub fn press_start_time(&self) -> Option<f64> {
1304        self.press_start_time
1305    }
1306
1307    /// Latest reported pointer position.
1308    /// When tapping a touch screen, this will be `None`.
1309    #[inline(always)]
1310    pub fn latest_pos(&self) -> Option<Pos2> {
1311        self.latest_pos
1312    }
1313
1314    /// If it is a good idea to show a tooltip, where is pointer?
1315    #[inline(always)]
1316    pub fn hover_pos(&self) -> Option<Pos2> {
1317        self.latest_pos
1318    }
1319
1320    /// If you detect a click or drag and wants to know where it happened, use this.
1321    ///
1322    /// Latest position of the mouse, but ignoring any [`Event::PointerGone`]
1323    /// if there were interactions this frame.
1324    /// When tapping a touch screen, this will be the location of the touch.
1325    #[inline(always)]
1326    pub fn interact_pos(&self) -> Option<Pos2> {
1327        self.interact_pos
1328    }
1329
1330    /// Do we have a pointer?
1331    ///
1332    /// `false` if the mouse is not over the egui area, or if no touches are down on touch screens.
1333    #[inline(always)]
1334    pub fn has_pointer(&self) -> bool {
1335        self.latest_pos.is_some()
1336    }
1337
1338    /// Is the pointer currently still?
1339    /// This is smoothed so a few frames of stillness is required before this returns `true`.
1340    #[inline(always)]
1341    pub fn is_still(&self) -> bool {
1342        self.velocity == Vec2::ZERO
1343    }
1344
1345    /// Is the pointer currently moving?
1346    /// This is smoothed so a few frames of stillness is required before this returns `false`.
1347    #[inline]
1348    pub fn is_moving(&self) -> bool {
1349        self.velocity != Vec2::ZERO
1350    }
1351
1352    /// How long has it been (in seconds) since the pointer was last moved?
1353    #[inline(always)]
1354    pub fn time_since_last_movement(&self) -> f32 {
1355        (self.time - self.last_move_time) as f32
1356    }
1357
1358    /// How long has it been (in seconds) since the pointer was clicked?
1359    #[inline(always)]
1360    pub fn time_since_last_click(&self) -> f32 {
1361        (self.time - self.last_click_time) as f32
1362    }
1363
1364    /// Was any pointer button pressed (`!down -> down`) this frame?
1365    ///
1366    /// This can sometimes return `true` even if `any_down() == false`
1367    /// because a press can be shorted than one frame.
1368    pub fn any_pressed(&self) -> bool {
1369        self.pointer_events.iter().any(|event| event.is_press())
1370    }
1371
1372    /// Was any pointer button released (`down -> !down`) this frame?
1373    pub fn any_released(&self) -> bool {
1374        self.pointer_events.iter().any(|event| event.is_release())
1375    }
1376
1377    /// Was the button given pressed this frame?
1378    pub fn button_pressed(&self, button: PointerButton) -> bool {
1379        self.pointer_events
1380            .iter()
1381            .any(|event| matches!(event, &PointerEvent::Pressed{button: b, ..} if button == b))
1382    }
1383
1384    /// Was the button given released this frame?
1385    pub fn button_released(&self, button: PointerButton) -> bool {
1386        self.pointer_events
1387            .iter()
1388            .any(|event| matches!(event, &PointerEvent::Released{button: b, ..} if button == b))
1389    }
1390
1391    /// Was the primary button pressed this frame?
1392    pub fn primary_pressed(&self) -> bool {
1393        self.button_pressed(PointerButton::Primary)
1394    }
1395
1396    /// Was the secondary button pressed this frame?
1397    pub fn secondary_pressed(&self) -> bool {
1398        self.button_pressed(PointerButton::Secondary)
1399    }
1400
1401    /// Was the primary button released this frame?
1402    pub fn primary_released(&self) -> bool {
1403        self.button_released(PointerButton::Primary)
1404    }
1405
1406    /// Was the secondary button released this frame?
1407    pub fn secondary_released(&self) -> bool {
1408        self.button_released(PointerButton::Secondary)
1409    }
1410
1411    /// Is any pointer button currently down?
1412    ///
1413    /// Buttons released this frame are NOT considered down.
1414    pub fn any_down(&self) -> bool {
1415        self.down.iter().any(|&down| down)
1416    }
1417
1418    /// Were there any type of click this frame?
1419    pub fn any_click(&self) -> bool {
1420        self.pointer_events.iter().any(|event| event.is_click())
1421    }
1422
1423    /// Was the given pointer button given clicked this frame?
1424    ///
1425    /// A click is registered when the mouse or touch is released within
1426    /// a certain amount of time and distance from when and where it was pressed.
1427    ///
1428    /// Returns true on double- and triple- clicks too.
1429    pub fn button_clicked(&self, button: PointerButton) -> bool {
1430        self.pointer_events
1431            .iter()
1432            .any(|event| matches!(event, &PointerEvent::Released { button: b, click: Some(_) } if button == b))
1433    }
1434
1435    /// Was the button given double clicked this frame?
1436    pub fn button_double_clicked(&self, button: PointerButton) -> bool {
1437        self.pointer_events.iter().any(|event| {
1438            matches!(
1439                &event,
1440                PointerEvent::Released {
1441                    click: Some(click),
1442                    button: b,
1443                } if *b == button && click.is_double()
1444            )
1445        })
1446    }
1447
1448    /// Was the button given triple clicked this frame?
1449    pub fn button_triple_clicked(&self, button: PointerButton) -> bool {
1450        self.pointer_events.iter().any(|event| {
1451            matches!(
1452                &event,
1453                PointerEvent::Released {
1454                    click: Some(click),
1455                    button: b,
1456                } if *b == button && click.is_triple()
1457            )
1458        })
1459    }
1460
1461    /// Was the primary button clicked this frame?
1462    ///
1463    /// A click is registered when the mouse or touch is released within
1464    /// a certain amount of time and distance from when and where it was pressed.
1465    pub fn primary_clicked(&self) -> bool {
1466        self.button_clicked(PointerButton::Primary)
1467    }
1468
1469    /// Was the secondary button clicked this frame?
1470    ///
1471    /// A click is registered when the mouse or touch is released within
1472    /// a certain amount of time and distance from when and where it was pressed.
1473    pub fn secondary_clicked(&self) -> bool {
1474        self.button_clicked(PointerButton::Secondary)
1475    }
1476
1477    /// Is this button currently down?
1478    ///
1479    /// Buttons released this frame are NOT considered down.
1480    #[inline(always)]
1481    pub fn button_down(&self, button: PointerButton) -> bool {
1482        self.down[button as usize]
1483    }
1484
1485    /// If the pointer button is down, will it register as a click when released?
1486    ///
1487    /// See also [`Self::is_decidedly_dragging`].
1488    pub fn could_any_button_be_click(&self) -> bool {
1489        if self.any_down() || self.any_released() {
1490            if self.has_moved_too_much_for_a_click {
1491                return false;
1492            }
1493
1494            if let Some(press_start_time) = self.press_start_time
1495                && self.time - press_start_time > self.options.max_click_duration
1496            {
1497                return false;
1498            }
1499
1500            true
1501        } else {
1502            false
1503        }
1504    }
1505
1506    /// Just because the mouse is down doesn't mean we are dragging.
1507    /// We could be at the start of a click.
1508    /// But if the mouse is down long enough, or has moved far enough,
1509    /// then we consider it a drag.
1510    ///
1511    /// This function can return true on the same frame the drag is released,
1512    /// but NOT on the first frame it was started.
1513    ///
1514    /// See also [`Self::could_any_button_be_click`].
1515    pub fn is_decidedly_dragging(&self) -> bool {
1516        (self.any_down() || self.any_released())
1517            && !self.any_pressed()
1518            && !self.could_any_button_be_click()
1519            && !self.any_click()
1520    }
1521
1522    /// A long press is something we detect on touch screens
1523    /// to trigger a secondary click (context menu).
1524    ///
1525    /// Returns `true` only on one frame.
1526    pub(crate) fn is_long_press(&self) -> bool {
1527        self.started_decidedly_dragging
1528            && !self.has_moved_too_much_for_a_click
1529            && self.button_down(PointerButton::Primary)
1530            && self.press_start_time.is_some_and(|press_start_time| {
1531                self.time - press_start_time > self.options.max_click_duration
1532            })
1533    }
1534
1535    /// Is the primary button currently down?
1536    ///
1537    /// Buttons released this frame are NOT considered down.
1538    #[inline(always)]
1539    pub fn primary_down(&self) -> bool {
1540        self.button_down(PointerButton::Primary)
1541    }
1542
1543    /// Is the secondary button currently down?
1544    ///
1545    /// Buttons released this frame are NOT considered down.
1546    #[inline(always)]
1547    pub fn secondary_down(&self) -> bool {
1548        self.button_down(PointerButton::Secondary)
1549    }
1550
1551    /// Is the middle button currently down?
1552    ///
1553    /// Buttons released this frame are NOT considered down.
1554    #[inline(always)]
1555    pub fn middle_down(&self) -> bool {
1556        self.button_down(PointerButton::Middle)
1557    }
1558
1559    /// Is the mouse moving in the direction of the given rect?
1560    pub fn is_moving_towards_rect(&self, rect: &Rect) -> bool {
1561        if self.is_still() {
1562            return false;
1563        }
1564
1565        if let Some(pos) = self.hover_pos() {
1566            let dir = self.direction();
1567            if dir != Vec2::ZERO {
1568                return rect.intersects_ray(pos, self.direction());
1569            }
1570        }
1571        false
1572    }
1573}
1574
1575impl InputState {
1576    pub fn ui(&self, ui: &mut crate::Ui) {
1577        let Self {
1578            raw,
1579            pointer,
1580            touch_states,
1581            wheel,
1582            smooth_scroll_delta,
1583            rotation_radians,
1584            zoom_factor_delta,
1585            viewport_rect,
1586            safe_area_insets,
1587            pixels_per_point,
1588            max_texture_side,
1589            time,
1590            unstable_dt,
1591            predicted_dt,
1592            stable_dt,
1593            focused,
1594            modifiers,
1595            keys_down,
1596            events,
1597            options: _,
1598        } = self;
1599
1600        if let Some(style) = ui.style_mut().text_styles.get_mut(&crate::TextStyle::Body) {
1601            style.family = crate::FontFamily::Monospace;
1602        }
1603
1604        ui.collapsing("Raw Input", |ui| raw.ui(ui));
1605
1606        crate::containers::CollapsingHeader::new("🖱 Pointer")
1607            .default_open(false)
1608            .show(ui, |ui| {
1609                pointer.ui(ui);
1610            });
1611
1612        for (device_id, touch_state) in touch_states {
1613            ui.collapsing(format!("Touch State [device {}]", device_id.0), |ui| {
1614                touch_state.ui(ui);
1615            });
1616        }
1617
1618        crate::containers::CollapsingHeader::new("⬍ Scroll")
1619            .default_open(false)
1620            .show(ui, |ui| {
1621                wheel.ui(ui);
1622            });
1623
1624        ui.label(format!("smooth_scroll_delta: {smooth_scroll_delta:4.1}x"));
1625        ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
1626        ui.label(format!("rotation_radians: {rotation_radians:.3} radians"));
1627
1628        ui.label(format!("viewport_rect: {viewport_rect:?} points"));
1629        ui.label(format!("safe_area_insets: {safe_area_insets:?} points"));
1630        ui.label(format!(
1631            "{pixels_per_point} physical pixels for each logical point"
1632        ));
1633        ui.label(format!(
1634            "max texture size (on each side): {max_texture_side}"
1635        ));
1636        ui.label(format!("time: {time:.3} s"));
1637        ui.label(format!(
1638            "time since previous frame: {:.1} ms",
1639            1e3 * unstable_dt
1640        ));
1641        ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
1642        ui.label(format!("stable_dt:    {:.1} ms", 1e3 * stable_dt));
1643        ui.label(format!("focused:   {focused}"));
1644        ui.label(format!("modifiers: {modifiers:#?}"));
1645        ui.label(format!("keys_down: {keys_down:?}"));
1646        ui.scope(|ui| {
1647            ui.set_min_height(150.0);
1648            ui.label(format!("events: {events:#?}"))
1649                .on_hover_text("key presses etc");
1650        });
1651    }
1652}
1653
1654impl PointerState {
1655    pub fn ui(&self, ui: &mut crate::Ui) {
1656        let Self {
1657            time: _,
1658            latest_pos,
1659            interact_pos,
1660            delta,
1661            motion,
1662            velocity,
1663            direction,
1664            pos_history: _,
1665            down,
1666            press_origin,
1667            press_start_time,
1668            has_moved_too_much_for_a_click,
1669            started_decidedly_dragging,
1670            last_click_pos,
1671            last_click_time,
1672            last_last_click_time,
1673            pointer_events,
1674            last_move_time,
1675            options: _,
1676        } = self;
1677
1678        ui.label(format!("latest_pos: {latest_pos:?}"));
1679        ui.label(format!("interact_pos: {interact_pos:?}"));
1680        ui.label(format!("delta: {delta:?}"));
1681        ui.label(format!("motion: {motion:?}"));
1682        ui.label(format!(
1683            "velocity: [{:3.0} {:3.0}] points/sec",
1684            velocity.x, velocity.y
1685        ));
1686        ui.label(format!("direction: {direction:?}"));
1687        ui.label(format!("down: {down:#?}"));
1688        ui.label(format!("press_origin: {press_origin:?}"));
1689        ui.label(format!("press_start_time: {press_start_time:?} s"));
1690        ui.label(format!(
1691            "has_moved_too_much_for_a_click: {has_moved_too_much_for_a_click}"
1692        ));
1693        ui.label(format!(
1694            "started_decidedly_dragging: {started_decidedly_dragging}"
1695        ));
1696        ui.label(format!("last_click_pos: {last_click_pos:#?}"));
1697        ui.label(format!("last_click_time: {last_click_time:#?}"));
1698        ui.label(format!("last_last_click_time: {last_last_click_time:#?}"));
1699        ui.label(format!("last_move_time: {last_move_time:#?}"));
1700        ui.label(format!("pointer_events: {pointer_events:?}"));
1701    }
1702}