use crate::buffer::CellStyle;
use crate::geom::Insets;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Theme {
Dark,
Light,
}
thread_local! {
pub static CURRENT_THEME: std::cell::RefCell<Theme> = std::cell::RefCell::new(Theme::Dark);
}
pub fn set_theme(theme: Theme) {
CURRENT_THEME.with(|t| *t.borrow_mut() = theme);
crate::runtime::FORCE_FULL_PAINT.with(|f| *f.borrow_mut() = true);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Color {
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
Gray,
LightRed,
LightGreen,
LightYellow,
LightBlue,
LightMagenta,
LightCyan,
DarkGray,
Indexed(u8),
Rgb(u8, u8, u8),
}
impl Color {
pub fn hex(hex: &str) -> Option<Color> {
let s = hex.trim().trim_start_matches('#');
if s.is_empty() {
return None;
}
match s.len() {
3 => {
let chars: Vec<char> = s.chars().collect();
let r = u8::from_str_radix(&chars[0].to_string().repeat(2), 16).ok()?;
let g = u8::from_str_radix(&chars[1].to_string().repeat(2), 16).ok()?;
let b = u8::from_str_radix(&chars[2].to_string().repeat(2), 16).ok()?;
Some(Color::Rgb(r, g, b))
}
6 => {
let r = u8::from_str_radix(&s[0..2], 16).ok()?;
let g = u8::from_str_radix(&s[2..4], 16).ok()?;
let b = u8::from_str_radix(&s[4..6], 16).ok()?;
Some(Color::Rgb(r, g, b))
}
_ => None,
}
}
pub fn rgb(r: u8, g: u8, b: u8) -> Color {
Color::Rgb(r, g, b)
}
pub fn indexed(i: u8) -> Color {
Color::Indexed(i)
}
}
impl From<Color> for crossterm::style::Color {
fn from(c: Color) -> Self {
let dark = CURRENT_THEME.with(|t| *t.borrow() == Theme::Dark);
match c {
Color::Rgb(r, g, b) => crossterm::style::Color::Rgb { r, g, b },
Color::Indexed(i) => crossterm::style::Color::AnsiValue(i),
Color::Black => crossterm::style::Color::Black,
Color::Red if dark => crossterm::style::Color::DarkRed,
Color::Red => crossterm::style::Color::Red,
Color::Green if dark => crossterm::style::Color::DarkGreen,
Color::Green => crossterm::style::Color::Green,
Color::Yellow if dark => crossterm::style::Color::DarkYellow,
Color::Yellow => crossterm::style::Color::Yellow,
Color::Blue if dark => crossterm::style::Color::DarkBlue,
Color::Blue => crossterm::style::Color::Blue,
Color::Magenta if dark => crossterm::style::Color::DarkMagenta,
Color::Magenta => crossterm::style::Color::Magenta,
Color::Cyan if dark => crossterm::style::Color::DarkCyan,
Color::Cyan => crossterm::style::Color::Cyan,
Color::White => crossterm::style::Color::Grey,
Color::Gray => crossterm::style::Color::DarkGrey,
Color::LightRed => crossterm::style::Color::Red,
Color::LightGreen => crossterm::style::Color::Green,
Color::LightYellow => crossterm::style::Color::Yellow,
Color::LightBlue => crossterm::style::Color::Blue,
Color::LightMagenta => crossterm::style::Color::Magenta,
Color::LightCyan => crossterm::style::Color::Cyan,
Color::DarkGray => crossterm::style::Color::DarkGrey,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Length {
Auto,
Fixed(u16),
Percent(u16),
Fraction(u16),
}
impl Default for Length {
fn default() -> Self {
Length::Auto
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
None,
Vertical,
Horizontal,
}
impl Default for Layout {
fn default() -> Self {
Layout::None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextWrap {
#[default]
None,
Char,
Word,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextAlign {
#[default]
Left,
Center,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextTruncate {
#[default]
None,
Ellipsis,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Border {
#[default]
None,
Plain,
Rounded,
Double,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Style {
pub fg: Option<Color>,
pub bg: Option<Color>,
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub width: Length,
pub height: Length,
pub padding: Insets,
pub margin: Insets,
pub layout: Layout,
pub gap: u16,
pub flex_grow: u16,
pub flex_shrink: bool,
pub border: Border,
}
impl Default for Style {
fn default() -> Self {
Self {
fg: None,
bg: None,
bold: false,
italic: false,
underline: false,
width: Length::Auto,
height: Length::Auto,
padding: Insets::ZERO,
margin: Insets::ZERO,
layout: Layout::None,
gap: 0,
flex_grow: 0,
flex_shrink: true,
border: Border::None,
}
}
}
impl Style {
pub fn fg(mut self, color: Color) -> Self {
self.fg = Some(color);
self
}
pub fn bg(mut self, color: Color) -> Self {
self.bg = Some(color);
self
}
pub fn bold(mut self) -> Self {
self.bold = true;
self
}
pub fn italic(mut self) -> Self {
self.italic = true;
self
}
pub fn underline(mut self) -> Self {
self.underline = true;
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn padding(mut self, value: u16) -> Self {
self.padding = Insets::all(value);
self
}
pub fn padding_each(mut self, top: u16, right: u16, bottom: u16, left: u16) -> Self {
self.padding = Insets { top, right, bottom, left };
self
}
pub fn margin(mut self, value: u16) -> Self {
self.margin = Insets::all(value);
self
}
pub fn layout(mut self, layout: Layout) -> Self {
self.layout = layout;
self
}
pub fn gap(mut self, gap: u16) -> Self {
self.gap = gap;
self
}
pub fn flex_grow(mut self, grow: u16) -> Self {
self.flex_grow = grow;
self
}
pub fn flex_shrink(mut self, shrink: bool) -> Self {
self.flex_shrink = shrink;
self
}
pub fn border(mut self, border: Border) -> Self {
self.border = border;
self
}
pub fn into_cell_style(&self) -> CellStyle {
CellStyle {
fg: self.fg,
bg: self.bg,
bold: self.bold,
italic: self.italic,
underline: self.underline,
}
}
}