diff --git a/src/main.rs b/src/main.rs index 7502b61b..6a6f5713 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use std::{ time::Duration, }; +use crate::theme::Theme; use clap::Parser; use crossterm::{ cursor::RestorePosition, @@ -25,24 +26,21 @@ use ratatui::{ }; use state::AppState; use tempdir::TempDir; -use theme::THEMES; -/// This is a binary :), Chris, change this to update the documentation on -h +// Linux utility toolbox #[derive(Debug, Parser)] struct Args { - /// Enable compatibility mode (disable icons and RGB colors) - #[arg(short, long, default_value_t = false)] - compat: bool, + #[arg(short, long, value_enum)] + #[arg(default_value_t = Theme::Default)] + #[arg(help = "Set the theme to use in the application")] + theme: Theme, } fn main() -> std::io::Result<()> { let args = Args::parse(); - let theme = if args.compat { - THEMES[0].clone() - } else { - THEMES[1].clone() - }; + let theme = args.theme; + let commands_dir = include_dir!("src/commands"); let temp_dir: TempDir = TempDir::new("linutil_scripts").unwrap(); commands_dir diff --git a/src/state.rs b/src/state.rs index 62620929..0ce78ffb 100644 --- a/src/state.rs +++ b/src/state.rs @@ -69,7 +69,7 @@ impl AppState { pub fn draw(&mut self, frame: &mut Frame) { let longest_tab_display_len = TABS .iter() - .map(|tab| tab.name.len() + self.theme.tab_icon.len()) + .map(|tab| tab.name.len() + self.theme.tab_icon().len()) .max() .unwrap_or(0); @@ -88,15 +88,15 @@ impl AppState { let tabs = TABS.iter().map(|tab| tab.name).collect::>(); let tab_hl_style = if let Focus::TabList = self.focus { - Style::default().reversed().fg(self.theme.tab_color) + Style::default().reversed().fg(self.theme.tab_color()) } else { - Style::new().fg(self.theme.tab_color) + Style::new().fg(self.theme.tab_color()) }; let list = List::new(tabs) .block(Block::default().borders(Borders::ALL)) .highlight_style(tab_hl_style) - .highlight_symbol(self.theme.tab_icon); + .highlight_symbol(self.theme.tab_icon()); frame.render_stateful_widget(list, left_chunks[1], &mut self.current_tab); let chunks = Layout::default() @@ -122,7 +122,7 @@ impl AppState { let mut items: Vec = Vec::new(); if !self.at_root() { items.push( - Line::from(format!("{} ..", self.theme.dir_icon)).style(self.theme.dir_color), + Line::from(format!("{} ..", self.theme.dir_icon())).style(self.theme.dir_color()), ); } @@ -131,11 +131,11 @@ impl AppState { node, has_children, .. }| { if *has_children { - Line::from(format!("{} {}", self.theme.dir_icon, node.name)) - .style(self.theme.dir_color) + Line::from(format!("{} {}", self.theme.dir_icon(), node.name)) + .style(self.theme.dir_color()) } else { - Line::from(format!("{} {}", self.theme.cmd_icon, node.name)) - .style(self.theme.cmd_color) + Line::from(format!("{} {}", self.theme.cmd_icon(), node.name)) + .style(self.theme.cmd_color()) } }, )); diff --git a/src/theme.rs b/src/theme.rs index 0ba0c77d..b0ab75fb 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,42 +1,102 @@ +use clap::ValueEnum; use ratatui::style::Color; -#[derive(Clone)] -pub struct Theme { - pub dir_color: Color, - pub cmd_color: Color, - pub tab_color: Color, - pub dir_icon: &'static str, - pub cmd_icon: &'static str, - pub tab_icon: &'static str, - pub success_color: Color, - pub fail_color: Color, - pub focused_color: Color, - pub unfocused_color: Color, +// Add the Theme name here for a new theme +// This is more secure than the previous list +// We cannot index out of bounds, and we are giving +// names to our various themes, making it very clear +// This will make it easy to add new themes +#[derive(Clone, Debug, PartialEq, Default, ValueEnum, Copy)] +pub enum Theme { + #[default] + Default, + Compatible, } -pub const THEMES: [Theme; 2] = [ - Theme { - dir_color: Color::Blue, - cmd_color: Color::LightGreen, - tab_color: Color::Yellow, - dir_icon: "[DIR]", - cmd_icon: "[CMD]", - tab_icon: ">> ", - success_color: Color::Green, - fail_color: Color::Red, - focused_color: Color::LightBlue, - unfocused_color: Color::Gray, - }, - Theme { - dir_color: Color::Blue, - cmd_color: Color::Rgb(204, 224, 208), - tab_color: Color::Rgb(255, 255, 85), - dir_icon: "  ", - cmd_icon: "  ", - tab_icon: " ", - fail_color: Color::Rgb(199, 55, 44), - success_color: Color::Rgb(5, 255, 55), - focused_color: Color::LightBlue, - unfocused_color: Color::Gray, - }, -]; +impl Theme { + pub fn dir_color(&self) -> Color { + match self { + Theme::Default => Color::Blue, + Theme::Compatible => Color::Blue, + } + } + + pub fn cmd_color(&self) -> Color { + match self { + Theme::Default => Color::Rgb(204, 224, 208), + Theme::Compatible => Color::LightGreen, + } + } + + pub fn tab_color(&self) -> Color{ + match self { + Theme::Default => Color::Rgb(255, 255, 85), + Theme::Compatible => Color::Yellow, + } + } + + pub fn dir_icon(&self) -> &'static str { + match self { + Theme::Default => "  ", + Theme::Compatible => "[DIR]", + } + } + + pub fn cmd_icon(&self) -> &'static str { + match self { + Theme::Default => "  ", + Theme::Compatible => "[CMD]", + } + } + + pub fn tab_icon(&self) -> &'static str { + match self { + Theme::Default => " ", + Theme::Compatible => ">> ", + } + } + + pub fn success_color(&self) -> Color { + match self { + Theme::Default => Color::Rgb(199, 55, 44), + Theme::Compatible => Color::Green, + } + } + + pub fn fail_color(&self) -> Color { + match self { + Theme::Default => Color::Rgb(5, 255, 55), + Theme::Compatible => Color::Red, + } + } + + pub fn focused_color(&self) -> Color { + match self { + Theme::Default => Color::LightBlue, + Theme::Compatible => Color::LightBlue, + } + } + + pub fn unfocused_color(&self) -> Color { + match self { + Theme::Default => Color::Gray, + Theme::Compatible => Color::Gray, + } + } +} + +impl Theme { + #[allow(unused)] + pub fn next(self) -> Self { + let position = self as usize; + let types = Theme::value_variants(); + types[(position + 1) % types.len()].into() + } + + #[allow(unused)] + pub fn prev(self) -> Self { + let position = self as usize; + let types = Theme::value_variants(); + types[(position + types.len() - 1) % types.len()].into() + } +}