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