Skip to main content

term_wm_layout_engine/
scroll.rs

1/// Scroll state for a scrollable viewport.
2///
3/// Tracks an accumulated `pending` offset (from bump operations) and an
4/// `offset` that is clamped against the scrollable content range on `apply`.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct ScrollState {
7    pub offset: usize,
8    pub pending: isize,
9}
10
11impl ScrollState {
12    pub fn new() -> Self {
13        Self {
14            offset: 0,
15            pending: 0,
16        }
17    }
18
19    pub fn reset(&mut self) {
20        self.offset = 0;
21        self.pending = 0;
22    }
23
24    pub fn bump(&mut self, delta: isize) {
25        self.pending = self.pending.saturating_add(delta);
26    }
27
28    pub fn apply(&mut self, total: usize, view: usize) {
29        let max_offset = total.saturating_sub(view);
30        let new_offset = (self.offset as isize).saturating_add(self.pending).max(0) as usize;
31        self.offset = new_offset.min(max_offset);
32        self.pending = 0;
33    }
34}
35
36impl Default for ScrollState {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn scroll_apply_clamps_to_max() {
48        let mut s = ScrollState::new();
49        s.bump(100);
50        s.apply(50, 10); // total=50, view=10, max_offset=40
51        assert_eq!(s.offset, 40);
52    }
53
54    #[test]
55    fn scroll_apply_clamps_to_zero() {
56        let mut s = ScrollState::new();
57        s.bump(-10);
58        s.apply(50, 10);
59        assert_eq!(s.offset, 0);
60    }
61
62    #[test]
63    fn scroll_apply_normal() {
64        let mut s = ScrollState::new();
65        s.bump(5);
66        s.apply(50, 10); // max_offset = 40
67        assert_eq!(s.offset, 5);
68    }
69
70    #[test]
71    fn scroll_reset() {
72        let mut s = ScrollState::new();
73        s.bump(10);
74        s.apply(100, 20);
75        assert_eq!(s.offset, 10);
76        s.reset();
77        assert_eq!(s.offset, 0);
78        assert_eq!(s.pending, 0);
79    }
80}