use crate::utils::accessibility::Priority;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum BackendType {
#[default]
Auto,
MacOS,
Windows,
Linux,
Logging,
None,
}
impl BackendType {
pub fn detect() -> Self {
#[cfg(target_os = "macos")]
{
if super::detection::is_voiceover_running() {
return BackendType::MacOS;
}
}
#[cfg(target_os = "windows")]
{
if super::detection::is_windows_screen_reader_running() {
return BackendType::Windows;
}
}
#[cfg(target_os = "linux")]
{
if super::detection::is_atspi_available() {
return BackendType::Linux;
}
}
if std::env::var("REVUE_A11Y_LOG").is_ok() {
return BackendType::Logging;
}
BackendType::None
}
pub fn name(&self) -> &'static str {
match self {
BackendType::Auto => "Auto",
BackendType::MacOS => "macOS/VoiceOver",
BackendType::Windows => "Windows/Narrator",
BackendType::Linux => "Linux/AT-SPI",
BackendType::Logging => "Logging",
BackendType::None => "None",
}
}
}
pub trait ScreenReader: Send + Sync {
fn announce(&self, message: &str, priority: Priority);
fn is_available(&self) -> bool;
fn active_screen_reader(&self) -> Option<String>;
fn announce_focus(&self, label: &str, role: &str) {
self.announce(&format!("{}, {}", label, role), Priority::Polite);
}
fn announce_state(&self, label: &str, state: &str) {
self.announce(&format!("{}: {}", label, state), Priority::Polite);
}
fn announce_error(&self, message: &str) {
self.announce(&format!("Error: {}", message), Priority::Assertive);
}
fn stop(&self) {}
}
#[derive(Clone, Debug)]
pub struct ScreenReaderConfig {
pub backend_type: BackendType,
pub log_announcements: bool,
pub debounce_ms: u64,
pub announce_roles: bool,
}
impl Default for ScreenReaderConfig {
fn default() -> Self {
Self {
backend_type: BackendType::Auto,
log_announcements: false,
debounce_ms: 100,
announce_roles: true,
}
}
}