1use crate::{
2 AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels,
3 DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
4 PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
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<DevicePixels>,
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.map(|pixels| (pixels.0 as i32).into());
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<DevicePixels> {
112 self.0.lock().bounds
113 }
114
115 fn is_maximized(&self) -> bool {
116 false
117 }
118
119 fn is_minimized(&self) -> bool {
120 false
121 }
122
123 fn content_size(&self) -> Size<Pixels> {
124 self.bounds().size.into()
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) -> std::rc::Rc<dyn crate::PlatformDisplay> {
136 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 as_any_mut(&mut self) -> &mut dyn std::any::Any {
148 self
149 }
150
151 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
152 self.0.lock().input_handler = Some(input_handler);
153 }
154
155 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
156 self.0.lock().input_handler.take()
157 }
158
159 fn prompt(
160 &self,
161 _level: crate::PromptLevel,
162 _msg: &str,
163 _detail: Option<&str>,
164 _answers: &[&str],
165 ) -> Option<futures::channel::oneshot::Receiver<usize>> {
166 Some(
167 self.0
168 .lock()
169 .platform
170 .upgrade()
171 .expect("platform dropped")
172 .prompt(),
173 )
174 }
175
176 fn activate(&self) {
177 self.0
178 .lock()
179 .platform
180 .upgrade()
181 .unwrap()
182 .set_active_window(Some(self.clone()))
183 }
184
185 fn is_active(&self) -> bool {
186 false
187 }
188
189 fn set_title(&mut self, title: &str) {
190 self.0.lock().title = Some(title.to_owned());
191 }
192
193 fn set_edited(&mut self, edited: bool) {
194 self.0.lock().edited = edited;
195 }
196
197 fn show_character_palette(&self) {
198 unimplemented!()
199 }
200
201 fn minimize(&self) {
202 unimplemented!()
203 }
204
205 fn zoom(&self) {
206 unimplemented!()
207 }
208
209 fn toggle_fullscreen(&self) {
210 let mut lock = self.0.lock();
211 lock.is_fullscreen = !lock.is_fullscreen;
212 }
213
214 fn is_fullscreen(&self) -> bool {
215 self.0.lock().is_fullscreen
216 }
217
218 fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
219
220 fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>) {
221 self.0.lock().input_callback = Some(callback)
222 }
223
224 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
225 self.0.lock().active_status_change_callback = Some(callback)
226 }
227
228 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
229 self.0.lock().resize_callback = Some(callback)
230 }
231
232 fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
233 unimplemented!()
234 }
235
236 fn on_moved(&self, callback: Box<dyn FnMut()>) {
237 self.0.lock().moved_callback = Some(callback)
238 }
239
240 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
241 self.0.lock().should_close_handler = Some(callback);
242 }
243
244 fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
245
246 fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
247
248 fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
249 unimplemented!()
250 }
251
252 fn draw(&self, _scene: &crate::Scene) {}
253
254 fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
255 self.0.lock().sprite_atlas.clone()
256 }
257
258 fn as_test(&mut self) -> Option<&mut TestWindow> {
259 Some(self)
260 }
261
262 #[cfg(target_os = "windows")]
263 fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND {
264 unimplemented!()
265 }
266}
267
268pub(crate) struct TestAtlasState {
269 next_id: u32,
270 tiles: HashMap<AtlasKey, AtlasTile>,
271}
272
273pub(crate) struct TestAtlas(Mutex<TestAtlasState>);
274
275impl TestAtlas {
276 pub fn new() -> Self {
277 TestAtlas(Mutex::new(TestAtlasState {
278 next_id: 0,
279 tiles: HashMap::default(),
280 }))
281 }
282}
283
284impl PlatformAtlas for TestAtlas {
285 fn get_or_insert_with<'a>(
286 &self,
287 key: &crate::AtlasKey,
288 build: &mut dyn FnMut() -> anyhow::Result<(
289 Size<crate::DevicePixels>,
290 std::borrow::Cow<'a, [u8]>,
291 )>,
292 ) -> anyhow::Result<crate::AtlasTile> {
293 let mut state = self.0.lock();
294 if let Some(tile) = state.tiles.get(key) {
295 return Ok(tile.clone());
296 }
297
298 state.next_id += 1;
299 let texture_id = state.next_id;
300 state.next_id += 1;
301 let tile_id = state.next_id;
302
303 drop(state);
304 let (size, _) = build()?;
305 let mut state = self.0.lock();
306
307 state.tiles.insert(
308 key.clone(),
309 crate::AtlasTile {
310 texture_id: AtlasTextureId {
311 index: texture_id,
312 kind: crate::AtlasTextureKind::Path,
313 },
314 tile_id: TileId(tile_id),
315 padding: 0,
316 bounds: crate::Bounds {
317 origin: Point::default(),
318 size,
319 },
320 },
321 );
322
323 Ok(state.tiles[key].clone())
324 }
325}