1use gpui_kit::{
2 App, IntoElement, Radians, RenderOnce, SharedString, StyleRefinement, Styled, Svg,
3 Transformation, Window, base::StyledExt as _, prelude::FluentBuilder as _, svg,
4};
5
6#[derive(Clone, Debug)]
8pub enum IconSource {
9 Path(SharedString),
12 Bytes(&'static [u8]),
14}
15
16#[derive(Clone, IntoElement)]
39pub struct Icon {
40 source: IconSource,
41 style: StyleRefinement,
42 rotation: Option<Radians>,
43}
44
45impl Icon {
46 pub fn new(path: impl Into<SharedString>) -> Self {
48 Self::from_source(IconSource::Path(path.into()))
49 }
50
51 pub fn from_bytes(bytes: &'static [u8]) -> Self {
53 Self::from_source(IconSource::Bytes(bytes))
54 }
55
56 fn from_source(source: IconSource) -> Self {
57 Self {
58 source,
59 style: StyleRefinement::default(),
60 rotation: None,
61 }
62 }
63
64 pub fn rotate(mut self, angle: impl Into<Radians>) -> Self {
67 self.rotation = Some(angle.into());
68 self
69 }
70
71 pub fn source(&self) -> &IconSource {
73 &self.source
74 }
75}
76
77impl From<&'static str> for Icon {
78 fn from(path: &'static str) -> Self {
79 Self::new(path)
80 }
81}
82
83impl From<SharedString> for Icon {
84 fn from(path: SharedString) -> Self {
85 Self::new(path)
86 }
87}
88
89impl From<&'static [u8]> for Icon {
90 fn from(bytes: &'static [u8]) -> Self {
91 Self::from_bytes(bytes)
92 }
93}
94
95impl<const N: usize> From<&'static [u8; N]> for Icon {
98 fn from(bytes: &'static [u8; N]) -> Self {
99 Self::from_bytes(bytes)
100 }
101}
102
103impl From<gpui_kit::assets::IconName> for Icon {
104 fn from(name: gpui_kit::assets::IconName) -> Self {
105 Self::new(name.path())
106 }
107}
108
109impl Styled for Icon {
110 fn style(&mut self) -> &mut StyleRefinement {
111 &mut self.style
112 }
113}
114
115impl RenderOnce for Icon {
116 fn render(self, window: &mut Window, _: &mut App) -> impl IntoElement {
117 let text_style = window.text_style();
118 let element: Svg = match &self.source {
119 IconSource::Path(path) => svg().path(path.clone()),
120 IconSource::Bytes(bytes) => svg().data(bytes),
121 };
122 element
123 .flex_shrink_0()
124 .size_4()
125 .text_color(text_style.color)
126 .when_some(self.rotation, |this, angle| {
127 this.with_transformation(Transformation::rotate(angle))
128 })
129 .refine_style(&self.style)
130 }
131}