1use std::{
2 io::{self, Stdout, Write},
3 ops,
4};
5
6use crossterm::cursor;
7use crossterm::execute;
8use crossterm::{
9 queue,
10 style::{self, StyledContent},
11 terminal::{self, ClearType},
12};
13
14use crate::math::Vec2;
15
16pub trait Render<'a> {
17 fn render(
18 &'a self,
19 ) -> Box<dyn std::iter::Iterator<Item = (&'a Vec2, &'a StyledContent<String>)> + 'a>;
20}
21
22pub struct Renderer {
23 stdout: Stdout,
24}
25
26impl Renderer {
27 pub fn new() -> Self {
28 let mut stdout = io::stdout();
29
30 terminal::enable_raw_mode().unwrap();
31
32 execute!(
33 stdout,
34 terminal::EnterAlternateScreen,
35 cursor::Hide,
36 terminal::SetTitle("Snake Game")
37 )
38 .unwrap();
39
40 Self { stdout }
41 }
42 pub fn render<'a>(&mut self, objects: &[&'a (dyn Render<'a> + 'a)]) -> crossterm::Result<()> {
43 for obj in objects.iter() {
44 for (pos, c) in obj.render() {
45 queue!(
46 self.stdout,
47 cursor::MoveTo(pos.x as u16, pos.y as u16),
48 style::PrintStyledContent(c.clone())
49 )?;
50 }
51 }
52 self.stdout.flush()?;
53 Ok(())
54 }
55 pub fn clear<'a>(&mut self, objects: &[&'a (dyn Render<'a> + 'a)]) -> crossterm::Result<()> {
56 for obj in objects {
57 for (pos, _) in obj.render() {
58 queue!(
59 self.stdout,
60 cursor::MoveTo(pos.x as u16, pos.y as u16),
61 style::Print(" "),
62 )?;
63 }
64 }
65 self.stdout.flush()?;
66 Ok(())
67 }
68 pub fn clear_all(&mut self) -> crossterm::Result<()> {
69 queue!(self.stdout, terminal::Clear(ClearType::All),)?;
70 self.stdout.flush()?;
71 Ok(())
72 }
73}
74
75impl ops::Drop for Renderer {
76 fn drop(&mut self) {
77 execute!(self.stdout, terminal::LeaveAlternateScreen).unwrap();
78 terminal::disable_raw_mode().unwrap();
79 }
80}