From f34116f3f7279bfbc7d6f7a126977dd193ed1311 Mon Sep 17 00:00:00 2001 From: Mohammad AlSaleh Date: Fri, 12 Jan 2024 01:41:37 +0300 Subject: [PATCH] Support DIM * Add a color deriver and derive dim colors for all themes. * Derive actual bright colors for the OneHalfDark theme while at it. * Use dim colors when the DIM flag is set. Signed-off-by: Mohammad AlSaleh --- Cargo.lock | 1 + Cargo.toml | 1 + src/terminal.rs | 20 ++-- src/terminal_theme.rs | 208 ++++++++++++++++++++++++++++-------------- 4 files changed, 158 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 662830a..3994df3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -874,6 +874,7 @@ dependencies = [ "lazy_static", "libcosmic", "log", + "palette", "paste", "rust-embed", "serde", diff --git a/Cargo.toml b/Cargo.toml index aaf80d8..9c701fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester i18n-embed-fl = "0.6" rust-embed = "6" paste = "1.0" +palette = "0.7" [dependencies.cosmic-text] git = "https://github.com/pop-os/cosmic-text.git" diff --git a/src/terminal.rs b/src/terminal.rs index 24b5343..4512abc 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -85,6 +85,13 @@ fn as_bright(mut color: Color) -> Color { color } +fn as_dim(mut color: Color) -> Color { + if let Color::Named(named) = color { + color = Color::Named(named.to_dim()); + } + color +} + fn convert_color(colors: &Colors, color: Color) -> cosmic_text::Color { let rgb = match color { Color::Named(named_color) => match colors[named_color] { @@ -581,12 +588,13 @@ impl Terminal { let mut attrs = self.default_attrs; - let cell_fg = - if self.use_bright_bold && indexed.cell.flags.contains(Flags::BOLD) { - as_bright(indexed.cell.fg) - } else { - indexed.cell.fg - }; + let cell_fg = if indexed.cell.flags.contains(Flags::DIM) { + as_dim(indexed.cell.fg) + } else if self.use_bright_bold && indexed.cell.flags.contains(Flags::BOLD) { + as_bright(indexed.cell.fg) + } else { + indexed.cell.fg + }; let (mut fg, mut bg) = if indexed.cell.flags.contains(Flags::INVERSE) { ( diff --git a/src/terminal_theme.rs b/src/terminal_theme.rs index 38b2bde..f7814e0 100644 --- a/src/terminal_theme.rs +++ b/src/terminal_theme.rs @@ -2,8 +2,113 @@ use alacritty_terminal::{ term::color::Colors, vte::ansi::{NamedColor, Rgb}, }; + +use palette::{encoding::Srgb, rgb::Rgb as PRgb, Okhsl, FromColor, num::Abs}; use std::collections::HashMap; +// Fill missing dim/bright colors with derived values from normal ones. +struct ColorDerive { + dim_saturation_adjustment: f32, + dim_lightness_adjustment: f32, + bright_saturation_adjustment: f32, + bright_lightness_adjustment: f32, +} + +impl ColorDerive { + fn new() -> Self { + Self { + // The dim flag/escape code is also sometimes described as faint. + // So we reduce lightness and saturation to get both effects. + dim_saturation_adjustment: -0.2, + dim_lightness_adjustment: -0.2, + // Normal colors are usually saturated enough. So we default this to 0.0 + // to avoid pushing colors towards white. + bright_saturation_adjustment: 0.0, + bright_lightness_adjustment: 0.10, + } + } + + fn with_dim_lightness_adjustment(self, dim_lightness_adjustment: f32) -> Self { + Self { dim_lightness_adjustment, ..self } + } + + fn rgb_to_okhsl(c: Rgb) -> Okhsl { + let p_rgb = PRgb::::new(c.r, c.g, c.b) + .into_format::(); + Okhsl::from_color(p_rgb) + } + + fn okhsl_to_rgb(c: Okhsl) -> Rgb { + let p_rgb = PRgb::::from_color(c) + .into_format::(); + let (r, g, b) = p_rgb.into_components(); + Rgb{r, g, b} + } + + fn color_adj(rgb: Rgb, saturation_adj: f32, lightness_adj: f32) -> Rgb { + let mut okhsl = Self::rgb_to_okhsl(rgb); + + okhsl.saturation = (okhsl.saturation + saturation_adj) + .max(0.0) + .min(1.0); + okhsl.lightness = (okhsl.lightness + lightness_adj) + .max(0.0) + .min(1.0); + + Self::okhsl_to_rgb(okhsl) + } + + fn brighten(&self, rgb: Rgb) -> Rgb { + let saturation_adj = self.bright_saturation_adjustment; + let lightness_adj = self.bright_lightness_adjustment; + Self::color_adj(rgb, saturation_adj, lightness_adj) + } + + fn dim_and_faint(&self, rgb: Rgb) -> Rgb { + let saturation_adj = self.dim_saturation_adjustment; + let lightness_adj = self.dim_lightness_adjustment; + Self::color_adj(rgb, saturation_adj, lightness_adj) + } + + fn fill_missing_brights(&self, colors: &mut Colors) { + macro_rules! populate { + ($($normal:ident$(,)?)+) => { + paste::paste!{ + $( + if colors[NamedColor::[]].is_none() { + match colors[NamedColor::$normal] { + None => panic!("tried to derive bright color from {} which is not set", stringify!($normal)), + Some(rgb) => colors[NamedColor::[]] = Some(self.brighten(rgb)), + } + } + )+ + } + }; + } + + populate!{ Foreground, Black, Red, Green, Yellow, Blue, Magenta, Cyan, White }; + } + + fn fill_missing_dims(&self, colors: &mut Colors) { + macro_rules! populate { + ($($normal:ident$(,)?)+) => { + paste::paste!{ + $( + if colors[NamedColor::[]].is_none() { + match colors[NamedColor::$normal] { + None => panic!("tried to derive dim color from {} which is not set", stringify!($normal)), + Some(rgb) => colors[NamedColor::[]] = Some(self.dim_and_faint(rgb)), + } + } + )+ + } + }; + } + + populate!{ Foreground, Black, Red, Green, Yellow, Blue, Magenta, Cyan, White }; + } +} + fn auto_colors() -> Colors { let mut colors = Colors::default(); @@ -69,18 +174,10 @@ fn cosmic_dark() -> Colors { colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite]; colors[NamedColor::Background] = colors[NamedColor::Black]; colors[NamedColor::Cursor] = colors[NamedColor::BrightWhite]; - /*TODO - colors[NamedColor::DimBlack] = colors[NamedColor::]; - colors[NamedColor::DimRed] = colors[NamedColor::]; - colors[NamedColor::DimGreen] = colors[NamedColor::]; - colors[NamedColor::DimYellow] = colors[NamedColor::]; - colors[NamedColor::DimBlue] = colors[NamedColor::]; - colors[NamedColor::DimMagenta] = colors[NamedColor::]; - colors[NamedColor::DimCyan] = colors[NamedColor::]; - colors[NamedColor::DimWhite] = colors[NamedColor::]; - */ colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite]; - //TODO colors[NamedColor::DimForeground] = colors[NamedColor::]; + + // Fill missing dim colors + ColorDerive::new().fill_missing_dims(&mut colors); colors } @@ -118,18 +215,16 @@ fn cosmic_light() -> Colors { colors[NamedColor::Foreground] = colors[NamedColor::Black]; colors[NamedColor::Background] = colors[NamedColor::BrightWhite]; colors[NamedColor::Cursor] = colors[NamedColor::Black]; - /*TODO - colors[NamedColor::DimBlack] = colors[NamedColor::]; - colors[NamedColor::DimRed] = colors[NamedColor::]; - colors[NamedColor::DimGreen] = colors[NamedColor::]; - colors[NamedColor::DimYellow] = colors[NamedColor::]; - colors[NamedColor::DimBlue] = colors[NamedColor::]; - colors[NamedColor::DimMagenta] = colors[NamedColor::]; - colors[NamedColor::DimCyan] = colors[NamedColor::]; - colors[NamedColor::DimWhite] = colors[NamedColor::]; - */ - colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite]; - //TODO colors[NamedColor::DimForeground] = colors[NamedColor::]; + colors[NamedColor::BrightForeground] = colors[NamedColor::BrightBlack]; + + // Fill missing dim colors + ColorDerive::new() + // With light backgrounds, the dim and faint descriptions are at odds! + // To make the color fainter, we would need to increase lightness not decrease it! + // But other terminals seem to still dim colors in light themes. So we dim too, but + // not by much, since normal colors are dim enough already. + .with_dim_lightness_adjustment(-0.07) + .fill_missing_dims(&mut colors); colors } @@ -167,18 +262,13 @@ fn gruvbox_dark() -> Colors { colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite]; colors[NamedColor::Background] = colors[NamedColor::Black]; colors[NamedColor::Cursor] = colors[NamedColor::BrightWhite]; - /*TODO - colors[NamedColor::DimBlack] = colors[NamedColor::]; - colors[NamedColor::DimRed] = colors[NamedColor::]; - colors[NamedColor::DimGreen] = colors[NamedColor::]; - colors[NamedColor::DimYellow] = colors[NamedColor::]; - colors[NamedColor::DimBlue] = colors[NamedColor::]; - colors[NamedColor::DimMagenta] = colors[NamedColor::]; - colors[NamedColor::DimCyan] = colors[NamedColor::]; - colors[NamedColor::DimWhite] = colors[NamedColor::]; - */ colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite]; - //TODO colors[NamedColor::DimForeground] = colors[NamedColor::]; + + // Fill missing dim colors + ColorDerive::new() + // Dim less so colors are readable with default bg + .with_dim_lightness_adjustment(-0.15) + .fill_missing_dims(&mut colors); colors } @@ -204,30 +294,21 @@ fn one_half_dark() -> Colors { colors[NamedColor::White] = Some(encode_rgb(0xdcdfe4)); colors[NamedColor::BrightBlack] = Some(encode_rgb(0x5d677a)); - colors[NamedColor::BrightRed] = Some(encode_rgb(0xe06c75)); - colors[NamedColor::BrightGreen] = Some(encode_rgb(0x98c379)); - colors[NamedColor::BrightYellow] = Some(encode_rgb(0xe5c07b)); - colors[NamedColor::BrightBlue] = Some(encode_rgb(0x61afef)); - colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xc678dd)); - colors[NamedColor::BrightCyan] = Some(encode_rgb(0x56b6c2)); - colors[NamedColor::BrightWhite] = Some(encode_rgb(0xdcdfe4)); - // Set special colors - colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite]; + // Set this before filling bright colors (including BrightForeground) + colors[NamedColor::Foreground] = colors[NamedColor::White]; + + let color_derive = ColorDerive::new(); + + // Fill missing bright colors + color_derive.fill_missing_brights(&mut colors); + + // Set the rest of special colors colors[NamedColor::Background] = colors[NamedColor::Black]; colors[NamedColor::Cursor] = colors[NamedColor::BrightWhite]; - /*TODO - colors[NamedColor::DimBlack] = colors[NamedColor::]; - colors[NamedColor::DimRed] = colors[NamedColor::]; - colors[NamedColor::DimGreen] = colors[NamedColor::]; - colors[NamedColor::DimYellow] = colors[NamedColor::]; - colors[NamedColor::DimBlue] = colors[NamedColor::]; - colors[NamedColor::DimMagenta] = colors[NamedColor::]; - colors[NamedColor::DimCyan] = colors[NamedColor::]; - colors[NamedColor::DimWhite] = colors[NamedColor::]; - */ - colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite]; - //TODO colors[NamedColor::DimForeground] = colors[NamedColor::]; + + // Fill missing dim colors + color_derive.fill_missing_dims(&mut colors); colors } @@ -260,18 +341,13 @@ fn pop_dark() -> Colors { colors[NamedColor::Foreground] = Some(encode_rgb(242, 242, 242)); colors[NamedColor::Background] = Some(encode_rgb(51, 51, 51)); colors[NamedColor::Cursor] = colors[NamedColor::BrightWhite]; - /*TODO - colors[NamedColor::DimBlack] = colors[NamedColor::]; - colors[NamedColor::DimRed] = colors[NamedColor::]; - colors[NamedColor::DimGreen] = colors[NamedColor::]; - colors[NamedColor::DimYellow] = colors[NamedColor::]; - colors[NamedColor::DimBlue] = colors[NamedColor::]; - colors[NamedColor::DimMagenta] = colors[NamedColor::]; - colors[NamedColor::DimCyan] = colors[NamedColor::]; - colors[NamedColor::DimWhite] = colors[NamedColor::]; - */ colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite]; - //TODO colors[NamedColor::DimForeground] = colors[NamedColor::]; + + // Fill missing dim colors + ColorDerive::new() + // Dim less so colors are readable with default bg + .with_dim_lightness_adjustment(-0.05) + .fill_missing_dims(&mut colors); colors }