use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
const MAX_SCALE: f64 = 10.0;
static SYSTEM: AtomicU64 = AtomicU64::new(0x3ff0_0000_0000_0000);
thread_local! {
static OVERRIDE: Cell<Option<bool>> = const { Cell::new(None) };
}
pub fn reduced_motion() -> bool {
motion_scale() == 0.0
}
pub fn motion_scale() -> f64 {
match OVERRIDE.with(Cell::get) {
Some(true) => 0.0,
Some(false) => 1.0,
None => system_motion_scale(),
}
}
pub fn motion_duration(duration: Duration) -> Duration {
let scale = motion_scale();
if scale == 1.0 {
duration
} else {
duration.mul_f64(scale)
}
}
pub fn set_reduced_motion(reduced: Option<bool>) {
OVERRIDE.with(|o| o.set(reduced));
}
pub fn system_motion_scale() -> f64 {
f64::from_bits(SYSTEM.load(Ordering::Relaxed))
}
pub(crate) fn set_system_motion_scale(scale: f64) -> bool {
let scale = if scale.is_finite() {
scale.clamp(0.0, MAX_SCALE)
} else {
1.0
};
SYSTEM.swap(scale.to_bits(), Ordering::Relaxed) != scale.to_bits()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_override_snaps_or_restores_durations() {
let d = Duration::from_millis(120);
set_reduced_motion(Some(true));
assert!(reduced_motion());
assert_eq!(motion_scale(), 0.0);
assert_eq!(motion_duration(d), Duration::ZERO);
set_reduced_motion(Some(false));
assert!(!reduced_motion());
assert_eq!(motion_duration(d), d);
set_reduced_motion(None);
}
#[test]
fn the_override_is_per_thread() {
set_reduced_motion(Some(true));
let other = std::thread::spawn(|| OVERRIDE.with(Cell::get)).join();
assert_eq!(other.unwrap(), None);
set_reduced_motion(None);
}
}