Add start and with_task to ProgressBar

This commit is contained in:
Alexandre Bury 2016-07-26 12:10:13 -07:00
parent eb6b5d5728
commit 8eb61e0239
2 changed files with 53 additions and 28 deletions

View File

@ -4,8 +4,6 @@ use cursive::prelude::*;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -14,37 +12,36 @@ fn main() {
.title("Progress bar example") .title("Progress bar example")
.padding((0, 0, 1, 1)) .padding((0, 0, 1, 1))
.content(Button::new("Start", |s| { .content(Button::new("Start", |s| {
// These two values will allow us to communicate. // Number of ticks
let value = Arc::new(AtomicUsize::new(0));
let n_max = 1000; let n_max = 1000;
s.pop_layer(); // This is the callback channel
s.add_layer(Panel::new(FullView::full_width(ProgressBar::new()
.range(0, n_max)
.with_value(value.clone()))));
let cb = s.cb_sink().clone(); let cb = s.cb_sink().clone();
// Spawn a thread to process things in the background. s.pop_layer();
thread::spawn(move || { s.add_layer(Panel::new(FullView::full_width(
ProgressBar::new()
.range(0, n_max)
.with_task(move |ticker| {
// This closure will be called in a separate thread.
for _ in 0..n_max { for _ in 0..n_max {
thread::sleep(Duration::from_millis(20)); thread::sleep(Duration::from_millis(5));
value.fetch_add(1, Ordering::Relaxed); // The ticker method increases the progress value
ticker(1);
} }
// When we're done, send a callback through the channel
cb.send(Box::new(move |s| { cb.send(Box::new(move |s| {
s.pop_layer(); s.pop_layer();
s.add_layer(Dialog::empty() s.add_layer(Dialog::empty()
.title("Work done!") .title("Work done!")
.content(TextView::new("Phew, that was some \ .content(TextView::new("Phew!"))
work!")) .button("Finally!", |s| s.quit()));
.button("Sure!", |s| s.quit()));
})) }))
.unwrap(); .unwrap();
}); })
)));
})) })));
.with_id("dialog"));
siv.set_fps(30); siv.set_fps(30);

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use std::thread;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use {Cursive, Printer}; use {Cursive, Printer};
@ -18,6 +19,8 @@ pub struct ProgressBar {
label_maker: Box<Fn(usize, (usize, usize)) -> String>, label_maker: Box<Fn(usize, (usize, usize)) -> String>,
} }
pub type Ticker = Box<Fn(usize) + Send>;
fn make_percentage(value: usize, (min, max): (usize, usize)) -> String { fn make_percentage(value: usize, (min, max): (usize, usize)) -> String {
let percent = 101 * (value - min) / (1 + max - min); let percent = 101 * (value - min) / (1 + max - min);
format!("{} %", percent) format!("{} %", percent)
@ -43,11 +46,36 @@ impl ProgressBar {
} }
/// Sets the value to follow. /// Sets the value to follow.
///
/// Use this to manually control the progress to display
/// by directly modifying the value pointed to by `value`.
pub fn with_value(mut self, value: Arc<AtomicUsize>) -> Self { pub fn with_value(mut self, value: Arc<AtomicUsize>) -> Self {
self.value = value; self.value = value;
self self
} }
/// 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
}
/// Sets the label generator. /// Sets the label generator.
/// ///
/// The given function will be called with `(value, (min, max))`. /// The given function will be called with `(value, (min, max))`.