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::prelude::*;
  9use crate::traits::transformable::Transformable;
 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    BusinessStamp,
 17    Grid,
 18    ProTrialStamp,
 19    ProUserStamp,
 20    StudentStamp,
 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
 76impl Transformable for Vector {
 77    fn transform(mut self, transformation: Transformation) -> Self {
 78        self.transformation = transformation;
 79        self
 80    }
 81}
 82
 83impl RenderOnce for Vector {
 84    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
 85        let width = self.size.width;
 86        let height = self.size.height;
 87
 88        svg()
 89            // By default, prevent the SVG from stretching
 90            // to fill its container.
 91            .flex_none()
 92            .w(width)
 93            .h(height)
 94            .path(self.path)
 95            .text_color(self.color.color(cx))
 96            .with_transformation(self.transformation)
 97    }
 98}
 99
100impl Component for Vector {
101    fn scope() -> ComponentScope {
102        ComponentScope::Images
103    }
104
105    fn name() -> &'static str {
106        "Vector"
107    }
108
109    fn description() -> Option<&'static str> {
110        Some("A vector image component that can be displayed at specific sizes.")
111    }
112
113    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
114        let size = rems_from_px(60.);
115
116        Some(
117            v_flex()
118                .gap_6()
119                .children(vec![
120                    example_group_with_title(
121                        "Basic Usage",
122                        vec![
123                            single_example(
124                                "Default",
125                                Vector::square(VectorName::ZedLogo, size).into_any_element(),
126                            ),
127                            single_example(
128                                "Custom Size",
129                                h_flex()
130                                    .h(rems_from_px(120.))
131                                    .justify_center()
132                                    .child(Vector::new(
133                                        VectorName::ZedLogo,
134                                        rems_from_px(120.),
135                                        rems_from_px(200.),
136                                    ))
137                                    .into_any_element(),
138                            ),
139                        ],
140                    ),
141                    example_group_with_title(
142                        "Colored",
143                        vec![
144                            single_example(
145                                "Accent Color",
146                                Vector::square(VectorName::ZedLogo, size)
147                                    .color(Color::Accent)
148                                    .into_any_element(),
149                            ),
150                            single_example(
151                                "Error Color",
152                                Vector::square(VectorName::ZedLogo, size)
153                                    .color(Color::Error)
154                                    .into_any_element(),
155                            ),
156                        ],
157                    ),
158                    example_group_with_title(
159                        "Different Vectors",
160                        vec![single_example(
161                            "Zed X Copilot",
162                            Vector::square(VectorName::ZedXCopilot, rems_from_px(100.))
163                                .into_any_element(),
164                        )],
165                    ),
166                ])
167                .into_any_element(),
168        )
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn vector_path() {
178        assert_eq!(VectorName::ZedLogo.path().as_ref(), "images/zed_logo.svg");
179    }
180}