use accesskit::{ActionHandler, NodeId, Role, TreeUpdate};
use accesskit_consumer::{FilterResult, Node, Tree, TreeChangeHandler, TreeState};
use atspi_common::{InterfaceSet, Live, State};
use std::{
collections::HashSet,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, RwLock,
},
};
use crate::{
context::{ActionHandlerNoMut, ActionHandlerWrapper, AppContext, Context},
filters::filter,
node::{NodeIdOrRoot, NodeWrapper, PlatformNode, PlatformRoot},
util::WindowBounds,
AdapterCallback, Event, ObjectEvent, WindowEvent,
};
struct AdapterChangeHandler<'a> {
adapter: &'a Adapter,
added_nodes: HashSet<NodeId>,
removed_nodes: HashSet<NodeId>,
checked_text_change: HashSet<NodeId>,
}
impl<'a> AdapterChangeHandler<'a> {
fn new(adapter: &'a Adapter) -> Self {
Self {
adapter,
added_nodes: HashSet::new(),
removed_nodes: HashSet::new(),
checked_text_change: HashSet::new(),
}
}
fn add_node(&mut self, node: &Node) {
let id = node.id();
if self.added_nodes.contains(&id) {
return;
}
self.added_nodes.insert(id);
let role = node.role();
let is_root = node.is_root();
let node = NodeWrapper(node);
let interfaces = node.interfaces();
self.adapter.register_interfaces(node.id(), interfaces);
if is_root && role == Role::Window {
let adapter_index = self
.adapter
.context
.read_app_context()
.adapter_index(self.adapter.id)
.unwrap();
self.adapter.window_created(adapter_index, node.id());
}
let live = node.live();
if live != Live::None {
if let Some(name) = node.name() {
self.adapter
.emit_object_event(node.id(), ObjectEvent::Announcement(name, live));
}
}
}
fn add_subtree(&mut self, node: &Node) {
self.add_node(node);
for child in node.filtered_children(&filter) {
self.add_subtree(&child);
}
}
fn remove_node(&mut self, node: &Node) {
let id = node.id();
if self.removed_nodes.contains(&id) {
return;
}
self.removed_nodes.insert(id);
let role = node.role();
let is_root = node.is_root();
let node = NodeWrapper(node);
if is_root && role == Role::Window {
self.adapter.window_destroyed(node.id());
}
self.adapter
.emit_object_event(node.id(), ObjectEvent::StateChanged(State::Defunct, true));
self.adapter
.unregister_interfaces(node.id(), node.interfaces());
}
fn remove_subtree(&mut self, node: &Node) {
for child in node.filtered_children(&filter) {
self.remove_subtree(&child);
}
self.remove_node(node);
}
fn emit_text_change_if_needed_parent(&mut self, old_node: &Node, new_node: &Node) {
if !new_node.supports_text_ranges() || !old_node.supports_text_ranges() {
return;
}
let id = new_node.id();
if self.checked_text_change.contains(&id) {
return;
}
self.checked_text_change.insert(id);
let old_text = old_node.document_range().text();
let new_text = new_node.document_range().text();
let mut old_chars = old_text.chars();
let mut new_chars = new_text.chars();
let mut prefix_usv_count = 0;
let mut prefix_byte_count = 0;
loop {
match (old_chars.next(), new_chars.next()) {
(Some(old_char), Some(new_char)) if old_char == new_char => {
prefix_usv_count += 1;
prefix_byte_count += new_char.len_utf8();
}
(None, None) => return,
_ => break,
}
}
let suffix_byte_count = old_text[prefix_byte_count..]
.chars()
.rev()
.zip(new_text[prefix_byte_count..].chars().rev())
.take_while(|(old_char, new_char)| old_char == new_char)
.fold(0, |count, (c, _)| count + c.len_utf8());
let old_content = &old_text[prefix_byte_count..old_text.len() - suffix_byte_count];
if let Ok(length) = old_content.chars().count().try_into() {
if length > 0 {
self.adapter.emit_object_event(
id,
ObjectEvent::TextRemoved {
start_index: prefix_usv_count,
length,
content: old_content.to_string(),
},
);
}
}
let new_content = &new_text[prefix_byte_count..new_text.len() - suffix_byte_count];
if let Ok(length) = new_content.chars().count().try_into() {
if length > 0 {
self.adapter.emit_object_event(
id,
ObjectEvent::TextInserted {
start_index: prefix_usv_count,
length,
content: new_content.to_string(),
},
);
}
}
}
fn emit_text_change_if_needed(&mut self, old_node: &Node, new_node: &Node) {
if let Role::InlineTextBox | Role::GenericContainer = new_node.role() {
if let (Some(old_parent), Some(new_parent)) = (
old_node.filtered_parent(&filter),
new_node.filtered_parent(&filter),
) {
self.emit_text_change_if_needed_parent(&old_parent, &new_parent);
}
} else {
self.emit_text_change_if_needed_parent(old_node, new_node);
}
}
fn emit_text_selection_change(&self, old_node: Option<&Node>, new_node: &Node) {
if !new_node.supports_text_ranges() {
return;
}
let Some(old_node) = old_node else {
if let Some(selection) = new_node.text_selection() {
if !selection.is_degenerate() {
self.adapter
.emit_object_event(new_node.id(), ObjectEvent::TextSelectionChanged);
}
}
if let Some(selection_focus) = new_node.text_selection_focus() {
if let Ok(offset) = selection_focus.to_global_usv_index().try_into() {
self.adapter
.emit_object_event(new_node.id(), ObjectEvent::CaretMoved(offset));
}
}
return;
};
if !old_node.is_focused() || new_node.raw_text_selection() == old_node.raw_text_selection()
{
return;
}
if let Some(selection) = new_node.text_selection() {
if !selection.is_degenerate()
|| old_node
.text_selection()
.map(|selection| !selection.is_degenerate())
.unwrap_or(false)
{
self.adapter
.emit_object_event(new_node.id(), ObjectEvent::TextSelectionChanged);
}
}
let old_caret_position = old_node
.raw_text_selection()
.map(|selection| selection.focus);
let new_caret_position = new_node
.raw_text_selection()
.map(|selection| selection.focus);
if old_caret_position != new_caret_position {
if let Some(selection_focus) = new_node.text_selection_focus() {
if let Ok(offset) = selection_focus.to_global_usv_index().try_into() {
self.adapter
.emit_object_event(new_node.id(), ObjectEvent::CaretMoved(offset));
}
}
}
}
}
impl TreeChangeHandler for AdapterChangeHandler<'_> {
fn node_added(&mut self, node: &Node) {
if filter(node) == FilterResult::Include {
self.add_node(node);
}
}
fn node_updated(&mut self, old_node: &Node, new_node: &Node) {
self.emit_text_change_if_needed(old_node, new_node);
let filter_old = filter(old_node);
let filter_new = filter(new_node);
if filter_new != filter_old {
if filter_new == FilterResult::Include {
if filter_old == FilterResult::ExcludeSubtree {
self.add_subtree(new_node);
} else {
self.add_node(new_node);
}
} else if filter_old == FilterResult::Include {
if filter_new == FilterResult::ExcludeSubtree {
self.remove_subtree(old_node);
} else {
self.remove_node(old_node);
}
}
} else if filter_new == FilterResult::Include {
let old_wrapper = NodeWrapper(old_node);
let new_wrapper = NodeWrapper(new_node);
let old_interfaces = old_wrapper.interfaces();
let new_interfaces = new_wrapper.interfaces();
let kept_interfaces = old_interfaces & new_interfaces;
self.adapter
.unregister_interfaces(new_wrapper.id(), old_interfaces ^ kept_interfaces);
self.adapter
.register_interfaces(new_node.id(), new_interfaces ^ kept_interfaces);
let bounds = *self.adapter.context.read_root_window_bounds();
new_wrapper.notify_changes(&bounds, self.adapter, &old_wrapper);
self.emit_text_selection_change(Some(old_node), new_node);
}
}
fn focus_moved(&mut self, old_node: Option<&Node>, new_node: Option<&Node>) {
if let (None, Some(new_node)) = (old_node, new_node) {
if let Some(root_window) = root_window(new_node.tree_state) {
self.adapter.window_activated(&NodeWrapper(&root_window));
}
} else if let (Some(old_node), None) = (old_node, new_node) {
if let Some(root_window) = root_window(old_node.tree_state) {
self.adapter.window_deactivated(&NodeWrapper(&root_window));
}
}
if let Some(node) = new_node {
self.adapter
.emit_object_event(node.id(), ObjectEvent::StateChanged(State::Focused, true));
self.emit_text_selection_change(None, node);
}
if let Some(node) = old_node {
self.adapter
.emit_object_event(node.id(), ObjectEvent::StateChanged(State::Focused, false));
}
}
fn node_removed(&mut self, node: &Node) {
if filter(node) == FilterResult::Include {
self.remove_node(node);
}
}
}
static NEXT_ADAPTER_ID: AtomicUsize = AtomicUsize::new(0);
pub fn next_adapter_id() -> usize {
NEXT_ADAPTER_ID.fetch_add(1, Ordering::Relaxed)
}
pub struct Adapter {
id: usize,
callback: Box<dyn AdapterCallback + Send + Sync>,
context: Arc<Context>,
}
impl Adapter {
pub fn new(
app_context: &Arc<RwLock<AppContext>>,
callback: impl 'static + AdapterCallback + Send + Sync,
initial_state: TreeUpdate,
is_window_focused: bool,
root_window_bounds: WindowBounds,
action_handler: impl 'static + ActionHandler + Send,
) -> Self {
let id = next_adapter_id();
Self::with_id(
id,
app_context,
callback,
initial_state,
is_window_focused,
root_window_bounds,
action_handler,
)
}
pub fn with_id(
id: usize,
app_context: &Arc<RwLock<AppContext>>,
callback: impl 'static + AdapterCallback + Send + Sync,
initial_state: TreeUpdate,
is_window_focused: bool,
root_window_bounds: WindowBounds,
action_handler: impl 'static + ActionHandler + Send,
) -> Self {
Self::with_wrapped_action_handler(
id,
app_context,
callback,
initial_state,
is_window_focused,
root_window_bounds,
Arc::new(ActionHandlerWrapper::new(action_handler)),
)
}
pub fn with_wrapped_action_handler(
id: usize,
app_context: &Arc<RwLock<AppContext>>,
callback: impl 'static + AdapterCallback + Send + Sync,
initial_state: TreeUpdate,
is_window_focused: bool,
root_window_bounds: WindowBounds,
action_handler: Arc<dyn ActionHandlerNoMut + Send + Sync>,
) -> Self {
let tree = Tree::new(initial_state, is_window_focused);
let focus_id = tree.state().focus_id();
let context = Context::new(app_context, tree, action_handler, root_window_bounds);
context.write_app_context().push_adapter(id, &context);
let adapter = Self {
id,
callback: Box::new(callback),
context,
};
adapter.register_tree();
if let Some(id) = focus_id {
adapter.emit_object_event(id, ObjectEvent::StateChanged(State::Focused, true));
}
adapter
}
fn register_tree(&self) {
fn add_children(node: Node<'_>, to_add: &mut Vec<(NodeId, InterfaceSet)>) {
for child in node.filtered_children(&filter) {
let child_id = child.id();
let wrapper = NodeWrapper(&child);
let interfaces = wrapper.interfaces();
to_add.push((child_id, interfaces));
add_children(child, to_add);
}
}
let mut objects_to_add = Vec::new();
let (adapter_index, root_id) = {
let tree = self.context.read_tree();
let tree_state = tree.state();
let mut app_context = self.context.write_app_context();
app_context.name = tree_state.app_name();
app_context.toolkit_name = tree_state.toolkit_name();
app_context.toolkit_version = tree_state.toolkit_version();
let adapter_index = app_context.adapter_index(self.id).unwrap();
let root = tree_state.root();
let root_id = root.id();
let wrapper = NodeWrapper(&root);
objects_to_add.push((root_id, wrapper.interfaces()));
add_children(root, &mut objects_to_add);
(adapter_index, root_id)
};
for (id, interfaces) in objects_to_add {
self.register_interfaces(id, interfaces);
if id == root_id {
self.window_created(adapter_index, id);
}
}
}
pub fn platform_node(&self, id: NodeId) -> PlatformNode {
PlatformNode::new(&self.context, self.id, id)
}
pub fn root_id(&self) -> NodeId {
self.context.read_tree().state().root_id()
}
pub fn platform_root(&self) -> PlatformRoot {
PlatformRoot::new(&self.context.app_context)
}
fn register_interfaces(&self, id: NodeId, new_interfaces: InterfaceSet) {
self.callback.register_interfaces(self, id, new_interfaces);
}
fn unregister_interfaces(&self, id: NodeId, old_interfaces: InterfaceSet) {
self.callback
.unregister_interfaces(self, id, old_interfaces);
}
pub(crate) fn emit_object_event(&self, target: NodeId, event: ObjectEvent) {
let target = NodeIdOrRoot::Node(target);
self.callback
.emit_event(self, Event::Object { target, event });
}
fn emit_root_object_event(&self, event: ObjectEvent) {
let target = NodeIdOrRoot::Root;
self.callback
.emit_event(self, Event::Object { target, event });
}
pub fn set_root_window_bounds(&mut self, new_bounds: WindowBounds) {
let mut bounds = self.context.root_window_bounds.write().unwrap();
*bounds = new_bounds;
}
pub fn update(&mut self, update: TreeUpdate) {
let mut handler = AdapterChangeHandler::new(self);
let mut tree = self.context.tree.write().unwrap();
tree.update_and_process_changes(update, &mut handler);
}
pub fn update_window_focus_state(&mut self, is_focused: bool) {
let mut handler = AdapterChangeHandler::new(self);
let mut tree = self.context.tree.write().unwrap();
tree.update_host_focus_state_and_process_changes(is_focused, &mut handler);
}
fn window_created(&self, adapter_index: usize, window: NodeId) {
self.emit_root_object_event(ObjectEvent::ChildAdded(adapter_index, window));
}
fn window_activated(&self, window: &NodeWrapper<'_>) {
self.callback.emit_event(
self,
Event::Window {
target: window.id(),
name: window.name().unwrap_or_default(),
event: WindowEvent::Activated,
},
);
self.emit_object_event(window.id(), ObjectEvent::StateChanged(State::Active, true));
self.emit_root_object_event(ObjectEvent::ActiveDescendantChanged(window.id()));
}
fn window_deactivated(&self, window: &NodeWrapper<'_>) {
self.callback.emit_event(
self,
Event::Window {
target: window.id(),
name: window.name().unwrap_or_default(),
event: WindowEvent::Deactivated,
},
);
self.emit_object_event(window.id(), ObjectEvent::StateChanged(State::Active, false));
}
fn window_destroyed(&self, window: NodeId) {
self.emit_root_object_event(ObjectEvent::ChildRemoved(window));
}
pub fn id(&self) -> usize {
self.id
}
pub fn is_window_focused(&self) -> bool {
self.context.read_tree().state().is_host_focused()
}
pub fn root_window_bounds(&self) -> WindowBounds {
*self.context.read_root_window_bounds()
}
pub fn wrapped_action_handler(&self) -> Arc<dyn ActionHandlerNoMut + Send + Sync> {
Arc::clone(&self.context.action_handler)
}
}
fn root_window(current_state: &TreeState) -> Option<Node> {
const WINDOW_ROLES: &[Role] = &[Role::AlertDialog, Role::Dialog, Role::Window];
let root = current_state.root();
if WINDOW_ROLES.contains(&root.role()) {
Some(root)
} else {
None
}
}
impl Drop for Adapter {
fn drop(&mut self) {
let root_id = self.context.read_tree().state().root_id();
self.window_destroyed(root_id);
self.context.write_app_context().remove_adapter(self.id);
}
}