Primitives · Icon::from_path draws an application's SVG with the theme's size and color; a path the asset source lacks fails at render

This commit is contained in:
Amit Verma authored and 雷电芽衣 committed 2026-10-04 17:24:39 -04:00
1 parent 9977785de6
commit dfe1e9f42c
9 files changed
+145 -17

No files matched your search

+59 -6
View File
@@ -1,6 +1,8 @@
use std::collections::HashSet;
use gpui::{
App, Hsla, IntoElement, Radians, RenderOnce, SharedString, Transformation, Window, prelude::*,
size, svg,
App, AssetSource, Global, Hsla, IntoElement, Radians, RenderOnce, SharedString, Transformation,
Window, prelude::*, size, svg,
};
use crate::theme::{ActiveTheme, IconSize};
@@ -343,7 +345,8 @@ icons! {
#[derive(IntoElement)]
pub struct Icon {
name: IconName,
path: SharedString,
bundled: bool,
size: IconSize,
color: Option<Hsla>,
hover: Option<(SharedString, Hsla)>,
@@ -354,7 +357,16 @@ pub struct Icon {
impl Icon {
pub fn new(name: IconName) -> Self {
Self {
name,
bundled: true,
..Self::from_path(name.path())
}
}
/// Draws an SVG supplied by the application's asset source; a missing one fails at render.
pub fn from_path(path: impl Into<SharedString>) -> Self {
Self {
path: path.into(),
bundled: false,
size: IconSize::Md,
color: None,
hover: None,
@@ -402,12 +414,32 @@ impl From<IconName> for Icon {
}
}
/// App paths already found in the asset source.
#[derive(Default)]
struct FoundIcons(HashSet<SharedString>);
impl Global for FoundIcons {}
fn ensure_found(source: &dyn AssetSource, path: &str) {
match source.load(path) {
Ok(Some(_)) => {}
Ok(None) => panic!("ely: icon {path} is not in the app's asset source"),
Err(error) => panic!("ely: icon {path} failed to load: {error:#}"),
}
}
impl RenderOnce for Icon {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
if !self.bundled && !cx.default_global::<FoundIcons>().0.contains(&self.path) {
ensure_found(cx.asset_source().as_ref(), &self.path);
cx.default_global::<FoundIcons>()
.0
.insert(self.path.clone());
}
let theme = cx.theme();
let color = self.color.unwrap_or(theme.colors.fg);
svg()
.path(self.name.path())
.path(self.path)
.size(theme.icon_size(self.size))
.flex_none()
.text_color(color)
@@ -425,7 +457,7 @@ impl RenderOnce for Icon {
#[cfg(test)]
mod tests {
use super::IconName;
use super::{Icon, IconName, ensure_found};
#[test]
fn every_icon_ships_in_the_bundle() {
@@ -437,4 +469,25 @@ mod tests {
);
}
}
#[test]
fn an_app_path_draws_as_given_and_a_name_as_its_bundled_file() {
let custom = Icon::from_path("app-icons/mark.svg");
assert_eq!(custom.path.as_ref(), "app-icons/mark.svg");
assert!(!custom.bundled);
let named = Icon::new(IconName::Check);
assert_eq!(named.path.as_ref(), IconName::Check.path());
assert!(named.bundled);
}
#[test]
fn a_path_the_source_holds_is_found() {
ensure_found(&crate::Assets, IconName::Check.path());
}
#[test]
#[should_panic(expected = "ely: icon app-icons/missing.svg is not in the app's asset source")]
fn a_path_the_source_lacks_fails() {
ensure_found(&crate::Assets, "app-icons/missing.svg");
}
}