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:
Lee Wonjoon
2024-04-02 01:06:01 -04:00
committed by GitHub
co-authored by ClementTsang
parent 2ee0df1502
commit a083ec00dd
12 changed files with 169 additions and 41 deletions
+3
View File
@@ -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<LegendPosition>,
// TODO: Remove these, move network details state-side.
pub network_unit_type: DataUnit,
pub network_legend_position: Option<LegendPosition>,
pub network_scale_type: AxisScaling,
pub network_use_binary_prefix: bool,
pub retention_ms: u64,
+8 -1
View File
@@ -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<LegendPosition>,
/// 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,
}
+22 -1
View File
@@ -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<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
///
/// This is the main element composing a [`TimeChart`].
+1
View File
@@ -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,
}
+1
View File
@@ -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,
}
+1
View File
@@ -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,
}
+5 -1
View File
@@ -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]
+48 -1
View File
@@ -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<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)]
mod test {
use clap::ArgMatches;
+39 -2
View File
@@ -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,
+2
View File
@@ -68,6 +68,8 @@ pub(crate) struct ConfigFlags {
pub(crate) battery: Option<bool>,
pub(crate) disable_click: 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.
pub(crate) color: Option<String>,
pub(crate) mem_as_value: Option<bool>,