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