1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 05:12:40 +03:00

color: refactor docs, add a couple more methods

Moved the gradient function into the color module, but kept an alias
under the old name.

Gradients now return color objects.

Converting colors to string now uses rgba format when alpha is not 100%.
This commit is contained in:
Wez Furlong 2022-07-12 07:38:39 -07:00
parent 98fb67c029
commit aa697c72ba
30 changed files with 338 additions and 211 deletions

1
.gitignore vendored
View File

@ -14,6 +14,7 @@
/docs/install/*.md
/docs/cli/cli/index.md
/docs/colorschemes/**/index.md
/docs/config/lua/color/index.md
/docs/config/lua/config/index.md
/docs/config/lua/gui-events/index.md
/docs/config/lua/keyassignment/index.md

View File

@ -336,6 +336,7 @@ TOC = [
"enum: KeyAssignment",
"config/lua/keyassignment",
),
Gen("object: Color", "config/lua/color"),
Page("object: ExecDomain", "config/lua/ExecDomain.md"),
Page("object: LocalProcessInfo", "config/lua/LocalProcessInfo.md"),
Gen("object: MuxWindow", "config/lua/mux-window"),

View File

@ -238,6 +238,13 @@ impl From<SrgbaTuple> for (f32, f32, f32, f32) {
}
}
impl From<csscolorparser::Color> for SrgbaTuple {
fn from(color: csscolorparser::Color) -> Self {
let (r, g, b, a) = color.rgba();
Self(r as f32, g as f32, b as f32, a as f32)
}
}
lazy_static::lazy_static! {
static ref NAMED_COLORS: HashMap<String, SrgbaTuple> = build_colors();
}
@ -315,6 +322,14 @@ impl SrgbaTuple {
)
}
pub fn to_string(self) -> String {
if self.3 == 1.0 {
self.to_rgb_string()
} else {
self.to_rgba_string()
}
}
/// Returns a string of the form `#RRGGBB`
pub fn to_rgb_string(self) -> String {
format!(
@ -327,11 +342,11 @@ impl SrgbaTuple {
pub fn to_rgba_string(self) -> String {
format!(
"rgba:{} {} {} {}%",
(self.0 * 255.) as u8,
(self.1 * 255.) as u8,
(self.2 * 255.) as u8,
(self.3 * 100.) as u8
"rgba({}% {}% {}% {}%)",
(self.0 * 100.),
(self.1 * 100.),
(self.2 * 100.),
(self.3 * 100.)
)
}
@ -782,13 +797,13 @@ mod tests {
fn from_rgba() {
assert_eq!(
SrgbaTuple::from_str("clear").unwrap().to_rgba_string(),
"rgba:0 0 0 0%"
"rgba(0% 0% 0% 0%)"
);
assert_eq!(
SrgbaTuple::from_str("rgba:100% 0 0 50%")
.unwrap()
.to_rgba_string(),
"rgba:255 0 0 50%"
"rgba(100% 0% 0% 50%)"
);
}
@ -800,12 +815,11 @@ mod tests {
.to_rgb_string(),
"#ff0000"
);
assert_eq!(
SrgbaTuple::from_str("rgba(255,0,0,1)")
.unwrap()
.to_rgba_string(),
"rgba:255 0 0 100%"
);
let rgba = SrgbaTuple::from_str("rgba(255,0,0,1)").unwrap();
let round_trip = SrgbaTuple::from_str(&rgba.to_rgba_string()).unwrap();
assert_eq!(rgba, round_trip);
assert_eq!(rgba.to_rgba_string(), "rgba(100% 0% 0% 100%)");
}
#[test]

View File

@ -56,13 +56,13 @@ impl std::ops::Deref for RgbaColor {
impl Into<String> for &RgbaColor {
fn into(self) -> String {
self.color.to_rgb_string()
self.color.to_string()
}
}
impl Into<String> for RgbaColor {
fn into(self) -> String {
self.color.to_rgb_string()
self.color.to_string()
}
}

View File

@ -1,8 +1,7 @@
use crate::exec_domain::{ExecDomain, ValueOrFunc};
use crate::keyassignment::KeyAssignment;
use crate::{
FontAttributes, FontStretch, FontStyle, FontWeight, FreeTypeLoadTarget, Gradient, RgbaColor,
TextStyle,
FontAttributes, FontStretch, FontStyle, FontWeight, FreeTypeLoadTarget, RgbaColor, TextStyle,
};
use anyhow::anyhow;
use luahelper::{from_lua_value_dynamic, lua_value_to_dynamic};
@ -205,7 +204,6 @@ end
wezterm_mod.set("sleep_ms", lua.create_async_function(sleep_ms)?)?;
wezterm_mod.set("strftime", lua.create_function(strftime)?)?;
wezterm_mod.set("strftime_utc", lua.create_function(strftime_utc)?)?;
wezterm_mod.set("gradient_colors", lua.create_function(gradient_colors)?)?;
wezterm_mod.set("shell_join_args", lua.create_function(shell_join_args)?)?;
wezterm_mod.set("shell_quote_arg", lua.create_function(shell_quote_arg)?)?;
wezterm_mod.set("shell_split", lua.create_function(shell_split)?)?;
@ -672,17 +670,6 @@ fn utf16_to_utf8<'lua>(_: &'lua Lua, text: mlua::String) -> mlua::Result<String>
String::from_utf16(wide).map_err(|e| mlua::Error::external(e))
}
fn gradient_colors<'lua>(
_lua: &'lua Lua,
(gradient, num_colors): (Gradient, usize),
) -> mlua::Result<Vec<String>> {
let g = gradient.build().map_err(|e| mlua::Error::external(e))?;
Ok(g.colors(num_colors)
.into_iter()
.map(|c| c.to_hex_string())
.collect())
}
pub fn add_to_config_reload_watch_list<'lua>(
lua: &'lua Lua,
args: Variadic<String>,

View File

@ -0,0 +1,11 @@
## `color:adjust_hue_fixed(degrees)`
*Since: nightly builds only*
Adjust the hue angle by the specified number of degrees.
180 degrees gives the complementary color.
Three colors separated by 120 degrees form the triad.
Four colors separated by 90 degrees form the square.
See also [color:adjust_hue_fixed_ryb()](adjust_hue_fixed_ryb.md).

View File

@ -0,0 +1,16 @@
## `color:adjust_hue_fixed_ryb(degrees)`
*Since: nightly builds only*
Adjust the hue angle by the specified number of degrees.
This method uses the [RYB color
model](https://en.wikipedia.org/wiki/RYB_color_model), which more
closely matches how artists think of mixing colors and which is
sometimes referred to as the "artist's color wheel".
180 degrees gives the complementary color.
Three colors separated by 120 degrees form the triad.
Four colors separated by 90 degrees form the square.
See also [color:adjust_hue_fixed()](adjust_hue_fixed.md).

View File

@ -0,0 +1,9 @@
## `color:complement()`
*Since: nightly builds only*
Returns the complement of the color. The complement is computed
by converting to HSL, rotating by 180 degrees and converting back
to RGBA.
See also: [color:complement_ryb()](complement_ryb.md).

View File

@ -0,0 +1,13 @@
## `color:complement_ryb()`
*Since: nightly builds only*
Returns the complement of the color using the [RYB color
model](https://en.wikipedia.org/wiki/RYB_color_model), which more closely
matches how artists think of mixing colors.
The complement is computed by converting to HSL, converting the
hue angle to the equivalent RYB angle, rotating by 180 degrees and
and then converting back to RGBA.
See also: [color:complement()](complement.md).

View File

@ -0,0 +1,25 @@
### `color:contrast_ratio(color)`
*Since: nightly builds only*
Computes the contrast ratio between the two colors.
```lua
> wezterm.color.parse("red"):contrast_ratio(wezterm.color.parse("yellow"))
1
> wezterm.color.parse("red"):contrast_ratio(wezterm.color.parse("navy"))
1.8273614734023298
```
The contrast ratio is computed by first converting to HSL, taking the
L components, and diving the lighter one by the darker one.
A contrast ratio of 1 means no contrast.
The maximum possible contrast ratio is 21:
```lua
> wezterm.color.parse("black"):contrast_ratio(wezterm.color.parse("white"))
21
```

View File

@ -0,0 +1,8 @@
## `color:darken(factor)`
*Since: nightly builds only*
Scales the color towards the minimum lightness by the provided
factor, which should be in the range `0.0` through `1.0`.

View File

@ -0,0 +1,7 @@
## `color:darken_fixed(amount)`
*Since: nightly builds only*
Decrease the lightness by amount, a value ranging from `0.0` to `1.0`.

View File

@ -0,0 +1,16 @@
### `color:delta_e(color)`
*Since: nightly builds only*
Computes the CIEDE2000 DeltaE value representing the difference
between the two colors.
A value:
* <= 1.0: difference is not perceptible by the human eye
* 1-2: difference is perceptible through close observation
* 2-10: difference is perceptible at a glance
* 11-49: Colors are more similar than the opposite
* 50-99: Colors are more opposite than similar
* 100: Colors are exactly the opposite

View File

@ -0,0 +1,8 @@
## `color:desaturate(factor)`
*Since: nightly builds only*
Scales the color towards the minimum saturation by the provided factor, which
should be in the range `0.0` through `1.0`.

View File

@ -0,0 +1,7 @@
## `color:desaturate_fixed(amount)`
*Since: nightly builds only*
Decrease the saturation by amount, a value ranging from `0.0` to `1.0`.

View File

@ -0,0 +1,12 @@
## `color:hsla()`
*Since: nightly builds only*
Converts the color to the HSL colorspace and returns those values +
alpha:
```lua
local h, s, l, a = color:hsla()
```

View File

@ -0,0 +1,16 @@
# `Color` object
Color objects can be created by calling
[wezterm.color.parse()](../wezterm.color/parse.md) and may also be
returned by various wezterm functions and methods.
They represent a color that is internally stored in SRGBA.
Color objects have a number of methods that are helpful to
compare and compute other color values, which is helpful
when programmatically generating color schemes.
## Available methods

View File

@ -0,0 +1,12 @@
### `color:laba()`
*Since: nightly builds only*
Converts the color to the LAB colorspace and returns those values +
alpha:
```lua
local l, a, b, alpha = color:laba()
```

View File

@ -0,0 +1,8 @@
## `color:lighten(factor)`
*Since: nightly builds only*
Scales the color towards the maximum lightness by the provided
factor, which should be in the range `0.0` through `1.0`.

View File

@ -0,0 +1,7 @@
## `color:lighten_fixed(amount)`
*Since: nightly builds only*
Increase the lightness by amount, a value ranging from `0.0` to `1.0`.

View File

@ -0,0 +1,13 @@
## `color:linear_rgba()`
*Since: nightly builds only*
Returns a tuple of the colors converted to linear RGBA and
expressed as floating point numbers in the range `0.0-1.0`:
```lua
> r, g, b, a = wezterm.color.parse("purple"):linear_rgba()
> print(r, g, b, a)
07:32:17.734 INFO logging > lua: 0.2158605307340622 0 0.2158605307340622 1
```

View File

@ -0,0 +1,8 @@
## `color:saturate(factor)`
*Since: nightly builds only*
Scales the color towards the maximum saturation by the provided factor, which
should be in the range `0.0` through `1.0`.

View File

@ -0,0 +1,7 @@
## `color:saturate_fixed(amount)`
*Since: nightly builds only*
Increase the saturation by amount, a value ranging from `0.0` to `1.0`.

View File

@ -0,0 +1,12 @@
## `color:square()`
*Since: nightly builds only*
Returns the other three colors that form a square. The other colors
are 90 degrees apart on the HSL color wheel.
```lua
local a, b, c = wezterm:color.parse("yellow"):square()
```

View File

@ -0,0 +1,12 @@
## `color:srgba_u8()`
*Since: nightly builds only*
Returns a tuple of the internal SRGBA colors expressed
as unsigned 8-bit integers in the range 0-255:
```lua
> r, g, b, a = wezterm.color.parse("purple"):srgba_u8()
> print(r, g, b, a)
07:30:20.045 INFO logging > lua: 128 0 128 255
```

View File

@ -0,0 +1,12 @@
## `color:triad()`
*Since: nightly builds only*
Returns the other two colors that form a triad. The other colors
are at +/- 120 degrees in the HSL color wheel.
```lua
local a, b = wezterm:color.parse("yellow"):triad()
```

View File

@ -0,0 +1,25 @@
# `wezterm.color.gradient(gradient, num_colors)`
*Since: nightly builds only*
Given a gradient spec and a number of colors, returns a table
holding that many colors spaced evenly across the range of
the gradient.
Each element in the returned array is a [Color
object](../color/index.md).
This is useful for example to generate colors for tabs, or
to do something fancy like interpolate colors across a gradient
based on the time of the day.
`gradient` is any gradient allowed by the
[window_background_gradient](../config/window_background_gradient.md) option.
This example is what you'd see if you opened up the [debug overlay](../keyassignment/ShowDebugOverlay.md) to try this out in the repl:
```lua
> wezterm.color.gradient({preset="Rainbow"}, 4)
["#6e40aa", "#ff8c38", "#5dea8d", "#6e40aa"]
```

View File

@ -2,23 +2,25 @@
*Since: nightly builds only*
Parses the passed color and returns an `RgbaColor` object.
`RgbaColor` objects evaluate as strings but have a number of methods
that allow transforming colors.
Parses the passed color and returns a [Color
object](../color/index.md). `Color` objects evaluate as strings but
have a number of methods that allow transforming and comparing
colors.
```
> wezterm.color.parse("black")
#000000
```
This example picks a foreground color, computes its complement
and darkens it to use it as a background color:
This example picks a foreground color, computes its complement in
the "artist's color wheel" to produce a purple color and then
darkens it to use it as a background color:
```lua
local wezterm = require 'wezterm'
local fg = wezterm.color.parse("yellow")
local bg = fg:complement():darken(0.2)
local bg = fg:complement_ryb():darken(0.2)
return {
colors = {
@ -28,177 +30,3 @@ return {
}
```
## `color:complement()`
*Since: nightly builds only*
Returns the complement of the color. The complement is computed
by converting to HSL, rotating by 180 degrees and converting back
to RGBA.
## `color:complement_ryb()`
*Since: nightly builds only*
Returns the complement of the color using the [RYB color
model](https://en.wikipedia.org/wiki/RYB_color_model), which more closely
matches how artists think of mixing colors.
The complement is computed by converting to HSL, converting the
hue angle to the equivalent RYB angle, rotating by 180 degrees and
and then converting back to RGBA.
## `color:triad()`
*Since: nightly builds only*
Returns the other two colors that form a triad. The other colors
are at +/- 120 degrees in the HSL color wheel.
```lua
local a, b = wezterm:color.parse("yellow"):triad()
```
## `color:square()`
*Since: nightly builds only*
Returns the other three colors that form a square. The other colors
are 90 degrees apart on the HSL color wheel.
```lua
local a, b, c = wezterm:color.parse("yellow"):square()
```
## `color:saturate(factor)`
*Since: nightly builds only*
Scales the color towards the maximum saturation by the provided factor, which
should be in the range `0.0` through `1.0`.
## `color:saturate_fixed(amount)`
*Since: nightly builds only*
Increase the saturation by amount, a value ranging from `0.0` to `1.0`.
## `color:desaturate(factor)`
*Since: nightly builds only*
Scales the color towards the minimum saturation by the provided factor, which
should be in the range `0.0` through `1.0`.
## `color:desaturate_fixed(amount)`
*Since: nightly builds only*
Decrease the saturation by amount, a value ranging from `0.0` to `1.0`.
## `color:lighten(factor)`
*Since: nightly builds only*
Scales the color towards the maximum lightness by the provided factor, which
should be in the range `0.0` through `1.0`.
## `color:lighten_fixed(amount)`
*Since: nightly builds only*
Increase the lightness by amount, a value ranging from `0.0` to `1.0`.
## `color:darken(factor)`
*Since: nightly builds only*
Scales the color towards the minimum lightness by the provided factor, which
should be in the range `0.0` through `1.0`.
## `color:darken_fixed(amount)`
*Since: nightly builds only*
Decrease the lightness by amount, a value ranging from `0.0` to `1.0`.
## `color:adjust_hue_fixed(degrees)`
*Since: nightly builds only*
Adjust the hue angle by the specified number of degrees.
180 degrees gives the complementary color.
Three colors separated by 120 degrees form the triad.
Four colors separated by 90 degrees form the square.
## `color:adjust_hue_fixed_ryb(degrees)`
*Since: nightly builds only*
Adjust the hue angle using the [RYB color model](https://en.wikipedia.org/wiki/RYB_color_model), which more closely
matches how artists think of mixing colors, by the specified number of degrees.
180 degrees gives the complementary color.
Three colors separated by 120 degrees form the triad.
Four colors separated by 90 degrees form the square.
## `color:hsla()`
*Since: nightly builds only*
Converts the color to the HSL colorspace and returns those values + alpha:
```lua
local h, s, l, a = color:hsla()
```
### `color:laba()`
*Since: nightly builds only*
Converts the color to the LAB colorspace and returns those values + alpha:
```lua
local l, a, b, alpha = color:laba()
```
### `color:contrast_ratio(color)`
*Since: nightly builds only*
Computes the contrast ratio between the two colors.
```lua
> wezterm.color.parse("red"):contrast_ratio(wezterm.color.parse("yellow"))
1
> wezterm.color.parse("red"):contrast_ratio(wezterm.color.parse("navy"))
1.8273614734023298
```
The contrast ratio is computed by first converting to HSL, taking the L
components, and diving the lighter one by the darker one.
A contrast ratio of 1 means no contrast.
The maximum possible contrast ratio is 21:
```lua
> wezterm.color.parse("black"):contrast_ratio(wezterm.color.parse("white"))
21
```
### `color:delta_e(color)`
*Since: nightly builds only*
Computes the CIEDE2000 DeltaE value for the two colors.
A value:
* <= 1.0: Not perceptible by the human eye
* 1-2: Perceptible through close observation
* 2-10: Perceptible at a glance
* 11-49: Colors are more similar than the opposite
* 100: Colors are exactly the opposite

View File

@ -19,3 +19,12 @@ This example is what you'd see if you opened up the [debug overlay](../keyassign
> wezterm.gradient_colors({preset="Rainbow"}, 4)
["#6e40aa", "#ff8c38", "#5dea8d", "#6e40aa"]
```
*Since: nightly builds only*
This function has moved to
[wezterm.color.gradient](../wezterm.color/gradient.md) and that name
should be used instead of this name.
In addition, the returned colors are now [Color
objects](../color/index.md).

View File

@ -1,6 +1,6 @@
use config::lua::get_or_create_sub_module;
use config::lua::mlua::{self, Lua, MetaMethod, UserData, UserDataMethods};
use config::{RgbaColor, SrgbaTuple};
use config::lua::{get_or_create_module, get_or_create_sub_module};
use config::{Gradient, RgbaColor, SrgbaTuple};
mod image_colors;
@ -85,6 +85,11 @@ impl UserData for ColorWrap {
methods.add_method("adjust_hue_fixed_ryb", |_, this, amount: f64| {
Ok(this.adjust_hue_fixed_ryb(amount))
});
methods.add_method("srgba_u8", |_, this, _: ()| Ok(this.0.to_srgb_u8()));
methods.add_method("linear_rgba", |_, this, _: ()| {
let rgba = this.0.to_linear();
Ok((rgba.0, rgba.1, rgba.2, rgba.3))
});
methods.add_method("hsla", |_, this, _: ()| Ok(this.0.to_hsla()));
methods.add_method("laba", |_, this, _: ()| Ok(this.0.to_laba()));
methods.add_method("contrast_ratio", |_, this, other: ColorWrap| {
@ -109,6 +114,10 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> {
"extract_colors_from_image",
lua.create_function(image_colors::extract_colors_from_image)?,
)?;
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set("gradient_colors", lua.create_function(gradient_colors)?)?;
color.set("gradient", lua.create_function(gradient_colors)?)?;
Ok(())
}
@ -117,3 +126,17 @@ fn parse_color<'lua>(_: &'lua Lua, spec: String) -> mlua::Result<ColorWrap> {
RgbaColor::try_from(spec).map_err(|err| mlua::Error::external(format!("{err:#}")))?;
Ok(ColorWrap(color))
}
fn gradient_colors<'lua>(
_lua: &'lua Lua,
(gradient, num_colors): (Gradient, usize),
) -> mlua::Result<Vec<ColorWrap>> {
let g = gradient.build().map_err(|e| mlua::Error::external(e))?;
Ok(g.colors(num_colors)
.into_iter()
.map(|c| {
let tuple = SrgbaTuple::from(c);
ColorWrap(tuple.into())
})
.collect())
}