From 4cdcac1b16da71692802cf72c1b2abab41f5e58e Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 17 Jan 2024 11:39:09 -0500 Subject: [PATCH] Update docs --- crates/color/src/color.rs | 93 ++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/crates/color/src/color.rs b/crates/color/src/color.rs index d3d832099a..8529f3bc5f 100644 --- a/crates/color/src/color.rs +++ b/crates/color/src/color.rs @@ -4,25 +4,49 @@ //! //! It is used to create a manipulate colors when building themes. //! -//! **Note:** This crate does not depend on `gpui`, so it does not provide any -//! interfaces for converting to `gpui` style colors. - +//! === In development note === +//! +//! This crate is meant to sit between gpui and the theme/ui for all the color related stuff. +//! +//! It could be folded into gpui, ui or theme potentially but for now we'll continue +//! to develop it in isolation. +//! +//! Once we have a good idea of the needs of the theme system and color in gpui in general I see 3 paths: +//! 1. Use `palette` (or another color library) directly in gpui and everywhere else, rather than rolling our own color system. +//! 2. Keep this crate as a thin wrapper around `palette` and use it everywhere except gpui, and convert to gpui's color system when needed. +//! 3. Build the needed functionality into gpui and keep using it's color system everywhere. +//! +//! I'm leaning towards 2 in the short term and 1 in the long term, but we'll need to discuss it more. +//! +//! === End development note === use palette::{ blend::Blend, convert::FromColorUnclamped, encoding, rgb::Rgb, Clamp, Mix, Srgb, WithAlpha, }; +/// The types of blend modes supported #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum BlendMode { + /// Multiplies the colors, resulting in a darker color. This mode is useful for creating shadows. Multiply, + /// Lightens the color by adding the source and destination colors. It results in a lighter color. Screen, + /// Combines Multiply and Screen blend modes. Parts of the image that are lighter than 50% gray are lightened, and parts that are darker are darkened. Overlay, + /// Selects the darker of the base or blend color as the resulting color. Useful for darkening images without affecting the overall contrast. Darken, + /// Selects the lighter of the base or blend color as the resulting color. Useful for lightening images without affecting the overall contrast. Lighten, + /// Brightens the base color to reflect the blend color. The result is a lightened image. Dodge, + /// Darkens the base color to reflect the blend color. The result is a darkened image. Burn, + /// Similar to Overlay, but with a stronger effect. Hard Light can either multiply or screen colors, depending on the blend color. HardLight, + /// A softer version of Hard Light. Soft Light either darkens or lightens colors, depending on the blend color. SoftLight, + /// Subtracts the darker of the two constituent colors from the lighter color. Difference mode is useful for creating more vivid colors. Difference, + /// Similar to Difference, but with a lower contrast. Exclusion mode produces an effect similar to Difference but with less intensity. Exclusion, } @@ -30,7 +54,7 @@ pub enum BlendMode { /// /// This function supports the following hex formats: /// `#RGB`, `#RGBA`, `#RRGGBB`, `#RRGGBBAA`. -pub fn hex_to_hsla(s: &str) -> Result { +pub fn hex_to_hsla(s: &str) -> Result { let hex = s.trim_start_matches('#'); // Expand shorthand formats #RGB and #RGBA to #RRGGBB and #RRGGBBAA @@ -61,16 +85,15 @@ pub fn hex_to_hsla(s: &str) -> Result { let b = ((hex_val >> 8) & 0xFF) as f32 / 255.0; let a = (hex_val & 0xFF) as f32 / 255.0; - let color = Color { r, g, b, a }; + let color = RGBAColor { r, g, b, a }; Ok(color) } -// This implements conversion to and from all Palette colors. +// These derives implement to and from palette's color types. #[derive(FromColorUnclamped, WithAlpha, Debug, Clone)] -// We have to tell Palette that we will take care of converting to/from sRGB. #[palette(skip_derives(Rgb), rgb_standard = "encoding::Srgb")] -pub struct Color { +pub struct RGBAColor { r: f32, g: f32, b: f32, @@ -79,22 +102,19 @@ pub struct Color { a: f32, } -// There's no blanket implementation for Self -> Self, unlike the From trait. -// This is to better allow cases like Self -> Self. -impl FromColorUnclamped for Color { - fn from_color_unclamped(color: Color) -> Color { +impl FromColorUnclamped for RGBAColor { + fn from_color_unclamped(color: RGBAColor) -> RGBAColor { color } } -// Convert from any kind of f32 sRGB. -impl FromColorUnclamped> for Color +impl FromColorUnclamped> for RGBAColor where Srgb: FromColorUnclamped>, { - fn from_color_unclamped(color: Rgb) -> Color { + fn from_color_unclamped(color: Rgb) -> RGBAColor { let srgb = Srgb::from_color_unclamped(color); - Color { + RGBAColor { r: srgb.red, g: srgb.green, b: srgb.blue, @@ -103,21 +123,19 @@ where } } -// Convert into any kind of f32 sRGB. -impl FromColorUnclamped for Rgb +impl FromColorUnclamped for Rgb where Rgb: FromColorUnclamped, { - fn from_color_unclamped(color: Color) -> Self { + fn from_color_unclamped(color: RGBAColor) -> Self { let srgb = Srgb::new(color.r, color.g, color.b); Self::from_color_unclamped(srgb) } } -// Add the required clamping. -impl Clamp for Color { +impl Clamp for RGBAColor { fn clamp(self) -> Self { - Color { + RGBAColor { r: self.r.min(1.0).max(0.0), g: self.g.min(1.0).max(0.0), b: self.b.min(1.0).max(0.0), @@ -126,9 +144,12 @@ impl Clamp for Color { } } -impl Color { +impl RGBAColor { + /// Creates a new color from the given RGBA values. + /// + /// This color can be used to convert to any [`palette::Color`] type. pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { - Color { r, g, b, a } + RGBAColor { r, g, b, a } } /// Returns a set of states for this color. @@ -137,16 +158,16 @@ impl Color { } /// Mixes this color with another [`palette::Hsl`] color at the given `mix_ratio`. - pub fn mixed(&self, other: Color, mix_ratio: f32) -> Self { + pub fn mixed(&self, other: RGBAColor, mix_ratio: f32) -> Self { let srgb_self = Srgb::new(self.r, self.g, self.b); let srgb_other = Srgb::new(other.r, other.g, other.b); // Directly mix the colors as sRGB values let mixed = srgb_self.mix(srgb_other, mix_ratio); - Color::from_color_unclamped(mixed) + RGBAColor::from_color_unclamped(mixed) } - pub fn blend(&self, other: Color, blend_mode: BlendMode) -> Self { + pub fn blend(&self, other: RGBAColor, blend_mode: BlendMode) -> Self { let srgb_self = Srgb::new(self.r, self.g, self.b); let srgb_other = Srgb::new(other.r, other.g, other.b); @@ -169,31 +190,31 @@ impl Color { #[derive(Debug, Clone)] pub struct ColorStates { /// The default color. - pub default: Color, + pub default: RGBAColor, /// The color when the mouse is hovering over the element. - pub hover: Color, + pub hover: RGBAColor, /// The color when the mouse button is held down on the element. - pub active: Color, + pub active: RGBAColor, /// The color when the element is focused with the keyboard. - pub focused: Color, + pub focused: RGBAColor, /// The color when the element is disabled. - pub disabled: Color, + pub disabled: RGBAColor, } /// Returns a set of colors for different states of an element. /// -/// todo!("Test and improve this function") -pub fn states_for_color(color: Color, is_light: bool) -> ColorStates { +/// todo!("This should take a theme and use appropriate colors from it") +pub fn states_for_color(color: RGBAColor, is_light: bool) -> ColorStates { let adjustment_factor = if is_light { 0.1 } else { -0.1 }; let hover_adjustment = 1.0 - adjustment_factor; let active_adjustment = 1.0 - 2.0 * adjustment_factor; let focused_adjustment = 1.0 - 3.0 * adjustment_factor; let disabled_adjustment = 1.0 - 4.0 * adjustment_factor; - let make_adjustment = |color: Color, adjustment: f32| -> Color { + let make_adjustment = |color: RGBAColor, adjustment: f32| -> RGBAColor { // Adjust lightness for each state // Note: Adjustment logic may differ; simplify as needed for sRGB - Color::new( + RGBAColor::new( color.r * adjustment, color.g * adjustment, color.b * adjustment,