mirror of
https://github.com/ClementTsang/bottom.git
synced 2026-09-24 11:35:38 +00:00
feature: add more pipe gauge bar fill options (#2245)
Adds block and square character options for pipe gauge widgets. May still redesign this (maybe add option for a background), but this is fine for now.
This commit is contained in:
@@ -29,6 +29,7 @@ That said, these are more guidelines rather than hard rules, though the project
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
- [#2239](https://github.com/ClementTsang/bottom/pull/2239): Initial Intel GPU support for Linux to get process GPU usage.
|
- [#2239](https://github.com/ClementTsang/bottom/pull/2239): Initial Intel GPU support for Linux to get process GPU usage.
|
||||||
|
- [#1434](https://github.com/ClementTsang/bottom/issues/1434): Support solid bars using block and square characters via `styles.widgets.bar_type`.
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
|
|||||||
@@ -187,13 +187,14 @@ These can be set under `[styles.graphs]`:
|
|||||||
|
|
||||||
These can be set under `[styles.widgets]`:
|
These can be set under `[styles.widgets]`:
|
||||||
|
|
||||||
| Config field | Details | Examples |
|
| Config field | Details | Examples |
|
||||||
| ------------------------ | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
| ------------------------ | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||||
| `border_colour` | The colour of the widgets' borders | `border_colour = "white"` |
|
| `border_colour` | The colour of the widgets' borders | `border_colour = "white"` |
|
||||||
| `selected_border_colour` | The colour of a widget's borders when the widget is selected | `selected_border_colour = "white"` |
|
| `selected_border_colour` | The colour of a widget's borders when the widget is selected | `selected_border_colour = "white"` |
|
||||||
| `widget_title` | Text styling for a widget's title | `widget_title = { colour = "black", bg_colour = "blue", bold = true }` |
|
| `widget_title` | Text styling for a widget's title | `widget_title = { colour = "black", bg_colour = "blue", bold = true }` |
|
||||||
| `bg_colour` | The background colour of the widgets | `bg_colour = "black"` |
|
| `bg_colour` | The background colour of the widgets | `bg_colour = "black"` |
|
||||||
| `text` | Text styling for text in general | `text = { colour = "black", bg_colour = "blue", bold = true }` |
|
| `text` | Text styling for text in general | `text = { colour = "black", bg_colour = "blue", bold = true }` |
|
||||||
| `selected_text` | Text styling for text when representing something that is selected | `selected_text = { colour = "black", bg_colour = "blue", bold = true }` |
|
| `selected_text` | Text styling for text when representing something that is selected | `selected_text = { colour = "black", bg_colour = "blue", bold = true }` |
|
||||||
| `disabled_text` | Text styling for text when representing something that is disabled | `disabled_text = { colour = "black", bg_colour = "blue", bold = true }` |
|
| `disabled_text` | Text styling for text when representing something that is disabled | `disabled_text = { colour = "black", bg_colour = "blue", bold = true }` |
|
||||||
| `thread_text` | Text styling for text when representing process thread, only usable on Linux at the moment | `thread_text = { colour = "green", bg_colour = "blue", bold = true }` |
|
| `thread_text` | Text styling for text when representing process thread, only usable on Linux at the moment | `thread_text = { colour = "green", bg_colour = "blue", bold = true }` |
|
||||||
|
| `bar_type` | What to fill bar widgets with (e.g. basic CPU/memory); either `"pipe"` (default), `"block"`, or `"square"` | `bar_type = "block"` |
|
||||||
|
|||||||
@@ -474,10 +474,12 @@
|
|||||||
#text = {colour = "gray"}
|
#text = {colour = "gray"}
|
||||||
#selected_text = {colour = "black", bg_colour = "light blue"}
|
#selected_text = {colour = "black", bg_colour = "light blue"}
|
||||||
#disabled_text = {colour = "dark gray"}
|
#disabled_text = {colour = "dark gray"}
|
||||||
# Disabled by default
|
# Disabled by default.
|
||||||
#bg_colour = "black"
|
#bg_colour = "black"
|
||||||
# Only on Linux
|
# Only supported on Linux.
|
||||||
#thread_text = {colour = "green"}
|
#thread_text = {colour = "green"}
|
||||||
|
# One of "pipe", "block", or "square".
|
||||||
|
#bar_type = "pipe"
|
||||||
|
|
||||||
# Layout - layouts follow a pattern like this:
|
# Layout - layouts follow a pattern like this:
|
||||||
# [[row]] represents a row in the application.
|
# [[row]] represents a row in the application.
|
||||||
|
|||||||
+46
-15
@@ -268,7 +268,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include_unmounted": {
|
"include_unmounted": {
|
||||||
"description": "Whether to include block devices that aren't currently mounted (currently Linux only). Defaults to false.",
|
"description": "Whether to include block devices that aren't currently mounted\n(currently Linux only). Defaults to false.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
@@ -322,14 +322,14 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"include_unmounted": {
|
"include_unmounted": {
|
||||||
"description": "Whether to include block devices that aren't currently mounted (currently Linux only). Defaults to false.",
|
"description": "Whether to include block devices that aren't currently mounted\n(currently Linux only). Defaults to false.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"legend": {
|
"legend": {
|
||||||
"description": "Whether to label legend entries by device name or mount point. Defaults to disk name.",
|
"description": "Whether to label legend entries by device name or mount point. Defaults\nto disk name.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/$defs/DiskGraphLegend"
|
"$ref": "#/$defs/DiskGraphLegend"
|
||||||
@@ -812,7 +812,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"cache_memory": {
|
"cache_memory": {
|
||||||
"description": "Whether to collect and display cache and buffer memory. Not available on Windows.",
|
"description": "Whether to collect and display cache and buffer memory. Not available on\nWindows.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
@@ -931,14 +931,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"start_zeroed": {
|
"start_zeroed": {
|
||||||
"description": "Zeroes out the total network usage (\"All\") counters so it shows the total usage\nsince the app is started, rather than the total usage since boot.",
|
"description": "Zeroes out the total network usage (\"All\") counters so it shows the\ntotal usage since the app is started, rather than the total usage\nsince boot.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"use_binary_prefix": {
|
"use_binary_prefix": {
|
||||||
"description": "Displays the network widget with a binary prefix (e.g. kibibits) rather than a decimal\nprefix (e.g. kilobits). Defaults to decimal prefixes.",
|
"description": "Displays the network widget with a binary prefix (e.g. kibibits) rather\nthan a decimal prefix (e.g. kilobits). Defaults to decimal prefixes.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
@@ -952,7 +952,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"use_log": {
|
"use_log": {
|
||||||
"description": "Displays the network widget with a log scale. Defaults to a non-log scale.",
|
"description": "Displays the network widget with a log scale. Defaults to a non-log\nscale.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
@@ -1101,14 +1101,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"current_usage": {
|
"current_usage": {
|
||||||
"description": "Calculates process CPU usage as a percentage of current usage rather than total usage.",
|
"description": "Calculates process CPU usage as a percentage of current usage rather\nthan total usage.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default_grouped": {
|
"default_grouped": {
|
||||||
"description": "Groups processes with the same name by default. No effect if `--tree` is set.",
|
"description": "Groups processes with the same name by default. No effect if `--tree` is\nset.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
@@ -1140,7 +1140,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"disable_advanced_kill": {
|
"disable_advanced_kill": {
|
||||||
"description": "Disable the advanced kill dialog and just show the basic one with no options.",
|
"description": "Disable the advanced kill dialog and just show the basic one with no\noptions.",
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
@@ -1441,7 +1441,7 @@
|
|||||||
"default": null
|
"default": null
|
||||||
},
|
},
|
||||||
"max_temp": {
|
"max_temp": {
|
||||||
"description": "An upper temperature value for the graph; entries higher than this will be hidden. If not set,\nthere is no limit.\n\nIs in the configured temperature unit.",
|
"description": "An upper temperature value for the graph; entries higher than this will\nbe hidden. If not set, there is no limit.\n\nIs in the configured temperature unit.",
|
||||||
"type": [
|
"type": [
|
||||||
"number",
|
"number",
|
||||||
"null"
|
"null"
|
||||||
@@ -1538,19 +1538,50 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"WidgetBarType": {
|
||||||
|
"description": "The type of character used to fill in bars, such as the ones used by the\nbasic CPU and memory widgets.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"description": "Fill bars with pipe characters (`|`).",
|
||||||
|
"type": "string",
|
||||||
|
"const": "pipe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fill bars with block characters (`█`, `▉`, etc.).",
|
||||||
|
"type": "string",
|
||||||
|
"const": "block"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Fill bars with square characters (`■`).",
|
||||||
|
"type": "string",
|
||||||
|
"const": "square"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"WidgetBorderType": {
|
"WidgetBorderType": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"Default",
|
"default",
|
||||||
"Rounded",
|
"rounded",
|
||||||
"Double",
|
"double",
|
||||||
"Thick"
|
"thick"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"WidgetStyle": {
|
"WidgetStyle": {
|
||||||
"description": "General styling for generic widgets.",
|
"description": "General styling for generic widgets.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"bar_type": {
|
||||||
|
"description": "What character to fill in bars with, such as the ones used by the basic\nCPU and memory widgets.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/WidgetBarType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"bg_colour": {
|
"bg_colour": {
|
||||||
"description": "Background colour for widgets.",
|
"description": "Background colour for widgets.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ macro_rules! generate_column_schemas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_schema(schema_options: SchemaOptions) -> anyhow::Result<()> {
|
fn generate_schema(schema_options: SchemaOptions) -> anyhow::Result<()> {
|
||||||
|
// TODO: remove newlines in descriptions?
|
||||||
|
|
||||||
let mut schema = schemars::schema_for!(config::Config);
|
let mut schema = schemars::schema_for!(config::Config);
|
||||||
{
|
{
|
||||||
// TODO: Maybe make this case insensitive? See https://stackoverflow.com/a/68639341
|
// TODO: Maybe make this case insensitive? See https://stackoverflow.com/a/68639341
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use ratatui::{
|
|||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::Style,
|
style::Style,
|
||||||
|
symbols,
|
||||||
text::Line,
|
text::Line,
|
||||||
widgets::{Block, Widget},
|
widgets::{Block, Widget},
|
||||||
};
|
};
|
||||||
@@ -16,7 +17,41 @@ pub enum LabelLimit {
|
|||||||
StartLabel,
|
StartLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A widget to measure something, using pipe characters ('|') as a unit.
|
/// What bar character type to use.
|
||||||
|
#[derive(Debug, Clone, Default, Copy)]
|
||||||
|
pub enum BarType {
|
||||||
|
#[default]
|
||||||
|
/// The pipe character (`|`)
|
||||||
|
Pipe,
|
||||||
|
/// Block characters (`█`, `▉`, etc.)
|
||||||
|
Block,
|
||||||
|
/// Square characters (`■`)
|
||||||
|
Square,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BarType {
|
||||||
|
#[inline]
|
||||||
|
fn is_block(&self) -> bool {
|
||||||
|
matches!(self, BarType::Block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_unicode_block(frac: f64) -> &'static str {
|
||||||
|
match (frac * 8.0).round() as u16 {
|
||||||
|
0 => " ",
|
||||||
|
1 => symbols::block::ONE_EIGHTH,
|
||||||
|
2 => symbols::block::ONE_QUARTER,
|
||||||
|
3 => symbols::block::THREE_EIGHTHS,
|
||||||
|
4 => symbols::block::HALF,
|
||||||
|
5 => symbols::block::FIVE_EIGHTHS,
|
||||||
|
6 => symbols::block::THREE_QUARTERS,
|
||||||
|
7 => symbols::block::SEVEN_EIGHTHS,
|
||||||
|
8 => symbols::block::FULL,
|
||||||
|
_ => unreachable!("this case should never occur"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A widget to measure something, using pipe characters ('|') or horizontal bar characters as a unit.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PipeGauge<'a> {
|
pub struct PipeGauge<'a> {
|
||||||
block: Option<Block<'a>>,
|
block: Option<Block<'a>>,
|
||||||
@@ -26,6 +61,7 @@ pub struct PipeGauge<'a> {
|
|||||||
label_style: Style,
|
label_style: Style,
|
||||||
gauge_style: Style,
|
gauge_style: Style,
|
||||||
hide_parts: LabelLimit,
|
hide_parts: LabelLimit,
|
||||||
|
bar_type: BarType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PipeGauge<'_> {
|
impl Default for PipeGauge<'_> {
|
||||||
@@ -38,6 +74,7 @@ impl Default for PipeGauge<'_> {
|
|||||||
label_style: Style::default(),
|
label_style: Style::default(),
|
||||||
gauge_style: Style::default(),
|
gauge_style: Style::default(),
|
||||||
hide_parts: LabelLimit::default(),
|
hide_parts: LabelLimit::default(),
|
||||||
|
bar_type: BarType::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,6 +126,12 @@ impl<'a> PipeGauge<'a> {
|
|||||||
self.hide_parts = hide_parts;
|
self.hide_parts = hide_parts;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What type of bar character to use.
|
||||||
|
pub fn bar_type(mut self, bar_type: BarType) -> Self {
|
||||||
|
self.bar_type = bar_type;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for PipeGauge<'_> {
|
impl Widget for PipeGauge<'_> {
|
||||||
@@ -184,11 +227,11 @@ impl Widget for PipeGauge<'_> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
LabelLimit::Auto(_) | LabelLimit::None => {
|
LabelLimit::Auto(_) | LabelLimit::None => {
|
||||||
|
// FIXME: "[" and "]" don't look that great with block bars.
|
||||||
let (start, _) = buf.set_line(col, row, &Line::from("["), gauge_area.width);
|
let (start, _) = buf.set_line(col, row, &Line::from("["), gauge_area.width);
|
||||||
if start >= gauge_area.right() {
|
if start >= gauge_area.right() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (end, _) = buf.set_line(
|
let (end, _) = buf.set_line(
|
||||||
(gauge_area.x + gauge_area.width).saturating_sub(1),
|
(gauge_area.x + gauge_area.width).saturating_sub(1),
|
||||||
row,
|
row,
|
||||||
@@ -196,22 +239,40 @@ impl Widget for PipeGauge<'_> {
|
|||||||
gauge_area.width,
|
gauge_area.width,
|
||||||
);
|
);
|
||||||
|
|
||||||
let pipe_end = end.saturating_sub(1).min(
|
let bar_end = end.saturating_sub(1);
|
||||||
start + (f64::from(end.saturating_sub(start)) * self.ratio).floor() as u16,
|
let filled_width = f64::from(bar_end.saturating_sub(start)) * self.ratio;
|
||||||
);
|
let pipe_end = bar_end.min(start + filled_width.floor() as u16);
|
||||||
|
|
||||||
|
let symbol = match self.bar_type {
|
||||||
|
BarType::Pipe => "|",
|
||||||
|
BarType::Block => symbols::block::FULL,
|
||||||
|
BarType::Square => "■",
|
||||||
|
};
|
||||||
|
|
||||||
|
let bar_style = Style {
|
||||||
|
fg: self.gauge_style.fg,
|
||||||
|
bg: None,
|
||||||
|
add_modifier: self.gauge_style.add_modifier,
|
||||||
|
sub_modifier: self.gauge_style.sub_modifier,
|
||||||
|
underline_color: None,
|
||||||
|
};
|
||||||
|
|
||||||
for col in start..pipe_end {
|
for col in start..pipe_end {
|
||||||
if let Some(cell) = buf.cell_mut((col, row)) {
|
if let Some(cell) = buf.cell_mut((col, row)) {
|
||||||
cell.set_symbol("|").set_style(Style {
|
cell.set_symbol(symbol).set_style(bar_style);
|
||||||
fg: self.gauge_style.fg,
|
|
||||||
bg: None,
|
|
||||||
add_modifier: self.gauge_style.add_modifier,
|
|
||||||
sub_modifier: self.gauge_style.sub_modifier,
|
|
||||||
underline_color: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlike pipes, blocks can also show the leftover fraction of a cell.
|
||||||
|
// Based on what Ratatui does!
|
||||||
|
if self.bar_type.is_block()
|
||||||
|
&& pipe_end < bar_end
|
||||||
|
&& let Some(cell) = buf.cell_mut((pipe_end, row))
|
||||||
|
{
|
||||||
|
cell.set_symbol(get_unicode_block(filled_width.fract()))
|
||||||
|
.set_style(bar_style);
|
||||||
|
}
|
||||||
|
|
||||||
if (end_label.width() as u16) < end.saturating_sub(start) {
|
if (end_label.width() as u16) < end.saturating_sub(start) {
|
||||||
let gauge_end = gauge_area
|
let gauge_end = gauge_area
|
||||||
.right()
|
.right()
|
||||||
@@ -223,3 +284,107 @@ impl Widget for PipeGauge<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_unicode_block() {
|
||||||
|
// Test exact values.
|
||||||
|
assert_eq!(get_unicode_block(0.0), " ");
|
||||||
|
assert_eq!(get_unicode_block(0.125), symbols::block::ONE_EIGHTH);
|
||||||
|
assert_eq!(get_unicode_block(0.25), symbols::block::ONE_QUARTER);
|
||||||
|
assert_eq!(get_unicode_block(0.375), symbols::block::THREE_EIGHTHS);
|
||||||
|
assert_eq!(get_unicode_block(0.5), symbols::block::HALF);
|
||||||
|
assert_eq!(get_unicode_block(0.625), symbols::block::FIVE_EIGHTHS);
|
||||||
|
assert_eq!(get_unicode_block(0.75), symbols::block::THREE_QUARTERS);
|
||||||
|
assert_eq!(get_unicode_block(0.875), symbols::block::SEVEN_EIGHTHS);
|
||||||
|
assert_eq!(get_unicode_block(1.0), symbols::block::FULL);
|
||||||
|
|
||||||
|
// Test rounding.
|
||||||
|
assert_eq!(get_unicode_block(0.05), " ");
|
||||||
|
assert_eq!(get_unicode_block(0.1), symbols::block::ONE_EIGHTH);
|
||||||
|
assert_eq!(get_unicode_block(0.3), symbols::block::ONE_QUARTER);
|
||||||
|
assert_eq!(get_unicode_block(0.4), symbols::block::THREE_EIGHTHS);
|
||||||
|
assert_eq!(get_unicode_block(0.55), symbols::block::HALF);
|
||||||
|
assert_eq!(get_unicode_block(0.6), symbols::block::FIVE_EIGHTHS);
|
||||||
|
assert_eq!(get_unicode_block(0.8), symbols::block::THREE_QUARTERS);
|
||||||
|
assert_eq!(get_unicode_block(0.9), symbols::block::SEVEN_EIGHTHS);
|
||||||
|
assert_eq!(get_unicode_block(0.99), symbols::block::FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`PipeGauge`] and return what it would have rendered.
|
||||||
|
fn render_gauge(
|
||||||
|
ratio: f64, bar_type: BarType, start_label: Option<&str>, inner_label: Option<&str>,
|
||||||
|
) -> String {
|
||||||
|
const WIDTH: u16 = 12;
|
||||||
|
|
||||||
|
let area = Rect::new(0, 0, WIDTH, 1);
|
||||||
|
let mut buf = Buffer::empty(area);
|
||||||
|
let mut gauge = PipeGauge::default().ratio(ratio).bar_type(bar_type);
|
||||||
|
|
||||||
|
if let Some(start_label) = start_label {
|
||||||
|
gauge = gauge.start_label(start_label.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(inner_label) = inner_label {
|
||||||
|
gauge = gauge.inner_label(inner_label.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
gauge.render(area, &mut buf);
|
||||||
|
|
||||||
|
(0..WIDTH).map(|x| buf[(x, 0)].symbol()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pipe_bars() {
|
||||||
|
assert_eq!(render_gauge(0.0, BarType::Pipe, None, None), "[ ]");
|
||||||
|
assert_eq!(render_gauge(0.5, BarType::Pipe, None, None), "[||||| ]");
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.95, BarType::Pipe, None, None),
|
||||||
|
"[||||||||| ]"
|
||||||
|
);
|
||||||
|
assert_eq!(render_gauge(1.0, BarType::Pipe, None, None), "[||||||||||]");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_solid_bars() {
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.0, BarType::Block, None, None),
|
||||||
|
"[ ]"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.5, BarType::Block, None, None),
|
||||||
|
"[█████ ]"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.55, BarType::Block, None, None),
|
||||||
|
"[█████▌ ]"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.9, BarType::Block, None, None),
|
||||||
|
"[█████████ ]"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(1.0, BarType::Block, None, None),
|
||||||
|
"[██████████]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_labelled_bars() {
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.5, BarType::Pipe, Some("CPU"), Some(" 50%")),
|
||||||
|
"CPU[||| 50%]"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(0.5, BarType::Block, Some("CPU"), Some(" 50%")),
|
||||||
|
"CPU[███ 50%]"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
render_gauge(1.0, BarType::Block, Some("CPU"), Some("100%")),
|
||||||
|
"CPU[███100%]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -214,7 +214,8 @@ impl Painter {
|
|||||||
|
|
||||||
let block_inner = block.inner(margined_draw_loc);
|
let block_inner = block.inner(margined_draw_loc);
|
||||||
|
|
||||||
// Draw bar
|
// Draw bar.
|
||||||
|
// TODO: Switch to pipe_gauge.
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Table::new(battery_charge_rows, [Constraint::Percentage(100)])
|
Table::new(battery_charge_rows, [Constraint::Percentage(100)])
|
||||||
.block(block.clone())
|
.block(block.clone())
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ impl Painter {
|
|||||||
.label_style(style)
|
.label_style(style)
|
||||||
.inner_label(inner)
|
.inner_label(inner)
|
||||||
.start_label(outer)
|
.start_label(outer)
|
||||||
.ratio(ratio.into()),
|
.ratio(ratio.into())
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
avg_loc,
|
avg_loc,
|
||||||
);
|
);
|
||||||
avg_row_count += 1;
|
avg_row_count += 1;
|
||||||
@@ -139,7 +140,8 @@ impl Painter {
|
|||||||
.inner_label(inner_label)
|
.inner_label(inner_label)
|
||||||
.start_label(start_label)
|
.start_label(start_label)
|
||||||
.ratio(ratio.into())
|
.ratio(ratio.into())
|
||||||
.hide_parts(hide_parts),
|
.hide_parts(hide_parts)
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
*row,
|
*row,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ impl Painter {
|
|||||||
.start_label("RAM")
|
.start_label("RAM")
|
||||||
.inner_label(ram_label)
|
.inner_label(ram_label)
|
||||||
.label_style(self.styles.ram_style)
|
.label_style(self.styles.ram_style)
|
||||||
.gauge_style(self.styles.ram_style),
|
.gauge_style(self.styles.ram_style)
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(swap_harvest) = &data.swap_harvest {
|
if let Some(swap_harvest) = &data.swap_harvest {
|
||||||
@@ -94,7 +95,8 @@ impl Painter {
|
|||||||
.start_label("SWP")
|
.start_label("SWP")
|
||||||
.inner_label(swap_label)
|
.inner_label(swap_label)
|
||||||
.label_style(self.styles.swap_style)
|
.label_style(self.styles.swap_style)
|
||||||
.gauge_style(self.styles.swap_style),
|
.gauge_style(self.styles.swap_style)
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +113,8 @@ impl Painter {
|
|||||||
.start_label("CHE")
|
.start_label("CHE")
|
||||||
.inner_label(cache_fraction_label)
|
.inner_label(cache_fraction_label)
|
||||||
.label_style(self.styles.cache_style)
|
.label_style(self.styles.cache_style)
|
||||||
.gauge_style(self.styles.cache_style),
|
.gauge_style(self.styles.cache_style)
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +132,8 @@ impl Painter {
|
|||||||
.start_label("ARC")
|
.start_label("ARC")
|
||||||
.inner_label(arc_fraction_label)
|
.inner_label(arc_fraction_label)
|
||||||
.label_style(self.styles.arc_style)
|
.label_style(self.styles.arc_style)
|
||||||
.gauge_style(self.styles.arc_style),
|
.gauge_style(self.styles.arc_style)
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +164,8 @@ impl Painter {
|
|||||||
.start_label("GPU")
|
.start_label("GPU")
|
||||||
.inner_label(label)
|
.inner_label(label)
|
||||||
.label_style(style)
|
.label_style(style)
|
||||||
.gauge_style(style),
|
.gauge_style(style)
|
||||||
|
.bar_type(self.styles.bar_type),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -723,10 +723,12 @@ pub(crate) const CONFIG_TEXT: &str = r#"# This is a default config file for bott
|
|||||||
#text = {colour = "gray"}
|
#text = {colour = "gray"}
|
||||||
#selected_text = {colour = "black", bg_colour = "light blue"}
|
#selected_text = {colour = "black", bg_colour = "light blue"}
|
||||||
#disabled_text = {colour = "dark gray"}
|
#disabled_text = {colour = "dark gray"}
|
||||||
# Disabled by default
|
# Disabled by default.
|
||||||
#bg_colour = "black"
|
#bg_colour = "black"
|
||||||
# Only on Linux
|
# Only supported on Linux.
|
||||||
#thread_text = {colour = "green"}
|
#thread_text = {colour = "green"}
|
||||||
|
# One of "pipe", "block", or "square".
|
||||||
|
#bar_type = "pipe"
|
||||||
|
|
||||||
# Layout - layouts follow a pattern like this:
|
# Layout - layouts follow a pattern like this:
|
||||||
# [[row]] represents a row in the application.
|
# [[row]] represents a row in the application.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Config options around styling.
|
//! Config options around styling.
|
||||||
|
|
||||||
|
mod bars;
|
||||||
mod battery;
|
mod battery;
|
||||||
mod borders;
|
mod borders;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
@@ -29,8 +30,9 @@ use utils::{opt, set_colour, set_colour_list, set_style};
|
|||||||
use widgets::WidgetStyle;
|
use widgets::WidgetStyle;
|
||||||
|
|
||||||
use super::Config;
|
use super::Config;
|
||||||
use crate::options::{
|
use crate::{
|
||||||
OptionError, OptionResult, args::BottomArgs, config::style::utils::set_bg_colour,
|
canvas::components::pipe_gauge::BarType,
|
||||||
|
options::{OptionError, OptionResult, args::BottomArgs, config::style::utils::set_bg_colour},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
@@ -143,6 +145,7 @@ pub struct Styles {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub(crate) thread_text_style: Style,
|
pub(crate) thread_text_style: Style,
|
||||||
pub(crate) border_type: BorderType,
|
pub(crate) border_type: BorderType,
|
||||||
|
pub(crate) bar_type: BarType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Styles {
|
impl Default for Styles {
|
||||||
@@ -261,10 +264,14 @@ impl Styles {
|
|||||||
selected_border_colour
|
selected_border_colour
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(widgets) = &config.widgets
|
if let Some(widgets) = &config.widgets {
|
||||||
&& let Some(widget_borders) = widgets.widget_border_type
|
if let Some(widget_borders) = widgets.widget_border_type {
|
||||||
{
|
self.border_type = widget_borders.into();
|
||||||
self.border_type = widget_borders.into();
|
}
|
||||||
|
|
||||||
|
if let Some(bar_type) = widgets.bar_type {
|
||||||
|
self.bar_type = bar_type.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::canvas::components::pipe_gauge::BarType;
|
||||||
|
|
||||||
|
/// The type of character used to fill in bars, such as the ones used by the
|
||||||
|
/// basic CPU and memory widgets.
|
||||||
|
#[derive(Default, Clone, Copy, Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
pub(crate) enum WidgetBarType {
|
||||||
|
/// Fill bars with pipe characters (`|`).
|
||||||
|
#[default]
|
||||||
|
Pipe,
|
||||||
|
/// Fill bars with block characters (`█`, `▉`, etc.).
|
||||||
|
Block,
|
||||||
|
/// Fill bars with square characters (`■`).
|
||||||
|
Square,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for WidgetBarType {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?.to_lowercase();
|
||||||
|
match value.as_str() {
|
||||||
|
"pipe" => Ok(WidgetBarType::Pipe),
|
||||||
|
"block" => Ok(WidgetBarType::Block),
|
||||||
|
"square" => Ok(WidgetBarType::Square),
|
||||||
|
_ => Err(serde::de::Error::custom("doesn't match any bar type")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WidgetBarType> for BarType {
|
||||||
|
fn from(value: WidgetBarType) -> Self {
|
||||||
|
match value {
|
||||||
|
WidgetBarType::Pipe => BarType::Pipe,
|
||||||
|
WidgetBarType::Block => BarType::Block,
|
||||||
|
WidgetBarType::Square => BarType::Square,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::WidgetBarType;
|
||||||
|
use crate::options::config::style::widgets::WidgetStyle;
|
||||||
|
|
||||||
|
fn parse(value: &str) -> anyhow::Result<Option<WidgetBarType>> {
|
||||||
|
let style = toml_edit::de::from_str::<WidgetStyle>(&format!("bar_type = {value}"))?;
|
||||||
|
|
||||||
|
Ok(style.bar_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_bar_types() {
|
||||||
|
assert_eq!(parse("\"pipe\"").unwrap(), Some(WidgetBarType::Pipe));
|
||||||
|
assert_eq!(parse("\"block\"").unwrap(), Some(WidgetBarType::Block));
|
||||||
|
assert_eq!(parse("\"square\"").unwrap(), Some(WidgetBarType::Square));
|
||||||
|
|
||||||
|
// Casing shouldn't matter.
|
||||||
|
assert_eq!(parse("\"PIPE\"").unwrap(), Some(WidgetBarType::Pipe));
|
||||||
|
assert_eq!(parse("\"BLOCK\"").unwrap(), Some(WidgetBarType::Block));
|
||||||
|
assert_eq!(parse("\"SQUARE\"").unwrap(), Some(WidgetBarType::Square));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_bar_types() {
|
||||||
|
assert!(parse("\"bar\"").is_err());
|
||||||
|
assert!(parse("\"\"").is_err());
|
||||||
|
assert!(parse("true").is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use ratatui::widgets::BorderType;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Debug, Serialize)]
|
#[derive(Default, Clone, Copy, Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
pub(crate) enum WidgetBorderType {
|
pub(crate) enum WidgetBorderType {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::colour;
|
use super::colour;
|
||||||
use crate::options::config::style::Styles;
|
use crate::{canvas::components::pipe_gauge::BarType, options::config::style::Styles};
|
||||||
|
|
||||||
impl Styles {
|
impl Styles {
|
||||||
pub(crate) fn default_palette() -> Self {
|
pub(crate) fn default_palette() -> Self {
|
||||||
@@ -75,6 +75,7 @@ impl Styles {
|
|||||||
invalid_query_style: colour!(Color::Red),
|
invalid_query_style: colour!(Color::Red),
|
||||||
disabled_text_style: colour!(Color::DarkGray),
|
disabled_text_style: colour!(Color::DarkGray),
|
||||||
border_type: BorderType::Plain,
|
border_type: BorderType::Plain,
|
||||||
|
bar_type: BarType::default(),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
thread_text_style: colour!(Color::Green),
|
thread_text_style: colour!(Color::Green),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{colour, hex};
|
use super::{colour, hex};
|
||||||
use crate::options::config::style::{Styles, themes::hex_colour};
|
use crate::{
|
||||||
|
canvas::components::pipe_gauge::BarType,
|
||||||
|
options::config::style::{Styles, themes::hex_colour},
|
||||||
|
};
|
||||||
|
|
||||||
impl Styles {
|
impl Styles {
|
||||||
pub(crate) fn gruvbox_palette() -> Self {
|
pub(crate) fn gruvbox_palette() -> Self {
|
||||||
@@ -75,6 +78,7 @@ impl Styles {
|
|||||||
invalid_query_style: colour!(Color::Red),
|
invalid_query_style: colour!(Color::Red),
|
||||||
disabled_text_style: hex!("#665c54"),
|
disabled_text_style: hex!("#665c54"),
|
||||||
border_type: BorderType::Plain,
|
border_type: BorderType::Plain,
|
||||||
|
bar_type: BarType::default(),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
thread_text_style: hex!("#458588"),
|
thread_text_style: hex!("#458588"),
|
||||||
}
|
}
|
||||||
@@ -154,6 +158,7 @@ impl Styles {
|
|||||||
invalid_query_style: colour!(Color::Red),
|
invalid_query_style: colour!(Color::Red),
|
||||||
disabled_text_style: hex!("#d5c4a1"),
|
disabled_text_style: hex!("#d5c4a1"),
|
||||||
border_type: BorderType::Plain,
|
border_type: BorderType::Plain,
|
||||||
|
bar_type: BarType::default(),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
thread_text_style: hex!("#458588"),
|
thread_text_style: hex!("#458588"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{colour, hex};
|
use super::{colour, hex};
|
||||||
use crate::options::config::style::{Styles, themes::hex_colour};
|
use crate::{
|
||||||
|
canvas::components::pipe_gauge::BarType,
|
||||||
|
options::config::style::{Styles, themes::hex_colour},
|
||||||
|
};
|
||||||
|
|
||||||
impl Styles {
|
impl Styles {
|
||||||
pub(crate) fn nord_palette() -> Self {
|
pub(crate) fn nord_palette() -> Self {
|
||||||
@@ -63,6 +66,7 @@ impl Styles {
|
|||||||
invalid_query_style: colour!(Color::Red),
|
invalid_query_style: colour!(Color::Red),
|
||||||
disabled_text_style: hex!("#4c566a"),
|
disabled_text_style: hex!("#4c566a"),
|
||||||
border_type: BorderType::Plain,
|
border_type: BorderType::Plain,
|
||||||
|
bar_type: BarType::default(),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
thread_text_style: hex!("#a3be8c"),
|
thread_text_style: hex!("#a3be8c"),
|
||||||
}
|
}
|
||||||
@@ -124,6 +128,7 @@ impl Styles {
|
|||||||
invalid_query_style: colour!(Color::Red),
|
invalid_query_style: colour!(Color::Red),
|
||||||
disabled_text_style: hex!("#d8dee9"),
|
disabled_text_style: hex!("#d8dee9"),
|
||||||
border_type: BorderType::Plain,
|
border_type: BorderType::Plain,
|
||||||
|
bar_type: BarType::default(),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
thread_text_style: hex!("#a3be8c"),
|
thread_text_style: hex!("#a3be8c"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{ColourStr, TextStyleConfig, borders::WidgetBorderType};
|
use super::{ColourStr, TextStyleConfig, bars::WidgetBarType, borders::WidgetBorderType};
|
||||||
|
|
||||||
/// General styling for generic widgets.
|
/// General styling for generic widgets.
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
@@ -37,4 +37,8 @@ pub(crate) struct WidgetStyle {
|
|||||||
/// Background colour for widgets.
|
/// Background colour for widgets.
|
||||||
#[serde(alias = "bg_color")]
|
#[serde(alias = "bg_color")]
|
||||||
pub(crate) bg_colour: Option<ColourStr>,
|
pub(crate) bg_colour: Option<ColourStr>,
|
||||||
|
|
||||||
|
/// What character to fill in bars with, such as the ones used by the basic
|
||||||
|
/// CPU and memory widgets.
|
||||||
|
pub(crate) bar_type: Option<WidgetBarType>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,3 +64,4 @@ text = { colour = "gray" }
|
|||||||
selected_text = { colour = "black", bg_colour = "light blue" }
|
selected_text = { colour = "black", bg_colour = "light blue" }
|
||||||
disabled_text = { colour = "dark gray" }
|
disabled_text = { colour = "dark gray" }
|
||||||
bg_colour = "black"
|
bg_colour = "black"
|
||||||
|
bar_type = "block"
|
||||||
|
|||||||
Reference in New Issue
Block a user