1use super::validate_helpers::{
6 self, DEFAULT_FONT_DPI, require, require_font, require_text_scale_entry,
7};
8use crate::error::ThemeResolutionError;
9use crate::model::ThemeVariant;
10use crate::model::resolved::{
11 ResolvedIconSizes, ResolvedTextScale, ResolvedThemeDefaults, ResolvedThemeVariant,
12};
13
14impl ThemeVariant {
15 #[must_use = "this returns the validation result; handle the Result or propagate with ?"]
28 pub fn validate(&self) -> crate::Result<ResolvedThemeVariant> {
29 let mut missing = Vec::new();
30 let dpi = self.defaults.font_dpi.unwrap_or(DEFAULT_FONT_DPI);
31
32 let defaults_font = require_font(&self.defaults.font, "defaults.font", dpi, &mut missing);
34 let defaults_line_height = require(
35 &self.defaults.line_height,
36 "defaults.line_height",
37 &mut missing,
38 );
39 let defaults_mono_font = require_font(
40 &self.defaults.mono_font,
41 "defaults.mono_font",
42 dpi,
43 &mut missing,
44 );
45
46 let defaults_background = require(
47 &self.defaults.background_color,
48 "defaults.background_color",
49 &mut missing,
50 );
51 let defaults_foreground = require(
52 &self.defaults.text_color,
53 "defaults.text_color",
54 &mut missing,
55 );
56 let defaults_accent = require(
57 &self.defaults.accent_color,
58 "defaults.accent_color",
59 &mut missing,
60 );
61 let defaults_accent_foreground = require(
62 &self.defaults.accent_text_color,
63 "defaults.accent_text_color",
64 &mut missing,
65 );
66 let defaults_surface = require(
67 &self.defaults.surface_color,
68 "defaults.surface_color",
69 &mut missing,
70 );
71 let defaults_border = require(
72 &self.defaults.border.color,
73 "defaults.border.color",
74 &mut missing,
75 );
76 let defaults_muted = require(
77 &self.defaults.muted_color,
78 "defaults.muted_color",
79 &mut missing,
80 );
81 let defaults_shadow = require(
82 &self.defaults.shadow_color,
83 "defaults.shadow_color",
84 &mut missing,
85 );
86 let defaults_link = require(
87 &self.defaults.link_color,
88 "defaults.link_color",
89 &mut missing,
90 );
91 let defaults_selection = require(
92 &self.defaults.selection_background,
93 "defaults.selection_background",
94 &mut missing,
95 );
96 let defaults_selection_foreground = require(
97 &self.defaults.selection_text_color,
98 "defaults.selection_text_color",
99 &mut missing,
100 );
101 let defaults_selection_inactive = require(
102 &self.defaults.selection_inactive_background,
103 "defaults.selection_inactive_background",
104 &mut missing,
105 );
106 let defaults_disabled_foreground = require(
107 &self.defaults.disabled_text_color,
108 "defaults.disabled_text_color",
109 &mut missing,
110 );
111
112 let defaults_danger = require(
113 &self.defaults.danger_color,
114 "defaults.danger_color",
115 &mut missing,
116 );
117 let defaults_danger_foreground = require(
118 &self.defaults.danger_text_color,
119 "defaults.danger_text_color",
120 &mut missing,
121 );
122 let defaults_warning = require(
123 &self.defaults.warning_color,
124 "defaults.warning_color",
125 &mut missing,
126 );
127 let defaults_warning_foreground = require(
128 &self.defaults.warning_text_color,
129 "defaults.warning_text_color",
130 &mut missing,
131 );
132 let defaults_success = require(
133 &self.defaults.success_color,
134 "defaults.success_color",
135 &mut missing,
136 );
137 let defaults_success_foreground = require(
138 &self.defaults.success_text_color,
139 "defaults.success_text_color",
140 &mut missing,
141 );
142 let defaults_info = require(
143 &self.defaults.info_color,
144 "defaults.info_color",
145 &mut missing,
146 );
147 let defaults_info_foreground = require(
148 &self.defaults.info_text_color,
149 "defaults.info_text_color",
150 &mut missing,
151 );
152
153 let defaults_radius = require(
154 &self.defaults.border.corner_radius,
155 "defaults.border.corner_radius",
156 &mut missing,
157 );
158 let defaults_radius_lg = require(
159 &self.defaults.border.corner_radius_lg,
160 "defaults.border.corner_radius_lg",
161 &mut missing,
162 );
163 let defaults_frame_width = require(
164 &self.defaults.border.line_width,
165 "defaults.border.line_width",
166 &mut missing,
167 );
168 let defaults_disabled_opacity = require(
169 &self.defaults.disabled_opacity,
170 "defaults.disabled_opacity",
171 &mut missing,
172 );
173 let defaults_border_opacity = require(
174 &self.defaults.border.opacity,
175 "defaults.border.opacity",
176 &mut missing,
177 );
178 let defaults_shadow_enabled = require(
179 &self.defaults.border.shadow_enabled,
180 "defaults.border.shadow_enabled",
181 &mut missing,
182 );
183
184 let defaults_focus_ring_color = require(
185 &self.defaults.focus_ring_color,
186 "defaults.focus_ring_color",
187 &mut missing,
188 );
189 let defaults_focus_ring_width = require(
190 &self.defaults.focus_ring_width,
191 "defaults.focus_ring_width",
192 &mut missing,
193 );
194 let defaults_focus_ring_offset = require(
195 &self.defaults.focus_ring_offset,
196 "defaults.focus_ring_offset",
197 &mut missing,
198 );
199
200 let defaults_border_padding_h = require(
201 &self.defaults.border.padding_horizontal,
202 "defaults.border.padding_horizontal",
203 &mut missing,
204 );
205 let defaults_border_padding_v = require(
206 &self.defaults.border.padding_vertical,
207 "defaults.border.padding_vertical",
208 &mut missing,
209 );
210 let defaults_text_selection_background = require(
211 &self.defaults.text_selection_background,
212 "defaults.text_selection_background",
213 &mut missing,
214 );
215 let defaults_text_selection_color = require(
216 &self.defaults.text_selection_color,
217 "defaults.text_selection_color",
218 &mut missing,
219 );
220
221 let defaults_icon_sizes_toolbar = require(
222 &self.defaults.icon_sizes.toolbar,
223 "defaults.icon_sizes.toolbar",
224 &mut missing,
225 );
226 let defaults_icon_sizes_small = require(
227 &self.defaults.icon_sizes.small,
228 "defaults.icon_sizes.small",
229 &mut missing,
230 );
231 let defaults_icon_sizes_large = require(
232 &self.defaults.icon_sizes.large,
233 "defaults.icon_sizes.large",
234 &mut missing,
235 );
236 let defaults_icon_sizes_dialog = require(
237 &self.defaults.icon_sizes.dialog,
238 "defaults.icon_sizes.dialog",
239 &mut missing,
240 );
241 let defaults_icon_sizes_panel = require(
242 &self.defaults.icon_sizes.panel,
243 "defaults.icon_sizes.panel",
244 &mut missing,
245 );
246
247 let defaults_font_dpi = dpi;
248 let defaults_text_scaling_factor = require(
249 &self.defaults.text_scaling_factor,
250 "defaults.text_scaling_factor",
251 &mut missing,
252 );
253 let defaults_reduce_motion = require(
254 &self.defaults.reduce_motion,
255 "defaults.reduce_motion",
256 &mut missing,
257 );
258 let defaults_high_contrast = require(
259 &self.defaults.high_contrast,
260 "defaults.high_contrast",
261 &mut missing,
262 );
263 let defaults_reduce_transparency = require(
264 &self.defaults.reduce_transparency,
265 "defaults.reduce_transparency",
266 &mut missing,
267 );
268
269 let ts_caption = require_text_scale_entry(
270 &self.text_scale.caption,
271 "text_scale.caption",
272 dpi,
273 &mut missing,
274 );
275 let ts_section_heading = require_text_scale_entry(
276 &self.text_scale.section_heading,
277 "text_scale.section_heading",
278 dpi,
279 &mut missing,
280 );
281 let ts_dialog_title = require_text_scale_entry(
282 &self.text_scale.dialog_title,
283 "text_scale.dialog_title",
284 dpi,
285 &mut missing,
286 );
287 let ts_display = require_text_scale_entry(
288 &self.text_scale.display,
289 "text_scale.display",
290 dpi,
291 &mut missing,
292 );
293
294 use crate::model::border::ResolvedBorderSpec;
296 let defaults = ResolvedThemeDefaults {
297 font: defaults_font,
298 line_height: defaults_line_height,
299 mono_font: defaults_mono_font,
300 background_color: defaults_background,
301 text_color: defaults_foreground,
302 accent_color: defaults_accent,
303 accent_text_color: defaults_accent_foreground,
304 surface_color: defaults_surface,
305 border: ResolvedBorderSpec {
306 color: defaults_border,
307 corner_radius: defaults_radius,
308 corner_radius_lg: defaults_radius_lg,
309 line_width: defaults_frame_width,
310 opacity: defaults_border_opacity,
311 shadow_enabled: defaults_shadow_enabled,
312 padding_horizontal: defaults_border_padding_h,
313 padding_vertical: defaults_border_padding_v,
314 },
315 muted_color: defaults_muted,
316 shadow_color: defaults_shadow,
317 link_color: defaults_link,
318 selection_background: defaults_selection,
319 selection_text_color: defaults_selection_foreground,
320 selection_inactive_background: defaults_selection_inactive,
321 text_selection_background: defaults_text_selection_background,
322 text_selection_color: defaults_text_selection_color,
323 disabled_text_color: defaults_disabled_foreground,
324 danger_color: defaults_danger,
325 danger_text_color: defaults_danger_foreground,
326 warning_color: defaults_warning,
327 warning_text_color: defaults_warning_foreground,
328 success_color: defaults_success,
329 success_text_color: defaults_success_foreground,
330 info_color: defaults_info,
331 info_text_color: defaults_info_foreground,
332 disabled_opacity: defaults_disabled_opacity,
333 focus_ring_color: defaults_focus_ring_color,
334 focus_ring_width: defaults_focus_ring_width,
335 focus_ring_offset: defaults_focus_ring_offset,
336 icon_sizes: ResolvedIconSizes {
337 toolbar: defaults_icon_sizes_toolbar,
338 small: defaults_icon_sizes_small,
339 large: defaults_icon_sizes_large,
340 dialog: defaults_icon_sizes_dialog,
341 panel: defaults_icon_sizes_panel,
342 },
343 font_dpi: defaults_font_dpi,
344 text_scaling_factor: defaults_text_scaling_factor,
345 reduce_motion: defaults_reduce_motion,
346 high_contrast: defaults_high_contrast,
347 reduce_transparency: defaults_reduce_transparency,
348 };
349 let text_scale = ResolvedTextScale {
350 caption: ts_caption,
351 section_heading: ts_section_heading,
352 dialog_title: ts_dialog_title,
353 display: ts_display,
354 };
355
356 validate_helpers::check_defaults_ranges(&defaults, &text_scale, &mut missing);
357
358 use crate::model::widgets::*;
360 let window =
361 ResolvedWindowTheme::validate_widget(&self.window, "window", dpi, &mut missing);
362 let button =
363 ResolvedButtonTheme::validate_widget(&self.button, "button", dpi, &mut missing);
364 let input = ResolvedInputTheme::validate_widget(&self.input, "input", dpi, &mut missing);
365 let checkbox =
366 ResolvedCheckboxTheme::validate_widget(&self.checkbox, "checkbox", dpi, &mut missing);
367 let menu = ResolvedMenuTheme::validate_widget(&self.menu, "menu", dpi, &mut missing);
368 let tooltip =
369 ResolvedTooltipTheme::validate_widget(&self.tooltip, "tooltip", dpi, &mut missing);
370 let scrollbar = ResolvedScrollbarTheme::validate_widget(
371 &self.scrollbar,
372 "scrollbar",
373 dpi,
374 &mut missing,
375 );
376 let slider =
377 ResolvedSliderTheme::validate_widget(&self.slider, "slider", dpi, &mut missing);
378 let progress_bar = ResolvedProgressBarTheme::validate_widget(
379 &self.progress_bar,
380 "progress_bar",
381 dpi,
382 &mut missing,
383 );
384 let tab = ResolvedTabTheme::validate_widget(&self.tab, "tab", dpi, &mut missing);
385 let sidebar =
386 ResolvedSidebarTheme::validate_widget(&self.sidebar, "sidebar", dpi, &mut missing);
387 let toolbar =
388 ResolvedToolbarTheme::validate_widget(&self.toolbar, "toolbar", dpi, &mut missing);
389 let status_bar = ResolvedStatusBarTheme::validate_widget(
390 &self.status_bar,
391 "status_bar",
392 dpi,
393 &mut missing,
394 );
395 let list = ResolvedListTheme::validate_widget(&self.list, "list", dpi, &mut missing);
396 let popover =
397 ResolvedPopoverTheme::validate_widget(&self.popover, "popover", dpi, &mut missing);
398 let splitter =
399 ResolvedSplitterTheme::validate_widget(&self.splitter, "splitter", dpi, &mut missing);
400 let separator = ResolvedSeparatorTheme::validate_widget(
401 &self.separator,
402 "separator",
403 dpi,
404 &mut missing,
405 );
406 let switch =
407 ResolvedSwitchTheme::validate_widget(&self.switch, "switch", dpi, &mut missing);
408 let dialog =
409 ResolvedDialogTheme::validate_widget(&self.dialog, "dialog", dpi, &mut missing);
410 let spinner =
411 ResolvedSpinnerTheme::validate_widget(&self.spinner, "spinner", dpi, &mut missing);
412 let combo_box =
413 ResolvedComboBoxTheme::validate_widget(&self.combo_box, "combo_box", dpi, &mut missing);
414 let segmented_control = ResolvedSegmentedControlTheme::validate_widget(
415 &self.segmented_control,
416 "segmented_control",
417 dpi,
418 &mut missing,
419 );
420 let card = ResolvedCardTheme::validate_widget(&self.card, "card", dpi, &mut missing);
421 let expander =
422 ResolvedExpanderTheme::validate_widget(&self.expander, "expander", dpi, &mut missing);
423 let link = ResolvedLinkTheme::validate_widget(&self.link, "link", dpi, &mut missing);
424
425 let icon_set = require(&self.icon_set, "icon_set", &mut missing);
426 let icon_theme = require(&self.icon_theme, "icon_theme", &mut missing);
427
428 window.check_ranges("window", &mut missing);
430 button.check_ranges("button", &mut missing);
431 input.check_ranges("input", &mut missing);
432 checkbox.check_ranges("checkbox", &mut missing);
433 menu.check_ranges("menu", &mut missing);
434 tooltip.check_ranges("tooltip", &mut missing);
435 scrollbar.check_ranges("scrollbar", &mut missing);
436 slider.check_ranges("slider", &mut missing);
437 progress_bar.check_ranges("progress_bar", &mut missing);
438 tab.check_ranges("tab", &mut missing);
439 sidebar.check_ranges("sidebar", &mut missing);
440 toolbar.check_ranges("toolbar", &mut missing);
441 status_bar.check_ranges("status_bar", &mut missing);
442 list.check_ranges("list", &mut missing);
443 popover.check_ranges("popover", &mut missing);
444 splitter.check_ranges("splitter", &mut missing);
445 separator.check_ranges("separator", &mut missing);
446 switch.check_ranges("switch", &mut missing);
447 dialog.check_ranges("dialog", &mut missing);
448 spinner.check_ranges("spinner", &mut missing);
449 combo_box.check_ranges("combo_box", &mut missing);
450 segmented_control.check_ranges("segmented_control", &mut missing);
451 expander.check_ranges("expander", &mut missing);
452 link.check_ranges("link", &mut missing);
453
454 if !missing.is_empty() {
455 return Err(crate::Error::Resolution(ThemeResolutionError {
456 missing_fields: missing,
457 }));
458 }
459
460 Ok(ResolvedThemeVariant {
461 defaults,
462 text_scale,
463 window,
464 button,
465 input,
466 checkbox,
467 menu,
468 tooltip,
469 scrollbar,
470 slider,
471 progress_bar,
472 tab,
473 sidebar,
474 toolbar,
475 status_bar,
476 list,
477 popover,
478 splitter,
479 separator,
480 switch,
481 dialog,
482 spinner,
483 combo_box,
484 segmented_control,
485 card,
486 expander,
487 link,
488 icon_set,
489 icon_theme,
490 })
491 }
492}