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, 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        // Don't check for absence of a parent node, in case a non-root node
63        // somehow gets detached from the tree.
64        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    /// Returns the transform defined directly on this node, or the identity
231    /// transform, without taking into account transforms on ancestors.
232    pub fn direct_transform(&self) -> Affine {
233        self.data()
234            .transform()
235            .map_or(Affine::IDENTITY, |value| *value)
236    }
237
238    /// Returns the combined affine transform of this node and its ancestors,
239    /// up to and including the root of this node's tree.
240    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    /// Returns the node's transformed bounding box relative to the tree's
268    /// container (e.g. window).
269    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    /// Returns the deepest filtered node, either this node or a descendant,
311    /// at the given point in this node's coordinate space.
312    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    // When probing for supported actions as the next several functions do,
461    // it's tempting to check the role. But it's better to not assume anything
462    // beyond what the provider has explicitly told us. Rationale:
463    // if the provider developer forgot to call `add_action` for an action,
464    // an AT (or even AccessKit itself) can fall back to simulating
465    // a mouse click. But if the provider doesn't handle an action request
466    // and we assume that it will based on the role, the attempted action
467    // does nothing. This stance is a departure from Chromium.
468
469    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        // It's selectable if it has the attribute, whether it's true or false.
475        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        // TODO: compute this if it is not provided (#9).
492        self.data().size_of_set()
493    }
494
495    pub fn position_in_set(&self) -> Option<usize> {
496        // TODO: compute this if it is not provided (#9).
497        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        // A control is "invocable" if it initiates an action when activated but
510        // does not maintain any state. A control that maintains state
511        // when activated would be considered a toggle or expand-collapse
512        // control - these controls are "clickable" but not "invocable".
513        // Similarly, if the action only gives the control keyboard focus,
514        // such as when clicking a text input, the control is not considered
515        // "invocable", as the "invoke" action would be a redundant synonym
516        // for the "set focus" action. The same logic applies to selection.
517        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        // The following mock UI probably isn't very localization-friendly,
1178        // but it's good for this test.
1179        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}