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 node visible.
317    /// Optionally set [`ActionRequest::data`] to [`ActionData::ScrollHint`].
318    ScrollIntoView,
319
320    /// Scroll the given object to a specified point in the tree's container
321    /// (e.g. window). Requires [`ActionRequest::data`] to be set to
322    /// [`ActionData::ScrollToPoint`].
323    ScrollToPoint,
324
325    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetScrollOffset`].
326    SetScrollOffset,
327
328    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetTextSelection`].
329    SetTextSelection,
330
331    /// Don't focus this node, but set it as the sequential focus navigation
332    /// starting point, so that pressing Tab moves to the next element
333    /// following this one, for example.
334    SetSequentialFocusNavigationStartingPoint,
335
336    /// Replace the value of the control with the specified value and
337    /// reset the selection, if applicable. Requires [`ActionRequest::data`]
338    /// to be set to [`ActionData::Value`] or [`ActionData::NumericValue`].
339    SetValue,
340
341    ShowContextMenu,
342}
343
344impl Action {
345    fn mask(self) -> u32 {
346        1 << (self as u8)
347    }
348
349    #[cfg(not(feature = "enumn"))]
350    fn n(value: u8) -> Option<Self> {
351        // Manually implement something similar to the enumn crate. We don't
352        // want to bring this crate by default though and we can't use a
353        // macro as it would break C bindings header file generation.
354        match value {
355            0 => Some(Action::Click),
356            1 => Some(Action::Focus),
357            2 => Some(Action::Blur),
358            3 => Some(Action::Collapse),
359            4 => Some(Action::Expand),
360            5 => Some(Action::CustomAction),
361            6 => Some(Action::Decrement),
362            7 => Some(Action::Increment),
363            8 => Some(Action::HideTooltip),
364            9 => Some(Action::ShowTooltip),
365            10 => Some(Action::ReplaceSelectedText),
366            11 => Some(Action::ScrollDown),
367            12 => Some(Action::ScrollLeft),
368            13 => Some(Action::ScrollRight),
369            14 => Some(Action::ScrollUp),
370            15 => Some(Action::ScrollIntoView),
371            16 => Some(Action::ScrollToPoint),
372            17 => Some(Action::SetScrollOffset),
373            18 => Some(Action::SetTextSelection),
374            19 => Some(Action::SetSequentialFocusNavigationStartingPoint),
375            20 => Some(Action::SetValue),
376            21 => Some(Action::ShowContextMenu),
377            _ => None,
378        }
379    }
380}
381
382fn action_mask_to_action_vec(mask: u32) -> Vec<Action> {
383    let mut actions = Vec::new();
384    let mut i = 0;
385    while let Some(variant) = Action::n(i) {
386        if mask & variant.mask() != 0 {
387            actions.push(variant);
388        }
389        i += 1;
390    }
391    actions
392}
393
394#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
395#[cfg_attr(feature = "enumn", derive(enumn::N))]
396#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
397#[cfg_attr(feature = "schemars", derive(JsonSchema))]
398#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
399#[cfg_attr(
400    feature = "pyo3",
401    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
402)]
403#[repr(u8)]
404pub enum Orientation {
405    /// E.g. most toolbars and separators.
406    Horizontal,
407    /// E.g. menu or combo box.
408    Vertical,
409}
410
411#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
412#[cfg_attr(feature = "enumn", derive(enumn::N))]
413#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
414#[cfg_attr(feature = "schemars", derive(JsonSchema))]
415#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
416#[cfg_attr(
417    feature = "pyo3",
418    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
419)]
420#[repr(u8)]
421pub enum TextDirection {
422    LeftToRight,
423    RightToLeft,
424    TopToBottom,
425    BottomToTop,
426}
427
428/// Indicates if a form control has invalid input or if a web DOM element has an
429/// [`aria-invalid`] attribute.
430///
431/// [`aria-invalid`]: https://www.w3.org/TR/wai-aria-1.1/#aria-invalid
432#[derive(Clone, Copy, Debug, PartialEq, Eq)]
433#[cfg_attr(feature = "enumn", derive(enumn::N))]
434#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
435#[cfg_attr(feature = "schemars", derive(JsonSchema))]
436#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
437#[cfg_attr(
438    feature = "pyo3",
439    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
440)]
441#[repr(u8)]
442pub enum Invalid {
443    True,
444    Grammar,
445    Spelling,
446}
447
448#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
449#[cfg_attr(feature = "enumn", derive(enumn::N))]
450#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
451#[cfg_attr(feature = "schemars", derive(JsonSchema))]
452#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
453#[cfg_attr(
454    feature = "pyo3",
455    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
456)]
457#[repr(u8)]
458pub enum Toggled {
459    False,
460    True,
461    Mixed,
462}
463
464impl From<bool> for Toggled {
465    #[inline]
466    fn from(b: bool) -> Self {
467        match b {
468            false => Self::False,
469            true => Self::True,
470        }
471    }
472}
473
474#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
475#[cfg_attr(feature = "enumn", derive(enumn::N))]
476#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
477#[cfg_attr(feature = "schemars", derive(JsonSchema))]
478#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
479#[cfg_attr(
480    feature = "pyo3",
481    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
482)]
483#[repr(u8)]
484pub enum SortDirection {
485    Ascending,
486    Descending,
487    Other,
488}
489
490#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
491#[cfg_attr(feature = "enumn", derive(enumn::N))]
492#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
493#[cfg_attr(feature = "schemars", derive(JsonSchema))]
494#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
495#[cfg_attr(
496    feature = "pyo3",
497    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
498)]
499#[repr(u8)]
500pub enum AriaCurrent {
501    False,
502    True,
503    Page,
504    Step,
505    Location,
506    Date,
507    Time,
508}
509
510#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
511#[cfg_attr(feature = "enumn", derive(enumn::N))]
512#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
513#[cfg_attr(feature = "schemars", derive(JsonSchema))]
514#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
515#[cfg_attr(
516    feature = "pyo3",
517    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
518)]
519#[repr(u8)]
520pub enum AutoComplete {
521    Inline,
522    List,
523    Both,
524}
525
526#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
527#[cfg_attr(feature = "enumn", derive(enumn::N))]
528#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
529#[cfg_attr(feature = "schemars", derive(JsonSchema))]
530#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
531#[cfg_attr(
532    feature = "pyo3",
533    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
534)]
535#[repr(u8)]
536pub enum Live {
537    Off,
538    Polite,
539    Assertive,
540}
541
542#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
543#[cfg_attr(feature = "enumn", derive(enumn::N))]
544#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
545#[cfg_attr(feature = "schemars", derive(JsonSchema))]
546#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
547#[cfg_attr(
548    feature = "pyo3",
549    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
550)]
551#[repr(u8)]
552pub enum HasPopup {
553    Menu,
554    Listbox,
555    Tree,
556    Grid,
557    Dialog,
558}
559
560#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
561#[cfg_attr(feature = "enumn", derive(enumn::N))]
562#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
563#[cfg_attr(feature = "schemars", derive(JsonSchema))]
564#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
565#[cfg_attr(
566    feature = "pyo3",
567    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
568)]
569#[repr(u8)]
570pub enum ListStyle {
571    Circle,
572    Disc,
573    Image,
574    Numeric,
575    Square,
576    /// Language specific ordering (alpha, roman, cjk-ideographic, etc...)
577    Other,
578}
579
580#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
581#[cfg_attr(feature = "enumn", derive(enumn::N))]
582#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
583#[cfg_attr(feature = "schemars", derive(JsonSchema))]
584#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
585#[cfg_attr(
586    feature = "pyo3",
587    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
588)]
589#[repr(u8)]
590pub enum TextAlign {
591    Left,
592    Right,
593    Center,
594    Justify,
595}
596
597#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
598#[cfg_attr(feature = "enumn", derive(enumn::N))]
599#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
600#[cfg_attr(feature = "schemars", derive(JsonSchema))]
601#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
602#[cfg_attr(
603    feature = "pyo3",
604    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
605)]
606#[repr(u8)]
607pub enum VerticalOffset {
608    Subscript,
609    Superscript,
610}
611
612#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
613#[cfg_attr(feature = "enumn", derive(enumn::N))]
614#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
615#[cfg_attr(feature = "schemars", derive(JsonSchema))]
616#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
617#[cfg_attr(
618    feature = "pyo3",
619    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
620)]
621#[repr(u8)]
622pub enum TextDecoration {
623    Solid,
624    Dotted,
625    Dashed,
626    Double,
627    Wavy,
628}
629
630pub type NodeIdContent = u64;
631
632/// The stable identity of a [`Node`], unique within the node's tree.
633#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
634#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
635#[cfg_attr(feature = "schemars", derive(JsonSchema))]
636#[repr(transparent)]
637pub struct NodeId(pub NodeIdContent);
638
639impl From<NodeIdContent> for NodeId {
640    #[inline]
641    fn from(inner: NodeIdContent) -> Self {
642        Self(inner)
643    }
644}
645
646impl From<NodeId> for NodeIdContent {
647    #[inline]
648    fn from(outer: NodeId) -> Self {
649        outer.0
650    }
651}
652
653impl fmt::Debug for NodeId {
654    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655        write!(f, "#{}", self.0)
656    }
657}
658
659/// Defines a custom action for a UI element.
660///
661/// For example, a list UI can allow a user to reorder items in the list by dragging the
662/// items.
663#[derive(Clone, Debug, PartialEq, Eq)]
664#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
665#[cfg_attr(feature = "schemars", derive(JsonSchema))]
666#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
667#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
668pub struct CustomAction {
669    pub id: i32,
670    pub description: Box<str>,
671}
672
673#[derive(Clone, Copy, Debug, PartialEq, Eq)]
674#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
675#[cfg_attr(feature = "schemars", derive(JsonSchema))]
676#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
677#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
678pub struct TextPosition {
679    /// The node's role must be [`Role::TextRun`].
680    pub node: NodeId,
681    /// The index of an item in [`Node::character_lengths`], or the length
682    /// of that slice if the position is at the end of the line.
683    pub character_index: usize,
684}
685
686#[derive(Clone, Copy, Debug, PartialEq, Eq)]
687#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
688#[cfg_attr(feature = "schemars", derive(JsonSchema))]
689#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
690#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
691pub struct TextSelection {
692    /// The position where the selection started, and which does not change
693    /// as the selection is expanded or contracted. If there is no selection
694    /// but only a caret, this must be equal to the value of [`TextSelection::focus`].
695    /// This is also known as a degenerate selection.
696    pub anchor: TextPosition,
697    /// The active end of the selection, which changes as the selection
698    /// is expanded or contracted, or the position of the caret if there is
699    /// no selection.
700    pub focus: TextPosition,
701}
702
703#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
704#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
705#[cfg_attr(feature = "schemars", derive(JsonSchema))]
706#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
707#[repr(u8)]
708enum Flag {
709    Hidden,
710    Multiselectable,
711    Required,
712    Visited,
713    Busy,
714    LiveAtomic,
715    Modal,
716    TouchTransparent,
717    ReadOnly,
718    Disabled,
719    Bold,
720    Italic,
721    ClipsChildren,
722    IsLineBreakingObject,
723    IsPageBreakingObject,
724    IsSpellingError,
725    IsGrammarError,
726    IsSearchMatch,
727    IsSuggestion,
728}
729
730impl Flag {
731    fn mask(self) -> u32 {
732        1 << (self as u8)
733    }
734}
735
736// The following is based on the technique described here:
737// https://viruta.org/reducing-memory-consumption-in-librsvg-2.html
738
739#[derive(Clone, Debug, PartialEq)]
740enum PropertyValue {
741    None,
742    NodeIdVec(Vec<NodeId>),
743    NodeId(NodeId),
744    String(Box<str>),
745    F64(f64),
746    Usize(usize),
747    Color(u32),
748    TextDecoration(TextDecoration),
749    LengthSlice(Box<[u8]>),
750    CoordSlice(Box<[f32]>),
751    Bool(bool),
752    Invalid(Invalid),
753    Toggled(Toggled),
754    Live(Live),
755    TextDirection(TextDirection),
756    Orientation(Orientation),
757    SortDirection(SortDirection),
758    AriaCurrent(AriaCurrent),
759    AutoComplete(AutoComplete),
760    HasPopup(HasPopup),
761    ListStyle(ListStyle),
762    TextAlign(TextAlign),
763    VerticalOffset(VerticalOffset),
764    Affine(Box<Affine>),
765    Rect(Rect),
766    TextSelection(Box<TextSelection>),
767    CustomActionVec(Vec<CustomAction>),
768}
769
770#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
771#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
772#[cfg_attr(feature = "schemars", derive(JsonSchema))]
773#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
774#[repr(u8)]
775enum PropertyId {
776    // NodeIdVec
777    Children,
778    Controls,
779    Details,
780    DescribedBy,
781    FlowTo,
782    LabelledBy,
783    Owns,
784    RadioGroup,
785
786    // NodeId
787    ActiveDescendant,
788    ErrorMessage,
789    InPageLinkTarget,
790    MemberOf,
791    NextOnLine,
792    PreviousOnLine,
793    PopupFor,
794
795    // String
796    Label,
797    Description,
798    Value,
799    AccessKey,
800    AuthorId,
801    ClassName,
802    FontFamily,
803    HtmlTag,
804    InnerHtml,
805    KeyboardShortcut,
806    Language,
807    Placeholder,
808    RoleDescription,
809    StateDescription,
810    Tooltip,
811    Url,
812    RowIndexText,
813    ColumnIndexText,
814
815    // f64
816    ScrollX,
817    ScrollXMin,
818    ScrollXMax,
819    ScrollY,
820    ScrollYMin,
821    ScrollYMax,
822    NumericValue,
823    MinNumericValue,
824    MaxNumericValue,
825    NumericValueStep,
826    NumericValueJump,
827    FontSize,
828    FontWeight,
829
830    // usize
831    RowCount,
832    ColumnCount,
833    RowIndex,
834    ColumnIndex,
835    RowSpan,
836    ColumnSpan,
837    Level,
838    SizeOfSet,
839    PositionInSet,
840
841    // Color
842    ColorValue,
843    BackgroundColor,
844    ForegroundColor,
845
846    // TextDecoration
847    Overline,
848    Strikethrough,
849    Underline,
850
851    // LengthSlice
852    CharacterLengths,
853    WordLengths,
854
855    // CoordSlice
856    CharacterPositions,
857    CharacterWidths,
858
859    // bool
860    Expanded,
861    Selected,
862
863    // Unique enums
864    Invalid,
865    Toggled,
866    Live,
867    TextDirection,
868    Orientation,
869    SortDirection,
870    AriaCurrent,
871    AutoComplete,
872    HasPopup,
873    ListStyle,
874    TextAlign,
875    VerticalOffset,
876
877    // Other
878    Transform,
879    Bounds,
880    TextSelection,
881    CustomActions,
882
883    // This MUST be last.
884    Unset,
885}
886
887#[derive(Clone, Copy, Debug, PartialEq, Eq)]
888#[repr(transparent)]
889struct PropertyIndices([u8; PropertyId::Unset as usize]);
890
891impl Default for PropertyIndices {
892    fn default() -> Self {
893        Self([PropertyId::Unset as u8; PropertyId::Unset as usize])
894    }
895}
896
897#[derive(Clone, Debug, Default, PartialEq)]
898struct Properties {
899    indices: PropertyIndices,
900    values: Vec<PropertyValue>,
901}
902
903/// A single accessible object. A complete UI is represented as a tree of these.
904///
905/// For brevity, and to make more of the documentation usable in bindings
906/// to other languages, documentation of getter methods is written as if
907/// documenting fields in a struct, and such methods are referred to
908/// as properties.
909#[derive(Clone, Default, PartialEq)]
910#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
911#[cfg_attr(feature = "schemars", derive(JsonSchema))]
912#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
913#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
914pub struct Node {
915    role: Role,
916    actions: u32,
917    child_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    /// Return whether the specified action is in the set supported on this node's
1605    /// direct children in the filtered tree.
1606    #[inline]
1607    pub fn child_supports_action(&self, action: Action) -> bool {
1608        (self.child_actions & action.mask()) != 0
1609    }
1610    /// Add the specified action to the set supported on this node's direct
1611    /// children in the filtered tree.
1612    #[inline]
1613    pub fn add_child_action(&mut self, action: Action) {
1614        self.child_actions |= action.mask();
1615    }
1616    /// Remove the specified action from the set supported on this node's direct
1617    /// children in the filtered tree.
1618    #[inline]
1619    pub fn remove_child_action(&mut self, action: Action) {
1620        self.child_actions &= !(action.mask());
1621    }
1622    /// Clear the set of actions supported on this node's direct children in the
1623    /// filtered tree.
1624    #[inline]
1625    pub fn clear_child_actions(&mut self) {
1626        self.child_actions = 0;
1627    }
1628}
1629
1630flag_methods! {
1631    /// Exclude this node and its descendants from the tree presented to
1632    /// assistive technologies, and from hit testing.
1633    (Hidden, is_hidden, set_hidden, clear_hidden),
1634    (Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
1635    (Required, is_required, set_required, clear_required),
1636    (Visited, is_visited, set_visited, clear_visited),
1637    (Busy, is_busy, set_busy, clear_busy),
1638    (LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
1639    /// If a dialog box is marked as explicitly modal.
1640    (Modal, is_modal, set_modal, clear_modal),
1641    /// This element allows touches to be passed through when a screen reader
1642    /// is in touch exploration mode, e.g. a virtual keyboard normally
1643    /// behaves this way.
1644    (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1645    /// Use for a text widget that allows focus/selection but not input.
1646    (ReadOnly, is_read_only, set_read_only, clear_read_only),
1647    /// Use for a control or group of controls that disallows input.
1648    (Disabled, is_disabled, set_disabled, clear_disabled),
1649    (Bold, is_bold, set_bold, clear_bold),
1650    (Italic, is_italic, set_italic, clear_italic),
1651    /// Indicates that this node clips its children, i.e. may have
1652    /// `overflow: hidden` or clip children by default.
1653    (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1654    /// Indicates whether this node causes a hard line-break
1655    /// (e.g. block level elements, or `<br>`).
1656    (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1657    /// Indicates whether this node causes a page break.
1658    (IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
1659    (IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
1660    (IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
1661    (IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
1662    (IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
1663}
1664
1665option_ref_type_getters! {
1666    (get_affine_property, Affine, Affine),
1667    (get_string_property, str, String),
1668    (get_coord_slice_property, [f32], CoordSlice),
1669    (get_text_selection_property, TextSelection, TextSelection)
1670}
1671
1672slice_type_getters! {
1673    (get_length_slice_property, u8, LengthSlice)
1674}
1675
1676copy_type_getters! {
1677    (get_rect_property, Rect, Rect),
1678    (get_node_id_property, NodeId, NodeId),
1679    (get_f64_property, f64, F64),
1680    (get_usize_property, usize, Usize),
1681    (get_color_property, u32, Color),
1682    (get_text_decoration_property, TextDecoration, TextDecoration),
1683    (get_bool_property, bool, Bool)
1684}
1685
1686box_type_setters! {
1687    (set_affine_property, Affine, Affine),
1688    (set_string_property, str, String),
1689    (set_length_slice_property, [u8], LengthSlice),
1690    (set_coord_slice_property, [f32], CoordSlice),
1691    (set_text_selection_property, TextSelection, TextSelection)
1692}
1693
1694copy_type_setters! {
1695    (set_rect_property, Rect, Rect),
1696    (set_node_id_property, NodeId, NodeId),
1697    (set_f64_property, f64, F64),
1698    (set_usize_property, usize, Usize),
1699    (set_color_property, u32, Color),
1700    (set_text_decoration_property, TextDecoration, TextDecoration),
1701    (set_bool_property, bool, Bool)
1702}
1703
1704vec_type_methods! {
1705    (NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
1706    (CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
1707}
1708
1709node_id_vec_property_methods! {
1710    (Children, children, set_children, push_child, clear_children),
1711    (Controls, controls, set_controls, push_controlled, clear_controls),
1712    (Details, details, set_details, push_detail, clear_details),
1713    (DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
1714    (FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
1715    (LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
1716    /// As with the `aria-owns` property in ARIA, this property should be set
1717    /// only if the nodes referenced in the property are not descendants
1718    /// of the owning node in the AccessKit tree. In the common case, where the
1719    /// owned nodes are direct children or indirect descendants, this property
1720    /// is unnecessary.
1721    (Owns, owns, set_owns, push_owned, clear_owns),
1722    /// On radio buttons this should be set to a list of all of the buttons
1723    /// in the same group as this one, including this radio button itself.
1724    (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
1725}
1726
1727node_id_property_methods! {
1728    (ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
1729    (ErrorMessage, error_message, set_error_message, clear_error_message),
1730    (InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
1731    (MemberOf, member_of, set_member_of, clear_member_of),
1732    (NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
1733    (PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
1734    (PopupFor, popup_for, set_popup_for, clear_popup_for)
1735}
1736
1737string_property_methods! {
1738    /// The label of a control that can have a label. If the label is specified
1739    /// via the [`Node::labelled_by`] relation, this doesn't need to be set.
1740    /// Note that the text content of a node with the [`Role::Label`] role
1741    /// should be provided via [`Node::value`], not this property.
1742    (Label, label, set_label, clear_label),
1743    (Description, description, set_description, clear_description),
1744    (Value, value, set_value, clear_value),
1745    /// A single character, usually part of this node's name, that can be pressed,
1746    /// possibly along with a platform-specific modifier, to perform
1747    /// this node's default action. For menu items, the access key is only active
1748    /// while the menu is active, in contrast with [`keyboard_shortcut`];
1749    /// a single menu item may in fact have both properties.
1750    ///
1751    /// [`keyboard_shortcut`]: Node::keyboard_shortcut
1752    (AccessKey, access_key, set_access_key, clear_access_key),
1753    /// A way for application authors to identify this node for automated
1754    /// testing purpose. The value must be unique among this node's siblings.
1755    (AuthorId, author_id, set_author_id, clear_author_id),
1756    (ClassName, class_name, set_class_name, clear_class_name),
1757    /// Only present when different from parent.
1758    (FontFamily, font_family, set_font_family, clear_font_family),
1759    (HtmlTag, html_tag, set_html_tag, clear_html_tag),
1760    /// Inner HTML of an element. Only used for a top-level math element,
1761    /// to support third-party math accessibility products that parse MathML.
1762    (InnerHtml, inner_html, set_inner_html, clear_inner_html),
1763    /// A keystroke or sequence of keystrokes, complete with any required
1764    /// modifiers(s), that will perform this node's default action.
1765    /// The value of this property should be in a human-friendly format.
1766    (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
1767    /// Only present when different from parent.
1768    (Language, language, set_language, clear_language),
1769    /// If a text input has placeholder text, it should be exposed
1770    /// through this property rather than [`label`].
1771    ///
1772    /// [`label`]: Node::label
1773    (Placeholder, placeholder, set_placeholder, clear_placeholder),
1774    /// An optional string that may override an assistive technology's
1775    /// description of the node's role. Only provide this for custom control types.
1776    /// The value of this property should be in a human-friendly, localized format.
1777    (RoleDescription, role_description, set_role_description, clear_role_description),
1778    /// An optional string that may override an assistive technology's
1779    /// description of the node's state, replacing default strings such as
1780    /// "checked" or "selected". Note that most platform accessibility APIs
1781    /// and assistive technologies do not support this feature.
1782    (StateDescription, state_description, set_state_description, clear_state_description),
1783    /// If a node's only accessible name comes from a tooltip, it should be
1784    /// exposed through this property rather than [`label`].
1785    ///
1786    /// [`label`]: Node::label
1787    (Tooltip, tooltip, set_tooltip, clear_tooltip),
1788    (Url, url, set_url, clear_url),
1789    (RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
1790    (ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text)
1791}
1792
1793f64_property_methods! {
1794    (ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
1795    (ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
1796    (ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
1797    (ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
1798    (ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
1799    (ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
1800    (NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
1801    (MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
1802    (MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
1803    (NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
1804    (NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump),
1805    /// Font size is in pixels.
1806    (FontSize, font_size, set_font_size, clear_font_size),
1807    /// Font weight can take on any arbitrary numeric value. Increments of 100 in
1808    /// range `[0, 900]` represent keywords such as light, normal, bold, etc.
1809    (FontWeight, font_weight, set_font_weight, clear_font_weight)
1810}
1811
1812usize_property_methods! {
1813    (RowCount, row_count, set_row_count, clear_row_count),
1814    (ColumnCount, column_count, set_column_count, clear_column_count),
1815    (RowIndex, row_index, set_row_index, clear_row_index),
1816    (ColumnIndex, column_index, set_column_index, clear_column_index),
1817    (RowSpan, row_span, set_row_span, clear_row_span),
1818    (ColumnSpan, column_span, set_column_span, clear_column_span),
1819    (Level, level, set_level, clear_level),
1820    /// For containers like [`Role::ListBox`], specifies the total number of items.
1821    (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
1822    /// For items like [`Role::ListBoxOption`], specifies their index in the item list.
1823    /// This may not exceed the value of [`size_of_set`] as set on the container.
1824    ///
1825    /// [`size_of_set`]: Node::size_of_set
1826    (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
1827}
1828
1829color_property_methods! {
1830    /// For [`Role::ColorWell`], specifies the selected color in RGBA.
1831    (ColorValue, color_value, set_color_value, clear_color_value),
1832    /// Background color in RGBA.
1833    (BackgroundColor, background_color, set_background_color, clear_background_color),
1834    /// Foreground color in RGBA.
1835    (ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
1836}
1837
1838text_decoration_property_methods! {
1839    (Overline, overline, set_overline, clear_overline),
1840    (Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
1841    (Underline, underline, set_underline, clear_underline)
1842}
1843
1844length_slice_property_methods! {
1845    /// For text runs, the length (non-inclusive) of each character
1846    /// in UTF-8 code units (bytes). The sum of these lengths must equal
1847    /// the length of [`value`], also in bytes.
1848    ///
1849    /// A character is defined as the smallest unit of text that
1850    /// can be selected. This isn't necessarily a single Unicode
1851    /// scalar value (code point). This is why AccessKit can't compute
1852    /// the lengths of the characters from the text itself; this information
1853    /// must be provided by the text editing implementation.
1854    ///
1855    /// If this node is the last text run in a line that ends with a hard
1856    /// line break, that line break should be included at the end of this
1857    /// node's value as either a CRLF or LF; in both cases, the line break
1858    /// should be counted as a single character for the sake of this slice.
1859    /// When the caret is at the end of such a line, the focus of the text
1860    /// selection should be on the line break, not after it.
1861    ///
1862    /// [`value`]: Node::value
1863    (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
1864
1865    /// For text runs, the length of each word in characters, as defined
1866    /// in [`character_lengths`]. The sum of these lengths must equal
1867    /// the length of [`character_lengths`].
1868    ///
1869    /// The end of each word is the beginning of the next word; there are no
1870    /// characters that are not considered part of a word. Trailing whitespace
1871    /// is typically considered part of the word that precedes it, while
1872    /// a line's leading whitespace is considered its own word. Whether
1873    /// punctuation is considered a separate word or part of the preceding
1874    /// word depends on the particular text editing implementation.
1875    /// Some editors may have their own definition of a word; for example,
1876    /// in an IDE, words may correspond to programming language tokens.
1877    ///
1878    /// Not all assistive technologies require information about word
1879    /// boundaries, and not all platform accessibility APIs even expose
1880    /// this information, but for assistive technologies that do use
1881    /// this information, users will get unpredictable results if the word
1882    /// boundaries exposed by the accessibility tree don't match
1883    /// the editor's behavior. This is why AccessKit does not determine
1884    /// word boundaries itself.
1885    ///
1886    /// [`character_lengths`]: Node::character_lengths
1887    (WordLengths, word_lengths, set_word_lengths, clear_word_lengths)
1888}
1889
1890coord_slice_property_methods! {
1891    /// For text runs, this is the position of each character within
1892    /// the node's bounding box, in the direction given by
1893    /// [`text_direction`], in the coordinate space of this node.
1894    ///
1895    /// When present, the length of this slice should be the same as the length
1896    /// of [`character_lengths`], including for lines that end
1897    /// with a hard line break. The position of such a line break should
1898    /// be the position where an end-of-paragraph marker would be rendered.
1899    ///
1900    /// This property is optional. Without it, AccessKit can't support some
1901    /// use cases, such as screen magnifiers that track the caret position
1902    /// or screen readers that display a highlight cursor. However,
1903    /// most text functionality still works without this information.
1904    ///
1905    /// [`text_direction`]: Node::text_direction
1906    /// [`character_lengths`]: Node::character_lengths
1907    (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
1908
1909    /// For text runs, this is the advance width of each character,
1910    /// in the direction given by [`text_direction`], in the coordinate
1911    /// space of this node.
1912    ///
1913    /// When present, the length of this slice should be the same as the length
1914    /// of [`character_lengths`], including for lines that end
1915    /// with a hard line break. The width of such a line break should
1916    /// be non-zero if selecting the line break by itself results in
1917    /// a visible highlight (as in Microsoft Word), or zero if not
1918    /// (as in Windows Notepad).
1919    ///
1920    /// This property is optional. Without it, AccessKit can't support some
1921    /// use cases, such as screen magnifiers that track the caret position
1922    /// or screen readers that display a highlight cursor. However,
1923    /// most text functionality still works without this information.
1924    ///
1925    /// [`text_direction`]: Node::text_direction
1926    /// [`character_lengths`]: Node::character_lengths
1927    (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
1928}
1929
1930bool_property_methods! {
1931    /// Whether this node is expanded, collapsed, or neither.
1932    ///
1933    /// Setting this to `false` means the node is collapsed; omitting it means this state
1934    /// isn't applicable.
1935    (Expanded, is_expanded, set_expanded, clear_expanded),
1936
1937    /// Indicates whether this node is selected or unselected.
1938    ///
1939    /// The absence of this flag (as opposed to a `false` setting)
1940    /// means that the concept of "selected" doesn't apply.
1941    /// When deciding whether to set the flag to false or omit it,
1942    /// consider whether it would be appropriate for a screen reader
1943    /// to announce "not selected". The ambiguity of this flag
1944    /// in platform accessibility APIs has made extraneous
1945    /// "not selected" announcements a common annoyance.
1946    (Selected, is_selected, set_selected, clear_selected)
1947}
1948
1949unique_enum_property_methods! {
1950    (Invalid, invalid, set_invalid, clear_invalid, Grammar),
1951    (Toggled, toggled, set_toggled, clear_toggled, True),
1952    (Live, live, set_live, clear_live, Polite),
1953    (TextDirection, text_direction, set_text_direction, clear_text_direction, RightToLeft),
1954    (Orientation, orientation, set_orientation, clear_orientation, Vertical),
1955    (SortDirection, sort_direction, set_sort_direction, clear_sort_direction, Descending),
1956    (AriaCurrent, aria_current, set_aria_current, clear_aria_current, True),
1957    (AutoComplete, auto_complete, set_auto_complete, clear_auto_complete, List),
1958    (HasPopup, has_popup, set_has_popup, clear_has_popup, Menu),
1959    /// The list style type. Only available on list items.
1960    (ListStyle, list_style, set_list_style, clear_list_style, Disc),
1961    (TextAlign, text_align, set_text_align, clear_text_align, Right),
1962    (VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset, Superscript)
1963}
1964
1965property_methods! {
1966    /// An affine transform to apply to any coordinates within this node
1967    /// and its descendants, including the [`bounds`] property of this node.
1968    /// The combined transforms of this node and its ancestors define
1969    /// the coordinate space of this node. /// This should be `None` if
1970    /// it would be set to the identity transform, which should be the case
1971    /// for most nodes.
1972    ///
1973    /// AccessKit expects the final transformed coordinates to be relative
1974    /// to the origin of the tree's container (e.g. window), in physical
1975    /// pixels, with the y coordinate being top-down.
1976    ///
1977    /// [`bounds`]: Node::bounds
1978    (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
1979
1980    /// The bounding box of this node, in the node's coordinate space.
1981    /// This property does not affect the coordinate space of either this node
1982    /// or its descendants; only the [`transform`] property affects that.
1983    /// This, along with the recommendation that most nodes should have
1984    /// a [`transform`] of `None`, implies that the `bounds` property
1985    /// of most nodes should be in the coordinate space of the nearest ancestor
1986    /// with a non-`None` [`transform`], or if there is no such ancestor,
1987    /// the tree's container (e.g. window).
1988    ///
1989    /// [`transform`]: Node::transform
1990    (Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
1991
1992    (TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection)
1993}
1994
1995impl Node {
1996    option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection,] }
1997}
1998
1999#[cfg(test)]
2000mod transform {
2001    use super::{Affine, Node, Role};
2002
2003    #[test]
2004    fn getter_should_return_default_value() {
2005        let node = Node::new(Role::Unknown);
2006        assert!(node.transform().is_none());
2007    }
2008    #[test]
2009    fn setter_should_update_the_property() {
2010        let mut node = Node::new(Role::Unknown);
2011        node.set_transform(Affine::IDENTITY);
2012        assert_eq!(node.transform(), Some(&Affine::IDENTITY));
2013    }
2014    #[test]
2015    fn clearer_should_reset_the_property() {
2016        let mut node = Node::new(Role::Unknown);
2017        node.set_transform(Affine::IDENTITY);
2018        node.clear_transform();
2019        assert!(node.transform().is_none());
2020    }
2021}
2022
2023#[cfg(test)]
2024mod bounds {
2025    use super::{Node, Rect, Role};
2026
2027    #[test]
2028    fn getter_should_return_default_value() {
2029        let node = Node::new(Role::Unknown);
2030        assert!(node.bounds().is_none());
2031    }
2032    #[test]
2033    fn setter_should_update_the_property() {
2034        let mut node = Node::new(Role::Unknown);
2035        let value = Rect {
2036            x0: 0.0,
2037            y0: 1.0,
2038            x1: 2.0,
2039            y1: 3.0,
2040        };
2041        node.set_bounds(value);
2042        assert_eq!(node.bounds(), Some(value));
2043    }
2044    #[test]
2045    fn clearer_should_reset_the_property() {
2046        let mut node = Node::new(Role::Unknown);
2047        node.set_bounds(Rect {
2048            x0: 0.0,
2049            y0: 1.0,
2050            x1: 2.0,
2051            y1: 3.0,
2052        });
2053        node.clear_bounds();
2054        assert!(node.bounds().is_none());
2055    }
2056}
2057
2058#[cfg(test)]
2059mod text_selection {
2060    use super::{Node, NodeId, Role, TextPosition, TextSelection};
2061
2062    #[test]
2063    fn getter_should_return_default_value() {
2064        let node = Node::new(Role::Unknown);
2065        assert!(node.text_selection().is_none());
2066    }
2067    #[test]
2068    fn setter_should_update_the_property() {
2069        let mut node = Node::new(Role::Unknown);
2070        let value = TextSelection {
2071            anchor: TextPosition {
2072                node: NodeId(0),
2073                character_index: 0,
2074            },
2075            focus: TextPosition {
2076                node: NodeId(0),
2077                character_index: 2,
2078            },
2079        };
2080        node.set_text_selection(value);
2081        assert_eq!(node.text_selection(), Some(&value));
2082    }
2083    #[test]
2084    fn clearer_should_reset_the_property() {
2085        let mut node = Node::new(Role::Unknown);
2086        node.set_text_selection(TextSelection {
2087            anchor: TextPosition {
2088                node: NodeId(0),
2089                character_index: 0,
2090            },
2091            focus: TextPosition {
2092                node: NodeId(0),
2093                character_index: 2,
2094            },
2095        });
2096        node.clear_text_selection();
2097        assert!(node.text_selection().is_none());
2098    }
2099}
2100
2101vec_property_methods! {
2102    (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)
2103}
2104
2105#[cfg(test)]
2106mod custom_actions {
2107    use super::{CustomAction, Node, Role};
2108    use core::slice;
2109
2110    #[test]
2111    fn getter_should_return_default_value() {
2112        let node = Node::new(Role::Unknown);
2113        assert!(node.custom_actions().is_empty());
2114    }
2115    #[test]
2116    fn setter_should_update_the_property() {
2117        let mut node = Node::new(Role::Unknown);
2118        let value = alloc::vec![
2119            CustomAction {
2120                id: 0,
2121                description: "first test action".into(),
2122            },
2123            CustomAction {
2124                id: 1,
2125                description: "second test action".into(),
2126            },
2127        ];
2128        node.set_custom_actions(value.clone());
2129        assert_eq!(node.custom_actions(), value);
2130    }
2131    #[test]
2132    fn pusher_should_update_the_property() {
2133        let mut node = Node::new(Role::Unknown);
2134        let first_action = CustomAction {
2135            id: 0,
2136            description: "first test action".into(),
2137        };
2138        let second_action = CustomAction {
2139            id: 1,
2140            description: "second test action".into(),
2141        };
2142        node.push_custom_action(first_action.clone());
2143        assert_eq!(node.custom_actions(), slice::from_ref(&first_action));
2144        node.push_custom_action(second_action.clone());
2145        assert_eq!(node.custom_actions(), &[first_action, second_action]);
2146    }
2147    #[test]
2148    fn clearer_should_reset_the_property() {
2149        let mut node = Node::new(Role::Unknown);
2150        node.set_custom_actions([CustomAction {
2151            id: 0,
2152            description: "test action".into(),
2153        }]);
2154        node.clear_custom_actions();
2155        assert!(node.custom_actions().is_empty());
2156    }
2157}
2158
2159impl fmt::Debug for Node {
2160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2161        let mut fmt = f.debug_struct("Node");
2162
2163        fmt.field("role", &self.role());
2164
2165        let supported_actions = action_mask_to_action_vec(self.actions);
2166        if !supported_actions.is_empty() {
2167            fmt.field("actions", &supported_actions);
2168        }
2169
2170        let child_supported_actions = action_mask_to_action_vec(self.child_actions);
2171        if !child_supported_actions.is_empty() {
2172            fmt.field("child_actions", &child_supported_actions);
2173        }
2174
2175        self.debug_flag_properties(&mut fmt);
2176        self.debug_node_id_vec_properties(&mut fmt);
2177        self.debug_node_id_properties(&mut fmt);
2178        self.debug_string_properties(&mut fmt);
2179        self.debug_f64_properties(&mut fmt);
2180        self.debug_usize_properties(&mut fmt);
2181        self.debug_color_properties(&mut fmt);
2182        self.debug_text_decoration_properties(&mut fmt);
2183        self.debug_length_slice_properties(&mut fmt);
2184        self.debug_coord_slice_properties(&mut fmt);
2185        self.debug_bool_properties(&mut fmt);
2186        self.debug_unique_enum_properties(&mut fmt);
2187        self.debug_option_properties(&mut fmt);
2188
2189        let custom_actions = self.custom_actions();
2190        if !custom_actions.is_empty() {
2191            fmt.field("custom_actions", &custom_actions);
2192        }
2193
2194        fmt.finish()
2195    }
2196}
2197
2198#[cfg(feature = "serde")]
2199macro_rules! serialize_property {
2200    ($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
2201        match &$self.values[$index as usize] {
2202            PropertyValue::None => (),
2203            $(PropertyValue::$variant(value) => {
2204                $map.serialize_entry(&$id, &value)?;
2205            })*
2206        }
2207    }
2208}
2209
2210#[cfg(feature = "serde")]
2211macro_rules! deserialize_property {
2212    ($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
2213        match $key {
2214            $($(PropertyId::$id => {
2215                let value = $map.next_value()?;
2216                $props.set(PropertyId::$id, PropertyValue::$type(value));
2217            })*)*
2218            PropertyId::Unset => {
2219                let _ = $map.next_value::<IgnoredAny>()?;
2220            }
2221        }
2222    }
2223}
2224
2225#[cfg(feature = "serde")]
2226impl Serialize for Properties {
2227    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2228    where
2229        S: Serializer,
2230    {
2231        let mut len = 0;
2232        for value in &*self.values {
2233            if !matches!(*value, PropertyValue::None) {
2234                len += 1;
2235            }
2236        }
2237        let mut map = serializer.serialize_map(Some(len))?;
2238        for (id, index) in self.indices.0.iter().copied().enumerate() {
2239            if index == PropertyId::Unset as u8 {
2240                continue;
2241            }
2242            let id = PropertyId::n(id as _).unwrap();
2243            serialize_property!(self, map, index, id, {
2244                NodeIdVec,
2245                NodeId,
2246                String,
2247                F64,
2248                Usize,
2249                Color,
2250                TextDecoration,
2251                LengthSlice,
2252                CoordSlice,
2253                Bool,
2254                Invalid,
2255                Toggled,
2256                Live,
2257                TextDirection,
2258                Orientation,
2259                SortDirection,
2260                AriaCurrent,
2261                AutoComplete,
2262                HasPopup,
2263                ListStyle,
2264                TextAlign,
2265                VerticalOffset,
2266                Affine,
2267                Rect,
2268                TextSelection,
2269                CustomActionVec
2270            });
2271        }
2272        map.end()
2273    }
2274}
2275
2276#[cfg(feature = "serde")]
2277struct PropertiesVisitor;
2278
2279#[cfg(feature = "serde")]
2280impl<'de> Visitor<'de> for PropertiesVisitor {
2281    type Value = Properties;
2282
2283    #[inline]
2284    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2285        formatter.write_str("property map")
2286    }
2287
2288    fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
2289    where
2290        V: MapAccess<'de>,
2291    {
2292        let mut props = Properties::default();
2293        while let Some(id) = map.next_key()? {
2294            deserialize_property!(props, map, id, {
2295                NodeIdVec {
2296                    Children,
2297                    Controls,
2298                    Details,
2299                    DescribedBy,
2300                    FlowTo,
2301                    LabelledBy,
2302                    Owns,
2303                    RadioGroup
2304                },
2305                NodeId {
2306                    ActiveDescendant,
2307                    ErrorMessage,
2308                    InPageLinkTarget,
2309                    MemberOf,
2310                    NextOnLine,
2311                    PreviousOnLine,
2312                    PopupFor
2313                },
2314                String {
2315                    Label,
2316                    Description,
2317                    Value,
2318                    AccessKey,
2319                    AuthorId,
2320                    ClassName,
2321                    FontFamily,
2322                    HtmlTag,
2323                    InnerHtml,
2324                    KeyboardShortcut,
2325                    Language,
2326                    Placeholder,
2327                    RoleDescription,
2328                    StateDescription,
2329                    Tooltip,
2330                    Url,
2331                    RowIndexText,
2332                    ColumnIndexText
2333                },
2334                F64 {
2335                    ScrollX,
2336                    ScrollXMin,
2337                    ScrollXMax,
2338                    ScrollY,
2339                    ScrollYMin,
2340                    ScrollYMax,
2341                    NumericValue,
2342                    MinNumericValue,
2343                    MaxNumericValue,
2344                    NumericValueStep,
2345                    NumericValueJump,
2346                    FontSize,
2347                    FontWeight
2348                },
2349                Usize {
2350                    RowCount,
2351                    ColumnCount,
2352                    RowIndex,
2353                    ColumnIndex,
2354                    RowSpan,
2355                    ColumnSpan,
2356                    Level,
2357                    SizeOfSet,
2358                    PositionInSet
2359                },
2360                Color {
2361                    ColorValue,
2362                    BackgroundColor,
2363                    ForegroundColor
2364                },
2365                TextDecoration {
2366                    Overline,
2367                    Strikethrough,
2368                    Underline
2369                },
2370                LengthSlice {
2371                    CharacterLengths,
2372                    WordLengths
2373                },
2374                CoordSlice {
2375                    CharacterPositions,
2376                    CharacterWidths
2377                },
2378                Bool {
2379                    Expanded,
2380                    Selected
2381                },
2382                Invalid { Invalid },
2383                Toggled { Toggled },
2384                Live { Live },
2385                TextDirection { TextDirection },
2386                Orientation { Orientation },
2387                SortDirection { SortDirection },
2388                AriaCurrent { AriaCurrent },
2389                AutoComplete { AutoComplete },
2390                HasPopup { HasPopup },
2391                ListStyle { ListStyle },
2392                TextAlign { TextAlign },
2393                VerticalOffset { VerticalOffset },
2394                Affine { Transform },
2395                Rect { Bounds },
2396                TextSelection { TextSelection },
2397                CustomActionVec { CustomActions }
2398            });
2399        }
2400
2401        Ok(props)
2402    }
2403}
2404
2405#[cfg(feature = "serde")]
2406impl<'de> Deserialize<'de> for Properties {
2407    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2408    where
2409        D: Deserializer<'de>,
2410    {
2411        deserializer.deserialize_map(PropertiesVisitor)
2412    }
2413}
2414
2415#[cfg(feature = "schemars")]
2416macro_rules! add_schema_property {
2417    ($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
2418        let name = format!("{:?}", $enum_value);
2419        let name = name[..1].to_ascii_lowercase() + &name[1..];
2420        let subschema = $gen.subschema_for::<$type>();
2421        $properties.insert(name, subschema);
2422    }};
2423}
2424
2425#[cfg(feature = "schemars")]
2426macro_rules! add_properties_to_schema {
2427    ($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
2428        $($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
2429    }
2430}
2431
2432#[cfg(feature = "schemars")]
2433impl JsonSchema for Properties {
2434    #[inline]
2435    fn schema_name() -> String {
2436        "Properties".into()
2437    }
2438
2439    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
2440        let mut properties = SchemaMap::<String, Schema>::new();
2441        add_properties_to_schema!(gen, properties, {
2442            Vec<NodeId> {
2443                Children,
2444                Controls,
2445                Details,
2446                DescribedBy,
2447                FlowTo,
2448                LabelledBy,
2449                Owns,
2450                RadioGroup
2451            },
2452            NodeId {
2453                ActiveDescendant,
2454                ErrorMessage,
2455                InPageLinkTarget,
2456                MemberOf,
2457                NextOnLine,
2458                PreviousOnLine,
2459                PopupFor
2460            },
2461            Box<str> {
2462                Label,
2463                Description,
2464                Value,
2465                AccessKey,
2466                AuthorId,
2467                ClassName,
2468                FontFamily,
2469                HtmlTag,
2470                InnerHtml,
2471                KeyboardShortcut,
2472                Language,
2473                Placeholder,
2474                RoleDescription,
2475                StateDescription,
2476                Tooltip,
2477                Url,
2478                RowIndexText,
2479                ColumnIndexText
2480            },
2481            f64 {
2482                ScrollX,
2483                ScrollXMin,
2484                ScrollXMax,
2485                ScrollY,
2486                ScrollYMin,
2487                ScrollYMax,
2488                NumericValue,
2489                MinNumericValue,
2490                MaxNumericValue,
2491                NumericValueStep,
2492                NumericValueJump,
2493                FontSize,
2494                FontWeight
2495            },
2496            usize {
2497                RowCount,
2498                ColumnCount,
2499                RowIndex,
2500                ColumnIndex,
2501                RowSpan,
2502                ColumnSpan,
2503                Level,
2504                SizeOfSet,
2505                PositionInSet
2506            },
2507            u32 {
2508                ColorValue,
2509                BackgroundColor,
2510                ForegroundColor
2511            },
2512            TextDecoration {
2513                Overline,
2514                Strikethrough,
2515                Underline
2516            },
2517            Box<[u8]> {
2518                CharacterLengths,
2519                WordLengths
2520            },
2521            Box<[f32]> {
2522                CharacterPositions,
2523                CharacterWidths
2524            },
2525            bool {
2526                Expanded,
2527                Selected
2528            },
2529            Invalid { Invalid },
2530            Toggled { Toggled },
2531            Live { Live },
2532            TextDirection { TextDirection },
2533            Orientation { Orientation },
2534            SortDirection { SortDirection },
2535            AriaCurrent { AriaCurrent },
2536            AutoComplete { AutoComplete },
2537            HasPopup { HasPopup },
2538            ListStyle { ListStyle },
2539            TextAlign { TextAlign },
2540            VerticalOffset { VerticalOffset },
2541            Affine { Transform },
2542            Rect { Bounds },
2543            TextSelection { TextSelection },
2544            Vec<CustomAction> { CustomActions }
2545        });
2546        SchemaObject {
2547            instance_type: Some(InstanceType::Object.into()),
2548            object: Some(
2549                ObjectValidation {
2550                    properties,
2551                    ..Default::default()
2552                }
2553                .into(),
2554            ),
2555            ..Default::default()
2556        }
2557        .into()
2558    }
2559}
2560
2561/// The data associated with an accessibility tree that's global to the
2562/// tree and not associated with any particular node.
2563#[derive(Clone, Debug, PartialEq, Eq)]
2564#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2565#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2566#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2567#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2568pub struct Tree {
2569    /// The identifier of the tree's root node.
2570    pub root: NodeId,
2571    /// The name of the UI toolkit in use.
2572    pub toolkit_name: Option<String>,
2573    /// The version of the UI toolkit.
2574    pub toolkit_version: Option<String>,
2575}
2576
2577impl Tree {
2578    #[inline]
2579    pub fn new(root: NodeId) -> Tree {
2580        Tree {
2581            root,
2582            toolkit_name: None,
2583            toolkit_version: None,
2584        }
2585    }
2586}
2587
2588/// A serializable representation of an atomic change to a [`Tree`].
2589///
2590/// The sender and receiver must be in sync; the update is only meant
2591/// to bring the tree from a specific previous state into its next state.
2592/// Trying to apply it to the wrong tree should immediately panic.
2593///
2594/// Note that for performance, an update should only include nodes that are
2595/// new or changed. AccessKit platform adapters will avoid raising extraneous
2596/// events for nodes that have not changed since the previous update,
2597/// but there is still a cost in processing these nodes and replacing
2598/// the previous instances.
2599#[derive(Clone, Debug, PartialEq)]
2600#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2601#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2602#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2603#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2604pub struct TreeUpdate {
2605    /// Zero or more new or updated nodes. Order doesn't matter.
2606    ///
2607    /// Each node in this list will overwrite any existing node with the same ID.
2608    /// This means that when updating a node, fields that are unchanged
2609    /// from the previous version must still be set to the same values
2610    /// as before.
2611    ///
2612    /// It is an error for any node in this list to not be either the root
2613    /// or a child of another node. For nodes other than the root, the parent
2614    /// must be either an unchanged node already in the tree, or another node
2615    /// in this list.
2616    ///
2617    /// To add a child to the tree, the list must include both the child
2618    /// and an updated version of the parent with the child's ID added to
2619    /// [`Node::children`].
2620    ///
2621    /// To remove a child and all of its descendants, this list must include
2622    /// an updated version of the parent node with the child's ID removed
2623    /// from [`Node::children`]. Neither the child nor any of its descendants
2624    /// may be included in this list.
2625    pub nodes: Vec<(NodeId, Node)>,
2626
2627    /// Rarely updated information about the tree as a whole. This may be omitted
2628    /// if it has not changed since the previous update, but providing the same
2629    /// information again is also allowed. This is required when initializing
2630    /// a tree.
2631    pub tree: Option<Tree>,
2632
2633    /// The node within this tree that has keyboard focus when the native
2634    /// host (e.g. window) has focus. If no specific node within the tree
2635    /// has keyboard focus, this must be set to the root. The latest focus state
2636    /// must be provided with every tree update, even if the focus state
2637    /// didn't change in a given update.
2638    pub focus: NodeId,
2639}
2640
2641/// The amount by which to scroll in the direction specified by one of the
2642/// `Scroll` actions.
2643#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2644#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2645#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2646#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2647#[cfg_attr(
2648    feature = "pyo3",
2649    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
2650)]
2651#[repr(u8)]
2652pub enum ScrollUnit {
2653    /// A single item of a list, line of text (for vertical scrolling),
2654    /// character (for horizontal scrolling), or an approximation of
2655    /// one of these.
2656    Item,
2657    /// The amount of content that fits in the viewport.
2658    Page,
2659}
2660
2661/// A suggestion about where the node being scrolled into view should be
2662/// positioned relative to the edges of the scrollable container.
2663#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2664#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2665#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2666#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2667#[cfg_attr(
2668    feature = "pyo3",
2669    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
2670)]
2671#[repr(u8)]
2672pub enum ScrollHint {
2673    TopLeft,
2674    BottomRight,
2675    TopEdge,
2676    BottomEdge,
2677    LeftEdge,
2678    RightEdge,
2679}
2680
2681#[derive(Clone, Debug, PartialEq)]
2682#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2683#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2684#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2685#[repr(C)]
2686pub enum ActionData {
2687    CustomAction(i32),
2688    Value(Box<str>),
2689    NumericValue(f64),
2690    ScrollUnit(ScrollUnit),
2691    /// Optional suggestion for [`Action::ScrollIntoView`], specifying
2692    /// the preferred position of the target node relative to the scrollable
2693    /// container's viewport.
2694    ScrollHint(ScrollHint),
2695    /// Target for [`Action::ScrollToPoint`], in platform-native coordinates
2696    /// relative to the origin of the tree's container (e.g. window).
2697    ScrollToPoint(Point),
2698    /// Target for [`Action::SetScrollOffset`], in the coordinate space
2699    /// of the action's target node.
2700    SetScrollOffset(Point),
2701    SetTextSelection(TextSelection),
2702}
2703
2704#[derive(Clone, Debug, PartialEq)]
2705#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2706#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2707#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2708#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2709pub struct ActionRequest {
2710    pub action: Action,
2711    pub target: NodeId,
2712    pub data: Option<ActionData>,
2713}
2714
2715/// Handles activation of the application's accessibility implementation.
2716pub trait ActivationHandler {
2717    /// Requests a [`TreeUpdate`] with a full tree. If the application
2718    /// can generate the tree synchronously within this method call,
2719    /// it should do so and return the [`TreeUpdate`]. Otherwise,
2720    /// it must send the update to the platform adapter asynchronously,
2721    /// no later than the next display refresh, even if a frame would not
2722    /// normally be rendered due to user input or other activity.
2723    /// The application should not return or send a placeholder [`TreeUpdate`];
2724    /// the platform adapter will provide one if necessary until the real
2725    /// tree is sent.
2726    ///
2727    /// The primary purpose of this method is to allow the application
2728    /// to lazily initialize its accessibility implementation. However,
2729    /// this method may be called consecutively without any call to
2730    /// [`DeactivationHandler::deactivate_accessibility`]; this typically happens
2731    /// if the platform adapter merely forwards tree updates to assistive
2732    /// technologies without maintaining any state. A call to this method
2733    /// must always generate a [`TreeUpdate`] with a full tree, even if
2734    /// the application normally sends incremental updates.
2735    ///
2736    /// The thread on which this method is called is platform-dependent.
2737    /// Refer to the platform adapter documentation for more details.
2738    fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
2739}
2740
2741/// Handles requests from assistive technologies or other clients.
2742pub trait ActionHandler {
2743    /// Perform the requested action. If the requested action is not supported,
2744    /// this method must do nothing.
2745    ///
2746    /// The thread on which this method is called is platform-dependent.
2747    /// Refer to the platform adapter documentation for more details.
2748    ///
2749    /// This method may queue the request and handle it asynchronously.
2750    /// This behavior is preferred over blocking, e.g. when dispatching
2751    /// the request to another thread.
2752    fn do_action(&mut self, request: ActionRequest);
2753}
2754
2755/// Handles deactivation of the application's accessibility implementation.
2756pub trait DeactivationHandler {
2757    /// Deactivate the application's accessibility implementation and drop any
2758    /// associated data that can be reconstructed later. After this method
2759    /// is called, if an accessibility tree is needed again, the platform
2760    /// adapter will call [`ActivationHandler::request_initial_tree`] again.
2761    ///
2762    /// The thread on which this method is called is platform-dependent.
2763    /// Refer to the platform adapter documentation for more details.
2764    fn deactivate_accessibility(&mut self);
2765}
2766
2767#[cfg(test)]
2768mod tests {
2769    use super::*;
2770    use alloc::format;
2771
2772    #[test]
2773    fn u64_should_be_convertible_to_node_id() {
2774        assert_eq!(NodeId::from(0u64), NodeId(0));
2775        assert_eq!(NodeId::from(1u64), NodeId(1));
2776    }
2777
2778    #[test]
2779    fn node_id_should_be_convertible_to_u64() {
2780        assert_eq!(u64::from(NodeId(0)), 0u64);
2781        assert_eq!(u64::from(NodeId(1)), 1u64);
2782    }
2783
2784    #[test]
2785    fn node_id_should_have_debug_repr() {
2786        assert_eq!(&format!("{:?}", NodeId(0)), "#0");
2787        assert_eq!(&format!("{:?}", NodeId(1)), "#1");
2788    }
2789
2790    #[test]
2791    fn action_n_should_return_the_corresponding_variant() {
2792        assert_eq!(Action::n(0), Some(Action::Click));
2793        assert_eq!(Action::n(1), Some(Action::Focus));
2794        assert_eq!(Action::n(2), Some(Action::Blur));
2795        assert_eq!(Action::n(3), Some(Action::Collapse));
2796        assert_eq!(Action::n(4), Some(Action::Expand));
2797        assert_eq!(Action::n(5), Some(Action::CustomAction));
2798        assert_eq!(Action::n(6), Some(Action::Decrement));
2799        assert_eq!(Action::n(7), Some(Action::Increment));
2800        assert_eq!(Action::n(8), Some(Action::HideTooltip));
2801        assert_eq!(Action::n(9), Some(Action::ShowTooltip));
2802        assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
2803        assert_eq!(Action::n(11), Some(Action::ScrollDown));
2804        assert_eq!(Action::n(12), Some(Action::ScrollLeft));
2805        assert_eq!(Action::n(13), Some(Action::ScrollRight));
2806        assert_eq!(Action::n(14), Some(Action::ScrollUp));
2807        assert_eq!(Action::n(15), Some(Action::ScrollIntoView));
2808        assert_eq!(Action::n(16), Some(Action::ScrollToPoint));
2809        assert_eq!(Action::n(17), Some(Action::SetScrollOffset));
2810        assert_eq!(Action::n(18), Some(Action::SetTextSelection));
2811        assert_eq!(
2812            Action::n(19),
2813            Some(Action::SetSequentialFocusNavigationStartingPoint)
2814        );
2815        assert_eq!(Action::n(20), Some(Action::SetValue));
2816        assert_eq!(Action::n(21), Some(Action::ShowContextMenu));
2817        assert_eq!(Action::n(22), None);
2818    }
2819
2820    #[test]
2821    fn empty_action_mask_should_be_converted_to_empty_vec() {
2822        assert_eq!(
2823            Vec::<Action>::new(),
2824            action_mask_to_action_vec(Node::new(Role::Unknown).actions)
2825        );
2826    }
2827
2828    #[test]
2829    fn action_mask_should_be_convertible_to_vec() {
2830        let mut node = Node::new(Role::Unknown);
2831        node.add_action(Action::Click);
2832        assert_eq!(
2833            &[Action::Click],
2834            action_mask_to_action_vec(node.actions).as_slice()
2835        );
2836
2837        let mut node = Node::new(Role::Unknown);
2838        node.add_action(Action::ShowContextMenu);
2839        assert_eq!(
2840            &[Action::ShowContextMenu],
2841            action_mask_to_action_vec(node.actions).as_slice()
2842        );
2843
2844        let mut node = Node::new(Role::Unknown);
2845        node.add_action(Action::Click);
2846        node.add_action(Action::ShowContextMenu);
2847        assert_eq!(
2848            &[Action::Click, Action::ShowContextMenu],
2849            action_mask_to_action_vec(node.actions).as_slice()
2850        );
2851
2852        let mut node = Node::new(Role::Unknown);
2853        node.add_action(Action::Focus);
2854        node.add_action(Action::Blur);
2855        node.add_action(Action::Collapse);
2856        assert_eq!(
2857            &[Action::Focus, Action::Blur, Action::Collapse],
2858            action_mask_to_action_vec(node.actions).as_slice()
2859        );
2860    }
2861
2862    #[test]
2863    fn new_node_should_have_user_provided_role() {
2864        let node = Node::new(Role::Button);
2865        assert_eq!(node.role(), Role::Button);
2866    }
2867
2868    #[test]
2869    fn node_role_setter_should_update_the_role() {
2870        let mut node = Node::new(Role::Button);
2871        node.set_role(Role::CheckBox);
2872        assert_eq!(node.role(), Role::CheckBox);
2873    }
2874
2875    macro_rules! assert_absent_action {
2876        ($node:ident, $action:ident) => {
2877            assert!(!$node.supports_action(Action::$action));
2878            assert!(!$node.child_supports_action(Action::$action));
2879        };
2880    }
2881
2882    #[test]
2883    fn new_node_should_not_support_anyaction() {
2884        let node = Node::new(Role::Unknown);
2885        assert_absent_action!(node, Click);
2886        assert_absent_action!(node, Focus);
2887        assert_absent_action!(node, Blur);
2888        assert_absent_action!(node, Collapse);
2889        assert_absent_action!(node, Expand);
2890        assert_absent_action!(node, CustomAction);
2891        assert_absent_action!(node, Decrement);
2892        assert_absent_action!(node, Increment);
2893        assert_absent_action!(node, HideTooltip);
2894        assert_absent_action!(node, ShowTooltip);
2895        assert_absent_action!(node, ReplaceSelectedText);
2896        assert_absent_action!(node, ScrollDown);
2897        assert_absent_action!(node, ScrollLeft);
2898        assert_absent_action!(node, ScrollRight);
2899        assert_absent_action!(node, ScrollUp);
2900        assert_absent_action!(node, ScrollIntoView);
2901        assert_absent_action!(node, ScrollToPoint);
2902        assert_absent_action!(node, SetScrollOffset);
2903        assert_absent_action!(node, SetTextSelection);
2904        assert_absent_action!(node, SetSequentialFocusNavigationStartingPoint);
2905        assert_absent_action!(node, SetValue);
2906        assert_absent_action!(node, ShowContextMenu);
2907    }
2908
2909    #[test]
2910    fn node_add_action_should_add_the_action() {
2911        let mut node = Node::new(Role::Unknown);
2912        node.add_action(Action::Focus);
2913        assert!(node.supports_action(Action::Focus));
2914        node.add_action(Action::Blur);
2915        assert!(node.supports_action(Action::Blur));
2916    }
2917
2918    #[test]
2919    fn node_add_child_action_should_add_the_action() {
2920        let mut node = Node::new(Role::Unknown);
2921        node.add_child_action(Action::Focus);
2922        assert!(node.child_supports_action(Action::Focus));
2923        node.add_child_action(Action::Blur);
2924        assert!(node.child_supports_action(Action::Blur));
2925    }
2926
2927    #[test]
2928    fn node_add_action_should_do_nothing_if_the_action_is_already_supported() {
2929        let mut node = Node::new(Role::Unknown);
2930        node.add_action(Action::Focus);
2931        node.add_action(Action::Focus);
2932        assert!(node.supports_action(Action::Focus));
2933    }
2934
2935    #[test]
2936    fn node_add_child_action_should_do_nothing_if_the_action_is_already_supported() {
2937        let mut node = Node::new(Role::Unknown);
2938        node.add_child_action(Action::Focus);
2939        node.add_child_action(Action::Focus);
2940        assert!(node.child_supports_action(Action::Focus));
2941    }
2942
2943    #[test]
2944    fn node_remove_action_should_remove_the_action() {
2945        let mut node = Node::new(Role::Unknown);
2946        node.add_action(Action::Blur);
2947        node.remove_action(Action::Blur);
2948        assert!(!node.supports_action(Action::Blur));
2949    }
2950
2951    #[test]
2952    fn node_remove_child_action_should_remove_the_action() {
2953        let mut node = Node::new(Role::Unknown);
2954        node.add_child_action(Action::Blur);
2955        node.remove_child_action(Action::Blur);
2956        assert!(!node.child_supports_action(Action::Blur));
2957    }
2958
2959    #[test]
2960    fn node_clear_actions_should_remove_all_actions() {
2961        let mut node = Node::new(Role::Unknown);
2962        node.add_action(Action::Focus);
2963        node.add_action(Action::Blur);
2964        node.clear_actions();
2965        assert!(!node.supports_action(Action::Focus));
2966        assert!(!node.supports_action(Action::Blur));
2967    }
2968
2969    #[test]
2970    fn node_clear_child_actions_should_remove_all_actions() {
2971        let mut node = Node::new(Role::Unknown);
2972        node.add_child_action(Action::Focus);
2973        node.add_child_action(Action::Blur);
2974        node.clear_child_actions();
2975        assert!(!node.child_supports_action(Action::Focus));
2976        assert!(!node.child_supports_action(Action::Blur));
2977    }
2978
2979    #[test]
2980    fn node_should_have_debug_repr() {
2981        let mut node = Node::new(Role::Unknown);
2982        node.add_action(Action::Click);
2983        node.add_action(Action::Focus);
2984        node.add_child_action(Action::ScrollIntoView);
2985        node.set_hidden();
2986        node.set_multiselectable();
2987        node.set_children([NodeId(0), NodeId(1)]);
2988        node.set_active_descendant(NodeId(2));
2989        node.push_custom_action(CustomAction {
2990            id: 0,
2991            description: "test action".into(),
2992        });
2993
2994        assert_eq!(
2995            &format!("{node:?}"),
2996            r#"Node { role: Unknown, actions: [Click, Focus], child_actions: [ScrollIntoView], is_hidden: true, is_multiselectable: true, children: [#0, #1], active_descendant: #2, custom_actions: [CustomAction { id: 0, description: "test action" }] }"#
2997        );
2998    }
2999
3000    #[test]
3001    fn new_tree_should_have_root_id() {
3002        let tree = Tree::new(NodeId(1));
3003        assert_eq!(tree.root, NodeId(1));
3004        assert_eq!(tree.toolkit_name, None);
3005        assert_eq!(tree.toolkit_version, None);
3006    }
3007}