1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Platform gesture vocabulary shared by `gpui` and its platform backends.
use gpui_types::{Pixels, px};
use std::time::Duration;
/// Feel constants consumed by gesture recognizers. Provided on a best-effort
/// basis, depending on each platform's support, defaulting to GPUI's own
/// (iOS flavored) values
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GestureTuning {
/// Distance a touch may travel before it stops being a potential tap and
/// becomes a pan/drag.
pub touch_slop: Pixels,
/// Maximum interval between taps for them to accumulate a tap count.
pub multi_tap_interval: Duration,
/// Maximum distance between taps for them to accumulate a tap count.
pub multi_tap_slop: Pixels,
/// How long a touch must remain within [`Self::touch_slop`] to be
/// recognized as a long press.
pub long_press_duration: Duration,
/// How scroll momentum decelerates after a fling.
pub scroll_physics: ScrollPhysics,
/// Minimum release velocity, in pixels per second, required to start
/// scroll momentum.
pub min_fling_velocity: f32,
}
impl Default for GestureTuning {
fn default() -> Self {
Self {
touch_slop: px(8.),
multi_tap_interval: Duration::from_millis(400),
multi_tap_slop: px(16.),
long_press_duration: Duration::from_millis(500),
scroll_physics: ScrollPhysics::ios(),
min_fling_velocity: 50.,
}
}
}
/// How free scrolling decelerates after a fling.
///
/// This models deceleration only. Boundary behavior — bouncing, edge glow,
/// clamping — is the scroll container's policy: the container is the one that
/// knows its extents.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ScrollPhysics {
/// Exponential velocity decay, the `UIScrollView` model:
/// `velocity(t) = v₀ · decay_per_msᵐˢ`.
Exponential {
/// Per-millisecond velocity decay factor. `UIScrollView`'s normal
/// deceleration rate is `0.998`.
decay_per_ms: f32,
},
/// The friction spline of Android's `OverScroller`: fling duration and
/// distance follow a logarithmic deceleration law, and progress along
/// the fling follows a cubic-Bezier ease-out curve. Transcribed from
/// AOSP's `SplineOverScroller` (Apache-2.0).
FrictionSpline {
/// The scroll friction coefficient;
/// `ViewConfiguration.getScrollFriction()` is `0.015` on Android.
friction: f32,
/// Pixels per physical inch of the display, in the coordinate space
/// the fling runs in. Android folds display density into its
/// deceleration coefficient, so the same finger speed flings
/// further in pixels on a denser screen.
pixels_per_inch: f32,
},
}
impl ScrollPhysics {
/// iOS scroll feel: `UIScrollView`'s normal deceleration rate.
pub fn ios() -> Self {
Self::Exponential {
decay_per_ms: 0.998,
}
}
/// Android scroll feel: `OverScroller` with stock friction, at Android's
/// nominal density of 160 density-independent pixels per inch — the
/// right pairing when fling distances are in logical pixels. Platforms
/// that fling in physical pixels, or know the display's true density in
/// their logical space, should construct
/// [`ScrollPhysics::FrictionSpline`] directly.
pub fn android() -> Self {
Self::FrictionSpline {
friction: 0.015,
pixels_per_inch: 160.,
}
}
}
/// The gesture kinds a platform recognizes natively, rather than leaving to
/// gpui core's portable recognizers.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct GestureKinds {
/// Tap (and multi-tap), surfaced as `ClickEvent::Touch`.
pub tap: bool,
/// Long press, surfaced as `LongPressEvent`.
pub long_press: bool,
/// Pan/scroll (including fling momentum), surfaced as `ScrollWheelEvent`s.
pub pan: bool,
/// Pinch to zoom, surfaced as `PinchEvent`s.
pub pinch: bool,
}
impl GestureKinds {
/// No gestures; gpui core's portable recognizers handle everything.
pub const NONE: Self = Self {
tap: false,
long_press: false,
pan: false,
pinch: false,
};
/// All gesture kinds.
pub const ALL: Self = Self {
tap: true,
long_press: true,
pan: true,
pinch: true,
};
}
/// Platform gesture recognition services.
///
/// If your mobile platform supports native gesture recognition, use this
/// to share it with GPUI.
pub trait PlatformGestures {
/// Feel constants for the portable recognizers on this platform.
fn tuning(&self) -> GestureTuning {
GestureTuning::default()
}
/// The gesture kinds this platform recognizes natively.
fn native_recognizers(&self) -> GestureKinds {
GestureKinds::NONE
}
}
/// A no-op [`PlatformGestures`] implementation: no native recognizers and
/// default tuning. Suitable for desktop platforms and tests.
pub struct NullPlatformGestures;
impl PlatformGestures for NullPlatformGestures {}