Use custom color wrapper type everywhere in gpui & zed

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld
2021-08-03 12:48:58 -07:00
co-authored by Nathan Sobo
parent fa01273466
commit ef0ffbe819
20 changed files with 319 additions and 233 deletions
+72 -3
View File
@@ -1,9 +1,78 @@
use std::{
fmt,
ops::{Deref, DerefMut},
};
use crate::json::ToJson;
pub use pathfinder_color::*;
use pathfinder_color::ColorU;
use serde::{Deserialize, Deserializer};
use serde_json::json;
impl ToJson for ColorU {
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Color(ColorU);
impl Color {
pub fn transparent_black() -> Self {
Self(ColorU::transparent_black())
}
pub fn black() -> Self {
Self(ColorU::black())
}
pub fn white() -> Self {
Self(ColorU::white())
}
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self(ColorU::new(r, g, b, a))
}
pub fn from_u32(rgba: u32) -> Self {
Self(ColorU::from_u32(rgba))
}
}
impl<'de> Deserialize<'de> for Color {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut rgba = u32::deserialize(deserializer)?;
if rgba <= 0xFFFFFF {
rgba = (rgba << 8) + 0xFF;
}
Ok(Self::from_u32(rgba))
}
}
impl ToJson for Color {
fn to_json(&self) -> serde_json::Value {
json!(format!("0x{:x}{:x}{:x}", self.r, self.g, self.b))
json!(format!(
"0x{:x}{:x}{:x}{:x}",
self.0.r, self.0.g, self.0.b, self.0.a
))
}
}
impl Deref for Color {
type Target = ColorU;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Color {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl fmt::Debug for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
+17 -12
View File
@@ -1,20 +1,24 @@
use pathfinder_geometry::rect::RectF;
use serde::Deserialize;
use serde_json::json;
use crate::{
color::ColorU,
geometry::vector::{vec2f, Vector2F},
color::Color,
geometry::{
deserialize_vec2f,
vector::{vec2f, Vector2F},
},
json::ToJson,
scene::{self, Border, Quad},
AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
};
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Deserialize)]
pub struct ContainerStyle {
margin: Margin,
padding: Padding,
background_color: Option<ColorU>,
background_color: Option<Color>,
border: Border,
corner_radius: f32,
shadow: Option<Shadow>,
@@ -80,8 +84,8 @@ impl Container {
self
}
pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
self.style.background_color = Some(color.into());
pub fn with_background_color(mut self, color: Color) -> Self {
self.style.background_color = Some(color);
self
}
@@ -95,11 +99,11 @@ impl Container {
self
}
pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
self.style.shadow = Some(Shadow {
offset,
blur,
color: color.into(),
color,
});
self
}
@@ -241,7 +245,7 @@ impl ToJson for ContainerStyle {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Margin {
top: f32,
left: f32,
@@ -268,7 +272,7 @@ impl ToJson for Margin {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Padding {
top: f32,
left: f32,
@@ -295,11 +299,12 @@ impl ToJson for Padding {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Shadow {
#[serde(deserialize_with = "deserialize_vec2f")]
offset: Vector2F,
blur: f32,
color: ColorU,
color: Color,
}
impl ToJson for Shadow {
+27 -23
View File
@@ -1,10 +1,7 @@
use serde_json::json;
use smallvec::{smallvec, SmallVec};
use crate::{
color::ColorU,
color::Color,
font_cache::FamilyId,
fonts::{FontId, Properties},
fonts::{deserialize_font_properties, deserialize_option_font_properties, FontId, Properties},
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
@@ -14,6 +11,9 @@ use crate::{
AfterLayoutContext, DebugContext, Element, Event, EventContext, FontCache, LayoutContext,
PaintContext, SizeConstraint,
};
use serde::Deserialize;
use serde_json::json;
use smallvec::{smallvec, SmallVec};
pub struct Label {
text: String,
@@ -23,12 +23,14 @@ pub struct Label {
highlight_indices: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Deserialize)]
pub struct LabelStyle {
pub default_color: ColorU,
pub highlight_color: ColorU,
pub color: Color,
pub highlight_color: Option<Color>,
#[serde(deserialize_with = "deserialize_font_properties")]
pub font_properties: Properties,
pub highlight_font_properties: Properties,
#[serde(default, deserialize_with = "deserialize_option_font_properties")]
pub highlight_font_properties: Option<Properties>,
}
impl Label {
@@ -47,8 +49,8 @@ impl Label {
self
}
pub fn with_default_color(mut self, color: ColorU) -> Self {
self.style.default_color = color;
pub fn with_default_color(mut self, color: Color) -> Self {
self.style.color = color;
self
}
@@ -61,13 +63,15 @@ impl Label {
&self,
font_cache: &FontCache,
font_id: FontId,
) -> SmallVec<[(usize, FontId, ColorU); 8]> {
) -> SmallVec<[(usize, FontId, Color); 8]> {
if self.highlight_indices.is_empty() {
return smallvec![(self.text.len(), font_id, self.style.default_color)];
return smallvec![(self.text.len(), font_id, self.style.color)];
}
let highlight_font_id = font_cache
.select_font(self.family_id, &self.style.highlight_font_properties)
let highlight_font_id = self
.style
.highlight_font_properties
.and_then(|properties| font_cache.select_font(self.family_id, &properties).ok())
.unwrap_or(font_id);
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
@@ -75,11 +79,11 @@ impl Label {
for (char_ix, c) in self.text.char_indices() {
let mut font_id = font_id;
let mut color = self.style.default_color;
let mut color = self.style.color;
if let Some(highlight_ix) = highlight_indices.peek() {
if char_ix == *highlight_ix {
font_id = highlight_font_id;
color = self.style.highlight_color;
color = self.style.highlight_color.unwrap_or(self.style.color);
highlight_indices.next();
}
}
@@ -179,7 +183,7 @@ impl Element for Label {
impl ToJson for LabelStyle {
fn to_json(&self) -> Value {
json!({
"default_color": self.default_color.to_json(),
"default_color": self.color.to_json(),
"default_font_properties": self.font_properties.to_json(),
"highlight_color": self.highlight_color.to_json(),
"highlight_font_properties": self.highlight_font_properties.to_json(),
@@ -204,14 +208,14 @@ mod tests {
.font_cache()
.select_font(menlo, Properties::new().weight(Weight::BOLD))
.unwrap();
let black = ColorU::black();
let red = ColorU::new(255, 0, 0, 255);
let black = Color::black();
let red = Color::new(255, 0, 0, 255);
let label = Label::new(".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(), menlo, 12.0)
.with_style(&LabelStyle {
default_color: black,
highlight_color: red,
highlight_font_properties: *Properties::new().weight(Weight::BOLD),
color: black,
highlight_color: Some(red),
highlight_font_properties: Some(*Properties::new().weight(Weight::BOLD)),
..Default::default()
})
.with_highlights(vec![
+4 -4
View File
@@ -3,7 +3,7 @@ use std::borrow::Cow;
use serde_json::json;
use crate::{
color::ColorU,
color::Color,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
@@ -14,18 +14,18 @@ use crate::{
pub struct Svg {
path: Cow<'static, str>,
color: ColorU,
color: Color,
}
impl Svg {
pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
Self {
path: path.into(),
color: ColorU::black(),
color: Color::black(),
}
}
pub fn with_color(mut self, color: ColorU) -> Self {
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
+66 -5
View File
@@ -1,14 +1,75 @@
use crate::json::json;
pub use font_kit::metrics::Metrics;
pub use font_kit::properties::{Properties, Stretch, Style, Weight};
use crate::json::ToJson;
use crate::json::{json, ToJson};
pub use font_kit::{
metrics::Metrics,
properties::{Properties, Stretch, Style, Weight},
};
use serde::{Deserialize, Deserializer};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct FontId(pub usize);
pub type GlyphId = u32;
#[allow(non_camel_case_types)]
#[derive(Deserialize)]
enum WeightJson {
thin,
extra_light,
light,
normal,
medium,
semibold,
bold,
extra_bold,
black,
}
#[derive(Deserialize)]
struct PropertiesJson {
weight: Option<WeightJson>,
#[serde(default)]
italic: bool,
}
impl Into<Properties> for PropertiesJson {
fn into(self) -> Properties {
let mut result = Properties::new();
result.weight = match self.weight.unwrap_or(WeightJson::normal) {
WeightJson::thin => Weight::THIN,
WeightJson::extra_light => Weight::EXTRA_LIGHT,
WeightJson::light => Weight::LIGHT,
WeightJson::normal => Weight::NORMAL,
WeightJson::medium => Weight::MEDIUM,
WeightJson::semibold => Weight::SEMIBOLD,
WeightJson::bold => Weight::BOLD,
WeightJson::extra_bold => Weight::EXTRA_BOLD,
WeightJson::black => Weight::BLACK,
};
if self.italic {
result.style = Style::Italic;
}
result
}
}
pub fn deserialize_option_font_properties<'de, D>(
deserializer: D,
) -> Result<Option<Properties>, D::Error>
where
D: Deserializer<'de>,
{
let json: Option<PropertiesJson> = Deserialize::deserialize(deserializer)?;
Ok(json.map(Into::into))
}
pub fn deserialize_font_properties<'de, D>(deserializer: D) -> Result<Properties, D::Error>
where
D: Deserializer<'de>,
{
let json: PropertiesJson = Deserialize::deserialize(deserializer)?;
Ok(json.into())
}
impl ToJson for Properties {
fn to_json(&self) -> crate::json::Value {
json!({
+11 -2
View File
@@ -1,7 +1,8 @@
use super::scene::{Path, PathVertex};
use crate::{color::ColorU, json::ToJson};
use crate::{color::Color, json::ToJson};
pub use pathfinder_geometry::*;
use rect::RectF;
use serde::{Deserialize, Deserializer};
use serde_json::json;
use vector::{vec2f, Vector2F};
@@ -55,7 +56,7 @@ impl PathBuilder {
self.current = point;
}
pub fn build(mut self, color: ColorU, clip_bounds: Option<RectF>) -> Path {
pub fn build(mut self, color: Color, clip_bounds: Option<RectF>) -> Path {
if let Some(clip_bounds) = clip_bounds {
self.bounds = self
.bounds
@@ -108,6 +109,14 @@ impl PathBuilder {
}
}
pub fn deserialize_vec2f<'de, D>(deserializer: D) -> Result<Vector2F, D::Error>
where
D: Deserializer<'de>,
{
let [x, y]: [f32; 2] = Deserialize::deserialize(deserializer)?;
Ok(vec2f(x, y))
}
impl ToJson for Vector2F {
fn to_json(&self) -> serde_json::Value {
json!([self.x(), self.y()])
+2 -2
View File
@@ -8,7 +8,7 @@ pub mod current {
}
use crate::{
color::ColorU,
color::Color,
executor,
fonts::{FontId, GlyphId, Metrics as FontMetrics, Properties as FontProperties},
geometry::{
@@ -134,7 +134,7 @@ pub trait FontSystem: Send + Sync {
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, ColorU)],
runs: &[(usize, FontId, Color)],
) -> LineLayout;
fn wrap_line(&self, text: &str, font_id: FontId, font_size: f32, width: f32) -> Vec<usize>;
}
+6 -6
View File
@@ -1,5 +1,5 @@
use crate::{
color::ColorU,
color::Color,
fonts::{FontId, GlyphId, Metrics, Properties},
geometry::{
rect::{RectF, RectI},
@@ -82,7 +82,7 @@ impl platform::FontSystem for FontSystem {
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, ColorU)],
runs: &[(usize, FontId, Color)],
) -> LineLayout {
self.0.read().layout_line(text, font_size, runs)
}
@@ -191,7 +191,7 @@ impl FontSystemState {
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, ColorU)],
runs: &[(usize, FontId, Color)],
) -> LineLayout {
let font_id_attr_name = CFString::from_static_string("zed_font_id");
@@ -436,9 +436,9 @@ mod tests {
text,
16.0,
&[
(9, zapfino_regular, ColorU::default()),
(13, menlo_regular, ColorU::default()),
(text.len() - 22, zapfino_regular, ColorU::default()),
(9, zapfino_regular, Color::default()),
(13, menlo_regular, Color::default()),
(text.len() - 22, zapfino_regular, Color::default()),
],
);
assert_eq!(
+7 -11
View File
@@ -1,6 +1,6 @@
use super::{atlas::AtlasAllocator, sprite_cache::SpriteCache};
use crate::{
color::ColorU,
color::Color,
geometry::{
rect::RectF,
vector::{vec2f, vec2i, Vector2F},
@@ -11,7 +11,7 @@ use crate::{
};
use cocoa::foundation::NSUInteger;
use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
use shaders::{ToFloat2 as _, ToUchar4 as _};
use shaders::ToFloat2 as _;
use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, sync::Arc, vec};
const SHADERS_METALLIB: &'static [u8] =
@@ -438,7 +438,7 @@ impl Renderer {
size: bounds.size().round().to_float2(),
background_color: quad
.background
.unwrap_or(ColorU::transparent_black())
.unwrap_or(Color::transparent_black())
.to_uchar4(),
border_top: border_width * (quad.border.top as usize as f32),
border_right: border_width * (quad.border.right as usize as f32),
@@ -447,7 +447,7 @@ impl Renderer {
border_color: quad
.border
.color
.unwrap_or(ColorU::transparent_black())
.unwrap_or(Color::transparent_black())
.to_uchar4(),
corner_radius: quad.corner_radius * scene.scale_factor(),
};
@@ -782,7 +782,7 @@ mod shaders {
use pathfinder_geometry::vector::Vector2I;
use crate::{color::ColorU, geometry::vector::Vector2F};
use crate::{color::Color, geometry::vector::Vector2F};
use std::mem;
include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
@@ -791,10 +791,6 @@ mod shaders {
fn to_float2(&self) -> vector_float2;
}
pub trait ToUchar4 {
fn to_uchar4(&self) -> vector_uchar4;
}
impl ToFloat2 for (f32, f32) {
fn to_float2(&self) -> vector_float2 {
unsafe {
@@ -823,8 +819,8 @@ mod shaders {
}
}
impl ToUchar4 for ColorU {
fn to_uchar4(&self) -> vector_uchar4 {
impl Color {
pub fn to_uchar4(&self) -> vector_uchar4 {
let mut vec = self.a as vector_uchar4;
vec <<= 8;
vec |= self.b as vector_uchar4;
+18 -18
View File
@@ -1,9 +1,9 @@
use serde::Deserialize;
use serde_json::json;
use std::borrow::Cow;
use serde_json::json;
use crate::{
color::ColorU,
color::Color,
fonts::{FontId, GlyphId},
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
@@ -28,7 +28,7 @@ pub struct Layer {
#[derive(Default, Debug)]
pub struct Quad {
pub bounds: RectF,
pub background: Option<ColorU>,
pub background: Option<Color>,
pub border: Border,
pub corner_radius: f32,
}
@@ -38,7 +38,7 @@ pub struct Shadow {
pub bounds: RectF,
pub corner_radius: f32,
pub sigma: f32,
pub color: ColorU,
pub color: Color,
}
#[derive(Debug)]
@@ -47,20 +47,20 @@ pub struct Glyph {
pub font_size: f32,
pub id: GlyphId,
pub origin: Vector2F,
pub color: ColorU,
pub color: Color,
}
pub struct Icon {
pub bounds: RectF,
pub svg: usvg::Tree,
pub path: Cow<'static, str>,
pub color: ColorU,
pub color: Color,
}
#[derive(Clone, Copy, Default, Debug)]
#[derive(Clone, Copy, Default, Debug, Deserialize)]
pub struct Border {
pub width: f32,
pub color: Option<ColorU>,
pub color: Option<Color>,
pub top: bool,
pub right: bool,
pub bottom: bool,
@@ -70,7 +70,7 @@ pub struct Border {
#[derive(Debug)]
pub struct Path {
pub bounds: RectF,
pub color: ColorU,
pub color: Color,
pub vertices: Vec<PathVertex>,
}
@@ -193,10 +193,10 @@ impl Layer {
}
impl Border {
pub fn new(width: f32, color: impl Into<ColorU>) -> Self {
pub fn new(width: f32, color: Color) -> Self {
Self {
width,
color: Some(color.into()),
color: Some(color),
top: false,
left: false,
bottom: false,
@@ -204,10 +204,10 @@ impl Border {
}
}
pub fn all(width: f32, color: impl Into<ColorU>) -> Self {
pub fn all(width: f32, color: Color) -> Self {
Self {
width,
color: Some(color.into()),
color: Some(color),
top: true,
left: true,
bottom: true,
@@ -215,25 +215,25 @@ impl Border {
}
}
pub fn top(width: f32, color: impl Into<ColorU>) -> Self {
pub fn top(width: f32, color: Color) -> Self {
let mut border = Self::new(width, color);
border.top = true;
border
}
pub fn left(width: f32, color: impl Into<ColorU>) -> Self {
pub fn left(width: f32, color: Color) -> Self {
let mut border = Self::new(width, color);
border.left = true;
border
}
pub fn bottom(width: f32, color: impl Into<ColorU>) -> Self {
pub fn bottom(width: f32, color: Color) -> Self {
let mut border = Self::new(width, color);
border.bottom = true;
border
}
pub fn right(width: f32, color: impl Into<ColorU>) -> Self {
pub fn right(width: f32, color: Color) -> Self {
let mut border = Self::new(width, color);
border.right = true;
border
+8 -8
View File
@@ -1,5 +1,5 @@
use crate::{
color::ColorU,
color::Color,
fonts::{FontId, GlyphId},
geometry::{
rect::RectF,
@@ -43,7 +43,7 @@ impl TextLayoutCache {
&'a self,
text: &'a str,
font_size: f32,
runs: &'a [(usize, FontId, ColorU)],
runs: &'a [(usize, FontId, Color)],
) -> Line {
let key = &CacheKeyRef {
text,
@@ -94,7 +94,7 @@ impl<'a> Hash for (dyn CacheKey + 'a) {
struct CacheKeyValue {
text: String,
font_size: OrderedFloat<f32>,
runs: SmallVec<[(usize, FontId, ColorU); 1]>,
runs: SmallVec<[(usize, FontId, Color); 1]>,
}
impl CacheKey for CacheKeyValue {
@@ -123,7 +123,7 @@ impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
struct CacheKeyRef<'a> {
text: &'a str,
font_size: OrderedFloat<f32>,
runs: &'a [(usize, FontId, ColorU)],
runs: &'a [(usize, FontId, Color)],
}
impl<'a> CacheKey for CacheKeyRef<'a> {
@@ -135,7 +135,7 @@ impl<'a> CacheKey for CacheKeyRef<'a> {
#[derive(Default, Debug)]
pub struct Line {
layout: Arc<LineLayout>,
color_runs: SmallVec<[(u32, ColorU); 32]>,
color_runs: SmallVec<[(u32, Color); 32]>,
}
#[derive(Default, Debug)]
@@ -162,7 +162,7 @@ pub struct Glyph {
}
impl Line {
fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, ColorU)]) -> Self {
fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, Color)]) -> Self {
let mut color_runs = SmallVec::new();
for (len, _, color) in runs {
color_runs.push((*len as u32, *color));
@@ -206,7 +206,7 @@ impl Line {
let mut color_runs = self.color_runs.iter();
let mut color_end = 0;
let mut color = ColorU::black();
let mut color = Color::black();
for run in &self.layout.runs {
let max_glyph_width = cx
@@ -230,7 +230,7 @@ impl Line {
color = next_run.1;
} else {
color_end = self.layout.len;
color = ColorU::black();
color = Color::black();
}
}