icon.rs

  1use gpui::{rems, svg, Hsla};
  2use strum::EnumIter;
  3
  4use crate::{prelude::*, LabelColor};
  5
  6#[derive(Default, PartialEq, Copy, Clone)]
  7pub enum IconSize {
  8    Small,
  9    #[default]
 10    Medium,
 11}
 12
 13#[derive(Default, PartialEq, Copy, Clone)]
 14pub enum IconColor {
 15    #[default]
 16    Default,
 17    Accent,
 18    Created,
 19    Deleted,
 20    Disabled,
 21    Error,
 22    Hidden,
 23    Info,
 24    Modified,
 25    Muted,
 26    Placeholder,
 27    Player(u32),
 28    Selected,
 29    Success,
 30    Warning,
 31}
 32
 33impl IconColor {
 34    pub fn color(self, cx: &WindowContext) -> Hsla {
 35        match self {
 36            IconColor::Default => cx.theme().colors().icon,
 37            IconColor::Muted => cx.theme().colors().icon_muted,
 38            IconColor::Disabled => cx.theme().colors().icon_disabled,
 39            IconColor::Placeholder => cx.theme().colors().icon_placeholder,
 40            IconColor::Accent => cx.theme().colors().icon_accent,
 41            IconColor::Error => cx.theme().status().error,
 42            IconColor::Warning => cx.theme().status().warning,
 43            IconColor::Success => cx.theme().status().success,
 44            IconColor::Info => cx.theme().status().info,
 45            IconColor::Selected => cx.theme().colors().icon_accent,
 46            IconColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor,
 47            IconColor::Created => cx.theme().status().created,
 48            IconColor::Modified => cx.theme().status().modified,
 49            IconColor::Deleted => cx.theme().status().deleted,
 50            IconColor::Hidden => cx.theme().status().hidden,
 51        }
 52    }
 53}
 54
 55impl From<LabelColor> for IconColor {
 56    fn from(label: LabelColor) -> Self {
 57        match label {
 58            LabelColor::Default => IconColor::Default,
 59            LabelColor::Muted => IconColor::Muted,
 60            LabelColor::Disabled => IconColor::Disabled,
 61            LabelColor::Placeholder => IconColor::Placeholder,
 62            LabelColor::Accent => IconColor::Accent,
 63            LabelColor::Error => IconColor::Error,
 64            LabelColor::Warning => IconColor::Warning,
 65            LabelColor::Success => IconColor::Success,
 66            LabelColor::Info => IconColor::Info,
 67            LabelColor::Selected => IconColor::Selected,
 68            LabelColor::Player(i) => IconColor::Player(i),
 69            LabelColor::Created => IconColor::Created,
 70            LabelColor::Modified => IconColor::Modified,
 71            LabelColor::Deleted => IconColor::Deleted,
 72            LabelColor::Hidden => IconColor::Hidden,
 73        }
 74    }
 75}
 76
 77#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
 78pub enum Icon {
 79    Ai,
 80    ArrowLeft,
 81    ArrowRight,
 82    ArrowUpRight,
 83    AudioOff,
 84    AudioOn,
 85    Bolt,
 86    Check,
 87    ChevronDown,
 88    ChevronLeft,
 89    ChevronRight,
 90    ChevronUp,
 91    Close,
 92    Dash,
 93    Exit,
 94    ExclamationTriangle,
 95    File,
 96    FileGeneric,
 97    FileDoc,
 98    FileGit,
 99    FileLock,
100    FileRust,
101    FileToml,
102    FileTree,
103    Folder,
104    FolderOpen,
105    FolderX,
106    Hash,
107    InlayHint,
108    MagicWand,
109    MagnifyingGlass,
110    Maximize,
111    Menu,
112    MessageBubbles,
113    Mic,
114    MicMute,
115    Plus,
116    Quote,
117    Replace,
118    ReplaceAll,
119    Screen,
120    SelectAll,
121    Split,
122    SplitMessage,
123    Terminal,
124    XCircle,
125    Copilot,
126    Envelope,
127    Bell,
128    BellOff,
129    BellRing,
130    MailOpen,
131    AtSign,
132}
133
134impl Icon {
135    pub fn path(self) -> &'static str {
136        match self {
137            Icon::Ai => "icons/ai.svg",
138            Icon::ArrowLeft => "icons/arrow_left.svg",
139            Icon::ArrowRight => "icons/arrow_right.svg",
140            Icon::ArrowUpRight => "icons/arrow_up_right.svg",
141            Icon::AudioOff => "icons/speaker-off.svg",
142            Icon::AudioOn => "icons/speaker-loud.svg",
143            Icon::Bolt => "icons/bolt.svg",
144            Icon::Check => "icons/check.svg",
145            Icon::ChevronDown => "icons/chevron_down.svg",
146            Icon::ChevronLeft => "icons/chevron_left.svg",
147            Icon::ChevronRight => "icons/chevron_right.svg",
148            Icon::ChevronUp => "icons/chevron_up.svg",
149            Icon::Close => "icons/x.svg",
150            Icon::Dash => "icons/dash.svg",
151            Icon::Exit => "icons/exit.svg",
152            Icon::ExclamationTriangle => "icons/warning.svg",
153            Icon::File => "icons/file.svg",
154            Icon::FileGeneric => "icons/file_icons/file.svg",
155            Icon::FileDoc => "icons/file_icons/book.svg",
156            Icon::FileGit => "icons/file_icons/git.svg",
157            Icon::FileLock => "icons/file_icons/lock.svg",
158            Icon::FileRust => "icons/file_icons/rust.svg",
159            Icon::FileToml => "icons/file_icons/toml.svg",
160            Icon::FileTree => "icons/project.svg",
161            Icon::Folder => "icons/file_icons/folder.svg",
162            Icon::FolderOpen => "icons/file_icons/folder_open.svg",
163            Icon::FolderX => "icons/stop_sharing.svg",
164            Icon::Hash => "icons/hash.svg",
165            Icon::InlayHint => "icons/inlay_hint.svg",
166            Icon::MagicWand => "icons/magic-wand.svg",
167            Icon::MagnifyingGlass => "icons/magnifying_glass.svg",
168            Icon::Maximize => "icons/maximize.svg",
169            Icon::Menu => "icons/menu.svg",
170            Icon::MessageBubbles => "icons/conversations.svg",
171            Icon::Mic => "icons/mic.svg",
172            Icon::MicMute => "icons/mic-mute.svg",
173            Icon::Plus => "icons/plus.svg",
174            Icon::Quote => "icons/quote.svg",
175            Icon::Replace => "icons/replace.svg",
176            Icon::ReplaceAll => "icons/replace_all.svg",
177            Icon::Screen => "icons/desktop.svg",
178            Icon::SelectAll => "icons/select-all.svg",
179            Icon::Split => "icons/split.svg",
180            Icon::SplitMessage => "icons/split_message.svg",
181            Icon::Terminal => "icons/terminal.svg",
182            Icon::XCircle => "icons/error.svg",
183            Icon::Copilot => "icons/copilot.svg",
184            Icon::Envelope => "icons/feedback.svg",
185            Icon::Bell => "icons/bell.svg",
186            Icon::BellOff => "icons/bell-off.svg",
187            Icon::BellRing => "icons/bell-ring.svg",
188            Icon::MailOpen => "icons/mail-open.svg",
189            Icon::AtSign => "icons/at-sign.svg",
190        }
191    }
192}
193
194#[derive(Component)]
195pub struct IconElement {
196    icon: Icon,
197    color: IconColor,
198    size: IconSize,
199}
200
201impl IconElement {
202    pub fn new(icon: Icon) -> Self {
203        Self {
204            icon,
205            color: IconColor::default(),
206            size: IconSize::default(),
207        }
208    }
209
210    pub fn color(mut self, color: IconColor) -> Self {
211        self.color = color;
212        self
213    }
214
215    pub fn size(mut self, size: IconSize) -> Self {
216        self.size = size;
217        self
218    }
219
220    fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
221        let svg_size = match self.size {
222            IconSize::Small => rems(0.75),
223            IconSize::Medium => rems(0.9375),
224        };
225
226        svg()
227            .size(svg_size)
228            .flex_none()
229            .path(self.icon.path())
230            .text_color(self.color.color(cx))
231    }
232}
233
234#[cfg(feature = "stories")]
235pub use stories::*;
236
237#[cfg(feature = "stories")]
238mod stories {
239    use gpui::{Div, Render};
240    use strum::IntoEnumIterator;
241
242    use crate::Story;
243
244    use super::*;
245
246    pub struct IconStory;
247
248    impl Render for IconStory {
249        type Element = Div<Self>;
250
251        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
252            let icons = Icon::iter();
253
254            Story::container(cx)
255                .child(Story::title_for::<_, IconElement>(cx))
256                .child(Story::label(cx, "All Icons"))
257                .child(div().flex().gap_3().children(icons.map(IconElement::new)))
258        }
259    }
260}