feat: add option to move avg CPU to another row (#1487)

Adds an `average_cpu_row` option to move the average CPU usage to its own row
when using basic mode.
This commit is contained in:
Zeb Piasecki 2024-08-03 01:10:36 -04:00 committed by GitHub
parent 9379c03595
commit d6c2ef3e22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 147 additions and 58 deletions

View File

@ -15,7 +15,7 @@ Most of the [command line flags](../command-line-options.md) have config file eq
each time: each time:
| Field | Type | Functionality | | Field | Type | Functionality |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| `hide_avg_cpu` | Boolean | Hides the average CPU usage. | | `hide_avg_cpu` | Boolean | Hides the average CPU usage. |
| `dot_marker` | Boolean | Uses a dot marker for graphs. | | `dot_marker` | Boolean | Uses a dot marker for graphs. |
| `cpu_left_legend` | Boolean | Puts the CPU chart legend to the left side. | | `cpu_left_legend` | Boolean | Puts the CPU chart legend to the left side. |
@ -50,3 +50,4 @@ each time:
| `expanded` | Boolean | Expand the default widget upon starting the app. | | `expanded` | Boolean | Expand the default widget upon starting the app. |
| `memory_legend` | String (one of ["none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right"]) | Where to place the legend for the memory widget. | | `memory_legend` | String (one of ["none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right"]) | Where to place the legend for the memory widget. |
| `network_legend` | String (one of ["none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right"]) | Where to place the legend for the network widget. | | `network_legend` | String (one of ["none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right"]) | Where to place the legend for the network widget. |
| `average_cpu_row` | Boolean | Moves the average CPU usage entry to its own row when using basic mode. |

View File

@ -246,6 +246,12 @@
"null" "null"
] ]
}, },
"average_cpu_row": {
"type": [
"boolean",
"null"
]
},
"basic": { "basic": {
"type": [ "type": [
"boolean", "boolean",

View File

@ -68,6 +68,7 @@ pub struct AppConfigFields {
pub network_scale_type: AxisScaling, pub network_scale_type: AxisScaling,
pub network_use_binary_prefix: bool, pub network_use_binary_prefix: bool,
pub retention_ms: u64, pub retention_ms: u64,
pub dedicated_average_row: bool,
} }
/// For filtering out information /// For filtering out information

View File

@ -20,7 +20,7 @@ use crate::{
impl Painter { impl Painter {
/// Inspired by htop. /// Inspired by htop.
pub fn draw_basic_cpu( pub fn draw_basic_cpu(
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64, &self, f: &mut Frame<'_>, app_state: &mut App, mut draw_loc: Rect, widget_id: u64,
) { ) {
// Skip the first element, it's the "all" element // Skip the first element, it's the "all" element
if app_state.converted_data.cpu_data.len() > 1 { if app_state.converted_data.cpu_data.len() > 1 {
@ -45,6 +45,32 @@ impl Painter {
); );
} }
let (cpu_data, avg_data) =
maybe_split_avg(cpu_data, app_state.app_config_fields.dedicated_average_row);
if let Some(avg) = avg_data {
let (outer, inner, ratio, style) = self.cpu_info(&avg);
let [cores_loc, mut avg_loc] =
Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(draw_loc);
// The cores section all have horizontal margin, so to line up with the cores we
// need to add some margin ourselves.
avg_loc.x += 1;
avg_loc.width -= 2;
f.render_widget(
PipeGauge::default()
.gauge_style(style)
.label_style(style)
.inner_label(inner)
.start_label(outer)
.ratio(ratio),
avg_loc,
);
draw_loc = cores_loc;
}
if draw_loc.height > 0 { if draw_loc.height > 0 {
let remaining_height = usize::from(draw_loc.height); let remaining_height = usize::from(draw_loc.height);
const REQUIRED_COLUMNS: usize = 4; const REQUIRED_COLUMNS: usize = 4;
@ -56,27 +82,7 @@ impl Painter {
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.split(draw_loc); .split(draw_loc);
let mut gauge_info = cpu_data.iter().map(|cpu| match cpu { let mut gauge_info = cpu_data.iter().map(|cpu| self.cpu_info(cpu));
CpuWidgetData::All => unreachable!(),
CpuWidgetData::Entry {
data_type,
data: _,
last_entry,
} => {
let (outer, style) = match data_type {
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_cpu_colour),
CpuDataType::Cpu(index) => (
format!("{index:<3}",),
self.colours.cpu_colour_styles
[index % self.colours.cpu_colour_styles.len()],
),
};
let inner = format!("{:>3.0}%", last_entry.round());
let ratio = last_entry / 100.0;
(outer, inner, ratio, style)
}
});
// Very ugly way to sync the gauge limit across all gauges. // Very ugly way to sync the gauge limit across all gauges.
let hide_parts = columns let hide_parts = columns
@ -138,4 +144,64 @@ impl Painter {
} }
} }
} }
fn cpu_info(&self, cpu: &CpuWidgetData) -> (String, String, f64, tui::style::Style) {
let CpuWidgetData::Entry {
data_type,
last_entry,
..
} = cpu
else {
unreachable!()
};
let (outer, style) = match data_type {
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_cpu_colour),
CpuDataType::Cpu(index) => (
format!("{index:<3}",),
self.colours.cpu_colour_styles[index % self.colours.cpu_colour_styles.len()],
),
};
let inner = format!("{:>3.0}%", last_entry.round());
let ratio = last_entry / 100.0;
(outer, inner, ratio, style)
}
}
fn maybe_split_avg(
data: &[CpuWidgetData], separate_avg: bool,
) -> (Vec<CpuWidgetData>, Option<CpuWidgetData>) {
let mut cpu_data = vec![];
let mut avg_data = None;
for cpu in data {
let CpuWidgetData::Entry {
data_type,
data,
last_entry,
} = cpu
else {
unreachable!()
};
match data_type {
CpuDataType::Avg if separate_avg => {
avg_data = Some(CpuWidgetData::Entry {
data_type: *data_type,
data: data.clone(),
last_entry: *last_entry,
});
}
_ => {
cpu_data.push(CpuWidgetData::Entry {
data_type: *data_type,
data: data.clone(),
last_entry: *last_entry,
});
}
}
}
(cpu_data, avg_data)
} }

View File

@ -269,6 +269,7 @@ pub(crate) fn init_app(
network_unit_type, network_unit_type,
network_use_binary_prefix, network_use_binary_prefix,
retention_ms, retention_ms,
dedicated_average_row: get_dedicated_avg_row(args, config),
}; };
let table_config = ProcTableConfig { let table_config = ProcTableConfig {
@ -677,6 +678,17 @@ fn get_show_average_cpu(args: &BottomArgs, config: &Config) -> bool {
true true
} }
fn get_dedicated_avg_row(_args: &BottomArgs, config: &Config) -> bool {
let conf = config
.flags
.as_ref()
.and_then(|flags| flags.average_cpu_row)
.unwrap_or(false);
// args.cpu.average_cpu_row || conf
conf
}
#[inline] #[inline]
fn get_default_time_value( fn get_default_time_value(
args: &BottomArgs, config: &Config, retention_ms: u64, args: &BottomArgs, config: &Config, retention_ms: u64,

View File

@ -428,6 +428,8 @@ pub struct CpuArgs {
help = "Puts the CPU chart legend on the left side." help = "Puts the CPU chart legend on the left side."
)] )]
pub cpu_left_legend: bool, pub cpu_left_legend: bool,
// #[arg(short = 'A', long, action = ArgAction::SetTrue, help = "Moves the average CPU usage entry to its own row when using basic mode.")]
// pub average_cpu_row: bool,
} }
/// Memory argument/config options. /// Memory argument/config options.

View File

@ -43,4 +43,5 @@ pub(crate) struct FlagConfig {
pub(crate) enable_gpu: Option<bool>, pub(crate) enable_gpu: Option<bool>,
pub(crate) enable_cache_memory: Option<bool>, pub(crate) enable_cache_memory: Option<bool>,
pub(crate) retention: Option<StringOrNum>, pub(crate) retention: Option<StringOrNum>,
pub(crate) average_cpu_row: Option<bool>,
} }