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 #[doc(hidden)]
189 fn prepare_for_toolbar(self) -> Self {
190 self
191 }
192
193 #[inline(always)]
195 fn xsmall(self) -> Self {
196 self.with_size(Size::XSmall)
197 }
198
199 #[inline(always)]
201 fn small(self) -> Self {
202 self.with_size(Size::Small)
203 }
204
205 #[inline(always)]
207 fn large(self) -> Self {
208 self.with_size(Size::Large)
209 }
210}
211
212#[allow(unused)]
213pub trait StyleSized<T: Styled> {
214 fn input_text_size(self, size: Size) -> Self;
215 fn input_size(self, size: Size) -> Self;
216 fn input_pl(self, size: Size) -> Self;
217 fn input_pr(self, size: Size) -> Self;
218 fn input_px(self, size: Size) -> Self;
219 fn input_py(self, size: Size) -> Self;
220 fn input_h(self, size: Size) -> Self;
221 fn list_size(self, size: Size) -> Self;
222 fn list_px(self, size: Size) -> Self;
223 fn list_py(self, size: Size) -> Self;
224 fn size_with(self, size: Size) -> Self;
226 fn table_cell_size(self, size: Size) -> Self;
228 fn button_text_size(self, size: Size) -> Self;
229}
230
231impl<T: Styled> StyleSized<T> for T {
232 #[inline]
233 fn input_text_size(self, size: Size) -> Self {
234 match size {
235 Size::XSmall => self.text_xs(),
236 Size::Small => self.text_sm(),
237 Size::Medium => self.text_sm(),
238 Size::Large => self.text_base(),
239 Size::Size(size) => self.text_size(size * 0.875),
240 }
241 }
242
243 #[inline]
244 fn input_size(self, size: Size) -> Self {
245 self.input_px(size).input_py(size).input_h(size)
246 }
247
248 #[inline]
249 fn input_pl(self, size: Size) -> Self {
250 self.pl(size.input_px())
251 }
252
253 #[inline]
254 fn input_pr(self, size: Size) -> Self {
255 self.pr(size.input_px())
256 }
257
258 #[inline]
259 fn input_px(self, size: Size) -> Self {
260 self.px(size.input_px())
261 }
262
263 #[inline]
264 fn input_py(self, size: Size) -> Self {
265 self.py(size.input_py())
266 }
267
268 #[inline]
269 fn input_h(self, size: Size) -> Self {
270 match size {
271 Size::Large => self.h_11(),
272 Size::Medium => self.h_8(),
273 Size::Small => self.h_6(),
274 Size::XSmall => self.h_5(),
275 _ => self.h_6(),
276 }
277 }
278
279 #[inline]
280 fn list_size(self, size: Size) -> Self {
281 self.list_px(size).list_py(size).input_text_size(size)
282 }
283
284 #[inline]
285 fn list_px(self, size: Size) -> Self {
286 match size {
287 Size::Small => self.px_2(),
288 _ => self.px_3(),
289 }
290 }
291
292 #[inline]
293 fn list_py(self, size: Size) -> Self {
294 match size {
295 Size::Large => self.py_2(),
296 Size::Medium => self.py_1(),
297 Size::Small => self.py_0p5(),
298 _ => self.py_1(),
299 }
300 }
301
302 #[inline]
303 fn size_with(self, size: Size) -> Self {
304 match size {
305 Size::Large => self.size_11(),
306 Size::Medium => self.size_8(),
307 Size::Small => self.size_5(),
308 Size::XSmall => self.size_4(),
309 Size::Size(size) => self.size(size),
310 }
311 }
312
313 #[inline]
314 fn table_cell_size(self, size: Size) -> Self {
315 let padding = size.table_cell_padding();
316 match size {
317 Size::XSmall => self.text_sm(),
318 Size::Small => self.text_sm(),
319 _ => self,
320 }
321 .pl(padding.left)
322 .pr(padding.right)
323 .pt(padding.top)
324 .pb(padding.bottom)
325 }
326
327 fn button_text_size(self, size: Size) -> Self {
328 match size {
329 Size::XSmall => self.text_xs(),
330 Size::Small => self.text_sm(),
331 _ => self.text_base(),
332 }
333 }
334}
335#[cfg(test)]
336mod tests {
337 use gpui::px;
338
339 use crate::Size;
340
341 #[test]
342 fn test_size_max_min() {
343 assert_eq!(Size::Small.min(Size::XSmall), Size::Small);
344 assert_eq!(Size::XSmall.min(Size::Small), Size::Small);
345 assert_eq!(Size::Small.min(Size::Medium), Size::Medium);
346 assert_eq!(Size::Medium.min(Size::Large), Size::Large);
347 assert_eq!(Size::Large.min(Size::Small), Size::Large);
348
349 assert_eq!(
350 Size::Size(px(10.)).min(Size::Size(px(20.))),
351 Size::Size(px(20.))
352 );
353
354 assert_eq!(Size::Small.max(Size::XSmall), Size::XSmall);
356 assert_eq!(Size::XSmall.max(Size::Small), Size::XSmall);
357 assert_eq!(Size::Small.max(Size::Medium), Size::Small);
358 assert_eq!(Size::Medium.max(Size::Large), Size::Medium);
359 assert_eq!(Size::Large.max(Size::Small), Size::Small);
360
361 assert_eq!(
362 Size::Size(px(10.)).max(Size::Size(px(20.))),
363 Size::Size(px(10.))
364 );
365 }
366
367 #[test]
368 fn test_size_as_str() {
369 assert_eq!(Size::XSmall.as_str(), "xs");
370 assert_eq!(Size::Small.as_str(), "sm");
371 assert_eq!(Size::Medium.as_str(), "md");
372 assert_eq!(Size::Large.as_str(), "lg");
373 assert_eq!(Size::Size(px(15.)).as_str(), "custom");
374 }
375
376 #[test]
377 fn test_table_row_height() {
378 assert_eq!(Size::XSmall.table_row_height(), px(26.));
379 assert_eq!(Size::Small.table_row_height(), px(30.));
380 assert_eq!(Size::Medium.table_row_height(), px(32.));
381 assert_eq!(Size::Large.table_row_height(), px(40.));
382 assert_eq!(Size::Size(px(48.)).table_row_height(), px(48.));
383 }
384
385 #[test]
386 fn test_size_from_str() {
387 assert_eq!(Size::from_str("xs"), Size::XSmall);
388 assert_eq!(Size::from_str("xsmall"), Size::XSmall);
389 assert_eq!(Size::from_str("sm"), Size::Small);
390 assert_eq!(Size::from_str("small"), Size::Small);
391 assert_eq!(Size::from_str("md"), Size::Medium);
392 assert_eq!(Size::from_str("medium"), Size::Medium);
393 assert_eq!(Size::from_str("lg"), Size::Large);
394 assert_eq!(Size::from_str("large"), Size::Large);
395 assert_eq!(Size::from_str("unknown"), Size::Medium);
396
397 assert_eq!(Size::from_str("XS"), Size::XSmall);
399 assert_eq!(Size::from_str("SMALL"), Size::Small);
400 assert_eq!(Size::from_str("Md"), Size::Medium);
401 }
402}