#[derive(Debug, Clone, PartialEq)]
pub struct AccessibilityPreferences {
pub high_contrast: bool,
pub reduced_motion: bool,
pub text_scale_factor: f64,
}
impl Default for AccessibilityPreferences {
fn default() -> Self {
Self {
high_contrast: false,
reduced_motion: false,
text_scale_factor: 1.0,
}
}
}
impl AccessibilityPreferences {
pub fn query() -> Self {
platform::query()
}
pub fn prefers_large_text(&self) -> bool {
self.text_scale_factor > 1.0
}
}
#[cfg(target_os = "linux")]
mod platform {
use super::AccessibilityPreferences;
use crate::linux_helpers::{read_gsettings, read_portal_u32};
pub(super) fn query() -> AccessibilityPreferences {
let mut prefs = AccessibilityPreferences::default();
if let Some(v) = read_portal_u32("org.freedesktop.appearance", "contrast") {
prefs.high_contrast = v == 1;
}
if let Some(v) = read_portal_u32("org.freedesktop.appearance", "reduced-motion") {
prefs.reduced_motion = v == 1;
}
if !prefs.high_contrast
&& let Some(theme) = read_gsettings("org.gnome.desktop.interface", "gtk-theme")
{
prefs.high_contrast = theme.contains("HighContrast");
}
if !prefs.high_contrast
&& let Some(val) = read_gsettings("org.gnome.desktop.a11y.interface", "high-contrast")
{
prefs.high_contrast = val == "true";
}
if !prefs.reduced_motion
&& let Some(val) = read_gsettings("org.gnome.desktop.interface", "enable-animations")
{
prefs.reduced_motion = val == "false";
}
if let Some(val) = read_gsettings("org.gnome.desktop.interface", "text-scaling-factor")
&& let Ok(scale) = val.parse::<f64>()
{
prefs.text_scale_factor = scale;
}
prefs
}
}
#[cfg(target_os = "macos")]
mod platform {
use super::AccessibilityPreferences;
pub(super) fn query() -> AccessibilityPreferences {
let mut prefs = AccessibilityPreferences::default();
let workspace = objc2_app_kit::NSWorkspace::sharedWorkspace();
prefs.high_contrast = workspace.accessibilityDisplayShouldIncreaseContrast();
prefs.reduced_motion = workspace.accessibilityDisplayShouldReduceMotion();
prefs
}
}
#[cfg(target_os = "windows")]
mod platform {
use super::AccessibilityPreferences;
pub(super) fn query() -> AccessibilityPreferences {
let mut prefs = AccessibilityPreferences::default();
prefs.high_contrast = query_high_contrast();
let (reduced_motion, text_scale) = query_ui_settings();
prefs.reduced_motion = reduced_motion;
prefs.text_scale_factor = text_scale;
prefs
}
fn query_high_contrast() -> bool {
use std::mem;
use windows::Win32::UI::Accessibility::HIGHCONTRASTW;
use windows::Win32::UI::WindowsAndMessaging::{
SPI_GETHIGHCONTRAST, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, SystemParametersInfoW,
};
unsafe {
let mut hc = HIGHCONTRASTW {
cbSize: mem::size_of::<HIGHCONTRASTW>() as u32,
..Default::default()
};
let ok = SystemParametersInfoW(
SPI_GETHIGHCONTRAST,
hc.cbSize,
Some(&mut hc as *mut _ as *mut _),
SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),
);
if ok.is_ok() {
(hc.dwFlags.0 & 0x01) != 0
} else {
false
}
}
}
fn query_ui_settings() -> (bool, f64) {
use windows::UI::ViewManagement::UISettings;
let mut reduced_motion = false;
let mut text_scale = 1.0_f64;
if let Ok(settings) = UISettings::new() {
if let Ok(animations_enabled) = settings.AnimationsEnabled() {
reduced_motion = !animations_enabled;
}
if let Ok(scale) = settings.TextScaleFactor() {
text_scale = scale as f64;
}
}
(reduced_motion, text_scale)
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
mod platform {
use super::AccessibilityPreferences;
pub(super) fn query() -> AccessibilityPreferences {
AccessibilityPreferences::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_preferences() {
let prefs = AccessibilityPreferences::default();
assert!(!prefs.high_contrast);
assert!(!prefs.reduced_motion);
assert!((prefs.text_scale_factor - 1.0).abs() < f64::EPSILON);
assert!(!prefs.prefers_large_text());
}
#[test]
fn large_text_threshold() {
let mut prefs = AccessibilityPreferences {
text_scale_factor: 1.25,
..AccessibilityPreferences::default()
};
assert!(prefs.prefers_large_text());
prefs.text_scale_factor = 1.0;
assert!(!prefs.prefers_large_text());
}
#[test]
fn query_does_not_panic() {
let prefs = AccessibilityPreferences::query();
assert!(prefs.text_scale_factor > 0.0);
}
}