diff --git a/common/src/lib.rs b/common/src/lib.rs index ce52c4538..3b01874df 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -748,16 +748,11 @@ pub struct Node { #[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] pub hovered: bool, - /// Skip over this node in the accessibility tree, but keep its subtree. - /// - /// This flag also indicates that this node, but not its descendants, - /// should be skipped when hit testing. - #[cfg_attr(feature = "serde", serde(default))] - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] - pub ignored: bool, + /// Exclude this node and its descendants from the tree presented to + /// assistive technologies, and from hit testing. #[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] - pub invisible: bool, + pub hidden: bool, #[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] pub linked: bool, diff --git a/consumer/src/iterators.rs b/consumer/src/iterators.rs index 5cff48869..0b6bca4c4 100644 --- a/consumer/src/iterators.rs +++ b/consumer/src/iterators.rs @@ -178,36 +178,40 @@ impl<'a> ExactSizeIterator for PrecedingSiblings<'a> {} impl<'a> FusedIterator for PrecedingSiblings<'a> {} -fn next_unignored_sibling(node: Option) -> Option { - // Search for the next sibling of this node, skipping over any ignored nodes - // encountered. - // - // In our search: - // If we find an ignored sibling, we consider its children as our siblings. - // If we run out of siblings, we consider an ignored parent's siblings as our - // own siblings. - // - // Note: this behaviour of 'skipping over' an ignored node makes this subtly - // different to finding the next (direct) sibling which is unignored. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum FilterResult { + Include, + ExcludeNode, + ExcludeSubtree, +} + +fn next_filtered_sibling<'a>( + node: Option>, + filter: &impl Fn(&Node) -> FilterResult, +) -> Option> { let mut next = node; let mut consider_children = false; while let Some(current) = next { if let Some(Some(child)) = consider_children.then(|| current.children().next()) { + let result = filter(&child); next = Some(child); - if !child.is_ignored() { + if result == FilterResult::Include { return next; } } else if let Some(sibling) = current.following_siblings().next() { + let result = filter(&sibling); next = Some(sibling); - if !sibling.is_ignored() { + if result == FilterResult::Include { return next; } - consider_children = true; + if result == FilterResult::ExcludeNode { + consider_children = true; + } } else { let parent = current.parent(); next = parent; if let Some(parent) = parent { - if !parent.is_ignored() { + if filter(&parent) != FilterResult::ExcludeNode { return None; } consider_children = false; @@ -219,33 +223,33 @@ fn next_unignored_sibling(node: Option) -> Option { None } -fn previous_unignored_sibling(node: Option) -> Option { - // Search for the previous sibling of this node, skipping over any ignored nodes - // encountered. - // - // In our search for a sibling: - // If we find an ignored sibling, we may consider its children as siblings. - // If we run out of siblings, we may consider an ignored parent's siblings as - // our own. +fn previous_filtered_sibling<'a>( + node: Option>, + filter: &impl Fn(&Node) -> FilterResult, +) -> Option> { let mut previous = node; let mut consider_children = false; while let Some(current) = previous { if let Some(Some(child)) = consider_children.then(|| current.children().next_back()) { + let result = filter(&child); previous = Some(child); - if !child.is_ignored() { + if result == FilterResult::Include { return previous; } } else if let Some(sibling) = current.preceding_siblings().next() { + let result = filter(&sibling); previous = Some(sibling); - if !sibling.is_ignored() { + if result == FilterResult::Include { return previous; } - consider_children = true; + if result == FilterResult::ExcludeNode { + consider_children = true; + } } else { let parent = current.parent(); previous = parent; if let Some(parent) = parent { - if !parent.is_ignored() { + if filter(&parent) != FilterResult::ExcludeNode { return None; } consider_children = false; @@ -257,20 +261,25 @@ fn previous_unignored_sibling(node: Option) -> Option { None } -/// An iterator that yields following unignored siblings of a node. +/// An iterator that yields following siblings of a node according to the +/// specified filter. /// -/// This struct is created by the [following_unignored_siblings](Node::following_unignored_siblings) method on [Node]. -pub struct FollowingUnignoredSiblings<'a> { +/// This struct is created by the [following_filtered_siblings](Node::following_filtered_siblings) method on [Node]. +pub struct FollowingFilteredSiblings<'a, Filter: Fn(&Node) -> FilterResult> { + filter: Filter, back: Option>, done: bool, front: Option>, } -impl<'a> FollowingUnignoredSiblings<'a> { - pub(crate) fn new(node: Node<'a>) -> Self { - let front = next_unignored_sibling(Some(node)); - let back = node.parent().and_then(Node::last_unignored_child); +impl<'a, Filter: Fn(&Node) -> FilterResult> FollowingFilteredSiblings<'a, Filter> { + pub(crate) fn new(node: Node<'a>, filter: Filter) -> Self { + let front = next_filtered_sibling(Some(node), &filter); + let back = node + .parent() + .and_then(|parent| parent.last_filtered_child(&filter)); Self { + filter, back, done: back.is_none() || front.is_none(), front, @@ -278,7 +287,7 @@ impl<'a> FollowingUnignoredSiblings<'a> { } } -impl<'a> Iterator for FollowingUnignoredSiblings<'a> { +impl<'a, Filter: Fn(&Node) -> FilterResult> Iterator for FollowingFilteredSiblings<'a, Filter> { type Item = Node<'a>; fn next(&mut self) -> Option { @@ -287,41 +296,51 @@ impl<'a> Iterator for FollowingUnignoredSiblings<'a> { } else { self.done = self.front.as_ref().unwrap().id() == self.back.as_ref().unwrap().id(); let current = self.front; - self.front = next_unignored_sibling(self.front); + self.front = next_filtered_sibling(self.front, &self.filter); current } } } -impl<'a> DoubleEndedIterator for FollowingUnignoredSiblings<'a> { +impl<'a, Filter: Fn(&Node) -> FilterResult> DoubleEndedIterator + for FollowingFilteredSiblings<'a, Filter> +{ fn next_back(&mut self) -> Option { if self.done { None } else { self.done = self.back.as_ref().unwrap().id() == self.front.as_ref().unwrap().id(); let current = self.back; - self.back = previous_unignored_sibling(self.back); + self.back = previous_filtered_sibling(self.back, &self.filter); current } } } -impl<'a> FusedIterator for FollowingUnignoredSiblings<'a> {} +impl<'a, Filter: Fn(&Node) -> FilterResult> FusedIterator + for FollowingFilteredSiblings<'a, Filter> +{ +} -/// An iterator that yields preceding unignored siblings of a node. +/// An iterator that yields preceding siblings of a node according to the +/// specified filter. /// -/// This struct is created by the [preceding_unignored_siblings](Node::preceding_unignored_siblings) method on [Node]. -pub struct PrecedingUnignoredSiblings<'a> { +/// This struct is created by the [preceding_filtered_siblings](Node::preceding_filtered_siblings) method on [Node]. +pub struct PrecedingFilteredSiblings<'a, Filter: Fn(&Node) -> FilterResult> { + filter: Filter, back: Option>, done: bool, front: Option>, } -impl<'a> PrecedingUnignoredSiblings<'a> { - pub(crate) fn new(node: Node<'a>) -> Self { - let front = previous_unignored_sibling(Some(node)); - let back = node.parent().and_then(Node::first_unignored_child); +impl<'a, Filter: Fn(&Node) -> FilterResult> PrecedingFilteredSiblings<'a, Filter> { + pub(crate) fn new(node: Node<'a>, filter: Filter) -> Self { + let front = previous_filtered_sibling(Some(node), &filter); + let back = node + .parent() + .and_then(|parent| parent.first_filtered_child(&filter)); Self { + filter, back, done: back.is_none() || front.is_none(), front, @@ -329,7 +348,7 @@ impl<'a> PrecedingUnignoredSiblings<'a> { } } -impl<'a> Iterator for PrecedingUnignoredSiblings<'a> { +impl<'a, Filter: Fn(&Node) -> FilterResult> Iterator for PrecedingFilteredSiblings<'a, Filter> { type Item = Node<'a>; fn next(&mut self) -> Option { @@ -338,41 +357,49 @@ impl<'a> Iterator for PrecedingUnignoredSiblings<'a> { } else { self.done = self.front.as_ref().unwrap().id() == self.back.as_ref().unwrap().id(); let current = self.front; - self.front = previous_unignored_sibling(self.front); + self.front = previous_filtered_sibling(self.front, &self.filter); current } } } -impl<'a> DoubleEndedIterator for PrecedingUnignoredSiblings<'a> { +impl<'a, Filter: Fn(&Node) -> FilterResult> DoubleEndedIterator + for PrecedingFilteredSiblings<'a, Filter> +{ fn next_back(&mut self) -> Option { if self.done { None } else { self.done = self.back.as_ref().unwrap().id() == self.front.as_ref().unwrap().id(); let current = self.back; - self.back = next_unignored_sibling(self.back); + self.back = next_filtered_sibling(self.back, &self.filter); current } } } -impl<'a> FusedIterator for PrecedingUnignoredSiblings<'a> {} +impl<'a, Filter: Fn(&Node) -> FilterResult> FusedIterator + for PrecedingFilteredSiblings<'a, Filter> +{ +} -/// An iterator that yields unignored children of a node. +/// An iterator that yields children of a node according to the specified +/// filter. /// -/// This struct is created by the [unignored_children](Node::unignored_children) method on [Node]. -pub struct UnignoredChildren<'a> { +/// This struct is created by the [filtered_children](Node::filtered_children) method on [Node]. +pub struct FilteredChildren<'a, Filter: Fn(&Node) -> FilterResult> { + filter: Filter, back: Option>, done: bool, front: Option>, } -impl<'a> UnignoredChildren<'a> { - pub(crate) fn new(node: Node<'a>) -> Self { - let front = node.first_unignored_child(); - let back = node.last_unignored_child(); +impl<'a, Filter: Fn(&Node) -> FilterResult> FilteredChildren<'a, Filter> { + pub(crate) fn new(node: Node<'a>, filter: Filter) -> Self { + let front = node.first_filtered_child(&filter); + let back = node.last_filtered_child(&filter); Self { + filter, back, done: back.is_none() || front.is_none(), front, @@ -380,7 +407,7 @@ impl<'a> UnignoredChildren<'a> { } } -impl<'a> Iterator for UnignoredChildren<'a> { +impl<'a, Filter: Fn(&Node) -> FilterResult> Iterator for FilteredChildren<'a, Filter> { type Item = Node<'a>; fn next(&mut self) -> Option { @@ -389,26 +416,26 @@ impl<'a> Iterator for UnignoredChildren<'a> { } else { self.done = self.front.as_ref().unwrap().id() == self.back.as_ref().unwrap().id(); let current = self.front; - self.front = next_unignored_sibling(self.front); + self.front = next_filtered_sibling(self.front, &self.filter); current } } } -impl<'a> DoubleEndedIterator for UnignoredChildren<'a> { +impl<'a, Filter: Fn(&Node) -> FilterResult> DoubleEndedIterator for FilteredChildren<'a, Filter> { fn next_back(&mut self) -> Option { if self.done { None } else { self.done = self.back.as_ref().unwrap().id() == self.front.as_ref().unwrap().id(); let current = self.back; - self.back = previous_unignored_sibling(self.back); + self.back = previous_filtered_sibling(self.back, &self.filter); current } } } -impl<'a> FusedIterator for UnignoredChildren<'a> {} +impl<'a, Filter: Fn(&Node) -> FilterResult> FusedIterator for FilteredChildren<'a, Filter> {} #[cfg(test)] mod tests { @@ -558,12 +585,12 @@ mod tests { } #[test] - fn following_unignored_siblings() { + fn following_filtered_siblings() { let tree = test_tree(); assert!(tree .read() .root() - .following_unignored_siblings() + .following_filtered_siblings(test_tree_filter) .next() .is_none()); assert_eq!( @@ -576,7 +603,7 @@ mod tests { tree.read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .following_unignored_siblings() + .following_filtered_siblings(test_tree_filter) .map(|node| node.id()) .collect::>()[..] ); @@ -584,18 +611,18 @@ mod tests { .read() .node_by_id(PARAGRAPH_3_IGNORED_ID) .unwrap() - .following_unignored_siblings() + .following_filtered_siblings(test_tree_filter) .next() .is_none()); } #[test] - fn following_unignored_siblings_reversed() { + fn following_filtered_siblings_reversed() { let tree = test_tree(); assert!(tree .read() .root() - .following_unignored_siblings() + .following_filtered_siblings(test_tree_filter) .next_back() .is_none()); assert_eq!( @@ -608,7 +635,7 @@ mod tests { tree.read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .following_unignored_siblings() + .following_filtered_siblings(test_tree_filter) .rev() .map(|node| node.id()) .collect::>()[..] @@ -617,18 +644,18 @@ mod tests { .read() .node_by_id(PARAGRAPH_3_IGNORED_ID) .unwrap() - .following_unignored_siblings() + .following_filtered_siblings(test_tree_filter) .next_back() .is_none()); } #[test] - fn preceding_unignored_siblings() { + fn preceding_filtered_siblings() { let tree = test_tree(); assert!(tree .read() .root() - .preceding_unignored_siblings() + .preceding_filtered_siblings(test_tree_filter) .next() .is_none()); assert_eq!( @@ -636,7 +663,7 @@ mod tests { tree.read() .node_by_id(PARAGRAPH_3_IGNORED_ID) .unwrap() - .preceding_unignored_siblings() + .preceding_filtered_siblings(test_tree_filter) .map(|node| node.id()) .collect::>()[..] ); @@ -644,18 +671,18 @@ mod tests { .read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .preceding_unignored_siblings() + .preceding_filtered_siblings(test_tree_filter) .next() .is_none()); } #[test] - fn preceding_unignored_siblings_reversed() { + fn preceding_filtered_siblings_reversed() { let tree = test_tree(); assert!(tree .read() .root() - .preceding_unignored_siblings() + .preceding_filtered_siblings(test_tree_filter) .next_back() .is_none()); assert_eq!( @@ -663,7 +690,7 @@ mod tests { tree.read() .node_by_id(PARAGRAPH_3_IGNORED_ID) .unwrap() - .preceding_unignored_siblings() + .preceding_filtered_siblings(test_tree_filter) .rev() .map(|node| node.id()) .collect::>()[..] @@ -672,13 +699,13 @@ mod tests { .read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .preceding_unignored_siblings() + .preceding_filtered_siblings(test_tree_filter) .next_back() .is_none()); } #[test] - fn unignored_children() { + fn filtered_children() { let tree = test_tree(); assert_eq!( [ @@ -690,7 +717,7 @@ mod tests { ], tree.read() .root() - .unignored_children() + .filtered_children(test_tree_filter) .map(|node| node.id()) .collect::>()[..] ); @@ -698,20 +725,20 @@ mod tests { .read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .unignored_children() + .filtered_children(test_tree_filter) .next() .is_none()); assert!(tree .read() .node_by_id(STATIC_TEXT_0_0_IGNORED_ID) .unwrap() - .unignored_children() + .filtered_children(test_tree_filter) .next() .is_none()); } #[test] - fn unignored_children_reversed() { + fn filtered_children_reversed() { let tree = test_tree(); assert_eq!( [ @@ -723,7 +750,7 @@ mod tests { ], tree.read() .root() - .unignored_children() + .filtered_children(test_tree_filter) .rev() .map(|node| node.id()) .collect::>()[..] @@ -732,14 +759,14 @@ mod tests { .read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .unignored_children() + .filtered_children(test_tree_filter) .next_back() .is_none()); assert!(tree .read() .node_by_id(STATIC_TEXT_0_0_IGNORED_ID) .unwrap() - .unignored_children() + .filtered_children(test_tree_filter) .next_back() .is_none()); } diff --git a/consumer/src/lib.rs b/consumer/src/lib.rs index 0d5a88629..ff99182e7 100644 --- a/consumer/src/lib.rs +++ b/consumer/src/lib.rs @@ -10,10 +10,7 @@ pub(crate) mod node; pub use node::Node; pub(crate) mod iterators; -pub use iterators::{ - FollowingSiblings, FollowingUnignoredSiblings, PrecedingSiblings, PrecedingUnignoredSiblings, - UnignoredChildren, -}; +pub use iterators::FilterResult; #[cfg(test)] mod tests { @@ -22,6 +19,8 @@ mod tests { use std::num::NonZeroU128; use std::sync::Arc; + use crate::FilterResult; + pub const ROOT_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(1) }); pub const PARAGRAPH_0_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(2) }); pub const STATIC_TEXT_0_0_IGNORED_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(3) }); @@ -62,7 +61,6 @@ mod tests { }); let static_text_0_0_ignored = Arc::new(Node { role: Role::StaticText, - ignored: true, name: Some("static_text_0_0_ignored".into()), ..Default::default() }); @@ -76,7 +74,6 @@ mod tests { y1: 40.0, }), children: vec![STATIC_TEXT_1_0_ID], - ignored: true, ..Default::default() }); let static_text_1_0 = Arc::new(Node { @@ -108,18 +105,15 @@ mod tests { BUTTON_3_2_ID, EMPTY_CONTAINER_3_3_IGNORED_ID, ], - ignored: true, ..Default::default() }); let empty_container_3_0_ignored = Arc::new(Node { role: Role::GenericContainer, - ignored: true, ..Default::default() }); let link_3_1_ignored = Arc::new(Node { role: Role::Link, children: vec![STATIC_TEXT_3_1_0_ID], - ignored: true, linked: true, ..Default::default() }); @@ -135,7 +129,6 @@ mod tests { }); let empty_container_3_3_ignored = Arc::new(Node { role: Role::GenericContainer, - ignored: true, ..Default::default() }); let initial_update = TreeUpdate { @@ -159,4 +152,19 @@ mod tests { }; crate::tree::Tree::new(initial_update, Box::new(NullActionHandler {})) } + + pub fn test_tree_filter(node: &crate::Node) -> FilterResult { + let id = node.id(); + if id == STATIC_TEXT_0_0_IGNORED_ID + || id == PARAGRAPH_1_IGNORED_ID + || id == PARAGRAPH_3_IGNORED_ID + || id == EMPTY_CONTAINER_3_0_IGNORED_ID + || id == LINK_3_1_IGNORED_ID + || id == EMPTY_CONTAINER_3_3_IGNORED_ID + { + FilterResult::ExcludeNode + } else { + FilterResult::Include + } + } } diff --git a/consumer/src/node.rs b/consumer/src/node.rs index cddc2cf09..810eadfc6 100644 --- a/consumer/src/node.rs +++ b/consumer/src/node.rs @@ -14,8 +14,8 @@ use accesskit::kurbo::{Affine, Point, Rect}; use accesskit::{CheckedState, DefaultActionVerb, Live, Node as NodeData, NodeId, Role}; use crate::iterators::{ - FollowingSiblings, FollowingUnignoredSiblings, PrecedingSiblings, PrecedingUnignoredSiblings, - UnignoredChildren, + FilterResult, FilteredChildren, FollowingFilteredSiblings, FollowingSiblings, + PrecedingFilteredSiblings, PrecedingSiblings, }; use crate::tree::{NodeState, ParentAndIndex, State as TreeState}; @@ -40,14 +40,6 @@ impl<'a> Node<'a> { self.data().focusable } - pub fn is_ignored(&self) -> bool { - self.data().ignored || (self.role() == Role::Presentation) - } - - pub fn is_invisible_or_ignored(&self) -> bool { - (self.is_invisible() || self.is_ignored()) && !self.is_focused() - } - pub fn is_root(&self) -> bool { // Don't check for absence of a parent node, in case a non-root node // somehow gets detached from the tree. @@ -66,10 +58,10 @@ impl<'a> Node<'a> { .map(|id| self.tree_state.node_by_id(id).unwrap()) } - pub fn unignored_parent(self) -> Option> { + pub fn filtered_parent(&self, filter: &impl Fn(&Node) -> FilterResult) -> Option> { if let Some(parent) = self.parent() { - if parent.is_ignored() { - parent.unignored_parent() + if filter(&parent) != FilterResult::Include { + parent.filtered_parent(filter) } else { Some(parent) } @@ -108,10 +100,11 @@ impl<'a> Node<'a> { .map(move |id| state.node_by_id(id).unwrap()) } - pub fn unignored_children( - self, + pub fn filtered_children( + &self, + filter: impl Fn(&Node) -> FilterResult + 'a, ) -> impl DoubleEndedIterator> + FusedIterator> + 'a { - UnignoredChildren::new(self) + FilteredChildren::new(*self, filter) } pub fn following_sibling_ids( @@ -134,10 +127,11 @@ impl<'a> Node<'a> { .map(move |id| state.node_by_id(id).unwrap()) } - pub fn following_unignored_siblings( - self, + pub fn following_filtered_siblings( + &self, + filter: impl Fn(&Node) -> FilterResult + 'a, ) -> impl DoubleEndedIterator> + FusedIterator> + 'a { - FollowingUnignoredSiblings::new(self) + FollowingFilteredSiblings::new(*self, filter) } pub fn preceding_sibling_ids( @@ -160,10 +154,11 @@ impl<'a> Node<'a> { .map(move |id| state.node_by_id(id).unwrap()) } - pub fn preceding_unignored_siblings( - self, + pub fn preceding_filtered_siblings( + &self, + filter: impl Fn(&Node) -> FilterResult + 'a, ) -> impl DoubleEndedIterator> + FusedIterator> + 'a { - PrecedingUnignoredSiblings::new(self) + PrecedingFilteredSiblings::new(*self, filter) } pub fn deepest_first_child(self) -> Option> { @@ -174,9 +169,12 @@ impl<'a> Node<'a> { Some(deepest_child) } - pub fn deepest_first_unignored_child(self) -> Option> { - let mut deepest_child = self.first_unignored_child()?; - while let Some(first_child) = deepest_child.first_unignored_child() { + pub fn deepest_first_filtered_child( + &self, + filter: &impl Fn(&Node) -> FilterResult, + ) -> Option> { + let mut deepest_child = self.first_filtered_child(filter)?; + while let Some(first_child) = deepest_child.first_filtered_child(filter) { deepest_child = first_child; } Some(deepest_child) @@ -190,9 +188,12 @@ impl<'a> Node<'a> { Some(deepest_child) } - pub fn deepest_last_unignored_child(self) -> Option> { - let mut deepest_child = self.last_unignored_child()?; - while let Some(last_child) = deepest_child.last_unignored_child() { + pub fn deepest_last_filtered_child( + &self, + filter: &impl Fn(&Node) -> FilterResult, + ) -> Option> { + let mut deepest_child = self.last_filtered_child(filter)?; + while let Some(last_child) = deepest_child.last_filtered_child(filter) { deepest_child = last_child; } Some(deepest_child) @@ -234,24 +235,30 @@ impl<'a> Node<'a> { .map(|rect| self.transform().transform_rect_bbox(*rect)) } - /// Returns the deepest visible node, either this node or a descendant, + /// Returns the deepest filtered node, either this node or a descendant, /// at the given point in this node's coordinate space. - pub fn node_at_point(self, point: Point) -> Option> { - if self.is_invisible() { + pub fn node_at_point( + &self, + point: Point, + filter: &impl Fn(&Node) -> FilterResult, + ) -> Option> { + let filter_result = filter(self); + + if filter_result == FilterResult::ExcludeSubtree { return None; } for child in self.children().rev() { let point = child.direct_transform().inverse() * point; - if let Some(node) = child.node_at_point(point) { + if let Some(node) = child.node_at_point(point, filter) { return Some(node); } } - if !self.is_ignored() { + if filter_result == FilterResult::Include { if let Some(rect) = &self.data().bounds { if rect.contains(point) { - return Some(self); + return Some(*self); } } } @@ -267,8 +274,8 @@ impl<'a> Node<'a> { self.data().role } - pub fn is_invisible(&self) -> bool { - self.data().invisible + pub fn is_hidden(&self) -> bool { + self.data().hidden } pub fn is_disabled(&self) -> bool { @@ -473,25 +480,37 @@ impl<'a> Node<'a> { self.data().selected } - pub(crate) fn first_unignored_child(self) -> Option> { + pub(crate) fn first_filtered_child( + &self, + filter: &impl Fn(&Node) -> FilterResult, + ) -> Option> { for child in self.children() { - if !child.is_ignored() { + let result = filter(&child); + if result == FilterResult::Include { return Some(child); } - if let Some(descendant) = child.first_unignored_child() { - return Some(descendant); + if result == FilterResult::ExcludeNode { + if let Some(descendant) = child.first_filtered_child(filter) { + return Some(descendant); + } } } None } - pub(crate) fn last_unignored_child(self) -> Option> { + pub(crate) fn last_filtered_child( + &self, + filter: &impl Fn(&Node) -> FilterResult, + ) -> Option> { for child in self.children().rev() { - if !child.is_ignored() { + let result = filter(&child); + if result == FilterResult::Include { return Some(child); } - if let Some(descendant) = child.last_unignored_child() { - return Some(descendant); + if result == FilterResult::ExcludeNode { + if let Some(descendant) = child.last_filtered_child(filter) { + return Some(descendant); + } } } None @@ -567,13 +586,13 @@ mod tests { } #[test] - fn deepest_first_unignored_child() { + fn deepest_first_filtered_child() { let tree = test_tree(); assert_eq!( PARAGRAPH_0_ID, tree.read() .root() - .deepest_first_unignored_child() + .deepest_first_filtered_child(&test_tree_filter) .unwrap() .id() ); @@ -581,13 +600,13 @@ mod tests { .read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .deepest_first_unignored_child() + .deepest_first_filtered_child(&test_tree_filter) .is_none()); assert!(tree .read() .node_by_id(STATIC_TEXT_0_0_IGNORED_ID) .unwrap() - .deepest_first_unignored_child() + .deepest_first_filtered_child(&test_tree_filter) .is_none()); } @@ -616,13 +635,13 @@ mod tests { } #[test] - fn deepest_last_unignored_child() { + fn deepest_last_filtered_child() { let tree = test_tree(); assert_eq!( BUTTON_3_2_ID, tree.read() .root() - .deepest_last_unignored_child() + .deepest_last_filtered_child(&test_tree_filter) .unwrap() .id() ); @@ -631,7 +650,7 @@ mod tests { tree.read() .node_by_id(PARAGRAPH_3_IGNORED_ID) .unwrap() - .deepest_last_unignored_child() + .deepest_last_filtered_child(&test_tree_filter) .unwrap() .id() ); @@ -639,13 +658,13 @@ mod tests { .read() .node_by_id(BUTTON_3_2_ID) .unwrap() - .deepest_last_unignored_child() + .deepest_last_filtered_child(&test_tree_filter) .is_none()); assert!(tree .read() .node_by_id(PARAGRAPH_0_ID) .unwrap() - .deepest_last_unignored_child() + .deepest_last_filtered_child(&test_tree_filter) .is_none()); } @@ -727,26 +746,26 @@ mod tests { assert!(tree .read() .root() - .node_at_point(Point::new(10.0, 40.0)) + .node_at_point(Point::new(10.0, 40.0), &test_tree_filter) .is_none()); assert_eq!( Some(STATIC_TEXT_1_0_ID), tree.read() .root() - .node_at_point(Point::new(20.0, 50.0)) + .node_at_point(Point::new(20.0, 50.0), &test_tree_filter) .map(|node| node.id()) ); assert_eq!( Some(STATIC_TEXT_1_0_ID), tree.read() .root() - .node_at_point(Point::new(50.0, 60.0)) + .node_at_point(Point::new(50.0, 60.0), &test_tree_filter) .map(|node| node.id()) ); assert!(tree .read() .root() - .node_at_point(Point::new(100.0, 70.0)) + .node_at_point(Point::new(100.0, 70.0), &test_tree_filter) .is_none()); } diff --git a/platforms/windows/src/adapter.rs b/platforms/windows/src/adapter.rs index 86984f748..1e327c88e 100644 --- a/platforms/windows/src/adapter.rs +++ b/platforms/windows/src/adapter.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use accesskit::{ActionHandler, Live, TreeUpdate}; -use accesskit_consumer::{Node, Tree, TreeChangeHandler}; +use accesskit_consumer::{FilterResult, Node, Tree, TreeChangeHandler}; use lazy_init::LazyTransform; use windows::Win32::{ Foundation::*, @@ -14,7 +14,7 @@ use windows::Win32::{ }; use crate::{ - node::{NodeWrapper, PlatformNode}, + node::{filter, NodeWrapper, PlatformNode}, util::QueuedEvent, }; @@ -103,10 +103,10 @@ impl Adapter { } impl TreeChangeHandler for Handler<'_> { fn node_added(&mut self, node: &Node) { - if !node.is_invisible_or_ignored() - && node.name().is_some() - && node.live() != Live::Off - { + if filter(node) != FilterResult::Include { + return; + } + if node.name().is_some() && node.live() != Live::Off { let platform_node = PlatformNode::new(self.tree, node.id(), self.hwnd); let element: IRawElementProviderSimple = platform_node.into(); self.queue.push(QueuedEvent::Simple { @@ -116,15 +116,19 @@ impl Adapter { } } fn node_updated(&mut self, old_node: &Node, new_node: &Node) { + if filter(new_node) != FilterResult::Include { + return; + } let platform_node = PlatformNode::new(self.tree, new_node.id(), self.hwnd); let element: IRawElementProviderSimple = platform_node.into(); let old_wrapper = NodeWrapper::new(old_node); let new_wrapper = NodeWrapper::new(new_node); new_wrapper.enqueue_property_changes(&mut self.queue, &element, &old_wrapper); - if !new_node.is_invisible_or_ignored() - && new_node.name().is_some() + if new_node.name().is_some() && new_node.live() != Live::Off - && (new_node.name() != old_node.name() || new_node.live() != old_node.live()) + && (new_node.name() != old_node.name() + || new_node.live() != old_node.live() + || filter(old_node) != FilterResult::Include) { self.queue.push(QueuedEvent::Simple { element, diff --git a/platforms/windows/src/node.rs b/platforms/windows/src/node.rs index fe21cfd2a..e0a7dc252 100644 --- a/platforms/windows/src/node.rs +++ b/platforms/windows/src/node.rs @@ -12,7 +12,7 @@ use accesskit::kurbo::Point; use accesskit::{CheckedState, Live, NodeId, NodeIdContent, Role}; -use accesskit_consumer::{Node, Tree, TreeState}; +use accesskit_consumer::{FilterResult, Node, Tree, TreeState}; use arrayvec::ArrayVec; use paste::paste; use std::sync::{Arc, Weak}; @@ -35,6 +35,30 @@ fn runtime_id_from_node_id(id: NodeId) -> impl std::ops::Deref { result } +pub(crate) fn filter(node: &Node) -> FilterResult { + if node.is_focused() { + return FilterResult::Include; + } + + if node.is_hidden() { + return FilterResult::ExcludeSubtree; + } + + let role = node.role(); + if role == Role::Presentation || role == Role::GenericContainer || role == Role::InlineTextBox { + return FilterResult::ExcludeNode; + } + + FilterResult::Include +} + +fn filter_with_root_exception(node: &Node) -> FilterResult { + if node.is_root() { + return FilterResult::Include; + } + filter(node) +} + pub(crate) struct NodeWrapper<'a> { node: &'a Node<'a>, } @@ -263,7 +287,7 @@ impl<'a> NodeWrapper<'a> { } fn is_content_element(&self) -> bool { - !self.node.is_invisible_or_ignored() + filter(self.node) == FilterResult::Include } fn is_enabled(&self) -> bool { @@ -430,13 +454,15 @@ impl<'a> NodeWrapper<'a> { }); } - fn navigate(&self, direction: NavigateDirection) -> Option { + fn navigate(&self, direction: NavigateDirection) -> Option { match direction { - NavigateDirection_Parent => self.node.parent_id(), - NavigateDirection_NextSibling => self.node.following_sibling_ids().next(), - NavigateDirection_PreviousSibling => self.node.preceding_sibling_ids().next(), - NavigateDirection_FirstChild => self.node.child_ids().next(), - NavigateDirection_LastChild => self.node.child_ids().next_back(), + NavigateDirection_Parent => self.node.filtered_parent(&filter_with_root_exception), + NavigateDirection_NextSibling => self.node.following_filtered_siblings(&filter).next(), + NavigateDirection_PreviousSibling => { + self.node.preceding_filtered_siblings(&filter).next() + } + NavigateDirection_FirstChild => self.node.filtered_children(&filter).next(), + NavigateDirection_LastChild => self.node.filtered_children(&filter).next_back(), _ => None, } } @@ -565,7 +591,7 @@ impl IRawElementProviderSimple_Impl for PlatformNode { impl IRawElementProviderFragment_Impl for PlatformNode { fn Navigate(&self, direction: NavigateDirection) -> Result { self.resolve(|wrapper| match wrapper.navigate(direction) { - Some(result) => Ok(self.relative(result).into()), + Some(result) => Ok(self.relative(result.id()).into()), None => Err(Error::OK), }) } @@ -622,7 +648,7 @@ impl IRawElementProviderFragmentRoot_Impl for PlatformNode { let client_top_left = self.client_top_left(); let point = Point::new(x - client_top_left.x, y - client_top_left.y); let point = wrapper.node.transform().inverse() * point; - wrapper.node.node_at_point(point).map_or_else( + wrapper.node.node_at_point(point, &filter).map_or_else( || Err(Error::OK), |node| Ok(self.relative(node.id()).into()), )