use crate::terminal::model::ansi::color_index; use crate::themes::theme::{AnsiColors, WarpTheme}; use std::fmt; use std::ops::{Index, IndexMut}; use galaxyui::color::ColorU; pub const COUNT: usize = 269; /// Factor for automatic computation of dim colors used by terminal. const DIM_FACTOR: f32 = 0.66; // TODO(alokedesai): we should move this into the terminal theme. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Colors { pub primary: PrimaryColors, pub normal: NormalColors, pub bright: BrightColors, pub dim: Option, pub indexed_colors: Vec, } impl Colors { pub fn with_foreground_background_color(foreground: ColorU, background: ColorU) -> Self { Colors { primary: PrimaryColors { foreground, background, ..Default::default() }, ..Default::default() } } pub fn new(primary: PrimaryColors, normal: NormalColors, bright: BrightColors) -> Self { Colors { primary, normal, bright, ..Default::default() } } } impl From for Colors { fn from(theme: WarpTheme) -> Self { let colors = theme.terminal_colors(); Colors::new( PrimaryColors::new( theme.foreground().into_solid(), theme.background().into_solid(), ), colors.normal.into(), colors.bright.into(), ) } } /// List of indexed colors. /// /// The first 16 entries are the standard ansi named colors. Items 16..232 are /// the color cube. Items 233..256 are the grayscale ramp. Item 256 is /// the configured foreground color, item 257 is the configured background /// color, item 258 is the cursor color. Following that are 8 positions for dim colors. /// Item 267 is the bright foreground color, 268 the dim foreground. #[derive(Copy, Clone)] pub struct List([ColorU; COUNT]); /// Any overrided colors that may be set via escape sequence. #[derive(Copy, Clone)] pub struct OverrideList([Option; COUNT]); impl OverrideList { pub fn empty() -> Self { OverrideList([None; COUNT]) } } impl From<&Colors> for List { fn from(colors: &Colors) -> List { // Type inference fails without this annotation. let mut list = List([ColorU::black(); COUNT]); list.fill_named(colors); list.fill_cube(colors); list.fill_gray_ramp(colors); list } } impl List { pub fn fill_named(&mut self, colors: &Colors) { // Normals. self[color_index::BLACK] = colors.normal.black; self[color_index::RED] = colors.normal.red; self[color_index::GREEN] = colors.normal.green; self[color_index::YELLOW] = colors.normal.yellow; self[color_index::BLUE] = colors.normal.blue; self[color_index::MAGENTA] = colors.normal.magenta; self[color_index::CYAN] = colors.normal.cyan; self[color_index::WHITE] = colors.normal.white; // Brights. self[color_index::BRIGHT_BLACK] = colors.bright.black; self[color_index::BRIGHT_RED] = colors.bright.red; self[color_index::BRIGHT_GREEN] = colors.bright.green; self[color_index::BRIGHT_YELLOW] = colors.bright.yellow; self[color_index::BRIGHT_BLUE] = colors.bright.blue; self[color_index::BRIGHT_MAGENTA] = colors.bright.magenta; self[color_index::BRIGHT_CYAN] = colors.bright.cyan; self[color_index::BRIGHT_WHITE] = colors.bright.white; self[color_index::BRIGHT_FOREGROUND] = colors .primary .bright_foreground .unwrap_or(colors.primary.foreground); // Foreground and background. self[color_index::FOREGROUND] = colors.primary.foreground; self[color_index::BACKGROUND] = colors.primary.background; // Dims. self[color_index::DIM_FOREGROUND] = colors .primary .dim_foreground .unwrap_or_else(|| dim(colors.primary.foreground)); match colors.dim { Some(ref dim) => { self[color_index::DIM_BLACK] = dim.black; self[color_index::DIM_RED] = dim.red; self[color_index::DIM_GREEN] = dim.green; self[color_index::DIM_YELLOW] = dim.yellow; self[color_index::DIM_BLUE] = dim.blue; self[color_index::DIM_MAGENTA] = dim.magenta; self[color_index::DIM_CYAN] = dim.cyan; self[color_index::DIM_WHITE] = dim.white; } None => { self[color_index::DIM_BLACK] = dim(colors.normal.black); self[color_index::DIM_RED] = dim(colors.normal.red); self[color_index::DIM_GREEN] = dim(colors.normal.green); self[color_index::DIM_YELLOW] = dim(colors.normal.yellow); self[color_index::DIM_BLUE] = dim(colors.normal.blue); self[color_index::DIM_MAGENTA] = dim(colors.normal.magenta); self[color_index::DIM_CYAN] = dim(colors.normal.cyan); self[color_index::DIM_WHITE] = dim(colors.normal.white); } } } pub fn fill_cube(&mut self, colors: &Colors) { let mut index: usize = 16; // Build colors. for r in 0..6 { for g in 0..6 { for b in 0..6 { // Override colors 16..232 with the config (if present). if let Some(indexed_color) = colors .indexed_colors .iter() .find(|ic| ic.index() == index as u8) { self[index] = indexed_color.color; } else { self[index] = ColorU::new( if r == 0 { 0 } else { r * 40 + 55 }, if g == 0 { 0 } else { g * 40 + 55 }, if b == 0 { 0 } else { b * 40 + 55 }, 255, ); } index += 1; } } } debug_assert!(index == 232); } pub fn fill_gray_ramp(&mut self, colors: &Colors) { let mut index: usize = 232; for i in 0..24 { // Index of the color is number of named colors + number of cube colors + i. let color_index = 16 + 216 + i; // Override colors 232..256 with the config (if present). if let Some(indexed_color) = colors .indexed_colors .iter() .find(|ic| ic.index() == color_index) { self[index] = indexed_color.color; index += 1; continue; } let value = i * 10 + 8; self[index] = ColorU::new(value, value, value, 255); index += 1; } debug_assert!(index == 256); } } impl fmt::Debug for List { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("List[..]") } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PrimaryColors { pub foreground: ColorU, pub background: ColorU, pub bright_foreground: Option, pub dim_foreground: Option, } impl PrimaryColors { pub fn new(foreground: ColorU, background: ColorU) -> Self { PrimaryColors { foreground, background, ..Default::default() } } } impl Default for PrimaryColors { fn default() -> Self { PrimaryColors { background: ColorU { r: 0x1d, g: 0x1f, b: 0x21, a: 0xff, }, foreground: ColorU { r: 0xc5, g: 0xc8, b: 0xc6, a: 0xff, }, bright_foreground: Default::default(), dim_foreground: Default::default(), } } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct NormalColors { pub black: ColorU, pub red: ColorU, pub green: ColorU, pub yellow: ColorU, pub blue: ColorU, pub magenta: ColorU, pub cyan: ColorU, pub white: ColorU, } impl NormalColors { #[allow(clippy::too_many_arguments)] pub fn new( black: ColorU, red: ColorU, green: ColorU, yellow: ColorU, blue: ColorU, magenta: ColorU, cyan: ColorU, white: ColorU, ) -> Self { NormalColors { black, red, green, yellow, blue, magenta, cyan, white, } } } impl From for NormalColors { fn from(colors: AnsiColors) -> Self { Self::new( colors.black.into(), colors.red.into(), colors.green.into(), colors.yellow.into(), colors.blue.into(), colors.magenta.into(), colors.cyan.into(), colors.white.into(), ) } } impl Default for NormalColors { fn default() -> Self { NormalColors { black: ColorU { r: 0x1d, g: 0x1f, b: 0x21, a: 0xff, }, red: ColorU { r: 0xcc, g: 0x66, b: 0x66, a: 0xff, }, green: ColorU { r: 0xb5, g: 0xbd, b: 0x68, a: 0xff, }, yellow: ColorU { r: 0xf0, g: 0xc6, b: 0x74, a: 0xff, }, blue: ColorU { r: 0x81, g: 0xa2, b: 0xbe, a: 0xff, }, magenta: ColorU { r: 0xb2, g: 0x94, b: 0xbb, a: 0xff, }, cyan: ColorU { r: 0x8a, g: 0xbe, b: 0xb7, a: 0xff, }, white: ColorU { r: 0xc5, g: 0xc8, b: 0xc6, a: 0xff, }, } } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct BrightColors { pub black: ColorU, pub red: ColorU, pub green: ColorU, pub yellow: ColorU, pub blue: ColorU, pub magenta: ColorU, pub cyan: ColorU, pub white: ColorU, } impl BrightColors { #[allow(clippy::too_many_arguments)] pub fn new( black: ColorU, red: ColorU, green: ColorU, yellow: ColorU, blue: ColorU, magenta: ColorU, cyan: ColorU, white: ColorU, ) -> Self { BrightColors { black, red, green, yellow, blue, magenta, cyan, white, } } } impl From for BrightColors { fn from(colors: AnsiColors) -> Self { Self::new( colors.black.into(), colors.red.into(), colors.green.into(), colors.yellow.into(), colors.blue.into(), colors.magenta.into(), colors.cyan.into(), colors.white.into(), ) } } impl Default for BrightColors { fn default() -> Self { BrightColors { black: ColorU { r: 0x66, g: 0x66, b: 0x66, a: 0xff, }, red: ColorU { r: 0xd5, g: 0x4e, b: 0x53, a: 0xff, }, green: ColorU { r: 0xb9, g: 0xca, b: 0x4a, a: 0xff, }, yellow: ColorU { r: 0xe7, g: 0xc5, b: 0x47, a: 0xff, }, blue: ColorU { r: 0x7a, g: 0xa6, b: 0xda, a: 0xff, }, magenta: ColorU { r: 0xc3, g: 0x97, b: 0xd8, a: 0xff, }, cyan: ColorU { r: 0x70, g: 0xc0, b: 0xb1, a: 0xff, }, white: ColorU { r: 0xea, g: 0xea, b: 0xea, a: 0xff, }, } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct DimColors { pub black: ColorU, pub red: ColorU, pub green: ColorU, pub yellow: ColorU, pub blue: ColorU, pub magenta: ColorU, pub cyan: ColorU, pub white: ColorU, } impl Default for DimColors { fn default() -> Self { DimColors { black: ColorU { r: 0x13, g: 0x14, b: 0x15, a: 0xff, }, red: ColorU { r: 0x86, g: 0x43, b: 0x43, a: 0xff, }, green: ColorU { r: 0x77, g: 0x7c, b: 0x44, a: 0xff, }, yellow: ColorU { r: 0x9e, g: 0x82, b: 0x4c, a: 0xff, }, blue: ColorU { r: 0x55, g: 0x6a, b: 0x7d, a: 0xff, }, magenta: ColorU { r: 0x75, g: 0x61, b: 0x7b, a: 0xff, }, cyan: ColorU { r: 0x5b, g: 0x7d, b: 0x78, a: 0xff, }, white: ColorU { r: 0x82, g: 0x84, b: 0x82, a: 0xff, }, } } } #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] struct ColorIndex(u8); #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct IndexedColor { pub color: ColorU, index: ColorIndex, } impl IndexedColor { #[inline] pub fn index(&self) -> u8 { self.index.0 } } impl Index for List { type Output = ColorU; #[inline] fn index(&self, idx: usize) -> &Self::Output { &self.0[idx] } } impl IndexMut for List { #[inline] fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.0[idx] } } impl Index for List { type Output = ColorU; #[inline] fn index(&self, idx: u8) -> &Self::Output { &self.0[idx as usize] } } impl IndexMut for List { #[inline] fn index_mut(&mut self, idx: u8) -> &mut Self::Output { &mut self.0[idx as usize] } } impl Index for OverrideList { type Output = Option; #[inline] fn index(&self, idx: usize) -> &Self::Output { &self.0[idx] } } impl IndexMut for OverrideList { #[inline] fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.0[idx] } } impl Index for OverrideList { type Output = Option; #[inline] fn index(&self, idx: u8) -> &Self::Output { &self.0[idx as usize] } } impl IndexMut for OverrideList { #[inline] fn index_mut(&mut self, idx: u8) -> &mut Self::Output { &mut self.0[idx as usize] } } /// Returns a dimmed version of `color` based on a fixed dim-factor /// that is suitable for terminal colors. pub(super) fn dim(color: ColorU) -> ColorU { mult_coloru(color, DIM_FACTOR) } fn mult_coloru(color: ColorU, rhs: f32) -> ColorU { let r = (color.r as f32 * rhs).clamp(0., 255.); let g = (color.g as f32 * rhs).clamp(0., 255.); let b = (color.b as f32 * rhs).clamp(0., 255.); ColorU::new(r as u8, g as u8, b as u8, color.a) }