Add async-callback channel to Cursive

And remove it from ProgressBar
This commit is contained in:
Alexandre Bury 2016-07-26 10:13:36 -07:00
parent 04f961657f
commit 0643c50bd5
3 changed files with 37 additions and 33 deletions

View File

@ -4,7 +4,7 @@ use cursive::prelude::*;
use std::thread;
use std::time::Duration;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
@ -16,15 +16,15 @@ fn main() {
.content(Button::new("Start", |s| {
// These two values will allow us to communicate.
let value = Arc::new(AtomicUsize::new(0));
let cb = Arc::new(Mutex::new(None));
let n_max = 1000;
s.pop_layer();
s.add_layer(Panel::new(FullView::full_width(ProgressBar::new()
.range(0, n_max)
.with_value(value.clone())
.with_callback(cb.clone()))));
.with_value(value.clone()))));
let cb = s.cb_sink().clone();
// Spawn a thread to process things in the background.
thread::spawn(move || {
@ -32,13 +32,15 @@ fn main() {
thread::sleep(Duration::from_millis(20));
value.fetch_add(1, Ordering::Relaxed);
}
*cb.lock().unwrap() = Some(Box::new(move |s| {
s.pop_layer();
s.add_layer(Dialog::empty()
.title("Work done!")
.content(TextView::new("Phew, that was some work!"))
.button("Sure!", |s| s.quit()));
}));
cb.send(Box::new(move |s| {
s.pop_layer();
s.add_layer(Dialog::empty()
.title("Work done!")
.content(TextView::new("Phew, that was some \
work!"))
.button("Sure!", |s| s.quit()));
}))
.unwrap();
});
}))

View File

@ -97,6 +97,7 @@ pub use printer::Printer;
use backend::{Backend, NcursesBackend};
use std::sync::mpsc;
use std::any::Any;
use std::collections::HashMap;
use std::path::Path;
@ -126,6 +127,9 @@ pub struct Cursive {
active_screen: ScreenId,
running: bool,
cb_source: mpsc::Receiver<Box<Fn(&mut Cursive) + Send>>,
cb_sink: mpsc::Sender<Box<Fn(&mut Cursive) + Send>>,
}
new_default!(Cursive);
@ -143,6 +147,8 @@ impl Cursive {
let theme = theme::load_default();
// let theme = theme::load_theme("assets/style.toml").unwrap();
let (tx, rx) = mpsc::channel();
let mut res = Cursive {
theme: theme,
screens: Vec::new(),
@ -150,6 +156,8 @@ impl Cursive {
menubar: view::Menubar::new(),
active_screen: 0,
running: true,
cb_source: rx,
cb_sink: tx,
};
res.screens.push(StackView::new());
@ -157,6 +165,17 @@ impl Cursive {
res
}
/// Returns a sink for asynchronous callbacks.
///
/// Returns the sender part of a channel, that allows to send
/// callbacks to `self` from other threads.
///
/// Callbacks will be executed in the order
/// of arrival on the next event cycle.
pub fn cb_sink(&self) -> &mpsc::Sender<Box<Fn(&mut Cursive) + Send>> {
&self.cb_sink
}
/// Selects the menubar
pub fn select_menubar(&mut self) {
self.menubar.take_focus(direction::Direction::none());
@ -454,6 +473,10 @@ impl Cursive {
// And the big event loop begins!
while self.running {
if let Ok(cb) = self.cb_source.try_recv() {
cb(self);
}
// Do we need to redraw everytime?
// Probably, actually.
// TODO: Do we need to re-layout everytime?

View File

@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
@ -6,7 +6,6 @@ use {Cursive, Printer};
use vec::Vec2;
use align::HAlign;
use direction::Orientation;
use event::*;
use theme::{ColorStyle, Effect};
use view::View;
@ -18,7 +17,6 @@ pub struct ProgressBar {
max: usize,
value: Arc<AtomicUsize>,
// TODO: use a Promise instead?
callback: Option<Arc<Mutex<CbPromise>>>,
label_maker: Box<Fn(usize, (usize, usize)) -> String>,
}
@ -42,7 +40,6 @@ impl ProgressBar {
min: 0,
max: 100,
value: Arc::new(AtomicUsize::new(0)),
callback: None,
label_maker: Box::new(make_percentage),
}
}
@ -53,14 +50,6 @@ impl ProgressBar {
self
}
/// Sets the callback to follow.
///
/// Whenever `callback` is set, it will be called on the next event loop.
pub fn with_callback(mut self, callback: Arc<Mutex<CbPromise>>) -> Self {
self.callback = Some(callback);
self
}
/// Sets the label generator.
///
/// The given function will be called with `(value, (min, max))`.
@ -132,16 +121,6 @@ impl View for ProgressBar {
});
}
fn on_event(&mut self, _: Event) -> EventResult {
if let Some(ref cb) = self.callback {
if let Some(cb) = cb.lock().unwrap().take() {
return EventResult::Consumed(Some(cb.into()));
}
}
EventResult::Ignored
}
fn get_min_size(&mut self, size: Vec2) -> Vec2 {
size.with_axis(Orientation::Vertical, 1)
}