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