snake_tui_rs/
renderer.rs

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}