Get playground rendering with backward compatible theming

This commit is contained in:
Nathan Sobo 2023-08-30 11:09:34 -06:00
parent d763946b18
commit 1d491fcd78
7 changed files with 115 additions and 40 deletions

3
Cargo.lock generated
View file

@ -5176,9 +5176,12 @@ dependencies = [
"parking_lot 0.11.2", "parking_lot 0.11.2",
"playground_macros", "playground_macros",
"refineable", "refineable",
"rust-embed",
"serde", "serde",
"settings",
"simplelog", "simplelog",
"smallvec", "smallvec",
"theme",
"util", "util",
] ]

View file

@ -16,9 +16,12 @@ log.workspace = true
playground_macros = { path = "../playground_macros" } playground_macros = { path = "../playground_macros" }
parking_lot.workspace = true parking_lot.workspace = true
refineable.workspace = true refineable.workspace = true
rust-embed.workspace = true
serde.workspace = true serde.workspace = true
settings = { path = "../../settings" }
simplelog = "0.9" simplelog = "0.9"
smallvec.workspace = true smallvec.workspace = true
theme = { path = "../../theme" }
util = { path = "../../util" } util = { path = "../../util" }
[dev-dependencies] [dev-dependencies]

View file

@ -3,9 +3,12 @@ use crate::element::Element;
use gpui::{ use gpui::{
geometry::{rect::RectF, vector::vec2f}, geometry::{rect::RectF, vector::vec2f},
platform::WindowOptions, platform::WindowOptions,
serde_json, ViewContext,
}; };
use log::LevelFilter; use log::LevelFilter;
use settings::{default_settings, SettingsStore};
use simplelog::SimpleLogger; use simplelog::SimpleLogger;
use theme::ThemeSettings;
use themes::Theme; use themes::Theme;
use view::view; use view::view;
use workspace::workspace; use workspace::workspace;
@ -30,6 +33,13 @@ fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
gpui::App::new(()).unwrap().run(|cx| { 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( cx.add_window(
WindowOptions { WindowOptions {
bounds: gpui::platform::WindowBounds::Fixed(RectF::new( bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
@ -39,12 +49,51 @@ fn main() {
center: true, center: true,
..Default::default() ..Default::default()
}, },
|_| view(|cx| playground(Theme::default())), |_| view(|cx| playground(cx)),
); );
cx.platform().activate(true); cx.platform().activate(true);
}); });
} }
fn playground<V: 'static>(theme: Theme) -> impl Element<V> { fn playground<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
workspace().themed(theme) 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()
}
} }

View file

@ -9,59 +9,59 @@ use std::{collections::HashMap, fmt, marker::PhantomData};
#[derive(Deserialize, Clone, Default, Debug)] #[derive(Deserialize, Clone, Default, Debug)]
pub struct Theme { pub struct Theme {
name: String, pub name: String,
is_light: bool, pub is_light: bool,
lowest: Layer, pub lowest: Layer,
middle: Layer, pub middle: Layer,
highest: Layer, pub highest: Layer,
popover_shadow: Shadow, pub popover_shadow: Shadow,
modal_shadow: Shadow, pub modal_shadow: Shadow,
#[serde(deserialize_with = "deserialize_player_colors")] #[serde(deserialize_with = "deserialize_player_colors")]
players: Vec<PlayerColors>, pub players: Vec<PlayerColors>,
#[serde(deserialize_with = "deserialize_syntax_colors")] #[serde(deserialize_with = "deserialize_syntax_colors")]
syntax: HashMap<String, Hsla>, pub syntax: HashMap<String, Hsla>,
} }
#[derive(Deserialize, Clone, Default, Debug)] #[derive(Deserialize, Clone, Default, Debug)]
pub struct Layer { pub struct Layer {
base: StyleSet, pub base: StyleSet,
variant: StyleSet, pub variant: StyleSet,
on: StyleSet, pub on: StyleSet,
accent: StyleSet, pub accent: StyleSet,
positive: StyleSet, pub positive: StyleSet,
warning: StyleSet, pub warning: StyleSet,
negative: StyleSet, pub negative: StyleSet,
} }
#[derive(Deserialize, Clone, Default, Debug)] #[derive(Deserialize, Clone, Default, Debug)]
pub struct StyleSet { pub struct StyleSet {
#[serde(rename = "default")] #[serde(rename = "default")]
default: ContainerColors, pub default: ContainerColors,
hovered: ContainerColors, pub hovered: ContainerColors,
pressed: ContainerColors, pub pressed: ContainerColors,
active: ContainerColors, pub active: ContainerColors,
disabled: ContainerColors, pub disabled: ContainerColors,
inverted: ContainerColors, pub inverted: ContainerColors,
} }
#[derive(Deserialize, Clone, Default, Debug)] #[derive(Deserialize, Clone, Default, Debug)]
pub struct ContainerColors { pub struct ContainerColors {
background: Hsla, pub background: Hsla,
foreground: Hsla, pub foreground: Hsla,
border: Hsla, pub border: Hsla,
} }
#[derive(Deserialize, Clone, Default, Debug)] #[derive(Deserialize, Clone, Default, Debug)]
pub struct PlayerColors { pub struct PlayerColors {
selection: Hsla, pub selection: Hsla,
cursor: Hsla, pub cursor: Hsla,
} }
#[derive(Deserialize, Clone, Default, Debug)] #[derive(Deserialize, Clone, Default, Debug)]
pub struct Shadow { pub struct Shadow {
blur: u8, pub blur: u8,
color: Hsla, pub color: Hsla,
offset: Vec<u8>, pub offset: Vec<u8>,
} }
pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme { 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 where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
#[derive(Deserialize)]
struct ColorWrapper {
color: Hsla,
}
struct SyntaxVisitor; struct SyntaxVisitor;
impl<'de> Visitor<'de> for SyntaxVisitor { impl<'de> Visitor<'de> for SyntaxVisitor {
@ -122,8 +127,8 @@ where
{ {
let mut result = HashMap::new(); let mut result = HashMap::new();
while let Some(key) = map.next_key()? { while let Some(key) = map.next_key()? {
let hsla: Hsla = map.next_value()?; // Deserialize values as Hsla let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
result.insert(key, hsla); result.insert(key, wrapper.color);
} }
Ok(result) Ok(result)
} }

View file

@ -2,6 +2,7 @@ use crate::{
div::div, div::div,
element::{Element, IntoElement, ParentElement}, element::{Element, IntoElement, ParentElement},
style::StyleHelpers, style::StyleHelpers,
themes::theme,
}; };
use gpui::{geometry::pixels, ViewContext}; use gpui::{geometry::pixels, ViewContext};
use playground_macros::Element; use playground_macros::Element;
@ -16,20 +17,22 @@ pub fn workspace<V: 'static>() -> impl Element<V> {
impl WorkspaceElement { impl WorkspaceElement {
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> { 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() div()
.full() .full()
.flex() .flex()
.flex_col() .flex_col()
// .fill(theme.base(0.5)) .fill(theme.middle.base.default.background)
.child(self.title_bar(cx)) .child(self.title_bar(cx))
.child(self.stage(cx)) .child(self.stage(cx))
.child(self.status_bar(cx)) .child(self.status_bar(cx))
} }
fn title_bar<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> { fn title_bar<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
// let colors = &theme(cx).colors; let theme = theme(cx);
div().h(pixels(cx.titlebar_height())) //.fill(colors.base(0.)) 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> { fn status_bar<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {

View file

@ -10,11 +10,12 @@ use gpui::{
fonts::{HighlightStyle, TextStyle}, fonts::{HighlightStyle, TextStyle},
platform, AppContext, AssetSource, Border, MouseState, platform, AppContext, AssetSource, Border, MouseState,
}; };
use parking_lot::Mutex;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{de::DeserializeOwned, Deserialize}; use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value; use serde_json::Value;
use settings::SettingsStore; 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}; use ui::{CheckboxStyle, CopilotCTAButton, IconStyle, ModalStyle};
pub use theme_registry::*; pub use theme_registry::*;
@ -67,6 +68,14 @@ pub struct Theme {
pub welcome: WelcomeStyle, pub welcome: WelcomeStyle,
pub titlebar: Titlebar, pub titlebar: Titlebar,
pub component_test: ComponentTest, 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)] #[derive(Deserialize, Default, Clone, JsonSchema)]

View file

@ -32,6 +32,9 @@ function write_themes(themes: Theme[], output_directory: string) {
setTheme(theme) setTheme(theme)
const style_tree = app() 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 style_tree_json = JSON.stringify(style_tree, null, 2)
const temp_path = path.join(temp_directory, `${theme.name}.json`) const temp_path = path.join(temp_directory, `${theme.name}.json`)
const out_path = path.join( const out_path = path.join(