Skip to main content

gpui_macos/
native_assets.rs

1//! Runtime SF Symbols for both macOS adapters and upstream GPUI Kit icons.
2use gpui_kit::{AssetSource, Result, SharedString};
3use std::borrow::Cow;
4
5/// An asset source that substitutes host-supplied SF Symbols on macOS.
6///
7/// Wrap the application's existing asset source with `NativeAssets::new(source)`.
8/// Icon paths keep their upstream names, so GPUI Kit's own nested icons also use
9/// the native artwork. Unavailable symbols, custom assets, and other platforms
10/// delegate to the supplied source. System artwork is generated only in memory.
11#[derive(Clone, Copy, Debug, Default)]
12pub struct NativeAssets<A> {
13    fallback: A,
14}
15
16impl<A> NativeAssets<A> {
17    pub fn new(fallback: A) -> Self {
18        Self { fallback }
19    }
20}
21
22impl<A: AssetSource> AssetSource for NativeAssets<A> {
23    fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
24        #[cfg(target_os = "macos")]
25        if let Some(svg) = crate::native_symbols::svg_for_path(path) {
26            return Ok(Some(Cow::Owned(svg.as_ref().to_vec())));
27        }
28        self.fallback.load(path)
29    }
30
31    fn list(&self, path: &str) -> Result<Vec<SharedString>> {
32        self.fallback.list(path)
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    struct CustomAssets;
41    impl AssetSource for CustomAssets {
42        fn load(&self, _: &str) -> Result<Option<Cow<'static, [u8]>>> {
43            Ok(Some(Cow::Borrowed(b"custom asset bytes")))
44        }
45        fn list(&self, _: &str) -> Result<Vec<SharedString>> {
46            Ok(vec!["custom.svg".into()])
47        }
48    }
49
50    #[test]
51    fn custom_assets_and_listing_pass_through() {
52        let assets = NativeAssets::new(CustomAssets);
53        assert_eq!(
54            assets.load("custom.svg").unwrap().unwrap().as_ref(),
55            b"custom asset bytes"
56        );
57        assert_eq!(
58            assets.list("").unwrap(),
59            vec![SharedString::from("custom.svg")]
60        );
61    }
62
63    #[test]
64    fn fallback_artwork_is_embedded_in_debug_application_bundles() {
65        let bytes = gpui_kit::assets::AllAssets
66            .load("icons/star.svg")
67            .expect("bundled fallback icon loads")
68            .expect("bundled fallback icon exists");
69        assert!(
70            matches!(bytes, Cow::Borrowed(_)),
71            "App bundles must load embedded bytes without opening checkout files"
72        );
73        assert!(String::from_utf8_lossy(&bytes).contains("<svg"));
74    }
75
76    #[cfg(target_os = "macos")]
77    #[test]
78    fn gpui_svg_renderer_accepts_native_symbols_with_embedded_alpha() {
79        use gpui_kit::{DevicePixels, SvgRenderer, SvgSize, size};
80        use std::sync::Arc;
81        let assets = NativeAssets::new(gpui_kit::assets::AllAssets);
82        let bytes = assets.load("icons/check.svg").unwrap().unwrap();
83        assert!(String::from_utf8_lossy(&bytes).contains("data:image/png;base64,"));
84        let renderer = SvgRenderer::new(Arc::new(assets));
85        let parsed = renderer
86            .parse_svg(&bytes)
87            .expect("embedded SF Symbol PNG parses");
88        let image = renderer
89            .render_parsed(
90                &parsed,
91                SvgSize::ExactSize(size(DevicePixels(128), DevicePixels(128))),
92            )
93            .expect("embedded SF Symbol PNG rasterizes");
94        let pixels = image.as_bytes(0).unwrap();
95        assert_eq!(pixels.len(), 128 * 128 * 4);
96        assert!(pixels.as_chunks::<4>().0.iter().any(|pixel| pixel[3] > 0));
97        assert!(pixels.as_chunks::<4>().0.iter().any(|pixel| pixel[3] == 0));
98        let native = crate::native_symbols::image(
99            crate::IconName::Check,
100            16.,
101            8.,
102            gpui_kit::FontWeight::NORMAL,
103            gpui_kit::rgb(0xffffff).into(),
104        )
105        .unwrap();
106        let native_alpha = native
107            .as_bytes(0)
108            .unwrap()
109            .as_chunks::<4>()
110            .0
111            .iter()
112            .map(|pixel| pixel[3]);
113        let rendered_alpha = pixels.as_chunks::<4>().0.iter().map(|pixel| pixel[3]);
114        assert!(
115            native_alpha.eq(rendered_alpha),
116            "the Kit SVG pipeline preserves native symbol coverage exactly"
117        );
118    }
119}