mod battery;
mod borders;
mod cpu;
mod graphs;
mod memory;
mod network;
mod tables;
mod themes;
mod utils;
mod widgets;
use std::borrow::Cow;
use battery::BatteryStyle;
use cpu::CpuStyle;
use graphs::GraphStyle;
use memory::MemoryStyle;
use network::NetworkStyle;
use serde::{Deserialize, Serialize};
use tables::TableStyle;
use tui::{style::Style, widgets::BorderType};
use utils::{opt, set_colour, set_colour_list, set_style};
use widgets::WidgetStyle;
use super::Config;
use crate::options::{OptionError, OptionResult, args::BottomArgs};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub(crate) struct ColorStr(Cow<'static, str>);
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
pub(crate) enum TextStyleConfig {
Colour(ColorStr),
TextStyle {
#[serde(alias = "colour")]
color: Option<ColorStr>,
#[serde(alias = "bg_colour")]
bg_color: Option<ColorStr>,
bold: Option<bool>,
italics: Option<bool>,
},
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
pub(crate) struct StyleConfig {
pub(crate) theme: Option<Cow<'static, str>>,
pub(crate) cpu: Option<CpuStyle>,
pub(crate) memory: Option<MemoryStyle>,
pub(crate) network: Option<NetworkStyle>,
pub(crate) battery: Option<BatteryStyle>,
pub(crate) tables: Option<TableStyle>,
pub(crate) graphs: Option<GraphStyle>,
pub(crate) widgets: Option<WidgetStyle>,
}
#[derive(Debug)]
pub struct Styles {
pub(crate) ram_style: Style,
#[cfg(not(target_os = "windows"))]
pub(crate) cache_style: Style,
pub(crate) swap_style: Style,
#[cfg(feature = "zfs")]
pub(crate) arc_style: Style,
#[cfg(feature = "gpu")]
pub(crate) gpu_colours: Vec<Style>,
pub(crate) rx_style: Style,
pub(crate) tx_style: Style,
pub(crate) total_rx_style: Style,
pub(crate) total_tx_style: Style,
pub(crate) all_cpu_colour: Style,
pub(crate) avg_cpu_colour: Style,
pub(crate) cpu_colour_styles: Vec<Style>,
pub(crate) border_style: Style,
pub(crate) highlighted_border_style: Style,
pub(crate) text_style: Style,
pub(crate) selected_text_style: Style,
pub(crate) table_header_style: Style,
pub(crate) widget_title_style: Style,
pub(crate) graph_style: Style,
pub(crate) graph_legend_style: Style,
pub(crate) high_battery: Style,
pub(crate) medium_battery: Style,
pub(crate) low_battery: Style,
pub(crate) invalid_query_style: Style,
pub(crate) disabled_text_style: Style,
#[cfg(target_os = "linux")]
pub(crate) thread_text_style: Style,
pub(crate) border_type: BorderType,
}
impl Default for Styles {
fn default() -> Self {
Self::default_palette()
}
}
impl Styles {
pub fn new(args: &BottomArgs, config: &Config) -> anyhow::Result<Self> {
let mut palette = match &args.style.theme {
Some(theme) => Self::from_theme(theme)?,
None => match config.styles.as_ref().and_then(|s| s.theme.as_ref()) {
Some(theme) => Self::from_theme(theme)?,
None => Self::default(),
},
};
if let Some(config_style) = &config.styles {
palette.set_styles_from_config(config_style)?;
}
Ok(palette)
}
fn from_theme(theme: &str) -> anyhow::Result<Self> {
let lower_case = theme.to_lowercase();
match lower_case.as_str() {
"default" => Ok(Self::default_palette()),
"default-light" => Ok(Self::default_light_palette()),
"gruvbox" => Ok(Self::gruvbox_palette()),
"gruvbox-light" => Ok(Self::gruvbox_light_palette()),
"nord" => Ok(Self::nord_palette()),
"nord-light" => Ok(Self::nord_light_palette()),
_ => Err(
OptionError::other(format!("'{theme}' is an invalid built-in color scheme."))
.into(),
),
}
}
fn set_styles_from_config(&mut self, config: &StyleConfig) -> OptionResult<()> {
set_colour!(self.avg_cpu_colour, config.cpu, avg_entry_color);
set_colour!(self.all_cpu_colour, config.cpu, all_entry_color);
set_colour_list!(self.cpu_colour_styles, config.cpu, cpu_core_colors);
set_colour!(self.ram_style, config.memory, ram_color);
set_colour!(self.swap_style, config.memory, swap_color);
#[cfg(not(target_os = "windows"))]
set_colour!(self.cache_style, config.memory, cache_color);
#[cfg(feature = "zfs")]
set_colour!(self.arc_style, config.memory, arc_color);
#[cfg(feature = "gpu")]
set_colour_list!(self.gpu_colours, config.memory, gpu_colors);
set_colour!(self.rx_style, config.network, rx_color);
set_colour!(self.tx_style, config.network, tx_color);
set_colour!(self.total_rx_style, config.network, rx_total_color);
set_colour!(self.total_tx_style, config.network, tx_total_color);
set_colour!(self.high_battery, config.battery, high_battery_color);
set_colour!(self.medium_battery, config.battery, medium_battery_color);
set_colour!(self.low_battery, config.battery, low_battery_color);
set_style!(self.table_header_style, config.tables, headers);
set_colour!(self.graph_style, config.graphs, graph_color);
set_style!(self.graph_legend_style, config.graphs, legend_text);
set_style!(self.widget_title_style, config.widgets, widget_title);
set_style!(self.text_style, config.widgets, text);
set_style!(self.selected_text_style, config.widgets, selected_text);
set_style!(self.disabled_text_style, config.widgets, disabled_text);
#[cfg(target_os = "linux")]
{
set_style!(self.thread_text_style, config.widgets, thread_text);
}
set_colour!(self.border_style, config.widgets, border_color);
set_colour!(
self.highlighted_border_style,
config.widgets,
selected_border_color
);
if let Some(widgets) = &config.widgets {
if let Some(widget_borders) = widgets.widget_border_type {
self.border_type = widget_borders.into();
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use tui::style::{Color, Style};
use super::Styles;
use crate::options::config::style::utils::str_to_colour;
#[test]
fn default_selected_colour_works() {
let mut colours = Styles::default();
let original_selected_text_colour =
Styles::default_palette().selected_text_style.fg.unwrap();
let original_selected_bg_colour = Styles::default_palette().selected_text_style.bg.unwrap();
assert_eq!(
colours.selected_text_style,
Style::default()
.fg(original_selected_text_colour)
.bg(original_selected_bg_colour),
);
colours.selected_text_style = colours
.selected_text_style
.fg(str_to_colour("magenta").unwrap())
.bg(str_to_colour("red").unwrap());
assert_eq!(
colours.selected_text_style,
Style::default().fg(Color::Magenta).bg(Color::Red),
);
}
#[test]
fn built_in_colour_schemes_work() {
Styles::from_theme("default").unwrap();
Styles::from_theme("default-light").unwrap();
Styles::from_theme("gruvbox").unwrap();
Styles::from_theme("gruvbox-light").unwrap();
Styles::from_theme("nord").unwrap();
Styles::from_theme("nord-light").unwrap();
}
}