ferroui_browser/
windowing_platform.rs1use crate::browser_activatable_lifetime::BrowserActivatableLifetime;
2use crate::browser_app_builder::BrowserPlatformOptions;
3use crate::browser_platform_settings::BrowserPlatformSettings;
4use crate::browser_runtime_platform::BrowserRuntimePlatform;
5use crate::browser_screens::BrowserScreens;
6use crate::browser_system_navigation_manager::BrowserSystemNavigationManagerImpl;
7use crate::browser_single_threaded_dispatcher_impl::BrowserSingleThreadedDispatcherImpl;
8use crate::cursor::CssCursorFactory;
9use crate::interop::{ferro_module, JsObject};
10use crate::rendering::BrowserSharedRenderLoop;
11use crate::win_stubs::IconLoaderStub;
12use ferroui_base::input::platform::{KeyGestureFormatInfo, PlatformHotkeyConfiguration};
13use ferroui_base::input::{IKeyboardDevice, KeyboardDevice};
14use ferroui_base::platform::{ICursorFactory, IPlatformSettings, IRuntimePlatform, ISystemNavigationManagerImpl};
15use ferroui_base::threading::Dispatcher;
16use ferroui_base::{FerroLocator, LocatorExtensions};
17use ferroui_controls::application_lifetimes::IActivatableLifetime;
18use ferroui_controls::platform::{
19 IPlatformIconLoader, IScreenImpl, ITopLevelImpl, ITrayIconImpl, IWindowImpl, IWindowingPlatform,
20};
21use std::cell::RefCell;
22use std::collections::HashMap;
23use std::rc::Rc;
24
25ferroui_base::ferro_thread_local! {
26 static GLOBAL_THIS: RefCell<Option<JsObject>> = const { RefCell::new(None) };
28 static KEYBOARD: RefCell<Option<Rc<KeyboardDevice>>> = const { RefCell::new(None) };
29}
30
31#[derive(Default)]
34pub struct BrowserWindowingPlatform;
35
36impl BrowserWindowingPlatform {
37 pub fn global_this() -> JsObject {
42 match GLOBAL_THIS.with(|global_this| global_this.borrow().clone()) {
43 Some(global_this) => global_this,
44 None => panic!("Browser backend wasn't initialized. GlobalThis is null."),
45 }
46 }
47
48 pub(crate) fn set_global_this(value: JsObject) {
49 GLOBAL_THIS.with(|global_this| *global_this.borrow_mut() = Some(value));
50 }
51
52 pub fn keyboard() -> Rc<KeyboardDevice> {
57 match KEYBOARD.with(|keyboard| keyboard.borrow().clone()) {
58 Some(keyboard) => keyboard,
59 None => panic!("BrowserWindowingPlatform not registered."),
60 }
61 }
62
63 #[cfg(test)]
66 pub(crate) fn set_keyboard_for_unit_tests(keyboard: Option<Rc<KeyboardDevice>>) {
67 KEYBOARD.with(|slot| *slot.borrow_mut() = keyboard);
68 }
69
70 pub fn register() {
73 let instance: Rc<dyn IWindowingPlatform> = Rc::new(BrowserWindowingPlatform);
74
75 let keyboard = KeyboardDevice::new();
76 KEYBOARD.with(|slot| *slot.borrow_mut() = Some(keyboard.clone()));
77 let keyboard_device: Rc<dyn IKeyboardDevice> = keyboard;
78 let platform_settings = BrowserPlatformSettings::new();
79 let activatable_lifetime = BrowserActivatableLifetime::new();
80 let system_navigation_manager = Rc::new(BrowserSystemNavigationManagerImpl::default());
81 let locator = FerroLocator::current_mutable();
82 locator
83 .bind::<dyn IRuntimePlatform>()
84 .to_singleton::<BrowserRuntimePlatform>(|instance| instance)
85 .bind::<dyn ICursorFactory>()
86 .to_singleton::<CssCursorFactory>(|instance| instance)
87 .bind::<dyn IKeyboardDevice>()
88 .to_constant(keyboard_device)
89 .bind::<dyn IPlatformSettings>()
90 .to_constant(platform_settings.clone())
91 .bind_to_self(platform_settings)
94 .bind::<dyn ISystemNavigationManagerImpl>()
95 .to_constant(system_navigation_manager.clone())
96 .bind_to_self(system_navigation_manager)
97 .bind::<dyn IScreenImpl>()
99 .to_func(|| Some(BrowserScreens::instance() as Rc<dyn IScreenImpl>))
100 .bind::<BrowserScreens>()
101 .to_func(|| Some(BrowserScreens::instance()))
102 .bind::<dyn IWindowingPlatform>()
103 .to_constant(instance)
104 .bind::<dyn IPlatformIconLoader>()
105 .to_singleton::<IconLoaderStub>(|instance| instance)
106 .bind_to_self_singleton::<PlatformHotkeyConfiguration>()
107 .bind_to_self(Rc::new(KeyGestureFormatInfo::new(Some(HashMap::new()), "Cmd", "Ctrl", "Alt", "Shift")))
108 .bind::<dyn IActivatableLifetime>()
109 .to_constant(activatable_lifetime.clone())
110 .bind_to_self(activatable_lifetime);
111
112 Dispatcher::initialize_ui_thread_dispatcher(BrowserSingleThreadedDispatcherImpl::new());
113
114 let options = FerroLocator::current().get_service::<BrowserPlatformOptions>();
121 if options.as_ref().is_none_or(|options| options.render_thread) {
122 BrowserSharedRenderLoop::start_render_thread();
123 }
124
125 if let Some(options) = options {
126 if options.register_ferro_service_worker {
127 let sw_path = ferro_module::resolve_service_worker_path();
128 ferro_module::register_service_worker(sw_path, options.ferro_service_worker_scope.as_deref());
129 }
130 }
131 }
132}
133
134impl IWindowingPlatform for BrowserWindowingPlatform {
135 fn create_window(&self) -> Rc<dyn IWindowImpl> {
136 panic!(
137 "Browser doesn't support windowing platform. In order to display a single-view content, set ISingleViewApplicationLifetime.MainView."
138 );
139 }
140
141 fn create_embeddable_top_level(&self) -> Rc<dyn ITopLevelImpl> {
142 panic!("The method or operation is not implemented.");
143 }
144
145 fn create_embeddable_window(&self) -> Rc<dyn IWindowImpl> {
146 panic!("Browser doesn't support embeddable windowing platform.");
147 }
148
149 fn create_tray_icon(&self) -> Option<Rc<dyn ITrayIconImpl>> {
150 None
151 }
152
153 fn get_windows_z_order(&self, _windows: &[Rc<dyn IWindowImpl>], _z_order: &mut [i64]) {
154 panic!("Specified method is not supported.");
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn there_is_no_tray_icon() {
164 assert!(BrowserWindowingPlatform.create_tray_icon().is_none());
165 }
166
167 #[test]
168 #[should_panic(expected = "Browser doesn't support windowing platform")]
169 fn windows_cannot_be_created() {
170 BrowserWindowingPlatform.create_window();
171 }
172
173 #[test]
174 #[should_panic(expected = "Browser doesn't support embeddable windowing platform")]
175 fn embeddable_windows_cannot_be_created() {
176 BrowserWindowingPlatform.create_embeddable_window();
177 }
178
179 #[test]
180 #[should_panic(expected = "Specified method is not supported")]
181 fn the_z_order_of_windows_is_not_available() {
182 BrowserWindowingPlatform.get_windows_z_order(&[], &mut []);
183 }
184}