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

View File

@ -97,6 +97,7 @@ pub use printer::Printer;
use backend::{Backend, NcursesBackend}; use backend::{Backend, NcursesBackend};
use std::sync::mpsc;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
@ -126,6 +127,9 @@ pub struct Cursive {
active_screen: ScreenId, active_screen: ScreenId,
running: bool, running: bool,
cb_source: mpsc::Receiver<Box<Fn(&mut Cursive) + Send>>,
cb_sink: mpsc::Sender<Box<Fn(&mut Cursive) + Send>>,
} }
new_default!(Cursive); new_default!(Cursive);
@ -143,6 +147,8 @@ impl Cursive {
let theme = theme::load_default(); let theme = theme::load_default();
// let theme = theme::load_theme("assets/style.toml").unwrap(); // let theme = theme::load_theme("assets/style.toml").unwrap();
let (tx, rx) = mpsc::channel();
let mut res = Cursive { let mut res = Cursive {
theme: theme, theme: theme,
screens: Vec::new(), screens: Vec::new(),
@ -150,6 +156,8 @@ impl Cursive {
menubar: view::Menubar::new(), menubar: view::Menubar::new(),
active_screen: 0, active_screen: 0,
running: true, running: true,
cb_source: rx,
cb_sink: tx,
}; };
res.screens.push(StackView::new()); res.screens.push(StackView::new());
@ -157,6 +165,17 @@ impl Cursive {
res 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 /// Selects the menubar
pub fn select_menubar(&mut self) { pub fn select_menubar(&mut self) {
self.menubar.take_focus(direction::Direction::none()); self.menubar.take_focus(direction::Direction::none());
@ -454,6 +473,10 @@ impl Cursive {
// And the big event loop begins! // And the big event loop begins!
while self.running { while self.running {
if let Ok(cb) = self.cb_source.try_recv() {
cb(self);
}
// Do we need to redraw everytime? // Do we need to redraw everytime?
// Probably, actually. // Probably, actually.
// TODO: Do we need to re-layout everytime? // 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}; use std::sync::atomic::{AtomicUsize, Ordering};
@ -6,7 +6,6 @@ use {Cursive, Printer};
use vec::Vec2; use vec::Vec2;
use align::HAlign; use align::HAlign;
use direction::Orientation; use direction::Orientation;
use event::*;
use theme::{ColorStyle, Effect}; use theme::{ColorStyle, Effect};
use view::View; use view::View;
@ -18,7 +17,6 @@ pub struct ProgressBar {
max: usize, max: usize,
value: Arc<AtomicUsize>, value: Arc<AtomicUsize>,
// TODO: use a Promise instead? // TODO: use a Promise instead?
callback: Option<Arc<Mutex<CbPromise>>>,
label_maker: Box<Fn(usize, (usize, usize)) -> String>, label_maker: Box<Fn(usize, (usize, usize)) -> String>,
} }
@ -42,7 +40,6 @@ impl ProgressBar {
min: 0, min: 0,
max: 100, max: 100,
value: Arc::new(AtomicUsize::new(0)), value: Arc::new(AtomicUsize::new(0)),
callback: None,
label_maker: Box::new(make_percentage), label_maker: Box::new(make_percentage),
} }
} }
@ -53,14 +50,6 @@ impl ProgressBar {
self 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. /// Sets the label generator.
/// ///
/// The given function will be called with `(value, (min, max))`. /// 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 { fn get_min_size(&mut self, size: Vec2) -> Vec2 {
size.with_axis(Orientation::Vertical, 1) size.with_axis(Orientation::Vertical, 1)
} }