window.rs

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