egui/input_state/
mod.rs

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