use crate::*; use rand::{thread_rng, distributions::uniform::UniformSampler}; impl Tui { pub const fn null () -> Color { Color::Reset } pub const fn g (g: u8) -> Color { Color::Rgb(g, g, g) } pub const fn red () -> Color { Color::Rgb(255,0, 0) } pub const fn orange () -> Color { Color::Rgb(255,128,0) } pub const fn yellow () -> Color { Color::Rgb(255,255,0) } pub const fn brown () -> Color { Color::Rgb(128,255,0) } pub const fn green () -> Color { Color::Rgb(0,255,0) } pub const fn electric () -> Color { Color::Rgb(0,255,128) } //fn bg0 () -> Color { Color::Rgb(20, 20, 20) } //fn bg () -> Color { Color::Rgb(28, 35, 25) } //fn border_bg () -> Color { Color::Rgb(40, 50, 30) } //fn border_fg (f: bool) -> Color { if f { Self::bo1() } else { Self::bo2() } } //fn title_fg (f: bool) -> Color { if f { Self::ti1() } else { Self::ti2() } } //fn separator_fg (_: bool) -> Color { Color::Rgb(0, 0, 0) } //fn mode_bg () -> Color { Color::Rgb(150, 160, 90) } //fn mode_fg () -> Color { Color::Rgb(255, 255, 255) } //fn status_bar_bg () -> Color { Color::Rgb(28, 35, 25) } //fn bo1 () -> Color { Color::Rgb(100, 110, 40) } //fn bo2 () -> Color { Color::Rgb(70, 80, 50) } //fn ti1 () -> Color { Color::Rgb(150, 160, 90) } //fn ti2 () -> Color { Color::Rgb(120, 130, 100) } } pub trait HasColor { fn color (&self) -> ItemColor; } #[macro_export] macro_rules! has_color { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasColor for $Struct $(<$($L),*$($T),*>)? { fn color (&$self) -> ItemColor { $cb } } } } /// A color in OKHSL and RGB representations. #[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemColor { pub okhsl: Okhsl, pub rgb: Color, } from!(|okhsl: Okhsl|ItemColor = Self { okhsl, rgb: okhsl_to_rgb(okhsl) }); pub fn okhsl_to_rgb (color: Okhsl) -> Color { let Srgb { red, green, blue, .. }: Srgb = Srgb::from_color_unclamped(color); Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) } from!(|rgb: Color|ItemColor = Self { rgb, okhsl: rgb_to_okhsl(rgb) }); pub fn rgb_to_okhsl (color: Color) -> Okhsl { if let Color::Rgb(r, g, b) = color { Okhsl::from_color(Srgb::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)) } else { unreachable!("only Color::Rgb is supported") } } // A single color within item theme parameters, in OKHSL and RGB representations. impl ItemColor { pub const fn from_rgb (rgb: Color) -> Self { Self { rgb, okhsl: Okhsl::new_const(OklabHue::new(0.0), 0.0, 0.0) } } pub const fn from_okhsl (okhsl: Okhsl) -> Self { Self { rgb: Color::Rgb(0, 0, 0), okhsl } } pub fn random () -> Self { let mut rng = thread_rng(); let lo = Okhsl::new(-180.0, 0.01, 0.25); let hi = Okhsl::new( 180.0, 0.9, 0.5); UniformOkhsl::new(lo, hi).sample(&mut rng).into() } pub fn random_dark () -> Self { let mut rng = thread_rng(); let lo = Okhsl::new(-180.0, 0.025, 0.075); let hi = Okhsl::new( 180.0, 0.5, 0.150); UniformOkhsl::new(lo, hi).sample(&mut rng).into() } pub fn random_near (color: Self, distance: f32) -> Self { color.mix(Self::random(), distance) } pub fn mix (&self, other: Self, distance: f32) -> Self { if distance > 1.0 { panic!("color mixing takes distance between 0.0 and 1.0"); } self.okhsl.mix(other.okhsl, distance).into() } } /// A color in OKHSL and RGB with lighter and darker variants. #[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemPalette { pub base: ItemColor, pub light: ItemColor, pub lighter: ItemColor, pub lightest: ItemColor, pub dark: ItemColor, pub darker: ItemColor, pub darkest: ItemColor, } impl ItemPalette { pub const G: [Self;256] = { let mut builder = konst::array::ArrayBuilder::new(); while !builder.is_full() { let index = builder.len() as u8; let light = (index as f64 * 1.3) as u8; let lighter = (index as f64 * 1.6) as u8; let lightest = (index as f64 * 1.9) as u8; let dark = (index as f64 * 0.9) as u8; let darker = (index as f64 * 0.6) as u8; let darkest = (index as f64 * 0.3) as u8; builder.push(ItemPalette { base: ItemColor::from_rgb(Color::Rgb(index, index, index )), light: ItemColor::from_rgb(Color::Rgb(light, light, light, )), lighter: ItemColor::from_rgb(Color::Rgb(lighter, lighter, lighter, )), lightest: ItemColor::from_rgb(Color::Rgb(lightest, lightest, lightest, )), dark: ItemColor::from_rgb(Color::Rgb(dark, dark, dark, )), darker: ItemColor::from_rgb(Color::Rgb(darker, darker, darker, )), darkest: ItemColor::from_rgb(Color::Rgb(darkest, darkest, darkest, )), }); } builder.build() }; pub fn random () -> Self { ItemColor::random().into() } pub fn random_near (color: Self, distance: f32) -> Self { color.base.mix(ItemColor::random(), distance).into() } pub const G00: Self = { let color: ItemColor = ItemColor { okhsl: Okhsl { hue: OklabHue::new(0.0), lightness: 0.0, saturation: 0.0 }, rgb: Color::Rgb(0, 0, 0) }; Self { base: color, light: color, lighter: color, lightest: color, dark: color, darker: color, darkest: color, } }; pub fn from_tui_color (base: Color) -> Self { Self::from_item_color(ItemColor::from_rgb(base)) } pub fn from_item_color (base: ItemColor) -> Self { let mut light = base.okhsl; light.lightness = (light.lightness * 1.3).min(1.0); let mut lighter = light; lighter.lightness = (lighter.lightness * 1.3).min(1.0); let mut lightest = base.okhsl; lightest.lightness = 0.95; let mut dark = base.okhsl; dark.lightness = (dark.lightness * 0.75).max(0.0); dark.saturation = (dark.saturation * 0.75).max(0.0); let mut darker = dark; darker.lightness = (darker.lightness * 0.66).max(0.0); darker.saturation = (darker.saturation * 0.66).max(0.0); let mut darkest = darker; darkest.lightness = 0.1; darkest.saturation = (darkest.saturation * 0.50).max(0.0); Self { base, light: light.into(), lighter: lighter.into(), lightest: lightest.into(), dark: dark.into(), darker: darker.into(), darkest: darkest.into(), } } } from!(|base: Color| ItemPalette = Self::from_tui_color(base)); from!(|base: ItemColor|ItemPalette = Self::from_item_color(base));