1pub mod ease;
10
11mod presence;
12mod spring;
13
14pub use presence::{Presence, PresenceEvent};
15pub use spring::Spring;
16
17use std::time::Duration;
18
19use gpui::Animation;
20
21#[derive(Debug, Clone, Copy, PartialEq)]
24pub enum Easing {
25 Linear,
26 EaseIn,
27 EaseOut,
28 EaseInOut,
29 EaseInCubic,
30 EaseOutCubic,
31 EaseInOutCubic,
32 EaseOutQuint,
33 EaseOutExpo,
34 EaseOutBack,
35 EaseOutElastic,
36 EaseOutBounce,
37 CubicBezier(f32, f32, f32, f32),
39 Spring(Spring),
41}
42
43impl Default for Easing {
44 fn default() -> Self {
45 Easing::EaseOut
46 }
47}
48
49impl Easing {
50 pub fn apply(self, t: f32) -> f32 {
51 match self {
52 Easing::Linear => ease::linear(t),
53 Easing::EaseIn => ease::ease_in(t),
54 Easing::EaseOut => ease::ease_out(t),
55 Easing::EaseInOut => ease::ease_in_out(t),
56 Easing::EaseInCubic => ease::ease_in_cubic(t),
57 Easing::EaseOutCubic => ease::ease_out_cubic(t),
58 Easing::EaseInOutCubic => ease::ease_in_out_cubic(t),
59 Easing::EaseOutQuint => ease::ease_out_quint(t),
60 Easing::EaseOutExpo => ease::ease_out_expo(t),
61 Easing::EaseOutBack => ease::ease_out_back(t),
62 Easing::EaseOutElastic => ease::ease_out_elastic(t),
63 Easing::EaseOutBounce => ease::ease_out_bounce(t),
64 Easing::CubicBezier(x1, y1, x2, y2) => ease::cubic_bezier(x1, y1, x2, y2, t),
65 Easing::Spring(spring) => spring.easing()(t),
66 }
67 }
68
69 pub fn animation(self, duration_ms: u64) -> Animation {
86 self.clock(duration_ms)
87 .with_easing(move |t| self.apply(t).clamp(0.0, 1.0))
88 }
89
90 pub fn clock(self, duration_ms: u64) -> Animation {
96 let duration = match self {
97 Easing::Spring(spring) => Duration::from_secs_f32(spring.settle_seconds()),
98 _ => Duration::from_millis(duration_ms),
99 };
100 Animation::new(duration)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn every_variant_hits_the_endpoints() {
110 let variants = [
111 Easing::Linear,
112 Easing::EaseIn,
113 Easing::EaseOut,
114 Easing::EaseInOut,
115 Easing::EaseInCubic,
116 Easing::EaseOutCubic,
117 Easing::EaseInOutCubic,
118 Easing::EaseOutQuint,
119 Easing::EaseOutExpo,
120 Easing::EaseOutBack,
121 Easing::EaseOutElastic,
122 Easing::EaseOutBounce,
123 Easing::CubicBezier(0.25, 0.1, 0.25, 1.0),
124 Easing::Spring(Spring::default()),
125 ];
126 for easing in variants {
127 assert!(easing.apply(0.0).abs() < 1e-3, "{easing:?} at 0");
128 assert!((easing.apply(1.0) - 1.0).abs() < 1e-3, "{easing:?} at 1");
129 }
130 }
131
132 #[test]
136 fn overshooting_curves_really_overshoot() {
137 let overshooters = [
138 Easing::EaseOutBack,
139 Easing::EaseOutElastic,
140 Easing::Spring(Spring::default()),
141 ];
142 for easing in overshooters {
143 let peak = (1..100)
144 .map(|i| easing.apply(i as f32 / 100.0))
145 .fold(f32::MIN, f32::max);
146 assert!(peak > 1.0, "{easing:?} never exceeded 1.0 (peak {peak})");
147 }
148 }
149}