window.rs

  1use crate::{
  2    AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, GpuSpecs,
  3    Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
  4    Point, PromptButton, RequestFrameOptions, ScaledPixels, Size, TestPlatform, TileId,
  5    WindowAppearance, 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 resize(&mut self, size: Size<Pixels>) {
130        let mut lock = self.0.lock();
131        lock.bounds.size = size;
132    }
133
134    fn scale_factor(&self) -> f32 {
135        2.0
136    }
137
138    fn appearance(&self) -> WindowAppearance {
139        WindowAppearance::Light
140    }
141
142    fn display(&self) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
143        Some(self.0.lock().display.clone())
144    }
145
146    fn mouse_position(&self) -> Point<Pixels> {
147        Point::default()
148    }
149
150    fn modifiers(&self) -> crate::Modifiers {
151        crate::Modifiers::default()
152    }
153
154    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
155        self.0.lock().input_handler = Some(input_handler);
156    }
157
158    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
159        self.0.lock().input_handler.take()
160    }
161
162    fn prompt(
163        &self,
164        _level: crate::PromptLevel,
165        msg: &str,
166        detail: Option<&str>,
167        answers: &[PromptButton],
168    ) -> Option<futures::channel::oneshot::Receiver<usize>> {
169        Some(
170            self.0
171                .lock()
172                .platform
173                .upgrade()
174                .expect("platform dropped")
175                .prompt(msg, detail, answers),
176        )
177    }
178
179    fn activate(&self) {
180        self.0
181            .lock()
182            .platform
183            .upgrade()
184            .unwrap()
185            .set_active_window(Some(self.clone()))
186    }
187
188    fn is_active(&self) -> bool {
189        false
190    }
191
192    fn is_hovered(&self) -> bool {
193        false
194    }
195
196    fn set_title(&mut self, title: &str) {
197        self.0.lock().title = Some(title.to_owned());
198    }
199
200    fn set_app_id(&mut self, _app_id: &str) {}
201
202    fn set_background_appearance(&self, _background: WindowBackgroundAppearance) {}
203
204    fn set_edited(&mut self, edited: bool) {
205        self.0.lock().edited = edited;
206    }
207
208    fn show_character_palette(&self) {
209        unimplemented!()
210    }
211
212    fn minimize(&self) {
213        unimplemented!()
214    }
215
216    fn zoom(&self) {
217        unimplemented!()
218    }
219
220    fn toggle_fullscreen(&self) {
221        let mut lock = self.0.lock();
222        lock.is_fullscreen = !lock.is_fullscreen;
223    }
224
225    fn is_fullscreen(&self) -> bool {
226        self.0.lock().is_fullscreen
227    }
228
229    fn on_request_frame(&self, _callback: Box<dyn FnMut(RequestFrameOptions)>) {}
230
231    fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>) {
232        self.0.lock().input_callback = Some(callback)
233    }
234
235    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
236        self.0.lock().active_status_change_callback = Some(callback)
237    }
238
239    fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>) {
240        self.0.lock().hover_status_change_callback = Some(callback)
241    }
242
243    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
244        self.0.lock().resize_callback = Some(callback)
245    }
246
247    fn on_moved(&self, callback: Box<dyn FnMut()>) {
248        self.0.lock().moved_callback = Some(callback)
249    }
250
251    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
252        self.0.lock().should_close_handler = Some(callback);
253    }
254
255    fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
256
257    fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
258
259    fn draw(&self, _scene: &crate::Scene) {}
260
261    fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
262        self.0.lock().sprite_atlas.clone()
263    }
264
265    fn as_test(&mut self) -> Option<&mut TestWindow> {
266        Some(self)
267    }
268
269    #[cfg(target_os = "windows")]
270    fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND {
271        unimplemented!()
272    }
273
274    fn show_window_menu(&self, _position: Point<Pixels>) {
275        unimplemented!()
276    }
277
278    fn start_window_move(&self) {
279        unimplemented!()
280    }
281
282    fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {}
283
284    fn gpu_specs(&self) -> Option<GpuSpecs> {
285        None
286    }
287}
288
289pub(crate) struct TestAtlasState {
290    next_id: u32,
291    tiles: HashMap<AtlasKey, AtlasTile>,
292}
293
294pub(crate) struct TestAtlas(Mutex<TestAtlasState>);
295
296impl TestAtlas {
297    pub fn new() -> Self {
298        TestAtlas(Mutex::new(TestAtlasState {
299            next_id: 0,
300            tiles: HashMap::default(),
301        }))
302    }
303}
304
305impl PlatformAtlas for TestAtlas {
306    fn get_or_insert_with<'a>(
307        &self,
308        key: &crate::AtlasKey,
309        build: &mut dyn FnMut() -> anyhow::Result<
310            Option<(Size<crate::DevicePixels>, std::borrow::Cow<'a, [u8]>)>,
311        >,
312    ) -> anyhow::Result<Option<crate::AtlasTile>> {
313        let mut state = self.0.lock();
314        if let Some(tile) = state.tiles.get(key) {
315            return Ok(Some(tile.clone()));
316        }
317        drop(state);
318
319        let Some((size, _)) = build()? else {
320            return Ok(None);
321        };
322
323        let mut state = self.0.lock();
324        state.next_id += 1;
325        let texture_id = state.next_id;
326        state.next_id += 1;
327        let tile_id = state.next_id;
328
329        state.tiles.insert(
330            key.clone(),
331            crate::AtlasTile {
332                texture_id: AtlasTextureId {
333                    index: texture_id,
334                    kind: crate::AtlasTextureKind::Path,
335                },
336                tile_id: TileId(tile_id),
337                padding: 0,
338                bounds: crate::Bounds {
339                    origin: Point::default(),
340                    size,
341                },
342            },
343        );
344
345        Ok(Some(state.tiles[key].clone()))
346    }
347
348    fn remove(&self, key: &AtlasKey) {
349        let mut state = self.0.lock();
350        state.tiles.remove(key);
351    }
352}