use std::fmt;
use std::path::Path;
use anyhow::Result;
use owo_colors::{OwoColorize, Rgb};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use palette::{
color_theory::Complementary,
convert::FromColorUnclamped,
Hsv, Srgb, IntoColor,
};
use crate::args::Sequences;
use crate::sequences;
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct Colors {
pub cursor: Myrgb,
pub background: Myrgb,
pub foreground: Myrgb,
pub color0 : Myrgb,
pub color1 : Myrgb,
pub color2 : Myrgb,
pub color3 : Myrgb,
pub color4 : Myrgb,
pub color5 : Myrgb,
pub color6 : Myrgb,
pub color7 : Myrgb,
pub color8 : Myrgb,
pub color9 : Myrgb,
pub color10: Myrgb,
pub color11: Myrgb,
pub color12: Myrgb,
pub color13: Myrgb,
pub color14: Myrgb,
pub color15: Myrgb,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Myrgb(pub Srgb);
impl Serialize for Myrgb {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
use serde::de::{Visitor, Error};
impl<'de> Deserialize<'de> for Myrgb {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct RgbVisitor;
impl Visitor<'_> for RgbVisitor {
type Value = Myrgb;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string representing an RGB value")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: Error,
{
let value = if value.len() == 8 || value.len() == 9 { &value[..value.len() - 2] } else { value };
let s: Srgb<u8> = value.parse()
.map_err(Error::custom)?;
Ok(Myrgb(s.into_format()))
}
}
deserializer.deserialize_str(RgbVisitor)
}
}
impl fmt::Display for Myrgb {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (r, g, b) = self.0.into_format::<u8>().into_components();
write!(f, "#{r:02X}{g:02X}{b:02X}", )
}
}
pub trait Compl: palette::Clamp + Sized + FromColorUnclamped<Hsv>
where
Hsv: FromColorUnclamped<Self>,
{
fn complementary(self) -> Self {
let hsv: Hsv = self.into_color();
hsv
.complementary()
.into_color()
}
}
impl Compl for Srgb {}
impl Compl for palette::Srgba {}
pub fn blend(a: Srgb, b: Srgb) -> Srgb {
Srgb::new(
0.5 * a.red + 0.5 * b.red,
0.5 * a.green + 0.5 * b.green,
0.5 * a.blue + 0.5 * b.blue,
)
}
use palette::Srgba;
pub fn blend_alpha(a: Srgba, b: Srgba) -> Srgba {
Srgba::new(
0.5 * a.red + 0.5 * b.red,
0.5 * a.green + 0.5 * b.green,
0.5 * a.blue + 0.5 * b.blue,
0.5 * a.alpha + 0.5 * b.alpha,
)
}
pub trait SrgbString {
fn strsrgb(&self) -> String;
fn striped(&self) -> String;
fn owo_col(&self) -> Rgb;
}
impl SrgbString for Srgb {
fn strsrgb(&self) -> String {
let (r, g, b) = self.into_format::<u8>().into_components();
format!("#{r:02X}{g:02X}{b:02X}")
}
fn striped(&self) -> String {
let (r, g, b) = self.into_format::<u8>().into_components();
format!("{r:02X}{g:02X}{b:02X}")
}
fn owo_col(&self) -> Rgb {
let (r, g, b) = self.into_format::<u8>().into_components();
Rgb(r, g, b)
}
}
impl Myrgb {
pub fn owo_col(&self) -> Rgb {
let (r, g, b) = self.0.into_format::<u8>().into_components();
Rgb(r, g, b)
}
pub fn to_rgb8(self) -> (u8, u8, u8) {
self.0.into_format::<u8>().into_components()
}
fn set_color(&self, index: u32) -> String {
sequences::set_color(&self.0, index)
}
fn set_special(&self, index: u32) -> String {
sequences::set_special(&self.0, index)
}
pub fn darken(&self, amount: f32) -> Self {
use palette::Darken;
Self(self.0.darken(amount))
}
pub fn lighten(&self, amount: f32) -> Self {
use palette::Lighten;
Self(self.0.lighten(amount))
}
pub fn blend(&self, other: Self) -> Self {
let me = self.0;
let other = other.0;
let new = Srgb::new(
0.5 * me.red + 0.5 * other.red,
0.5 * me.green + 0.5 * other.green,
0.5 * me.blue + 0.5 * other.blue,
);
Self(new)
}
pub fn saturate(&self, amount: f32) -> Self {
use palette::Saturate;
let a: Hsv = self.0.into_color();
let rgb: Srgb<f32> = a.saturate(amount).into_color();
Self(rgb)
}
pub fn complementary(&self) -> Self {
Self(
self.0.complementary()
)
}
}
impl Colors {
pub fn output_nl(&self) {
let mut s = String::with_capacity((6+1+1)*16); for c in self.into_iter().take(16) {
s.push_str(&c.to_string());
s.push('\n');
}
print!("{s}");
}
pub fn print(&self) {
print!(
"
{}{}{}{}{}{}{}{}
{}{}{}{}{}{}{}{}
",
" ".on_color(self.color0 .owo_col()),
" ".on_color(self.color1 .owo_col()),
" ".on_color(self.color2 .owo_col()),
" ".on_color(self.color3 .owo_col()),
" ".on_color(self.color4 .owo_col()),
" ".on_color(self.color5 .owo_col()),
" ".on_color(self.color6 .owo_col()),
" ".on_color(self.color7 .owo_col()),
" ".on_color(self.color8 .owo_col()),
" ".on_color(self.color9 .owo_col()),
" ".on_color(self.color10.owo_col()),
" ".on_color(self.color11.owo_col()),
" ".on_color(self.color12.owo_col()),
" ".on_color(self.color13.owo_col()),
" ".on_color(self.color14.owo_col()),
" ".on_color(self.color15.owo_col()),
);
}
pub fn done(&self) {
let space = " ".strikethrough();
print!(
"
{}{}{}{}{}{space}{}{}{}{space}{}{}{}{}{}{}{}{}
",
"E ".color(self.color15.owo_col()).bold().blink(),
"N ".color(self.color14.owo_col()).bold().blink(),
"J ".color(self.color13.owo_col()).bold().blink(),
"O ".color(self.color12.owo_col()).bold().blink(),
"Y ".color(self.color11.owo_col()).bold().blink(),
"T ".color(self.color10.owo_col()).bold().blink(),
"H ".color(self.color9 .owo_col()).bold().blink(),
"E ".color(self.color8 .owo_col()).bold().blink(),
"P ".color(self.color7 .owo_col()).bold().blink(),
"A ".color(self.color6 .owo_col()).bold().blink(),
"L ".color(self.color5 .owo_col()).bold().blink(),
"E ".color(self.color4 .owo_col()).bold().blink(),
"T ".color(self.color3 .owo_col()).bold().blink(),
"T ".color(self.color2 .owo_col()).bold().blink(),
"E ".color(self.color1 .owo_col()).bold().blink(),
"! ".color(self.foreground.owo_col()).bold().blink(),
);
}
pub fn to_16col(self) -> Self {
let c = self;
Self {
color1: c.color1.darken(0.25),
color2: c.color2.darken(0.25),
color3: c.color3.darken(0.25),
color4: c.color4.darken(0.25),
color5: c.color5.darken(0.25),
color6: c.color6.darken(0.25),
..c
}
}
pub fn to_comp(self) -> Self {
let c = self;
Self {
color1 : c.color1.saturate(0.3).complementary(),
color9 : c.color1.saturate(0.3).complementary(),
color2 : c.color2.saturate(0.3).complementary(),
color10 : c.color2.saturate(0.3).complementary(),
color3 : c.color3.saturate(0.3).complementary(),
color11 : c.color3.saturate(0.3).complementary(),
color4 : c.color4.saturate(0.3).complementary(),
color12 : c.color4.saturate(0.3).complementary(),
color5 : c.color5.saturate(0.3).complementary(),
color13 : c.color5.saturate(0.3).complementary(),
color6 : c.color6.saturate(0.3).complementary(),
color14 : c.color6.saturate(0.3).complementary(),
..c
}
}
pub fn saturate_colors(&mut self, amount: f32) {
if amount > 1.0 && amount.is_sign_negative() { return }
let _ = [
&mut self.color1,
&mut self.color2,
&mut self.color3,
&mut self.color4,
&mut self.color5,
&mut self.color6,
&mut self.color9,
&mut self.color10,
&mut self.color11,
&mut self.color12,
&mut self.color13,
&mut self.color14,
].map(|i| *i = i.saturate(amount));
}
pub fn contrast_well(a: Myrgb, b: Myrgb) -> bool {
use palette::color_difference::Wcag21RelativeContrast;
a.0.has_min_contrast_text(b.0)
}
pub fn check_contrast_all(&mut self) {
let a = [
&mut self.color1,
&mut self.color2,
&mut self.color3,
&mut self.color4,
&mut self.color5,
&mut self.color6,
&mut self.color9,
&mut self.color10,
&mut self.color11,
&mut self.color12,
&mut self.color13,
&mut self.color14,
];
let mut i: u32 = 0;
let mut bg_already_dark = false;
while !Self::contrast_well(self.background, self.foreground) && i < 10 {
self.background = self.background.darken(0.15);
self.foreground = self.foreground.lighten(0.15);
bg_already_dark = true;
i += 1;
}
for col in a {
i = 0;
while !Self::contrast_well(self.background, *col) && i < 5 {
if !bg_already_dark {
self.background = self.background.darken(0.15);
bg_already_dark = true;
}
*col = col.lighten(0.05);
i += 1;
}
}
}
pub fn to_seq(&self, remove: Option<&[Sequences]>) -> String {
let c = self;
let cols = [
c.color0 .set_color(0 ),
c.color1 .set_color(1 ),
c.color2 .set_color(2 ),
c.color3 .set_color(3 ),
c.color4 .set_color(4 ),
c.color5 .set_color(5 ),
c.color6 .set_color(6 ),
c.color7 .set_color(7 ),
c.color8 .set_color(8 ),
c.color9 .set_color(9 ),
c.color10.set_color(10),
c.color11.set_color(11),
c.color12.set_color(12),
c.color13.set_color(13),
c.color14.set_color(14),
c.color15.set_color(15),
];
let bg = [
c.background.set_special(11),
c.background.set_special(19),
c.background.set_color(232),
c.background.set_color(257),
c.background.set_special(708),
];
let fg = [
c.foreground.set_special(10),
c.foreground.set_special(17),
c.foreground.set_color(256),
];
let cursor = [
c.cursor.set_special(12), c.cursor.set_special(13), ];
let arr;
if let Some(seqs) = remove {
use crate::args::Sequences as Seq;
use std::collections::HashMap;
let bg = bg.join("");
let fg = fg.join("");
let cursor = cursor.join("");
let mut h = HashMap::from([
(Seq::Color0 , &cols[0 ]),
(Seq::Color1 , &cols[1 ]),
(Seq::Color2 , &cols[2 ]),
(Seq::Color3 , &cols[3 ]),
(Seq::Color4 , &cols[4 ]),
(Seq::Color5 , &cols[5 ]),
(Seq::Color6 , &cols[6 ]),
(Seq::Color7 , &cols[7 ]),
(Seq::Color8 , &cols[8 ]),
(Seq::Color9 , &cols[9 ]),
(Seq::Color10 , &cols[10]),
(Seq::Color11 , &cols[11]),
(Seq::Color12 , &cols[12]),
(Seq::Color13 , &cols[13]),
(Seq::Color14 , &cols[14]),
(Seq::Color15 , &cols[15]),
(Seq::Background , &bg ),
(Seq::Foreground , &fg ),
(Seq::Cursor , &cursor ),
]);
for i in seqs {
h.remove(i);
}
arr = h
.into_values()
.map(|x| x.to_owned())
.collect();
} else {
arr = [ cols.join(""), bg.join(""), fg.join(""), cursor.join("")].join("");
}
arr
}
pub fn sequences(&self, _cache_path: &Path, _ignore: Option<&[Sequences]>) -> anyhow::Result<()> {
#[cfg(target_family = "windows")]
return sequences::windows_term(self);
#[cfg(target_family = "unix")]
return sequences::unix_term(self, _cache_path, _ignore);
}
}
impl From<Srgb> for Myrgb {
fn from(v: Srgb) -> Myrgb {
Myrgb(v)
}
}
impl From<&Srgb> for Myrgb {
fn from(v: &Srgb) -> Myrgb {
Myrgb(*v)
}
}
pub struct ColorsIntoIter {
pub me: Colors,
pub index: usize,
}
impl IntoIterator for Colors {
type Item = Myrgb;
type IntoIter = ColorsIntoIter;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
me: self,
index: 0,
}
}
}
impl Iterator for ColorsIntoIter {
type Item = Myrgb;
fn next(&mut self) -> Option<Myrgb> {
let result = match self.index {
0 => self.me.color0,
1 => self.me.color1,
2 => self.me.color2,
3 => self.me.color3,
4 => self.me.color4,
5 => self.me.color5,
6 => self.me.color6,
7 => self.me.color7,
8 => self.me.color8,
9 => self.me.color9,
10 => self.me.color10,
11 => self.me.color11,
12 => self.me.color12,
13 => self.me.color13,
14 => self.me.color14,
15 => self.me.color15,
16 => self.me.background,
17 => self.me.foreground,
_ => return None,
};
self.index += 1;
Some(result)
}
}