diff --git a/Cargo.lock b/Cargo.lock index 5caf273d..1119bb37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1844,9 +1844,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "5f8751d9c1b03c6500c387e96f81f815a4f8e72d142d2d4a9ffa6fedd51ddee7" dependencies = [ "indexmap 2.0.0", "serde", @@ -2328,9 +2328,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] diff --git a/src/config/theme/color.rs b/src/config/theme/color.rs index f2556b7b..c9d58c0f 100644 --- a/src/config/theme/color.rs +++ b/src/config/theme/color.rs @@ -6,7 +6,7 @@ use serde::Deserialize; #[derive(Deserialize)] #[serde(try_from = "String")] -pub struct Color(style::Color); +pub struct Color(pub(super) style::Color); impl Default for Color { fn default() -> Self { Self(style::Color::Reset) } @@ -33,10 +33,15 @@ impl Deref for Color { fn deref(&self) -> &Self::Target { &self.0 } } -#[derive(Deserialize)] -pub struct ColorDual { - #[serde(default)] - pub fg: Color, - #[serde(default)] - pub bg: Color, +impl Color { + pub fn fg(&self) -> style::Style { style::Style::new().fg(self.0) } + + pub fn bg(&self) -> style::Style { style::Style::new().bg(self.0) } +} + +#[derive(Deserialize)] +pub struct ColorGroup { + pub normal: Color, + pub select: Color, + pub unselect: Color, } diff --git a/src/config/theme/mod.rs b/src/config/theme/mod.rs index 9989b048..ed9aa3df 100644 --- a/src/config/theme/mod.rs +++ b/src/config/theme/mod.rs @@ -1,9 +1,11 @@ mod color; mod filetype; mod icon; +mod style; mod theme; pub use color::*; pub use filetype::*; pub use icon::*; +pub use style::*; pub use theme::*; diff --git a/src/config/theme/style.rs b/src/config/theme/style.rs new file mode 100644 index 00000000..865b7de3 --- /dev/null +++ b/src/config/theme/style.rs @@ -0,0 +1,40 @@ +use ratatui::style::{self, Modifier}; +use serde::Deserialize; + +use super::Color; + +#[derive(Deserialize)] +pub struct Style { + pub fg: Option, + pub bg: Option, + pub bold: Option, + pub underline: Option, +} + +impl Style { + pub fn get(&self) -> style::Style { + let mut style = style::Style::new(); + + if let Some(fg) = &self.fg { + style = style.fg(fg.0); + } + if let Some(bg) = &self.bg { + style = style.bg(bg.0); + } + if let Some(bold) = self.bold { + if bold { + style = style.add_modifier(Modifier::BOLD); + } else { + style = style.remove_modifier(Modifier::BOLD); + } + } + if let Some(underline) = self.underline { + if underline { + style = style.add_modifier(Modifier::UNDERLINED); + } else { + style = style.remove_modifier(Modifier::UNDERLINED); + } + } + style + } +} diff --git a/src/config/theme/theme.rs b/src/config/theme/theme.rs index 0523a4e8..1b256f10 100644 --- a/src/config/theme/theme.rs +++ b/src/config/theme/theme.rs @@ -3,27 +3,36 @@ use std::{fs, path::PathBuf}; use serde::Deserialize; use xdg::BaseDirectories; -use super::{ColorDual, Filetype, Icon}; +use super::{ColorGroup, Filetype, Icon, Style}; use crate::misc::absolute_path; #[derive(Deserialize)] -pub struct Mode { - pub normal: ColorDual, - pub select: ColorDual, - pub unselect: ColorDual, +pub struct Tab { + pub active: Style, + pub inactive: Style, } #[derive(Deserialize)] -pub struct Tab { - pub active: ColorDual, - pub inactive: ColorDual, +pub struct Status { + pub primary: ColorGroup, + pub secondary: ColorGroup, + pub emphasis: ColorGroup, + pub body: ColorGroup, + pub info: ColorGroup, + pub success: ColorGroup, + pub warning: ColorGroup, + pub danger: ColorGroup, } #[derive(Deserialize)] pub struct Selection { - pub normal: ColorDual, - pub hovered: ColorDual, - pub selected: ColorDual, + pub hovered: Style, +} + +#[derive(Deserialize)] +pub struct Marker { + pub selecting: Style, + pub selected: Style, } #[derive(Deserialize)] @@ -33,9 +42,10 @@ pub struct Syntect { #[derive(Deserialize)] pub struct Theme { - pub mode: Mode, pub tab: Tab, + pub status: Status, pub selection: Selection, + pub marker: Marker, #[serde(deserialize_with = "Filetype::deserialize")] pub filetypes: Vec, #[serde(deserialize_with = "Icon::deserialize")] diff --git a/src/core/manager/mode.rs b/src/core/manager/mode.rs index 63382102..c66a75ed 100644 --- a/src/core/manager/mode.rs +++ b/src/core/manager/mode.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use crate::config::{theme, THEME}; +use crate::config::theme::{self, ColorGroup}; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum Mode { @@ -12,11 +12,11 @@ pub enum Mode { impl Mode { #[inline] - pub fn color(&self) -> &theme::ColorDual { + pub fn color<'a>(&self, group: &'a ColorGroup) -> &'a theme::Color { match *self { - Mode::Normal => &THEME.mode.normal, - Mode::Select(_) => &THEME.mode.select, - Mode::Unselect(_) => &THEME.mode.unselect, + Mode::Normal => &group.normal, + Mode::Select(_) => &group.select, + Mode::Unselect(_) => &group.unselect, } } diff --git a/src/ui/manager/folder.rs b/src/ui/manager/folder.rs index 609d142a..2b1dc0b8 100644 --- a/src/ui/manager/folder.rs +++ b/src/ui/manager/folder.rs @@ -10,7 +10,7 @@ pub struct Folder<'a> { impl<'a> Folder<'a> { pub fn new(folder: &'a core::manager::Folder) -> Self { - Self { folder, is_preview: false, is_selection: false } + Self { folder, is_preview: false, is_selection: true } } #[inline] @@ -28,11 +28,12 @@ impl<'a> Folder<'a> { impl<'a> Widget for Folder<'a> { fn render(self, area: Rect, buf: &mut Buffer) { - let items = self - .folder - .paginate() + let page = self.folder.paginate(); + + let items = page .iter() - .map(|(k, v)| { + .enumerate() + .map(|(i, (k, v))| { let icon = THEME .icons .iter() @@ -40,34 +41,32 @@ impl<'a> Widget for Folder<'a> { .map(|x| x.display.as_ref()) .unwrap_or(""); - let name = readable_path(k, &self.folder.cwd); - let item = ListItem::new(if v.is_selected { - format!("> {} {}", icon, name) - } else { - format!("{} {}", icon, name) - }); - let hovered = matches!(self.folder.hovered, Some(ref h) if h.path == *k); + if v.is_selected { + buf.set_style( + Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 }, + Style::default().fg(Color::Red).bg(Color::Red), + ); + } + let name = if self.is_selection && v.is_selected { + format!(" {} {}", icon, readable_path(k, &self.folder.cwd)) + } else { + format!(" {} {}", icon, readable_path(k, &self.folder.cwd)) + }; + + let hovered = matches!(self.folder.hovered, Some(ref h) if h.path == *k); let mut style = Style::default(); - if self.is_selection { - if hovered { - style = style.fg(Color::Black).bg(Color::Red); - } else if v.is_selected { - style = style.fg(Color::Red); - } - } else if self.is_preview { + if self.is_preview { if hovered { style = style.add_modifier(Modifier::UNDERLINED) } } else { if hovered { style = style.fg(Color::Black).bg(Color::Yellow); - } else if v.is_selected { - style = style.fg(Color::Red); } } - item.style(style) + ListItem::new(name).style(style) }) .collect::>(); diff --git a/src/ui/manager/layout.rs b/src/ui/manager/layout.rs index 55f65f05..c1e86c75 100644 --- a/src/ui/manager/layout.rs +++ b/src/ui/manager/layout.rs @@ -1,4 +1,4 @@ -use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Widget}}; +use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}}; use super::{Folder, Preview}; use crate::{core::manager::{Mode, ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_RATIO}, ui::Ctx}; @@ -28,7 +28,7 @@ impl<'a> Widget for Layout<'a> { .split(area); // Parent - let block = Block::default().borders(Borders::RIGHT); + let block = Block::default().borders(Borders::RIGHT).padding(Padding::new(1, 0, 0, 0)); if let Some(ref parent) = manager.parent() { Folder::new(parent).render(block.inner(chunks[0]), buf); } @@ -40,7 +40,7 @@ impl<'a> Widget for Layout<'a> { .render(chunks[1], buf); // Preview - let block = Block::default().borders(Borders::LEFT); + let block = Block::default().borders(Borders::LEFT).padding(Padding::new(0, 1, 0, 0)); Preview::new(self.cx).render(block.inner(chunks[2]), buf); block.render(chunks[2], buf); } diff --git a/src/ui/status/layout.rs b/src/ui/status/layout.rs index ee15d201..4212aa44 100644 --- a/src/ui/status/layout.rs +++ b/src/ui/status/layout.rs @@ -1,7 +1,7 @@ -use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, style::{Color, Modifier, Style}, widgets::{Paragraph, Widget}}; +use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, style::Modifier, widgets::{Paragraph, Widget}}; use super::Progress; -use crate::ui::Ctx; +use crate::{config::THEME, ui::Ctx}; pub struct Layout<'a> { cx: &'a Ctx, @@ -29,19 +29,19 @@ impl<'a> Widget for Layout<'a> { ) .split(area); - Paragraph::new("").style(Style::default().fg(*mode.color().bg)).render(chunks[0], buf); + let primary = mode.color(&THEME.status.primary); + let secondary = mode.color(&THEME.status.secondary); + let body = mode.color(&THEME.status.body); + + Paragraph::new("").style(primary.fg()).render(chunks[0], buf); Paragraph::new(format!(" {} ", mode)) - .style( - Style::default().fg(*mode.color().fg).bg(*mode.color().bg).add_modifier(Modifier::BOLD), - ) + .style(primary.bg().fg(**secondary).add_modifier(Modifier::BOLD)) .render(chunks[1], buf); - Paragraph::new(" master ") - .style(Style::default().fg(*mode.color().bg).bg(Color::Rgb(72, 77, 102))) - .render(chunks[2], buf); + Paragraph::new(" master ").style(body.bg().fg(**primary)).render(chunks[2], buf); - Paragraph::new("").style(Style::default().fg(Color::Rgb(72, 77, 102))).render(chunks[3], buf); + Paragraph::new("").style(body.fg()).render(chunks[3], buf); Progress::new(self.cx).render(chunks[4], buf); }