Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/src/platform/headless/windowing.rs
1use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::mpsc};
2 
3use anyhow::Result;
4 
5use crate::{
6 geometry::rect::RectF,
7 geometry::vector::{vec2f, Vector2F},
8 platform::{self, WindowOptions},
9 windowing::WindowCallbacks,
10 WindowId,
11};
12 
13use super::event_loop::AppEvent;
14 
15pub struct WindowManager {
16 windows: HashMap<WindowId, Rc<Window>>,
17 active_window: RefCell<Option<WindowId>>,
18 event_sender: mpsc::Sender<AppEvent>,
19}
20 
21impl WindowManager {
22 pub(super) fn new(event_sender: mpsc::Sender<AppEvent>) -> Self {
23 Self {
24 windows: HashMap::new(),
25 active_window: RefCell::new(None),
26 event_sender,
27 }
28 }
29 
30 fn set_active_window(&self, window_id: Option<WindowId>) {
31 *self.active_window.borrow_mut() = window_id;
32 
33 if self
34 .event_sender
35 .send(AppEvent::ActiveWindowChanged(window_id))
36 .is_err()
37 {
38 log::warn!(
39 "Tried to send ActiveWindowChanged event, but event loop is no longer running"
40 );
41 }
42 }
43}
44 
45impl strato_ui_core::platform::WindowManager for WindowManager {
46 fn open_window(
47 &mut self,
48 window_id: WindowId,
49 window_options: WindowOptions,
50 callbacks: WindowCallbacks,
51 ) -> Result<()> {
52 let window = Rc::new(Window::new(window_options, callbacks));
53 self.windows.insert(window_id, window);
54 self.set_active_window(Some(window_id));
55 Ok(())
56 }
57 
58 fn platform_window(&self, window_id: WindowId) -> strato_ui_core::OptionalPlatformWindow {
59 self.windows
60 .get(&window_id)
61 .map(Rc::clone)
62 .map(|inner| inner as Rc<dyn crate::platform::Window>)
63 }
64 
65 fn remove_window(&mut self, window_id: WindowId) {
66 self.windows.remove(&window_id);
67 if *self.active_window.borrow() == Some(window_id) {
68 self.set_active_window(None);
69 }
70 }
71 
72 fn active_window_id(&self) -> Option<WindowId> {
73 *self.active_window.borrow()
74 }
75 
76 fn key_window_is_modal_panel(&self) -> bool {
77 false
78 }
79 
80 fn app_is_active(&self) -> bool {
81 true
82 }
83 
84 fn activate_app(&self, last_active_window: Option<WindowId>) -> Option<WindowId> {
85 self.set_active_window(last_active_window);
86 last_active_window
87 }
88 
89 fn show_window_and_focus_app(
90 &self,
91 window_id: WindowId,
92 _behavior: platform::WindowFocusBehavior,
93 ) {
94 self.set_active_window(Some(window_id));
95 }
96 
97 fn hide_app(&self) {
98 // No-op.
99 }
100 
101 fn hide_window(&self, window_id: WindowId) {
102 // If hiding the active window, clear focus.
103 if *self.active_window.borrow() == Some(window_id) {
104 self.set_active_window(None);
105 }
106 }
107 
108 fn set_window_bounds(&self, window_id: WindowId, bound: RectF) {
109 if let Some(window) = self.windows.get(&window_id) {
110 window.set_bounds(bound);
111 }
112 }
113 
114 fn set_all_windows_background_blur_radius(&self, _blur_radius_pixels: u8) {
115 // No-op for headless.
116 }
117 
118 fn set_all_windows_background_blur_texture(&self, _use_blur_texture: bool) {
119 // No-op for headless.
120 }
121 
122 fn set_window_title(&self, _window_id: WindowId, _title: &str) {
123 // No-op for headless.
124 }
125 
126 fn close_window_async(
127 &self,
128 window_id: WindowId,
129 _termination_mode: platform::TerminationMode,
130 ) {
131 // In headless mode, always force-close the window since there's no confirmation dialog.
132 if self
133 .event_sender
134 .send(AppEvent::CloseWindow(window_id))
135 .is_err()
136 {
137 log::warn!("Tried to send event, but event loop is no longer running");
138 }
139 }
140 
141 fn active_display_bounds(&self) -> RectF {
142 // A single default display.
143 Default::default()
144 }
145 
146 fn active_display_id(&self) -> crate::DisplayId {
147 crate::DisplayId::from(0)
148 }
149 
150 fn display_count(&self) -> usize {
151 1
152 }
153 
154 fn bounds_for_display_idx(&self, _idx: crate::DisplayIdx) -> Option<RectF> {
155 Default::default()
156 }
157 
158 fn active_cursor_position_updated(&self) {
159 // No-op.
160 }
161 
162 fn windowing_system(&self) -> Option<crate::windowing::System> {
163 None
164 }
165 
166 fn os_window_manager_name(&self) -> Option<String> {
167 None
168 }
169 
170 fn is_tiling_window_manager(&self) -> bool {
171 false
172 }
173}
174 
175pub struct Window {
176 callbacks: WindowCallbacks,
177 bounds: RefCell<RectF>,
178 fullscreen_state: RefCell<platform::FullscreenState>,
179}
180 
181impl Window {
182 fn new(options: WindowOptions, callbacks: WindowCallbacks) -> Self {
183 let bounds = match options.bounds {
184 platform::WindowBounds::Default => RectF::new(vec2f(0.0, 0.0), vec2f(1024.0, 768.0)),
185 platform::WindowBounds::ExactSize(size) => RectF::new(vec2f(0.0, 0.0), size),
186 platform::WindowBounds::ExactPosition(rect) => rect,
187 };
188 Self {
189 callbacks,
190 bounds: RefCell::new(bounds),
191 fullscreen_state: RefCell::new(options.fullscreen_state),
192 }
193 }
194 
195 fn set_bounds(&self, rect: RectF) {
196 *self.bounds.borrow_mut() = rect;
197 }
198}
199 
200impl platform::Window for Window {
201 fn minimize(&self) {}
202 
203 fn toggle_maximized(&self) {}
204 
205 fn toggle_fullscreen(&self) {}
206 
207 fn fullscreen_state(&self) -> platform::FullscreenState {
208 *self.fullscreen_state.borrow()
209 }
210 
211 fn set_titlebar_height(&self, _height: f64) {}
212 
213 fn supports_transparency(&self) -> bool {
214 false
215 }
216 
217 fn graphics_backend(&self) -> platform::GraphicsBackend {
218 platform::GraphicsBackend::Empty
219 }
220 
221 fn supported_backends(&self) -> Vec<platform::GraphicsBackend> {
222 vec![]
223 }
224 
225 fn uses_native_window_decorations(&self) -> bool {
226 false
227 }
228 
229 fn as_ctx(&self) -> &dyn platform::WindowContext {
230 self
231 }
232 
233 fn callbacks(&self) -> &crate::windowing::WindowCallbacks {
234 &self.callbacks
235 }
236 
237 fn as_any(&self) -> &dyn std::any::Any {
238 self
239 }
240}
241 
242impl platform::WindowContext for Window {
243 fn size(&self) -> Vector2F {
244 self.bounds.borrow().size()
245 }
246 
247 fn origin(&self) -> Vector2F {
248 self.bounds.borrow().origin()
249 }
250 
251 fn backing_scale_factor(&self) -> f32 {
252 1.0
253 }
254 
255 fn max_texture_dimension_2d(&self) -> Option<u32> {
256 Some(2048)
257 }
258 
259 fn render_scene(&self, _scene: Rc<crate::Scene>) {}
260 
261 fn request_redraw(&self) {}
262 
263 fn request_frame_capture(
264 &self,
265 _callback: Box<dyn FnOnce(platform::CapturedFrame) + Send + 'static>,
266 ) {
267 // no-op for headless
268 }
269}
270