1use accesskit::{
12 Action, Affine, Live, Node as NodeData, NodeId, Orientation, Point, Rect, Role, TextSelection,
13 Toggled,
14};
15use alloc::{
16 string::{String, ToString},
17 vec::Vec,
18};
19use core::{fmt, iter::FusedIterator};
20
21use crate::filters::FilterResult;
22use crate::iterators::{
23 FilteredChildren, FollowingFilteredSiblings, FollowingSiblings, LabelledBy,
24 PrecedingFilteredSiblings, PrecedingSiblings,
25};
26use crate::tree::State as TreeState;
27
28#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29pub(crate) struct ParentAndIndex(pub(crate) NodeId, pub(crate) usize);
30
31#[derive(Clone, Debug)]
32pub(crate) struct NodeState {
33 pub(crate) parent_and_index: Option<ParentAndIndex>,
34 pub(crate) data: NodeData,
35}
36
37#[derive(Copy, Clone)]
38pub struct Node<'a> {
39 pub tree_state: &'a TreeState,
40 pub(crate) id: NodeId,
41 pub(crate) state: &'a NodeState,
42}
43
44impl<'a> Node<'a> {
45 pub fn data(&self) -> &'a NodeData {
46 &self.state.data
47 }
48
49 pub fn is_focused(&self) -> bool {
50 self.tree_state.focus_id() == Some(self.id())
51 }
52
53 pub fn is_focused_in_tree(&self) -> bool {
54 self.tree_state.focus == self.id()
55 }
56
57 pub fn is_focusable(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
58 self.supports_action(Action::Focus, parent_filter) || self.is_focused_in_tree()
59 }
60
61 pub fn is_root(&self) -> bool {
62 self.id() == self.tree_state.root_id()
65 }
66
67 pub fn parent_id(&self) -> Option<NodeId> {
68 self.state
69 .parent_and_index
70 .as_ref()
71 .map(|ParentAndIndex(id, _)| *id)
72 }
73
74 pub fn parent(&self) -> Option<Node<'a>> {
75 self.parent_id()
76 .map(|id| self.tree_state.node_by_id(id).unwrap())
77 }
78
79 pub fn filtered_parent(&self, filter: &impl Fn(&Node) -> FilterResult) -> Option<Node<'a>> {
80 self.parent().and_then(move |parent| {
81 if filter(&parent) == FilterResult::Include {
82 Some(parent)
83 } else {
84 parent.filtered_parent(filter)
85 }
86 })
87 }
88
89 pub fn parent_and_index(self) -> Option<(Node<'a>, usize)> {
90 self.state
91 .parent_and_index
92 .as_ref()
93 .map(|ParentAndIndex(parent, index)| {
94 (self.tree_state.node_by_id(*parent).unwrap(), *index)
95 })
96 }
97
98 pub fn child_ids(
99 &self,
100 ) -> impl DoubleEndedIterator<Item = NodeId>
101 + ExactSizeIterator<Item = NodeId>
102 + FusedIterator<Item = NodeId>
103 + '_ {
104 let data = &self.state.data;
105 data.children().iter().copied()
106 }
107
108 pub fn children(
109 &self,
110 ) -> impl DoubleEndedIterator<Item = Node<'a>>
111 + ExactSizeIterator<Item = Node<'a>>
112 + FusedIterator<Item = Node<'a>>
113 + 'a {
114 let state = self.tree_state;
115 let data = &self.state.data;
116 data.children()
117 .iter()
118 .map(move |id| state.node_by_id(*id).unwrap())
119 }
120
121 pub fn filtered_children(
122 &self,
123 filter: impl Fn(&Node) -> FilterResult + 'a,
124 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
125 FilteredChildren::new(*self, filter)
126 }
127
128 pub fn following_sibling_ids(
129 &self,
130 ) -> impl DoubleEndedIterator<Item = NodeId>
131 + ExactSizeIterator<Item = NodeId>
132 + FusedIterator<Item = NodeId>
133 + 'a {
134 FollowingSiblings::new(*self)
135 }
136
137 pub fn following_siblings(
138 &self,
139 ) -> impl DoubleEndedIterator<Item = Node<'a>>
140 + ExactSizeIterator<Item = Node<'a>>
141 + FusedIterator<Item = Node<'a>>
142 + 'a {
143 let state = self.tree_state;
144 self.following_sibling_ids()
145 .map(move |id| state.node_by_id(id).unwrap())
146 }
147
148 pub fn following_filtered_siblings(
149 &self,
150 filter: impl Fn(&Node) -> FilterResult + 'a,
151 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
152 FollowingFilteredSiblings::new(*self, filter)
153 }
154
155 pub fn preceding_sibling_ids(
156 &self,
157 ) -> impl DoubleEndedIterator<Item = NodeId>
158 + ExactSizeIterator<Item = NodeId>
159 + FusedIterator<Item = NodeId>
160 + 'a {
161 PrecedingSiblings::new(*self)
162 }
163
164 pub fn preceding_siblings(
165 &self,
166 ) -> impl DoubleEndedIterator<Item = Node<'a>>
167 + ExactSizeIterator<Item = Node<'a>>
168 + FusedIterator<Item = Node<'a>>
169 + 'a {
170 let state = self.tree_state;
171 self.preceding_sibling_ids()
172 .map(move |id| state.node_by_id(id).unwrap())
173 }
174
175 pub fn preceding_filtered_siblings(
176 &self,
177 filter: impl Fn(&Node) -> FilterResult + 'a,
178 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
179 PrecedingFilteredSiblings::new(*self, filter)
180 }
181
182 pub fn deepest_first_child(self) -> Option<Node<'a>> {
183 let mut deepest_child = self.children().next()?;
184 while let Some(first_child) = deepest_child.children().next() {
185 deepest_child = first_child;
186 }
187 Some(deepest_child)
188 }
189
190 pub fn deepest_first_filtered_child(
191 &self,
192 filter: &impl Fn(&Node) -> FilterResult,
193 ) -> Option<Node<'a>> {
194 let mut deepest_child = self.first_filtered_child(filter)?;
195 while let Some(first_child) = deepest_child.first_filtered_child(filter) {
196 deepest_child = first_child;
197 }
198 Some(deepest_child)
199 }
200
201 pub fn deepest_last_child(self) -> Option<Node<'a>> {
202 let mut deepest_child = self.children().next_back()?;
203 while let Some(last_child) = deepest_child.children().next_back() {
204 deepest_child = last_child;
205 }
206 Some(deepest_child)
207 }
208
209 pub fn deepest_last_filtered_child(
210 &self,
211 filter: &impl Fn(&Node) -> FilterResult,
212 ) -> Option<Node<'a>> {
213 let mut deepest_child = self.last_filtered_child(filter)?;
214 while let Some(last_child) = deepest_child.last_filtered_child(filter) {
215 deepest_child = last_child;
216 }
217 Some(deepest_child)
218 }
219
220 pub fn is_descendant_of(&self, ancestor: &Node) -> bool {
221 if self.id() == ancestor.id() {
222 return true;
223 }
224 if let Some(parent) = self.parent() {
225 return parent.is_descendant_of(ancestor);
226 }
227 false
228 }
229
230 pub fn direct_transform(&self) -> Affine {
233 self.data()
234 .transform()
235 .map_or(Affine::IDENTITY, |value| *value)
236 }
237
238 pub fn transform(&self) -> Affine {
241 self.parent()
242 .map_or(Affine::IDENTITY, |parent| parent.transform())
243 * self.direct_transform()
244 }
245
246 pub(crate) fn relative_transform(&self, stop_at: &Node) -> Affine {
247 let parent_transform = if let Some(parent) = self.parent() {
248 if parent.id() == stop_at.id() {
249 Affine::IDENTITY
250 } else {
251 parent.relative_transform(stop_at)
252 }
253 } else {
254 Affine::IDENTITY
255 };
256 parent_transform * self.direct_transform()
257 }
258
259 pub fn raw_bounds(&self) -> Option<Rect> {
260 self.data().bounds()
261 }
262
263 pub fn has_bounds(&self) -> bool {
264 self.raw_bounds().is_some()
265 }
266
267 pub fn bounding_box(&self) -> Option<Rect> {
270 self.raw_bounds()
271 .as_ref()
272 .map(|rect| self.transform().transform_rect_bbox(*rect))
273 }
274
275 pub(crate) fn bounding_box_in_coordinate_space(&self, other: &Node) -> Option<Rect> {
276 self.raw_bounds()
277 .as_ref()
278 .map(|rect| self.relative_transform(other).transform_rect_bbox(*rect))
279 }
280
281 pub(crate) fn hit_test(
282 &self,
283 point: Point,
284 filter: &impl Fn(&Node) -> FilterResult,
285 ) -> Option<(Node<'a>, Point)> {
286 let filter_result = filter(self);
287
288 if filter_result == FilterResult::ExcludeSubtree {
289 return None;
290 }
291
292 for child in self.children().rev() {
293 let point = child.direct_transform().inverse() * point;
294 if let Some(result) = child.hit_test(point, filter) {
295 return Some(result);
296 }
297 }
298
299 if filter_result == FilterResult::Include {
300 if let Some(rect) = &self.raw_bounds() {
301 if rect.contains(point) {
302 return Some((*self, point));
303 }
304 }
305 }
306
307 None
308 }
309
310 pub fn node_at_point(
313 &self,
314 point: Point,
315 filter: &impl Fn(&Node) -> FilterResult,
316 ) -> Option<Node<'a>> {
317 self.hit_test(point, filter).map(|(node, _)| node)
318 }
319
320 pub fn id(&self) -> NodeId {
321 self.id
322 }
323
324 pub fn role(&self) -> Role {
325 self.data().role()
326 }
327
328 pub fn role_description(&self) -> Option<&str> {
329 self.data().role_description()
330 }
331
332 pub fn has_role_description(&self) -> bool {
333 self.data().role_description().is_some()
334 }
335
336 pub fn is_hidden(&self) -> bool {
337 self.data().is_hidden()
338 }
339
340 pub fn is_disabled(&self) -> bool {
341 self.data().is_disabled()
342 }
343
344 pub fn is_read_only(&self) -> bool {
345 let data = self.data();
346 if data.is_read_only() {
347 true
348 } else {
349 self.should_have_read_only_state_by_default() || !self.is_read_only_supported()
350 }
351 }
352
353 pub fn is_read_only_or_disabled(&self) -> bool {
354 self.is_read_only() || self.is_disabled()
355 }
356
357 pub fn toggled(&self) -> Option<Toggled> {
358 self.data().toggled()
359 }
360
361 pub fn numeric_value(&self) -> Option<f64> {
362 self.data().numeric_value()
363 }
364
365 pub fn min_numeric_value(&self) -> Option<f64> {
366 self.data().min_numeric_value()
367 }
368
369 pub fn max_numeric_value(&self) -> Option<f64> {
370 self.data().max_numeric_value()
371 }
372
373 pub fn numeric_value_step(&self) -> Option<f64> {
374 self.data().numeric_value_step()
375 }
376
377 pub fn numeric_value_jump(&self) -> Option<f64> {
378 self.data().numeric_value_jump()
379 }
380
381 pub fn clips_children(&self) -> bool {
382 self.data().clips_children()
383 }
384
385 pub fn scroll_x(&self) -> Option<f64> {
386 self.data().scroll_x()
387 }
388
389 pub fn scroll_x_min(&self) -> Option<f64> {
390 self.data().scroll_x_min()
391 }
392
393 pub fn scroll_x_max(&self) -> Option<f64> {
394 self.data().scroll_x_max()
395 }
396
397 pub fn scroll_y(&self) -> Option<f64> {
398 self.data().scroll_y()
399 }
400
401 pub fn scroll_y_min(&self) -> Option<f64> {
402 self.data().scroll_y_min()
403 }
404
405 pub fn scroll_y_max(&self) -> Option<f64> {
406 self.data().scroll_y_max()
407 }
408
409 pub(crate) fn fetch_inherited_property<T>(
410 &self,
411 getter: fn(&'a NodeData) -> Option<T>,
412 ) -> Option<T> {
413 let mut node = *self;
414 loop {
415 let value = getter(node.data());
416 if value.is_some() {
417 return value;
418 }
419 node = node.parent()?;
420 }
421 }
422
423 pub fn is_text_input(&self) -> bool {
424 matches!(
425 self.role(),
426 Role::TextInput
427 | Role::MultilineTextInput
428 | Role::SearchInput
429 | Role::DateInput
430 | Role::DateTimeInput
431 | Role::WeekInput
432 | Role::MonthInput
433 | Role::TimeInput
434 | Role::EmailInput
435 | Role::NumberInput
436 | Role::PasswordInput
437 | Role::PhoneNumberInput
438 | Role::UrlInput
439 | Role::EditableComboBox
440 | Role::SpinButton
441 )
442 }
443
444 pub fn is_multiline(&self) -> bool {
445 self.role() == Role::MultilineTextInput
446 }
447
448 pub fn orientation(&self) -> Option<Orientation> {
449 self.data().orientation().or_else(|| {
450 if self.role() == Role::ListBox {
451 Some(Orientation::Vertical)
452 } else if self.role() == Role::TabList {
453 Some(Orientation::Horizontal)
454 } else {
455 None
456 }
457 })
458 }
459
460 pub fn is_clickable(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
470 self.supports_action(Action::Click, parent_filter)
471 }
472
473 pub fn is_selectable(&self) -> bool {
474 self.is_selected().is_some() && !self.is_disabled()
476 }
477
478 pub fn is_multiselectable(&self) -> bool {
479 self.data().is_multiselectable()
480 }
481
482 pub fn size_of_set_from_container(
483 &self,
484 filter: &impl Fn(&Node) -> FilterResult,
485 ) -> Option<usize> {
486 self.selection_container(filter)
487 .and_then(|c| c.size_of_set())
488 }
489
490 pub fn size_of_set(&self) -> Option<usize> {
491 self.data().size_of_set()
493 }
494
495 pub fn position_in_set(&self) -> Option<usize> {
496 self.data().position_in_set()
498 }
499
500 pub fn supports_toggle(&self) -> bool {
501 self.toggled().is_some()
502 }
503
504 pub fn supports_expand_collapse(&self) -> bool {
505 self.data().is_expanded().is_some()
506 }
507
508 pub fn is_invocable(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
509 self.is_clickable(parent_filter)
518 && !self.is_text_input()
519 && !matches!(self.role(), Role::Document | Role::Terminal)
520 && !self.supports_toggle()
521 && !self.supports_expand_collapse()
522 && self.is_selected().is_none()
523 }
524
525 pub fn supports_action(
526 &self,
527 action: Action,
528 parent_filter: &impl Fn(&Node) -> FilterResult,
529 ) -> bool {
530 if self.data().supports_action(action) {
531 return true;
532 }
533 if let Some(parent) = self.filtered_parent(parent_filter) {
534 return parent.data().child_supports_action(action);
535 }
536 false
537 }
538
539 pub fn supports_increment(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
540 self.supports_action(Action::Increment, parent_filter)
541 }
542
543 pub fn supports_decrement(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
544 self.supports_action(Action::Decrement, parent_filter)
545 }
546}
547
548fn descendant_label_filter(node: &Node) -> FilterResult {
549 match node.role() {
550 Role::Label | Role::Image => FilterResult::Include,
551 Role::GenericContainer => FilterResult::ExcludeNode,
552 _ => FilterResult::ExcludeSubtree,
553 }
554}
555
556impl<'a> Node<'a> {
557 pub fn labelled_by(
558 &self,
559 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
560 let explicit = &self.state.data.labelled_by();
561 if explicit.is_empty()
562 && matches!(
563 self.role(),
564 Role::Button
565 | Role::CheckBox
566 | Role::DefaultButton
567 | Role::Link
568 | Role::MenuItem
569 | Role::MenuItemCheckBox
570 | Role::MenuItemRadio
571 | Role::RadioButton
572 )
573 {
574 LabelledBy::FromDescendants(FilteredChildren::new(*self, &descendant_label_filter))
575 } else {
576 LabelledBy::Explicit {
577 ids: explicit.iter(),
578 tree_state: self.tree_state,
579 }
580 }
581 }
582
583 pub fn label_comes_from_value(&self) -> bool {
584 self.role() == Role::Label
585 }
586
587 pub fn label(&self) -> Option<String> {
588 let mut result = String::new();
589 self.write_label(&mut result).unwrap().then_some(result)
590 }
591
592 fn write_label_direct<W: fmt::Write>(&self, mut writer: W) -> Result<bool, fmt::Error> {
593 if let Some(label) = &self.data().label() {
594 writer.write_str(label)?;
595 Ok(true)
596 } else {
597 Ok(false)
598 }
599 }
600
601 pub fn write_label<W: fmt::Write>(&self, mut writer: W) -> Result<bool, fmt::Error> {
602 if self.write_label_direct(&mut writer)? {
603 Ok(true)
604 } else {
605 let mut wrote_one = false;
606 for node in self.labelled_by() {
607 let writer = SpacePrefixingWriter {
608 inner: &mut writer,
609 need_prefix: wrote_one,
610 };
611 let wrote_this_time = if node.label_comes_from_value() {
612 node.write_value(writer)
613 } else {
614 node.write_label_direct(writer)
615 }?;
616 wrote_one = wrote_one || wrote_this_time;
617 }
618 Ok(wrote_one)
619 }
620 }
621
622 pub fn description(&self) -> Option<String> {
623 self.data()
624 .description()
625 .map(|description| description.to_string())
626 }
627
628 fn is_empty_text_input(&self) -> bool {
629 let mut text_runs = self.text_runs();
630 if let Some(first_text_run) = text_runs.next() {
631 first_text_run
632 .data()
633 .value()
634 .map_or(true, |value| value.is_empty())
635 && text_runs.next().is_none()
636 } else {
637 true
638 }
639 }
640
641 pub fn placeholder(&self) -> Option<&str> {
642 self.data()
643 .placeholder()
644 .filter(|_| self.is_text_input() && self.is_empty_text_input())
645 }
646
647 pub fn value(&self) -> Option<String> {
648 let mut result = String::new();
649 self.write_value(&mut result).unwrap().then_some(result)
650 }
651
652 pub fn write_value<W: fmt::Write>(&self, mut writer: W) -> Result<bool, fmt::Error> {
653 if let Some(value) = &self.data().value() {
654 writer.write_str(value)?;
655 Ok(true)
656 } else if self.supports_text_ranges() && !self.is_multiline() {
657 self.document_range().write_text(writer)?;
658 Ok(true)
659 } else {
660 Ok(false)
661 }
662 }
663
664 pub fn has_value(&self) -> bool {
665 self.data().value().is_some() || (self.supports_text_ranges() && !self.is_multiline())
666 }
667
668 pub fn is_read_only_supported(&self) -> bool {
669 self.is_text_input()
670 || matches!(
671 self.role(),
672 Role::CheckBox
673 | Role::ColorWell
674 | Role::ComboBox
675 | Role::Grid
676 | Role::ListBox
677 | Role::MenuItemCheckBox
678 | Role::MenuItemRadio
679 | Role::MenuListPopup
680 | Role::RadioButton
681 | Role::RadioGroup
682 | Role::Slider
683 | Role::Switch
684 | Role::TreeGrid
685 )
686 }
687
688 pub fn should_have_read_only_state_by_default(&self) -> bool {
689 matches!(
690 self.role(),
691 Role::Article
692 | Role::Definition
693 | Role::DescriptionList
694 | Role::DescriptionListTerm
695 | Role::Directory
696 | Role::Document
697 | Role::GraphicsDocument
698 | Role::Image
699 | Role::List
700 | Role::ListItem
701 | Role::PdfRoot
702 | Role::ProgressIndicator
703 | Role::RootWebArea
704 | Role::Term
705 | Role::Timer
706 | Role::Toolbar
707 | Role::Tooltip
708 )
709 }
710
711 pub fn is_required(&self) -> bool {
712 self.data().is_required()
713 }
714
715 pub fn live(&self) -> Live {
716 self.data()
717 .live()
718 .unwrap_or_else(|| self.parent().map_or(Live::Off, |parent| parent.live()))
719 }
720
721 pub fn is_selected(&self) -> Option<bool> {
722 self.data().is_selected()
723 }
724
725 pub fn is_item_like(&self) -> bool {
726 matches!(
727 self.role(),
728 Role::Article
729 | Role::Comment
730 | Role::ListItem
731 | Role::MenuItem
732 | Role::MenuItemRadio
733 | Role::Tab
734 | Role::MenuItemCheckBox
735 | Role::TreeItem
736 | Role::ListBoxOption
737 | Role::MenuListOption
738 | Role::RadioButton
739 | Role::DescriptionListTerm
740 | Role::Term
741 )
742 }
743
744 pub fn is_container_with_selectable_children(&self) -> bool {
745 matches!(
746 self.role(),
747 Role::ComboBox
748 | Role::EditableComboBox
749 | Role::Grid
750 | Role::ListBox
751 | Role::ListGrid
752 | Role::Menu
753 | Role::MenuBar
754 | Role::MenuListPopup
755 | Role::RadioGroup
756 | Role::TabList
757 | Role::Toolbar
758 | Role::Tree
759 | Role::TreeGrid
760 )
761 }
762
763 pub fn controls(
764 &self,
765 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
766 let state = self.tree_state;
767 let data = &self.state.data;
768 data.controls()
769 .iter()
770 .map(move |id| state.node_by_id(*id).unwrap())
771 }
772
773 pub fn raw_text_selection(&self) -> Option<&TextSelection> {
774 self.data().text_selection()
775 }
776
777 pub fn raw_value(&self) -> Option<&str> {
778 self.data().value()
779 }
780
781 pub fn author_id(&self) -> Option<&str> {
782 self.data().author_id()
783 }
784
785 pub fn class_name(&self) -> Option<&str> {
786 self.data().class_name()
787 }
788
789 pub fn index_path(&self) -> Vec<usize> {
790 self.relative_index_path(self.tree_state.root_id())
791 }
792
793 pub fn relative_index_path(&self, ancestor_id: NodeId) -> Vec<usize> {
794 let mut result = Vec::new();
795 let mut current = *self;
796 while current.id() != ancestor_id {
797 let (parent, index) = current.parent_and_index().unwrap();
798 result.push(index);
799 current = parent;
800 }
801 result.reverse();
802 result
803 }
804
805 pub(crate) fn first_filtered_child(
806 &self,
807 filter: &impl Fn(&Node) -> FilterResult,
808 ) -> Option<Node<'a>> {
809 for child in self.children() {
810 let result = filter(&child);
811 if result == FilterResult::Include {
812 return Some(child);
813 }
814 if result == FilterResult::ExcludeNode {
815 if let Some(descendant) = child.first_filtered_child(filter) {
816 return Some(descendant);
817 }
818 }
819 }
820 None
821 }
822
823 pub(crate) fn last_filtered_child(
824 &self,
825 filter: &impl Fn(&Node) -> FilterResult,
826 ) -> Option<Node<'a>> {
827 for child in self.children().rev() {
828 let result = filter(&child);
829 if result == FilterResult::Include {
830 return Some(child);
831 }
832 if result == FilterResult::ExcludeNode {
833 if let Some(descendant) = child.last_filtered_child(filter) {
834 return Some(descendant);
835 }
836 }
837 }
838 None
839 }
840
841 pub fn selection_container(&self, filter: &impl Fn(&Node) -> FilterResult) -> Option<Node<'a>> {
842 self.filtered_parent(&|parent| match filter(parent) {
843 FilterResult::Include if parent.is_container_with_selectable_children() => {
844 FilterResult::Include
845 }
846 FilterResult::Include => FilterResult::ExcludeNode,
847 filter_result => filter_result,
848 })
849 }
850
851 pub fn items(
852 &self,
853 filter: impl Fn(&Node) -> FilterResult + 'a,
854 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
855 self.filtered_children(move |child| match filter(child) {
856 FilterResult::Include if child.is_item_like() => FilterResult::Include,
857 FilterResult::Include => FilterResult::ExcludeNode,
858 filter_result => filter_result,
859 })
860 }
861}
862
863struct SpacePrefixingWriter<W: fmt::Write> {
864 inner: W,
865 need_prefix: bool,
866}
867
868impl<W: fmt::Write> SpacePrefixingWriter<W> {
869 fn write_prefix_if_needed(&mut self) -> fmt::Result {
870 if self.need_prefix {
871 self.inner.write_char(' ')?;
872 self.need_prefix = false;
873 }
874 Ok(())
875 }
876}
877
878impl<W: fmt::Write> fmt::Write for SpacePrefixingWriter<W> {
879 fn write_str(&mut self, s: &str) -> fmt::Result {
880 self.write_prefix_if_needed()?;
881 self.inner.write_str(s)
882 }
883
884 fn write_char(&mut self, c: char) -> fmt::Result {
885 self.write_prefix_if_needed()?;
886 self.inner.write_char(c)
887 }
888}
889
890#[cfg(test)]
891mod tests {
892 use accesskit::{
893 Action, Node, NodeId, Point, Rect, Role, TextDirection, TextPosition, TextSelection, Tree,
894 TreeUpdate,
895 };
896 use alloc::vec;
897
898 use crate::tests::*;
899
900 #[test]
901 fn parent_and_index() {
902 let tree = test_tree();
903 assert!(tree.state().root().parent_and_index().is_none());
904 assert_eq!(
905 Some((ROOT_ID, 0)),
906 tree.state()
907 .node_by_id(PARAGRAPH_0_ID)
908 .unwrap()
909 .parent_and_index()
910 .map(|(parent, index)| (parent.id(), index))
911 );
912 assert_eq!(
913 Some((PARAGRAPH_0_ID, 0)),
914 tree.state()
915 .node_by_id(LABEL_0_0_IGNORED_ID)
916 .unwrap()
917 .parent_and_index()
918 .map(|(parent, index)| (parent.id(), index))
919 );
920 assert_eq!(
921 Some((ROOT_ID, 1)),
922 tree.state()
923 .node_by_id(PARAGRAPH_1_IGNORED_ID)
924 .unwrap()
925 .parent_and_index()
926 .map(|(parent, index)| (parent.id(), index))
927 );
928 }
929
930 #[test]
931 fn deepest_first_child() {
932 let tree = test_tree();
933 assert_eq!(
934 LABEL_0_0_IGNORED_ID,
935 tree.state().root().deepest_first_child().unwrap().id()
936 );
937 assert_eq!(
938 LABEL_0_0_IGNORED_ID,
939 tree.state()
940 .node_by_id(PARAGRAPH_0_ID)
941 .unwrap()
942 .deepest_first_child()
943 .unwrap()
944 .id()
945 );
946 assert!(tree
947 .state()
948 .node_by_id(LABEL_0_0_IGNORED_ID)
949 .unwrap()
950 .deepest_first_child()
951 .is_none());
952 }
953
954 #[test]
955 fn filtered_parent() {
956 let tree = test_tree();
957 assert_eq!(
958 ROOT_ID,
959 tree.state()
960 .node_by_id(LABEL_1_1_ID)
961 .unwrap()
962 .filtered_parent(&test_tree_filter)
963 .unwrap()
964 .id()
965 );
966 assert!(tree
967 .state()
968 .root()
969 .filtered_parent(&test_tree_filter)
970 .is_none());
971 }
972
973 #[test]
974 fn deepest_first_filtered_child() {
975 let tree = test_tree();
976 assert_eq!(
977 PARAGRAPH_0_ID,
978 tree.state()
979 .root()
980 .deepest_first_filtered_child(&test_tree_filter)
981 .unwrap()
982 .id()
983 );
984 assert!(tree
985 .state()
986 .node_by_id(PARAGRAPH_0_ID)
987 .unwrap()
988 .deepest_first_filtered_child(&test_tree_filter)
989 .is_none());
990 assert!(tree
991 .state()
992 .node_by_id(LABEL_0_0_IGNORED_ID)
993 .unwrap()
994 .deepest_first_filtered_child(&test_tree_filter)
995 .is_none());
996 }
997
998 #[test]
999 fn deepest_last_child() {
1000 let tree = test_tree();
1001 assert_eq!(
1002 EMPTY_CONTAINER_3_3_IGNORED_ID,
1003 tree.state().root().deepest_last_child().unwrap().id()
1004 );
1005 assert_eq!(
1006 EMPTY_CONTAINER_3_3_IGNORED_ID,
1007 tree.state()
1008 .node_by_id(PARAGRAPH_3_IGNORED_ID)
1009 .unwrap()
1010 .deepest_last_child()
1011 .unwrap()
1012 .id()
1013 );
1014 assert!(tree
1015 .state()
1016 .node_by_id(BUTTON_3_2_ID)
1017 .unwrap()
1018 .deepest_last_child()
1019 .is_none());
1020 }
1021
1022 #[test]
1023 fn deepest_last_filtered_child() {
1024 let tree = test_tree();
1025 assert_eq!(
1026 BUTTON_3_2_ID,
1027 tree.state()
1028 .root()
1029 .deepest_last_filtered_child(&test_tree_filter)
1030 .unwrap()
1031 .id()
1032 );
1033 assert_eq!(
1034 BUTTON_3_2_ID,
1035 tree.state()
1036 .node_by_id(PARAGRAPH_3_IGNORED_ID)
1037 .unwrap()
1038 .deepest_last_filtered_child(&test_tree_filter)
1039 .unwrap()
1040 .id()
1041 );
1042 assert!(tree
1043 .state()
1044 .node_by_id(BUTTON_3_2_ID)
1045 .unwrap()
1046 .deepest_last_filtered_child(&test_tree_filter)
1047 .is_none());
1048 assert!(tree
1049 .state()
1050 .node_by_id(PARAGRAPH_0_ID)
1051 .unwrap()
1052 .deepest_last_filtered_child(&test_tree_filter)
1053 .is_none());
1054 }
1055
1056 #[test]
1057 fn is_descendant_of() {
1058 let tree = test_tree();
1059 assert!(tree
1060 .state()
1061 .node_by_id(PARAGRAPH_0_ID)
1062 .unwrap()
1063 .is_descendant_of(&tree.state().root()));
1064 assert!(tree
1065 .state()
1066 .node_by_id(LABEL_0_0_IGNORED_ID)
1067 .unwrap()
1068 .is_descendant_of(&tree.state().root()));
1069 assert!(tree
1070 .state()
1071 .node_by_id(LABEL_0_0_IGNORED_ID)
1072 .unwrap()
1073 .is_descendant_of(&tree.state().node_by_id(PARAGRAPH_0_ID).unwrap()));
1074 assert!(!tree
1075 .state()
1076 .node_by_id(LABEL_0_0_IGNORED_ID)
1077 .unwrap()
1078 .is_descendant_of(&tree.state().node_by_id(PARAGRAPH_2_ID).unwrap()));
1079 assert!(!tree
1080 .state()
1081 .node_by_id(PARAGRAPH_0_ID)
1082 .unwrap()
1083 .is_descendant_of(&tree.state().node_by_id(PARAGRAPH_2_ID).unwrap()));
1084 }
1085
1086 #[test]
1087 fn is_root() {
1088 let tree = test_tree();
1089 assert!(tree.state().node_by_id(ROOT_ID).unwrap().is_root());
1090 assert!(!tree.state().node_by_id(PARAGRAPH_0_ID).unwrap().is_root());
1091 }
1092
1093 #[test]
1094 fn bounding_box() {
1095 let tree = test_tree();
1096 assert!(tree
1097 .state()
1098 .node_by_id(ROOT_ID)
1099 .unwrap()
1100 .bounding_box()
1101 .is_none());
1102 assert_eq!(
1103 Some(Rect {
1104 x0: 10.0,
1105 y0: 40.0,
1106 x1: 810.0,
1107 y1: 80.0,
1108 }),
1109 tree.state()
1110 .node_by_id(PARAGRAPH_1_IGNORED_ID)
1111 .unwrap()
1112 .bounding_box()
1113 );
1114 assert_eq!(
1115 Some(Rect {
1116 x0: 20.0,
1117 y0: 50.0,
1118 x1: 100.0,
1119 y1: 70.0,
1120 }),
1121 tree.state()
1122 .node_by_id(LABEL_1_1_ID)
1123 .unwrap()
1124 .bounding_box()
1125 );
1126 }
1127
1128 #[test]
1129 fn node_at_point() {
1130 let tree = test_tree();
1131 assert!(tree
1132 .state()
1133 .root()
1134 .node_at_point(Point::new(10.0, 40.0), &test_tree_filter)
1135 .is_none());
1136 assert_eq!(
1137 Some(LABEL_1_1_ID),
1138 tree.state()
1139 .root()
1140 .node_at_point(Point::new(20.0, 50.0), &test_tree_filter)
1141 .map(|node| node.id())
1142 );
1143 assert_eq!(
1144 Some(LABEL_1_1_ID),
1145 tree.state()
1146 .root()
1147 .node_at_point(Point::new(50.0, 60.0), &test_tree_filter)
1148 .map(|node| node.id())
1149 );
1150 assert!(tree
1151 .state()
1152 .root()
1153 .node_at_point(Point::new(100.0, 70.0), &test_tree_filter)
1154 .is_none());
1155 }
1156
1157 #[test]
1158 fn no_label_or_labelled_by() {
1159 let update = TreeUpdate {
1160 nodes: vec![
1161 (NodeId(0), {
1162 let mut node = Node::new(Role::Window);
1163 node.set_children(vec![NodeId(1)]);
1164 node
1165 }),
1166 (NodeId(1), Node::new(Role::Button)),
1167 ],
1168 tree: Some(Tree::new(NodeId(0))),
1169 focus: NodeId(0),
1170 };
1171 let tree = crate::Tree::new(update, false);
1172 assert_eq!(None, tree.state().node_by_id(NodeId(1)).unwrap().label());
1173 }
1174
1175 #[test]
1176 fn label_from_labelled_by() {
1177 const LABEL_1: &str = "Check email every";
1180 const LABEL_2: &str = "minutes";
1181
1182 let update = TreeUpdate {
1183 nodes: vec![
1184 (NodeId(0), {
1185 let mut node = Node::new(Role::Window);
1186 node.set_children(vec![NodeId(1), NodeId(2), NodeId(3), NodeId(4)]);
1187 node
1188 }),
1189 (NodeId(1), {
1190 let mut node = Node::new(Role::CheckBox);
1191 node.set_labelled_by(vec![NodeId(2), NodeId(4)]);
1192 node
1193 }),
1194 (NodeId(2), {
1195 let mut node = Node::new(Role::Label);
1196 node.set_value(LABEL_1);
1197 node
1198 }),
1199 (NodeId(3), {
1200 let mut node = Node::new(Role::TextInput);
1201 node.push_labelled_by(NodeId(4));
1202 node
1203 }),
1204 (NodeId(4), {
1205 let mut node = Node::new(Role::Label);
1206 node.set_value(LABEL_2);
1207 node
1208 }),
1209 ],
1210 tree: Some(Tree::new(NodeId(0))),
1211 focus: NodeId(0),
1212 };
1213 let tree = crate::Tree::new(update, false);
1214 assert_eq!(
1215 Some([LABEL_1, LABEL_2].join(" ")),
1216 tree.state().node_by_id(NodeId(1)).unwrap().label()
1217 );
1218 assert_eq!(
1219 Some(LABEL_2.into()),
1220 tree.state().node_by_id(NodeId(3)).unwrap().label()
1221 );
1222 }
1223
1224 #[test]
1225 fn label_from_descendant_label() {
1226 const ROOT_ID: NodeId = NodeId(0);
1227 const DEFAULT_BUTTON_ID: NodeId = NodeId(1);
1228 const DEFAULT_BUTTON_LABEL_ID: NodeId = NodeId(2);
1229 const LINK_ID: NodeId = NodeId(3);
1230 const LINK_LABEL_CONTAINER_ID: NodeId = NodeId(4);
1231 const LINK_LABEL_ID: NodeId = NodeId(5);
1232 const CHECKBOX_ID: NodeId = NodeId(6);
1233 const CHECKBOX_LABEL_ID: NodeId = NodeId(7);
1234 const RADIO_BUTTON_ID: NodeId = NodeId(8);
1235 const RADIO_BUTTON_LABEL_ID: NodeId = NodeId(9);
1236 const MENU_BUTTON_ID: NodeId = NodeId(10);
1237 const MENU_BUTTON_LABEL_ID: NodeId = NodeId(11);
1238 const MENU_ID: NodeId = NodeId(12);
1239 const MENU_ITEM_ID: NodeId = NodeId(13);
1240 const MENU_ITEM_LABEL_ID: NodeId = NodeId(14);
1241 const MENU_ITEM_CHECKBOX_ID: NodeId = NodeId(15);
1242 const MENU_ITEM_CHECKBOX_LABEL_ID: NodeId = NodeId(16);
1243 const MENU_ITEM_RADIO_ID: NodeId = NodeId(17);
1244 const MENU_ITEM_RADIO_LABEL_ID: NodeId = NodeId(18);
1245
1246 const DEFAULT_BUTTON_LABEL: &str = "Play";
1247 const LINK_LABEL: &str = "Watch in browser";
1248 const CHECKBOX_LABEL: &str = "Resume from previous position";
1249 const RADIO_BUTTON_LABEL: &str = "Normal speed";
1250 const MENU_BUTTON_LABEL: &str = "More";
1251 const MENU_ITEM_LABEL: &str = "Share";
1252 const MENU_ITEM_CHECKBOX_LABEL: &str = "Apply volume processing";
1253 const MENU_ITEM_RADIO_LABEL: &str = "Maximize loudness for noisy environment";
1254
1255 let update = TreeUpdate {
1256 nodes: vec![
1257 (ROOT_ID, {
1258 let mut node = Node::new(Role::Window);
1259 node.set_children(vec![
1260 DEFAULT_BUTTON_ID,
1261 LINK_ID,
1262 CHECKBOX_ID,
1263 RADIO_BUTTON_ID,
1264 MENU_BUTTON_ID,
1265 MENU_ID,
1266 ]);
1267 node
1268 }),
1269 (DEFAULT_BUTTON_ID, {
1270 let mut node = Node::new(Role::DefaultButton);
1271 node.push_child(DEFAULT_BUTTON_LABEL_ID);
1272 node
1273 }),
1274 (DEFAULT_BUTTON_LABEL_ID, {
1275 let mut node = Node::new(Role::Image);
1276 node.set_label(DEFAULT_BUTTON_LABEL);
1277 node
1278 }),
1279 (LINK_ID, {
1280 let mut node = Node::new(Role::Link);
1281 node.push_child(LINK_LABEL_CONTAINER_ID);
1282 node
1283 }),
1284 (LINK_LABEL_CONTAINER_ID, {
1285 let mut node = Node::new(Role::GenericContainer);
1286 node.push_child(LINK_LABEL_ID);
1287 node
1288 }),
1289 (LINK_LABEL_ID, {
1290 let mut node = Node::new(Role::Label);
1291 node.set_value(LINK_LABEL);
1292 node
1293 }),
1294 (CHECKBOX_ID, {
1295 let mut node = Node::new(Role::CheckBox);
1296 node.push_child(CHECKBOX_LABEL_ID);
1297 node
1298 }),
1299 (CHECKBOX_LABEL_ID, {
1300 let mut node = Node::new(Role::Label);
1301 node.set_value(CHECKBOX_LABEL);
1302 node
1303 }),
1304 (RADIO_BUTTON_ID, {
1305 let mut node = Node::new(Role::RadioButton);
1306 node.push_child(RADIO_BUTTON_LABEL_ID);
1307 node
1308 }),
1309 (RADIO_BUTTON_LABEL_ID, {
1310 let mut node = Node::new(Role::Label);
1311 node.set_value(RADIO_BUTTON_LABEL);
1312 node
1313 }),
1314 (MENU_BUTTON_ID, {
1315 let mut node = Node::new(Role::Button);
1316 node.push_child(MENU_BUTTON_LABEL_ID);
1317 node
1318 }),
1319 (MENU_BUTTON_LABEL_ID, {
1320 let mut node = Node::new(Role::Label);
1321 node.set_value(MENU_BUTTON_LABEL);
1322 node
1323 }),
1324 (MENU_ID, {
1325 let mut node = Node::new(Role::Menu);
1326 node.set_children([MENU_ITEM_ID, MENU_ITEM_CHECKBOX_ID, MENU_ITEM_RADIO_ID]);
1327 node
1328 }),
1329 (MENU_ITEM_ID, {
1330 let mut node = Node::new(Role::MenuItem);
1331 node.push_child(MENU_ITEM_LABEL_ID);
1332 node
1333 }),
1334 (MENU_ITEM_LABEL_ID, {
1335 let mut node = Node::new(Role::Label);
1336 node.set_value(MENU_ITEM_LABEL);
1337 node
1338 }),
1339 (MENU_ITEM_CHECKBOX_ID, {
1340 let mut node = Node::new(Role::MenuItemCheckBox);
1341 node.push_child(MENU_ITEM_CHECKBOX_LABEL_ID);
1342 node
1343 }),
1344 (MENU_ITEM_CHECKBOX_LABEL_ID, {
1345 let mut node = Node::new(Role::Label);
1346 node.set_value(MENU_ITEM_CHECKBOX_LABEL);
1347 node
1348 }),
1349 (MENU_ITEM_RADIO_ID, {
1350 let mut node = Node::new(Role::MenuItemRadio);
1351 node.push_child(MENU_ITEM_RADIO_LABEL_ID);
1352 node
1353 }),
1354 (MENU_ITEM_RADIO_LABEL_ID, {
1355 let mut node = Node::new(Role::Label);
1356 node.set_value(MENU_ITEM_RADIO_LABEL);
1357 node
1358 }),
1359 ],
1360 tree: Some(Tree::new(ROOT_ID)),
1361 focus: ROOT_ID,
1362 };
1363 let tree = crate::Tree::new(update, false);
1364 assert_eq!(
1365 Some(DEFAULT_BUTTON_LABEL.into()),
1366 tree.state().node_by_id(DEFAULT_BUTTON_ID).unwrap().label()
1367 );
1368 assert_eq!(
1369 Some(LINK_LABEL.into()),
1370 tree.state().node_by_id(LINK_ID).unwrap().label()
1371 );
1372 assert_eq!(
1373 Some(CHECKBOX_LABEL.into()),
1374 tree.state().node_by_id(CHECKBOX_ID).unwrap().label()
1375 );
1376 assert_eq!(
1377 Some(RADIO_BUTTON_LABEL.into()),
1378 tree.state().node_by_id(RADIO_BUTTON_ID).unwrap().label()
1379 );
1380 assert_eq!(
1381 Some(MENU_BUTTON_LABEL.into()),
1382 tree.state().node_by_id(MENU_BUTTON_ID).unwrap().label()
1383 );
1384 assert_eq!(
1385 Some(MENU_ITEM_LABEL.into()),
1386 tree.state().node_by_id(MENU_ITEM_ID).unwrap().label()
1387 );
1388 assert_eq!(
1389 Some(MENU_ITEM_CHECKBOX_LABEL.into()),
1390 tree.state()
1391 .node_by_id(MENU_ITEM_CHECKBOX_ID)
1392 .unwrap()
1393 .label()
1394 );
1395 assert_eq!(
1396 Some(MENU_ITEM_RADIO_LABEL.into()),
1397 tree.state().node_by_id(MENU_ITEM_RADIO_ID).unwrap().label()
1398 );
1399 }
1400
1401 #[test]
1402 fn placeholder_should_be_exposed_on_empty_text_input() {
1403 const ROOT_ID: NodeId = NodeId(0);
1404 const TEXT_INPUT_ID: NodeId = NodeId(1);
1405 const TEXT_RUN_ID: NodeId = NodeId(2);
1406
1407 const PLACEHOLDER: &str = "John Doe";
1408
1409 let update = TreeUpdate {
1410 nodes: vec![
1411 (ROOT_ID, {
1412 let mut node = Node::new(Role::Window);
1413 node.set_children(vec![TEXT_INPUT_ID]);
1414 node
1415 }),
1416 (TEXT_INPUT_ID, {
1417 let mut node = Node::new(Role::MultilineTextInput);
1418 node.set_bounds(Rect {
1419 x0: 8.0,
1420 y0: 8.0,
1421 x1: 296.0,
1422 y1: 69.5,
1423 });
1424 node.push_child(TEXT_RUN_ID);
1425 node.set_placeholder(PLACEHOLDER);
1426 node.set_text_selection(TextSelection {
1427 anchor: TextPosition {
1428 node: TEXT_RUN_ID,
1429 character_index: 0,
1430 },
1431 focus: TextPosition {
1432 node: TEXT_RUN_ID,
1433 character_index: 0,
1434 },
1435 });
1436 node.add_action(Action::Focus);
1437 node
1438 }),
1439 (TEXT_RUN_ID, {
1440 let mut node = Node::new(Role::TextRun);
1441 node.set_bounds(Rect {
1442 x0: 12.0,
1443 y0: 10.0,
1444 x1: 12.0,
1445 y1: 24.0,
1446 });
1447 node.set_value("");
1448 node.set_character_lengths([]);
1449 node.set_character_positions([]);
1450 node.set_character_widths([]);
1451 node.set_word_lengths([0]);
1452 node.set_text_direction(TextDirection::LeftToRight);
1453 node
1454 }),
1455 ],
1456 tree: Some(Tree::new(ROOT_ID)),
1457 focus: TEXT_INPUT_ID,
1458 };
1459 let tree = crate::Tree::new(update, false);
1460 assert_eq!(
1461 Some(PLACEHOLDER),
1462 tree.state()
1463 .node_by_id(TEXT_INPUT_ID)
1464 .unwrap()
1465 .placeholder()
1466 );
1467 }
1468
1469 #[test]
1470 fn placeholder_should_be_ignored_on_non_empty_text_input() {
1471 const ROOT_ID: NodeId = NodeId(0);
1472 const TEXT_INPUT_ID: NodeId = NodeId(1);
1473 const TEXT_RUN_ID: NodeId = NodeId(2);
1474
1475 const PLACEHOLDER: &str = "John Doe";
1476
1477 let update = TreeUpdate {
1478 nodes: vec![
1479 (ROOT_ID, {
1480 let mut node = Node::new(Role::Window);
1481 node.set_children(vec![TEXT_INPUT_ID]);
1482 node
1483 }),
1484 (TEXT_INPUT_ID, {
1485 let mut node = Node::new(Role::MultilineTextInput);
1486 node.set_bounds(Rect {
1487 x0: 8.0,
1488 y0: 8.0,
1489 x1: 296.0,
1490 y1: 69.5,
1491 });
1492 node.push_child(TEXT_RUN_ID);
1493 node.set_placeholder(PLACEHOLDER);
1494 node.set_text_selection(TextSelection {
1495 anchor: TextPosition {
1496 node: TEXT_RUN_ID,
1497 character_index: 1,
1498 },
1499 focus: TextPosition {
1500 node: TEXT_RUN_ID,
1501 character_index: 1,
1502 },
1503 });
1504 node.add_action(Action::Focus);
1505 node
1506 }),
1507 (TEXT_RUN_ID, {
1508 let mut node = Node::new(Role::TextRun);
1509 node.set_bounds(Rect {
1510 x0: 12.0,
1511 y0: 10.0,
1512 x1: 20.0,
1513 y1: 24.0,
1514 });
1515 node.set_value("A");
1516 node.set_character_lengths([1]);
1517 node.set_character_positions([0.0]);
1518 node.set_character_widths([8.0]);
1519 node.set_word_lengths([1]);
1520 node.set_text_direction(TextDirection::LeftToRight);
1521 node
1522 }),
1523 ],
1524 tree: Some(Tree::new(ROOT_ID)),
1525 focus: TEXT_INPUT_ID,
1526 };
1527 let tree = crate::Tree::new(update, false);
1528 assert_eq!(
1529 None,
1530 tree.state()
1531 .node_by_id(TEXT_INPUT_ID)
1532 .unwrap()
1533 .placeholder()
1534 );
1535 }
1536}