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