image.rs

  1use std::sync::Arc;
  2
  3use gpui::{App, IntoElement, Rems, RenderOnce, Size, Styled, Window, svg};
  4use serde::{Deserialize, Serialize};
  5use strum::{EnumIter, EnumString, IntoStaticStr};
  6
  7use crate::Color;
  8use crate::prelude::*;
  9
 10#[derive(
 11    Debug, PartialEq, Eq, Copy, Clone, EnumIter, EnumString, IntoStaticStr, Serialize, Deserialize,
 12)]
 13#[strum(serialize_all = "snake_case")]
 14pub enum VectorName {
 15    ZedLogo,
 16    ZedXCopilot,
 17    Grid,
 18    AiGrid,
 19    DebuggerGrid,
 20}
 21
 22impl VectorName {
 23    /// Returns the path to this vector image.
 24    pub fn path(&self) -> Arc<str> {
 25        let file_stem: &'static str = self.into();
 26        format!("images/{file_stem}.svg").into()
 27    }
 28}
 29
 30/// A vector image, such as an SVG.
 31///
 32/// A [`Vector`] is different from an [`crate::Icon`] in that it is intended
 33/// to be displayed at a specific size, or series of sizes, rather
 34/// than conforming to the standard size of an icon.
 35#[derive(IntoElement, RegisterComponent)]
 36pub struct Vector {
 37    path: Arc<str>,
 38    color: Color,
 39    size: Size<Rems>,
 40}
 41
 42impl Vector {
 43    /// Creates a new [`Vector`] image with the given [`VectorName`] and size.
 44    pub fn new(vector: VectorName, width: Rems, height: Rems) -> Self {
 45        Self {
 46            path: vector.path(),
 47            color: Color::default(),
 48            size: Size { width, height },
 49        }
 50    }
 51
 52    /// Creates a new [`Vector`] image where the width and height are the same.
 53    pub fn square(vector: VectorName, size: Rems) -> Self {
 54        Self::new(vector, size, size)
 55    }
 56
 57    /// Sets the vector color.
 58    pub fn color(mut self, color: Color) -> Self {
 59        self.color = color;
 60        self
 61    }
 62
 63    /// Sets the vector size.
 64    pub fn size(mut self, size: impl Into<Size<Rems>>) -> Self {
 65        let size = size.into();
 66        self.size = size;
 67        self
 68    }
 69}
 70
 71impl RenderOnce for Vector {
 72    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
 73        let width = self.size.width;
 74        let height = self.size.height;
 75
 76        svg()
 77            // By default, prevent the SVG from stretching
 78            // to fill its container.
 79            .flex_none()
 80            .w(width)
 81            .h(height)
 82            .path(self.path)
 83            .text_color(self.color.color(cx))
 84    }
 85}
 86
 87impl Component for Vector {
 88    fn scope() -> ComponentScope {
 89        ComponentScope::Images
 90    }
 91
 92    fn name() -> &'static str {
 93        "Vector"
 94    }
 95
 96    fn description() -> Option<&'static str> {
 97        Some("A vector image component that can be displayed at specific sizes.")
 98    }
 99
100    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
101        Some(
102            v_flex()
103                .gap_6()
104                .children(vec![
105                    example_group_with_title(
106                        "Basic Usage",
107                        vec![
108                            single_example(
109                                "Default",
110                                Vector::square(VectorName::ZedLogo, rems(8.)).into_any_element(),
111                            ),
112                            single_example(
113                                "Custom Size",
114                                Vector::new(VectorName::ZedLogo, rems(12.), rems(6.))
115                                    .into_any_element(),
116                            ),
117                        ],
118                    ),
119                    example_group_with_title(
120                        "Colored",
121                        vec![
122                            single_example(
123                                "Accent Color",
124                                Vector::square(VectorName::ZedLogo, rems(8.))
125                                    .color(Color::Accent)
126                                    .into_any_element(),
127                            ),
128                            single_example(
129                                "Error Color",
130                                Vector::square(VectorName::ZedLogo, rems(8.))
131                                    .color(Color::Error)
132                                    .into_any_element(),
133                            ),
134                        ],
135                    ),
136                    example_group_with_title(
137                        "Different Vectors",
138                        vec![
139                            single_example(
140                                "Zed Logo",
141                                Vector::square(VectorName::ZedLogo, rems(8.)).into_any_element(),
142                            ),
143                            single_example(
144                                "Zed X Copilot",
145                                Vector::square(VectorName::ZedXCopilot, rems(8.))
146                                    .into_any_element(),
147                            ),
148                        ],
149                    ),
150                ])
151                .into_any_element(),
152        )
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn vector_path() {
162        assert_eq!(VectorName::ZedLogo.path().as_ref(), "images/zed_logo.svg");
163    }
164}