Skip to main content

ferroui_browser/
browser_single_view_lifetime.rs

1use crate::ferro_view::FerroView;
2use ferroui_base::Ref;
3use ferroui_controls::application_lifetimes::{
4    IApplicationLifetime, ISingleTopLevelApplicationLifetime, ISingleViewApplicationLifetime,
5};
6use ferroui_controls::{Control, TopLevel};
7use std::any::Any;
8use std::cell::RefCell;
9use std::rc::Rc;
10
11/// The lifetime of an application that shows one view in a web page.
12#[derive(Default)]
13pub struct BrowserSingleViewLifetime {
14    view: RefCell<Option<Rc<FerroView>>>,
15}
16
17impl BrowserSingleViewLifetime {
18    /// Creates a lifetime without a view; the start-up of the platform
19    /// sets it.
20    pub fn new() -> Rc<Self> {
21        Rc::new(Self::default())
22    }
23
24    /// The view the main view is shown in.
25    pub fn view(&self) -> Option<Rc<FerroView>> {
26        self.view.borrow().clone()
27    }
28
29    /// Sets the view the main view is shown in.
30    pub fn set_view(&self, view: Option<Rc<FerroView>>) {
31        *self.view.borrow_mut() = view;
32    }
33
34    fn ensure_view(&self) -> Rc<FerroView> {
35        match self.view() {
36            Some(view) => view,
37            None => panic!(
38                "Browser lifetime was not initialized. Make sure AppBuilder::start_browser_app was called."
39            ),
40        }
41    }
42}
43
44impl IApplicationLifetime for BrowserSingleViewLifetime {
45    fn as_any(&self) -> &dyn Any {
46        self
47    }
48
49    fn as_single_view_application_lifetime(&self) -> Option<&dyn ISingleViewApplicationLifetime> {
50        Some(self)
51    }
52
53    fn as_single_top_level_application_lifetime(&self) -> Option<&dyn ISingleTopLevelApplicationLifetime> {
54        Some(self)
55    }
56}
57
58impl ISingleViewApplicationLifetime for BrowserSingleViewLifetime {
59    fn main_view(&self) -> Option<Ref<Control>> {
60        self.ensure_view().content()
61    }
62
63    fn set_main_view(&self, value: Option<Ref<Control>>) {
64        self.ensure_view().set_content(value);
65    }
66}
67
68impl ISingleTopLevelApplicationLifetime for BrowserSingleViewLifetime {
69    fn top_level(&self) -> Option<Ref<TopLevel>> {
70        self.view().map(|view| view.top_level())
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn a_lifetime_without_a_view_has_no_top_level() {
80        let lifetime = BrowserSingleViewLifetime::new();
81
82        assert!(lifetime.view().is_none());
83        assert!(lifetime.top_level().is_none());
84    }
85
86    #[test]
87    fn the_lifetime_is_a_single_view_and_single_top_level_lifetime() {
88        let lifetime = BrowserSingleViewLifetime::new();
89        let lifetime: &dyn IApplicationLifetime = &*lifetime;
90
91        assert!(lifetime.as_single_view_application_lifetime().is_some());
92        assert!(lifetime.as_single_top_level_application_lifetime().is_some());
93        assert!(lifetime.as_controlled_application_lifetime().is_none());
94        assert!(lifetime.as_classic_desktop_style_application_lifetime().is_none());
95    }
96
97    #[test]
98    #[should_panic(expected = "Browser lifetime was not initialized")]
99    fn reading_the_main_view_before_the_view_exists_fails() {
100        BrowserSingleViewLifetime::new().main_view();
101    }
102
103    #[test]
104    #[should_panic(expected = "Browser lifetime was not initialized")]
105    fn setting_the_main_view_before_the_view_exists_fails() {
106        BrowserSingleViewLifetime::new().set_main_view(None);
107    }
108}