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