1use std::{
4 collections::HashMap,
5 fmt::{self, Display},
6 io::stdout,
7};
8
9use crate::loading_bar::LoadingBar;
10pub use auto_run::{TextLoadingBarAutoOptions, TextLoadingBarAutoPoint};
11pub use colored::Color;
12
13use colored::Colorize;
14use crossterm::{
15 cursor::{MoveTo, RestorePosition, SavePosition},
16 execute,
17 style::Print,
18 terminal::{Clear, ClearType},
19};
20
21pub enum TextLoadingBarOptions {
22 Text(String),
23 Color(Option<Color>),
24 Number(u16),
25 Float(f32),
26 Pos(u16, u16),
27 None,
28}
29
30impl TextLoadingBarOptions {
31 fn get_text(&self) -> &str {
32 match self {
33 TextLoadingBarOptions::Text(text) => text,
34 _ => "",
35 }
36 }
37
38 fn get_color(&self) -> Option<Color> {
39 match self {
40 TextLoadingBarOptions::Color(color) => *color,
41 _ => None,
42 }
43 }
44
45 fn get_number(&self) -> u16 {
46 match self {
47 TextLoadingBarOptions::Number(number) => *number,
48 _ => 0,
49 }
50 }
51
52 fn get_float(&self) -> f32 {
53 match self {
54 TextLoadingBarOptions::Float(float) => *float,
55 _ => 0.0,
56 }
57 }
58
59 fn get_pos(&self) -> (u16, u16) {
60 match self {
61 TextLoadingBarOptions::Pos(x, y) => (*x, *y),
62 _ => (0, 0),
63 }
64 }
65}
66
67#[derive(Debug)]
71struct TextItem {
72 text: String,
73 color: Option<colored::Color>,
74}
75
76#[derive(Debug)]
80pub struct TextLoadingBar {
81 top_text: TextItem,
82 bottom_text: TextItem,
83 t_bar: LoadingBar,
84 t_start_pos: (u16, u16),
85}
86
87impl Display for TextLoadingBar {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 write!(
90 f,
91 "{}\n{}\n{}",
92 self.top_text
93 .text
94 .color(self.top_text.color.unwrap_or(colored::Color::White)),
95 self.t_bar,
96 self.bottom_text
97 .text
98 .color(self.bottom_text.color.unwrap_or(colored::Color::White)) )
100 }
101}
102
103impl TextLoadingBar {
104 pub fn new(
105 top_text: String,
106 bottom_text: String,
107 len: u16,
108 color: (
109 Option<colored::Color>, Option<colored::Color>, Option<colored::Color>, ),
113 t_start_pos: (u16, u16),
114 ) -> TextLoadingBar {
115 let (top_text_color, bar_color, bottom_text_color) = color;
116 TextLoadingBar {
117 top_text: TextItem {
118 text: top_text,
119 color: top_text_color,
120 },
121 t_bar: LoadingBar::new(len, bar_color, (0, 0)),
122 bottom_text: TextItem {
123 text: bottom_text,
124 color: bottom_text_color,
125 },
126 t_start_pos,
127 }
128 }
129
130 fn goline_clear_print(&self) {
131 let line_count = get_num_lines_witdh(&self.to_string());
132
133 let (x, y) = self.t_start_pos;
134 let mut y_copy = y;
135 execute!(stdout(), SavePosition).expect("\x07failed to save position\x07");
136 for _ in 0..line_count {
137 execute!(stdout(), MoveTo(x, y_copy)).expect("\x07failed to move cursor\x07");
138 execute!(stdout(), Clear(ClearType::UntilNewLine)).expect("\x07failed to clear\x07");
139 execute!(stdout(), RestorePosition).expect("\x07failed to restore cursor\x07");
140 y_copy += 1;
141 }
142
143 let text = self.to_string();
144 let text = text.split('\n').collect::<Vec<&str>>();
145
146 y_copy = y;
147 for i in 0..line_count {
148 let texts = &text[i as usize];
149 execute!(stdout(), MoveTo(x, y_copy)).expect("\x07failed to move cursor\x07");
150 execute!(stdout(), Print(texts)).expect("\x07failed to print\x07");
151 execute!(stdout(), RestorePosition).expect("\x07failed to restore cursor\x07");
152 y_copy += 1;
153 }
154 }
155
156 pub fn print(&self) {
157 self.goline_clear_print();
158 }
159
160 pub fn change_pos(&mut self, t_start_pos: (u16, u16)) {
161 let line_count = get_num_lines_witdh(&self.to_string());
163
164 let (x, y) = self.t_start_pos;
165 let mut y_copy = y;
166 execute!(stdout(), SavePosition).expect("\x07failed to save position\x07");
167 for _ in 0..line_count {
168 execute!(stdout(), MoveTo(x, y_copy)).expect("\x07failed to move cursor\x07");
169 execute!(stdout(), Clear(ClearType::UntilNewLine)).expect("\x07failed to clear\x07");
170 execute!(stdout(), RestorePosition).expect("\x07failed to restore cursor\x07");
171 y_copy += 1;
172 }
173
174 self.t_start_pos = t_start_pos;
175 }
176
177 pub fn change_pos_print(&mut self, t_start_pos: (u16, u16)) {
178 self.change_pos(t_start_pos);
179 self.print();
180 }
181
182 pub fn change_top_text(&mut self, text: String) {
183 self.top_text.text = text;
184 }
185
186 pub fn change_bottom_text(&mut self, text: String) {
187 self.bottom_text.text = text;
188 }
189
190 pub fn change_top_text_color(&mut self, color: Option<Color>) {
191 self.top_text.color = color;
192 }
193
194 pub fn change_bottom_text_color(&mut self, color: Option<Color>) {
195 self.bottom_text.color = color;
196 }
197
198 pub fn change_bar_color(&mut self, color: Option<Color>) {
199 self.t_bar.color = color;
200 }
201
202 pub fn change_all_text_colors(&mut self, color: Option<Color>) {
203 self.top_text.color = color;
204 self.bottom_text.color = color;
205 self.t_bar.color = color;
206 }
207
208 pub fn advance(&mut self) {
209 self.t_bar.advance();
210 }
211
212 pub fn advance_print(&mut self) {
213 self.advance();
214 self.goline_clear_print();
215 }
216
217 pub fn advance_by(&mut self, index: u16) {
218 self.t_bar.advance_by(index);
219 }
220
221 pub fn advance_by_print(&mut self, index: u16) {
222 self.advance_by(index);
223 self.goline_clear_print();
224 }
225
226 pub fn advance_by_percent(&mut self, percent: f32) {
227 self.t_bar.advance_by_percent(percent);
228 }
229
230 pub fn advance_by_percent_print(&mut self, percent: f32) {
231 self.advance_by_percent(percent);
232 self.goline_clear_print();
233 }
234
235 pub fn change(&mut self, map: HashMap<&str, TextLoadingBarOptions>, print: bool) {
237 for (key, value) in map {
238 match key {
239 "top_text" => {
240 self.change_top_text(value.get_text().to_string());
241 }
242 "bottom_text" => {
243 self.change_bottom_text(value.get_text().to_string());
244 }
245 "top_text_color" => {
246 self.change_top_text_color(value.get_color());
247 }
248 "bottom_text_color" => {
249 self.change_bottom_text_color(value.get_color());
250 }
251 "bar_color" => {
252 self.change_bar_color(value.get_color());
253 }
254 "all_text_colors" => {
255 self.change_all_text_colors(value.get_color());
256 }
257 "advance" => {
258 self.advance();
259 }
260 "advance_by" => {
261 self.advance_by(value.get_number());
262 }
263 "advance_by_percent" => {
264 self.advance_by_percent(value.get_float());
265 }
266 "change_pos" => {
267 self.change_pos(value.get_pos());
268 }
269 _ => {
270 panic!("\x07{}\x07 is not a valid key", key);
271 }
272 }
273 }
274 if print {
275 self.print();
276 }
277 }
278}
279
280fn get_num_lines_witdh(text: &str) -> u16 {
281 let mut num_lines = 1;
282 for c in text.chars() {
283 if c == '\n' {
284 num_lines += 1;
285 }
286 }
287 num_lines
288}
289
290mod auto_run {
291 use std::{
292 collections::HashMap,
293 fmt::{self, Display},
294 thread,
295 time::Duration,
296 };
297
298 use colored::Color;
299
300 use crate::{text_loading_bar::TextLoadingBar, Types};
301 #[derive(Debug)]
302 pub struct TextLoadingBarAutoOptions {
303 pub top_text: Vec<String>,
304 pub bottom_text: Vec<String>,
305 pub top: Vec<Option<Color>>,
306 pub bottom: Vec<Option<Color>>,
307 pub bar: Vec<Option<Color>>,
308 }
309
310 pub struct TextLoadingBarAutoPoint<T> {
311 pub top_text: HashMap<T, String>,
312 pub bottom_text: HashMap<T, String>,
313 pub top: HashMap<T, Option<Color>>,
314 pub bottom: HashMap<T, Option<Color>>,
315 pub bar: HashMap<T, Option<Color>>,
316 }
317 enum TextLoadingBarAutoOptionsType {
318 TopText,
319 BottomText,
320 Top,
321 Bottom,
322 Bar,
323 }
324
325 impl Display for TextLoadingBarAutoOptionsType {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 match self {
328 TextLoadingBarAutoOptionsType::TopText => write!(f, "top_text"),
329 TextLoadingBarAutoOptionsType::BottomText => write!(f, "bottom_text"),
330 TextLoadingBarAutoOptionsType::Top => write!(f, "top"),
331 TextLoadingBarAutoOptionsType::Bottom => write!(f, "bottom"),
332 TextLoadingBarAutoOptionsType::Bar => write!(f, "bar"),
333 }
334 }
335 }
336
337 impl TextLoadingBarAutoOptions {
338 pub fn get_len(&self) -> (u16, u16, u16, u16, u16) {
339 (
340 TextLoadingBarAutoOptions::check(
341 self.top_text.len(),
342 TextLoadingBarAutoOptionsType::TopText,
343 ),
344 TextLoadingBarAutoOptions::check(
345 self.bottom_text.len(),
346 TextLoadingBarAutoOptionsType::BottomText,
347 ),
348 TextLoadingBarAutoOptions::check(
349 self.top.len(),
350 TextLoadingBarAutoOptionsType::Top,
351 ),
352 TextLoadingBarAutoOptions::check(
353 self.bottom.len(),
354 TextLoadingBarAutoOptionsType::Bottom,
355 ),
356 TextLoadingBarAutoOptions::check(
357 self.bar.len(),
358 TextLoadingBarAutoOptionsType::Bar,
359 ),
360 )
361 }
362 fn check(num: usize, types: TextLoadingBarAutoOptionsType) -> u16 {
363 if num == 0 {
364 panic!("{} is 0", types);
365 } else {
366 num as u16
367 }
368 }
369 }
370 impl TextLoadingBar {
371 pub fn auto_run(
372 top_text: String,
373 bottom_text: String,
374 time_in_seconds: u16,
375 len: u16,
376 start: u16,
377 color: (
378 Option<colored::Color>, Option<colored::Color>, Option<colored::Color>, ),
382 t_start_pos: (u16, u16),
383 ) {
384 if start >= len {
385 println!();
386 panic!("\x07start must be less than len\x07");
387 }
388 let (top_text_color, bar_color, bottom_text_color) = color;
389 let self_clone = TextLoadingBar::new(
390 top_text,
391 bottom_text,
392 len,
393 (top_text_color, bar_color, bottom_text_color),
394 t_start_pos,
395 );
396 TextLoadingBar::auto_run_from(self_clone, time_in_seconds)
397 }
398
399 pub fn auto_run_change(
400 option: TextLoadingBarAutoOptions,
401 time_in_seconds: u16,
402 len: u16,
403 start: u16,
404 start_pos: (u16, u16),
405 ) {
406 if start >= len {
407 println!();
408 panic!("\x07start must be less than len\x07");
409 }
410 let mut self_clone = TextLoadingBar::new(
411 option.top_text[0].clone(),
412 option.bottom_text[0].clone(),
413 len,
414 (option.top[0], option.bar[0], option.bottom[0]),
415 start_pos,
416 );
417 self_clone.advance_by(start);
418 TextLoadingBar::auto_run_from_change(self_clone, option, time_in_seconds)
419 }
420
421 pub fn auto_run_change_points<T>(
423 time_in_seconds: u16,
424 len: u16,
425 start: u16,
426 start_pos: (u16, u16),
427 change: TextLoadingBarAutoPoint<T>,
428 type_change: Types,
429 ) where
430 T: Copy + fmt::Debug,
431 u16: From<T>,
432 f32: From<T>,
433 {
434 if start >= len {
436 println!();
437 panic!("\x07start must be less than len\x07");
438 }
439 let mut self_clone = TextLoadingBar::new(
440 "".to_string(),
441 "".to_string(),
442 len,
443 (None, None, None),
444 start_pos,
445 );
446 self_clone.advance_by(start);
447 TextLoadingBar::auto_run_from_change_points(
448 self_clone,
449 change,
450 time_in_seconds,
451 type_change,
452 )
453 }
454 pub fn auto_run_from(mut text_loading_bar: TextLoadingBar, time_in_seconds: u16) {
455 let index = time_in_seconds as f32 / (text_loading_bar.t_bar.space_left + 1) as f32;
456
457 text_loading_bar.print();
458 thread::spawn(move || {
459 for _ in 0..(text_loading_bar.t_bar.space_left) {
460 text_loading_bar.advance_print();
461 thread::sleep(Duration::from_secs_f32(index));
462 }
463 });
464 }
465
466 pub fn auto_run_from_change(
467 text_loading_bar: TextLoadingBar,
468 option: TextLoadingBarAutoOptions,
469 time_in_seconds: u16,
470 ) {
471 let (top_len, bottom_len, bar_color_len, top_color_len, bottom_color_len) =
472 option.get_len();
473 let change = TextLoadingBarAutoPoint {
476 bottom: crate::get_index_and_value(
477 bottom_color_len,
478 text_loading_bar.t_bar.space_left + 1,
479 text_loading_bar.t_bar.len,
480 &option.bottom,
481 ),
482 top: crate::get_index_and_value(
483 top_color_len,
484 text_loading_bar.t_bar.space_left + 1,
485 text_loading_bar.t_bar.len,
486 &option.top,
487 ),
488 top_text: crate::get_index_and_value(
489 top_len,
490 text_loading_bar.t_bar.space_left + 1,
491 text_loading_bar.t_bar.len,
492 &option.top_text,
493 ),
494 bottom_text: crate::get_index_and_value(
495 bottom_len,
496 text_loading_bar.t_bar.space_left + 1,
497 text_loading_bar.t_bar.len,
498 &option.bottom_text,
499 ),
500 bar: crate::get_index_and_value(
501 bar_color_len,
502 text_loading_bar.t_bar.space_left + 1,
503 text_loading_bar.t_bar.len,
504 &option.bar,
505 ),
506 };
507 TextLoadingBar::auto_run_from_change_points(
508 text_loading_bar,
509 change,
510 time_in_seconds,
511 Types::Index,
512 )
513 }
514
515 pub fn auto_run_from_change_points<T>(
516 mut loading_bar: TextLoadingBar,
517 change: TextLoadingBarAutoPoint<T>,
518 time_in_seconds: u16,
519 type_change: Types,
520 ) where
521 T: Copy + fmt::Debug,
522 u16: From<T>,
523 f32: From<T>,
524 {
525 let index = time_in_seconds as f32 / (loading_bar.t_bar.space_left + 1) as f32;
527 let mut total = loading_bar.t_bar.len - (loading_bar.t_bar.space_left);
528 let top = crate::generic_to_u16(loading_bar.t_bar.len, change.top_text, type_change);
529 let bottom =
530 crate::generic_to_u16(loading_bar.t_bar.len, change.bottom_text, type_change);
531 let bar_color = crate::generic_to_u16(loading_bar.t_bar.len, change.bar, type_change);
532 let top_color = crate::generic_to_u16(loading_bar.t_bar.len, change.top, type_change);
533 let bottom_color =
534 crate::generic_to_u16(loading_bar.t_bar.len, change.bottom, type_change);
535 loading_bar.print();
536 thread::spawn(move || {
537 for _ in 0..(loading_bar.t_bar.space_left) {
538 total += 1;
539 if top.contains_key(&total) {
540 loading_bar.top_text.text = top[&total].clone();
541 }
542 if bottom.contains_key(&total) {
543 loading_bar.bottom_text.text = bottom[&total].clone();
544 }
545 if bar_color.contains_key(&total) {
546 loading_bar.t_bar.color = bar_color[&total];
547 }
548 if top_color.contains_key(&total) {
549 loading_bar.top_text.color = top_color[&total];
550 }
551 if bottom_color.contains_key(&total) {
552 loading_bar.bottom_text.color = bottom_color[&total];
553 }
554
555 loading_bar.advance();
556 thread::sleep(Duration::from_secs_f32(index));
557 loading_bar.print()
558 }
559 });
560 }
561 }
562}
563
564mod change_at {
565 use crate::text_loading_bar::TextLoadingBar;
566 impl TextLoadingBar {
567 }
569}