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