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}