system_theme/platform/
windows.rs

1use windows::{
2    core::HSTRING,
3    Foundation::Metadata::ApiInformation,
4    UI::{
5        Color,
6        ViewManagement::{AccessibilitySettings, UIColorType, UISettings},
7    },
8};
9
10use crate::{error::Error, ThemeColor, ThemeContrast, ThemeKind, ThemeScheme};
11
12impl From<Color> for ThemeColor {
13    fn from(color: Color) -> Self {
14        ThemeColor {
15            red: color.R as f32 / 255.0,
16            green: color.G as f32 / 255.0,
17            blue: color.B as f32 / 255.0,
18        }
19    }
20}
21
22// Check if GetColorValue is supported
23fn check_color_supported() -> Result<bool, Error> {
24    const GET_COLOR_VALUE_TYPE: &str = "Windows.UI.ViewManagement.UISettings";
25    const GET_COLOR_VALUE_METHOD: &str = "GetColorValue";
26
27    ApiInformation::IsMethodPresent(
28        &HSTRING::from(GET_COLOR_VALUE_TYPE),
29        &HSTRING::from(GET_COLOR_VALUE_METHOD),
30    )
31    .map_err(Error::from_platform)
32}
33
34// Check if GetColorValue is supported
35fn check_high_contrast_supported() -> Result<bool, Error> {
36    const GET_HIGH_CONTRAST_TYPE: &str = "Windows.UI.ViewManagement.AccessibilitySettings";
37    const GET_HIGH_CONTRAST_VALUE_METHOD: &str = "HighContrast";
38
39    ApiInformation::IsPropertyPresent(
40        &HSTRING::from(GET_HIGH_CONTRAST_TYPE),
41        &HSTRING::from(GET_HIGH_CONTRAST_VALUE_METHOD),
42    )
43    .map_err(Error::from_platform)
44}
45
46pub struct Platform {
47    ui_settings: Option<UISettings>,
48    a11y_settings: Option<AccessibilitySettings>,
49}
50
51impl Platform {
52    pub fn new() -> Result<Self, Error> {
53        // Check if GetColorValue is supported
54        let ui_settings = if check_color_supported()? {
55            Some(UISettings::new().map_err(Error::from_platform)?)
56        } else {
57            None
58        };
59
60        // Check if HighContrast is supported
61        let a11y_settings = if check_high_contrast_supported()? {
62            Some(AccessibilitySettings::new().map_err(Error::from_platform)?)
63        } else {
64            None
65        };
66
67        Ok(Platform {
68            ui_settings,
69            a11y_settings,
70        })
71    }
72
73    pub fn theme_kind(&self) -> Result<ThemeKind, Error> {
74        Ok(ThemeKind::Windows)
75    }
76
77    pub fn theme_scheme(&self) -> Result<ThemeScheme, Error> {
78        // Get the background color reported by windows and check if dark
79        let background = self.get_ui_color(UIColorType::Background)?;
80        Ok(if ThemeColor::from(background).is_dark() {
81            ThemeScheme::Dark
82        } else {
83            ThemeScheme::Light
84        })
85    }
86
87    pub fn theme_contrast(&self) -> Result<ThemeContrast, Error> {
88        // Check if high contrast mode is enabled (if supported)
89        self.a11y_settings
90            .as_ref()
91            .map(|settings| {
92                settings
93                    .HighContrast()
94                    .map(|high_contrast| {
95                        if high_contrast {
96                            ThemeContrast::High
97                        } else {
98                            ThemeContrast::Normal
99                        }
100                    })
101                    .map_err(Error::from_platform)
102            })
103            .unwrap_or(Err(Error::Unsupported))
104    }
105
106    pub fn theme_accent(&self) -> Result<ThemeColor, Error> {
107        // Get main accent color. Ignoring accent shades for now.
108        self.get_ui_color(UIColorType::Accent)
109            .map(|color| color.into())
110    }
111
112    fn get_ui_color(&self, color_type: UIColorType) -> Result<Color, Error> {
113        // Get the color reported by windows (if supported)
114        self.ui_settings
115            .as_ref()
116            .map(|settings| {
117                settings
118                    .GetColorValue(color_type)
119                    .map_err(Error::from_platform)
120            })
121            .unwrap_or(Err(Error::Unsupported))
122    }
123}