Browse Source

utilities/ProgressSpinner: add interval field and new spinners

master
Manos Pitsidianakis 6 days ago
parent
commit
48d4343082
Signed by: epilys GPG Key ID: 73627C2F690DF710
4 changed files with 163 additions and 87 deletions
  1. +55
    -37
      docs/meli.conf.5
  2. +12
    -6
      src/components/utilities.rs
  3. +87
    -43
      src/components/utilities/widgets.rs
  4. +9
    -1
      src/conf/terminal.rs

+ 55
- 37
docs/meli.conf.5 View File

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

+ 12
- 6
src/components/utilities.rs View File

@ -78,13 +78,16 @@ impl fmt::Display for StatusBar {
impl StatusBar {
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() {
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
progress_spinner.set_kind(*k);
}
Some(conf::terminal::ProgressSpinnerSequence::Custom(ref s)) => {
progress_spinner.set_custom_kind(s.clone());
Some(conf::terminal::ProgressSpinnerSequence::Custom {
ref frames,
ref interval_ms,
}) => {
progress_spinner.set_custom_kind(frames.clone(), *interval_ms);
}
None => {}
}
@ -472,13 +475,16 @@ impl Component for StatusBar {
match event {
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() {
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
progress_spinner.set_kind(*k);
}
Some(conf::terminal::ProgressSpinnerSequence::Custom(ref s)) => {
progress_spinner.set_custom_kind(s.clone());
Some(conf::terminal::ProgressSpinnerSequence::Custom {
ref frames,
ref interval_ms,
}) => {
progress_spinner.set_custom_kind(frames.clone(), *interval_ms);
}
None => {}
}

+ 87
- 43
src/components/utilities/widgets.rs View File

@ -22,6 +22,7 @@
use super::*;
use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;
type AutoCompleteFn = Box<dyn Fn(&Context, &str) -> Vec<AutoCompleteEntry> + Send + Sync>;
@ -1238,54 +1239,93 @@ pub struct ProgressSpinner {
}
impl ProgressSpinner {
pub const KINDS: [&'static [&'static str]; 30] = [
&["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"],
&["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"],
&["⣀", "⣄", "⣆", "⣇", "⣧", "⣷", "⣿"],
&["○", "◔", "◐", "◕", "⬤"],
&["□", "◱", "◧", "▣", "■"],
&["□", "◱", "▨", "▩", "■"],
&["□", "◱", "▥", "▦", "■"],
&["░", "▒", "▓", "█"],
&["░", "█"],
&["⬜", "⬛"],
&["▱", "▰"],
&["▭", "◼"],
&["▯", "▮"],
&["◯", "⬤"],
&["⚪", "⚫"],
&["▖", "▗", "▘", "▝", "▞", "▚", "▙", "▟", "▜", "▛"],
&["|", "/", "-", "\\"],
&[".", "o", "O", "@", "*"],
&["◡◡", "⊙⊙", "◠◠", "⊙⊙"],
&["◜ ", " ◝", " ◞", "◟ "],
&["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
&["▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃"],
&[
"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉",
],
&["▖", "▘", "▝", "▗"],
&["▌", "▀", "▐", "▄"],
&["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"],
&["◢", "◣", "◤", "◥"],
&["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"],
&["⢎⡰", "⢎⡡", "⢎⡑", "⢎⠱", "⠎⡱", "⢊⡱", "⢌⡱", "⢆⡱"],
&[".", "o", "O", "°", "O", "o", "."],
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, &["⚪", "⚫"]),
(
Self::INTERVAL,
&["▖", "▗", "▘", "▝", "▞", "▚", "▙", "▟", "▜", "▛"],
),
(Self::INTERVAL, &["|", "/", "-", "\\"]),
(Self::INTERVAL, &[".", "o", "O", "@", "*"]),
(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", "°", "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 {
let timer = context
.job_executor
.clone()
.create_timer(Self::INTERVAL, Self::INTERVAL);
let kind = kind % Self::KINDS.len();
let width = Self::KINDS[kind]
.1
.iter()
.map(|f| f.grapheme_len())
.max()
.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");
if !context.settings.terminal.use_color() {
theme_attr.attrs |= Attr::REVERSE;
@ -1310,21 +1350,25 @@ impl ProgressSpinner {
pub fn set_kind(&mut self, kind: usize) {
self.stage = 0;
self.width = Self::KINDS[kind % Self::KINDS.len()]
.1
.iter()
.map(|f| f.grapheme_len())
.max()
.unwrap_or(0);
self.kind = Ok(kind % Self::KINDS.len());
let interval = Self::KINDS[kind % Self::KINDS.len()].0;
self.timer.set_interval(interval);
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.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 {
self.stop();
}
self.kind = Err(custom);
self.kind = Err(frames);
self.timer.set_interval(Duration::from_millis(interval));
self.dirty = true;
}
@ -1356,7 +1400,7 @@ impl Component for ProgressSpinner {
if self.active {
write_string_to_grid(
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(),
},
grid,
@ -1377,7 +1421,7 @@ impl Component for ProgressSpinner {
UIEvent::Timer(id) if *id == self.timer.id() => {
match self.kind.as_ref() {
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) => {
self.stage = (self.stage + 1).wrapping_rem(custom.len());

+ 9
- 1
src/conf/terminal.rs View File

@ -114,7 +114,15 @@ impl DotAddressable for TerminalSettings {
#[serde(untagged)]
pub enum ProgressSpinnerSequence {
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 {}

Loading…
Cancel
Save