use crate::config::file::ConfigThemeColors;
use anyhow::anyhow;
use serde::Deserialize;
use std::collections::HashMap;
use strum::{EnumString, VariantNames};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct TuiTheme {
pub bg: TuiColor,
pub border: TuiColor,
pub text: TuiColor,
pub tab_text: TuiColor,
pub hops_table_header_bg: TuiColor,
pub hops_table_header_text: TuiColor,
pub hops_table_row_active_text: TuiColor,
pub hops_table_row_inactive_text: TuiColor,
pub hops_chart_selected: TuiColor,
pub hops_chart_unselected: TuiColor,
pub hops_chart_axis: TuiColor,
pub frequency_chart_bar: TuiColor,
pub frequency_chart_text: TuiColor,
pub flows_chart_bar_selected: TuiColor,
pub flows_chart_bar_unselected: TuiColor,
pub flows_chart_text_current: TuiColor,
pub flows_chart_text_non_current: TuiColor,
pub samples_chart: TuiColor,
pub samples_chart_lost: TuiColor,
pub help_dialog_bg: TuiColor,
pub help_dialog_text: TuiColor,
pub settings_dialog_bg: TuiColor,
pub settings_tab_text: TuiColor,
pub settings_table_header_text: TuiColor,
pub settings_table_header_bg: TuiColor,
pub settings_table_row_text: TuiColor,
pub map_world: TuiColor,
pub map_radius: TuiColor,
pub map_selected: TuiColor,
pub map_info_panel_border: TuiColor,
pub map_info_panel_bg: TuiColor,
pub map_info_panel_text: TuiColor,
pub info_bar_bg: TuiColor,
pub info_bar_text: TuiColor,
}
impl Default for TuiTheme {
fn default() -> Self {
Self {
bg: TuiColor::Black,
border: TuiColor::Gray,
text: TuiColor::Gray,
tab_text: TuiColor::Green,
hops_table_header_bg: TuiColor::White,
hops_table_header_text: TuiColor::Black,
hops_table_row_active_text: TuiColor::Gray,
hops_table_row_inactive_text: TuiColor::DarkGray,
hops_chart_selected: TuiColor::Green,
hops_chart_unselected: TuiColor::Gray,
hops_chart_axis: TuiColor::DarkGray,
frequency_chart_bar: TuiColor::Green,
frequency_chart_text: TuiColor::Gray,
flows_chart_bar_selected: TuiColor::Green,
flows_chart_bar_unselected: TuiColor::DarkGray,
flows_chart_text_current: TuiColor::LightGreen,
flows_chart_text_non_current: TuiColor::White,
samples_chart: TuiColor::Yellow,
samples_chart_lost: TuiColor::Red,
help_dialog_bg: TuiColor::Blue,
help_dialog_text: TuiColor::Gray,
settings_dialog_bg: TuiColor::Blue,
settings_tab_text: TuiColor::Green,
settings_table_header_text: TuiColor::Black,
settings_table_header_bg: TuiColor::White,
settings_table_row_text: TuiColor::Gray,
map_world: TuiColor::White,
map_radius: TuiColor::Yellow,
map_selected: TuiColor::Green,
map_info_panel_border: TuiColor::Gray,
map_info_panel_bg: TuiColor::Black,
map_info_panel_text: TuiColor::Gray,
info_bar_bg: TuiColor::White,
info_bar_text: TuiColor::Black,
}
}
}
impl From<(HashMap<TuiThemeItem, TuiColor>, ConfigThemeColors)> for TuiTheme {
#[allow(clippy::too_many_lines, clippy::or_fun_call)]
fn from(value: (HashMap<TuiThemeItem, TuiColor>, ConfigThemeColors)) -> Self {
let (color_map, cfg) = value;
Self {
bg: *color_map
.get(&TuiThemeItem::BgColor)
.or(cfg.bg_color.as_ref())
.unwrap_or(&Self::default().bg),
border: *color_map
.get(&TuiThemeItem::BorderColor)
.or(cfg.border_color.as_ref())
.unwrap_or(&Self::default().border),
text: *color_map
.get(&TuiThemeItem::TextColor)
.or(cfg.text_color.as_ref())
.unwrap_or(&Self::default().text),
tab_text: *color_map
.get(&TuiThemeItem::TabTextColor)
.or(cfg.tab_text_color.as_ref())
.unwrap_or(&Self::default().tab_text),
hops_table_header_bg: *color_map
.get(&TuiThemeItem::HopsTableHeaderBgColor)
.or(cfg.hops_table_header_bg_color.as_ref())
.unwrap_or(&Self::default().hops_table_header_bg),
hops_table_header_text: *color_map
.get(&TuiThemeItem::HopsTableHeaderTextColor)
.or(cfg.hops_table_header_text_color.as_ref())
.unwrap_or(&Self::default().hops_table_header_text),
hops_table_row_active_text: *color_map
.get(&TuiThemeItem::HopsTableRowActiveTextColor)
.or(cfg.hops_table_row_active_text_color.as_ref())
.unwrap_or(&Self::default().hops_table_row_active_text),
hops_table_row_inactive_text: *color_map
.get(&TuiThemeItem::HopsTableRowInactiveTextColor)
.or(cfg.hops_table_row_inactive_text_color.as_ref())
.unwrap_or(&Self::default().hops_table_row_inactive_text),
hops_chart_selected: *color_map
.get(&TuiThemeItem::HopsChartSelectedColor)
.or(cfg.hops_chart_selected_color.as_ref())
.unwrap_or(&Self::default().hops_chart_selected),
hops_chart_unselected: *color_map
.get(&TuiThemeItem::HopsChartUnselectedColor)
.or(cfg.hops_chart_unselected_color.as_ref())
.unwrap_or(&Self::default().hops_chart_unselected),
hops_chart_axis: *color_map
.get(&TuiThemeItem::HopsChartAxisColor)
.or(cfg.hops_chart_axis_color.as_ref())
.unwrap_or(&Self::default().hops_chart_axis),
frequency_chart_bar: *color_map
.get(&TuiThemeItem::FrequencyChartBarColor)
.or(cfg.frequency_chart_bar_color.as_ref())
.unwrap_or(&Self::default().frequency_chart_bar),
frequency_chart_text: *color_map
.get(&TuiThemeItem::FrequencyChartTextColor)
.or(cfg.frequency_chart_text_color.as_ref())
.unwrap_or(&Self::default().frequency_chart_text),
flows_chart_bar_selected: *color_map
.get(&TuiThemeItem::FlowsChartBarSelectedColor)
.or(cfg.flows_chart_bar_selected_color.as_ref())
.unwrap_or(&Self::default().flows_chart_bar_selected),
flows_chart_bar_unselected: *color_map
.get(&TuiThemeItem::FlowsChartBarUnselectedColor)
.or(cfg.flows_chart_bar_unselected_color.as_ref())
.unwrap_or(&Self::default().flows_chart_bar_unselected),
flows_chart_text_current: *color_map
.get(&TuiThemeItem::FlowsChartTextCurrentColor)
.or(cfg.flows_chart_text_current_color.as_ref())
.unwrap_or(&Self::default().flows_chart_text_current),
flows_chart_text_non_current: *color_map
.get(&TuiThemeItem::FlowsChartTextNonCurrentColor)
.or(cfg.flows_chart_text_non_current_color.as_ref())
.unwrap_or(&Self::default().flows_chart_text_non_current),
samples_chart: *color_map
.get(&TuiThemeItem::SamplesChartColor)
.or(cfg.samples_chart_color.as_ref())
.unwrap_or(&Self::default().samples_chart),
samples_chart_lost: *color_map
.get(&TuiThemeItem::SamplesChartLostColor)
.or(cfg.samples_chart_lost_color.as_ref())
.unwrap_or(&Self::default().samples_chart_lost),
help_dialog_bg: *color_map
.get(&TuiThemeItem::HelpDialogBgColor)
.or(cfg.help_dialog_bg_color.as_ref())
.unwrap_or(&Self::default().help_dialog_bg),
help_dialog_text: *color_map
.get(&TuiThemeItem::HelpDialogTextColor)
.or(cfg.help_dialog_text_color.as_ref())
.unwrap_or(&Self::default().help_dialog_text),
settings_dialog_bg: *color_map
.get(&TuiThemeItem::SettingsDialogBgColor)
.or(cfg.settings_dialog_bg_color.as_ref())
.unwrap_or(&Self::default().settings_dialog_bg),
settings_tab_text: *color_map
.get(&TuiThemeItem::SettingsTabTextColor)
.or(cfg.settings_tab_text_color.as_ref())
.unwrap_or(&Self::default().settings_tab_text),
settings_table_header_text: *color_map
.get(&TuiThemeItem::SettingsTableHeaderTextColor)
.or(cfg.settings_table_header_text_color.as_ref())
.unwrap_or(&Self::default().settings_table_header_text),
settings_table_header_bg: *color_map
.get(&TuiThemeItem::SettingsTableHeaderBgColor)
.or(cfg.settings_table_header_bg_color.as_ref())
.unwrap_or(&Self::default().settings_table_header_bg),
settings_table_row_text: *color_map
.get(&TuiThemeItem::SettingsTableRowTextColor)
.or(cfg.settings_table_row_text_color.as_ref())
.unwrap_or(&Self::default().settings_table_row_text),
map_world: *color_map
.get(&TuiThemeItem::MapWorldColor)
.or(cfg.map_world_color.as_ref())
.unwrap_or(&Self::default().map_world),
map_radius: *color_map
.get(&TuiThemeItem::MapRadiusColor)
.or(cfg.map_radius_color.as_ref())
.unwrap_or(&Self::default().map_radius),
map_selected: *color_map
.get(&TuiThemeItem::MapSelectedColor)
.or(cfg.map_selected_color.as_ref())
.unwrap_or(&Self::default().map_selected),
map_info_panel_border: *color_map
.get(&TuiThemeItem::MapInfoPanelBorderColor)
.or(cfg.map_info_panel_border_color.as_ref())
.unwrap_or(&Self::default().map_info_panel_border),
map_info_panel_bg: *color_map
.get(&TuiThemeItem::MapInfoPanelBgColor)
.or(cfg.map_info_panel_bg_color.as_ref())
.unwrap_or(&Self::default().map_info_panel_bg),
map_info_panel_text: *color_map
.get(&TuiThemeItem::MapInfoPanelTextColor)
.or(cfg.map_info_panel_text_color.as_ref())
.unwrap_or(&Self::default().map_info_panel_text),
info_bar_bg: *color_map
.get(&TuiThemeItem::InfoBarBgColor)
.or(cfg.info_bar_bg_color.as_ref())
.unwrap_or(&Self::default().info_bar_bg),
info_bar_text: *color_map
.get(&TuiThemeItem::InfoBarTextColor)
.or(cfg.info_bar_text_color.as_ref())
.unwrap_or(&Self::default().info_bar_text),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, EnumString, VariantNames)]
#[strum(serialize_all = "kebab-case")]
#[allow(clippy::enum_variant_names)]
pub enum TuiThemeItem {
BgColor,
BorderColor,
TextColor,
TabTextColor,
HopsTableHeaderBgColor,
HopsTableHeaderTextColor,
HopsTableRowActiveTextColor,
HopsTableRowInactiveTextColor,
HopsChartSelectedColor,
HopsChartUnselectedColor,
HopsChartAxisColor,
FrequencyChartBarColor,
FrequencyChartTextColor,
FlowsChartBarSelectedColor,
FlowsChartBarUnselectedColor,
FlowsChartTextCurrentColor,
FlowsChartTextNonCurrentColor,
SamplesChartColor,
SamplesChartLostColor,
HelpDialogBgColor,
HelpDialogTextColor,
SettingsTabTextColor,
SettingsDialogBgColor,
SettingsTableHeaderTextColor,
SettingsTableHeaderBgColor,
SettingsTableRowTextColor,
MapWorldColor,
MapRadiusColor,
MapSelectedColor,
MapInfoPanelBorderColor,
MapInfoPanelBgColor,
MapInfoPanelTextColor,
InfoBarBgColor,
InfoBarTextColor,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize)]
#[serde(try_from = "String")]
pub enum TuiColor {
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
Gray,
DarkGray,
LightRed,
LightGreen,
LightYellow,
LightBlue,
LightMagenta,
LightCyan,
White,
AliceBlue,
AntiqueWhite,
Aqua,
Aquamarine,
Azure,
Beige,
Bisque,
BlanchedAlmond,
BlueViolet,
Brown,
BurlyWood,
CadetBlue,
Chartreuse,
Chocolate,
Coral,
CornflowerBlue,
CornSilk,
Crimson,
DarkBlue,
DarkCyan,
DarkGoldenrod,
DarkGreen,
DarkKhaki,
DarkMagenta,
DarkOliveGreen,
DarkOrange,
DarkOrchid,
DarkRed,
DarkSalmon,
DarkSeaGreen,
DarkSlateBlue,
DarkSlateGray,
DarkTurquoise,
DarkViolet,
DeepPink,
DeepSkyBlue,
DimGray,
DodgerBlue,
Firebrick,
FloralWhite,
ForestGreen,
Fuchsia,
Gainsboro,
GhostWhite,
Gold,
Goldenrod,
GreenYellow,
Honeydew,
HotPink,
IndianRed,
Indigo,
Ivory,
Khaki,
Lavender,
LavenderBlush,
LawnGreen,
LemonChiffon,
LightCoral,
LightGoldenrodYellow,
LightGray,
LightPink,
LightSalmon,
LightSeaGreen,
LightSkyBlue,
LightSlateGray,
LightSteelBlue,
Lime,
LimeGreen,
Linen,
Maroon,
MediumAquamarine,
MediumBlue,
MediumOrchid,
MediumPurple,
MediumSeaGreen,
MediumSlateBlue,
MediumSpringGreen,
MediumTurquoise,
MediumVioletRed,
MidnightBlue,
MintCream,
MistyRose,
Moccasin,
NavajoWhite,
Navy,
OldLace,
Olive,
OliveDrab,
Orange,
OrangeRed,
Orchid,
PaleGoldenrod,
PaleGreen,
PaleTurquoise,
PaleVioletRed,
PapayaWhip,
PeachPuff,
Peru,
Pink,
Plum,
PowderBlue,
Purple,
RebeccaPurple,
RosyBrown,
RoyalBlue,
SaddleBrown,
Salmon,
SandyBrown,
SeaGreen,
SeaShell,
Sienna,
Silver,
SkyBlue,
SlateBlue,
SlateGray,
Snow,
SpringGreen,
SteelBlue,
Tan,
Teal,
Thistle,
Tomato,
Turquoise,
Violet,
Wheat,
WhiteSmoke,
YellowGreen,
Rgb(u8, u8, u8),
}
impl TryFrom<String> for TuiColor {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_from(value.as_ref())
}
}
impl TryFrom<&str> for TuiColor {
type Error = anyhow::Error;
#[allow(clippy::too_many_lines)]
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.to_ascii_lowercase().replace('-', "").as_ref() {
"black" => Ok(Self::Black),
"red" => Ok(Self::Red),
"green" => Ok(Self::Green),
"yellow" => Ok(Self::Yellow),
"blue" => Ok(Self::Blue),
"magenta" => Ok(Self::Magenta),
"cyan" => Ok(Self::Cyan),
"gray" => Ok(Self::Gray),
"darkgray" => Ok(Self::DarkGray),
"lightred" => Ok(Self::LightRed),
"lightgreen" => Ok(Self::LightGreen),
"lightyellow" => Ok(Self::LightYellow),
"lightblue" => Ok(Self::LightBlue),
"lightmagenta" => Ok(Self::LightMagenta),
"lightcyan" => Ok(Self::LightCyan),
"white" => Ok(Self::White),
"aliceblue" => Ok(Self::AliceBlue),
"antiquewhite" => Ok(Self::AntiqueWhite),
"aqua" => Ok(Self::Aqua),
"aquamarine" => Ok(Self::Aquamarine),
"azure" => Ok(Self::Azure),
"beige" => Ok(Self::Beige),
"bisque" => Ok(Self::Bisque),
"blanchedalmond" => Ok(Self::BlanchedAlmond),
"blueviolet" => Ok(Self::BlueViolet),
"brown" => Ok(Self::Brown),
"burlywood" => Ok(Self::BurlyWood),
"cadetblue" => Ok(Self::CadetBlue),
"chartreuse" => Ok(Self::Chartreuse),
"chocolate" => Ok(Self::Chocolate),
"coral" => Ok(Self::Coral),
"cornflowerblue" => Ok(Self::CornflowerBlue),
"cornsilk" => Ok(Self::CornSilk),
"crimson" => Ok(Self::Crimson),
"darkblue" => Ok(Self::DarkBlue),
"darkcyan" => Ok(Self::DarkCyan),
"darkgoldenrod" => Ok(Self::DarkGoldenrod),
"darkgreen" => Ok(Self::DarkGreen),
"darkkhaki" => Ok(Self::DarkKhaki),
"darkmagenta" => Ok(Self::DarkMagenta),
"darkolivegreen" => Ok(Self::DarkOliveGreen),
"darkorange" => Ok(Self::DarkOrange),
"darkorchid" => Ok(Self::DarkOrchid),
"darkred" => Ok(Self::DarkRed),
"darksalmon" => Ok(Self::DarkSalmon),
"darkseagreen" => Ok(Self::DarkSeaGreen),
"darkslateblue" => Ok(Self::DarkSlateBlue),
"darkslategray" | "darkslategrey" => Ok(Self::DarkSlateGray),
"darkturquoise" => Ok(Self::DarkTurquoise),
"darkviolet" => Ok(Self::DarkViolet),
"deeppink" => Ok(Self::DeepPink),
"deepskyblue" => Ok(Self::DeepSkyBlue),
"dimgray" | "dimgrey" => Ok(Self::DimGray),
"dodgerblue" => Ok(Self::DodgerBlue),
"firebrick" => Ok(Self::Firebrick),
"floralwhite" => Ok(Self::FloralWhite),
"forestgreen" => Ok(Self::ForestGreen),
"fuchsia" => Ok(Self::Fuchsia),
"gainsboro" => Ok(Self::Gainsboro),
"ghostwhite" => Ok(Self::GhostWhite),
"gold" => Ok(Self::Gold),
"goldenrod" => Ok(Self::Goldenrod),
"greenyellow" => Ok(Self::GreenYellow),
"honeydew" => Ok(Self::Honeydew),
"hotpink" => Ok(Self::HotPink),
"indianred" => Ok(Self::IndianRed),
"indigo" => Ok(Self::Indigo),
"ivory" => Ok(Self::Ivory),
"khaki" => Ok(Self::Khaki),
"lavender" => Ok(Self::Lavender),
"lavenderblush" => Ok(Self::LavenderBlush),
"lawngreen" => Ok(Self::LawnGreen),
"lemonchiffon" => Ok(Self::LemonChiffon),
"lightcoral" => Ok(Self::LightCoral),
"lightgoldenrodyellow" => Ok(Self::LightGoldenrodYellow),
"lightgray" | "lightgrey" => Ok(Self::LightGray),
"lightpink" => Ok(Self::LightPink),
"lightsalmon" => Ok(Self::LightSalmon),
"lightseagreen" => Ok(Self::LightSeaGreen),
"lightskyblue" => Ok(Self::LightSkyBlue),
"lightslategray" | "lightslategrey" => Ok(Self::LightSlateGray),
"lightsteelblue" => Ok(Self::LightSteelBlue),
"lime" => Ok(Self::Lime),
"limegreen" => Ok(Self::LimeGreen),
"linen" => Ok(Self::Linen),
"maroon" => Ok(Self::Maroon),
"mediumaquamarine" => Ok(Self::MediumAquamarine),
"mediumblue" => Ok(Self::MediumBlue),
"mediumorchid" => Ok(Self::MediumOrchid),
"mediumpurple" => Ok(Self::MediumPurple),
"mediumseagreen" => Ok(Self::MediumSeaGreen),
"mediumslateblue" => Ok(Self::MediumSlateBlue),
"mediumspringgreen" => Ok(Self::MediumSpringGreen),
"mediumturquoise" => Ok(Self::MediumTurquoise),
"mediumvioletred" => Ok(Self::MediumVioletRed),
"midnightblue" => Ok(Self::MidnightBlue),
"mintcream" => Ok(Self::MintCream),
"mistyrose" => Ok(Self::MistyRose),
"moccasin" => Ok(Self::Moccasin),
"navajowhite" => Ok(Self::NavajoWhite),
"navy" => Ok(Self::Navy),
"oldlace" => Ok(Self::OldLace),
"olive" => Ok(Self::Olive),
"olivedrab" => Ok(Self::OliveDrab),
"orange" => Ok(Self::Orange),
"orangered" => Ok(Self::OrangeRed),
"orchid" => Ok(Self::Orchid),
"palegoldenrod" => Ok(Self::PaleGoldenrod),
"palegreen" => Ok(Self::PaleGreen),
"paleturquoise" => Ok(Self::PaleTurquoise),
"palevioletred" => Ok(Self::PaleVioletRed),
"papayawhip" => Ok(Self::PapayaWhip),
"peachpuff" => Ok(Self::PeachPuff),
"peru" => Ok(Self::Peru),
"pink" => Ok(Self::Pink),
"plum" => Ok(Self::Plum),
"powderblue" => Ok(Self::PowderBlue),
"purple" => Ok(Self::Purple),
"rebeccapurple" => Ok(Self::RebeccaPurple),
"rosybrown" => Ok(Self::RosyBrown),
"royalblue" => Ok(Self::RoyalBlue),
"saddlebrown" => Ok(Self::SaddleBrown),
"salmon" => Ok(Self::Salmon),
"sandybrown" => Ok(Self::SandyBrown),
"seagreen" => Ok(Self::SeaGreen),
"seashell" => Ok(Self::SeaShell),
"sienna" => Ok(Self::Sienna),
"silver" => Ok(Self::Silver),
"skyblue" => Ok(Self::SkyBlue),
"slateblue" => Ok(Self::SlateBlue),
"slategray" | "slategrey" => Ok(Self::SlateGray),
"snow" => Ok(Self::Snow),
"springgreen" => Ok(Self::SpringGreen),
"steelblue" => Ok(Self::SteelBlue),
"tan" => Ok(Self::Tan),
"teal" => Ok(Self::Teal),
"thistle" => Ok(Self::Thistle),
"tomato" => Ok(Self::Tomato),
"turquoise" => Ok(Self::Turquoise),
"violet" => Ok(Self::Violet),
"wheat" => Ok(Self::Wheat),
"whitesmoke" => Ok(Self::WhiteSmoke),
"yellowgreen" => Ok(Self::YellowGreen),
rgb_hex if value.len() == 6 && value.chars().all(|c| c.is_ascii_hexdigit()) => {
let red = u8::from_str_radix(&rgb_hex[0..2], 16)?;
let green = u8::from_str_radix(&rgb_hex[2..4], 16)?;
let blue = u8::from_str_radix(&rgb_hex[4..6], 16)?;
Ok(Self::Rgb(red, green, blue))
}
_ => Err(anyhow!("unknown color: {value}")),
}
}
}