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