diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/theme.rs | 69 | ||||
| -rw-r--r-- | src/ui.rs | 97 |
3 files changed, 135 insertions, 32 deletions
diff --git a/src/main.rs b/src/main.rs index 0835fb7..56016d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod app; mod event; mod gentoo; +mod theme; mod ui; use std::{ diff --git a/src/theme.rs b/src/theme.rs new file mode 100644 index 0000000..17db722 --- /dev/null +++ b/src/theme.rs @@ -0,0 +1,69 @@ +use ratatui::style::{Color, Modifier, Style}; + +pub struct Theme; + +impl Theme { + pub const BG: Color = Color::Rgb(30, 30, 46); + + pub const PRIMARY: Color = Color::Rgb(139, 92, 246); + pub const SECONDARY: Color = Color::Rgb(196, 181, 253); + + pub const SUCCESS: Color = Color::Rgb(34, 197, 94); + + pub const WARNING: Color = Color::Rgb(245, 158, 11); + pub const ERROR: Color = Color::Rgb(239, 68, 68); + + pub const TEXT: Color = Color::Rgb(226, 232, 240); + pub const MUTED: Color = Color::Rgb(148, 163, 184); + + pub const INFO: Color = Color::Rgb(56, 189, 248); + + pub fn primary() -> Style { + Style::default().fg(Self::PRIMARY) + } + + pub fn secondary() -> Style { + Style::default().fg(Self::SECONDARY) + } + + pub fn block() -> Style { + Style::default().fg(Self::PRIMARY).bg(Self::BG) + } + + pub fn text() -> Style { + Style::default().fg(Self::TEXT) + } + + pub fn muted() -> Style { + Style::default().fg(Self::MUTED) + } + + pub fn title() -> Style { + Style::default() + .fg(Self::SECONDARY) + .add_modifier(Modifier::BOLD) + } + + pub fn selected() -> Style { + Style::default() + .bg(Self::PRIMARY) + .fg(Self::TEXT) + .add_modifier(Modifier::BOLD) + } + + pub fn success() -> Style { + Style::default().fg(Self::SUCCESS) + } + + pub fn warning() -> Style { + Style::default().fg(Self::WARNING) + } + + pub fn error() -> Style { + Style::default().fg(Self::ERROR) + } + + pub fn info() -> Style { + Style::default().fg(Self::INFO) + } +} @@ -5,13 +5,15 @@ use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Spacing}, prelude::Rect, style::{Color, Modifier, Style, Stylize}, - text::{Line, Span}, + text::{Line, Span, Text}, widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph, Wrap}, }; -use crate::{app::App, gentoo::Package}; +use crate::{app::App, gentoo::Package, theme::Theme}; pub fn ui(frame: &mut Frame, app: &mut App) { + frame.render_widget(Block::default().bg(Theme::BG), frame.area()); + match app.view { crate::app::ViewState::Dashboard => render_dashboard(frame, app), crate::app::ViewState::InstalledPackages => render_installed_packages(frame, app), @@ -20,14 +22,14 @@ pub fn ui(frame: &mut Frame, app: &mut App) { fn render_dashboard(frame: &mut Frame, app: &App) { let logo = " - ██████╗ ██╗ ██╗██╗███████╗██╗ ██╗ -██╔═══██╗██║ ██║██║██╔════╝██║ ██║ -██║ ██║██║ ██║██║█████╗ ██║ █╗ ██║ -██║▄▄ ██║╚██╗ ██╔╝██║██╔══╝ ██║███╗██║ -╚██████╔╝ ╚████╔╝ ██║███████╗╚███╔███╔╝ - ╚══▀▀═╝ ╚═══╝ ╚═╝╚══════╝ ╚══╝╚══╝ - gentoo portage dashboard - "; + ██████╗ ██╗ ██╗██╗███████╗██╗ ██╗ + ██╔═══██╗██║ ██║██║██╔════╝██║ ██║ + ██║ ██║██║ ██║██║█████╗ ██║ █╗ ██║ + ██║▄▄ ██║╚██╗ ██╔╝██║██╔══╝ ██║███╗██║ + ╚██████╔╝ ╚████╔╝ ██║███████╗╚███╔███╔╝ + ╚══▀▀═╝ ╚═══╝ ╚═╝╚══════╝ ╚══╝╚══╝ + gentoo portage dashboard + "; let constraints = [ Constraint::Length(9), @@ -56,11 +58,18 @@ fn render_dashboard(frame: &mut Frame, app: &App) { .spacing(Spacing::Overlap(1)) .split(stats_top); - let logo = Paragraph::new(logo); + let logo = Paragraph::new(Text::styled(logo, Theme::title())); frame.render_widget(logo, logo_top); - let keys = Paragraph::new("(q) to quit | (tab) to switch tabs".fg(Color::Yellow)) - .block(Block::default().borders(Borders::ALL)); + let keys = Paragraph::new(Span::styled( + "(q) to quit | (tab) to switch tabs", + Theme::muted(), + )) + .block( + Block::default() + .borders(Borders::ALL) + .border_style(Theme::block()), + ); frame.render_widget(keys, key_hints); let installed_packages_len = app.installed_packages().len().to_string(); @@ -127,15 +136,17 @@ fn human_size(bytes: usize) -> String { fn create_stats<'a>(title: &'a str, value: &'a str) -> Paragraph<'a> { Paragraph::new(vec![ - Line::from(Span::styled(title, Style::default().bold())), + Line::from(Span::styled(title, Theme::title())), Line::from(""), Line::from(value), ]) .block( Block::default() .borders(Borders::ALL) + .border_style(Theme::block()) .merge_borders(ratatui::symbols::merge::MergeStrategy::Exact), ) + .style(Theme::text()) .alignment(Alignment::Center) } @@ -171,32 +182,43 @@ fn render_installed_packages(frame: &mut Frame, app: &mut App) { main_key_hint }; - let key_hint = Span::styled(text, Style::default().fg(Color::Yellow)); + let key_hint = Span::styled(text, Theme::muted()); - let key_notes_footer = - Paragraph::new(Line::from(key_hint)).block(Block::default().borders(Borders::ALL)); + let key_notes_footer = Paragraph::new(Line::from(key_hint)).block( + Block::default() + .borders(Borders::ALL) + .border_style(Theme::block()), + ); frame.render_widget(key_notes_footer, bottom); if app.showing_search_window { let mut popup_block = Block::default() .title("Search") .borders(Borders::ALL) - .border_type(ratatui::widgets::BorderType::Rounded); + .border_type(ratatui::widgets::BorderType::Rounded) + .border_style(Theme::block()) + .style(Style::default().bg(Theme::BG)); if let Some(found) = app.search_found { if found { - popup_block = popup_block.border_style(Style::default().fg(Color::Green)); + popup_block = popup_block.border_style(Theme::success()); } else { - popup_block = popup_block.border_style(Style::default().fg(Color::Red)); + popup_block = popup_block.border_style(Theme::error()); } } app.textarea.set_block(popup_block); - app.textarea.set_style(Style::default().fg(Color::Yellow)); + app.textarea.set_style(Theme::muted()); app.textarea.set_cursor_line_style(Style::default()); let area = search_popup_rect(70, frame.area()); + + frame.render_widget( + Block::default().style(Style::default().bg(Theme::BG).add_modifier(Modifier::DIM)), + frame.area(), + ); frame.render_widget(Clear, area); + frame.render_widget(Block::default().style(Style::default().bg(Theme::BG)), area); frame.render_widget(&app.textarea, area); } } @@ -204,7 +226,7 @@ fn render_installed_packages(frame: &mut Frame, app: &mut App) { fn render_package_metadata(frame: &mut Frame, area: Rect, app: &App) { let pkg = app.current_package(); - let bold_style = Style::default().add_modifier(Modifier::BOLD); + let bold_style = Theme::title(); //Style::default().add_modifier(Modifier::BOLD); let maintainer = Line::from_iter([ Span::styled("Maintainer: ", bold_style), Span::raw(pkg.maintainer.as_deref().unwrap_or("Unknown")), @@ -241,7 +263,12 @@ fn render_package_metadata(frame: &mut Frame, area: Rect, app: &App) { lines.append(&mut homepage); let paragraph = Paragraph::new(lines) - .block(Block::default().title("Metadata").borders(Borders::ALL)) + .block( + Block::default() + .title("Metadata") + .borders(Borders::ALL) + .border_style(Theme::block()), + ) .wrap(Wrap { trim: false }); frame.render_widget(paragraph, area); @@ -303,11 +330,12 @@ fn render_packages(frame: &mut Frame, area: Rect, app: &mut App) { let list = List::new(items) .style(Color::White) - .highlight_style(Modifier::REVERSED) + .highlight_style(Theme::selected()) .highlight_symbol("> ") .block( - Block::new() + Block::default() .borders(Borders::ALL) + .border_style(Theme::block()) .title("Installed packages"), ); @@ -318,18 +346,18 @@ fn render_use_flags(frame: &mut Frame, area: Rect, app: &App) { let selected_package = app.current_package(); let items = selected_package.use_flags.iter().map(|x| { - let color = if x.enabled { - Color::Green + let text_style = if x.enabled { + Theme::success() } else if x.default { - Color::Blue + Theme::info() } else { - Color::Red + Theme::error() }; let text_style = if x.default { - Style::default().add_modifier(Modifier::ITALIC).fg(color) + text_style.add_modifier(Modifier::ITALIC) } else { - Style::default().fg(color) + text_style }; ListItem::new(x.name.clone()).style(text_style) @@ -338,7 +366,12 @@ fn render_use_flags(frame: &mut Frame, area: Rect, app: &App) { .style(Color::White) .scroll_padding(1) .direction(ratatui::widgets::ListDirection::TopToBottom) - .block(Block::new().borders(Borders::ALL).title("USE Flags")); + .block( + Block::default() + .borders(Borders::ALL) + .title("USE Flags") + .border_style(Theme::block()), + ); let mut list_state = ListState::default(); list_state.select_first(); |
