Ely-built primitives plus gallery demos for every chapter-one entry, including gpui natives. The gallery now scripts real input by posting NSEvents to its own queue and shoots each interaction in both modes. Codex gpt-6-astra (max): three rounds; the last finding, a two-line doc comment, was fixed after the three-review cap.
82 lines
2.0 KiB
Rust
82 lines
2.0 KiB
Rust
use std::time::Duration;
|
|
|
|
use gpui::{App, Pixels, px};
|
|
|
|
use crate::theme::ActiveTheme;
|
|
|
|
pub const FAST: Duration = Duration::from_millis(120);
|
|
pub const BASE: Duration = Duration::from_millis(200);
|
|
pub const SLOW: Duration = Duration::from_millis(320);
|
|
pub const THEME: Duration = Duration::from_millis(280);
|
|
|
|
/// Travel of small entrances.
|
|
pub const NUDGE: Pixels = px(4.0);
|
|
|
|
const DAMPING: f32 = 0.75;
|
|
const OMEGA: f32 = 6.91 / DAMPING;
|
|
|
|
/// `base`, or one millisecond under reduced motion.
|
|
pub fn duration(base: Duration, cx: &App) -> Duration {
|
|
if cx.theme().reduced_motion {
|
|
Duration::from_millis(1)
|
|
} else {
|
|
base
|
|
}
|
|
}
|
|
|
|
pub fn lerp(from: f32, to: f32, t: f32) -> f32 {
|
|
from + (to - from) * t
|
|
}
|
|
|
|
pub fn ease_out_cubic(t: f32) -> f32 {
|
|
1.0 - (1.0 - t).powi(3)
|
|
}
|
|
|
|
pub fn ease_in_out_cubic(t: f32) -> f32 {
|
|
if t < 0.5 {
|
|
4.0 * t * t * t
|
|
} else {
|
|
1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
|
|
}
|
|
}
|
|
|
|
/// Damped spring. Overshoots; not a gpui easing.
|
|
pub fn spring(t: f32) -> f32 {
|
|
if t >= 1.0 {
|
|
return 1.0;
|
|
}
|
|
let damped = OMEGA * (1.0 - DAMPING * DAMPING).sqrt();
|
|
let decay = (-DAMPING * OMEGA * t).exp();
|
|
1.0 - decay * ((damped * t).cos() + DAMPING * OMEGA / damped * (damped * t).sin())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn easings_pin_their_ends() {
|
|
for ease in [ease_out_cubic, ease_in_out_cubic, spring] {
|
|
assert!(ease(0.0).abs() < 1e-6);
|
|
assert_eq!(ease(1.0), 1.0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn spring_overshoots_a_little_then_settles() {
|
|
let peak = (0..=100)
|
|
.map(|i| spring(i as f32 / 100.0))
|
|
.fold(0.0, f32::max);
|
|
assert!(peak > 1.01 && peak < 1.05, "peak {peak}");
|
|
assert!((spring(0.95) - 1.0).abs() < 0.01);
|
|
}
|
|
|
|
#[test]
|
|
fn ease_in_out_is_symmetric() {
|
|
for i in 0..=10 {
|
|
let t = i as f32 / 10.0;
|
|
assert!((ease_in_out_cubic(t) + ease_in_out_cubic(1.0 - t) - 1.0).abs() < 1e-5);
|
|
}
|
|
}
|
|
}
|