mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-22 12:13:06 +00:00
feature: Add option to set a position of legend (#1430)
* Add option to set a position of legend * some small changes --------- Co-authored-by: ClementTsang <34804052+ClementTsang@users.noreply.github.com>
This commit is contained in:
parent
2ee0df1502
commit
a083ec00dd
12 changed files with 169 additions and 41 deletions
|
@ -58,6 +58,7 @@ see information on these options by running `btm -h`, or run `btm --help` to dis
|
||||||
| ----------------------- | --------------------------------------------------------- |
|
| ----------------------- | --------------------------------------------------------- |
|
||||||
| `--enable_cache_memory` | Enable collecting and displaying cache and buffer memory. |
|
| `--enable_cache_memory` | Enable collecting and displaying cache and buffer memory. |
|
||||||
| `--mem_as_value` | Defaults to showing process memory usage by value. |
|
| `--mem_as_value` | Defaults to showing process memory usage by value. |
|
||||||
|
| `--memory_legend` | Where to place the legend for the memory widget. |
|
||||||
|
|
||||||
## Network Options
|
## Network Options
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ see information on these options by running `btm -h`, or run `btm --help` to dis
|
||||||
| `--network_use_binary_prefix` | Displays the network widget with binary prefixes. |
|
| `--network_use_binary_prefix` | Displays the network widget with binary prefixes. |
|
||||||
| `--network_use_bytes` | Displays the network widget using bytes. |
|
| `--network_use_bytes` | Displays the network widget using bytes. |
|
||||||
| `--network_use_log` | Displays the network widget with a log scale. |
|
| `--network_use_log` | Displays the network widget with a log scale. |
|
||||||
|
| `--network_legend` | Where to place the legend for the network widget. |
|
||||||
| `--use_old_network_legend` | DEPRECATED - uses a separate network legend. |
|
| `--use_old_network_legend` | DEPRECATED - uses a separate network legend. |
|
||||||
|
|
||||||
## Battery Options
|
## Battery Options
|
||||||
|
|
|
@ -8,7 +8,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. |
|
||||||
| `left_legend` | Boolean | Puts the CPU chart legend to the left side. |
|
| `left_legend` | Boolean | Puts the CPU chart legend to the left side. |
|
||||||
|
@ -29,7 +29,7 @@ each time:
|
||||||
| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. |
|
| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. |
|
||||||
| `disable_click` | Boolean | Disables mouse clicks. |
|
| `disable_click` | Boolean | Disables mouse clicks. |
|
||||||
| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. |
|
| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. |
|
||||||
| `enable_cache_memory` | Boolean | Enable collecting and displaying cache and buffer memory (not available on Windows). |
|
| `enable_cache_memory` | Boolean | Enable cache and buffer memory stats (not available on Windows). |
|
||||||
| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. |
|
| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. |
|
||||||
| `tree` | Boolean | Defaults to showing the process widget in tree mode. |
|
| `tree` | Boolean | Defaults to showing the process widget in tree mode. |
|
||||||
| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. |
|
| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. |
|
||||||
|
@ -42,3 +42,5 @@ each time:
|
||||||
| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. |
|
| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. |
|
||||||
| `unnormalized_cpu` | Boolean | Show process CPU% without normalizing over the number of cores. |
|
| `unnormalized_cpu` | Boolean | Show process CPU% without normalizing over the number of cores. |
|
||||||
| `expanded_on_startup` | Boolean | Expand the default widget upon starting the app. |
|
| `expanded_on_startup` | 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. |
|
||||||
|
| `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. |
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub use states::*;
|
||||||
use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation};
|
use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
canvas::components::time_chart::LegendPosition,
|
||||||
constants,
|
constants,
|
||||||
data_collection::temperature,
|
data_collection::temperature,
|
||||||
data_conversion::ConvertedData,
|
data_conversion::ConvertedData,
|
||||||
|
@ -62,8 +63,10 @@ pub struct AppConfigFields {
|
||||||
pub enable_cache_memory: bool,
|
pub enable_cache_memory: bool,
|
||||||
pub show_table_scroll_position: bool,
|
pub show_table_scroll_position: bool,
|
||||||
pub is_advanced_kill: bool,
|
pub is_advanced_kill: bool,
|
||||||
|
pub memory_legend_position: Option<LegendPosition>,
|
||||||
// TODO: Remove these, move network details state-side.
|
// TODO: Remove these, move network details state-side.
|
||||||
pub network_unit_type: DataUnit,
|
pub network_unit_type: DataUnit,
|
||||||
|
pub network_legend_position: Option<LegendPosition>,
|
||||||
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,
|
||||||
|
|
|
@ -11,7 +11,9 @@ use tui::{
|
||||||
};
|
};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::time_chart::{Axis, Dataset, Point, TimeChart, DEFAULT_LEGEND_CONSTRAINTS};
|
use super::time_chart::{
|
||||||
|
Axis, Dataset, LegendPosition, Point, TimeChart, DEFAULT_LEGEND_CONSTRAINTS,
|
||||||
|
};
|
||||||
|
|
||||||
/// Represents the data required by the [`TimeGraph`].
|
/// Represents the data required by the [`TimeGraph`].
|
||||||
pub struct GraphData<'a> {
|
pub struct GraphData<'a> {
|
||||||
|
@ -48,6 +50,9 @@ pub struct TimeGraph<'a> {
|
||||||
/// The title style.
|
/// The title style.
|
||||||
pub title_style: Style,
|
pub title_style: Style,
|
||||||
|
|
||||||
|
/// The legend position.
|
||||||
|
pub legend_position: Option<LegendPosition>,
|
||||||
|
|
||||||
/// Any legend constraints.
|
/// Any legend constraints.
|
||||||
pub legend_constraints: Option<(Constraint, Constraint)>,
|
pub legend_constraints: Option<(Constraint, Constraint)>,
|
||||||
|
|
||||||
|
@ -142,6 +147,7 @@ impl<'a> TimeGraph<'a> {
|
||||||
.y_axis(y_axis)
|
.y_axis(y_axis)
|
||||||
.marker(self.marker)
|
.marker(self.marker)
|
||||||
.legend_style(self.graph_style)
|
.legend_style(self.graph_style)
|
||||||
|
.legend_position(self.legend_position)
|
||||||
.hidden_legend_constraints(
|
.hidden_legend_constraints(
|
||||||
self.legend_constraints
|
self.legend_constraints
|
||||||
.unwrap_or(DEFAULT_LEGEND_CONSTRAINTS),
|
.unwrap_or(DEFAULT_LEGEND_CONSTRAINTS),
|
||||||
|
@ -202,6 +208,7 @@ mod test {
|
||||||
border_style: Style::default().fg(Color::Blue),
|
border_style: Style::default().fg(Color::Blue),
|
||||||
is_expanded: false,
|
is_expanded: false,
|
||||||
title_style: Style::default().fg(Color::Cyan),
|
title_style: Style::default().fg(Color::Cyan),
|
||||||
|
legend_position: None,
|
||||||
legend_constraints: None,
|
legend_constraints: None,
|
||||||
marker: Marker::Braille,
|
marker: Marker::Braille,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
mod canvas;
|
mod canvas;
|
||||||
mod points;
|
mod points;
|
||||||
|
|
||||||
use std::cmp::max;
|
use std::{cmp::max, str::FromStr};
|
||||||
|
|
||||||
use canvas::*;
|
use canvas::*;
|
||||||
use tui::{
|
use tui::{
|
||||||
|
@ -213,6 +213,27 @@ impl LegendPosition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ParseLegendPositionError;
|
||||||
|
|
||||||
|
impl FromStr for LegendPosition {
|
||||||
|
type Err = ParseLegendPositionError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_ascii_lowercase().as_str() {
|
||||||
|
"top" => Ok(Self::Top),
|
||||||
|
"top-left" => Ok(Self::TopLeft),
|
||||||
|
"top-right" => Ok(Self::TopRight),
|
||||||
|
"left" => Ok(Self::Left),
|
||||||
|
"right" => Ok(Self::Right),
|
||||||
|
"bottom-left" => Ok(Self::BottomLeft),
|
||||||
|
"bottom" => Ok(Self::Bottom),
|
||||||
|
"bottom-right" => Ok(Self::BottomRight),
|
||||||
|
_ => Err(ParseLegendPositionError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A group of data points
|
/// A group of data points
|
||||||
///
|
///
|
||||||
/// This is the main element composing a [`TimeChart`].
|
/// This is the main element composing a [`TimeChart`].
|
||||||
|
|
|
@ -233,6 +233,7 @@ impl Painter {
|
||||||
title,
|
title,
|
||||||
is_expanded: app_state.is_expanded,
|
is_expanded: app_state.is_expanded,
|
||||||
title_style: self.colours.widget_title_style,
|
title_style: self.colours.widget_title_style,
|
||||||
|
legend_position: None,
|
||||||
legend_constraints: None,
|
legend_constraints: None,
|
||||||
marker,
|
marker,
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ impl Painter {
|
||||||
title: " Memory ".into(),
|
title: " Memory ".into(),
|
||||||
is_expanded: app_state.is_expanded,
|
is_expanded: app_state.is_expanded,
|
||||||
title_style: self.colours.widget_title_style,
|
title_style: self.colours.widget_title_style,
|
||||||
|
legend_position: app_state.app_config_fields.memory_legend_position,
|
||||||
legend_constraints: Some((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))),
|
legend_constraints: Some((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))),
|
||||||
marker,
|
marker,
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,6 +162,7 @@ impl Painter {
|
||||||
title: " Network ".into(),
|
title: " Network ".into(),
|
||||||
is_expanded: app_state.is_expanded,
|
is_expanded: app_state.is_expanded,
|
||||||
title_style: self.colours.widget_title_style,
|
title_style: self.colours.widget_title_style,
|
||||||
|
legend_position: app_state.app_config_fields.network_legend_position,
|
||||||
legend_constraints: Some(legend_constraints),
|
legend_constraints: Some(legend_constraints),
|
||||||
marker,
|
marker,
|
||||||
}
|
}
|
||||||
|
|
|
@ -597,6 +597,10 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom. Al
|
||||||
#enable_cache_memory = false
|
#enable_cache_memory = false
|
||||||
# How much data is stored at once in terms of time.
|
# How much data is stored at once in terms of time.
|
||||||
#retention = "10m"
|
#retention = "10m"
|
||||||
|
# Where to place the legend for the memory widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
||||||
|
#memory_legend = "TopRight".
|
||||||
|
# Where to place the legend for the network widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
||||||
|
#network_legend = "TopRight".
|
||||||
|
|
||||||
# These are flags around the process widget.
|
# These are flags around the process widget.
|
||||||
#[processes]
|
#[processes]
|
||||||
|
|
|
@ -24,7 +24,7 @@ use starship_battery::Manager;
|
||||||
use self::config::{layout::Row, IgnoreList, StringOrNum};
|
use self::config::{layout::Row, IgnoreList, StringOrNum};
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{filter::Filter, layout_manager::*, *},
|
app::{filter::Filter, layout_manager::*, *},
|
||||||
canvas::{styling::CanvasStyling, ColourScheme},
|
canvas::{components::time_chart::LegendPosition, styling::CanvasStyling, ColourScheme},
|
||||||
constants::*,
|
constants::*,
|
||||||
data_collection::temperature::TemperatureType,
|
data_collection::temperature::TemperatureType,
|
||||||
utils::{
|
utils::{
|
||||||
|
@ -126,6 +126,9 @@ pub fn init_app(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let network_legend_position = get_network_legend(matches, config)?;
|
||||||
|
let memory_legend_position = get_memory_legend(matches, config)?;
|
||||||
|
|
||||||
// TODO: Can probably just reuse the options struct.
|
// TODO: Can probably just reuse the options struct.
|
||||||
let app_config_fields = AppConfigFields {
|
let app_config_fields = AppConfigFields {
|
||||||
update_rate: get_update_rate(matches, config)
|
update_rate: get_update_rate(matches, config)
|
||||||
|
@ -150,6 +153,8 @@ pub fn init_app(
|
||||||
enable_cache_memory: get_enable_cache_memory(matches, config),
|
enable_cache_memory: get_enable_cache_memory(matches, config),
|
||||||
show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config),
|
show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config),
|
||||||
is_advanced_kill,
|
is_advanced_kill,
|
||||||
|
memory_legend_position,
|
||||||
|
network_legend_position,
|
||||||
network_scale_type,
|
network_scale_type,
|
||||||
network_unit_type,
|
network_unit_type,
|
||||||
network_use_binary_prefix,
|
network_use_binary_prefix,
|
||||||
|
@ -772,6 +777,48 @@ fn get_retention(matches: &ArgMatches, config: &Config) -> error::Result<u64> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_network_legend(
|
||||||
|
matches: &ArgMatches, config: &Config,
|
||||||
|
) -> error::Result<Option<LegendPosition>> {
|
||||||
|
let error =
|
||||||
|
|_| BottomError::ConfigError("network_legend is set to an invalid value".to_string());
|
||||||
|
if let Some(s) = matches.get_one::<String>("network_legend") {
|
||||||
|
match s.to_ascii_lowercase().trim() {
|
||||||
|
"none" => Ok(None),
|
||||||
|
position => Ok(Some(position.parse::<LegendPosition>().map_err(error)?)),
|
||||||
|
}
|
||||||
|
} else if let Some(flags) = &config.flags {
|
||||||
|
if let Some(legend) = &flags.network_legend {
|
||||||
|
Ok(Some(legend.parse::<LegendPosition>().map_err(error)?))
|
||||||
|
} else {
|
||||||
|
Ok(Some(LegendPosition::default()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Some(LegendPosition::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_legend(
|
||||||
|
matches: &ArgMatches, config: &Config,
|
||||||
|
) -> error::Result<Option<LegendPosition>> {
|
||||||
|
let error =
|
||||||
|
|_| BottomError::ConfigError("memory_legend is set to an invalid value".to_string());
|
||||||
|
if let Some(s) = matches.get_one::<String>("memory_legend") {
|
||||||
|
match s.to_ascii_lowercase().trim() {
|
||||||
|
"none" => Ok(None),
|
||||||
|
position => Ok(Some(position.parse::<LegendPosition>().map_err(error)?)),
|
||||||
|
}
|
||||||
|
} else if let Some(flags) = &config.flags {
|
||||||
|
if let Some(legend) = &flags.memory_legend {
|
||||||
|
Ok(Some(legend.parse::<LegendPosition>().map_err(error)?))
|
||||||
|
} else {
|
||||||
|
Ok(Some(LegendPosition::default()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Some(LegendPosition::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
|
@ -436,6 +436,24 @@ fn mem_args(cmd: Command) -> Command {
|
||||||
to showing it by percentage.",
|
to showing it by percentage.",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let memory_legend = Arg::new("memory_legend")
|
||||||
|
.long("memory_legend")
|
||||||
|
.action(ArgAction::Set)
|
||||||
|
.value_name("POSITION")
|
||||||
|
.ignore_case(true)
|
||||||
|
.help("Where to place the legend for the memory widget.")
|
||||||
|
.value_parser([
|
||||||
|
"none",
|
||||||
|
"top-left",
|
||||||
|
"top",
|
||||||
|
"top-right",
|
||||||
|
"left",
|
||||||
|
"right",
|
||||||
|
"bottom-left",
|
||||||
|
"bottom",
|
||||||
|
"bottom-right",
|
||||||
|
]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
{
|
{
|
||||||
let enable_cache_memory = Arg::new("enable_cache_memory")
|
let enable_cache_memory = Arg::new("enable_cache_memory")
|
||||||
|
@ -443,11 +461,11 @@ fn mem_args(cmd: Command) -> Command {
|
||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
.help("Enable collecting and displaying cache and buffer memory.");
|
.help("Enable collecting and displaying cache and buffer memory.");
|
||||||
|
|
||||||
cmd.args(args![mem_as_value, enable_cache_memory])
|
cmd.args(args![mem_as_value, memory_legend, enable_cache_memory])
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
cmd.arg(mem_as_value)
|
cmd.args(args![mem_as_value, memory_legend])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,6 +482,24 @@ fn network_args(cmd: Command) -> Command {
|
||||||
display is not tested anymore and may be broken.",
|
display is not tested anymore and may be broken.",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let network_legend = Arg::new("network_legend")
|
||||||
|
.long("network_legend")
|
||||||
|
.action(ArgAction::Set)
|
||||||
|
.value_name("POSITION")
|
||||||
|
.ignore_case(true)
|
||||||
|
.help("Where to place the legend for the network widget.")
|
||||||
|
.value_parser([
|
||||||
|
"none",
|
||||||
|
"top-left",
|
||||||
|
"top",
|
||||||
|
"top-right",
|
||||||
|
"left",
|
||||||
|
"right",
|
||||||
|
"bottom-left",
|
||||||
|
"bottom",
|
||||||
|
"bottom-right",
|
||||||
|
]);
|
||||||
|
|
||||||
let network_use_bytes = Arg::new("network_use_bytes")
|
let network_use_bytes = Arg::new("network_use_bytes")
|
||||||
.long("network_use_bytes")
|
.long("network_use_bytes")
|
||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
|
@ -487,6 +523,7 @@ fn network_args(cmd: Command) -> Command {
|
||||||
|
|
||||||
cmd.args(args![
|
cmd.args(args![
|
||||||
use_old_network_legend,
|
use_old_network_legend,
|
||||||
|
network_legend,
|
||||||
network_use_bytes,
|
network_use_bytes,
|
||||||
network_use_log,
|
network_use_log,
|
||||||
network_use_binary_prefix,
|
network_use_binary_prefix,
|
||||||
|
|
|
@ -68,6 +68,8 @@ pub(crate) struct ConfigFlags {
|
||||||
pub(crate) battery: Option<bool>,
|
pub(crate) battery: Option<bool>,
|
||||||
pub(crate) disable_click: Option<bool>,
|
pub(crate) disable_click: Option<bool>,
|
||||||
pub(crate) no_write: Option<bool>,
|
pub(crate) no_write: Option<bool>,
|
||||||
|
pub(crate) network_legend: Option<String>,
|
||||||
|
pub(crate) memory_legend: Option<String>,
|
||||||
/// For built-in colour palettes.
|
/// For built-in colour palettes.
|
||||||
pub(crate) color: Option<String>,
|
pub(crate) color: Option<String>,
|
||||||
pub(crate) mem_as_value: Option<bool>,
|
pub(crate) mem_as_value: Option<bool>,
|
||||||
|
|
Loading…
Reference in a new issue