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