1use gpui::{svg, Hsla, IntoElement, Rems, Transformation};
2use strum::EnumIter;
3
4use crate::{prelude::*, Indicator};
5
6#[derive(Default, PartialEq, Copy, Clone)]
7pub enum IconSize {
8 Indicator,
9 XSmall,
10 Small,
11 #[default]
12 Medium,
13}
14
15impl IconSize {
16 pub fn rems(self) -> Rems {
17 match self {
18 IconSize::Indicator => rems_from_px(10.),
19 IconSize::XSmall => rems_from_px(12.),
20 IconSize::Small => rems_from_px(14.),
21 IconSize::Medium => rems_from_px(16.),
22 }
23 }
24}
25
26#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
27pub enum IconName {
28 Ai,
29 ArrowDown,
30 ArrowLeft,
31 ArrowRight,
32 ArrowUp,
33 ArrowUpRight,
34 ArrowCircle,
35 AtSign,
36 AudioOff,
37 AudioOn,
38 Backspace,
39 Bell,
40 BellOff,
41 BellRing,
42 BellDot,
43 Bolt,
44 CaseSensitive,
45 Check,
46 ChevronDown,
47 ChevronLeft,
48 ChevronRight,
49 ChevronUp,
50 ExpandVertical,
51 Close,
52 Collab,
53 Command,
54 Control,
55 Copilot,
56 CopilotDisabled,
57 CopilotError,
58 CopilotInit,
59 Copy,
60 Dash,
61 Delete,
62 Disconnected,
63 Ellipsis,
64 Envelope,
65 Escape,
66 ExclamationTriangle,
67 Exit,
68 ExternalLink,
69 File,
70 FileDoc,
71 FileGeneric,
72 FileGit,
73 FileLock,
74 FileRust,
75 FileToml,
76 FileTree,
77 Filter,
78 Folder,
79 FolderOpen,
80 FolderX,
81 Github,
82 Hash,
83 InlayHint,
84 Link,
85 MagicWand,
86 MagnifyingGlass,
87 MailOpen,
88 Maximize,
89 Menu,
90 MessageBubbles,
91 Mic,
92 MicMute,
93 Minimize,
94 Option,
95 PageDown,
96 PageUp,
97 Pencil,
98 Person,
99 Play,
100 Plus,
101 Public,
102 Quote,
103 Regex,
104 Replace,
105 ReplaceAll,
106 ReplaceNext,
107 Return,
108 ReplyArrowRight,
109 Settings,
110 Sliders,
111 Screen,
112 SelectAll,
113 Server,
114 Shift,
115 Snip,
116 Space,
117 Split,
118 Tab,
119 Terminal,
120 Trash,
121 Update,
122 WholeWord,
123 XCircle,
124 ZedXCopilot,
125 ZedAssistant,
126 PullRequest,
127}
128
129impl IconName {
130 pub fn path(self) -> &'static str {
131 match self {
132 IconName::Ai => "icons/ai.svg",
133 IconName::ArrowDown => "icons/arrow_down.svg",
134 IconName::ArrowLeft => "icons/arrow_left.svg",
135 IconName::ArrowRight => "icons/arrow_right.svg",
136 IconName::ArrowUp => "icons/arrow_up.svg",
137 IconName::ArrowUpRight => "icons/arrow_up_right.svg",
138 IconName::ArrowCircle => "icons/arrow_circle.svg",
139 IconName::AtSign => "icons/at_sign.svg",
140 IconName::AudioOff => "icons/speaker_off.svg",
141 IconName::AudioOn => "icons/speaker_loud.svg",
142 IconName::Backspace => "icons/backspace.svg",
143 IconName::Bell => "icons/bell.svg",
144 IconName::BellOff => "icons/bell_off.svg",
145 IconName::BellRing => "icons/bell_ring.svg",
146 IconName::BellDot => "icons/bell_dot.svg",
147 IconName::Bolt => "icons/bolt.svg",
148 IconName::CaseSensitive => "icons/case_insensitive.svg",
149 IconName::Check => "icons/check.svg",
150 IconName::ChevronDown => "icons/chevron_down.svg",
151 IconName::ChevronLeft => "icons/chevron_left.svg",
152 IconName::ChevronRight => "icons/chevron_right.svg",
153 IconName::ChevronUp => "icons/chevron_up.svg",
154 IconName::ExpandVertical => "icons/expand_vertical.svg",
155 IconName::Close => "icons/x.svg",
156 IconName::Collab => "icons/user_group_16.svg",
157 IconName::Command => "icons/command.svg",
158 IconName::Control => "icons/control.svg",
159 IconName::Copilot => "icons/copilot.svg",
160 IconName::CopilotDisabled => "icons/copilot_disabled.svg",
161 IconName::CopilotError => "icons/copilot_error.svg",
162 IconName::CopilotInit => "icons/copilot_init.svg",
163 IconName::Copy => "icons/copy.svg",
164 IconName::Dash => "icons/dash.svg",
165 IconName::Delete => "icons/delete.svg",
166 IconName::Disconnected => "icons/disconnected.svg",
167 IconName::Ellipsis => "icons/ellipsis.svg",
168 IconName::Envelope => "icons/feedback.svg",
169 IconName::Escape => "icons/escape.svg",
170 IconName::ExclamationTriangle => "icons/warning.svg",
171 IconName::Exit => "icons/exit.svg",
172 IconName::ExternalLink => "icons/external_link.svg",
173 IconName::File => "icons/file.svg",
174 IconName::FileDoc => "icons/file_icons/book.svg",
175 IconName::FileGeneric => "icons/file_icons/file.svg",
176 IconName::FileGit => "icons/file_icons/git.svg",
177 IconName::FileLock => "icons/file_icons/lock.svg",
178 IconName::FileRust => "icons/file_icons/rust.svg",
179 IconName::FileToml => "icons/file_icons/toml.svg",
180 IconName::FileTree => "icons/project.svg",
181 IconName::Filter => "icons/filter.svg",
182 IconName::Folder => "icons/file_icons/folder.svg",
183 IconName::FolderOpen => "icons/file_icons/folder_open.svg",
184 IconName::FolderX => "icons/stop_sharing.svg",
185 IconName::Github => "icons/github.svg",
186 IconName::Hash => "icons/hash.svg",
187 IconName::InlayHint => "icons/inlay_hint.svg",
188 IconName::Link => "icons/link.svg",
189 IconName::MagicWand => "icons/magic_wand.svg",
190 IconName::MagnifyingGlass => "icons/magnifying_glass.svg",
191 IconName::MailOpen => "icons/mail_open.svg",
192 IconName::Maximize => "icons/maximize.svg",
193 IconName::Menu => "icons/menu.svg",
194 IconName::MessageBubbles => "icons/conversations.svg",
195 IconName::Mic => "icons/mic.svg",
196 IconName::MicMute => "icons/mic_mute.svg",
197 IconName::Minimize => "icons/minimize.svg",
198 IconName::Option => "icons/option.svg",
199 IconName::PageDown => "icons/page_down.svg",
200 IconName::PageUp => "icons/page_up.svg",
201 IconName::Person => "icons/person.svg",
202 IconName::Pencil => "icons/pencil.svg",
203 IconName::Play => "icons/play.svg",
204 IconName::Plus => "icons/plus.svg",
205 IconName::Public => "icons/public.svg",
206 IconName::Quote => "icons/quote.svg",
207 IconName::Regex => "icons/regex.svg",
208 IconName::Replace => "icons/replace.svg",
209 IconName::ReplaceAll => "icons/replace_all.svg",
210 IconName::ReplaceNext => "icons/replace_next.svg",
211 IconName::Return => "icons/return.svg",
212 IconName::ReplyArrowRight => "icons/reply_arrow_right.svg",
213 IconName::Settings => "icons/file_icons/settings.svg",
214 IconName::Sliders => "icons/sliders.svg",
215 IconName::Screen => "icons/desktop.svg",
216 IconName::SelectAll => "icons/select_all.svg",
217 IconName::Server => "icons/server.svg",
218 IconName::Shift => "icons/shift.svg",
219 IconName::Snip => "icons/snip.svg",
220 IconName::Space => "icons/space.svg",
221 IconName::Split => "icons/split.svg",
222 IconName::Tab => "icons/tab.svg",
223 IconName::Terminal => "icons/terminal.svg",
224 IconName::Trash => "icons/trash.svg",
225 IconName::Update => "icons/update.svg",
226 IconName::WholeWord => "icons/word_search.svg",
227 IconName::XCircle => "icons/error.svg",
228 IconName::ZedXCopilot => "icons/zed_x_copilot.svg",
229 IconName::ZedAssistant => "icons/zed_assistant.svg",
230 IconName::PullRequest => "icons/pull_request.svg",
231 }
232 }
233}
234
235#[derive(IntoElement)]
236pub struct Icon {
237 path: SharedString,
238 color: Color,
239 size: IconSize,
240 transformation: Transformation,
241}
242
243impl Icon {
244 pub fn new(icon: IconName) -> Self {
245 Self {
246 path: icon.path().into(),
247 color: Color::default(),
248 size: IconSize::default(),
249 transformation: Transformation::default(),
250 }
251 }
252
253 pub fn from_path(path: impl Into<SharedString>) -> Self {
254 Self {
255 path: path.into(),
256 color: Color::default(),
257 size: IconSize::default(),
258 transformation: Transformation::default(),
259 }
260 }
261
262 pub fn color(mut self, color: Color) -> Self {
263 self.color = color;
264 self
265 }
266
267 pub fn size(mut self, size: IconSize) -> Self {
268 self.size = size;
269 self
270 }
271
272 pub fn transform(mut self, transformation: Transformation) -> Self {
273 self.transformation = transformation;
274 self
275 }
276}
277
278impl RenderOnce for Icon {
279 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
280 svg()
281 .with_transformation(self.transformation)
282 .size(self.size.rems())
283 .flex_none()
284 .path(self.path)
285 .text_color(self.color.color(cx))
286 }
287}
288
289#[derive(IntoElement)]
290pub struct IconWithIndicator {
291 icon: Icon,
292 indicator: Option<Indicator>,
293 indicator_border_color: Option<Hsla>,
294}
295
296impl IconWithIndicator {
297 pub fn new(icon: Icon, indicator: Option<Indicator>) -> Self {
298 Self {
299 icon,
300 indicator,
301 indicator_border_color: None,
302 }
303 }
304
305 pub fn indicator(mut self, indicator: Option<Indicator>) -> Self {
306 self.indicator = indicator;
307 self
308 }
309
310 pub fn indicator_color(mut self, color: Color) -> Self {
311 if let Some(indicator) = self.indicator.as_mut() {
312 indicator.color = color;
313 }
314 self
315 }
316
317 pub fn indicator_border_color(mut self, color: Option<Hsla>) -> Self {
318 self.indicator_border_color = color;
319 self
320 }
321}
322
323impl RenderOnce for IconWithIndicator {
324 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
325 let indicator_border_color = self
326 .indicator_border_color
327 .unwrap_or_else(|| cx.theme().colors().elevated_surface_background);
328
329 div()
330 .relative()
331 .child(self.icon)
332 .when_some(self.indicator, |this, indicator| {
333 this.child(
334 div()
335 .absolute()
336 .w_2()
337 .h_2()
338 .border()
339 .border_color(indicator_border_color)
340 .rounded_full()
341 .neg_bottom_0p5()
342 .neg_right_1()
343 .child(indicator),
344 )
345 })
346 }
347}