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