Skip to content

Commit

Permalink
2 progress bars (#12)
Browse files Browse the repository at this point in the history
* added progress struct basis
* added terminal.rs for common terminal tasks
* added basic progress bar
* readme updates for new progress bar
  • Loading branch information
Arteiii authored Apr 9, 2024
1 parent d87b637 commit 1d5dc8b
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ readme = "README.md"
categories = ["command-line-utilities", "command-line-interface"]


description = "100+ Cool Animations and Support for Multiple Animations at Once (Yet Another Spinner Lib)"
description = "100+ spinner animations and Progress Bars and Support for Multiple Animations at Once"
repository = "https://github.com/Arteiii/zenity"
keywords = ["console", "animations", "cli", "spinner", "loading"]
homepage = "https://arteiii.github.io"
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# zenity (Yet Another Spinner Lib)

Elevate your Rust command-line interfaces with 100+ spinner animations and multiline support
Elevate your Rust command-line interfaces with 100+ spinner animations and Progress Bars + multiline support

[![Publish to Crates](https://github.com/Arteiii/zenity/actions/workflows/publish_crate.yml/badge.svg)](https://github.com/Arteiii/zenity/actions/workflows/publish_crate.yml)
[![Compile Rust](https://github.com/Arteiii/zenity/actions/workflows/release_examples.yml/badge.svg)](https://github.com/Arteiii/zenity/actions/workflows/release_examples.yml)
Expand All @@ -13,6 +13,10 @@ Elevate your Rust command-line interfaces with 100+ spinner animations and multi

![](./images/rustrover64_tlGiHM9JP0.gif)

![progress bar](./images/rustrover64_WupAJU44Lu.gif)

checkout the examples for this^^


Do you often find yourself gazing into the void of your terminal,
wondering if your computer has decided to take a coffee break without notifying you?
Expand Down
52 changes: 52 additions & 0 deletions examples/progress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use rand::Rng;

use zenity::animations::{
frames::progress::ProgressBarFrames,
progress::{Bar, Progress},
};

fn main() {
// I know that this is not the best solution I will rework it asap
// contributions welcome

println!("test Header line");
thread::sleep(Duration::from_secs(8));

let mut progress = Progress::default();

let progress1 = progress.add(Bar::default());

// TODO: create wrapper for this
let progress2 = progress.add(Bar {
frames: Arc::new(Mutex::new(ProgressBarFrames::rect())),
size: Arc::new(Mutex::new(70_usize)),
current: Arc::new(Mutex::new(0_usize)),
goal: Arc::new(Mutex::new(253_usize)),
});

let progress3 = progress.add(Bar {
frames: Arc::new(Mutex::new(ProgressBarFrames::hash())),
size: Arc::new(Mutex::new(7_usize)),
current: Arc::new(Mutex::new(0_usize)),
goal: Arc::new(Mutex::new(253_usize)),
});

progress.run_all();

let loading = 1_usize;

for loading in loading..=253 {
progress.set(&progress1, &loading);
progress.set(&progress2, &loading);
progress.set(&progress3, &loading);

let sleep_time = rand::thread_rng().gen_range(1..=70);
thread::sleep(Duration::from_millis(sleep_time));
}

thread::sleep(Duration::from_millis(1000));
}
Binary file added images/rustrover64_WupAJU44Lu.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/animations/frames/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! stores predefined animations and the `Frames` struct

///
pub mod progress;
pub mod spinner;
51 changes: 51 additions & 0 deletions src/animations/frames/progress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! progressbar frames

/// struct storing the data needed to render a ProgressBar
pub struct ProgressBarFrames {
/// begin string
pub begin: Vec<&'static str>,

/// string to place on complete places
pub bar_complete_char: Vec<&'static str>,

/// string to place on incomplete places
pub bar_incomplete_char: Vec<&'static str>,

/// ending string
pub end: Vec<&'static str>,
}

// TODO: add animations by adding +1 for each bar so you can have a wave animation and others

impl ProgressBarFrames {
/// '=' as the complete char and '-' as the incomplete char
pub fn equal() -> Self {
Self {
begin: vec!["["],
bar_complete_char: vec!["="],
bar_incomplete_char: vec!["-"],
end: vec!["]"],
}
}

/// '#' as the complete char and '.' as the incomplete char
pub fn hash() -> Self {
Self {
begin: vec!["["],
bar_complete_char: vec!["#"],
bar_incomplete_char: vec!["."],
end: vec!["]"],
}
}
/// '#' as the complete char and '.' as the incomplete char
pub fn rect() -> Self {
Self {
begin: vec![" "],
bar_complete_char: vec!["\u{25A0}"],
bar_incomplete_char: vec![" "],
end: vec![" "],
}
}

// TODO: add more
}
1 change: 0 additions & 1 deletion src/animations/frames/spinner.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Predefined Frames and other aniamtions


/// represents a collection of frames and their display speed, typically used for animations
///
/// # Example
Expand Down
1 change: 1 addition & 0 deletions src/animations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
pub(crate) mod animation;
pub mod frames;

pub mod progress;
pub mod spinner;
161 changes: 161 additions & 0 deletions src/animations/progress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! mod for progress bars

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;

use rand::Rng;

use crate::animations::frames::progress::ProgressBarFrames;
use crate::terminal::{console_cursor, console_render};

/// bar struct encapsulating the loading bar data animation
pub struct Bar {
/// frames to use for animation
pub frames: Arc<Mutex<ProgressBarFrames>>,

/// size of progress bar
pub size: Arc<Mutex<usize>>,

/// goal value
pub goal: Arc<Mutex<usize>>,

/// current value
pub current: Arc<Mutex<usize>>,
}

impl Default for Bar {
fn default() -> Self {
Bar {
frames: Arc::new(Mutex::new(ProgressBarFrames::equal())),
size: Arc::new(Mutex::new(31)),
goal: Arc::new(Mutex::new(253)),
current: Arc::new(Mutex::new(0)),
}
}
}

/// struct holding multiple bars
pub struct Progress {
// TODO: instead of random ids go after creation and increment by one
// this would allow to render them line for line based on this and order them correctly
bar: Arc<Mutex<HashMap<usize, Bar>>>,
stop: Arc<Mutex<bool>>,
}

impl Default for Progress {
fn default() -> Self {
Self::new()
}
}

impl Progress {
/// creates a new Progress instance
///
/// ## Example
/// ```
/// # use zenity::multi_spinner::MultiSpinner;
/// let _spinner = MultiSpinner::new();
/// ```
pub fn new() -> Self {
// console_cursor::reset_cursor();

console_cursor::save_hide_cursor();

Progress {
bar: Arc::new(Mutex::new(HashMap::new())),
stop: Arc::new(Mutex::new(false)),
}
}

/// add a new progress bar
pub fn add(&self, bar: Bar) -> usize {
let mut rng = rand::thread_rng();
let mut uid: usize;

loop {
uid = rng.gen();
if !self.bar.lock().unwrap().contains_key(&uid) {
break;
}
}

self.bar.lock().unwrap().insert(uid, bar);

uid
}

/// set the current
///
/// # Arguments
///
/// * `uid` - the unique identifier of the progress bar
/// * `new_current` - the new value to set as the current progress
///
/// **NOTE:**
/// - if the UID is invalid, this function does nothing
/// - this function locks the progress bar associated with the provided uid and updates its current value
pub fn set(&self, uid: &usize, new_current: &usize) {
if let Some(bar) = self.bar.lock().unwrap().get(uid) {
let mut current = bar.current.lock().unwrap();
*current = *new_current;
}
}

/// start each queued progressbar
pub fn run_all(&mut self) {
let bars = Arc::clone(&self.bar);
let stop = Arc::clone(&self.stop);

thread::spawn(move || {
while !*stop.lock().unwrap() {
let mut rendered_frames = Vec::new();

for (_, spinner) in bars.lock().unwrap().iter() {
let frames = spinner.frames.lock().unwrap();
let begin: &str = frames.begin[0];
let end: &str = frames.end[0];
let current_incomplete: &str = frames.bar_incomplete_char[0];
let current_complete: &str = frames.bar_complete_char[0];

let size: usize = *spinner.size.lock().unwrap();
let goal = *spinner.goal.lock().unwrap();
let current: usize = *spinner.current.lock().unwrap();

// calculate percentage completion
let completion_percentage = (current as f64 / goal as f64) * 100.0;

// calculate number of characters to represent the completion percentage
let complete_size = ((completion_percentage / 100.0) * size as f64) as usize;
let incomplete_size = size - complete_size;

// Render the frame with the updated incomplete string and add it to the vector
let rendered_frame = format!(
"{begin}{}{}{end} {:.2}% | {}/{}",
current_complete.repeat(complete_size),
current_incomplete.repeat(incomplete_size),
completion_percentage,
current,
goal,
);
rendered_frames.push(rendered_frame);
}

// Join all the rendered frames from the vector
let combined_output = rendered_frames.join("\n");

// render the frame with the updated incomplete string
console_render::render_frame(&combined_output);
}
});
}
}

impl Drop for Progress {
/// stops the thread when the object is dropped
fn drop(&mut self) {
// cleanup methods
console_render::cleanup();
console_cursor::reset_cursor();
}
}
Loading

0 comments on commit 1d5dc8b

Please sign in to comment.