Skip to main content

kozan_core/scroll/
offsets.rs

1//! Scroll offsets — mutable scroll position per node.
2//!
3//! Chrome: stored in `TransformTree` because scroll IS a translate transform.
4//! Separated from [`ScrollTree`](super::ScrollTree) so offset can change
5//! without touching the node graph — the only mutable state during scroll.
6
7use kozan_primitives::arena::Storage;
8use kozan_primitives::geometry::Offset;
9
10/// Current scroll displacement for every scrollable node.
11///
12/// Paint subtracts these offsets to translate children.
13/// Hit-test adds them back to map screen coords to content coords.
14#[derive(Clone)]
15pub struct ScrollOffsets {
16    offsets: Storage<Offset>,
17}
18
19impl ScrollOffsets {
20    pub fn new() -> Self {
21        Self {
22            offsets: Storage::new(),
23        }
24    }
25
26    /// Returns `Offset::ZERO` for nodes that have never been scrolled.
27    pub fn offset(&self, dom_id: u32) -> Offset {
28        self.offsets.get(dom_id).copied().unwrap_or(Offset::ZERO)
29    }
30
31    pub fn set_offset(&mut self, dom_id: u32, offset: Offset) {
32        self.offsets.set(dom_id, offset);
33    }
34
35    pub fn iter(&self) -> impl Iterator<Item = (u32, &Offset)> + '_ {
36        self.offsets.iter()
37    }
38
39    pub fn clear(&mut self) {
40        self.offsets.clear();
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn default_offset_is_zero() {
50        let offsets = ScrollOffsets::new();
51        assert_eq!(offsets.offset(42), Offset::ZERO);
52    }
53
54    #[test]
55    fn set_then_read() {
56        let mut offsets = ScrollOffsets::new();
57        offsets.set_offset(5, Offset::new(0.0, 120.0));
58        assert_eq!(offsets.offset(5), Offset::new(0.0, 120.0));
59    }
60}