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