Skip to content

Commit 7b5b06b

Browse files
committed
feat: add option to move avg CPU to another row
Adds an `average_cpu_row` option to move the average CPU usage to its own row when using basic mode.
1 parent 28416dd commit 7b5b06b

File tree

8 files changed

+152
-63
lines changed

8 files changed

+152
-63
lines changed

docs/content/configuration/command-line-options.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ see information on these options by running `btm -h`, or run `btm --help` to dis
4848

4949
## CPU Options
5050

51-
| Option | Behaviour |
52-
| -------------------- | ------------------------------------------- |
53-
| `--cpu_left_legend` | Puts the CPU chart legend on the left side. |
54-
| `-a, --hide_avg_cpu` | Hides the average CPU usage entry. |
51+
| Option | Behaviour |
52+
| ----------------------- | -------------------------------------------------------------------------------- |
53+
| `--cpu_left_legend` | Puts the CPU chart legend on the left side. |
54+
| `-a, --hide_avg_cpu` | Hides the average CPU usage entry. |
55+
| `-A, --average_cpu_row` | Moves the average CPU usage entry to its own row when using basic mode. |
5556

5657
## Memory Options
5758

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

Lines changed: 38 additions & 37 deletions
Large diffs are not rendered by default.

schema/nightly/bottom.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,12 @@
562562
"boolean",
563563
"null"
564564
]
565+
},
566+
"average_cpu_row": {
567+
"type": [
568+
"boolean",
569+
"null"
570+
]
565571
}
566572
}
567573
},

src/app.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub struct AppConfigFields {
7070
pub network_scale_type: AxisScaling,
7171
pub network_use_binary_prefix: bool,
7272
pub retention_ms: u64,
73+
pub dedicated_average_row: bool,
7374
}
7475

7576
/// For filtering out information

src/canvas/widgets/cpu_basic.rs

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
impl Painter {
2121
/// Inspired by htop.
2222
pub fn draw_basic_cpu(
23-
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
23+
&self, f: &mut Frame<'_>, app_state: &mut App, mut draw_loc: Rect, widget_id: u64,
2424
) {
2525
// Skip the first element, it's the "all" element
2626
if app_state.converted_data.cpu_data.len() > 1 {
@@ -45,6 +45,32 @@ impl Painter {
4545
);
4646
}
4747

48+
let (cpu_data, avg_data) =
49+
maybe_split_avg(cpu_data, app_state.app_config_fields.dedicated_average_row);
50+
51+
if let Some(avg) = avg_data {
52+
let (outer, inner, ratio, style) = self.cpu_info(&avg);
53+
let [cores_loc, mut avg_loc] =
54+
Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(draw_loc);
55+
56+
// The cores section all have horizontal margin, so to line up with the cores we
57+
// need to add some margin ourselves.
58+
avg_loc.x += 1;
59+
avg_loc.width -= 2;
60+
61+
f.render_widget(
62+
PipeGauge::default()
63+
.gauge_style(style)
64+
.label_style(style)
65+
.inner_label(inner)
66+
.start_label(outer)
67+
.ratio(ratio),
68+
avg_loc,
69+
);
70+
71+
draw_loc = cores_loc;
72+
}
73+
4874
if draw_loc.height > 0 {
4975
let remaining_height = usize::from(draw_loc.height);
5076
const REQUIRED_COLUMNS: usize = 4;
@@ -56,27 +82,7 @@ impl Painter {
5682
.direction(Direction::Horizontal)
5783
.split(draw_loc);
5884

59-
let mut gauge_info = cpu_data.iter().map(|cpu| match cpu {
60-
CpuWidgetData::All => unreachable!(),
61-
CpuWidgetData::Entry {
62-
data_type,
63-
data: _,
64-
last_entry,
65-
} => {
66-
let (outer, style) = match data_type {
67-
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_colour_style),
68-
CpuDataType::Cpu(index) => (
69-
format!("{index:<3}",),
70-
self.colours.cpu_colour_styles
71-
[index % self.colours.cpu_colour_styles.len()],
72-
),
73-
};
74-
let inner = format!("{:>3.0}%", last_entry.round());
75-
let ratio = last_entry / 100.0;
76-
77-
(outer, inner, ratio, style)
78-
}
79-
});
85+
let mut gauge_info = cpu_data.iter().map(|cpu| self.cpu_info(cpu));
8086

8187
// Very ugly way to sync the gauge limit across all gauges.
8288
let hide_parts = columns
@@ -138,4 +144,64 @@ impl Painter {
138144
}
139145
}
140146
}
147+
148+
fn cpu_info(&self, cpu: &CpuWidgetData) -> (String, String, f64, tui::style::Style) {
149+
let CpuWidgetData::Entry {
150+
data_type,
151+
last_entry,
152+
..
153+
} = cpu
154+
else {
155+
unreachable!()
156+
};
157+
158+
let (outer, style) = match data_type {
159+
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_colour_style),
160+
CpuDataType::Cpu(index) => (
161+
format!("{index:<3}",),
162+
self.colours.cpu_colour_styles[index % self.colours.cpu_colour_styles.len()],
163+
),
164+
};
165+
let inner = format!("{:>3.0}%", last_entry.round());
166+
let ratio = last_entry / 100.0;
167+
168+
(outer, inner, ratio, style)
169+
}
170+
}
171+
172+
fn maybe_split_avg(
173+
data: &[CpuWidgetData], separate_avg: bool,
174+
) -> (Vec<CpuWidgetData>, Option<CpuWidgetData>) {
175+
let mut cpu_data = vec![];
176+
let mut avg_data = None;
177+
178+
for cpu in data {
179+
let CpuWidgetData::Entry {
180+
data_type,
181+
data,
182+
last_entry,
183+
} = cpu
184+
else {
185+
unreachable!()
186+
};
187+
188+
match data_type {
189+
CpuDataType::Avg if separate_avg => {
190+
avg_data = Some(CpuWidgetData::Entry {
191+
data_type: *data_type,
192+
data: data.clone(),
193+
last_entry: *last_entry,
194+
});
195+
}
196+
_ => {
197+
cpu_data.push(CpuWidgetData::Entry {
198+
data_type: *data_type,
199+
data: data.clone(),
200+
last_entry: *last_entry,
201+
});
202+
}
203+
}
204+
}
205+
206+
(cpu_data, avg_data)
141207
}

src/options.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ pub fn init_app(
225225
network_unit_type,
226226
network_use_binary_prefix,
227227
retention_ms,
228+
dedicated_average_row: get_dedicated_avg_row(args, config),
228229
};
229230

230231
let table_config = ProcTableConfig {
@@ -564,6 +565,16 @@ fn get_show_average_cpu(args: &BottomArgs, config: &ConfigV1) -> bool {
564565
true
565566
}
566567

568+
fn get_dedicated_avg_row(args: &BottomArgs, config: &ConfigV1) -> bool {
569+
let conf = config
570+
.flags
571+
.as_ref()
572+
.and_then(|flags| flags.average_cpu_row)
573+
.unwrap_or(false);
574+
575+
args.cpu.average_cpu_row || conf
576+
}
577+
567578
fn try_parse_ms(s: &str) -> error::Result<u64> {
568579
if let Ok(val) = humantime::parse_duration(s) {
569580
Ok(val.as_millis().try_into()?)

src/options/args.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,8 @@ pub struct CpuArgs {
428428
help = "Puts the CPU chart legend on the left side."
429429
)]
430430
pub cpu_left_legend: bool,
431+
#[arg(short = 'A', long, action = ArgAction::SetTrue, help = "Moves the average CPU usage entry to its own row when using basic mode.")]
432+
pub average_cpu_row: bool,
431433
}
432434

433435
/// Memory argument/config options.

src/options/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,5 @@ pub(crate) struct FlagConfig {
9494
pub(crate) enable_gpu: Option<bool>,
9595
pub(crate) enable_cache_memory: Option<bool>,
9696
pub(crate) retention: Option<StringOrNum>,
97+
pub(crate) average_cpu_row: Option<bool>,
9798
}

0 commit comments

Comments
 (0)