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