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