@@ -1,8 +1,9 @@
#![allow(dead_code)]
-use std::{num::ParseIntError, ops::Range};
-
+use serde::de::{self, Deserialize, Deserializer, Visitor};
use smallvec::SmallVec;
+use std::fmt;
+use std::{num::ParseIntError, ops::Range};
pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
@@ -19,6 +20,40 @@ pub struct Rgba {
pub a: f32,
}
+struct RgbaVisitor;
+
+impl<'de> Visitor<'de> for RgbaVisitor {
+ type Value = Rgba;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a string in the format #rrggbb or #rrggbbaa")
+ }
+
+ fn visit_str<E: de::Error>(self, value: &str) -> Result<Rgba, E> {
+ if value.len() == 7 || value.len() == 9 {
+ let r = u8::from_str_radix(&value[1..3], 16).unwrap() as f32 / 255.0;
+ let g = u8::from_str_radix(&value[3..5], 16).unwrap() as f32 / 255.0;
+ let b = u8::from_str_radix(&value[5..7], 16).unwrap() as f32 / 255.0;
+ let a = if value.len() == 9 {
+ u8::from_str_radix(&value[7..9], 16).unwrap() as f32 / 255.0
+ } else {
+ 1.0
+ };
+ Ok(Rgba { r, g, b, a })
+ } else {
+ Err(E::custom(
+ "Bad format for RGBA. Expected #rrggbb or #rrggbbaa.",
+ ))
+ }
+ }
+}
+
+impl<'de> Deserialize<'de> for Rgba {
+ fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+ deserializer.deserialize_str(RgbaVisitor)
+ }
+}
+
pub trait Lerp {
fn lerp(&self, level: f32) -> Hsla;
}
@@ -219,6 +254,19 @@ impl Into<gpui::color::Color> for Hsla {
}
}
+impl<'de> Deserialize<'de> for Hsla {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ // First, deserialize it into Rgba
+ let rgba = Rgba::deserialize(deserializer)?;
+
+ // Then, use the From<Rgba> for Hsla implementation to convert it
+ Ok(Hsla::from(rgba))
+ }
+}
+
pub struct ColorScale {
colors: SmallVec<[Hsla; 2]>,
positions: SmallVec<[f32; 2]>,
@@ -1,107 +1,134 @@
use crate::{
- color::{Hsla, Lerp},
+ color::Hsla,
element::{Element, PaintContext},
layout_context::LayoutContext,
};
-use gpui::{AppContext, WindowContext};
-use std::{marker::PhantomData, ops::Range};
+use gpui::WindowContext;
+use serde::{de::Visitor, Deserialize, Deserializer};
+use std::{collections::HashMap, fmt, marker::PhantomData};
-pub mod rose_pine;
-
-#[derive(Clone, Debug)]
+#[derive(Deserialize, Clone, Default, Debug)]
pub struct Theme {
- pub colors: ThemeColors,
+ name: String,
+ is_light: bool,
+ lowest: Layer,
+ middle: Layer,
+ highest: Layer,
+ popover_shadow: Shadow,
+ modal_shadow: Shadow,
+ #[serde(deserialize_with = "deserialize_player_colors")]
+ players: Vec<PlayerColors>,
+ #[serde(deserialize_with = "deserialize_syntax_colors")]
+ syntax: HashMap<String, Hsla>,
}
-pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
- cx.theme::<Theme>()
+#[derive(Deserialize, Clone, Default, Debug)]
+pub struct Layer {
+ base: StyleSet,
+ variant: StyleSet,
+ on: StyleSet,
+ accent: StyleSet,
+ positive: StyleSet,
+ warning: StyleSet,
+ negative: StyleSet,
}
-#[derive(Clone, Debug)]
-pub struct ThemeColors {
- pub base: Range<Hsla>,
- pub surface: Range<Hsla>,
- pub overlay: Range<Hsla>,
- pub muted: Range<Hsla>,
- pub subtle: Range<Hsla>,
- pub text: Range<Hsla>,
- pub highlight_low: Range<Hsla>,
- pub highlight_med: Range<Hsla>,
- pub highlight_high: Range<Hsla>,
- pub success: Range<Hsla>,
- pub warning: Range<Hsla>,
- pub error: Range<Hsla>,
- pub inserted: Range<Hsla>,
- pub deleted: Range<Hsla>,
- pub modified: Range<Hsla>,
+#[derive(Deserialize, Clone, Default, Debug)]
+pub struct StyleSet {
+ #[serde(rename = "default")]
+ default: ContainerColors,
+ hovered: ContainerColors,
+ pressed: ContainerColors,
+ active: ContainerColors,
+ disabled: ContainerColors,
+ inverted: ContainerColors,
}
-impl ThemeColors {
- fn current(cx: &AppContext) -> &Self {
- cx.global::<Vec<Self>>()
- .last()
- .expect("must call within a theme provider")
- }
-
- pub fn base(&self, level: f32) -> Hsla {
- self.base.lerp(level)
- }
-
- pub fn surface(&self, level: f32) -> Hsla {
- self.surface.lerp(level)
- }
-
- pub fn overlay(&self, level: f32) -> Hsla {
- self.overlay.lerp(level)
- }
-
- pub fn muted(&self, level: f32) -> Hsla {
- self.muted.lerp(level)
- }
-
- pub fn subtle(&self, level: f32) -> Hsla {
- self.subtle.lerp(level)
- }
-
- pub fn text(&self, level: f32) -> Hsla {
- self.text.lerp(level)
- }
-
- pub fn highlight_low(&self, level: f32) -> Hsla {
- self.highlight_low.lerp(level)
- }
-
- pub fn highlight_med(&self, level: f32) -> Hsla {
- self.highlight_med.lerp(level)
- }
-
- pub fn highlight_high(&self, level: f32) -> Hsla {
- self.highlight_high.lerp(level)
- }
-
- pub fn success(&self, level: f32) -> Hsla {
- self.success.lerp(level)
- }
+#[derive(Deserialize, Clone, Default, Debug)]
+pub struct ContainerColors {
+ background: Hsla,
+ foreground: Hsla,
+ border: Hsla,
+}
- pub fn warning(&self, level: f32) -> Hsla {
- self.warning.lerp(level)
- }
+#[derive(Deserialize, Clone, Default, Debug)]
+pub struct PlayerColors {
+ selection: Hsla,
+ cursor: Hsla,
+}
- pub fn error(&self, level: f32) -> Hsla {
- self.error.lerp(level)
- }
+#[derive(Deserialize, Clone, Default, Debug)]
+pub struct Shadow {
+ blur: u8,
+ color: Hsla,
+ offset: Vec<u8>,
+}
- pub fn inserted(&self, level: f32) -> Hsla {
- self.inserted.lerp(level)
- }
+pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
+ cx.theme::<Theme>()
+}
- pub fn deleted(&self, level: f32) -> Hsla {
- self.deleted.lerp(level)
- }
+fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ struct PlayerArrayVisitor;
+
+ impl<'de> Visitor<'de> for PlayerArrayVisitor {
+ type Value = Vec<PlayerColors>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("an object with integer keys")
+ }
+
+ fn visit_map<A: serde::de::MapAccess<'de>>(
+ self,
+ mut map: A,
+ ) -> Result<Self::Value, A::Error> {
+ let mut players = Vec::with_capacity(8);
+ while let Some((key, value)) = map.next_entry::<usize, PlayerColors>()? {
+ if key < 8 {
+ players.push(value);
+ } else {
+ return Err(serde::de::Error::invalid_value(
+ serde::de::Unexpected::Unsigned(key as u64),
+ &"a key in range 0..7",
+ ));
+ }
+ }
+ Ok(players)
+ }
+ }
+
+ deserializer.deserialize_map(PlayerArrayVisitor)
+}
- pub fn modified(&self, level: f32) -> Hsla {
- self.modified.lerp(level)
- }
+fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String, Hsla>, D::Error>
+where
+ D: serde::Deserializer<'de>,
+{
+ struct SyntaxVisitor;
+
+ impl<'de> Visitor<'de> for SyntaxVisitor {
+ type Value = HashMap<String, Hsla>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a map with keys and objects with a single color field as values")
+ }
+
+ fn visit_map<M>(self, mut map: M) -> Result<HashMap<String, Hsla>, M::Error>
+ where
+ M: serde::de::MapAccess<'de>,
+ {
+ let mut result = HashMap::new();
+ while let Some(key) = map.next_key()? {
+ let hsla: Hsla = map.next_value()?; // Deserialize values as Hsla
+ result.insert(key, hsla);
+ }
+ Ok(result)
+ }
+ }
+ deserializer.deserialize_map(SyntaxVisitor)
}
pub struct Themed<V: 'static, E> {
@@ -1,10 +1,9 @@
-use crate::div::div;
-use crate::element::{IntoElement, ParentElement};
-use crate::style::StyleHelpers;
-use crate::themes::theme;
-use crate::{element::Element, themes::Theme};
-use gpui::geometry::pixels;
-use gpui::ViewContext;
+use crate::{
+ div::div,
+ element::{Element, IntoElement, ParentElement},
+ style::StyleHelpers,
+};
+use gpui::{geometry::pixels, ViewContext};
use playground_macros::Element;
use crate as playground;
@@ -17,29 +16,27 @@ pub fn workspace<V: 'static>() -> impl Element<V> {
impl WorkspaceElement {
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
- let theme = &cx.theme::<Theme>().colors;
+ // let theme = &cx.theme::<Theme>().colors;
div()
.full()
.flex()
.flex_col()
- .fill(theme.base(0.5))
+ // .fill(theme.base(0.5))
.child(self.title_bar(cx))
.child(self.stage(cx))
.child(self.status_bar(cx))
}
fn title_bar<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
- let colors = &theme(cx).colors;
- div().h(pixels(cx.titlebar_height())).fill(colors.base(0.))
+ // let colors = &theme(cx).colors;
+ div().h(pixels(cx.titlebar_height())) //.fill(colors.base(0.))
}
fn status_bar<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
- let colors = &theme(cx).colors;
- div().h(pixels(cx.titlebar_height())).fill(colors.base(0.))
+ div().h(pixels(cx.titlebar_height())) //.fill(colors.base(0.))
}
fn stage<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
- let colors = &theme(cx).colors;
div().flex_grow()
}
}