1use crate::{
2 px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay,
3 PlatformInputHandler, PlatformWindow, Point, Scene, Size, TestPlatform, TileId,
4 WindowAppearance, WindowBounds, WindowOptions,
5};
6use collections::HashMap;
7use parking_lot::Mutex;
8use std::{
9 rc::{Rc, Weak},
10 sync::{self, Arc},
11};
12
13#[derive(Default)]
14pub(crate) struct TestWindowHandlers {
15 pub(crate) active_status_change: Vec<Box<dyn FnMut(bool)>>,
16 pub(crate) input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
17 pub(crate) moved: Vec<Box<dyn FnMut()>>,
18 pub(crate) resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
19}
20
21pub struct TestWindow {
22 pub(crate) bounds: WindowBounds,
23 current_scene: Mutex<Option<Scene>>,
24 display: Rc<dyn PlatformDisplay>,
25 pub(crate) window_title: Option<String>,
26 pub(crate) input_handler: Option<Arc<Mutex<Box<dyn PlatformInputHandler>>>>,
27 pub(crate) handlers: Arc<Mutex<TestWindowHandlers>>,
28 platform: Weak<TestPlatform>,
29 sprite_atlas: Arc<dyn PlatformAtlas>,
30}
31
32impl TestWindow {
33 pub fn new(
34 options: WindowOptions,
35 platform: Weak<TestPlatform>,
36 display: Rc<dyn PlatformDisplay>,
37 ) -> Self {
38 Self {
39 bounds: options.bounds,
40 current_scene: Default::default(),
41 display,
42 platform,
43 input_handler: None,
44 sprite_atlas: Arc::new(TestAtlas::new()),
45 handlers: Default::default(),
46 window_title: Default::default(),
47 }
48 }
49}
50
51impl PlatformWindow for TestWindow {
52 fn bounds(&self) -> WindowBounds {
53 self.bounds
54 }
55
56 fn content_size(&self) -> Size<Pixels> {
57 let bounds = match self.bounds {
58 WindowBounds::Fixed(bounds) => bounds,
59 WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
60 };
61 bounds.size.map(|p| px(p.0))
62 }
63
64 fn scale_factor(&self) -> f32 {
65 2.0
66 }
67
68 fn titlebar_height(&self) -> Pixels {
69 unimplemented!()
70 }
71
72 fn appearance(&self) -> WindowAppearance {
73 unimplemented!()
74 }
75
76 fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
77 self.display.clone()
78 }
79
80 fn mouse_position(&self) -> Point<Pixels> {
81 Point::default()
82 }
83
84 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
85 self
86 }
87
88 fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
89 self.input_handler = Some(Arc::new(Mutex::new(input_handler)));
90 }
91
92 fn clear_input_handler(&mut self) {
93 self.input_handler = None;
94 }
95
96 fn prompt(
97 &self,
98 _level: crate::PromptLevel,
99 _msg: &str,
100 _answers: &[&str],
101 ) -> futures::channel::oneshot::Receiver<usize> {
102 self.platform.upgrade().expect("platform dropped").prompt()
103 }
104
105 fn activate(&self) {
106 unimplemented!()
107 }
108
109 fn set_title(&mut self, title: &str) {
110 self.window_title = Some(title.to_owned());
111 }
112
113 fn set_edited(&mut self, _edited: bool) {
114 unimplemented!()
115 }
116
117 fn show_character_palette(&self) {
118 unimplemented!()
119 }
120
121 fn minimize(&self) {
122 unimplemented!()
123 }
124
125 fn zoom(&self) {
126 unimplemented!()
127 }
128
129 fn toggle_full_screen(&self) {
130 unimplemented!()
131 }
132
133 fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
134 self.handlers.lock().input.push(callback)
135 }
136
137 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
138 self.handlers.lock().active_status_change.push(callback)
139 }
140
141 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
142 self.handlers.lock().resize.push(callback)
143 }
144
145 fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
146 unimplemented!()
147 }
148
149 fn on_moved(&self, callback: Box<dyn FnMut()>) {
150 self.handlers.lock().moved.push(callback)
151 }
152
153 fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {
154 unimplemented!()
155 }
156
157 fn on_close(&self, _callback: Box<dyn FnOnce()>) {
158 unimplemented!()
159 }
160
161 fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {
162 unimplemented!()
163 }
164
165 fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
166 unimplemented!()
167 }
168
169 fn draw(&self, scene: crate::Scene) {
170 self.current_scene.lock().replace(scene);
171 }
172
173 fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
174 self.sprite_atlas.clone()
175 }
176
177 fn as_test(&mut self) -> Option<&mut TestWindow> {
178 Some(self)
179 }
180}
181
182pub struct TestAtlasState {
183 next_id: u32,
184 tiles: HashMap<AtlasKey, AtlasTile>,
185}
186
187pub struct TestAtlas(Mutex<TestAtlasState>);
188
189impl TestAtlas {
190 pub fn new() -> Self {
191 TestAtlas(Mutex::new(TestAtlasState {
192 next_id: 0,
193 tiles: HashMap::default(),
194 }))
195 }
196}
197
198impl PlatformAtlas for TestAtlas {
199 fn get_or_insert_with<'a>(
200 &self,
201 key: &crate::AtlasKey,
202 build: &mut dyn FnMut() -> anyhow::Result<(
203 Size<crate::DevicePixels>,
204 std::borrow::Cow<'a, [u8]>,
205 )>,
206 ) -> anyhow::Result<crate::AtlasTile> {
207 let mut state = self.0.lock();
208 if let Some(tile) = state.tiles.get(key) {
209 return Ok(tile.clone());
210 }
211
212 state.next_id += 1;
213 let texture_id = state.next_id;
214 state.next_id += 1;
215 let tile_id = state.next_id;
216
217 drop(state);
218 let (size, _) = build()?;
219 let mut state = self.0.lock();
220
221 state.tiles.insert(
222 key.clone(),
223 crate::AtlasTile {
224 texture_id: AtlasTextureId {
225 index: texture_id,
226 kind: crate::AtlasTextureKind::Path,
227 },
228 tile_id: TileId(tile_id),
229 bounds: crate::Bounds {
230 origin: Point::default(),
231 size,
232 },
233 },
234 );
235
236 Ok(state.tiles[key].clone())
237 }
238
239 fn clear(&self) {
240 let mut state = self.0.lock();
241 state.tiles = HashMap::default();
242 state.next_id = 0;
243 }
244}