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