use std::sync::Arc;
use dioxus_core::{
use_hook,
AttributeValue,
};
use dioxus_hooks::{
use_context,
use_memo,
};
use dioxus_signals::{
Memo,
ReadOnlySignal,
Readable,
Signal,
Writable,
};
use freya_common::AccessibilityGenerator;
use freya_core::{
accessibility::ACCESSIBILITY_ROOT_ID,
platform_state::NavigationMode,
prelude::EventMessage,
types::{
AccessibilityId,
AccessibilityNode,
},
};
use freya_elements::events::{
keyboard::Code,
KeyboardEvent,
};
use freya_node_state::CustomAttributeValues;
use crate::{
use_platform,
NavigationMark,
UsePlatform,
};
#[derive(Clone, Copy)]
pub struct UseFocus {
id: AccessibilityId,
is_selected: Memo<bool>,
is_focused: Memo<bool>,
navigation_mode: Signal<NavigationMode>,
navigation_mark: Signal<NavigationMark>,
platform: UsePlatform,
focused_id: Signal<AccessibilityId>,
focused_node: Signal<AccessibilityNode>,
}
impl UseFocus {
pub fn focus(&mut self) {
if !*self.is_focused.peek() {
self.platform
.send(EventMessage::FocusAccessibilityNode(self.id))
.ok();
}
}
pub fn id(&self) -> AccessibilityId {
self.id
}
pub fn attribute(&self) -> AttributeValue {
Self::attribute_for_id(self.id)
}
pub fn attribute_for_id(id: AccessibilityId) -> AttributeValue {
AttributeValue::any_value(CustomAttributeValues::AccessibilityId(id))
}
pub fn is_focused(&self) -> bool {
*self.is_focused.read()
}
pub fn is_selected(&self) -> bool {
*self.is_selected.read() && *self.navigation_mode.read() == NavigationMode::Keyboard
}
pub fn unfocus(&mut self) {
self.platform
.send(EventMessage::FocusAccessibilityNode(ACCESSIBILITY_ROOT_ID))
.ok();
}
pub fn validate_globalkeydown(&self, e: &KeyboardEvent) -> bool {
e.data.code == Code::Enter && self.is_selected()
}
pub fn prevent_navigation(&mut self) {
self.navigation_mark.write().set_allowed(false);
}
pub fn focused_id(&self) -> ReadOnlySignal<AccessibilityId> {
self.focused_id.into()
}
pub fn focused_node(&self) -> ReadOnlySignal<AccessibilityNode> {
self.focused_node.into()
}
}
pub fn use_focus() -> UseFocus {
let accessibility_generator = use_context::<Arc<AccessibilityGenerator>>();
let focused_id = use_context::<Signal<AccessibilityId>>();
let focused_node = use_context::<Signal<AccessibilityNode>>();
let navigation_mode = use_context::<Signal<NavigationMode>>();
let navigation_mark = use_context::<Signal<NavigationMark>>();
let platform = use_platform();
let id = use_hook(|| AccessibilityId(accessibility_generator.new_id()));
let is_focused = use_memo(move || id == *focused_id.read());
let is_selected =
use_memo(move || *is_focused.read() && *navigation_mode.read() == NavigationMode::Keyboard);
use_hook(move || UseFocus {
id,
is_focused,
is_selected,
navigation_mode,
navigation_mark,
platform,
focused_id,
focused_node,
})
}