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 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        let size = rems_from_px(60.);
119
120        Some(
121            v_flex()
122                .gap_6()
123                .children(vec![
124                    example_group_with_title(
125                        "Basic Usage",
126                        vec![
127                            single_example(
128                                "Default",
129                                Vector::square(VectorName::ZedLogo, size).into_any_element(),
130                            ),
131                            single_example(
132                                "Custom Size",
133                                h_flex()
134                                    .h(rems_from_px(120.))
135                                    .justify_center()
136                                    .child(Vector::new(
137                                        VectorName::ZedLogo,
138                                        rems_from_px(120.),
139                                        rems_from_px(200.),
140                                    ))
141                                    .into_any_element(),
142                            ),
143                        ],
144                    ),
145                    example_group_with_title(
146                        "Colored",
147                        vec![
148                            single_example(
149                                "Accent Color",
150                                Vector::square(VectorName::ZedLogo, size)
151                                    .color(Color::Accent)
152                                    .into_any_element(),
153                            ),
154                            single_example(
155                                "Error Color",
156                                Vector::square(VectorName::ZedLogo, size)
157                                    .color(Color::Error)
158                                    .into_any_element(),
159                            ),
160                        ],
161                    ),
162                    example_group_with_title(
163                        "Different Vectors",
164                        vec![single_example(
165                            "Zed X Copilot",
166                            Vector::square(VectorName::ZedXCopilot, rems_from_px(100.))
167                                .into_any_element(),
168                        )],
169                    ),
170                ])
171                .into_any_element(),
172        )
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn vector_path() {
182        assert_eq!(VectorName::ZedLogo.path().as_ref(), "images/zed_logo.svg");
183    }
184}