mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: status color customing
This commit is contained in:
parent
19fcc78a12
commit
ca08a8447a
9 changed files with 119 additions and 63 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -1844,9 +1844,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.19.12"
|
version = "0.19.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78"
|
checksum = "5f8751d9c1b03c6500c387e96f81f815a4f8e72d142d2d4a9ffa6fedd51ddee7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -2328,9 +2328,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.4.9"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529"
|
checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
pub struct Color(style::Color);
|
pub struct Color(pub(super) style::Color);
|
||||||
|
|
||||||
impl Default for Color {
|
impl Default for Color {
|
||||||
fn default() -> Self { Self(style::Color::Reset) }
|
fn default() -> Self { Self(style::Color::Reset) }
|
||||||
|
|
@ -33,10 +33,15 @@ impl Deref for Color {
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
impl Color {
|
||||||
pub struct ColorDual {
|
pub fn fg(&self) -> style::Style { style::Style::new().fg(self.0) }
|
||||||
#[serde(default)]
|
|
||||||
pub fg: Color,
|
pub fn bg(&self) -> style::Style { style::Style::new().bg(self.0) }
|
||||||
#[serde(default)]
|
}
|
||||||
pub bg: Color,
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ColorGroup {
|
||||||
|
pub normal: Color,
|
||||||
|
pub select: Color,
|
||||||
|
pub unselect: Color,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
mod color;
|
mod color;
|
||||||
mod filetype;
|
mod filetype;
|
||||||
mod icon;
|
mod icon;
|
||||||
|
mod style;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
pub use filetype::*;
|
pub use filetype::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
|
pub use style::*;
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
|
|
|
||||||
40
src/config/theme/style.rs
Normal file
40
src/config/theme/style.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
use ratatui::style::{self, Modifier};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::Color;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Style {
|
||||||
|
pub fg: Option<Color>,
|
||||||
|
pub bg: Option<Color>,
|
||||||
|
pub bold: Option<bool>,
|
||||||
|
pub underline: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,27 +3,36 @@ use std::{fs, path::PathBuf};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
use super::{ColorDual, Filetype, Icon};
|
use super::{ColorGroup, Filetype, Icon, Style};
|
||||||
use crate::misc::absolute_path;
|
use crate::misc::absolute_path;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Mode {
|
pub struct Tab {
|
||||||
pub normal: ColorDual,
|
pub active: Style,
|
||||||
pub select: ColorDual,
|
pub inactive: Style,
|
||||||
pub unselect: ColorDual,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Tab {
|
pub struct Status {
|
||||||
pub active: ColorDual,
|
pub primary: ColorGroup,
|
||||||
pub inactive: ColorDual,
|
pub secondary: ColorGroup,
|
||||||
|
pub emphasis: ColorGroup,
|
||||||
|
pub body: ColorGroup,
|
||||||
|
pub info: ColorGroup,
|
||||||
|
pub success: ColorGroup,
|
||||||
|
pub warning: ColorGroup,
|
||||||
|
pub danger: ColorGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Selection {
|
pub struct Selection {
|
||||||
pub normal: ColorDual,
|
pub hovered: Style,
|
||||||
pub hovered: ColorDual,
|
}
|
||||||
pub selected: ColorDual,
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Marker {
|
||||||
|
pub selecting: Style,
|
||||||
|
pub selected: Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
@ -33,9 +42,10 @@ pub struct Syntect {
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub mode: Mode,
|
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
|
pub status: Status,
|
||||||
pub selection: Selection,
|
pub selection: Selection,
|
||||||
|
pub marker: Marker,
|
||||||
#[serde(deserialize_with = "Filetype::deserialize")]
|
#[serde(deserialize_with = "Filetype::deserialize")]
|
||||||
pub filetypes: Vec<Filetype>,
|
pub filetypes: Vec<Filetype>,
|
||||||
#[serde(deserialize_with = "Icon::deserialize")]
|
#[serde(deserialize_with = "Icon::deserialize")]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::config::{theme, THEME};
|
use crate::config::theme::{self, ColorGroup};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
|
|
@ -12,11 +12,11 @@ pub enum Mode {
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn color(&self) -> &theme::ColorDual {
|
pub fn color<'a>(&self, group: &'a ColorGroup) -> &'a theme::Color {
|
||||||
match *self {
|
match *self {
|
||||||
Mode::Normal => &THEME.mode.normal,
|
Mode::Normal => &group.normal,
|
||||||
Mode::Select(_) => &THEME.mode.select,
|
Mode::Select(_) => &group.select,
|
||||||
Mode::Unselect(_) => &THEME.mode.unselect,
|
Mode::Unselect(_) => &group.unselect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ pub struct Folder<'a> {
|
||||||
|
|
||||||
impl<'a> Folder<'a> {
|
impl<'a> Folder<'a> {
|
||||||
pub fn new(folder: &'a core::manager::Folder) -> Self {
|
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]
|
#[inline]
|
||||||
|
|
@ -28,11 +28,12 @@ impl<'a> Folder<'a> {
|
||||||
|
|
||||||
impl<'a> Widget for Folder<'a> {
|
impl<'a> Widget for Folder<'a> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let items = self
|
let page = self.folder.paginate();
|
||||||
.folder
|
|
||||||
.paginate()
|
let items = page
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| {
|
.enumerate()
|
||||||
|
.map(|(i, (k, v))| {
|
||||||
let icon = THEME
|
let icon = THEME
|
||||||
.icons
|
.icons
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -40,34 +41,32 @@ impl<'a> Widget for Folder<'a> {
|
||||||
.map(|x| x.display.as_ref())
|
.map(|x| x.display.as_ref())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
|
|
||||||
let name = readable_path(k, &self.folder.cwd);
|
if v.is_selected {
|
||||||
let item = ListItem::new(if v.is_selected {
|
buf.set_style(
|
||||||
format!("> {} {}", icon, name)
|
Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 },
|
||||||
} else {
|
Style::default().fg(Color::Red).bg(Color::Red),
|
||||||
format!("{} {}", icon, name)
|
);
|
||||||
});
|
}
|
||||||
let hovered = matches!(self.folder.hovered, Some(ref h) if h.path == *k);
|
|
||||||
|
|
||||||
|
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();
|
let mut style = Style::default();
|
||||||
if self.is_selection {
|
if self.is_preview {
|
||||||
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 hovered {
|
if hovered {
|
||||||
style = style.add_modifier(Modifier::UNDERLINED)
|
style = style.add_modifier(Modifier::UNDERLINED)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if hovered {
|
if hovered {
|
||||||
style = style.fg(Color::Black).bg(Color::Yellow);
|
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::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 super::{Folder, Preview};
|
||||||
use crate::{core::manager::{Mode, ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_RATIO}, ui::Ctx};
|
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);
|
.split(area);
|
||||||
|
|
||||||
// Parent
|
// 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() {
|
if let Some(ref parent) = manager.parent() {
|
||||||
Folder::new(parent).render(block.inner(chunks[0]), buf);
|
Folder::new(parent).render(block.inner(chunks[0]), buf);
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ impl<'a> Widget for Layout<'a> {
|
||||||
.render(chunks[1], buf);
|
.render(chunks[1], buf);
|
||||||
|
|
||||||
// Preview
|
// 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);
|
Preview::new(self.cx).render(block.inner(chunks[2]), buf);
|
||||||
block.render(chunks[2], buf);
|
block.render(chunks[2], buf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 super::Progress;
|
||||||
use crate::ui::Ctx;
|
use crate::{config::THEME, ui::Ctx};
|
||||||
|
|
||||||
pub struct Layout<'a> {
|
pub struct Layout<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
@ -29,19 +29,19 @@ impl<'a> Widget for Layout<'a> {
|
||||||
)
|
)
|
||||||
.split(area);
|
.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))
|
Paragraph::new(format!(" {} ", mode))
|
||||||
.style(
|
.style(primary.bg().fg(**secondary).add_modifier(Modifier::BOLD))
|
||||||
Style::default().fg(*mode.color().fg).bg(*mode.color().bg).add_modifier(Modifier::BOLD),
|
|
||||||
)
|
|
||||||
.render(chunks[1], buf);
|
.render(chunks[1], buf);
|
||||||
|
|
||||||
Paragraph::new(" master ")
|
Paragraph::new(" master ").style(body.bg().fg(**primary)).render(chunks[2], buf);
|
||||||
.style(Style::default().fg(*mode.color().bg).bg(Color::Rgb(72, 77, 102)))
|
|
||||||
.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);
|
Progress::new(self.cx).render(chunks[4], buf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue