Merge branch 'main' into panels
This commit is contained in:
@@ -1,19 +1,40 @@
|
||||
mod theme_registry;
|
||||
mod theme_settings;
|
||||
pub mod ui;
|
||||
|
||||
use gpui::{
|
||||
color::Color,
|
||||
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
|
||||
fonts::{HighlightStyle, TextStyle},
|
||||
platform, Border, MouseState,
|
||||
platform, AppContext, AssetSource, Border, MouseState,
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use serde_json::Value;
|
||||
use settings::SettingsStore;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle};
|
||||
|
||||
pub mod ui;
|
||||
|
||||
pub use theme_registry::*;
|
||||
pub use theme_settings::*;
|
||||
|
||||
pub fn current(cx: &AppContext) -> Arc<Theme> {
|
||||
settings::get::<ThemeSettings>(cx).theme.clone()
|
||||
}
|
||||
|
||||
pub fn init(source: impl AssetSource, cx: &mut AppContext) {
|
||||
cx.set_global(ThemeRegistry::new(source, cx.font_cache().clone()));
|
||||
settings::register::<ThemeSettings>(cx);
|
||||
|
||||
let mut prev_buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;
|
||||
cx.observe_global::<SettingsStore, _>(move |cx| {
|
||||
let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;
|
||||
if buffer_font_size != prev_buffer_font_size {
|
||||
prev_buffer_font_size = buffer_font_size;
|
||||
reset_font_size(cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct Theme {
|
||||
|
||||
@@ -22,13 +22,25 @@ pub struct ThemeRegistry {
|
||||
|
||||
impl ThemeRegistry {
|
||||
pub fn new(source: impl AssetSource, font_cache: Arc<FontCache>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
let this = Arc::new(Self {
|
||||
assets: Box::new(source),
|
||||
themes: Default::default(),
|
||||
theme_data: Default::default(),
|
||||
next_theme_id: Default::default(),
|
||||
font_cache,
|
||||
})
|
||||
});
|
||||
|
||||
this.themes.lock().insert(
|
||||
settings::EMPTY_THEME_NAME.to_string(),
|
||||
gpui::fonts::with_font_cache(this.font_cache.clone(), || {
|
||||
let mut theme = Theme::default();
|
||||
theme.meta.id = this.next_theme_id.fetch_add(1, SeqCst);
|
||||
theme.meta.name = settings::EMPTY_THEME_NAME.into();
|
||||
Arc::new(theme)
|
||||
}),
|
||||
);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub fn list(&self, staff: bool) -> impl Iterator<Item = ThemeMeta> + '_ {
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
use crate::{Theme, ThemeRegistry};
|
||||
use anyhow::Result;
|
||||
use gpui::{font_cache::FamilyId, fonts, AppContext};
|
||||
use schemars::{
|
||||
gen::SchemaGenerator,
|
||||
schema::{InstanceType, Schema, SchemaObject},
|
||||
JsonSchema,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use settings::SettingsJsonSchemaParams;
|
||||
use std::sync::Arc;
|
||||
use util::ResultExt as _;
|
||||
|
||||
const MIN_FONT_SIZE: f32 = 6.0;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ThemeSettings {
|
||||
pub buffer_font_family_name: String,
|
||||
pub buffer_font_features: fonts::Features,
|
||||
pub buffer_font_family: FamilyId,
|
||||
pub(crate) buffer_font_size: f32,
|
||||
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<String>,
|
||||
#[serde(default)]
|
||||
pub buffer_font_size: Option<f32>,
|
||||
#[serde(default)]
|
||||
pub buffer_font_features: Option<fonts::Features>,
|
||||
#[serde(default)]
|
||||
pub theme: Option<String>,
|
||||
}
|
||||
|
||||
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 adjusted_font_size(size: f32, cx: &AppContext) -> f32 {
|
||||
if cx.has_global::<AdjustedBufferFontSize>() {
|
||||
let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;
|
||||
let delta = cx.global::<AdjustedBufferFontSize>().0 - buffer_font_size;
|
||||
size + delta
|
||||
} else {
|
||||
size
|
||||
}
|
||||
.max(MIN_FONT_SIZE)
|
||||
}
|
||||
|
||||
pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut f32)) {
|
||||
if !cx.has_global::<AdjustedBufferFontSize>() {
|
||||
let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;
|
||||
cx.set_global(AdjustedBufferFontSize(buffer_font_size));
|
||||
}
|
||||
|
||||
cx.update_global::<AdjustedBufferFontSize, _, _>(|delta, cx| {
|
||||
f(&mut delta.0);
|
||||
delta.0 = delta
|
||||
.0
|
||||
.max(MIN_FONT_SIZE - settings::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 settings::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 buffer_font_features = defaults.buffer_font_features.clone().unwrap();
|
||||
let themes = cx.global::<Arc<ThemeRegistry>>();
|
||||
|
||||
let mut this = Self {
|
||||
buffer_font_family: cx
|
||||
.font_cache()
|
||||
.load_family(
|
||||
&[defaults.buffer_font_family.as_ref().unwrap()],
|
||||
&buffer_font_features,
|
||||
)
|
||||
.unwrap(),
|
||||
buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(),
|
||||
buffer_font_features,
|
||||
buffer_font_size: defaults.buffer_font_size.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);
|
||||
}
|
||||
|
||||
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(params.staff_mode)
|
||||
.map(|theme| Value::String(theme.name.clone()))
|
||||
.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;
|
||||
}
|
||||
}
|
||||
+54
-3
@@ -1,9 +1,10 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use fs::repository::GitFileStatus;
|
||||
use gpui::{
|
||||
color::Color,
|
||||
elements::{
|
||||
ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
|
||||
ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label, LabelStyle,
|
||||
MouseEventHandler, ParentElement, Stack, Svg,
|
||||
},
|
||||
fonts::TextStyle,
|
||||
@@ -11,11 +12,11 @@ use gpui::{
|
||||
platform,
|
||||
platform::MouseButton,
|
||||
scene::MouseClick,
|
||||
Action, Element, EventContext, MouseState, View, ViewContext,
|
||||
Action, AnyElement, Element, EventContext, MouseState, View, ViewContext,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ContainedText, Interactive};
|
||||
use crate::{ContainedText, Interactive, Theme};
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
pub struct CheckboxStyle {
|
||||
@@ -252,3 +253,53 @@ where
|
||||
.constrained()
|
||||
.with_height(style.dimensions().y())
|
||||
}
|
||||
|
||||
pub struct FileName {
|
||||
filename: String,
|
||||
git_status: Option<GitFileStatus>,
|
||||
style: FileNameStyle,
|
||||
}
|
||||
|
||||
pub struct FileNameStyle {
|
||||
template_style: LabelStyle,
|
||||
git_inserted: Color,
|
||||
git_modified: Color,
|
||||
git_deleted: Color,
|
||||
}
|
||||
|
||||
impl FileName {
|
||||
pub fn new(filename: String, git_status: Option<GitFileStatus>, style: FileNameStyle) -> Self {
|
||||
FileName {
|
||||
filename,
|
||||
git_status,
|
||||
style,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn style<I: Into<LabelStyle>>(style: I, theme: &Theme) -> FileNameStyle {
|
||||
FileNameStyle {
|
||||
template_style: style.into(),
|
||||
git_inserted: theme.editor.diff.inserted,
|
||||
git_modified: theme.editor.diff.modified,
|
||||
git_deleted: theme.editor.diff.deleted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> gpui::elements::Component<V> for FileName {
|
||||
fn render(&self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
// Prepare colors for git statuses
|
||||
let mut filename_text_style = self.style.template_style.text.clone();
|
||||
filename_text_style.color = self
|
||||
.git_status
|
||||
.as_ref()
|
||||
.map(|status| match status {
|
||||
GitFileStatus::Added => self.style.git_inserted,
|
||||
GitFileStatus::Modified => self.style.git_modified,
|
||||
GitFileStatus::Conflict => self.style.git_deleted,
|
||||
})
|
||||
.unwrap_or(self.style.template_style.text.color);
|
||||
|
||||
Label::new(self.filename.clone(), filename_text_style).into_any()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user