use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
pub use frames::*;
use crate::iterators::balanced_iterator;
use crate::style;
use crate::style::{Attribute, Color, ContentStyle, StyledString};
use crate::terminal::console_render::{get_rows, push_content_up};
use crate::terminal::{console_cursor, console_render};
pub mod frames;
#[derive(Clone)]
pub struct MultiSpinner {
spinner: Arc<Mutex<HashMap<usize, Frames>>>,
show_line_number: Arc<Mutex<bool>>,
clear_type: Arc<Mutex<Option<u16>>>,
stop: Arc<Mutex<bool>>,
}
impl Default for MultiSpinner {
fn default() -> Self {
let spinner = Self::new();
spinner.add(Frames::default());
spinner.run_all();
spinner
}
}
impl MultiSpinner {
pub fn new() -> Self {
MultiSpinner {
spinner: Arc::new(Mutex::new(HashMap::new())),
stop: Arc::new(Mutex::new(false)),
show_line_number: Arc::new(Mutex::new(false)),
clear_type: Arc::new(Mutex::new(Some(get_rows()))),
}
}
pub fn add(&self, frames: Frames) -> usize {
let mut spinner_map = self.spinner.lock().unwrap();
let uid = spinner_map.len() + 1;
spinner_map.insert(uid, frames);
uid
}
pub fn get_last(&self) -> usize {
let spinner_map = self.spinner.lock().unwrap();
if spinner_map.is_empty() {
return 0;
}
spinner_map.keys().copied().max().unwrap()
}
pub fn clear(&self, rows: Option<u16>) {
*self.clear_type.lock().unwrap() = rows;
}
pub fn set_text(&self, uid: &usize, new_text: String) {
if let Some(spinner) = self.spinner.lock().unwrap().get_mut(uid) {
spinner.text = StyledString::new(&new_text);
}
}
pub fn set_styled_text(&self, uid: &usize, new_text: StyledString) {
if let Some(spinner) = self.spinner.lock().unwrap().get_mut(uid) {
spinner.text = new_text;
}
}
pub fn stop(&self, uid: &usize) {
if let Some(spinner) = self.spinner.lock().unwrap().get_mut(uid) {
spinner.stop = true; }
}
pub fn show_line_number(&self) {
*self.show_line_number.lock().unwrap() = true;
}
pub fn run_all(&self) {
if let Some(rows) = *self.clear_type.lock().unwrap() {
push_content_up(rows);
}
let spinners = Arc::clone(&self.spinner);
let stop = Arc::clone(&self.stop);
let show_line_number = Arc::clone(&self.show_line_number);
thread::spawn(move || {
console_cursor::save_hide_cursor();
let mut index = 1_u16;
let mut max_line_number: usize = 0;
while !*stop.lock().unwrap() {
for (line_number, spinner) in spinners.lock().unwrap().iter() {
let mut combined_vec = Vec::new();
if *show_line_number.lock().unwrap() {
if *line_number > max_line_number {
max_line_number = *line_number;
}
console_render::push_styled_string!(
combined_vec,
format!("[{}/{}]", &line_number, &max_line_number),
Some(Color::Grey),
None,
None,
style::combine_attributes(&[&Attribute::Italic])
);
console_render::push_unstyled_spaces!(combined_vec, 1);
}
if !spinner.stop {
let frames = vec![spinner.frames.clone()];
let current_frame = balanced_iterator(index as usize, &frames)
.iter()
.map(|opt| opt.cloned().unwrap_or_default())
.collect::<Vec<_>>();
if let Some(first_frame) = current_frame.first() {
combined_vec.push(first_frame.clone());
}
console_render::push_unstyled_spaces!(combined_vec, 1);
}
combined_vec.push(spinner.text.clone());
console_render::render_styled_line(*line_number as u16, &combined_vec);
}
index += 1;
thread::sleep(Duration::from_millis(80));
}
});
}
}
impl Drop for MultiSpinner {
fn drop(&mut self) {
*self.stop.lock().unwrap() = true;
console_render::cleanup();
console_cursor::reset_cursor();
}
}