use ratatui::crossterm::event::{self, Event as CtEvent, MouseEvent, MouseEventKind};
pub(crate) fn coalesce_scroll(first: &MouseEvent) -> std::io::Result<Option<MouseEvent>> {
let same_dir = |k: MouseEventKind| -> bool {
matches!(
(first.kind, k),
(MouseEventKind::ScrollUp, MouseEventKind::ScrollUp)
| (MouseEventKind::ScrollDown, MouseEventKind::ScrollDown)
)
};
if !matches!(
first.kind,
MouseEventKind::ScrollUp | MouseEventKind::ScrollDown
) {
return Ok(None);
}
const SCROLL_BATCH_CAP: u32 = 40;
let mut count: u32 = 1;
while count < SCROLL_BATCH_CAP {
if !event::poll(std::time::Duration::ZERO)? {
break;
}
let ev = event::read()?;
match ev {
CtEvent::Mouse(m) if same_dir(m.kind) => {
count += 1;
continue;
}
other => {
COALESCE_LEFTOVER.with(|s| {
let mut slot = s.borrow_mut();
debug_assert!(
slot.is_none(),
"COALESCE_LEFTOVER was not drained before re-stashing"
);
*slot = Some(other);
});
break;
}
}
}
if count <= 1 {
return Ok(None);
}
SCROLL_BATCH_COUNT.store(count, std::sync::atomic::Ordering::Relaxed);
Ok(Some(*first))
}
pub(crate) static SCROLL_BATCH_COUNT: std::sync::atomic::AtomicU32 =
std::sync::atomic::AtomicU32::new(1);
thread_local! {
static COALESCE_LEFTOVER: std::cell::RefCell<Option<CtEvent>> =
const { std::cell::RefCell::new(None) };
}
pub(crate) fn take_coalesce_leftover() -> Option<CtEvent> {
COALESCE_LEFTOVER.with(|s| s.borrow_mut().take())
}
pub(crate) fn take_scroll_batch_count() -> u32 {
SCROLL_BATCH_COUNT
.swap(1, std::sync::atomic::Ordering::Relaxed)
.max(1)
}