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