1use ncurses::*;
2use std::fmt::Write;
3use unicode_segmentation::UnicodeSegmentation;
4
5pub struct Window {
6 pub height: i32,
7 pub width: i32,
8 pub x: i32,
9 pub y: i32,
10 curse_window: ncurses::WINDOW,
11 inner_window: ncurses::WINDOW,
12 text_color: attr_t,
13 border_color: attr_t,
14 title: String,
15}
16
17impl Window {
18 pub fn new(
19 height: i32,
20 width: i32,
21 x: i32,
22 y: i32,
23 border_color: attr_t,
24 text_color: attr_t,
25 title: String,
26 ) -> Self {
27 let win_box = newwin(height, width, y, x);
28 let win_inner = derwin(win_box, height - 2, width - 4, 1, 2);
29 let new_win = Self {
30 height,
31 width,
32 x,
33 y,
34 curse_window: win_box,
35 inner_window: win_inner,
36 text_color,
37 border_color,
38 title,
39 };
40 wattrset(new_win.inner_window, text_color);
41 new_win.draw_border();
42 new_win
43 }
44
45 pub fn refresh(&self) {
46 wrefresh(self.curse_window);
47 wrefresh(self.inner_window);
48 }
49
50 pub fn write(&self, content: &str) {
51 werase(self.inner_window);
52
53 let mut color_applied = vec![];
54 let mut effect_applied = vec![];
55
56 let mut trimmed_text = String::new();
57 let mut formated_string = String::new();
58
59 for part in content.split("[[EFFECT_").collect::<Vec<&str>>() {
60 if part.contains("]]") {
61 let effect: Vec<&str> = part.split("]]").collect();
62
63 let aa: String = part.chars().skip(effect[0].len() + 2).collect();
64 trimmed_text += &aa;
65 } else {
66 trimmed_text += part;
67 }
68 }
69
70 for (i, line) in trimmed_text.split('\n').into_iter().enumerate() {
71 if line.graphemes(true).count() == self.width as usize - 4 && line.ends_with('\n') {
72 let tmp = content.split('\n').collect::<Vec<&str>>()[i].split("");
73
74 formated_string += &tmp
75 .take(line.graphemes(true).count() - 1)
76 .into_iter()
77 .collect::<String>();
78 } else if line.graphemes(true).count() == self.width as usize - 4 {
79 formated_string += content.split('\n').collect::<Vec<&str>>()[i];
80 } else {
81 writeln!(
82 &mut formated_string,
83 "{}",
84 content.split('\n').collect::<Vec<&str>>()[i]
85 )
86 .unwrap();
87 }
88 }
89
90 for el in formated_string.split("[[EFFECT_").collect::<Vec<&str>>() {
91 if el.contains("]]") {
92 let effect: Vec<&str> = el.split("]]").collect();
93
94 let attr = get_attr_from_string(effect[0]);
95 if let Some(attr) = attr {
96 if effect[0].starts_with("COLOR") {
97 if !color_applied.is_empty()
98 && color_applied[color_applied.len() - 1] == attr
99 {
100 color_applied.pop();
101 } else {
102 color_applied.push(attr);
103 }
104 } else if !effect_applied.is_empty() {
105 effect_applied.pop();
106 } else {
107 effect_applied.push(attr);
108 }
109 }
110
111 if effect_applied.is_empty() {
112 wattr_off(self.inner_window, A_ATTRIBUTES());
113 } else {
114 wattr_on(self.inner_window, effect_applied[effect_applied.len() - 1]);
115 }
116 if color_applied.is_empty() {
117 wattr_on(self.inner_window, self.text_color);
118 } else {
119 wattr_on(self.inner_window, color_applied[color_applied.len() - 1]);
120 }
121 let aa: String = el.chars().skip(effect[0].len() + 2).collect();
122 waddstr(self.inner_window, &aa);
123 } else {
124 waddstr(self.inner_window, el);
125 }
126 }
127
128 wattrset(self.inner_window, self.text_color);
129 }
130
131 fn draw_border(&self) {
132 wattron(self.curse_window, self.border_color);
133 box_(self.curse_window, 0, 0);
134 wattroff(self.curse_window, self.border_color);
135 mvwaddstr(self.curse_window, 0, 2, " ");
136
137 let mut color_applied = vec![];
138 let mut effect_applied = vec![];
139 for el in self.title.split("[[EFFECT_").collect::<Vec<&str>>() {
140 if el.contains("]]") {
141 let effect: Vec<&str> = el.split("]]").collect();
142
143 let attr = get_attr_from_string(effect[0]);
144 if let Some(attr) = attr {
145 if effect[0].starts_with("COLOR") {
146 if !color_applied.is_empty()
147 && color_applied[color_applied.len() - 1] == attr
148 {
149 color_applied.pop();
150 } else {
151 color_applied.push(attr);
152 }
153 } else if !effect_applied.is_empty() {
154 effect_applied.pop();
155 } else {
156 effect_applied.push(attr);
157 }
158 }
159
160 if effect_applied.is_empty() {
161 wattr_off(self.curse_window, A_ATTRIBUTES());
162 } else {
163 wattr_on(self.curse_window, effect_applied[effect_applied.len() - 1]);
164 }
165 if color_applied.is_empty() {
166 wattr_off(self.curse_window, self.border_color);
167 } else {
168 wattr_on(self.curse_window, color_applied[color_applied.len() - 1]);
169 }
170
171 let a: String = el.chars().skip(effect[0].len() + 2).collect();
172 waddstr(self.curse_window, &a);
173 } else {
174 waddstr(self.curse_window, el);
175 }
176 }
177
178 waddstr(self.curse_window, " ");
179 wattrset(self.curse_window, ncurses::A_NORMAL());
180 wattron(self.curse_window, self.border_color);
181 }
182
183 pub fn resize(&mut self, height: i32, width: i32) {
184 self.height = height;
185 self.width = width;
186 }
187
188 pub fn deplace(&mut self, x: i32, y: i32) {
189 delwin(self.curse_window);
190 delwin(self.inner_window);
191 self.curse_window = newwin(self.height, self.width, y, x);
192 self.inner_window = derwin(self.curse_window, self.height - 2, self.width - 2, 1, 1);
193 wattron(self.inner_window, self.text_color);
194 self.draw_border();
195 }
196
197 pub fn set_title(&mut self, title: String) {
198 self.title = title;
199 }
200
201 pub fn set_border_color(&mut self, border_color: attr_t) {
202 self.border_color = border_color;
203 self.draw_border();
204 }
205}
206
207fn get_attr_from_string(attribute: &str) -> std::option::Option<attr_t> {
208 let mut tmp = attribute.chars().collect::<Vec<char>>();
209 tmp.retain(|&c| c == '_');
210
211 if attribute.starts_with("COLOR_") && tmp.len() == 2 {
212 let foreground = match attribute.split('_').collect::<Vec<&str>>()[1] {
213 "RED" => COLOR_RED,
214 "GREEN" => COLOR_GREEN,
215 "YELLOW" => COLOR_YELLOW,
216 "BLUE" => COLOR_BLUE,
217 "MAGENTA" => COLOR_MAGENTA,
218 "CYAN" => COLOR_CYAN,
219 "WHITE" => COLOR_WHITE,
220 "BLACK" => COLOR_BLACK,
221 _ => -1,
222 };
223 let background = match attribute.split('_').collect::<Vec<&str>>()[2] {
224 "RED" => COLOR_RED,
225 "GREEN" => COLOR_GREEN,
226 "YELLOW" => COLOR_YELLOW,
227 "BLUE" => COLOR_BLUE,
228 "MAGENTA" => COLOR_MAGENTA,
229 "CYAN" => COLOR_CYAN,
230 "WHITE" => COLOR_WHITE,
231 "BLACK" => COLOR_BLACK,
232 _ => -1,
233 };
234
235 init_pair(foreground * 10 + background, foreground, background);
236 std::option::Option::from(COLOR_PAIR(foreground * 10 + background))
237 } else {
238 match attribute {
239 "REVERSE" => std::option::Option::from(ncurses::A_REVERSE()),
240 "BOLD" => std::option::Option::from(ncurses::A_BOLD()),
241 "ITALIC" => std::option::Option::from(ncurses::A_ITALIC()),
242 "DIMMED" => std::option::Option::from(ncurses::A_DIM()),
243 "UNDERLINE" => std::option::Option::from(ncurses::A_UNDERLINE()),
244 "COLOR_RED" => std::option::Option::from(COLOR_PAIR(1)),
245 "COLOR_GREEN" => std::option::Option::from(COLOR_PAIR(2)),
246 "COLOR_YELLOW" => std::option::Option::from(COLOR_PAIR(3)),
247 "COLOR_BLUE" => std::option::Option::from(COLOR_PAIR(4)),
248 "COLOR_MAGENTA" => std::option::Option::from(COLOR_PAIR(5)),
249 "COLOR_CYAN" => std::option::Option::from(COLOR_PAIR(6)),
250 "COLOR_WHITE" => std::option::Option::from(COLOR_PAIR(7)),
251 "COLOR_BLACK" => std::option::Option::from(COLOR_PAIR(8)),
252 _ => std::option::Option::None,
253 }
254 }
255}