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