rcli_loader/
loading_drawer.rs1use std::{collections::VecDeque, io::Write, sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock}, vec::Vec};
2
3use crate::{drawer_helper::{print_splitter_line, set_terminal_pos, LoadingColorScheme, Position}, loading_element::LoadingElement, terminal_helper::{get_terminal_size, V2Usz}};
4
5const PROGRESS_CHARS_COUNT: u8 = 8;
6static PROGRESS_CHARS: &'static [char] = &['\u{258F}', '\u{258E}', '\u{258D}', '\u{258C}', '\u{258B}', '\u{258A}', '\u{2589}', '\u{2588}'];
7static _LOADING_DRAWER: OnceLock<Mutex<LoadingDrawer>> = OnceLock::new();
8struct LoadingDrawer {
11 list: Vec<Arc<RwLock<LoadingElement>>>,
12 color_scheme: Option<Box<dyn LoadingColorScheme + Send + Sync>>,
13 print_history: VecDeque<String>,
14 loadingbar_anchor_position: Position,
15 max_history: usize,
16}
17#[allow(private_interfaces)]
18fn get_loading_drawer() -> MutexGuard<'static, LoadingDrawer> {
19 _LOADING_DRAWER.get_or_init(||
20 Mutex::new(
21 LoadingDrawer {
22 list: (Vec::new()),
23 color_scheme: None,
24 print_history: VecDeque::new(),
25 loadingbar_anchor_position: Position::BOTTOM, max_history: 50, }
28 )
29 ).lock().unwrap()
30}
31pub fn set_colorscheme(color_scheme: Box<dyn LoadingColorScheme + Send + Sync>) {
32 get_loading_drawer().color_scheme = Some(color_scheme);
33}
34pub fn set_loadingbar_anchor_position(position: Position) {
35 get_loading_drawer().loadingbar_anchor_position = position; }
37
38pub fn erase_screen() { println!("\x1B[2J");
40}
41pub fn hide_cursor() { println!("\x1b[?25l");
43}
44pub fn show_cursor() { println!("\x1b[?25h");
46}
47
48pub fn add_loading_element(l_elem: Arc<RwLock<LoadingElement>>) {
49 get_loading_drawer().list.push(l_elem);
50}
51
52
53pub fn rcli_print(print_str: String) {
54 let mut drawer: MutexGuard<'static, LoadingDrawer> = get_loading_drawer();
55 if drawer.print_history.len() > drawer.max_history { drawer.print_history.pop_back(); } drawer.print_history.push_front(print_str);
57 drop(drawer);
58 redraw_print_history();
59}
60
61pub fn redraw_print_history() {
63 let drawer: MutexGuard<'static, LoadingDrawer> = get_loading_drawer();
64 let history: &VecDeque<String> = &drawer.print_history;
65 let sz: V2Usz = get_terminal_size();
66 let pos: &Position = &drawer.loadingbar_anchor_position;
67
68 let offset: usize = match pos { Position::BOTTOM => 0,
70 Position::TOP => drawer.list.len() + 1
71 };
72 if sz.y <= drawer.list.len() + 1 { println!("\x1b[1EWindow is too small to print history\x1b[0K"); return;
75 }
76 let mut remaining_height: usize = sz.y - drawer.list.len();
77
78 print_splitter_line(&sz, match pos { Position::BOTTOM => remaining_height, Position::TOP => offset }); remaining_height -= 1;
80
81 'outer: for prt_stmnt in history.iter() { for line in prt_stmnt.lines().rev() {
84 for term_fit_line in line.as_bytes().chunks(sz.x as usize - 1).rev() { set_terminal_pos(V2Usz { x: 0, y: offset + remaining_height });
86 print!("{}\x1b[0K", std::str::from_utf8(term_fit_line).unwrap()); remaining_height -= 1;
88 if remaining_height == 0 { break 'outer; } };
90 };
91 };
92
93 std::io::stdout().flush().unwrap();
94}
95
96pub fn draw_loader() {
97 let sz: V2Usz = get_terminal_size();
98 let drawer = get_loading_drawer();
99 for (i, elem) in drawer.list.iter().enumerate() {
100 let line = match drawer.loadingbar_anchor_position {
101 Position::TOP => i+1,
102 Position::BOTTOM => sz.y as usize - i };
104 print!("\x1B[{line};{column}H", line=line, column=0);
105 let mut unused_char_count: usize = sz.x as usize - 2; let elem_l = elem.read().unwrap();
110 let progress: usize = elem_l.get_progress();
111 let max: usize = elem_l.get_max();
112 let decimal_progress: f32 = elem_l.get_progress_decimal() as f32; let name: Arc<Box<str>> = elem_l.get_name();
114
115 let progress_chunks_str: String = format!("{progress}/{max} ", progress=elem_l.format_progress_unit(progress),
118 max=elem_l.format_progress_unit(max));
119 let name_str: String = format!("{}: ", name);
120
121
122 print!("{}", name_str);
124 print!("{progress}", progress = progress_chunks_str);
125
126
127 unused_char_count -= name.len(); unused_char_count -= progress_chunks_str.len();
130
131 let pct_per_char: f32 = 1.0 / unused_char_count as f32;
133 let endchar: char = PROGRESS_CHARS[ ( (decimal_progress%pct_per_char) / pct_per_char * PROGRESS_CHARS_COUNT as f32 ) as usize ];
134 let fillchar_len: usize = (decimal_progress / pct_per_char) as usize;
135 match &drawer.color_scheme {
136 None => print!("{endchar:\u{2588}>fillchar_len$}", endchar = endchar, fillchar_len = fillchar_len ),
137 Some(x) => print!("{col_start}{endchar:\u{2588}>fillchar_len$}\x1b[0m", endchar = endchar, fillchar_len = fillchar_len, col_start = x.get_char_block_color(&elem_l))
138 }
139
140 print!("\x1B[0K"); std::io::stdout().flush().unwrap(); }
146}
147