gpui: Add support for rendering SVG from external files (#42024)

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
Danilo Leal
2025-11-05 16:12:30 -03:00
committed by GitHub
co-authored by Mikayla Maki
parent 4724aa5cb8
commit feed34cafe
6 changed files with 152 additions and 31 deletions
+41 -1
View File
@@ -48,6 +48,7 @@ pub struct ContextMenuEntry {
label: SharedString,
icon: Option<IconName>,
custom_icon_path: Option<SharedString>,
custom_icon_svg: Option<SharedString>,
icon_position: IconPosition,
icon_size: IconSize,
icon_color: Option<Color>,
@@ -68,6 +69,7 @@ impl ContextMenuEntry {
label: label.into(),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_position: IconPosition::Start,
icon_size: IconSize::Small,
icon_color: None,
@@ -94,7 +96,15 @@ impl ContextMenuEntry {
pub fn custom_icon_path(mut self, path: impl Into<SharedString>) -> Self {
self.custom_icon_path = Some(path.into());
self.icon = None; // Clear IconName if custom path is set
self.custom_icon_svg = None; // Clear other icon sources if custom path is set
self.icon = None;
self
}
pub fn custom_icon_svg(mut self, svg: impl Into<SharedString>) -> Self {
self.custom_icon_svg = Some(svg.into());
self.custom_icon_path = None; // Clear other icon sources if custom path is set
self.icon = None;
self
}
@@ -396,6 +406,7 @@ impl ContextMenu {
handler: Rc::new(move |_, window, cx| handler(window, cx)),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_position: IconPosition::End,
icon_size: IconSize::Small,
icon_color: None,
@@ -425,6 +436,7 @@ impl ContextMenu {
handler: Rc::new(move |_, window, cx| handler(window, cx)),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_position: IconPosition::End,
icon_size: IconSize::Small,
icon_color: None,
@@ -454,6 +466,7 @@ impl ContextMenu {
handler: Rc::new(move |_, window, cx| handler(window, cx)),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_position: IconPosition::End,
icon_size: IconSize::Small,
icon_color: None,
@@ -482,6 +495,7 @@ impl ContextMenu {
handler: Rc::new(move |_, window, cx| handler(window, cx)),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_position: position,
icon_size: IconSize::Small,
icon_color: None,
@@ -541,6 +555,7 @@ impl ContextMenu {
}),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_position: IconPosition::End,
icon_size: IconSize::Small,
icon_color: None,
@@ -572,6 +587,7 @@ impl ContextMenu {
}),
icon: None,
custom_icon_path: None,
custom_icon_svg: None,
icon_size: IconSize::Small,
icon_position: IconPosition::End,
icon_color: None,
@@ -593,6 +609,7 @@ impl ContextMenu {
handler: Rc::new(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)),
icon: Some(IconName::ArrowUpRight),
custom_icon_path: None,
custom_icon_svg: None,
icon_size: IconSize::XSmall,
icon_position: IconPosition::End,
icon_color: None,
@@ -913,6 +930,7 @@ impl ContextMenu {
handler,
icon,
custom_icon_path,
custom_icon_svg,
icon_position,
icon_size,
icon_color,
@@ -965,6 +983,28 @@ impl ContextMenu {
)
})
.into_any_element()
} else if let Some(custom_icon_svg) = custom_icon_svg {
h_flex()
.gap_1p5()
.when(
*icon_position == IconPosition::Start && toggle.is_none(),
|flex| {
flex.child(
Icon::from_external_svg(custom_icon_svg.clone())
.size(*icon_size)
.color(icon_color),
)
},
)
.child(Label::new(label.clone()).color(label_color).truncate())
.when(*icon_position == IconPosition::End, |flex| {
flex.child(
Icon::from_external_svg(custom_icon_svg.clone())
.size(*icon_size)
.color(icon_color),
)
})
.into_any_element()
} else if let Some(icon_name) = icon {
h_flex()
.gap_1p5()
+26 -10
View File
@@ -115,24 +115,24 @@ impl From<IconName> for Icon {
/// The source of an icon.
enum IconSource {
/// An SVG embedded in the Zed binary.
Svg(SharedString),
Embedded(SharedString),
/// An image file located at the specified path.
///
/// Currently our SVG renderer is missing support for the following features:
/// 1. Loading SVGs from external files.
/// 2. Rendering polychrome SVGs.
/// Currently our SVG renderer is missing support for rendering polychrome SVGs.
///
/// In order to support icon themes, we render the icons as images instead.
Image(Arc<Path>),
External(Arc<Path>),
/// An SVG not embedded in the Zed binary.
ExternalSvg(SharedString),
}
impl IconSource {
fn from_path(path: impl Into<SharedString>) -> Self {
let path = path.into();
if path.starts_with("icons/") {
Self::Svg(path)
Self::Embedded(path)
} else {
Self::Image(Arc::from(PathBuf::from(path.as_ref())))
Self::External(Arc::from(PathBuf::from(path.as_ref())))
}
}
}
@@ -148,7 +148,7 @@ pub struct Icon {
impl Icon {
pub fn new(icon: IconName) -> Self {
Self {
source: IconSource::Svg(icon.path().into()),
source: IconSource::Embedded(icon.path().into()),
color: Color::default(),
size: IconSize::default().rems(),
transformation: Transformation::default(),
@@ -164,6 +164,15 @@ impl Icon {
}
}
pub fn from_external_svg(svg: SharedString) -> Self {
Self {
source: IconSource::ExternalSvg(svg),
color: Color::default(),
size: IconSize::default().rems(),
transformation: Transformation::default(),
}
}
pub fn color(mut self, color: Color) -> Self {
self.color = color;
self
@@ -193,14 +202,21 @@ impl Transformable for Icon {
impl RenderOnce for Icon {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
match self.source {
IconSource::Svg(path) => svg()
IconSource::Embedded(path) => svg()
.with_transformation(self.transformation)
.size(self.size)
.flex_none()
.path(path)
.text_color(self.color.color(cx))
.into_any_element(),
IconSource::Image(path) => img(path)
IconSource::ExternalSvg(path) => svg()
.external_path(path)
.with_transformation(self.transformation)
.size(self.size)
.flex_none()
.text_color(self.color.color(cx))
.into_any_element(),
IconSource::External(path) => img(path)
.size(self.size)
.flex_none()
.text_color(self.color.color(cx))