system_theme/platform/
windows.rs1use std::sync::Arc;
2use tokio::sync::Notify;
3use windows::{
4 core::HSTRING,
5 Foundation::{Metadata::ApiInformation, TypedEventHandler},
6 UI::{
7 Color,
8 ViewManagement::{AccessibilitySettings, UIColorType, UISettings},
9 },
10};
11
12use crate::{error::Error, ThemeColor, ThemeContrast, ThemeKind, ThemeScheme};
13
14impl From<Color> for ThemeColor {
15 fn from(color: Color) -> Self {
16 ThemeColor {
17 red: color.R as f32 / 255.0,
18 green: color.G as f32 / 255.0,
19 blue: color.B as f32 / 255.0,
20 }
21 }
22}
23
24fn check_color_supported() -> Result<bool, Error> {
26 const GET_COLOR_VALUE_TYPE: &str = "Windows.UI.ViewManagement.UISettings";
27 const GET_COLOR_VALUE_METHOD: &str = "GetColorValue";
28
29 ApiInformation::IsMethodPresent(
30 &HSTRING::from(GET_COLOR_VALUE_TYPE),
31 &HSTRING::from(GET_COLOR_VALUE_METHOD),
32 )
33 .map_err(Error::from_platform)
34}
35
36fn check_high_contrast_supported() -> Result<bool, Error> {
38 const GET_HIGH_CONTRAST_TYPE: &str = "Windows.UI.ViewManagement.AccessibilitySettings";
39 const GET_HIGH_CONTRAST_VALUE_METHOD: &str = "HighContrast";
40
41 ApiInformation::IsPropertyPresent(
42 &HSTRING::from(GET_HIGH_CONTRAST_TYPE),
43 &HSTRING::from(GET_HIGH_CONTRAST_VALUE_METHOD),
44 )
45 .map_err(Error::from_platform)
46}
47
48pub struct Platform {
49 ui_settings: Option<UISettings>,
50 a11y_settings: Option<AccessibilitySettings>,
51 notify: Arc<Notify>,
52}
53
54impl Platform {
55 pub fn new() -> Result<Self, Error> {
56 let notify = Arc::new(Notify::new());
57
58 let ui_settings = if check_color_supported()? {
60 let ui_settings = UISettings::new().map_err(Error::from_platform)?;
61
62 let notify_cloned = notify.clone();
64 let _ = ui_settings.ColorValuesChanged(&TypedEventHandler::new(move |_, _| {
65 notify_cloned.notify_waiters();
66 Ok(())
67 }));
68
69 Some(ui_settings)
70 } else {
71 None
72 };
73
74 let a11y_settings = if check_high_contrast_supported()? {
76 Some(AccessibilitySettings::new().map_err(Error::from_platform)?)
77 } else {
78 None
79 };
80
81 Ok(Platform {
82 ui_settings,
83 a11y_settings,
84 notify,
85 })
86 }
87
88 pub fn theme_kind(&self) -> Result<ThemeKind, Error> {
89 Ok(ThemeKind::Windows)
90 }
91
92 pub fn theme_scheme(&self) -> Result<ThemeScheme, Error> {
93 let background = self.get_ui_color(UIColorType::Background)?;
95
96 let color_sum = background.R as u16 + background.G as u16 + background.B as u16;
98 Ok(if color_sum < 3 * 128 {
99 ThemeScheme::Dark
100 } else {
101 ThemeScheme::Light
102 })
103 }
104
105 pub fn theme_contrast(&self) -> Result<ThemeContrast, Error> {
106 self.a11y_settings
108 .as_ref()
109 .map(|settings| {
110 settings
111 .HighContrast()
112 .map(|high_contrast| {
113 if high_contrast {
114 ThemeContrast::High
115 } else {
116 ThemeContrast::Normal
117 }
118 })
119 .map_err(Error::from_platform)
120 })
121 .unwrap_or(Err(Error::Unsupported))
122 }
123
124 pub fn theme_accent(&self) -> Result<ThemeColor, Error> {
125 self.get_ui_color(UIColorType::Accent)
127 .map(|color| color.into())
128 }
129
130 pub fn get_notify(&self) -> Arc<Notify> {
131 self.notify.clone()
132 }
133
134 fn get_ui_color(&self, color_type: UIColorType) -> Result<Color, Error> {
135 self.ui_settings
137 .as_ref()
138 .map(|settings| {
139 settings
140 .GetColorValue(color_type)
141 .map_err(Error::from_platform)
142 })
143 .unwrap_or(Err(Error::Unsupported))
144 }
145}