Skip to main content

typo_eq/app/
render.rs

1use std::io::{stdout, Write};
2
3use crossterm::{terminal::{EnterAlternateScreen, enable_raw_mode, disable_raw_mode, LeaveAlternateScreen, Clear, ClearType}, style::{Color, SetForegroundColor, ResetColor}, execute, cursor::{MoveTo, position, MoveToRow, Hide, Show, MoveToColumn}, event::{PushKeyboardEnhancementFlags, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags}};
4
5use super::util::term_center;
6
7pub struct Renderer;
8
9#[derive(Default)]
10pub enum TextAlign {
11    #[default]
12    Left,
13    Center,
14    Right
15}
16
17impl Renderer {
18    pub fn init () -> Self {
19        enable_raw_mode()
20        .expect("This app requires raw mode to be available in order to function correctly");
21        let mut stdout = stdout();
22        execute!(
23            stdout,
24            EnterAlternateScreen,
25            Hide,
26            PushKeyboardEnhancementFlags(
27                KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
28                    | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
29                    | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
30            )
31        ).unwrap();
32        Self
33    }
34    pub fn exit(self: &Self) {
35        let mut stdout = stdout();
36        execute!(
37            stdout,
38            PopKeyboardEnhancementFlags,
39            Show,
40            LeaveAlternateScreen,
41        ).unwrap();
42    
43        disable_raw_mode().unwrap();
44    }
45    pub fn reset_color(self: &Self) {
46        let mut stdout = stdout();
47        execute!(
48            stdout,
49            ResetColor,
50        ).expect("Could not reset terminal colours");
51    }
52    pub fn print(self: &Self, word: &str, color: Option<Color>, background_color: Option<Color>) {
53        let (row, col) = position().unwrap();
54        self.print_at(
55            word, 
56            (row, col), 
57            TextAlign::Left, 
58            color, background_color, 
59            Some(Clear(ClearType::CurrentLine))
60        )
61    }
62    pub fn print_at_center_default(self: &Self, word: &str) {
63        self.print_at(
64            word, term_center(), 
65            TextAlign::Center, 
66            None, None, 
67            Some(Clear(ClearType::CurrentLine))
68        )
69    }
70    pub fn print_at_center(self: &Self, word: &str, offset: (i16, i16), align: Option<TextAlign>, color: Option<Color>, background_color: Option<Color>, clear: Option<Clear>) {
71        let center = term_center();
72        self.print_at(
73            word, 
74            (
75                (center.0 as i16 + offset.0) as u16, 
76                (center.1 as i16 + offset.1) as u16
77            ), 
78            align.unwrap_or(TextAlign::Center), 
79            color, background_color, 
80            clear
81        )
82    }
83    pub fn print_at(self: &Self, word: &str, position: (u16, u16), align: TextAlign, color: Option<Color>, background_color: Option<Color>, clear: Option<Clear>) {
84        let mut stdout = stdout();
85        if let Some(color) = color {
86            execute!(
87                stdout,
88                SetForegroundColor(color),
89            ).expect("Could not set text colour");
90        }
91        if let Some(color) = background_color {
92            execute!(
93                stdout,
94                SetForegroundColor(color),
95            ).expect("Could not set background colour");
96        }
97        let x ;
98        let y = position.1;
99        match align {
100            TextAlign::Left => {
101                x = position.0;
102            }
103            TextAlign::Center => {
104                x = position.0 - word.chars().count() as u16 / 2;
105            }
106            TextAlign::Right => {
107                x = position.0 - word.chars().count() as u16;
108            }
109        }
110        execute!(
111            stdout,
112            MoveTo(x, y),
113        ).expect(format!("Could not move to position {} {}", x, y).as_str());
114        if let Some(clear) = clear {
115            execute!(
116                stdout,
117                clear,
118            ).expect("Could not clear line");
119        }
120        write!(
121            stdout,
122            "{}", word,
123        ).expect("Could not write to stdout");
124        stdout.lock().flush().unwrap();
125        execute!(
126            stdout,
127            ResetColor
128        ).expect("Could not reset text colour after render");
129    }
130    pub fn clear_line_at_center(self: &Self, offset: (i16, i16)) {
131        let center = term_center();
132        self.clear_line_at(
133            (
134                (center.0 as i16 + offset.0) as u16, 
135                (center.1 as i16 + offset.1) as u16
136            ), 
137        )
138    }
139    pub fn clear_down_from_center_at(self: &Self, offset_y: i16) {
140        let center = term_center();
141        let mut stdout = stdout();
142        execute!(
143            stdout,
144            MoveToColumn(0),
145            MoveToRow((center.1 as i16 + offset_y) as u16),
146            Clear(ClearType::FromCursorDown)
147        ).expect("Could not clear lines down from center")
148    }
149    pub fn clear_line_at(self: &Self, position: (u16, u16)) {
150        let mut stdout = stdout();
151        execute!(
152            stdout,
153            MoveTo(position.0, position.1),
154            Clear(ClearType::CurrentLine)
155        ).expect("Could not clear line")
156    }
157}