rtop_rs/
window.rs

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}