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