image.rs

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