Skip to content

Commit

Permalink
Refactor design tokens to use a proper color table (#8322)
Browse files Browse the repository at this point in the history
### Related

* Part of/related to #3133
* Part of #3058

### What

This PR refactors the design token as follows:

- All colors from `design_tokens.json` are now loaded in a big table at
startup.
- Colors are now referred to using the new the `ColorToken`, which is
basically an index into this table.
- Removed all of the color aliases stuff. This was only partially used
and very cumbersome to update. The `design_token.rs` file is the _de
facto_ source of truth of semantic aliasing for colors.
  • Loading branch information
abey79 authored Dec 6, 2024
1 parent f8bd31d commit c2304a0
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 146 deletions.
8 changes: 3 additions & 5 deletions crates/viewer/re_ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,20 @@ re_log.workspace = true
re_log_types.workspace = true # syntax-highlighting for EntityPath
re_tracing.workspace = true

eframe = { workspace = true, default-features = false, features = ["wgpu"] }
egui.workspace = true
egui_commonmark = { workspace = true, features = ["pulldown_cmark"] }
egui_extras.workspace = true
egui_tiles.workspace = true
once_cell.workspace = true
parking_lot.workspace = true
rand.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
strum.workspace = true
strum_macros.workspace = true
sublime_fuzzy.workspace = true


eframe = { workspace = true, default-features = false, features = ["wgpu"] }
egui_tiles.workspace = true
rand.workspace = true


[dev-dependencies]
egui_kittest.workspace = true
100 changes: 2 additions & 98 deletions crates/viewer/re_ui/data/design_tokens.json
Original file line number Diff line number Diff line change
@@ -1,101 +1,5 @@
{
"Alias": {
"Color": {
"Surface": {
"Default": {
"description": "Background color for most UI surfaces in Rerun",
"value": "{Global.Color.Grey.100}",
"type": "color"
},
"Floating": {
"description": "Background color for floating elements like menus, dropdown options, notifications etc.",
"value": "{Global.Color.Grey.175}",
"type": "color"
}
},
"Action": {
"Default": {
"description": "Background color for UI elements like buttons and selects",
"value": "{Global.Color.Grey.200}",
"type": "color"
},
"Hovered": {
"description": "Background color for hovered UI elements",
"value": "{Global.Color.Grey.225}",
"type": "color"
},
"Active": {
"description": "Background color for pressed UI elements",
"value": "{Global.Color.Grey.250}",
"type": "color"
},
"Pressed": {
"description": "Background color for suppressed UI elements, like a select that is currently showing a menu",
"value": "{Global.Color.Grey.250}",
"type": "color"
}
},
"NotificationBadge": {
"Unread": {
"description": "Used for unread notification indicators",
"value": "{Global.Color.Blue.500}",
"type": "color"
},
"Read": {
"description": "Used for read notification indicators",
"value": "{Global.Color.Grey.250}",
"type": "color"
}
},
"Text": {
"Default": {
"description": "Default text color",
"value": "{Global.Color.Grey.775}",
"type": "color"
},
"Subdued": {
"description": "Used for less important text",
"value": "{Global.Color.Grey.550}",
"type": "color"
},
"Strong": {
"description": "Used for highlighted or emphasized items, such as current navigation items",
"value": "{Global.Color.Grey.1000}",
"type": "color"
}
},
"Border": {
"Default": {
"value": "{Global.Color.OpaqueGrey.Default}",
"description": "Default color for borders",
"type": "color"
}
},
"Icon": {
"Default": {
"description": "Default icon color",
"value": "{Global.Color.Grey.775}",
"type": "color"
},
"Subdued": {
"description": "Used together with subdued text",
"value": "{Global.Color.Grey.550}",
"type": "color"
},
"Strong": {
"description": "Used together width strong text",
"value": "{Global.Color.Grey.1000}",
"type": "color"
}
},
"Highlight": {
"Default": {
"value": "{Global.Color.Blue.350}",
"description": "Default color for highlighted items, like hovered menu items",
"type": "color"
}
}
},
"Typography": {
"Default": {
"value": "{Global.Typography.200}",
Expand Down Expand Up @@ -123,7 +27,7 @@
},
"Global": {
"Color": {
"Grey": {
"Gray": {
"0": {
"value": "#000000",
"description": "0 - 0",
Expand Down Expand Up @@ -1158,7 +1062,7 @@
"type": "color"
}
},
"OpaqueGrey": {
"OpaqueGray": {
"Default": {
"value": "#7c7c7c20",
"description": "An opaque grey that picks up some, but not all, of the colors behind it",
Expand Down
219 changes: 219 additions & 0 deletions crates/viewer/re_ui/src/color_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use std::fmt::{Display, Formatter};

use strum::{EnumCount, EnumIter, IntoEnumIterator};

/// A hue for a [`ColorToken`].
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, EnumIter, EnumCount)]
pub enum Hue {
Gray,
Green,
Red,
Blue,
Purple,
}

impl Display for Hue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
// these must be as they appear in `design_token.json`
Self::Gray => f.write_str("Gray"),
Self::Green => f.write_str("Green"),
Self::Red => f.write_str("Red"),
Self::Blue => f.write_str("Blue"),
Self::Purple => f.write_str("Purple"),
}
}
}

/// A color scale for a [`ColorToken`].
///
/// A scale is an arbitrary… well… scale of subjective color "intensity". Both brightness and
/// saturation may vary along the scale. For a dark mode theme, low scales are typically darker and
/// used for backgrounds, whereas high scales are typically brighter and used for text and
/// interactive UI elements.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, EnumIter, EnumCount)]
pub enum Scale {
S0,
S25,
S50,
S75,
S100,
S125,
S150,
S175,
S200,
S225,
S250,
S275,
S300,
S325,
S350,
S375,
S400,
S425,
S450,
S475,
S500,
S525,
S550,
S575,
S600,
S625,
S650,
S675,
S700,
S725,
S750,
S775,
S800,
S825,
S850,
S875,
S900,
S925,
S950,
S975,
S1000,
}

impl Display for Scale {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let txt = match self {
Self::S0 => "0",
Self::S25 => "25",
Self::S50 => "50",
Self::S75 => "75",
Self::S100 => "100",
Self::S125 => "125",
Self::S150 => "150",
Self::S175 => "175",
Self::S200 => "200",
Self::S225 => "225",
Self::S250 => "250",
Self::S275 => "275",
Self::S300 => "300",
Self::S325 => "325",
Self::S350 => "350",
Self::S375 => "375",
Self::S400 => "400",
Self::S425 => "425",
Self::S450 => "450",
Self::S475 => "475",
Self::S500 => "500",
Self::S525 => "525",
Self::S550 => "550",
Self::S575 => "575",
Self::S600 => "600",
Self::S625 => "625",
Self::S650 => "650",
Self::S675 => "675",
Self::S700 => "700",
Self::S725 => "725",
Self::S750 => "750",
Self::S775 => "775",
Self::S800 => "800",
Self::S825 => "825",
Self::S850 => "850",
Self::S875 => "875",
Self::S900 => "900",
Self::S925 => "925",
Self::S950 => "950",
Self::S975 => "975",
Self::S1000 => "1000",
};

txt.fmt(f)
}
}

/// A table mapping all combination of [`Hue`] and [`Scale`] to a [`egui::Color32`].
#[derive(Debug)]
pub struct ColorTable {
color_lut: Vec<Vec<egui::Color32>>,
}

impl ColorTable {
/// Build a new color table by calling the provided closure with all possible entries.
pub fn new(mut color_lut_fn: impl FnMut(ColorToken) -> egui::Color32) -> Self {
Self {
color_lut: Hue::iter()
.map(|hue| {
Scale::iter()
.map(|scale| color_lut_fn(ColorToken::new(hue, scale)))
.collect()
})
.collect(),
}
}

#[inline]
pub fn get(&self, token: ColorToken) -> egui::Color32 {
self.color_lut[token.hue as usize][token.scale as usize]
}

#[inline]
pub fn gray(&self, shade: Scale) -> egui::Color32 {
self.get(ColorToken::gray(shade))
}

#[inline]
pub fn green(&self, shade: Scale) -> egui::Color32 {
self.get(ColorToken::green(shade))
}

#[inline]
pub fn red(&self, shade: Scale) -> egui::Color32 {
self.get(ColorToken::red(shade))
}

#[inline]
pub fn blue(&self, shade: Scale) -> egui::Color32 {
self.get(ColorToken::blue(shade))
}

#[inline]
pub fn purple(&self, shade: Scale) -> egui::Color32 {
self.get(ColorToken::purple(shade))
}
}

/// A token representing a color in the global color table.
///
/// Use [`crate::DesignTokens::color`] to get the color corresponding to a token.
#[derive(Debug, Clone, Copy, Hash)]
pub struct ColorToken {
pub hue: Hue,
pub scale: Scale,
}

impl ColorToken {
#[inline]
pub fn new(hue: Hue, shade: Scale) -> Self {
Self { hue, scale: shade }
}

#[inline]
pub fn gray(shade: Scale) -> Self {
Self::new(Hue::Gray, shade)
}

#[inline]
pub fn green(shade: Scale) -> Self {
Self::new(Hue::Green, shade)
}

#[inline]
pub fn red(shade: Scale) -> Self {
Self::new(Hue::Red, shade)
}

#[inline]
pub fn blue(shade: Scale) -> Self {
Self::new(Hue::Blue, shade)
}

#[inline]
pub fn purple(shade: Scale) -> Self {
Self::new(Hue::Purple, shade)
}
}
Loading

0 comments on commit c2304a0

Please sign in to comment.