window.rs

  1use crate::{
  2    px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, InputEvent, KeyDownEvent,
  3    Keystroke, Pixels, Platform, PlatformAtlas, PlatformDisplay, PlatformInputHandler,
  4    PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance, WindowBounds,
  5    WindowOptions,
  6};
  7use collections::HashMap;
  8use parking_lot::Mutex;
  9use std::{
 10    rc::{Rc, Weak},
 11    sync::{self, Arc},
 12};
 13
 14pub struct TestWindowState {
 15    pub(crate) bounds: WindowBounds,
 16    pub(crate) handle: AnyWindowHandle,
 17    display: Rc<dyn PlatformDisplay>,
 18    pub(crate) title: Option<String>,
 19    pub(crate) edited: bool,
 20    platform: Weak<TestPlatform>,
 21    sprite_atlas: Arc<dyn PlatformAtlas>,
 22
 23    input_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
 24    active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
 25    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 26    moved_callback: Option<Box<dyn FnMut()>>,
 27    input_handler: Option<Box<dyn PlatformInputHandler>>,
 28}
 29
 30#[derive(Clone)]
 31pub struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
 32
 33impl TestWindow {
 34    pub fn new(
 35        options: WindowOptions,
 36        handle: AnyWindowHandle,
 37        platform: Weak<TestPlatform>,
 38        display: Rc<dyn PlatformDisplay>,
 39    ) -> Self {
 40        Self(Arc::new(Mutex::new(TestWindowState {
 41            bounds: options.bounds,
 42            display,
 43            platform,
 44            handle,
 45            sprite_atlas: Arc::new(TestAtlas::new()),
 46            title: Default::default(),
 47            edited: false,
 48
 49            input_callback: None,
 50            active_status_change_callback: None,
 51            resize_callback: None,
 52            moved_callback: None,
 53            input_handler: None,
 54        })))
 55    }
 56
 57    pub fn simulate_resize(&mut self, size: Size<Pixels>) {
 58        let scale_factor = self.scale_factor();
 59        let mut lock = self.0.lock();
 60        let Some(mut callback) = lock.resize_callback.take() else {
 61            return;
 62        };
 63        match &mut lock.bounds {
 64            WindowBounds::Fullscreen | WindowBounds::Maximized => {
 65                lock.bounds = WindowBounds::Fixed(Bounds {
 66                    origin: Point::default(),
 67                    size: size.map(|pixels| f64::from(pixels).into()),
 68                });
 69            }
 70            WindowBounds::Fixed(bounds) => {
 71                bounds.size = size.map(|pixels| f64::from(pixels).into());
 72            }
 73        }
 74        drop(lock);
 75        callback(size, scale_factor);
 76        self.0.lock().resize_callback = Some(callback);
 77    }
 78
 79    pub fn simulate_active_status_change(&self, active: bool) {
 80        let mut lock = self.0.lock();
 81        let Some(mut callback) = lock.active_status_change_callback.take() else {
 82            return;
 83        };
 84        drop(lock);
 85        callback(active);
 86        self.0.lock().active_status_change_callback = Some(callback);
 87    }
 88
 89    pub fn simulate_input(&mut self, event: InputEvent) -> bool {
 90        let mut lock = self.0.lock();
 91        let Some(mut callback) = lock.input_callback.take() else {
 92            return false;
 93        };
 94        drop(lock);
 95        let result = callback(event);
 96        self.0.lock().input_callback = Some(callback);
 97        result
 98    }
 99
100    pub fn simulate_keystroke(&mut self, keystroke: Keystroke, is_held: bool) {
101        if self.simulate_input(InputEvent::KeyDown(KeyDownEvent {
102            keystroke: keystroke.clone(),
103            is_held,
104        })) {
105            return;
106        }
107
108        let mut lock = self.0.lock();
109        let Some(mut input_handler) = lock.input_handler.take() else {
110            panic!(
111                "simulate_keystroke {:?} input event was not handled and there was no active input",
112                &keystroke
113            );
114        };
115        drop(lock);
116        let text = keystroke.ime_key.unwrap_or(keystroke.key);
117        input_handler.replace_text_in_range(None, &text);
118
119        self.0.lock().input_handler = Some(input_handler);
120    }
121}
122
123impl PlatformWindow for TestWindow {
124    fn bounds(&self) -> WindowBounds {
125        self.0.lock().bounds
126    }
127
128    fn content_size(&self) -> Size<Pixels> {
129        let bounds = match self.bounds() {
130            WindowBounds::Fixed(bounds) => bounds,
131            WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
132        };
133        bounds.size.map(|p| px(p.0))
134    }
135
136    fn scale_factor(&self) -> f32 {
137        2.0
138    }
139
140    fn titlebar_height(&self) -> Pixels {
141        unimplemented!()
142    }
143
144    fn appearance(&self) -> WindowAppearance {
145        unimplemented!()
146    }
147
148    fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
149        self.0.lock().display.clone()
150    }
151
152    fn mouse_position(&self) -> Point<Pixels> {
153        Point::default()
154    }
155
156    fn modifiers(&self) -> crate::Modifiers {
157        crate::Modifiers::default()
158    }
159
160    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
161        self
162    }
163
164    fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
165        self.0.lock().input_handler = Some(input_handler);
166    }
167
168    fn clear_input_handler(&mut self) {
169        self.0.lock().input_handler = None;
170    }
171
172    fn prompt(
173        &self,
174        _level: crate::PromptLevel,
175        _msg: &str,
176        _answers: &[&str],
177    ) -> futures::channel::oneshot::Receiver<usize> {
178        self.0
179            .lock()
180            .platform
181            .upgrade()
182            .expect("platform dropped")
183            .prompt()
184    }
185
186    fn activate(&self) {
187        let this = self.clone();
188        let executor = self
189            .0
190            .lock()
191            .platform
192            .upgrade()
193            .unwrap()
194            .foreground_executor()
195            .clone();
196
197        executor
198            .spawn(async move {
199                let state = this.0.lock();
200                let platform = state.platform.upgrade().unwrap();
201                let previous_window = platform.active_window.borrow_mut().replace(this.clone());
202                drop(state);
203                drop(platform);
204                if let Some(previous_window) = previous_window {
205                    if Arc::ptr_eq(&previous_window.0, &this.0) {
206                        return;
207                    }
208                    previous_window.simulate_active_status_change(false);
209                }
210
211                this.simulate_active_status_change(true);
212            })
213            .detach();
214    }
215
216    fn set_title(&mut self, title: &str) {
217        self.0.lock().title = Some(title.to_owned());
218    }
219
220    fn set_edited(&mut self, edited: bool) {
221        self.0.lock().edited = edited;
222    }
223
224    fn show_character_palette(&self) {
225        unimplemented!()
226    }
227
228    fn minimize(&self) {
229        unimplemented!()
230    }
231
232    fn zoom(&self) {
233        unimplemented!()
234    }
235
236    fn toggle_full_screen(&self) {
237        unimplemented!()
238    }
239
240    fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
241        self.0.lock().input_callback = Some(callback)
242    }
243
244    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
245        self.0.lock().active_status_change_callback = Some(callback)
246    }
247
248    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
249        self.0.lock().resize_callback = Some(callback)
250    }
251
252    fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
253        unimplemented!()
254    }
255
256    fn on_moved(&self, callback: Box<dyn FnMut()>) {
257        self.0.lock().moved_callback = Some(callback)
258    }
259
260    fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {
261        unimplemented!()
262    }
263
264    fn on_close(&self, _callback: Box<dyn FnOnce()>) {
265        unimplemented!()
266    }
267
268    fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {
269        unimplemented!()
270    }
271
272    fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
273        unimplemented!()
274    }
275
276    fn invalidate(&self) {
277        // (self.draw.lock())().unwrap();
278    }
279
280    fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
281        self.0.lock().sprite_atlas.clone()
282    }
283
284    fn as_test(&mut self) -> Option<&mut TestWindow> {
285        Some(self)
286    }
287}
288
289pub struct TestAtlasState {
290    next_id: u32,
291    tiles: HashMap<AtlasKey, AtlasTile>,
292}
293
294pub 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            Size<crate::DevicePixels>,
311            std::borrow::Cow<'a, [u8]>,
312        )>,
313    ) -> anyhow::Result<crate::AtlasTile> {
314        let mut state = self.0.lock();
315        if let Some(tile) = state.tiles.get(key) {
316            return Ok(tile.clone());
317        }
318
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        drop(state);
325        let (size, _) = build()?;
326        let mut state = self.0.lock();
327
328        state.tiles.insert(
329            key.clone(),
330            crate::AtlasTile {
331                texture_id: AtlasTextureId {
332                    index: texture_id,
333                    kind: crate::AtlasTextureKind::Path,
334                },
335                tile_id: TileId(tile_id),
336                bounds: crate::Bounds {
337                    origin: Point::default(),
338                    size,
339                },
340            },
341        );
342
343        Ok(state.tiles[key].clone())
344    }
345
346    fn clear(&self) {
347        let mut state = self.0.lock();
348        state.tiles = HashMap::default();
349        state.next_id = 0;
350    }
351}