use super::redraw::Redraw;
use std::time::Duration;
use crossterm::event::{
Event as CtEvent, KeyModifiers, MouseButton, MouseEvent as CtMouseEvent, MouseEventKind,
};
use super::App;
use crate::render::backend::Backend;
pub(super) const AUTOSCROLL_PERIOD: Duration = Duration::from_millis(50);
#[derive(Debug, Default)]
pub(super) struct AutoscrollSession {
pub(super) pointer: Option<(u16, u16)>,
pub(super) next: Option<std::time::Instant>,
pub(super) container: Option<crate::NodeId>,
}
impl<B: Backend> App<B> {
fn autoscroll_drag_source(&self) -> Option<crate::NodeId> {
match self.dom.pointer_capture() {
Some(captured) => self.dom.drag_autoscroll().then_some(captured),
None => self.router.selection_drag.map(|flow| flow.owner()),
}
}
fn disarm_autoscroll(&mut self) {
self.autoscroll.pointer = None;
self.autoscroll.next = None;
self.autoscroll.container = None;
}
pub(super) fn note_autoscroll(&mut self, col: u16, row: u16) {
let Some(source) = self.autoscroll_drag_source() else {
self.disarm_autoscroll();
return;
};
if self.autoscroll.container.is_none() {
let resolved = crate::runtime::scrollbar::resolve_autoscroll_container(
&self.dom,
source,
(col, row),
)
.filter(|&c| {
crate::runtime::scrollbar::autoscroll_step_for(&self.dom, c, (col, row)).is_some()
});
match resolved {
Some(c) => self.autoscroll.container = Some(c),
None => {
self.autoscroll.pointer = None;
self.autoscroll.next = None;
return;
}
}
}
self.autoscroll.pointer = Some((col, row));
if self.autoscroll.next.is_none() {
self.autoscroll.next = Some(self.scheduler.borrow().now() + AUTOSCROLL_PERIOD);
}
}
pub(super) fn service_autoscroll(&mut self) {
let _current = crate::runtime::timers::SchedulerGuard::install(&self.scheduler);
if self.autoscroll_drag_source().is_none() {
self.disarm_autoscroll();
return;
}
let (Some((col, row)), Some(container)) =
(self.autoscroll.pointer, self.autoscroll.container)
else {
return;
};
let now = self.scheduler.borrow().now();
let mut guard = 0u8;
while let Some(next) = self.autoscroll.next {
if now < next || guard >= 8 {
break;
}
guard += 1;
self.prelude.touched = true;
self.autoscroll_tick(container, col, row);
self.autoscroll.next = Some(next + AUTOSCROLL_PERIOD);
}
}
fn autoscroll_tick(&mut self, container: crate::NodeId, col: u16, row: u16) {
let Some((axis, step)) =
crate::runtime::scrollbar::autoscroll_step_for(&self.dom, container, (col, row))
else {
return; };
if !crate::runtime::scrollbar::autoscroll_step(&mut self.dom, container, axis, step) {
return; }
let area = self.terminal.size();
self.cascade_and_layout(area);
let synthetic = CtMouseEvent {
kind: MouseEventKind::Drag(MouseButton::Left),
column: col,
row,
modifiers: KeyModifiers::empty(),
};
let outcome = self.router.route(&mut self.dom, CtEvent::Mouse(synthetic));
self.note_route(outcome);
self.redraw.note(Redraw::Layout); }
}