1use iced::Task;
2use iced::mouse::ScrollDelta;
3use std::time::{Duration, Instant};
4
5const SCROLL_PIXELS: f32 = 24.0;
7
8const SCROLL_TIMEOUT: Duration = Duration::from_millis(100);
10
11#[derive(Debug, Default, Clone, Copy)]
13pub struct DiscreteScrollDelta {
14 pub x: isize,
15 pub y: isize,
16}
17
18#[derive(Debug, Default)]
21pub struct DiscreteScrollState {
22 x: Scroll,
23 y: Scroll,
24 rate_limit: Option<Duration>,
25}
26
27impl DiscreteScrollState {
28 pub fn rate_limit(mut self, rate_limit: Option<Duration>) -> Self {
32 self.rate_limit = rate_limit;
33 self
34 }
35
36 pub fn reset(&mut self) {
39 self.x.reset();
40 self.y.reset();
41 }
42
43 pub fn update(&mut self, delta: ScrollDelta) -> DiscreteScrollDelta {
45 let (x, y) = match delta {
46 ScrollDelta::Pixels { x, y } => (x / SCROLL_PIXELS, y / SCROLL_PIXELS),
47 ScrollDelta::Lines { x, y } => (x, y),
48 };
49
50 DiscreteScrollDelta {
51 x: self.x.update(x, self.rate_limit),
52 y: self.y.update(y, self.rate_limit),
53 }
54 }
55}
56
57#[derive(Debug, Default)]
59struct Scroll {
60 scroll: Option<(f32, Instant)>,
61 last_discrete: Option<Instant>,
62}
63
64impl Scroll {
65 fn reset(&mut self) {
66 *self = Default::default();
67 }
68
69 fn update(&mut self, delta: f32, rate_limit: Option<Duration>) -> isize {
70 if delta == 0. {
71 self.reset();
73 0
74 } else {
75 let previous_scroll = if let Some((scroll, last_scroll_time)) = self.scroll {
76 if last_scroll_time.elapsed() > SCROLL_TIMEOUT {
77 0.
78 } else {
79 scroll
80 }
81 } else {
82 0.
83 };
84
85 let scroll = previous_scroll + delta;
86
87 if self
88 .last_discrete
89 .is_some_and(|time| time.elapsed() < rate_limit.unwrap_or(Duration::ZERO))
90 {
91 self.scroll = Some((scroll, Instant::now()));
94 0
95 } else {
96 self.scroll = Some((scroll.fract(), Instant::now()));
98 let mut discrete = scroll.trunc() as isize;
99 if discrete != 0 {
100 self.last_discrete = Some(Instant::now());
101 }
102 if rate_limit.is_some() {
103 discrete.signum()
106 } else {
107 discrete
108 }
109 }
110 }
111 }
112}