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