1use std::collections::VecDeque;
2
3use instant::Instant;
4
5use abstutil::{elapsed_seconds, Timer, TimerSink};
6use geom::{Percent, Polygon};
7
8use crate::{
9 svg, Canvas, CanvasSettings, Color, Drawable, Event, GeomBatch, GfxCtx, HorizontalAlignment,
10 Key, Line, Panel, PanelDims, Prerender, ScreenDims, Style, Text, UserInput, VerticalAlignment,
11 Widget,
12};
13
14#[derive(Clone, PartialEq, Debug)]
15pub enum UpdateType {
16 InputOnly,
17 Game,
18 Pan,
19 ScreenCaptureEverything {
20 dir: String,
21 zoom: f64,
22 dims: ScreenDims,
23 },
24}
25
26pub struct EventCtx<'a> {
27 pub(crate) fake_mouseover: bool,
28 pub input: UserInput,
29 pub canvas: &'a mut Canvas,
31 pub prerender: &'a Prerender,
32 pub(crate) style: &'a mut Style,
33 pub(crate) updates_requested: Vec<UpdateType>,
34 pub(crate) canvas_movement_called: bool,
35
36 pub(crate) focus_owned_by: Option<String>,
38 pub(crate) next_focus_owned_by: Option<String>,
41}
42
43impl<'a> EventCtx<'a> {
44 pub fn loading_screen<O, S: Into<String>, F: FnOnce(&mut EventCtx, &mut Timer) -> O>(
45 &mut self,
46 raw_timer_name: S,
47 f: F,
48 ) -> O {
49 let timer_name = raw_timer_name.into();
50 let mut timer = Timer::new_with_sink(
51 &timer_name,
52 Box::new(LoadingScreen::new(
53 self.prerender,
54 self.style.clone(),
55 self.canvas.get_window_dims(),
56 timer_name.clone(),
57 )),
58 );
59 f(self, &mut timer)
60 }
61
62 pub fn request_update(&mut self, update_type: UpdateType) {
63 self.updates_requested.push(update_type);
64 }
65
66 pub fn canvas_movement(&mut self) -> bool {
70 self.canvas_movement_called = true;
71 let prev = (self.canvas.cam_x, self.canvas.cam_y, self.canvas.cam_zoom);
72 self.updates_requested
73 .extend(self.canvas.handle_event(&mut self.input));
74 prev != (self.canvas.cam_x, self.canvas.cam_y, self.canvas.cam_zoom)
75 }
76
77 pub fn no_op_event<O, F: FnMut(&mut EventCtx) -> O>(
79 &mut self,
80 fake_mouseover: bool,
81 mut cb: F,
82 ) -> O {
83 let mut tmp = EventCtx {
84 fake_mouseover,
85 input: UserInput::new(Event::NoOp, self.canvas),
86 canvas: self.canvas,
87 prerender: self.prerender,
88 style: self.style,
89 updates_requested: vec![],
90 canvas_movement_called: false,
91 focus_owned_by: None,
92 next_focus_owned_by: None,
93 };
94 let result = cb(&mut tmp);
95 self.updates_requested.extend(tmp.updates_requested);
96 result
97 }
98
99 pub fn redo_mouseover(&self) -> bool {
100 self.fake_mouseover
101 || self.input.window_lost_cursor()
102 || (!self.canvas.is_dragging() && self.input.get_moved_mouse().is_some())
103 || self
104 .input
105 .get_mouse_scroll()
106 .map(|(_, dy)| dy != 0.0)
107 .unwrap_or(false)
108 }
109
110 pub fn normal_left_click(&mut self) -> bool {
111 if self.input.has_been_consumed() {
112 return false;
113 }
114 if !self.canvas.is_dragging() && self.input.left_mouse_button_released() {
115 self.input.consume_event();
116 return true;
117 }
118 false
119 }
120
121 pub fn is_key_down(&self, key: Key) -> bool {
122 self.canvas.keys_held.contains(&key)
123 }
124
125 pub fn default_line_height(&self) -> f64 {
127 *self.prerender.assets.default_line_height.borrow()
128 }
129
130 pub fn upload(&self, batch: GeomBatch) -> Drawable {
132 self.prerender.upload(batch)
133 }
134
135 pub(crate) fn cursor_clickable(&mut self) {
136 self.prerender
137 .inner
138 .set_cursor_icon(winit::window::CursorIcon::Hand);
139 }
140
141 pub(crate) fn cursor_grabbable(&mut self) {
142 self.prerender
143 .inner
144 .set_cursor_icon(winit::window::CursorIcon::Grab);
145 }
146
147 pub(crate) fn cursor_grabbing(&mut self) {
148 self.prerender
149 .inner
150 .set_cursor_icon(winit::window::CursorIcon::Grabbing);
151 }
152
153 pub fn style(&self) -> &Style {
154 self.style
155 }
156
157 pub fn set_style(&mut self, style: Style) {
158 *self.prerender.assets.style.borrow_mut() = style.clone();
159 self.prerender.assets.clear_text_cache();
160 *self.style = style;
161 }
162
163 pub fn make_loading_screen(&mut self, txt: Text) -> Panel {
164 let border = Color::hex("#F4DA22");
165 let (label, bytes) = crate::include_labeled_bytes!("../icons/loading.svg");
166 Panel::new_builder(Widget::row(vec![
167 Widget::custom_col(vec![
168 svg::load_svg_bytes(self.prerender, label, bytes)
169 .unwrap()
170 .0
171 .scale(5.0)
172 .into_widget(self)
173 .container()
174 .bg(Color::BLACK)
175 .padding(15)
176 .outline((5.0, border))
177 .centered_horiz()
178 .margin_below(5),
179 GeomBatch::from(vec![(Color::grey(0.5), Polygon::rectangle(10.0, 30.0))])
180 .into_widget(self)
181 .centered_horiz(),
182 self.style
183 .loading_tips
184 .clone()
185 .default_fg(Color::WHITE)
186 .wrap_to_pct(self, 25)
187 .into_widget(self)
188 .container()
189 .bg(Color::BLACK)
190 .padding(15)
191 .outline((5.0, Color::YELLOW))
192 .force_width_window_pct(self, Percent::int(30))
193 .margin_below(5),
194 GeomBatch::from(vec![(Color::grey(0.5), Polygon::rectangle(10.0, 100.0))])
195 .into_widget(self)
196 .centered_horiz(),
197 ])
198 .centered_vert(),
199 txt.change_fg(Color::WHITE)
200 .inner_render(&self.prerender.assets, svg::LOW_QUALITY)
201 .into_widget(self)
202 .container()
203 .fill_width()
204 .padding(16)
205 .bg(Color::grey(0.3)),
206 ]))
207 .dims_width(PanelDims::ExactPercent(0.8))
208 .dims_height(PanelDims::ExactPercent(0.8))
209 .aligned(HorizontalAlignment::Center, VerticalAlignment::Center)
210 .build_custom(self)
211 }
212
213 pub fn is_font_loaded(&self, filename: &str) -> bool {
216 self.prerender.assets.is_font_loaded(filename)
217 }
218
219 pub fn load_font(&mut self, filename: &str, bytes: Vec<u8>) {
221 self.prerender.assets.load_font(filename, bytes)
222 }
223
224 pub fn hide_cursor(&self) {
225 self.prerender.inner.set_cursor_visible(false);
226 }
227 pub fn show_cursor(&self) {
228 self.prerender.inner.set_cursor_visible(true);
229 }
230
231 pub fn set_scale_factor(&mut self, scale_factor: f64) {
233 self.prerender.scale_factor.set(scale_factor);
234 let new_size = self.prerender.window_size();
235 self.prerender.window_resized(new_size);
236 self.canvas.window_width = new_size.width;
237 self.canvas.window_height = new_size.height;
238 }
239
240 pub fn set_texture(
242 &mut self,
243 sprite_bytes: Vec<u8>,
244 sprite_dims: (u32, u32),
245 texture_scale: (f32, f32),
246 ) {
247 self.prerender.inner.upload_texture(
248 crate::backend_glow::SpriteTexture::new(sprite_bytes, sprite_dims.0, sprite_dims.1)
249 .expect("failed to format texture sprite sheet"),
250 texture_scale,
251 );
252 }
253}
254
255struct LoadingScreen<'a> {
256 canvas: Canvas,
257 style: Style,
258 prerender: &'a Prerender,
259 lines: VecDeque<String>,
260 max_capacity: usize,
261 last_drawn: Instant,
262 title: String,
263}
264
265impl<'a> LoadingScreen<'a> {
266 fn new(
267 prerender: &'a Prerender,
268 style: Style,
269 initial_size: ScreenDims,
270 title: String,
271 ) -> LoadingScreen<'a> {
272 let canvas = Canvas::new(initial_size, CanvasSettings::new());
273 let max_capacity =
274 (0.8 * initial_size.height / *prerender.assets.default_line_height.borrow()) as usize;
275 LoadingScreen {
276 prerender,
277 lines: VecDeque::new(),
278 max_capacity,
279 last_drawn: Instant::now(),
281 title,
282 canvas,
283 style,
284 }
285 }
286
287 fn redraw(&mut self) {
288 if elapsed_seconds(self.last_drawn) < 0.5 {
290 return;
291 }
292 self.last_drawn = Instant::now();
293 let mut ctx = EventCtx {
294 fake_mouseover: true,
295 input: UserInput::new(Event::NoOp, &self.canvas),
296 canvas: &mut self.canvas,
297 prerender: self.prerender,
298 style: &mut self.style,
299 updates_requested: vec![],
300 canvas_movement_called: false,
301 focus_owned_by: None,
302 next_focus_owned_by: None,
303 };
304
305 let mut txt = Text::from(Line(&self.title).small_heading());
306 for l in &self.lines {
307 txt.add_line(l);
308 }
309 let panel = ctx.make_loading_screen(txt);
310
311 let mut g = GfxCtx::new(self.prerender, &self.canvas, &self.style, false);
312 g.clear(Color::BLACK);
313 panel.draw(&mut g);
314 g.prerender.inner.draw_finished(g.inner);
315 }
316}
317
318impl<'a> TimerSink for LoadingScreen<'a> {
319 fn println(&mut self, line: String) {
321 if self.lines.len() == self.max_capacity {
322 self.lines.pop_front();
323 }
324 self.lines.push_back(line);
325 self.redraw();
326 }
327
328 fn reprintln(&mut self, line: String) {
329 self.lines.pop_back();
330 self.lines.push_back(line);
331 self.redraw();
332 }
333}