use std::{
collections::HashMap,
fmt::{self, Display},
io::stdout,
};
use crate::loading_bar::LoadingBar;
pub use auto_run::{TextLoadingBarAutoOptions, TextLoadingBarAutoPoint};
pub use colored::Color;
use colored::Colorize;
use crossterm::{
cursor::{MoveTo, RestorePosition, SavePosition},
execute,
style::Print,
terminal::{Clear, ClearType},
};
pub enum TextLoadingBarOptions {
Text(String),
Color(Option<Color>),
Number(u16),
Float(f32),
Pos(u16, u16),
None,
}
impl TextLoadingBarOptions {
fn get_text(&self) -> &str {
match self {
TextLoadingBarOptions::Text(text) => text,
_ => "",
}
}
fn get_color(&self) -> Option<Color> {
match self {
TextLoadingBarOptions::Color(color) => *color,
_ => None,
}
}
fn get_number(&self) -> u16 {
match self {
TextLoadingBarOptions::Number(number) => *number,
_ => 0,
}
}
fn get_float(&self) -> f32 {
match self {
TextLoadingBarOptions::Float(float) => *float,
_ => 0.0,
}
}
fn get_pos(&self) -> (u16, u16) {
match self {
TextLoadingBarOptions::Pos(x, y) => (*x, *y),
_ => (0, 0),
}
}
}
#[derive(Debug)]
struct TextItem {
text: String,
color: Option<colored::Color>,
}
#[derive(Debug)]
pub struct TextLoadingBar {
top_text: TextItem,
bottom_text: TextItem,
t_bar: LoadingBar,
t_start_pos: (u16, u16),
}
impl Display for TextLoadingBar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}\n{}\n{}",
self.top_text
.text
.color(self.top_text.color.unwrap_or(colored::Color::White)),
self.t_bar,
self.bottom_text
.text
.color(self.bottom_text.color.unwrap_or(colored::Color::White)) )
}
}
impl TextLoadingBar {
pub fn new(
top_text: String,
bottom_text: String,
len: u16,
color: (
Option<colored::Color>, // top text color
Option<colored::Color>, // bar color
Option<colored::Color>, // bottom text color
),
t_start_pos: (u16, u16),
) -> TextLoadingBar {
let (top_text_color, bar_color, bottom_text_color) = color;
TextLoadingBar {
top_text: TextItem {
text: top_text,
color: top_text_color,
},
t_bar: LoadingBar::new(len, bar_color, (0, 0)),
bottom_text: TextItem {
text: bottom_text,
color: bottom_text_color,
},
t_start_pos,
}
}
fn goline_clear_print(&self) {
let line_count = get_num_lines_witdh(&self.to_string());
let (x, y) = self.t_start_pos;
let mut y_copy = y;
execute!(stdout(), SavePosition).expect("\x07failed to save position\x07");
for _ in 0..line_count {
execute!(stdout(), MoveTo(x, y_copy)).expect("\x07failed to move cursor\x07");
execute!(stdout(), Clear(ClearType::UntilNewLine)).expect("\x07failed to clear\x07");
execute!(stdout(), RestorePosition).expect("\x07failed to restore cursor\x07");
y_copy += 1;
}
let text = self.to_string();
let text = text.split('\n').collect::<Vec<&str>>();
y_copy = y;
for i in 0..line_count {
let texts = &text[i as usize];
execute!(stdout(), MoveTo(x, y_copy)).expect("\x07failed to move cursor\x07");
execute!(stdout(), Print(texts)).expect("\x07failed to print\x07");
execute!(stdout(), RestorePosition).expect("\x07failed to restore cursor\x07");
y_copy += 1;
}
}
pub fn print(&self) {
self.goline_clear_print();
}
pub fn change_pos(&mut self, t_start_pos: (u16, u16)) {
let line_count = get_num_lines_witdh(&self.to_string());
let (x, y) = self.t_start_pos;
let mut y_copy = y;
execute!(stdout(), SavePosition).expect("\x07failed to save position\x07");
for _ in 0..line_count {
execute!(stdout(), MoveTo(x, y_copy)).expect("\x07failed to move cursor\x07");
execute!(stdout(), Clear(ClearType::UntilNewLine)).expect("\x07failed to clear\x07");
execute!(stdout(), RestorePosition).expect("\x07failed to restore cursor\x07");
y_copy += 1;
}
self.t_start_pos = t_start_pos;
}
pub fn change_pos_print(&mut self, t_start_pos: (u16, u16)) {
self.change_pos(t_start_pos);
self.print();
}
pub fn change_top_text(&mut self, text: String) {
self.top_text.text = text;
}
pub fn change_bottom_text(&mut self, text: String) {
self.bottom_text.text = text;
}
pub fn change_top_text_color(&mut self, color: Option<Color>) {
self.top_text.color = color;
}
pub fn change_bottom_text_color(&mut self, color: Option<Color>) {
self.bottom_text.color = color;
}
pub fn change_bar_color(&mut self, color: Option<Color>) {
self.t_bar.color = color;
}
pub fn change_all_text_colors(&mut self, color: Option<Color>) {
self.top_text.color = color;
self.bottom_text.color = color;
self.t_bar.color = color;
}
pub fn advance(&mut self) {
self.t_bar.advance();
}
pub fn advance_print(&mut self) {
self.advance();
self.goline_clear_print();
}
pub fn advance_by(&mut self, index: u16) {
self.t_bar.advance_by(index);
}
pub fn advance_by_print(&mut self, index: u16) {
self.advance_by(index);
self.goline_clear_print();
}
pub fn advance_by_percent(&mut self, percent: f32) {
self.t_bar.advance_by_percent(percent);
}
pub fn advance_by_percent_print(&mut self, percent: f32) {
self.advance_by_percent(percent);
self.goline_clear_print();
}
pub fn change(&mut self, map: HashMap<&str, TextLoadingBarOptions>, print: bool) {
for (key, value) in map {
match key {
"top_text" => {
self.change_top_text(value.get_text().to_string());
}
"bottom_text" => {
self.change_bottom_text(value.get_text().to_string());
}
"top_text_color" => {
self.change_top_text_color(value.get_color());
}
"bottom_text_color" => {
self.change_bottom_text_color(value.get_color());
}
"bar_color" => {
self.change_bar_color(value.get_color());
}
"all_text_colors" => {
self.change_all_text_colors(value.get_color());
}
"advance" => {
self.advance();
}
"advance_by" => {
self.advance_by(value.get_number());
}
"advance_by_percent" => {
self.advance_by_percent(value.get_float());
}
"change_pos" => {
self.change_pos(value.get_pos());
}
_ => {
panic!("\x07{}\x07 is not a valid key", key);
}
}
}
if print {
self.print();
}
}
}
fn get_num_lines_witdh(text: &str) -> u16 {
let mut num_lines = 1;
for c in text.chars() {
if c == '\n' {
num_lines += 1;
}
}
num_lines
}
mod auto_run {
use std::{
collections::HashMap,
fmt::{self, Display},
thread,
time::Duration,
};
use colored::Color;
use crate::{text_loading_bar::TextLoadingBar, Types};
#[derive(Debug)]
pub struct TextLoadingBarAutoOptions {
pub top_text: Vec<String>,
pub bottom_text: Vec<String>,
pub top: Vec<Option<Color>>,
pub bottom: Vec<Option<Color>>,
pub bar: Vec<Option<Color>>,
}
pub struct TextLoadingBarAutoPoint<T> {
pub top_text: HashMap<T, String>,
pub bottom_text: HashMap<T, String>,
pub top: HashMap<T, Option<Color>>,
pub bottom: HashMap<T, Option<Color>>,
pub bar: HashMap<T, Option<Color>>,
}
enum TextLoadingBarAutoOptionsType {
TopText,
BottomText,
Top,
Bottom,
Bar,
}
impl Display for TextLoadingBarAutoOptionsType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TextLoadingBarAutoOptionsType::TopText => write!(f, "top_text"),
TextLoadingBarAutoOptionsType::BottomText => write!(f, "bottom_text"),
TextLoadingBarAutoOptionsType::Top => write!(f, "top"),
TextLoadingBarAutoOptionsType::Bottom => write!(f, "bottom"),
TextLoadingBarAutoOptionsType::Bar => write!(f, "bar"),
}
}
}
impl TextLoadingBarAutoOptions {
pub fn get_len(&self) -> (u16, u16, u16, u16, u16) {
(
TextLoadingBarAutoOptions::check(
self.top_text.len(),
TextLoadingBarAutoOptionsType::TopText,
),
TextLoadingBarAutoOptions::check(
self.bottom_text.len(),
TextLoadingBarAutoOptionsType::BottomText,
),
TextLoadingBarAutoOptions::check(
self.top.len(),
TextLoadingBarAutoOptionsType::Top,
),
TextLoadingBarAutoOptions::check(
self.bottom.len(),
TextLoadingBarAutoOptionsType::Bottom,
),
TextLoadingBarAutoOptions::check(
self.bar.len(),
TextLoadingBarAutoOptionsType::Bar,
),
)
}
fn check(num: usize, types: TextLoadingBarAutoOptionsType) -> u16 {
if num == 0 {
panic!("{} is 0", types);
} else {
num as u16
}
}
}
impl TextLoadingBar {
pub fn auto_run(
top_text: String,
bottom_text: String,
time_in_seconds: u16,
len: u16,
start: u16,
color: (
Option<colored::Color>, // top text color
Option<colored::Color>, // bar color
Option<colored::Color>, // bottom text color
),
t_start_pos: (u16, u16),
) {
if start >= len {
println!();
panic!("\x07start must be less than len\x07");
}
let (top_text_color, bar_color, bottom_text_color) = color;
let self_clone = TextLoadingBar::new(
top_text,
bottom_text,
len,
(top_text_color, bar_color, bottom_text_color),
t_start_pos,
);
TextLoadingBar::auto_run_from(self_clone, time_in_seconds)
}
pub fn auto_run_change(
option: TextLoadingBarAutoOptions,
time_in_seconds: u16,
len: u16,
start: u16,
start_pos: (u16, u16),
) {
if start >= len {
println!();
panic!("\x07start must be less than len\x07");
}
let mut self_clone = TextLoadingBar::new(
option.top_text[0].clone(),
option.bottom_text[0].clone(),
len,
(option.top[0], option.bar[0], option.bottom[0]),
start_pos,
);
self_clone.advance_by(start);
TextLoadingBar::auto_run_from_change(self_clone, option, time_in_seconds)
}
pub fn auto_run_change_points<T>(
time_in_seconds: u16,
len: u16,
start: u16,
start_pos: (u16, u16),
change: TextLoadingBarAutoPoint<T>,
type_change: Types,
) where
T: Copy + fmt::Debug,
u16: From<T>,
f32: From<T>,
{
if start >= len {
println!();
panic!("\x07start must be less than len\x07");
}
let mut self_clone = TextLoadingBar::new(
"".to_string(),
"".to_string(),
len,
(None, None, None),
start_pos,
);
self_clone.advance_by(start);
TextLoadingBar::auto_run_from_change_points(
self_clone,
change,
time_in_seconds,
type_change,
)
}
pub fn auto_run_from(mut text_loading_bar: TextLoadingBar, time_in_seconds: u16) {
let index = time_in_seconds as f32 / (text_loading_bar.t_bar.space_left + 1) as f32;
text_loading_bar.print();
thread::spawn(move || {
for _ in 0..(text_loading_bar.t_bar.space_left) {
text_loading_bar.advance_print();
thread::sleep(Duration::from_secs_f32(index));
}
});
}
pub fn auto_run_from_change(
text_loading_bar: TextLoadingBar,
option: TextLoadingBarAutoOptions,
time_in_seconds: u16,
) {
let (top_len, bottom_len, bar_color_len, top_color_len, bottom_color_len) =
option.get_len();
let change = TextLoadingBarAutoPoint {
bottom: crate::get_index_and_value(
bottom_color_len,
text_loading_bar.t_bar.space_left + 1,
text_loading_bar.t_bar.len,
&option.bottom,
),
top: crate::get_index_and_value(
top_color_len,
text_loading_bar.t_bar.space_left + 1,
text_loading_bar.t_bar.len,
&option.top,
),
top_text: crate::get_index_and_value(
top_len,
text_loading_bar.t_bar.space_left + 1,
text_loading_bar.t_bar.len,
&option.top_text,
),
bottom_text: crate::get_index_and_value(
bottom_len,
text_loading_bar.t_bar.space_left + 1,
text_loading_bar.t_bar.len,
&option.bottom_text,
),
bar: crate::get_index_and_value(
bar_color_len,
text_loading_bar.t_bar.space_left + 1,
text_loading_bar.t_bar.len,
&option.bar,
),
};
TextLoadingBar::auto_run_from_change_points(
text_loading_bar,
change,
time_in_seconds,
Types::Index,
)
}
pub fn auto_run_from_change_points<T>(
mut loading_bar: TextLoadingBar,
change: TextLoadingBarAutoPoint<T>,
time_in_seconds: u16,
type_change: Types,
) where
T: Copy + fmt::Debug,
u16: From<T>,
f32: From<T>,
{
let index = time_in_seconds as f32 / (loading_bar.t_bar.space_left + 1) as f32;
let mut total = loading_bar.t_bar.len - (loading_bar.t_bar.space_left);
let top = crate::generic_to_u16(loading_bar.t_bar.len, change.top_text, type_change);
let bottom =
crate::generic_to_u16(loading_bar.t_bar.len, change.bottom_text, type_change);
let bar_color = crate::generic_to_u16(loading_bar.t_bar.len, change.bar, type_change);
let top_color = crate::generic_to_u16(loading_bar.t_bar.len, change.top, type_change);
let bottom_color =
crate::generic_to_u16(loading_bar.t_bar.len, change.bottom, type_change);
loading_bar.print();
thread::spawn(move || {
for _ in 0..(loading_bar.t_bar.space_left) {
total += 1;
if top.contains_key(&total) {
loading_bar.top_text.text = top[&total].clone();
}
if bottom.contains_key(&total) {
loading_bar.bottom_text.text = bottom[&total].clone();
}
if bar_color.contains_key(&total) {
loading_bar.t_bar.color = bar_color[&total];
}
if top_color.contains_key(&total) {
loading_bar.top_text.color = top_color[&total];
}
if bottom_color.contains_key(&total) {
loading_bar.bottom_text.color = bottom_color[&total];
}
loading_bar.advance();
thread::sleep(Duration::from_secs_f32(index));
loading_bar.print()
}
});
}
}
}
mod change_at {
use crate::text_loading_bar::TextLoadingBar;
impl TextLoadingBar {
}
}