use std::cell::RefCell;
use std::collections::HashMap;
use web_time::{Duration, Instant};
use repose_core::animation::{AnimationSpec, Easing};
use repose_core::scroll::ScrollbarStyle;
pub const FADE_KEY_V: u64 = 0x8000_0010;
pub const FADE_KEY_H: u64 = 0x8000_0011;
struct FadeEntry {
last_offset: f32,
last_active: Instant,
delay_ms: u64,
}
thread_local! {
static ENTRIES: RefCell<HashMap<u64, FadeEntry>> = RefCell::new(HashMap::new());
}
pub fn scrollbar_alpha(key: u64, offset: f32, engaged: bool, style: &ScrollbarStyle) -> f32 {
if !style.fade_enabled {
return 1.0;
}
let now = Instant::now();
let target = ENTRIES.with(|entries| {
let mut entries = entries.borrow_mut();
match entries.get_mut(&key) {
None => {
let active = engaged;
entries.insert(
key,
FadeEntry {
last_offset: offset,
last_active: if active {
now
} else {
now.checked_sub(Duration::from_millis(style.fade_delay_ms))
.unwrap_or(now)
},
delay_ms: style.fade_delay_ms,
},
);
if active { 1.0 } else { 0.0 }
}
Some(entry) => {
if engaged || entry.last_offset != offset {
entry.last_offset = offset;
entry.last_active = now;
entry.delay_ms = style.fade_delay_ms;
1.0
} else if now.duration_since(entry.last_active)
>= Duration::from_millis(entry.delay_ms)
{
0.0
} else {
1.0
}
}
}
});
crate::anim::animate_f32(
format!("scrollbar_fade:{key}"),
target,
AnimationSpec::tween(
Duration::from_millis(style.fade_duration_ms),
Easing::FastOutSlowIn,
),
)
}
pub fn scrollbar_fade_deadline() -> Option<Instant> {
let now = Instant::now();
ENTRIES.with(|entries| {
let mut entries = entries.borrow_mut();
entries.retain(|_, entry| {
now.duration_since(entry.last_active)
< Duration::from_millis(entry.delay_ms) + Duration::from_secs(10)
});
entries
.values()
.map(|entry| entry.last_active + Duration::from_millis(entry.delay_ms))
.filter(|deadline| *deadline > now)
.min()
})
}
#[cfg(test)]
pub fn reset_fade_for_tests() {
ENTRIES.with(|entries| {
entries.borrow_mut().clear();
});
}