cursive_extras/views/
loading.rs1use crate::SpannedStrExt;
2use std::{
3 sync::{Arc, RwLock},
4 thread::{self, JoinHandle}
5};
6use crossbeam_channel::{bounded, Receiver, TryRecvError};
7use cursive_core::{
8 Printer, Vec2,
9 view::View,
10 theme::{ColorStyle, PaletteColor},
11 utils::markup::StyledString
12};
13
14type WorkerThread<T> = Arc<RwLock<Option<JoinHandle<T>>>>;
15
16#[derive(Clone)]
39pub struct LoadingAnimation<T: Send + Sync + 'static> {
40 worker: WorkerThread<T>,
41 recv: Receiver<()>,
42 message: StyledString,
43 width: usize,
44 anim_x: usize,
45 reversed: bool
46}
47
48impl<T: Send + Sync> LoadingAnimation<T> {
49 pub fn new<U, M: Into<StyledString>>(message: M, task: U) -> LoadingAnimation<T>
51 where U: FnOnce() -> T + Send + Sync + 'static
52 {
53 let (sender, recv) = bounded(0);
54 let worker = Arc::new(RwLock::new(
55 Some(
56 thread::spawn(move || {
57 let out = task();
58 sender.send(()).unwrap();
59 out
60 })
61 )
62 ));
63 let message = message.into();
64
65 LoadingAnimation {
66 worker,
67 recv,
68 width: message.char_len(),
69 message,
70 anim_x: 0,
71 reversed: false
72 }
73 }
74
75 pub fn is_done(&self) -> bool {
77 match self.recv.try_recv() {
78 Ok(()) => true,
79 Err(e) => e == TryRecvError::Disconnected
80 }
81 }
82
83 #[track_caller]
92 pub fn finish(&mut self) -> Option<T> {
93 let worker = self.worker.write().unwrap().take()?;
94 Some(worker.join().unwrap())
95 }
96}
97
98impl<T: Send + Sync> View for LoadingAnimation<T> {
99 fn draw(&self, printer: &Printer) {
100 let style = ColorStyle::new(
101 PaletteColor::Highlight,
102 PaletteColor::Background,
103 );
104 printer.with_color(style, |printer| {
105 printer.print((self.anim_x, 0), "███");
106 });
107 printer.print_styled((0, 1), self.message.as_spanned_str());
108 }
109
110 fn layout(&mut self, _: Vec2) {
111 if self.width > 0 {
112 if self.reversed {
113 if self.anim_x == 0 {
114 self.reversed = false;
115 }
116 else {
117 self.anim_x -= 1;
118 }
119 }
120 else if self.anim_x < self.width - 3 {
121 self.anim_x += 1;
122 }
123 else {
124 self.reversed = true;
125 }
126 }
127 }
128
129 fn required_size(&mut self, _: Vec2) -> Vec2 { Vec2::new(self.message.char_len(), 2) }
130}