utilities/ProgressSpinner: add interval field and new spinners

lazy_fetch
Manos Pitsidianakis 2021-01-10 23:38:13 +02:00
parent 2dfeb29b75
commit 48d4343082
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 163 additions and 87 deletions

View File

@ -1039,49 +1039,67 @@ This setting can be toggled with
String to show in status bar if mouse is active. String to show in status bar if mouse is active.
.\" default value .\" default value
.Pq Em 🖱️ .Pq Em 🖱️
.It Ic progress_spinner_sequence Ar Either \&< Integer, [String] \&> .It Ic progress_spinner_sequence Ar Either \&< Integer, ProgressSpinner \&>
Choose between 30-something built in sequences (integers between 0-30) or define your own list of strings for the progress spinner animation. Choose between 37 built in sequences (integers between 0-36) or define your own list of strings for the progress spinner animation.
Set to an empty array to disable the progress spinner. Set to an empty array to disable the progress spinner.
.\" default value .\" default value
.Pq Em 19 .Pq Em 20
.Pp
Builtin sequences are: Builtin sequences are:
.Bd -literal .Bd -literal
0 ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] 0 ["-", "\\", "|", "/"]
1 ["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"] 1 ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
2 ["⣀", "⣄", "⣆", "⣇", "⣧", "⣷", "⣿"] 2 ["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"]
3 ["○", "◔", "◐", "◕", "⬤"] 3 ["⣀", "⣄", "⣆", "⣇", "⣧", "⣷", "⣿"]
4 ["□", "◱", "◧", "▣", "■"] 4 ["○", "◔", "◐", "◕", "⬤"]
5 ["□", "◱", "▨", "▩", "■"] 5 ["□", "◱", "◧", "▣", "■"]
6 ["□", "◱", "▥", "▦", "■"] 6 ["□", "◱", "▨", "▩", "■"]
7 ["░", "▒", "▓", "█"] 7 ["□", "◱", "▥", "▦", "■"]
8 ["░", "█"] 8 ["░", "▒", "▓", "█"]
9 ["⬜", "⬛"] 9 ["░", "█"]
10 ["▱", "▰"] 10 ["⬜", "⬛"]
11 ["▭", "◼"] 11 ["▱", "▰"]
12 ["▯", "▮"] 12 ["▭", "◼"]
13 ["◯", "⬤"] 13 ["▯", "▮"]
14 ["⚪", "⚫"] 14 ["◯", "⬤"]
15 ["▖", "▗", "▘", "▝", "▞", "▚", "▙", "▟", "▜", "▛"] 15 ["⚪", "⚫"]
16 ["|", "/", "-", "\\"] 16 ["▖", "▗", "▘", "▝", "▞", "▚", "▙", "▟", "▜", "▛"]
17 [".", "o", "O", "@", "*"] 17 ["|", "/", "-", "\\"]
18 ["◡◡", "⊙⊙", "◠◠", "⊙⊙"] 18 [".", "o", "O", "@", "*"]
19 ["◜ ", " ◝", " ◞", "◟ "] 19 ["◡◡", "⊙⊙", "◠◠", "⊙⊙"]
10 ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"] 20 ["◜ ", " ◝", " ◞", "◟ "]
11 ["▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃"] 21 ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"]
22 ["▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"] 22 ["▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃"]
23 ["▖", "▘", "▝", "▗"] 23 [ "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉" ]
24 ["▌", "▀", "▐", "▄"] 24 ["▖", "▘", "▝", "▗"]
25 ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"] 25 ["▌", "▀", "▐", "▄"]
26 ["◢", "◣", "◤", "◥"] 26 ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"]
27 ["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"] 27 ["◢", "◣", "◤", "◥"]
28 ["⢎⡰", "⢎⡡", "⢎⡑", "⢎⠱", "⠎⡱", "⢊⡱", "⢌⡱", "⢆⡱"] 28 ["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"]
29 [".", "o", "O", "°", "O", "o", "."] 29 ["⢎⡰", "⢎⡡", "⢎⡑", "⢎⠱", "⠎⡱", "⢊⡱", "⢌⡱", "⢆⡱"]
30 [".", "o", "O", "°", "O", "o", "."]
31 ["㊂", "㊀", "㊁"]
32 ["💛 ", "💙 ", "💜 ", "💚 ", "❤️ "]
33 [ "🕛 ", "🕐 ", "🕑 ", "🕒 ", "🕓 ", "🕔 ", "🕕 ", "🕖 ", "🕗 ", "🕘 ", "🕙 ", "🕚 " ]
34 ["🌍 ", "🌎 ", "🌏 "]
35 [ "[ ]", "[= ]", "[== ]", "[=== ]", "[ ===]", "[ ==]", "[ =]", "[ ]", "[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]" ]
36 ["🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "]
.Ed .Ed
Or, define an array of strings each consisting of a frame in the progress sequence indicator: .Pp
Or, define an array of strings each consisting of a frame in the progress sequence indicator for a custom spinner:
.Bl -tag -width 36n
.It Ic interval_ms Ar u64
.Pq Em optional
Frame interval.
.\" default value
.Pq 50
.It Ic frames Ar [String]
The animation frames.
.El
.Pp
Example:
.Bd -literal .Bd -literal
# 𝄈⡂″⡈߳܃⢂:߳̈⢁܄ː“⢐″„⠑։ ⡁⡈;ܹ⡂։𝂬̤⡂꞉⣀ܹ⢁⠊𝄈⠉⠑ܸ̈׃ ;⢐;߳⠡܉˸⠒߳꞉⁚𝂬⠑⠒܅⠊;⠔⠢܄ ”⠉ֵ”⢂⢁̈⁚⠊˸⠌ܸ̤⣀𝂬⠤⠨⠢‥¨ ⡠܉꞉꞉⠑׃⠑⡐⠨؛ܸ܆„ܹ⡈⢁;⢄܄؛ ܲ⢄⠡⡁‥؛ܲ⢂“⢈։⠔⢄”꞉܉⠔ progress_spinner_sequence = { interval_ms = 150, frames = [ "-", "=", "≡" ] }
# Taken from @SmoothUnicode@botsin.space
progress_spinner_sequence = ["։","𝄈","⡂","″","⡈߳","܃","⢂",":߳̈","⢁","܄","ː","“","⢐","″","„","⠑","։"," ","⡁","⡈",";ܹ","⡂","։","𝂬̤","⡂","","⣀ܹ","⢁","⠊","𝄈","⠉","⠑ܸ̈","׃"," ",";","⢐",";߳","⠡","܉","˸","⠒߳","","","𝂬","⠑","⠒","܅","⠊",";","⠔","⠢","܄"," ","”","⠉ֵ","”","⢂","⢁̈","","⠊","˸","⠌ܸ̤","⣀","𝂬","⠤","⠨","⠢","‥","¨"," ","⡠","܉","","","⠑","׃","⠑","⡐","⠨","؛ܸ","܆","„ܹ","⡈","⢁",";","⢄܄","؛"," ܲ","⢄","⠡","⡁","‥","؛ܲ","⢂","“","⢈","։","⠔","⢄","”","","܉","⠔"]
.Ed .Ed
.El .El
.Sh LOG .Sh LOG

View File

@ -78,13 +78,16 @@ impl fmt::Display for StatusBar {
impl StatusBar { impl StatusBar {
pub fn new(context: &Context, container: Box<dyn Component>) -> Self { pub fn new(context: &Context, container: Box<dyn Component>) -> Self {
let mut progress_spinner = ProgressSpinner::new(19, context); let mut progress_spinner = ProgressSpinner::new(20, context);
match context.settings.terminal.progress_spinner_sequence.as_ref() { match context.settings.terminal.progress_spinner_sequence.as_ref() {
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => { Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
progress_spinner.set_kind(*k); progress_spinner.set_kind(*k);
} }
Some(conf::terminal::ProgressSpinnerSequence::Custom(ref s)) => { Some(conf::terminal::ProgressSpinnerSequence::Custom {
progress_spinner.set_custom_kind(s.clone()); ref frames,
ref interval_ms,
}) => {
progress_spinner.set_custom_kind(frames.clone(), *interval_ms);
} }
None => {} None => {}
} }
@ -472,13 +475,16 @@ impl Component for StatusBar {
match event { match event {
UIEvent::ConfigReload { old_settings: _ } => { UIEvent::ConfigReload { old_settings: _ } => {
let mut progress_spinner = ProgressSpinner::new(19, context); let mut progress_spinner = ProgressSpinner::new(20, context);
match context.settings.terminal.progress_spinner_sequence.as_ref() { match context.settings.terminal.progress_spinner_sequence.as_ref() {
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => { Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
progress_spinner.set_kind(*k); progress_spinner.set_kind(*k);
} }
Some(conf::terminal::ProgressSpinnerSequence::Custom(ref s)) => { Some(conf::terminal::ProgressSpinnerSequence::Custom {
progress_spinner.set_custom_kind(s.clone()); ref frames,
ref interval_ms,
}) => {
progress_spinner.set_custom_kind(frames.clone(), *interval_ms);
} }
None => {} None => {}
} }

View File

@ -22,6 +22,7 @@
use super::*; use super::*;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration;
type AutoCompleteFn = Box<dyn Fn(&Context, &str) -> Vec<AutoCompleteEntry> + Send + Sync>; type AutoCompleteFn = Box<dyn Fn(&Context, &str) -> Vec<AutoCompleteEntry> + Send + Sync>;
@ -1238,54 +1239,93 @@ pub struct ProgressSpinner {
} }
impl ProgressSpinner { impl ProgressSpinner {
pub const KINDS: [&'static [&'static str]; 30] = [ pub const KINDS: [(Duration, &'static [&'static str]); 37] = [
&["", "", "", "", "", "", "", ""], (Duration::from_millis(130), &["-", "\\", "|", "/"]),
&["", "", "", "", "", "", ""], (Self::INTERVAL, &["", "", "", "", "", "", "", ""]),
&["", "", "", "", "", "", ""], (Self::INTERVAL, &["", "", "", "", "", "", ""]),
&["", "", "", "", ""], (Self::INTERVAL, &["", "", "", "", "", "", ""]),
&["", "", "", "", ""], (Self::INTERVAL, &["", "", "", "", ""]),
&["", "", "", "", ""], (Self::INTERVAL, &["", "", "", "", ""]),
&["", "", "", "", ""], (Self::INTERVAL, &["", "", "", "", ""]),
&["", "", "", ""], (Self::INTERVAL, &["", "", "", "", ""]),
&["", ""], (Self::INTERVAL, &["", "", "", ""]),
&["", ""], (Self::INTERVAL, &["", ""]),
&["", ""], (Self::INTERVAL, &["", ""]),
&["", ""], (Self::INTERVAL, &["", ""]),
&["", ""], (Self::INTERVAL, &["", ""]),
&["", ""], (Self::INTERVAL, &["", ""]),
&["", ""], (Self::INTERVAL, &["", ""]),
&["", "", "", "", "", "", "", "", "", ""], (Self::INTERVAL, &["", ""]),
&["|", "/", "-", "\\"], (
&[".", "o", "O", "@", "*"], Self::INTERVAL,
&["◡◡", "⊙⊙", "◠◠", "⊙⊙"], &["", "", "", "", "", "", "", "", "", ""],
&["", "", "", ""], ),
&["", "", "", "", "", "", "", ""], (Self::INTERVAL, &["|", "/", "-", "\\"]),
&["", "", "", "", "", "", "", "", "", "", "", ""], (Self::INTERVAL, &[".", "o", "O", "@", "*"]),
&[ (Self::INTERVAL, &["◡◡", "⊙⊙", "◠◠", "⊙⊙"]),
"", "", "", "", "", "", "", "", "", "", "", "", "", (Self::INTERVAL, &["", "", "", ""]),
], (Self::INTERVAL, &["", "", "", "", "", "", "", ""]),
&["", "", "", ""], (
&["", "", "", ""], Self::INTERVAL,
&["", "", "", "", "", "", "", ""], &["", "", "", "", "", "", "", "", "", "", "", ""],
&["", "", "", ""], ),
&["", "", "", "", "", "", "", ""], (
&["⢎⡰", "⢎⡡", "⢎⡑", "⢎⠱", "⠎⡱", "⢊⡱", "⢌⡱", "⢆⡱"], Self::INTERVAL,
&[".", "o", "O", "°", "O", "o", "."], &[
"", "", "", "", "", "", "", "", "", "", "", "", "",
],
),
(Self::INTERVAL, &["", "", "", ""]),
(Self::INTERVAL, &["", "", "", ""]),
(Self::INTERVAL, &["", "", "", "", "", "", "", ""]),
(Self::INTERVAL, &["", "", "", ""]),
(Self::INTERVAL, &["", "", "", "", "", "", "", ""]),
(
Self::INTERVAL,
&["⢎⡰", "⢎⡡", "⢎⡑", "⢎⠱", "⠎⡱", "⢊⡱", "⢌⡱", "⢆⡱"],
),
(Self::INTERVAL, &[".", "o", "O", "°", "O", "o", "."]),
(Duration::from_millis(100), &["", "", ""]),
(
Duration::from_millis(100),
&["💛 ", "💙 ", "💜 ", "💚 ", "❤️ "],
),
(
Duration::from_millis(100),
&[
"🕛 ", "🕐 ", "🕑 ", "🕒 ", "🕓 ", "🕔 ", "🕕 ", "🕖 ", "🕗 ", "🕘 ", "🕙 ", "🕚 ",
],
),
(Duration::from_millis(100), &["🌍 ", "🌎 ", "🌏 "]),
(
Duration::from_millis(80),
&[
"[ ]", "[= ]", "[== ]", "[=== ]", "[ ===]", "[ ==]", "[ =]", "[ ]",
"[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]",
],
),
(
Duration::from_millis(80),
&["🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "],
),
]; ];
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(50); pub const INTERVAL_MS: u64 = 50;
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(Self::INTERVAL_MS);
pub fn new(kind: usize, context: &Context) -> Self { pub fn new(kind: usize, context: &Context) -> Self {
let timer = context
.job_executor
.clone()
.create_timer(Self::INTERVAL, Self::INTERVAL);
let kind = kind % Self::KINDS.len(); let kind = kind % Self::KINDS.len();
let width = Self::KINDS[kind] let width = Self::KINDS[kind]
.1
.iter() .iter()
.map(|f| f.grapheme_len()) .map(|f| f.grapheme_len())
.max() .max()
.unwrap_or(0); .unwrap_or(0);
let interval = Self::KINDS[kind].0;
let timer = context
.job_executor
.clone()
.create_timer(interval, interval);
let mut theme_attr = crate::conf::value(context, "status.bar"); let mut theme_attr = crate::conf::value(context, "status.bar");
if !context.settings.terminal.use_color() { if !context.settings.terminal.use_color() {
theme_attr.attrs |= Attr::REVERSE; theme_attr.attrs |= Attr::REVERSE;
@ -1310,21 +1350,25 @@ impl ProgressSpinner {
pub fn set_kind(&mut self, kind: usize) { pub fn set_kind(&mut self, kind: usize) {
self.stage = 0; self.stage = 0;
self.width = Self::KINDS[kind % Self::KINDS.len()] self.width = Self::KINDS[kind % Self::KINDS.len()]
.1
.iter() .iter()
.map(|f| f.grapheme_len()) .map(|f| f.grapheme_len())
.max() .max()
.unwrap_or(0); .unwrap_or(0);
self.kind = Ok(kind % Self::KINDS.len()); self.kind = Ok(kind % Self::KINDS.len());
let interval = Self::KINDS[kind % Self::KINDS.len()].0;
self.timer.set_interval(interval);
self.dirty = true; self.dirty = true;
} }
pub fn set_custom_kind(&mut self, custom: Vec<String>) { pub fn set_custom_kind(&mut self, frames: Vec<String>, interval: u64) {
self.stage = 0; self.stage = 0;
self.width = custom.iter().map(|f| f.grapheme_len()).max().unwrap_or(0); self.width = frames.iter().map(|f| f.grapheme_len()).max().unwrap_or(0);
if self.width == 0 { if self.width == 0 {
self.stop(); self.stop();
} }
self.kind = Err(custom); self.kind = Err(frames);
self.timer.set_interval(Duration::from_millis(interval));
self.dirty = true; self.dirty = true;
} }
@ -1356,7 +1400,7 @@ impl Component for ProgressSpinner {
if self.active { if self.active {
write_string_to_grid( write_string_to_grid(
match self.kind.as_ref() { match self.kind.as_ref() {
Ok(kind) => Self::KINDS[*kind][self.stage].as_ref(), Ok(kind) => (Self::KINDS[*kind].1)[self.stage].as_ref(),
Err(custom) => custom[self.stage].as_ref(), Err(custom) => custom[self.stage].as_ref(),
}, },
grid, grid,
@ -1377,7 +1421,7 @@ impl Component for ProgressSpinner {
UIEvent::Timer(id) if *id == self.timer.id() => { UIEvent::Timer(id) if *id == self.timer.id() => {
match self.kind.as_ref() { match self.kind.as_ref() {
Ok(kind) => { Ok(kind) => {
self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[*kind].len()); self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[*kind].1.len());
} }
Err(custom) => { Err(custom) => {
self.stage = (self.stage + 1).wrapping_rem(custom.len()); self.stage = (self.stage + 1).wrapping_rem(custom.len());

View File

@ -114,7 +114,15 @@ impl DotAddressable for TerminalSettings {
#[serde(untagged)] #[serde(untagged)]
pub enum ProgressSpinnerSequence { pub enum ProgressSpinnerSequence {
Integer(usize), Integer(usize),
Custom(Vec<String>), Custom {
frames: Vec<String>,
#[serde(default = "interval_ms_val")]
interval_ms: u64,
},
}
const fn interval_ms_val() -> u64 {
crate::components::utilities::ProgressSpinner::INTERVAL_MS
} }
impl DotAddressable for ProgressSpinnerSequence {} impl DotAddressable for ProgressSpinnerSequence {}