1use gpui::{px, rems, Pixels, Rems, WindowContext};
2use settings::Settings;
3use theme::{ThemeSettings, UiDensity};
4
5use crate::{rems_from_px, BASE_REM_SIZE_IN_PX};
6
7/// A dynamic spacing system that adjusts spacing based on
8/// [UiDensity].
9///
10/// When possible, [Spacing] should be used over manual
11/// or built-in spacing values in places dynamic spacing is needed.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum Spacing {
14 /// No spacing
15 None,
16 /// Usually a one pixel spacing. Grows to 2px in comfortable density.
17 /// @16px/rem: `1px`|`1px`|`2px`
18 XXSmall,
19 /// Extra small spacing - @16px/rem: `1px`|`2px`|`4px`
20 ///
21 /// Relative to the user's `ui_font_size` and [UiDensity] setting.
22 XSmall,
23 /// Small spacing - @16px/rem: `2px`|`4px`|`6px`
24 ///
25 /// Relative to the user's `ui_font_size` and [UiDensity] setting.
26 Small,
27 /// Medium spacing - @16px/rem: `3px`|`6px`|`8px`
28 ///
29 /// Relative to the user's `ui_font_size` and [UiDensity] setting.
30 Medium,
31 /// Large spacing - @16px/rem: `4px`|`8px`|`10px`
32 ///
33 /// Relative to the user's `ui_font_size` and [UiDensity] setting.
34 Large,
35 /// Extra Large spacing - @16px/rem: `8px`|`12px`|`16px`
36 ///
37 /// Relative to the user's `ui_font_size` and [UiDensity] setting.
38 XLarge,
39 /// 2X Large spacing - @16px/rem: `12px`|`16px`|`20px`
40 ///
41 /// Relative to the user's `ui_font_size` and [UiDensity] setting.
42 XXLarge,
43}
44
45impl Spacing {
46 /// Returns the spacing's scaling ratio in pixels.
47 pub fn spacing_ratio(self, cx: &WindowContext) -> f32 {
48 match ThemeSettings::get_global(cx).ui_density {
49 UiDensity::Compact => match self {
50 Spacing::None => 0.,
51 Spacing::XXSmall => 1. / BASE_REM_SIZE_IN_PX,
52 Spacing::XSmall => 1. / BASE_REM_SIZE_IN_PX,
53 Spacing::Small => 2. / BASE_REM_SIZE_IN_PX,
54 Spacing::Medium => 3. / BASE_REM_SIZE_IN_PX,
55 Spacing::Large => 4. / BASE_REM_SIZE_IN_PX,
56 Spacing::XLarge => 8. / BASE_REM_SIZE_IN_PX,
57 Spacing::XXLarge => 12. / BASE_REM_SIZE_IN_PX,
58 },
59 UiDensity::Default => match self {
60 Spacing::None => 0.,
61 Spacing::XXSmall => 1. / BASE_REM_SIZE_IN_PX,
62 Spacing::XSmall => 2. / BASE_REM_SIZE_IN_PX,
63 Spacing::Small => 4. / BASE_REM_SIZE_IN_PX,
64 Spacing::Medium => 6. / BASE_REM_SIZE_IN_PX,
65 Spacing::Large => 8. / BASE_REM_SIZE_IN_PX,
66 Spacing::XLarge => 12. / BASE_REM_SIZE_IN_PX,
67 Spacing::XXLarge => 16. / BASE_REM_SIZE_IN_PX,
68 },
69 UiDensity::Comfortable => match self {
70 Spacing::None => 0.,
71 Spacing::XXSmall => 2. / BASE_REM_SIZE_IN_PX,
72 Spacing::XSmall => 3. / BASE_REM_SIZE_IN_PX,
73 Spacing::Small => 6. / BASE_REM_SIZE_IN_PX,
74 Spacing::Medium => 8. / BASE_REM_SIZE_IN_PX,
75 Spacing::Large => 10. / BASE_REM_SIZE_IN_PX,
76 Spacing::XLarge => 16. / BASE_REM_SIZE_IN_PX,
77 Spacing::XXLarge => 20. / BASE_REM_SIZE_IN_PX,
78 },
79 }
80 }
81
82 /// Returns the spacing's value in rems.
83 pub fn rems(self, cx: &WindowContext) -> Rems {
84 rems(self.spacing_ratio(cx))
85 }
86
87 /// Returns the spacing's value in pixels.
88 pub fn px(self, cx: &WindowContext) -> Pixels {
89 let ui_font_size_f32: f32 = ThemeSettings::get_global(cx).ui_font_size.into();
90
91 px(ui_font_size_f32 * self.spacing_ratio(cx))
92 }
93}
94
95fn user_spacing_style(cx: &WindowContext) -> UiDensity {
96 ThemeSettings::get_global(cx).ui_density
97}
98
99/// Returns a custom spacing value based on the current [`UiDensity`].
100///
101/// If you use this, talk to @iamnbutler and let me know what you're doing
102/// that needs custom spacing– I'd love to understand so we can extend the system further and remove the need for this.
103pub fn custom_spacing(cx: &WindowContext, size: f32) -> Rems {
104 rems_from_px(size * user_spacing_style(cx).spacing_ratio())
105}