1use gpui::{Edges, Pixels, Styled, px};
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Default, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
6pub enum Size {
7 Size(Pixels),
8 XSmall,
9 Small,
10 #[default]
11 Medium,
12 Large,
13}
14
15impl Size {
16 fn as_f32(&self) -> f32 {
17 match self {
18 Size::Size(val) => val.as_f32(),
19 Size::XSmall => 0.,
20 Size::Small => 1.,
21 Size::Medium => 2.,
22 Size::Large => 3.,
23 }
24 }
25
26 pub fn as_str(&self) -> &'static str {
28 match self {
29 Size::XSmall => "xs",
30 Size::Small => "sm",
31 Size::Medium => "md",
32 Size::Large => "lg",
33 Size::Size(_) => "custom",
34 }
35 }
36
37 pub fn from_str(size: &str) -> Self {
46 match size.to_lowercase().as_str() {
47 "xs" | "xsmall" => Size::XSmall,
48 "sm" | "small" => Size::Small,
49 "md" | "medium" => Size::Medium,
50 "lg" | "large" => Size::Large,
51 _ => Size::Medium,
52 }
53 }
54
55 #[inline]
57 pub fn table_row_height(&self) -> Pixels {
58 match self {
59 Size::Size(size) => *size,
60 Size::XSmall => px(26.),
61 Size::Small => px(30.),
62 Size::Large => px(40.),
63 _ => px(32.),
64 }
65 }
66
67 #[inline]
69 pub fn table_cell_padding(&self) -> Edges<Pixels> {
70 match self {
71 Size::XSmall => Edges {
72 top: px(2.),
73 bottom: px(2.),
74 left: px(4.),
75 right: px(4.),
76 },
77 Size::Small => Edges {
78 top: px(3.),
79 bottom: px(3.),
80 left: px(6.),
81 right: px(6.),
82 },
83 Size::Large => Edges {
84 top: px(8.),
85 bottom: px(8.),
86 left: px(12.),
87 right: px(12.),
88 },
89 _ => Edges {
90 top: px(4.),
91 bottom: px(4.),
92 left: px(8.),
93 right: px(8.),
94 },
95 }
96 }
97
98 pub fn smaller(&self) -> Self {
100 match self {
101 Size::XSmall => Size::XSmall,
102 Size::Small => Size::XSmall,
103 Size::Medium => Size::Small,
104 Size::Large => Size::Medium,
105 Size::Size(val) => Size::Size(*val * 0.2),
106 }
107 }
108
109 pub fn larger(&self) -> Self {
111 match self {
112 Size::XSmall => Size::Small,
113 Size::Small => Size::Medium,
114 Size::Medium => Size::Large,
115 Size::Large => Size::Large,
116 Size::Size(val) => Size::Size(*val * 1.2),
117 }
118 }
119
120 pub fn max(&self, other: Self) -> Self {
124 match (self, other) {
125 (Size::Size(a), Size::Size(b)) => Size::Size(px(a.as_f32().min(b.as_f32()))),
126 (Size::Size(a), _) => Size::Size(*a),
127 (_, Size::Size(b)) => Size::Size(b),
128 (a, b) if a.as_f32() < b.as_f32() => *a,
129 _ => other,
130 }
131 }
132
133 pub fn min(&self, other: Self) -> Self {
137 match (self, other) {
138 (Size::Size(a), Size::Size(b)) => Size::Size(px(a.as_f32().max(b.as_f32()))),
139 (Size::Size(a), _) => Size::Size(*a),
140 (_, Size::Size(b)) => Size::Size(b),
141 (a, b) if a.as_f32() > b.as_f32() => *a,
142 _ => other,
143 }
144 }
145
146 pub fn input_px(&self) -> Pixels {
148 match self {
149 Self::Large => px(12.),
150 Self::Medium => px(10.),
151 Self::Small => px(8.),
152 Self::XSmall => px(4.),
153 _ => px(8.),
154 }
155 }
156
157 pub fn input_py(&self) -> Pixels {
159 match self {
160 Size::Large => px(10.),
161 Size::Medium => px(8.),
162 Size::Small => px(2.),
163 Size::XSmall => px(0.),
164 _ => px(2.),
165 }
166 }
167}
168
169impl From<Pixels> for Size {
170 fn from(size: Pixels) -> Self {
171 Size::Size(size)
172 }
173}
174
175#[allow(patterns_in_fns_without_body)]
178pub trait Sizable: Sized {
179 fn with_size(mut self, size: impl Into<Size>) -> Self;
184
185 #[inline(always)]
187 fn xsmall(self) -> Self {
188 self.with_size(Size::XSmall)
189 }
190
191 #[inline(always)]
193 fn small(self) -> Self {
194 self.with_size(Size::Small)
195 }
196
197 #[inline(always)]
199 fn large(self) -> Self {
200 self.with_size(Size::Large)
201 }
202}
203
204#[allow(unused)]
205pub trait StyleSized<T: Styled> {
206 fn input_text_size(self, size: Size) -> Self;
207 fn input_size(self, size: Size) -> Self;
208 fn input_pl(self, size: Size) -> Self;
209 fn input_pr(self, size: Size) -> Self;
210 fn input_px(self, size: Size) -> Self;
211 fn input_py(self, size: Size) -> Self;
212 fn input_h(self, size: Size) -> Self;
213 fn list_size(self, size: Size) -> Self;
214 fn list_px(self, size: Size) -> Self;
215 fn list_py(self, size: Size) -> Self;
216 fn size_with(self, size: Size) -> Self;
218 fn table_cell_size(self, size: Size) -> Self;
220 fn button_text_size(self, size: Size) -> Self;
221}
222
223impl<T: Styled> StyleSized<T> for T {
224 #[inline]
225 fn input_text_size(self, size: Size) -> Self {
226 match size {
227 Size::XSmall => self.text_xs(),
228 Size::Small => self.text_sm(),
229 Size::Medium => self.text_sm(),
230 Size::Large => self.text_base(),
231 Size::Size(size) => self.text_size(size * 0.875),
232 }
233 }
234
235 #[inline]
236 fn input_size(self, size: Size) -> Self {
237 self.input_px(size).input_py(size).input_h(size)
238 }
239
240 #[inline]
241 fn input_pl(self, size: Size) -> Self {
242 self.pl(size.input_px())
243 }
244
245 #[inline]
246 fn input_pr(self, size: Size) -> Self {
247 self.pr(size.input_px())
248 }
249
250 #[inline]
251 fn input_px(self, size: Size) -> Self {
252 self.px(size.input_px())
253 }
254
255 #[inline]
256 fn input_py(self, size: Size) -> Self {
257 self.py(size.input_py())
258 }
259
260 #[inline]
261 fn input_h(self, size: Size) -> Self {
262 match size {
263 Size::Large => self.h_11(),
264 Size::Medium => self.h_8(),
265 Size::Small => self.h_6(),
266 Size::XSmall => self.h_5(),
267 _ => self.h_6(),
268 }
269 }
270
271 #[inline]
272 fn list_size(self, size: Size) -> Self {
273 self.list_px(size).list_py(size).input_text_size(size)
274 }
275
276 #[inline]
277 fn list_px(self, size: Size) -> Self {
278 match size {
279 Size::Small => self.px_2(),
280 _ => self.px_3(),
281 }
282 }
283
284 #[inline]
285 fn list_py(self, size: Size) -> Self {
286 match size {
287 Size::Large => self.py_2(),
288 Size::Medium => self.py_1(),
289 Size::Small => self.py_0p5(),
290 _ => self.py_1(),
291 }
292 }
293
294 #[inline]
295 fn size_with(self, size: Size) -> Self {
296 match size {
297 Size::Large => self.size_11(),
298 Size::Medium => self.size_8(),
299 Size::Small => self.size_5(),
300 Size::XSmall => self.size_4(),
301 Size::Size(size) => self.size(size),
302 }
303 }
304
305 #[inline]
306 fn table_cell_size(self, size: Size) -> Self {
307 let padding = size.table_cell_padding();
308 match size {
309 Size::XSmall => self.text_sm(),
310 Size::Small => self.text_sm(),
311 _ => self,
312 }
313 .pl(padding.left)
314 .pr(padding.right)
315 .pt(padding.top)
316 .pb(padding.bottom)
317 }
318
319 fn button_text_size(self, size: Size) -> Self {
320 match size {
321 Size::XSmall => self.text_xs(),
322 Size::Small => self.text_sm(),
323 _ => self.text_base(),
324 }
325 }
326}
327#[cfg(test)]
328mod tests {
329 use gpui::px;
330
331 use crate::Size;
332
333 #[test]
334 fn test_size_max_min() {
335 assert_eq!(Size::Small.min(Size::XSmall), Size::Small);
336 assert_eq!(Size::XSmall.min(Size::Small), Size::Small);
337 assert_eq!(Size::Small.min(Size::Medium), Size::Medium);
338 assert_eq!(Size::Medium.min(Size::Large), Size::Large);
339 assert_eq!(Size::Large.min(Size::Small), Size::Large);
340
341 assert_eq!(
342 Size::Size(px(10.)).min(Size::Size(px(20.))),
343 Size::Size(px(20.))
344 );
345
346 assert_eq!(Size::Small.max(Size::XSmall), Size::XSmall);
348 assert_eq!(Size::XSmall.max(Size::Small), Size::XSmall);
349 assert_eq!(Size::Small.max(Size::Medium), Size::Small);
350 assert_eq!(Size::Medium.max(Size::Large), Size::Medium);
351 assert_eq!(Size::Large.max(Size::Small), Size::Small);
352
353 assert_eq!(
354 Size::Size(px(10.)).max(Size::Size(px(20.))),
355 Size::Size(px(10.))
356 );
357 }
358
359 #[test]
360 fn test_size_as_str() {
361 assert_eq!(Size::XSmall.as_str(), "xs");
362 assert_eq!(Size::Small.as_str(), "sm");
363 assert_eq!(Size::Medium.as_str(), "md");
364 assert_eq!(Size::Large.as_str(), "lg");
365 assert_eq!(Size::Size(px(15.)).as_str(), "custom");
366 }
367
368 #[test]
369 fn test_table_row_height() {
370 assert_eq!(Size::XSmall.table_row_height(), px(26.));
371 assert_eq!(Size::Small.table_row_height(), px(30.));
372 assert_eq!(Size::Medium.table_row_height(), px(32.));
373 assert_eq!(Size::Large.table_row_height(), px(40.));
374 assert_eq!(Size::Size(px(48.)).table_row_height(), px(48.));
375 }
376
377 #[test]
378 fn test_size_from_str() {
379 assert_eq!(Size::from_str("xs"), Size::XSmall);
380 assert_eq!(Size::from_str("xsmall"), Size::XSmall);
381 assert_eq!(Size::from_str("sm"), Size::Small);
382 assert_eq!(Size::from_str("small"), Size::Small);
383 assert_eq!(Size::from_str("md"), Size::Medium);
384 assert_eq!(Size::from_str("medium"), Size::Medium);
385 assert_eq!(Size::from_str("lg"), Size::Large);
386 assert_eq!(Size::from_str("large"), Size::Large);
387 assert_eq!(Size::from_str("unknown"), Size::Medium);
388
389 assert_eq!(Size::from_str("XS"), Size::XSmall);
391 assert_eq!(Size::from_str("SMALL"), Size::Small);
392 assert_eq!(Size::from_str("Md"), Size::Medium);
393 }
394}