mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-07 02:57:34 +00:00
206 lines
6.2 KiB
Rust
206 lines
6.2 KiB
Rust
|
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;
|
||
|
}
|
||
|
}
|