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