use keyboard_types::{
Key,
Modifiers,
NamedKey,
};
use crate::{
accessibility::id::AccessibilityId,
integration::{
ACCESSIBILITY_ROOT_ID,
AccessibilityGenerator,
},
lifecycle::reactive::use_reactive,
platform::{
NavigationMode,
Platform,
},
prelude::{
AccessibilityFocusStrategy,
KeyboardEventData,
Memo,
ScreenReader,
UserEvent,
consume_root_context,
use_hook,
use_memo,
},
};
pub trait AccessibilityIdExt {
fn is_focused(&self) -> bool;
fn request_focus(&self);
fn request_unfocus(&self);
fn new_unique() -> AccessibilityId;
}
impl AccessibilityIdExt for AccessibilityId {
fn is_focused(&self) -> bool {
let platform = Platform::get();
*platform.focused_accessibility_id.read() == *self
}
fn request_focus(&self) {
let platform = Platform::get();
if *platform.focused_accessibility_id.peek() != *self {
Platform::get().send(UserEvent::FocusAccessibilityNode(
AccessibilityFocusStrategy::Node(*self),
));
}
}
fn request_unfocus(&self) {
let platform = Platform::get();
if *platform.focused_accessibility_id.peek() == *self {
Platform::get().send(UserEvent::FocusAccessibilityNode(
AccessibilityFocusStrategy::Node(ACCESSIBILITY_ROOT_ID),
));
}
}
fn new_unique() -> Self {
let accessibility_generator = consume_root_context::<AccessibilityGenerator>();
AccessibilityId(accessibility_generator.new_id())
}
}
pub fn use_a11y() -> AccessibilityId {
use_hook(AccessibilityId::new_unique)
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Focus {
Not,
Pointer,
Keyboard,
}
impl Focus {
pub fn is_focused(&self) -> bool {
matches!(self, Self::Pointer | Self::Keyboard)
}
}
pub trait KeyboardEventExt {
fn is_press_event(&self) -> bool;
}
impl KeyboardEventExt for KeyboardEventData {
fn is_press_event(&self) -> bool {
let is_space = matches!(self.key, Key::Character(ref s) if s == " ");
let is_enter = self.key == Key::Named(NamedKey::Enter);
if cfg!(target_os = "macos") {
let screen_reader = ScreenReader::get();
if screen_reader.is_on() {
is_space
&& self.modifiers.contains(Modifiers::CONTROL)
&& self.modifiers.contains(Modifiers::ALT)
} else {
is_enter || is_space
}
} else {
is_enter || is_space
}
}
}
pub fn use_focus(a11y_id: AccessibilityId) -> Memo<Focus> {
let id = use_reactive(&a11y_id);
use_memo(move || {
let platform = Platform::get();
let is_focused = *platform.focused_accessibility_id.read() == id();
let is_keyboard = *platform.navigation_mode.read() == NavigationMode::Keyboard;
match (is_focused, is_keyboard) {
(true, false) => Focus::Pointer,
(true, true) => Focus::Keyboard,
_ => Focus::Not,
}
})
}