image.rs

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