Start work on defining color scales

Marshall Bowers created

Change summary

crates/theme2/src/default.rs | 101 +++++++++++++++++++++++
crates/theme2/src/scale.rs   | 166 ++++++++++++++++++++++++++++++++++++++
crates/theme2/src/theme2.rs  |   8 +
3 files changed, 275 insertions(+)

Detailed changes

crates/theme2/src/default.rs 🔗

@@ -0,0 +1,101 @@
+use std::collections::HashMap;
+
+use gpui2::Rgba;
+
+use crate::scale::{ColorScaleName, ColorScaleSet, ColorScales};
+
+struct DefaultColorScaleSet {
+    scale: ColorScaleName,
+    light: [&'static str; 12],
+    light_alpha: [&'static str; 12],
+    dark: [&'static str; 12],
+    dark_alpha: [&'static str; 12],
+}
+
+impl From<DefaultColorScaleSet> for ColorScaleSet {
+    fn from(default: DefaultColorScaleSet) -> Self {
+        Self::new(
+            default.scale,
+            default
+                .light
+                .map(|color| Rgba::try_from(color).unwrap().into()),
+            default
+                .light_alpha
+                .map(|color| Rgba::try_from(color).unwrap().into()),
+            default
+                .dark
+                .map(|color| Rgba::try_from(color).unwrap().into()),
+            default
+                .dark_alpha
+                .map(|color| Rgba::try_from(color).unwrap().into()),
+        )
+    }
+}
+
+pub fn default_color_scales() -> ColorScales {
+    use ColorScaleName::*;
+
+    HashMap::from_iter([(Red, red().into())])
+}
+
+fn red() -> DefaultColorScaleSet {
+    DefaultColorScaleSet {
+        scale: ColorScaleName::Red,
+        light: [
+            "#fffcfc00",
+            "#fff7f700",
+            "#feebec00",
+            "#ffdbdc00",
+            "#ffcdce00",
+            "#fdbdbe00",
+            "#f4a9aa00",
+            "#eb8e9000",
+            "#e5484d00",
+            "#dc3e4200",
+            "#ce2c3100",
+            "#64172300",
+        ],
+        light_alpha: [
+            "#ff000003",
+            "#ff000008",
+            "#f3000d14",
+            "#ff000824",
+            "#ff000632",
+            "#f8000442",
+            "#df000356",
+            "#d2000571",
+            "#db0007b7",
+            "#d10005c1",
+            "#c40006d3",
+            "#55000de8",
+        ],
+        dark: [
+            "#19111100",
+            "#20131400",
+            "#3b121900",
+            "#500f1c00",
+            "#61162300",
+            "#72232d00",
+            "#8c333a00",
+            "#b5454800",
+            "#e5484d00",
+            "#ec5d5e00",
+            "#ff959200",
+            "#ffd1d900",
+        ],
+        dark_alpha: [
+            "#f4121209",
+            "#f22f3e11",
+            "#ff173f2d",
+            "#fe0a3b44",
+            "#ff204756",
+            "#ff3e5668",
+            "#ff536184",
+            "#ff5d61b0",
+            "#fe4e54e4",
+            "#ff6465eb",
+            "#ff959200",
+            "#ffd1d900",
+        ],
+    }
+}

crates/theme2/src/scale.rs 🔗

@@ -0,0 +1,166 @@
+use std::collections::HashMap;
+
+use gpui2::{AppContext, Hsla};
+
+use crate::{theme, Appearance};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum ColorScaleName {
+    Gray,
+    Mauve,
+    Slate,
+    Sage,
+    Olive,
+    Sand,
+    Gold,
+    Bronze,
+    Brown,
+    Yellow,
+    Amber,
+    Orange,
+    Tomato,
+    Red,
+    Ruby,
+    Crimson,
+    Pink,
+    Plum,
+    Purple,
+    Violet,
+    Iris,
+    Indigo,
+    Blue,
+    Cyan,
+    Teal,
+    Jade,
+    Green,
+    Grass,
+    Lime,
+    Mint,
+    Sky,
+    Black,
+    White,
+}
+
+impl std::fmt::Display for ColorScaleName {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{}",
+            match self {
+                Self::Gray => "Gray",
+                Self::Mauve => "Mauve",
+                Self::Slate => "Slate",
+                Self::Sage => "Sage",
+                Self::Olive => "Olive",
+                Self::Sand => "Sand",
+                Self::Gold => "Gold",
+                Self::Bronze => "Bronze",
+                Self::Brown => "Brown",
+                Self::Yellow => "Yellow",
+                Self::Amber => "Amber",
+                Self::Orange => "Orange",
+                Self::Tomato => "Tomato",
+                Self::Red => "Red",
+                Self::Ruby => "Ruby",
+                Self::Crimson => "Crimson",
+                Self::Pink => "Pink",
+                Self::Plum => "Plum",
+                Self::Purple => "Purple",
+                Self::Violet => "Violet",
+                Self::Iris => "Iris",
+                Self::Indigo => "Indigo",
+                Self::Blue => "Blue",
+                Self::Cyan => "Cyan",
+                Self::Teal => "Teal",
+                Self::Jade => "Jade",
+                Self::Green => "Green",
+                Self::Grass => "Grass",
+                Self::Lime => "Lime",
+                Self::Mint => "Mint",
+                Self::Sky => "Sky",
+                Self::Black => "Black",
+                Self::White => "White",
+            }
+        )
+    }
+}
+
+pub type ColorScale = [Hsla; 12];
+
+pub type ColorScales = HashMap<ColorScaleName, ColorScaleSet>;
+
+pub struct ColorScaleSet {
+    name: ColorScaleName,
+    light: ColorScale,
+    dark: ColorScale,
+    light_alpha: ColorScale,
+    dark_alpha: ColorScale,
+}
+
+impl ColorScaleSet {
+    pub fn new(
+        name: ColorScaleName,
+        light: ColorScale,
+        light_alpha: ColorScale,
+        dark: ColorScale,
+        dark_alpha: ColorScale,
+    ) -> Self {
+        Self {
+            name,
+            light,
+            light_alpha,
+            dark,
+            dark_alpha,
+        }
+    }
+
+    pub fn name(&self) -> String {
+        self.name.to_string()
+    }
+
+    pub fn light(&self, step: usize) -> Hsla {
+        self.light[step - 1]
+    }
+
+    pub fn light_alpha(&self, step: usize) -> Hsla {
+        self.light_alpha[step - 1]
+    }
+
+    pub fn dark(&self, step: usize) -> Hsla {
+        self.dark[step - 1]
+    }
+
+    pub fn dark_alpha(&self, step: usize) -> Hsla {
+        self.dark[step - 1]
+    }
+
+    fn current_appearance(cx: &AppContext) -> Appearance {
+        let theme = theme(cx);
+        if theme.metadata.is_light {
+            Appearance::Light
+        } else {
+            Appearance::Dark
+        }
+    }
+
+    /// Returns the one-based step in the scale.
+    ///
+    /// We usually reference steps as 1-12 instead of 0-11, so we
+    /// automatically subtract 1 from the index.
+    pub fn step(self, cx: &AppContext, index: usize) -> Hsla {
+        let appearance = Self::current_appearance(cx);
+
+        match appearance {
+            Appearance::Light => self.light(index),
+            Appearance::Dark => self.dark(index),
+        }
+    }
+
+    pub fn step_alpha(self, cx: &AppContext, index: usize) -> Hsla {
+        let appearance = Self::current_appearance(cx);
+        match appearance {
+            Appearance::Light => self.light_alpha(index),
+            Appearance::Dark => self.dark_alpha(index),
+        }
+    }
+}

crates/theme2/src/theme2.rs 🔗

@@ -1,4 +1,6 @@
+mod default;
 mod registry;
+mod scale;
 mod settings;
 mod themes;
 
@@ -9,6 +11,12 @@ use gpui2::{AppContext, HighlightStyle, Hsla, SharedString};
 use settings2::Settings;
 use std::sync::Arc;
 
+#[derive(Debug, Clone, PartialEq)]
+pub enum Appearance {
+    Light,
+    Dark,
+}
+
 pub fn init(cx: &mut AppContext) {
     cx.set_global(ThemeRegistry::default());
     ThemeSettings::register(cx);