1use crate::TransientPresentationResolution;
2use crate::application_shell::ApplicationShellService;
3use crate::application_types::ApplicationLifecycle;
4use crate::application_types::{RestorableWindowMetadata, WindowError, WindowRestorationId};
5use crate::context::BuildContext;
6use crate::environment::{
7 BuildScope, BuildScopeGuard, EnvironmentFields, InitialBuildDependencies, ReactiveQueue,
8};
9use crate::file_dialogs::{FileDialogBridge, FileDialogService};
10use crate::frame::Runtime;
11use crate::global_shortcuts::GlobalShortcutBridge;
12use crate::profiling::{FrameRecord, GpuSample, RenderFrameMetrics};
13use crate::restoration;
14use crate::tasks::{self, TaskScope};
15use crate::window_commands::{
16 DisplayCatalog, NativeWindowCommand, WindowCommandBridge, WindowHandle,
17};
18use incular_accessibility::AccessibilityDiagnostics;
19use incular_config::{Constraints, ContentSensitivity, RuntimeEnvironment, WindowSizePolicy};
20use incular_core::{RestorationKey, RestorationScope, Size};
21use incular_platform::{
22 PlatformCapabilities, PlatformOperationError, WindowId, WindowLifecycle, WindowMetrics,
23 WindowObservedState, WindowOptions, WindowRequestedState,
24};
25use incular_widgets::Widget;
26use std::{
27 cell::{Cell, RefCell},
28 collections::{HashSet, VecDeque},
29 rc::{Rc, Weak},
30 sync::{Arc, RwLock},
31};
32
33#[derive(Clone, Debug, PartialEq)]
35pub struct WindowDiagnostics {
36 pub id: WindowId,
37 pub title: String,
38 pub logical_size: incular_core::Size,
39 pub physical_size: incular_platform::PhysicalSize,
40 pub scale_factor: f64,
41 pub visible: bool,
42 pub native_focused: bool,
43 pub lifecycle: WindowLifecycle,
44 pub application_lifecycle: ApplicationLifecycle,
48 pub frame_requested: bool,
49 pub requested_frames: u64,
50 pub presented_frames: u64,
51 pub skipped_frames: u64,
52 pub input_events: u64,
53 pub surface_generation: u64,
54 pub content_sensitivity: ContentSensitivity,
56 pub elements: usize,
57 pub render_objects: usize,
58 pub semantics: usize,
59 pub accessibility: AccessibilityDiagnostics,
62 pub capabilities: PlatformCapabilities,
64 pub last_platform_error: Option<PlatformOperationError>,
67 pub requested_state: WindowRequestedState,
69 pub observed_state: WindowObservedState,
72 pub transient_presentations: Vec<TransientPresentationResolution>,
75}
76
77#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
79pub struct ApplicationDiagnostics {
80 pub windows_created: u64,
81 pub windows_closed: u64,
82 pub active_windows: usize,
83 pub stale_window_commands: u64,
84 pub pending_native_requests: usize,
85 pub pending_file_dialog_requests: usize,
86}
87
88pub(crate) struct WindowRecord {
89 pub(crate) runtime: Runtime,
90 pub(crate) scope: TaskScope,
91 pub(crate) last_frame: FrameRecord,
92 pub(crate) render_metrics: RenderFrameMetrics,
93 pub(crate) gpu_sample: Option<GpuSample>,
94 pub(crate) options: WindowOptions,
95 pub(crate) lifecycle: WindowLifecycle,
96 pub(crate) metrics: WindowMetrics,
97 pub(crate) native_focused: bool,
98 pub(crate) requested_frames: u64,
99 pub(crate) presented_frames: u64,
100 pub(crate) skipped_frames: u64,
101 pub(crate) input_events: u64,
102 pub(crate) surface_generation: u64,
103 pub(crate) accessibility: AccessibilityDiagnostics,
104 pub(crate) last_content_sensitivity: Option<ContentSensitivity>,
105 pub(crate) content_sizing: ContentSizeCoordinator,
106 pub(crate) restoration: Option<RestorableWindowMetadata>,
107 pub(crate) _restoration_scope_lease: Option<restoration::ScopeLease>,
108 pub(crate) capabilities: Arc<RwLock<PlatformCapabilities>>,
109 pub(crate) last_platform_error: Option<PlatformOperationError>,
110 pub(crate) requested_state: WindowRequestedState,
111 pub(crate) observed_state: Arc<RwLock<WindowObservedState>>,
112 pub(crate) transient_presentations: Arc<RwLock<Vec<TransientPresentationResolution>>>,
113}
114
115#[derive(Clone, Copy, Debug, Default, PartialEq)]
123pub(crate) struct ContentSizeCoordinator {
124 pending: Option<Size>,
125}
126
127impl ContentSizeCoordinator {
128 pub(crate) fn layout_constraints(
129 options: &WindowOptions,
130 viewport_constraints: Constraints,
131 ) -> Constraints {
132 if options.size_policy != WindowSizePolicy::Content {
133 return viewport_constraints;
134 }
135 let minimum = content_minimum(options);
136 let (maximum_width, maximum_height) = options
137 .maximum_logical_size
138 .map_or((f32::INFINITY, f32::INFINITY), |maximum| {
139 (maximum.width, maximum.height)
140 });
141 Constraints::new(
142 minimum.width,
143 maximum_width.max(minimum.width),
144 minimum.height,
145 maximum_height.max(minimum.height),
146 )
147 }
148
149 pub(crate) fn reconcile(
150 &mut self,
151 options: &WindowOptions,
152 metrics: WindowMetrics,
153 root_layout_size: Option<Size>,
154 ) -> Option<Size> {
155 if options.size_policy != WindowSizePolicy::Content {
156 self.pending = None;
157 return None;
158 }
159
160 let minimum = content_minimum(options);
161 let extent = root_layout_size.unwrap_or(Size::ZERO);
162 let mut target = Size::new(
163 minimum.width.max(extent.width),
164 minimum.height.max(extent.height),
165 );
166 if let Some(maximum) = options.maximum_logical_size {
167 target.width = target.width.min(maximum.width);
168 target.height = target.height.min(maximum.height);
169 }
170 target = snap_outward_to_physical_pixels(target, metrics.scale_factor);
171 if let Some(maximum) = options.maximum_logical_size {
172 target.width = target.width.min(maximum.width);
173 target.height = target.height.min(maximum.height);
174 }
175
176 let current = metrics.logical_size();
177 if equivalent_logical_size(target, current, metrics.scale_factor) {
178 self.pending = None;
179 return None;
180 }
181 if self
182 .pending
183 .is_some_and(|pending| equivalent_logical_size(pending, target, metrics.scale_factor))
184 {
185 return None;
186 }
187 self.pending = Some(target);
188 Some(target)
189 }
190
191 pub(crate) fn reset(&mut self) {
192 self.pending = None;
193 }
194}
195
196fn content_minimum(options: &WindowOptions) -> Size {
197 let declared = options.minimum_logical_size.unwrap_or(Size::ZERO);
198 Size::new(
199 options.initial_logical_size.width.max(declared.width),
200 options.initial_logical_size.height.max(declared.height),
201 )
202}
203
204fn snap_outward_to_physical_pixels(size: Size, scale_factor: f64) -> Size {
205 let scale = if scale_factor.is_finite() && scale_factor > 0.0 {
206 scale_factor as f32
207 } else {
208 1.0
209 };
210 Size::new(
211 (size.width * scale).ceil() / scale,
212 (size.height * scale).ceil() / scale,
213 )
214}
215
216fn equivalent_logical_size(left: Size, right: Size, scale_factor: f64) -> bool {
217 let scale = if scale_factor.is_finite() && scale_factor > 0.0 {
218 scale_factor as f32
219 } else {
220 1.0
221 };
222 let tolerance = 0.5 / scale + f32::EPSILON;
223 (left.width - right.width).abs() <= tolerance && (left.height - right.height).abs() <= tolerance
224}
225
226pub(crate) struct WindowSlot {
227 pub(crate) generation: u32,
228 pub(crate) reserved: bool,
229 pub(crate) retired: bool,
230 pub(crate) record: Option<WindowRecord>,
231}
232
233#[derive(Default)]
234pub(crate) struct WindowRegistry {
235 pub(crate) slots: Vec<WindowSlot>,
236 pub(crate) windows_created: u64,
237 pub(crate) windows_closed: u64,
238 pub(crate) stale_window_commands: u64,
239}
240
241impl WindowRegistry {
242 pub(crate) fn reserve(&mut self) -> WindowId {
243 if let Some((index, slot)) = self
244 .slots
245 .iter_mut()
246 .enumerate()
247 .find(|(_, slot)| !slot.retired && !slot.reserved && slot.record.is_none())
248 {
249 slot.reserved = true;
250 return WindowId::from_parts(
251 u32::try_from(index).expect("window index was validated on insertion"),
252 slot.generation,
253 );
254 }
255 let index = u32::try_from(self.slots.len()).expect("window identity space exhausted");
256 self.slots.push(WindowSlot {
257 generation: 0,
258 reserved: true,
259 retired: false,
260 record: None,
261 });
262 WindowId::from_parts(index, 0)
263 }
264
265 pub(crate) fn insert(&mut self, id: WindowId, record: WindowRecord) {
266 let slot = &mut self.slots[id.index() as usize];
267 debug_assert!(slot.reserved && slot.generation == id.generation());
268 slot.record = Some(record);
269 self.windows_created += 1;
270 }
271
272 pub(crate) fn take(&mut self, id: WindowId) -> Option<WindowRecord> {
273 self.slots
274 .get_mut(id.index() as usize)
275 .filter(|slot| slot.reserved && slot.generation == id.generation())?
276 .record
277 .take()
278 }
279
280 pub(crate) fn restore(&mut self, id: WindowId, record: WindowRecord) {
281 let slot = &mut self.slots[id.index() as usize];
282 debug_assert!(slot.reserved && slot.generation == id.generation());
283 debug_assert!(slot.record.is_none());
284 slot.record = Some(record);
285 }
286
287 pub(crate) fn close(&mut self, id: WindowId) -> Option<WindowRecord> {
288 let slot = self
289 .slots
290 .get_mut(id.index() as usize)
291 .filter(|slot| slot.reserved && slot.generation == id.generation())?;
292 let record = slot.record.take()?;
293 slot.reserved = false;
294 if let Some(next_generation) = slot.generation.checked_add(1) {
295 slot.generation = next_generation;
296 } else {
297 slot.retired = true;
298 }
299 self.windows_closed += 1;
300 Some(record)
301 }
302
303 pub(crate) fn ids(&self) -> Vec<WindowId> {
304 self.slots
305 .iter()
306 .enumerate()
307 .filter_map(|(index, slot)| {
308 slot.record.as_ref().map(|_| {
309 WindowId::from_parts(
310 u32::try_from(index).expect("window index was validated on insertion"),
311 slot.generation,
312 )
313 })
314 })
315 .collect()
316 }
317
318 pub(crate) fn contains(&self, id: WindowId) -> bool {
319 self.slots.get(id.index() as usize).is_some_and(|slot| {
320 slot.reserved && slot.generation == id.generation() && slot.record.is_some()
321 })
322 }
323
324 #[cfg_attr(not(feature = "devtools"), allow(dead_code))]
325 pub(crate) fn get(&self, id: WindowId) -> Option<&WindowRecord> {
326 self.slots
327 .get(id.index() as usize)
328 .filter(|slot| slot.reserved && slot.generation == id.generation())?
329 .record
330 .as_ref()
331 }
332
333 pub(crate) fn visible_count(&self) -> usize {
334 self.slots
335 .iter()
336 .filter_map(|slot| slot.record.as_ref())
337 .filter(|record| record.options.visible)
338 .count()
339 }
340}
341
342#[derive(Clone)]
343pub(crate) struct WindowManager {
344 pub(crate) registry: Weak<RefCell<WindowRegistry>>,
345 pub(crate) scheduler: Rc<RefCell<tasks::TaskScheduler>>,
346 pub(crate) bridge: Arc<WindowCommandBridge>,
347 pub(crate) file_dialog_bridge: Arc<FileDialogBridge>,
348 pub(crate) global_shortcut_bridge: Arc<GlobalShortcutBridge>,
349 pub(crate) application_shell: ApplicationShellService,
350 pub(crate) native_commands: Rc<RefCell<VecDeque<NativeWindowCommand>>>,
351 pub(crate) restoration: Option<restoration::RestorationManager>,
352 pub(crate) application_capabilities: Arc<RwLock<PlatformCapabilities>>,
353 pub(crate) displays: Arc<RwLock<DisplayCatalog>>,
354 pub(crate) application_lifecycle: Rc<Cell<ApplicationLifecycle>>,
355}
356
357impl WindowManager {
358 pub(crate) fn open_window(
359 &self,
360 options: WindowOptions,
361 root: Widget,
362 ) -> Result<WindowHandle, WindowError> {
363 self.open_window_inner(options, root, None)
364 }
365
366 pub(crate) fn open_window_inner(
367 &self,
368 mut options: WindowOptions,
369 root: Widget,
370 restoration: Option<RestorableWindowMetadata>,
371 ) -> Result<WindowHandle, WindowError> {
372 self.apply_restored_window_options(&mut options, restoration.as_ref());
373 options.validate()?;
374 let registry = self
375 .registry
376 .upgrade()
377 .ok_or(WindowError::ApplicationStopped)?;
378 let restoration_lease = self.acquire_restoration_scope(restoration.as_ref())?;
379 let id = registry.borrow_mut().reserve();
380 let scope = tasks::TaskScheduler::spawner(&self.scheduler).scope();
381 scope.bind_window(id);
382 let mut runtime = Runtime::with_window(
383 root,
384 self.scheduler.clone(),
385 Some(id),
386 scope,
387 Some(self.clone()),
388 )?;
389 runtime.lifecycle = self.application_lifecycle.get();
390 runtime.update_window_metrics(initial_metrics(&options));
391 Ok(self.install_window(id, runtime, options, restoration, restoration_lease))
392 }
393
394 pub(crate) fn open_window_with(
395 &self,
396 options: WindowOptions,
397 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
398 ) -> Result<WindowHandle, WindowError> {
399 self.open_window_with_inner(options, build, None)
400 }
401
402 pub(crate) fn open_restorable_window_with(
403 &self,
404 restoration_id: WindowRestorationId,
405 kind: impl Into<String>,
406 options: WindowOptions,
407 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
408 ) -> Result<WindowHandle, WindowError> {
409 self.open_window_with_inner(
410 options,
411 build,
412 Some(RestorableWindowMetadata::new(restoration_id, kind)?),
413 )
414 }
415
416 pub(crate) fn open_window_with_inner(
417 &self,
418 mut options: WindowOptions,
419 build: impl FnMut(&mut BuildContext) -> Widget + 'static,
420 restoration: Option<RestorableWindowMetadata>,
421 ) -> Result<WindowHandle, WindowError> {
422 self.apply_restored_window_options(&mut options, restoration.as_ref());
423 options.validate()?;
424 let registry = self
425 .registry
426 .upgrade()
427 .ok_or(WindowError::ApplicationStopped)?;
428 let restoration_scope = self.restoration_scope(restoration.as_ref());
429 let restoration_lease = self.acquire_restoration_scope(restoration.as_ref())?;
430 let id = registry.borrow_mut().reserve();
431 let spawner = tasks::TaskScheduler::spawner_for(&self.scheduler, id);
432 let window_scope = spawner.scope();
433 window_scope.bind_window(id);
434 let root_scope = window_scope.child();
435 let metrics = initial_metrics(&options);
436 let initial_environment = RuntimeEnvironment {
437 viewport: metrics.logical_size(),
438 physical_width: metrics.physical_size.width,
439 physical_height: metrics.physical_size.height,
440 scale_factor: metrics.scale_factor,
441 ..RuntimeEnvironment::default()
442 };
443 let environment = Rc::new(RefCell::new(initial_environment));
444 let environment_dependencies = Rc::new(Cell::new(EnvironmentFields::empty()));
445 let build = Rc::new(RefCell::new(build));
446 let reactive = ReactiveQueue::new();
447 let initial_dependencies = Rc::new(RefCell::new(InitialBuildDependencies::default()));
448 let mut build_context = BuildContext::new(
449 spawner.clone(),
450 root_scope.clone(),
451 environment.clone(),
452 environment_dependencies.clone(),
453 Some(self.clone()),
454 restoration_scope.clone(),
455 );
456 let initial = {
457 let _build_scope = BuildScopeGuard::enter(BuildScope {
458 root: reactive.borrow().root,
459 element: None,
460 queue: Rc::downgrade(&reactive),
461 initial_dependencies: Some(initial_dependencies.clone()),
462 spawner: spawner.clone(),
463 owner_scope: root_scope.clone(),
464 });
465 incular_core::hot_reload::call_with(&mut *build.borrow_mut(), &mut build_context)
466 };
467 let mut runtime = Runtime::with_window_and_reactive(
468 initial,
469 self.scheduler.clone(),
470 Some(id),
471 window_scope.clone(),
472 Some(self.clone()),
473 reactive,
474 )?;
475 runtime.environment = environment;
476 runtime.environment_dependencies = environment_dependencies;
477 let root = runtime.tree().root().expect("new runtime has root");
478 root_scope.bind_owner(root);
479 runtime.install_initial_dependencies(root, initial_dependencies);
480 let closure = build.clone();
481 let builder_spawner = spawner.clone();
482 let builder_scope = root_scope.clone();
483 let builder_environment = runtime.environment.clone();
484 let builder_dependencies = runtime.environment_dependencies.clone();
485 let builder_manager = self.clone();
486 runtime.register_builder_without_rebuild(root, move || {
487 let mut context = BuildContext::new(
488 builder_spawner.clone(),
489 builder_scope.clone(),
490 builder_environment.clone(),
491 builder_dependencies.clone(),
492 Some(builder_manager.clone()),
493 restoration_scope.clone(),
494 );
495 incular_core::hot_reload::call_with(&mut *closure.borrow_mut(), &mut context)
498 })?;
499 runtime.application_root = Some(root);
500 runtime.owner_scopes.insert(root, root_scope);
501 runtime.lifecycle = self.application_lifecycle.get();
505 Ok(self.install_window(id, runtime, options, restoration, restoration_lease))
506 }
507
508 pub(crate) fn adopt_runtime(&self, mut runtime: Runtime) -> WindowId {
509 let registry = self
510 .registry
511 .upgrade()
512 .expect("application owns its registry");
513 let id = registry.borrow_mut().reserve();
514 runtime.window_id = Some(id);
515 runtime.window_manager = Some(self.clone());
516 runtime.window_scope.bind_window(id);
517 for scope in runtime.owner_scopes.values() {
518 scope.bind_window(id);
519 }
520 self.install_window(id, runtime, WindowOptions::default(), None, None)
521 .id()
522 }
523
524 fn install_window(
525 &self,
526 id: WindowId,
527 runtime: Runtime,
528 options: WindowOptions,
529 restoration: Option<RestorableWindowMetadata>,
530 restoration_lease: Option<restoration::ScopeLease>,
531 ) -> WindowHandle {
532 let registry = self
533 .registry
534 .upgrade()
535 .expect("application owns its registry");
536 let window_scope = runtime.window_scope.clone();
537 let metrics = initial_metrics(&options);
538 let capabilities = Arc::new(RwLock::new(
539 *self
540 .application_capabilities
541 .read()
542 .expect("application capability snapshot lock"),
543 ));
544 let observed_state = Arc::new(RwLock::new(WindowObservedState::default()));
545 let transient_presentations = Arc::new(RwLock::new(Vec::new()));
546 let requested_state = options
547 .requested_state()
548 .expect("WindowOptions were validated before requested-state construction");
549 registry.borrow_mut().insert(
550 id,
551 WindowRecord {
552 runtime,
553 scope: window_scope,
554 last_frame: FrameRecord::default(),
555 render_metrics: RenderFrameMetrics::default(),
556 gpu_sample: None,
557 lifecycle: if options.visible {
558 WindowLifecycle::Visible
559 } else {
560 WindowLifecycle::Hidden
561 },
562 metrics,
563 options: options.clone(),
564 native_focused: false,
565 requested_frames: 0,
566 presented_frames: 0,
567 skipped_frames: 0,
568 input_events: 0,
569 surface_generation: 1,
570 accessibility: AccessibilityDiagnostics::default(),
571 last_content_sensitivity: None,
572 content_sizing: ContentSizeCoordinator::default(),
573 restoration,
574 _restoration_scope_lease: restoration_lease,
575 capabilities: capabilities.clone(),
576 last_platform_error: None,
577 requested_state,
578 observed_state: observed_state.clone(),
579 transient_presentations: transient_presentations.clone(),
580 },
581 );
582 self.sync_restorable_windows(true);
583 self.native_commands
584 .borrow_mut()
585 .push_back(NativeWindowCommand::Create {
586 window_id: id,
587 options,
588 });
589 WindowHandle {
590 id,
591 bridge: self.bridge.clone(),
592 file_dialogs: FileDialogService::new(
593 id,
594 self.file_dialog_bridge.clone(),
595 capabilities.clone(),
596 ),
597 capabilities,
598 observed_state,
599 displays: self.displays.clone(),
600 transient_presentations,
601 }
602 }
603
604 pub(crate) fn handle(&self, id: WindowId) -> Option<WindowHandle> {
605 let registry = self.registry.upgrade()?;
606 let (capabilities, observed_state, transient_presentations) = {
607 let registry = registry.borrow();
608 let record = registry.get(id)?;
609 (
610 record.capabilities.clone(),
611 record.observed_state.clone(),
612 record.transient_presentations.clone(),
613 )
614 };
615 Some(WindowHandle {
616 id,
617 bridge: self.bridge.clone(),
618 file_dialogs: FileDialogService::new(
619 id,
620 self.file_dialog_bridge.clone(),
621 capabilities.clone(),
622 ),
623 capabilities,
624 observed_state,
625 displays: self.displays.clone(),
626 transient_presentations,
627 })
628 }
629
630 pub(crate) fn send_window_operation(
631 &self,
632 id: WindowId,
633 operation: incular_platform::WindowOperation,
634 ) -> bool {
635 self.bridge
636 .send(incular_platform::WindowCommand::new(id, operation))
637 .is_ok()
638 }
639
640 pub(crate) fn restoration_scope(
641 &self,
642 restoration: Option<&RestorableWindowMetadata>,
643 ) -> Option<RestorationScope> {
644 self.restoration.as_ref().map(|manager| {
645 let root = manager.scope();
646 restoration.map_or_else(
647 || manager.application_scope(),
648 |metadata| {
649 root.child_unchecked(RestorationKey::new("window").expect("static key"))
650 .child_unchecked(metadata.id.0.clone())
651 },
652 )
653 })
654 }
655
656 pub(crate) fn acquire_restoration_scope(
657 &self,
658 restoration: Option<&RestorableWindowMetadata>,
659 ) -> Result<Option<restoration::ScopeLease>, WindowError> {
660 let Some(metadata) = restoration else {
661 return Ok(None);
662 };
663 let Some(manager) = &self.restoration else {
664 return Ok(None);
665 };
666 let path = [
667 RestorationKey::new("window").expect("static key"),
668 metadata.id.0.clone(),
669 ];
670 manager
671 .acquire_scope(&path)
672 .map(Some)
673 .map_err(WindowError::Restoration)
674 }
675
676 pub(crate) fn apply_restored_window_options(
677 &self,
678 options: &mut WindowOptions,
679 restoration: Option<&RestorableWindowMetadata>,
680 ) {
681 let Some(metadata) = restoration else {
682 return;
683 };
684 let Some(manager) = &self.restoration else {
685 return;
686 };
687 let Some(saved) = manager
688 .windows()
689 .into_iter()
690 .find(|saved| saved.restoration_id == metadata.id.as_key().as_str())
691 else {
692 return;
693 };
694 if saved.kind != metadata.kind {
695 return;
696 }
697 let saved_size = incular_core::Size::new(saved.logical_width, saved.logical_height);
698 if saved_size.width.is_finite()
699 && saved_size.height.is_finite()
700 && saved_size.width > 0.0
701 && saved_size.height > 0.0
702 {
703 let minimum = options
704 .minimum_logical_size
705 .unwrap_or(incular_core::Size::ZERO);
706 let maximum = options.maximum_logical_size;
707 options.initial_logical_size = incular_core::Size::new(
708 saved_size
709 .width
710 .clamp(minimum.width, maximum.map_or(f32::MAX, |size| size.width)),
711 saved_size
712 .height
713 .clamp(minimum.height, maximum.map_or(f32::MAX, |size| size.height)),
714 );
715 }
716 options.maximized = saved.maximized;
717 options.fullscreen = saved
718 .fullscreen
719 .then_some(incular_platform::Fullscreen::Borderless);
720 manager.note_restored_window();
721 }
722
723 pub(crate) fn sync_restorable_windows(&self, retain_unopened: bool) {
724 let Some(manager) = &self.restoration else {
725 return;
726 };
727 let Some(registry) = self.registry.upgrade() else {
728 return;
729 };
730 let active_windows = registry
731 .borrow()
732 .slots
733 .iter()
734 .filter_map(|slot| slot.record.as_ref())
735 .filter_map(|record| {
736 let metadata = record.restoration.as_ref()?;
737 let logical = if record.options.size_policy == WindowSizePolicy::Content {
743 record.options.initial_logical_size
744 } else {
745 record.metrics.logical_size()
746 };
747 Some(restoration::RestoredWindow {
748 restoration_id: metadata.id.as_key().as_str().to_owned(),
749 kind: metadata.kind.clone(),
750 logical_width: logical.width,
751 logical_height: logical.height,
752 maximized: record.options.maximized,
753 fullscreen: record.options.fullscreen.is_some(),
754 })
755 })
756 .collect::<Vec<_>>();
757 let windows = if retain_unopened {
758 let active_ids = active_windows
759 .iter()
760 .map(|window| window.restoration_id.clone())
761 .collect::<HashSet<_>>();
762 manager
763 .windows()
764 .into_iter()
765 .filter(|window| !active_ids.contains(&window.restoration_id))
766 .chain(active_windows)
767 .collect()
768 } else {
769 active_windows
770 };
771 manager.replace_windows(windows);
772 }
773}
774
775pub(crate) fn initial_metrics(options: &WindowOptions) -> WindowMetrics {
776 WindowMetrics::new(
777 incular_platform::PhysicalSize::new(
778 options.initial_logical_size.width.ceil() as u32,
779 options.initial_logical_size.height.ceil() as u32,
780 ),
781 incular_config::ApplicationDefaults::DEFAULT.scale_factor,
782 )
783}