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::prelude::*;
9use crate::traits::transformable::Transformable;
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 BusinessStamp,
17 Grid,
18 ProTrialStamp,
19 ProUserStamp,
20 StudentStamp,
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
76impl Transformable for Vector {
77 fn transform(mut self, transformation: Transformation) -> Self {
78 self.transformation = transformation;
79 self
80 }
81}
82
83impl RenderOnce for Vector {
84 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
85 let width = self.size.width;
86 let height = self.size.height;
87
88 svg()
89 // By default, prevent the SVG from stretching
90 // to fill its container.
91 .flex_none()
92 .w(width)
93 .h(height)
94 .path(self.path)
95 .text_color(self.color.color(cx))
96 .with_transformation(self.transformation)
97 }
98}
99
100impl Component for Vector {
101 fn scope() -> ComponentScope {
102 ComponentScope::Images
103 }
104
105 fn name() -> &'static str {
106 "Vector"
107 }
108
109 fn description() -> Option<&'static str> {
110 Some("A vector image component that can be displayed at specific sizes.")
111 }
112
113 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
114 let size = rems_from_px(60.);
115
116 Some(
117 v_flex()
118 .gap_6()
119 .children(vec![
120 example_group_with_title(
121 "Basic Usage",
122 vec![
123 single_example(
124 "Default",
125 Vector::square(VectorName::ZedLogo, size).into_any_element(),
126 ),
127 single_example(
128 "Custom Size",
129 h_flex()
130 .h(rems_from_px(120.))
131 .justify_center()
132 .child(Vector::new(
133 VectorName::ZedLogo,
134 rems_from_px(120.),
135 rems_from_px(200.),
136 ))
137 .into_any_element(),
138 ),
139 ],
140 ),
141 example_group_with_title(
142 "Colored",
143 vec![
144 single_example(
145 "Accent Color",
146 Vector::square(VectorName::ZedLogo, size)
147 .color(Color::Accent)
148 .into_any_element(),
149 ),
150 single_example(
151 "Error Color",
152 Vector::square(VectorName::ZedLogo, size)
153 .color(Color::Error)
154 .into_any_element(),
155 ),
156 ],
157 ),
158 example_group_with_title(
159 "Different Vectors",
160 vec![single_example(
161 "Zed X Copilot",
162 Vector::square(VectorName::ZedXCopilot, rems_from_px(100.))
163 .into_any_element(),
164 )],
165 ),
166 ])
167 .into_any_element(),
168 )
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn vector_path() {
178 assert_eq!(VectorName::ZedLogo.path().as_ref(), "images/zed_logo.svg");
179 }
180}