Cargo.lock 🔗
@@ -5176,9 +5176,12 @@ dependencies = [
"parking_lot 0.11.2",
"playground_macros",
"refineable",
+ "rust-embed",
"serde",
+ "settings",
"simplelog",
"smallvec",
+ "theme",
"util",
]
Nathan Sobo created
Cargo.lock | 3 +
crates/gpui/playground/Cargo.toml | 3 +
crates/gpui/playground/src/playground.rs | 55 +++++++++++++++++++-
crates/gpui/playground/src/themes.rs | 69 +++++++++++++------------
crates/gpui/playground/src/workspace.rs | 11 ++-
crates/theme/src/theme.rs | 11 +++
styles/src/build_themes.ts | 3 +
7 files changed, 115 insertions(+), 40 deletions(-)
@@ -5176,9 +5176,12 @@ dependencies = [
"parking_lot 0.11.2",
"playground_macros",
"refineable",
+ "rust-embed",
"serde",
+ "settings",
"simplelog",
"smallvec",
+ "theme",
"util",
]
@@ -16,9 +16,12 @@ log.workspace = true
playground_macros = { path = "../playground_macros" }
parking_lot.workspace = true
refineable.workspace = true
+rust-embed.workspace = true
serde.workspace = true
+settings = { path = "../../settings" }
simplelog = "0.9"
smallvec.workspace = true
+theme = { path = "../../theme" }
util = { path = "../../util" }
[dev-dependencies]
@@ -3,9 +3,12 @@ use crate::element::Element;
use gpui::{
geometry::{rect::RectF, vector::vec2f},
platform::WindowOptions,
+ serde_json, ViewContext,
};
use log::LevelFilter;
+use settings::{default_settings, SettingsStore};
use simplelog::SimpleLogger;
+use theme::ThemeSettings;
use themes::Theme;
use view::view;
use workspace::workspace;
@@ -30,6 +33,13 @@ fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
gpui::App::new(()).unwrap().run(|cx| {
+ let mut store = SettingsStore::default();
+ store
+ .set_default_settings(default_settings().as_ref(), cx)
+ .unwrap();
+ cx.set_global(store);
+ theme::init(Assets, cx);
+
cx.add_window(
WindowOptions {
bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
@@ -39,12 +49,51 @@ fn main() {
center: true,
..Default::default()
},
- |_| view(|cx| playground(Theme::default())),
+ |_| view(|cx| playground(cx)),
);
cx.platform().activate(true);
});
}
-fn playground<V: 'static>(theme: Theme) -> impl Element<V> {
- workspace().themed(theme)
+fn playground<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
+ workspace().themed(current_theme(cx))
+}
+
+// Nathan: During the transition, we will include the base theme on the legacy Theme struct.
+fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
+ settings::get::<ThemeSettings>(cx)
+ .theme
+ .deserialized_base_theme
+ .lock()
+ .get_or_insert_with(|| {
+ let theme: Theme =
+ serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
+ .unwrap();
+ Box::new(theme)
+ })
+ .downcast_ref::<Theme>()
+ .unwrap()
+ .clone()
+}
+
+use anyhow::{anyhow, Result};
+use gpui::AssetSource;
+use rust_embed::RustEmbed;
+
+#[derive(RustEmbed)]
+#[folder = "../../../assets"]
+#[include = "themes/**/*"]
+#[exclude = "*.DS_Store"]
+pub struct Assets;
+
+impl AssetSource for Assets {
+ fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
+ Self::get(path)
+ .map(|f| f.data)
+ .ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
+ }
+
+ fn list(&self, path: &str) -> Vec<std::borrow::Cow<'static, str>> {
+ Self::iter().filter(|p| p.starts_with(path)).collect()
+ }
}
@@ -9,59 +9,59 @@ use std::{collections::HashMap, fmt, marker::PhantomData};
#[derive(Deserialize, Clone, Default, Debug)]
pub struct Theme {
- name: String,
- is_light: bool,
- lowest: Layer,
- middle: Layer,
- highest: Layer,
- popover_shadow: Shadow,
- modal_shadow: Shadow,
+ pub name: String,
+ pub is_light: bool,
+ pub lowest: Layer,
+ pub middle: Layer,
+ pub highest: Layer,
+ pub popover_shadow: Shadow,
+ pub modal_shadow: Shadow,
#[serde(deserialize_with = "deserialize_player_colors")]
- players: Vec<PlayerColors>,
+ pub players: Vec<PlayerColors>,
#[serde(deserialize_with = "deserialize_syntax_colors")]
- syntax: HashMap<String, Hsla>,
+ pub syntax: HashMap<String, Hsla>,
}
#[derive(Deserialize, Clone, Default, Debug)]
pub struct Layer {
- base: StyleSet,
- variant: StyleSet,
- on: StyleSet,
- accent: StyleSet,
- positive: StyleSet,
- warning: StyleSet,
- negative: StyleSet,
+ pub base: StyleSet,
+ pub variant: StyleSet,
+ pub on: StyleSet,
+ pub accent: StyleSet,
+ pub positive: StyleSet,
+ pub warning: StyleSet,
+ pub negative: StyleSet,
}
#[derive(Deserialize, Clone, Default, Debug)]
pub struct StyleSet {
#[serde(rename = "default")]
- default: ContainerColors,
- hovered: ContainerColors,
- pressed: ContainerColors,
- active: ContainerColors,
- disabled: ContainerColors,
- inverted: ContainerColors,
+ pub default: ContainerColors,
+ pub hovered: ContainerColors,
+ pub pressed: ContainerColors,
+ pub active: ContainerColors,
+ pub disabled: ContainerColors,
+ pub inverted: ContainerColors,
}
#[derive(Deserialize, Clone, Default, Debug)]
pub struct ContainerColors {
- background: Hsla,
- foreground: Hsla,
- border: Hsla,
+ pub background: Hsla,
+ pub foreground: Hsla,
+ pub border: Hsla,
}
#[derive(Deserialize, Clone, Default, Debug)]
pub struct PlayerColors {
- selection: Hsla,
- cursor: Hsla,
+ pub selection: Hsla,
+ pub cursor: Hsla,
}
#[derive(Deserialize, Clone, Default, Debug)]
pub struct Shadow {
- blur: u8,
- color: Hsla,
- offset: Vec<u8>,
+ pub blur: u8,
+ pub color: Hsla,
+ pub offset: Vec<u8>,
}
pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
@@ -107,6 +107,11 @@ fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String,
where
D: serde::Deserializer<'de>,
{
+ #[derive(Deserialize)]
+ struct ColorWrapper {
+ color: Hsla,
+ }
+
struct SyntaxVisitor;
impl<'de> Visitor<'de> for SyntaxVisitor {
@@ -122,8 +127,8 @@ where
{
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);
+ let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
+ result.insert(key, wrapper.color);
}
Ok(result)
}
@@ -2,6 +2,7 @@ use crate::{
div::div,
element::{Element, IntoElement, ParentElement},
style::StyleHelpers,
+ themes::theme,
};
use gpui::{geometry::pixels, ViewContext};
use playground_macros::Element;
@@ -16,20 +17,22 @@ 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 = theme(cx);
div()
.full()
.flex()
.flex_col()
- // .fill(theme.base(0.5))
+ .fill(theme.middle.base.default.background)
.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 theme = theme(cx);
+ div()
+ .h(pixels(cx.titlebar_height()))
+ .fill(theme.lowest.base.default.background)
}
fn status_bar<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
@@ -10,11 +10,12 @@ use gpui::{
fonts::{HighlightStyle, TextStyle},
platform, AppContext, AssetSource, Border, MouseState,
};
+use parking_lot::Mutex;
use schemars::JsonSchema;
use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
use settings::SettingsStore;
-use std::{collections::HashMap, ops::Deref, sync::Arc};
+use std::{any::Any, collections::HashMap, ops::Deref, sync::Arc};
use ui::{CheckboxStyle, CopilotCTAButton, IconStyle, ModalStyle};
pub use theme_registry::*;
@@ -67,6 +68,14 @@ pub struct Theme {
pub welcome: WelcomeStyle,
pub titlebar: Titlebar,
pub component_test: ComponentTest,
+ // Nathan: New elements are styled in Rust, directly from the base theme.
+ // We store it on the legacy theme so we can mix both kinds of elements during the transition.
+ #[schemars(skip)]
+ pub base_theme: serde_json::Value,
+ // A place to cache deserialized base theme.
+ #[serde(skip_deserializing)]
+ #[schemars(skip)]
+ pub deserialized_base_theme: Mutex<Option<Box<dyn Any + Send + Sync>>>,
}
#[derive(Deserialize, Default, Clone, JsonSchema)]
@@ -32,6 +32,9 @@ function write_themes(themes: Theme[], output_directory: string) {
setTheme(theme)
const style_tree = app()
+ // Nathan: New elements will read directly from the theme colors.
+ // Adding this during the transition. Afterwards, we can port all themes to Rust.
+ style_tree.base_theme = theme
const style_tree_json = JSON.stringify(style_tree, null, 2)
const temp_path = path.join(temp_directory, `${theme.name}.json`)
const out_path = path.join(