Skip to main content

repose_core/
nested_scroll.rs

1use std::rc::Rc;
2
3use crate::Vec2;
4
5/// Possible sources of scroll events in the nested scroll system.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum NestedScrollSource {
8    UserInput,
9    SideEffect,
10}
11
12pub type PreScrollFn = Rc<dyn Fn(Vec2, NestedScrollSource) -> Vec2>;
13pub type PostScrollFn = Rc<dyn Fn(Vec2, Vec2, NestedScrollSource) -> Vec2>;
14
15#[derive(Clone, Default)]
16pub struct NestedScrollConnection {
17    pub on_pre_scroll: Option<PreScrollFn>,
18    pub on_post_scroll: Option<PostScrollFn>,
19    pub on_pre_fling: Option<PreScrollFn>,
20    pub on_post_fling: Option<PostScrollFn>,
21}
22
23impl std::fmt::Debug for NestedScrollConnection {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        f.debug_struct("NestedScrollConnection")
26            .field("has_pre_scroll", &self.on_pre_scroll.is_some())
27            .field("has_post_scroll", &self.on_post_scroll.is_some())
28            .field("has_pre_fling", &self.on_pre_fling.is_some())
29            .field("has_post_fling", &self.on_post_fling.is_some())
30            .finish()
31    }
32}
33
34impl NestedScrollConnection {
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    pub fn on_pre_scroll(mut self, f: impl Fn(Vec2, NestedScrollSource) -> Vec2 + 'static) -> Self {
40        self.on_pre_scroll = Some(Rc::new(f));
41        self
42    }
43
44    pub fn on_post_scroll(
45        mut self,
46        f: impl Fn(Vec2, Vec2, NestedScrollSource) -> Vec2 + 'static,
47    ) -> Self {
48        self.on_post_scroll = Some(Rc::new(f));
49        self
50    }
51
52    pub fn on_pre_fling(mut self, f: impl Fn(Vec2, NestedScrollSource) -> Vec2 + 'static) -> Self {
53        self.on_pre_fling = Some(Rc::new(f));
54        self
55    }
56
57    pub fn on_post_fling(
58        mut self,
59        f: impl Fn(Vec2, Vec2, NestedScrollSource) -> Vec2 + 'static,
60    ) -> Self {
61        self.on_post_fling = Some(Rc::new(f));
62        self
63    }
64
65    pub fn dispatch_pre_scroll(&self, available: Vec2, source: NestedScrollSource) -> Vec2 {
66        self.on_pre_scroll
67            .as_ref()
68            .map(|f| f(available, source))
69            .unwrap_or(Vec2::ZERO)
70    }
71
72    pub fn dispatch_post_scroll(
73        &self,
74        consumed: Vec2,
75        available: Vec2,
76        source: NestedScrollSource,
77    ) -> Vec2 {
78        self.on_post_scroll
79            .as_ref()
80            .map(|f| f(consumed, available, source))
81            .unwrap_or(Vec2::ZERO)
82    }
83
84    pub fn dispatch_pre_fling(&self, available: Vec2, source: NestedScrollSource) -> Vec2 {
85        self.on_pre_fling
86            .as_ref()
87            .map(|f| f(available, source))
88            .unwrap_or(Vec2::ZERO)
89    }
90
91    pub fn dispatch_post_fling(
92        &self,
93        consumed: Vec2,
94        available: Vec2,
95        source: NestedScrollSource,
96    ) -> Vec2 {
97        self.on_post_fling
98            .as_ref()
99            .map(|f| f(consumed, available, source))
100            .unwrap_or(Vec2::ZERO)
101    }
102
103    pub fn then(&self, further: &Self) -> Self {
104        let on_pre_scroll: Option<PreScrollFn> = match (&self.on_pre_scroll, &further.on_pre_scroll)
105        {
106            (Some(close), Some(far)) => {
107                let close = close.clone();
108                let far = far.clone();
109                Some(Rc::new(move |v, s| {
110                    let fc = far(v, s);
111                    let cc = close(v - fc, s);
112                    fc + cc
113                }))
114            }
115            (Some(close), None) => Some(close.clone()),
116            (None, Some(far)) => Some(far.clone()),
117            (None, None) => None,
118        };
119        let on_post_scroll: Option<PostScrollFn> =
120            match (&self.on_post_scroll, &further.on_post_scroll) {
121                (Some(close), Some(far)) => {
122                    let close = close.clone();
123                    let far = far.clone();
124                    Some(Rc::new(move |consumed, available, s| {
125                        let cc = close(consumed, available, s);
126                        let fc = far(consumed + cc, available - cc, s);
127                        cc + fc
128                    }))
129                }
130                (Some(close), None) => Some(close.clone()),
131                (None, Some(far)) => Some(far.clone()),
132                (None, None) => None,
133            };
134        let on_pre_fling: Option<PreScrollFn> = match (&self.on_pre_fling, &further.on_pre_fling) {
135            (Some(close), Some(far)) => {
136                let close = close.clone();
137                let far = far.clone();
138                Some(Rc::new(move |v, s| {
139                    let fc = far(v, s);
140                    let cc = close(v - fc, s);
141                    fc + cc
142                }))
143            }
144            (Some(close), None) => Some(close.clone()),
145            (None, Some(far)) => Some(far.clone()),
146            (None, None) => None,
147        };
148        let on_post_fling: Option<PostScrollFn> = match (&self.on_post_fling, &further.on_post_fling)
149        {
150            (Some(close), Some(far)) => {
151                let close = close.clone();
152                let far = far.clone();
153                Some(Rc::new(move |consumed, available, s| {
154                    let cc = close(consumed, available, s);
155                    let fc = far(consumed + cc, available - cc, s);
156                    cc + fc
157                }))
158            }
159            (Some(close), None) => Some(close.clone()),
160            (None, Some(far)) => Some(far.clone()),
161            (None, None) => None,
162        };
163        Self {
164            on_pre_scroll,
165            on_post_scroll,
166            on_pre_fling,
167            on_post_fling,
168        }
169    }
170}