Skip to main content

ferroui_browser/
browser_system_navigation_manager.rs

1use ferroui_base::interactivity::RoutedEventArgs;
2use ferroui_base::platform::ISystemNavigationManagerImpl;
3use ferroui_base::reactive::{Disposable, IDisposable};
4use ferroui_base::utilities::HandlerList;
5use std::rc::Rc;
6
7/// The system navigation of the page: the back navigation of the browser,
8/// once [`add_back_handler`](crate::interop::navigation_helper::add_back_handler)
9/// has made it a back request.
10#[derive(Default)]
11pub struct BrowserSystemNavigationManagerImpl {
12    back_requested: Rc<HandlerList<dyn Fn(&Rc<RoutedEventArgs>)>>,
13}
14
15impl BrowserSystemNavigationManagerImpl {
16    /// Raises the back request. Returns whether a handler handled it.
17    pub fn on_back_requested(&self) -> bool {
18        let routed_event_args = Rc::new(RoutedEventArgs::new());
19
20        for (_, handler) in self.back_requested.snapshot().iter() {
21            handler(&routed_event_args);
22        }
23
24        routed_event_args.handled()
25    }
26}
27
28impl ISystemNavigationManagerImpl for BrowserSystemNavigationManagerImpl {
29    fn back_requested(&self, handler: Rc<dyn Fn(&Rc<RoutedEventArgs>)>) -> Rc<dyn IDisposable> {
30        let token = self.back_requested.add(handler);
31        let handlers = self.back_requested.clone();
32        Disposable::create(move || {
33            handlers.remove(token);
34        })
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41    use std::cell::Cell;
42
43    #[test]
44    fn a_back_request_is_not_handled_without_handlers() {
45        assert!(!BrowserSystemNavigationManagerImpl::default().on_back_requested());
46    }
47
48    #[test]
49    fn a_back_request_is_handled_when_a_handler_marks_it_handled() {
50        let manager = BrowserSystemNavigationManagerImpl::default();
51        let calls = Rc::new(Cell::new(0));
52        let subscription = manager.back_requested({
53            let calls = calls.clone();
54            Rc::new(move |args: &Rc<RoutedEventArgs>| {
55                calls.set(calls.get() + 1);
56                args.set_handled(true);
57            })
58        });
59
60        assert!(manager.on_back_requested());
61        assert_eq!(1, calls.get());
62
63        subscription.dispose();
64        assert!(!manager.on_back_requested());
65        assert_eq!(1, calls.get());
66    }
67
68    #[test]
69    fn every_handler_sees_the_request() {
70        let manager = BrowserSystemNavigationManagerImpl::default();
71        let seen = Rc::new(Cell::new(0));
72        let mut subscriptions = Vec::new();
73        for _ in 0..2 {
74            let seen = seen.clone();
75            subscriptions.push(manager.back_requested(Rc::new(move |_: &Rc<RoutedEventArgs>| seen.set(seen.get() + 1))));
76        }
77
78        assert!(!manager.on_back_requested());
79        assert_eq!(2, seen.get());
80    }
81}