image.rs

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