accesskit_consumer/
node.rs

1// Copyright 2021 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file) or the MIT license (found in
4// the LICENSE-MIT file), at your option.
5
6// Derived from Chromium's accessibility abstraction.
7// Copyright 2021 The Chromium Authors. All rights reserved.
8// Use of this source code is governed by a BSD-style license that can be
9// found in the LICENSE.chromium file.
10
11use accesskit::{
12    Action, Affine, AriaCurrent, HasPopup, Live, Node as NodeData, NodeId as LocalNodeId,
13    Orientation, Point, Rect, Role, SortDirection, TextSelection, 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    ChildIds, FilteredChildren, FollowingFilteredSiblings, FollowingSiblings, LabelledBy,
24    PrecedingFilteredSiblings, PrecedingSiblings,
25};
26use crate::tree::{State as TreeState, TreeIndex};
27
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
29pub struct NodeId(TreeIndex, LocalNodeId);
30
31impl NodeId {
32    pub(crate) fn new(local_id: LocalNodeId, tree_index: TreeIndex) -> Self {
33        Self(tree_index, local_id)
34    }
35
36    pub(crate) fn with_same_tree(&self, local_id: LocalNodeId) -> Self {
37        Self(self.0, local_id)
38    }
39
40    pub(crate) fn to_components(self) -> (LocalNodeId, TreeIndex) {
41        (self.1, self.0)
42    }
43}
44
45impl From<NodeId> for u128 {
46    fn from(id: NodeId) -> Self {
47        let tree_index = id.0 .0 as u128;
48        let local_id = id.1 .0 as u128;
49        (local_id << 64) | tree_index
50    }
51}
52
53#[derive(Clone, Copy, PartialEq, Eq, Debug)]
54pub(crate) struct ParentAndIndex(pub(crate) NodeId, pub(crate) usize);
55
56#[derive(Clone, Debug)]
57pub(crate) struct NodeState {
58    pub(crate) parent_and_index: Option<ParentAndIndex>,
59    pub(crate) data: NodeData,
60}
61
62#[derive(Copy, Clone, Debug)]
63pub struct Node<'a> {
64    pub tree_state: &'a TreeState,
65    pub(crate) id: NodeId,
66    pub(crate) state: &'a NodeState,
67}
68
69impl<'a> Node<'a> {
70    pub fn data(&self) -> &'a NodeData {
71        &self.state.data
72    }
73
74    pub fn is_focused(&self) -> bool {
75        let dominated_by_active_descendant = |node_id| {
76            self.tree_state
77                .node_by_id(node_id)
78                .and_then(|node| node.active_descendant())
79                .is_some()
80        };
81        match self.tree_state.focus_id() {
82            Some(focus_id) if focus_id == self.id() => !dominated_by_active_descendant(focus_id),
83            Some(focus_id) => self
84                .tree_state
85                .node_by_id(focus_id)
86                .and_then(|focused| focused.active_descendant())
87                .is_some_and(|active_descendant| active_descendant.id() == self.id()),
88            None => false,
89        }
90    }
91
92    pub fn is_focused_in_tree(&self) -> bool {
93        self.tree_state.focus == self.id()
94    }
95
96    pub fn is_focusable(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
97        self.supports_action(Action::Focus, parent_filter) || self.is_focused_in_tree()
98    }
99
100    pub fn is_root(&self) -> bool {
101        // Don't check for absence of a parent node, in case a non-root node
102        // somehow gets detached from the tree.
103        self.id() == self.tree_state.root_id()
104    }
105
106    /// Returns true if this node is a graft node (has a tree_id property set).
107    pub fn is_graft(&self) -> bool {
108        self.state.data.tree_id().is_some()
109    }
110
111    pub fn parent_id(&self) -> Option<NodeId> {
112        self.state
113            .parent_and_index
114            .as_ref()
115            .map(|ParentAndIndex(id, _)| *id)
116    }
117
118    pub fn parent(&self) -> Option<Node<'a>> {
119        self.parent_id()
120            .map(|id| self.tree_state.node_by_id(id).unwrap())
121    }
122
123    pub fn filtered_parent(&self, filter: &impl Fn(&Node) -> FilterResult) -> Option<Node<'a>> {
124        self.parent().and_then(move |parent| {
125            if filter(&parent) == FilterResult::Include {
126                Some(parent)
127            } else {
128                parent.filtered_parent(filter)
129            }
130        })
131    }
132
133    pub fn parent_and_index(self) -> Option<(Node<'a>, usize)> {
134        self.state
135            .parent_and_index
136            .as_ref()
137            .map(|ParentAndIndex(parent, index)| {
138                (self.tree_state.node_by_id(*parent).unwrap(), *index)
139            })
140    }
141
142    /// Returns the single child of a graft node (the subtree root), if available.
143    fn graft_child_id(&self) -> Option<NodeId> {
144        self.state
145            .data
146            .tree_id()
147            .and_then(|tree_id| self.tree_state.subtree_root(tree_id))
148    }
149
150    pub fn child_ids(
151        &self,
152    ) -> impl DoubleEndedIterator<Item = NodeId>
153           + ExactSizeIterator<Item = NodeId>
154           + FusedIterator<Item = NodeId>
155           + 'a {
156        if self.is_graft() {
157            ChildIds::Graft(self.graft_child_id())
158        } else {
159            ChildIds::Normal {
160                parent_id: self.id,
161                children: self.state.data.children().iter(),
162            }
163        }
164    }
165
166    pub fn children(
167        &self,
168    ) -> impl DoubleEndedIterator<Item = Node<'a>>
169           + ExactSizeIterator<Item = Node<'a>>
170           + FusedIterator<Item = Node<'a>>
171           + 'a {
172        let state = self.tree_state;
173        self.child_ids()
174            .map(move |id| state.node_by_id(id).unwrap())
175    }
176
177    pub fn filtered_children(
178        &self,
179        filter: impl Fn(&Node) -> FilterResult + 'a,
180    ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
181        FilteredChildren::new(*self, filter)
182    }
183
184    pub fn following_sibling_ids(
185        &self,
186    ) -> impl DoubleEndedIterator<Item = NodeId>
187           + ExactSizeIterator<Item = NodeId>
188           + FusedIterator<Item = NodeId>
189           + 'a {
190        FollowingSiblings::new(*self)
191    }
192
193    pub fn following_siblings(
194        &self,
195    ) -> impl DoubleEndedIterator<Item = Node<'a>>
196           + ExactSizeIterator<Item = Node<'a>>
197           + FusedIterator<Item = Node<'a>>
198           + 'a {
199        let state = self.tree_state;
200        self.following_sibling_ids()
201            .map(move |id| state.node_by_id(id).unwrap())
202    }
203
204    pub fn following_filtered_siblings(
205        &self,
206        filter: impl Fn(&Node) -> FilterResult + 'a,
207    ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
208        FollowingFilteredSiblings::new(*self, filter)
209    }
210
211    pub fn preceding_sibling_ids(
212        &self,
213    ) -> impl DoubleEndedIterator<Item = NodeId>
214           + ExactSizeIterator<Item = NodeId>
215           + FusedIterator<Item = NodeId>
216           + 'a {
217        PrecedingSiblings::new(*self)
218    }
219
220    pub fn preceding_siblings(
221        &self,
222    ) -> impl DoubleEndedIterator<Item = Node<'a>>
223           + ExactSizeIterator<Item = Node<'a>>
224           + FusedIterator<Item = Node<'a>>
225           + 'a {
226        let state = self.tree_state;
227        self.preceding_sibling_ids()
228            .map(move |id| state.node_by_id(id).unwrap())
229    }
230
231    pub fn preceding_filtered_siblings(
232        &self,
233        filter: impl Fn(&Node) -> FilterResult + 'a,
234    ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
235        PrecedingFilteredSiblings::new(*self, filter)
236    }
237
238    pub fn deepest_first_child(self) -> Option<Node<'a>> {
239        let mut deepest_child = self.children().next()?;
240        while let Some(first_child) = deepest_child.children().next() {
241            deepest_child = first_child;
242        }
243        Some(deepest_child)
244    }
245
246    pub fn deepest_first_filtered_child(
247        &self,
248        filter: &impl Fn(&Node) -> FilterResult,
249    ) -> Option<Node<'a>> {
250        let mut deepest_child = self.first_filtered_child(filter)?;
251        while let Some(first_child) = deepest_child.first_filtered_child(filter) {
252            deepest_child = first_child;
253        }
254        Some(deepest_child)
255    }
256
257    pub fn deepest_last_child(self) -> Option<Node<'a>> {
258        let mut deepest_child = self.children().next_back()?;
259        while let Some(last_child) = deepest_child.children().next_back() {
260            deepest_child = last_child;
261        }
262        Some(deepest_child)
263    }
264
265    pub fn deepest_last_filtered_child(
266        &self,
267        filter: &impl Fn(&Node) -> FilterResult,
268    ) -> Option<Node<'a>> {
269        let mut deepest_child = self.last_filtered_child(filter)?;
270        while let Some(last_child) = deepest_child.last_filtered_child(filter) {
271            deepest_child = last_child;
272        }
273        Some(deepest_child)
274    }
275
276    pub fn is_descendant_of(&self, ancestor: &Node) -> bool {
277        if self.id() == ancestor.id() {
278            return true;
279        }
280        if let Some(parent) = self.parent() {
281            return parent.is_descendant_of(ancestor);
282        }
283        false
284    }
285
286    /// Returns the transform defined directly on this node, or the identity
287    /// transform, without taking into account transforms on ancestors.
288    pub fn direct_transform(&self) -> Affine {
289        self.data()
290            .transform()
291            .map_or(Affine::IDENTITY, |value| *value)
292    }
293
294    /// Returns the combined affine transform of this node and its ancestors,
295    /// up to and including the root of this node's tree.
296    pub fn transform(&self) -> Affine {
297        self.parent()
298            .map_or(Affine::IDENTITY, |parent| parent.transform())
299            * self.direct_transform()
300    }
301
302    pub(crate) fn relative_transform(&self, stop_at: &Node) -> Affine {
303        let parent_transform = if let Some(parent) = self.parent() {
304            if parent.id() == stop_at.id() {
305                Affine::IDENTITY
306            } else {
307                parent.relative_transform(stop_at)
308            }
309        } else {
310            Affine::IDENTITY
311        };
312        parent_transform * self.direct_transform()
313    }
314
315    pub fn raw_bounds(&self) -> Option<Rect> {
316        self.data().bounds()
317    }
318
319    pub fn has_bounds(&self) -> bool {
320        self.raw_bounds().is_some()
321    }
322
323    /// Returns the node's transformed bounding box relative to the tree's
324    /// container (e.g. window).
325    pub fn bounding_box(&self) -> Option<Rect> {
326        self.raw_bounds()
327            .as_ref()
328            .map(|rect| self.transform().transform_rect_bbox(*rect))
329    }
330
331    pub(crate) fn bounding_box_in_coordinate_space(&self, other: &Node) -> Option<Rect> {
332        self.raw_bounds()
333            .as_ref()
334            .map(|rect| self.relative_transform(other).transform_rect_bbox(*rect))
335    }
336
337    pub(crate) fn hit_test(
338        &self,
339        point: Point,
340        filter: &impl Fn(&Node) -> FilterResult,
341    ) -> Option<(Node<'a>, Point)> {
342        let filter_result = filter(self);
343
344        if filter_result == FilterResult::ExcludeSubtree {
345            return None;
346        }
347
348        for child in self.children().rev() {
349            let point = child.direct_transform().inverse() * point;
350            if let Some(result) = child.hit_test(point, filter) {
351                return Some(result);
352            }
353        }
354
355        if filter_result == FilterResult::Include {
356            if let Some(rect) = &self.raw_bounds() {
357                if rect.contains(point) {
358                    return Some((*self, point));
359                }
360            }
361        }
362
363        None
364    }
365
366    /// Returns the deepest filtered node, either this node or a descendant,
367    /// at the given point in this node's coordinate space.
368    pub fn node_at_point(
369        &self,
370        point: Point,
371        filter: &impl Fn(&Node) -> FilterResult,
372    ) -> Option<Node<'a>> {
373        self.hit_test(point, filter).map(|(node, _)| node)
374    }
375
376    pub fn id(&self) -> NodeId {
377        self.id
378    }
379
380    pub fn role(&self) -> Role {
381        self.data().role()
382    }
383
384    pub fn role_description(&self) -> Option<&str> {
385        self.data().role_description()
386    }
387
388    pub fn has_role_description(&self) -> bool {
389        self.data().role_description().is_some()
390    }
391
392    pub fn is_live_atomic(&self) -> bool {
393        self.data().is_live_atomic()
394    }
395
396    pub fn is_busy(&self) -> bool {
397        self.data().is_busy()
398    }
399
400    pub fn column_index_text(&self) -> Option<&str> {
401        self.data().column_index_text()
402    }
403
404    pub fn row_index_text(&self) -> Option<&str> {
405        self.data().row_index_text()
406    }
407
408    pub fn braille_label(&self) -> Option<&str> {
409        self.data().braille_label()
410    }
411
412    pub fn has_braille_label(&self) -> bool {
413        self.data().braille_label().is_some()
414    }
415
416    pub fn braille_role_description(&self) -> Option<&str> {
417        self.data().braille_role_description()
418    }
419
420    pub fn has_braille_role_description(&self) -> bool {
421        self.data().braille_role_description().is_some()
422    }
423
424    pub fn aria_current(&self) -> Option<AriaCurrent> {
425        self.data().aria_current()
426    }
427
428    pub fn has_popup(&self) -> Option<HasPopup> {
429        self.data().has_popup()
430    }
431
432    pub fn is_hidden(&self) -> bool {
433        self.fetch_inherited_flag(NodeData::is_hidden)
434    }
435
436    pub fn level(&self) -> Option<usize> {
437        self.data().level()
438    }
439
440    pub fn is_disabled(&self) -> bool {
441        self.data().is_disabled()
442    }
443
444    pub fn is_read_only(&self) -> bool {
445        let data = self.data();
446        if data.is_read_only() {
447            true
448        } else {
449            self.should_have_read_only_state_by_default() || !self.is_read_only_supported()
450        }
451    }
452
453    pub fn is_read_only_or_disabled(&self) -> bool {
454        self.is_read_only() || self.is_disabled()
455    }
456
457    pub fn toggled(&self) -> Option<Toggled> {
458        self.data().toggled()
459    }
460
461    pub fn numeric_value(&self) -> Option<f64> {
462        self.data().numeric_value()
463    }
464
465    pub fn min_numeric_value(&self) -> Option<f64> {
466        self.data().min_numeric_value()
467    }
468
469    pub fn max_numeric_value(&self) -> Option<f64> {
470        self.data().max_numeric_value()
471    }
472
473    pub fn numeric_value_step(&self) -> Option<f64> {
474        self.data().numeric_value_step()
475    }
476
477    pub fn numeric_value_jump(&self) -> Option<f64> {
478        self.data().numeric_value_jump()
479    }
480
481    pub fn clips_children(&self) -> bool {
482        self.data().clips_children()
483    }
484
485    pub fn scroll_x(&self) -> Option<f64> {
486        self.data().scroll_x()
487    }
488
489    pub fn scroll_x_min(&self) -> Option<f64> {
490        self.data().scroll_x_min()
491    }
492
493    pub fn scroll_x_max(&self) -> Option<f64> {
494        self.data().scroll_x_max()
495    }
496
497    pub fn scroll_y(&self) -> Option<f64> {
498        self.data().scroll_y()
499    }
500
501    pub fn scroll_y_min(&self) -> Option<f64> {
502        self.data().scroll_y_min()
503    }
504
505    pub fn scroll_y_max(&self) -> Option<f64> {
506        self.data().scroll_y_max()
507    }
508
509    pub(crate) fn fetch_inherited_property<T>(
510        &self,
511        getter: fn(&'a NodeData) -> Option<T>,
512    ) -> Option<T> {
513        let mut node = *self;
514        loop {
515            let value = getter(node.data());
516            if value.is_some() {
517                return value;
518            }
519            node = node.parent()?;
520        }
521    }
522
523    pub(crate) fn fetch_inherited_flag(&self, getter: fn(&'a NodeData) -> bool) -> bool {
524        let mut node = *self;
525        loop {
526            if getter(node.data()) {
527                return true;
528            }
529            if let Some(parent) = node.parent() {
530                node = parent;
531            } else {
532                return false;
533            }
534        }
535    }
536
537    pub fn is_text_input(&self) -> bool {
538        matches!(
539            self.role(),
540            Role::TextInput
541                | Role::MultilineTextInput
542                | Role::SearchInput
543                | Role::DateInput
544                | Role::DateTimeInput
545                | Role::WeekInput
546                | Role::MonthInput
547                | Role::TimeInput
548                | Role::EmailInput
549                | Role::NumberInput
550                | Role::PasswordInput
551                | Role::PhoneNumberInput
552                | Role::UrlInput
553                | Role::EditableComboBox
554                | Role::SpinButton
555        )
556    }
557
558    pub fn is_multiline(&self) -> bool {
559        self.role() == Role::MultilineTextInput
560    }
561
562    pub fn orientation(&self) -> Option<Orientation> {
563        self.data().orientation().or_else(|| {
564            if self.role() == Role::ListBox {
565                Some(Orientation::Vertical)
566            } else if self.role() == Role::TabList {
567                Some(Orientation::Horizontal)
568            } else {
569                None
570            }
571        })
572    }
573
574    pub fn is_dialog(&self) -> bool {
575        matches!(self.role(), Role::AlertDialog | Role::Dialog)
576    }
577
578    pub fn is_modal(&self) -> bool {
579        self.data().is_modal()
580    }
581
582    // When probing for supported actions as the next several functions do,
583    // it's tempting to check the role. But it's better to not assume anything
584    // beyond what the provider has explicitly told us. Rationale:
585    // if the provider developer forgot to call `add_action` for an action,
586    // an AT (or even AccessKit itself) can fall back to simulating
587    // a mouse click. But if the provider doesn't handle an action request
588    // and we assume that it will based on the role, the attempted action
589    // does nothing. This stance is a departure from Chromium.
590
591    pub fn is_clickable(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
592        self.supports_action(Action::Click, parent_filter)
593    }
594
595    pub fn is_selectable(&self) -> bool {
596        // It's selectable if it has the attribute, whether it's true or false.
597        self.is_selected().is_some() && !self.is_disabled()
598    }
599
600    pub fn is_multiselectable(&self) -> bool {
601        self.data().is_multiselectable()
602    }
603
604    pub fn size_of_set_from_container(
605        &self,
606        filter: &impl Fn(&Node) -> FilterResult,
607    ) -> Option<usize> {
608        self.selection_container(filter)
609            .and_then(|c| c.size_of_set())
610    }
611
612    pub fn size_of_set(&self) -> Option<usize> {
613        // TODO: compute this if it is not provided (#9).
614        self.data().size_of_set()
615    }
616
617    pub fn position_in_set(&self) -> Option<usize> {
618        // TODO: compute this if it is not provided (#9).
619        self.data().position_in_set()
620    }
621
622    pub fn sort_direction(&self) -> Option<SortDirection> {
623        self.data().sort_direction()
624    }
625
626    pub fn supports_toggle(&self) -> bool {
627        self.toggled().is_some()
628    }
629
630    pub fn supports_expand_collapse(&self) -> bool {
631        self.data().is_expanded().is_some()
632    }
633
634    pub fn is_invocable(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
635        // A control is "invocable" if it initiates an action when activated but
636        // does not maintain any state. A control that maintains state
637        // when activated would be considered a toggle or expand-collapse
638        // control - these controls are "clickable" but not "invocable".
639        // Similarly, if the action only gives the control keyboard focus,
640        // such as when clicking a text input, the control is not considered
641        // "invocable", as the "invoke" action would be a redundant synonym
642        // for the "set focus" action. The same logic applies to selection.
643        self.is_clickable(parent_filter)
644            && !self.is_text_input()
645            && !matches!(self.role(), Role::Document | Role::Terminal)
646            && !self.supports_toggle()
647            && !self.supports_expand_collapse()
648            && self.is_selected().is_none()
649    }
650
651    pub fn supports_action(
652        &self,
653        action: Action,
654        parent_filter: &impl Fn(&Node) -> FilterResult,
655    ) -> bool {
656        if self.data().supports_action(action) {
657            return true;
658        }
659        if let Some(parent) = self.filtered_parent(parent_filter) {
660            return parent.data().child_supports_action(action);
661        }
662        false
663    }
664
665    pub fn supports_increment(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
666        self.supports_action(Action::Increment, parent_filter)
667    }
668
669    pub fn supports_decrement(&self, parent_filter: &impl Fn(&Node) -> FilterResult) -> bool {
670        self.supports_action(Action::Decrement, parent_filter)
671    }
672}
673
674fn descendant_label_filter(node: &Node) -> FilterResult {
675    match node.role() {
676        Role::Label | Role::Image => FilterResult::Include,
677        Role::GenericContainer => FilterResult::ExcludeNode,
678        _ => FilterResult::ExcludeSubtree,
679    }
680}
681
682impl<'a> Node<'a> {
683    pub fn labelled_by(
684        &self,
685    ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
686        let explicit = &self.state.data.labelled_by();
687        if explicit.is_empty()
688            && matches!(
689                self.role(),
690                Role::Button
691                    | Role::CheckBox
692                    | Role::DefaultButton
693                    | Role::Link
694                    | Role::MenuItem
695                    | Role::MenuItemCheckBox
696                    | Role::MenuItemRadio
697                    | Role::RadioButton
698            )
699        {
700            LabelledBy::FromDescendants(FilteredChildren::new(*self, &descendant_label_filter))
701        } else {
702            LabelledBy::Explicit {
703                ids: explicit.iter(),
704                tree_state: self.tree_state,
705                node_id: self.id,
706            }
707        }
708    }
709
710    pub fn label_comes_from_value(&self) -> bool {
711        self.role() == Role::Label
712    }
713
714    pub fn label(&self) -> Option<String> {
715        let mut result = String::new();
716        self.write_label(&mut result).unwrap().then_some(result)
717    }
718
719    fn write_label_direct<W: fmt::Write>(&self, mut writer: W) -> Result<bool, fmt::Error> {
720        if let Some(label) = &self.data().label() {
721            writer.write_str(label)?;
722            Ok(true)
723        } else {
724            Ok(false)
725        }
726    }
727
728    pub fn write_label<W: fmt::Write>(&self, mut writer: W) -> Result<bool, fmt::Error> {
729        if self.write_label_direct(&mut writer)? {
730            Ok(true)
731        } else {
732            let mut wrote_one = false;
733            for node in self.labelled_by() {
734                let writer = SpacePrefixingWriter {
735                    inner: &mut writer,
736                    need_prefix: wrote_one,
737                };
738                let wrote_this_time = if node.label_comes_from_value() {
739                    node.write_value(writer)
740                } else {
741                    node.write_label_direct(writer)
742                }?;
743                wrote_one = wrote_one || wrote_this_time;
744            }
745            Ok(wrote_one)
746        }
747    }
748
749    pub fn description(&self) -> Option<String> {
750        self.data()
751            .description()
752            .map(|description| description.to_string())
753    }
754
755    pub fn url(&self) -> Option<&str> {
756        self.data().url()
757    }
758
759    pub fn supports_url(&self) -> bool {
760        matches!(
761            self.role(),
762            Role::Link
763                | Role::DocBackLink
764                | Role::DocBiblioRef
765                | Role::DocGlossRef
766                | Role::DocNoteRef
767        ) && self.url().is_some()
768    }
769
770    fn is_empty_text_input(&self) -> bool {
771        let mut text_runs = self.text_runs();
772        if let Some(first_text_run) = text_runs.next() {
773            first_text_run
774                .data()
775                .value()
776                .is_none_or(|value| value.is_empty())
777                && text_runs.next().is_none()
778        } else {
779            true
780        }
781    }
782
783    pub fn placeholder(&self) -> Option<&str> {
784        self.data()
785            .placeholder()
786            .filter(|_| self.is_text_input() && self.is_empty_text_input())
787    }
788
789    pub fn value(&self) -> Option<String> {
790        let mut result = String::new();
791        self.write_value(&mut result).unwrap().then_some(result)
792    }
793
794    pub fn write_value<W: fmt::Write>(&self, mut writer: W) -> Result<bool, fmt::Error> {
795        if let Some(value) = &self.data().value() {
796            writer.write_str(value)?;
797            Ok(true)
798        } else if self.supports_text_ranges() && !self.is_multiline() {
799            self.document_range().write_text(writer)?;
800            Ok(true)
801        } else {
802            Ok(false)
803        }
804    }
805
806    pub fn has_value(&self) -> bool {
807        self.data().value().is_some() || (self.supports_text_ranges() && !self.is_multiline())
808    }
809
810    pub fn is_read_only_supported(&self) -> bool {
811        self.is_text_input()
812            || matches!(
813                self.role(),
814                Role::CheckBox
815                    | Role::ColorWell
816                    | Role::ComboBox
817                    | Role::Grid
818                    | Role::ListBox
819                    | Role::MenuItemCheckBox
820                    | Role::MenuItemRadio
821                    | Role::MenuListPopup
822                    | Role::RadioButton
823                    | Role::RadioGroup
824                    | Role::Slider
825                    | Role::Switch
826                    | Role::TreeGrid
827            )
828    }
829
830    pub fn should_have_read_only_state_by_default(&self) -> bool {
831        matches!(
832            self.role(),
833            Role::Article
834                | Role::Definition
835                | Role::DescriptionList
836                | Role::Document
837                | Role::GraphicsDocument
838                | Role::Image
839                | Role::List
840                | Role::ListItem
841                | Role::PdfRoot
842                | Role::ProgressIndicator
843                | Role::RootWebArea
844                | Role::Term
845                | Role::Timer
846                | Role::Toolbar
847                | Role::Tooltip
848        )
849    }
850
851    pub fn is_required(&self) -> bool {
852        self.data().is_required()
853    }
854
855    pub fn live(&self) -> Live {
856        self.data()
857            .live()
858            .unwrap_or_else(|| self.parent().map_or(Live::Off, |parent| parent.live()))
859    }
860
861    pub fn is_selected(&self) -> Option<bool> {
862        self.data().is_selected()
863    }
864
865    pub fn is_item_like(&self) -> bool {
866        matches!(
867            self.role(),
868            Role::Article
869                | Role::Comment
870                | Role::ListItem
871                | Role::MenuItem
872                | Role::MenuItemRadio
873                | Role::Tab
874                | Role::MenuItemCheckBox
875                | Role::TreeItem
876                | Role::ListBoxOption
877                | Role::MenuListOption
878                | Role::RadioButton
879                | Role::Term
880        )
881    }
882
883    pub fn is_container_with_selectable_children(&self) -> bool {
884        matches!(
885            self.role(),
886            Role::ComboBox
887                | Role::EditableComboBox
888                | Role::Grid
889                | Role::ListBox
890                | Role::ListGrid
891                | Role::Menu
892                | Role::MenuBar
893                | Role::MenuListPopup
894                | Role::RadioGroup
895                | Role::TabList
896                | Role::Toolbar
897                | Role::Tree
898                | Role::TreeGrid
899        )
900    }
901
902    pub fn controls(
903        &self,
904    ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
905        let state = self.tree_state;
906        let id = self.id;
907        let data = &self.state.data;
908        data.controls()
909            .iter()
910            .map(move |control_id| state.node_by_id(id.with_same_tree(*control_id)).unwrap())
911    }
912
913    pub fn active_descendant(&self) -> Option<Node<'a>> {
914        self.state
915            .data
916            .active_descendant()
917            .and_then(|id| self.tree_state.node_by_id(self.id.with_same_tree(id)))
918    }
919
920    pub fn raw_text_selection(&self) -> Option<&TextSelection> {
921        self.data().text_selection()
922    }
923
924    pub fn raw_value(&self) -> Option<&str> {
925        self.data().value()
926    }
927
928    pub fn author_id(&self) -> Option<&str> {
929        self.data().author_id()
930    }
931
932    pub fn class_name(&self) -> Option<&str> {
933        self.data().class_name()
934    }
935
936    pub fn index_path(&self) -> Vec<usize> {
937        self.relative_index_path(self.tree_state.root_id())
938    }
939
940    pub fn relative_index_path(&self, ancestor_id: NodeId) -> Vec<usize> {
941        let mut result = Vec::new();
942        let mut current = *self;
943        while current.id() != ancestor_id {
944            let (parent, index) = current.parent_and_index().unwrap();
945            result.push(index);
946            current = parent;
947        }
948        result.reverse();
949        result
950    }
951
952    pub(crate) fn first_filtered_child(
953        &self,
954        filter: &impl Fn(&Node) -> FilterResult,
955    ) -> Option<Node<'a>> {
956        for child in self.children() {
957            let result = filter(&child);
958            if result == FilterResult::Include {
959                return Some(child);
960            }
961            if result == FilterResult::ExcludeNode {
962                if let Some(descendant) = child.first_filtered_child(filter) {
963                    return Some(descendant);
964                }
965            }
966        }
967        None
968    }
969
970    pub(crate) fn last_filtered_child(
971        &self,
972        filter: &impl Fn(&Node) -> FilterResult,
973    ) -> Option<Node<'a>> {
974        for child in self.children().rev() {
975            let result = filter(&child);
976            if result == FilterResult::Include {
977                return Some(child);
978            }
979            if result == FilterResult::ExcludeNode {
980                if let Some(descendant) = child.last_filtered_child(filter) {
981                    return Some(descendant);
982                }
983            }
984        }
985        None
986    }
987
988    pub fn selection_container(&self, filter: &impl Fn(&Node) -> FilterResult) -> Option<Node<'a>> {
989        self.filtered_parent(&|parent| match filter(parent) {
990            FilterResult::Include if parent.is_container_with_selectable_children() => {
991                FilterResult::Include
992            }
993            FilterResult::Include => FilterResult::ExcludeNode,
994            filter_result => filter_result,
995        })
996    }
997
998    pub fn items(
999        &self,
1000        filter: impl Fn(&Node) -> FilterResult + 'a,
1001    ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
1002        self.filtered_children(move |child| match filter(child) {
1003            FilterResult::Include if child.is_item_like() => FilterResult::Include,
1004            FilterResult::Include => FilterResult::ExcludeNode,
1005            filter_result => filter_result,
1006        })
1007    }
1008}
1009
1010struct SpacePrefixingWriter<W: fmt::Write> {
1011    inner: W,
1012    need_prefix: bool,
1013}
1014
1015impl<W: fmt::Write> SpacePrefixingWriter<W> {
1016    fn write_prefix_if_needed(&mut self) -> fmt::Result {
1017        if self.need_prefix {
1018            self.inner.write_char(' ')?;
1019            self.need_prefix = false;
1020        }
1021        Ok(())
1022    }
1023}
1024
1025impl<W: fmt::Write> fmt::Write for SpacePrefixingWriter<W> {
1026    fn write_str(&mut self, s: &str) -> fmt::Result {
1027        self.write_prefix_if_needed()?;
1028        self.inner.write_str(s)
1029    }
1030
1031    fn write_char(&mut self, c: char) -> fmt::Result {
1032        self.write_prefix_if_needed()?;
1033        self.inner.write_char(c)
1034    }
1035}
1036
1037#[cfg(test)]
1038mod tests {
1039    use accesskit::{
1040        Action, Node, NodeId, Point, Rect, Role, TextDirection, TextPosition, TextSelection, Tree,
1041        TreeId, TreeUpdate,
1042    };
1043    use alloc::vec;
1044
1045    use crate::tests::*;
1046
1047    #[test]
1048    fn parent_and_index() {
1049        let tree = test_tree();
1050        assert!(tree.state().root().parent_and_index().is_none());
1051        assert_eq!(
1052            Some((ROOT_ID, 0)),
1053            tree.state()
1054                .node_by_id(nid(PARAGRAPH_0_ID))
1055                .unwrap()
1056                .parent_and_index()
1057                .map(|(parent, index)| (parent.id().to_components().0, index))
1058        );
1059        assert_eq!(
1060            Some((PARAGRAPH_0_ID, 0)),
1061            tree.state()
1062                .node_by_id(nid(LABEL_0_0_IGNORED_ID))
1063                .unwrap()
1064                .parent_and_index()
1065                .map(|(parent, index)| (parent.id().to_components().0, index))
1066        );
1067        assert_eq!(
1068            Some((ROOT_ID, 1)),
1069            tree.state()
1070                .node_by_id(nid(PARAGRAPH_1_IGNORED_ID))
1071                .unwrap()
1072                .parent_and_index()
1073                .map(|(parent, index)| (parent.id().to_components().0, index))
1074        );
1075    }
1076
1077    #[test]
1078    fn deepest_first_child() {
1079        let tree = test_tree();
1080        assert_eq!(
1081            LABEL_0_0_IGNORED_ID,
1082            tree.state()
1083                .root()
1084                .deepest_first_child()
1085                .unwrap()
1086                .id()
1087                .to_components()
1088                .0
1089        );
1090        assert_eq!(
1091            LABEL_0_0_IGNORED_ID,
1092            tree.state()
1093                .node_by_id(nid(PARAGRAPH_0_ID))
1094                .unwrap()
1095                .deepest_first_child()
1096                .unwrap()
1097                .id()
1098                .to_components()
1099                .0
1100        );
1101        assert!(tree
1102            .state()
1103            .node_by_id(nid(LABEL_0_0_IGNORED_ID))
1104            .unwrap()
1105            .deepest_first_child()
1106            .is_none());
1107    }
1108
1109    #[test]
1110    fn filtered_parent() {
1111        let tree = test_tree();
1112        assert_eq!(
1113            ROOT_ID,
1114            tree.state()
1115                .node_by_id(nid(LABEL_1_1_ID))
1116                .unwrap()
1117                .filtered_parent(&test_tree_filter)
1118                .unwrap()
1119                .id()
1120                .to_components()
1121                .0
1122        );
1123        assert!(tree
1124            .state()
1125            .root()
1126            .filtered_parent(&test_tree_filter)
1127            .is_none());
1128    }
1129
1130    #[test]
1131    fn deepest_first_filtered_child() {
1132        let tree = test_tree();
1133        assert_eq!(
1134            PARAGRAPH_0_ID,
1135            tree.state()
1136                .root()
1137                .deepest_first_filtered_child(&test_tree_filter)
1138                .unwrap()
1139                .id()
1140                .to_components()
1141                .0
1142        );
1143        assert!(tree
1144            .state()
1145            .node_by_id(nid(PARAGRAPH_0_ID))
1146            .unwrap()
1147            .deepest_first_filtered_child(&test_tree_filter)
1148            .is_none());
1149        assert!(tree
1150            .state()
1151            .node_by_id(nid(LABEL_0_0_IGNORED_ID))
1152            .unwrap()
1153            .deepest_first_filtered_child(&test_tree_filter)
1154            .is_none());
1155    }
1156
1157    #[test]
1158    fn deepest_last_child() {
1159        let tree = test_tree();
1160        assert_eq!(
1161            EMPTY_CONTAINER_3_3_IGNORED_ID,
1162            tree.state()
1163                .root()
1164                .deepest_last_child()
1165                .unwrap()
1166                .id()
1167                .to_components()
1168                .0
1169        );
1170        assert_eq!(
1171            EMPTY_CONTAINER_3_3_IGNORED_ID,
1172            tree.state()
1173                .node_by_id(nid(PARAGRAPH_3_IGNORED_ID))
1174                .unwrap()
1175                .deepest_last_child()
1176                .unwrap()
1177                .id()
1178                .to_components()
1179                .0
1180        );
1181        assert!(tree
1182            .state()
1183            .node_by_id(nid(BUTTON_3_2_ID))
1184            .unwrap()
1185            .deepest_last_child()
1186            .is_none());
1187    }
1188
1189    #[test]
1190    fn deepest_last_filtered_child() {
1191        let tree = test_tree();
1192        assert_eq!(
1193            BUTTON_3_2_ID,
1194            tree.state()
1195                .root()
1196                .deepest_last_filtered_child(&test_tree_filter)
1197                .unwrap()
1198                .id()
1199                .to_components()
1200                .0
1201        );
1202        assert_eq!(
1203            BUTTON_3_2_ID,
1204            tree.state()
1205                .node_by_id(nid(PARAGRAPH_3_IGNORED_ID))
1206                .unwrap()
1207                .deepest_last_filtered_child(&test_tree_filter)
1208                .unwrap()
1209                .id()
1210                .to_components()
1211                .0
1212        );
1213        assert!(tree
1214            .state()
1215            .node_by_id(nid(BUTTON_3_2_ID))
1216            .unwrap()
1217            .deepest_last_filtered_child(&test_tree_filter)
1218            .is_none());
1219        assert!(tree
1220            .state()
1221            .node_by_id(nid(PARAGRAPH_0_ID))
1222            .unwrap()
1223            .deepest_last_filtered_child(&test_tree_filter)
1224            .is_none());
1225    }
1226
1227    #[test]
1228    fn is_descendant_of() {
1229        let tree = test_tree();
1230        assert!(tree
1231            .state()
1232            .node_by_id(nid(PARAGRAPH_0_ID))
1233            .unwrap()
1234            .is_descendant_of(&tree.state().root()));
1235        assert!(tree
1236            .state()
1237            .node_by_id(nid(LABEL_0_0_IGNORED_ID))
1238            .unwrap()
1239            .is_descendant_of(&tree.state().root()));
1240        assert!(tree
1241            .state()
1242            .node_by_id(nid(LABEL_0_0_IGNORED_ID))
1243            .unwrap()
1244            .is_descendant_of(&tree.state().node_by_id(nid(PARAGRAPH_0_ID)).unwrap()));
1245        assert!(!tree
1246            .state()
1247            .node_by_id(nid(LABEL_0_0_IGNORED_ID))
1248            .unwrap()
1249            .is_descendant_of(&tree.state().node_by_id(nid(PARAGRAPH_2_ID)).unwrap()));
1250        assert!(!tree
1251            .state()
1252            .node_by_id(nid(PARAGRAPH_0_ID))
1253            .unwrap()
1254            .is_descendant_of(&tree.state().node_by_id(nid(PARAGRAPH_2_ID)).unwrap()));
1255    }
1256
1257    #[test]
1258    fn is_root() {
1259        let tree = test_tree();
1260        assert!(tree.state().node_by_id(nid(ROOT_ID)).unwrap().is_root());
1261        assert!(!tree
1262            .state()
1263            .node_by_id(nid(PARAGRAPH_0_ID))
1264            .unwrap()
1265            .is_root());
1266    }
1267
1268    #[test]
1269    fn bounding_box() {
1270        let tree = test_tree();
1271        assert!(tree
1272            .state()
1273            .node_by_id(nid(ROOT_ID))
1274            .unwrap()
1275            .bounding_box()
1276            .is_none());
1277        assert_eq!(
1278            Some(Rect {
1279                x0: 10.0,
1280                y0: 40.0,
1281                x1: 810.0,
1282                y1: 80.0,
1283            }),
1284            tree.state()
1285                .node_by_id(nid(PARAGRAPH_1_IGNORED_ID))
1286                .unwrap()
1287                .bounding_box()
1288        );
1289        assert_eq!(
1290            Some(Rect {
1291                x0: 20.0,
1292                y0: 50.0,
1293                x1: 100.0,
1294                y1: 70.0,
1295            }),
1296            tree.state()
1297                .node_by_id(nid(LABEL_1_1_ID))
1298                .unwrap()
1299                .bounding_box()
1300        );
1301    }
1302
1303    #[test]
1304    fn node_at_point() {
1305        let tree = test_tree();
1306        assert!(tree
1307            .state()
1308            .root()
1309            .node_at_point(Point::new(10.0, 40.0), &test_tree_filter)
1310            .is_none());
1311        assert_eq!(
1312            Some(nid(LABEL_1_1_ID)),
1313            tree.state()
1314                .root()
1315                .node_at_point(Point::new(20.0, 50.0), &test_tree_filter)
1316                .map(|node| node.id())
1317        );
1318        assert_eq!(
1319            Some(nid(LABEL_1_1_ID)),
1320            tree.state()
1321                .root()
1322                .node_at_point(Point::new(50.0, 60.0), &test_tree_filter)
1323                .map(|node| node.id())
1324        );
1325        assert!(tree
1326            .state()
1327            .root()
1328            .node_at_point(Point::new(100.0, 70.0), &test_tree_filter)
1329            .is_none());
1330    }
1331
1332    #[test]
1333    fn no_label_or_labelled_by() {
1334        let update = TreeUpdate {
1335            nodes: vec![
1336                (NodeId(0), {
1337                    let mut node = Node::new(Role::Window);
1338                    node.set_children(vec![NodeId(1)]);
1339                    node
1340                }),
1341                (NodeId(1), Node::new(Role::Button)),
1342            ],
1343            tree: Some(Tree::new(NodeId(0))),
1344            tree_id: TreeId::ROOT,
1345            focus: NodeId(0),
1346        };
1347        let tree = crate::Tree::new(update, false);
1348        assert_eq!(
1349            None,
1350            tree.state().node_by_id(nid(NodeId(1))).unwrap().label()
1351        );
1352    }
1353
1354    #[test]
1355    fn label_from_labelled_by() {
1356        // The following mock UI probably isn't very localization-friendly,
1357        // but it's good for this test.
1358        const LABEL_1: &str = "Check email every";
1359        const LABEL_2: &str = "minutes";
1360
1361        let update = TreeUpdate {
1362            nodes: vec![
1363                (NodeId(0), {
1364                    let mut node = Node::new(Role::Window);
1365                    node.set_children(vec![NodeId(1), NodeId(2), NodeId(3), NodeId(4)]);
1366                    node
1367                }),
1368                (NodeId(1), {
1369                    let mut node = Node::new(Role::CheckBox);
1370                    node.set_labelled_by(vec![NodeId(2), NodeId(4)]);
1371                    node
1372                }),
1373                (NodeId(2), {
1374                    let mut node = Node::new(Role::Label);
1375                    node.set_value(LABEL_1);
1376                    node
1377                }),
1378                (NodeId(3), {
1379                    let mut node = Node::new(Role::TextInput);
1380                    node.push_labelled_by(NodeId(4));
1381                    node
1382                }),
1383                (NodeId(4), {
1384                    let mut node = Node::new(Role::Label);
1385                    node.set_value(LABEL_2);
1386                    node
1387                }),
1388            ],
1389            tree: Some(Tree::new(NodeId(0))),
1390            tree_id: TreeId::ROOT,
1391            focus: NodeId(0),
1392        };
1393        let tree = crate::Tree::new(update, false);
1394        assert_eq!(
1395            Some([LABEL_1, LABEL_2].join(" ")),
1396            tree.state().node_by_id(nid(NodeId(1))).unwrap().label()
1397        );
1398        assert_eq!(
1399            Some(LABEL_2.into()),
1400            tree.state().node_by_id(nid(NodeId(3))).unwrap().label()
1401        );
1402    }
1403
1404    #[test]
1405    fn label_from_descendant_label() {
1406        const ROOT_ID: NodeId = NodeId(0);
1407        const DEFAULT_BUTTON_ID: NodeId = NodeId(1);
1408        const DEFAULT_BUTTON_LABEL_ID: NodeId = NodeId(2);
1409        const LINK_ID: NodeId = NodeId(3);
1410        const LINK_LABEL_CONTAINER_ID: NodeId = NodeId(4);
1411        const LINK_LABEL_ID: NodeId = NodeId(5);
1412        const CHECKBOX_ID: NodeId = NodeId(6);
1413        const CHECKBOX_LABEL_ID: NodeId = NodeId(7);
1414        const RADIO_BUTTON_ID: NodeId = NodeId(8);
1415        const RADIO_BUTTON_LABEL_ID: NodeId = NodeId(9);
1416        const MENU_BUTTON_ID: NodeId = NodeId(10);
1417        const MENU_BUTTON_LABEL_ID: NodeId = NodeId(11);
1418        const MENU_ID: NodeId = NodeId(12);
1419        const MENU_ITEM_ID: NodeId = NodeId(13);
1420        const MENU_ITEM_LABEL_ID: NodeId = NodeId(14);
1421        const MENU_ITEM_CHECKBOX_ID: NodeId = NodeId(15);
1422        const MENU_ITEM_CHECKBOX_LABEL_ID: NodeId = NodeId(16);
1423        const MENU_ITEM_RADIO_ID: NodeId = NodeId(17);
1424        const MENU_ITEM_RADIO_LABEL_ID: NodeId = NodeId(18);
1425
1426        const DEFAULT_BUTTON_LABEL: &str = "Play";
1427        const LINK_LABEL: &str = "Watch in browser";
1428        const CHECKBOX_LABEL: &str = "Resume from previous position";
1429        const RADIO_BUTTON_LABEL: &str = "Normal speed";
1430        const MENU_BUTTON_LABEL: &str = "More";
1431        const MENU_ITEM_LABEL: &str = "Share";
1432        const MENU_ITEM_CHECKBOX_LABEL: &str = "Apply volume processing";
1433        const MENU_ITEM_RADIO_LABEL: &str = "Maximize loudness for noisy environment";
1434
1435        let update = TreeUpdate {
1436            nodes: vec![
1437                (ROOT_ID, {
1438                    let mut node = Node::new(Role::Window);
1439                    node.set_children(vec![
1440                        DEFAULT_BUTTON_ID,
1441                        LINK_ID,
1442                        CHECKBOX_ID,
1443                        RADIO_BUTTON_ID,
1444                        MENU_BUTTON_ID,
1445                        MENU_ID,
1446                    ]);
1447                    node
1448                }),
1449                (DEFAULT_BUTTON_ID, {
1450                    let mut node = Node::new(Role::DefaultButton);
1451                    node.push_child(DEFAULT_BUTTON_LABEL_ID);
1452                    node
1453                }),
1454                (DEFAULT_BUTTON_LABEL_ID, {
1455                    let mut node = Node::new(Role::Image);
1456                    node.set_label(DEFAULT_BUTTON_LABEL);
1457                    node
1458                }),
1459                (LINK_ID, {
1460                    let mut node = Node::new(Role::Link);
1461                    node.push_child(LINK_LABEL_CONTAINER_ID);
1462                    node
1463                }),
1464                (LINK_LABEL_CONTAINER_ID, {
1465                    let mut node = Node::new(Role::GenericContainer);
1466                    node.push_child(LINK_LABEL_ID);
1467                    node
1468                }),
1469                (LINK_LABEL_ID, {
1470                    let mut node = Node::new(Role::Label);
1471                    node.set_value(LINK_LABEL);
1472                    node
1473                }),
1474                (CHECKBOX_ID, {
1475                    let mut node = Node::new(Role::CheckBox);
1476                    node.push_child(CHECKBOX_LABEL_ID);
1477                    node
1478                }),
1479                (CHECKBOX_LABEL_ID, {
1480                    let mut node = Node::new(Role::Label);
1481                    node.set_value(CHECKBOX_LABEL);
1482                    node
1483                }),
1484                (RADIO_BUTTON_ID, {
1485                    let mut node = Node::new(Role::RadioButton);
1486                    node.push_child(RADIO_BUTTON_LABEL_ID);
1487                    node
1488                }),
1489                (RADIO_BUTTON_LABEL_ID, {
1490                    let mut node = Node::new(Role::Label);
1491                    node.set_value(RADIO_BUTTON_LABEL);
1492                    node
1493                }),
1494                (MENU_BUTTON_ID, {
1495                    let mut node = Node::new(Role::Button);
1496                    node.push_child(MENU_BUTTON_LABEL_ID);
1497                    node
1498                }),
1499                (MENU_BUTTON_LABEL_ID, {
1500                    let mut node = Node::new(Role::Label);
1501                    node.set_value(MENU_BUTTON_LABEL);
1502                    node
1503                }),
1504                (MENU_ID, {
1505                    let mut node = Node::new(Role::Menu);
1506                    node.set_children([MENU_ITEM_ID, MENU_ITEM_CHECKBOX_ID, MENU_ITEM_RADIO_ID]);
1507                    node
1508                }),
1509                (MENU_ITEM_ID, {
1510                    let mut node = Node::new(Role::MenuItem);
1511                    node.push_child(MENU_ITEM_LABEL_ID);
1512                    node
1513                }),
1514                (MENU_ITEM_LABEL_ID, {
1515                    let mut node = Node::new(Role::Label);
1516                    node.set_value(MENU_ITEM_LABEL);
1517                    node
1518                }),
1519                (MENU_ITEM_CHECKBOX_ID, {
1520                    let mut node = Node::new(Role::MenuItemCheckBox);
1521                    node.push_child(MENU_ITEM_CHECKBOX_LABEL_ID);
1522                    node
1523                }),
1524                (MENU_ITEM_CHECKBOX_LABEL_ID, {
1525                    let mut node = Node::new(Role::Label);
1526                    node.set_value(MENU_ITEM_CHECKBOX_LABEL);
1527                    node
1528                }),
1529                (MENU_ITEM_RADIO_ID, {
1530                    let mut node = Node::new(Role::MenuItemRadio);
1531                    node.push_child(MENU_ITEM_RADIO_LABEL_ID);
1532                    node
1533                }),
1534                (MENU_ITEM_RADIO_LABEL_ID, {
1535                    let mut node = Node::new(Role::Label);
1536                    node.set_value(MENU_ITEM_RADIO_LABEL);
1537                    node
1538                }),
1539            ],
1540            tree: Some(Tree::new(ROOT_ID)),
1541            tree_id: TreeId::ROOT,
1542            focus: ROOT_ID,
1543        };
1544        let tree = crate::Tree::new(update, false);
1545        assert_eq!(
1546            Some(DEFAULT_BUTTON_LABEL.into()),
1547            tree.state()
1548                .node_by_id(nid(DEFAULT_BUTTON_ID))
1549                .unwrap()
1550                .label()
1551        );
1552        assert_eq!(
1553            Some(LINK_LABEL.into()),
1554            tree.state().node_by_id(nid(LINK_ID)).unwrap().label()
1555        );
1556        assert_eq!(
1557            Some(CHECKBOX_LABEL.into()),
1558            tree.state().node_by_id(nid(CHECKBOX_ID)).unwrap().label()
1559        );
1560        assert_eq!(
1561            Some(RADIO_BUTTON_LABEL.into()),
1562            tree.state()
1563                .node_by_id(nid(RADIO_BUTTON_ID))
1564                .unwrap()
1565                .label()
1566        );
1567        assert_eq!(
1568            Some(MENU_BUTTON_LABEL.into()),
1569            tree.state()
1570                .node_by_id(nid(MENU_BUTTON_ID))
1571                .unwrap()
1572                .label()
1573        );
1574        assert_eq!(
1575            Some(MENU_ITEM_LABEL.into()),
1576            tree.state().node_by_id(nid(MENU_ITEM_ID)).unwrap().label()
1577        );
1578        assert_eq!(
1579            Some(MENU_ITEM_CHECKBOX_LABEL.into()),
1580            tree.state()
1581                .node_by_id(nid(MENU_ITEM_CHECKBOX_ID))
1582                .unwrap()
1583                .label()
1584        );
1585        assert_eq!(
1586            Some(MENU_ITEM_RADIO_LABEL.into()),
1587            tree.state()
1588                .node_by_id(nid(MENU_ITEM_RADIO_ID))
1589                .unwrap()
1590                .label()
1591        );
1592    }
1593
1594    #[test]
1595    fn placeholder_should_be_exposed_on_empty_text_input() {
1596        const ROOT_ID: NodeId = NodeId(0);
1597        const TEXT_INPUT_ID: NodeId = NodeId(1);
1598        const TEXT_RUN_ID: NodeId = NodeId(2);
1599
1600        const PLACEHOLDER: &str = "John Doe";
1601
1602        let update = TreeUpdate {
1603            nodes: vec![
1604                (ROOT_ID, {
1605                    let mut node = Node::new(Role::Window);
1606                    node.set_children(vec![TEXT_INPUT_ID]);
1607                    node
1608                }),
1609                (TEXT_INPUT_ID, {
1610                    let mut node = Node::new(Role::MultilineTextInput);
1611                    node.set_bounds(Rect {
1612                        x0: 8.0,
1613                        y0: 8.0,
1614                        x1: 296.0,
1615                        y1: 69.5,
1616                    });
1617                    node.push_child(TEXT_RUN_ID);
1618                    node.set_placeholder(PLACEHOLDER);
1619                    node.set_text_selection(TextSelection {
1620                        anchor: TextPosition {
1621                            node: TEXT_RUN_ID,
1622                            character_index: 0,
1623                        },
1624                        focus: TextPosition {
1625                            node: TEXT_RUN_ID,
1626                            character_index: 0,
1627                        },
1628                    });
1629                    node.add_action(Action::Focus);
1630                    node
1631                }),
1632                (TEXT_RUN_ID, {
1633                    let mut node = Node::new(Role::TextRun);
1634                    node.set_bounds(Rect {
1635                        x0: 12.0,
1636                        y0: 10.0,
1637                        x1: 12.0,
1638                        y1: 24.0,
1639                    });
1640                    node.set_value("");
1641                    node.set_character_lengths([]);
1642                    node.set_character_positions([]);
1643                    node.set_character_widths([]);
1644                    node.set_text_direction(TextDirection::LeftToRight);
1645                    node
1646                }),
1647            ],
1648            tree: Some(Tree::new(ROOT_ID)),
1649            tree_id: TreeId::ROOT,
1650            focus: TEXT_INPUT_ID,
1651        };
1652        let tree = crate::Tree::new(update, false);
1653        assert_eq!(
1654            Some(PLACEHOLDER),
1655            tree.state()
1656                .node_by_id(nid(TEXT_INPUT_ID))
1657                .unwrap()
1658                .placeholder()
1659        );
1660    }
1661
1662    #[test]
1663    fn placeholder_should_be_ignored_on_non_empty_text_input() {
1664        const ROOT_ID: NodeId = NodeId(0);
1665        const TEXT_INPUT_ID: NodeId = NodeId(1);
1666        const TEXT_RUN_ID: NodeId = NodeId(2);
1667
1668        const PLACEHOLDER: &str = "John Doe";
1669
1670        let update = TreeUpdate {
1671            nodes: vec![
1672                (ROOT_ID, {
1673                    let mut node = Node::new(Role::Window);
1674                    node.set_children(vec![TEXT_INPUT_ID]);
1675                    node
1676                }),
1677                (TEXT_INPUT_ID, {
1678                    let mut node = Node::new(Role::MultilineTextInput);
1679                    node.set_bounds(Rect {
1680                        x0: 8.0,
1681                        y0: 8.0,
1682                        x1: 296.0,
1683                        y1: 69.5,
1684                    });
1685                    node.push_child(TEXT_RUN_ID);
1686                    node.set_placeholder(PLACEHOLDER);
1687                    node.set_text_selection(TextSelection {
1688                        anchor: TextPosition {
1689                            node: TEXT_RUN_ID,
1690                            character_index: 1,
1691                        },
1692                        focus: TextPosition {
1693                            node: TEXT_RUN_ID,
1694                            character_index: 1,
1695                        },
1696                    });
1697                    node.add_action(Action::Focus);
1698                    node
1699                }),
1700                (TEXT_RUN_ID, {
1701                    let mut node = Node::new(Role::TextRun);
1702                    node.set_bounds(Rect {
1703                        x0: 12.0,
1704                        y0: 10.0,
1705                        x1: 20.0,
1706                        y1: 24.0,
1707                    });
1708                    node.set_value("A");
1709                    node.set_character_lengths([1]);
1710                    node.set_character_positions([0.0]);
1711                    node.set_character_widths([8.0]);
1712                    node.set_word_starts([0]);
1713                    node.set_text_direction(TextDirection::LeftToRight);
1714                    node
1715                }),
1716            ],
1717            tree: Some(Tree::new(ROOT_ID)),
1718            tree_id: TreeId::ROOT,
1719            focus: TEXT_INPUT_ID,
1720        };
1721        let tree = crate::Tree::new(update, false);
1722        assert_eq!(
1723            None,
1724            tree.state()
1725                .node_by_id(nid(TEXT_INPUT_ID))
1726                .unwrap()
1727                .placeholder()
1728        );
1729    }
1730
1731    #[test]
1732    fn hidden_flag_should_be_inherited() {
1733        const ROOT_ID: NodeId = NodeId(0);
1734        const CONTAINER_ID: NodeId = NodeId(1);
1735        const LEAF_ID: NodeId = NodeId(2);
1736
1737        let update = TreeUpdate {
1738            nodes: vec![
1739                (ROOT_ID, {
1740                    let mut node = Node::new(Role::Window);
1741                    node.set_children(vec![CONTAINER_ID]);
1742                    node
1743                }),
1744                (CONTAINER_ID, {
1745                    let mut node = Node::new(Role::GenericContainer);
1746                    node.set_hidden();
1747                    node.push_child(LEAF_ID);
1748                    node
1749                }),
1750                (LEAF_ID, {
1751                    let mut node = Node::new(Role::Button);
1752                    node.set_label("OK");
1753                    node
1754                }),
1755            ],
1756            tree: Some(Tree::new(ROOT_ID)),
1757            tree_id: TreeId::ROOT,
1758            focus: ROOT_ID,
1759        };
1760        let tree = crate::Tree::new(update, false);
1761        assert!(tree.state().node_by_id(nid(LEAF_ID)).unwrap().is_hidden());
1762    }
1763
1764    mod node_id {
1765        use super::NodeId as LocalNodeId;
1766        use crate::node::NodeId;
1767        use crate::tree::TreeIndex;
1768
1769        #[test]
1770        fn new_and_to_components_round_trip() {
1771            let node_id = LocalNodeId(42);
1772            let tree_index = TreeIndex(7);
1773            let id = NodeId::new(node_id, tree_index);
1774            let (extracted_node_id, extracted_tree_index) = id.to_components();
1775            assert_eq!(node_id, extracted_node_id);
1776            assert_eq!(tree_index, extracted_tree_index);
1777        }
1778
1779        #[test]
1780        fn with_same_tree_preserves_tree_index() {
1781            let original_node_id = LocalNodeId(100);
1782            let tree_index = TreeIndex(5);
1783            let id = NodeId::new(original_node_id, tree_index);
1784
1785            let new_node_id = LocalNodeId(200);
1786            let new_id = id.with_same_tree(new_node_id);
1787
1788            let (extracted_node_id, extracted_tree_index) = new_id.to_components();
1789            assert_eq!(new_node_id, extracted_node_id);
1790            assert_eq!(tree_index, extracted_tree_index);
1791        }
1792
1793        #[test]
1794        fn into_u128() {
1795            let node_id = LocalNodeId(12345);
1796            let tree_index = TreeIndex(67);
1797            let id = NodeId::new(node_id, tree_index);
1798            let (extracted_node_id, extracted_tree_index) = id.to_components();
1799            assert_eq!(node_id, extracted_node_id);
1800            assert_eq!(tree_index, extracted_tree_index);
1801        }
1802
1803        #[test]
1804        fn equality() {
1805            let id1 = NodeId::new(LocalNodeId(1), TreeIndex(2));
1806            let id2 = NodeId::new(LocalNodeId(1), TreeIndex(2));
1807            let id3 = NodeId::new(LocalNodeId(1), TreeIndex(3));
1808            let id4 = NodeId::new(LocalNodeId(2), TreeIndex(2));
1809
1810            assert_eq!(id1, id2);
1811            assert_ne!(id1, id3);
1812            assert_ne!(id1, id4);
1813        }
1814    }
1815
1816    #[test]
1817    fn is_focused_when_node_has_focus() {
1818        const ROOT_ID: NodeId = NodeId(0);
1819        const BUTTON_ID: NodeId = NodeId(1);
1820
1821        let update = TreeUpdate {
1822            nodes: vec![
1823                (ROOT_ID, {
1824                    let mut node = Node::new(Role::Window);
1825                    node.set_children(vec![BUTTON_ID]);
1826                    node
1827                }),
1828                (BUTTON_ID, Node::new(Role::Button)),
1829            ],
1830            tree: Some(Tree::new(ROOT_ID)),
1831            tree_id: TreeId::ROOT,
1832            focus: BUTTON_ID,
1833        };
1834        let tree = crate::Tree::new(update, true);
1835        assert!(tree
1836            .state()
1837            .node_by_id(nid(BUTTON_ID))
1838            .unwrap()
1839            .is_focused());
1840    }
1841
1842    #[test]
1843    fn is_focused_when_node_does_not_have_focus() {
1844        const ROOT_ID: NodeId = NodeId(0);
1845        const BUTTON_ID: NodeId = NodeId(1);
1846
1847        let update = TreeUpdate {
1848            nodes: vec![
1849                (ROOT_ID, {
1850                    let mut node = Node::new(Role::Window);
1851                    node.set_children(vec![BUTTON_ID]);
1852                    node
1853                }),
1854                (BUTTON_ID, Node::new(Role::Button)),
1855            ],
1856            tree: Some(Tree::new(ROOT_ID)),
1857            tree_id: TreeId::ROOT,
1858            focus: ROOT_ID,
1859        };
1860        let tree = crate::Tree::new(update, true);
1861        assert!(!tree
1862            .state()
1863            .node_by_id(nid(BUTTON_ID))
1864            .unwrap()
1865            .is_focused());
1866    }
1867
1868    #[test]
1869    fn is_focused_active_descendant_is_focused() {
1870        const ROOT_ID: NodeId = NodeId(0);
1871        const LISTBOX_ID: NodeId = NodeId(1);
1872        const ITEM_ID: NodeId = NodeId(2);
1873
1874        let update = TreeUpdate {
1875            nodes: vec![
1876                (ROOT_ID, {
1877                    let mut node = Node::new(Role::Window);
1878                    node.set_children(vec![LISTBOX_ID]);
1879                    node
1880                }),
1881                (LISTBOX_ID, {
1882                    let mut node = Node::new(Role::ListBox);
1883                    node.set_children(vec![ITEM_ID]);
1884                    node.set_active_descendant(ITEM_ID);
1885                    node
1886                }),
1887                (ITEM_ID, Node::new(Role::ListBoxOption)),
1888            ],
1889            tree: Some(Tree::new(ROOT_ID)),
1890            tree_id: TreeId::ROOT,
1891            focus: LISTBOX_ID,
1892        };
1893        let tree = crate::Tree::new(update, true);
1894        assert!(tree.state().node_by_id(nid(ITEM_ID)).unwrap().is_focused());
1895    }
1896
1897    #[test]
1898    fn is_focused_node_with_active_descendant_is_not_focused() {
1899        const ROOT_ID: NodeId = NodeId(0);
1900        const LISTBOX_ID: NodeId = NodeId(1);
1901        const ITEM_ID: NodeId = NodeId(2);
1902
1903        let update = TreeUpdate {
1904            nodes: vec![
1905                (ROOT_ID, {
1906                    let mut node = Node::new(Role::Window);
1907                    node.set_children(vec![LISTBOX_ID]);
1908                    node
1909                }),
1910                (LISTBOX_ID, {
1911                    let mut node = Node::new(Role::ListBox);
1912                    node.set_children(vec![ITEM_ID]);
1913                    node.set_active_descendant(ITEM_ID);
1914                    node
1915                }),
1916                (ITEM_ID, Node::new(Role::ListBoxOption)),
1917            ],
1918            tree: Some(Tree::new(ROOT_ID)),
1919            tree_id: TreeId::ROOT,
1920            focus: LISTBOX_ID,
1921        };
1922        let tree = crate::Tree::new(update, true);
1923        assert!(!tree
1924            .state()
1925            .node_by_id(nid(LISTBOX_ID))
1926            .unwrap()
1927            .is_focused());
1928    }
1929}