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