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