conf: add setting for progress spinner
Choose between 30-something built in sequences (integers between 0-30) or define your own list of strings for the progress spinner animation. Default: 0jmap-eventsource
parent
310d02042f
commit
4e72b6552a
|
@ -654,6 +654,17 @@ 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(0);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
StatusBar {
|
StatusBar {
|
||||||
container,
|
container,
|
||||||
status: String::with_capacity(256),
|
status: String::with_capacity(256),
|
||||||
|
@ -667,12 +678,13 @@ impl StatusBar {
|
||||||
height: 1,
|
height: 1,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
auto_complete: AutoComplete::new(Vec::new()),
|
auto_complete: AutoComplete::new(Vec::new()),
|
||||||
progress_spinner: ProgressSpinner::new(1),
|
progress_spinner,
|
||||||
in_progress_jobs: HashSet::default(),
|
in_progress_jobs: HashSet::default(),
|
||||||
done_jobs: HashSet::default(),
|
done_jobs: HashSet::default(),
|
||||||
cmd_history: crate::command::history::old_cmd_history(),
|
cmd_history: crate::command::history::old_cmd_history(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_status_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw_status_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
let mut attribute = crate::conf::value(context, "status.bar");
|
let mut attribute = crate::conf::value(context, "status.bar");
|
||||||
if !context.settings.terminal.use_color() {
|
if !context.settings.terminal.use_color() {
|
||||||
|
@ -706,7 +718,10 @@ impl StatusBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (x, y) = bottom_right!(area);
|
let (mut x, y) = bottom_right!(area);
|
||||||
|
if self.progress_spinner.is_active() {
|
||||||
|
x = x.saturating_sub(1 + self.progress_spinner.width);
|
||||||
|
}
|
||||||
for (idx, c) in self.display_buffer.chars().rev().enumerate() {
|
for (idx, c) in self.display_buffer.chars().rev().enumerate() {
|
||||||
if let Some(cell) = grid.get_mut(x.saturating_sub(idx).saturating_sub(1), y) {
|
if let Some(cell) = grid.get_mut(x.saturating_sub(idx).saturating_sub(1), y) {
|
||||||
cell.set_ch(c);
|
cell.set_ch(c);
|
||||||
|
@ -714,6 +729,16 @@ impl StatusBar {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.progress_spinner.is_dirty() {
|
||||||
|
self.progress_spinner.draw(
|
||||||
|
grid,
|
||||||
|
(
|
||||||
|
pos_dec(bottom_right!(area), (self.progress_spinner.width, 0)),
|
||||||
|
bottom_right!(area),
|
||||||
|
),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
@ -763,26 +788,16 @@ impl Component for StatusBar {
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.progress_spinner.is_dirty() {
|
|
||||||
self.progress_spinner.draw(
|
|
||||||
grid,
|
|
||||||
(
|
|
||||||
(get_x(bottom_right).saturating_sub(1), get_y(bottom_right)),
|
|
||||||
bottom_right,
|
|
||||||
),
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.mode != UIMode::Command && !self.is_dirty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
self.draw_status_bar(
|
self.draw_status_bar(
|
||||||
grid,
|
grid,
|
||||||
(set_y(upper_left, get_y(bottom_right)), bottom_right),
|
(set_y(upper_left, get_y(bottom_right)), bottom_right),
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if self.mode != UIMode::Command && !self.is_dirty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
UIMode::Normal => {}
|
UIMode::Normal => {}
|
||||||
UIMode::Command => {
|
UIMode::Command => {
|
||||||
|
@ -1020,7 +1035,11 @@ impl Component for StatusBar {
|
||||||
match event {
|
match event {
|
||||||
UIEvent::ChangeMode(m) => {
|
UIEvent::ChangeMode(m) => {
|
||||||
let offset = self.status.find('|').unwrap_or_else(|| self.status.len());
|
let offset = self.status.find('|').unwrap_or_else(|| self.status.len());
|
||||||
self.status.replace_range(..offset, &format!("{} {}", m,
|
self.status.replace_range(
|
||||||
|
..offset,
|
||||||
|
&format!(
|
||||||
|
"{} {}",
|
||||||
|
m,
|
||||||
if self.mouse {
|
if self.mouse {
|
||||||
context
|
context
|
||||||
.settings
|
.settings
|
||||||
|
@ -1032,7 +1051,8 @@ impl Component for StatusBar {
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
));
|
),
|
||||||
|
);
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
self.container.set_dirty(true);
|
self.container.set_dirty(true);
|
||||||
self.mode = *m;
|
self.mode = *m;
|
||||||
|
|
|
@ -1049,18 +1049,17 @@ impl ScrollBar {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProgressSpinner {
|
pub struct ProgressSpinner {
|
||||||
//total_work: usize,
|
|
||||||
//finished: usize,
|
|
||||||
timer: crate::timer::PosixTimer,
|
timer: crate::timer::PosixTimer,
|
||||||
stage: usize,
|
stage: usize,
|
||||||
kind: usize,
|
pub kind: std::result::Result<usize, Vec<String>>,
|
||||||
|
pub width: usize,
|
||||||
active: bool,
|
active: bool,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgressSpinner {
|
impl ProgressSpinner {
|
||||||
const KINDS: [&'static [&'static str]; 15] = [
|
pub const KINDS: [&'static [&'static str]; 30] = [
|
||||||
&["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"],
|
&["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"],
|
||||||
&["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"],
|
&["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"],
|
||||||
&["⣀", "⣄", "⣆", "⣇", "⣧", "⣷", "⣿"],
|
&["⣀", "⣄", "⣆", "⣇", "⣧", "⣷", "⣿"],
|
||||||
|
@ -1076,10 +1075,26 @@ impl ProgressSpinner {
|
||||||
&["▯", "▮"],
|
&["▯", "▮"],
|
||||||
&["◯", "⬤"],
|
&["◯", "⬤"],
|
||||||
&["⚪", "⚫"],
|
&["⚪", "⚫"],
|
||||||
|
&["▖", "▗", "▘", "▝", "▞", "▚", "▙", "▟", "▜", "▛"],
|
||||||
|
&["|", "/", "-", "\\"],
|
||||||
|
&[".", "o", "O", "@", "*"],
|
||||||
|
&["◡◡", "⊙⊙", "◠◠", "⊙⊙"],
|
||||||
|
&["◜ ", " ◝", " ◞", "◟ "],
|
||||||
|
&["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
|
||||||
|
&["▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃"],
|
||||||
|
&[
|
||||||
|
"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉",
|
||||||
|
],
|
||||||
|
&["▖", "▘", "▝", "▗"],
|
||||||
|
&["▌", "▀", "▐", "▄"],
|
||||||
|
&["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"],
|
||||||
|
&["◢", "◣", "◤", "◥"],
|
||||||
|
&["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"],
|
||||||
|
&["⢎⡰", "⢎⡡", "⢎⡑", "⢎⠱", "⠎⡱", "⢊⡱", "⢌⡱", "⢆⡱"],
|
||||||
|
&[".", "o", "O", "°", "O", "o", "."],
|
||||||
];
|
];
|
||||||
|
|
||||||
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
|
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
|
||||||
const VALUE: std::time::Duration = std::time::Duration::from_millis(500);
|
|
||||||
|
|
||||||
pub fn new(kind: usize) -> Self {
|
pub fn new(kind: usize) -> Self {
|
||||||
let timer = crate::timer::PosixTimer::new_with_signal(
|
let timer = crate::timer::PosixTimer::new_with_signal(
|
||||||
|
@ -1088,22 +1103,50 @@ impl ProgressSpinner {
|
||||||
nix::sys::signal::Signal::SIGALRM,
|
nix::sys::signal::Signal::SIGALRM,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
debug!("Requested timer {:?} for ProgressSpinner", timer.si_value);
|
let kind = kind % Self::KINDS.len();
|
||||||
|
let width = Self::KINDS[kind]
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.grapheme_len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
ProgressSpinner {
|
ProgressSpinner {
|
||||||
timer,
|
timer,
|
||||||
stage: 0,
|
stage: 0,
|
||||||
kind: kind % Self::KINDS.len(),
|
kind: Ok(kind),
|
||||||
|
width,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
active: false,
|
active: false,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_active(&self) -> bool {
|
||||||
|
self.active
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_kind(&mut self, kind: usize) {
|
||||||
|
self.stage = 0;
|
||||||
|
self.width = Self::KINDS[kind % Self::KINDS.len()]
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.grapheme_len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
self.kind = Ok(kind % Self::KINDS.len());
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_custom_kind(&mut self, custom: Vec<String>) {
|
||||||
|
self.stage = 0;
|
||||||
|
self.width = custom.iter().map(|f| f.grapheme_len()).max().unwrap_or(0);
|
||||||
|
self.kind = Err(custom);
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
self.active = true;
|
self.active = true;
|
||||||
self.timer
|
self.timer
|
||||||
.set_interval(Self::INTERVAL)
|
.set_interval(Self::INTERVAL)
|
||||||
.set_value(Self::VALUE)
|
.set_value(Self::INTERVAL)
|
||||||
.rearm()
|
.rearm()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,6 +1160,12 @@ impl ProgressSpinner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for ProgressSpinner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for ProgressSpinner {
|
impl fmt::Display for ProgressSpinner {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "progress bar")
|
write!(f, "progress bar")
|
||||||
|
@ -1129,10 +1178,19 @@ impl Component for ProgressSpinner {
|
||||||
let theme_attr = crate::conf::value(context, "status.bar");
|
let theme_attr = crate::conf::value(context, "status.bar");
|
||||||
clear_area(grid, area, theme_attr);
|
clear_area(grid, area, theme_attr);
|
||||||
if self.active {
|
if self.active {
|
||||||
let stage = self.stage;
|
|
||||||
self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[self.kind].len());
|
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
Self::KINDS[self.kind][stage],
|
match self.kind.as_ref() {
|
||||||
|
Ok(kind) => {
|
||||||
|
let stage = self.stage;
|
||||||
|
self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[*kind].len());
|
||||||
|
Self::KINDS[*kind][stage].as_ref()
|
||||||
|
}
|
||||||
|
Err(custom) => {
|
||||||
|
let stage = self.stage;
|
||||||
|
self.stage = (self.stage + 1).wrapping_rem(custom.len());
|
||||||
|
custom[stage].as_ref()
|
||||||
|
}
|
||||||
|
},
|
||||||
grid,
|
grid,
|
||||||
theme_attr.fg,
|
theme_attr.fg,
|
||||||
theme_attr.bg,
|
theme_attr.bg,
|
||||||
|
|
|
@ -47,6 +47,11 @@ pub struct TerminalSettings {
|
||||||
pub window_title: Option<String>,
|
pub window_title: Option<String>,
|
||||||
#[serde(deserialize_with = "non_empty_string")]
|
#[serde(deserialize_with = "non_empty_string")]
|
||||||
pub file_picker_command: Option<String>,
|
pub file_picker_command: Option<String>,
|
||||||
|
/// Choose between 30-something built in sequences (integers between 0-30) or define your own
|
||||||
|
/// list of strings for the progress spinner animation.
|
||||||
|
/// Default: 0
|
||||||
|
#[serde(default)]
|
||||||
|
pub progress_spinner_sequence: Option<ProgressSpinnerSequence>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TerminalSettings {
|
impl Default for TerminalSettings {
|
||||||
|
@ -60,6 +65,7 @@ impl Default for TerminalSettings {
|
||||||
mouse_flag: Some("🖱️ ".to_string()),
|
mouse_flag: Some("🖱️ ".to_string()),
|
||||||
window_title: Some("meli".to_string()),
|
window_title: Some("meli".to_string()),
|
||||||
file_picker_command: None,
|
file_picker_command: None,
|
||||||
|
progress_spinner_sequence: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,3 +106,12 @@ impl DotAddressable for TerminalSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ProgressSpinnerSequence {
|
||||||
|
Integer(usize),
|
||||||
|
Custom(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DotAddressable for ProgressSpinnerSequence {}
|
||||||
|
|
Loading…
Reference in New Issue