1use crate::application_shell::ApplicationShellService;
2use crate::application_types::WindowError;
3use crate::environment::{
4 BUILD_SCOPE, ENV_ALL, ENV_BRIGHTNESS, ENV_DIRECTION, ENV_LOCALE, ENV_SAFE_INSETS, ENV_SCALE,
5 ENV_TEXT_SCALE, ENV_VIEW_PADDING, ENV_VIEWPORT, ENV_WINDOW_FOCUS, install_focus_scope_watch,
6};
7use crate::file_dialogs::FileDialogService;
8use crate::frame::Runtime;
9use crate::global_shortcuts::GlobalShortcutService;
10use crate::restoration::{self, Restorable, RestorationHandle};
11use crate::tasks::{RuntimeSpawner, Task, TaskFailure, TaskHandle, TaskScope, TokioHandle};
12use crate::window_commands::{WindowHandle, WindowOpener};
13use crate::window_state::WindowManager;
14use incular_config::RuntimeEnvironment;
15use incular_core::{RestorationKey, RestorationScope};
16use incular_platform::WindowOptions;
17use incular_widgets::{FocusScopeNode, Widget};
18use std::{
19 cell::{Cell, RefCell},
20 rc::Rc,
21};
22
23pub struct BuildContext {
28 spawner: RuntimeSpawner,
29 owner_scope: TaskScope,
30 environment: Rc<RefCell<RuntimeEnvironment>>,
31 environment_dependencies: Rc<Cell<u16>>,
32 window_manager: Option<WindowManager>,
33 restoration_scope: Option<RestorationScope>,
34 restoration: Option<restoration::RestorationManager>,
35}
36impl BuildContext {
37 pub(crate) fn new(
38 spawner: RuntimeSpawner,
39 owner_scope: TaskScope,
40 environment: Rc<RefCell<RuntimeEnvironment>>,
41 environment_dependencies: Rc<Cell<u16>>,
42 window_manager: Option<WindowManager>,
43 restoration_scope: Option<RestorationScope>,
44 ) -> Self {
45 Self {
46 spawner,
47 owner_scope,
48 environment,
49 environment_dependencies,
50 restoration_scope,
51 restoration: window_manager
52 .as_ref()
53 .and_then(|manager| manager.restoration.clone()),
54 window_manager,
55 }
56 }
57 pub fn spawn<F, T>(&self, future: F) -> Task<T>
60 where
61 F: std::future::Future<Output = T> + Send + 'static,
62 T: Send + 'static,
63 {
64 self.spawner.spawn_in(&self.owner_scope, future)
65 }
66 pub fn spawn_in<F, T>(&self, scope: &TaskScope, future: F) -> Task<T>
67 where
68 F: std::future::Future<Output = T> + Send + 'static,
69 T: Send + 'static,
70 {
71 self.spawner.spawn_in(scope, future)
72 }
73 pub fn spawn_into<F, T>(
76 &self,
77 future: F,
78 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
79 ) -> TaskHandle
80 where
81 F: std::future::Future<Output = T> + Send + 'static,
82 T: Send + 'static,
83 {
84 self.spawner
85 .spawn_into_in(&self.owner_scope, future, complete)
86 }
87 pub fn dispatch(&self, callback: impl FnOnce(&mut Runtime) + Send + 'static) {
90 self.spawner.dispatcher().dispatch(callback);
91 }
92 pub fn spawn_blocking<T>(
93 &self,
94 work: impl FnOnce() -> T + Send + 'static,
95 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
96 ) -> TaskHandle
97 where
98 T: Send + 'static,
99 {
100 self.spawner
101 .spawn_blocking_in(&self.owner_scope, work, complete)
102 }
103 #[must_use]
104 pub fn task_scope(&self) -> TaskScope {
105 self.owner_scope.child()
106 }
107
108 pub fn watch_focus_scope(&self, scope: &FocusScopeNode) {
115 let target = BUILD_SCOPE.with(|current| {
116 let current = current.borrow();
117 current.as_ref().map(|build| {
118 (
119 build.element,
120 build.queue.clone(),
121 build.initial_dependencies.clone(),
122 )
123 })
124 });
125 let Some((element, queue, initial_dependencies)) = target else {
126 return;
127 };
128 if let Some(element) = element {
129 if let Some(queue) = queue.upgrade() {
130 install_focus_scope_watch(&queue, element, scope);
131 }
132 } else if let Some(initial_dependencies) = initial_dependencies {
133 initial_dependencies
134 .borrow_mut()
135 .focus_scopes
136 .push(scope.clone());
137 }
138 }
139
140 #[must_use]
144 pub fn restoration_scope(&self) -> Option<RestorationScope> {
145 self.restoration_scope.clone()
146 }
147
148 #[must_use]
152 pub fn restoration(&self) -> Option<RestorationHandle> {
153 self.restoration.clone().map(RestorationHandle::new)
154 }
155
156 #[must_use]
160 pub fn restorable<T>(&self, key: RestorationKey, default: T) -> Option<Restorable<T>>
161 where
162 T: Clone + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
163 {
164 self.restoration_scope
165 .clone()
166 .map(|scope| Restorable::from_scope(scope, key, default))
167 }
168
169 #[must_use]
173 pub fn restored_signal<T>(&self, key: RestorationKey, default: T) -> Option<Restorable<T>>
174 where
175 T: Clone + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
176 {
177 self.restorable(key, default)
178 }
179
180 pub fn open_window(
185 &self,
186 options: WindowOptions,
187 root: Widget,
188 ) -> Result<WindowHandle, WindowError> {
189 self.window_manager
190 .as_ref()
191 .ok_or(WindowError::ApplicationStopped)?
192 .open_window(options, root)
193 }
194
195 #[must_use]
198 pub fn window_opener(&self) -> Option<WindowOpener> {
199 self.window_manager
200 .as_ref()
201 .cloned()
202 .map(|manager| WindowOpener { manager })
203 }
204
205 #[must_use]
208 pub fn window_handle(&self) -> Option<WindowHandle> {
209 let window_id = self.spawner.window_id()?;
210 self.window_manager.as_ref()?.handle(window_id)
211 }
212
213 #[must_use]
216 pub fn file_dialogs(&self) -> Option<FileDialogService> {
217 self.window_handle().map(|window| window.file_dialogs())
218 }
219
220 #[must_use]
224 pub fn global_shortcuts(&self) -> Option<GlobalShortcutService> {
225 let manager = self.window_manager.as_ref()?;
226 Some(GlobalShortcutService::new(
227 manager.global_shortcut_bridge.clone(),
228 manager.application_capabilities.clone(),
229 ))
230 }
231
232 #[must_use]
235 pub fn application_shell(&self) -> Option<ApplicationShellService> {
236 self.window_manager
237 .as_ref()
238 .map(|manager| manager.application_shell.clone())
239 }
240
241 #[must_use]
245 pub fn tokio_handle(&self) -> TokioHandle {
246 self.spawner.tokio_handle()
247 }
248 #[must_use]
251 pub fn environment(&self) -> RuntimeEnvironment {
252 self.environment_dependencies
253 .set(self.environment_dependencies.get() | ENV_ALL);
254 self.environment.borrow().clone()
255 }
256 #[must_use]
257 pub fn viewport(&self) -> incular_core::Size {
258 self.record_environment(ENV_VIEWPORT);
259 self.environment.borrow().viewport
260 }
261 #[must_use]
262 pub fn scale_factor(&self) -> f64 {
263 self.record_environment(ENV_SCALE);
264 self.environment.borrow().scale_factor
265 }
266 #[must_use]
267 pub fn text_scale(&self) -> f32 {
268 self.record_environment(ENV_TEXT_SCALE);
269 self.environment.borrow().text_scale
270 }
271 #[must_use]
272 pub fn safe_insets(&self) -> incular_config::EdgeInsets {
273 self.record_environment(ENV_SAFE_INSETS);
274 self.environment.borrow().safe_insets
275 }
276 #[must_use]
277 pub fn view_insets(&self) -> incular_config::EdgeInsets {
278 self.record_environment(crate::environment::ENV_VIEW_INSETS);
279 self.environment.borrow().view_insets
280 }
281 #[must_use]
285 pub fn view_padding(&self) -> incular_config::EdgeInsets {
286 self.record_environment(ENV_VIEW_PADDING);
287 self.environment.borrow().view_padding
288 }
289 #[must_use]
295 pub fn safe_area(&self, safe_area: incular_widgets::SafeArea) -> Widget {
296 let safe = self.safe_insets();
297 if safe_area.maintains_bottom_view_padding() {
298 safe_area.resolve_with_padding(safe, self.view_padding())
299 } else {
300 safe_area.resolve(safe)
301 }
302 }
303 #[must_use]
304 pub fn brightness(&self) -> incular_config::Brightness {
305 self.record_environment(ENV_BRIGHTNESS);
306 self.environment.borrow().brightness
307 }
308 #[must_use]
309 pub fn locales(&self) -> Vec<incular_config::Locale> {
310 self.record_environment(ENV_LOCALE);
311 self.environment.borrow().locales.clone()
312 }
313 #[must_use]
316 pub fn resolve_locale(
317 &self,
318 supported: &[incular_config::Locale],
319 ) -> Option<incular_config::Locale> {
320 self.record_environment(ENV_LOCALE);
321 self.environment.borrow().resolve_locale(supported)
322 }
323 #[must_use]
324 pub fn text_direction(&self) -> incular_config::TextDirection {
325 self.record_environment(ENV_DIRECTION);
326 self.environment.borrow().text_direction
327 }
328 #[must_use]
329 pub fn window_focused(&self) -> bool {
330 self.record_environment(ENV_WINDOW_FOCUS);
331 self.environment.borrow().window_focused
332 }
333 #[must_use]
334 pub fn reduced_motion(&self) -> bool {
335 self.record_environment(crate::environment::ENV_REDUCED_MOTION);
336 self.environment.borrow().reduced_motion
337 }
338 #[must_use]
339 pub fn high_contrast(&self) -> bool {
340 self.record_environment(crate::environment::ENV_HIGH_CONTRAST);
341 self.environment.borrow().high_contrast
342 }
343 #[must_use]
344 pub fn input_capabilities(&self) -> incular_config::InputCapabilities {
345 self.record_environment(crate::environment::ENV_INPUT);
346 self.environment.borrow().input
347 }
348 #[must_use]
349 pub fn window_occluded(&self) -> bool {
350 self.record_environment(crate::environment::ENV_WINDOW_OCCLUSION);
351 self.environment.borrow().window_occluded
352 }
353 fn record_environment(&self, field: u16) {
354 self.environment_dependencies
355 .set(self.environment_dependencies.get() | field);
356 }
357}