2016-07-26 17:13:36 +00:00
|
|
|
use std::sync::Arc;
|
2016-07-25 06:00:13 +00:00
|
|
|
|
2016-07-26 19:10:13 +00:00
|
|
|
use std::thread;
|
2016-07-25 06:00:13 +00:00
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
|
|
|
|
use {Cursive, Printer};
|
2016-07-26 06:54:33 +00:00
|
|
|
use align::HAlign;
|
|
|
|
use theme::{ColorStyle, Effect};
|
2016-07-25 06:00:13 +00:00
|
|
|
use view::View;
|
|
|
|
|
2016-07-25 20:38:13 +00:00
|
|
|
pub type CbPromise = Option<Box<Fn(&mut Cursive) + Send>>;
|
|
|
|
|
2016-07-25 06:00:13 +00:00
|
|
|
/// Display progress.
|
|
|
|
pub struct ProgressBar {
|
|
|
|
min: usize,
|
|
|
|
max: usize,
|
|
|
|
value: Arc<AtomicUsize>,
|
|
|
|
// TODO: use a Promise instead?
|
2016-07-26 06:54:33 +00:00
|
|
|
label_maker: Box<Fn(usize, (usize, usize)) -> String>,
|
|
|
|
}
|
|
|
|
|
2016-07-26 19:10:13 +00:00
|
|
|
pub type Ticker = Box<Fn(usize) + Send>;
|
|
|
|
|
2016-07-26 06:54:33 +00:00
|
|
|
fn make_percentage(value: usize, (min, max): (usize, usize)) -> String {
|
|
|
|
let percent = 101 * (value - min) / (1 + max - min);
|
|
|
|
format!("{} %", percent)
|
2016-07-25 06:00:13 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 20:38:13 +00:00
|
|
|
new_default!(ProgressBar);
|
|
|
|
|
2016-07-25 06:00:13 +00:00
|
|
|
impl ProgressBar {
|
|
|
|
/// Creates a new progress bar.
|
|
|
|
///
|
|
|
|
/// Default values:
|
|
|
|
///
|
|
|
|
/// * `min`: 0
|
|
|
|
/// * `max`: 100
|
|
|
|
/// * `value`: 0
|
|
|
|
pub fn new() -> Self {
|
|
|
|
ProgressBar {
|
|
|
|
min: 0,
|
|
|
|
max: 100,
|
|
|
|
value: Arc::new(AtomicUsize::new(0)),
|
2016-07-26 06:54:33 +00:00
|
|
|
label_maker: Box::new(make_percentage),
|
2016-07-25 06:00:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the value to follow.
|
2016-07-26 19:10:13 +00:00
|
|
|
///
|
|
|
|
/// Use this to manually control the progress to display
|
|
|
|
/// by directly modifying the value pointed to by `value`.
|
2016-07-25 06:00:13 +00:00
|
|
|
pub fn with_value(mut self, value: Arc<AtomicUsize>) -> Self {
|
|
|
|
self.value = value;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-07-26 19:10:13 +00:00
|
|
|
/// Starts a function in a separate thread, and monitor the progress.
|
|
|
|
///
|
|
|
|
/// `f` will be given a `Ticker` to increment the bar's progress.
|
|
|
|
pub fn start<F: FnOnce(Ticker) + Send + 'static>(&mut self, f: F) {
|
|
|
|
let value = self.value.clone();
|
|
|
|
let ticker: Ticker = Box::new(move |ticks| {
|
|
|
|
value.fetch_add(ticks, Ordering::Relaxed);
|
|
|
|
});
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
|
|
|
f(ticker);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Starts a function in a separate thread, and monitor the progress.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn with_task<F: FnOnce(Ticker) + Send + 'static>(mut self, task: F) -> Self {
|
|
|
|
self.start(task);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-07-26 06:54:33 +00:00
|
|
|
/// Sets the label generator.
|
|
|
|
///
|
|
|
|
/// The given function will be called with `(value, (min, max))`.
|
|
|
|
/// Its output will be used as the label to print inside the progress bar.
|
|
|
|
///
|
|
|
|
/// The default one shows a percentage progress:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// fn make_percentage(value: usize, (min, max): (usize, usize)) -> String {
|
|
|
|
/// let percent = 101 * (value - min) / (1 + max - min);
|
|
|
|
/// format!("{} %", percent)
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub fn with_label<F: Fn(usize, (usize, usize)) -> String + 'static>
|
|
|
|
(mut self, label_maker: F)
|
|
|
|
-> Self {
|
|
|
|
self.label_maker = Box::new(label_maker);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-07-25 06:00:13 +00:00
|
|
|
/// Sets the minimum value.
|
|
|
|
///
|
|
|
|
/// When `value` equals `min`, the bar is at the minimum level.
|
|
|
|
pub fn min(mut self, min: usize) -> Self {
|
|
|
|
self.min = min;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the maximum value.
|
|
|
|
///
|
|
|
|
/// When `value` equals `max`, the bar is at the maximum level.
|
|
|
|
pub fn max(mut self, max: usize) -> Self {
|
|
|
|
self.max = max;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the `min` and `max` range for the value.
|
|
|
|
pub fn range(self, min: usize, max: usize) -> Self {
|
|
|
|
self.min(min).max(max)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the current value.
|
|
|
|
///
|
|
|
|
/// Value is clamped between `min` and `max`.
|
|
|
|
pub fn set_value(&mut self, value: usize) {
|
|
|
|
self.value.store(value, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl View for ProgressBar {
|
|
|
|
fn draw(&self, printer: &Printer) {
|
|
|
|
// Now, the bar itself...
|
2016-07-25 06:03:59 +00:00
|
|
|
let available = printer.size.x;
|
2016-07-25 06:00:13 +00:00
|
|
|
|
|
|
|
let value = self.value.load(Ordering::Relaxed);
|
2016-07-25 20:39:10 +00:00
|
|
|
let length = ((1 + available) * (value - self.min)) /
|
|
|
|
(1 + self.max - self.min);
|
2016-07-26 06:54:33 +00:00
|
|
|
|
|
|
|
let label = (self.label_maker)(value, (self.min, self.max));
|
|
|
|
let offset = HAlign::Center.get_offset(label.len(), printer.size.x);
|
|
|
|
|
2016-07-25 06:00:13 +00:00
|
|
|
printer.with_color(ColorStyle::Highlight, |printer| {
|
2016-07-26 06:54:33 +00:00
|
|
|
printer.with_effect(Effect::Reverse, |printer| {
|
|
|
|
printer.print((offset, 0), &label);
|
|
|
|
});
|
|
|
|
let printer = &printer.sub_printer((0, 0), (length, 1), true);
|
2016-07-25 06:03:59 +00:00
|
|
|
printer.print_hline((0, 0), length, " ");
|
2016-07-26 06:54:33 +00:00
|
|
|
printer.print((offset, 0), &label);
|
2016-07-25 06:00:13 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|