accesskit/
lib.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 2018 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
11#![cfg_attr(not(any(feature = "pyo3", feature = "schemars")), no_std)]
12
13extern crate alloc;
14
15use alloc::{boxed::Box, string::String, vec::Vec};
16use core::fmt;
17#[cfg(feature = "pyo3")]
18use pyo3::pyclass;
19#[cfg(feature = "schemars")]
20use schemars::{
21    gen::SchemaGenerator,
22    schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
23    JsonSchema, Map as SchemaMap,
24};
25#[cfg(feature = "serde")]
26use serde::{
27    de::{Deserializer, IgnoredAny, MapAccess, Visitor},
28    ser::{SerializeMap, Serializer},
29    Deserialize, Serialize,
30};
31
32mod geometry;
33pub use geometry::{Affine, Point, Rect, Size, Vec2};
34
35/// The type of an accessibility node.
36///
37/// The majority of these roles come from the ARIA specification. Reference
38/// the latest draft for proper usage.
39///
40/// Like the AccessKit schema as a whole, this list is largely taken
41/// from Chromium. However, unlike Chromium's alphabetized list, this list
42/// is ordered roughly by expected usage frequency (with the notable exception
43/// of [`Role::Unknown`]). This is more efficient in serialization formats
44/// where integers use a variable-length encoding.
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
46#[cfg_attr(feature = "enumn", derive(enumn::N))]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48#[cfg_attr(feature = "schemars", derive(JsonSchema))]
49#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
50#[cfg_attr(
51    feature = "pyo3",
52    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
53)]
54#[repr(u8)]
55pub enum Role {
56    #[default]
57    Unknown,
58    TextRun,
59    Cell,
60    Label,
61    Image,
62    Link,
63    Row,
64    ListItem,
65
66    /// Contains the bullet, number, or other marker for a list item.
67    ListMarker,
68
69    TreeItem,
70    ListBoxOption,
71    MenuItem,
72    MenuListOption,
73    Paragraph,
74
75    /// A generic container that should be ignored by assistive technologies
76    /// and filtered out of platform accessibility trees. Equivalent to the ARIA
77    /// `none` or `presentation` role, or to an HTML `div` with no role.
78    GenericContainer,
79
80    CheckBox,
81    RadioButton,
82    TextInput,
83    Button,
84    DefaultButton,
85    Pane,
86    RowHeader,
87    ColumnHeader,
88    RowGroup,
89    List,
90    Table,
91    LayoutTableCell,
92    LayoutTableRow,
93    LayoutTable,
94    Switch,
95    Menu,
96
97    MultilineTextInput,
98    SearchInput,
99    DateInput,
100    DateTimeInput,
101    WeekInput,
102    MonthInput,
103    TimeInput,
104    EmailInput,
105    NumberInput,
106    PasswordInput,
107    PhoneNumberInput,
108    UrlInput,
109
110    Abbr,
111    Alert,
112    AlertDialog,
113    Application,
114    Article,
115    Audio,
116    Banner,
117    Blockquote,
118    Canvas,
119    Caption,
120    Caret,
121    Code,
122    ColorWell,
123    ComboBox,
124    EditableComboBox,
125    Complementary,
126    Comment,
127    ContentDeletion,
128    ContentInsertion,
129    ContentInfo,
130    Definition,
131    DescriptionList,
132    DescriptionListDetail,
133    DescriptionListTerm,
134    Details,
135    Dialog,
136    Directory,
137    DisclosureTriangle,
138    Document,
139    EmbeddedObject,
140    Emphasis,
141    Feed,
142    FigureCaption,
143    Figure,
144    Footer,
145    FooterAsNonLandmark,
146    Form,
147    Grid,
148    Group,
149    Header,
150    HeaderAsNonLandmark,
151    Heading,
152    Iframe,
153    IframePresentational,
154    ImeCandidate,
155    Keyboard,
156    Legend,
157    LineBreak,
158    ListBox,
159    Log,
160    Main,
161    Mark,
162    Marquee,
163    Math,
164    MenuBar,
165    MenuItemCheckBox,
166    MenuItemRadio,
167    MenuListPopup,
168    Meter,
169    Navigation,
170    Note,
171    PluginObject,
172    Portal,
173    Pre,
174    ProgressIndicator,
175    RadioGroup,
176    Region,
177    RootWebArea,
178    Ruby,
179    RubyAnnotation,
180    ScrollBar,
181    ScrollView,
182    Search,
183    Section,
184    Slider,
185    SpinButton,
186    Splitter,
187    Status,
188    Strong,
189    Suggestion,
190    SvgRoot,
191    Tab,
192    TabList,
193    TabPanel,
194    Term,
195    Time,
196    Timer,
197    TitleBar,
198    Toolbar,
199    Tooltip,
200    Tree,
201    TreeGrid,
202    Video,
203    WebView,
204    Window,
205
206    PdfActionableHighlight,
207    PdfRoot,
208
209    // ARIA Graphics module roles:
210    // https://rawgit.com/w3c/graphics-aam/master/#mapping_role_table
211    GraphicsDocument,
212    GraphicsObject,
213    GraphicsSymbol,
214
215    // DPub Roles:
216    // https://www.w3.org/TR/dpub-aam-1.0/#mapping_role_table
217    DocAbstract,
218    DocAcknowledgements,
219    DocAfterword,
220    DocAppendix,
221    DocBackLink,
222    DocBiblioEntry,
223    DocBibliography,
224    DocBiblioRef,
225    DocChapter,
226    DocColophon,
227    DocConclusion,
228    DocCover,
229    DocCredit,
230    DocCredits,
231    DocDedication,
232    DocEndnote,
233    DocEndnotes,
234    DocEpigraph,
235    DocEpilogue,
236    DocErrata,
237    DocExample,
238    DocFootnote,
239    DocForeword,
240    DocGlossary,
241    DocGlossRef,
242    DocIndex,
243    DocIntroduction,
244    DocNoteRef,
245    DocNotice,
246    DocPageBreak,
247    DocPageFooter,
248    DocPageHeader,
249    DocPageList,
250    DocPart,
251    DocPreface,
252    DocPrologue,
253    DocPullquote,
254    DocQna,
255    DocSubtitle,
256    DocTip,
257    DocToc,
258
259    /// Behaves similar to an ARIA grid but is primarily used by Chromium's
260    /// `TableView` and its subclasses, so they can be exposed correctly
261    /// on certain platforms.
262    ListGrid,
263
264    /// This is just like a multi-line document, but signals that assistive
265    /// technologies should implement behavior specific to a VT-100-style
266    /// terminal.
267    Terminal,
268}
269
270/// An action to be taken on an accessibility node.
271#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
272#[cfg_attr(feature = "enumn", derive(enumn::N))]
273#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
274#[cfg_attr(feature = "schemars", derive(JsonSchema))]
275#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
276#[cfg_attr(
277    feature = "pyo3",
278    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
279)]
280#[repr(u8)]
281pub enum Action {
282    /// Do the equivalent of a single click or tap.
283    Click,
284
285    Focus,
286    Blur,
287
288    Collapse,
289    Expand,
290
291    /// Requires [`ActionRequest::data`] to be set to [`ActionData::CustomAction`].
292    CustomAction,
293
294    /// Decrement a numeric value by one step.
295    Decrement,
296    /// Increment a numeric value by one step.
297    Increment,
298
299    HideTooltip,
300    ShowTooltip,
301
302    /// Delete any selected text in the control's text value and
303    /// insert the specified value in its place, like when typing or pasting.
304    /// Requires [`ActionRequest::data`] to be set to [`ActionData::Value`].
305    ReplaceSelectedText,
306
307    // Scrolls by approximately one screen in a specific direction.
308    // TBD: Do we need a doc comment on each of the values below?
309    // Or does this awkwardness suggest a refactor?
310    ScrollBackward,
311    ScrollDown,
312    ScrollForward,
313    ScrollLeft,
314    ScrollRight,
315    ScrollUp,
316
317    /// Scroll any scrollable containers to make the target object visible
318    /// on the screen.  Optionally set [`ActionRequest::data`] to
319    /// [`ActionData::ScrollTargetRect`].
320    ScrollIntoView,
321
322    /// Scroll the given object to a specified point in the tree's container
323    /// (e.g. window). Requires [`ActionRequest::data`] to be set to
324    /// [`ActionData::ScrollToPoint`].
325    ScrollToPoint,
326
327    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetScrollOffset`].
328    SetScrollOffset,
329
330    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetTextSelection`].
331    SetTextSelection,
332
333    /// Don't focus this node, but set it as the sequential focus navigation
334    /// starting point, so that pressing Tab moves to the next element
335    /// following this one, for example.
336    SetSequentialFocusNavigationStartingPoint,
337
338    /// Replace the value of the control with the specified value and
339    /// reset the selection, if applicable. Requires [`ActionRequest::data`]
340    /// to be set to [`ActionData::Value`] or [`ActionData::NumericValue`].
341    SetValue,
342
343    ShowContextMenu,
344}
345
346impl Action {
347    fn mask(self) -> u32 {
348        1 << (self as u8)
349    }
350
351    #[cfg(not(feature = "enumn"))]
352    fn n(value: u8) -> Option<Self> {
353        // Manually implement something similar to the enumn crate. We don't
354        // want to bring this crate by default though and we can't use a
355        // macro as it would break C bindings header file generation.
356        match value {
357            0 => Some(Action::Click),
358            1 => Some(Action::Focus),
359            2 => Some(Action::Blur),
360            3 => Some(Action::Collapse),
361            4 => Some(Action::Expand),
362            5 => Some(Action::CustomAction),
363            6 => Some(Action::Decrement),
364            7 => Some(Action::Increment),
365            8 => Some(Action::HideTooltip),
366            9 => Some(Action::ShowTooltip),
367            10 => Some(Action::ReplaceSelectedText),
368            11 => Some(Action::ScrollBackward),
369            12 => Some(Action::ScrollDown),
370            13 => Some(Action::ScrollForward),
371            14 => Some(Action::ScrollLeft),
372            15 => Some(Action::ScrollRight),
373            16 => Some(Action::ScrollUp),
374            17 => Some(Action::ScrollIntoView),
375            18 => Some(Action::ScrollToPoint),
376            19 => Some(Action::SetScrollOffset),
377            20 => Some(Action::SetTextSelection),
378            21 => Some(Action::SetSequentialFocusNavigationStartingPoint),
379            22 => Some(Action::SetValue),
380            23 => Some(Action::ShowContextMenu),
381            _ => None,
382        }
383    }
384}
385
386fn action_mask_to_action_vec(mask: u32) -> Vec<Action> {
387    let mut actions = Vec::new();
388    let mut i = 0;
389    while let Some(variant) = Action::n(i) {
390        if mask & variant.mask() != 0 {
391            actions.push(variant);
392        }
393        i += 1;
394    }
395    actions
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
399#[cfg_attr(feature = "enumn", derive(enumn::N))]
400#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
401#[cfg_attr(feature = "schemars", derive(JsonSchema))]
402#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
403#[cfg_attr(
404    feature = "pyo3",
405    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
406)]
407#[repr(u8)]
408pub enum Orientation {
409    /// E.g. most toolbars and separators.
410    Horizontal,
411    /// E.g. menu or combo box.
412    Vertical,
413}
414
415#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
416#[cfg_attr(feature = "enumn", derive(enumn::N))]
417#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
418#[cfg_attr(feature = "schemars", derive(JsonSchema))]
419#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
420#[cfg_attr(
421    feature = "pyo3",
422    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
423)]
424#[repr(u8)]
425pub enum TextDirection {
426    LeftToRight,
427    RightToLeft,
428    TopToBottom,
429    BottomToTop,
430}
431
432/// Indicates if a form control has invalid input or if a web DOM element has an
433/// [`aria-invalid`] attribute.
434///
435/// [`aria-invalid`]: https://www.w3.org/TR/wai-aria-1.1/#aria-invalid
436#[derive(Clone, Copy, Debug, PartialEq, Eq)]
437#[cfg_attr(feature = "enumn", derive(enumn::N))]
438#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
439#[cfg_attr(feature = "schemars", derive(JsonSchema))]
440#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
441#[cfg_attr(
442    feature = "pyo3",
443    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
444)]
445#[repr(u8)]
446pub enum Invalid {
447    True,
448    Grammar,
449    Spelling,
450}
451
452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
453#[cfg_attr(feature = "enumn", derive(enumn::N))]
454#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
455#[cfg_attr(feature = "schemars", derive(JsonSchema))]
456#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
457#[cfg_attr(
458    feature = "pyo3",
459    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
460)]
461#[repr(u8)]
462pub enum Toggled {
463    False,
464    True,
465    Mixed,
466}
467
468#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
469#[cfg_attr(feature = "enumn", derive(enumn::N))]
470#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
471#[cfg_attr(feature = "schemars", derive(JsonSchema))]
472#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
473#[cfg_attr(
474    feature = "pyo3",
475    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
476)]
477#[repr(u8)]
478pub enum SortDirection {
479    Ascending,
480    Descending,
481    Other,
482}
483
484#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
485#[cfg_attr(feature = "enumn", derive(enumn::N))]
486#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
487#[cfg_attr(feature = "schemars", derive(JsonSchema))]
488#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
489#[cfg_attr(
490    feature = "pyo3",
491    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
492)]
493#[repr(u8)]
494pub enum AriaCurrent {
495    False,
496    True,
497    Page,
498    Step,
499    Location,
500    Date,
501    Time,
502}
503
504#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
505#[cfg_attr(feature = "enumn", derive(enumn::N))]
506#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
507#[cfg_attr(feature = "schemars", derive(JsonSchema))]
508#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
509#[cfg_attr(
510    feature = "pyo3",
511    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
512)]
513#[repr(u8)]
514pub enum AutoComplete {
515    Inline,
516    List,
517    Both,
518}
519
520#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
521#[cfg_attr(feature = "enumn", derive(enumn::N))]
522#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
523#[cfg_attr(feature = "schemars", derive(JsonSchema))]
524#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
525#[cfg_attr(
526    feature = "pyo3",
527    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
528)]
529#[repr(u8)]
530pub enum Live {
531    Off,
532    Polite,
533    Assertive,
534}
535
536#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
537#[cfg_attr(feature = "enumn", derive(enumn::N))]
538#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
539#[cfg_attr(feature = "schemars", derive(JsonSchema))]
540#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
541#[cfg_attr(
542    feature = "pyo3",
543    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
544)]
545#[repr(u8)]
546pub enum HasPopup {
547    Menu,
548    Listbox,
549    Tree,
550    Grid,
551    Dialog,
552}
553
554#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
555#[cfg_attr(feature = "enumn", derive(enumn::N))]
556#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
557#[cfg_attr(feature = "schemars", derive(JsonSchema))]
558#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
559#[cfg_attr(
560    feature = "pyo3",
561    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
562)]
563#[repr(u8)]
564pub enum ListStyle {
565    Circle,
566    Disc,
567    Image,
568    Numeric,
569    Square,
570    /// Language specific ordering (alpha, roman, cjk-ideographic, etc...)
571    Other,
572}
573
574#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
575#[cfg_attr(feature = "enumn", derive(enumn::N))]
576#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
577#[cfg_attr(feature = "schemars", derive(JsonSchema))]
578#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
579#[cfg_attr(
580    feature = "pyo3",
581    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
582)]
583#[repr(u8)]
584pub enum TextAlign {
585    Left,
586    Right,
587    Center,
588    Justify,
589}
590
591#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
592#[cfg_attr(feature = "enumn", derive(enumn::N))]
593#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
594#[cfg_attr(feature = "schemars", derive(JsonSchema))]
595#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
596#[cfg_attr(
597    feature = "pyo3",
598    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
599)]
600#[repr(u8)]
601pub enum VerticalOffset {
602    Subscript,
603    Superscript,
604}
605
606#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
607#[cfg_attr(feature = "enumn", derive(enumn::N))]
608#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
609#[cfg_attr(feature = "schemars", derive(JsonSchema))]
610#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
611#[cfg_attr(
612    feature = "pyo3",
613    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
614)]
615#[repr(u8)]
616pub enum TextDecoration {
617    Solid,
618    Dotted,
619    Dashed,
620    Double,
621    Wavy,
622}
623
624pub type NodeIdContent = u64;
625
626/// The stable identity of a [`Node`], unique within the node's tree.
627#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
628#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
629#[cfg_attr(feature = "schemars", derive(JsonSchema))]
630#[repr(transparent)]
631pub struct NodeId(pub NodeIdContent);
632
633impl From<NodeIdContent> for NodeId {
634    #[inline]
635    fn from(inner: NodeIdContent) -> Self {
636        Self(inner)
637    }
638}
639
640impl From<NodeId> for NodeIdContent {
641    #[inline]
642    fn from(outer: NodeId) -> Self {
643        outer.0
644    }
645}
646
647impl fmt::Debug for NodeId {
648    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649        write!(f, "#{}", self.0)
650    }
651}
652
653/// Defines a custom action for a UI element.
654///
655/// For example, a list UI can allow a user to reorder items in the list by dragging the
656/// items.
657#[derive(Clone, Debug, PartialEq, Eq)]
658#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
659#[cfg_attr(feature = "schemars", derive(JsonSchema))]
660#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
661#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
662pub struct CustomAction {
663    pub id: i32,
664    pub description: Box<str>,
665}
666
667#[derive(Clone, Copy, Debug, PartialEq, Eq)]
668#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
669#[cfg_attr(feature = "schemars", derive(JsonSchema))]
670#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
671#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
672pub struct TextPosition {
673    /// The node's role must be [`Role::TextRun`].
674    pub node: NodeId,
675    /// The index of an item in [`Node::character_lengths`], or the length
676    /// of that slice if the position is at the end of the line.
677    pub character_index: usize,
678}
679
680#[derive(Clone, Copy, Debug, PartialEq, Eq)]
681#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
682#[cfg_attr(feature = "schemars", derive(JsonSchema))]
683#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
684#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
685pub struct TextSelection {
686    /// The position where the selection started, and which does not change
687    /// as the selection is expanded or contracted. If there is no selection
688    /// but only a caret, this must be equal to the value of [`TextSelection::focus`].
689    /// This is also known as a degenerate selection.
690    pub anchor: TextPosition,
691    /// The active end of the selection, which changes as the selection
692    /// is expanded or contracted, or the position of the caret if there is
693    /// no selection.
694    pub focus: TextPosition,
695}
696
697#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
698#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
699#[cfg_attr(feature = "schemars", derive(JsonSchema))]
700#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
701#[repr(u8)]
702enum Flag {
703    Hidden,
704    Multiselectable,
705    Required,
706    Visited,
707    Busy,
708    LiveAtomic,
709    Modal,
710    TouchTransparent,
711    ReadOnly,
712    Disabled,
713    Bold,
714    Italic,
715    ClipsChildren,
716    IsLineBreakingObject,
717    IsPageBreakingObject,
718    IsSpellingError,
719    IsGrammarError,
720    IsSearchMatch,
721    IsSuggestion,
722}
723
724impl Flag {
725    fn mask(self) -> u32 {
726        1 << (self as u8)
727    }
728}
729
730// The following is based on the technique described here:
731// https://viruta.org/reducing-memory-consumption-in-librsvg-2.html
732
733#[derive(Clone, Debug, PartialEq)]
734enum PropertyValue {
735    None,
736    NodeIdVec(Vec<NodeId>),
737    NodeId(NodeId),
738    String(Box<str>),
739    F64(f64),
740    Usize(usize),
741    Color(u32),
742    TextDecoration(TextDecoration),
743    LengthSlice(Box<[u8]>),
744    CoordSlice(Box<[f32]>),
745    Bool(bool),
746    Invalid(Invalid),
747    Toggled(Toggled),
748    Live(Live),
749    TextDirection(TextDirection),
750    Orientation(Orientation),
751    SortDirection(SortDirection),
752    AriaCurrent(AriaCurrent),
753    AutoComplete(AutoComplete),
754    HasPopup(HasPopup),
755    ListStyle(ListStyle),
756    TextAlign(TextAlign),
757    VerticalOffset(VerticalOffset),
758    Affine(Box<Affine>),
759    Rect(Rect),
760    TextSelection(Box<TextSelection>),
761    CustomActionVec(Vec<CustomAction>),
762}
763
764#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
765#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
766#[cfg_attr(feature = "schemars", derive(JsonSchema))]
767#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
768#[repr(u8)]
769enum PropertyId {
770    // NodeIdVec
771    Children,
772    Controls,
773    Details,
774    DescribedBy,
775    FlowTo,
776    LabelledBy,
777    Owns,
778    RadioGroup,
779
780    // NodeId
781    ActiveDescendant,
782    ErrorMessage,
783    InPageLinkTarget,
784    MemberOf,
785    NextOnLine,
786    PreviousOnLine,
787    PopupFor,
788
789    // String
790    Label,
791    Description,
792    Value,
793    AccessKey,
794    AuthorId,
795    ClassName,
796    FontFamily,
797    HtmlTag,
798    InnerHtml,
799    KeyboardShortcut,
800    Language,
801    Placeholder,
802    RoleDescription,
803    StateDescription,
804    Tooltip,
805    Url,
806    RowIndexText,
807    ColumnIndexText,
808
809    // f64
810    ScrollX,
811    ScrollXMin,
812    ScrollXMax,
813    ScrollY,
814    ScrollYMin,
815    ScrollYMax,
816    NumericValue,
817    MinNumericValue,
818    MaxNumericValue,
819    NumericValueStep,
820    NumericValueJump,
821    FontSize,
822    FontWeight,
823
824    // usize
825    RowCount,
826    ColumnCount,
827    RowIndex,
828    ColumnIndex,
829    RowSpan,
830    ColumnSpan,
831    Level,
832    SizeOfSet,
833    PositionInSet,
834
835    // Color
836    ColorValue,
837    BackgroundColor,
838    ForegroundColor,
839
840    // TextDecoration
841    Overline,
842    Strikethrough,
843    Underline,
844
845    // LengthSlice
846    CharacterLengths,
847    WordLengths,
848
849    // CoordSlice
850    CharacterPositions,
851    CharacterWidths,
852
853    // bool
854    Expanded,
855    Selected,
856
857    // Unique enums
858    Invalid,
859    Toggled,
860    Live,
861    TextDirection,
862    Orientation,
863    SortDirection,
864    AriaCurrent,
865    AutoComplete,
866    HasPopup,
867    ListStyle,
868    TextAlign,
869    VerticalOffset,
870
871    // Other
872    Transform,
873    Bounds,
874    TextSelection,
875    CustomActions,
876
877    // This MUST be last.
878    Unset,
879}
880
881#[derive(Clone, Copy, Debug, PartialEq, Eq)]
882#[repr(transparent)]
883struct PropertyIndices([u8; PropertyId::Unset as usize]);
884
885impl Default for PropertyIndices {
886    fn default() -> Self {
887        Self([PropertyId::Unset as u8; PropertyId::Unset as usize])
888    }
889}
890
891#[derive(Clone, Debug, Default, PartialEq)]
892struct Properties {
893    indices: PropertyIndices,
894    values: Vec<PropertyValue>,
895}
896
897/// A single accessible object. A complete UI is represented as a tree of these.
898///
899/// For brevity, and to make more of the documentation usable in bindings
900/// to other languages, documentation of getter methods is written as if
901/// documenting fields in a struct, and such methods are referred to
902/// as properties.
903#[derive(Clone, Default, PartialEq)]
904#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
905#[cfg_attr(feature = "schemars", derive(JsonSchema))]
906#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
907#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
908pub struct Node {
909    role: Role,
910    actions: u32,
911    flags: u32,
912    properties: Properties,
913}
914
915impl PropertyIndices {
916    fn get<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a PropertyValue {
917        let index = self.0[id as usize];
918        if index == PropertyId::Unset as u8 {
919            &PropertyValue::None
920        } else {
921            &values[index as usize]
922        }
923    }
924}
925
926impl Properties {
927    fn get_mut(&mut self, id: PropertyId, default: PropertyValue) -> &mut PropertyValue {
928        let index = self.indices.0[id as usize] as usize;
929        if index == PropertyId::Unset as usize {
930            self.values.push(default);
931            let index = self.values.len() - 1;
932            self.indices.0[id as usize] = index as u8;
933            &mut self.values[index]
934        } else {
935            &mut self.values[index]
936        }
937    }
938
939    fn set(&mut self, id: PropertyId, value: PropertyValue) {
940        let index = self.indices.0[id as usize];
941        if index == PropertyId::Unset as u8 {
942            self.values.push(value);
943            self.indices.0[id as usize] = (self.values.len() - 1) as u8;
944        } else {
945            self.values[index as usize] = value;
946        }
947    }
948
949    fn clear(&mut self, id: PropertyId) {
950        let index = self.indices.0[id as usize];
951        if index != PropertyId::Unset as u8 {
952            self.values[index as usize] = PropertyValue::None;
953        }
954    }
955}
956
957macro_rules! flag_methods {
958    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
959        impl Node {
960            $($(#[$doc])*
961            #[inline]
962            pub fn $getter(&self) -> bool {
963                (self.flags & (Flag::$id).mask()) != 0
964            }
965            #[inline]
966            pub fn $setter(&mut self) {
967                self.flags |= (Flag::$id).mask();
968            }
969            #[inline]
970            pub fn $clearer(&mut self) {
971                self.flags &= !((Flag::$id).mask());
972            })*
973            fn debug_flag_properties(&self, fmt: &mut fmt::DebugStruct) {
974                $(
975                    if self.$getter() {
976                        fmt.field(stringify!($getter), &true);
977                    }
978                )*
979            }
980        }
981        $(#[cfg(test)]
982        mod $getter {
983            use super::{Node, Role};
984
985            #[test]
986            fn getter_should_return_default_value() {
987                let node = Node::new(Role::Unknown);
988                assert!(!node.$getter());
989            }
990
991            #[test]
992            fn setter_should_update_the_property() {
993                let mut node = Node::new(Role::Unknown);
994                node.$setter();
995                assert!(node.$getter());
996            }
997
998            #[test]
999            fn clearer_should_reset_the_property() {
1000                let mut node = Node::new(Role::Unknown);
1001                node.$setter();
1002                node.$clearer();
1003                assert!(!node.$getter());
1004            }
1005        })*
1006    }
1007}
1008
1009macro_rules! option_ref_type_getters {
1010    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1011        impl PropertyIndices {
1012            $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> Option<&'a $type> {
1013                match self.get(values, id) {
1014                    PropertyValue::$variant(value) => Some(value),
1015                    _ => None,
1016                }
1017            })*
1018        }
1019    }
1020}
1021
1022macro_rules! slice_type_getters {
1023    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1024        impl PropertyIndices {
1025            $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a [$type] {
1026                match self.get(values, id) {
1027                    PropertyValue::$variant(value) => value,
1028                    _ => &[],
1029                }
1030            })*
1031        }
1032    }
1033}
1034
1035macro_rules! copy_type_getters {
1036    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1037        impl PropertyIndices {
1038            $(fn $method(&self, values: &[PropertyValue], id: PropertyId) -> Option<$type> {
1039                match self.get(values, id) {
1040                    PropertyValue::$variant(value) => Some(*value),
1041                    _ => None,
1042                }
1043            })*
1044        }
1045    }
1046}
1047
1048macro_rules! box_type_setters {
1049    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1050        impl Node {
1051            $(fn $method(&mut self, id: PropertyId, value: impl Into<Box<$type>>) {
1052                self.properties.set(id, PropertyValue::$variant(value.into()));
1053            })*
1054        }
1055    }
1056}
1057
1058macro_rules! copy_type_setters {
1059    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1060        impl Node {
1061            $(fn $method(&mut self, id: PropertyId, value: $type) {
1062                self.properties.set(id, PropertyValue::$variant(value));
1063            })*
1064        }
1065    }
1066}
1067
1068macro_rules! vec_type_methods {
1069    ($(($type:ty, $variant:ident, $getter:ident, $setter:ident, $pusher:ident)),+) => {
1070        $(slice_type_getters! {
1071            ($getter, $type, $variant)
1072        })*
1073        impl Node {
1074            $(fn $setter(&mut self, id: PropertyId, value: impl Into<Vec<$type>>) {
1075                self.properties.set(id, PropertyValue::$variant(value.into()));
1076            }
1077            fn $pusher(&mut self, id: PropertyId, item: $type) {
1078                if let PropertyValue::$variant(v) = self.properties.get_mut(id, PropertyValue::$variant(Vec::new())) {
1079                    v.push(item);
1080                }
1081            })*
1082        }
1083    }
1084}
1085
1086macro_rules! property_methods {
1087    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $type_getter:ident, $getter_result:ty, $setter:ident, $type_setter:ident, $setter_param:ty, $clearer:ident)),+) => {
1088        impl Node {
1089            $($(#[$doc])*
1090            #[inline]
1091            pub fn $getter(&self) -> $getter_result {
1092                self.properties.indices.$type_getter(&self.properties.values, PropertyId::$id)
1093            }
1094            #[inline]
1095            pub fn $setter(&mut self, value: $setter_param) {
1096                self.$type_setter(PropertyId::$id, value);
1097            }
1098            #[inline]
1099            pub fn $clearer(&mut self) {
1100                self.properties.clear(PropertyId::$id);
1101            })*
1102        }
1103    }
1104}
1105
1106macro_rules! vec_property_methods {
1107    ($($(#[$doc:meta])* ($id:ident, $item_type:ty, $getter:ident, $type_getter:ident, $setter:ident, $type_setter:ident, $pusher:ident, $type_pusher:ident, $clearer:ident)),+) => {
1108        $(property_methods! {
1109            $(#[$doc])*
1110            ($id, $getter, $type_getter, &[$item_type], $setter, $type_setter, impl Into<Vec<$item_type>>, $clearer)
1111        }
1112        impl Node {
1113            #[inline]
1114            pub fn $pusher(&mut self, item: $item_type) {
1115                self.$type_pusher(PropertyId::$id, item);
1116            }
1117        })*
1118    }
1119}
1120
1121macro_rules! slice_properties_debug_method {
1122    ($name:ident, [$($getter:ident,)*]) => {
1123        fn $name(&self, fmt: &mut fmt::DebugStruct) {
1124            $(
1125                let value = self.$getter();
1126                if !value.is_empty() {
1127                    fmt.field(stringify!($getter), &value);
1128                }
1129            )*
1130        }
1131    }
1132}
1133
1134macro_rules! node_id_vec_property_methods {
1135    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $pusher:ident, $clearer:ident)),+) => {
1136        $(vec_property_methods! {
1137            $(#[$doc])*
1138            ($id, NodeId, $getter, get_node_id_vec, $setter, set_node_id_vec, $pusher, push_to_node_id_vec, $clearer)
1139        })*
1140        impl Node {
1141            slice_properties_debug_method! { debug_node_id_vec_properties, [$($getter,)*] }
1142        }
1143        $(#[cfg(test)]
1144        mod $getter {
1145            use super::{Node, NodeId, Role};
1146
1147            #[test]
1148            fn getter_should_return_default_value() {
1149                let node = Node::new(Role::Unknown);
1150                assert!(node.$getter().is_empty());
1151            }
1152            #[test]
1153            fn setter_should_update_the_property() {
1154                let mut node = Node::new(Role::Unknown);
1155                node.$setter([]);
1156                assert!(node.$getter().is_empty());
1157                node.$setter([NodeId(0), NodeId(1)]);
1158                assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1159            }
1160            #[test]
1161            fn pusher_should_update_the_property() {
1162                let mut node = Node::new(Role::Unknown);
1163                node.$pusher(NodeId(0));
1164                assert_eq!(node.$getter(), &[NodeId(0)]);
1165                node.$pusher(NodeId(1));
1166                assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1167            }
1168            #[test]
1169            fn clearer_should_reset_the_property() {
1170                let mut node = Node::new(Role::Unknown);
1171                node.$setter([NodeId(0)]);
1172                node.$clearer();
1173                assert!(node.$getter().is_empty());
1174            }
1175        })*
1176    }
1177}
1178
1179macro_rules! option_properties_debug_method {
1180    ($name:ident, [$($getter:ident,)*]) => {
1181        fn $name(&self, fmt: &mut fmt::DebugStruct) {
1182            $(
1183                if let Some(value) = self.$getter() {
1184                    fmt.field(stringify!($getter), &value);
1185                }
1186            )*
1187        }
1188    }
1189}
1190
1191macro_rules! node_id_property_methods {
1192    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1193        $(property_methods! {
1194            $(#[$doc])*
1195            ($id, $getter, get_node_id_property, Option<NodeId>, $setter, set_node_id_property, NodeId, $clearer)
1196        })*
1197        impl Node {
1198            option_properties_debug_method! { debug_node_id_properties, [$($getter,)*] }
1199        }
1200        $(#[cfg(test)]
1201        mod $getter {
1202            use super::{Node, NodeId, Role};
1203
1204            #[test]
1205            fn getter_should_return_default_value() {
1206                let node = Node::new(Role::Unknown);
1207                assert!(node.$getter().is_none());
1208            }
1209            #[test]
1210            fn setter_should_update_the_property() {
1211                let mut node = Node::new(Role::Unknown);
1212                node.$setter(NodeId(1));
1213                assert_eq!(node.$getter(), Some(NodeId(1)));
1214            }
1215            #[test]
1216            fn clearer_should_reset_the_property() {
1217                let mut node = Node::new(Role::Unknown);
1218                node.$setter(NodeId(1));
1219                node.$clearer();
1220                assert!(node.$getter().is_none());
1221            }
1222        })*
1223    }
1224}
1225
1226macro_rules! string_property_methods {
1227    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1228        $(property_methods! {
1229            $(#[$doc])*
1230            ($id, $getter, get_string_property, Option<&str>, $setter, set_string_property, impl Into<Box<str>>, $clearer)
1231        })*
1232        impl Node {
1233            option_properties_debug_method! { debug_string_properties, [$($getter,)*] }
1234        }
1235        $(#[cfg(test)]
1236        mod $getter {
1237            use super::{Node, Role};
1238
1239            #[test]
1240            fn getter_should_return_default_value() {
1241                let node = Node::new(Role::Unknown);
1242                assert!(node.$getter().is_none());
1243            }
1244            #[test]
1245            fn setter_should_update_the_property() {
1246                let mut node = Node::new(Role::Unknown);
1247                node.$setter("test");
1248                assert_eq!(node.$getter(), Some("test"));
1249            }
1250            #[test]
1251            fn clearer_should_reset_the_property() {
1252                let mut node = Node::new(Role::Unknown);
1253                node.$setter("test");
1254                node.$clearer();
1255                assert!(node.$getter().is_none());
1256            }
1257        })*
1258    }
1259}
1260
1261macro_rules! f64_property_methods {
1262    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1263        $(property_methods! {
1264            $(#[$doc])*
1265            ($id, $getter, get_f64_property, Option<f64>, $setter, set_f64_property, f64, $clearer)
1266        })*
1267        impl Node {
1268            option_properties_debug_method! { debug_f64_properties, [$($getter,)*] }
1269        }
1270        $(#[cfg(test)]
1271        mod $getter {
1272            use super::{Node, Role};
1273
1274            #[test]
1275            fn getter_should_return_default_value() {
1276                let node = Node::new(Role::Unknown);
1277                assert!(node.$getter().is_none());
1278            }
1279            #[test]
1280            fn setter_should_update_the_property() {
1281                let mut node = Node::new(Role::Unknown);
1282                node.$setter(1.0);
1283                assert_eq!(node.$getter(), Some(1.0));
1284            }
1285            #[test]
1286            fn clearer_should_reset_the_property() {
1287                let mut node = Node::new(Role::Unknown);
1288                node.$setter(1.0);
1289                node.$clearer();
1290                assert!(node.$getter().is_none());
1291            }
1292        })*
1293    }
1294}
1295
1296macro_rules! usize_property_methods {
1297    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1298        $(property_methods! {
1299            $(#[$doc])*
1300            ($id, $getter, get_usize_property, Option<usize>, $setter, set_usize_property, usize, $clearer)
1301        })*
1302        impl Node {
1303            option_properties_debug_method! { debug_usize_properties, [$($getter,)*] }
1304        }
1305        $(#[cfg(test)]
1306        mod $getter {
1307            use super::{Node, Role};
1308
1309            #[test]
1310            fn getter_should_return_default_value() {
1311                let node = Node::new(Role::Unknown);
1312                assert!(node.$getter().is_none());
1313            }
1314            #[test]
1315            fn setter_should_update_the_property() {
1316                let mut node = Node::new(Role::Unknown);
1317                node.$setter(1);
1318                assert_eq!(node.$getter(), Some(1));
1319            }
1320            #[test]
1321            fn clearer_should_reset_the_property() {
1322                let mut node = Node::new(Role::Unknown);
1323                node.$setter(1);
1324                node.$clearer();
1325                assert!(node.$getter().is_none());
1326            }
1327        })*
1328    }
1329}
1330
1331macro_rules! color_property_methods {
1332    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1333        $(property_methods! {
1334            $(#[$doc])*
1335            ($id, $getter, get_color_property, Option<u32>, $setter, set_color_property, u32, $clearer)
1336        })*
1337        impl Node {
1338            option_properties_debug_method! { debug_color_properties, [$($getter,)*] }
1339        }
1340        $(#[cfg(test)]
1341        mod $getter {
1342            use super::{Node, Role};
1343
1344            #[test]
1345            fn getter_should_return_default_value() {
1346                let node = Node::new(Role::Unknown);
1347                assert!(node.$getter().is_none());
1348            }
1349            #[test]
1350            fn setter_should_update_the_property() {
1351                let mut node = Node::new(Role::Unknown);
1352                node.$setter(1);
1353                assert_eq!(node.$getter(), Some(1));
1354            }
1355            #[test]
1356            fn clearer_should_reset_the_property() {
1357                let mut node = Node::new(Role::Unknown);
1358                node.$setter(1);
1359                node.$clearer();
1360                assert!(node.$getter().is_none());
1361            }
1362        })*
1363    }
1364}
1365
1366macro_rules! text_decoration_property_methods {
1367    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1368        $(property_methods! {
1369            $(#[$doc])*
1370            ($id, $getter, get_text_decoration_property, Option<TextDecoration>, $setter, set_text_decoration_property, TextDecoration, $clearer)
1371        })*
1372        impl Node {
1373            option_properties_debug_method! { debug_text_decoration_properties, [$($getter,)*] }
1374        }
1375        $(#[cfg(test)]
1376        mod $getter {
1377            use super::{Node, Role, TextDecoration};
1378
1379            #[test]
1380            fn getter_should_return_default_value() {
1381                let node = Node::new(Role::Unknown);
1382                assert!(node.$getter().is_none());
1383            }
1384            #[test]
1385            fn setter_should_update_the_property() {
1386                let mut node = Node::new(Role::Unknown);
1387                node.$setter(TextDecoration::Dotted);
1388                assert_eq!(node.$getter(), Some(TextDecoration::Dotted));
1389            }
1390            #[test]
1391            fn clearer_should_reset_the_property() {
1392                let mut node = Node::new(Role::Unknown);
1393                node.$setter(TextDecoration::Dotted);
1394                node.$clearer();
1395                assert!(node.$getter().is_none());
1396            }
1397        })*
1398    }
1399}
1400
1401macro_rules! length_slice_property_methods {
1402    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1403        $(property_methods! {
1404            $(#[$doc])*
1405            ($id, $getter, get_length_slice_property, &[u8], $setter, set_length_slice_property, impl Into<Box<[u8]>>, $clearer)
1406        })*
1407        impl Node {
1408            slice_properties_debug_method! { debug_length_slice_properties, [$($getter,)*] }
1409        }
1410        $(#[cfg(test)]
1411        mod $getter {
1412            use super::{Node, Role};
1413
1414            #[test]
1415            fn getter_should_return_default_value() {
1416                let node = Node::new(Role::Unknown);
1417                assert!(node.$getter().is_empty());
1418            }
1419            #[test]
1420            fn setter_should_update_the_property() {
1421                let mut node = Node::new(Role::Unknown);
1422                node.$setter([]);
1423                assert!(node.$getter().is_empty());
1424                node.$setter([1, 2]);
1425                assert_eq!(node.$getter(), &[1, 2]);
1426            }
1427            #[test]
1428            fn clearer_should_reset_the_property() {
1429                let mut node = Node::new(Role::Unknown);
1430                node.$setter([1, 2]);
1431                node.$clearer();
1432                assert!(node.$getter().is_empty());
1433            }
1434        })*
1435    }
1436}
1437
1438macro_rules! coord_slice_property_methods {
1439    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1440        $(property_methods! {
1441            $(#[$doc])*
1442            ($id, $getter, get_coord_slice_property, Option<&[f32]>, $setter, set_coord_slice_property, impl Into<Box<[f32]>>, $clearer)
1443        })*
1444        impl Node {
1445            option_properties_debug_method! { debug_coord_slice_properties, [$($getter,)*] }
1446        }
1447        $(#[cfg(test)]
1448        mod $getter {
1449            use super::{Node, Role};
1450
1451            #[test]
1452            fn getter_should_return_default_value() {
1453                let node = Node::new(Role::Unknown);
1454                assert!(node.$getter().is_none());
1455            }
1456            #[test]
1457            fn setter_should_update_the_property() {
1458                let mut node = Node::new(Role::Unknown);
1459                node.$setter([]);
1460                let expected: Option<&[f32]> = Some(&[]);
1461                assert_eq!(node.$getter(), expected);
1462                node.$setter([1.0, 2.0]);
1463                let expected: Option<&[f32]> = Some(&[1.0, 2.0]);
1464                assert_eq!(node.$getter(), expected);
1465            }
1466            #[test]
1467            fn clearer_should_reset_the_property() {
1468                let mut node = Node::new(Role::Unknown);
1469                node.$setter([1.0, 2.0]);
1470                node.$clearer();
1471                assert!(node.$getter().is_none());
1472            }
1473        })*
1474    }
1475}
1476
1477macro_rules! bool_property_methods {
1478    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1479        $(property_methods! {
1480            $(#[$doc])*
1481            ($id, $getter, get_bool_property, Option<bool>, $setter, set_bool_property, bool, $clearer)
1482        })*
1483        impl Node {
1484            option_properties_debug_method! { debug_bool_properties, [$($getter,)*] }
1485        }
1486        $(#[cfg(test)]
1487        mod $getter {
1488            use super::{Node, Role};
1489
1490            #[test]
1491            fn getter_should_return_default_value() {
1492                let node = Node::new(Role::Unknown);
1493                assert!(node.$getter().is_none());
1494            }
1495            #[test]
1496            fn setter_should_update_the_property() {
1497                let mut node = Node::new(Role::Unknown);
1498                node.$setter(true);
1499                assert_eq!(node.$getter(), Some(true));
1500            }
1501            #[test]
1502            fn clearer_should_reset_the_property() {
1503                let mut node = Node::new(Role::Unknown);
1504                node.$setter(true);
1505                node.$clearer();
1506                assert!(node.$getter().is_none());
1507            }
1508        })*
1509    }
1510}
1511
1512macro_rules! unique_enum_property_methods {
1513    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident, $variant:ident)),+) => {
1514        impl Node {
1515            $($(#[$doc])*
1516            #[inline]
1517            pub fn $getter(&self) -> Option<$id> {
1518                match self.properties.indices.get(&self.properties.values, PropertyId::$id) {
1519                    PropertyValue::$id(value) => Some(*value),
1520                    _ => None,
1521                }
1522            }
1523            #[inline]
1524            pub fn $setter(&mut self, value: $id) {
1525                self.properties.set(PropertyId::$id, PropertyValue::$id(value));
1526            }
1527            #[inline]
1528            pub fn $clearer(&mut self) {
1529                self.properties.clear(PropertyId::$id);
1530            })*
1531            option_properties_debug_method! { debug_unique_enum_properties, [$($getter,)*] }
1532        }
1533        $(#[cfg(test)]
1534        mod $getter {
1535            use super::{Node, Role};
1536
1537            #[test]
1538            fn getter_should_return_default_value() {
1539                let node = Node::new(Role::Unknown);
1540                assert!(node.$getter().is_none());
1541            }
1542            #[test]
1543            fn setter_should_update_the_property() {
1544                let mut node = Node::new(Role::Unknown);
1545                let variant = super::$id::$variant;
1546                node.$setter(variant);
1547                assert_eq!(node.$getter(), Some(variant));
1548            }
1549            #[test]
1550            fn clearer_should_reset_the_property() {
1551                let mut node = Node::new(Role::Unknown);
1552                node.$setter(super::$id::$variant);
1553                node.$clearer();
1554                assert!(node.$getter().is_none());
1555            }
1556        })*
1557    }
1558}
1559
1560impl Node {
1561    #[inline]
1562    pub fn new(role: Role) -> Self {
1563        Self {
1564            role,
1565            ..Default::default()
1566        }
1567    }
1568}
1569
1570impl Node {
1571    #[inline]
1572    pub fn role(&self) -> Role {
1573        self.role
1574    }
1575    #[inline]
1576    pub fn set_role(&mut self, value: Role) {
1577        self.role = value;
1578    }
1579
1580    #[inline]
1581    pub fn supports_action(&self, action: Action) -> bool {
1582        (self.actions & action.mask()) != 0
1583    }
1584    #[inline]
1585    pub fn add_action(&mut self, action: Action) {
1586        self.actions |= action.mask();
1587    }
1588    #[inline]
1589    pub fn remove_action(&mut self, action: Action) {
1590        self.actions &= !(action.mask());
1591    }
1592    #[inline]
1593    pub fn clear_actions(&mut self) {
1594        self.actions = 0;
1595    }
1596}
1597
1598flag_methods! {
1599    /// Exclude this node and its descendants from the tree presented to
1600    /// assistive technologies, and from hit testing.
1601    (Hidden, is_hidden, set_hidden, clear_hidden),
1602    (Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
1603    (Required, is_required, set_required, clear_required),
1604    (Visited, is_visited, set_visited, clear_visited),
1605    (Busy, is_busy, set_busy, clear_busy),
1606    (LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
1607    /// If a dialog box is marked as explicitly modal.
1608    (Modal, is_modal, set_modal, clear_modal),
1609    /// This element allows touches to be passed through when a screen reader
1610    /// is in touch exploration mode, e.g. a virtual keyboard normally
1611    /// behaves this way.
1612    (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1613    /// Use for a text widget that allows focus/selection but not input.
1614    (ReadOnly, is_read_only, set_read_only, clear_read_only),
1615    /// Use for a control or group of controls that disallows input.
1616    (Disabled, is_disabled, set_disabled, clear_disabled),
1617    (Bold, is_bold, set_bold, clear_bold),
1618    (Italic, is_italic, set_italic, clear_italic),
1619    /// Indicates that this node clips its children, i.e. may have
1620    /// `overflow: hidden` or clip children by default.
1621    (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1622    /// Indicates whether this node causes a hard line-break
1623    /// (e.g. block level elements, or `<br>`).
1624    (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1625    /// Indicates whether this node causes a page break.
1626    (IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
1627    (IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
1628    (IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
1629    (IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
1630    (IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
1631}
1632
1633option_ref_type_getters! {
1634    (get_affine_property, Affine, Affine),
1635    (get_string_property, str, String),
1636    (get_coord_slice_property, [f32], CoordSlice),
1637    (get_text_selection_property, TextSelection, TextSelection)
1638}
1639
1640slice_type_getters! {
1641    (get_length_slice_property, u8, LengthSlice)
1642}
1643
1644copy_type_getters! {
1645    (get_rect_property, Rect, Rect),
1646    (get_node_id_property, NodeId, NodeId),
1647    (get_f64_property, f64, F64),
1648    (get_usize_property, usize, Usize),
1649    (get_color_property, u32, Color),
1650    (get_text_decoration_property, TextDecoration, TextDecoration),
1651    (get_bool_property, bool, Bool)
1652}
1653
1654box_type_setters! {
1655    (set_affine_property, Affine, Affine),
1656    (set_string_property, str, String),
1657    (set_length_slice_property, [u8], LengthSlice),
1658    (set_coord_slice_property, [f32], CoordSlice),
1659    (set_text_selection_property, TextSelection, TextSelection)
1660}
1661
1662copy_type_setters! {
1663    (set_rect_property, Rect, Rect),
1664    (set_node_id_property, NodeId, NodeId),
1665    (set_f64_property, f64, F64),
1666    (set_usize_property, usize, Usize),
1667    (set_color_property, u32, Color),
1668    (set_text_decoration_property, TextDecoration, TextDecoration),
1669    (set_bool_property, bool, Bool)
1670}
1671
1672vec_type_methods! {
1673    (NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
1674    (CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
1675}
1676
1677node_id_vec_property_methods! {
1678    (Children, children, set_children, push_child, clear_children),
1679    (Controls, controls, set_controls, push_controlled, clear_controls),
1680    (Details, details, set_details, push_detail, clear_details),
1681    (DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
1682    (FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
1683    (LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
1684    /// As with the `aria-owns` property in ARIA, this property should be set
1685    /// only if the nodes referenced in the property are not descendants
1686    /// of the owning node in the AccessKit tree. In the common case, where the
1687    /// owned nodes are direct children or indirect descendants, this property
1688    /// is unnecessary.
1689    (Owns, owns, set_owns, push_owned, clear_owns),
1690    /// On radio buttons this should be set to a list of all of the buttons
1691    /// in the same group as this one, including this radio button itself.
1692    (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
1693}
1694
1695node_id_property_methods! {
1696    (ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
1697    (ErrorMessage, error_message, set_error_message, clear_error_message),
1698    (InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
1699    (MemberOf, member_of, set_member_of, clear_member_of),
1700    (NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
1701    (PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
1702    (PopupFor, popup_for, set_popup_for, clear_popup_for)
1703}
1704
1705string_property_methods! {
1706    /// The label of a control that can have a label. If the label is specified
1707    /// via the [`Node::labelled_by`] relation, this doesn't need to be set.
1708    /// Note that the text content of a node with the [`Role::Label`] role
1709    /// should be provided via [`Node::value`], not this property.
1710    (Label, label, set_label, clear_label),
1711    (Description, description, set_description, clear_description),
1712    (Value, value, set_value, clear_value),
1713    /// A single character, usually part of this node's name, that can be pressed,
1714    /// possibly along with a platform-specific modifier, to perform
1715    /// this node's default action. For menu items, the access key is only active
1716    /// while the menu is active, in contrast with [`keyboard_shortcut`];
1717    /// a single menu item may in fact have both properties.
1718    ///
1719    /// [`keyboard_shortcut`]: Node::keyboard_shortcut
1720    (AccessKey, access_key, set_access_key, clear_access_key),
1721    /// A way for application authors to identify this node for automated
1722    /// testing purpose. The value must be unique among this node's siblings.
1723    (AuthorId, author_id, set_author_id, clear_author_id),
1724    (ClassName, class_name, set_class_name, clear_class_name),
1725    /// Only present when different from parent.
1726    (FontFamily, font_family, set_font_family, clear_font_family),
1727    (HtmlTag, html_tag, set_html_tag, clear_html_tag),
1728    /// Inner HTML of an element. Only used for a top-level math element,
1729    /// to support third-party math accessibility products that parse MathML.
1730    (InnerHtml, inner_html, set_inner_html, clear_inner_html),
1731    /// A keystroke or sequence of keystrokes, complete with any required
1732    /// modifiers(s), that will perform this node's default action.
1733    /// The value of this property should be in a human-friendly format.
1734    (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
1735    /// Only present when different from parent.
1736    (Language, language, set_language, clear_language),
1737    /// If a text input has placeholder text, it should be exposed
1738    /// through this property rather than [`label`].
1739    ///
1740    /// [`label`]: Node::label
1741    (Placeholder, placeholder, set_placeholder, clear_placeholder),
1742    /// An optional string that may override an assistive technology's
1743    /// description of the node's role. Only provide this for custom control types.
1744    /// The value of this property should be in a human-friendly, localized format.
1745    (RoleDescription, role_description, set_role_description, clear_role_description),
1746    /// An optional string that may override an assistive technology's
1747    /// description of the node's state, replacing default strings such as
1748    /// "checked" or "selected". Note that most platform accessibility APIs
1749    /// and assistive technologies do not support this feature.
1750    (StateDescription, state_description, set_state_description, clear_state_description),
1751    /// If a node's only accessible name comes from a tooltip, it should be
1752    /// exposed through this property rather than [`label`].
1753    ///
1754    /// [`label`]: Node::label
1755    (Tooltip, tooltip, set_tooltip, clear_tooltip),
1756    (Url, url, set_url, clear_url),
1757    (RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
1758    (ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text)
1759}
1760
1761f64_property_methods! {
1762    (ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
1763    (ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
1764    (ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
1765    (ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
1766    (ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
1767    (ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
1768    (NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
1769    (MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
1770    (MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
1771    (NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
1772    (NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump),
1773    /// Font size is in pixels.
1774    (FontSize, font_size, set_font_size, clear_font_size),
1775    /// Font weight can take on any arbitrary numeric value. Increments of 100 in
1776    /// range `[0, 900]` represent keywords such as light, normal, bold, etc.
1777    (FontWeight, font_weight, set_font_weight, clear_font_weight)
1778}
1779
1780usize_property_methods! {
1781    (RowCount, row_count, set_row_count, clear_row_count),
1782    (ColumnCount, column_count, set_column_count, clear_column_count),
1783    (RowIndex, row_index, set_row_index, clear_row_index),
1784    (ColumnIndex, column_index, set_column_index, clear_column_index),
1785    (RowSpan, row_span, set_row_span, clear_row_span),
1786    (ColumnSpan, column_span, set_column_span, clear_column_span),
1787    (Level, level, set_level, clear_level),
1788    /// For containers like [`Role::ListBox`], specifies the total number of items.
1789    (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
1790    /// For items like [`Role::ListBoxOption`], specifies their index in the item list.
1791    /// This may not exceed the value of [`size_of_set`] as set on the container.
1792    ///
1793    /// [`size_of_set`]: Node::size_of_set
1794    (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
1795}
1796
1797color_property_methods! {
1798    /// For [`Role::ColorWell`], specifies the selected color in RGBA.
1799    (ColorValue, color_value, set_color_value, clear_color_value),
1800    /// Background color in RGBA.
1801    (BackgroundColor, background_color, set_background_color, clear_background_color),
1802    /// Foreground color in RGBA.
1803    (ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
1804}
1805
1806text_decoration_property_methods! {
1807    (Overline, overline, set_overline, clear_overline),
1808    (Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
1809    (Underline, underline, set_underline, clear_underline)
1810}
1811
1812length_slice_property_methods! {
1813    /// For text runs, the length (non-inclusive) of each character
1814    /// in UTF-8 code units (bytes). The sum of these lengths must equal
1815    /// the length of [`value`], also in bytes.
1816    ///
1817    /// A character is defined as the smallest unit of text that
1818    /// can be selected. This isn't necessarily a single Unicode
1819    /// scalar value (code point). This is why AccessKit can't compute
1820    /// the lengths of the characters from the text itself; this information
1821    /// must be provided by the text editing implementation.
1822    ///
1823    /// If this node is the last text run in a line that ends with a hard
1824    /// line break, that line break should be included at the end of this
1825    /// node's value as either a CRLF or LF; in both cases, the line break
1826    /// should be counted as a single character for the sake of this slice.
1827    /// When the caret is at the end of such a line, the focus of the text
1828    /// selection should be on the line break, not after it.
1829    ///
1830    /// [`value`]: Node::value
1831    (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
1832
1833    /// For text runs, the length of each word in characters, as defined
1834    /// in [`character_lengths`]. The sum of these lengths must equal
1835    /// the length of [`character_lengths`].
1836    ///
1837    /// The end of each word is the beginning of the next word; there are no
1838    /// characters that are not considered part of a word. Trailing whitespace
1839    /// is typically considered part of the word that precedes it, while
1840    /// a line's leading whitespace is considered its own word. Whether
1841    /// punctuation is considered a separate word or part of the preceding
1842    /// word depends on the particular text editing implementation.
1843    /// Some editors may have their own definition of a word; for example,
1844    /// in an IDE, words may correspond to programming language tokens.
1845    ///
1846    /// Not all assistive technologies require information about word
1847    /// boundaries, and not all platform accessibility APIs even expose
1848    /// this information, but for assistive technologies that do use
1849    /// this information, users will get unpredictable results if the word
1850    /// boundaries exposed by the accessibility tree don't match
1851    /// the editor's behavior. This is why AccessKit does not determine
1852    /// word boundaries itself.
1853    ///
1854    /// [`character_lengths`]: Node::character_lengths
1855    (WordLengths, word_lengths, set_word_lengths, clear_word_lengths)
1856}
1857
1858coord_slice_property_methods! {
1859    /// For text runs, this is the position of each character within
1860    /// the node's bounding box, in the direction given by
1861    /// [`text_direction`], in the coordinate space of this node.
1862    ///
1863    /// When present, the length of this slice should be the same as the length
1864    /// of [`character_lengths`], including for lines that end
1865    /// with a hard line break. The position of such a line break should
1866    /// be the position where an end-of-paragraph marker would be rendered.
1867    ///
1868    /// This property is optional. Without it, AccessKit can't support some
1869    /// use cases, such as screen magnifiers that track the caret position
1870    /// or screen readers that display a highlight cursor. However,
1871    /// most text functionality still works without this information.
1872    ///
1873    /// [`text_direction`]: Node::text_direction
1874    /// [`character_lengths`]: Node::character_lengths
1875    (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
1876
1877    /// For text runs, this is the advance width of each character,
1878    /// in the direction given by [`text_direction`], in the coordinate
1879    /// space of this node.
1880    ///
1881    /// When present, the length of this slice should be the same as the length
1882    /// of [`character_lengths`], including for lines that end
1883    /// with a hard line break. The width of such a line break should
1884    /// be non-zero if selecting the line break by itself results in
1885    /// a visible highlight (as in Microsoft Word), or zero if not
1886    /// (as in Windows Notepad).
1887    ///
1888    /// This property is optional. Without it, AccessKit can't support some
1889    /// use cases, such as screen magnifiers that track the caret position
1890    /// or screen readers that display a highlight cursor. However,
1891    /// most text functionality still works without this information.
1892    ///
1893    /// [`text_direction`]: Node::text_direction
1894    /// [`character_lengths`]: Node::character_lengths
1895    (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
1896}
1897
1898bool_property_methods! {
1899    /// Whether this node is expanded, collapsed, or neither.
1900    ///
1901    /// Setting this to `false` means the node is collapsed; omitting it means this state
1902    /// isn't applicable.
1903    (Expanded, is_expanded, set_expanded, clear_expanded),
1904
1905    /// Indicates whether this node is selected or unselected.
1906    ///
1907    /// The absence of this flag (as opposed to a `false` setting)
1908    /// means that the concept of "selected" doesn't apply.
1909    /// When deciding whether to set the flag to false or omit it,
1910    /// consider whether it would be appropriate for a screen reader
1911    /// to announce "not selected". The ambiguity of this flag
1912    /// in platform accessibility APIs has made extraneous
1913    /// "not selected" announcements a common annoyance.
1914    (Selected, is_selected, set_selected, clear_selected)
1915}
1916
1917unique_enum_property_methods! {
1918    (Invalid, invalid, set_invalid, clear_invalid, Grammar),
1919    (Toggled, toggled, set_toggled, clear_toggled, True),
1920    (Live, live, set_live, clear_live, Polite),
1921    (TextDirection, text_direction, set_text_direction, clear_text_direction, RightToLeft),
1922    (Orientation, orientation, set_orientation, clear_orientation, Vertical),
1923    (SortDirection, sort_direction, set_sort_direction, clear_sort_direction, Descending),
1924    (AriaCurrent, aria_current, set_aria_current, clear_aria_current, True),
1925    (AutoComplete, auto_complete, set_auto_complete, clear_auto_complete, List),
1926    (HasPopup, has_popup, set_has_popup, clear_has_popup, Menu),
1927    /// The list style type. Only available on list items.
1928    (ListStyle, list_style, set_list_style, clear_list_style, Disc),
1929    (TextAlign, text_align, set_text_align, clear_text_align, Right),
1930    (VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset, Superscript)
1931}
1932
1933property_methods! {
1934    /// An affine transform to apply to any coordinates within this node
1935    /// and its descendants, including the [`bounds`] property of this node.
1936    /// The combined transforms of this node and its ancestors define
1937    /// the coordinate space of this node. /// This should be `None` if
1938    /// it would be set to the identity transform, which should be the case
1939    /// for most nodes.
1940    ///
1941    /// AccessKit expects the final transformed coordinates to be relative
1942    /// to the origin of the tree's container (e.g. window), in physical
1943    /// pixels, with the y coordinate being top-down.
1944    ///
1945    /// [`bounds`]: Node::bounds
1946    (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
1947
1948    /// The bounding box of this node, in the node's coordinate space.
1949    /// This property does not affect the coordinate space of either this node
1950    /// or its descendants; only the [`transform`] property affects that.
1951    /// This, along with the recommendation that most nodes should have
1952    /// a [`transform`] of `None`, implies that the `bounds` property
1953    /// of most nodes should be in the coordinate space of the nearest ancestor
1954    /// with a non-`None` [`transform`], or if there is no such ancestor,
1955    /// the tree's container (e.g. window).
1956    ///
1957    /// [`transform`]: Node::transform
1958    (Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
1959
1960    (TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection)
1961}
1962
1963impl Node {
1964    option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection,] }
1965}
1966
1967#[cfg(test)]
1968mod transform {
1969    use super::{Affine, Node, Role};
1970
1971    #[test]
1972    fn getter_should_return_default_value() {
1973        let node = Node::new(Role::Unknown);
1974        assert!(node.transform().is_none());
1975    }
1976    #[test]
1977    fn setter_should_update_the_property() {
1978        let mut node = Node::new(Role::Unknown);
1979        node.set_transform(Affine::IDENTITY);
1980        assert_eq!(node.transform(), Some(&Affine::IDENTITY));
1981    }
1982    #[test]
1983    fn clearer_should_reset_the_property() {
1984        let mut node = Node::new(Role::Unknown);
1985        node.set_transform(Affine::IDENTITY);
1986        node.clear_transform();
1987        assert!(node.transform().is_none());
1988    }
1989}
1990
1991#[cfg(test)]
1992mod bounds {
1993    use super::{Node, Rect, Role};
1994
1995    #[test]
1996    fn getter_should_return_default_value() {
1997        let node = Node::new(Role::Unknown);
1998        assert!(node.bounds().is_none());
1999    }
2000    #[test]
2001    fn setter_should_update_the_property() {
2002        let mut node = Node::new(Role::Unknown);
2003        let value = Rect {
2004            x0: 0.0,
2005            y0: 1.0,
2006            x1: 2.0,
2007            y1: 3.0,
2008        };
2009        node.set_bounds(value);
2010        assert_eq!(node.bounds(), Some(value));
2011    }
2012    #[test]
2013    fn clearer_should_reset_the_property() {
2014        let mut node = Node::new(Role::Unknown);
2015        node.set_bounds(Rect {
2016            x0: 0.0,
2017            y0: 1.0,
2018            x1: 2.0,
2019            y1: 3.0,
2020        });
2021        node.clear_bounds();
2022        assert!(node.bounds().is_none());
2023    }
2024}
2025
2026#[cfg(test)]
2027mod text_selection {
2028    use super::{Node, NodeId, Role, TextPosition, TextSelection};
2029
2030    #[test]
2031    fn getter_should_return_default_value() {
2032        let node = Node::new(Role::Unknown);
2033        assert!(node.text_selection().is_none());
2034    }
2035    #[test]
2036    fn setter_should_update_the_property() {
2037        let mut node = Node::new(Role::Unknown);
2038        let value = TextSelection {
2039            anchor: TextPosition {
2040                node: NodeId(0),
2041                character_index: 0,
2042            },
2043            focus: TextPosition {
2044                node: NodeId(0),
2045                character_index: 2,
2046            },
2047        };
2048        node.set_text_selection(value);
2049        assert_eq!(node.text_selection(), Some(&value));
2050    }
2051    #[test]
2052    fn clearer_should_reset_the_property() {
2053        let mut node = Node::new(Role::Unknown);
2054        node.set_text_selection(TextSelection {
2055            anchor: TextPosition {
2056                node: NodeId(0),
2057                character_index: 0,
2058            },
2059            focus: TextPosition {
2060                node: NodeId(0),
2061                character_index: 2,
2062            },
2063        });
2064        node.clear_text_selection();
2065        assert!(node.text_selection().is_none());
2066    }
2067}
2068
2069vec_property_methods! {
2070    (CustomActions, CustomAction, custom_actions, get_custom_action_vec, set_custom_actions, set_custom_action_vec, push_custom_action, push_to_custom_action_vec, clear_custom_actions)
2071}
2072
2073#[cfg(test)]
2074mod custom_actions {
2075    use super::{CustomAction, Node, Role};
2076
2077    #[test]
2078    fn getter_should_return_default_value() {
2079        let node = Node::new(Role::Unknown);
2080        assert!(node.custom_actions().is_empty());
2081    }
2082    #[test]
2083    fn setter_should_update_the_property() {
2084        let mut node = Node::new(Role::Unknown);
2085        let value = alloc::vec![
2086            CustomAction {
2087                id: 0,
2088                description: "first test action".into(),
2089            },
2090            CustomAction {
2091                id: 1,
2092                description: "second test action".into(),
2093            },
2094        ];
2095        node.set_custom_actions(value.clone());
2096        assert_eq!(node.custom_actions(), value);
2097    }
2098    #[test]
2099    fn pusher_should_update_the_property() {
2100        let mut node = Node::new(Role::Unknown);
2101        let first_action = CustomAction {
2102            id: 0,
2103            description: "first test action".into(),
2104        };
2105        let second_action = CustomAction {
2106            id: 1,
2107            description: "second test action".into(),
2108        };
2109        node.push_custom_action(first_action.clone());
2110        assert_eq!(node.custom_actions(), &[first_action.clone()]);
2111        node.push_custom_action(second_action.clone());
2112        assert_eq!(node.custom_actions(), &[first_action, second_action]);
2113    }
2114    #[test]
2115    fn clearer_should_reset_the_property() {
2116        let mut node = Node::new(Role::Unknown);
2117        node.set_custom_actions([CustomAction {
2118            id: 0,
2119            description: "test action".into(),
2120        }]);
2121        node.clear_custom_actions();
2122        assert!(node.custom_actions().is_empty());
2123    }
2124}
2125
2126impl fmt::Debug for Node {
2127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2128        let mut fmt = f.debug_struct("Node");
2129
2130        fmt.field("role", &self.role());
2131
2132        let supported_actions = action_mask_to_action_vec(self.actions);
2133        if !supported_actions.is_empty() {
2134            fmt.field("actions", &supported_actions);
2135        }
2136
2137        self.debug_flag_properties(&mut fmt);
2138        self.debug_node_id_vec_properties(&mut fmt);
2139        self.debug_node_id_properties(&mut fmt);
2140        self.debug_string_properties(&mut fmt);
2141        self.debug_f64_properties(&mut fmt);
2142        self.debug_usize_properties(&mut fmt);
2143        self.debug_color_properties(&mut fmt);
2144        self.debug_text_decoration_properties(&mut fmt);
2145        self.debug_length_slice_properties(&mut fmt);
2146        self.debug_coord_slice_properties(&mut fmt);
2147        self.debug_bool_properties(&mut fmt);
2148        self.debug_unique_enum_properties(&mut fmt);
2149        self.debug_option_properties(&mut fmt);
2150
2151        let custom_actions = self.custom_actions();
2152        if !custom_actions.is_empty() {
2153            fmt.field("custom_actions", &custom_actions);
2154        }
2155
2156        fmt.finish()
2157    }
2158}
2159
2160#[cfg(feature = "serde")]
2161macro_rules! serialize_property {
2162    ($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
2163        match &$self.values[$index as usize] {
2164            PropertyValue::None => (),
2165            $(PropertyValue::$variant(value) => {
2166                $map.serialize_entry(&$id, &value)?;
2167            })*
2168        }
2169    }
2170}
2171
2172#[cfg(feature = "serde")]
2173macro_rules! deserialize_property {
2174    ($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
2175        match $key {
2176            $($(PropertyId::$id => {
2177                let value = $map.next_value()?;
2178                $props.set(PropertyId::$id, PropertyValue::$type(value));
2179            })*)*
2180            PropertyId::Unset => {
2181                let _ = $map.next_value::<IgnoredAny>()?;
2182            }
2183        }
2184    }
2185}
2186
2187#[cfg(feature = "serde")]
2188impl Serialize for Properties {
2189    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2190    where
2191        S: Serializer,
2192    {
2193        let mut len = 0;
2194        for value in &*self.values {
2195            if !matches!(*value, PropertyValue::None) {
2196                len += 1;
2197            }
2198        }
2199        let mut map = serializer.serialize_map(Some(len))?;
2200        for (id, index) in self.indices.0.iter().copied().enumerate() {
2201            if index == PropertyId::Unset as u8 {
2202                continue;
2203            }
2204            let id = PropertyId::n(id as _).unwrap();
2205            serialize_property!(self, map, index, id, {
2206                NodeIdVec,
2207                NodeId,
2208                String,
2209                F64,
2210                Usize,
2211                Color,
2212                TextDecoration,
2213                LengthSlice,
2214                CoordSlice,
2215                Bool,
2216                Invalid,
2217                Toggled,
2218                Live,
2219                TextDirection,
2220                Orientation,
2221                SortDirection,
2222                AriaCurrent,
2223                AutoComplete,
2224                HasPopup,
2225                ListStyle,
2226                TextAlign,
2227                VerticalOffset,
2228                Affine,
2229                Rect,
2230                TextSelection,
2231                CustomActionVec
2232            });
2233        }
2234        map.end()
2235    }
2236}
2237
2238#[cfg(feature = "serde")]
2239struct PropertiesVisitor;
2240
2241#[cfg(feature = "serde")]
2242impl<'de> Visitor<'de> for PropertiesVisitor {
2243    type Value = Properties;
2244
2245    #[inline]
2246    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2247        formatter.write_str("property map")
2248    }
2249
2250    fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
2251    where
2252        V: MapAccess<'de>,
2253    {
2254        let mut props = Properties::default();
2255        while let Some(id) = map.next_key()? {
2256            deserialize_property!(props, map, id, {
2257                NodeIdVec {
2258                    Children,
2259                    Controls,
2260                    Details,
2261                    DescribedBy,
2262                    FlowTo,
2263                    LabelledBy,
2264                    Owns,
2265                    RadioGroup
2266                },
2267                NodeId {
2268                    ActiveDescendant,
2269                    ErrorMessage,
2270                    InPageLinkTarget,
2271                    MemberOf,
2272                    NextOnLine,
2273                    PreviousOnLine,
2274                    PopupFor
2275                },
2276                String {
2277                    Label,
2278                    Description,
2279                    Value,
2280                    AccessKey,
2281                    AuthorId,
2282                    ClassName,
2283                    FontFamily,
2284                    HtmlTag,
2285                    InnerHtml,
2286                    KeyboardShortcut,
2287                    Language,
2288                    Placeholder,
2289                    RoleDescription,
2290                    StateDescription,
2291                    Tooltip,
2292                    Url,
2293                    RowIndexText,
2294                    ColumnIndexText
2295                },
2296                F64 {
2297                    ScrollX,
2298                    ScrollXMin,
2299                    ScrollXMax,
2300                    ScrollY,
2301                    ScrollYMin,
2302                    ScrollYMax,
2303                    NumericValue,
2304                    MinNumericValue,
2305                    MaxNumericValue,
2306                    NumericValueStep,
2307                    NumericValueJump,
2308                    FontSize,
2309                    FontWeight
2310                },
2311                Usize {
2312                    RowCount,
2313                    ColumnCount,
2314                    RowIndex,
2315                    ColumnIndex,
2316                    RowSpan,
2317                    ColumnSpan,
2318                    Level,
2319                    SizeOfSet,
2320                    PositionInSet
2321                },
2322                Color {
2323                    ColorValue,
2324                    BackgroundColor,
2325                    ForegroundColor
2326                },
2327                TextDecoration {
2328                    Overline,
2329                    Strikethrough,
2330                    Underline
2331                },
2332                LengthSlice {
2333                    CharacterLengths,
2334                    WordLengths
2335                },
2336                CoordSlice {
2337                    CharacterPositions,
2338                    CharacterWidths
2339                },
2340                Bool {
2341                    Expanded,
2342                    Selected
2343                },
2344                Invalid { Invalid },
2345                Toggled { Toggled },
2346                Live { Live },
2347                TextDirection { TextDirection },
2348                Orientation { Orientation },
2349                SortDirection { SortDirection },
2350                AriaCurrent { AriaCurrent },
2351                AutoComplete { AutoComplete },
2352                HasPopup { HasPopup },
2353                ListStyle { ListStyle },
2354                TextAlign { TextAlign },
2355                VerticalOffset { VerticalOffset },
2356                Affine { Transform },
2357                Rect { Bounds },
2358                TextSelection { TextSelection },
2359                CustomActionVec { CustomActions }
2360            });
2361        }
2362
2363        Ok(props)
2364    }
2365}
2366
2367#[cfg(feature = "serde")]
2368impl<'de> Deserialize<'de> for Properties {
2369    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2370    where
2371        D: Deserializer<'de>,
2372    {
2373        deserializer.deserialize_map(PropertiesVisitor)
2374    }
2375}
2376
2377#[cfg(feature = "schemars")]
2378macro_rules! add_schema_property {
2379    ($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
2380        let name = format!("{:?}", $enum_value);
2381        let name = name[..1].to_ascii_lowercase() + &name[1..];
2382        let subschema = $gen.subschema_for::<$type>();
2383        $properties.insert(name, subschema);
2384    }};
2385}
2386
2387#[cfg(feature = "schemars")]
2388macro_rules! add_properties_to_schema {
2389    ($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
2390        $($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
2391    }
2392}
2393
2394#[cfg(feature = "schemars")]
2395impl JsonSchema for Properties {
2396    #[inline]
2397    fn schema_name() -> String {
2398        "Properties".into()
2399    }
2400
2401    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
2402        let mut properties = SchemaMap::<String, Schema>::new();
2403        add_properties_to_schema!(gen, properties, {
2404            Vec<NodeId> {
2405                Children,
2406                Controls,
2407                Details,
2408                DescribedBy,
2409                FlowTo,
2410                LabelledBy,
2411                Owns,
2412                RadioGroup
2413            },
2414            NodeId {
2415                ActiveDescendant,
2416                ErrorMessage,
2417                InPageLinkTarget,
2418                MemberOf,
2419                NextOnLine,
2420                PreviousOnLine,
2421                PopupFor
2422            },
2423            Box<str> {
2424                Label,
2425                Description,
2426                Value,
2427                AccessKey,
2428                AuthorId,
2429                ClassName,
2430                FontFamily,
2431                HtmlTag,
2432                InnerHtml,
2433                KeyboardShortcut,
2434                Language,
2435                Placeholder,
2436                RoleDescription,
2437                StateDescription,
2438                Tooltip,
2439                Url,
2440                RowIndexText,
2441                ColumnIndexText
2442            },
2443            f64 {
2444                ScrollX,
2445                ScrollXMin,
2446                ScrollXMax,
2447                ScrollY,
2448                ScrollYMin,
2449                ScrollYMax,
2450                NumericValue,
2451                MinNumericValue,
2452                MaxNumericValue,
2453                NumericValueStep,
2454                NumericValueJump,
2455                FontSize,
2456                FontWeight
2457            },
2458            usize {
2459                RowCount,
2460                ColumnCount,
2461                RowIndex,
2462                ColumnIndex,
2463                RowSpan,
2464                ColumnSpan,
2465                Level,
2466                SizeOfSet,
2467                PositionInSet
2468            },
2469            u32 {
2470                ColorValue,
2471                BackgroundColor,
2472                ForegroundColor
2473            },
2474            TextDecoration {
2475                Overline,
2476                Strikethrough,
2477                Underline
2478            },
2479            Box<[u8]> {
2480                CharacterLengths,
2481                WordLengths
2482            },
2483            Box<[f32]> {
2484                CharacterPositions,
2485                CharacterWidths
2486            },
2487            bool {
2488                Expanded,
2489                Selected
2490            },
2491            Invalid { Invalid },
2492            Toggled { Toggled },
2493            Live { Live },
2494            TextDirection { TextDirection },
2495            Orientation { Orientation },
2496            SortDirection { SortDirection },
2497            AriaCurrent { AriaCurrent },
2498            AutoComplete { AutoComplete },
2499            HasPopup { HasPopup },
2500            ListStyle { ListStyle },
2501            TextAlign { TextAlign },
2502            VerticalOffset { VerticalOffset },
2503            Affine { Transform },
2504            Rect { Bounds },
2505            TextSelection { TextSelection },
2506            Vec<CustomAction> { CustomActions }
2507        });
2508        SchemaObject {
2509            instance_type: Some(InstanceType::Object.into()),
2510            object: Some(
2511                ObjectValidation {
2512                    properties,
2513                    ..Default::default()
2514                }
2515                .into(),
2516            ),
2517            ..Default::default()
2518        }
2519        .into()
2520    }
2521}
2522
2523/// The data associated with an accessibility tree that's global to the
2524/// tree and not associated with any particular node.
2525#[derive(Clone, Debug, PartialEq, Eq)]
2526#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2527#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2528#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2529#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2530pub struct Tree {
2531    /// The identifier of the tree's root node.
2532    pub root: NodeId,
2533    /// The name of the UI toolkit in use.
2534    pub toolkit_name: Option<String>,
2535    /// The version of the UI toolkit.
2536    pub toolkit_version: Option<String>,
2537}
2538
2539impl Tree {
2540    #[inline]
2541    pub fn new(root: NodeId) -> Tree {
2542        Tree {
2543            root,
2544            toolkit_name: None,
2545            toolkit_version: None,
2546        }
2547    }
2548}
2549
2550/// A serializable representation of an atomic change to a [`Tree`].
2551///
2552/// The sender and receiver must be in sync; the update is only meant
2553/// to bring the tree from a specific previous state into its next state.
2554/// Trying to apply it to the wrong tree should immediately panic.
2555///
2556/// Note that for performance, an update should only include nodes that are
2557/// new or changed. AccessKit platform adapters will avoid raising extraneous
2558/// events for nodes that have not changed since the previous update,
2559/// but there is still a cost in processing these nodes and replacing
2560/// the previous instances.
2561#[derive(Clone, Debug, PartialEq)]
2562#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2563#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2564#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2565#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2566pub struct TreeUpdate {
2567    /// Zero or more new or updated nodes. Order doesn't matter.
2568    ///
2569    /// Each node in this list will overwrite any existing node with the same ID.
2570    /// This means that when updating a node, fields that are unchanged
2571    /// from the previous version must still be set to the same values
2572    /// as before.
2573    ///
2574    /// It is an error for any node in this list to not be either the root
2575    /// or a child of another node. For nodes other than the root, the parent
2576    /// must be either an unchanged node already in the tree, or another node
2577    /// in this list.
2578    ///
2579    /// To add a child to the tree, the list must include both the child
2580    /// and an updated version of the parent with the child's ID added to
2581    /// [`Node::children`].
2582    ///
2583    /// To remove a child and all of its descendants, this list must include
2584    /// an updated version of the parent node with the child's ID removed
2585    /// from [`Node::children`]. Neither the child nor any of its descendants
2586    /// may be included in this list.
2587    pub nodes: Vec<(NodeId, Node)>,
2588
2589    /// Rarely updated information about the tree as a whole. This may be omitted
2590    /// if it has not changed since the previous update, but providing the same
2591    /// information again is also allowed. This is required when initializing
2592    /// a tree.
2593    pub tree: Option<Tree>,
2594
2595    /// The node within this tree that has keyboard focus when the native
2596    /// host (e.g. window) has focus. If no specific node within the tree
2597    /// has keyboard focus, this must be set to the root. The latest focus state
2598    /// must be provided with every tree update, even if the focus state
2599    /// didn't change in a given update.
2600    pub focus: NodeId,
2601}
2602
2603#[derive(Clone, Debug, PartialEq)]
2604#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2605#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2606#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2607#[repr(C)]
2608pub enum ActionData {
2609    CustomAction(i32),
2610    Value(Box<str>),
2611    NumericValue(f64),
2612    /// Optional target rectangle for [`Action::ScrollIntoView`], in
2613    /// the coordinate space of the action's target node.
2614    ScrollTargetRect(Rect),
2615    /// Target for [`Action::ScrollToPoint`], in platform-native coordinates
2616    /// relative to the origin of the tree's container (e.g. window).
2617    ScrollToPoint(Point),
2618    /// Target for [`Action::SetScrollOffset`], in the coordinate space
2619    /// of the action's target node.
2620    SetScrollOffset(Point),
2621    SetTextSelection(TextSelection),
2622}
2623
2624#[derive(Clone, Debug, PartialEq)]
2625#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2626#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2627#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2628#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2629pub struct ActionRequest {
2630    pub action: Action,
2631    pub target: NodeId,
2632    pub data: Option<ActionData>,
2633}
2634
2635/// Handles activation of the application's accessibility implementation.
2636pub trait ActivationHandler {
2637    /// Requests a [`TreeUpdate`] with a full tree. If the application
2638    /// can generate the tree synchronously within this method call,
2639    /// it should do so and return the [`TreeUpdate`]. Otherwise,
2640    /// it must send the update to the platform adapter asynchronously,
2641    /// no later than the next display refresh, even if a frame would not
2642    /// normally be rendered due to user input or other activity.
2643    /// The application should not return or send a placeholder [`TreeUpdate`];
2644    /// the platform adapter will provide one if necessary until the real
2645    /// tree is sent.
2646    ///
2647    /// The primary purpose of this method is to allow the application
2648    /// to lazily initialize its accessibility implementation. However,
2649    /// this method may be called consecutively without any call to
2650    /// [`DeactivationHandler::deactivate_accessibility`]; this typically happens
2651    /// if the platform adapter merely forwards tree updates to assistive
2652    /// technologies without maintaining any state. A call to this method
2653    /// must always generate a [`TreeUpdate`] with a full tree, even if
2654    /// the application normally sends incremental updates.
2655    ///
2656    /// The thread on which this method is called is platform-dependent.
2657    /// Refer to the platform adapter documentation for more details.
2658    fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
2659}
2660
2661/// Handles requests from assistive technologies or other clients.
2662pub trait ActionHandler {
2663    /// Perform the requested action. If the requested action is not supported,
2664    /// this method must do nothing.
2665    ///
2666    /// The thread on which this method is called is platform-dependent.
2667    /// Refer to the platform adapter documentation for more details.
2668    ///
2669    /// This method may queue the request and handle it asynchronously.
2670    /// This behavior is preferred over blocking, e.g. when dispatching
2671    /// the request to another thread.
2672    fn do_action(&mut self, request: ActionRequest);
2673}
2674
2675/// Handles deactivation of the application's accessibility implementation.
2676pub trait DeactivationHandler {
2677    /// Deactivate the application's accessibility implementation and drop any
2678    /// associated data that can be reconstructed later. After this method
2679    /// is called, if an accessibility tree is needed again, the platform
2680    /// adapter will call [`ActivationHandler::request_initial_tree`] again.
2681    ///
2682    /// The thread on which this method is called is platform-dependent.
2683    /// Refer to the platform adapter documentation for more details.
2684    fn deactivate_accessibility(&mut self);
2685}
2686
2687#[cfg(test)]
2688mod tests {
2689    use super::*;
2690    use alloc::format;
2691
2692    #[test]
2693    fn u64_should_be_convertible_to_node_id() {
2694        assert_eq!(NodeId::from(0u64), NodeId(0));
2695        assert_eq!(NodeId::from(1u64), NodeId(1));
2696    }
2697
2698    #[test]
2699    fn node_id_should_be_convertible_to_u64() {
2700        assert_eq!(u64::from(NodeId(0)), 0u64);
2701        assert_eq!(u64::from(NodeId(1)), 1u64);
2702    }
2703
2704    #[test]
2705    fn node_id_should_have_debug_repr() {
2706        assert_eq!(&format!("{:?}", NodeId(0)), "#0");
2707        assert_eq!(&format!("{:?}", NodeId(1)), "#1");
2708    }
2709
2710    #[test]
2711    fn action_n_should_return_the_corresponding_variant() {
2712        assert_eq!(Action::n(0), Some(Action::Click));
2713        assert_eq!(Action::n(1), Some(Action::Focus));
2714        assert_eq!(Action::n(2), Some(Action::Blur));
2715        assert_eq!(Action::n(3), Some(Action::Collapse));
2716        assert_eq!(Action::n(4), Some(Action::Expand));
2717        assert_eq!(Action::n(5), Some(Action::CustomAction));
2718        assert_eq!(Action::n(6), Some(Action::Decrement));
2719        assert_eq!(Action::n(7), Some(Action::Increment));
2720        assert_eq!(Action::n(8), Some(Action::HideTooltip));
2721        assert_eq!(Action::n(9), Some(Action::ShowTooltip));
2722        assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
2723        assert_eq!(Action::n(11), Some(Action::ScrollBackward));
2724        assert_eq!(Action::n(12), Some(Action::ScrollDown));
2725        assert_eq!(Action::n(13), Some(Action::ScrollForward));
2726        assert_eq!(Action::n(14), Some(Action::ScrollLeft));
2727        assert_eq!(Action::n(15), Some(Action::ScrollRight));
2728        assert_eq!(Action::n(16), Some(Action::ScrollUp));
2729        assert_eq!(Action::n(17), Some(Action::ScrollIntoView));
2730        assert_eq!(Action::n(18), Some(Action::ScrollToPoint));
2731        assert_eq!(Action::n(19), Some(Action::SetScrollOffset));
2732        assert_eq!(Action::n(20), Some(Action::SetTextSelection));
2733        assert_eq!(
2734            Action::n(21),
2735            Some(Action::SetSequentialFocusNavigationStartingPoint)
2736        );
2737        assert_eq!(Action::n(22), Some(Action::SetValue));
2738        assert_eq!(Action::n(23), Some(Action::ShowContextMenu));
2739        assert_eq!(Action::n(24), None);
2740    }
2741
2742    #[test]
2743    fn empty_action_mask_should_be_converted_to_empty_vec() {
2744        assert_eq!(
2745            Vec::<Action>::new(),
2746            action_mask_to_action_vec(Node::new(Role::Unknown).actions)
2747        );
2748    }
2749
2750    #[test]
2751    fn action_mask_should_be_convertible_to_vec() {
2752        let mut node = Node::new(Role::Unknown);
2753        node.add_action(Action::Click);
2754        assert_eq!(
2755            &[Action::Click],
2756            action_mask_to_action_vec(node.actions).as_slice()
2757        );
2758
2759        let mut node = Node::new(Role::Unknown);
2760        node.add_action(Action::ShowContextMenu);
2761        assert_eq!(
2762            &[Action::ShowContextMenu],
2763            action_mask_to_action_vec(node.actions).as_slice()
2764        );
2765
2766        let mut node = Node::new(Role::Unknown);
2767        node.add_action(Action::Click);
2768        node.add_action(Action::ShowContextMenu);
2769        assert_eq!(
2770            &[Action::Click, Action::ShowContextMenu],
2771            action_mask_to_action_vec(node.actions).as_slice()
2772        );
2773
2774        let mut node = Node::new(Role::Unknown);
2775        node.add_action(Action::Focus);
2776        node.add_action(Action::Blur);
2777        node.add_action(Action::Collapse);
2778        assert_eq!(
2779            &[Action::Focus, Action::Blur, Action::Collapse],
2780            action_mask_to_action_vec(node.actions).as_slice()
2781        );
2782    }
2783
2784    #[test]
2785    fn new_node_should_have_user_provided_role() {
2786        let node = Node::new(Role::Button);
2787        assert_eq!(node.role(), Role::Button);
2788    }
2789
2790    #[test]
2791    fn node_role_setter_should_update_the_role() {
2792        let mut node = Node::new(Role::Button);
2793        node.set_role(Role::CheckBox);
2794        assert_eq!(node.role(), Role::CheckBox);
2795    }
2796
2797    #[test]
2798    fn new_node_should_not_support_anyaction() {
2799        let node = Node::new(Role::Unknown);
2800        assert!(!node.supports_action(Action::Click));
2801        assert!(!node.supports_action(Action::Focus));
2802        assert!(!node.supports_action(Action::Blur));
2803        assert!(!node.supports_action(Action::Collapse));
2804        assert!(!node.supports_action(Action::Expand));
2805        assert!(!node.supports_action(Action::CustomAction));
2806        assert!(!node.supports_action(Action::Decrement));
2807        assert!(!node.supports_action(Action::Increment));
2808        assert!(!node.supports_action(Action::HideTooltip));
2809        assert!(!node.supports_action(Action::ShowTooltip));
2810        assert!(!node.supports_action(Action::ReplaceSelectedText));
2811        assert!(!node.supports_action(Action::ScrollBackward));
2812        assert!(!node.supports_action(Action::ScrollDown));
2813        assert!(!node.supports_action(Action::ScrollForward));
2814        assert!(!node.supports_action(Action::ScrollLeft));
2815        assert!(!node.supports_action(Action::ScrollRight));
2816        assert!(!node.supports_action(Action::ScrollUp));
2817        assert!(!node.supports_action(Action::ScrollIntoView));
2818        assert!(!node.supports_action(Action::ScrollToPoint));
2819        assert!(!node.supports_action(Action::SetScrollOffset));
2820        assert!(!node.supports_action(Action::SetTextSelection));
2821        assert!(!node.supports_action(Action::SetSequentialFocusNavigationStartingPoint));
2822        assert!(!node.supports_action(Action::SetValue));
2823        assert!(!node.supports_action(Action::ShowContextMenu));
2824    }
2825
2826    #[test]
2827    fn node_add_action_should_add_the_action() {
2828        let mut node = Node::new(Role::Unknown);
2829        node.add_action(Action::Focus);
2830        assert!(node.supports_action(Action::Focus));
2831        node.add_action(Action::Blur);
2832        assert!(node.supports_action(Action::Blur));
2833    }
2834
2835    #[test]
2836    fn node_add_action_should_do_nothing_if_the_action_is_already_supported() {
2837        let mut node = Node::new(Role::Unknown);
2838        node.add_action(Action::Focus);
2839        node.add_action(Action::Focus);
2840        assert!(node.supports_action(Action::Focus));
2841    }
2842
2843    #[test]
2844    fn node_remove_action_should_remove_the_action() {
2845        let mut node = Node::new(Role::Unknown);
2846        node.add_action(Action::Blur);
2847        node.remove_action(Action::Blur);
2848        assert!(!node.supports_action(Action::Blur));
2849    }
2850
2851    #[test]
2852    fn node_clear_actions_should_remove_all_actions() {
2853        let mut node = Node::new(Role::Unknown);
2854        node.add_action(Action::Focus);
2855        node.add_action(Action::Blur);
2856        node.clear_actions();
2857        assert!(!node.supports_action(Action::Focus));
2858        assert!(!node.supports_action(Action::Blur));
2859    }
2860
2861    #[test]
2862    fn node_should_have_debug_repr() {
2863        let mut node = Node::new(Role::Unknown);
2864        node.add_action(Action::Click);
2865        node.add_action(Action::Focus);
2866        node.set_hidden();
2867        node.set_multiselectable();
2868        node.set_children([NodeId(0), NodeId(1)]);
2869        node.set_active_descendant(NodeId(2));
2870        node.push_custom_action(CustomAction {
2871            id: 0,
2872            description: "test action".into(),
2873        });
2874
2875        assert_eq!(
2876            &format!("{:?}", node),
2877            r#"Node { role: Unknown, actions: [Click, Focus], is_hidden: true, is_multiselectable: true, children: [#0, #1], active_descendant: #2, custom_actions: [CustomAction { id: 0, description: "test action" }] }"#
2878        );
2879    }
2880
2881    #[test]
2882    fn new_tree_should_have_root_id() {
2883        let tree = Tree::new(NodeId(1));
2884        assert_eq!(tree.root, NodeId(1));
2885        assert_eq!(tree.toolkit_name, None);
2886        assert_eq!(tree.toolkit_version, None);
2887    }
2888}