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