use accesskit::NodeId as AccessibilityId;
use dioxus_core::{use_hook, AttributeValue};
use dioxus_hooks::use_context;
use dioxus_signals::{Readable, Signal, Writable};
use freya_core::{accessibility::ACCESSIBILITY_ROOT_ID, navigation_mode::NavigationMode};
use freya_elements::events::{keyboard::Code, KeyboardEvent};
use freya_node_state::CustomAttributeValues;
use crate::AccessibilityIdCounter;
#[derive(Clone)]
pub struct UseFocus {
id: AccessibilityId,
focused_id: Signal<AccessibilityId>,
navigation_mode: Signal<NavigationMode>,
}
impl UseFocus {
pub fn focus(&mut self) {
*self.focused_id.write() = self.id
}
pub fn id(&self) -> AccessibilityId {
self.id
}
pub fn attribute(&self) -> AttributeValue {
AttributeValue::any_value(CustomAttributeValues::AccessibilityId(self.id))
}
pub fn is_focused(&self) -> bool {
self.id == *self.focused_id.read()
}
pub fn is_selected(&self) -> bool {
self.is_focused() && *self.navigation_mode.read() == NavigationMode::Keyboard
}
pub fn unfocus(&mut self) {
*self.focused_id.write() = ACCESSIBILITY_ROOT_ID;
}
pub fn validate_keydown(&self, e: KeyboardEvent) -> bool {
e.data.code == Code::Enter && self.is_selected()
}
}
pub fn use_focus() -> UseFocus {
let accessibility_id_counter = use_context::<AccessibilityIdCounter>();
let focused_id = use_context::<Signal<AccessibilityId>>();
let navigation_mode = use_context::<Signal<NavigationMode>>();
use_hook(move || {
let mut counter = accessibility_id_counter.borrow_mut();
*counter += 1;
let id = AccessibilityId(*counter);
UseFocus {
id,
focused_id,
navigation_mode,
}
})
}
#[cfg(test)]
mod test {
use crate::use_focus;
use freya::prelude::*;
use freya_testing::{
events::pointer::MouseButton, launch_test_with_config, FreyaEvent, TestingConfig,
};
#[tokio::test]
pub async fn track_focus() {
#[allow(non_snake_case)]
fn OherChild() -> Element {
let mut focus_manager = use_focus();
rsx!(
rect {
width: "100%",
height: "50%",
onclick: move |_| focus_manager.focus(),
"{focus_manager.is_focused()}"
}
)
}
fn use_focus_app() -> Element {
rsx!(
rect {
width: "100%",
height: "100%",
OherChild {},
OherChild {}
}
)
}
let mut utils = launch_test_with_config(
use_focus_app,
*TestingConfig::default().with_size((100.0, 100.0).into()),
);
utils.wait_for_update().await;
let root = utils.root().get(0);
assert_eq!(root.get(0).get(0).text(), Some("false"));
assert_eq!(root.get(1).get(0).text(), Some("false"));
utils.push_event(FreyaEvent::Mouse {
name: "click".to_string(),
cursor: (5.0, 5.0).into(),
button: Some(MouseButton::Left),
});
utils.wait_for_update().await;
assert_eq!(root.get(0).get(0).text(), Some("true"));
assert_eq!(root.get(1).get(0).text(), Some("false"));
utils.push_event(FreyaEvent::Mouse {
name: "click".to_string(),
cursor: (5.0, 75.0).into(),
button: Some(MouseButton::Left),
});
utils.wait_for_update().await;
assert_eq!(root.get(0).get(0).text(), Some("false"));
assert_eq!(root.get(1).get(0).text(), Some("true"));
}
}