1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use cursive::traits::*;
use cursive::utils::Counter;
use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView};
use cursive::Cursive;
use rand::Rng;
use std::cmp::min;
use std::thread;
use std::time::Duration;
// This example shows a ProgressBar reporting the status from an asynchronous
// job.
//
// It works by sharing a counter with the job thread. This counter can be
// "ticked" to indicate progress.
fn main() {
let mut siv = cursive::default();
// We'll start slowly with a simple start button...
siv.add_layer(
Dialog::new()
.title("Progress bar example")
.padding_lrtb(0, 0, 1, 1)
.content(Button::new("Start", phase_1)),
);
siv.run();
}
fn phase_1(s: &mut Cursive) {
// Phase 1 is easy: a simple pre-loading.
// Number of ticks
let n_max = 1000;
// This is the callback channel
let cb = s.cb_sink().clone();
s.pop_layer();
s.add_layer(Dialog::around(
ProgressBar::new()
// We need to know how many ticks represent a full bar.
.range(0, n_max)
.with_task(move |counter| {
// This closure will be called in a separate thread.
fake_load(n_max, &counter);
// When we're done, send a callback through the channel
cb.send(Box::new(coffee_break)).unwrap();
})
.full_width(),
));
s.set_autorefresh(true);
}
fn coffee_break(s: &mut Cursive) {
// A little break before things get serious.
s.set_autorefresh(false);
s.pop_layer();
s.add_layer(
Dialog::new()
.title("Preparation complete")
.content(TextView::new("Now, the real deal!").center())
.button("Again??", phase_2),
);
}
fn phase_2(s: &mut Cursive) {
// Now, we'll run N tasks
// (It could be downloading a file, extracting an archive,
// reticulating sprites, ...)
let n_bars = 10;
// Each task will have its own shiny counter
let counters: Vec<_> = (0..n_bars).map(|_| Counter::new(0)).collect();
// To make things more interesting, we'll give a random speed to each bar
let speeds: Vec<_> = (0..n_bars)
.map(|_| rand::thread_rng().gen_range(50..150))
.collect();
let n_max = 100_000;
let cb = s.cb_sink().clone();
// Let's prepare the progress bars...
let mut linear = LinearLayout::vertical();
for c in &counters {
linear.add_child(ProgressBar::new().max(n_max).with_value(c.clone()));
}
s.pop_layer();
s.add_layer(Dialog::around(linear.full_width()).title("Just a moment..."));
// And we start the worker thread.
thread::spawn(move || {
loop {
thread::sleep(Duration::from_millis(5));
let mut done = true;
for (c, s) in counters.iter().zip(&speeds) {
let ticks = min(n_max - c.get(), *s);
c.tick(ticks);
if c.get() < n_max {
done = false;
}
}
if done {
break;
}
}
cb.send(Box::new(final_step)).unwrap();
});
s.set_autorefresh(true);
}
fn final_step(s: &mut Cursive) {
// A little break before things get serious.
s.set_autorefresh(false);
s.pop_layer();
s.add_layer(
Dialog::new()
.title("Report")
.content(
TextView::new(
"Time travel was a success!\n\
We went forward a few seconds!!",
)
.center(),
)
.button("That's it?", |s| s.quit()),
);
}
// Function to simulate a long process.
fn fake_load(n_max: usize, counter: &Counter) {
for _ in 0..n_max {
thread::sleep(Duration::from_millis(5));
// The `counter.tick()` method increases the progress value
counter.tick(1);
}
}