From 5d09d1846918e1afce84a9eaf9495dc47749a616 Mon Sep 17 00:00:00 2001 From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:27:25 -0400 Subject: [PATCH] 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 --- CHANGELOG.md | 5 ++-- docs/content/configuration/config-file/cpu.md | 9 ++++++++ sample_configs/default_config.toml | 3 +++ schema/nightly/bottom.json | 7 ++++++ src/app.rs | 1 + src/canvas/widgets/cpu_basic.rs | 15 ++++++++---- src/constants.rs | 3 +++ src/options.rs | 9 ++++++++ src/options/config/cpu.rs | 3 +++ src/widgets/cpu_graph.rs | 23 +++++++++++++------ 10 files changed, 65 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b219fac7..8f6138d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/docs/content/configuration/config-file/cpu.md b/docs/content/configuration/config-file/cpu.md index 77d8fe30..10223c36 100644 --- a/docs/content/configuration/config-file/cpu.md +++ b/docs/content/configuration/config-file/cpu.md @@ -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 +``` diff --git a/sample_configs/default_config.toml b/sample_configs/default_config.toml index 3e49e845..5b1ed6ee 100644 --- a/sample_configs/default_config.toml +++ b/sample_configs/default_config.toml @@ -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] diff --git a/schema/nightly/bottom.json b/schema/nightly/bottom.json index 6f104384..9fcdadca 100644 --- a/schema/nightly/bottom.json +++ b/schema/nightly/bottom.json @@ -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" + ] } } }, diff --git a/src/app.rs b/src/app.rs index 2cee2bc3..d22b1aac 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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, diff --git a/src/canvas/widgets/cpu_basic.rs b/src/canvas/widgets/cpu_basic.rs index fc89f646..aded2b1e 100644 --- a/src/canvas/widgets/cpu_basic.rs +++ b/src/canvas/widgets/cpu_basic.rs @@ -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) diff --git a/src/constants.rs b/src/constants.rs index c78aed1b..3bfed224 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -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] diff --git a/src/options.rs b/src/options.rs index 1752c91b..b3ba1ca5 100644 --- a/src/options.rs +++ b/src/options.rs @@ -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 diff --git a/src/options/config/cpu.rs b/src/options/config/cpu.rs index 67fe5b78..58995281 100644 --- a/src/options/config/cpu.rs +++ b/src/options/config/cpu.rs @@ -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, } #[cfg(test)] diff --git a/src/widgets/cpu_graph.rs b/src/widgets/cpu_graph.rs index 75dfa04d..34be2309 100644 --- a/src/widgets/cpu_graph.rs +++ b/src/widgets/cpu_graph.rs @@ -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 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 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, colours: &Styles, ) -> Self { - const COLUMNS: [Column; 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 => {}