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