1#![warn(missing_docs)]
31#![forbid(unsafe_code)]
32#![deny(clippy::unwrap_used)]
33#![deny(clippy::expect_used)]
34
35pub mod extended;
36pub mod icons;
37pub mod palette;
38
39#[deprecated(since = "0.3.2", note = "Use NativeTheme::pick_variant() instead")]
46#[allow(deprecated)]
47pub fn pick_variant(
48 theme: &native_theme::NativeTheme,
49 is_dark: bool,
50) -> Option<&native_theme::ThemeVariant> {
51 theme.pick_variant(is_dark)
52}
53
54pub fn to_theme(resolved: &native_theme::ResolvedTheme, name: &str) -> iced_core::theme::Theme {
67 let pal = palette::to_palette(resolved);
68
69 let button_bg = resolved.button.background;
71 let button_fg = resolved.button.foreground;
72 let surface = resolved.defaults.surface;
73 let foreground = resolved.defaults.foreground;
74
75 iced_core::theme::Theme::custom_with_fn(name.to_string(), pal, move |p| {
76 let mut ext = iced_core::theme::palette::Extended::generate(p);
77
78 ext.secondary.base.color = palette::to_color(button_bg);
79 ext.secondary.base.text = palette::to_color(button_fg);
80 ext.background.weak.color = palette::to_color(surface);
81 ext.background.weak.text = palette::to_color(foreground);
82
83 ext
84 })
85}
86
87pub fn button_padding(resolved: &native_theme::ResolvedTheme) -> [f32; 2] {
89 [
90 resolved.button.padding_horizontal,
91 resolved.button.padding_vertical,
92 ]
93}
94
95pub fn input_padding(resolved: &native_theme::ResolvedTheme) -> [f32; 2] {
97 [
98 resolved.input.padding_horizontal,
99 resolved.input.padding_vertical,
100 ]
101}
102
103pub fn border_radius(resolved: &native_theme::ResolvedTheme) -> f32 {
105 resolved.defaults.radius
106}
107
108pub fn border_radius_lg(resolved: &native_theme::ResolvedTheme) -> f32 {
110 resolved.defaults.radius_lg
111}
112
113pub fn scrollbar_width(resolved: &native_theme::ResolvedTheme) -> f32 {
115 resolved.scrollbar.width
116}
117
118pub fn font_family(resolved: &native_theme::ResolvedTheme) -> &str {
120 &resolved.defaults.font.family
121}
122
123pub fn font_size(resolved: &native_theme::ResolvedTheme) -> f32 {
128 resolved.defaults.font.size
129}
130
131pub fn mono_font_family(resolved: &native_theme::ResolvedTheme) -> &str {
133 &resolved.defaults.mono_font.family
134}
135
136pub fn mono_font_size(resolved: &native_theme::ResolvedTheme) -> f32 {
141 resolved.defaults.mono_font.size
142}
143
144#[cfg(test)]
145#[allow(deprecated)]
146#[allow(clippy::unwrap_used, clippy::expect_used)]
147mod tests {
148 use super::*;
149 use native_theme::{NativeTheme, ThemeVariant};
150
151 fn make_resolved(is_dark: bool) -> native_theme::ResolvedTheme {
152 let nt = NativeTheme::preset("catppuccin-mocha").unwrap();
153 let mut variant = nt.pick_variant(is_dark).unwrap().clone();
154 variant.resolve();
155 variant.validate().unwrap()
156 }
157
158 #[test]
161 fn pick_variant_light_preferred_returns_light() {
162 let mut theme = NativeTheme::new("Test");
163 theme.light = Some(ThemeVariant::default());
164 theme.dark = Some(ThemeVariant::default());
165
166 let result = pick_variant(&theme, false);
167 assert!(result.is_some());
168 assert!(std::ptr::eq(result.unwrap(), theme.light.as_ref().unwrap()));
169 }
170
171 #[test]
172 fn pick_variant_dark_preferred_returns_dark() {
173 let mut theme = NativeTheme::new("Test");
174 theme.light = Some(ThemeVariant::default());
175 theme.dark = Some(ThemeVariant::default());
176
177 let result = pick_variant(&theme, true);
178 assert!(result.is_some());
179 assert!(std::ptr::eq(result.unwrap(), theme.dark.as_ref().unwrap()));
180 }
181
182 #[test]
183 fn pick_variant_falls_back_to_light_when_no_dark() {
184 let mut theme = NativeTheme::new("Test");
185 theme.light = Some(ThemeVariant::default());
186
187 let result = pick_variant(&theme, true);
188 assert!(result.is_some());
189 assert!(std::ptr::eq(result.unwrap(), theme.light.as_ref().unwrap()));
190 }
191
192 #[test]
193 fn pick_variant_falls_back_to_dark_when_no_light() {
194 let mut theme = NativeTheme::new("Test");
195 theme.dark = Some(ThemeVariant::default());
196
197 let result = pick_variant(&theme, false);
198 assert!(result.is_some());
199 assert!(std::ptr::eq(result.unwrap(), theme.dark.as_ref().unwrap()));
200 }
201
202 #[test]
203 fn pick_variant_returns_none_when_empty() {
204 let theme = NativeTheme::new("Test");
205 assert!(pick_variant(&theme, false).is_none());
206 assert!(pick_variant(&theme, true).is_none());
207 }
208
209 #[test]
212 fn to_theme_produces_non_default_theme() {
213 let resolved = make_resolved(true);
214 let theme = to_theme(&resolved, "Test Theme");
215
216 assert_ne!(theme, iced_core::theme::Theme::Light);
217 assert_ne!(theme, iced_core::theme::Theme::Dark);
218
219 let palette = theme.palette();
220 assert!(
222 palette.primary.r > 0.0 || palette.primary.g > 0.0 || palette.primary.b > 0.0,
223 "primary should be non-zero"
224 );
225 }
226
227 #[test]
228 fn to_theme_from_preset() {
229 let resolved = make_resolved(false);
230 let theme = to_theme(&resolved, "Default");
231
232 let palette = theme.palette();
233 assert!(palette.background.r > 0.9);
235 }
236
237 #[test]
240 fn border_radius_returns_resolved_value() {
241 let resolved = make_resolved(false);
242 let r = border_radius(&resolved);
243 assert!(r > 0.0, "resolved radius should be > 0");
244 }
245
246 #[test]
247 fn border_radius_lg_returns_resolved_value() {
248 let resolved = make_resolved(false);
249 let r = border_radius_lg(&resolved);
250 assert!(r > 0.0, "resolved radius_lg should be > 0");
251 assert!(
252 r >= border_radius(&resolved),
253 "radius_lg should be >= radius"
254 );
255 }
256
257 #[test]
258 fn scrollbar_width_returns_resolved_value() {
259 let resolved = make_resolved(false);
260 let w = scrollbar_width(&resolved);
261 assert!(w > 0.0, "scrollbar width should be > 0");
262 }
263
264 #[test]
265 fn button_padding_returns_resolved_values() {
266 let resolved = make_resolved(false);
267 let [h, v] = button_padding(&resolved);
268 assert!(h > 0.0, "button horizontal padding should be > 0");
269 assert!(v > 0.0, "button vertical padding should be > 0");
270 }
271
272 #[test]
273 fn input_padding_returns_resolved_values() {
274 let resolved = make_resolved(false);
275 let [h, v] = input_padding(&resolved);
276 assert!(h > 0.0, "input horizontal padding should be > 0");
277 assert!(v > 0.0, "input vertical padding should be > 0");
278 }
279
280 #[test]
283 fn font_family_returns_concrete_value() {
284 let resolved = make_resolved(false);
285 let ff = font_family(&resolved);
286 assert!(!ff.is_empty(), "font family should not be empty");
287 }
288
289 #[test]
290 fn font_size_returns_concrete_value() {
291 let resolved = make_resolved(false);
292 let fs = font_size(&resolved);
293 assert!(fs > 0.0, "font size should be > 0");
294 }
295
296 #[test]
297 fn mono_font_family_returns_concrete_value() {
298 let resolved = make_resolved(false);
299 let mf = mono_font_family(&resolved);
300 assert!(!mf.is_empty(), "mono font family should not be empty");
301 }
302
303 #[test]
304 fn mono_font_size_returns_concrete_value() {
305 let resolved = make_resolved(false);
306 let ms = mono_font_size(&resolved);
307 assert!(ms > 0.0, "mono font size should be > 0");
308 }
309}