egui/
input_state.rs

1mod touch_state;
2
3use crate::data::input::*;
4use crate::{emath::*, util::History};
5use std::{
6    collections::{BTreeMap, HashSet},
7    time::Duration,
8};
9
10pub use crate::Key;
11pub use touch_state::MultiTouchInfo;
12use touch_state::TouchState;
13
14/// If the pointer moves more than this, it won't become a click (but it is still a drag)
15const MAX_CLICK_DIST: f32 = 6.0; // TODO(emilk): move to settings
16
17/// If the pointer is down for longer than this it will no longer register as a click.
18///
19/// If a touch is held for this many seconds while still,
20/// then it will register as a "long-touch" which is equivalent to a secondary click.
21///
22/// This is to support "press and hold for context menu" on touch screens.
23const MAX_CLICK_DURATION: f64 = 0.8; // TODO(emilk): move to settings
24
25/// The new pointer press must come within this many seconds from previous pointer release
26const MAX_DOUBLE_CLICK_DELAY: f64 = 0.3; // TODO(emilk): move to settings
27
28/// Input state that egui updates each frame.
29///
30/// You can access this with [`crate::Context::input`].
31///
32/// You can check if `egui` is using the inputs using
33/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`].
34#[derive(Clone, Debug)]
35#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
36pub struct InputState {
37    /// The raw input we got this frame from the backend.
38    pub raw: RawInput,
39
40    /// State of the mouse or simple touch gestures which can be mapped to mouse operations.
41    pub pointer: PointerState,
42
43    /// State of touches, except those covered by `PointerState` (like clicks and drags).
44    /// (We keep a separate [`TouchState`] for each encountered touch device.)
45    touch_states: BTreeMap<TouchDeviceId, TouchState>,
46
47    // ----------------------------------------------
48    // Scrolling:
49    //
50    /// Time of the last scroll event.
51    last_scroll_time: f64,
52
53    /// Used for smoothing the scroll delta.
54    unprocessed_scroll_delta: Vec2,
55
56    /// Used for smoothing the scroll delta when zooming.
57    unprocessed_scroll_delta_for_zoom: f32,
58
59    /// You probably want to use [`Self::smooth_scroll_delta`] instead.
60    ///
61    /// The raw input of how many points the user scrolled.
62    ///
63    /// The delta dictates how the _content_ should move.
64    ///
65    /// A positive X-value indicates the content is being moved right,
66    /// as when swiping right on a touch-screen or track-pad with natural scrolling.
67    ///
68    /// A positive Y-value indicates the content is being moved down,
69    /// as when swiping down on a touch-screen or track-pad with natural scrolling.
70    ///
71    /// When using a notched scroll-wheel this will spike very large for one frame,
72    /// then drop to zero. For a smoother experience, use [`Self::smooth_scroll_delta`].
73    pub raw_scroll_delta: Vec2,
74
75    /// How many points the user scrolled, smoothed over a few frames.
76    ///
77    /// The delta dictates how the _content_ should move.
78    ///
79    /// A positive X-value indicates the content is being moved right,
80    /// as when swiping right on a touch-screen or track-pad with natural scrolling.
81    ///
82    /// A positive Y-value indicates the content is being moved down,
83    /// as when swiping down on a touch-screen or track-pad with natural scrolling.
84    ///
85    /// [`crate::ScrollArea`] will both read and write to this field, so that
86    /// at the end of the frame this will be zero if a scroll-area consumed the delta.
87    pub smooth_scroll_delta: Vec2,
88
89    /// Zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
90    ///
91    /// * `zoom = 1`: no change.
92    /// * `zoom < 1`: pinch together
93    /// * `zoom > 1`: pinch spread
94    zoom_factor_delta: f32,
95
96    // ----------------------------------------------
97    /// Position and size of the egui area.
98    pub screen_rect: Rect,
99
100    /// Also known as device pixel ratio, > 1 for high resolution screens.
101    pub pixels_per_point: f32,
102
103    /// Maximum size of one side of a texture.
104    ///
105    /// This depends on the backend.
106    pub max_texture_side: usize,
107
108    /// Time in seconds. Relative to whatever. Used for animation.
109    pub time: f64,
110
111    /// Time since last frame, in seconds.
112    ///
113    /// This can be very unstable in reactive mode (when we don't paint each frame).
114    /// For animations it is therefore better to use [`Self::stable_dt`].
115    pub unstable_dt: f32,
116
117    /// Estimated time until next frame (provided we repaint right away).
118    ///
119    /// Used for animations to get instant feedback (avoid frame delay).
120    /// Should be set to the expected time between frames when painting at vsync speeds.
121    ///
122    /// On most integrations this has a fixed value of `1.0 / 60.0`, so it is not a very accurate estimate.
123    pub predicted_dt: f32,
124
125    /// Time since last frame (in seconds), but gracefully handles the first frame after sleeping in reactive mode.
126    ///
127    /// In reactive mode (available in e.g. `eframe`), `egui` only updates when there is new input
128    /// or something is animating.
129    /// This can lead to large gaps of time (sleep), leading to large [`Self::unstable_dt`].
130    ///
131    /// If `egui` requested a repaint the previous frame, then `egui` will use
132    /// `stable_dt = unstable_dt;`, but if `egui` did not not request a repaint last frame,
133    /// then `egui` will assume `unstable_dt` is too large, and will use
134    /// `stable_dt = predicted_dt;`.
135    ///
136    /// This means that for the first frame after a sleep,
137    /// `stable_dt` will be a prediction of the delta-time until the next frame,
138    /// and in all other situations this will be an accurate measurement of time passed
139    /// since the previous frame.
140    ///
141    /// Note that a frame can still stall for various reasons, so `stable_dt` can
142    /// still be unusually large in some situations.
143    ///
144    /// When animating something, it is recommended that you use something like
145    /// `stable_dt.min(0.1)` - this will give you smooth animations when the framerate is good
146    /// (even in reactive mode), but will avoid large jumps when framerate is bad,
147    /// and will effectively slow down the animation when FPS drops below 10.
148    pub stable_dt: f32,
149
150    /// The native window has the keyboard focus (i.e. is receiving key presses).
151    ///
152    /// False when the user alt-tab away from the application, for instance.
153    pub focused: bool,
154
155    /// Which modifier keys are down at the start of the frame?
156    pub modifiers: Modifiers,
157
158    // The keys that are currently being held down.
159    pub keys_down: HashSet<Key>,
160
161    /// In-order events received this frame
162    pub events: Vec<Event>,
163}
164
165impl Default for InputState {
166    fn default() -> Self {
167        Self {
168            raw: Default::default(),
169            pointer: Default::default(),
170            touch_states: Default::default(),
171
172            last_scroll_time: f64::NEG_INFINITY,
173            unprocessed_scroll_delta: Vec2::ZERO,
174            unprocessed_scroll_delta_for_zoom: 0.0,
175            raw_scroll_delta: Vec2::ZERO,
176            smooth_scroll_delta: Vec2::ZERO,
177            zoom_factor_delta: 1.0,
178
179            screen_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
180            pixels_per_point: 1.0,
181            max_texture_side: 2048,
182            time: 0.0,
183            unstable_dt: 1.0 / 60.0,
184            predicted_dt: 1.0 / 60.0,
185            stable_dt: 1.0 / 60.0,
186            focused: false,
187            modifiers: Default::default(),
188            keys_down: Default::default(),
189            events: Default::default(),
190        }
191    }
192}
193
194impl InputState {
195    #[must_use]
196    pub fn begin_frame(
197        mut self,
198        mut new: RawInput,
199        requested_immediate_repaint_prev_frame: bool,
200        pixels_per_point: f32,
201        options: &crate::Options,
202    ) -> Self {
203        crate::profile_function!();
204
205        let time = new.time.unwrap_or(self.time + new.predicted_dt as f64);
206        let unstable_dt = (time - self.time) as f32;
207
208        let stable_dt = if requested_immediate_repaint_prev_frame {
209            // we should have had a repaint straight away,
210            // so this should be trustable.
211            unstable_dt
212        } else {
213            new.predicted_dt
214        };
215
216        let screen_rect = new.screen_rect.unwrap_or(self.screen_rect);
217        self.create_touch_states_for_new_devices(&new.events);
218        for touch_state in self.touch_states.values_mut() {
219            touch_state.begin_frame(time, &new, self.pointer.interact_pos);
220        }
221        let pointer = self.pointer.begin_frame(time, &new);
222
223        let mut keys_down = self.keys_down;
224        let mut zoom_factor_delta = 1.0; // TODO(emilk): smoothing for zoom factor
225        let mut raw_scroll_delta = Vec2::ZERO;
226
227        let mut unprocessed_scroll_delta = self.unprocessed_scroll_delta;
228        let mut unprocessed_scroll_delta_for_zoom = self.unprocessed_scroll_delta_for_zoom;
229        let mut smooth_scroll_delta = Vec2::ZERO;
230        let mut smooth_scroll_delta_for_zoom = 0.0;
231
232        for event in &mut new.events {
233            match event {
234                Event::Key {
235                    key,
236                    pressed,
237                    repeat,
238                    ..
239                } => {
240                    if *pressed {
241                        let first_press = keys_down.insert(*key);
242                        *repeat = !first_press;
243                    } else {
244                        keys_down.remove(key);
245                    }
246                }
247                Event::MouseWheel {
248                    unit,
249                    delta,
250                    modifiers,
251                } => {
252                    let mut delta = match unit {
253                        MouseWheelUnit::Point => *delta,
254                        MouseWheelUnit::Line => options.line_scroll_speed * *delta,
255                        MouseWheelUnit::Page => screen_rect.height() * *delta,
256                    };
257
258                    if modifiers.shift {
259                        // Treat as horizontal scrolling.
260                        // Note: one Mac we already get horizontal scroll events when shift is down.
261                        delta = vec2(delta.x + delta.y, 0.0);
262                    }
263
264                    raw_scroll_delta += delta;
265
266                    // Mouse wheels often go very large steps.
267                    // A single notch on a logitech mouse wheel connected to a Macbook returns 14.0 raw_scroll_delta.
268                    // So we smooth it out over several frames for a nicer user experience when scrolling in egui.
269                    // BUT: if the user is using a nice smooth mac trackpad, we don't add smoothing,
270                    // because it adds latency.
271                    let is_smooth = match unit {
272                        MouseWheelUnit::Point => delta.length() < 8.0, // a bit arbitrary here
273                        MouseWheelUnit::Line | MouseWheelUnit::Page => false,
274                    };
275
276                    let is_zoom = modifiers.ctrl || modifiers.mac_cmd || modifiers.command;
277
278                    #[allow(clippy::collapsible_else_if)]
279                    if is_zoom {
280                        if is_smooth {
281                            smooth_scroll_delta_for_zoom += delta.y;
282                        } else {
283                            unprocessed_scroll_delta_for_zoom += delta.y;
284                        }
285                    } else {
286                        if is_smooth {
287                            smooth_scroll_delta += delta;
288                        } else {
289                            unprocessed_scroll_delta += delta;
290                        }
291                    }
292                }
293                Event::Zoom(factor) => {
294                    zoom_factor_delta *= *factor;
295                }
296                _ => {}
297            }
298        }
299
300        {
301            let dt = stable_dt.at_most(0.1);
302            let t = crate::emath::exponential_smooth_factor(0.90, 0.1, dt); // reach _% in _ seconds. TODO(emilk): parameterize
303
304            if unprocessed_scroll_delta != Vec2::ZERO {
305                for d in 0..2 {
306                    if unprocessed_scroll_delta[d].abs() < 1.0 {
307                        smooth_scroll_delta[d] += unprocessed_scroll_delta[d];
308                        unprocessed_scroll_delta[d] = 0.0;
309                    } else {
310                        let applied = t * unprocessed_scroll_delta[d];
311                        smooth_scroll_delta[d] += applied;
312                        unprocessed_scroll_delta[d] -= applied;
313                    }
314                }
315            }
316
317            {
318                // Smooth scroll-to-zoom:
319                if unprocessed_scroll_delta_for_zoom.abs() < 1.0 {
320                    smooth_scroll_delta_for_zoom += unprocessed_scroll_delta_for_zoom;
321                    unprocessed_scroll_delta_for_zoom = 0.0;
322                } else {
323                    let applied = t * unprocessed_scroll_delta_for_zoom;
324                    smooth_scroll_delta_for_zoom += applied;
325                    unprocessed_scroll_delta_for_zoom -= applied;
326                }
327
328                zoom_factor_delta *=
329                    (options.scroll_zoom_speed * smooth_scroll_delta_for_zoom).exp();
330            }
331        }
332
333        let is_scrolling = raw_scroll_delta != Vec2::ZERO || smooth_scroll_delta != Vec2::ZERO;
334        let last_scroll_time = if is_scrolling {
335            time
336        } else {
337            self.last_scroll_time
338        };
339
340        Self {
341            pointer,
342            touch_states: self.touch_states,
343
344            last_scroll_time,
345            unprocessed_scroll_delta,
346            unprocessed_scroll_delta_for_zoom,
347            raw_scroll_delta,
348            smooth_scroll_delta,
349            zoom_factor_delta,
350
351            screen_rect,
352            pixels_per_point,
353            max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
354            time,
355            unstable_dt,
356            predicted_dt: new.predicted_dt,
357            stable_dt,
358            focused: new.focused,
359            modifiers: new.modifiers,
360            keys_down,
361            events: new.events.clone(), // TODO(emilk): remove clone() and use raw.events
362            raw: new,
363        }
364    }
365
366    /// Info about the active viewport
367    #[inline]
368    pub fn viewport(&self) -> &ViewportInfo {
369        self.raw.viewport()
370    }
371
372    #[inline(always)]
373    pub fn screen_rect(&self) -> Rect {
374        self.screen_rect
375    }
376
377    /// Zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
378    /// * `zoom = 1`: no change
379    /// * `zoom < 1`: pinch together
380    /// * `zoom > 1`: pinch spread
381    #[inline(always)]
382    pub fn zoom_delta(&self) -> f32 {
383        // If a multi touch gesture is detected, it measures the exact and linear proportions of
384        // the distances of the finger tips. It is therefore potentially more accurate than
385        // `zoom_factor_delta` which is based on the `ctrl-scroll` event which, in turn, may be
386        // synthesized from an original touch gesture.
387        self.multi_touch()
388            .map_or(self.zoom_factor_delta, |touch| touch.zoom_delta)
389    }
390
391    /// 2D non-proportional zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
392    ///
393    /// For multitouch devices the user can do a horizontal or vertical pinch gesture.
394    /// In these cases a non-proportional zoom factor is a available.
395    /// In other cases, this reverts to `Vec2::splat(self.zoom_delta())`.
396    ///
397    /// For horizontal pinches, this will return `[z, 1]`,
398    /// for vertical pinches this will return `[1, z]`,
399    /// and otherwise this will return `[z, z]`,
400    /// where `z` is the zoom factor:
401    /// * `zoom = 1`: no change
402    /// * `zoom < 1`: pinch together
403    /// * `zoom > 1`: pinch spread
404    #[inline(always)]
405    pub fn zoom_delta_2d(&self) -> Vec2 {
406        // If a multi touch gesture is detected, it measures the exact and linear proportions of
407        // the distances of the finger tips.  It is therefore potentially more accurate than
408        // `zoom_factor_delta` which is based on the `ctrl-scroll` event which, in turn, may be
409        // synthesized from an original touch gesture.
410        self.multi_touch().map_or_else(
411            || Vec2::splat(self.zoom_factor_delta),
412            |touch| touch.zoom_delta_2d,
413        )
414    }
415
416    /// How long has it been (in seconds) since the use last scrolled?
417    #[inline(always)]
418    pub fn time_since_last_scroll(&self) -> f32 {
419        (self.time - self.last_scroll_time) as f32
420    }
421
422    /// The [`crate::Context`] will call this at the end of each frame to see if we need a repaint.
423    ///
424    /// Returns how long to wait for a repaint.
425    pub fn wants_repaint_after(&self) -> Option<Duration> {
426        if self.pointer.wants_repaint()
427            || self.unprocessed_scroll_delta.abs().max_elem() > 0.2
428            || self.unprocessed_scroll_delta_for_zoom.abs() > 0.2
429            || !self.events.is_empty()
430        {
431            // Immediate repaint
432            return Some(Duration::ZERO);
433        }
434
435        if self.any_touches() && !self.pointer.is_decidedly_dragging() {
436            // We need to wake up and check for press-and-hold for the context menu.
437            if let Some(press_start_time) = self.pointer.press_start_time {
438                let press_duration = self.time - press_start_time;
439                if press_duration < MAX_CLICK_DURATION {
440                    let secs_until_menu = MAX_CLICK_DURATION - press_duration;
441                    return Some(Duration::from_secs_f64(secs_until_menu));
442                }
443            }
444        }
445
446        None
447    }
448
449    /// Count presses of a key. If non-zero, the presses are consumed, so that this will only return non-zero once.
450    ///
451    /// Includes key-repeat events.
452    ///
453    /// This uses [`Modifiers::matches_logically`] to match modifiers,
454    /// meaning extra Shift and Alt modifiers are ignored.
455    /// Therefore, you should match most specific shortcuts first,
456    /// i.e. check for `Cmd-Shift-S` ("Save as…") before `Cmd-S` ("Save"),
457    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!
458    pub fn count_and_consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> usize {
459        let mut count = 0usize;
460
461        self.events.retain(|event| {
462            let is_match = matches!(
463                event,
464                Event::Key {
465                    key: ev_key,
466                    modifiers: ev_mods,
467                    pressed: true,
468                    ..
469                } if *ev_key == logical_key && ev_mods.matches_logically(modifiers)
470            );
471
472            count += is_match as usize;
473
474            !is_match
475        });
476
477        count
478    }
479
480    /// Check for a key press. If found, `true` is returned and the key pressed is consumed, so that this will only return `true` once.
481    ///
482    /// Includes key-repeat events.
483    ///
484    /// This uses [`Modifiers::matches_logically`] to match modifiers,
485    /// meaning extra Shift and Alt modifiers are ignored.
486    /// Therefore, you should match most specific shortcuts first,
487    /// i.e. check for `Cmd-Shift-S` ("Save as…") before `Cmd-S` ("Save"),
488    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!
489    pub fn consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> bool {
490        self.count_and_consume_key(modifiers, logical_key) > 0
491    }
492
493    /// Check if the given shortcut has been pressed.
494    ///
495    /// If so, `true` is returned and the key pressed is consumed, so that this will only return `true` once.
496    ///
497    /// This uses [`Modifiers::matches_logically`] to match modifiers,
498    /// meaning extra Shift and Alt modifiers are ignored.
499    /// Therefore, you should match most specific shortcuts first,
500    /// i.e. check for `Cmd-Shift-S` ("Save as…") before `Cmd-S` ("Save"),
501    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!
502    pub fn consume_shortcut(&mut self, shortcut: &KeyboardShortcut) -> bool {
503        let KeyboardShortcut {
504            modifiers,
505            logical_key,
506        } = *shortcut;
507        self.consume_key(modifiers, logical_key)
508    }
509
510    /// Was the given key pressed this frame?
511    ///
512    /// Includes key-repeat events.
513    pub fn key_pressed(&self, desired_key: Key) -> bool {
514        self.num_presses(desired_key) > 0
515    }
516
517    /// How many times was the given key pressed this frame?
518    ///
519    /// Includes key-repeat events.
520    pub fn num_presses(&self, desired_key: Key) -> usize {
521        self.events
522            .iter()
523            .filter(|event| {
524                matches!(
525                    event,
526                    Event::Key { key, pressed: true, .. }
527                    if *key == desired_key
528                )
529            })
530            .count()
531    }
532
533    /// Is the given key currently held down?
534    pub fn key_down(&self, desired_key: Key) -> bool {
535        self.keys_down.contains(&desired_key)
536    }
537
538    /// Was the given key released this frame?
539    pub fn key_released(&self, desired_key: Key) -> bool {
540        self.events.iter().any(|event| {
541            matches!(
542                event,
543                Event::Key {
544                    key,
545                    pressed: false,
546                    ..
547                } if *key == desired_key
548            )
549        })
550    }
551
552    /// Also known as device pixel ratio, > 1 for high resolution screens.
553    #[inline(always)]
554    pub fn pixels_per_point(&self) -> f32 {
555        self.pixels_per_point
556    }
557
558    /// Size of a physical pixel in logical gui coordinates (points).
559    #[inline(always)]
560    pub fn physical_pixel_size(&self) -> f32 {
561        1.0 / self.pixels_per_point()
562    }
563
564    /// How imprecise do we expect the mouse/touch input to be?
565    /// Returns imprecision in points.
566    #[inline(always)]
567    pub fn aim_radius(&self) -> f32 {
568        // TODO(emilk): multiply by ~3 for touch inputs because fingers are fat
569        self.physical_pixel_size()
570    }
571
572    /// Returns details about the currently ongoing multi-touch gesture, if any. Note that this
573    /// method returns `None` for single-touch gestures (click, drag, …).
574    ///
575    /// ```
576    /// # use egui::emath::Rot2;
577    /// # egui::__run_test_ui(|ui| {
578    /// let mut zoom = 1.0; // no zoom
579    /// let mut rotation = 0.0; // no rotation
580    /// let multi_touch = ui.input(|i| i.multi_touch());
581    /// if let Some(multi_touch) = multi_touch {
582    ///     zoom *= multi_touch.zoom_delta;
583    ///     rotation += multi_touch.rotation_delta;
584    /// }
585    /// let transform = zoom * Rot2::from_angle(rotation);
586    /// # });
587    /// ```
588    ///
589    /// By far not all touch devices are supported, and the details depend on the `egui`
590    /// integration backend you are using. `eframe` web supports multi touch for most mobile
591    /// devices, but not for a `Trackpad` on `MacOS`, for example. The backend has to be able to
592    /// capture native touch events, but many browsers seem to pass such events only for touch
593    /// _screens_, but not touch _pads._
594    ///
595    /// Refer to [`MultiTouchInfo`] for details about the touch information available.
596    ///
597    /// Consider using `zoom_delta()` instead of `MultiTouchInfo::zoom_delta` as the former
598    /// delivers a synthetic zoom factor based on ctrl-scroll events, as a fallback.
599    pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
600        // In case of multiple touch devices simply pick the touch_state of the first active device
601        self.touch_states.values().find_map(|t| t.info())
602    }
603
604    /// True if there currently are any fingers touching egui.
605    pub fn any_touches(&self) -> bool {
606        self.touch_states.values().any(|t| t.any_touches())
607    }
608
609    /// True if we have ever received a touch event.
610    pub fn has_touch_screen(&self) -> bool {
611        !self.touch_states.is_empty()
612    }
613
614    /// Scans `events` for device IDs of touch devices we have not seen before,
615    /// and creates a new [`TouchState`] for each such device.
616    fn create_touch_states_for_new_devices(&mut self, events: &[Event]) {
617        for event in events {
618            if let Event::Touch { device_id, .. } = event {
619                self.touch_states
620                    .entry(*device_id)
621                    .or_insert_with(|| TouchState::new(*device_id));
622            }
623        }
624    }
625
626    #[cfg(feature = "accesskit")]
627    pub fn accesskit_action_requests(
628        &self,
629        id: crate::Id,
630        action: accesskit::Action,
631    ) -> impl Iterator<Item = &accesskit::ActionRequest> {
632        let accesskit_id = id.accesskit_id();
633        self.events.iter().filter_map(move |event| {
634            if let Event::AccessKitActionRequest(request) = event {
635                if request.target == accesskit_id && request.action == action {
636                    return Some(request);
637                }
638            }
639            None
640        })
641    }
642
643    #[cfg(feature = "accesskit")]
644    pub fn has_accesskit_action_request(&self, id: crate::Id, action: accesskit::Action) -> bool {
645        self.accesskit_action_requests(id, action).next().is_some()
646    }
647
648    #[cfg(feature = "accesskit")]
649    pub fn num_accesskit_action_requests(&self, id: crate::Id, action: accesskit::Action) -> usize {
650        self.accesskit_action_requests(id, action).count()
651    }
652
653    /// Get all events that matches the given filter.
654    pub fn filtered_events(&self, filter: &EventFilter) -> Vec<Event> {
655        self.events
656            .iter()
657            .filter(|event| filter.matches(event))
658            .cloned()
659            .collect()
660    }
661
662    /// A long press is something we detect on touch screens
663    /// to trigger a secondary click (context menu).
664    ///
665    /// Returns `true` only on one frame.
666    pub(crate) fn is_long_touch(&self) -> bool {
667        self.any_touches() && self.pointer.is_long_press()
668    }
669}
670
671// ----------------------------------------------------------------------------
672
673/// A pointer (mouse or touch) click.
674#[derive(Clone, Debug, PartialEq)]
675#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
676pub(crate) struct Click {
677    pub pos: Pos2,
678
679    /// 1 or 2 (double-click) or 3 (triple-click)
680    pub count: u32,
681
682    /// Allows you to check for e.g. shift-click
683    pub modifiers: Modifiers,
684}
685
686impl Click {
687    pub fn is_double(&self) -> bool {
688        self.count == 2
689    }
690
691    pub fn is_triple(&self) -> bool {
692        self.count == 3
693    }
694}
695
696#[derive(Clone, Debug, PartialEq)]
697#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
698pub(crate) enum PointerEvent {
699    Moved(Pos2),
700    Pressed {
701        position: Pos2,
702        button: PointerButton,
703    },
704    Released {
705        click: Option<Click>,
706        button: PointerButton,
707    },
708}
709
710impl PointerEvent {
711    pub fn is_press(&self) -> bool {
712        matches!(self, Self::Pressed { .. })
713    }
714
715    pub fn is_release(&self) -> bool {
716        matches!(self, Self::Released { .. })
717    }
718
719    pub fn is_click(&self) -> bool {
720        matches!(self, Self::Released { click: Some(_), .. })
721    }
722}
723
724/// Mouse or touch state.
725#[derive(Clone, Debug)]
726#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
727pub struct PointerState {
728    /// Latest known time
729    time: f64,
730
731    // Consider a finger tapping a touch screen.
732    // What position should we report?
733    // The location of the touch, or `None`, because the finger is gone?
734    //
735    // For some cases we want the first: e.g. to check for interaction.
736    // For showing tooltips, we want the latter (no tooltips, since there are no fingers).
737    /// Latest reported pointer position.
738    /// When tapping a touch screen, this will be `None`.
739    latest_pos: Option<Pos2>,
740
741    /// Latest position of the mouse, but ignoring any [`Event::PointerGone`]
742    /// if there were interactions this frame.
743    /// When tapping a touch screen, this will be the location of the touch.
744    interact_pos: Option<Pos2>,
745
746    /// How much the pointer moved compared to last frame, in points.
747    delta: Vec2,
748
749    /// How much the mouse moved since the last frame, in unspecified units.
750    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
751    /// May be unavailable on some integrations.
752    motion: Option<Vec2>,
753
754    /// Current velocity of pointer.
755    velocity: Vec2,
756
757    /// Current direction of pointer.
758    direction: Vec2,
759
760    /// Recent movement of the pointer.
761    /// Used for calculating velocity of pointer.
762    pos_history: History<Pos2>,
763
764    down: [bool; NUM_POINTER_BUTTONS],
765
766    /// Where did the current click/drag originate?
767    /// `None` if no mouse button is down.
768    press_origin: Option<Pos2>,
769
770    /// When did the current click/drag originate?
771    /// `None` if no mouse button is down.
772    press_start_time: Option<f64>,
773
774    /// Set to `true` if the pointer has moved too much (since being pressed)
775    /// for it to be registered as a click.
776    pub(crate) has_moved_too_much_for_a_click: bool,
777
778    /// Did [`Self::is_decidedly_dragging`] go from `false` to `true` this frame?
779    ///
780    /// This could also be the trigger point for a long-touch.
781    pub(crate) started_decidedly_dragging: bool,
782
783    /// When did the pointer get click last?
784    /// Used to check for double-clicks.
785    last_click_time: f64,
786
787    /// When did the pointer get click two clicks ago?
788    /// Used to check for triple-clicks.
789    last_last_click_time: f64,
790
791    /// When was the pointer last moved?
792    /// Used for things like showing hover ui/tooltip with a delay.
793    last_move_time: f64,
794
795    /// All button events that occurred this frame
796    pub(crate) pointer_events: Vec<PointerEvent>,
797}
798
799impl Default for PointerState {
800    fn default() -> Self {
801        Self {
802            time: -f64::INFINITY,
803            latest_pos: None,
804            interact_pos: None,
805            delta: Vec2::ZERO,
806            motion: None,
807            velocity: Vec2::ZERO,
808            direction: Vec2::ZERO,
809            pos_history: History::new(2..1000, 0.1),
810            down: Default::default(),
811            press_origin: None,
812            press_start_time: None,
813            has_moved_too_much_for_a_click: false,
814            started_decidedly_dragging: false,
815            last_click_time: std::f64::NEG_INFINITY,
816            last_last_click_time: std::f64::NEG_INFINITY,
817            last_move_time: std::f64::NEG_INFINITY,
818            pointer_events: vec![],
819        }
820    }
821}
822
823impl PointerState {
824    #[must_use]
825    pub(crate) fn begin_frame(mut self, time: f64, new: &RawInput) -> Self {
826        let was_decidedly_dragging = self.is_decidedly_dragging();
827
828        self.time = time;
829
830        self.pointer_events.clear();
831
832        let old_pos = self.latest_pos;
833        self.interact_pos = self.latest_pos;
834        if self.motion.is_some() {
835            self.motion = Some(Vec2::ZERO);
836        }
837
838        for event in &new.events {
839            match event {
840                Event::PointerMoved(pos) => {
841                    let pos = *pos;
842
843                    self.latest_pos = Some(pos);
844                    self.interact_pos = Some(pos);
845
846                    if let Some(press_origin) = self.press_origin {
847                        self.has_moved_too_much_for_a_click |=
848                            press_origin.distance(pos) > MAX_CLICK_DIST;
849                    }
850
851                    self.pointer_events.push(PointerEvent::Moved(pos));
852                }
853                Event::PointerButton {
854                    pos,
855                    button,
856                    pressed,
857                    modifiers,
858                } => {
859                    let pos = *pos;
860                    let button = *button;
861                    let pressed = *pressed;
862                    let modifiers = *modifiers;
863
864                    self.latest_pos = Some(pos);
865                    self.interact_pos = Some(pos);
866
867                    if pressed {
868                        // Start of a drag: we want to track the velocity for during the drag
869                        // and ignore any incoming movement
870                        self.pos_history.clear();
871                    }
872
873                    if pressed {
874                        self.press_origin = Some(pos);
875                        self.press_start_time = Some(time);
876                        self.has_moved_too_much_for_a_click = false;
877                        self.pointer_events.push(PointerEvent::Pressed {
878                            position: pos,
879                            button,
880                        });
881                    } else {
882                        // Released
883                        let clicked = self.could_any_button_be_click();
884
885                        let click = if clicked {
886                            let double_click =
887                                (time - self.last_click_time) < MAX_DOUBLE_CLICK_DELAY;
888                            let triple_click =
889                                (time - self.last_last_click_time) < (MAX_DOUBLE_CLICK_DELAY * 2.0);
890                            let count = if triple_click {
891                                3
892                            } else if double_click {
893                                2
894                            } else {
895                                1
896                            };
897
898                            self.last_last_click_time = self.last_click_time;
899                            self.last_click_time = time;
900
901                            Some(Click {
902                                pos,
903                                count,
904                                modifiers,
905                            })
906                        } else {
907                            None
908                        };
909
910                        self.pointer_events
911                            .push(PointerEvent::Released { click, button });
912
913                        self.press_origin = None;
914                        self.press_start_time = None;
915                    }
916
917                    self.down[button as usize] = pressed; // must be done after the above call to `could_any_button_be_click`
918                }
919                Event::PointerGone => {
920                    self.latest_pos = None;
921                    // When dragging a slider and the mouse leaves the viewport, we still want the drag to work,
922                    // so we don't treat this as a `PointerEvent::Released`.
923                    // NOTE: we do NOT clear `self.interact_pos` here. It will be cleared next frame.
924                    self.pos_history.clear();
925                }
926                Event::MouseMoved(delta) => *self.motion.get_or_insert(Vec2::ZERO) += *delta,
927                _ => {}
928            }
929        }
930
931        self.delta = if let (Some(old_pos), Some(new_pos)) = (old_pos, self.latest_pos) {
932            new_pos - old_pos
933        } else {
934            Vec2::ZERO
935        };
936
937        if let Some(pos) = self.latest_pos {
938            self.pos_history.add(time, pos);
939        } else {
940            // we do not clear the `pos_history` here, because it is exactly when a finger has
941            // released from the touch screen that we may want to assign a velocity to whatever
942            // the user tried to throw.
943        }
944
945        self.pos_history.flush(time);
946
947        self.velocity = if self.pos_history.len() >= 3 && self.pos_history.duration() > 0.01 {
948            self.pos_history.velocity().unwrap_or_default()
949        } else {
950            Vec2::default()
951        };
952        if self.velocity != Vec2::ZERO {
953            self.last_move_time = time;
954        }
955
956        self.direction = self.pos_history.velocity().unwrap_or_default().normalized();
957
958        self.started_decidedly_dragging = self.is_decidedly_dragging() && !was_decidedly_dragging;
959
960        self
961    }
962
963    fn wants_repaint(&self) -> bool {
964        !self.pointer_events.is_empty() || self.delta != Vec2::ZERO
965    }
966
967    /// How much the pointer moved compared to last frame, in points.
968    #[inline(always)]
969    pub fn delta(&self) -> Vec2 {
970        self.delta
971    }
972
973    /// How much the mouse moved since the last frame, in unspecified units.
974    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
975    /// May be unavailable on some integrations.
976    #[inline(always)]
977    pub fn motion(&self) -> Option<Vec2> {
978        self.motion
979    }
980
981    /// Current velocity of pointer.
982    ///
983    /// This is smoothed over a few frames,
984    /// but can be ZERO when frame-rate is bad.
985    #[inline(always)]
986    pub fn velocity(&self) -> Vec2 {
987        self.velocity
988    }
989
990    /// Current direction of the pointer.
991    ///
992    /// This is less sensitive to bad framerate than [`Self::velocity`].
993    #[inline(always)]
994    pub fn direction(&self) -> Vec2 {
995        self.direction
996    }
997
998    /// Where did the current click/drag originate?
999    /// `None` if no mouse button is down.
1000    #[inline(always)]
1001    pub fn press_origin(&self) -> Option<Pos2> {
1002        self.press_origin
1003    }
1004
1005    /// When did the current click/drag originate?
1006    /// `None` if no mouse button is down.
1007    #[inline(always)]
1008    pub fn press_start_time(&self) -> Option<f64> {
1009        self.press_start_time
1010    }
1011
1012    /// Latest reported pointer position.
1013    /// When tapping a touch screen, this will be `None`.
1014    #[inline(always)]
1015    pub fn latest_pos(&self) -> Option<Pos2> {
1016        self.latest_pos
1017    }
1018
1019    /// If it is a good idea to show a tooltip, where is pointer?
1020    #[inline(always)]
1021    pub fn hover_pos(&self) -> Option<Pos2> {
1022        self.latest_pos
1023    }
1024
1025    /// If you detect a click or drag and wants to know where it happened, use this.
1026    ///
1027    /// Latest position of the mouse, but ignoring any [`Event::PointerGone`]
1028    /// if there were interactions this frame.
1029    /// When tapping a touch screen, this will be the location of the touch.
1030    #[inline(always)]
1031    pub fn interact_pos(&self) -> Option<Pos2> {
1032        self.interact_pos
1033    }
1034
1035    /// Do we have a pointer?
1036    ///
1037    /// `false` if the mouse is not over the egui area, or if no touches are down on touch screens.
1038    #[inline(always)]
1039    pub fn has_pointer(&self) -> bool {
1040        self.latest_pos.is_some()
1041    }
1042
1043    /// Is the pointer currently still?
1044    /// This is smoothed so a few frames of stillness is required before this returns `true`.
1045    #[inline(always)]
1046    pub fn is_still(&self) -> bool {
1047        self.velocity == Vec2::ZERO
1048    }
1049
1050    /// Is the pointer currently moving?
1051    /// This is smoothed so a few frames of stillness is required before this returns `false`.
1052    #[inline]
1053    pub fn is_moving(&self) -> bool {
1054        self.velocity != Vec2::ZERO
1055    }
1056
1057    /// How long has it been (in seconds) since the pointer was last moved?
1058    #[inline(always)]
1059    pub fn time_since_last_movement(&self) -> f32 {
1060        (self.time - self.last_move_time) as f32
1061    }
1062
1063    /// How long has it been (in seconds) since the pointer was clicked?
1064    #[inline(always)]
1065    pub fn time_since_last_click(&self) -> f32 {
1066        (self.time - self.last_click_time) as f32
1067    }
1068
1069    /// Was any pointer button pressed (`!down -> down`) this frame?
1070    ///
1071    /// This can sometimes return `true` even if `any_down() == false`
1072    /// because a press can be shorted than one frame.
1073    pub fn any_pressed(&self) -> bool {
1074        self.pointer_events.iter().any(|event| event.is_press())
1075    }
1076
1077    /// Was any pointer button released (`down -> !down`) this frame?
1078    pub fn any_released(&self) -> bool {
1079        self.pointer_events.iter().any(|event| event.is_release())
1080    }
1081
1082    /// Was the button given pressed this frame?
1083    pub fn button_pressed(&self, button: PointerButton) -> bool {
1084        self.pointer_events
1085            .iter()
1086            .any(|event| matches!(event, &PointerEvent::Pressed{button: b, ..} if button == b))
1087    }
1088
1089    /// Was the button given released this frame?
1090    pub fn button_released(&self, button: PointerButton) -> bool {
1091        self.pointer_events
1092            .iter()
1093            .any(|event| matches!(event, &PointerEvent::Released{button: b, ..} if button == b))
1094    }
1095
1096    /// Was the primary button pressed this frame?
1097    pub fn primary_pressed(&self) -> bool {
1098        self.button_pressed(PointerButton::Primary)
1099    }
1100
1101    /// Was the secondary button pressed this frame?
1102    pub fn secondary_pressed(&self) -> bool {
1103        self.button_pressed(PointerButton::Secondary)
1104    }
1105
1106    /// Was the primary button released this frame?
1107    pub fn primary_released(&self) -> bool {
1108        self.button_released(PointerButton::Primary)
1109    }
1110
1111    /// Was the secondary button released this frame?
1112    pub fn secondary_released(&self) -> bool {
1113        self.button_released(PointerButton::Secondary)
1114    }
1115
1116    /// Is any pointer button currently down?
1117    pub fn any_down(&self) -> bool {
1118        self.down.iter().any(|&down| down)
1119    }
1120
1121    /// Were there any type of click this frame?
1122    pub fn any_click(&self) -> bool {
1123        self.pointer_events.iter().any(|event| event.is_click())
1124    }
1125
1126    /// Was the given pointer button given clicked this frame?
1127    ///
1128    /// Returns true on double- and triple- clicks too.
1129    pub fn button_clicked(&self, button: PointerButton) -> bool {
1130        self.pointer_events
1131            .iter()
1132            .any(|event| matches!(event, &PointerEvent::Released { button: b, click: Some(_) } if button == b))
1133    }
1134
1135    /// Was the button given double clicked this frame?
1136    pub fn button_double_clicked(&self, button: PointerButton) -> bool {
1137        self.pointer_events.iter().any(|event| {
1138            matches!(
1139                &event,
1140                PointerEvent::Released {
1141                    click: Some(click),
1142                    button: b,
1143                } if *b == button && click.is_double()
1144            )
1145        })
1146    }
1147
1148    /// Was the button given triple clicked this frame?
1149    pub fn button_triple_clicked(&self, button: PointerButton) -> bool {
1150        self.pointer_events.iter().any(|event| {
1151            matches!(
1152                &event,
1153                PointerEvent::Released {
1154                    click: Some(click),
1155                    button: b,
1156                } if *b == button && click.is_triple()
1157            )
1158        })
1159    }
1160
1161    /// Was the primary button clicked this frame?
1162    pub fn primary_clicked(&self) -> bool {
1163        self.button_clicked(PointerButton::Primary)
1164    }
1165
1166    /// Was the secondary button clicked this frame?
1167    pub fn secondary_clicked(&self) -> bool {
1168        self.button_clicked(PointerButton::Secondary)
1169    }
1170
1171    /// Is this button currently down?
1172    #[inline(always)]
1173    pub fn button_down(&self, button: PointerButton) -> bool {
1174        self.down[button as usize]
1175    }
1176
1177    /// If the pointer button is down, will it register as a click when released?
1178    ///
1179    /// See also [`Self::is_decidedly_dragging`].
1180    pub fn could_any_button_be_click(&self) -> bool {
1181        if self.any_down() || self.any_released() {
1182            if self.has_moved_too_much_for_a_click {
1183                return false;
1184            }
1185
1186            if let Some(press_start_time) = self.press_start_time {
1187                if self.time - press_start_time > MAX_CLICK_DURATION {
1188                    return false;
1189                }
1190            }
1191
1192            true
1193        } else {
1194            false
1195        }
1196    }
1197
1198    /// Just because the mouse is down doesn't mean we are dragging.
1199    /// We could be at the start of a click.
1200    /// But if the mouse is down long enough, or has moved far enough,
1201    /// then we consider it a drag.
1202    ///
1203    /// This function can return true on the same frame the drag is released,
1204    /// but NOT on the first frame it was started.
1205    ///
1206    /// See also [`Self::could_any_button_be_click`].
1207    pub fn is_decidedly_dragging(&self) -> bool {
1208        (self.any_down() || self.any_released())
1209            && !self.any_pressed()
1210            && !self.could_any_button_be_click()
1211            && !self.any_click()
1212    }
1213
1214    /// A long press is something we detect on touch screens
1215    /// to trigger a secondary click (context menu).
1216    ///
1217    /// Returns `true` only on one frame.
1218    pub(crate) fn is_long_press(&self) -> bool {
1219        self.started_decidedly_dragging
1220            && !self.has_moved_too_much_for_a_click
1221            && self.button_down(PointerButton::Primary)
1222            && self.press_start_time.map_or(false, |press_start_time| {
1223                self.time - press_start_time > MAX_CLICK_DURATION
1224            })
1225    }
1226
1227    /// Is the primary button currently down?
1228    #[inline(always)]
1229    pub fn primary_down(&self) -> bool {
1230        self.button_down(PointerButton::Primary)
1231    }
1232
1233    /// Is the secondary button currently down?
1234    #[inline(always)]
1235    pub fn secondary_down(&self) -> bool {
1236        self.button_down(PointerButton::Secondary)
1237    }
1238
1239    /// Is the middle button currently down?
1240    #[inline(always)]
1241    pub fn middle_down(&self) -> bool {
1242        self.button_down(PointerButton::Middle)
1243    }
1244}
1245
1246impl InputState {
1247    pub fn ui(&self, ui: &mut crate::Ui) {
1248        let Self {
1249            raw,
1250            pointer,
1251            touch_states,
1252
1253            last_scroll_time,
1254            unprocessed_scroll_delta,
1255            unprocessed_scroll_delta_for_zoom,
1256            raw_scroll_delta,
1257            smooth_scroll_delta,
1258
1259            zoom_factor_delta,
1260            screen_rect,
1261            pixels_per_point,
1262            max_texture_side,
1263            time,
1264            unstable_dt,
1265            predicted_dt,
1266            stable_dt,
1267            focused,
1268            modifiers,
1269            keys_down,
1270            events,
1271        } = self;
1272
1273        ui.style_mut()
1274            .text_styles
1275            .get_mut(&crate::TextStyle::Body)
1276            .unwrap()
1277            .family = crate::FontFamily::Monospace;
1278
1279        ui.collapsing("Raw Input", |ui| raw.ui(ui));
1280
1281        crate::containers::CollapsingHeader::new("🖱 Pointer")
1282            .default_open(false)
1283            .show(ui, |ui| {
1284                pointer.ui(ui);
1285            });
1286
1287        for (device_id, touch_state) in touch_states {
1288            ui.collapsing(format!("Touch State [device {}]", device_id.0), |ui| {
1289                touch_state.ui(ui);
1290            });
1291        }
1292
1293        ui.label(format!(
1294            "Time since last scroll: {:.1} s",
1295            time - last_scroll_time
1296        ));
1297        if cfg!(debug_assertions) {
1298            ui.label(format!(
1299                "unprocessed_scroll_delta: {unprocessed_scroll_delta:?} points"
1300            ));
1301            ui.label(format!(
1302                "unprocessed_scroll_delta_for_zoom: {unprocessed_scroll_delta_for_zoom:?} points"
1303            ));
1304        }
1305        ui.label(format!("raw_scroll_delta: {raw_scroll_delta:?} points"));
1306        ui.label(format!(
1307            "smooth_scroll_delta: {smooth_scroll_delta:?} points"
1308        ));
1309        ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
1310
1311        ui.label(format!("screen_rect: {screen_rect:?} points"));
1312        ui.label(format!(
1313            "{pixels_per_point} physical pixels for each logical point"
1314        ));
1315        ui.label(format!(
1316            "max texture size (on each side): {max_texture_side}"
1317        ));
1318        ui.label(format!("time: {time:.3} s"));
1319        ui.label(format!(
1320            "time since previous frame: {:.1} ms",
1321            1e3 * unstable_dt
1322        ));
1323        ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
1324        ui.label(format!("stable_dt:    {:.1} ms", 1e3 * stable_dt));
1325        ui.label(format!("focused:   {focused}"));
1326        ui.label(format!("modifiers: {modifiers:#?}"));
1327        ui.label(format!("keys_down: {keys_down:?}"));
1328        ui.scope(|ui| {
1329            ui.set_min_height(150.0);
1330            ui.label(format!("events: {events:#?}"))
1331                .on_hover_text("key presses etc");
1332        });
1333    }
1334}
1335
1336impl PointerState {
1337    pub fn ui(&self, ui: &mut crate::Ui) {
1338        let Self {
1339            time: _,
1340            latest_pos,
1341            interact_pos,
1342            delta,
1343            motion,
1344            velocity,
1345            direction,
1346            pos_history: _,
1347            down,
1348            press_origin,
1349            press_start_time,
1350            has_moved_too_much_for_a_click,
1351            started_decidedly_dragging,
1352            last_click_time,
1353            last_last_click_time,
1354            pointer_events,
1355            last_move_time,
1356        } = self;
1357
1358        ui.label(format!("latest_pos: {latest_pos:?}"));
1359        ui.label(format!("interact_pos: {interact_pos:?}"));
1360        ui.label(format!("delta: {delta:?}"));
1361        ui.label(format!("motion: {motion:?}"));
1362        ui.label(format!(
1363            "velocity: [{:3.0} {:3.0}] points/sec",
1364            velocity.x, velocity.y
1365        ));
1366        ui.label(format!("direction: {direction:?}"));
1367        ui.label(format!("down: {down:#?}"));
1368        ui.label(format!("press_origin: {press_origin:?}"));
1369        ui.label(format!("press_start_time: {press_start_time:?} s"));
1370        ui.label(format!(
1371            "has_moved_too_much_for_a_click: {has_moved_too_much_for_a_click}"
1372        ));
1373        ui.label(format!(
1374            "started_decidedly_dragging: {started_decidedly_dragging}"
1375        ));
1376        ui.label(format!("last_click_time: {last_click_time:#?}"));
1377        ui.label(format!("last_last_click_time: {last_last_click_time:#?}"));
1378        ui.label(format!("last_move_time: {last_move_time:#?}"));
1379        ui.label(format!("pointer_events: {pointer_events:?}"));
1380    }
1381}