rab/tui/components/
cancellable_loader.rs1use crate::tui::Component;
2use crate::tui::components::loader::Loader;
3use crate::tui::keybindings::{ACTION_SELECT_CANCEL, get_keybindings};
4
5pub struct CancellableLoader {
8 loader: Loader,
9 cancelled: bool,
10 pub on_abort: Option<Box<dyn FnMut()>>,
11}
12
13impl CancellableLoader {
14 pub fn new(
15 spinner_color_fn: Box<dyn Fn(&str) -> String>,
16 message_color_fn: Box<dyn Fn(&str) -> String>,
17 message: impl Into<String>,
18 ) -> Self {
19 Self {
20 loader: Loader::new(spinner_color_fn, message_color_fn, message),
21 cancelled: false,
22 on_abort: None,
23 }
24 }
25
26 pub fn start(&mut self) {
27 self.loader.start();
28 }
29
30 pub fn stop(&mut self) {
31 self.loader.stop();
32 }
33
34 pub fn dispose(&mut self) {
36 self.loader.stop();
37 }
38
39 pub fn is_cancelled(&self) -> bool {
40 self.cancelled
41 }
42
43 pub fn tick(&mut self) -> bool {
44 self.loader.tick()
45 }
46
47 pub fn set_message(&mut self, message: impl Into<String>) {
48 self.loader.set_message(message);
49 }
50}
51
52impl Component for CancellableLoader {
53 fn render(&self, width: usize) -> Vec<String> {
54 self.loader.render(width)
55 }
56
57 fn handle_input(&mut self, key: &crossterm::event::KeyEvent) -> bool {
58 let kb = get_keybindings();
59 if kb.matches(key, ACTION_SELECT_CANCEL) {
60 self.cancelled = true;
61 if let Some(ref mut cb) = self.on_abort {
62 cb();
63 }
64 return true;
65 }
66 false
67 }
68
69 fn invalidate(&mut self) {
70 self.loader.invalidate();
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
78
79 #[test]
80 fn test_cancel_on_escape() {
81 let mut cl = CancellableLoader::new(
82 Box::new(|s| s.to_string()),
83 Box::new(|s| s.to_string()),
84 "Working...",
85 );
86 let escape = KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE);
87 assert!(!cl.is_cancelled());
88 cl.handle_input(&escape);
89 assert!(cl.is_cancelled());
90 }
91
92 #[test]
93 fn test_dispose_stops() {
94 let mut cl = CancellableLoader::new(
95 Box::new(|s| s.to_string()),
96 Box::new(|s| s.to_string()),
97 "Working...",
98 );
99 cl.start();
100 cl.dispose();
101 assert!(!cl.tick());
103 }
104}