Skip to main content

repose_platform/
window_v2.rs

1//! Android/Web: `collect_screens` returns primary; `apply` is no-op like compose.
2use repose_core::{Rect, Size};
3use repose_ui::window_v2::{
4    Screen, ScreenInsets, WindowBoundsProvider, WindowGeometryProviderScope, WindowMetrics,
5    WindowPlacement, WindowScreenProviderScope, WindowState,
6};
7
8pub use repose_ui::window_v2::{
9    DialogState, WindowConstraints, WindowPositionProvider, WindowScreenProvider,
10    WindowSizeProvider,
11};
12
13#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
14pub fn collect_screens(
15    event_loop: &winit::event_loop::ActiveEventLoop,
16    fallback_bounds: Rect,
17) -> Vec<Screen> {
18    let mut screens = Vec::new();
19    for monitor in event_loop.available_monitors() {
20        let pos = monitor.position();
21        let size = monitor.size();
22        let scale = monitor.scale_factor() as f32;
23        let bounds = Rect {
24            x: pos.x as f32 / scale,
25            y: pos.y as f32 / scale,
26            w: size.width as f32 / scale,
27            h: size.height as f32 / scale,
28        };
29        let id = monitor
30            .name()
31            .unwrap_or_else(|| format!("monitor-{}", screens.len()));
32        screens.push(Screen::new(id, bounds, ScreenInsets::default()));
33    }
34    if screens.is_empty() {
35        screens.push(Screen::primary(fallback_bounds));
36    }
37    screens
38}
39#[cfg(any(target_os = "android", target_arch = "wasm32"))]
40pub fn collect_screens(_: (), fallback_bounds: Rect) -> Vec<Screen> {
41    vec![Screen::primary(fallback_bounds)]
42}
43
44#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
45pub fn apply_window_state_to_winit(
46    state: &mut WindowState,
47    window: &winit::window::Window,
48    event_loop: Option<&winit::event_loop::ActiveEventLoop>,
49) {
50    let scale = window.scale_factor() as f32;
51    let inner = window.inner_size();
52    let outer_pos = window
53        .outer_position()
54        .map(|p| {
55            winit::dpi::LogicalPosition::new(p.x as f64 / scale as f64, p.y as f64 / scale as f64)
56        })
57        .unwrap_or(winit::dpi::LogicalPosition::new(0.0, 0.0));
58    let current_bounds = Rect {
59        x: outer_pos.x as f32,
60        y: outer_pos.y as f32,
61        w: inner.width as f32 / scale,
62        h: inner.height as f32 / scale,
63    };
64    let screens = if let Some(el) = event_loop {
65        collect_screens(el, current_bounds)
66    } else {
67        vec![Screen::primary(current_bounds)]
68    };
69    let default = screens
70        .first()
71        .cloned()
72        .unwrap_or_else(|| Screen::primary(current_bounds));
73    let screen_scope = WindowScreenProviderScope::new(screens.clone(), default.clone());
74    let screen_for_metrics = screens
75        .iter()
76        .find(|s| state.try_screen_id().map(|id| id == s.id).unwrap_or(false))
77        .cloned()
78        .unwrap_or(default);
79    let metrics = WindowMetrics::new(screen_for_metrics, current_bounds, ScreenInsets::default());
80    let geometry_scope = WindowGeometryProviderScope::new(None, metrics, |_c| Size {
81        width: current_bounds.w,
82        height: current_bounds.h,
83    });
84    if let Some(p) = state.take_pending_screen() {
85        let target = p.get_screen(&screen_scope);
86        let avail = target.available_bounds();
87        let x = avail.x + (avail.w - current_bounds.w) / 2.0;
88        let y = avail.y + (avail.h - current_bounds.h) / 2.0;
89        window.set_outer_position(winit::dpi::LogicalPosition::new(x as f64, y as f64));
90    }
91    if let Some(p) = state.take_pending_placement() {
92        apply_placement_to_winit(window, p);
93        state.on_host_placement_changed(p);
94    }
95    if let Some(m) = state.take_pending_minimized() {
96        window.set_minimized(m);
97        state.on_host_minimized_changed(m);
98    }
99    let pending = state.drain_pending_bounds();
100    for provider in pending {
101        let rect = provider.get_bounds(&geometry_scope);
102        let phys_w = (rect.w * scale).round() as u32;
103        let phys_h = (rect.h * scale).round() as u32;
104        let phys_x = (rect.x * scale).round() as i32;
105        let phys_y = (rect.y * scale).round() as i32;
106        let _ =
107            window.request_inner_size(winit::dpi::PhysicalSize::new(phys_w.max(1), phys_h.max(1)));
108        window.set_outer_position(winit::dpi::PhysicalPosition::new(phys_x, phys_y));
109        state.on_host_bounds_changed(rect, geometry_scope.window_metrics.screen.id.clone());
110        if state.try_placement() != Some(WindowPlacement::Floating) {
111            state.on_host_placement_changed(WindowPlacement::Floating);
112        }
113    }
114    if !state.is_initialized {
115        state.is_initialized = true;
116    }
117}
118#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
119fn apply_placement_to_winit(window: &winit::window::Window, placement: WindowPlacement) {
120    match placement {
121        WindowPlacement::Floating => {
122            window.set_maximized(false);
123            window.set_fullscreen(None);
124        }
125        WindowPlacement::Maximized => {
126            window.set_fullscreen(None);
127            window.set_maximized(true);
128        }
129        WindowPlacement::Fullscreen => {
130            window.set_maximized(false);
131            if let Some(m) = window
132                .current_monitor()
133                .or_else(|| window.available_monitors().next())
134            {
135                window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(Some(m))));
136            }
137        }
138    }
139}
140#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
141pub fn sync_window_state_from_winit(
142    state: &mut WindowState,
143    window: &winit::window::Window,
144    new_inner_size: Option<winit::dpi::PhysicalSize<u32>>,
145    new_outer_position: Option<winit::dpi::PhysicalPosition<i32>>,
146) {
147    let scale = window.scale_factor() as f32;
148    let size = new_inner_size.unwrap_or_else(|| window.inner_size());
149    let pos = new_outer_position
150        .or_else(|| window.outer_position().ok())
151        .map(|p| {
152            winit::dpi::LogicalPosition::new(p.x as f64 / scale as f64, p.y as f64 / scale as f64)
153        })
154        .unwrap_or(winit::dpi::LogicalPosition::new(0.0, 0.0));
155    let bounds = Rect {
156        x: pos.x as f32,
157        y: pos.y as f32,
158        w: size.width as f32 / scale,
159        h: size.height as f32 / scale,
160    };
161    let screen_id = window
162        .current_monitor()
163        .and_then(|m| m.name())
164        .unwrap_or_else(|| "primary".into());
165    state.on_host_bounds_changed(bounds, screen_id);
166}
167pub fn resolve_initial_bounds(
168    provider: &WindowBoundsProvider,
169    screen: Screen,
170    current_bounds: Rect,
171    measure_content: impl Fn(WindowConstraints) -> Size,
172) -> Rect {
173    let metrics = WindowMetrics::new(screen, current_bounds, ScreenInsets::default());
174    let scope = WindowGeometryProviderScope::new(None, metrics, measure_content);
175    provider.get_bounds(&scope)
176}