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 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 let size = rems_from_px(60.);
119
120 Some(
121 v_flex()
122 .gap_6()
123 .children(vec![
124 example_group_with_title(
125 "Basic Usage",
126 vec![
127 single_example(
128 "Default",
129 Vector::square(VectorName::ZedLogo, size).into_any_element(),
130 ),
131 single_example(
132 "Custom Size",
133 h_flex()
134 .h(rems_from_px(120.))
135 .justify_center()
136 .child(Vector::new(
137 VectorName::ZedLogo,
138 rems_from_px(120.),
139 rems_from_px(200.),
140 ))
141 .into_any_element(),
142 ),
143 ],
144 ),
145 example_group_with_title(
146 "Colored",
147 vec![
148 single_example(
149 "Accent Color",
150 Vector::square(VectorName::ZedLogo, size)
151 .color(Color::Accent)
152 .into_any_element(),
153 ),
154 single_example(
155 "Error Color",
156 Vector::square(VectorName::ZedLogo, size)
157 .color(Color::Error)
158 .into_any_element(),
159 ),
160 ],
161 ),
162 example_group_with_title(
163 "Different Vectors",
164 vec![single_example(
165 "Zed X Copilot",
166 Vector::square(VectorName::ZedXCopilot, rems_from_px(100.))
167 .into_any_element(),
168 )],
169 ),
170 ])
171 .into_any_element(),
172 )
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn vector_path() {
182 assert_eq!(VectorName::ZedLogo.path().as_ref(), "images/zed_logo.svg");
183 }
184}