1#![allow(dead_code)]
2
3use std::io::Write;
4
5use crate::event_handler;
7use crossbeam;
8
9
10pub static CLEAR: &str = "\x1b[0m";
20pub static SHOW_CURSOR: &str = "\x1b[?25h";
21pub static HIDE_CURSOR: &str = "\x1b[?25l";
22
23pub static EMPTY_MODIFIER_REFERENCE: &[&str] = &[]; pub static BLACK: (Option <&str>, &[&str], bool) = (Some("30"), &[], false);
27pub static RED: (Option <&str>, &[&str], bool) = (Some("31"), &[], false);
28pub static GREEN: (Option <&str>, &[&str], bool) = (Some("32"), &[], false);
29pub static YELLOW: (Option <&str>, &[&str], bool) = (Some("33"), &[], false);
30pub static BLUE: (Option <&str>, &[&str], bool) = (Some("34"), &[], false);
31pub static MAGENTA: (Option <&str>, &[&str], bool) = (Some("35"), &[], false);
32pub static CYAN: (Option <&str>, &[&str], bool) = (Some("36"), &[], false);
33pub static WHITE: (Option <&str>, &[&str], bool) = (Some("37"), &[], false);
34pub static DEFAULT: (Option <&str>, &[&str], bool) = (Some("39"), &[], false);
35
36pub static BRIGHT_BLACK: (Option <&str>, &[&str], bool) = (Some("90"), &[], false );
37pub static BRIGHT_RED: (Option <&str>, &[&str], bool) = (Some("91"), &[], false );
38pub static BRIGHT_GREEN: (Option <&str>, &[&str], bool) = (Some("92"), &[], false );
39pub static BRIGHT_YELLOW: (Option <&str>, &[&str], bool) = (Some("93"), &[], false );
40pub static BRIGHT_BLUE: (Option <&str>, &[&str], bool) = (Some("94"), &[], false );
41pub static BRIGHT_MAGENTA: (Option <&str>, &[&str], bool) = (Some("95"), &[], false );
42pub static BRIGHT_CYAN: (Option <&str>, &[&str], bool) = (Some("96"), &[], false );
43pub static BRIGHT_WHITE: (Option <&str>, &[&str], bool) = (Some("97"), &[], false );
44pub static BRIGHT_DEFAULT: (Option <&str>, &[&str], bool) = (Some("99"), &[], false );
45
46pub static ON_BLACK: (Option <&str>, &[&str], bool) = (Some("100"), &[], true );
47pub static ON_RED: (Option <&str>, &[&str], bool) = (Some("101"), &[], true );
48pub static ON_GREEN: (Option <&str>, &[&str], bool) = (Some("102"), &[], true );
49pub static ON_YELLOW: (Option <&str>, &[&str], bool) = (Some("103"), &[], true );
50pub static ON_BLUE: (Option <&str>, &[&str], bool) = (Some("104"), &[], true );
51pub static ON_MAGENTA: (Option <&str>, &[&str], bool) = (Some("105"), &[], true );
52pub static ON_CYAN: (Option <&str>, &[&str], bool) = (Some("106"), &[], true );
53pub static ON_WHITE: (Option <&str>, &[&str], bool) = (Some("107"), &[], true );
54pub static ON_DEFAULT: (Option <&str>, &[&str], bool) = (Some("109"), &[], true );
55
56pub static ON_BRIGHT_BLACK: (Option <&str>, &[&str], bool) = (Some("40"), &[], true );
57pub static ON_BRIGHT_RED: (Option <&str>, &[&str], bool) = (Some("41"), &[], true );
58pub static ON_BRIGHT_GREEN: (Option <&str>, &[&str], bool) = (Some("42"), &[], true );
59pub static ON_BRIGHT_YELLOW: (Option <&str>, &[&str], bool) = (Some("43"), &[], true );
60pub static ON_BRIGHT_BLUE: (Option <&str>, &[&str], bool) = (Some("44"), &[], true );
61pub static ON_BRIGHT_MAGENTA: (Option <&str>, &[&str], bool) = (Some("45"), &[], true );
62pub static ON_BRIGHT_CYAN: (Option <&str>, &[&str], bool) = (Some("46"), &[], true );
63pub static ON_BRIGHT_WHITE: (Option <&str>, &[&str], bool) = (Some("47"), &[], true );
64pub static ON_BRIGHT_DEFAULT: (Option <&str>, &[&str], bool) = (Some("49"), &[], true );
65
66pub static BOLD: (Option <&str>, &[&str], bool) = (None , &["1"], false);
67pub static DIM: (Option <&str>, &[&str], bool) = (None , &["2"], false);
68pub static ITALIC: (Option <&str>, &[&str], bool) = (None , &["3"], false);
69pub static UNDERLINE: (Option <&str>, &[&str], bool) = (None , &["4"], false);
70pub static BLINK: (Option <&str>, &[&str], bool) = (None , &["5"], false);
71pub static REVERSE: (Option <&str>, &[&str], bool) = (None , &["7"], false);
72pub static HIDE: (Option <&str>, &[&str], bool) = (None , &["8"], false);
73
74
75#[derive(Clone, PartialEq, Eq, Debug, Default, Hash, Copy)]
78pub enum ColorMode {
79 #[default] Dark,
80 Light,
81}
82
83impl ColorMode {
84 pub fn to_light () {
85 unsafe {COLOR_MODE = ColorMode::Light};
86 }
87
88 pub fn to_dark () {
89 unsafe {COLOR_MODE = ColorMode::Dark};
90 }
91}
92
93static mut COLOR_MODE: ColorMode = ColorMode::Dark;
96
97
98#[derive(Clone, Debug, Eq, PartialEq, Default, Hash, Copy)]
106pub enum ColorType {
107 Black,
108 Red,
109 Green,
110 Yellow,
111 Blue,
112 Magenta,
113 Cyan,
114 White,
115 #[default] Default,
116
117 BrightBlack,
118 BrightRed,
119 BrightGreen,
120 BrightYellow,
121 BrightBlue,
122 BrightMagenta,
123 BrightCyan,
124 BrightWhite,
125 BrightDefault,
126
127 OnBlack,
128 OnRed,
129 OnGreen,
130 OnYellow,
131 OnBlue,
132 OnMagenta,
133 OnCyan,
134 OnWhite,
135 OnDefault,
136
137 OnBrightBlack,
138 OnBrightRed,
139 OnBrightGreen,
140 OnBrightYellow,
141 OnBrightBlue,
142 OnBrightMagenta,
143 OnBrightCyan,
144 OnBrightWhite,
145 OnBrightDefault,
146
147 Bold,
148 Dim,
149 Italic,
150 Underline,
151 Blink,
152 Reverse,
153 Hide,
154
155 OnRGB (u8, u8, u8),
156 Rgb(u8, u8, u8),
157 OnANSI (u8),
158 Ansi(u8),
159}
160
161#[derive(Clone, Debug, Eq, PartialEq, Hash)]
171pub enum UniqueColor {
172 Static ((Option <&'static str>, &'static [&'static str], bool)),
173 Dynamic ((Option < String >, &'static [&'static str], bool)),
174}
175
176impl UniqueColor {
177 fn as_string_vec(&self, attributes: &'static [&'static str]) -> Vec<String> {
181 let mut mods = vec![];
182 for modifier in attributes {
183 mods.push(modifier.to_string())
184 }
185 mods
186 }
187
188 pub fn unwrap_into_tuple (&self) -> (Option <String>, Vec <String>, bool) {
194 match self {
195 UniqueColor::Static(s) => {
196 (s.0.map(|t| t.to_owned()), self.as_string_vec(s.1), s.2)
197 },
198 UniqueColor::Dynamic(s) => {
199 (s.0.clone(), self.as_string_vec(s.1), s.2)
200 },
201 }
202 }
203}
204
205impl ColorType {
206 pub fn get_color (&self) -> UniqueColor {
208 if unsafe { COLOR_MODE } == ColorMode::Dark {
209 self.get_dark_color()
210 } else {
211 self.get_light_color()
212 }
213 }
214
215 fn get_light_color (&self) -> UniqueColor {
216 match self {
217 ColorType::Black => { UniqueColor::Static(BRIGHT_WHITE) },
218 ColorType::Red => { UniqueColor::Static(RED) },
219 ColorType::Green => { UniqueColor::Static(GREEN) },
220 ColorType::Yellow => { UniqueColor::Static(YELLOW) },
221 ColorType::Blue => { UniqueColor::Static(BLUE) },
222 ColorType::Magenta => { UniqueColor::Static(MAGENTA) },
223 ColorType::Cyan => { UniqueColor::Static(CYAN) },
224 ColorType::White => { UniqueColor::Static(BRIGHT_BLACK) },
225 ColorType::Default => { UniqueColor::Static(BRIGHT_DEFAULT) },
226
227 ColorType::BrightBlack => { UniqueColor::Static(WHITE) },
228 ColorType::BrightRed => { UniqueColor::Static(RED) },
229 ColorType::BrightGreen => { UniqueColor::Static(GREEN) },
230 ColorType::BrightYellow => { UniqueColor::Static(YELLOW) },
231 ColorType::BrightBlue => { UniqueColor::Static(BLUE) },
232 ColorType::BrightMagenta => { UniqueColor::Static(MAGENTA) },
233 ColorType::BrightCyan => { UniqueColor::Static(CYAN) },
234 ColorType::BrightWhite => { UniqueColor::Static(BLACK) },
235 ColorType::BrightDefault => { UniqueColor::Static(DEFAULT) },
236
237 ColorType::OnBlack => { UniqueColor::Static(ON_WHITE) },
238 ColorType::OnRed => { UniqueColor::Static(ON_RED) },
239 ColorType::OnGreen => { UniqueColor::Static(ON_GREEN) },
240 ColorType::OnYellow => { UniqueColor::Static(ON_YELLOW) },
241 ColorType::OnBlue => { UniqueColor::Static(ON_BLUE) },
242 ColorType::OnMagenta => { UniqueColor::Static(ON_MAGENTA) },
243 ColorType::OnCyan => { UniqueColor::Static(ON_CYAN) },
244 ColorType::OnWhite => { UniqueColor::Static(ON_BRIGHT_BLACK) },
245 ColorType::OnDefault => { UniqueColor::Static(ON_BRIGHT_DEFAULT) },
246
247 ColorType::OnBrightBlack => { UniqueColor::Static(ON_BRIGHT_WHITE) },
248 ColorType::OnBrightRed => { UniqueColor::Static(ON_RED) },
249 ColorType::OnBrightGreen => { UniqueColor::Static(ON_GREEN) },
250 ColorType::OnBrightYellow => { UniqueColor::Static(ON_YELLOW) },
251 ColorType::OnBrightBlue => { UniqueColor::Static(ON_BLUE) },
252 ColorType::OnBrightMagenta => { UniqueColor::Static(ON_MAGENTA) },
253 ColorType::OnBrightCyan => { UniqueColor::Static(ON_CYAN) },
254 ColorType::OnBrightWhite => { UniqueColor::Static(ON_BLACK) },
255 ColorType::OnBrightDefault => { UniqueColor::Static(ON_DEFAULT) },
256
257 ColorType::Rgb(r, g, b) => {
259 let (mut rn, mut gn, mut bn) = (*r, *g, *b);
260 if rn > 128 { rn -= 128; }
261 if gn > 128 { gn -= 128; }
262 if bn > 128 { bn -= 128; }
263 UniqueColor::Dynamic((Some(format!("38;2;{};{};{}", rn, gn, bn)), EMPTY_MODIFIER_REFERENCE, false))
264 },
265 ColorType::OnRGB (r, g, b) => {
267 let (mut rn, mut gn, mut bn) = (*r, *g, *b);
268 if rn > 128 { rn -= 128; }
269 if gn > 128 { gn -= 128; }
270 if bn > 128 { bn -= 128; }
271 UniqueColor::Dynamic((Some(format!("48;2;{};{};{}", rn, gn, bn)), EMPTY_MODIFIER_REFERENCE, true))
272 },
273 ColorType::Ansi(index) => {
274 UniqueColor::Dynamic((Some(format!("38;5;{}", index)), EMPTY_MODIFIER_REFERENCE, false))
275 },
276 ColorType::OnANSI (index) => {
277 UniqueColor::Dynamic((Some(format!("48;5;{}", index)), EMPTY_MODIFIER_REFERENCE, true))
278 },
279
280 ColorType::Bold => { UniqueColor::Static(BOLD) },
281 ColorType::Dim => { UniqueColor::Static(DIM) },
282 ColorType::Italic => { UniqueColor::Static(ITALIC) },
283 ColorType::Underline => { UniqueColor::Static(UNDERLINE) },
284 ColorType::Blink => { UniqueColor::Static(BLINK) },
285 ColorType::Reverse => { UniqueColor::Static(REVERSE) },
286 ColorType::Hide => { UniqueColor::Static(HIDE) },
287 }
288 }
289
290 fn get_dark_color (&self) -> UniqueColor {
291 match self {
292 ColorType::Black => { UniqueColor::Static(BLACK) },
293 ColorType::Red => { UniqueColor::Static(RED) },
294 ColorType::Green => { UniqueColor::Static(GREEN) },
295 ColorType::Yellow => { UniqueColor::Static(YELLOW) },
296 ColorType::Blue => { UniqueColor::Static(BLUE) },
297 ColorType::Magenta => { UniqueColor::Static(MAGENTA) },
298 ColorType::Cyan => { UniqueColor::Static(CYAN) },
299 ColorType::White => { UniqueColor::Static(WHITE) },
300 ColorType::Default => { UniqueColor::Static(DEFAULT) },
301
302 ColorType::BrightBlack => { UniqueColor::Static(BRIGHT_BLACK) },
303 ColorType::BrightRed => { UniqueColor::Static(BRIGHT_RED) },
304 ColorType::BrightGreen => { UniqueColor::Static(BRIGHT_GREEN) },
305 ColorType::BrightYellow => { UniqueColor::Static(BRIGHT_YELLOW) },
306 ColorType::BrightBlue => { UniqueColor::Static(BRIGHT_BLUE) },
307 ColorType::BrightMagenta => { UniqueColor::Static(BRIGHT_MAGENTA) },
308 ColorType::BrightCyan => { UniqueColor::Static(BRIGHT_CYAN) },
309 ColorType::BrightWhite => { UniqueColor::Static(BRIGHT_WHITE) },
310 ColorType::BrightDefault => { UniqueColor::Static(BRIGHT_DEFAULT) },
311
312 ColorType::OnBlack => { UniqueColor::Static(ON_BRIGHT_BLACK) },
313 ColorType::OnRed => { UniqueColor::Static(ON_BRIGHT_RED) },
314 ColorType::OnGreen => { UniqueColor::Static(ON_BRIGHT_GREEN) },
315 ColorType::OnYellow => { UniqueColor::Static(ON_BRIGHT_YELLOW) },
316 ColorType::OnBlue => { UniqueColor::Static(ON_BRIGHT_BLUE) },
317 ColorType::OnMagenta => { UniqueColor::Static(ON_BRIGHT_MAGENTA) },
318 ColorType::OnCyan => { UniqueColor::Static(ON_BRIGHT_CYAN) },
319 ColorType::OnWhite => { UniqueColor::Static(ON_BRIGHT_WHITE) },
320 ColorType::OnDefault => { UniqueColor::Static(ON_DEFAULT) },
321
322 ColorType::OnBrightBlack => { UniqueColor::Static(ON_BLACK) },
323 ColorType::OnBrightRed => { UniqueColor::Static(ON_RED) },
324 ColorType::OnBrightGreen => { UniqueColor::Static(ON_GREEN) },
325 ColorType::OnBrightYellow => { UniqueColor::Static(ON_YELLOW) },
326 ColorType::OnBrightBlue => { UniqueColor::Static(ON_BLUE) },
327 ColorType::OnBrightMagenta => { UniqueColor::Static(ON_MAGENTA) },
328 ColorType::OnBrightCyan => { UniqueColor::Static(ON_CYAN) },
329 ColorType::OnBrightWhite => { UniqueColor::Static(ON_WHITE) },
330 ColorType::OnBrightDefault => { UniqueColor::Static(ON_BRIGHT_DEFAULT) },
331
332 ColorType::Rgb(r, g, b) => {
334 UniqueColor::Dynamic((Some(format!("38;2;{};{};{}", r, g, b)), EMPTY_MODIFIER_REFERENCE, false))
335 },
336 ColorType::OnRGB (r, g, b) => {
338 UniqueColor::Dynamic((Some(format!("48;2;{};{};{}", r, g, b)), EMPTY_MODIFIER_REFERENCE, true))
339 },
340 ColorType::Ansi(index) => {
341 UniqueColor::Dynamic((Some(format!("38;5;{}", index)), EMPTY_MODIFIER_REFERENCE, false))
342 },
343 ColorType::OnANSI (index) => {
344 UniqueColor::Dynamic((Some(format!("48;5;{}", index)), EMPTY_MODIFIER_REFERENCE, true))
345 },
346
347 ColorType::Bold => { UniqueColor::Static(BOLD) },
348 ColorType::Dim => { UniqueColor::Static(DIM) },
349 ColorType::Italic => { UniqueColor::Static(ITALIC) },
350 ColorType::Underline => { UniqueColor::Static(UNDERLINE) },
351 ColorType::Blink => { UniqueColor::Static(BLINK) },
352 ColorType::Reverse => { UniqueColor::Static(REVERSE) },
353 ColorType::Hide => { UniqueColor::Static(HIDE) },
354 }
355 }
356}
357
358pub trait Colorize {
378 fn colorizes (&self, colors: Vec <ColorType>) -> Colored;
380
381 fn colorize (&self, colors: ColorType) -> Colored;
383}
384
385impl Colorize for &str {
386 fn colorizes (&self, colors: Vec <ColorType>) -> Colored {
387 Colored::get_from_color_types_str(self, colors)
388 }
389
390 fn colorize (&self, color: ColorType) -> Colored {
391 Colored::get_from_color_types_str(self, vec![color])
392 }
393}
394
395impl Colorize for String {
396 fn colorizes (&self, colors: Vec <ColorType>) -> Colored {
397 Colored::get_from_color_types_str(self.as_str(), colors)
398 }
399
400 fn colorize (&self, color: ColorType) -> Colored {
401 Colored::get_from_color_types_str(self.as_str(), vec![color])
402 }
403}
404
405
406#[derive(Clone, Debug, Eq, PartialEq, Default, Hash)]
412pub struct Colored {
413 text: String,
414 mods: Vec <String>,
415 color: Option <String>,
416 bg_color: Option <String>,
417}
418
419impl Colorize for Colored {
420 fn colorizes (&self, colors: Vec <ColorType>) -> Colored {
421 let mut mods = vec![];
422 for modifier in colors {
423 mods.push(modifier);
424 }
425 Colored::get_from_color_types(self, mods)
426 }
427
428 fn colorize (&self, color: ColorType) -> Colored {
429 Colored::get_from_color_types(self, vec![color])
430 }
431}
432
433impl Colored {
434 pub fn new (text: String) -> Colored {
436 Colored {
437 text,
438 mods: vec![],
439 color: None,
440 bg_color: None,
441 }
442 }
443
444 pub fn split (&self, mid_point: usize) -> (Colored, Colored) {
447 (
448 Colored {
449 text: self.text[..mid_point].to_string(),
450 mods: self.mods.clone(),
451 color: self.color.clone(),
452 bg_color: self.bg_color.clone(),
453 },
454 Colored {
455 text: self.text[mid_point..].to_string(),
456 mods: self.mods.clone(),
457 color: self.color.clone(),
458 bg_color: self.bg_color.clone(),
459 }
460 )
461 }
462
463 pub fn is_uncolored (&self) -> bool {
465 self.mods.is_empty() && self.color.is_none() && self.bg_color.is_none()
466 }
467
468 pub fn contains (&self, color: &ColorType) -> bool {
470 let col = color.get_color().unwrap_into_tuple();
471 if col.0 == self.bg_color && col.2 { return true; }
472 if let Some(self_color) = &self.color {
473 if let Some(other_col) = col.0 {
474 if self_color.contains(&other_col) { return true; }
475 }
476 }
477 for modifier in col.1 {
478 if self.mods.contains(&modifier) { return true; }
479 } false
480 }
481
482 pub fn change_text (&mut self, text: String) {
484 self.text = text;
485 }
486
487 pub fn add_color (&mut self, color: ColorType) {
495 self.add_unique(color.get_color());
496 }
497
498 pub fn add_unique (&mut self, unique_color: UniqueColor) {
510 let (color, mods, background) = unique_color.unwrap_into_tuple();
511 if background { self.bg_color = color; }
512 else if let Some(col) = color {
513 self.color = Some(col);
515 }
516 for modifier in mods {
517 self.mods.push(modifier);
518 }
519 }
520
521 pub fn get_from_color_types (colored: &Colored, colors: Vec <ColorType>) -> Colored {
525 let mut colored = Colored {
526 text: colored.text.clone(),
527 mods: colored.mods.clone(),
528 color: colored.color.clone(),
529 bg_color: colored.bg_color.clone(),
530 };
531 for color in colors {
532 colored.add_color(color);
533 } colored
534 }
535
536 pub fn get_from_color_types_str (text: &str, colors: Vec <ColorType>) -> Colored {
541 let mut colored = Colored::new(text.to_owned());
542 for color in colors {
543 colored.add_color(color);
544 } colored
545 }
546
547 pub fn get_from_unique_colors (text: String, unique_colors: Vec <UniqueColor>) -> Colored {
553 let mut colored = Colored::new(text);
554 for color in unique_colors {
555 colored.add_unique(color);
556 } colored
557 }
558
559 pub fn get_text (&self, last_color: &mut String) -> (String, usize) {
563 let mut text = String::new();
564
565 let col = match &self.color {
566 Some(colr) => colr,
567 _ => &String::new()
568 };
569
570 let bg_col = match &self.bg_color {
571 Some(colr) => colr,
572 _ => &String::new()
573 };
574
575 let color = match
576 (self.bg_color.is_some(), self.color.is_some(), !self.mods.is_empty())
577 {
578 (true, true, true) => format!("\x1b[0;{};{};{}m", col, bg_col, self.mods.join(";")),
579 (true, true, false) => format!("\x1b[0;{};{}m", col, bg_col),
580 (false, true, true) => format!("\x1b[0;{};{}m", col, self.mods.join(";")),
581 (false, true, false) => format!("\x1b[0;{}m", col),
582 (true, false, true) => format!("\x1b[0;{};{}m", bg_col, self.mods.join(";")),
583 (true, false, false) => format!("\x1b[0;{}m", bg_col),
584 (false, false, _) => String::from("\x1b[0m"),
585 };
586
587 if color != *last_color {
588 text.push_str(&color);
590 *last_color = color;
591 }
592
593 text.push_str(&self.text);
594 (text, self.text.chars().count())
595 }
596
597 pub fn get_size (&self) -> usize {
599 self.text.chars().count()
600 }
601}
602
603#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
608pub struct Span {
609 line: Vec <Colored>,
610}
611
612impl Span {
613 pub fn from_tokens (tokens: Vec <Colored>) -> Self {
615 Span {
616 line: tokens,
617 }
618 }
619
620 pub fn size (&self) -> usize {
622 let mut size = 0;
623 for colored in &self.line {
624 size += colored.get_size();
625 }
626 size
627 }
628
629 pub fn join (&self) -> (String, usize) {
633 let mut last_colored = String::new();
635 let mut total = String::new();
636 let mut total_size = 0;
637 for colored in &self.line {
638 let (text, size) = colored.get_text(&mut last_colored);
639 total.push_str(&text);
640 total_size += size;
641 }
642 (total, total_size)
643 }
644}
645
646#[derive(Clone, Debug, Eq, PartialEq, Default, Hash)]
658pub struct Window {
659 pub position: (u16, u16),
660 pub depth: u16,
661 pub size: (u16, u16),
662 updated: Vec <bool>,
663 was_updated: bool,
664
665 lines: Vec <(Span, String, usize)>,
667
668 bordered: bool,
669 title: (Span, usize),
670 color: Colored,
671 pub hidden: bool,
672}
673
674type RenderClosure = Vec <(Box <dyn FnOnce () -> String + Send>, u16, u16, u16)>;
678
679impl Window {
680 pub fn new (position: (u16, u16), depth: u16, size: (u16, u16)) -> Self {
686 Window {
687 position,
688 depth,
689 size,
690 updated: vec![false; size.1 as usize],
691 was_updated: false,
692 lines: vec![],
693 bordered: false,
694 title: (Span::default(), 0),
695 color: Colored::new(String::new()), hidden: false,
697 }
698 }
699
700 pub fn hide (&mut self) -> bool {
704 if self.hidden { return false; }
705 self.hidden = true;
706 self.update_all();
707 true
708 }
709
710 pub fn show(&mut self) -> bool {
714 if !self.hidden { return false; }
715 self.hidden = false;
716 self.update_all();
717 true
718 }
719
720 pub fn r#move (&mut self, new_position: (u16, u16)) {
725 if new_position == self.position { return; }
726 self.position = new_position;
727 self.update_all();
728 }
729
730 pub fn colorizes (&mut self, colors: Vec <ColorType>) {
733 for color in colors {
734 self.color.add_color(color);
735 } self.update_all();
736 }
737
738 pub fn colorize (&mut self, color: ColorType) {
741 self.color.add_color(color);
742 self.update_all();
743 }
744
745 pub fn try_colorize (&mut self, color: ColorType) -> bool {
750 if self.color.contains(&color) { return false; }
751 self.color.add_color(color);
752 self.update_all();
753 true
754 }
755
756 pub fn clear_colors (&mut self) -> bool {
761 if self.color.is_uncolored() { return false; }
762 self.color = Colored::new(String::new());
763 self.update_all();
764 true
765 }
766
767 pub fn bordered (&mut self) {
770 self.bordered = true;
771 }
772
773 pub fn titled (&mut self, title: String) {
776 self.title = (
777 Span::from_tokens(
778 vec![title.colorizes(vec![])]),
779 title.chars().count()
780 );
781 self.was_updated = false;
782 self.updated[0] = false;
783 }
785
786 pub fn has_title (&self) -> bool {
788 self.title.1 != 0
789 }
790
791 pub fn titled_colored (&mut self, title: Span) {
793 let size = title.size();
794 self.title = (title, size);
795 self.was_updated = false;
796 self.updated[0] = false;
797 }
798
799 pub fn resize (&mut self, changed: (u16, u16)) -> bool {
803 if self.size == changed { return false; }
804 self.size = (
805 std::cmp::max(changed.0, 0),
806 std::cmp::max(changed.1, 0)
807 );
808 self.updated = vec![false; self.size.1 as usize];
809 self.update_all();
810 true
811 }
812
813 fn clamp_string_visible_utf_8 (text: &str, max_length: usize) -> String {
823 let mut accumulative: String = String::new();
824
825 let mut visible = 0;
826 let mut in_escape = false;
827 for chr in text.chars() {
828 if chr == '\x1b' {
829 in_escape = true;
830 } else if in_escape {
831 if chr == 'm' {
832 in_escape = false;
833 }
834 } else {
835 visible += 1;
836 if visible > max_length { break; }
837 }
838 accumulative.push(chr);
839 }
840
841 accumulative
842 }
843
844 pub fn render_window_slice (color: (String, usize),
854 bordered: bool,
855 render_text: (String, usize),
856 size: (u16, u16)
857 ) -> String {
858 let mut text = String::new();
859
860 let border_size = match bordered {
862 true => 2, false => 0
863 };
864 let line_text = Window::clamp_string_visible_utf_8(
865 &render_text.0, size.0 as usize - border_size
866 );
867 let line_size = std::cmp::min(render_text.1, size.0 as usize - border_size);
868
869 if bordered {
871 text.push_str(&color.0);
872 text.push('│');
873 text.push_str(CLEAR);
874 text.push_str(&line_text);
875 text.push_str(CLEAR);
876 let padding = (size.0 as usize - 2) - line_size;
877 text.push_str(&" ".repeat(padding));
878 text.push_str(&color.0);
879 text.push('│');
880 text.push_str(CLEAR);
881 } else {
882 text.push_str(&line_text);
883 text.push_str(CLEAR); let padding = (size.0 as usize) - line_size;
885 text.push_str(&" ".repeat(padding));
886 } text
887 }
888
889 fn handle_hidden_closure (&mut self, mut render_closures: RenderClosure) -> RenderClosure {
890 self.was_updated = true;
891 for i in 0..self.updated.len() {
892 if self.updated[i] { continue; }
893 self.updated[i] = true;
894 let width = self.size.0;
895 render_closures.push((Box::new(move || {
896 " ".repeat(width as usize)
897 }), self.position.0, self.position.1 + i as u16, 0)); }
899 render_closures
900 }
901
902 pub fn get_render_closure (&mut self) -> RenderClosure {
919 if self.was_updated { return vec![]; } let mut render_closures: RenderClosure = vec![];
922 if self.hidden {
923 return self.handle_hidden_closure(render_closures);
924 }
925
926 let border_color = self.color.get_text(&mut String::new());
928 self.was_updated = true;
929
930 let bordered_size = {
932 if self.bordered { 1 }
933 else { 0 }
934 };
935 let mut updated = false;
936 for index in bordered_size..self.size.1 as usize - bordered_size {
937 if self.updated[index] { continue; }
938 self.updated[index] = true;
939 updated = true;
940
941 let (text, size);
942 if index - bordered_size < self.lines.len() {
943 (text, size) = self.lines[index - bordered_size].0.join();
944 self.lines[index - bordered_size].1 = text.clone();
945 self.lines[index - bordered_size].2 = size;
946 } else {
947 (text, size) = (String::new(), 0);
948 }
949
950 let color = border_color.clone();
952 let window_size = self.size; let bordered = self.bordered;
954
955 let closure = move || {
956 Window::render_window_slice(color, bordered, (text, size), window_size)
957 };
958 render_closures.push((Box::new(closure), self.position.0, self.position.1 + index as u16, self.depth + 1));
959 }
960
961 if updated && self.bordered {
962 self.updated[self.size.1 as usize - 1] = true;
963 self.updated[0] = true;
964
965 let color = border_color.clone();
967 let window_size = self.size.0; let closure = move || { let mut text = String::new();
970 text.push_str(&color.0);
971 text.push('└');
972 text.push_str(&"─".repeat(window_size as usize - 2));
973 text.push('┘');
974 text.push_str(CLEAR);
975 text
976 };
977 render_closures.push((Box::new(closure), self.position.0, self.position.1 + self.size.1 - 1, self.depth + 1));
978
979 let color = border_color; let window_size = self.size.0; let title = self.title.clone();
983 let closure = move || {
984 let mut text = String::new();
985 text.push_str(&color.0);
986 text.push('┌');
987 let half = window_size / 2 - title.1 as u16 / 2 - 1;
988 text.push_str(&"─".repeat(half as usize));
989 text.push_str(CLEAR);
990 text.push_str(&title.0.join().0);
991 text.push_str(&color.0);
992 text.push_str(&"─".repeat(window_size as usize - 2 - half as usize - title.1));
993 text.push('┐');
994 text.push_str(CLEAR);
995 text
996 };
997 render_closures.push((Box::new(closure), self.position.0, self.position.1, self.depth + 1));
998 }
999
1000 render_closures
1001 }
1002
1003 pub fn get_render_deprecated(&self) -> Vec<String> {
1014 let mut text = vec![String::new()];
1015 let color = self.color.get_text(&mut String::new());
1016
1017 let border_size =
1019 if self.bordered {
1020 let mut line_size = 1;
1021 text[0].push_str(&color.0);
1022 text[0].push('┌');
1023 let split_size = (self.size.0 - 2) / 2 - self.title.1 as u16 / 2;
1024 line_size += split_size;
1025 text[0].push_str(&"─".repeat(split_size as usize));
1026 line_size += self.title.1 as u16;
1027 text[0].push_str(&self.title.0.join().0);
1028 text[0].push_str(&"─".repeat(
1030 (self.size.0 as usize).saturating_sub(1 + line_size as usize)
1031 ));
1032 text[0].push('┐');
1033 text[0].push_str(CLEAR);
1034 text.push(String::new());
1036 2
1037 }
1038 else { 0 };
1039 let bordered = border_size / 2;
1040 for index in bordered..self.size.1 as usize - bordered {
1041 let line_text;
1042 let line_size;
1043 if index <= self.lines.len() {
1044 let line = &self.lines[index - 1];line_text = Window::clamp_string_visible_utf_8(
1046 &line.1, self.size.0 as usize - border_size
1047 );
1048 line_size = std::cmp::min(self.lines[index - 1].2, self.size.0 as usize - border_size);
1049 } else {
1050 line_text = String::new();
1051 line_size = 0;
1052 }
1053
1054 if self.bordered {
1056 text[index].push_str(&color.0);
1057 text[index].push('│');
1058 text[index].push_str(CLEAR);
1059 text[index].push_str(&line_text);
1060 text[index].push_str(CLEAR);
1061 let padding = (self.size.0 as usize - 2) - line_size;
1062 text[index].push_str(&" ".repeat(padding));
1063 text[index].push_str(&color.0);
1064 text[index].push('│');
1065 text[index].push_str(CLEAR);
1066 } else {
1067 text[index].push_str(&line_text);
1068 let padding = (self.size.0 as usize) - line_size;
1069 text[index].push_str(&" ".repeat(padding));
1070 }
1071 text.push(String::new());
1072 }
1073
1074 let last_index = text.len() - 1;
1076 if self.bordered {
1077 text[last_index].push_str(&color.0);
1078 text[last_index].push('└');
1079 text[last_index].push_str(&"─".repeat(self.size.0 as usize - 2));
1080 text[last_index].push('┘');
1081 text[last_index].push_str(CLEAR);
1082 } else {
1083 text.pop();
1085 }
1086 text
1087 }
1088
1089 pub fn update_line (&mut self, index: usize, span: Span) {
1095 if index >= self.lines.len() { return; }
1096 self.lines[index] = (span, String::new(), 0);
1097 self.updated[index] = false;
1098 self.was_updated = false;
1099 }
1100
1101 pub fn add_line (&mut self, span: Span) {
1104 self.lines.push((span, String::new(), 0));
1105 self.updated.push(false);
1106 self.was_updated = false;
1107 }
1108
1109 pub fn from_lines (&mut self, lines: Vec <Span>) {
1117 self.lines.clear();let mut index = {
1119 if self.bordered { 1 }
1120 else { 0 }
1121 };
1122 for span in lines {
1123 self.lines.push((span, String::new(), 0));
1124 self.updated[index] = false;
1125 self.was_updated = false;
1126 index += 1;
1127 }
1128 }
1129
1130 pub fn try_update_lines (&mut self, mut lines: Vec <Span>) -> bool {
1137 if lines.len() != self.lines.len() {
1138 self.update_all(); self.was_updated = false;
1140 self.lines.clear();
1141 for (index, span) in lines.into_iter().enumerate() {
1142 if index >= self.updated.len() { break; }
1143 self.lines.push((span, String::new(), 0));
1144 }
1145 return true;
1146 }
1147 let mut index = lines.len();
1148 let bordered = {
1149 if self.bordered { 1 }
1150 else { 0 }
1151 };
1152 while let Some(span) = lines.pop() {
1153 index -= 1; if self.lines[index].0 != span {
1155 self.lines[index] = (span, String::new(), 0);
1156 self.updated[index + bordered] = false; self.was_updated = false;
1158 }
1159 } self.was_updated
1160 }
1161
1162 pub fn is_empty (&self) -> bool {
1164 self.lines.is_empty()
1165 }
1166
1167 pub fn update_all (&mut self) {
1170 for line in self.updated.iter_mut() {
1171 *line = false;
1172 }
1173 self.was_updated = false;
1174 }
1175
1176 pub fn supress_updates (&mut self) {
1179 for line in self.updated.iter_mut() {
1180 *line = true;
1181 }
1182 self.was_updated = true;
1183 }
1184}
1185
1186
1187#[derive(Clone, Debug, Eq, PartialEq, Default, Hash)]
1192pub struct Rect {
1193 pub width: u16,
1194 pub height: u16,
1195}
1196
1197#[derive(Debug, Default)]
1205pub struct App {
1206 area: Rect,
1207 active_windows: Vec <(Window, Vec <String>)>, window_references: std::collections::HashMap <String, usize>,
1209 change_window_layout: bool,
1210 updated: bool,
1211 render_handle: Option <std::thread::JoinHandle <()>>,
1212 buffer: std::sync::Arc <parking_lot::RwLock <String>>,
1213 reset_windows: bool,
1214 concluded_receiver: Option<crossbeam::channel::Receiver <()>>,
1215 pub concluded_sender: Option<crossbeam::channel::Sender <()>>,
1216}
1217
1218impl Drop for App {
1224 fn drop (&mut self) {
1225 let error = if let Some(receiver) = self.concluded_receiver.take() {
1227 if receiver.try_recv().is_err() {
1228 true
1230 } else {
1233 false
1234 }
1235 } else { false };
1236 if !error { print!("\x1B[?1049l"); }
1237
1238 event_handler::disable_mouse_capture();
1239 crossterm::terminal::disable_raw_mode().unwrap();
1240
1241 print!("{SHOW_CURSOR}"); print!("\x1B[0m");
1245 print!("\x1B[2K\x1B[E");
1246
1247 let _ = std::io::stdout().flush();
1249 }
1250}
1251
1252impl App {
1253 pub fn new () -> std::io::Result<Self> { event_handler::enable_mouse_capture();
1260 crossterm::terminal::enable_raw_mode()?;
1261
1262 print!("\x1B[0m");
1263 print!("\x1B[2K\x1B[E");
1264
1265 print!("\x1B7");
1266 print!("\x1B[?1049h");
1267 print!("\x1B[?25l");
1268
1269 let mut stdout = std::io::stdout();
1270 crossterm::execute!(stdout, crossterm::terminal::Clear(crossterm::terminal::ClearType::All))?;
1271
1272
1273 let (sender, receiver) = crossbeam::channel::unbounded();
1274 Ok(App {
1275 area: Rect::default(),
1276 active_windows: vec![],
1277 window_references: std::collections::HashMap::new(),
1278 change_window_layout: true,
1279 updated: true,
1280 render_handle: None,
1281 buffer: std::sync::Arc::new(parking_lot::RwLock::new(String::new())),
1282 reset_windows: false,
1283 concluded_receiver: Some(receiver),
1284 concluded_sender: Some(sender),
1285 })
1286 }
1287
1288 pub fn contains_window (&self, name: String) -> bool {
1295 self.window_references.contains_key(&name)
1296 }
1297
1298 pub fn get_window_reference (&self, name: String) -> &Window {
1304 &self.active_windows[self.window_references[&name]].0
1305 }
1306
1307 pub fn get_window_reference_mut (&mut self, name: String) -> &mut Window {
1313 &mut self.active_windows[self.window_references[&name]].0
1315 }
1316
1317 pub fn update_window_layout_order (&mut self) {
1321 self.change_window_layout = true;
1322 }
1324
1325 pub fn get_terminal_size (&self) -> Result <(u16, u16), std::io::Error> {
1328 crossterm::terminal::size()
1329 }
1330
1331 pub fn get_window_area (&self) -> &Rect {
1335 &self.area
1336 }
1337
1338 pub fn add_window (&mut self, window: Window, name: String, keywords: Vec <String>) {
1344 if !window.hidden { self.change_window_layout = true; } self.window_references.insert(name, self.window_references.len());
1346 self.active_windows.push((window, keywords));
1347 }
1349
1350 pub fn remove_window (&mut self, name: String) -> Result <Window, String> {
1359 self.change_window_layout = true;
1360 self.reset_windows = true;
1361 if !self.window_references.contains_key(&name) {
1364 return Err(format!("No window named '{}' found", name));
1365 }
1366 let index = *self.window_references.get(&name).unwrap_or(&usize::MAX);
1368 if index >= self.active_windows.len() {
1369 return Err(format!(
1370 "Invalid index; Accessed at {}, but the size is {}",
1371 index, self.active_windows.len()
1372 ));
1373 }
1374
1375 self.window_references.remove(&name);
1377 let mut keys_to_modify = vec![];
1378 for key in self.window_references.iter() {
1379 if *key.1 > index {
1380 keys_to_modify.push(key.0.clone());
1381 }
1382 }
1383 for key in keys_to_modify {
1384 *self.window_references.get_mut(&key).unwrap() -= 1;
1386 }
1387
1388 Ok(self.active_windows.remove(index).0)
1389 }
1390
1391 pub fn get_slice_utf_8 (text: &str, range: std::ops::Range <usize>) -> String
1395 where
1396 std::ops::Range<usize>: Iterator<Item = usize>
1397 {
1398 let mut visible = 0;
1399 let mut in_escape = false;
1400 let mut slice = String::new();
1401 for chr in text.chars() {
1402 if chr == '\x1b' {
1403 in_escape = true;
1404
1405 slice.push(chr);
1407 } else if in_escape {
1408 in_escape = chr != 'm';
1409
1410 slice.push(chr);
1412 } else {
1413 visible += 1;
1414 if visible >= range.start {
1415 if visible < range.end {
1416 slice.push(chr);
1418 continue;
1419 }
1420 return slice; }
1422 }
1423 } slice
1424 }
1425
1426 fn handle_render_window_changes (&mut self, size: &(u16, u16)) {
1431 let handle = self.render_handle.take();
1432 if let Some(handle) = handle {
1433 let _ = handle.join();
1435 }
1436
1437 self.buffer.write().clear();
1438 if size.0 != self.area.width || size.1 != self.area.height || self.reset_windows {
1439 self.reset_windows = false;
1440 *self.buffer.write() = String::with_capacity((size.0 * size.1 * 3) as usize);
1441
1442 for window in &mut self.active_windows {
1445 if window.0.hidden { continue; } window.0.update_all();
1447 }
1448
1449 print!("\x1b[2J\x1b[H"); }
1452 }
1453
1454 pub fn render (&mut self, terminal_size: Option <(u16, u16)>) -> usize {
1461 let size = terminal_size.unwrap_or(self.get_terminal_size().unwrap());
1464 self.handle_render_window_changes(&size);
1465
1466 self.area = Rect {
1467 width: size.0,
1468 height: size.1,
1469 };
1470
1471 let mut updated = false;
1474 for window in &self.active_windows {
1475 if window.0.was_updated { continue; }
1476 updated = true;
1477 break;
1478 }
1479 if !updated { return 0; }
1480
1481 let mut draw_calls = vec![];
1483
1484 for window in &mut self.active_windows {
1486 draw_calls.append(&mut window.0.get_render_closure());
1488 }
1489
1490 let num_calls = draw_calls.len();
1491
1492 let size = (self.area.width, self.area.height);
1493 let buffer = self.buffer.clone();
1494 self.render_handle = Some(std::thread::spawn(move || {
1496 draw_calls.sort_by_key(|draw_call| draw_call.2 as usize * size.0 as usize + draw_call.1 as usize + draw_call.3 as usize * size.0 as usize * size.1 as usize);
1501
1502 let write_buffer = &mut *buffer.write();
1504 for call in draw_calls {
1505 write_buffer.push_str("\x1b[");
1508 App::push_u16(write_buffer, call.2);
1509 write_buffer.push(';');
1510 App::push_u16(write_buffer, call.1);
1511 write_buffer.push('H');
1512
1513 let output = call.0();
1514 write_buffer.push_str(&output);
1515 }
1516
1517 write_buffer.push_str("\x1b[");
1519 App::push_u16(write_buffer, size.1);
1520 write_buffer.push(';');
1521 App::push_u16(write_buffer, size.0);
1522 write_buffer.push_str("H ");
1523
1524 let mut out = std::io::stdout().lock();
1526 out.write_all(write_buffer.as_bytes()).unwrap();
1527 out.flush().unwrap();
1528 }));
1529
1530 num_calls
1531
1532 }
1535
1536 pub fn push_u16 (buffer: &mut String, mut value: u16) {
1538 let mut reserved = [0u32; 5];
1539 let mut i = 0;
1540 loop {
1542 reserved[i] = (value % 10) as u32;
1543 if value < 10 { break; }
1544 value /= 10;
1545 i += 1;
1546 }
1547 for index in (0..=i).rev() {
1549 buffer.push(char::from_digit(reserved[index], 10).unwrap_or_default());
1551 }
1552 }
1553
1554 pub fn get_window_names (&self) -> Vec<&String> {
1557 let mut names = vec![];
1558 for name in self.window_references.keys() {
1559 names.push(name);
1560 } names
1561 }
1562
1563 pub fn prune_by_keywords (&mut self, keywords: Vec <String>) -> usize {
1566 let mut pruned = vec![];
1567 for (index, window) in self.active_windows.iter().enumerate() {
1568 for word in &window.1 {
1569 if keywords.contains(word) {
1570 pruned.push(index);
1572 break;
1573 }
1574 }
1575 }
1576 if pruned.is_empty() { return 0; }
1577 self.change_window_layout = true;
1578 self.reset_windows = true;
1579 self.updated = true;
1580
1581 let mut num_pruned = 0;
1582 for index in &pruned {
1584 self.prune_update(*index, &mut num_pruned);
1585 } num_pruned
1586 }
1587
1588 fn prune_update (&mut self, index: usize, num_pruned: &mut usize) {
1591 let mut to_remove = vec![];
1593 for pair in self.window_references.iter_mut() {
1594 if *pair.1 == index-*num_pruned {
1595 to_remove.push(pair.0.clone());
1596 continue;
1597 }
1598 if *pair.1 >= index-*num_pruned { *pair.1 -= 1 }
1599 }
1600 for key in to_remove {
1601 self.window_references.remove(&key);
1602 }
1603 let _ = self.active_windows.remove(index-*num_pruned);
1604 *num_pruned += 1;
1605 }
1606
1607 pub fn prune_by_key (&mut self, key: Box <dyn Fn (&Vec <String>) -> bool>) -> usize {
1611 let mut pruned = vec![];
1612 for (index, window) in self.active_windows.iter().enumerate() {
1613 if key(&window.1) {
1614 pruned.push(index);
1615 }
1616 }
1617 if pruned.is_empty() { return 0; }
1618 self.change_window_layout = true;
1619 self.reset_windows = true;
1620 self.updated = true;
1621
1622 let mut num_pruned = 0;
1623 for index in &pruned {
1625 self.prune_update(*index, &mut num_pruned);
1626 } num_pruned
1627 }
1628
1629 pub fn get_windows_by_keywords (&self, keywords: Vec <String>) -> Vec <&String> {
1632 let mut names = vec![];
1633 for name in &self.window_references {
1634 for keyword in &self.active_windows[*name.1].1 {
1635 if keywords.contains(keyword) {
1636 names.push(name.0);
1637 break;
1638 }
1639 }
1640 }
1641 names
1642 }
1643
1644 pub fn get_windows_by_keywords_non_ref (&self, keywords: Vec <String>) -> Vec <String> {
1647 let mut names = vec![];
1648 for name in &self.window_references {
1649 for keyword in &self.active_windows[*name.1].1 {
1650 if keywords.contains(keyword) {
1651 names.push(name.0.clone());
1652 break;
1653 }
1654 }
1655 } names
1656 }
1657
1658 pub fn get_windows_by_key (&self, key: Box <dyn Fn (&Vec <String>) -> bool>) -> Vec <&String> {
1662 let mut names = vec![];
1663 for name in &self.window_references {
1664 if key(&self.active_windows[*name.1].1) {
1665 names.push(name.0);
1666 }
1667 }
1668 names
1669 }
1670
1671 pub fn window_contains_keyword (&self, window_name: &String, keyword: &String) -> bool {
1673 let window_index = self.window_references[window_name];
1674 self.active_windows[window_index].1.contains(keyword)
1675 }
1676
1677 pub fn changed_window_layout (&self) -> bool {
1679 self.change_window_layout
1680 }
1681}
1682