use textmode::Textmode as _; pub struct Display { screen: vt100::Screen, current_frame: usize, total_frames: usize, done_loading: bool, paused: bool, speed: u32, show_ui: bool, show_help: bool, active_search: Option, } impl Display { pub fn new() -> Self { Self { screen: vt100::Parser::default().screen().clone(), current_frame: 0, total_frames: 0, done_loading: false, paused: false, speed: 16, show_ui: true, show_help: false, active_search: None, } } pub fn screen(&mut self, screen: vt100::Screen) { self.screen = screen; } pub fn current_frame(&mut self, idx: usize) { self.current_frame = idx; } pub fn total_frames(&mut self, n: usize) { self.total_frames = n; } pub fn done_loading(&mut self) { self.done_loading = true; } pub fn paused(&mut self, paused: bool) { self.paused = paused; } pub fn speed(&mut self, speed: u32) { self.speed = speed; } pub fn toggle_ui(&mut self) { self.show_ui = !self.show_ui; } pub fn toggle_help(&mut self) { self.show_help = !self.show_help; } pub fn active_search(&mut self, s: String) { self.active_search = Some(s); } pub fn clear_search(&mut self) { self.active_search = None; } pub async fn render( &self, output: &mut textmode::Output, ) -> anyhow::Result<()> { self.render_screen(output); let pos = output.screen().cursor_position(); if self.paused && self.show_ui { self.render_frame_count(output); self.render_speed(output); self.render_pause_symbol(output); if self.show_help { self.render_help(output); } } self.render_search(output); output.reset_attributes(); output.move_to(pos.0, pos.1); output.refresh().await?; Ok(()) } fn render_screen(&self, output: &mut textmode::Output) { output.clear(); output.move_to(0, 0); output.write(&self.screen.contents_formatted()); } fn render_frame_count(&self, output: &mut textmode::Output) { output.move_to(0, 0); output.reset_attributes(); output.set_fgcolor(textmode::color::BLACK); if self.done_loading { output.set_bgcolor(textmode::color::CYAN); } else { output.set_bgcolor(textmode::color::RED); } output.write_str(&format!( " {}/{} ", self.current_frame + 1, self.total_frames )); } fn render_speed(&self, output: &mut textmode::Output) { if self.speed != 16 { output.move_to(1, 0); output.reset_attributes(); output.set_fgcolor(textmode::color::BLACK); output.set_bgcolor(textmode::color::CYAN); output.write_str(&format!( "speed: {}x", 16.0 / f64::from(self.speed) )); } } #[allow(clippy::unused_self)] fn render_pause_symbol(&self, output: &mut textmode::Output) { let size = output.screen().size(); output.move_to(0, size.1 - 1); output.reset_attributes(); output.set_fgcolor(textmode::color::BLACK); output.set_bgcolor(textmode::color::RED); output.write_str("\u{23f8}"); } #[allow(clippy::unused_self)] fn render_help(&self, output: &mut textmode::Output) { let size = output.screen().size(); output.reset_attributes(); output.set_fgcolor(textmode::color::BLACK); output.set_bgcolor(textmode::color::CYAN); output.move_to(size.0 - 16, size.1 - 23); output.write_str(" keys "); output.move_to(size.0 - 15, size.1 - 23); output.write_str(" q: quit "); output.move_to(size.0 - 14, size.1 - 23); output.write_str(" space: pause/unpause "); output.move_to(size.0 - 13, size.1 - 23); output.write_str(" tab: hide/show ui "); output.move_to(size.0 - 12, size.1 - 23); output.write_str(" h: previous frame "); output.move_to(size.0 - 11, size.1 - 23); output.write_str(" l: next frame "); output.move_to(size.0 - 10, size.1 - 23); output.write_str(" 0: first frame "); output.move_to(size.0 - 9, size.1 - 23); output.write_str(" $: last frame "); output.move_to(size.0 - 8, size.1 - 23); output.write_str(" +: increase speed "); output.move_to(size.0 - 7, size.1 - 23); output.write_str(" -: decrease speed "); output.move_to(size.0 - 6, size.1 - 23); output.write_str(" =: normal speed "); output.move_to(size.0 - 5, size.1 - 23); output.write_str(" /: search "); output.move_to(size.0 - 4, size.1 - 23); output.write_str(" n: next match "); output.move_to(size.0 - 3, size.1 - 23); output.write_str(" p: previous match "); output.move_to(size.0 - 2, size.1 - 23); output.write_str(" ?: hide/show help "); } fn render_search(&self, output: &mut textmode::Output) { if let Some(search) = &self.active_search { let size = output.screen().size(); output.reset_attributes(); output.set_fgcolor(textmode::color::BLACK); output.set_bgcolor(textmode::color::CYAN); output.move_to(size.0 - 1, 0); output.write_str("/"); output.write_str(search); output.write_str( &" ".repeat(usize::from(size.1) - search.len() - 1), ); } } }