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}