use crate::application_shell::ApplicationShellService;
use crate::application_types::WindowError;
use crate::environment::{
BUILD_SCOPE, ENV_ALL, ENV_BRIGHTNESS, ENV_DIRECTION, ENV_LOCALE, ENV_SAFE_INSETS, ENV_SCALE,
ENV_TEXT_SCALE, ENV_VIEW_PADDING, ENV_VIEWPORT, ENV_WINDOW_FOCUS, install_focus_scope_watch,
};
use crate::file_dialogs::FileDialogService;
use crate::frame::Runtime;
use crate::global_shortcuts::GlobalShortcutService;
use crate::restoration::{self, Restorable, RestorationHandle};
use crate::tasks::{RuntimeSpawner, Task, TaskFailure, TaskHandle, TaskScope, TokioHandle};
use crate::window_commands::{WindowHandle, WindowOpener};
use crate::window_state::WindowManager;
use incular_config::RuntimeEnvironment;
use incular_core::{RestorationKey, RestorationScope};
use incular_platform::WindowOptions;
use incular_widgets::{FocusScopeNode, Widget};
use std::{
cell::{Cell, RefCell},
rc::Rc,
};
pub struct BuildContext {
spawner: RuntimeSpawner,
owner_scope: TaskScope,
environment: Rc<RefCell<RuntimeEnvironment>>,
environment_dependencies: Rc<Cell<u16>>,
window_manager: Option<WindowManager>,
restoration_scope: Option<RestorationScope>,
restoration: Option<restoration::RestorationManager>,
}
impl BuildContext {
pub(crate) fn new(
spawner: RuntimeSpawner,
owner_scope: TaskScope,
environment: Rc<RefCell<RuntimeEnvironment>>,
environment_dependencies: Rc<Cell<u16>>,
window_manager: Option<WindowManager>,
restoration_scope: Option<RestorationScope>,
) -> Self {
Self {
spawner,
owner_scope,
environment,
environment_dependencies,
restoration_scope,
restoration: window_manager
.as_ref()
.and_then(|manager| manager.restoration.clone()),
window_manager,
}
}
pub fn spawn<F, T>(&self, future: F) -> Task<T>
where
F: std::future::Future<Output = T> + Send + 'static,
T: Send + 'static,
{
self.spawner.spawn_in(&self.owner_scope, future)
}
pub fn spawn_in<F, T>(&self, scope: &TaskScope, future: F) -> Task<T>
where
F: std::future::Future<Output = T> + Send + 'static,
T: Send + 'static,
{
self.spawner.spawn_in(scope, future)
}
pub fn spawn_into<F, T>(
&self,
future: F,
complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
) -> TaskHandle
where
F: std::future::Future<Output = T> + Send + 'static,
T: Send + 'static,
{
self.spawner
.spawn_into_in(&self.owner_scope, future, complete)
}
pub fn dispatch(&self, callback: impl FnOnce(&mut Runtime) + Send + 'static) {
self.spawner.dispatcher().dispatch(callback);
}
pub fn spawn_blocking<T>(
&self,
work: impl FnOnce() -> T + Send + 'static,
complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
) -> TaskHandle
where
T: Send + 'static,
{
self.spawner
.spawn_blocking_in(&self.owner_scope, work, complete)
}
#[must_use]
pub fn task_scope(&self) -> TaskScope {
self.owner_scope.child()
}
pub fn watch_focus_scope(&self, scope: &FocusScopeNode) {
let target = BUILD_SCOPE.with(|current| {
let current = current.borrow();
current.as_ref().map(|build| {
(
build.element,
build.queue.clone(),
build.initial_dependencies.clone(),
)
})
});
let Some((element, queue, initial_dependencies)) = target else {
return;
};
if let Some(element) = element {
if let Some(queue) = queue.upgrade() {
install_focus_scope_watch(&queue, element, scope);
}
} else if let Some(initial_dependencies) = initial_dependencies {
initial_dependencies
.borrow_mut()
.focus_scopes
.push(scope.clone());
}
}
#[must_use]
pub fn restoration_scope(&self) -> Option<RestorationScope> {
self.restoration_scope.clone()
}
#[must_use]
pub fn restoration(&self) -> Option<RestorationHandle> {
self.restoration.clone().map(RestorationHandle::new)
}
#[must_use]
pub fn restorable<T>(&self, key: RestorationKey, default: T) -> Option<Restorable<T>>
where
T: Clone + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
{
self.restoration_scope
.clone()
.map(|scope| Restorable::from_scope(scope, key, default))
}
#[must_use]
pub fn restored_signal<T>(&self, key: RestorationKey, default: T) -> Option<Restorable<T>>
where
T: Clone + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
{
self.restorable(key, default)
}
pub fn open_window(
&self,
options: WindowOptions,
root: Widget,
) -> Result<WindowHandle, WindowError> {
self.window_manager
.as_ref()
.ok_or(WindowError::ApplicationStopped)?
.open_window(options, root)
}
#[must_use]
pub fn window_opener(&self) -> Option<WindowOpener> {
self.window_manager
.as_ref()
.cloned()
.map(|manager| WindowOpener { manager })
}
#[must_use]
pub fn window_handle(&self) -> Option<WindowHandle> {
let window_id = self.spawner.window_id()?;
self.window_manager.as_ref()?.handle(window_id)
}
#[must_use]
pub fn file_dialogs(&self) -> Option<FileDialogService> {
self.window_handle().map(|window| window.file_dialogs())
}
#[must_use]
pub fn global_shortcuts(&self) -> Option<GlobalShortcutService> {
let manager = self.window_manager.as_ref()?;
Some(GlobalShortcutService::new(
manager.global_shortcut_bridge.clone(),
manager.application_capabilities.clone(),
))
}
#[must_use]
pub fn application_shell(&self) -> Option<ApplicationShellService> {
self.window_manager
.as_ref()
.map(|manager| manager.application_shell.clone())
}
#[must_use]
pub fn tokio_handle(&self) -> TokioHandle {
self.spawner.tokio_handle()
}
#[must_use]
pub fn environment(&self) -> RuntimeEnvironment {
self.environment_dependencies
.set(self.environment_dependencies.get() | ENV_ALL);
self.environment.borrow().clone()
}
#[must_use]
pub fn viewport(&self) -> incular_core::Size {
self.record_environment(ENV_VIEWPORT);
self.environment.borrow().viewport
}
#[must_use]
pub fn scale_factor(&self) -> f64 {
self.record_environment(ENV_SCALE);
self.environment.borrow().scale_factor
}
#[must_use]
pub fn text_scale(&self) -> f32 {
self.record_environment(ENV_TEXT_SCALE);
self.environment.borrow().text_scale
}
#[must_use]
pub fn safe_insets(&self) -> incular_config::EdgeInsets {
self.record_environment(ENV_SAFE_INSETS);
self.environment.borrow().safe_insets
}
#[must_use]
pub fn view_insets(&self) -> incular_config::EdgeInsets {
self.record_environment(crate::environment::ENV_VIEW_INSETS);
self.environment.borrow().view_insets
}
#[must_use]
pub fn view_padding(&self) -> incular_config::EdgeInsets {
self.record_environment(ENV_VIEW_PADDING);
self.environment.borrow().view_padding
}
#[must_use]
pub fn safe_area(&self, safe_area: incular_widgets::SafeArea) -> Widget {
let safe = self.safe_insets();
if safe_area.maintains_bottom_view_padding() {
safe_area.resolve_with_padding(safe, self.view_padding())
} else {
safe_area.resolve(safe)
}
}
#[must_use]
pub fn brightness(&self) -> incular_config::Brightness {
self.record_environment(ENV_BRIGHTNESS);
self.environment.borrow().brightness
}
#[must_use]
pub fn locales(&self) -> Vec<incular_config::Locale> {
self.record_environment(ENV_LOCALE);
self.environment.borrow().locales.clone()
}
#[must_use]
pub fn resolve_locale(
&self,
supported: &[incular_config::Locale],
) -> Option<incular_config::Locale> {
self.record_environment(ENV_LOCALE);
self.environment.borrow().resolve_locale(supported)
}
#[must_use]
pub fn text_direction(&self) -> incular_config::TextDirection {
self.record_environment(ENV_DIRECTION);
self.environment.borrow().text_direction
}
#[must_use]
pub fn window_focused(&self) -> bool {
self.record_environment(ENV_WINDOW_FOCUS);
self.environment.borrow().window_focused
}
#[must_use]
pub fn reduced_motion(&self) -> bool {
self.record_environment(crate::environment::ENV_REDUCED_MOTION);
self.environment.borrow().reduced_motion
}
#[must_use]
pub fn high_contrast(&self) -> bool {
self.record_environment(crate::environment::ENV_HIGH_CONTRAST);
self.environment.borrow().high_contrast
}
#[must_use]
pub fn input_capabilities(&self) -> incular_config::InputCapabilities {
self.record_environment(crate::environment::ENV_INPUT);
self.environment.borrow().input
}
#[must_use]
pub fn window_occluded(&self) -> bool {
self.record_environment(crate::environment::ENV_WINDOW_OCCLUSION);
self.environment.borrow().window_occluded
}
fn record_environment(&self, field: u16) {
self.environment_dependencies
.set(self.environment_dependencies.get() | field);
}
}