Files
galaxy/app/src/terminal/color.rs
T

623 lines
16 KiB
Rust

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<DimColors>,
pub indexed_colors: Vec<IndexedColor>,
}
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<WarpTheme> 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<ColorU>; 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<ColorU>,
pub dim_foreground: Option<ColorU>,
}
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<AnsiColors> 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<AnsiColors> 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<usize> for List {
type Output = ColorU;
#[inline]
fn index(&self, idx: usize) -> &Self::Output {
&self.0[idx]
}
}
impl IndexMut<usize> for List {
#[inline]
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
&mut self.0[idx]
}
}
impl Index<u8> for List {
type Output = ColorU;
#[inline]
fn index(&self, idx: u8) -> &Self::Output {
&self.0[idx as usize]
}
}
impl IndexMut<u8> for List {
#[inline]
fn index_mut(&mut self, idx: u8) -> &mut Self::Output {
&mut self.0[idx as usize]
}
}
impl Index<usize> for OverrideList {
type Output = Option<ColorU>;
#[inline]
fn index(&self, idx: usize) -> &Self::Output {
&self.0[idx]
}
}
impl IndexMut<usize> for OverrideList {
#[inline]
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
&mut self.0[idx]
}
}
impl Index<u8> for OverrideList {
type Output = Option<ColorU>;
#[inline]
fn index(&self, idx: u8) -> &Self::Output {
&self.0[idx as usize]
}
}
impl IndexMut<u8> 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)
}