This commit is contained in:
Marshall Bowers
2023-10-24 16:59:24 +02:00
parent 9f8aaa4cdb
commit 09866ec3e9
11 changed files with 842 additions and 11 deletions
+41
View File
@@ -0,0 +1,41 @@
use crate::{Theme, ThemeMetadata, themes::one_dark};
use anyhow::Result;
use gpui2::{AnyAssetSource, SharedString};
use std::{
collections::HashMap,
sync::Arc
};
pub struct ThemeRegistry {
themes: HashMap<SharedString, Arc<Theme>>,
}
impl ThemeRegistry {
pub fn new(assets: AnyAssetSource) -> Self {
let mut this = Self {
themes: HashMap::default(),
};
this.insert_themes([one_dark()]);
this
}
fn insert_themes(&mut self, themes: impl IntoIterator<Item = Theme>) {
for theme in themes.into_iter() {
self.themes.insert(theme.metadata.name.clone(), Arc::new(theme));
}
}
pub fn list_names(&self, staff: bool) -> impl Iterator<Item = SharedString> + '_ {
None.into_iter()
}
pub fn list(&self, staff: bool) -> impl Iterator<Item = ThemeMetadata> + '_ {
None.into_iter()
}
pub fn get(&self, name: impl Into<SharedString>) -> Result<Arc<Theme>> {
todo!()
}
}
+205
View File
@@ -0,0 +1,205 @@
use crate::{Theme, ThemeRegistry};
use anyhow::Result;
use gpui2::{FontFeatures, SharedString, Font, AppContext, Pixels, px, FontWeight, FontStyle};
use schemars::{
gen::SchemaGenerator,
schema::{InstanceType, Schema, SchemaObject},
JsonSchema,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use settings2::SettingsJsonSchemaParams;
use std::sync::Arc;
use util::ResultExt as _;
const MIN_FONT_SIZE: Pixels = px(6.0);
const MIN_LINE_HEIGHT: f32 = 1.0;
#[derive(Clone, JsonSchema)]
pub struct ThemeSettings {
pub buffer_font: Font,
pub buffer_font_size: Pixels,
pub buffer_line_height: BufferLineHeight,
#[serde(skip)]
pub theme: Arc<Theme>,
}
pub struct AdjustedBufferFontSize(pub f32);
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct ThemeSettingsContent {
#[serde(default)]
pub buffer_font_family: Option<SharedString>,
#[serde(default)]
pub buffer_font_size: Option<f32>,
#[serde(default)]
pub buffer_line_height: Option<BufferLineHeight>,
#[serde(default)]
pub buffer_font_features: Option<FontFeatures>,
#[serde(default)]
pub theme: Option<SharedString>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub enum BufferLineHeight {
#[default]
Comfortable,
Standard,
Custom(f32),
}
impl BufferLineHeight {
pub fn value(&self) -> f32 {
match self {
BufferLineHeight::Comfortable => 1.618,
BufferLineHeight::Standard => 1.3,
BufferLineHeight::Custom(line_height) => *line_height,
}
}
}
impl ThemeSettings {
pub fn buffer_font_size(&self, cx: &AppContext) -> f32 {
if cx.has_global::<AdjustedBufferFontSize>() {
cx.global::<AdjustedBufferFontSize>().0
} else {
self.buffer_font_size
}
.max(MIN_FONT_SIZE)
}
pub fn line_height(&self) -> f32 {
f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)
}
}
pub fn adjusted_font_size(size: f32, cx: &AppContext) -> f32 {
if let Some(adjusted_size) = cx.try_global::<AdjustedBufferFontSize>() {
let buffer_font_size = settings2::get::<ThemeSettings>(cx).buffer_font_size;
let delta = adjusted_size - buffer_font_size;
size + delta
} else {
size
}.max(MIN_FONT_SIZE)
}
pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
if !cx.has_global::<AdjustedBufferFontSize>() {
let buffer_font_size = settings2::get::<ThemeSettings>(cx).buffer_font_size;
cx.set_global(AdjustedBufferFontSize(buffer_font_size));
}
let mut delta = cx.global_mut::<AdjustedBufferFontSize>();
f(&mut delta.0);
delta.0 = delta
.0
.max(MIN_FONT_SIZE - settings2::get::<ThemeSettings>(cx).buffer_font_size);
cx.refresh_windows();
}
pub fn reset_font_size(cx: &mut AppContext) {
if cx.has_global::<AdjustedBufferFontSize>() {
cx.remove_global::<AdjustedBufferFontSize>();
cx.refresh_windows();
}
}
impl settings2::Setting for ThemeSettings {
const KEY: Option<&'static str> = None;
type FileContent = ThemeSettingsContent;
fn load(
defaults: &Self::FileContent,
user_values: &[&Self::FileContent],
cx: &AppContext,
) -> Result<Self> {
let themes = cx.global::<Arc<ThemeRegistry>>();
let mut this = Self {
buffer_font: Font {
family: defaults.buffer_font_family.clone().unwrap(),
features: defaults.buffer_font_features.clone().unwrap(),
weight: FontWeight::default(),
style: FontStyle::default(),
},
buffer_font_size: defaults.buffer_font_size.unwrap(),
buffer_line_height: defaults.buffer_line_height.unwrap(),
theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
};
for value in user_values.into_iter().copied().cloned() {
let font_cache = cx.font_cache();
let mut family_changed = false;
if let Some(value) = value.buffer_font_family {
this.buffer_font_family_name = value;
family_changed = true;
}
if let Some(value) = value.buffer_font_features {
this.buffer_font_features = value;
family_changed = true;
}
if family_changed {
if let Some(id) = font_cache
.load_family(&[&this.buffer_font_family_name], &this.buffer_font_features)
.log_err()
{
this.buffer_font_family = id;
}
}
if let Some(value) = &value.theme {
if let Some(theme) = themes.get(value).log_err() {
this.theme = theme;
}
}
merge(&mut this.buffer_font_size, value.buffer_font_size);
merge(&mut this.buffer_line_height, value.buffer_line_height);
}
Ok(this)
}
fn json_schema(
generator: &mut SchemaGenerator,
params: &SettingsJsonSchemaParams,
cx: &AppContext,
) -> schemars::schema::RootSchema {
let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
let theme_names = cx
.global::<Arc<ThemeRegistry>>()
.list_names(params.staff_mode)
.map(|theme_name| Value::String(theme_name.to_string()))
.collect();
let theme_name_schema = SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(theme_names),
..Default::default()
};
root_schema
.definitions
.extend([("ThemeName".into(), theme_name_schema.into())]);
root_schema
.schema
.object
.as_mut()
.unwrap()
.properties
.extend([(
"theme".to_owned(),
Schema::new_ref("#/definitions/ThemeName".into()),
)]);
root_schema
}
}
fn merge<T: Copy>(target: &mut T, value: Option<T>) {
if let Some(value) = value {
*target = value;
}
}
+104 -11
View File
@@ -1,21 +1,114 @@
use gpui2::HighlightStyle;
mod registry;
mod settings;
mod themes;
pub use registry::*;
pub use settings::*;
use gpui2::{Hsla, SharedString};
use std::sync::Arc;
pub struct Theme {
pub editor: Editor,
pub metadata: ThemeMetadata,
pub transparent: Hsla,
pub mac_os_traffic_light_red: Hsla,
pub mac_os_traffic_light_yellow: Hsla,
pub mac_os_traffic_light_green: Hsla,
pub border: Hsla,
pub border_variant: Hsla,
pub border_focused: Hsla,
pub border_transparent: Hsla,
/// The background color of an elevated surface, like a modal, tooltip or toast.
pub elevated_surface: Hsla,
pub surface: Hsla,
/// Window background color of the base app
pub background: Hsla,
/// Default background for elements like filled buttons,
/// text fields, checkboxes, radio buttons, etc.
/// - TODO: Map to step 3.
pub filled_element: Hsla,
/// The background color of a hovered element, like a button being hovered
/// with a mouse, or hovered on a touch screen.
/// - TODO: Map to step 4.
pub filled_element_hover: Hsla,
/// The background color of an active element, like a button being pressed,
/// or tapped on a touch screen.
/// - TODO: Map to step 5.
pub filled_element_active: Hsla,
/// The background color of a selected element, like a selected tab,
/// a button toggled on, or a checkbox that is checked.
pub filled_element_selected: Hsla,
pub filled_element_disabled: Hsla,
pub ghost_element: Hsla,
/// The background color of a hovered element with no default background,
/// like a ghost-style button or an interactable list item.
/// - TODO: Map to step 3.
pub ghost_element_hover: Hsla,
/// - TODO: Map to step 4.
pub ghost_element_active: Hsla,
pub ghost_element_selected: Hsla,
pub ghost_element_disabled: Hsla,
pub text: Hsla,
pub text_muted: Hsla,
pub text_placeholder: Hsla,
pub text_disabled: Hsla,
pub text_accent: Hsla,
pub icon_muted: Hsla,
pub syntax: SyntaxTheme,
pub status_bar: Hsla,
pub title_bar: Hsla,
pub toolbar: Hsla,
pub tab_bar: Hsla,
/// The background of the editor
pub editor: Hsla,
pub editor_subheader: Hsla,
pub editor_active_line: Hsla,
pub terminal: Hsla,
pub image_fallback_background: Hsla,
pub git_created: Hsla,
pub git_modified: Hsla,
pub git_deleted: Hsla,
pub git_conflict: Hsla,
pub git_ignored: Hsla,
pub git_renamed: Hsla,
pub player: [PlayerTheme; 8],
}
#[derive(Clone, Copy)]
pub struct SyntaxTheme {
pub comment: Hsla,
pub string: Hsla,
pub function: Hsla,
pub keyword: Hsla,
}
#[derive(Clone, Copy)]
pub struct PlayerTheme {
pub cursor: Hsla,
pub selection: Hsla,
}
pub struct ThemeMetadata {
pub id: usize,
pub name: SharedString,
pub is_light: bool,
}
pub struct Editor {
pub syntax: Arc<SyntaxTheme>,
}
#[derive(Default)]
pub struct SyntaxTheme {
pub highlights: Vec<(String, HighlightStyle)>,
}
// #[derive(Default)]
// pub struct SyntaxTheme {
// pub highlights: Vec<(String, HighlightStyle)>,
// }
impl SyntaxTheme {
pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
Self { highlights }
}
}
// impl SyntaxTheme {
// pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
// Self { highlights }
// }
// }
+3
View File
@@ -0,0 +1,3 @@
mod one_dark;
pub use one_dark::*;
+90
View File
@@ -0,0 +1,90 @@
use gpui2::rgba;
use crate::{PlayerTheme, SyntaxTheme, Theme};
pub fn one_dark() -> Theme {
Theme {
transparent: rgba(0x00000000).into(),
mac_os_traffic_light_red: rgba(0xec695eff).into(),
mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(),
mac_os_traffic_light_green: rgba(0x61c553ff).into(),
border: rgba(0x464b57ff).into(),
border_variant: rgba(0x464b57ff).into(),
border_focused: rgba(0x293b5bff).into(),
border_transparent: rgba(0x00000000).into(),
elevated_surface: rgba(0x3b414dff).into(),
surface: rgba(0x2f343eff).into(),
background: rgba(0x3b414dff).into(),
filled_element: rgba(0x3b414dff).into(),
filled_element_hover: rgba(0xffffff1e).into(),
filled_element_active: rgba(0xffffff28).into(),
filled_element_selected: rgba(0x18243dff).into(),
filled_element_disabled: rgba(0x00000000).into(),
ghost_element: rgba(0x00000000).into(),
ghost_element_hover: rgba(0xffffff14).into(),
ghost_element_active: rgba(0xffffff1e).into(),
ghost_element_selected: rgba(0x18243dff).into(),
ghost_element_disabled: rgba(0x00000000).into(),
text: rgba(0xc8ccd4ff).into(),
text_muted: rgba(0x838994ff).into(),
text_placeholder: rgba(0xd07277ff).into(),
text_disabled: rgba(0x555a63ff).into(),
text_accent: rgba(0x74ade8ff).into(),
icon_muted: rgba(0x838994ff).into(),
syntax: SyntaxTheme {
comment: rgba(0x5d636fff).into(),
string: rgba(0xa1c181ff).into(),
function: rgba(0x73ade9ff).into(),
keyword: rgba(0xb477cfff).into(),
},
status_bar: rgba(0x3b414dff).into(),
title_bar: rgba(0x3b414dff).into(),
toolbar: rgba(0x282c33ff).into(),
tab_bar: rgba(0x2f343eff).into(),
editor: rgba(0x282c33ff).into(),
editor_subheader: rgba(0x2f343eff).into(),
editor_active_line: rgba(0x2f343eff).into(),
terminal: rgba(0x282c33ff).into(),
image_fallback_background: rgba(0x3b414dff).into(),
git_created: rgba(0xa1c181ff).into(),
git_modified: rgba(0x74ade8ff).into(),
git_deleted: rgba(0xd07277ff).into(),
git_conflict: rgba(0xdec184ff).into(),
git_ignored: rgba(0x555a63ff).into(),
git_renamed: rgba(0xdec184ff).into(),
player: [
PlayerTheme {
cursor: rgba(0x74ade8ff).into(),
selection: rgba(0x74ade83d).into(),
},
PlayerTheme {
cursor: rgba(0xa1c181ff).into(),
selection: rgba(0xa1c1813d).into(),
},
PlayerTheme {
cursor: rgba(0xbe5046ff).into(),
selection: rgba(0xbe50463d).into(),
},
PlayerTheme {
cursor: rgba(0xbf956aff).into(),
selection: rgba(0xbf956a3d).into(),
},
PlayerTheme {
cursor: rgba(0xb477cfff).into(),
selection: rgba(0xb477cf3d).into(),
},
PlayerTheme {
cursor: rgba(0x6eb4bfff).into(),
selection: rgba(0x6eb4bf3d).into(),
},
PlayerTheme {
cursor: rgba(0xd07277ff).into(),
selection: rgba(0xd072773d).into(),
},
PlayerTheme {
cursor: rgba(0xdec184ff).into(),
selection: rgba(0xdec1843d).into(),
},
],
}
}