1use fret_core::{FontId, Px, TextStyle};
2use fret_ui::Theme;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub enum Size {
9 XSmall,
10 Small,
11 #[default]
12 Medium,
13 Large,
14}
15
16impl Size {
17 pub fn as_str(self) -> &'static str {
18 match self {
19 Self::XSmall => "xs",
20 Self::Small => "sm",
21 Self::Medium => "md",
22 Self::Large => "lg",
23 }
24 }
25
26 fn metric(self, theme: &Theme, suffix: &'static str, fallback: Px) -> Px {
27 let key = match (self, suffix) {
28 (Self::XSmall, "control.text_px") => "component.size.xs.control.text_px",
29 (Self::Small, "control.text_px") => "component.size.sm.control.text_px",
30 (Self::Medium, "control.text_px") => "component.size.md.control.text_px",
31 (Self::Large, "control.text_px") => "component.size.lg.control.text_px",
32
33 (Self::XSmall, "control.radius") => "component.size.xs.control.radius",
34 (Self::Small, "control.radius") => "component.size.sm.control.radius",
35 (Self::Medium, "control.radius") => "component.size.md.control.radius",
36 (Self::Large, "control.radius") => "component.size.lg.control.radius",
37
38 (Self::XSmall, "input.px") => "component.size.xs.input.px",
39 (Self::Small, "input.px") => "component.size.sm.input.px",
40 (Self::Medium, "input.px") => "component.size.md.input.px",
41 (Self::Large, "input.px") => "component.size.lg.input.px",
42
43 (Self::XSmall, "input.py") => "component.size.xs.input.py",
44 (Self::Small, "input.py") => "component.size.sm.input.py",
45 (Self::Medium, "input.py") => "component.size.md.input.py",
46 (Self::Large, "input.py") => "component.size.lg.input.py",
47
48 (Self::XSmall, "input.h") => "component.size.xs.input.h",
49 (Self::Small, "input.h") => "component.size.sm.input.h",
50 (Self::Medium, "input.h") => "component.size.md.input.h",
51 (Self::Large, "input.h") => "component.size.lg.input.h",
52
53 (Self::XSmall, "button.px") => "component.size.xs.button.px",
54 (Self::Small, "button.px") => "component.size.sm.button.px",
55 (Self::Medium, "button.px") => "component.size.md.button.px",
56 (Self::Large, "button.px") => "component.size.lg.button.px",
57
58 (Self::XSmall, "button.py") => "component.size.xs.button.py",
59 (Self::Small, "button.py") => "component.size.sm.button.py",
60 (Self::Medium, "button.py") => "component.size.md.button.py",
61 (Self::Large, "button.py") => "component.size.lg.button.py",
62
63 (Self::XSmall, "button.h") => "component.size.xs.button.h",
64 (Self::Small, "button.h") => "component.size.sm.button.h",
65 (Self::Medium, "button.h") => "component.size.md.button.h",
66 (Self::Large, "button.h") => "component.size.lg.button.h",
67
68 (Self::XSmall, "icon_button.size") => "component.size.xs.icon_button.size",
69 (Self::Small, "icon_button.size") => "component.size.sm.icon_button.size",
70 (Self::Medium, "icon_button.size") => "component.size.md.icon_button.size",
71 (Self::Large, "icon_button.size") => "component.size.lg.icon_button.size",
72
73 (Self::XSmall, "list.px") => "component.size.xs.list.px",
74 (Self::Small, "list.px") => "component.size.sm.list.px",
75 (Self::Medium, "list.px") => "component.size.md.list.px",
76 (Self::Large, "list.px") => "component.size.lg.list.px",
77
78 (Self::XSmall, "list.py") => "component.size.xs.list.py",
79 (Self::Small, "list.py") => "component.size.sm.list.py",
80 (Self::Medium, "list.py") => "component.size.md.list.py",
81 (Self::Large, "list.py") => "component.size.lg.list.py",
82
83 (Self::XSmall, "list.row_h") => "component.size.xs.list.row_h",
84 (Self::Small, "list.row_h") => "component.size.sm.list.row_h",
85 (Self::Medium, "list.row_h") => "component.size.md.list.row_h",
86 (Self::Large, "list.row_h") => "component.size.lg.list.row_h",
87
88 _ => return fallback,
89 };
90
91 theme.metric_by_key(key).unwrap_or(fallback)
92 }
93
94 pub fn control_text_px(self, theme: &Theme) -> Px {
95 let base = theme
96 .metric_by_key("font.size")
97 .unwrap_or_else(|| theme.metric_token("font.size"));
98 let fallback = match self {
99 Self::XSmall => base - Px(1.0),
102 Self::Small => base,
103 Self::Medium => base,
104 Self::Large => base + Px(1.0),
105 };
106 self.metric(theme, "control.text_px", fallback)
107 }
108
109 pub fn control_text_style(self, theme: &Theme) -> TextStyle {
110 crate::typography::control_text_style_scaled(
111 theme,
112 FontId::ui(),
113 self.control_text_px(theme),
114 )
115 }
116
117 pub fn control_radius(self, theme: &Theme) -> Px {
118 self.metric(
119 theme,
120 "control.radius",
121 match self {
122 Self::XSmall => theme.metric_token("metric.radius.sm"),
123 Self::Small => theme.metric_token("metric.radius.sm"),
124 Self::Medium => theme.metric_token("metric.radius.md"),
125 Self::Large => theme.metric_token("metric.radius.md"),
126 },
127 )
128 }
129
130 pub fn input_px(self, theme: &Theme) -> Px {
131 self.metric(
132 theme,
133 "input.px",
134 match self {
135 Self::XSmall => Px(8.0),
136 Self::Small => Px(10.0),
137 Self::Medium => Px(12.0),
138 Self::Large => Px(14.0),
139 },
140 )
141 }
142
143 pub fn input_py(self, theme: &Theme) -> Px {
144 self.metric(
145 theme,
146 "input.py",
147 match self {
148 Self::XSmall => Px(4.0),
149 Self::Small => Px(5.0),
150 Self::Medium => Px(6.0),
151 Self::Large => Px(7.0),
152 },
153 )
154 }
155
156 pub fn input_h(self, theme: &Theme) -> Px {
157 self.metric(
158 theme,
159 "input.h",
160 match self {
161 Self::XSmall => Px(24.0),
162 Self::Small => Px(28.0),
163 Self::Medium => Px(32.0),
164 Self::Large => Px(36.0),
165 },
166 )
167 }
168
169 pub fn button_px(self, theme: &Theme) -> Px {
170 self.metric(
171 theme,
172 "button.px",
173 match self {
174 Self::XSmall => Px(8.0),
175 Self::Small => Px(10.0),
176 Self::Medium => Px(12.0),
177 Self::Large => Px(14.0),
178 },
179 )
180 }
181
182 pub fn button_py(self, theme: &Theme) -> Px {
183 self.metric(
184 theme,
185 "button.py",
186 match self {
187 Self::XSmall => Px(4.0),
188 Self::Small => Px(5.0),
189 Self::Medium => Px(6.0),
190 Self::Large => Px(7.0),
191 },
192 )
193 }
194
195 pub fn button_h(self, theme: &Theme) -> Px {
196 self.metric(
197 theme,
198 "button.h",
199 match self {
200 Self::XSmall => Px(24.0),
201 Self::Small => Px(28.0),
202 Self::Medium => Px(32.0),
203 Self::Large => Px(36.0),
204 },
205 )
206 }
207
208 pub fn icon_button_size(self, theme: &Theme) -> Px {
209 self.metric(
210 theme,
211 "icon_button.size",
212 match self {
213 Self::XSmall => Px(24.0),
214 Self::Small => Px(28.0),
215 Self::Medium => Px(32.0),
216 Self::Large => Px(36.0),
217 },
218 )
219 }
220
221 pub fn list_px(self, theme: &Theme) -> Px {
222 self.metric(
223 theme,
224 "list.px",
225 match self {
226 Self::XSmall => Px(8.0),
230 Self::Small => Px(8.0),
231 Self::Medium => Px(12.0),
232 Self::Large => Px(12.0),
233 },
234 )
235 }
236
237 pub fn list_py(self, theme: &Theme) -> Px {
238 self.metric(
239 theme,
240 "list.py",
241 match self {
242 Self::XSmall => Px(2.0),
244 Self::Small => Px(2.0),
245 Self::Medium => Px(4.0),
246 Self::Large => Px(8.0),
247 },
248 )
249 }
250
251 pub fn list_row_h(self, theme: &Theme) -> Px {
252 self.metric(
253 theme,
254 "list.row_h",
255 match self {
256 Self::XSmall => Px(24.0),
257 Self::Small => Px(28.0),
258 Self::Medium => Px(32.0),
259 Self::Large => Px(36.0),
260 },
261 )
262 }
263}
264
265pub trait Sizable: Sized {
267 fn with_size(self, size: Size) -> Self;
268
269 fn xsmall(self) -> Self {
270 self.with_size(Size::XSmall)
271 }
272
273 fn small(self) -> Self {
274 self.with_size(Size::Small)
275 }
276
277 fn medium(self) -> Self {
278 self.with_size(Size::Medium)
279 }
280
281 fn large(self) -> Self {
282 self.with_size(Size::Large)
283 }
284}