From a083ec00dd4e74c29bb96bd7c9b0e9cec1cd22b3 Mon Sep 17 00:00:00 2001 From: Lee Wonjoon Date: Tue, 2 Apr 2024 05:06:01 +0000 Subject: 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> --- src/app.rs | 3 ++ src/canvas/components/time_graph.rs | 9 ++++- src/canvas/components/tui_widget/time_chart.rs | 23 +++++++++++- src/canvas/widgets/cpu_graph.rs | 1 + src/canvas/widgets/mem_graph.rs | 1 + src/canvas/widgets/network_graph.rs | 1 + src/constants.rs | 6 +++- src/options.rs | 49 +++++++++++++++++++++++++- src/options/args.rs | 41 +++++++++++++++++++-- src/options/config.rs | 2 ++ 10 files changed, 130 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/app.rs b/src/app.rs index a149869f..60ad35b8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -21,6 +21,7 @@ pub use states::*; use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation}; use crate::{ + canvas::components::time_chart::LegendPosition, constants, data_collection::temperature, data_conversion::ConvertedData, @@ -62,8 +63,10 @@ pub struct AppConfigFields { pub enable_cache_memory: bool, pub show_table_scroll_position: bool, pub is_advanced_kill: bool, + pub memory_legend_position: Option, // TODO: Remove these, move network details state-side. pub network_unit_type: DataUnit, + pub network_legend_position: Option, pub network_scale_type: AxisScaling, pub network_use_binary_prefix: bool, pub retention_ms: u64, diff --git a/src/canvas/components/time_graph.rs b/src/canvas/components/time_graph.rs index 3f438349..361b502e 100644 --- a/src/canvas/components/time_graph.rs +++ b/src/canvas/components/time_graph.rs @@ -11,7 +11,9 @@ use tui::{ }; 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`]. pub struct GraphData<'a> { @@ -48,6 +50,9 @@ pub struct TimeGraph<'a> { /// The title style. pub title_style: Style, + /// The legend position. + pub legend_position: Option, + /// Any legend constraints. pub legend_constraints: Option<(Constraint, Constraint)>, @@ -142,6 +147,7 @@ impl<'a> TimeGraph<'a> { .y_axis(y_axis) .marker(self.marker) .legend_style(self.graph_style) + .legend_position(self.legend_position) .hidden_legend_constraints( self.legend_constraints .unwrap_or(DEFAULT_LEGEND_CONSTRAINTS), @@ -202,6 +208,7 @@ mod test { border_style: Style::default().fg(Color::Blue), is_expanded: false, title_style: Style::default().fg(Color::Cyan), + legend_position: None, legend_constraints: None, marker: Marker::Braille, } diff --git a/src/canvas/components/tui_widget/time_chart.rs b/src/canvas/components/tui_widget/time_chart.rs index 60c797bc..04f491bc 100644 --- a/src/canvas/components/tui_widget/time_chart.rs +++ b/src/canvas/components/tui_widget/time_chart.rs @@ -7,7 +7,7 @@ mod canvas; mod points; -use std::cmp::max; +use std::{cmp::max, str::FromStr}; use canvas::*; 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 { + 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 /// /// This is the main element composing a [`TimeChart`]. diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index b5027bc6..f882b210 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -233,6 +233,7 @@ impl Painter { title, is_expanded: app_state.is_expanded, title_style: self.colours.widget_title_style, + legend_position: None, legend_constraints: None, marker, } diff --git a/src/canvas/widgets/mem_graph.rs b/src/canvas/widgets/mem_graph.rs index b9782e5a..5c814f51 100644 --- a/src/canvas/widgets/mem_graph.rs +++ b/src/canvas/widgets/mem_graph.rs @@ -133,6 +133,7 @@ impl Painter { title: " Memory ".into(), is_expanded: app_state.is_expanded, 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))), marker, } diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 35ab9373..ec7e5fbd 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -162,6 +162,7 @@ impl Painter { title: " Network ".into(), is_expanded: app_state.is_expanded, title_style: self.colours.widget_title_style, + legend_position: app_state.app_config_fields.network_legend_position, legend_constraints: Some(legend_constraints), marker, } diff --git a/src/constants.rs b/src/constants.rs index eb185779..1c3aa097 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -573,7 +573,7 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom. Al #battery = false # Disable mouse clicks #disable_click = false -# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light" +# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light" #color = "default" # Show memory values in the processes widget as values by default #mem_as_value = false @@ -597,6 +597,10 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom. Al #enable_cache_memory = false # How much data is stored at once in terms of time. #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. #[processes] diff --git a/src/options.rs b/src/options.rs index 902c5c87..e36685ea 100644 --- a/src/options.rs +++ b/src/options.rs @@ -24,7 +24,7 @@ use starship_battery::Manager; use self::config::{layout::Row, IgnoreList, StringOrNum}; use crate::{ app::{filter::Filter, layout_manager::*, *}, - canvas::{styling::CanvasStyling, ColourScheme}, + canvas::{components::time_chart::LegendPosition, styling::CanvasStyling, ColourScheme}, constants::*, data_collection::temperature::TemperatureType, 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. let app_config_fields = AppConfigFields { update_rate: get_update_rate(matches, config) @@ -150,6 +153,8 @@ pub fn init_app( enable_cache_memory: get_enable_cache_memory(matches, config), show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config), is_advanced_kill, + memory_legend_position, + network_legend_position, network_scale_type, network_unit_type, network_use_binary_prefix, @@ -772,6 +777,48 @@ fn get_retention(matches: &ArgMatches, config: &Config) -> error::Result { } } +fn get_network_legend( + matches: &ArgMatches, config: &Config, +) -> error::Result> { + let error = + |_| BottomError::ConfigError("network_legend is set to an invalid value".to_string()); + if let Some(s) = matches.get_one::("network_legend") { + match s.to_ascii_lowercase().trim() { + "none" => Ok(None), + position => Ok(Some(position.parse::().map_err(error)?)), + } + } else if let Some(flags) = &config.flags { + if let Some(legend) = &flags.network_legend { + Ok(Some(legend.parse::().map_err(error)?)) + } else { + Ok(Some(LegendPosition::default())) + } + } else { + Ok(Some(LegendPosition::default())) + } +} + +fn get_memory_legend( + matches: &ArgMatches, config: &Config, +) -> error::Result> { + let error = + |_| BottomError::ConfigError("memory_legend is set to an invalid value".to_string()); + if let Some(s) = matches.get_one::("memory_legend") { + match s.to_ascii_lowercase().trim() { + "none" => Ok(None), + position => Ok(Some(position.parse::().map_err(error)?)), + } + } else if let Some(flags) = &config.flags { + if let Some(legend) = &flags.memory_legend { + Ok(Some(legend.parse::().map_err(error)?)) + } else { + Ok(Some(LegendPosition::default())) + } + } else { + Ok(Some(LegendPosition::default())) + } +} + #[cfg(test)] mod test { use clap::ArgMatches; diff --git a/src/options/args.rs b/src/options/args.rs index b410eba4..c8fe35320 100644 --- a/src/options/args.rs +++ b/src/options/args.rs @@ -436,6 +436,24 @@ fn mem_args(cmd: Command) -> Command { 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"))] { let enable_cache_memory = Arg::new("enable_cache_memory") @@ -443,11 +461,11 @@ fn mem_args(cmd: Command) -> Command { .action(ArgAction::SetTrue) .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")] { - 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.", ); + 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") .long("network_use_bytes") .action(ArgAction::SetTrue) @@ -487,6 +523,7 @@ fn network_args(cmd: Command) -> Command { cmd.args(args![ use_old_network_legend, + network_legend, network_use_bytes, network_use_log, network_use_binary_prefix, diff --git a/src/options/config.rs b/src/options/config.rs index 23c2f956..6b3643f0 100644 --- a/src/options/config.rs +++ b/src/options/config.rs @@ -68,6 +68,8 @@ pub(crate) struct ConfigFlags { pub(crate) battery: Option, pub(crate) disable_click: Option, pub(crate) no_write: Option, + pub(crate) network_legend: Option, + pub(crate) memory_legend: Option, /// For built-in colour palettes. pub(crate) color: Option, pub(crate) mem_as_value: Option, -- cgit v1.2.3