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",
"playground_macros",
"refineable",
"rust-embed",
"serde",
"settings",
"simplelog",
"smallvec",
"theme",
"util",
]

View file

@ -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]

View file

@ -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()
}
}

View file

@ -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)
}

View file

@ -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> {

View file

@ -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)]

View file

@ -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(