dioxus_flow/press.rs
1//! When a press has become a drag.
2//!
3//! This is one number and one comparison, and it is here on purpose. Whether a
4//! press is still a click decides three separate things — whether a gesture has
5//! started, whether releasing counts as a click, and when the pointer may be
6//! captured — and the last of those is easy to get wrong: capturing on the press
7//! itself makes the browser re-target `click` and `dblclick` at the capturing
8//! element, so every double-click on anything inside the surface is lost. Capture
9//! belongs to a drag, not to a press, and [`Press::is_drag`] is what says which
10//! one is in hand.
11
12use crate::types::Point;
13
14/// How far a mouse or trackpad must travel before a press is a drag, in surface
15/// pixels.
16pub const PRECISE_THRESHOLD: f64 = 4.0;
17/// The same for touch and pen, which are noisier.
18pub const COARSE_THRESHOLD: f64 = 8.0;
19
20/// The press a gesture began with.
21#[derive(Clone, Copy, Debug, PartialEq)]
22pub struct Press {
23 /// Where it landed, in surface pixels.
24 pub at: Point,
25 /// How far it has to travel to become a drag.
26 pub threshold: f64,
27}
28
29impl Press {
30 pub const fn new(at: Point, threshold: f64) -> Self {
31 Self { at, threshold }
32 }
33
34 /// A press from a pointer that reports its own position precisely, or not.
35 pub const fn from_pointer(at: Point, coarse: bool) -> Self {
36 Self::new(
37 at,
38 if coarse {
39 COARSE_THRESHOLD
40 } else {
41 PRECISE_THRESHOLD
42 },
43 )
44 }
45
46 /// Whether the pointer has travelled far enough for this to be a drag.
47 pub fn is_drag(self, now: Point) -> bool {
48 self.at.distance(now) >= self.threshold
49 }
50
51 /// The same question for a pointer tracked in map coordinates, where the
52 /// threshold still means screen pixels however far the view is zoomed.
53 pub fn is_drag_in_world(self, moved: f64, zoom: f64) -> bool {
54 moved * zoom >= self.threshold
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn a_still_pointer_is_never_a_drag_and_a_travelled_one_always_is() {
64 let press = Press::from_pointer(Point::new(100.0, 100.0), false);
65 assert!(!press.is_drag(press.at));
66 assert!(!press.is_drag(Point::new(103.0, 100.0)));
67 assert!(press.is_drag(Point::new(104.0, 100.0)));
68 assert!(press.is_drag(Point::new(97.0, 97.0)));
69 }
70
71 #[test]
72 fn touch_is_allowed_more_wobble_than_a_mouse() {
73 let at = Point::new(0.0, 0.0);
74 let wobble = Point::new(6.0, 0.0);
75 assert!(Press::from_pointer(at, false).is_drag(wobble));
76 assert!(!Press::from_pointer(at, true).is_drag(wobble));
77 }
78
79 /// The threshold is a screen distance, so the further in the view is zoomed
80 /// the less of the map a drag has to cross.
81 #[test]
82 fn the_threshold_means_screen_pixels_at_any_zoom() {
83 let press = Press::from_pointer(Point::new(0.0, 0.0), false);
84 assert!(!press.is_drag_in_world(3.0, 1.0));
85 assert!(press.is_drag_in_world(3.0, 2.0));
86 assert!(!press.is_drag_in_world(3.0, 0.5));
87 }
88}