feature: support showing a decimal place for CPU usage (#2045)

* feature: support showing a decimal place for CPU usage

* update docs + changelog + schema

* fix basic cpu formatting
This commit is contained in:
Clement Tsang
2026-04-21 23:27:25 -04:00
committed by GitHub
parent 4b59c04547
commit 5d09d18469
10 changed files with 65 additions and 13 deletions
+3 -2
View File
@@ -32,8 +32,9 @@ That said, these are more guidelines rather than hard rules, though the project
- [#1938](https://github.com/ClementTsang/bottom/pull/1938), [#1980](https://github.com/ClementTsang/bottom/pull/1980): Report average packet size and packet rate.
- [#2003](https://github.com/ClementTsang/bottom/pull/2003): Configurable default sort column for temperature and disk table widgets.
- [#1979](https://github.com/ClementTsang/bottom/pull/1979): Add global `bg_color` option to set widget background colour.
- [#2039](https://github.com/ClementTsang/bottom/pull/2039): Support drawing a line separator between the column headers and data.
- [#1948](https://github.com/ClementTsang/bottom/pull/1948): Support `!=` operator and `!` negation prefixes in query searches.
- [#2039](https://github.com/ClementTsang/bottom/pull/2039): Add support for drawing a line separator between the column headers and data.
- [#1948](https://github.com/ClementTsang/bottom/pull/1948): Add support for both an `!=` operator and `!` negation prefixes in query searches.
- [#2045](https://github.com/ClementTsang/bottom/pull/2045): Add support for showing a decimal place for CPU usage
### Changes
@@ -9,3 +9,12 @@ You can configure which CPU graph is shown by default when starting up bottom by
# One of "all" (default), "average"/"avg"
default = "average"
```
## Show Decimal
You can configure whether CPU usage values are shown with a decimal place by setting `cpu.show_decimal`. Defaults to `false`.
```toml
[cpu]
show_decimal = true
```
+3
View File
@@ -147,6 +147,9 @@
# One of "all" (default), "average"/"avg"
#default = "average"
# Whether to show a decimal place for CPU usage values.
#show_decimal = false
# Disk widget configuration
#[disk]
+7
View File
@@ -135,6 +135,13 @@
"default": {
"description": "The default selected entry of the CPU widget.",
"$ref": "#/$defs/CpuDefault"
},
"show_decimal": {
"description": "Whether to show a decimal place for CPU usage values.",
"type": [
"boolean",
"null"
]
}
}
},
+1
View File
@@ -44,6 +44,7 @@ pub struct AppConfigFields {
pub use_dot: bool,
pub cpu_left_legend: bool,
pub show_average_cpu: bool, // TODO: Unify this in CPU options
pub show_cpu_decimal: bool,
pub use_current_cpu_total: bool,
pub unnormalized_cpu: bool,
pub get_process_threads: bool,
+11 -4
View File
@@ -47,6 +47,7 @@ impl Painter {
// TODO: This is pretty ugly. Is there a better way of doing it?
let mut avg_index = cpu_data.len() + 1;
let mut avg_row_count = 0;
let show_decimal = app_state.app_config_fields.show_cpu_decimal;
if app_state.app_config_fields.dedicated_average_row
&& app_state.app_config_fields.show_average_cpu
{
@@ -54,7 +55,7 @@ impl Painter {
.iter()
.find_position(|&datum| matches!(datum.data_type, CpuDataType::Avg))
{
let (outer, inner, ratio, style) = self.cpu_info(avg);
let (outer, inner, ratio, style) = self.cpu_info(avg, show_decimal);
let [cores_loc, mut avg_loc] =
Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(draw_loc);
@@ -93,7 +94,7 @@ impl Painter {
if index == avg_index {
None
} else {
Some(self.cpu_info(cpu))
Some(self.cpu_info(cpu, show_decimal))
}
});
@@ -156,7 +157,9 @@ impl Painter {
}
#[inline]
fn cpu_info(&self, data: &CpuData) -> (String, String, f32, tui::style::Style) {
fn cpu_info(
&self, data: &CpuData, show_decimal: bool,
) -> (String, String, f32, tui::style::Style) {
let (outer, style) = match data.data_type {
CpuDataType::Avg => ("AVG".to_string(), self.styles.avg_cpu_colour),
CpuDataType::Cpu(index) => (
@@ -165,7 +168,11 @@ impl Painter {
),
};
let inner = format!("{:>3.0}%", data.usage.round());
let inner = if show_decimal {
format!("{:>5.1}%", data.usage)
} else {
format!("{:>3.0}%", data.usage.round())
};
let ratio = data.usage / 100.0;
(outer, inner, ratio, style)
+3
View File
@@ -395,6 +395,9 @@ pub(crate) const CONFIG_TEXT: &str = r#"# This is a default config file for bott
# One of "all" (default), "average"/"avg"
#default = "average"
# Whether to show a decimal place for CPU usage values.
#show_decimal = false
# Disk widget configuration
#[disk]
+9
View File
@@ -303,6 +303,7 @@ pub(crate) fn init_app(args: BottomArgs, config: Config) -> Result<(App, BottomL
temperature_type: get_temperature(args, config)
.context("Update 'temperature_type' in your config file.")?,
show_average_cpu: get_show_average_cpu(args, config),
show_cpu_decimal: get_show_cpu_decimal(config),
use_dot: is_flag_enabled!(dot_marker, args.general, config),
cpu_left_legend: is_flag_enabled!(cpu_left_legend, args.cpu, config),
use_current_cpu_total: is_flag_enabled!(current_usage, args.process, config),
@@ -768,6 +769,14 @@ fn get_default_cpu_selection(args: &BottomArgs, config: &Config) -> config::cpu:
}
}
fn get_show_cpu_decimal(config: &Config) -> bool {
config
.cpu
.as_ref()
.and_then(|c| c.show_decimal)
.unwrap_or(false)
}
fn get_table_gap(config: &Config) -> TableGap {
config
.flags
+3
View File
@@ -20,6 +20,9 @@ pub(crate) struct CpuConfig {
/// The default selected entry of the CPU widget.
#[serde(default)]
pub(crate) default: CpuDefault,
/// Whether to show a decimal place for CPU usage values.
pub(crate) show_decimal: Option<bool>,
}
#[cfg(test)]
+16 -7
View File
@@ -18,14 +18,14 @@ use crate::{
pub enum CpuWidgetColumn {
Cpu,
Use,
Use { show_decimal: bool },
}
impl ColumnHeader for CpuWidgetColumn {
fn text(&self) -> Cow<'static, str> {
match self {
CpuWidgetColumn::Cpu => "CPU".into(),
CpuWidgetColumn::Use => "Use".into(),
CpuWidgetColumn::Use { .. } => "Use".into(),
}
}
}
@@ -62,7 +62,7 @@ impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
match &self {
CpuWidgetTableData::All => match column {
CpuWidgetColumn::Cpu => Some("All".into()),
CpuWidgetColumn::Use => None,
CpuWidgetColumn::Use { .. } => None,
},
CpuWidgetTableData::Entry {
data_type,
@@ -85,7 +85,11 @@ impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
Some(text)
}
},
CpuWidgetColumn::Use => Some(format!("{:.0}%", last_entry.round()).into()),
CpuWidgetColumn::Use { show_decimal } => Some(if *show_decimal {
format!("{last_entry:.1}%").into()
} else {
format!("{last_entry:.0}%").into()
}),
}
}
}
@@ -133,9 +137,14 @@ impl CpuWidgetState {
config: &AppConfigFields, default_selection: CpuDefault, current_display_time: u64,
autohide_timer: Option<Instant>, colours: &Styles,
) -> Self {
const COLUMNS: [Column<CpuWidgetColumn>; 2] = [
let columns = [
Column::soft(CpuWidgetColumn::Cpu, Some(0.5)),
Column::soft(CpuWidgetColumn::Use, Some(0.5)),
Column::soft(
CpuWidgetColumn::Use {
show_decimal: config.show_cpu_decimal,
},
Some(0.5),
),
];
let props = DataTableProps {
@@ -148,7 +157,7 @@ impl CpuWidgetState {
};
let styling = DataTableStyling::from_palette(colours);
let mut table = DataTable::new(COLUMNS, props, styling);
let mut table = DataTable::new(columns, props, styling);
match default_selection {
CpuDefault::All => {}
CpuDefault::Average if !config.show_average_cpu => {}