freya_components/scrollviews/
use_scroll_controller.rs

1use freya_core::prelude::*;
2use torin::prelude::Direction;
3
4#[derive(Default, PartialEq, Eq)]
5pub enum ScrollPosition {
6    #[default]
7    Start,
8    End,
9}
10
11#[derive(Default)]
12pub struct ScrollConfig {
13    pub default_vertical_position: ScrollPosition,
14    pub default_horizontal_position: ScrollPosition,
15}
16
17pub struct ScrollRequest {
18    pub(crate) position: ScrollPosition,
19    pub(crate) direction: Direction,
20    pub(crate) init: bool,
21}
22
23impl ScrollRequest {
24    pub fn new(position: ScrollPosition, direction: Direction) -> ScrollRequest {
25        ScrollRequest {
26            position,
27            direction,
28            init: false,
29        }
30    }
31}
32
33pub enum ScrollEvent {
34    X(i32),
35    Y(i32),
36}
37
38#[derive(PartialEq, Clone, Copy)]
39pub struct ScrollController {
40    notifier: State<()>,
41    requests: State<Vec<ScrollRequest>>,
42    on_scroll: State<Callback<ScrollEvent, bool>>,
43    get_scroll: State<Callback<(), (i32, i32)>>,
44}
45
46impl From<ScrollController> for (i32, i32) {
47    fn from(val: ScrollController) -> Self {
48        val.get_scroll.read().call(())
49    }
50}
51
52impl ScrollController {
53    pub fn new(x: i32, y: i32, initial_requests: Vec<ScrollRequest>) -> Self {
54        let mut scroll = State::create((x, y));
55        Self {
56            notifier: State::create(()),
57            requests: State::create(initial_requests),
58            on_scroll: State::create(Callback::new(move |ev| {
59                let current = *scroll.read();
60                match ev {
61                    ScrollEvent::X(x) => {
62                        scroll.write().0 = x;
63                    }
64                    ScrollEvent::Y(y) => {
65                        scroll.write().1 = y;
66                    }
67                }
68                current != *scroll.read()
69            })),
70            get_scroll: State::create(Callback::new(move |_| *scroll.read())),
71        }
72    }
73    pub fn managed(
74        notifier: State<()>,
75        requests: State<Vec<ScrollRequest>>,
76        on_scroll: State<Callback<ScrollEvent, bool>>,
77        get_scroll: State<Callback<(), (i32, i32)>>,
78    ) -> Self {
79        Self {
80            notifier,
81            requests,
82            on_scroll,
83            get_scroll,
84        }
85    }
86
87    pub fn use_apply(&mut self, width: f32, height: f32) {
88        let _ = self.notifier.read();
89        for request in self.requests.write().drain(..) {
90            match request {
91                ScrollRequest {
92                    position: ScrollPosition::Start,
93                    direction: Direction::Vertical,
94                    ..
95                } => {
96                    self.on_scroll.write().call(ScrollEvent::Y(0));
97                }
98                ScrollRequest {
99                    position: ScrollPosition::Start,
100                    direction: Direction::Horizontal,
101                    ..
102                } => {
103                    self.on_scroll.write().call(ScrollEvent::X(0));
104                }
105                ScrollRequest {
106                    position: ScrollPosition::End,
107                    direction: Direction::Vertical,
108                    init,
109                    ..
110                } => {
111                    if init && height == 0. {
112                        continue;
113                    }
114                    let (_x, y) = self.get_scroll.read().call(());
115                    self.on_scroll
116                        .write()
117                        .call(ScrollEvent::Y(y - height as i32));
118                }
119                ScrollRequest {
120                    position: ScrollPosition::End,
121                    direction: Direction::Horizontal,
122                    init,
123                    ..
124                } => {
125                    if init && width == 0. {
126                        continue;
127                    }
128
129                    let (x, _y) = self.get_scroll.read().call(());
130                    self.on_scroll
131                        .write()
132                        .call(ScrollEvent::X(x - width as i32));
133                }
134            }
135        }
136    }
137
138    pub fn scroll_to_x(&mut self, to: i32) -> bool {
139        self.on_scroll.write().call(ScrollEvent::X(to))
140    }
141
142    pub fn scroll_to_y(&mut self, to: i32) -> bool {
143        self.on_scroll.write().call(ScrollEvent::Y(to))
144    }
145
146    pub fn scroll_to(&mut self, scroll_position: ScrollPosition, scroll_direction: Direction) {
147        self.requests
148            .write()
149            .push(ScrollRequest::new(scroll_position, scroll_direction));
150        self.notifier.write();
151    }
152}
153
154pub fn use_scroll_controller(init: impl FnOnce() -> ScrollConfig) -> ScrollController {
155    use_hook(|| {
156        let config = init();
157
158        ScrollController::new(
159            0,
160            0,
161            vec![
162                ScrollRequest {
163                    position: config.default_vertical_position,
164                    direction: Direction::Vertical,
165                    init: true,
166                },
167                ScrollRequest {
168                    position: config.default_horizontal_position,
169                    direction: Direction::Horizontal,
170                    init: true,
171                },
172            ],
173        )
174    })
175}