window.rs

  1use crate::{
  2    AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, Pixels,
  3    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
  4    Size, TestPlatform, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
  5    WindowParams,
  6};
  7use collections::HashMap;
  8use parking_lot::Mutex;
  9use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
 10use std::{
 11    rc::{Rc, Weak},
 12    sync::{self, Arc},
 13};
 14
 15pub(crate) struct TestWindowState {
 16    pub(crate) bounds: Bounds<Pixels>,
 17    pub(crate) handle: AnyWindowHandle,
 18    display: Rc<dyn PlatformDisplay>,
 19    pub(crate) title: Option<String>,
 20    pub(crate) edited: bool,
 21    platform: Weak<TestPlatform>,
 22    sprite_atlas: Arc<dyn PlatformAtlas>,
 23    pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
 24    input_callback: Option<Box<dyn FnMut(PlatformInput) -> DispatchEventResult>>,
 25    active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
 26    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 27    moved_callback: Option<Box<dyn FnMut()>>,
 28    input_handler: Option<PlatformInputHandler>,
 29    is_fullscreen: bool,
 30}
 31
 32#[derive(Clone)]
 33pub(crate) struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
 34
 35impl HasWindowHandle for TestWindow {
 36    fn window_handle(
 37        &self,
 38    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
 39        unimplemented!("Test Windows are not backed by a real platform window")
 40    }
 41}
 42
 43impl HasDisplayHandle for TestWindow {
 44    fn display_handle(
 45        &self,
 46    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
 47        unimplemented!("Test Windows are not backed by a real platform window")
 48    }
 49}
 50
 51impl TestWindow {
 52    pub fn new(
 53        handle: AnyWindowHandle,
 54        params: WindowParams,
 55        platform: Weak<TestPlatform>,
 56        display: Rc<dyn PlatformDisplay>,
 57    ) -> Self {
 58        Self(Arc::new(Mutex::new(TestWindowState {
 59            bounds: params.bounds,
 60            display,
 61            platform,
 62            handle,
 63            sprite_atlas: Arc::new(TestAtlas::new()),
 64            title: Default::default(),
 65            edited: false,
 66            should_close_handler: None,
 67            input_callback: None,
 68            active_status_change_callback: None,
 69            resize_callback: None,
 70            moved_callback: None,
 71            input_handler: None,
 72            is_fullscreen: false,
 73        })))
 74    }
 75
 76    pub fn simulate_resize(&mut self, size: Size<Pixels>) {
 77        let scale_factor = self.scale_factor();
 78        let mut lock = self.0.lock();
 79        let Some(mut callback) = lock.resize_callback.take() else {
 80            return;
 81        };
 82        lock.bounds.size = size;
 83        drop(lock);
 84        callback(size, scale_factor);
 85        self.0.lock().resize_callback = Some(callback);
 86    }
 87
 88    pub(crate) fn simulate_active_status_change(&self, active: bool) {
 89        let mut lock = self.0.lock();
 90        let Some(mut callback) = lock.active_status_change_callback.take() else {
 91            return;
 92        };
 93        drop(lock);
 94        callback(active);
 95        self.0.lock().active_status_change_callback = Some(callback);
 96    }
 97
 98    pub fn simulate_input(&mut self, event: PlatformInput) -> bool {
 99        let mut lock = self.0.lock();
100        let Some(mut callback) = lock.input_callback.take() else {
101            return false;
102        };
103        drop(lock);
104        let result = callback(event);
105        self.0.lock().input_callback = Some(callback);
106        !result.propagate
107    }
108}
109
110impl PlatformWindow for TestWindow {
111    fn bounds(&self) -> Bounds<Pixels> {
112        self.0.lock().bounds
113    }
114
115    fn window_bounds(&self) -> WindowBounds {
116        WindowBounds::Windowed(self.bounds())
117    }
118
119    fn is_maximized(&self) -> bool {
120        false
121    }
122
123    fn content_size(&self) -> Size<Pixels> {
124        self.bounds().size
125    }
126
127    fn scale_factor(&self) -> f32 {
128        2.0
129    }
130
131    fn appearance(&self) -> WindowAppearance {
132        WindowAppearance::Light
133    }
134
135    fn display(&self) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
136        Some(self.0.lock().display.clone())
137    }
138
139    fn mouse_position(&self) -> Point<Pixels> {
140        Point::default()
141    }
142
143    fn modifiers(&self) -> crate::Modifiers {
144        crate::Modifiers::default()
145    }
146
147    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
148        self.0.lock().input_handler = Some(input_handler);
149    }
150
151    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
152        self.0.lock().input_handler.take()
153    }
154
155    fn prompt(
156        &self,
157        _level: crate::PromptLevel,
158        msg: &str,
159        detail: Option<&str>,
160        _answers: &[&str],
161    ) -> Option<futures::channel::oneshot::Receiver<usize>> {
162        Some(
163            self.0
164                .lock()
165                .platform
166                .upgrade()
167                .expect("platform dropped")
168                .prompt(msg, detail),
169        )
170    }
171
172    fn activate(&self) {
173        self.0
174            .lock()
175            .platform
176            .upgrade()
177            .unwrap()
178            .set_active_window(Some(self.clone()))
179    }
180
181    fn is_active(&self) -> bool {
182        false
183    }
184
185    fn set_title(&mut self, title: &str) {
186        self.0.lock().title = Some(title.to_owned());
187    }
188
189    fn set_app_id(&mut self, _app_id: &str) {}
190
191    fn set_background_appearance(&mut self, _background: WindowBackgroundAppearance) {
192        unimplemented!()
193    }
194
195    fn set_edited(&mut self, edited: bool) {
196        self.0.lock().edited = edited;
197    }
198
199    fn show_character_palette(&self) {
200        unimplemented!()
201    }
202
203    fn minimize(&self) {
204        unimplemented!()
205    }
206
207    fn zoom(&self) {
208        unimplemented!()
209    }
210
211    fn toggle_fullscreen(&self) {
212        let mut lock = self.0.lock();
213        lock.is_fullscreen = !lock.is_fullscreen;
214    }
215
216    fn is_fullscreen(&self) -> bool {
217        self.0.lock().is_fullscreen
218    }
219
220    fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
221
222    fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>) {
223        self.0.lock().input_callback = Some(callback)
224    }
225
226    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
227        self.0.lock().active_status_change_callback = Some(callback)
228    }
229
230    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
231        self.0.lock().resize_callback = Some(callback)
232    }
233
234    fn on_moved(&self, callback: Box<dyn FnMut()>) {
235        self.0.lock().moved_callback = Some(callback)
236    }
237
238    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
239        self.0.lock().should_close_handler = Some(callback);
240    }
241
242    fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
243
244    fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
245
246    fn draw(&self, _scene: &crate::Scene) {}
247
248    fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
249        self.0.lock().sprite_atlas.clone()
250    }
251
252    fn as_test(&mut self) -> Option<&mut TestWindow> {
253        Some(self)
254    }
255
256    #[cfg(target_os = "windows")]
257    fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND {
258        unimplemented!()
259    }
260
261    fn show_window_menu(&self, _position: Point<Pixels>) {
262        unimplemented!()
263    }
264
265    fn start_system_move(&self) {
266        unimplemented!()
267    }
268
269    fn should_render_window_controls(&self) -> bool {
270        false
271    }
272}
273
274pub(crate) struct TestAtlasState {
275    next_id: u32,
276    tiles: HashMap<AtlasKey, AtlasTile>,
277}
278
279pub(crate) struct TestAtlas(Mutex<TestAtlasState>);
280
281impl TestAtlas {
282    pub fn new() -> Self {
283        TestAtlas(Mutex::new(TestAtlasState {
284            next_id: 0,
285            tiles: HashMap::default(),
286        }))
287    }
288}
289
290impl PlatformAtlas for TestAtlas {
291    fn get_or_insert_with<'a>(
292        &self,
293        key: &crate::AtlasKey,
294        build: &mut dyn FnMut() -> anyhow::Result<
295            Option<(Size<crate::DevicePixels>, std::borrow::Cow<'a, [u8]>)>,
296        >,
297    ) -> anyhow::Result<Option<crate::AtlasTile>> {
298        let mut state = self.0.lock();
299        if let Some(tile) = state.tiles.get(key) {
300            return Ok(Some(tile.clone()));
301        }
302        drop(state);
303
304        let Some((size, _)) = build()? else {
305            return Ok(None);
306        };
307
308        let mut state = self.0.lock();
309        state.next_id += 1;
310        let texture_id = state.next_id;
311        state.next_id += 1;
312        let tile_id = state.next_id;
313
314        state.tiles.insert(
315            key.clone(),
316            crate::AtlasTile {
317                texture_id: AtlasTextureId {
318                    index: texture_id,
319                    kind: crate::AtlasTextureKind::Path,
320                },
321                tile_id: TileId(tile_id),
322                padding: 0,
323                bounds: crate::Bounds {
324                    origin: Point::default(),
325                    size,
326                },
327            },
328        );
329
330        Ok(Some(state.tiles[key].clone()))
331    }
332}