Skip to content

Commit 652f9d7

Browse files
committed
Add config for progress bars
x
1 parent b7fba3e commit 652f9d7

File tree

11 files changed

+164
-20
lines changed

11 files changed

+164
-20
lines changed

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ default = ["deploy"]
7171

7272
# Should not be included in builds.
7373
logging = ["fern", "log", "time"]
74-
generate_schema = ["schemars", "serde_json", "strum"]
74+
generate_schema = ["schemars", "serde_json", "strum", "mitsein/schemars"]
7575

7676
[dependencies]
7777
anyhow = "1.0.100"
@@ -84,6 +84,7 @@ dirs = "6.0.0"
8484
humantime = "2.3.0"
8585
indexmap = "2.11.4"
8686
indoc = "2.0.7"
87+
mitsein = { version = "0.8", features = ["serde"] }
8788
itertools = "0.14.0"
8889
nohash = "0.2.0"
8990
nvml-wrapper = { version = "0.11.0", optional = true, features = ["legacy-functions"] }

docs/content/configuration/config-file/styling.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,4 @@ These can be set under `[styles.widgets]`:
166166
| `selected_text` | Text styling for text when representing something that is selected | `selected_text = { color = "black", bg_color = "blue", bold = true }` |
167167
| `disabled_text` | Text styling for text when representing something that is disabled | `disabled_text = { color = "black", bg_color = "blue", bold = true }` |
168168
| `thread_text` | Text styling for text when representing process threads. Only usable on Linux at the moment. | `thread_text = { color = "green", bg_color = "blue", bold = true }` |
169+
| `progress_bar_chars` | Characters to use for progress bars | `progress_bar_chars = ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]` |

src/canvas/components/pipe_gauge.rs

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use mitsein::NonEmpty;
12
use tui::{
23
buffer::Buffer,
34
layout::Rect,
@@ -20,6 +21,8 @@ pub enum LabelLimit {
2021
#[derive(Debug, Clone)]
2122
pub struct PipeGauge<'a> {
2223
block: Option<Block<'a>>,
24+
/// Characters to use for the progress bar
25+
progress_chars: &'a NonEmpty<[char]>,
2326
ratio: f64,
2427
start_label: Option<Line<'a>>,
2528
inner_label: Option<Line<'a>>,
@@ -28,8 +31,8 @@ pub struct PipeGauge<'a> {
2831
hide_parts: LabelLimit,
2932
}
3033

31-
impl Default for PipeGauge<'_> {
32-
fn default() -> Self {
34+
impl<'a> PipeGauge<'a> {
35+
pub fn new(progress_chars: &'a NonEmpty<[char]>) -> Self {
3336
Self {
3437
block: None,
3538
ratio: 0.0,
@@ -38,11 +41,10 @@ impl Default for PipeGauge<'_> {
3841
label_style: Style::default(),
3942
gauge_style: Style::default(),
4043
hide_parts: LabelLimit::default(),
44+
progress_chars,
4145
}
4246
}
43-
}
4447

45-
impl<'a> PipeGauge<'a> {
4648
/// The ratio, a value from 0.0 to 1.0 (any other greater or less will be
4749
/// clamped) represents the portion of the pipe gauge to fill.
4850
///
@@ -196,11 +198,9 @@ impl Widget for PipeGauge<'_> {
196198
gauge_area.width,
197199
);
198200

199-
let pipe_end =
200-
start + (f64::from(end.saturating_sub(start)) * self.ratio).floor() as u16;
201-
for col in start..pipe_end {
201+
for (char, col) in progress_bar(self.progress_chars, start, end, self.ratio) {
202202
if let Some(cell) = buf.cell_mut((col, row)) {
203-
cell.set_symbol("|").set_style(Style {
203+
cell.set_char(char).set_style(Style {
204204
fg: self.gauge_style.fg,
205205
bg: None,
206206
add_modifier: self.gauge_style.add_modifier,
@@ -221,3 +221,90 @@ impl Widget for PipeGauge<'_> {
221221
}
222222
}
223223
}
224+
225+
/// Returns an iterator over characters of the progress bar, and their positions
226+
///
227+
/// # Arguments
228+
///
229+
/// - `chars`: The characters to use for the progress bar
230+
/// - `start`: Start position
231+
/// - `end`: End position
232+
/// - `ratio`: How full the progress bar is
233+
fn progress_bar(
234+
chars: &NonEmpty<[char]>, bar_start: u16, end: u16, ratio: f64,
235+
) -> impl Iterator<Item = (char, u16)> {
236+
let bar_len = f64::from(end.saturating_sub(bar_start)) * ratio;
237+
238+
// Length of the bar, without accounting for the partial final character
239+
let bar_len_truncated = bar_len.floor();
240+
241+
// The final progress character to display.
242+
// This might be `None` if we can't display even the minimum segment, in
243+
// which case we won't display anything at all.
244+
//
245+
// This might happen when, for example, we have 5 progress characters: [1, 2, 3, 4, .],
246+
// 10 cells, and our progress is 50.1%. We will display 5 full cells:
247+
//
248+
// 50.1%: .....
249+
//
250+
// If it was 50.2% progress, we would display 5 full cells, and 1 cell with the first character:
251+
//
252+
// 50.2%: .....1
253+
// ^ extra
254+
let final_progress_char = {
255+
// The ratio of a single progress bar character that we lost due to truncation
256+
//
257+
// This ratio will be displayed as a "partial" character
258+
let final_char_ratio = (bar_len - bar_len_truncated).clamp(0.0, 1.0);
259+
260+
let char_index = (final_char_ratio * chars.len().get() as f64).floor() as usize;
261+
262+
// 0-based indexing
263+
char_index.checked_sub(1).and_then(|it| chars.get(it))
264+
};
265+
266+
let bar_end = bar_start + bar_len_truncated as u16;
267+
268+
(bar_start..bar_end)
269+
.map(move |pos| (*chars.last(), pos))
270+
.chain(final_progress_char.map(|ch| (*ch, bar_end)))
271+
}
272+
273+
#[cfg(test)]
274+
mod tests {
275+
use super::*;
276+
277+
#[test]
278+
fn progress_bar() {
279+
let bars = (0..11)
280+
.map(|i| {
281+
let fill = i as f64 * 0.1;
282+
super::progress_bar(
283+
&NonEmpty::<Vec<_>>::from(['1', '2', '3', '4', '.']),
284+
0,
285+
2,
286+
fill,
287+
)
288+
.map(|(ch, _)| ch)
289+
.collect::<Vec<_>>()
290+
})
291+
.collect::<Vec<_>>();
292+
293+
assert_eq!(
294+
bars,
295+
vec![
296+
vec![],
297+
vec!['1'],
298+
vec!['2'],
299+
vec!['3'],
300+
vec!['4'],
301+
vec!['.'],
302+
vec!['.', '1'],
303+
vec!['.', '2'],
304+
vec!['.', '3'],
305+
vec!['.', '4'],
306+
vec!['.', '.']
307+
]
308+
);
309+
}
310+
}

src/canvas/widgets/cpu_basic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl Painter {
6161
avg_loc.width -= 2;
6262

6363
f.render_widget(
64-
PipeGauge::default()
64+
PipeGauge::new(&self.styles.progress_bar_chars.0)
6565
.gauge_style(style)
6666
.label_style(style)
6767
.inner_label(inner)
@@ -128,7 +128,7 @@ impl Painter {
128128

129129
for ((start_label, inner_label, ratio, style), row) in chunk.zip(rows.iter()) {
130130
f.render_widget(
131-
PipeGauge::default()
131+
PipeGauge::new(&self.styles.progress_bar_chars.0)
132132
.gauge_style(style)
133133
.label_style(style)
134134
.inner_label(inner_label)

src/canvas/widgets/mem_basic.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl Painter {
7373
};
7474

7575
draw_widgets.push(
76-
PipeGauge::default()
76+
PipeGauge::new(&self.styles.progress_bar_chars.0)
7777
.ratio(ram_percentage / 100.0)
7878
.start_label("RAM")
7979
.inner_label(ram_label)
@@ -86,7 +86,7 @@ impl Painter {
8686
let swap_label = memory_label(swap_harvest, app_state.basic_mode_use_percent);
8787

8888
draw_widgets.push(
89-
PipeGauge::default()
89+
PipeGauge::new(&self.styles.progress_bar_chars.0)
9090
.ratio(swap_percentage / 100.0)
9191
.start_label("SWP")
9292
.inner_label(swap_label)
@@ -103,7 +103,7 @@ impl Painter {
103103
memory_label(cache_harvest, app_state.basic_mode_use_percent);
104104

105105
draw_widgets.push(
106-
PipeGauge::default()
106+
PipeGauge::new(&self.styles.progress_bar_chars.0)
107107
.ratio(cache_percentage / 100.0)
108108
.start_label("CHE")
109109
.inner_label(cache_fraction_label)
@@ -121,7 +121,7 @@ impl Painter {
121121
memory_label(arc_harvest, app_state.basic_mode_use_percent);
122122

123123
draw_widgets.push(
124-
PipeGauge::default()
124+
PipeGauge::new(&self.styles.progress_bar_chars.0)
125125
.ratio(arc_percentage / 100.0)
126126
.start_label("ARC")
127127
.inner_label(arc_fraction_label)
@@ -152,7 +152,7 @@ impl Painter {
152152
};
153153

154154
draw_widgets.push(
155-
PipeGauge::default()
155+
PipeGauge::new(&self.styles.progress_bar_chars.0)
156156
.ratio(percentage / 100.0)
157157
.start_label("GPU")
158158
.inner_label(label)

src/options/config/style.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ use utils::{opt, set_colour, set_colour_list, set_style};
2525
use widgets::WidgetStyle;
2626

2727
use super::Config;
28-
use crate::options::{OptionError, OptionResult, args::BottomArgs};
28+
use crate::options::{
29+
OptionError, OptionResult, args::BottomArgs, config::style::widgets::ProgressBarChars,
30+
};
2931

3032
#[derive(Clone, Debug, Deserialize, Serialize)]
3133
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
@@ -127,6 +129,7 @@ pub struct Styles {
127129
#[cfg(target_os = "linux")]
128130
pub(crate) thread_text_style: Style,
129131
pub(crate) border_type: BorderType,
132+
pub(crate) progress_bar_chars: ProgressBarChars,
130133
}
131134

132135
impl Default for Styles {

src/options/config/style/themes/default.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use tui::{
44
};
55

66
use super::color;
7-
use crate::options::config::style::Styles;
7+
use crate::options::config::style::{Styles, widgets::ProgressBarChars};
88

99
impl Styles {
1010
pub(crate) fn default_palette() -> Self {
@@ -69,6 +69,7 @@ impl Styles {
6969
border_type: BorderType::Plain,
7070
#[cfg(target_os = "linux")]
7171
thread_text_style: color!(Color::Green),
72+
progress_bar_chars: ProgressBarChars::default(),
7273
}
7374
}
7475

src/options/config/style/themes/gruvbox.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use tui::{
44
};
55

66
use super::{color, hex};
7-
use crate::options::config::style::{Styles, themes::hex_colour};
7+
use crate::options::config::style::{Styles, themes::hex_colour, widgets::ProgressBarChars};
88

99
impl Styles {
1010
pub(crate) fn gruvbox_palette() -> Self {
@@ -69,6 +69,7 @@ impl Styles {
6969
border_type: BorderType::Plain,
7070
#[cfg(target_os = "linux")]
7171
thread_text_style: hex!("#458588"),
72+
progress_bar_chars: ProgressBarChars::default(),
7273
}
7374
}
7475

@@ -134,6 +135,7 @@ impl Styles {
134135
border_type: BorderType::Plain,
135136
#[cfg(target_os = "linux")]
136137
thread_text_style: hex!("#458588"),
138+
progress_bar_chars: ProgressBarChars::default(),
137139
}
138140
}
139141
}

src/options/config/style/themes/nord.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use tui::{
44
};
55

66
use super::{color, hex};
7-
use crate::options::config::style::{Styles, themes::hex_colour};
7+
use crate::options::config::style::{Styles, themes::hex_colour, widgets::ProgressBarChars};
88

99
impl Styles {
1010
pub(crate) fn nord_palette() -> Self {
@@ -57,6 +57,7 @@ impl Styles {
5757
border_type: BorderType::Plain,
5858
#[cfg(target_os = "linux")]
5959
thread_text_style: hex!("#a3be8c"),
60+
progress_bar_chars: ProgressBarChars::default(),
6061
}
6162
}
6263

@@ -110,6 +111,7 @@ impl Styles {
110111
border_type: BorderType::Plain,
111112
#[cfg(target_os = "linux")]
112113
thread_text_style: hex!("#a3be8c"),
114+
progress_bar_chars: ProgressBarChars::default(),
113115
}
114116
}
115117
}

0 commit comments

Comments
 (0)