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