1use crate::CooljapanTheme;
19use oxiui_core::{Color, FontSpec, Palette, Theme};
20
21#[derive(Clone, Debug, Default)]
26pub struct PartialTheme {
27 pub background: Option<Color>,
29 pub surface: Option<Color>,
31 pub primary: Option<Color>,
33 pub on_primary: Option<Color>,
35 pub text_primary: Option<Color>,
37 pub text_secondary: Option<Color>,
39 pub font_family: Option<String>,
41 pub font_size: Option<f32>,
43 pub font_weight: Option<u16>,
45}
46
47pub fn overlay(base: &dyn Theme, overrides: &PartialTheme) -> CooljapanTheme {
52 let bp = base.palette();
53 let bf = base.font();
54
55 let palette = Palette {
56 background: overrides.background.unwrap_or(bp.background),
57 surface: overrides.surface.unwrap_or(bp.surface),
58 primary: overrides.primary.unwrap_or(bp.primary),
59 on_primary: overrides.on_primary.unwrap_or(bp.on_primary),
60 text: overrides.text_primary.unwrap_or(bp.text),
61 muted: overrides.text_secondary.unwrap_or(bp.muted),
62 };
63 let font = FontSpec::new(
64 overrides.font_family.as_deref().unwrap_or(&bf.family),
65 overrides.font_size.unwrap_or(bf.size),
66 overrides.font_weight.unwrap_or(bf.weight),
67 );
68 CooljapanTheme::new(palette, font)
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use oxiui_core::Theme;
75
76 fn base_theme() -> Box<dyn Theme> {
77 crate::dark()
78 }
79
80 #[test]
81 fn overlay_override_precedence() {
82 let base = base_theme();
83 let red = Color(200, 0, 0, 255);
84 let overrides = PartialTheme {
85 background: Some(red),
86 ..PartialTheme::default()
87 };
88 let result = overlay(base.as_ref(), &overrides);
89 assert_eq!(
90 result.palette().background,
91 red,
92 "overlay must apply the override background"
93 );
94 }
95
96 #[test]
97 fn overlay_none_fields_keep_base() {
98 let base = base_theme();
99 let original_bg = base.palette().background;
100 let overrides = PartialTheme::default(); let result = overlay(base.as_ref(), &overrides);
102 assert_eq!(
103 result.palette().background,
104 original_bg,
105 "empty overlay must preserve base"
106 );
107 assert_eq!(result.palette().surface, base.palette().surface);
108 assert_eq!(result.palette().text, base.palette().text);
109 }
110
111 #[test]
112 fn overlay_multiple_fields() {
113 let base = base_theme();
114 let new_text = Color(0, 255, 0, 255);
115 let new_primary = Color(0, 0, 255, 255);
116 let overrides = PartialTheme {
117 text_primary: Some(new_text),
118 primary: Some(new_primary),
119 ..PartialTheme::default()
120 };
121 let result = overlay(base.as_ref(), &overrides);
122 assert_eq!(result.palette().text, new_text);
123 assert_eq!(result.palette().primary, new_primary);
124 assert_eq!(result.palette().background, base.palette().background);
126 }
127}