image.rs

  1use gpui::{svg, App, IntoElement, Rems, RenderOnce, Size, Styled, Window};
  2use serde::{Deserialize, Serialize};
  3use strum::{EnumIter, EnumString, IntoStaticStr};
  4use ui_macros::{path_str, DerivePathStr};
  5
  6use crate::Color;
  7
  8#[derive(
  9    Debug,
 10    PartialEq,
 11    Eq,
 12    Copy,
 13    Clone,
 14    EnumIter,
 15    EnumString,
 16    IntoStaticStr,
 17    Serialize,
 18    Deserialize,
 19    DerivePathStr,
 20)]
 21#[strum(serialize_all = "snake_case")]
 22#[path_str(prefix = "images", suffix = ".svg")]
 23pub enum VectorName {
 24    ZedLogo,
 25    ZedXCopilot,
 26}
 27
 28/// A vector image, such as an SVG.
 29///
 30/// A [`Vector`] is different from an [`crate::Icon`] in that it is intended
 31/// to be displayed at a specific size, or series of sizes, rather
 32/// than conforming to the standard size of an icon.
 33#[derive(IntoElement)]
 34pub struct Vector {
 35    path: &'static str,
 36    color: Color,
 37    size: Size<Rems>,
 38}
 39
 40impl Vector {
 41    /// Creates a new [`Vector`] image with the given [`VectorName`] and size.
 42    pub fn new(vector: VectorName, width: Rems, height: Rems) -> Self {
 43        Self {
 44            path: vector.path(),
 45            color: Color::default(),
 46            size: Size { width, height },
 47        }
 48    }
 49
 50    /// Creates a new [`Vector`] image where the width and height are the same.
 51    pub fn square(vector: VectorName, size: Rems) -> Self {
 52        Self::new(vector, size, size)
 53    }
 54
 55    /// Sets the vector color.
 56    pub fn color(mut self, color: Color) -> Self {
 57        self.color = color;
 58        self
 59    }
 60
 61    /// Sets the vector size.
 62    pub fn size(mut self, size: impl Into<Size<Rems>>) -> Self {
 63        let size = size.into();
 64
 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
 86#[cfg(feature = "stories")]
 87pub mod story {
 88    use gpui::Render;
 89    use story::{Story, StoryItem, StorySection};
 90    use strum::IntoEnumIterator;
 91
 92    use crate::prelude::*;
 93
 94    use super::{Vector, VectorName};
 95
 96    pub struct VectorStory;
 97
 98    impl Render for VectorStory {
 99        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
100            Story::container().child(StorySection::new().children(VectorName::iter().map(
101                |vector| StoryItem::new(format!("{:?}", vector), Vector::square(vector, rems(8.))),
102            )))
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn vector_path() {
113        assert_eq!(VectorName::ZedLogo.path(), "images/zed_logo.svg");
114    }
115}