on master help sign in
Add a `Spinner`
b2804b1wojciechkepka
helix-term/src/ui/mod.rs+2
@@ -5,6 +5,7 @@ mod menu;
5mod picker;
5mod picker;
6mod popup;
6mod popup;
7mod prompt;
7mod prompt;
8+mod spinner;
8mod text;
9mod text;
9
10
10pub use completion::Completion;
11pub use completion::Completion;
@@ -14,6 +15,7 @@ pub use menu::Menu;
14pub use picker::Picker;
15pub use picker::Picker;
15pub use popup::Popup;
16pub use popup::Popup;
16pub use prompt::{Prompt, PromptEvent};
17pub use prompt::{Prompt, PromptEvent};
18+pub use spinner::{ProgressSpinners, Spinner};
17pub use text::Text;
19pub use text::Text;
18
20
19pub use tui::layout::Rect;
21pub use tui::layout::Rect;
helix-term/src/ui/spinner.rsnew file+75
@@ -0,0 +1,75 @@
1+use std::{collections::HashMap, time::SystemTime};
2+
3+#[derive(Default, Debug)]
4+pub struct ProgressSpinners {
5+ inner: HashMap<usize, Spinner>,
6+}
7+
8+impl ProgressSpinners {
9+ pub fn get(&self, id: usize) -> Option<&Spinner> {
10+ self.inner.get(&id)
11+ }
12+
13+ pub fn get_or_create(&mut self, id: usize) -> &mut Spinner {
14+ self.inner.entry(id).or_insert_with(Spinner::default)
15+ }
16+}
17+
18+impl Default for Spinner {
19+ fn default() -> Self {
20+ Self::dots(80)
21+ }
22+}
23+
24+#[derive(Debug)]
25+pub struct Spinner {
26+ frames: Vec<&'static str>,
27+ count: usize,
28+ start: Option<SystemTime>,
29+ interval: u64,
30+}
31+
32+impl Spinner {
33+ /// Creates a new spinner with `frames` and `interval`.
34+ /// Expects the frames count and interval to be greater than 0.
35+ pub fn new(frames: Vec<&'static str>, interval: u64) -> Self {
36+ let count = frames.len();
37+ assert!(count > 0);
38+ assert!(interval > 0);
39+
40+ Self {
41+ frames,
42+ count,
43+ interval,
44+ start: None,
45+ }
46+ }
47+
48+ pub fn dots(interval: u64) -> Self {
49+ Self::new(vec!["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"], interval)
50+ }
51+
52+ pub fn start(&mut self) {
53+ self.start = Some(SystemTime::now());
54+ }
55+
56+ pub fn frame(&self) -> Option<&str> {
57+ let idx = (self
58+ .start
59+ .map(|time| SystemTime::now().duration_since(time))?
60+ .ok()?
61+ .as_millis()
62+ / self.interval as u128) as usize
63+ % self.count;
64+
65+ self.frames.get(idx).copied()
66+ }
67+
68+ pub fn stop(&mut self) {
69+ self.start = None;
70+ }
71+
72+ pub fn is_stopped(&self) -> bool {
73+ self.start.is_none()
74+ }
75+}