Skip to main content

term_render/
render.rs

1#![allow(dead_code)]
2
3use std::io::Write;
4
5// literally just for enabling or disabling mouse support and stuff
6use crate::event_handler;
7use crossbeam;
8
9
10// static color/mod pairs for default ascii/ansi codes
11// colorCode (if any), mods, background (bool)   when called if background then add that color as background col
12//      if no background found, provide no such parameter
13// /033[ is the base with the ending post-fix being
14// start;color;mod;mod;mod...suffix   how do I do different colored mods? Do I add another attachment? <- correct
15// https://notes.burke.libbey.me/ansi-escape-codes/
16// https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences
17// /033[... doesn't work; use /x1b[...
18
19pub static CLEAR: &str = "\x1b[0m";
20pub static SHOW_CURSOR: &str = "\x1b[?25h";
21pub static HIDE_CURSOR: &str = "\x1b[?25l";
22
23// * color, modifiers, is_background
24pub static EMPTY_MODIFIER_REFERENCE: &[&str] = &[];  // making a default static type is annoying
25
26pub 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// manages the global state for light/dark modes (handles basic colors switching around)
76// no support for RGB/custom color codes, only the default variants
77#[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
93// hopefully this will let full usage of colors while not worrying too much about light/dark mode
94// -- (basic but limited automatic support; not everything will look perfect by default)
95static mut COLOR_MODE: ColorMode = ColorMode::Dark;
96
97
98// Different base ascii text modifiers (static constants)
99/// The different color and text modifier types available.
100/// This is a much simpler and more ergonomic way to handle colors and text modifiers
101/// compared to the `UniqueColor` or `Colored` types.
102/// However, to convert a `ColorType` into `UniqueColor`, additional
103/// logic is necessary compared to directly using `UniqueColor`.
104/// This additional overhead is minimal and generally unnoticeable in most applications.
105#[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// Stores a unique color type.
162// The unique types are either a fully static color
163// or a partially dynamic type.
164// This allows for a passing of different types,
165// circumventing lifetime issues while preserving statics.
166/// A simpler representation of a `ColorType`, reducing the many variants into
167/// a simple set of parameters which represent the raw escape codes.
168/// the pre-defined static colors provided at the top of this file are
169/// representative of the internally stored values.
170#[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    // Converts a static slice into a vector of type String
178    /// Converts a static slice of string references into a vector of owned Strings.
179    /// This is used internally to standardize the format of modifiers
180    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    // Converts the unique color into a standardized tuple form.
189    // In other words, converts the dynamic and static versions
190    // into a single unified version for easier handling
191    /// Converts the unique color into a standardized tuple form, reducing variance
192    /// in the parameters for more efficient handling.
193    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    // Converts the color type into a unique color (static or dynamic)
207    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            // 24-bit? I think so but make sure it works
258            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            // background 24-bit? Make sure that's right
266            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            // 24-bit? I think so but make sure it works
333            ColorType::Rgb(r, g, b) => {
334                UniqueColor::Dynamic((Some(format!("38;2;{};{};{}", r, g, b)), EMPTY_MODIFIER_REFERENCE, false))
335            },
336            // background 24-bit? Make sure that's right
337            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
358// Color setters for standard primitives
359
360/// Converts the given instance into the type `Colored` based on a provided
361/// set of modifiers (in the form of `ColorType`).
362/// `colorizes` adds multiple colors or modifies, while `colorize` adds a single one.
363/// By default, `Colorize` is implemented for `&str`, `String`, and `Colored`.
364/// Additionally, under the proc_macros, the `color!` macro allows for a simplistic
365/// calling of these functions.
366/// For example, the following are equivalent:
367/// ```
368/// "Hello, world!".colorizes(vec![]);
369/// "Hello, world!".colorize(ColorType::Red);
370/// "Hello, world!".colorizes(vec![ColorType::Red, ColorType::Bold]);
371/// // and
372/// color!("Hello, world!");  // If zero modifiers are provided, it creates a `Colored` instance without any modifiers.
373/// color!("Hello, world!", Red);  // If zero modifiers are provided, it creates a `Colored` instance without any modifiers.
374/// color!("Hello, world!", Red, Bold);  // `ColorType::` is automatically added
375/// // where &str can be any `Colorize` implementor (e.g. String, Colored, or custom adaptations)
376/// ```
377pub trait Colorize {
378    // adds a set of modifiers/colors
379    fn colorizes (&self, colors: Vec <ColorType>) -> Colored;
380
381    // adds a single modifier/color
382    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// A colored string
407// It stores all of its modifiers like colors/underlying/other
408/// Represents a string with associated color and text modifiers.
409/// Multiple of these color text tokens can be combined into text
410/// spans or even into bigger blocks of stylized text.
411#[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    /// Creates a new Colored instance with a given text string, and no active modifiers or applied colors.
435    pub fn new (text: String) -> Colored {
436        Colored {
437            text,
438            mods: vec![],
439            color: None,
440            bg_color: None,
441        }
442    }
443
444    /// returns the left and right halves as unique Colored instances with the
445    /// same modifiers, background color, and main color still applied.
446    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    /// Checks if the Colored instance has no colors or modifiers applied.
464    pub fn is_uncolored (&self) -> bool {
465        self.mods.is_empty() && self.color.is_none() && self.bg_color.is_none()
466    }
467
468    /// Checks if the given color type is contained within the current Colored instance.
469    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    /// Changes the underlying text for the Colored text token.
483    pub fn change_text (&mut self, text: String) {
484        self.text = text;
485    }
486
487    // Adds a color type
488    /// Adds a `ColorType` to the current `Colored` instance.
489    /// This function converts the `ColorType` into a `UniqueColor`
490    /// and then calls `add_unique` to update the `Colored` instance.
491    /// Other member functions such as `add_unique` allow for
492    /// a direct assignment of a `UniqueColor`, which is a lower level representation,
493    /// but is typically unnecessary as the performance overhead is minimal.
494    pub fn add_color (&mut self, color: ColorType) {
495        self.add_unique(color.get_color());
496    }
497
498    // Adds a unique color
499    /// Adds a `UniqueColor` to the current `Colored` instance.
500    /// This function updates the color, background color, and modifiers
501    /// of the `Colored` instance based on the provided `UniqueColor`.
502    /// If the `UniqueColor` specifies a background color, it will overwrite
503    /// the existing background color. If it specifies a text color, it will
504    /// overwrite the existing text color only if it is not `None`.
505    /// Modifiers are appended to the existing list of modifiers.
506    /// Typically, other member functions such as `add_color` allow for
507    /// the adding a `ColorType`, which is a higher level abstraction
508    /// that is converted into a `UniqueColor` internally.
509    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            // making sure to not overwrite the existing color if this is None
514            self.color = Some(col);
515        }
516        for modifier in mods {
517            self.mods.push(modifier);
518        }
519    }
520
521    // Takes a set of color types and returns a filled out Colored instance
522    /// Given an existing Colored instance and a set of colors, generates a new Colored instance
523    /// with the specified colors applied on top of the existing ones.
524    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    // Takes a set of color types and returns a filled out Colored instance
537    /// Given a set of colors and a text string, generates a Colored instance with the specified colors applied.
538    /// This is a convenience function for quickly creating colored text without needing to manually
539    /// instantiate a Colored object first.
540    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    // Takes a set of unique colors and generates a filled out instance
548    /// A `UniqueColor` is a lower level representation of the color/modifier/background.
549    /// Usually, a ColorType is used to generate a UniqueColor. However, this function
550    /// allows for a direct assignment to avoid the extra computation if performance is a concern.
551    /// If the absolute best performance isn't needed, then using ColorType is recommended due to its simplicity.
552    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    /// Applies the specified colors to the specified text and returns the resulting string.
560    /// The `last_color` parameter is used to optimize the output by avoiding redundant color escape codes.
561    /// Returns the generated text and the character count of the text (not including escape codes).
562    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(CLEAR);
589            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    /// Gets the total character count of the word.
598    pub fn get_size (&self) -> usize {
599        self.text.chars().count()
600    }
601}
602
603// A colored span of text (fancy string)
604/// A colored span of text, consisting of multiple `Colored` segments.
605/// This allows for more complex text rendering with different colors and styles
606/// within a single line or span of text.
607#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
608pub struct Span {
609    line: Vec <Colored>,
610}
611
612impl Span {
613    /// Generates a span from a given vector of `Colored` segments.
614    pub fn from_tokens (tokens: Vec <Colored>) -> Self {
615        Span {
616            line: tokens,
617        }
618    }
619
620    /// Gets the total character count of the span (not including escape codes).
621    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    /// Joins the colored segments into a single string, applying necessary color codes.
630    /// Returns the combined string and its total character count (the actual character count, not
631    /// including the characters consumed by escape codes).
632    pub fn join (&self) -> (String, usize) {
633        //let mut lastColored = vec![];
634        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// Similar to a paragraph in Ratatui
647// Windows are a block or section within the terminal space
648// Multiple windows can be rendered at once
649// Each window can contain its own text or logic
650// This allows a separation/abstraction for individual sections
651// This also allows for a cached window to be reused if temporarily closed
652
653/// A window is a block or section within the terminal space, similar to a paragraph in Ratatui.
654/// Multiple windows can be rendered at once, and each window can contain its own text or logic.
655/// Each widow can be moved, resized, hidden, shown, and colored independently.
656/// Additionally, each window handles its own rendering logic, caching, and other behaviors.
657#[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    // (Span, cached render, num visible chars)
666    lines: Vec <(Span, String, usize)>,
667
668    bordered: bool,
669    title: (Span, usize),
670    color: Colored,
671    pub hidden: bool,
672}
673
674/// A type representing a closure that returns a String when called.
675/// The type is Send so that it can be sent into a background thread for rendering.
676/// The render closures are used to handle the rendering of windows in a background thread.
677type RenderClosure = Vec <(Box <dyn FnOnce () -> String + Send>, u16, u16, u16)>;
678
679impl Window {
680    /// Creates a new window at the given position with the given size.
681    /// The position is given as (x, y) coordinates.
682    /// The size is given as (width, height).
683    /// The depth is used to determine the rendering order of windows.
684    /// Windows with a higher depth are rendered on top of windows with a lower depth.
685    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()),  // format!("\x1b[38;2;{};{};{}m", 125, 125, 0),//String::new(),
696            hidden: false,
697        }
698    }
699
700    /// Hides the window. Returns true if the window was visible before.
701    /// Returns false if the window was already hidden.
702    /// Additionally, this will only mark the window to update if it was visible before.
703    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    /// Makes the window visible. Returns true if the window was hidden before.
711    /// Returns false if the window was already visible.
712    /// Additionally, this will only mark the window to update if it was hidden before.
713    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    /// Tries to move the window to a new position.
721    /// The window is only updated if the position is different from before.
722    /// If the position is the same as before, nothing happens and the window is not marked
723    /// for an update.
724    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    /// Applies multiple colors to the window.
731    /// This will always mark the window to update.
732    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    /// Adds a color to the window.
739    /// This will always mark the window to update.
740    pub fn colorize (&mut self, color: ColorType) {
741        self.color.add_color(color);
742        self.update_all();
743    }
744
745    /// Tries to add a color to the window.
746    /// Returns true if the color was added.
747    /// Returns false if the color was already present.
748    /// Additionally, this will only mark the window to update if the color was added.
749    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    /// Clears all colors and modifiers from the window.
757    /// Returns true if any colors or modifiers were cleared.
758    /// Returns false if the window was already uncolored.
759    /// Additionally, this will only mark the window to update if any colors or modifiers were cleared
760    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    // Adds a border around the window/block
768    /// Adds a border around the window.
769    pub fn bordered (&mut self) {
770        self.bordered = true;
771    }
772
773    // Sets/updates the title of the window/block
774    /// Sets or updates the title of the window.
775    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        //self.color.ChangeText(title);
784    }
785
786    /// Returns if the window has a title set
787    pub fn has_title (&self) -> bool {
788        self.title.1 != 0
789    }
790
791    /// Provides a title with color and modifiers.
792    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    /// Tries to resize the window. Returns true if the size was changed.
800    /// Returns false if the size is the same as before.
801    /// Additionally, this will only mark the window to update if the size was changed.
802    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    // Clamps a string to a maximum length of visible UTF-8 characters while preserving escape codes
814    /// Clamps a string a maximum length of visible UTF-8 characters while preserving ANSI escape codes.
815    /// This function iterates through the characters of the input string, counting only the visible characters
816    /// and ignoring the characters that are part of ANSI escape codes. When the count of visible characters
817    /// exceeds the specified maximum length, the function stops adding characters to the output string.
818    /// The resulting string contains the original text up to the maximum visible length,
819    /// along with any necessary ANSI escape codes to maintain the original formatting.
820    /// Note: This function assumes that the input string is valid UTF-8 and that ANSI escape codes
821    /// are well-formed (i.e., they start with `\x1b` and end with `m`).
822    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    /// Gets the raw string for a given line index.
845    /// This function handles the rendering of a single line of the window,
846    /// including borders and padding as necessary.
847    /// The function takes in the color for the border, whether the window is bordered,
848    /// the text to render, and the size of the window.
849    /// It returns the fully formatted string for that line, including any ANSI escape codes.
850    /// Note: This function does not handle cursor movement or positioning.
851    /// That is handled externally when rendering the window.
852    /// This function is called from within the closure provided by `get_render_closure`.
853    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 line = &self.lines[index - 1];//self.lines[0..self.size.1 as usize - borderSize][0];
861        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        // handling the side borders
870        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);  // making sure the following are blank
884            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));  // the depth is 0, right?
898        }
899        render_closures
900    }
901
902    /// Returns a vector of closures which are responsible for returning the stylized and formatted
903    /// text for rendering the window. These closures are executed in a background thread
904    /// to allow for non-blocking rendering of the window. This is the newer version of
905    /// `get_render_deprecated`, which is mostly deprecated in favor of this function.
906    /// This function checks if the window has been updated since the last render.
907    /// If the window has not been updated, it returns an empty vector, indicating that
908    /// no re-rendering is needed. If the window is hidden, it returns closures that render
909    /// blank spaces for each line of the window.
910    /// If the window is visible and has been updated, it generates closures for each
911    /// line of the window, handling borders and titles as necessary.
912    /// Each closure, when executed, will return the rendered text for that line,
913    /// including any ANSI escape codes for colors and styles.
914    /// The closures are returned along with their respective positions and depths
915    /// for rendering in the terminal.
916    /// Note: The closures capture the necessary variables by value to ensure
917    /// they can be executed independently in a background thread.
918    pub fn get_render_closure (&mut self) -> RenderClosure {
919        if self.was_updated {  return vec![];  }  // no re-rendering is needed
920
921        let mut render_closures: RenderClosure = vec![];
922        if self.hidden {
923            return self.handle_hidden_closure(render_closures);
924        }
925
926        // these will need to be sorted by row, and the cursor movement is handled externally (the u16 pair)
927        let border_color = self.color.get_text(&mut String::new());
928        self.was_updated = true;
929
930        // make sure to not call UpdateRender when using closures
931        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            // creating the closure
951            let color = border_color.clone();
952            let window_size = self.size;  // idk a better way to do this other than cloning
953            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            // adding the top and bottom lines to the closures
966            let color = border_color.clone();
967            let window_size = self.size.0;  // idk a better way to do this other than cloning
968            let closure = move || {  // top
969                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            // bottom
980            let color = border_color;  // consuming border color here
981            let window_size = self.size.0;  // idk a better way to do this other than cloning
982            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    // Gets the rendered text for the individual window
1004    // This shouldn't crash when rendering out of bounds unlike certain other libraries...
1005    /// Gets the rendered text for the individual window as a vector of strings, one for each line.
1006    /// This function handles the rendering of borders, titles, and text within the window.
1007    /// It ensures that the text fits within the specified size of the window,
1008    /// clamping lines as necessary and adding padding to shorter lines.
1009    /// The rendered text includes ANSI escape codes for colors and styles.
1010    /// This is mostly deprecated in favor of using `get_render_closure` for better performance
1011    /// and to allow for background rendering in a separate thread. This is no longer internally used,
1012    /// so any applications of it would need to be a manual call for a reason beyond the base rendering.
1013    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        // handling the top border
1018        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                //let lineSize = text[0].len();
1029                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[0].push('\n');  // fix this
1035                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];//self.lines[0..self.size.1 as usize - borderSize][0];
1045                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            // handling the side borders
1055            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        // handling the bottom border
1075        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            // removing the last \n
1084            text.pop();
1085        }
1086        text
1087    }
1088
1089    // Replaces a single line with an updated version
1090    /// Updates a specific line in the window with a new `Span`.
1091    /// The line is identified by its index (0-based).
1092    /// If the index is out of bounds, the function does nothing.
1093    /// The updated line is marked as needing to be re-rendered.
1094    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    // Appends a single line to the window
1102    /// Appends a new line to the window, and marks it as needing to be updated.
1103    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    // Takes a vector of type Span
1110    // That Span replaces the current set of lines for the window
1111    /// Fills the window with a new set of lines, replacing any existing lines.
1112    /// This function clears the current lines and adds the new lines from the provided vector.
1113    /// Each line is initialized with an empty cached render and a size of 0.
1114    /// The `updated` vector is also updated to match the new number of lines,
1115    /// marking each line as needing an update.
1116    pub fn from_lines (&mut self, lines: Vec <Span>) {
1117        self.lines.clear();// self.updated.clear();
1118        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    // checks to see if any lines need to be updated
1131    /// Tries to update each line in the window based on the provided vector of `Span`.
1132    /// If the number of lines is different from the current number of lines in the window,
1133    /// the window is fully updated and all lines are replaced.
1134    /// If the number of lines is the same, only the lines that have changed are updated.
1135    /// The function returns true if any lines were updated, and false otherwise.
1136    pub fn try_update_lines (&mut self, mut lines: Vec <Span>) -> bool {
1137        if lines.len() != self.lines.len() {
1138            self.update_all();  // making sure every line gets updated (incase it was shrunk)
1139            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;  // the pop already subtracted one
1154            if self.lines[index].0 != span {
1155                self.lines[index] = (span, String::new(), 0);
1156                self.updated[index + bordered] = false;  // it was as easy as adding a plus 1....... me sad
1157                self.was_updated = false;
1158            }
1159        } self.was_updated
1160    }
1161
1162    /// Returns whether the window has no lines.
1163    pub fn is_empty (&self) -> bool {
1164        self.lines.is_empty()
1165    }
1166
1167    /// Updates all the lines in the window.
1168    /// This marks all lines as needing an update, forcing a re-render.
1169    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    /// Suppresses updates for all lines in the window.
1177    /// This marks all lines as updated, preventing any re-rendering.
1178    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// the main window/application that handles all the windows
1188/// A basic Rectangle structure to represent width and height dimensions.
1189/// This is used for defining the overall area available for rendering windows
1190/// and managing their layout within the terminal.
1191#[derive(Clone, Debug, Eq, PartialEq, Default, Hash)]
1192pub struct Rect {
1193    pub width: u16,
1194    pub height: u16,
1195}
1196
1197// the main application. It stores and handles the active windows
1198// It also handles rendering the cumulative sum of the windows
1199/// The main application for rendering and managing windows in the terminal.
1200/// It stores and handles the active windows, manages their layout,
1201/// and handles rendering the cumulative output of all windows.
1202/// The App struct also manages terminal state, including entering and exiting
1203/// raw mode, enabling mouse capture, and cleaning up the terminal on exit.
1204#[derive(Debug, Default)]
1205pub struct App {
1206    area: Rect,
1207    active_windows: Vec <(Window, Vec <String>)>,  // window, mods
1208    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
1218/// Cleans up the terminal state when the App instance is dropped.
1219/// This includes disabling mouse capture, exiting raw mode, showing the cursor,
1220/// and clearing the terminal screen. This ensures that the terminal is returned
1221/// to a normal state after the application exits, preventing any lingering effects
1222/// such as hidden cursors or altered screen buffers.
1223impl Drop for App {
1224    fn drop (&mut self) {
1225        // should prevent clearing the screen if an error was thrown
1226        let error = if let Some(receiver) = self.concluded_receiver.take() {
1227            if receiver.try_recv().is_err() {
1228                // an error was thrown
1229                true
1230                // preventing clearing instead of sleeping for a much better experience
1231                //std::thread::sleep(std::time::Duration::from_secs_f64(5.));
1232            } 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}");  // showing the cursor
1242
1243        // clearing the screen
1244        print!("\x1B[0m");
1245        print!("\x1B[2K\x1B[E");
1246
1247        // I don't really care if an error is thrown at this point
1248        let _ = std::io::stdout().flush();
1249    }
1250}
1251
1252impl App {
1253    /// Creates a new instance of the renderer application.
1254    /// This function initializes the terminal in raw mode, enables mouse capture,
1255    /// and sets up the alternate screen buffer. It also clears the terminal screen
1256    /// and hides the cursor. The function returns a Result containing the new App instance
1257    /// or an error if any of the terminal operations fail.
1258    pub fn new () -> std::io::Result<Self> {  // 1049h
1259        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    /// Checks if a window with the given name exists.
1289    /// This function allows for checking the existence of a window by its name.
1290    /// It returns true if the window exists, and false otherwise.
1291    /// This can be used before calling `get_window_reference` or `get_window_reference_mut`
1292    /// to avoid runtime panics and to ensure a window is created (the `Widget` abstractions can
1293    /// also offer an extra layer of safety ensuring the given window exists).
1294    pub fn contains_window (&self, name: String) -> bool {
1295        self.window_references.contains_key(&name)
1296    }
1297
1298    /// Gets a reference to the window with the given name.
1299    /// This function allows for reading the properties of the specified window.
1300    /// It assumes that the window with the given name exists; if it does not, it
1301    /// will panic. Ensure to check for the window's existence using `contains_window`
1302    /// before calling this function to avoid potential runtime errors.
1303    pub fn get_window_reference (&self, name: String) -> &Window {
1304        &self.active_windows[self.window_references[&name]].0
1305    }
1306
1307    /// Gets a mutable reference to the window with the given name.
1308    /// This function allows for modifying the properties of the specified window.
1309    /// It assumes that the window with the given name exists; if it does not, it
1310    /// will panic. Ensure to check for the window's existence using `contains_window`
1311    /// before calling this function to avoid potential runtime errors.
1312    pub fn get_window_reference_mut (&mut self, name: String) -> &mut Window {
1313        //self.updated = true;  // assuming something is being changed
1314        &mut self.active_windows[self.window_references[&name]].0
1315    }
1316
1317    /// Marks that the window layout has changed and needs to be re-evaluated.
1318    /// This function sets the internal flag to indicate that the layout of windows
1319    /// has changed, which will trigger a re-rendering of the windows on the next update
1320    pub fn update_window_layout_order (&mut self) {
1321        self.change_window_layout = true;
1322        //self.updated = true;
1323    }
1324
1325    /// Gets the current terminal size as (width, height).
1326    /// This function returns a Result containing the terminal size or an error if it fails to retrieve it.
1327    pub fn get_terminal_size (&self) -> Result <(u16, u16), std::io::Error> {
1328        crossterm::terminal::size()
1329    }
1330
1331    // Gets the current window size and position
1332    // This returns a reference to this instance
1333    /// Returns the last recorded terminal area as a `Rect`.
1334    pub fn get_window_area (&self) -> &Rect {
1335        &self.area
1336    }
1337
1338    // Adds a new active window
1339    /// Adds a new active window to the application.
1340    /// The window is identified by a unique name and can be associated with keywords for searching or
1341    /// categorization. If the window is not hidden, it will trigger a layout change.
1342    /// The function updates the internal state to reflect the addition of the new window.
1343    pub fn add_window (&mut self, window: Window, name: String, keywords: Vec <String>) {
1344        if !window.hidden {  self.change_window_layout = true;  }  // if the window is hidden, it shouldn't change anything
1345        self.window_references.insert(name, self.window_references.len());
1346        self.active_windows.push((window, keywords));
1347        //self.updated = true;
1348    }
1349
1350    // Pops an active window.
1351    // Returns Ok(window) if the index is valid, or Err if out of bounds
1352    /// Removes a window by its name.
1353    /// Returns `Ok(Window)` if the window was found and removed successfully,
1354    /// or `Err(String)` if no window with the specified name exists.
1355    /// This function also updates the internal state to reflect the removal,
1356    /// including adjusting the indices of remaining windows and marking the layout
1357    /// as changed.
1358    pub fn remove_window (&mut self, name: String) -> Result <Window, String> {
1359        self.change_window_layout = true;
1360        self.reset_windows = true;
1361        //self.updated = true;
1362
1363        if !self.window_references.contains_key(&name) {
1364            return Err(format!("No window named '{}' found", name));
1365        }
1366        // the None case would produce a value too large, so it should throw the expected error
1367        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        // updating the references list
1376        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            // all the keys should still exist
1385            *self.window_references.get_mut(&key).unwrap() -= 1;
1386        }
1387
1388        Ok(self.active_windows.remove(index).0)
1389    }
1390
1391    /// Gathers the specified range of the string while accounting for non-visible
1392    /// UTF-8 character escape codes. Instead of each byte being a character, the characters
1393    /// are determined based on character boundaries and escape code sequences.
1394    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                // making sure to keep the initial escape codes
1406                slice.push(chr);
1407            } else if in_escape {
1408                in_escape = chr != 'm';
1409
1410                // making sure to keep the initial escape codes
1411                slice.push(chr);
1412            } else {
1413                visible += 1;
1414                if visible >= range.start {
1415                    if visible < range.end {
1416                        // adding the element to the slice
1417                        slice.push(chr);
1418                        continue;
1419                    }
1420                    return slice;  // no need to continue
1421                }
1422            }
1423        } slice
1424    }
1425
1426    /// Handles any changes needed for rendering the windows, such as resizing or resetting.
1427    /// This function checks if the terminal size has changed or if a reset is needed,
1428    /// and updates the internal buffer and window states accordingly.
1429    /// It also ensures that any previous rendering threads are properly joined before starting a new one.
1430    fn handle_render_window_changes (&mut self, size: &(u16, u16)) {
1431        let handle = self.render_handle.take();
1432        if let Some(handle) = handle {
1433            // if an error was thrown, I don't care anymore
1434            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            // making sure the windows get updated
1443            //self.updated = true;
1444            for window in &mut self.active_windows {
1445                if window.0.hidden {  continue;  }  // hidden windows don't need re-rendering
1446                window.0.update_all();
1447            }
1448            
1449            // replace with an actual clear..... this doesn't work (it just shifts the screen--or does it???)
1450            print!("\x1b[2J\x1b[H");  // re-clearing the screen (everything will need to update)
1451        }
1452    }
1453
1454    // Renders all the active windows to the consol
1455    // It also clears the screen from previous writing
1456    /// Renders all the active windows to the console.
1457    /// Optionally, the terminal size can be provided to avoid recalculating it.
1458    /// Returns the number of draw calls made during the render (useful for debugging
1459    /// lazy rendering and other optimizations).
1460    pub fn render (&mut self, terminal_size: Option <(u16, u16)>) -> usize {
1461        // incase the size is needed and thus calculated elsewhere (to prevent recalculation which is slow)
1462        // (aka I'm too lazy to update the code I already made.....)
1463        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        // only re-rendering on updates (otherwise the current results are perfectly fine)
1472        // this should reduce CPU usage by a fair bit and allow a fast refresh rate if needed
1473        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        // stores the draw calls
1482        let mut draw_calls = vec![];
1483
1484        // going through the sorted windows
1485        for window in &mut self.active_windows {
1486            //let window = &mut self.activeWindows[*index];
1487            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        //println!("Num calls: {}", drawCalls.len());
1495        self.render_handle = Some(std::thread::spawn(move || {
1496            // the buffer for the render string
1497
1498            // sorting the calls by action row (and left to right for same row calls)
1499            // drawCall.3 is the depth; higher numbers will be rendered last thus being on top (each depth is a unique layer)
1500            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            // iterating through the calls (consuming drawCalls)
1503            let write_buffer = &mut *buffer.write();
1504            for call in draw_calls {
1505                // moving the cursor into position
1506                // ESC[{line};{column}H
1507                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            // moving the cursor to the bottom right
1518            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            // rendering the buffer
1525            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        //let elapsed = start.elapsed();
1533        //panic!("Render thread completed in {:?}", elapsed);
1534    }
1535
1536    /// Takes an u16 value and pushes the text form of it in an efficient manner.
1537    pub fn push_u16 (buffer: &mut String, mut value: u16) {
1538        let mut reserved = [0u32; 5];
1539        let mut i = 0;
1540        //println!(": {}", value);
1541        loop {
1542            reserved[i] = (value % 10) as u32;
1543            if value < 10 {  break;  }
1544            value /= 10;
1545            i += 1;
1546        }
1547        //println!("[{}, {}; {:?}]", value, i, reserved);
1548        for index in (0..=i).rev() {
1549            //println!("({:?}, {})", char::from_digit(reserved[index], 10), reserved[index]);
1550            buffer.push(char::from_digit(reserved[index], 10).unwrap_or_default());
1551        }
1552    }
1553
1554    /// Returns a vector of references to the window names.
1555    /// References are being used to prevent unnecessary clones.
1556    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    /// Prunes all windows which contain one of the specified keywords.
1564    /// Returns the number of windows pruned.
1565    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                    //println!("\n:{:?}::", (index, word));
1571                    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        // pruned should be in ascending order
1583        for index in &pruned {
1584            self.prune_update(*index, &mut num_pruned);
1585        } num_pruned
1586    }
1587
1588    /// Removes a given window while also updating the indexes of all other windows
1589    /// which were shifted in the vector.
1590    fn prune_update (&mut self, index: usize, num_pruned: &mut usize) {
1591        // shifting all the indexes
1592        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    /// Prunes all windows based on a given key (closure).
1608    /// Returns the number of windows pruned.
1609    /// If the closure returns true, the element is pruned. If it returns false it's kept.
1610    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        // pruned should be in ascending order
1624        for index in &pruned {
1625            self.prune_update(*index, &mut num_pruned);
1626        } num_pruned
1627    }
1628
1629    /// Gets the names to all windows which contain at least one of the
1630    /// specified keywords in the form of a reference to an owned `String`.
1631    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    /// Gets the names to all windows which contain at least one of the
1645    /// specified keywords. This version returns owned Strings instead of references.
1646    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    /// Gets the names to all windows which satisfy the given key (closure).
1659    /// If the closure returns true, the name is provided. Otherwise, it's
1660    /// considered unrelated.
1661    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    /// Checks if a given window contains a specific keyword.
1672    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    /// Returns whether the window layout has changed since the last call to render.
1678    pub fn changed_window_layout (&self) -> bool {
1679        self.change_window_layout
1680    }
1681}
1682