use { crossbeam_channel::{ Receiver, Sender, unbounded, }, cursive::{ Cursive, CursiveExt, Printer, Rect, View, XY, direction::Direction, event::{ Event, EventResult, }, view::{ CannotFocus, Nameable, Selector, ViewNotFound, }, views::{ Button, LinearLayout, TextView, } }, std::{ time::Duration, thread::{ self, sleep, }, } }; type CursiveSender = Sender FnOnce(&'a mut Cursive) + Send + 'static)>>; struct DataView { data: Option, view: TextView, } impl DataView { pub fn new() -> Self { Self { data: None, view: TextView::new("uninitialized"), } } fn set_data(&mut self, data: &str) { self.data = Some(data.to_owned()); self.view.set_content(data.to_string()) } } impl View for DataView { fn draw(&self, printer: &Printer<'_, '_>) { self.view.draw(printer) } fn layout(&mut self, xy: XY) { self.view.layout(xy) } fn needs_relayout(&self) -> bool { self.view.needs_relayout() } fn required_size(&mut self, constraint: XY) -> XY { self.view.required_size(constraint) } fn on_event(&mut self, event: Event) -> EventResult { self.view.on_event(event) } // fn call_on_any(), should not be needed for this view. fn focus_view(&mut self, selector: &Selector<'_>) -> Result { self.view.focus_view(selector) } fn take_focus( &mut self, source: Direction) -> Result { self.view.take_focus(source) } fn important_area(&self, view_size: XY) -> Rect { self.view.important_area(view_size) } fn type_name(&self) -> &'static str { "DataView" } } fn data_request(cur: &mut Cursive) { if let Some(sender) = cur.user_data::>() { let _ = sender.send(true); } } fn populate_data(s: &mut Cursive, is_data: bool) { if let Some(mut data_view) = s.find_name::("populate_me") { if is_data { data_view.set_data("populated") } else { data_view.set_data("initialized") } } } fn background_thread(reciever: Receiver, sender: CursiveSender) { while let Ok(msg) = reciever.recv() { eprintln!("Received a message. Sleeping 3 seconds before sending response."); sleep(Duration::from_secs(3)); let _ = sender.send(Box::new(move |s| populate_data(s, msg))); } } fn main() { // In order to maintain the main thread responsive in dealing with the user interface, we wish // to offload all server communication to a background thread. This code demonstrates how to // send a message from the ui, process it in the background and send the result back for the // user interface to update the content of its view. // Firstly, we need a channel pair for sending a message from the ui to the worker thread. let (wrk_sender, wrk_receiver) = unbounded::(); // We then construct the actual user interface. Our DataView, a button to trigger populating // it and a button to quit the app. let mut app = Cursive::new(); let mut horizontal = LinearLayout::horizontal(); let mut vertical = LinearLayout::vertical(); let data_view = DataView::new().with_name("populate_me"); let populate_button = Button::new("data", data_request); let quit_button = Button::new("quit", |s| s.quit()); vertical.add_child(data_view); horizontal.add_child(populate_button); horizontal.add_child(quit_button); vertical.add_child(horizontal); app.add_layer(vertical); // Once having created the app, we send a request to initialize our view. let _ = wrk_sender.send(false); // We then place the sender in the user_data, to make it available for any ui function. app.set_user_data(wrk_sender); // Getting the callback sender from the user interface, needed for the worker to respond. let ui_sender = app.cb_sink().clone(); // Then we launch the background worker thread, providing it the appropriate channel ends. let _worker = thread::spawn(move || background_thread(wrk_receiver, ui_sender)); // Lastly we launch the user interface. app.run(); // Three seconds after launching, the text changes from "uninitialized" to "initialized" and // further, three seconds after pressing the button the text becomes "populated". // // Essentially, this design should work for the complete program. Yet with some slightly more // expressive choice of datatypes than merely a bool. }