Add infrastructure for loading icon themes from extensions (#23203)
This PR adds the supporting infrastructure to support loading icon themes defined by extensions. Here's an example icon theme: ```json { "name": "My Icon Theme", "author": "Me <me@example.com>", "themes": [ { "name": "My Icon Theme", "appearance": "dark", "file_icons": { "gleam": { "path": "./icons/file_type_gleam.svg" }, "toml": { "path": "./icons/file_type_toml.svg" } } } ] } ``` The icon paths are resolved relative to the root of the extension directory. Release Notes: - N/A
This commit is contained in:
parent
3b8a5c9647
commit
f53915c711
11 changed files with 303 additions and 10 deletions
|
@ -124,14 +124,14 @@ const FILE_ICONS: &[(&str, &str)] = &[
|
|||
("zig", "icons/file_icons/zig.svg"),
|
||||
];
|
||||
|
||||
/// The ID of the default icon theme.
|
||||
pub(crate) const DEFAULT_ICON_THEME_ID: &str = "zed";
|
||||
/// The name of the default icon theme.
|
||||
pub(crate) const DEFAULT_ICON_THEME_NAME: &str = "Zed (Default)";
|
||||
|
||||
/// Returns the default icon theme.
|
||||
pub fn default_icon_theme() -> IconTheme {
|
||||
IconTheme {
|
||||
id: DEFAULT_ICON_THEME_ID.into(),
|
||||
name: "Zed (Default)".into(),
|
||||
id: "zed".into(),
|
||||
name: DEFAULT_ICON_THEME_NAME.into(),
|
||||
appearance: Appearance::Dark,
|
||||
directory_icons: DirectoryIcons {
|
||||
collapsed: Some("icons/file_icons/folder.svg".into()),
|
||||
|
|
44
crates/theme/src/icon_theme_schema.rs
Normal file
44
crates/theme/src/icon_theme_schema.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::SharedString;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::AppearanceContent;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct IconThemeFamilyContent {
|
||||
pub name: String,
|
||||
pub author: String,
|
||||
pub themes: Vec<IconThemeContent>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct IconThemeContent {
|
||||
pub name: String,
|
||||
pub appearance: AppearanceContent,
|
||||
#[serde(default)]
|
||||
pub directory_icons: DirectoryIconsContent,
|
||||
#[serde(default)]
|
||||
pub chevron_icons: ChevronIconsContent,
|
||||
#[serde(default)]
|
||||
pub file_icons: HashMap<String, IconDefinitionContent>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct DirectoryIconsContent {
|
||||
pub collapsed: Option<SharedString>,
|
||||
pub expanded: Option<SharedString>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ChevronIconsContent {
|
||||
pub collapsed: Option<SharedString>,
|
||||
pub expanded: Option<SharedString>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct IconDefinitionContent {
|
||||
pub path: SharedString,
|
||||
}
|
|
@ -11,8 +11,9 @@ use parking_lot::RwLock;
|
|||
use util::ResultExt;
|
||||
|
||||
use crate::{
|
||||
read_user_theme, refine_theme_family, Appearance, IconTheme, Theme, ThemeFamily,
|
||||
ThemeFamilyContent, DEFAULT_ICON_THEME_ID,
|
||||
read_icon_theme, read_user_theme, refine_theme_family, Appearance, AppearanceContent,
|
||||
ChevronIcons, DirectoryIcons, IconDefinition, IconTheme, Theme, ThemeFamily,
|
||||
ThemeFamilyContent, DEFAULT_ICON_THEME_NAME,
|
||||
};
|
||||
|
||||
/// The metadata for a theme.
|
||||
|
@ -80,7 +81,7 @@ impl ThemeRegistry {
|
|||
|
||||
let default_icon_theme = crate::default_icon_theme();
|
||||
registry.state.write().icon_themes.insert(
|
||||
default_icon_theme.id.clone().into(),
|
||||
default_icon_theme.name.clone(),
|
||||
Arc::new(default_icon_theme),
|
||||
);
|
||||
|
||||
|
@ -208,7 +209,7 @@ impl ThemeRegistry {
|
|||
|
||||
/// Returns the default icon theme.
|
||||
pub fn default_icon_theme(&self) -> Result<Arc<IconTheme>> {
|
||||
self.get_icon_theme(DEFAULT_ICON_THEME_ID)
|
||||
self.get_icon_theme(DEFAULT_ICON_THEME_NAME)
|
||||
}
|
||||
|
||||
/// Returns the icon theme with the specified name.
|
||||
|
@ -220,6 +221,67 @@ impl ThemeRegistry {
|
|||
.ok_or_else(|| anyhow!("icon theme not found: {name}"))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Removes the icon themes with the given names from the registry.
|
||||
pub fn remove_icon_themes(&self, icon_themes_to_remove: &[SharedString]) {
|
||||
self.state
|
||||
.write()
|
||||
.icon_themes
|
||||
.retain(|name, _| !icon_themes_to_remove.contains(name))
|
||||
}
|
||||
|
||||
/// Loads the icon theme from the specified path and adds it to the registry.
|
||||
///
|
||||
/// The `icons_root_dir` parameter indicates the root directory from which
|
||||
/// the relative paths to icons in the theme should be resolved against.
|
||||
pub async fn load_icon_theme(
|
||||
&self,
|
||||
icon_theme_path: &Path,
|
||||
icons_root_dir: &Path,
|
||||
fs: Arc<dyn Fs>,
|
||||
) -> Result<()> {
|
||||
let icon_theme_family = read_icon_theme(icon_theme_path, fs).await?;
|
||||
|
||||
let mut state = self.state.write();
|
||||
for icon_theme in icon_theme_family.themes {
|
||||
let icon_theme = IconTheme {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
name: icon_theme.name.into(),
|
||||
appearance: match icon_theme.appearance {
|
||||
AppearanceContent::Light => Appearance::Light,
|
||||
AppearanceContent::Dark => Appearance::Dark,
|
||||
},
|
||||
directory_icons: DirectoryIcons {
|
||||
collapsed: icon_theme.directory_icons.collapsed,
|
||||
expanded: icon_theme.directory_icons.expanded,
|
||||
},
|
||||
chevron_icons: ChevronIcons {
|
||||
collapsed: icon_theme.chevron_icons.collapsed,
|
||||
expanded: icon_theme.chevron_icons.expanded,
|
||||
},
|
||||
file_icons: icon_theme
|
||||
.file_icons
|
||||
.into_iter()
|
||||
.map(|(key, icon)| {
|
||||
let path = icons_root_dir.join(icon.path.as_ref());
|
||||
|
||||
(
|
||||
key,
|
||||
IconDefinition {
|
||||
path: path.to_string_lossy().to_string().into(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
state
|
||||
.icon_themes
|
||||
.insert(icon_theme.name.clone(), Arc::new(icon_theme));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ThemeRegistry {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::fallback_themes::zed_default_dark;
|
||||
use crate::{
|
||||
Appearance, IconTheme, SyntaxTheme, Theme, ThemeRegistry, ThemeStyleContent,
|
||||
DEFAULT_ICON_THEME_ID,
|
||||
DEFAULT_ICON_THEME_NAME,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
|
@ -647,7 +647,7 @@ impl settings::Settings for ThemeSettings {
|
|||
.icon_theme
|
||||
.as_ref()
|
||||
.and_then(|name| themes.get_icon_theme(name).ok())
|
||||
.unwrap_or_else(|| themes.get_icon_theme(DEFAULT_ICON_THEME_ID).unwrap()),
|
||||
.unwrap_or_else(|| themes.get_icon_theme(DEFAULT_ICON_THEME_NAME).unwrap()),
|
||||
ui_density: defaults.ui_density.unwrap_or(UiDensity::Default),
|
||||
unnecessary_code_fade: defaults.unnecessary_code_fade.unwrap_or(0.0),
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ mod default_colors;
|
|||
mod fallback_themes;
|
||||
mod font_family_cache;
|
||||
mod icon_theme;
|
||||
mod icon_theme_schema;
|
||||
mod registry;
|
||||
mod scale;
|
||||
mod schema;
|
||||
|
@ -34,6 +35,7 @@ use uuid::Uuid;
|
|||
pub use crate::default_colors::*;
|
||||
pub use crate::font_family_cache::*;
|
||||
pub use crate::icon_theme::*;
|
||||
pub use crate::icon_theme_schema::*;
|
||||
pub use crate::registry::*;
|
||||
pub use crate::scale::*;
|
||||
pub use crate::schema::*;
|
||||
|
@ -364,3 +366,14 @@ pub async fn read_user_theme(theme_path: &Path, fs: Arc<dyn Fs>) -> Result<Theme
|
|||
|
||||
Ok(theme_family)
|
||||
}
|
||||
|
||||
/// Asynchronously reads the icon theme from the specified path.
|
||||
pub async fn read_icon_theme(
|
||||
icon_theme_path: &Path,
|
||||
fs: Arc<dyn Fs>,
|
||||
) -> Result<IconThemeFamilyContent> {
|
||||
let reader = fs.open_sync(icon_theme_path).await?;
|
||||
let icon_theme_family: IconThemeFamilyContent = serde_json_lenient::from_reader(reader)?;
|
||||
|
||||
Ok(icon_theme_family)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue