Skip to main content

ferroui_browser/
browser_runtime_platform.rs

1use crate::interop::ferro_module;
2use ferroui_base::platform::{
3    AssetAssembly, IAssetLoader, IRuntimePlatform, RuntimePlatformInfo, StandardAssetLoader, StandardRuntimePlatform,
4};
5use ferroui_base::utilities::DateTime;
6use ferroui_base::FerroLocator;
7use ferroui_controls::AppBuilder;
8use std::cell::Cell;
9use std::rc::Rc;
10
11/// Registers the runtime platform services of the browser.
12pub struct BrowserRuntimePlatformServices;
13
14impl BrowserRuntimePlatformServices {
15    /// Selects the runtime platform of the browser for the application.
16    pub fn use_browser_runtime_platform_subsystem(builder: &AppBuilder) -> AppBuilder {
17        builder.use_runtime_platform_subsystem(|| Self::register(None), "BrowserRuntimePlatform")
18    }
19
20    /// Registers the runtime platform and the asset loader with the current
21    /// service locator. `assembly` is the default assembly of the asset
22    /// loader.
23    pub fn register(assembly: Option<&AssetAssembly>) {
24        let asset_loader: Rc<dyn IAssetLoader> = Rc::new(StandardAssetLoader::new(assembly));
25        FerroLocator::current_mutable()
26            .bind::<dyn IRuntimePlatform>()
27            .to_singleton::<BrowserRuntimePlatform>(|instance| instance)
28            .bind::<dyn IAssetLoader>()
29            .to_constant(asset_loader);
30    }
31}
32
33ferroui_base::ferro_thread_local! {
34    static INFO: Cell<Option<RuntimePlatformInfo>> = const { Cell::new(None) };
35}
36
37/// The runtime platform of the browser: the kind of device comes from the
38/// page.
39#[derive(Default)]
40pub struct BrowserRuntimePlatform {
41    base: StandardRuntimePlatform,
42}
43
44impl BrowserRuntimePlatform {
45    /// The platform information for a device of the given kind.
46    fn runtime_info(is_mobile: bool, is_tv: bool) -> RuntimePlatformInfo {
47        RuntimePlatformInfo { is_mobile: is_mobile && !is_tv, is_desktop: !is_mobile && !is_tv, is_tv }
48    }
49}
50
51impl IRuntimePlatform for BrowserRuntimePlatform {
52    fn get_runtime_info(&self) -> RuntimePlatformInfo {
53        INFO.with(|info| match info.get() {
54            Some(value) => value,
55            None => {
56                let is_mobile = ferro_module::is_mobile();
57                let is_tv = ferro_module::is_tv();
58                let result = Self::runtime_info(is_mobile, is_tv);
59                info.set(Some(result));
60                result
61            }
62        })
63    }
64
65    fn get_utc_now(&self) -> DateTime {
66        self.base.get_utc_now()
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn a_device_is_exactly_one_of_desktop_mobile_and_tv() {
76        for (is_mobile, is_tv, expected) in [
77            (false, false, (true, false, false)),
78            (true, false, (false, true, false)),
79            (false, true, (false, false, true)),
80            (true, true, (false, false, true)),
81        ] {
82            let info = BrowserRuntimePlatform::runtime_info(is_mobile, is_tv);
83
84            assert_eq!(expected, (info.is_desktop, info.is_mobile, info.is_tv), "mobile={is_mobile} tv={is_tv}");
85        }
86    }
87}