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