icon.rs

  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}