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